diff options
author | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-22 18:20:03 +0000 |
---|---|---|
committer | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-22 18:20:03 +0000 |
commit | 489db0845d8070c3882a945fed1108acfb170185 (patch) | |
tree | f0a3a145789a6ba18c8fa4ebb4e786ae9360d1e5 /extensions | |
parent | a78957254f0072472366a75531f7e585cb23ff2c (diff) | |
download | chromium_src-489db0845d8070c3882a945fed1108acfb170185.zip chromium_src-489db0845d8070c3882a945fed1108acfb170185.tar.gz chromium_src-489db0845d8070c3882a945fed1108acfb170185.tar.bz2 |
Move ExtensionPrefs and friends to extensions/ directory.
Move ExtensionPrefs and related classes from
chrome/browser/extensions/ to the top-level extensions/
directory. The following classes are moved:
ExtensionPrefStore
ExtensionPrefValueMap
ExtensionPrefValueMapFactory
ExtensionPrefs
ExtensionPrefsFactory
ExtensionScopedPref
The TestExtensionPrefs helper class and the
ExtensionPrefsTest unit tests remain in
chrome/browser/extensions, since TestExtensionPrefs has a
dependency on Chrome's PrefServiceSyncable that seems like it
will be difficult to break (more explanation at
https://codereview.chromium.org/108643002/).
BUG=313284
TBR=sky@chromium.org,pam@chromium.org
Review URL: https://codereview.chromium.org/46853004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@246373 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/browser/DEPS | 4 | ||||
-rw-r--r-- | extensions/browser/app_sorting.h | 3 | ||||
-rw-r--r-- | extensions/browser/event_router.cc | 2 | ||||
-rw-r--r-- | extensions/browser/extension_pref_store.cc | 43 | ||||
-rw-r--r-- | extensions/browser/extension_pref_store.h | 38 | ||||
-rw-r--r-- | extensions/browser/extension_pref_value_map.cc | 376 | ||||
-rw-r--r-- | extensions/browser/extension_pref_value_map.h | 198 | ||||
-rw-r--r-- | extensions/browser/extension_pref_value_map_factory.cc | 35 | ||||
-rw-r--r-- | extensions/browser/extension_pref_value_map_factory.h | 31 | ||||
-rw-r--r-- | extensions/browser/extension_pref_value_map_unittest.cc | 400 | ||||
-rw-r--r-- | extensions/browser/extension_prefs.cc | 1892 | ||||
-rw-r--r-- | extensions/browser/extension_prefs.h | 638 | ||||
-rw-r--r-- | extensions/browser/extension_prefs_factory.cc | 61 | ||||
-rw-r--r-- | extensions/browser/extension_prefs_factory.h | 39 | ||||
-rw-r--r-- | extensions/browser/extension_scoped_prefs.h | 56 | ||||
-rw-r--r-- | extensions/browser/extensions_browser_client.h | 2 | ||||
-rw-r--r-- | extensions/extensions.gyp | 13 |
17 files changed, 3826 insertions, 5 deletions
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS index a2c9650..65d427b 100644 --- a/extensions/browser/DEPS +++ b/extensions/browser/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/browser_context_keyed_service", + "+components/user_prefs", "+content/public/browser", "+sync", @@ -10,11 +11,12 @@ include_rules = [ # # TODO(jamescook): Remove these. http://crbug.com/162530 "+chrome/browser/chrome_notification_types.h", + "+chrome/browser/extensions/api/content_settings/content_settings_store.h", + "+chrome/browser/extensions/api/preference/preference_api.h", "+chrome/browser/extensions/api/runtime/runtime_api.h", "+chrome/browser/extensions/extension_function_dispatcher.h", "+chrome/browser/extensions/extension_function_histogram_value.h", "+chrome/browser/extensions/extension_host.h", - "+chrome/browser/extensions/extension_prefs.h", "+chrome/browser/extensions/extension_service.h", "+chrome/browser/extensions/extension_system.h", "+chrome/browser/extensions/extension_util.h", diff --git a/extensions/browser/app_sorting.h b/extensions/browser/app_sorting.h index 967fb46..ede93f1 100644 --- a/extensions/browser/app_sorting.h +++ b/extensions/browser/app_sorting.h @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,7 @@ #include <string> #include "base/basictypes.h" +#include "extensions/browser/extension_prefs.h" #include "extensions/common/extension.h" #include "sync/api/string_ordinal.h" diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc index dbef450..763b3ff 100644 --- a/extensions/browser/event_router.cc +++ b/extensions/browser/event_router.cc @@ -13,13 +13,13 @@ #include "base/values.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_host.h" -#include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/common/extensions/extension_messages.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" +#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/lazy_background_task_queue.h" diff --git a/extensions/browser/extension_pref_store.cc b/extensions/browser/extension_pref_store.cc new file mode 100644 index 0000000..3f26cbb3 --- /dev/null +++ b/extensions/browser/extension_pref_store.cc @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium 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 "extensions/browser/extension_pref_store.h" + +#include "base/values.h" +#include "extensions/browser/extension_pref_value_map.h" + +ExtensionPrefStore::ExtensionPrefStore( + ExtensionPrefValueMap* extension_pref_value_map, + bool incognito_pref_store) + : extension_pref_value_map_(extension_pref_value_map), + incognito_pref_store_(incognito_pref_store) { + extension_pref_value_map_->AddObserver(this); +} + +void ExtensionPrefStore::OnInitializationCompleted() { + NotifyInitializationCompleted(); +} + +void ExtensionPrefStore::OnPrefValueChanged(const std::string& key) { + CHECK(extension_pref_value_map_); + const base::Value *winner = + extension_pref_value_map_->GetEffectivePrefValue(key, + incognito_pref_store_, + NULL); + if (winner) + SetValue(key, winner->DeepCopy()); + else + RemoveValue(key); +} + +void ExtensionPrefStore::OnExtensionPrefValueMapDestruction() { + CHECK(extension_pref_value_map_); + extension_pref_value_map_->RemoveObserver(this); + extension_pref_value_map_ = NULL; +} + +ExtensionPrefStore::~ExtensionPrefStore() { + if (extension_pref_value_map_) + extension_pref_value_map_->RemoveObserver(this); +} diff --git a/extensions/browser/extension_pref_store.h b/extensions/browser/extension_pref_store.h new file mode 100644 index 0000000..d93018c --- /dev/null +++ b/extensions/browser/extension_pref_store.h @@ -0,0 +1,38 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_PREF_STORE_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREF_STORE_H_ + +#include <string> + +#include "base/prefs/value_map_pref_store.h" +#include "extensions/browser/extension_pref_value_map.h" + +// A (non-persistent) PrefStore implementation that holds effective preferences +// set by extensions. These preferences are managed by and fetched from an +// ExtensionPrefValueMap. +class ExtensionPrefStore : public ValueMapPrefStore, + public ExtensionPrefValueMap::Observer { + public: + // Constructs an ExtensionPrefStore for a regular or an incognito profile. + ExtensionPrefStore(ExtensionPrefValueMap* extension_pref_value_map, + bool incognito_pref_store); + + // Overrides for ExtensionPrefValueMap::Observer: + virtual void OnInitializationCompleted() OVERRIDE; + virtual void OnPrefValueChanged(const std::string& key) OVERRIDE; + virtual void OnExtensionPrefValueMapDestruction() OVERRIDE; + + protected: + virtual ~ExtensionPrefStore(); + + private: + ExtensionPrefValueMap* extension_pref_value_map_; // Weak pointer. + bool incognito_pref_store_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefStore); +}; + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREF_STORE_H_ diff --git a/extensions/browser/extension_pref_value_map.cc b/extensions/browser/extension_pref_value_map.cc new file mode 100644 index 0000000..5cba7e5 --- /dev/null +++ b/extensions/browser/extension_pref_value_map.cc @@ -0,0 +1,376 @@ +// Copyright 2014 The Chromium 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 "extensions/browser/extension_pref_value_map.h" + +#include "base/prefs/pref_value_map.h" +#include "base/stl_util.h" +#include "base/values.h" + +using extensions::ExtensionPrefsScope; + +struct ExtensionPrefValueMap::ExtensionEntry { + // Installation time of the extension. + base::Time install_time; + // Whether extension is enabled in the profile. + bool enabled; + // Extension controlled preferences for the regular profile. + PrefValueMap regular_profile_preferences; + // Extension controlled preferences that should *only* apply to the regular + // profile. + PrefValueMap regular_only_profile_preferences; + // Persistent extension controlled preferences for the incognito profile, + // empty for regular profile ExtensionPrefStore. + PrefValueMap incognito_profile_preferences_persistent; + // Session only extension controlled preferences for the incognito profile. + // These preferences are deleted when the incognito profile is destroyed. + PrefValueMap incognito_profile_preferences_session_only; +}; + +ExtensionPrefValueMap::ExtensionPrefValueMap() : destroyed_(false) { +} + +ExtensionPrefValueMap::~ExtensionPrefValueMap() { + if (!destroyed_) { + NotifyOfDestruction(); + destroyed_ = true; + } + STLDeleteValues(&entries_); + entries_.clear(); +} + +void ExtensionPrefValueMap::Shutdown() { + NotifyOfDestruction(); + destroyed_ = true; +} + +void ExtensionPrefValueMap::SetExtensionPref(const std::string& ext_id, + const std::string& key, + ExtensionPrefsScope scope, + base::Value* value) { + PrefValueMap* prefs = GetExtensionPrefValueMap(ext_id, scope); + + if (prefs->SetValue(key, value)) + NotifyPrefValueChanged(key); +} + +void ExtensionPrefValueMap::RemoveExtensionPref( + const std::string& ext_id, + const std::string& key, + ExtensionPrefsScope scope) { + PrefValueMap* prefs = GetExtensionPrefValueMap(ext_id, scope); + if (prefs->RemoveValue(key)) + NotifyPrefValueChanged(key); +} + +bool ExtensionPrefValueMap::CanExtensionControlPref( + const std::string& extension_id, + const std::string& pref_key, + bool incognito) const { + ExtensionEntryMap::const_iterator ext = entries_.find(extension_id); + if (ext == entries_.end()) { + NOTREACHED(); + return false; + } + + ExtensionEntryMap::const_iterator winner = + GetEffectivePrefValueController(pref_key, incognito, NULL); + if (winner == entries_.end()) + return true; + + return winner->second->install_time <= ext->second->install_time; +} + +void ExtensionPrefValueMap::ClearAllIncognitoSessionOnlyPreferences() { + typedef std::set<std::string> KeySet; + KeySet deleted_keys; + + ExtensionEntryMap::iterator i; + for (i = entries_.begin(); i != entries_.end(); ++i) { + PrefValueMap& inc_prefs = + i->second->incognito_profile_preferences_session_only; + PrefValueMap::iterator j; + for (j = inc_prefs.begin(); j != inc_prefs.end(); ++j) + deleted_keys.insert(j->first); + inc_prefs.Clear(); + } + + KeySet::iterator k; + for (k = deleted_keys.begin(); k != deleted_keys.end(); ++k) + NotifyPrefValueChanged(*k); +} + +bool ExtensionPrefValueMap::DoesExtensionControlPref( + const std::string& extension_id, + const std::string& pref_key, + bool* from_incognito) const { + bool incognito = (from_incognito != NULL); + ExtensionEntryMap::const_iterator winner = + GetEffectivePrefValueController(pref_key, incognito, from_incognito); + if (winner == entries_.end()) + return false; + return winner->first == extension_id; +} + +void ExtensionPrefValueMap::RegisterExtension(const std::string& ext_id, + const base::Time& install_time, + bool is_enabled) { + if (entries_.find(ext_id) == entries_.end()) { + entries_[ext_id] = new ExtensionEntry; + + // Only update the install time if the extension is newly installed. + entries_[ext_id]->install_time = install_time; + } + + entries_[ext_id]->enabled = is_enabled; +} + +void ExtensionPrefValueMap::UnregisterExtension(const std::string& ext_id) { + ExtensionEntryMap::iterator i = entries_.find(ext_id); + if (i == entries_.end()) + return; + std::set<std::string> keys; // keys set by this extension + GetExtensionControlledKeys(*(i->second), &keys); + + delete i->second; + entries_.erase(i); + + NotifyPrefValueChanged(keys); +} + +void ExtensionPrefValueMap::SetExtensionState(const std::string& ext_id, + bool is_enabled) { + ExtensionEntryMap::const_iterator i = entries_.find(ext_id); + // This may happen when sync sets the extension state for an + // extension that is not installed. + if (i == entries_.end()) + return; + if (i->second->enabled == is_enabled) + return; + std::set<std::string> keys; // keys set by this extension + GetExtensionControlledKeys(*(i->second), &keys); + i->second->enabled = is_enabled; + NotifyPrefValueChanged(keys); +} + +PrefValueMap* ExtensionPrefValueMap::GetExtensionPrefValueMap( + const std::string& ext_id, + ExtensionPrefsScope scope) { + ExtensionEntryMap::const_iterator i = entries_.find(ext_id); + CHECK(i != entries_.end()); + switch (scope) { + case extensions::kExtensionPrefsScopeRegular: + return &(i->second->regular_profile_preferences); + case extensions::kExtensionPrefsScopeRegularOnly: + return &(i->second->regular_only_profile_preferences); + case extensions::kExtensionPrefsScopeIncognitoPersistent: + return &(i->second->incognito_profile_preferences_persistent); + case extensions::kExtensionPrefsScopeIncognitoSessionOnly: + return &(i->second->incognito_profile_preferences_session_only); + } + NOTREACHED(); + return NULL; +} + +const PrefValueMap* ExtensionPrefValueMap::GetExtensionPrefValueMap( + const std::string& ext_id, + ExtensionPrefsScope scope) const { + ExtensionEntryMap::const_iterator i = entries_.find(ext_id); + CHECK(i != entries_.end()); + switch (scope) { + case extensions::kExtensionPrefsScopeRegular: + return &(i->second->regular_profile_preferences); + case extensions::kExtensionPrefsScopeRegularOnly: + return &(i->second->regular_only_profile_preferences); + case extensions::kExtensionPrefsScopeIncognitoPersistent: + return &(i->second->incognito_profile_preferences_persistent); + case extensions::kExtensionPrefsScopeIncognitoSessionOnly: + return &(i->second->incognito_profile_preferences_session_only); + } + NOTREACHED(); + return NULL; +} + +void ExtensionPrefValueMap::GetExtensionControlledKeys( + const ExtensionEntry& entry, + std::set<std::string>* out) const { + PrefValueMap::const_iterator i; + + const PrefValueMap& regular_prefs = entry.regular_profile_preferences; + for (i = regular_prefs.begin(); i != regular_prefs.end(); ++i) + out->insert(i->first); + + const PrefValueMap& regular_only_prefs = + entry.regular_only_profile_preferences; + for (i = regular_only_prefs.begin(); i != regular_only_prefs.end(); ++i) + out->insert(i->first); + + const PrefValueMap& inc_prefs_pers = + entry.incognito_profile_preferences_persistent; + for (i = inc_prefs_pers.begin(); i != inc_prefs_pers.end(); ++i) + out->insert(i->first); + + const PrefValueMap& inc_prefs_session = + entry.incognito_profile_preferences_session_only; + for (i = inc_prefs_session.begin(); i != inc_prefs_session.end(); ++i) + out->insert(i->first); +} + +const base::Value* ExtensionPrefValueMap::GetEffectivePrefValue( + const std::string& key, + bool incognito, + bool* from_incognito) const { + ExtensionEntryMap::const_iterator winner = + GetEffectivePrefValueController(key, incognito, from_incognito); + if (winner == entries_.end()) + return NULL; + + const base::Value* value = NULL; + const std::string& ext_id = winner->first; + + // First search for incognito session only preferences. + if (incognito) { + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeIncognitoSessionOnly); + prefs->GetValue(key, &value); + if (value) + return value; + + // If no incognito session only preference exists, fall back to persistent + // incognito preference. + prefs = GetExtensionPrefValueMap( + ext_id, + extensions::kExtensionPrefsScopeIncognitoPersistent); + prefs->GetValue(key, &value); + if (value) + return value; + } else { + // Regular-only preference. + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeRegularOnly); + prefs->GetValue(key, &value); + if (value) + return value; + } + + // Regular preference. + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeRegular); + prefs->GetValue(key, &value); + return value; +} + +ExtensionPrefValueMap::ExtensionEntryMap::const_iterator +ExtensionPrefValueMap::GetEffectivePrefValueController( + const std::string& key, + bool incognito, + bool* from_incognito) const { + ExtensionEntryMap::const_iterator winner = entries_.end(); + base::Time winners_install_time; + + ExtensionEntryMap::const_iterator i; + for (i = entries_.begin(); i != entries_.end(); ++i) { + const std::string& ext_id = i->first; + const base::Time& install_time = i->second->install_time; + const bool enabled = i->second->enabled; + + if (!enabled) + continue; + if (install_time < winners_install_time) + continue; + + const base::Value* value = NULL; + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeRegular); + if (prefs->GetValue(key, &value)) { + winner = i; + winners_install_time = install_time; + if (from_incognito) + *from_incognito = false; + } + + if (!incognito) { + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeRegularOnly); + if (prefs->GetValue(key, &value)) { + winner = i; + winners_install_time = install_time; + if (from_incognito) + *from_incognito = false; + } + // Ignore the following prefs, because they're incognito-only. + continue; + } + + prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeIncognitoPersistent); + if (prefs->GetValue(key, &value)) { + winner = i; + winners_install_time = install_time; + if (from_incognito) + *from_incognito = true; + } + + prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeIncognitoSessionOnly); + if (prefs->GetValue(key, &value)) { + winner = i; + winners_install_time = install_time; + if (from_incognito) + *from_incognito = true; + } + } + return winner; +} + +void ExtensionPrefValueMap::AddObserver( + ExtensionPrefValueMap::Observer* observer) { + observers_.AddObserver(observer); + + // Collect all currently used keys and notify the new observer. + std::set<std::string> keys; + ExtensionEntryMap::const_iterator i; + for (i = entries_.begin(); i != entries_.end(); ++i) + GetExtensionControlledKeys(*(i->second), &keys); + + std::set<std::string>::const_iterator j; + for (j = keys.begin(); j != keys.end(); ++j) + observer->OnPrefValueChanged(*j); +} + +void ExtensionPrefValueMap::RemoveObserver( + ExtensionPrefValueMap::Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::string ExtensionPrefValueMap::GetExtensionControllingPref( + const std::string& pref_key) const { + ExtensionEntryMap::const_iterator winner = + GetEffectivePrefValueController(pref_key, false, NULL); + if (winner == entries_.end()) + return std::string(); + return winner->first; +} + +void ExtensionPrefValueMap::NotifyInitializationCompleted() { + FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_, + OnInitializationCompleted()); +} + +void ExtensionPrefValueMap::NotifyPrefValueChanged( + const std::set<std::string>& keys) { + std::set<std::string>::const_iterator i; + for (i = keys.begin(); i != keys.end(); ++i) + NotifyPrefValueChanged(*i); +} + +void ExtensionPrefValueMap::NotifyPrefValueChanged(const std::string& key) { + FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_, + OnPrefValueChanged(key)); +} + +void ExtensionPrefValueMap::NotifyOfDestruction() { + FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_, + OnExtensionPrefValueMapDestruction()); +} diff --git a/extensions/browser/extension_pref_value_map.h b/extensions/browser/extension_pref_value_map.h new file mode 100644 index 0000000..48f30d9 --- /dev/null +++ b/extensions/browser/extension_pref_value_map.h @@ -0,0 +1,198 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_H_ + +#include <map> +#include <set> +#include <string> + +#include "base/observer_list.h" +#include "base/prefs/pref_value_map.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" +#include "extensions/browser/extension_prefs_scope.h" + +// Non-persistent data container that is shared by ExtensionPrefStores. All +// extension pref values (incognito and regular) are stored herein and +// provided to ExtensionPrefStores. +// +// The semantics of the ExtensionPrefValueMap are: +// - A regular setting applies to regular browsing sessions as well as incognito +// browsing sessions. +// - An incognito setting applies only to incognito browsing sessions, not to +// regular ones. It takes precedence over a regular setting set by the same +// extension. +// - A regular-only setting applies only to regular browsing sessions, not to +// incognito ones. It takes precedence over a regular setting set by the same +// extension. +// - If two different extensions set a value for the same preference (and both +// values apply to the regular/incognito browsing session), the extension that +// was installed later takes precedence, regardless of whether the settings +// are regular, incognito or regular-only. +// +// The following table illustrates the behavior: +// A.reg | A.reg_only | A.inc | B.reg | B.reg_only | B.inc | E.reg | E.inc +// 1 | - | - | - | - | - | 1 | 1 +// 1 | 2 | - | - | - | - | 2 | 1 +// 1 | - | 3 | - | - | - | 1 | 3 +// 1 | 2 | 3 | - | - | - | 2 | 3 +// 1 | - | - | 4 | - | - | 4 | 4 +// 1 | 2 | 3 | 4 | - | - | 4 | 4 +// 1 | - | - | - | 5 | - | 5 | 1 +// 1 | - | 3 | 4 | 5 | - | 5 | 4 +// 1 | - | - | - | - | 6 | 1 | 6 +// 1 | 2 | - | 4 | - | 6 | 4 | 6 +// 1 | 2 | 3 | - | 5 | 6 | 5 | 6 +// +// A = extension A, B = extension B, E = effective value +// .reg = regular value +// .reg_only = regular-only value +// .inc = incognito value +// Extension B has higher precedence than A. +class ExtensionPrefValueMap : public BrowserContextKeyedService { + public: + // Observer interface for monitoring ExtensionPrefValueMap. + class Observer { + public: + // Called when the value for the given |key| set by one of the extensions + // changes. This does not necessarily mean that the effective value has + // changed. + virtual void OnPrefValueChanged(const std::string& key) = 0; + // Notification about the ExtensionPrefValueMap being fully initialized. + virtual void OnInitializationCompleted() = 0; + // Called when the ExtensionPrefValueMap is being destroyed. When called, + // observers must unsubscribe. + virtual void OnExtensionPrefValueMapDestruction() = 0; + + protected: + virtual ~Observer() {} + }; + + ExtensionPrefValueMap(); + virtual ~ExtensionPrefValueMap(); + + // BrowserContextKeyedService implementation. + virtual void Shutdown() OVERRIDE; + + // Set an extension preference |value| for |key| of extension |ext_id|. + // Takes ownership of |value|. + // Note that regular extension pref values need to be reported to + // incognito and to regular ExtensionPrefStores. + // Precondition: the extension must be registered. + void SetExtensionPref(const std::string& ext_id, + const std::string& key, + extensions::ExtensionPrefsScope scope, + base::Value* value); + + // Remove the extension preference value for |key| of extension |ext_id|. + // Precondition: the extension must be registered. + void RemoveExtensionPref(const std::string& ext_id, + const std::string& key, + extensions::ExtensionPrefsScope scope); + + // Returns true if currently no extension with higher precedence controls the + // preference. + // Note that the this function does does not consider the existence of + // policies. An extension is only really able to control a preference if + // PrefService::Preference::IsExtensionModifiable() returns true as well. + bool CanExtensionControlPref(const std::string& extension_id, + const std::string& pref_key, + bool incognito) const; + + // Removes all "incognito session only" preference values. + void ClearAllIncognitoSessionOnlyPreferences(); + + // Returns true if an extension identified by |extension_id| controls the + // preference. This means this extension has set a preference value and no + // other extension with higher precedence overrides it. If |from_incognito| + // is not NULL, looks at incognito preferences first, and |from_incognito| is + // set to true if the effective pref value is coming from the incognito + // preferences, false if it is coming from the normal ones. + // Note that the this function does does not consider the existence of + // policies. An extension is only really able to control a preference if + // PrefService::Preference::IsExtensionModifiable() returns true as well. + bool DoesExtensionControlPref(const std::string& extension_id, + const std::string& pref_key, + bool* from_incognito) const; + + // Returns the ID of the extension that currently controls this preference. + // Returns an empty string if this preference is not controlled by an + // extension. + std::string GetExtensionControllingPref(const std::string& pref_key) const; + + // Tell the store it's now fully initialized. + void NotifyInitializationCompleted(); + + // Registers the time when an extension |ext_id| is installed. + void RegisterExtension(const std::string& ext_id, + const base::Time& install_time, + bool is_enabled); + + // Deletes all entries related to extension |ext_id|. + void UnregisterExtension(const std::string& ext_id); + + // Hides or makes the extension preference values of the specified extension + // visible. + void SetExtensionState(const std::string& ext_id, bool is_enabled); + + // Adds an observer and notifies it about the currently stored keys. + void AddObserver(Observer* observer); + + void RemoveObserver(Observer* observer); + + const base::Value* GetEffectivePrefValue(const std::string& key, + bool incognito, + bool* from_incognito) const; + + private: + struct ExtensionEntry; + + typedef std::map<std::string, ExtensionEntry*> ExtensionEntryMap; + + const PrefValueMap* GetExtensionPrefValueMap( + const std::string& ext_id, + extensions::ExtensionPrefsScope scope) const; + + PrefValueMap* GetExtensionPrefValueMap( + const std::string& ext_id, + extensions::ExtensionPrefsScope scope); + + // Returns all keys of pref values that are set by the extension of |entry|, + // regardless whether they are set for incognito or regular pref values. + void GetExtensionControlledKeys(const ExtensionEntry& entry, + std::set<std::string>* out) const; + + // Returns an iterator to the extension which controls the preference |key|. + // If |incognito| is true, looks at incognito preferences first. In that case, + // if |from_incognito| is not NULL, it is set to true if the effective pref + // value is coming from the incognito preferences, false if it is coming from + // the normal ones. + ExtensionEntryMap::const_iterator GetEffectivePrefValueController( + const std::string& key, + bool incognito, + bool* from_incognito) const; + + void NotifyOfDestruction(); + void NotifyPrefValueChanged(const std::string& key); + void NotifyPrefValueChanged(const std::set<std::string>& keys); + + // Mapping of which extension set which preference value. The effective + // preferences values (i.e. the ones with the highest precedence) + // are stored in ExtensionPrefStores. + ExtensionEntryMap entries_; + + // In normal Profile shutdown, Shutdown() notifies observers that we are + // being destroyed. In tests, it isn't called, so the notification must + // be done in the destructor. This bit tracks whether it has been done yet. + bool destroyed_; + + ObserverList<Observer, true> observers_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefValueMap); +}; + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_H_ diff --git a/extensions/browser/extension_pref_value_map_factory.cc b/extensions/browser/extension_pref_value_map_factory.cc new file mode 100644 index 0000000..c8d3bfb --- /dev/null +++ b/extensions/browser/extension_pref_value_map_factory.cc @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium 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 "extensions/browser/extension_pref_value_map_factory.h" + +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "extensions/browser/extension_pref_value_map.h" + +ExtensionPrefValueMapFactory::ExtensionPrefValueMapFactory() + : BrowserContextKeyedServiceFactory( + "ExtensionPrefValueMap", + BrowserContextDependencyManager::GetInstance()) { +} + +ExtensionPrefValueMapFactory::~ExtensionPrefValueMapFactory() { +} + +// static +ExtensionPrefValueMap* ExtensionPrefValueMapFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<ExtensionPrefValueMap*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ExtensionPrefValueMapFactory* ExtensionPrefValueMapFactory::GetInstance() { + return Singleton<ExtensionPrefValueMapFactory>::get(); +} + +BrowserContextKeyedService* +ExtensionPrefValueMapFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new ExtensionPrefValueMap(); +} diff --git a/extensions/browser/extension_pref_value_map_factory.h b/extensions/browser/extension_pref_value_map_factory.h new file mode 100644 index 0000000..84537d3 --- /dev/null +++ b/extensions/browser/extension_pref_value_map_factory.h @@ -0,0 +1,31 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_FACTORY_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +class ExtensionPrefValueMap; + +// The usual factory boilerplate for ExtensionPrefValueMap. +class ExtensionPrefValueMapFactory : public BrowserContextKeyedServiceFactory { + public: + static ExtensionPrefValueMap* GetForBrowserContext( + content::BrowserContext* context); + + static ExtensionPrefValueMapFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<ExtensionPrefValueMapFactory>; + + ExtensionPrefValueMapFactory(); + virtual ~ExtensionPrefValueMapFactory(); + + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE; +}; + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_FACTORY_H_ diff --git a/extensions/browser/extension_pref_value_map_unittest.cc b/extensions/browser/extension_pref_value_map_unittest.cc new file mode 100644 index 0000000..35fa3c6 --- /dev/null +++ b/extensions/browser/extension_pref_value_map_unittest.cc @@ -0,0 +1,400 @@ +// Copyright 2014 The Chromium 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 "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/pref_store_observer_mock.h" +#include "base/values.h" +#include "extensions/browser/extension_pref_value_map.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const char kExt1[] = "ext1"; +const char kExt2[] = "ext2"; +const char kExt3[] = "ext3"; + +const char kPref1[] = "path1.subpath"; +const char kPref2[] = "path2"; +const char kPref3[] = "path3"; +const char kPref4[] = "path4"; +} // namespace + +static base::Value* CreateVal(const char* str) { + return new base::StringValue(str); +} + +static base::Time CreateTime(int64 t) { + return base::Time::FromInternalValue(t); +} + +template <typename BASECLASS> +class ExtensionPrefValueMapTestBase : public BASECLASS { + public: + static const extensions::ExtensionPrefsScope kRegular = + extensions::kExtensionPrefsScopeRegular; + static const extensions::ExtensionPrefsScope kRegularOnly = + extensions::kExtensionPrefsScopeRegularOnly; + static const extensions::ExtensionPrefsScope kIncognitoPersistent = + extensions::kExtensionPrefsScopeIncognitoPersistent; + static const extensions::ExtensionPrefsScope kIncognitoSessionOnly = + extensions::kExtensionPrefsScopeIncognitoSessionOnly; + + // Returns an empty string if the key is not set. + std::string GetValue(const char * key, bool incognito) const { + const base::Value *value = + epvm_.GetEffectivePrefValue(key, incognito, NULL); + std::string string_value; + if (value) + value->GetAsString(&string_value); + return string_value; + } + + protected: + ExtensionPrefValueMap epvm_; +}; + +class ExtensionPrefValueMapTest + : public ExtensionPrefValueMapTestBase<testing::Test> { +}; + +// A gmock-ified implementation of PrefStore::Observer. +class ExtensionPrefValueMapObserverMock + : public ExtensionPrefValueMap::Observer { + public: + ExtensionPrefValueMapObserverMock() {} + virtual ~ExtensionPrefValueMapObserverMock() {} + + MOCK_METHOD1(OnPrefValueChanged, void(const std::string&)); + MOCK_METHOD0(OnInitializationCompleted, void()); + MOCK_METHOD0(OnExtensionPrefValueMapDestruction, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefValueMapObserverMock); +}; + +TEST_F(ExtensionPrefValueMapTest, SetAndGetPrefValue) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + EXPECT_EQ("val1", GetValue(kPref1, false)); +}; + +TEST_F(ExtensionPrefValueMapTest, GetNotSetPrefValue) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + EXPECT_EQ("", GetValue(kPref1, false)); +}; + +// Make sure the last-installed extension wins for each preference. +TEST_F(ExtensionPrefValueMapTest, Override) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + epvm_.RegisterExtension(kExt3, CreateTime(30), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val2")); + epvm_.SetExtensionPref(kExt3, kPref1, kRegular, CreateVal("val3")); + + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val4")); + epvm_.SetExtensionPref(kExt2, kPref2, kRegular, CreateVal("val5")); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val6")); + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val7")); + epvm_.SetExtensionPref(kExt1, kPref3, kRegular, CreateVal("val8")); + + EXPECT_EQ("val3", GetValue(kPref1, false)); + EXPECT_EQ("val5", GetValue(kPref2, false)); + EXPECT_EQ("val8", GetValue(kPref3, false)); +} + +TEST_F(ExtensionPrefValueMapTest, OverrideChecks) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + epvm_.RegisterExtension(kExt3, CreateTime(30), true); + + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, NULL)); + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt2, kPref1, NULL)); + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, NULL)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt1, kPref1, false)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt2, kPref1, false)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt3, kPref1, false)); + + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val1")); + + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, NULL)); + EXPECT_TRUE(epvm_.DoesExtensionControlPref(kExt2, kPref1, NULL)); + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, NULL)); + EXPECT_FALSE(epvm_.CanExtensionControlPref(kExt1, kPref1, false)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt2, kPref1, false)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt3, kPref1, false)); +} + +TEST_F(ExtensionPrefValueMapTest, SetAndGetPrefValueIncognito) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + EXPECT_EQ("val1", GetValue(kPref1, true)); +} + +TEST_F(ExtensionPrefValueMapTest, UninstallOnlyExtension) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.UnregisterExtension(kExt1); + + EXPECT_EQ("", GetValue(kPref1, false)); +} + +// Tests uninstalling an extension that wasn't winning for any preferences. +TEST_F(ExtensionPrefValueMapTest, UninstallIrrelevantExtension) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(10), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val2")); + + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val3")); + epvm_.SetExtensionPref(kExt2, kPref2, kRegular, CreateVal("val4")); + + epvm_.UnregisterExtension(kExt1); + + EXPECT_EQ("val2", GetValue(kPref1, false)); + EXPECT_EQ("val4", GetValue(kPref2, false)); +} + +// Tests uninstalling an extension that was winning for all preferences. +TEST_F(ExtensionPrefValueMapTest, UninstallExtensionFromTop) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + epvm_.RegisterExtension(kExt3, CreateTime(30), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val2")); + epvm_.SetExtensionPref(kExt3, kPref1, kRegular, CreateVal("val3")); + + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val4")); + epvm_.SetExtensionPref(kExt3, kPref2, kRegular, CreateVal("val5")); + + epvm_.UnregisterExtension(kExt3); + + EXPECT_EQ("val2", GetValue(kPref1, false)); + EXPECT_EQ("val4", GetValue(kPref2, false)); +} + +// Tests uninstalling an extension that was winning for only some preferences. +TEST_F(ExtensionPrefValueMapTest, UninstallExtensionFromMiddle) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + epvm_.RegisterExtension(kExt3, CreateTime(30), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val2")); + epvm_.SetExtensionPref(kExt3, kPref1, kRegular, CreateVal("val3")); + + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val4")); + epvm_.SetExtensionPref(kExt2, kPref2, kRegular, CreateVal("val5")); + + epvm_.SetExtensionPref(kExt1, kPref3, kRegular, CreateVal("val6")); + + epvm_.SetExtensionPref(kExt2, kPref4, kRegular, CreateVal("val7")); + + epvm_.UnregisterExtension(kExt2); + + EXPECT_EQ("val3", GetValue(kPref1, false)); + EXPECT_EQ("val4", GetValue(kPref2, false)); + EXPECT_EQ("val6", GetValue(kPref3, false)); + EXPECT_EQ("", GetValue(kPref4, false)); +} + +// Tests triggering of notifications to registered observers. +TEST_F(ExtensionPrefValueMapTest, NotifyWhenNeeded) { + using testing::Mock; + using testing::StrEq; + + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + + ExtensionPrefValueMapObserverMock observer; + epvm_.AddObserver(&observer); + + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + Mock::VerifyAndClearExpectations(&observer); + + // Write the same value again. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))).Times(0); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + Mock::VerifyAndClearExpectations(&observer); + + // Override incognito value. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val2")); + Mock::VerifyAndClearExpectations(&observer); + + // Override non-incognito value. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val3")); + Mock::VerifyAndClearExpectations(&observer); + + // Disable. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionState(kExt1, false); + Mock::VerifyAndClearExpectations(&observer); + + // Enable. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionState(kExt1, true); + Mock::VerifyAndClearExpectations(&observer); + + // Uninstall + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.UnregisterExtension(kExt1); + Mock::VerifyAndClearExpectations(&observer); + + epvm_.RemoveObserver(&observer); + + // Write new value --> no notification after removing observer. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))).Times(0); + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val4")); + Mock::VerifyAndClearExpectations(&observer); +} + +// Tests disabling an extension. +TEST_F(ExtensionPrefValueMapTest, DisableExt) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionState(kExt1, false); + EXPECT_EQ("", GetValue(kPref1, false)); +} + +// Tests disabling and reenabling an extension. +TEST_F(ExtensionPrefValueMapTest, ReenableExt) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionState(kExt1, false); + epvm_.SetExtensionState(kExt1, true); + EXPECT_EQ("val1", GetValue(kPref1, false)); +} + +struct OverrideIncognitoTestCase { + OverrideIncognitoTestCase(int val_ext1_regular, + int val_ext1_regular_only, + int val_ext1_incognito_pers, + int val_ext1_incognito_sess, + int val_ext2_regular, + int val_ext2_regular_only, + int val_ext2_incognito_pers, + int val_ext2_incognito_sess, + int effective_value_regular, + int effective_value_incognito) + : val_ext1_regular_(val_ext1_regular), + val_ext1_regular_only_(val_ext1_regular_only), + val_ext1_incognito_pers_(val_ext1_incognito_pers), + val_ext1_incognito_sess_(val_ext1_incognito_sess), + val_ext2_regular_(val_ext2_regular), + val_ext2_regular_only_(val_ext2_regular_only), + val_ext2_incognito_pers_(val_ext2_incognito_pers), + val_ext2_incognito_sess_(val_ext2_incognito_sess), + effective_value_regular_(effective_value_regular), + effective_value_incognito_(effective_value_incognito) {} + + // pers. = persistent + // sess. = session only + int val_ext1_regular_; // pref value of extension 1 + int val_ext1_regular_only_; // pref value of extension 1 regular-only. + int val_ext1_incognito_pers_; // pref value of extension 1 incognito pers. + int val_ext1_incognito_sess_; // pref value of extension 1 incognito sess. + int val_ext2_regular_; // pref value of extension 2 + int val_ext2_regular_only_; // pref value of extension 2 regular-only. + int val_ext2_incognito_pers_; // pref value of extension 2 incognito pers. + int val_ext2_incognito_sess_; // pref value of extension 2 incognito sess. + int effective_value_regular_; // desired winner regular + int effective_value_incognito_; // desired winner incognito +}; + +class ExtensionPrefValueMapTestIncognitoTests + : public ExtensionPrefValueMapTestBase< + testing::TestWithParam<OverrideIncognitoTestCase> > { +}; + +TEST_P(ExtensionPrefValueMapTestIncognitoTests, OverrideIncognito) { + OverrideIncognitoTestCase test = GetParam(); + const char* strings[] = { + "undefined", + "val1", + "val2", + "val3", + "val4", + "val5", + "val6", + "val7", + "val8", + }; + + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + if (test.val_ext1_regular_) { + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, + CreateVal(strings[test.val_ext1_regular_])); + } + if (test.val_ext1_regular_only_) { + epvm_.SetExtensionPref(kExt1, kPref1, kRegularOnly, + CreateVal(strings[test.val_ext1_regular_only_])); + } + if (test.val_ext1_incognito_pers_) { + epvm_.SetExtensionPref(kExt1, kPref1, kIncognitoPersistent, + CreateVal(strings[test.val_ext1_incognito_pers_])); + } + if (test.val_ext1_incognito_sess_) { + epvm_.SetExtensionPref(kExt1, kPref1, kIncognitoSessionOnly, + CreateVal(strings[test.val_ext1_incognito_sess_])); + } + if (test.val_ext2_regular_) { + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, + CreateVal(strings[test.val_ext2_regular_])); + } + if (test.val_ext2_regular_only_) { + epvm_.SetExtensionPref(kExt2, kPref1, kRegularOnly, + CreateVal(strings[test.val_ext2_regular_only_])); + } + if (test.val_ext2_incognito_pers_) { + epvm_.SetExtensionPref(kExt2, kPref1, kIncognitoPersistent, + CreateVal(strings[test.val_ext2_incognito_pers_])); + } + if (test.val_ext2_incognito_sess_) { + epvm_.SetExtensionPref(kExt2, kPref1, kIncognitoSessionOnly, + CreateVal(strings[test.val_ext2_incognito_sess_])); + } + std::string actual; + EXPECT_EQ(strings[test.effective_value_regular_], GetValue(kPref1, false)); + EXPECT_EQ(strings[test.effective_value_incognito_], GetValue(kPref1, true)); + epvm_.UnregisterExtension(kExt1); + epvm_.UnregisterExtension(kExt2); +} + +INSTANTIATE_TEST_CASE_P( + ExtensionPrefValueMapTestIncognitoTestsInstance, + ExtensionPrefValueMapTestIncognitoTests, + testing::Values( + // e.g. (1, 0, 0, 0, 0, 0, 7, 0, 1, 7), means: + // ext1 regular is set to "val1", ext2 incognito persistent is set to + // "val7" + // --> the winning regular value is "val1", the winning incognito + // value is "val7". + OverrideIncognitoTestCase(1, 0, 0, 0, 0, 0, 0, 0, 1, 1), + OverrideIncognitoTestCase(1, 2, 0, 0, 0, 0, 0, 0, 2, 1), + OverrideIncognitoTestCase(1, 0, 3, 0, 0, 0, 0, 0, 1, 3), + OverrideIncognitoTestCase(1, 0, 0, 4, 0, 0, 0, 0, 1, 4), + OverrideIncognitoTestCase(1, 0, 3, 4, 0, 0, 0, 0, 1, 4), + OverrideIncognitoTestCase(1, 2, 3, 0, 0, 0, 0, 0, 2, 3), + OverrideIncognitoTestCase(1, 0, 0, 0, 5, 0, 0, 0, 5, 5), + OverrideIncognitoTestCase(1, 2, 3, 0, 5, 0, 0, 0, 5, 5), + OverrideIncognitoTestCase(1, 0, 0, 0, 0, 6, 0, 0, 6, 1), + OverrideIncognitoTestCase(1, 0, 3, 0, 5, 6, 0, 0, 6, 5), + OverrideIncognitoTestCase(1, 0, 0, 4, 5, 6, 0, 0, 6, 5), + OverrideIncognitoTestCase(1, 0, 0, 0, 0, 0, 7, 0, 1, 7), + OverrideIncognitoTestCase(1, 2, 0, 0, 5, 0, 7, 0, 5, 7), + OverrideIncognitoTestCase(1, 2, 0, 0, 5, 0, 0, 8, 5, 8), + OverrideIncognitoTestCase(1, 2, 0, 0, 5, 0, 7, 8, 5, 8), + OverrideIncognitoTestCase(1, 2, 3, 0, 0, 6, 7, 0, 6, 7))); diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc new file mode 100644 index 0000000..8e56ed7 --- /dev/null +++ b/extensions/browser/extension_prefs.cc @@ -0,0 +1,1892 @@ +// Copyright 2014 The Chromium 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 "extensions/browser/extension_prefs.h" + +#include <iterator> + +#include "base/command_line.h" +#include "base/prefs/pref_notifier.h" +#include "base/prefs/pref_service.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/value_conversions.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_store.h" +#include "chrome/browser/extensions/api/preference/preference_api.h" +#include "components/user_prefs/pref_registry_syncable.h" +#include "extensions/browser/admin_policy.h" +#include "extensions/browser/app_sorting.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_pref_store.h" +#include "extensions/browser/extension_prefs_factory.h" +#include "extensions/browser/pref_names.h" +#include "extensions/common/feature_switch.h" +#include "extensions/common/manifest.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/common/url_pattern.h" +#include "extensions/common/user_script.h" +#include "ui/base/l10n/l10n_util.h" + +using base::Value; +using base::DictionaryValue; +using base::ListValue; + +namespace extensions { + +namespace { + +// Additional preferences keys, which are not needed by external clients. + +// True if this extension is running. Note this preference stops getting updated +// during Chrome shutdown (and won't be updated on a browser crash) and so can +// be used at startup to determine whether the extension was running when Chrome +// was last terminated. +const char kPrefRunning[] = "running"; + +// Whether this extension had windows when it was last running. +const char kIsActive[] = "is_active"; + +// Where an extension was installed from. (see Manifest::Location) +const char kPrefLocation[] = "location"; + +// Enabled, disabled, killed, etc. (see Extension::State) +const char kPrefState[] = "state"; + +// The path to the current version's manifest file. +const char kPrefPath[] = "path"; + +// The dictionary containing the extension's manifest. +const char kPrefManifest[] = "manifest"; + +// The version number. +const char kPrefVersion[] = "manifest.version"; + +// Indicates whether an extension is blacklisted. +const char kPrefBlacklist[] = "blacklist"; + +// The count of how many times we prompted the user to acknowledge an +// extension. +const char kPrefAcknowledgePromptCount[] = "ack_prompt_count"; + +// Indicates whether the user has acknowledged various types of extensions. +const char kPrefExternalAcknowledged[] = "ack_external"; +const char kPrefBlacklistAcknowledged[] = "ack_blacklist"; +const char kPrefWipeoutAcknowledged[] = "ack_wiped"; + +// Indicates whether the external extension was installed during the first +// run of this profile. +const char kPrefExternalInstallFirstRun[] = "external_first_run"; + +// Indicates whether to show an install warning when the user enables. +const char kExtensionDidEscalatePermissions[] = "install_warning_on_enable"; + +// DO NOT USE, use kPrefDisableReasons instead. +// Indicates whether the extension was updated while it was disabled. +const char kDeprecatedPrefDisableReason[] = "disable_reason"; + +// A bitmask of all the reasons an extension is disabled. +const char kPrefDisableReasons[] = "disable_reasons"; + +// The key for a serialized Time value indicating the start of the day (from the +// server's perspective) an extension last included a "ping" parameter during +// its update check. +const char kLastPingDay[] = "lastpingday"; + +// Similar to kLastPingDay, but for "active" instead of "rollcall" pings. +const char kLastActivePingDay[] = "last_active_pingday"; + +// A bit we use to keep track of whether we need to do an "active" ping. +const char kActiveBit[] = "active_bit"; + +// Path for settings specific to blacklist update. +const char kExtensionsBlacklistUpdate[] = "extensions.blacklistupdate"; + +// Path for the delayed install info dictionary preference. The actual string +// value is a legacy artifact for when delayed installs only pertained to +// updates that were waiting for idle. +const char kDelayedInstallInfo[] = "idle_install_info"; + +// Reason why the extension's install was delayed. +const char kDelayedInstallReason[] = "delay_install_reason"; + +// Path for the suggested page ordinal of a delayed extension install. +const char kPrefSuggestedPageOrdinal[] = "suggested_page_ordinal"; + +// A preference that, if true, will allow this extension to run in incognito +// mode. +const char kPrefIncognitoEnabled[] = "incognito"; + +// A preference to control whether an extension is allowed to inject script in +// pages with file URLs. +const char kPrefAllowFileAccess[] = "newAllowFileAccess"; +// TODO(jstritar): As part of fixing http://crbug.com/91577, we revoked all +// extension file access by renaming the pref. We should eventually clean up +// the old flag and possibly go back to that name. +// const char kPrefAllowFileAccessOld[] = "allowFileAccess"; + +// A preference specifying if the user dragged the app on the NTP. +const char kPrefUserDraggedApp[] = "user_dragged_app_ntp"; + +// Preferences that hold which permissions the user has granted the extension. +// We explicitly keep track of these so that extensions can contain unknown +// permissions, for backwards compatibility reasons, and we can still prompt +// the user to accept them once recognized. We store the active permission +// permissions because they may differ from those defined in the manifest. +const char kPrefActivePermissions[] = "active_permissions"; +const char kPrefGrantedPermissions[] = "granted_permissions"; + +// The preference names for PermissionSet values. +const char kPrefAPIs[] = "api"; +const char kPrefManifestPermissions[] = "manifest_permissions"; +const char kPrefExplicitHosts[] = "explicit_host"; +const char kPrefScriptableHosts[] = "scriptable_host"; + +// The preference names for the old granted permissions scheme. +const char kPrefOldGrantedFullAccess[] = "granted_permissions.full"; +const char kPrefOldGrantedHosts[] = "granted_permissions.host"; +const char kPrefOldGrantedAPIs[] = "granted_permissions.api"; + +// A preference that indicates when an extension was installed. +const char kPrefInstallTime[] = "install_time"; + +// A preference which saves the creation flags for extensions. +const char kPrefCreationFlags[] = "creation_flags"; + +// A preference that indicates whether the extension was installed from the +// Chrome Web Store. +const char kPrefFromWebStore[] = "from_webstore"; + +// A preference that indicates whether the extension was installed from a +// mock App created from a bookmark. +const char kPrefFromBookmark[] = "from_bookmark"; + +// A preference that indicates whether the extension was installed as +// default apps. +const char kPrefWasInstalledByDefault[] = "was_installed_by_default"; + +// Key for Geometry Cache preference. +const char kPrefGeometryCache[] = "geometry_cache"; + +// A preference that indicates when an extension is last launched. +const char kPrefLastLaunchTime[] = "last_launch_time"; + +// A list of installed ids and a signature. +const char kInstallSignature[] = "extensions.install_signature"; + +// Provider of write access to a dictionary storing extension prefs. +class ScopedExtensionPrefUpdate : public DictionaryPrefUpdate { + public: + ScopedExtensionPrefUpdate(PrefService* service, + const std::string& extension_id) : + DictionaryPrefUpdate(service, pref_names::kExtensions), + extension_id_(extension_id) {} + + virtual ~ScopedExtensionPrefUpdate() { + } + + // DictionaryPrefUpdate overrides: + virtual base::DictionaryValue* Get() OVERRIDE { + base::DictionaryValue* dict = DictionaryPrefUpdate::Get(); + base::DictionaryValue* extension = NULL; + if (!dict->GetDictionary(extension_id_, &extension)) { + // Extension pref does not exist, create it. + extension = new base::DictionaryValue(); + dict->SetWithoutPathExpansion(extension_id_, extension); + } + return extension; + } + + private: + const std::string extension_id_; + + DISALLOW_COPY_AND_ASSIGN(ScopedExtensionPrefUpdate); +}; + +std::string JoinPrefs(const std::string& parent, const char* child) { + return parent + "." + child; +} + +// Checks if kPrefBlacklist is set to true in the base::DictionaryValue. +// Return false if the value is false or kPrefBlacklist does not exist. +// This is used to decide if an extension is blacklisted. +bool IsBlacklistBitSet(const base::DictionaryValue* ext) { + bool bool_value; + return ext->GetBoolean(kPrefBlacklist, &bool_value) && bool_value; +} + +} // namespace + +// +// TimeProvider +// + +ExtensionPrefs::TimeProvider::TimeProvider() { +} + +ExtensionPrefs::TimeProvider::~TimeProvider() { +} + +base::Time ExtensionPrefs::TimeProvider::GetCurrentTime() const { + return base::Time::Now(); +} + +// +// ScopedUpdate +// +template <typename T, base::Value::Type type_enum_value> +ExtensionPrefs::ScopedUpdate<T, type_enum_value>::ScopedUpdate( + ExtensionPrefs* prefs, + const std::string& extension_id, + const std::string& key) + : update_(prefs->pref_service(), pref_names::kExtensions), + extension_id_(extension_id), + key_(key) { + DCHECK(Extension::IdIsValid(extension_id_)); +} + +template <typename T, base::Value::Type type_enum_value> +ExtensionPrefs::ScopedUpdate<T, type_enum_value>::~ScopedUpdate() { +} + +template <typename T, base::Value::Type type_enum_value> +T* ExtensionPrefs::ScopedUpdate<T, type_enum_value>::Get() { + base::DictionaryValue* dict = update_.Get(); + base::DictionaryValue* extension = NULL; + base::Value* key_value = NULL; + if (!dict->GetDictionary(extension_id_, &extension) || + !extension->Get(key_, &key_value)) { + return NULL; + } + return key_value->GetType() == type_enum_value ? + static_cast<T*>(key_value) : + NULL; +} + +template <typename T, base::Value::Type type_enum_value> +T* ExtensionPrefs::ScopedUpdate<T, type_enum_value>::Create() { + base::DictionaryValue* dict = update_.Get(); + base::DictionaryValue* extension = NULL; + base::Value* key_value = NULL; + T* value_as_t = NULL; + if (!dict->GetDictionary(extension_id_, &extension)) { + extension = new base::DictionaryValue; + dict->SetWithoutPathExpansion(extension_id_, extension); + } + if (!extension->Get(key_, &key_value)) { + value_as_t = new T; + extension->SetWithoutPathExpansion(key_, value_as_t); + } else { + CHECK(key_value->GetType() == type_enum_value); + value_as_t = static_cast<T*>(key_value); + } + return value_as_t; +} + +// Explicit instantiations for Dictionary and List value types. +template class ExtensionPrefs::ScopedUpdate<base::DictionaryValue, + base::Value::TYPE_DICTIONARY>; +template class ExtensionPrefs::ScopedUpdate<base::ListValue, + base::Value::TYPE_LIST>; + +// +// ExtensionPrefs +// + +// static +ExtensionPrefs* ExtensionPrefs::Create( + PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + bool extensions_disabled) { + return ExtensionPrefs::Create(prefs, + root_dir, + extension_pref_value_map, + app_sorting.Pass(), + extensions_disabled, + make_scoped_ptr(new TimeProvider())); +} + +// static +ExtensionPrefs* ExtensionPrefs::Create( + PrefService* pref_service, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + bool extensions_disabled, + scoped_ptr<TimeProvider> time_provider) { + return new ExtensionPrefs(pref_service, + root_dir, + extension_pref_value_map, + app_sorting.Pass(), + time_provider.Pass(), + extensions_disabled); +} + +ExtensionPrefs::~ExtensionPrefs() { +} + +// static +ExtensionPrefs* ExtensionPrefs::Get(content::BrowserContext* context) { + return ExtensionPrefsFactory::GetInstance()->GetForBrowserContext(context); +} + +static base::FilePath::StringType MakePathRelative(const base::FilePath& parent, + const base::FilePath& child) { + if (!parent.IsParent(child)) + return child.value(); + + base::FilePath::StringType retval = child.value().substr( + parent.value().length()); + if (base::FilePath::IsSeparator(retval[0])) + return retval.substr(1); + else + return retval; +} + +void ExtensionPrefs::MakePathsRelative() { + const base::DictionaryValue* dict = + prefs_->GetDictionary(pref_names::kExtensions); + if (!dict || dict->empty()) + return; + + // Collect all extensions ids with absolute paths in |absolute_keys|. + std::set<std::string> absolute_keys; + for (base::DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) { + const base::DictionaryValue* extension_dict = NULL; + if (!i.value().GetAsDictionary(&extension_dict)) + continue; + int location_value; + if (extension_dict->GetInteger(kPrefLocation, &location_value) && + Manifest::IsUnpackedLocation( + static_cast<Manifest::Location>(location_value))) { + // Unpacked extensions can have absolute paths. + continue; + } + base::FilePath::StringType path_string; + if (!extension_dict->GetString(kPrefPath, &path_string)) + continue; + base::FilePath path(path_string); + if (path.IsAbsolute()) + absolute_keys.insert(i.key()); + } + if (absolute_keys.empty()) + return; + + // Fix these paths. + DictionaryPrefUpdate update(prefs_, pref_names::kExtensions); + base::DictionaryValue* update_dict = update.Get(); + for (std::set<std::string>::iterator i = absolute_keys.begin(); + i != absolute_keys.end(); ++i) { + base::DictionaryValue* extension_dict = NULL; + if (!update_dict->GetDictionaryWithoutPathExpansion(*i, &extension_dict)) { + NOTREACHED() << "Control should never reach here for extension " << *i; + continue; + } + base::FilePath::StringType path_string; + extension_dict->GetString(kPrefPath, &path_string); + base::FilePath path(path_string); + extension_dict->SetString(kPrefPath, + MakePathRelative(install_directory_, path)); + } +} + +const base::DictionaryValue* ExtensionPrefs::GetExtensionPref( + const std::string& extension_id) const { + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + const base::DictionaryValue* extension_dict = NULL; + if (!extensions || + !extensions->GetDictionary(extension_id, &extension_dict)) { + return NULL; + } + return extension_dict; +} + +void ExtensionPrefs::UpdateExtensionPref(const std::string& extension_id, + const std::string& key, + base::Value* data_value) { + if (!Extension::IdIsValid(extension_id)) { + NOTREACHED() << "Invalid extension_id " << extension_id; + return; + } + ScopedExtensionPrefUpdate update(prefs_, extension_id); + if (data_value) + update->Set(key, data_value); + else + update->Remove(key, NULL); +} + +void ExtensionPrefs::DeleteExtensionPrefs(const std::string& extension_id) { + extension_pref_value_map_->UnregisterExtension(extension_id); + content_settings_store_->UnregisterExtension(extension_id); + DictionaryPrefUpdate update(prefs_, pref_names::kExtensions); + base::DictionaryValue* dict = update.Get(); + dict->Remove(extension_id, NULL); +} + +bool ExtensionPrefs::ReadPrefAsBoolean(const std::string& extension_id, + const std::string& pref_key, + bool* out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + if (!ext || !ext->GetBoolean(pref_key, out_value)) + return false; + + return true; +} + +bool ExtensionPrefs::ReadPrefAsInteger(const std::string& extension_id, + const std::string& pref_key, + int* out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + if (!ext || !ext->GetInteger(pref_key, out_value)) + return false; + + return true; +} + +bool ExtensionPrefs::ReadPrefAsString(const std::string& extension_id, + const std::string& pref_key, + std::string* out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + if (!ext || !ext->GetString(pref_key, out_value)) + return false; + + return true; +} + +bool ExtensionPrefs::ReadPrefAsList(const std::string& extension_id, + const std::string& pref_key, + const base::ListValue** out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + const base::ListValue* out = NULL; + if (!ext || !ext->GetList(pref_key, &out)) + return false; + if (out_value) + *out_value = out; + + return true; +} + +bool ExtensionPrefs::ReadPrefAsDictionary( + const std::string& extension_id, + const std::string& pref_key, + const base::DictionaryValue** out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + const base::DictionaryValue* out = NULL; + if (!ext || !ext->GetDictionary(pref_key, &out)) + return false; + if (out_value) + *out_value = out; + + return true; +} + +bool ExtensionPrefs::HasPrefForExtension( + const std::string& extension_id) const { + return GetExtensionPref(extension_id) != NULL; +} + +bool ExtensionPrefs::ReadPrefAsURLPatternSet(const std::string& extension_id, + const std::string& pref_key, + URLPatternSet* result, + int valid_schemes) { + const base::ListValue* value = NULL; + if (!ReadPrefAsList(extension_id, pref_key, &value)) + return false; + + bool allow_file_access = AllowFileAccess(extension_id); + return result->Populate(*value, valid_schemes, allow_file_access, NULL); +} + +void ExtensionPrefs::SetExtensionPrefURLPatternSet( + const std::string& extension_id, + const std::string& pref_key, + const URLPatternSet& new_value) { + UpdateExtensionPref(extension_id, pref_key, new_value.ToValue().release()); +} + +bool ExtensionPrefs::ReadPrefAsBooleanAndReturn( + const std::string& extension_id, + const std::string& pref_key) const { + bool out_value = false; + return ReadPrefAsBoolean(extension_id, pref_key, &out_value) && out_value; +} + +PermissionSet* ExtensionPrefs::ReadPrefAsPermissionSet( + const std::string& extension_id, + const std::string& pref_key) { + if (!GetExtensionPref(extension_id)) + return NULL; + + // Retrieve the API permissions. Please refer SetExtensionPrefPermissionSet() + // for api_values format. + APIPermissionSet apis; + const base::ListValue* api_values = NULL; + std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); + if (ReadPrefAsList(extension_id, api_pref, &api_values)) { + APIPermissionSet::ParseFromJSON(api_values, + APIPermissionSet::kAllowInternalPermissions, + &apis, NULL, NULL); + } + + // Retrieve the Manifest Keys permissions. Please refer to + // |SetExtensionPrefPermissionSet| for manifest_permissions_values format. + ManifestPermissionSet manifest_permissions; + const base::ListValue* manifest_permissions_values = NULL; + std::string manifest_permission_pref = + JoinPrefs(pref_key, kPrefManifestPermissions); + if (ReadPrefAsList(extension_id, manifest_permission_pref, + &manifest_permissions_values)) { + ManifestPermissionSet::ParseFromJSON( + manifest_permissions_values, &manifest_permissions, NULL, NULL); + } + + // Retrieve the explicit host permissions. + URLPatternSet explicit_hosts; + ReadPrefAsURLPatternSet( + extension_id, JoinPrefs(pref_key, kPrefExplicitHosts), + &explicit_hosts, Extension::kValidHostPermissionSchemes); + + // Retrieve the scriptable host permissions. + URLPatternSet scriptable_hosts; + ReadPrefAsURLPatternSet( + extension_id, JoinPrefs(pref_key, kPrefScriptableHosts), + &scriptable_hosts, UserScript::ValidUserScriptSchemes()); + + return new PermissionSet( + apis, manifest_permissions, explicit_hosts, scriptable_hosts); +} + +// Set the API or Manifest permissions. +// The format of api_values is: +// [ "permission_name1", // permissions do not support detail. +// "permission_name2", +// {"permission_name3": value }, +// // permission supports detail, permission detail will be stored in value. +// ... +// ] +template<typename T> +static base::ListValue* CreatePermissionList(const T& permissions) { + base::ListValue* values = new base::ListValue(); + for (typename T::const_iterator i = permissions.begin(); + i != permissions.end(); ++i) { + scoped_ptr<base::Value> detail(i->ToValue()); + if (detail) { + base::DictionaryValue* tmp = new base::DictionaryValue(); + tmp->Set(i->name(), detail.release()); + values->Append(tmp); + } else { + values->Append(new base::StringValue(i->name())); + } + } + return values; +} + +void ExtensionPrefs::SetExtensionPrefPermissionSet( + const std::string& extension_id, + const std::string& pref_key, + const PermissionSet* new_value) { + std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); + base::ListValue* api_values = CreatePermissionList(new_value->apis()); + UpdateExtensionPref(extension_id, api_pref, api_values); + + std::string manifest_permissions_pref = + JoinPrefs(pref_key, kPrefManifestPermissions); + base::ListValue* manifest_permissions_values = CreatePermissionList( + new_value->manifest_permissions()); + UpdateExtensionPref(extension_id, + manifest_permissions_pref, + manifest_permissions_values); + + // Set the explicit host permissions. + if (!new_value->explicit_hosts().is_empty()) { + SetExtensionPrefURLPatternSet(extension_id, + JoinPrefs(pref_key, kPrefExplicitHosts), + new_value->explicit_hosts()); + } + + // Set the scriptable host permissions. + if (!new_value->scriptable_hosts().is_empty()) { + SetExtensionPrefURLPatternSet(extension_id, + JoinPrefs(pref_key, kPrefScriptableHosts), + new_value->scriptable_hosts()); + } +} + +int ExtensionPrefs::IncrementAcknowledgePromptCount( + const std::string& extension_id) { + int count = 0; + ReadPrefAsInteger(extension_id, kPrefAcknowledgePromptCount, &count); + ++count; + UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, + new base::FundamentalValue(count)); + return count; +} + +bool ExtensionPrefs::IsExternalExtensionAcknowledged( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefExternalAcknowledged); +} + +void ExtensionPrefs::AcknowledgeExternalExtension( + const std::string& extension_id) { + DCHECK(Extension::IdIsValid(extension_id)); + UpdateExtensionPref(extension_id, kPrefExternalAcknowledged, + new base::FundamentalValue(true)); + UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, NULL); +} + +bool ExtensionPrefs::IsBlacklistedExtensionAcknowledged( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefBlacklistAcknowledged); +} + +void ExtensionPrefs::AcknowledgeBlacklistedExtension( + const std::string& extension_id) { + DCHECK(Extension::IdIsValid(extension_id)); + UpdateExtensionPref(extension_id, kPrefBlacklistAcknowledged, + new base::FundamentalValue(true)); + UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, NULL); +} + +bool ExtensionPrefs::IsExternalInstallFirstRun( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefExternalInstallFirstRun); +} + +void ExtensionPrefs::SetExternalInstallFirstRun( + const std::string& extension_id) { + DCHECK(Extension::IdIsValid(extension_id)); + UpdateExtensionPref(extension_id, kPrefExternalInstallFirstRun, + new base::FundamentalValue(true)); +} + +bool ExtensionPrefs::HasWipeoutBeenAcknowledged( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefWipeoutAcknowledged); +} + +void ExtensionPrefs::SetWipeoutAcknowledged( + const std::string& extension_id, + bool value) { + UpdateExtensionPref(extension_id, kPrefWipeoutAcknowledged, + value ? base::Value::CreateBooleanValue(value) : NULL); +} + +bool ExtensionPrefs::SetAlertSystemFirstRun() { + if (prefs_->GetBoolean(pref_names::kAlertsInitialized)) { + return true; + } + prefs_->SetBoolean(pref_names::kAlertsInitialized, true); + return false; +} + +bool ExtensionPrefs::ExtensionsBlacklistedByDefault() const { + return admin_policy::BlacklistedByDefault( + prefs_->GetList(pref_names::kInstallDenyList)); +} + +bool ExtensionPrefs::DidExtensionEscalatePermissions( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, + kExtensionDidEscalatePermissions); +} + +void ExtensionPrefs::SetDidExtensionEscalatePermissions( + const Extension* extension, bool did_escalate) { + UpdateExtensionPref(extension->id(), kExtensionDidEscalatePermissions, + new base::FundamentalValue(did_escalate)); +} + +int ExtensionPrefs::GetDisableReasons(const std::string& extension_id) const { + int value = -1; + if (ReadPrefAsInteger(extension_id, kPrefDisableReasons, &value) && + value >= 0) { + return value; + } + return Extension::DISABLE_NONE; +} + +void ExtensionPrefs::AddDisableReason(const std::string& extension_id, + Extension::DisableReason disable_reason) { + int new_value = GetDisableReasons(extension_id) | + static_cast<int>(disable_reason); + UpdateExtensionPref(extension_id, kPrefDisableReasons, + new base::FundamentalValue(new_value)); +} + +void ExtensionPrefs::RemoveDisableReason( + const std::string& extension_id, + Extension::DisableReason disable_reason) { + int new_value = GetDisableReasons(extension_id) & + ~static_cast<int>(disable_reason); + if (new_value == Extension::DISABLE_NONE) { + UpdateExtensionPref(extension_id, kPrefDisableReasons, NULL); + } else { + UpdateExtensionPref(extension_id, kPrefDisableReasons, + new base::FundamentalValue(new_value)); + } +} + +void ExtensionPrefs::ClearDisableReasons(const std::string& extension_id) { + UpdateExtensionPref(extension_id, kPrefDisableReasons, NULL); +} + +std::set<std::string> ExtensionPrefs::GetBlacklistedExtensions() { + std::set<std::string> ids; + + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + if (!extensions) + return ids; + + for (base::DictionaryValue::Iterator it(*extensions); + !it.IsAtEnd(); it.Advance()) { + if (!it.value().IsType(base::Value::TYPE_DICTIONARY)) { + NOTREACHED() << "Invalid pref for extension " << it.key(); + continue; + } + if (IsBlacklistBitSet( + static_cast<const base::DictionaryValue*>(&it.value()))) { + ids.insert(it.key()); + } + } + + return ids; +} + +void ExtensionPrefs::SetExtensionBlacklisted(const std::string& extension_id, + bool is_blacklisted) { + bool currently_blacklisted = IsExtensionBlacklisted(extension_id); + if (is_blacklisted == currently_blacklisted) { + NOTREACHED() << extension_id << " is " << + (currently_blacklisted ? "already" : "not") << + " blacklisted"; + return; + } + + // Always make sure the "acknowledged" bit is cleared since the blacklist bit + // is changing. + UpdateExtensionPref(extension_id, kPrefBlacklistAcknowledged, NULL); + + if (is_blacklisted) { + UpdateExtensionPref(extension_id, + kPrefBlacklist, + new base::FundamentalValue(true)); + } else { + UpdateExtensionPref(extension_id, kPrefBlacklist, NULL); + const base::DictionaryValue* dict = GetExtensionPref(extension_id); + if (dict && dict->empty()) + DeleteExtensionPrefs(extension_id); + } +} + +bool ExtensionPrefs::IsExtensionBlacklisted(const std::string& id) const { + const base::DictionaryValue* ext_prefs = GetExtensionPref(id); + return ext_prefs && IsBlacklistBitSet(ext_prefs); +} + +namespace { + +// Serializes |time| as a string value mapped to |key| in |dictionary|. +void SaveTime(base::DictionaryValue* dictionary, + const char* key, + const base::Time& time) { + if (!dictionary) + return; + std::string string_value = base::Int64ToString(time.ToInternalValue()); + dictionary->SetString(key, string_value); +} + +// The opposite of SaveTime. If |key| is not found, this returns an empty Time +// (is_null() will return true). +base::Time ReadTime(const base::DictionaryValue* dictionary, const char* key) { + if (!dictionary) + return base::Time(); + std::string string_value; + int64 value; + if (dictionary->GetString(key, &string_value)) { + if (base::StringToInt64(string_value, &value)) { + return base::Time::FromInternalValue(value); + } + } + return base::Time(); +} + +} // namespace + +base::Time ExtensionPrefs::LastPingDay(const std::string& extension_id) const { + DCHECK(Extension::IdIsValid(extension_id)); + return ReadTime(GetExtensionPref(extension_id), kLastPingDay); +} + +void ExtensionPrefs::SetLastPingDay(const std::string& extension_id, + const base::Time& time) { + DCHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + SaveTime(update.Get(), kLastPingDay, time); +} + +base::Time ExtensionPrefs::BlacklistLastPingDay() const { + return ReadTime(prefs_->GetDictionary(kExtensionsBlacklistUpdate), + kLastPingDay); +} + +void ExtensionPrefs::SetBlacklistLastPingDay(const base::Time& time) { + DictionaryPrefUpdate update(prefs_, kExtensionsBlacklistUpdate); + SaveTime(update.Get(), kLastPingDay, time); +} + +base::Time ExtensionPrefs::LastActivePingDay(const std::string& extension_id) { + DCHECK(Extension::IdIsValid(extension_id)); + return ReadTime(GetExtensionPref(extension_id), kLastActivePingDay); +} + +void ExtensionPrefs::SetLastActivePingDay(const std::string& extension_id, + const base::Time& time) { + DCHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + SaveTime(update.Get(), kLastActivePingDay, time); +} + +bool ExtensionPrefs::GetActiveBit(const std::string& extension_id) { + const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); + bool result = false; + if (dictionary && dictionary->GetBoolean(kActiveBit, &result)) + return result; + return false; +} + +void ExtensionPrefs::SetActiveBit(const std::string& extension_id, + bool active) { + UpdateExtensionPref(extension_id, kActiveBit, + new base::FundamentalValue(active)); +} + +void ExtensionPrefs::MigratePermissions(const ExtensionIdList& extension_ids) { + PermissionsInfo* info = PermissionsInfo::GetInstance(); + for (ExtensionIdList::const_iterator ext_id = + extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) { + + // An extension's granted permissions need to be migrated if the + // full_access bit is present. This bit was always present in the previous + // scheme and is never present now. + bool full_access; + const base::DictionaryValue* ext = GetExtensionPref(*ext_id); + if (!ext || !ext->GetBoolean(kPrefOldGrantedFullAccess, &full_access)) + continue; + + // Remove the full access bit (empty list will get trimmed). + UpdateExtensionPref( + *ext_id, kPrefOldGrantedFullAccess, new base::ListValue()); + + // Add the plugin permission if the full access bit was set. + if (full_access) { + const base::ListValue* apis = NULL; + base::ListValue* new_apis = NULL; + + std::string granted_apis = + JoinPrefs(kPrefGrantedPermissions, kPrefAPIs); + if (ext->GetList(kPrefOldGrantedAPIs, &apis)) + new_apis = apis->DeepCopy(); + else + new_apis = new base::ListValue(); + + std::string plugin_name = info->GetByID( + APIPermission::kPlugin)->name(); + new_apis->Append(new base::StringValue(plugin_name)); + UpdateExtensionPref(*ext_id, granted_apis, new_apis); + } + + // The granted permissions originally only held the effective hosts, + // which are a combination of host and user script host permissions. + // We now maintain these lists separately. For migration purposes, it + // does not matter how we treat the old effective hosts as long as the + // new effective hosts will be the same, so we move them to explicit + // host permissions. + const base::ListValue* hosts = NULL; + std::string explicit_hosts = + JoinPrefs(kPrefGrantedPermissions, kPrefExplicitHosts); + if (ext->GetList(kPrefOldGrantedHosts, &hosts)) { + UpdateExtensionPref( + *ext_id, explicit_hosts, hosts->DeepCopy()); + + // We can get rid of the old one by setting it to an empty list. + UpdateExtensionPref(*ext_id, kPrefOldGrantedHosts, new base::ListValue()); + } + } +} + +void ExtensionPrefs::MigrateDisableReasons( + const ExtensionIdList& extension_ids) { + for (ExtensionIdList::const_iterator ext_id = + extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) { + int value = -1; + if (ReadPrefAsInteger(*ext_id, kDeprecatedPrefDisableReason, &value)) { + int new_value = Extension::DISABLE_NONE; + switch (value) { + case Extension::DEPRECATED_DISABLE_USER_ACTION: + new_value = Extension::DISABLE_USER_ACTION; + break; + case Extension::DEPRECATED_DISABLE_PERMISSIONS_INCREASE: + new_value = Extension::DISABLE_PERMISSIONS_INCREASE; + break; + case Extension::DEPRECATED_DISABLE_RELOAD: + new_value = Extension::DISABLE_RELOAD; + break; + } + + UpdateExtensionPref(*ext_id, kPrefDisableReasons, + new base::FundamentalValue(new_value)); + // Remove the old disable reason. + UpdateExtensionPref(*ext_id, kDeprecatedPrefDisableReason, NULL); + } + } +} + +PermissionSet* ExtensionPrefs::GetGrantedPermissions( + const std::string& extension_id) { + CHECK(Extension::IdIsValid(extension_id)); + return ReadPrefAsPermissionSet(extension_id, kPrefGrantedPermissions); +} + +void ExtensionPrefs::AddGrantedPermissions( + const std::string& extension_id, + const PermissionSet* permissions) { + CHECK(Extension::IdIsValid(extension_id)); + + scoped_refptr<PermissionSet> granted_permissions( + GetGrantedPermissions(extension_id)); + + // The new granted permissions are the union of the already granted + // permissions and the newly granted permissions. + scoped_refptr<PermissionSet> new_perms( + PermissionSet::CreateUnion( + permissions, granted_permissions.get())); + + SetExtensionPrefPermissionSet( + extension_id, kPrefGrantedPermissions, new_perms.get()); +} + +void ExtensionPrefs::RemoveGrantedPermissions( + const std::string& extension_id, + const PermissionSet* permissions) { + CHECK(Extension::IdIsValid(extension_id)); + + scoped_refptr<PermissionSet> granted_permissions( + GetGrantedPermissions(extension_id)); + + // The new granted permissions are the difference of the already granted + // permissions and the newly ungranted permissions. + scoped_refptr<PermissionSet> new_perms( + PermissionSet::CreateDifference( + granted_permissions.get(), permissions)); + + SetExtensionPrefPermissionSet( + extension_id, kPrefGrantedPermissions, new_perms.get()); +} + +PermissionSet* ExtensionPrefs::GetActivePermissions( + const std::string& extension_id) { + CHECK(Extension::IdIsValid(extension_id)); + return ReadPrefAsPermissionSet(extension_id, kPrefActivePermissions); +} + +void ExtensionPrefs::SetActivePermissions( + const std::string& extension_id, + const PermissionSet* permissions) { + SetExtensionPrefPermissionSet( + extension_id, kPrefActivePermissions, permissions); +} + +void ExtensionPrefs::SetExtensionRunning(const std::string& extension_id, + bool is_running) { + base::Value* value = new base::FundamentalValue(is_running); + UpdateExtensionPref(extension_id, kPrefRunning, value); +} + +bool ExtensionPrefs::IsExtensionRunning(const std::string& extension_id) { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return false; + bool running = false; + extension->GetBoolean(kPrefRunning, &running); + return running; +} + +void ExtensionPrefs::SetIsActive(const std::string& extension_id, + bool is_active) { + base::Value* value = new base::FundamentalValue(is_active); + UpdateExtensionPref(extension_id, kIsActive, value); +} + +bool ExtensionPrefs::IsActive(const std::string& extension_id) { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return false; + bool is_active = false; + extension->GetBoolean(kIsActive, &is_active); + return is_active; +} + +bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) const { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefIncognitoEnabled); +} + +void ExtensionPrefs::SetIsIncognitoEnabled(const std::string& extension_id, + bool enabled) { + UpdateExtensionPref(extension_id, kPrefIncognitoEnabled, + new base::FundamentalValue(enabled)); +} + +bool ExtensionPrefs::AllowFileAccess(const std::string& extension_id) const { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefAllowFileAccess); +} + +void ExtensionPrefs::SetAllowFileAccess(const std::string& extension_id, + bool allow) { + UpdateExtensionPref(extension_id, kPrefAllowFileAccess, + new base::FundamentalValue(allow)); +} + +bool ExtensionPrefs::HasAllowFileAccessSetting( + const std::string& extension_id) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + return ext && ext->HasKey(kPrefAllowFileAccess); +} + +bool ExtensionPrefs::DoesExtensionHaveState( + const std::string& id, Extension::State check_state) const { + const base::DictionaryValue* extension = GetExtensionPref(id); + int state = -1; + if (!extension || !extension->GetInteger(kPrefState, &state)) + return false; + + if (state < 0 || state >= Extension::NUM_STATES) { + LOG(ERROR) << "Bad pref 'state' for extension '" << id << "'"; + return false; + } + + return state == check_state; +} + +bool ExtensionPrefs::IsExternalExtensionUninstalled( + const std::string& id) const { + return DoesExtensionHaveState(id, Extension::EXTERNAL_EXTENSION_UNINSTALLED); +} + +bool ExtensionPrefs::IsExtensionDisabled( + const std::string& id) const { + return DoesExtensionHaveState(id, Extension::DISABLED); +} + +ExtensionIdList ExtensionPrefs::GetToolbarOrder() { + ExtensionIdList id_list_out; + GetUserExtensionPrefIntoContainer(pref_names::kToolbar, &id_list_out); + return id_list_out; +} + +void ExtensionPrefs::SetToolbarOrder(const ExtensionIdList& extension_ids) { + SetExtensionPrefFromContainer(pref_names::kToolbar, extension_ids); +} + +bool ExtensionPrefs::GetKnownDisabled(ExtensionIdSet* id_set_out) { + return GetUserExtensionPrefIntoContainer(pref_names::kKnownDisabled, + id_set_out); +} + +void ExtensionPrefs::SetKnownDisabled(const ExtensionIdSet& extension_ids) { + SetExtensionPrefFromContainer(pref_names::kKnownDisabled, extension_ids); +} + +void ExtensionPrefs::OnExtensionInstalled( + const Extension* extension, + Extension::State initial_state, + bool blacklisted_for_malware, + const syncer::StringOrdinal& page_ordinal) { + ScopedExtensionPrefUpdate update(prefs_, extension->id()); + base::DictionaryValue* extension_dict = update.Get(); + const base::Time install_time = time_provider_->GetCurrentTime(); + PopulateExtensionInfoPrefs(extension, install_time, initial_state, + blacklisted_for_malware, extension_dict); + FinishExtensionInfoPrefs(extension->id(), install_time, + extension->RequiresSortOrdinal(), + page_ordinal, extension_dict); +} + +void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id, + const Manifest::Location& location, + bool external_uninstall) { + app_sorting_->ClearOrdinals(extension_id); + + // For external extensions, we save a preference reminding ourself not to try + // and install the extension anymore (except when |external_uninstall| is + // true, which signifies that the registry key was deleted or the pref file + // no longer lists the extension). + if (!external_uninstall && Manifest::IsExternalLocation(location)) { + UpdateExtensionPref(extension_id, kPrefState, + new base::FundamentalValue( + Extension::EXTERNAL_EXTENSION_UNINSTALLED)); + extension_pref_value_map_->SetExtensionState(extension_id, false); + content_settings_store_->SetExtensionState(extension_id, false); + } else { + DeleteExtensionPrefs(extension_id); + } +} + +void ExtensionPrefs::SetExtensionState(const std::string& extension_id, + Extension::State state) { + UpdateExtensionPref(extension_id, kPrefState, + new base::FundamentalValue(state)); + bool enabled = (state == Extension::ENABLED); + extension_pref_value_map_->SetExtensionState(extension_id, enabled); + content_settings_store_->SetExtensionState(extension_id, enabled); +} + +std::string ExtensionPrefs::GetVersionString(const std::string& extension_id) { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return std::string(); + + std::string version; + extension->GetString(kPrefVersion, &version); + + return version; +} + +void ExtensionPrefs::UpdateManifest(const Extension* extension) { + if (!Manifest::IsUnpackedLocation(extension->location())) { + const base::DictionaryValue* extension_dict = + GetExtensionPref(extension->id()); + if (!extension_dict) + return; + const base::DictionaryValue* old_manifest = NULL; + bool update_required = + !extension_dict->GetDictionary(kPrefManifest, &old_manifest) || + !extension->manifest()->value()->Equals(old_manifest); + if (update_required) { + UpdateExtensionPref(extension->id(), kPrefManifest, + extension->manifest()->value()->DeepCopy()); + } + } +} + +base::FilePath ExtensionPrefs::GetExtensionPath( + const std::string& extension_id) { + const base::DictionaryValue* dict = GetExtensionPref(extension_id); + if (!dict) + return base::FilePath(); + + std::string path; + if (!dict->GetString(kPrefPath, &path)) + return base::FilePath(); + + return install_directory_.Append(base::FilePath::FromUTF8Unsafe(path)); +} + +scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledInfoHelper( + const std::string& extension_id, + const base::DictionaryValue* extension) const { + int location_value; + if (!extension->GetInteger(kPrefLocation, &location_value)) + return scoped_ptr<ExtensionInfo>(); + + base::FilePath::StringType path; + if (!extension->GetString(kPrefPath, &path)) + return scoped_ptr<ExtensionInfo>(); + + // Make path absolute. Unpacked extensions will already have absolute paths, + // otherwise make it so. + Manifest::Location location = static_cast<Manifest::Location>(location_value); + if (!Manifest::IsUnpackedLocation(location)) { + DCHECK(location == Manifest::COMPONENT || + !base::FilePath(path).IsAbsolute()); + path = install_directory_.Append(path).value(); + } + + // Only the following extension types have data saved in the preferences. + if (location != Manifest::INTERNAL && + !Manifest::IsUnpackedLocation(location) && + !Manifest::IsExternalLocation(location)) { + NOTREACHED(); + return scoped_ptr<ExtensionInfo>(); + } + + const base::DictionaryValue* manifest = NULL; + if (!Manifest::IsUnpackedLocation(location) && + !extension->GetDictionary(kPrefManifest, &manifest)) { + LOG(WARNING) << "Missing manifest for extension " << extension_id; + // Just a warning for now. + } + + return scoped_ptr<ExtensionInfo>(new ExtensionInfo( + manifest, extension_id, base::FilePath(path), location)); +} + +scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledExtensionInfo( + const std::string& extension_id) const { + const base::DictionaryValue* ext = NULL; + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + if (!extensions || + !extensions->GetDictionaryWithoutPathExpansion(extension_id, &ext)) + return scoped_ptr<ExtensionInfo>(); + int state_value; + if (!ext->GetInteger(kPrefState, &state_value) || + state_value == Extension::ENABLED_COMPONENT) { + // Old preferences files may not have kPrefState for component extensions. + return scoped_ptr<ExtensionInfo>(); + } + + if (state_value == Extension::EXTERNAL_EXTENSION_UNINSTALLED) { + LOG(WARNING) << "External extension with id " << extension_id + << " has been uninstalled by the user"; + return scoped_ptr<ExtensionInfo>(); + } + + return GetInstalledInfoHelper(extension_id, ext); +} + +scoped_ptr<ExtensionPrefs::ExtensionsInfo> +ExtensionPrefs::GetInstalledExtensionsInfo() const { + scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); + + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + for (base::DictionaryValue::Iterator extension_id(*extensions); + !extension_id.IsAtEnd(); extension_id.Advance()) { + if (!Extension::IdIsValid(extension_id.key())) + continue; + + scoped_ptr<ExtensionInfo> info = + GetInstalledExtensionInfo(extension_id.key()); + if (info) + extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); + } + + return extensions_info.Pass(); +} + +scoped_ptr<ExtensionPrefs::ExtensionsInfo> +ExtensionPrefs::GetUninstalledExtensionsInfo() const { + scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); + + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + for (base::DictionaryValue::Iterator extension_id(*extensions); + !extension_id.IsAtEnd(); extension_id.Advance()) { + const base::DictionaryValue* ext = NULL; + if (!Extension::IdIsValid(extension_id.key()) || + !IsExternalExtensionUninstalled(extension_id.key()) || + !extension_id.value().GetAsDictionary(&ext)) + continue; + + scoped_ptr<ExtensionInfo> info = + GetInstalledInfoHelper(extension_id.key(), ext); + if (info) + extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); + } + + return extensions_info.Pass(); +} + +void ExtensionPrefs::SetDelayedInstallInfo( + const Extension* extension, + Extension::State initial_state, + bool blacklisted_for_malware, + DelayReason delay_reason, + const syncer::StringOrdinal& page_ordinal) { + base::DictionaryValue* extension_dict = new base::DictionaryValue(); + PopulateExtensionInfoPrefs(extension, time_provider_->GetCurrentTime(), + initial_state, blacklisted_for_malware, + extension_dict); + + // Add transient data that is needed by FinishDelayedInstallInfo(), but + // should not be in the final extension prefs. All entries here should have + // a corresponding Remove() call in FinishDelayedInstallInfo(). + if (extension->RequiresSortOrdinal()) { + extension_dict->SetString( + kPrefSuggestedPageOrdinal, + page_ordinal.IsValid() ? page_ordinal.ToInternalValue() + : std::string()); + } + extension_dict->SetInteger(kDelayedInstallReason, + static_cast<int>(delay_reason)); + + UpdateExtensionPref(extension->id(), kDelayedInstallInfo, extension_dict); +} + +bool ExtensionPrefs::RemoveDelayedInstallInfo( + const std::string& extension_id) { + if (!GetExtensionPref(extension_id)) + return false; + ScopedExtensionPrefUpdate update(prefs_, extension_id); + bool result = update->Remove(kDelayedInstallInfo, NULL); + return result; +} + +bool ExtensionPrefs::FinishDelayedInstallInfo( + const std::string& extension_id) { + CHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + base::DictionaryValue* extension_dict = update.Get(); + base::DictionaryValue* pending_install_dict = NULL; + if (!extension_dict->GetDictionary(kDelayedInstallInfo, + &pending_install_dict)) { + return false; + } + + // Retrieve and clear transient values populated by SetDelayedInstallInfo(). + // Also do any other data cleanup that makes sense. + std::string serialized_ordinal; + syncer::StringOrdinal suggested_page_ordinal; + bool needs_sort_ordinal = false; + if (pending_install_dict->GetString(kPrefSuggestedPageOrdinal, + &serialized_ordinal)) { + suggested_page_ordinal = syncer::StringOrdinal(serialized_ordinal); + needs_sort_ordinal = true; + pending_install_dict->Remove(kPrefSuggestedPageOrdinal, NULL); + } + pending_install_dict->Remove(kDelayedInstallReason, NULL); + + const base::Time install_time = time_provider_->GetCurrentTime(); + pending_install_dict->Set( + kPrefInstallTime, + new base::StringValue( + base::Int64ToString(install_time.ToInternalValue()))); + + // Commit the delayed install data. + for (base::DictionaryValue::Iterator it(*pending_install_dict); !it.IsAtEnd(); + it.Advance()) { + extension_dict->Set(it.key(), it.value().DeepCopy()); + } + FinishExtensionInfoPrefs(extension_id, install_time, needs_sort_ordinal, + suggested_page_ordinal, extension_dict); + return true; +} + +scoped_ptr<ExtensionInfo> ExtensionPrefs::GetDelayedInstallInfo( + const std::string& extension_id) const { + const base::DictionaryValue* extension_prefs = + GetExtensionPref(extension_id); + if (!extension_prefs) + return scoped_ptr<ExtensionInfo>(); + + const base::DictionaryValue* ext = NULL; + if (!extension_prefs->GetDictionary(kDelayedInstallInfo, &ext)) + return scoped_ptr<ExtensionInfo>(); + + return GetInstalledInfoHelper(extension_id, ext); +} + +ExtensionPrefs::DelayReason ExtensionPrefs::GetDelayedInstallReason( + const std::string& extension_id) const { + const base::DictionaryValue* extension_prefs = + GetExtensionPref(extension_id); + if (!extension_prefs) + return DELAY_REASON_NONE; + + const base::DictionaryValue* ext = NULL; + if (!extension_prefs->GetDictionary(kDelayedInstallInfo, &ext)) + return DELAY_REASON_NONE; + + int delay_reason; + if (!ext->GetInteger(kDelayedInstallReason, &delay_reason)) + return DELAY_REASON_NONE; + + return static_cast<DelayReason>(delay_reason); +} + +scoped_ptr<ExtensionPrefs::ExtensionsInfo> ExtensionPrefs:: + GetAllDelayedInstallInfo() const { + scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); + + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + for (base::DictionaryValue::Iterator extension_id(*extensions); + !extension_id.IsAtEnd(); extension_id.Advance()) { + if (!Extension::IdIsValid(extension_id.key())) + continue; + + scoped_ptr<ExtensionInfo> info = GetDelayedInstallInfo(extension_id.key()); + if (info) + extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); + } + + return extensions_info.Pass(); +} + +bool ExtensionPrefs::WasAppDraggedByUser(const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefUserDraggedApp); +} + +void ExtensionPrefs::SetAppDraggedByUser(const std::string& extension_id) { + UpdateExtensionPref(extension_id, kPrefUserDraggedApp, + new base::FundamentalValue(true)); +} + +bool ExtensionPrefs::IsFromWebStore( + const std::string& extension_id) const { + const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); + bool result = false; + if (dictionary && dictionary->GetBoolean(kPrefFromWebStore, &result)) + return result; + return false; +} + +bool ExtensionPrefs::IsFromBookmark( + const std::string& extension_id) const { + const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); + bool result = false; + if (dictionary && dictionary->GetBoolean(kPrefFromBookmark, &result)) + return result; + return false; +} + +int ExtensionPrefs::GetCreationFlags(const std::string& extension_id) const { + int creation_flags = Extension::NO_FLAGS; + if (!ReadPrefAsInteger(extension_id, kPrefCreationFlags, &creation_flags)) { + // Since kPrefCreationFlags was added later, it will be missing for + // previously installed extensions. + if (IsFromBookmark(extension_id)) + creation_flags |= Extension::FROM_BOOKMARK; + if (IsFromWebStore(extension_id)) + creation_flags |= Extension::FROM_WEBSTORE; + if (WasInstalledByDefault(extension_id)) + creation_flags |= Extension::WAS_INSTALLED_BY_DEFAULT; + } + return creation_flags; +} + +int ExtensionPrefs::GetDelayedInstallCreationFlags( + const std::string& extension_id) const { + int creation_flags = Extension::NO_FLAGS; + const base::DictionaryValue* delayed_info = NULL; + if (ReadPrefAsDictionary(extension_id, kDelayedInstallInfo, &delayed_info)) { + delayed_info->GetInteger(kPrefCreationFlags, &creation_flags); + } + return creation_flags; +} + +bool ExtensionPrefs::WasInstalledByDefault( + const std::string& extension_id) const { + const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); + bool result = false; + if (dictionary && + dictionary->GetBoolean(kPrefWasInstalledByDefault, &result)) + return result; + return false; +} + +base::Time ExtensionPrefs::GetInstallTime( + const std::string& extension_id) const { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) { + NOTREACHED(); + return base::Time(); + } + std::string install_time_str; + if (!extension->GetString(kPrefInstallTime, &install_time_str)) + return base::Time(); + int64 install_time_i64 = 0; + if (!base::StringToInt64(install_time_str, &install_time_i64)) + return base::Time(); + return base::Time::FromInternalValue(install_time_i64); +} + +base::Time ExtensionPrefs::GetLastLaunchTime( + const std::string& extension_id) const { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return base::Time(); + + std::string launch_time_str; + if (!extension->GetString(kPrefLastLaunchTime, &launch_time_str)) + return base::Time(); + int64 launch_time_i64 = 0; + if (!base::StringToInt64(launch_time_str, &launch_time_i64)) + return base::Time(); + return base::Time::FromInternalValue(launch_time_i64); +} + +void ExtensionPrefs::SetLastLaunchTime(const std::string& extension_id, + const base::Time& time) { + DCHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + SaveTime(update.Get(), kPrefLastLaunchTime, time); +} + +void ExtensionPrefs::GetExtensions(ExtensionIdList* out) { + CHECK(out); + + scoped_ptr<ExtensionsInfo> extensions_info(GetInstalledExtensionsInfo()); + + for (size_t i = 0; i < extensions_info->size(); ++i) { + ExtensionInfo* info = extensions_info->at(i).get(); + out->push_back(info->extension_id); + } +} + +// static +ExtensionIdList ExtensionPrefs::GetExtensionsFrom( + const PrefService* pref_service) { + ExtensionIdList result; + + const base::DictionaryValue* extension_prefs = NULL; + const base::Value* extension_prefs_value = + pref_service->GetUserPrefValue(pref_names::kExtensions); + if (!extension_prefs_value || + !extension_prefs_value->GetAsDictionary(&extension_prefs)) { + return result; // Empty set + } + + for (base::DictionaryValue::Iterator it(*extension_prefs); !it.IsAtEnd(); + it.Advance()) { + const base::DictionaryValue* ext = NULL; + if (!it.value().GetAsDictionary(&ext)) { + NOTREACHED() << "Invalid pref for extension " << it.key(); + continue; + } + if (!IsBlacklistBitSet(ext)) + result.push_back(it.key()); + } + return result; +} + +void ExtensionPrefs::FixMissingPrefs(const ExtensionIdList& extension_ids) { + // Fix old entries that did not get an installation time entry when they + // were installed or don't have a preferences field. + for (ExtensionIdList::const_iterator ext_id = extension_ids.begin(); + ext_id != extension_ids.end(); ++ext_id) { + if (GetInstallTime(*ext_id) == base::Time()) { + VLOG(1) << "Could not parse installation time of extension " + << *ext_id << ". It was probably installed before setting " + << kPrefInstallTime << " was introduced. Updating " + << kPrefInstallTime << " to the current time."; + const base::Time install_time = time_provider_->GetCurrentTime(); + UpdateExtensionPref(*ext_id, + kPrefInstallTime, + new base::StringValue(base::Int64ToString( + install_time.ToInternalValue()))); + } + } +} + +void ExtensionPrefs::InitPrefStore() { + if (extensions_disabled_) { + extension_pref_value_map_->NotifyInitializationCompleted(); + return; + } + + // When this is called, the PrefService is initialized and provides access + // to the user preferences stored in a JSON file. + ExtensionIdList extension_ids; + GetExtensions(&extension_ids); + // Create empty preferences dictionary for each extension (these dictionaries + // are pruned when persisting the preferences to disk). + for (ExtensionIdList::iterator ext_id = extension_ids.begin(); + ext_id != extension_ids.end(); ++ext_id) { + ScopedExtensionPrefUpdate update(prefs_, *ext_id); + // This creates an empty dictionary if none is stored. + update.Get(); + } + + FixMissingPrefs(extension_ids); + MigratePermissions(extension_ids); + MigrateDisableReasons(extension_ids); + app_sorting_->Initialize(extension_ids); + + PreferenceAPI::InitExtensionControlledPrefs(this, extension_pref_value_map_); + + extension_pref_value_map_->NotifyInitializationCompleted(); +} + +bool ExtensionPrefs::HasIncognitoPrefValue(const std::string& pref_key) { + bool has_incognito_pref_value = false; + extension_pref_value_map_->GetEffectivePrefValue(pref_key, + true, + &has_incognito_pref_value); + return has_incognito_pref_value; +} + +URLPatternSet ExtensionPrefs::GetAllowedInstallSites() { + URLPatternSet result; + const base::ListValue* list = + prefs_->GetList(pref_names::kAllowedInstallSites); + CHECK(list); + + for (size_t i = 0; i < list->GetSize(); ++i) { + std::string entry_string; + URLPattern entry(URLPattern::SCHEME_ALL); + if (!list->GetString(i, &entry_string) || + entry.Parse(entry_string) != URLPattern::PARSE_SUCCESS) { + LOG(ERROR) << "Invalid value for preference: " + << pref_names::kAllowedInstallSites << "." << i; + continue; + } + result.AddPattern(entry); + } + + return result; +} + +const base::DictionaryValue* ExtensionPrefs::GetGeometryCache( + const std::string& extension_id) const { + const base::DictionaryValue* extension_prefs = GetExtensionPref(extension_id); + if (!extension_prefs) + return NULL; + + const base::DictionaryValue* ext = NULL; + if (!extension_prefs->GetDictionary(kPrefGeometryCache, &ext)) + return NULL; + + return ext; +} + +void ExtensionPrefs::SetGeometryCache( + const std::string& extension_id, + scoped_ptr<base::DictionaryValue> cache) { + UpdateExtensionPref(extension_id, kPrefGeometryCache, cache.release()); +} + +const base::DictionaryValue* ExtensionPrefs::GetInstallSignature() { + return prefs_->GetDictionary(kInstallSignature); +} + +void ExtensionPrefs::SetInstallSignature( + const base::DictionaryValue* signature) { + if (signature) { + prefs_->Set(kInstallSignature, *signature); + DVLOG(1) << "SetInstallSignature - saving"; + } else { + DVLOG(1) << "SetInstallSignature - clearing"; + prefs_->ClearPref(kInstallSignature); + } +} + + +ExtensionPrefs::ExtensionPrefs( + PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + scoped_ptr<TimeProvider> time_provider, + bool extensions_disabled) + : prefs_(prefs), + install_directory_(root_dir), + extension_pref_value_map_(extension_pref_value_map), + app_sorting_(app_sorting.Pass()), + content_settings_store_(new ContentSettingsStore()), + time_provider_(time_provider.Pass()), + extensions_disabled_(extensions_disabled) { + app_sorting_->SetExtensionScopedPrefs(this); + MakePathsRelative(); + InitPrefStore(); +} + +void ExtensionPrefs::SetNeedsStorageGarbageCollection(bool value) { + prefs_->SetBoolean(pref_names::kStorageGarbageCollect, value); +} + +bool ExtensionPrefs::NeedsStorageGarbageCollection() { + return prefs_->GetBoolean(pref_names::kStorageGarbageCollect); +} + +// static +void ExtensionPrefs::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterDictionaryPref( + pref_names::kExtensions, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kToolbar, + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + registry->RegisterIntegerPref( + pref_names::kToolbarSize, + -1, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterDictionaryPref( + kExtensionsBlacklistUpdate, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kInstallAllowList, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kInstallDenyList, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterDictionaryPref( + pref_names::kInstallForceList, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kAllowedTypes, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterBooleanPref( + pref_names::kStorageGarbageCollect, + false, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterInt64Pref( + pref_names::kLastUpdateCheck, + 0, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterInt64Pref( + pref_names::kNextUpdateCheck, + 0, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kAllowedInstallSites, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterStringPref( + pref_names::kLastChromeVersion, + std::string(), // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kKnownDisabled, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +#if defined(TOOLKIT_VIEWS) + registry->RegisterIntegerPref( + pref_names::kBrowserActionContainerWidth, + 0, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +#endif + registry->RegisterDictionaryPref( + kInstallSignature, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + + registry->RegisterListPref(pref_names::kNativeMessagingBlacklist, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kNativeMessagingWhitelist, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +} + +template <class ExtensionIdContainer> +bool ExtensionPrefs::GetUserExtensionPrefIntoContainer( + const char* pref, + ExtensionIdContainer* id_container_out) { + DCHECK(id_container_out->empty()); + + const base::Value* user_pref_value = prefs_->GetUserPrefValue(pref); + const base::ListValue* user_pref_as_list; + if (!user_pref_value || !user_pref_value->GetAsList(&user_pref_as_list)) + return false; + + std::insert_iterator<ExtensionIdContainer> insert_iterator( + *id_container_out, id_container_out->end()); + std::string extension_id; + for (base::ListValue::const_iterator value_it = user_pref_as_list->begin(); + value_it != user_pref_as_list->end(); ++value_it) { + if (!(*value_it)->GetAsString(&extension_id)) { + NOTREACHED(); + continue; + } + insert_iterator = extension_id; + } + return true; +} + +template <class ExtensionIdContainer> +void ExtensionPrefs::SetExtensionPrefFromContainer( + const char* pref, + const ExtensionIdContainer& strings) { + ListPrefUpdate update(prefs_, pref); + base::ListValue* list_of_values = update.Get(); + list_of_values->Clear(); + for (typename ExtensionIdContainer::const_iterator iter = strings.begin(); + iter != strings.end(); ++iter) { + list_of_values->Append(new base::StringValue(*iter)); + } +} + +void ExtensionPrefs::PopulateExtensionInfoPrefs( + const Extension* extension, + const base::Time install_time, + Extension::State initial_state, + bool blacklisted_for_malware, + base::DictionaryValue* extension_dict) { + // Leave the state blank for component extensions so that old chrome versions + // loading new profiles do not fail in GetInstalledExtensionInfo. Older + // Chrome versions would only check for an omitted state. + if (initial_state != Extension::ENABLED_COMPONENT) + extension_dict->Set(kPrefState, new base::FundamentalValue(initial_state)); + + extension_dict->Set(kPrefLocation, + new base::FundamentalValue(extension->location())); + extension_dict->Set(kPrefCreationFlags, + new base::FundamentalValue(extension->creation_flags())); + extension_dict->Set(kPrefFromWebStore, + new base::FundamentalValue(extension->from_webstore())); + extension_dict->Set(kPrefFromBookmark, + new base::FundamentalValue(extension->from_bookmark())); + extension_dict->Set( + kPrefWasInstalledByDefault, + new base::FundamentalValue(extension->was_installed_by_default())); + extension_dict->Set(kPrefInstallTime, + new base::StringValue( + base::Int64ToString(install_time.ToInternalValue()))); + if (blacklisted_for_malware) + extension_dict->Set(kPrefBlacklist, new base::FundamentalValue(true)); + + base::FilePath::StringType path = MakePathRelative(install_directory_, + extension->path()); + extension_dict->Set(kPrefPath, new base::StringValue(path)); + // We store prefs about LOAD extensions, but don't cache their manifest + // since it may change on disk. + if (!Manifest::IsUnpackedLocation(extension->location())) { + extension_dict->Set(kPrefManifest, + extension->manifest()->value()->DeepCopy()); + } +} + +void ExtensionPrefs::FinishExtensionInfoPrefs( + const std::string& extension_id, + const base::Time install_time, + bool needs_sort_ordinal, + const syncer::StringOrdinal& suggested_page_ordinal, + base::DictionaryValue* extension_dict) { + // Reinitializes various preferences with empty dictionaries. + if (!extension_dict->HasKey(pref_names::kPrefPreferences)) { + extension_dict->Set(pref_names::kPrefPreferences, + new base::DictionaryValue); + } + + if (!extension_dict->HasKey(pref_names::kPrefIncognitoPreferences)) { + extension_dict->Set(pref_names::kPrefIncognitoPreferences, + new base::DictionaryValue); + } + + if (!extension_dict->HasKey(pref_names::kPrefRegularOnlyPreferences)) { + extension_dict->Set(pref_names::kPrefRegularOnlyPreferences, + new base::DictionaryValue); + } + + if (!extension_dict->HasKey(pref_names::kPrefContentSettings)) + extension_dict->Set(pref_names::kPrefContentSettings, new base::ListValue); + + if (!extension_dict->HasKey(pref_names::kPrefIncognitoContentSettings)) { + extension_dict->Set(pref_names::kPrefIncognitoContentSettings, + new base::ListValue); + } + + // If this point has been reached, any pending installs should be considered + // out of date. + extension_dict->Remove(kDelayedInstallInfo, NULL); + + // Clear state that may be registered from a previous install. + extension_dict->Remove(EventRouter::kRegisteredEvents, NULL); + + // FYI, all code below here races on sudden shutdown because |extension_dict|, + // |app_sorting_|, |extension_pref_value_map_|, and |content_settings_store_| + // are updated non-transactionally. This is probably not fixable without + // nested transactional updates to pref dictionaries. + if (needs_sort_ordinal) + app_sorting_->EnsureValidOrdinals(extension_id, suggested_page_ordinal); + + bool is_enabled = false; + int initial_state; + if (extension_dict->GetInteger(kPrefState, &initial_state)) { + is_enabled = initial_state == Extension::ENABLED; + } + + extension_pref_value_map_->RegisterExtension(extension_id, install_time, + is_enabled); + content_settings_store_->RegisterExtension(extension_id, install_time, + is_enabled); +} + +} // namespace extensions diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h new file mode 100644 index 0000000..2525b36 --- /dev/null +++ b/extensions/browser/extension_prefs.h @@ -0,0 +1,638 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_PREFS_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREFS_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/scoped_user_pref_update.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" +#include "extensions/browser/app_sorting.h" +#include "extensions/browser/extension_scoped_prefs.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/url_pattern_set.h" +#include "sync/api/string_ordinal.h" + +class ExtensionPrefValueMap; +class PrefService; + +namespace content { +class BrowserContext; +} + +namespace user_prefs { +class PrefRegistrySyncable; +} + +namespace extensions { + +class AppSorting; +class ContentSettingsStore; +class ExtensionPrefsUninstallExtension; +class URLPatternSet; + +// Class for managing global and per-extension preferences. +// +// This class distinguishes the following kinds of preferences: +// - global preferences: +// internal state for the extension system in general, not associated +// with an individual extension, such as lastUpdateTime. +// - per-extension preferences: +// meta-preferences describing properties of the extension like +// installation time, whether the extension is enabled, etc. +// - extension controlled preferences: +// browser preferences that an extension controls. For example, an +// extension could use the proxy API to specify the browser's proxy +// preference. Extension-controlled preferences are stored in +// PrefValueStore::extension_prefs(), which this class populates and +// maintains as the underlying extensions change. +class ExtensionPrefs : public ExtensionScopedPrefs, + public BrowserContextKeyedService { + public: + typedef std::vector<linked_ptr<ExtensionInfo> > ExtensionsInfo; + + // Vector containing identifiers for preferences. + typedef std::set<std::string> PrefKeySet; + + // This enum is used to store the reason an extension's install has been + // delayed. Do not remove items or re-order this enum as it is used in + // preferences. + enum DelayReason { + DELAY_REASON_NONE = 0, + DELAY_REASON_GC = 1, + DELAY_REASON_WAIT_FOR_IDLE = 2, + DELAY_REASON_WAIT_FOR_IMPORTS = 3, + }; + + + // Creates base::Time classes. The default implementation is just to return + // the current time, but tests can inject alternative implementations. + class TimeProvider { + public: + TimeProvider(); + + virtual ~TimeProvider(); + + // By default, returns the current time (base::Time::Now()). + virtual base::Time GetCurrentTime() const; + + private: + DISALLOW_COPY_AND_ASSIGN(TimeProvider); + }; + + // A wrapper around a ScopedUserPrefUpdate, which allows us to access the + // entry of a particular key for an extension. Use this if you need a mutable + // record of a dictionary or list in the current settings. Otherwise, prefer + // ReadPrefAsT() and UpdateExtensionPref() methods. + template <typename T, base::Value::Type type_enum_value> + class ScopedUpdate { + public: + ScopedUpdate(ExtensionPrefs* prefs, + const std::string& extension_id, + const std::string& key); + virtual ~ScopedUpdate(); + + // Returns a mutable value for the key (ownership remains with the prefs), + // if one exists. Otherwise, returns NULL. + virtual T* Get(); + + // Creates and returns a mutable value for the key (the prefs own the new + // value), if one does not already exist. Otherwise, returns the current + // value. + virtual T* Create(); + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedUpdate); + + DictionaryPrefUpdate update_; + const std::string extension_id_; + const std::string key_; + }; + typedef ScopedUpdate<base::DictionaryValue, base::Value::TYPE_DICTIONARY> + ScopedDictionaryUpdate; + typedef ScopedUpdate<base::ListValue, base::Value::TYPE_LIST> + ScopedListUpdate; + + // Creates and initializes an ExtensionPrefs object. + // Does not take ownership of |prefs| and |extension_pref_value_map|. + // If |extensions_disabled| is true, extension controlled preferences and + // content settings do not become effective. + static ExtensionPrefs* Create( + PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + bool extensions_disabled); + + // A version of Create which allows injection of a custom base::Time provider. + // Use this as needed for testing. + static ExtensionPrefs* Create( + PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + bool extensions_disabled, + scoped_ptr<TimeProvider> time_provider); + + virtual ~ExtensionPrefs(); + + // Convenience function to get the ExtensionPrefs for a BrowserContext. + static ExtensionPrefs* Get(content::BrowserContext* context); + + // Returns all installed extensions from extension preferences provided by + // |pref_service|. This is exposed for ProtectedPrefsWatcher because it needs + // access to the extension ID list before the ExtensionService is initialized. + static ExtensionIdList GetExtensionsFrom(const PrefService* pref_service); + + // Returns true if the specified external extension was uninstalled by the + // user. + bool IsExternalExtensionUninstalled(const std::string& id) const; + + // Checks whether |extension_id| is disabled. If there's no state pref for + // the extension, this will return false. Generally you should use + // ExtensionService::IsExtensionEnabled instead. + bool IsExtensionDisabled(const std::string& id) const; + + // Get/Set the order that the browser actions appear in the toolbar. + ExtensionIdList GetToolbarOrder(); + void SetToolbarOrder(const ExtensionIdList& extension_ids); + + // Gets the set of known disabled extension IDs into |id_set_out|. Returns + // false iff the set of known disabled extension IDs hasn't been set yet. + bool GetKnownDisabled(ExtensionIdSet* id_set_out); + + // Sets the set of known disabled extension IDs. + void SetKnownDisabled(const ExtensionIdSet& extension_ids); + + // Called when an extension is installed, so that prefs get created. + // |blacklisted_for_malware| should be set if the extension was included in a + // blacklist due to being malware. If |page_ordinal| is an invalid ordinal, + // then a page will be found for the App. + void OnExtensionInstalled(const Extension* extension, + Extension::State initial_state, + bool blacklisted_for_malware, + const syncer::StringOrdinal& page_ordinal); + + // Called when an extension is uninstalled, so that prefs get cleaned up. + void OnExtensionUninstalled(const std::string& extension_id, + const Manifest::Location& location, + bool external_uninstall); + + // Called to change the extension's state when it is enabled/disabled. + void SetExtensionState(const std::string& extension_id, Extension::State); + + // Populates |out| with the ids of all installed extensions. + void GetExtensions(ExtensionIdList* out); + + // ExtensionScopedPrefs methods: + virtual void UpdateExtensionPref(const std::string& id, + const std::string& key, + base::Value* value) OVERRIDE; + + virtual void DeleteExtensionPrefs(const std::string& id) OVERRIDE; + + virtual bool ReadPrefAsBoolean(const std::string& extension_id, + const std::string& pref_key, + bool* out_value) const OVERRIDE; + + virtual bool ReadPrefAsInteger(const std::string& extension_id, + const std::string& pref_key, + int* out_value) const OVERRIDE; + + virtual bool ReadPrefAsString(const std::string& extension_id, + const std::string& pref_key, + std::string* out_value) const OVERRIDE; + + virtual bool ReadPrefAsList(const std::string& extension_id, + const std::string& pref_key, + const base::ListValue** out_value) const OVERRIDE; + + virtual bool ReadPrefAsDictionary( + const std::string& extension_id, + const std::string& pref_key, + const base::DictionaryValue** out_value) const OVERRIDE; + + virtual bool HasPrefForExtension(const std::string& extension_id) const + OVERRIDE; + + // Did the extension ask to escalate its permission during an upgrade? + bool DidExtensionEscalatePermissions(const std::string& id); + + // If |did_escalate| is true, the preferences for |extension| will be set to + // require the install warning when the user tries to enable. + void SetDidExtensionEscalatePermissions( + const Extension* extension, + bool did_escalate); + + // Getter and setters for disabled reason. + int GetDisableReasons(const std::string& extension_id) const; + void AddDisableReason(const std::string& extension_id, + Extension::DisableReason disable_reason); + void RemoveDisableReason(const std::string& extension_id, + Extension::DisableReason disable_reason); + void ClearDisableReasons(const std::string& extension_id); + + // Gets the set of extensions that have been blacklisted in prefs. + std::set<std::string> GetBlacklistedExtensions(); + + // Sets whether the extension with |id| is blacklisted. + void SetExtensionBlacklisted(const std::string& extension_id, + bool is_blacklisted); + + // Returns the version string for the currently installed extension, or + // the empty string if not found. + std::string GetVersionString(const std::string& extension_id); + + // Re-writes the extension manifest into the prefs. + // Called to change the extension's manifest when it's re-localized. + void UpdateManifest(const Extension* extension); + + // Returns extension path based on extension ID, or empty FilePath on error. + base::FilePath GetExtensionPath(const std::string& extension_id); + + // Returns base extensions install directory. + const base::FilePath& install_directory() const { return install_directory_; } + + // Returns whether the extension with |id| has its blacklist bit set. + // + // WARNING: this only checks the extension's entry in prefs, so by definition + // can only check extensions that prefs knows about. There may be other + // sources of blacklist information, such as safebrowsing. You probably want + // to use Blacklist::GetBlacklistedIDs rather than this method. + bool IsExtensionBlacklisted(const std::string& id) const; + + // Increment the count of how many times we prompted the user to acknowledge + // the given extension, and return the new count. + int IncrementAcknowledgePromptCount(const std::string& extension_id); + + // Whether the user has acknowledged an external extension. + bool IsExternalExtensionAcknowledged(const std::string& extension_id); + void AcknowledgeExternalExtension(const std::string& extension_id); + + // Whether the user has acknowledged a blacklisted extension. + bool IsBlacklistedExtensionAcknowledged(const std::string& extension_id); + void AcknowledgeBlacklistedExtension(const std::string& extension_id); + + // Whether the external extension was installed during the first run + // of this profile. + bool IsExternalInstallFirstRun(const std::string& extension_id); + void SetExternalInstallFirstRun(const std::string& extension_id); + + // Whether the user has been notified about extension with |extension_id| + // being wiped out. + bool HasWipeoutBeenAcknowledged(const std::string& extension_id); + void SetWipeoutAcknowledged(const std::string& extension_id, bool value); + + // Returns true if the extension notification code has already run for the + // first time for this profile. Currently we use this flag to mean that any + // extensions that would trigger notifications should get silently + // acknowledged. This is a fuse. Calling it the first time returns false. + // Subsequent calls return true. It's not possible through an API to ever + // reset it. Don't call it unless you mean it! + bool SetAlertSystemFirstRun(); + + // Checks if extensions are blacklisted by default, by policy. + // The ManagementPolicy::Provider methods also take this into account, and + // should be used instead when the extension ID is known. + bool ExtensionsBlacklistedByDefault() const; + + // Returns the last value set via SetLastPingDay. If there isn't such a + // pref, the returned Time will return true for is_null(). + base::Time LastPingDay(const std::string& extension_id) const; + + // The time stored is based on the server's perspective of day start time, not + // the client's. + void SetLastPingDay(const std::string& extension_id, const base::Time& time); + + // Similar to the 2 above, but for the extensions blacklist. + base::Time BlacklistLastPingDay() const; + void SetBlacklistLastPingDay(const base::Time& time); + + // Similar to LastPingDay/SetLastPingDay, but for sending "days since active" + // ping. + base::Time LastActivePingDay(const std::string& extension_id); + void SetLastActivePingDay(const std::string& extension_id, + const base::Time& time); + + // A bit we use for determining if we should send the "days since active" + // ping. A value of true means the item has been active (launched) since the + // last update check. + bool GetActiveBit(const std::string& extension_id); + void SetActiveBit(const std::string& extension_id, bool active); + + // Returns the granted permission set for the extension with |extension_id|, + // and NULL if no preferences were found for |extension_id|. + // This passes ownership of the returned set to the caller. + PermissionSet* GetGrantedPermissions(const std::string& extension_id); + + // Adds |permissions| to the granted permissions set for the extension with + // |extension_id|. The new granted permissions set will be the union of + // |permissions| and the already granted permissions. + void AddGrantedPermissions(const std::string& extension_id, + const PermissionSet* permissions); + + // As above, but subtracts the given |permissions| from the granted set. + void RemoveGrantedPermissions(const std::string& extension_id, + const PermissionSet* permissions); + + // Gets the active permission set for the specified extension. This may + // differ from the permissions in the manifest due to the optional + // permissions API. This passes ownership of the set to the caller. + PermissionSet* GetActivePermissions(const std::string& extension_id); + + // Sets the active |permissions| for the extension with |extension_id|. + void SetActivePermissions(const std::string& extension_id, + const PermissionSet* permissions); + + // Records whether or not this extension is currently running. + void SetExtensionRunning(const std::string& extension_id, bool is_running); + + // Returns whether or not this extension is marked as running. This is used to + // restart apps across browser restarts. + bool IsExtensionRunning(const std::string& extension_id); + + // Set/Get whether or not the app is active. Used to force a launch of apps + // that don't handle onRestarted() on a restart. We can only safely do that if + // the app was active when it was last running. + void SetIsActive(const std::string& extension_id, bool is_active); + bool IsActive(const std::string& extension_id); + + // Returns true if the user enabled this extension to be loaded in incognito + // mode. + // + // IMPORTANT: you probably want to use extension_utils::IsIncognitoEnabled + // instead of this method. + bool IsIncognitoEnabled(const std::string& extension_id) const; + void SetIsIncognitoEnabled(const std::string& extension_id, bool enabled); + + // Returns true if the user has chosen to allow this extension to inject + // scripts into pages with file URLs. + // + // IMPORTANT: you probably want to use extension_utils::AllowFileAccess + // instead of this method. + bool AllowFileAccess(const std::string& extension_id) const; + void SetAllowFileAccess(const std::string& extension_id, bool allow); + bool HasAllowFileAccessSetting(const std::string& extension_id) const; + + // Saves ExtensionInfo for each installed extension with the path to the + // version directory and the location. Blacklisted extensions won't be saved + // and neither will external extensions the user has explicitly uninstalled. + // Caller takes ownership of returned structure. + scoped_ptr<ExtensionsInfo> GetInstalledExtensionsInfo() const; + + // Same as above, but only includes external extensions the user has + // explicitly uninstalled. + scoped_ptr<ExtensionsInfo> GetUninstalledExtensionsInfo() const; + + // Returns the ExtensionInfo from the prefs for the given extension. If the + // extension is not present, NULL is returned. + scoped_ptr<ExtensionInfo> GetInstalledExtensionInfo( + const std::string& extension_id) const; + + // We've downloaded an updated .crx file for the extension, but are waiting + // to install it. + void SetDelayedInstallInfo(const Extension* extension, + Extension::State initial_state, + bool blacklisted_for_malware, + DelayReason delay_reason, + const syncer::StringOrdinal& page_ordinal); + + // Removes any delayed install information we have for the given + // |extension_id|. Returns true if there was info to remove; false otherwise. + bool RemoveDelayedInstallInfo(const std::string& extension_id); + + // Update the prefs to finish the update for an extension. + bool FinishDelayedInstallInfo(const std::string& extension_id); + + // Returns the ExtensionInfo from the prefs for delayed install information + // for |extension_id|, if we have any. Otherwise returns NULL. + scoped_ptr<ExtensionInfo> GetDelayedInstallInfo( + const std::string& extension_id) const; + + DelayReason GetDelayedInstallReason(const std::string& extension_id) const; + + // Returns information about all the extensions that have delayed install + // information. + scoped_ptr<ExtensionsInfo> GetAllDelayedInstallInfo() const; + + // Returns true if the user repositioned the app on the app launcher via drag + // and drop. + bool WasAppDraggedByUser(const std::string& extension_id); + + // Sets a flag indicating that the user repositioned the app on the app + // launcher by drag and dropping it. + void SetAppDraggedByUser(const std::string& extension_id); + + // Returns true if there is an extension which controls the preference value + // for |pref_key| *and* it is specific to incognito mode. + bool HasIncognitoPrefValue(const std::string& pref_key); + + // Returns the creation flags mask for the extension. + int GetCreationFlags(const std::string& extension_id) const; + + // Returns the creation flags mask for a delayed install extension. + int GetDelayedInstallCreationFlags(const std::string& extension_id) const; + + // Returns true if the extension was installed from the Chrome Web Store. + bool IsFromWebStore(const std::string& extension_id) const; + + // Returns true if the extension was installed from an App generated from a + // bookmark. + bool IsFromBookmark(const std::string& extension_id) const; + + // Returns true if the extension was installed as a default app. + bool WasInstalledByDefault(const std::string& extension_id) const; + + // Helper method to acquire the installation time of an extension. + // Returns base::Time() if the installation time could not be parsed or + // found. + base::Time GetInstallTime(const std::string& extension_id) const; + + // Gets/sets the last launch time of an extension. + base::Time GetLastLaunchTime(const std::string& extension_id) const; + void SetLastLaunchTime(const std::string& extension_id, + const base::Time& time); + + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + + bool extensions_disabled() { return extensions_disabled_; } + + ContentSettingsStore* content_settings_store() { + return content_settings_store_.get(); + } + + // The underlying PrefService. + PrefService* pref_service() const { return prefs_; } + + // The underlying AppSorting. + AppSorting* app_sorting() const { return app_sorting_.get(); } + + // Describes the URLs that are able to install extensions. See + // pref_names::kAllowedInstallSites for more information. + URLPatternSet GetAllowedInstallSites(); + + // Schedules garbage collection of an extension's on-disk data on the next + // start of this ExtensionService. Applies only to extensions with isolated + // storage. + void SetNeedsStorageGarbageCollection(bool value); + bool NeedsStorageGarbageCollection(); + + // Used by ShellWindowGeometryCache to persist its cache. These methods + // should not be called directly. + const base::DictionaryValue* GetGeometryCache( + const std::string& extension_id) const; + void SetGeometryCache(const std::string& extension_id, + scoped_ptr<base::DictionaryValue> cache); + + // Used for verification of installed extension ids. For the Set method, pass + // null to remove the preference. + const base::DictionaryValue* GetInstallSignature(); + void SetInstallSignature(const base::DictionaryValue* signature); + + private: + friend class ExtensionPrefsBlacklistedExtensions; // Unit test. + friend class ExtensionPrefsUninstallExtension; // Unit test. + + // See the Create methods. + ExtensionPrefs(PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + scoped_ptr<TimeProvider> time_provider, + bool extensions_disabled); + + // Converts absolute paths in the pref to paths relative to the + // install_directory_. + void MakePathsRelative(); + + // Converts internal relative paths to be absolute. Used for export to + // consumers who expect full paths. + void MakePathsAbsolute(base::DictionaryValue* dict); + + // Helper function used by GetInstalledExtensionInfo() and + // GetDelayedInstallInfo() to construct an ExtensionInfo from the provided + // |extension| dictionary. + scoped_ptr<ExtensionInfo> GetInstalledInfoHelper( + const std::string& extension_id, + const base::DictionaryValue* extension) const; + + // Interprets the list pref, |pref_key| in |extension_id|'s preferences, as a + // URLPatternSet. The |valid_schemes| specify how to parse the URLPatterns. + bool ReadPrefAsURLPatternSet(const std::string& extension_id, + const std::string& pref_key, + URLPatternSet* result, + int valid_schemes); + + // Converts |new_value| to a list of strings and sets the |pref_key| pref + // belonging to |extension_id|. + void SetExtensionPrefURLPatternSet(const std::string& extension_id, + const std::string& pref_key, + const URLPatternSet& new_value); + + // Read the boolean preference entry and return true if the preference exists + // and the preference's value is true; false otherwise. + bool ReadPrefAsBooleanAndReturn(const std::string& extension_id, + const std::string& key) const; + + // Interprets |pref_key| in |extension_id|'s preferences as an + // PermissionSet, and passes ownership of the set to the caller. + PermissionSet* ReadPrefAsPermissionSet(const std::string& extension_id, + const std::string& pref_key); + + // Converts the |new_value| to its value and sets the |pref_key| pref + // belonging to |extension_id|. + void SetExtensionPrefPermissionSet(const std::string& extension_id, + const std::string& pref_key, + const PermissionSet* new_value); + + // Returns an immutable dictionary for extension |id|'s prefs, or NULL if it + // doesn't exist. + const base::DictionaryValue* GetExtensionPref(const std::string& id) const; + + // Fix missing preference entries in the extensions that are were introduced + // in a later Chrome version. + void FixMissingPrefs(const ExtensionIdList& extension_ids); + + // Installs the persistent extension preferences into |prefs_|'s extension + // pref store. Does nothing if extensions_disabled_ is true. + void InitPrefStore(); + + // Migrates the permissions data in the pref store. + void MigratePermissions(const ExtensionIdList& extension_ids); + + // Migrates the disable reasons from a single enum to a bit mask. + void MigrateDisableReasons(const ExtensionIdList& extension_ids); + + // Checks whether there is a state pref for the extension and if so, whether + // it matches |check_state|. + bool DoesExtensionHaveState(const std::string& id, + Extension::State check_state) const; + + // Reads the list of strings for |pref| from user prefs into + // |id_container_out|. Returns false if the pref wasn't found in the user + // pref store. + template <class ExtensionIdContainer> + bool GetUserExtensionPrefIntoContainer( + const char* pref, + ExtensionIdContainer* id_container_out); + + // Writes the list of strings contained in |strings| to |pref| in prefs. + template <class ExtensionIdContainer> + void SetExtensionPrefFromContainer(const char* pref, + const ExtensionIdContainer& strings); + + // Helper function to populate |extension_dict| with the values needed + // by a newly installed extension. Work is broken up between this + // function and FinishExtensionInfoPrefs() to accomodate delayed + // installations. + void PopulateExtensionInfoPrefs(const Extension* extension, + const base::Time install_time, + Extension::State initial_state, + bool blacklisted_for_malware, + base::DictionaryValue* extension_dict); + + // Helper function to complete initialization of the values in + // |extension_dict| for an extension install. Also see + // PopulateExtensionInfoPrefs(). + void FinishExtensionInfoPrefs( + const std::string& extension_id, + const base::Time install_time, + bool needs_sort_ordinal, + const syncer::StringOrdinal& suggested_page_ordinal, + base::DictionaryValue* extension_dict); + + // The pref service specific to this set of extension prefs. Owned by the + // BrowserContext. + PrefService* prefs_; + + // Base extensions install directory. + base::FilePath install_directory_; + + // Weak pointer, owned by BrowserContext. + ExtensionPrefValueMap* extension_pref_value_map_; + + // Contains all the logic for handling the order for various extension + // properties. + scoped_ptr<AppSorting> app_sorting_; + + scoped_refptr<ContentSettingsStore> content_settings_store_; + + scoped_ptr<TimeProvider> time_provider_; + + bool extensions_disabled_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefs); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREFS_H_ diff --git a/extensions/browser/extension_prefs_factory.cc b/extensions/browser/extension_prefs_factory.cc new file mode 100644 index 0000000..e83e33b --- /dev/null +++ b/extensions/browser/extension_prefs_factory.cc @@ -0,0 +1,61 @@ +// Copyright 2014 The Chromium 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 "base/command_line.h" +#include "base/prefs/pref_service.h" +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/extension_pref_value_map.h" +#include "extensions/browser/extension_pref_value_map_factory.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_prefs_factory.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/common/constants.h" + +namespace extensions { + +// static +ExtensionPrefs* ExtensionPrefsFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<ExtensionPrefs*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ExtensionPrefsFactory* ExtensionPrefsFactory::GetInstance() { + return Singleton<ExtensionPrefsFactory>::get(); +} + +void ExtensionPrefsFactory::SetInstanceForTesting( + content::BrowserContext* context, ExtensionPrefs* prefs) { + Associate(context, prefs); +} + +ExtensionPrefsFactory::ExtensionPrefsFactory() + : BrowserContextKeyedServiceFactory( + "ExtensionPrefs", + BrowserContextDependencyManager::GetInstance()) { +} + +ExtensionPrefsFactory::~ExtensionPrefsFactory() { +} + +BrowserContextKeyedService* ExtensionPrefsFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get(); + return ExtensionPrefs::Create( + client->GetPrefServiceForContext(context), + context->GetPath().AppendASCII(extensions::kInstallDirectoryName), + ExtensionPrefValueMapFactory::GetForBrowserContext(context), + client->CreateAppSorting().Pass(), + client->AreExtensionsDisabled( + *CommandLine::ForCurrentProcess(), context)); +} + +content::BrowserContext* ExtensionPrefsFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return ExtensionsBrowserClient::Get()->GetOriginalContext(context); +} + +} // namespace extensions diff --git a/extensions/browser/extension_prefs_factory.h b/extensions/browser/extension_prefs_factory.h new file mode 100644 index 0000000..3aee29a --- /dev/null +++ b/extensions/browser/extension_prefs_factory.h @@ -0,0 +1,39 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_PREFS_FACTORY_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREFS_FACTORY_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +namespace extensions { + +class ExtensionPrefs; + +class ExtensionPrefsFactory : public BrowserContextKeyedServiceFactory { + public: + static ExtensionPrefs* GetForBrowserContext(content::BrowserContext* context); + + static ExtensionPrefsFactory* GetInstance(); + + void SetInstanceForTesting( + content::BrowserContext* context, ExtensionPrefs* prefs); + + private: + friend struct DefaultSingletonTraits<ExtensionPrefsFactory>; + + ExtensionPrefsFactory(); + virtual ~ExtensionPrefsFactory(); + + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE; + virtual content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const OVERRIDE; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREFS_FACTORY_H_ diff --git a/extensions/browser/extension_scoped_prefs.h b/extensions/browser/extension_scoped_prefs.h new file mode 100644 index 0000000..15fc577 --- /dev/null +++ b/extensions/browser/extension_scoped_prefs.h @@ -0,0 +1,56 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_SCOPED_PREFS_H_ +#define EXTENSIONS_BROWSER_EXTENSION_SCOPED_PREFS_H_ + +namespace extensions { + +class ExtensionScopedPrefs { + public: + ExtensionScopedPrefs() {} + ~ExtensionScopedPrefs() {} + + // Sets the pref |key| for extension |id| to |value|. + virtual void UpdateExtensionPref(const std::string& id, + const std::string& key, + base::Value* value) = 0; + + // Deletes the pref dictionary for extension |id|. + virtual void DeleteExtensionPrefs(const std::string& id) = 0; + + // Reads a boolean pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsBoolean(const std::string& extension_id, + const std::string& pref_key, + bool* out_value) const = 0; + + // Reads an integer pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsInteger(const std::string& extension_id, + const std::string& pref_key, + int* out_value) const = 0; + + // Reads a string pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsString(const std::string& extension_id, + const std::string& pref_key, + std::string* out_value) const = 0; + + // Reads a list pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsList(const std::string& extension_id, + const std::string& pref_key, + const base::ListValue** out_value) const = 0; + + // Reads a dictionary pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsDictionary( + const std::string& extension_id, + const std::string& pref_key, + const base::DictionaryValue** out_value) const = 0; + + // Returns true if the prefs contain an entry for an extension with id + // |extension_id|. + virtual bool HasPrefForExtension(const std::string& extension_id) const = 0; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_SCOPED_PREFS_H_ diff --git a/extensions/browser/extensions_browser_client.h b/extensions/browser/extensions_browser_client.h index 46405a0..ee7fde3 100644 --- a/extensions/browser/extensions_browser_client.h +++ b/extensions/browser/extensions_browser_client.h @@ -60,7 +60,7 @@ class ExtensionsBrowserClient { virtual content::BrowserContext* GetOffTheRecordContext( content::BrowserContext* context) = 0; - // Return the original "recording" context. This method returns |context| if + // Returns the original "recording" context. This method returns |context| if // |context| is not incognito. virtual content::BrowserContext* GetOriginalContext( content::BrowserContext* context) = 0; diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index 725c675..7ad28d0f5 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -172,15 +172,26 @@ 'browser/event_listener_map.h', 'browser/event_router.cc', 'browser/event_router.h', - 'browser/extension_prefs_scope.h', 'browser/extension_error.cc', 'browser/extension_error.h', 'browser/extension_function.cc', 'browser/extension_function.h', + 'browser/extension_pref_store.cc', + 'browser/extension_pref_store.h', + 'browser/extension_pref_value_map.cc', + 'browser/extension_pref_value_map_factory.cc', + 'browser/extension_pref_value_map_factory.h', + 'browser/extension_pref_value_map.h', + 'browser/extension_prefs.cc', + 'browser/extension_prefs.h', + 'browser/extension_prefs_factory.cc', + 'browser/extension_prefs_factory.h', + 'browser/extension_prefs_scope.h', 'browser/extension_registry.cc', 'browser/extension_registry.h', 'browser/extension_registry_factory.cc', 'browser/extension_registry_factory.h', + 'browser/extension_scoped_prefs.h', 'browser/extensions_browser_client.cc', 'browser/extensions_browser_client.h', 'browser/external_provider_interface.h', |