Commit 5780ea8b authored by Marshall Greenblatt's avatar Marshall Greenblatt

Support configuration of preferences during runtime (issue #1709)

- Preferences are now associated with a CefRequestContext instead of
  being stored globally.
- Add methods to CefRequestContext for getting/setting preferences.
parent 2328b9be
......@@ -428,6 +428,7 @@
'tests/unittests/os_rendering_unittest.cc',
'tests/unittests/parser_unittest.cc',
'tests/unittests/plugin_unittest.cc',
'tests/unittests/preference_unittest.cc',
'tests/unittests/print_unittest.cc',
'tests/unittests/process_message_unittest.cc',
'tests/unittests/request_context_unittest.cc',
......@@ -1798,6 +1799,7 @@
'tests/unittests/message_router_unittest.cc',
'tests/unittests/navigation_unittest.cc',
'tests/unittests/plugin_unittest.cc',
'tests/unittests/preference_unittest.cc',
'tests/unittests/process_message_unittest.cc',
'tests/unittests/request_handler_unittest.cc',
'tests/unittests/request_unittest.cc',
......
......@@ -171,6 +171,8 @@
'tests/cefclient/browser/osr_dragdrop_events.h',
'tests/cefclient/browser/osr_renderer.h',
'tests/cefclient/browser/osr_renderer.cc',
'tests/cefclient/browser/preferences_test.cc',
'tests/cefclient/browser/preferences_test.h',
'tests/cefclient/browser/resource.h',
'tests/cefclient/browser/resource_util.h',
'tests/cefclient/browser/root_window.cc',
......@@ -220,6 +222,7 @@
'tests/cefclient/resources/pdf.pdf',
'tests/cefclient/resources/performance.html',
'tests/cefclient/resources/performance2.html',
'tests/cefclient/resources/preferences.html',
'tests/cefclient/resources/transparency.html',
'tests/cefclient/resources/urlrequest.html',
'tests/cefclient/resources/window.html',
......
......@@ -40,6 +40,7 @@
#include "include/capi/cef_cookie_capi.h"
#include "include/capi/cef_request_context_handler_capi.h"
#include "include/capi/cef_values_capi.h"
#ifdef __cplusplus
extern "C" {
......@@ -150,6 +151,54 @@ typedef struct _cef_request_context_t {
///
void (CEF_CALLBACK *purge_plugin_list_cache)(
struct _cef_request_context_t* self, int reload_pages);
///
// Returns true (1) if a preference with the specified |name| exists. This
// function must be called on the browser process UI thread.
///
int (CEF_CALLBACK *has_preference)(struct _cef_request_context_t* self,
const cef_string_t* name);
///
// Returns the value for the preference with the specified |name|. Returns
// NULL if the preference does not exist. The returned object contains a copy
// of the underlying preference value and modifications to the returned object
// will not modify the underlying preference value. This function must be
// called on the browser process UI thread.
///
struct _cef_value_t* (CEF_CALLBACK *get_preference)(
struct _cef_request_context_t* self, const cef_string_t* name);
///
// Returns all preferences as a dictionary. If |include_defaults| is true (1)
// then preferences currently at their default value will be included. The
// returned object contains a copy of the underlying preference values and
// modifications to the returned object will not modify the underlying
// preference values. This function must be called on the browser process UI
// thread.
///
struct _cef_dictionary_value_t* (CEF_CALLBACK *get_all_preferences)(
struct _cef_request_context_t* self, int include_defaults);
///
// Returns true (1) if the preference with the specified |name| can be
// modified using SetPreference. As one example preferences set via the
// command-line usually cannot be modified. This function must be called on
// the browser process UI thread.
///
int (CEF_CALLBACK *can_set_preference)(struct _cef_request_context_t* self,
const cef_string_t* name);
///
// Set the |value| associated with preference |name|. Returns true (1) if the
// value is set successfully and false (0) otherwise. If |value| is NULL the
// preference will be restored to its default value. If setting the preference
// fails then |error| will be populated with a detailed description of the
// problem. This function must be called on the browser process UI thread.
///
int (CEF_CALLBACK *set_preference)(struct _cef_request_context_t* self,
const cef_string_t* name, struct _cef_value_t* value,
cef_string_t* error);
} cef_request_context_t;
......
......@@ -40,6 +40,7 @@
#include "include/cef_cookie.h"
#include "include/cef_request_context_handler.h"
#include "include/cef_values.h"
class CefSchemeHandlerFactory;
......@@ -166,6 +167,56 @@ class CefRequestContext : public virtual CefBase {
///
/*--cef()--*/
virtual void PurgePluginListCache(bool reload_pages) =0;
///
// Returns true if a preference with the specified |name| exists. This method
// must be called on the browser process UI thread.
///
/*--cef()--*/
virtual bool HasPreference(const CefString& name) =0;
///
// Returns the value for the preference with the specified |name|. Returns
// NULL if the preference does not exist. The returned object contains a copy
// of the underlying preference value and modifications to the returned object
// will not modify the underlying preference value. This method must be called
// on the browser process UI thread.
///
/*--cef()--*/
virtual CefRefPtr<CefValue> GetPreference(const CefString& name) =0;
///
// Returns all preferences as a dictionary. If |include_defaults| is true then
// preferences currently at their default value will be included. The returned
// object contains a copy of the underlying preference values and
// modifications to the returned object will not modify the underlying
// preference values. This method must be called on the browser process UI
// thread.
///
/*--cef()--*/
virtual CefRefPtr<CefDictionaryValue> GetAllPreferences(
bool include_defaults) =0;
///
// Returns true if the preference with the specified |name| can be modified
// using SetPreference. As one example preferences set via the command-line
// usually cannot be modified. This method must be called on the browser
// process UI thread.
///
/*--cef()--*/
virtual bool CanSetPreference(const CefString& name) =0;
///
// Set the |value| associated with preference |name|. Returns true if the
// value is set successfully and false otherwise. If |value| is NULL the
// preference will be restored to its default value. If setting the preference
// fails then |error| will be populated with a detailed description of the
// problem. This method must be called on the browser process UI thread.
///
/*--cef(optional_param=value)--*/
virtual bool SetPreference(const CefString& name,
CefRefPtr<CefValue> value,
CefString& error) =0;
};
#endif // CEF_INCLUDE_CEF_REQUEST_CONTEXT_H_
......@@ -7,7 +7,6 @@
#include <map>
#include "libcef/browser/browser_context_proxy.h"
#include "libcef/browser/content_browser_client.h"
#include "libcef/browser/context.h"
#include "libcef/browser/download_manager_delegate.h"
#include "libcef/browser/permission_manager.h"
......@@ -20,6 +19,7 @@
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/net/proxy_service_factory.h"
......@@ -166,6 +166,11 @@ void CefBrowserContextImpl::Initialize() {
CefString(&CefContext::Get()->settings().accept_language_list);
}
// Initialize user preferences.
pref_store_ = new CefBrowserPrefStore();
pref_store_->SetInitializationCompleted();
pref_service_ = pref_store_->CreateService().Pass();
CefBrowserContext::Initialize();
// Initialize proxy configuration tracker.
......@@ -378,6 +383,5 @@ HostContentSettingsMap* CefBrowserContextImpl::GetHostContentSettingsMap() {
}
PrefService* CefBrowserContextImpl::GetPrefs() {
// TODO(cef): Perhaps use per-context settings.
return CefContentBrowserClient::Get()->pref_service();
return pref_service_.get();
}
......@@ -8,6 +8,7 @@
#include "libcef/browser/browser_context.h"
#include "libcef/browser/browser_pref_store.h"
#include "libcef/browser/url_request_context_getter_impl.h"
#include "base/files/file_path.h"
......@@ -111,6 +112,8 @@ class CefBrowserContextImpl : public CefBrowserContext {
typedef std::vector<const CefBrowserContextProxy*> ProxyList;
ProxyList proxy_list_;
scoped_refptr<CefBrowserPrefStore> pref_store_;
scoped_ptr<PrefService> pref_service_;
scoped_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
scoped_ptr<CefDownloadManagerDelegate> download_manager_delegate_;
......
......@@ -122,11 +122,6 @@ int CefBrowserMainParts::PreCreateThreads() {
views::CreateDesktopScreen());
#endif
// Initialize user preferences.
pref_store_ = new CefBrowserPrefStore();
pref_store_->SetInitializationCompleted();
pref_service_ = pref_store_->CreateService().Pass();
return 0;
}
......
......@@ -6,7 +6,6 @@
#define CEF_LIBCEF_BROWSER_BROWSER_MAIN_H_
#pragma once
#include "libcef/browser/browser_pref_store.h"
#include "libcef/browser/browser_context_impl.h"
#include "libcef/browser/url_request_context_getter_impl.h"
......@@ -54,7 +53,6 @@ class CefBrowserMainParts : public content::BrowserMainParts {
CefDevToolsDelegate* devtools_delegate() const {
return devtools_delegate_;
}
PrefService* pref_service() const { return pref_service_.get(); }
private:
#if defined(OS_WIN)
......@@ -65,8 +63,6 @@ class CefBrowserMainParts : public content::BrowserMainParts {
scoped_refptr<CefBrowserContextImpl> global_browser_context_;
CefDevToolsDelegate* devtools_delegate_; // Deletes itself.
scoped_ptr<base::MessageLoop> message_loop_;
scoped_refptr<CefBrowserPrefStore> pref_store_;
scoped_ptr<PrefService> pref_service_;
scoped_ptr<extensions::ExtensionsClient> extensions_client_;
scoped_ptr<extensions::ExtensionsBrowserClient> extensions_browser_client_;
......
......@@ -129,6 +129,16 @@ scoped_ptr<PrefService> CefBrowserPrefStore::CreateService() {
registry->RegisterBooleanPref(prefs::kPluginsAllowOutdated, false);
registry->RegisterBooleanPref(prefs::kPluginsAlwaysAuthorize, false);
if (command_line->HasSwitch(switches::kEnablePreferenceTesting)) {
// Register preferences used with unit tests.
registry->RegisterBooleanPref("test.bool", true);
registry->RegisterIntegerPref("test.int", 2);
registry->RegisterDoublePref("test.double", 5.0);
registry->RegisterStringPref("test.string", "default");
registry->RegisterListPref("test.list");
registry->RegisterDictionaryPref("test.dict");
}
return factory.Create(registry.get());
}
......
......@@ -1034,10 +1034,6 @@ CefDevToolsDelegate* CefContentBrowserClient::devtools_delegate() const {
return browser_main_parts_->devtools_delegate();
}
PrefService* CefContentBrowserClient::pref_service() const {
return browser_main_parts_->pref_service();
}
// static
SkColor CefContentBrowserClient::GetBaseBackgroundColor(
CefRefPtr<CefBrowserHostImpl> browser) {
......
......@@ -29,7 +29,6 @@ class CefBrowserInfo;
class CefBrowserMainParts;
class CefDevToolsDelegate;
class CefResourceDispatcherHostDelegate;
class PrefService;
namespace content {
class PluginServiceFilter;
......@@ -167,7 +166,6 @@ class CefContentBrowserClient : public content::ContentBrowserClient {
scoped_refptr<CefBrowserContextImpl> browser_context() const;
CefDevToolsDelegate* devtools_delegate() const;
PrefService* pref_service() const;
private:
static SkColor GetBaseBackgroundColor(CefRefPtr<CefBrowserHostImpl> browser);
......
......@@ -124,7 +124,7 @@ bool CefExtensionsBrowserClient::AllowCrossRendererResourceLoad(
PrefService* CefExtensionsBrowserClient::GetPrefServiceForContext(
BrowserContext* context) {
return CefBrowserContextImpl::GetForContext(context)->GetPrefs();
return static_cast<CefBrowserContext*>(context)->GetPrefs();
}
void CefExtensionsBrowserClient::GetEarlyExtensionPrefsObservers(
......
......@@ -182,8 +182,8 @@ CefPluginInfoMessageFilter::CefPluginInfoMessageFilter(
int render_process_id,
CefBrowserContext* profile)
: BrowserMessageFilter(ExtensionMsgStart),
context_(render_process_id, profile),
browser_context_(profile),
context_(render_process_id, profile),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
weak_ptr_factory_(this) {
}
......
......@@ -121,8 +121,12 @@ class CefPluginInfoMessageFilter : public content::BrowserMessageFilter {
std::vector<base::string16>* additional_param_values);
#endif
scoped_refptr<CefBrowserContext> browser_context_;
// Members will be destroyed in reverse order of declaration. Due to Context
// depending on the PrefService owned by CefBrowserContext the Context object
// must be destroyed before the CefBrowserContext object.
Context context_;
CefBrowserContext* browser_context_;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
base::WeakPtrFactory<CefPluginInfoMessageFilter> weak_ptr_factory_;
......
......@@ -47,9 +47,12 @@ PrintViewManagerBase::PrintViewManagerBase(content::WebContents* web_contents)
#if !defined(OS_MACOSX)
expecting_first_page_ = true;
#endif // OS_MACOSX
PrefService* pref_service =
static_cast<CefBrowserContext*>(web_contents->GetBrowserContext())->
GetPrefs();
printing_enabled_.Init(
prefs::kPrintingEnabled,
CefContentBrowserClient::Get()->pref_service(),
pref_service,
base::Bind(&PrintViewManagerBase::UpdateScriptedPrintingBlocked,
base::Unretained(this)));
}
......
......@@ -9,9 +9,12 @@
#include "libcef/browser/context.h"
#include "libcef/browser/cookie_manager_impl.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/values_impl.h"
#include "base/atomic_sequence_num.h"
#include "base/logging.h"
#include "base/prefs/pref_service.h"
#include "base/strings/stringprintf.h"
#include "content/public/browser/plugin_service.h"
using content::BrowserThread;
......@@ -20,6 +23,30 @@ namespace {
base::StaticAtomicSequenceNumber g_next_id;
const char* GetTypeString(base::Value::Type type) {
switch (type) {
case base::Value::TYPE_NULL:
return "NULL";
case base::Value::TYPE_BOOLEAN:
return "BOOLEAN";
case base::Value::TYPE_INTEGER:
return "INTEGER";
case base::Value::TYPE_DOUBLE:
return "DOUBLE";
case base::Value::TYPE_STRING:
return "STRING";
case base::Value::TYPE_BINARY:
return "BINARY";
case base::Value::TYPE_DICTIONARY:
return "DICTIONARY";
case base::Value::TYPE_LIST:
return "LIST";
}
NOTREACHED();
return "UNKNOWN";
}
} // namespace
......@@ -292,6 +319,131 @@ void CefRequestContextImpl::PurgePluginListCache(bool reload_pages) {
this, reload_pages));
}
bool CefRequestContextImpl::HasPreference(const CefString& name) {
// Verify that this method is being called on the UI thread.
if (!CEF_CURRENTLY_ON_UIT()) {
NOTREACHED() << "called on invalid thread";
return false;
}
// Make sure the browser context exists.
EnsureBrowserContext();
PrefService* pref_service = browser_context_->GetPrefs();
return (pref_service->FindPreference(name) != NULL);
}
CefRefPtr<CefValue> CefRequestContextImpl::GetPreference(
const CefString& name) {
// Verify that this method is being called on the UI thread.
if (!CEF_CURRENTLY_ON_UIT()) {
NOTREACHED() << "called on invalid thread";
return NULL;
}
// Make sure the browser context exists.
EnsureBrowserContext();
PrefService* pref_service = browser_context_->GetPrefs();
const PrefService::Preference* pref = pref_service->FindPreference(name);
if (!pref)
return NULL;
return new CefValueImpl(pref->GetValue()->DeepCopy());
}
CefRefPtr<CefDictionaryValue> CefRequestContextImpl::GetAllPreferences(
bool include_defaults) {
// Verify that this method is being called on the UI thread.
if (!CEF_CURRENTLY_ON_UIT()) {
NOTREACHED() << "called on invalid thread";
return NULL;
}
// Make sure the browser context exists.
EnsureBrowserContext();
PrefService* pref_service = browser_context_->GetPrefs();
scoped_ptr<base::DictionaryValue> values;
if (include_defaults)
values = pref_service->GetPreferenceValues();
else
values = pref_service->GetPreferenceValuesOmitDefaults();
// CefDictionaryValueImpl takes ownership of |values|.
return new CefDictionaryValueImpl(values.release(), true, false);
}
bool CefRequestContextImpl::CanSetPreference(const CefString& name) {
// Verify that this method is being called on the UI thread.
if (!CEF_CURRENTLY_ON_UIT()) {
NOTREACHED() << "called on invalid thread";
return false;
}
// Make sure the browser context exists.
EnsureBrowserContext();
PrefService* pref_service = browser_context_->GetPrefs();
const PrefService::Preference* pref = pref_service->FindPreference(name);
return (pref && pref->IsUserModifiable());
}
bool CefRequestContextImpl::SetPreference(const CefString& name,
CefRefPtr<CefValue> value,
CefString& error) {
// Verify that this method is being called on the UI thread.
if (!CEF_CURRENTLY_ON_UIT()) {
NOTREACHED() << "called on invalid thread";
return false;
}
// Make sure the browser context exists.
EnsureBrowserContext();
PrefService* pref_service = browser_context_->GetPrefs();
// The below validation logic should match PrefService::SetUserPrefValue.
const PrefService::Preference* pref = pref_service->FindPreference(name);
if (!pref) {
error = "Trying to modify an unregistered preference";
return false;
}
if (!pref->IsUserModifiable()) {
error = "Trying to modify a preference that is not user modifiable";
return false;
}
if (!value.get()) {
// Reset the preference to its default value.
pref_service->ClearPref(name);
return true;
}
if (!value->IsValid()) {
error = "A valid value is required";
return false;
}
CefValueImpl* impl = static_cast<CefValueImpl*>(value.get());
CefValueImpl::ScopedLockedValue scoped_locked_value(impl);
base::Value* impl_value = impl->GetValueUnsafe();
if (pref->GetType() != impl_value->GetType()) {
error = base::StringPrintf(
"Trying to set a preference of type %s to value of type %s",
GetTypeString(pref->GetType()), GetTypeString(impl_value->GetType()));
return false;
}
// PrefService will make a DeepCopy of |impl_value|.
pref_service->Set(name, *impl_value);
return true;
}
CefRequestContextImpl::CefRequestContextImpl(
scoped_refptr<CefBrowserContext> browser_context)
: browser_context_(browser_context),
......@@ -319,6 +471,12 @@ CefRequestContextImpl::CefRequestContextImpl(
request_context_impl_(NULL) {
}
void CefRequestContextImpl::EnsureBrowserContext() {
GetBrowserContext();
DCHECK(browser_context_.get());
DCHECK(request_context_impl_);
}
void CefRequestContextImpl::GetBrowserContextOnUIThread(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const BrowserContextCallback& callback) {
......@@ -330,9 +488,7 @@ void CefRequestContextImpl::GetBrowserContextOnUIThread(
}
// Make sure the browser context exists.
GetBrowserContext();
DCHECK(browser_context_.get());
DCHECK(request_context_impl_);
EnsureBrowserContext();
if (task_runner->BelongsToCurrentThread()) {
// Execute the callback immediately.
......
......@@ -61,6 +61,14 @@ class CefRequestContextImpl : public CefRequestContext {
CefRefPtr<CefSchemeHandlerFactory> factory) override;
bool ClearSchemeHandlerFactories() override;
void PurgePluginListCache(bool reload_pages) override;
bool HasPreference(const CefString& name) override;
CefRefPtr<CefValue> GetPreference(const CefString& name) override;
CefRefPtr<CefDictionaryValue> GetAllPreferences(
bool include_defaults) override;
bool CanSetPreference(const CefString& name) override;
bool SetPreference(const CefString& name,
CefRefPtr<CefValue> value,
CefString& error) override;
const CefRequestContextSettings& settings() const { return settings_; }
......@@ -74,6 +82,9 @@ class CefRequestContextImpl : public CefRequestContext {
CefRequestContextImpl(CefRefPtr<CefRequestContextImpl> other,
CefRefPtr<CefRequestContextHandler> handler);
// Make sure the browser context exists. Only called on the UI thread.
void EnsureBrowserContext();
void GetBrowserContextOnUIThread(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const BrowserContextCallback& callback);
......
......@@ -126,4 +126,7 @@ const char kPluginPolicy_Detect[] = "detect";
// Block the content. The user can manually load blocked content.
const char kPluginPolicy_Block[] = "block";
// Expose preferences used only by unit tests.
const char kEnablePreferenceTesting[] = "enable-preference-testing";
} // namespace switches
......@@ -52,6 +52,7 @@ extern const char kPluginPolicy[];
extern const char kPluginPolicy_Allow[];
extern const char kPluginPolicy_Detect[];
extern const char kPluginPolicy_Block[];
extern const char kEnablePreferenceTesting[];
} // namespace switches
......
......@@ -49,6 +49,10 @@ class CefValueImpl : public CefValue {
bool new_read_only,
CefValueController* new_controller);
// Returns a reference to the underlying data. Access must be protected by
// calling AcquireLock/ReleaseLock.
base::Value* GetValueUnsafe() const;
// CefValue methods.
bool IsValid() override;
bool IsOwned() override;
......@@ -104,10 +108,6 @@ class CefValueImpl : public CefValue {
void AcquireLock();
void ReleaseLock();
// Returns a reference to the underlying data. Access must be protected by
// calling AcquireLock/ReleaseLock.
base::Value* GetValueUnsafe() const;
// Access to all members must be protected by |lock_|.
base::Lock lock_;
......
......@@ -11,7 +11,9 @@
//
#include "libcef_dll/cpptoc/cookie_manager_cpptoc.h"
#include "libcef_dll/cpptoc/dictionary_value_cpptoc.h"
#include "libcef_dll/cpptoc/request_context_cpptoc.h"
#include "libcef_dll/cpptoc/value_cpptoc.h"
#include "libcef_dll/ctocpp/completion_callback_ctocpp.h"
#include "libcef_dll/ctocpp/request_context_handler_ctocpp.h"
#include "libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h"
......@@ -239,6 +241,115 @@ void CEF_CALLBACK request_context_purge_plugin_list_cache(
reload_pages?true:false);
}
int CEF_CALLBACK request_context_has_preference(
struct _cef_request_context_t* self, const cef_string_t* name) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Verify param: name; type: string_byref_const
DCHECK(name);
if (!name)
return 0;
// Execute
bool _retval = CefRequestContextCppToC::Get(self)->HasPreference(
CefString(name));
// Return type: bool
return _retval;
}
struct _cef_value_t* CEF_CALLBACK request_context_get_preference(
struct _cef_request_context_t* self, const cef_string_t* name) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return NULL;
// Verify param: name; type: string_byref_const
DCHECK(name);
if (!name)
return NULL;
// Execute
CefRefPtr<CefValue> _retval = CefRequestContextCppToC::Get(
self)->GetPreference(
CefString(name));
// Return type: refptr_same
return CefValueCppToC::Wrap(_retval);
}
struct _cef_dictionary_value_t* CEF_CALLBACK request_context_get_all_preferences(
struct _cef_request_context_t* self, int include_defaults) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return NULL;
// Execute
CefRefPtr<CefDictionaryValue> _retval = CefRequestContextCppToC::Get(
self)->GetAllPreferences(
include_defaults?true:false);
// Return type: refptr_same
return CefDictionaryValueCppToC::Wrap(_retval);
}