cef/libcef/browser/request_context_impl.cc
Marshall Greenblatt ca0e381681 Add support for complete isolation of storage and permissions (cache, cookies, localStorage, access grants, etc) on a per-request-context basis (issue #1044).
- CefRequestContext instances can be configured using a new CefRequestContextSettings structure passed to CefRequestContext::CreateContext.
- Scheme registration is now per-request-context using new CefRequestContext::RegisterSchemeHandlerFactory and ClearSchemeHandlerFactories methods.
- Cookie managers are now per-request-context by default and can be retrieved using a new CefRequestContext::GetDefaultCookieManager method.
- CefURLRequest::Create now accepts an optional CefRequestContext argument for associating a URL request with a context (browser process only).
- The CefRequestContextHandler associated with a CefRequestContext will not be released until all objects related to that context have been destroyed.
- When the cache path is empty an in-memory cache ("incognito mode") will be used for storage and no data will be persisted to disk.
- Add CefSettings.user_data_path which specifies the location where user data such as spell checking dictionary files will be stored on disk.
- Add asynchronous callbacks for all CefCookieManager methods.
- Add PK_LOCAL_APP_DATA and PK_USER_DATA path keys for retrieving user directories via CefGetPath.
- cefclient: Add "New Window" test that creates a new window unrelated to existing windows. When used in combination with `--request-context-per-browser` the new window will be given a new and isolated request context.

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@2040 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
2015-03-02 20:25:14 +00:00

380 lines
12 KiB
C++

// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "libcef/browser/request_context_impl.h"
#include "libcef/browser/browser_context_impl.h"
#include "libcef/browser/browser_context_proxy.h"
#include "libcef/browser/content_browser_client.h"
#include "libcef/browser/context.h"
#include "libcef/browser/cookie_manager_impl.h"
#include "libcef/browser/thread_util.h"
#include "base/atomic_sequence_num.h"
#include "base/logging.h"
using content::BrowserThread;
namespace {
base::StaticAtomicSequenceNumber g_next_id;
CefRefPtr<CefBrowserContextImpl> GetImpl(CefRefPtr<CefBrowserContext> context) {
if (context->IsProxy())
return static_cast<CefBrowserContextProxy*>(context.get())->parent();
return static_cast<CefBrowserContextImpl*>(context.get());
}
} // namespace
// CefBrowserContext
// static
CefRefPtr<CefRequestContext> CefRequestContext::GetGlobalContext() {
// Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {
NOTREACHED() << "context not valid";
return NULL;
}
return CefRequestContextImpl::GetForBrowserContext(
CefContentBrowserClient::Get()->browser_context());
}
// static
CefRefPtr<CefRequestContext> CefRequestContext::CreateContext(
const CefRequestContextSettings& settings,
CefRefPtr<CefRequestContextHandler> handler) {
// Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {
NOTREACHED() << "context not valid";
return NULL;
}
return new CefRequestContextImpl(settings, handler);
}
// static
CefRefPtr<CefRequestContext> CefRequestContext::CreateContext(
CefRefPtr<CefRequestContext> other,
CefRefPtr<CefRequestContextHandler> handler) {
// Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {
NOTREACHED() << "context not valid";
return NULL;
}
if (!other.get())
return NULL;
return new CefRequestContextImpl(
static_cast<CefRequestContextImpl*>(other.get()), handler);
}
// CefBrowserContextImpl
CefRequestContextImpl::~CefRequestContextImpl() {
}
// static
CefRefPtr<CefRequestContextImpl> CefRequestContextImpl::GetForRequestContext(
CefRefPtr<CefRequestContext> request_context) {
if (request_context.get()) {
// Use the context from the provided CefRequestContext.
return static_cast<CefRequestContextImpl*>(request_context.get());
}
// Use the global context.
scoped_refptr<CefBrowserContext> browser_context =
CefContentBrowserClient::Get()->browser_context();
return GetForBrowserContext(browser_context);
}
// static
CefRefPtr<CefRequestContextImpl> CefRequestContextImpl::GetForBrowserContext(
scoped_refptr<CefBrowserContext> browser_context) {
DCHECK(browser_context.get());
return new CefRequestContextImpl(browser_context);
}
scoped_refptr<CefBrowserContext> CefRequestContextImpl::GetBrowserContext() {
CEF_REQUIRE_UIT();
if (!browser_context_) {
scoped_refptr<CefBrowserContextImpl> parent;
if (other_.get()) {
// Share storage with |other_|.
parent = GetImpl(other_->GetBrowserContext());
}
if (!parent.get()) {
const base::FilePath& cache_path =
base::FilePath(CefString(&settings_.cache_path));
if (!cache_path.empty()) {
// Check if a CefBrowserContextImpl is already globally registered for
// the specified cache path. If so then use it.
parent = CefBrowserContextImpl::GetForCachePath(cache_path);
}
}
if (!parent.get()) {
// Create a new CefBrowserContextImpl instance. If the cache path is non-
// empty then this new instance will become the globally registered
// CefBrowserContextImpl for that path. Otherwise, this new instance will
// be a completely isolated "incongento mode" context.
parent = new CefBrowserContextImpl(settings_);
parent->Initialize();
}
// The parent's settings may be different. Force our settings to match the
// parent.
settings_ = parent->GetSettings();
if (handler_.get()) {
// Use a proxy that will execute handler callbacks where appropriate and
// otherwise forward all requests to the parent implementation.
browser_context_ = new CefBrowserContextProxy(handler_, parent);
} else {
// Use the parent implementation directly.
browser_context_ = parent;
}
request_context_impl_ = parent->request_context().get();
DCHECK(request_context_impl_);
if (handler_.get()) {
// Keep the handler alive until the associated request context is
// destroyed.
request_context_impl_->AddHandler(handler_);
}
}
if (!request_context_impl_) {
request_context_impl_ = GetImpl(browser_context_)->request_context().get();
DCHECK(request_context_impl_);
}
if (other_.get()) {
// Clear the reference to |other_| after setting |request_context_impl_|.
// This is the reverse order of checks in IsSharedWith().
other_ = NULL;
}
return browser_context_;
}
void CefRequestContextImpl::GetBrowserContext(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const BrowserContextCallback& callback) {
if (!task_runner.get())
task_runner = base::MessageLoop::current()->task_runner();
GetBrowserContextOnUIThread(task_runner, callback);
}
void CefRequestContextImpl::GetRequestContextImpl(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const RequestContextCallback& callback) {
if (!task_runner.get())
task_runner = base::MessageLoop::current()->task_runner();
if (request_context_impl_) {
// The browser context already exists.
DCHECK(browser_context_.get());
GetRequestContextImplOnIOThread(task_runner, callback, browser_context_);
} else {
// Need to initialize the browser context first.
GetBrowserContextOnUIThread(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
base::Bind(&CefRequestContextImpl::GetRequestContextImplOnIOThread,
this, task_runner, callback));
}
}
bool CefRequestContextImpl::IsSame(CefRefPtr<CefRequestContext> other) {
CefRequestContextImpl* other_impl =
static_cast<CefRequestContextImpl*>(other.get());
if (!other_impl)
return false;
// Compare CefBrowserContext pointers if one has been associated.
if (browser_context_ && other_impl->browser_context_)
return (browser_context_ == other_impl->browser_context_);
else if (browser_context_ || other_impl->browser_context_)
return false;
// Otherwise compare unique IDs.
return (unique_id_ == other_impl->unique_id_);
}
bool CefRequestContextImpl::IsSharingWith(CefRefPtr<CefRequestContext> other) {
CefRequestContextImpl* other_impl =
static_cast<CefRequestContextImpl*>(other.get());
if (!other_impl)
return false;
if (IsSame(other))
return true;
CefRefPtr<CefRequestContext> pending_other = other_;
if (pending_other.get()) {
// This object is not initialized but we know what context this object will
// share with. Compare to that other context instead.
return pending_other->IsSharingWith(other);
}
pending_other = other_impl->other_;
if (pending_other.get()) {
// The other object is not initialized but we know what context that object
// will share with. Compare to that other context instead.
return pending_other->IsSharingWith(this);
}
if (request_context_impl_ && other_impl->request_context_impl_) {
// Both objects are initialized. Compare the request context objects.
return (request_context_impl_ == other_impl->request_context_impl_);
}
// This or the other object is not initialized. Compare the cache path values.
// If both are non-empty and the same then they'll share the same storage.
if (settings_.cache_path.length > 0 &&
other_impl->settings_.cache_path.length > 0) {
return (base::FilePath(CefString(&settings_.cache_path)) ==
base::FilePath(CefString(&other_impl->settings_.cache_path)));
}
return false;
}
bool CefRequestContextImpl::IsGlobal() {
return (browser_context_ ==
CefContentBrowserClient::Get()->browser_context());
}
CefRefPtr<CefRequestContextHandler> CefRequestContextImpl::GetHandler() {
return handler_;
}
CefString CefRequestContextImpl::GetCachePath() {
return CefString(&settings_.cache_path);
}
CefRefPtr<CefCookieManager> CefRequestContextImpl::GetDefaultCookieManager(
CefRefPtr<CefCompletionCallback> callback) {
CefRefPtr<CefCookieManagerImpl> cookie_manager = new CefCookieManagerImpl();
cookie_manager->Initialize(this, CefString(), false, callback);
return cookie_manager.get();
}
bool CefRequestContextImpl::RegisterSchemeHandlerFactory(
const CefString& scheme_name,
const CefString& domain_name,
CefRefPtr<CefSchemeHandlerFactory> factory) {
GetRequestContextImpl(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
base::Bind(&CefRequestContextImpl::RegisterSchemeHandlerFactoryInternal,
this, scheme_name, domain_name, factory));
return true;
}
bool CefRequestContextImpl::ClearSchemeHandlerFactories() {
GetRequestContextImpl(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
base::Bind(&CefRequestContextImpl::ClearSchemeHandlerFactoriesInternal,
this));
return true;
}
CefRequestContextImpl::CefRequestContextImpl(
scoped_refptr<CefBrowserContext> browser_context)
: browser_context_(browser_context),
settings_(browser_context->GetSettings()),
handler_(browser_context->GetHandler()),
unique_id_(0),
request_context_impl_(NULL) {
}
CefRequestContextImpl::CefRequestContextImpl(
const CefRequestContextSettings& settings,
CefRefPtr<CefRequestContextHandler> handler)
: settings_(settings),
handler_(handler),
unique_id_(g_next_id.GetNext()),
request_context_impl_(NULL) {
}
CefRequestContextImpl::CefRequestContextImpl(
CefRefPtr<CefRequestContextImpl> other,
CefRefPtr<CefRequestContextHandler> handler)
: other_(other),
handler_(handler),
unique_id_(g_next_id.GetNext()),
request_context_impl_(NULL) {
}
void CefRequestContextImpl::GetBrowserContextOnUIThread(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const BrowserContextCallback& callback) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT,
base::Bind(&CefRequestContextImpl::GetBrowserContextOnUIThread,
this, task_runner, callback));
return;
}
// Make sure the browser context exists.
GetBrowserContext();
DCHECK(browser_context_.get());
DCHECK(request_context_impl_);
if (task_runner->BelongsToCurrentThread()) {
// Execute the callback immediately.
callback.Run(browser_context_);
} else {
// Execute the callback on the target thread.
task_runner->PostTask(FROM_HERE, base::Bind(callback, browser_context_));
}
}
void CefRequestContextImpl::GetRequestContextImplOnIOThread(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const RequestContextCallback& callback,
scoped_refptr<CefBrowserContext> browser_context) {
if (!CEF_CURRENTLY_ON_IOT()) {
CEF_POST_TASK(CEF_IOT,
base::Bind(&CefRequestContextImpl::GetRequestContextImplOnIOThread,
this, task_runner, callback, browser_context));
return;
}
DCHECK(request_context_impl_);
// Make sure the request context exists.
request_context_impl_->GetURLRequestContext();
if (task_runner->BelongsToCurrentThread()) {
// Execute the callback immediately.
callback.Run(request_context_impl_);
} else {
// Execute the callback on the target thread.
task_runner->PostTask(FROM_HERE,
base::Bind(callback, make_scoped_refptr(request_context_impl_)));
}
}
void CefRequestContextImpl::RegisterSchemeHandlerFactoryInternal(
const CefString& scheme_name,
const CefString& domain_name,
CefRefPtr<CefSchemeHandlerFactory> factory,
scoped_refptr<CefURLRequestContextGetterImpl> request_context) {
CEF_REQUIRE_IOT();
request_context->request_manager()->AddFactory(scheme_name, domain_name,
factory);
}
void CefRequestContextImpl::ClearSchemeHandlerFactoriesInternal(
scoped_refptr<CefURLRequestContextGetterImpl> request_context) {
CEF_REQUIRE_IOT();
request_context->request_manager()->ClearFactories();
}