449 lines
15 KiB
C++
449 lines
15 KiB
C++
// Copyright (c) 2015 GitHub, Inc.
|
|
// Use of this source code is governed by the MIT license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "atom/browser/api/atom_api_session.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "atom/browser/api/atom_api_cookies.h"
|
|
#include "atom/browser/api/atom_api_download_item.h"
|
|
#include "atom/browser/api/atom_api_web_contents.h"
|
|
#include "atom/browser/api/save_page_handler.h"
|
|
#include "atom/browser/atom_browser_context.h"
|
|
#include "atom/browser/atom_browser_main_parts.h"
|
|
#include "atom/common/native_mate_converters/callback.h"
|
|
#include "atom/common/native_mate_converters/gurl_converter.h"
|
|
#include "atom/common/native_mate_converters/file_path_converter.h"
|
|
#include "atom/common/native_mate_converters/net_converter.h"
|
|
#include "atom/common/node_includes.h"
|
|
#include "base/files/file_path.h"
|
|
#include "base/prefs/pref_service.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "base/thread_task_runner_handle.h"
|
|
#include "brightray/browser/net/devtools_network_conditions.h"
|
|
#include "brightray/browser/net/devtools_network_controller.h"
|
|
#include "chrome/common/pref_names.h"
|
|
#include "content/public/browser/browser_thread.h"
|
|
#include "content/public/browser/storage_partition.h"
|
|
#include "native_mate/dictionary.h"
|
|
#include "native_mate/object_template_builder.h"
|
|
#include "net/base/load_flags.h"
|
|
#include "net/disk_cache/disk_cache.h"
|
|
#include "net/proxy/proxy_service.h"
|
|
#include "net/proxy/proxy_config_service_fixed.h"
|
|
#include "net/url_request/url_request_context.h"
|
|
#include "net/url_request/url_request_context_getter.h"
|
|
|
|
using content::BrowserThread;
|
|
using content::StoragePartition;
|
|
|
|
namespace {
|
|
|
|
struct ClearStorageDataOptions {
|
|
GURL origin;
|
|
uint32 storage_types = StoragePartition::REMOVE_DATA_MASK_ALL;
|
|
uint32 quota_types = StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL;
|
|
};
|
|
|
|
uint32 GetStorageMask(const std::vector<std::string>& storage_types) {
|
|
uint32 storage_mask = 0;
|
|
for (const auto& it : storage_types) {
|
|
auto type = base::StringToLowerASCII(it);
|
|
if (type == "appcache")
|
|
storage_mask |= StoragePartition::REMOVE_DATA_MASK_APPCACHE;
|
|
else if (type == "cookies")
|
|
storage_mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
|
|
else if (type == "filesystem")
|
|
storage_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
|
|
else if (type == "indexdb")
|
|
storage_mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
|
|
else if (type == "localstorage")
|
|
storage_mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
|
|
else if (type == "shadercache")
|
|
storage_mask |= StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
|
|
else if (type == "websql")
|
|
storage_mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
|
|
else if (type == "serviceworkers")
|
|
storage_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
|
|
}
|
|
return storage_mask;
|
|
}
|
|
|
|
uint32 GetQuotaMask(const std::vector<std::string>& quota_types) {
|
|
uint32 quota_mask = 0;
|
|
for (const auto& it : quota_types) {
|
|
auto type = base::StringToLowerASCII(it);
|
|
if (type == "temporary")
|
|
quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY;
|
|
else if (type == "persistent")
|
|
quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_PERSISTENT;
|
|
else if (type == "syncable")
|
|
quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_SYNCABLE;
|
|
}
|
|
return quota_mask;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace mate {
|
|
|
|
template<>
|
|
struct Converter<ClearStorageDataOptions> {
|
|
static bool FromV8(v8::Isolate* isolate,
|
|
v8::Local<v8::Value> val,
|
|
ClearStorageDataOptions* out) {
|
|
mate::Dictionary options;
|
|
if (!ConvertFromV8(isolate, val, &options))
|
|
return false;
|
|
options.Get("origin", &out->origin);
|
|
std::vector<std::string> types;
|
|
if (options.Get("storages", &types))
|
|
out->storage_types = GetStorageMask(types);
|
|
if (options.Get("quotas", &types))
|
|
out->quota_types = GetQuotaMask(types);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct Converter<net::ProxyConfig> {
|
|
static bool FromV8(v8::Isolate* isolate,
|
|
v8::Local<v8::Value> val,
|
|
net::ProxyConfig* out) {
|
|
std::string proxy;
|
|
if (!ConvertFromV8(isolate, val, &proxy))
|
|
return false;
|
|
auto pac_url = GURL(proxy);
|
|
if (pac_url.is_valid()) {
|
|
out->set_pac_url(pac_url);
|
|
} else {
|
|
out->proxy_rules().ParseFromString(proxy);
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
} // namespace mate
|
|
|
|
namespace atom {
|
|
|
|
namespace api {
|
|
|
|
namespace {
|
|
|
|
// The wrapSession funtion which is implemented in JavaScript
|
|
using WrapSessionCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
|
WrapSessionCallback g_wrap_session;
|
|
|
|
class ResolveProxyHelper {
|
|
public:
|
|
ResolveProxyHelper(AtomBrowserContext* browser_context,
|
|
const GURL& url,
|
|
Session::ResolveProxyCallback callback)
|
|
: callback_(callback),
|
|
original_thread_(base::ThreadTaskRunnerHandle::Get()) {
|
|
scoped_refptr<net::URLRequestContextGetter> context_getter =
|
|
browser_context->GetRequestContext();
|
|
context_getter->GetNetworkTaskRunner()->PostTask(
|
|
FROM_HERE,
|
|
base::Bind(&ResolveProxyHelper::ResolveProxy,
|
|
base::Unretained(this), context_getter, url));
|
|
}
|
|
|
|
void OnResolveProxyCompleted(int result) {
|
|
std::string proxy;
|
|
if (result == net::OK)
|
|
proxy = proxy_info_.ToPacString();
|
|
original_thread_->PostTask(FROM_HERE,
|
|
base::Bind(callback_, proxy));
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
void ResolveProxy(scoped_refptr<net::URLRequestContextGetter> context_getter,
|
|
const GURL& url) {
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
|
|
|
net::ProxyService* proxy_service =
|
|
context_getter->GetURLRequestContext()->proxy_service();
|
|
net::CompletionCallback completion_callback =
|
|
base::Bind(&ResolveProxyHelper::OnResolveProxyCompleted,
|
|
base::Unretained(this));
|
|
|
|
// Start the request.
|
|
int result = proxy_service->ResolveProxy(
|
|
url, net::LOAD_NORMAL, &proxy_info_, completion_callback,
|
|
&pac_req_, nullptr, net::BoundNetLog());
|
|
|
|
// Completed synchronously.
|
|
if (result != net::ERR_IO_PENDING)
|
|
completion_callback.Run(result);
|
|
}
|
|
|
|
Session::ResolveProxyCallback callback_;
|
|
net::ProxyInfo proxy_info_;
|
|
net::ProxyService::PacRequest* pac_req_;
|
|
scoped_refptr<base::SingleThreadTaskRunner> original_thread_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ResolveProxyHelper);
|
|
};
|
|
|
|
// Runs the callback in UI thread.
|
|
template <typename ...T>
|
|
void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) {
|
|
BrowserThread::PostTask(
|
|
BrowserThread::UI, FROM_HERE, base::Bind(callback, result...));
|
|
}
|
|
|
|
// Callback of HttpCache::GetBackend.
|
|
void OnGetBackend(disk_cache::Backend** backend_ptr,
|
|
const net::CompletionCallback& callback,
|
|
int result) {
|
|
if (result != net::OK) {
|
|
RunCallbackInUI(callback, result);
|
|
} else if (backend_ptr && *backend_ptr) {
|
|
(*backend_ptr)->DoomAllEntries(base::Bind(&RunCallbackInUI<int>, callback));
|
|
} else {
|
|
RunCallbackInUI<int>(callback, net::ERR_FAILED);
|
|
}
|
|
}
|
|
|
|
void ClearHttpCacheInIO(
|
|
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
|
|
const net::CompletionCallback& callback) {
|
|
auto request_context = context_getter->GetURLRequestContext();
|
|
auto http_cache = request_context->http_transaction_factory()->GetCache();
|
|
if (!http_cache)
|
|
RunCallbackInUI<int>(callback, net::ERR_FAILED);
|
|
|
|
// Call GetBackend and make the backend's ptr accessable in OnGetBackend.
|
|
using BackendPtr = disk_cache::Backend*;
|
|
BackendPtr* backend_ptr = new BackendPtr(nullptr);
|
|
net::CompletionCallback on_get_backend =
|
|
base::Bind(&OnGetBackend, base::Owned(backend_ptr), callback);
|
|
int rv = http_cache->GetBackend(backend_ptr, on_get_backend);
|
|
if (rv != net::ERR_IO_PENDING)
|
|
on_get_backend.Run(net::OK);
|
|
}
|
|
|
|
void SetProxyInIO(net::URLRequestContextGetter* getter,
|
|
const net::ProxyConfig& config,
|
|
const base::Closure& callback) {
|
|
auto proxy_service = getter->GetURLRequestContext()->proxy_service();
|
|
proxy_service->ResetConfigService(new net::ProxyConfigServiceFixed(config));
|
|
// Refetches and applies the new pac script if provided.
|
|
proxy_service->ForceReloadProxyConfig();
|
|
RunCallbackInUI(callback);
|
|
}
|
|
|
|
void PassVerificationResult(
|
|
scoped_refptr<AtomCertVerifier::CertVerifyRequest> request,
|
|
bool success) {
|
|
request->ContinueWithResult(success ? net::OK : net::ERR_FAILED);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Session::Session(AtomBrowserContext* browser_context)
|
|
: browser_context_(browser_context) {
|
|
AttachAsUserData(browser_context);
|
|
browser_context->cert_verifier()->SetDelegate(this);
|
|
|
|
// Observe DownloadManger to get download notifications.
|
|
content::BrowserContext::GetDownloadManager(browser_context)->
|
|
AddObserver(this);
|
|
}
|
|
|
|
Session::~Session() {
|
|
content::BrowserContext::GetDownloadManager(browser_context())->
|
|
RemoveObserver(this);
|
|
Destroy();
|
|
}
|
|
|
|
void Session::RequestCertVerification(
|
|
const scoped_refptr<AtomCertVerifier::CertVerifyRequest>& request) {
|
|
bool prevent_default = Emit(
|
|
"untrusted-certificate",
|
|
request->args().hostname,
|
|
request->args().cert,
|
|
base::Bind(&PassVerificationResult, request));
|
|
|
|
if (!prevent_default)
|
|
// Tell the request to use the result of default verifier.
|
|
request->ContinueWithResult(net::ERR_IO_PENDING);
|
|
}
|
|
|
|
void Session::OnDownloadCreated(content::DownloadManager* manager,
|
|
content::DownloadItem* item) {
|
|
auto web_contents = item->GetWebContents();
|
|
if (SavePageHandler::IsSavePageTypes(item->GetMimeType()))
|
|
return;
|
|
bool prevent_default = Emit(
|
|
"will-download",
|
|
DownloadItem::Create(isolate(), item),
|
|
api::WebContents::CreateFrom(isolate(), web_contents));
|
|
if (prevent_default) {
|
|
item->Cancel(true);
|
|
item->Remove();
|
|
}
|
|
}
|
|
|
|
bool Session::IsDestroyed() const {
|
|
return !browser_context_;
|
|
}
|
|
|
|
void Session::Destroy() {
|
|
browser_context_->cert_verifier()->SetDelegate(nullptr);
|
|
browser_context_ = nullptr;
|
|
}
|
|
|
|
void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
|
|
new ResolveProxyHelper(browser_context(), url, callback);
|
|
}
|
|
|
|
void Session::ClearCache(const net::CompletionCallback& callback) {
|
|
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
|
base::Bind(&ClearHttpCacheInIO,
|
|
make_scoped_refptr(browser_context_->GetRequestContext()),
|
|
callback));
|
|
}
|
|
|
|
void Session::ClearStorageData(mate::Arguments* args) {
|
|
// clearStorageData([options, ]callback)
|
|
ClearStorageDataOptions options;
|
|
args->GetNext(&options);
|
|
base::Closure callback;
|
|
if (!args->GetNext(&callback)) {
|
|
args->ThrowError();
|
|
return;
|
|
}
|
|
|
|
auto storage_partition =
|
|
content::BrowserContext::GetStoragePartition(browser_context(), nullptr);
|
|
storage_partition->ClearData(
|
|
options.storage_types, options.quota_types, options.origin,
|
|
content::StoragePartition::OriginMatcherFunction(),
|
|
base::Time(), base::Time::Max(), callback);
|
|
}
|
|
|
|
void Session::SetProxy(const net::ProxyConfig& config,
|
|
const base::Closure& callback) {
|
|
auto getter = browser_context_->GetRequestContext();
|
|
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
|
base::Bind(&SetProxyInIO, base::Unretained(getter), config, callback));
|
|
}
|
|
|
|
void Session::SetDownloadPath(const base::FilePath& path) {
|
|
browser_context_->prefs()->SetFilePath(
|
|
prefs::kDownloadDefaultDirectory, path);
|
|
}
|
|
|
|
void Session::EnableNetworkEmulation(const mate::Dictionary& options) {
|
|
scoped_ptr<brightray::DevToolsNetworkConditions> conditions;
|
|
bool offline = false;
|
|
double latency, download_throughput, upload_throughput;
|
|
if (options.Get("offline", &offline) && offline) {
|
|
conditions.reset(new brightray::DevToolsNetworkConditions(offline));
|
|
} else {
|
|
options.Get("latency", &latency);
|
|
options.Get("downloadThroughput", &download_throughput);
|
|
options.Get("uploadThroughput", &upload_throughput);
|
|
conditions.reset(
|
|
new brightray::DevToolsNetworkConditions(false,
|
|
latency,
|
|
download_throughput,
|
|
upload_throughput));
|
|
}
|
|
auto controller = browser_context_->GetDevToolsNetworkController();
|
|
|
|
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
|
base::Bind(&brightray::DevToolsNetworkController::SetNetworkState,
|
|
base::Unretained(controller),
|
|
std::string(),
|
|
base::Passed(&conditions)));
|
|
}
|
|
|
|
void Session::DisableNetworkEmulation() {
|
|
scoped_ptr<brightray::DevToolsNetworkConditions> conditions(
|
|
new brightray::DevToolsNetworkConditions(false));
|
|
auto controller = browser_context_->GetDevToolsNetworkController();
|
|
|
|
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
|
base::Bind(&brightray::DevToolsNetworkController::SetNetworkState,
|
|
base::Unretained(controller),
|
|
std::string(),
|
|
base::Passed(&conditions)));
|
|
}
|
|
|
|
v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
|
|
if (cookies_.IsEmpty()) {
|
|
auto handle = atom::api::Cookies::Create(isolate, browser_context());
|
|
cookies_.Reset(isolate, handle.ToV8());
|
|
}
|
|
return v8::Local<v8::Value>::New(isolate, cookies_);
|
|
}
|
|
|
|
mate::ObjectTemplateBuilder Session::GetObjectTemplateBuilder(
|
|
v8::Isolate* isolate) {
|
|
return mate::ObjectTemplateBuilder(isolate)
|
|
.SetMethod("resolveProxy", &Session::ResolveProxy)
|
|
.SetMethod("clearCache", &Session::ClearCache)
|
|
.SetMethod("clearStorageData", &Session::ClearStorageData)
|
|
.SetMethod("setProxy", &Session::SetProxy)
|
|
.SetMethod("setDownloadPath", &Session::SetDownloadPath)
|
|
.SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation)
|
|
.SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation)
|
|
.SetProperty("cookies", &Session::Cookies);
|
|
}
|
|
|
|
// static
|
|
mate::Handle<Session> Session::CreateFrom(
|
|
v8::Isolate* isolate, AtomBrowserContext* browser_context) {
|
|
auto existing = TrackableObject::FromWrappedClass(isolate, browser_context);
|
|
if (existing)
|
|
return mate::CreateHandle(isolate, static_cast<Session*>(existing));
|
|
|
|
auto handle = mate::CreateHandle(isolate, new Session(browser_context));
|
|
g_wrap_session.Run(handle.ToV8());
|
|
return handle;
|
|
}
|
|
|
|
// static
|
|
mate::Handle<Session> Session::FromPartition(
|
|
v8::Isolate* isolate, const std::string& partition, bool in_memory) {
|
|
auto browser_context = brightray::BrowserContext::From(partition, in_memory);
|
|
return CreateFrom(isolate,
|
|
static_cast<AtomBrowserContext*>(browser_context.get()));
|
|
}
|
|
|
|
void ClearWrapSession() {
|
|
g_wrap_session.Reset();
|
|
}
|
|
|
|
void SetWrapSession(const WrapSessionCallback& callback) {
|
|
g_wrap_session = callback;
|
|
|
|
// Cleanup the wrapper on exit.
|
|
atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(
|
|
base::Bind(ClearWrapSession));
|
|
}
|
|
|
|
} // namespace api
|
|
|
|
} // namespace atom
|
|
|
|
namespace {
|
|
|
|
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
|
v8::Local<v8::Context> context, void* priv) {
|
|
v8::Isolate* isolate = context->GetIsolate();
|
|
mate::Dictionary dict(isolate, exports);
|
|
dict.SetMethod("fromPartition", &atom::api::Session::FromPartition);
|
|
dict.SetMethod("_setWrapSession", &atom::api::SetWrapSession);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_session, Initialize)
|