Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3099)

Unified Diff: chrome/browser/extensions/extension_prefs.cc

Issue 5213002: Fix for Bug 50726 "Save extension list and "winning" prefs from extensions" (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Lint issues and factored out renaming of InMemoryPrefStore Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/extension_prefs.cc
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 57c5c9ba1427f06adde28d8a4b042800ca494958..1c3df4ec8a5dc79cbb1590ee4c955327ee568ec4 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -7,6 +7,7 @@
#include "base/string_util.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/prefs/in_memory_pref_store.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
@@ -82,6 +83,14 @@ const char kUpdateUrlData[] = "update_url_data";
// Whether the browser action is visible in the toolbar.
const char kBrowserActionVisible[] = "browser_action_visible";
+// A preference that indicates when an extension was installed.
+const char kPrefInstallTime[] = "install_time";
+
+// A preference that indicates the last effective preference values of an
+// extension. The value is a dictionary mapping (non-expanded) preference keys
+// to the values configured by the extension.
+const char kPrefPreferences[] = "preferences";
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -123,6 +132,8 @@ ExtensionPrefs::ExtensionPrefs(PrefService* prefs, const FilePath& root_dir)
CleanupBadExtensionKeys(prefs);
MakePathsRelative();
+
+ InstallPersistedExtensionControlledPrefs();
}
ExtensionPrefs::~ExtensionPrefs() {}
@@ -500,8 +511,8 @@ void ExtensionPrefs::GetKilledExtensionIds(std::set<std::string>* killed_ids) {
}
}
-std::vector<std::string> ExtensionPrefs::GetToolbarOrder() {
- std::vector<std::string> extension_ids;
+ExtensionPrefs::PrefKeySet ExtensionPrefs::GetToolbarOrder() {
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 Did you also fix callers of this function?
battre (please use the other) 2010/11/19 18:00:39 Reverted.
+ ExtensionPrefs::PrefKeySet extension_ids;
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 no need for the ExtensionPrefs qualifier in this l
battre (please use the other) 2010/11/19 18:00:39 Reverted.
const ListValue* toolbar_order = prefs_->GetList(kExtensionToolbar);
if (toolbar_order) {
for (size_t i = 0; i < toolbar_order->GetSize(); ++i) {
@@ -514,10 +525,10 @@ std::vector<std::string> ExtensionPrefs::GetToolbarOrder() {
}
void ExtensionPrefs::SetToolbarOrder(
- const std::vector<std::string>& extension_ids) {
+ const PrefKeySet& extension_ids) {
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 Now fits the previous line!
battre (please use the other) 2010/11/19 18:00:39 Reverted.
ListValue* toolbar_order = prefs_->GetMutableList(kExtensionToolbar);
toolbar_order->Clear();
- for (std::vector<std::string>::const_iterator iter = extension_ids.begin();
+ for (PrefKeySet::const_iterator iter = extension_ids.begin();
iter != extension_ids.end(); ++iter) {
toolbar_order->Append(new StringValue(*iter));
}
@@ -528,12 +539,18 @@ void ExtensionPrefs::OnExtensionInstalled(
const Extension* extension, Extension::State initial_state,
bool initial_incognito_enabled) {
const std::string& id = extension->id();
+ const base::Time installTime = GetCurrentTime();
UpdateExtensionPref(id, kPrefState,
Value::CreateIntegerValue(initial_state));
UpdateExtensionPref(id, kPrefIncognitoEnabled,
Value::CreateBooleanValue(initial_incognito_enabled));
UpdateExtensionPref(id, kPrefLocation,
Value::CreateIntegerValue(extension->location()));
+ UpdateExtensionPref(id, kPrefInstallTime,
+ Value::CreateStringValue(
+ base::Int64ToString(installTime.ToInternalValue())));
+ UpdateExtensionPref(id, kPrefPreferences, new DictionaryValue());
+
FilePath::StringType path = MakePathRelative(install_directory_,
extension->path(), NULL);
UpdateExtensionPref(id, kPrefPath, Value::CreateStringValue(path));
@@ -551,6 +568,9 @@ void ExtensionPrefs::OnExtensionInstalled(
void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id,
const Extension::Location& location,
bool external_uninstall) {
+ PrefKeySet prefKeys;
+ GetExtensionControlledPrefKeys(extension_id, &prefKeys);
+
// 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
@@ -562,10 +582,13 @@ void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id,
} else {
DeleteExtensionPrefs(extension_id);
}
+
+ for (PrefKeySet::iterator i = prefKeys.begin(); i != prefKeys.end(); ++i)
+ UpdateWinningPref(*i);
}
Extension::State ExtensionPrefs::GetExtensionState(
- const std::string& extension_id) {
+ const std::string& extension_id) const {
DictionaryValue* extension = GetExtensionPref(extension_id);
// If the extension doesn't have a pref, it's a --load-extension.
@@ -586,6 +609,12 @@ void ExtensionPrefs::SetExtensionState(const Extension* extension,
Extension::State state) {
UpdateExtensionPref(extension->id(), kPrefState,
Value::CreateIntegerValue(state));
+
+ PrefKeySet prefKeys;
+ GetExtensionControlledPrefKeys(extension->id(), &prefKeys);
+ for (PrefKeySet::iterator i = prefKeys.begin(); i != prefKeys.end(); ++i)
+ UpdateWinningPref(*i);
+
SavePrefsAndNotify();
}
@@ -689,6 +718,20 @@ DictionaryValue* ExtensionPrefs::GetExtensionPref(
return extension;
}
+DictionaryValue* ExtensionPrefs::GetExtensionControlledPrefs(
+ const std::string& extension_id) const {
+ DictionaryValue* extension = GetExtensionPref(extension_id);
+ if (!extension)
+ return NULL;
+ DictionaryValue* preferences = NULL;
+ extension->GetDictionary(kPrefPreferences, &preferences);
+ if (preferences == NULL) { // May be pruned when writing to disk.
+ preferences = new DictionaryValue;
+ extension->Set(kPrefPreferences, preferences);
+ }
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 I don't see why you add the empty dictionary here.
battre (please use the other) 2010/11/19 18:00:39 Done. (added a missing NULLity test for the change
+ return preferences;
+}
+
// Helper function for GetInstalledExtensionsInfo.
static ExtensionInfo* GetInstalledExtensionInfoImpl(
DictionaryValue* extension_data,
@@ -948,6 +991,196 @@ std::string ExtensionPrefs::GetUpdateUrlData(const std::string& extension_id) {
return data;
}
+base::Time ExtensionPrefs::GetCurrentTime() const {
+ return base::Time::Now();
+}
+
+base::Time ExtensionPrefs::GetInstallTime(const std::string& extension_id)
+ const {
+ const DictionaryValue* extension = GetExtensionPref(extension_id);
+ if (!extension)
+ return base::Time::FromInternalValue(0);
+ std::string install_time_str("0");
+ extension->GetString(kPrefInstallTime, &install_time_str);
+ int64 install_time_i64 = 0;
+ base::StringToInt64(install_time_str, &install_time_i64);
+ LOG_IF(ERROR, install_time_i64 == 0)
+ << "Error parsing installation time of an extension.";
+ return base::Time::FromInternalValue(install_time_i64);
+}
+
+void ExtensionPrefs::GetEnabledExtensions(ExtensionIdSet* out) const {
+ DCHECK(out);
+ const DictionaryValue* extensions =
+ pref_service()->GetDictionary(kExtensionsPref);
+
+ for (DictionaryValue::key_iterator ext_id = extensions->begin_keys();
+ ext_id != extensions->end_keys(); ++ext_id) {
+ if (GetExtensionState(*ext_id) != Extension::ENABLED)
+ continue;
+ out->push_back(*ext_id);
+ }
+}
+
+// Comparator that sorts extensions by their increasing installation time
+// or uses the extension id as a fall back for equal installation times.
+struct InstallTimeComparator {
+ explicit InstallTimeComparator(ExtensionPrefs* ext_prefs)
+ : ext_prefs_(ext_prefs) {}
+
+ bool operator()(std::string extid_a, std::string extid_b) {
+ base::Time install_time_a = ext_prefs_->GetInstallTime(extid_a);
+ base::Time install_time_b = ext_prefs_->GetInstallTime(extid_b);
+ if (install_time_a == install_time_b)
+ return extid_a < extid_b;
+ return install_time_a < install_time_b;
+ }
+
+ ExtensionPrefs* ext_prefs_; // Weak pointer.
+};
+
+void ExtensionPrefs::FixMissingPrefs(const ExtensionIdSet& extension_ids) {
+ // Fix old entries that did not get an installation time entry when they
+ // were installed or don't have a preferences field.
+ bool persistRequired = false;
+ for (ExtensionIdSet::const_iterator ext_id = extension_ids.begin();
+ ext_id != extension_ids.end(); ++ext_id) {
+ DictionaryValue* extension = GetExtensionPref(*ext_id);
+ CHECK(extension != NULL);
+
+ if (GetInstallTime(*ext_id) == base::Time::FromInternalValue(0)) {
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 base::Time::FromInternalValue(0) == base::Time()
battre (please use the other) 2010/11/19 18:00:39 Done.
+ const base::Time installTime = GetCurrentTime();
+ extension->Set(kPrefInstallTime,
+ Value::CreateStringValue(
+ base::Int64ToString(installTime.ToInternalValue())));
+ }
+ }
+ if (persistRequired) {
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 AFAICS, persistRequired is always false. Does that
battre (please use the other) 2010/11/19 18:00:39 Done.
+ SavePrefsAndNotify();
+ }
+}
+
+void ExtensionPrefs::InstallPersistedExtensionControlledPrefs() {
+ // When this is called, the PrefService is initialized and provides access
+ // to the user preferences stored in a JSON file. We take the persisted
+ // preferences of all extensions, calculate the effective preferences
+ // (considering that one extension overrides preferences of other extensions)
+ // and store the effective preferences in the PrefService.
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 Maybe say "extension pref store" instead of PrefSe
battre (please use the other) 2010/11/19 18:00:39 Done.
+ ExtensionIdSet extension_ids;
+ GetEnabledExtensions(&extension_ids);
+ FixMissingPrefs(extension_ids);
+
+ // Sort such that latest installed extension appears last.
+ std::sort(extension_ids.begin(), extension_ids.end(),
+ InstallTimeComparator(this));
+
+ // Collect all effective preferences (later ones override newer ones).
+ DictionaryValue merged_non_expanded;
+ for (ExtensionIdSet::iterator ext_id = extension_ids.begin();
+ ext_id != extension_ids.end(); ++ext_id) {
+ if (DictionaryValue* preferences = GetExtensionControlledPrefs(*ext_id))
+ merged_non_expanded.MergeDictionary(preferences);
+ }
+
+ // Expand all keys.
+ PrefStore* extension_prefs =
+ pref_service()->pref_value_store()->GetExtensionPrefStore();
+ for (DictionaryValue::key_iterator prefkey =
+ merged_non_expanded.begin_keys();
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 Doesn't this fit the previous line?
battre (please use the other) 2010/11/19 18:00:39 80 characters on the spot. :-) Done.
+ prefkey != merged_non_expanded.end_keys();
+ ++prefkey) {
+ Value* value;
+ CHECK(merged_non_expanded.GetWithoutPathExpansion(*prefkey, &value));
+ extension_prefs->prefs()->Set(*prefkey, value->DeepCopy());
+ }
+}
+
+static bool equalValues(const Value* a, const Value* b) {
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 Any reason, why you didn't move this to base/value
battre (please use the other) 2010/11/19 18:00:39 Respect for modifying the holy base classes. ;-)
+ return a == NULL ? b == NULL : a->Equals(b);
+}
+
+const Value* ExtensionPrefs::WinningExtensionControlledPrefValue(
+ const std::string& key) const {
+ Value *winner = NULL;
+ base::Time winners_install_time = base::Time::FromInternalValue(0);
+
+ ExtensionIdSet extension_ids;
+ GetEnabledExtensions(&extension_ids);
+ for (ExtensionIdSet::iterator ext_id = extension_ids.begin();
+ ext_id != extension_ids.end(); ++ext_id) {
+ base::Time extension_install_time = GetInstallTime(*ext_id);
+
+ // We do not need to consider extensions that were installed before the
+ // most recent extension found that provides the requested preference.
+ if (extension_install_time < winners_install_time)
+ continue;
+
+ DictionaryValue* preferences = GetExtensionControlledPrefs(*ext_id);
+ Value *value = NULL;
+ if (preferences && preferences->GetWithoutPathExpansion(key, &value)) {
+ // This extension is more recent than the last one providing this pref.
+ winner = value;
+ winners_install_time = extension_install_time;
+ }
+ }
+
+ return winner;
+}
+
+void ExtensionPrefs::UpdateWinningPref(const std::string& pref_key) {
+ PrefStore* extensionPrefStore =
+ pref_service()->pref_value_store()->GetExtensionPrefStore();
+ const Value* winningPrefValue = WinningExtensionControlledPrefValue(pref_key);
+ // TODO(battre): the following lines should go into a function of the
+ // PrefStores, which requires that they have a pointer to the pref_notifier.
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 s/PrefStores/InMemoryPrefStore/. I'm not too sure
battre (please use the other) 2010/11/19 18:00:39 Removed the comment. It would be a major refactori
+ Value* oldValue = NULL;
+ extensionPrefStore->prefs()->Get(pref_key, &oldValue);
+ bool changed = !equalValues(winningPrefValue, oldValue);
+
+ if (winningPrefValue) {
+ extensionPrefStore->prefs()->Set(pref_key, winningPrefValue->DeepCopy());
+ } else {
+ extensionPrefStore->prefs()->Remove(pref_key, NULL);
+ }
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 Non need for curlies, but keep them if you like th
battre (please use the other) 2010/11/19 18:00:39 Done.
+
+ if (changed)
+ pref_service()->pref_notifier()->OnPreferenceSet(
+ pref_key.c_str(), PrefNotifier::EXTENSION_STORE);
+}
+
+void ExtensionPrefs::SetExtensionControlledPref(const std::string& extension_id,
+ const std::string& pref_key,
+ Value* value) {
+ DictionaryValue* extensionPreferences =
+ GetExtensionControlledPrefs(extension_id);
+
+ Value* oldValue = NULL;
+ extensionPreferences->GetWithoutPathExpansion(pref_key, &oldValue);
+ bool modified = !equalValues(oldValue, value);
+ if (!modified)
+ return;
+
+ if (value == NULL)
+ extensionPreferences->RemoveWithoutPathExpansion(pref_key, NULL);
+ else
+ extensionPreferences->SetWithoutPathExpansion(pref_key, value);
Mattias Nissler (ping if slow) 2010/11/19 16:52:20 Well, if there are no curlies here, you shouldn't
battre (please use the other) 2010/11/19 18:00:39 Done.
+ pref_service()->ScheduleSavePersistentPrefs();
+
+ UpdateWinningPref(pref_key);
+}
+
+void ExtensionPrefs::GetExtensionControlledPrefKeys(
+ const std::string& extension_id, PrefKeySet *out) const {
+ DCHECK(out != NULL);
+ DictionaryValue* extPrefs = GetExtensionControlledPrefs(extension_id);
+ if (extPrefs) {
+ for (DictionaryValue::key_iterator i = extPrefs->begin_keys();
+ i != extPrefs->end_keys(); ++i) {
+ out->push_back(*i);
+ }
+ }
+}
+
// static
void ExtensionPrefs::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterDictionaryPref(kExtensionsPref);

Powered by Google App Engine
This is Rietveld 408576698