diff options
-rw-r--r-- | chrome/browser/extensions/extension_prefs.cc | 184 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_prefs.h | 85 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 273 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.h | 45 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service_unittest.cc | 6 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master.cc | 10 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master.h | 4 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 | ||||
-rw-r--r-- | chrome/common/notification_type.h | 5 |
9 files changed, 370 insertions, 244 deletions
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc new file mode 100644 index 0000000..d4de0d5 --- /dev/null +++ b/chrome/browser/extensions/extension_prefs.cc @@ -0,0 +1,184 @@ +// Copyright (c) 2009 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 "chrome/browser/extensions/extension_prefs.h" + +#include "base/string_util.h" +#include "chrome/common/extensions/extension.h" + +namespace { + +// Preferences keys + +// A preference that keeps track of per-extension settings. This is a dictionary +// object read from the Preferences file, keyed off of extension id's. +const wchar_t kExtensionsPref[] = L"extensions.settings"; + +// Where an extension was installed from. (see Extension::Location) +const wchar_t kPrefLocation[] = L"location"; + +// Enabled, disabled, killed, etc. (see Extension::State) +const wchar_t kPrefState[] = L"state"; + +// The path to the current version's manifest file. +const wchar_t kPrefPath[] = L"path"; + +// A preference that tracks extension shelf configuration. This is a list +// object read from the Preferences file, containing a list of toolstrip URLs. +const wchar_t kExtensionShelf[] = L"extensions.shelf"; +} + +//////////////////////////////////////////////////////////////////////////////// + +InstalledExtensions::InstalledExtensions(ExtensionPrefs* prefs) { + extension_data_ = prefs->CopyCurrentExtensions(); +} + +void InstalledExtensions::VisitInstalledExtensions( + InstalledExtensions::Callback *callback) { + DictionaryValue::key_iterator extension_id = extension_data_->begin_keys(); + for (; extension_id != extension_data_->end_keys(); ++extension_id) { + DictionaryValue* ext; + if (!extension_data_->GetDictionary(*extension_id, &ext)) { + LOG(WARNING) << "Invalid pref for extension " << *extension_id; + NOTREACHED(); + continue; + } + FilePath::StringType path; + if (!ext->GetString(kPrefPath, &path)) { + LOG(WARNING) << "Missing path pref for extension " << *extension_id; + NOTREACHED(); + continue; + } + int location_value; + if (!ext->GetInteger(kPrefLocation, &location_value)) { + LOG(WARNING) << "Missing location pref for extension " << *extension_id; + NOTREACHED(); + continue; + } + Extension::Location location = + static_cast<Extension::Location>(location_value); + callback->Run(WideToASCII(*extension_id), FilePath(path), location); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +ExtensionPrefs::ExtensionPrefs(PrefService* prefs) : prefs_(prefs) { + if (!prefs_->FindPreference(kExtensionsPref)) + prefs_->RegisterDictionaryPref(kExtensionsPref); + if (!prefs->FindPreference(kExtensionShelf)) + prefs->RegisterListPref(kExtensionShelf); +} + +DictionaryValue* ExtensionPrefs::CopyCurrentExtensions() { + const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref); + if (extensions) { + DictionaryValue* copy = + static_cast<DictionaryValue*>(extensions->DeepCopy()); + return copy; + } + return new DictionaryValue; +} + +void ExtensionPrefs::GetKilledExtensionIds(std::set<std::string>* killed_ids) { + const DictionaryValue* dict = prefs_->GetDictionary(kExtensionsPref); + if (!dict || dict->GetSize() == 0) + return; + + for (DictionaryValue::key_iterator i = dict->begin_keys(); + i != dict->end_keys(); ++i) { + std::wstring key_name = *i; + if (!Extension::IdIsValid(WideToASCII(key_name))) { + LOG(WARNING) << "Invalid external extension ID encountered: " + << WideToASCII(key_name); + continue; + } + + DictionaryValue* extension = NULL; + if (!dict->GetDictionary(key_name, &extension)) { + NOTREACHED(); + continue; + } + + // Check to see if the extension has been killed. + int state; + if (extension->GetInteger(kPrefState, &state) && + state == static_cast<int>(Extension::KILLBIT)) { + StringToLowerASCII(&key_name); + killed_ids->insert(WideToASCII(key_name)); + } + } +} + +ExtensionPrefs::URLList ExtensionPrefs::GetShelfToolstripOrder() { + URLList urls; + const ListValue* toolstrip_urls = prefs_->GetList(kExtensionShelf); + if (toolstrip_urls) { + for (size_t i = 0; i < toolstrip_urls->GetSize(); ++i) { + std::string url; + if (toolstrip_urls->GetString(i, &url)) + urls.push_back(GURL(url)); + } + } + return urls; +} + +void ExtensionPrefs::OnExtensionInstalled(Extension* extension) { + std::string id = extension->id(); + UpdateExtensionPref(id, kPrefState, + Value::CreateIntegerValue(Extension::ENABLED)); + UpdateExtensionPref(id, kPrefLocation, + Value::CreateIntegerValue(extension->location())); + UpdateExtensionPref(id, kPrefPath, + Value::CreateStringValue(extension->path().value())); + prefs_->ScheduleSavePersistentPrefs(); +} + +void ExtensionPrefs::OnExtensionUninstalled(const Extension* extension) { + // For external extensions, we save a preference reminding ourself not to try + // and install the extension anymore. + if (Extension::IsExternalLocation(extension->location())) { + UpdateExtensionPref(extension->id(), kPrefState, + Value::CreateIntegerValue(Extension::KILLBIT)); + prefs_->ScheduleSavePersistentPrefs(); + } else { + DeleteExtensionPrefs(extension->id()); + } +} + +bool ExtensionPrefs::UpdateExtensionPref(const std::string& extension_id, + const std::wstring& key, + Value* data_value) { + DictionaryValue* extension = GetOrCreateExtensionPref(extension_id); + if (!extension->Set(key, data_value)) { + NOTREACHED() << L"Cannot modify key: '" << key.c_str() + << "' for extension: '" << extension_id.c_str() << "'"; + return false; + } + return true; +} + +void ExtensionPrefs::DeleteExtensionPrefs(const std::string& extension_id) { + std::wstring id = ASCIIToWide(extension_id); + DictionaryValue* dict = prefs_->GetMutableDictionary(kExtensionsPref); + if (dict->HasKey(id)) { + dict->Remove(id, NULL); + prefs_->ScheduleSavePersistentPrefs(); + } +} + +DictionaryValue* ExtensionPrefs::GetOrCreateExtensionPref( + const std::string& extension_id) { + DictionaryValue* dict = prefs_->GetMutableDictionary(kExtensionsPref); + DictionaryValue* extension = NULL; + std::wstring id = ASCIIToWide(extension_id); + if (!dict->GetDictionary(id, &extension)) { + // Extension pref does not exist, create it. + extension = new DictionaryValue(); + dict->Set(id, extension); + } + return extension; +} + diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h new file mode 100644 index 0000000..db5847b --- /dev/null +++ b/chrome/browser/extensions/extension_prefs.h @@ -0,0 +1,85 @@ +// Copyright (c) 2009 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 CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_H +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_H + +#include <set> +#include <string> +#include <vector> + +#include "base/task.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/pref_service.h" +#include "googleurl/src/gurl.h" + +// Class for managing global and per-extension preferences. +// This class is instantiated by ExtensionsService, so it should be accessed +// from there. +class ExtensionPrefs { + public: + explicit ExtensionPrefs(PrefService* prefs); + + // Returns a copy of the Extensions prefs. + // TODO(erikkay) Remove this so that external consumers don't need to be + // aware of the internal structure of the preferences. + DictionaryValue* CopyCurrentExtensions(); + + // Populate |killed_ids| with extension ids that have been killed. + void GetKilledExtensionIds(std::set<std::string>* killed_ids); + + // Get the order that toolstrip URLs appear in the shelf. + typedef std::vector<GURL> URLList; + URLList GetShelfToolstripOrder(); + + // Called when an extension is installed, so that prefs get created. + void OnExtensionInstalled(Extension* extension); + + // Called when an extension is uninstalled, so that prefs get cleaned up. + void OnExtensionUninstalled(const Extension* extension); + + private: + // Sets the pref |key| for extension |id| to |value|. + bool UpdateExtensionPref(const std::string& id, + const std::wstring& key, + Value* value); + + // Deletes the pref dictionary for extension |id|. + void DeleteExtensionPrefs(const std::string& id); + + // Ensures and returns a mutable dictionary for extension |id|'s prefs. + DictionaryValue* GetOrCreateExtensionPref(const std::string& id); + + // The pref service specific to this set of extension prefs. + PrefService* prefs_; + + // The URLs of all of the toolstrips. + URLList shelf_order_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefs); +}; + +// A helper class that has a list of the currently installed extensions +// and can iterate over them to a provided callback. +class InstalledExtensions { + public: + explicit InstalledExtensions(ExtensionPrefs* prefs); + + typedef Callback3<const std::string&, + const FilePath&, + Extension::Location>::Type Callback; + + // Runs |callback| for each installed extension with the path to the + // version directory and the location. + void VisitInstalledExtensions(Callback *callback); + + private: + // A copy of the extensions pref dictionary so that this can be passed + // around without a dependency on prefs. + DictionaryValue* extension_data_; + + DISALLOW_COPY_AND_ASSIGN(InstalledExtensions); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_H diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index a39a4cd..2e655d10 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -60,24 +60,6 @@ const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL"; namespace { -//////////////////// -// Preferences keys - -// A preference that keeps track of per-extension settings. This is a dictionary -// object read from the Preferences file, keyed off of extension id's. -const wchar_t kExtensionsPref[] = L"extensions.settings"; - -// Where an extension was installed from (Extension::Location) -const wchar_t kPrefLocation[] = L"location"; - -// Enabled, disabled, killed, etc. (Extension::State) -const wchar_t kPrefState[] = L"state"; - -// The path to the current version's manifest file. -const wchar_t kPrefPath[] = L"path"; - -//////////////////// - // A temporary subdirectory where we unpack extensions. const char* kUnpackExtensionDir = "TEMP_UNPACK"; @@ -231,22 +213,18 @@ class ExtensionsServiceBackend::UnpackerClient ExtensionsService::ExtensionsService(Profile* profile, MessageLoop* frontend_loop, MessageLoop* backend_loop) - : prefs_(profile->GetPrefs()), + : extension_prefs_(new ExtensionPrefs(profile->GetPrefs())), backend_loop_(backend_loop), install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)), extensions_enabled_( CommandLine::ForCurrentProcess()-> HasSwitch(switches::kEnableExtensions)), show_extensions_prompts_(true) { - if (!prefs_->FindPreference(kExtensionsPref)) - prefs_->RegisterDictionaryPref(kExtensionsPref); - // We pass ownership of this object to the Backend. - DictionaryValue* external_extensions = new DictionaryValue; - GetExternalExtensions(external_extensions, NULL); + DictionaryValue* extensions = extension_prefs_->CopyCurrentExtensions(); backend_ = new ExtensionsServiceBackend( install_directory_, g_browser_process->resource_dispatcher_host(), - frontend_loop, external_extensions); + frontend_loop, extensions); } ExtensionsService::~ExtensionsService() { @@ -280,22 +258,12 @@ void ExtensionsService::UninstallExtension(const std::string& extension_id) { Extension* extension = GetExtensionById(extension_id); // Callers should not send us nonexistant extensions. - CHECK(extension); + DCHECK(extension); - Extension::Location location = extension->location(); - - // For external extensions, we save a preference reminding ourself not to try - // and install the extension anymore. - if (Extension::IsExternalLocation(location)) { - UpdateExtensionPref(ASCIIToWide(extension_id), kPrefState, - Value::CreateIntegerValue(Extension::KILLBIT), true); - } else { - DeleteExtensionPrefs(ASCIIToWide(extension_id)); - } + extension_prefs_->OnExtensionUninstalled(extension); // Tell the backend to start deleting installed extensions on the file thread. - if (location == Extension::INTERNAL || - Extension::IsExternalLocation(location)) { + if (Extension::LOAD != extension->location()) { backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(), &ExtensionsServiceBackend::UninstallExtension, extension_id)); } @@ -310,22 +278,17 @@ void ExtensionsService::LoadExtension(const FilePath& extension_path) { } void ExtensionsService::LoadAllExtensions() { - // Load the extensions we know about from previous runs. - const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref); - if (extensions) { - DictionaryValue* copy = - static_cast<DictionaryValue*>(extensions->DeepCopy()); - backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(), - &ExtensionsServiceBackend::LoadExtensionsFromPrefs, - scoped_refptr<ExtensionsService>(this), - copy)); - } + // Load the previously installed extensions. + backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(), + &ExtensionsServiceBackend::LoadInstalledExtensions, + scoped_refptr<ExtensionsService>(this), + new InstalledExtensions(extension_prefs_.get()))); } void ExtensionsService::CheckForUpdates() { // This installs or updates externally provided extensions. std::set<std::string> killed_extensions; - GetExternalExtensions(NULL, &killed_extensions); + extension_prefs_->GetKilledExtensionIds(&killed_extensions); backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates, killed_extensions, @@ -379,6 +342,13 @@ void ExtensionsService::GarbageCollectExtensions() { scoped_refptr<ExtensionsService>(this))); } +void ExtensionsService::OnLoadedInstalledExtensions() { + NotificationService::current()->Notify( + NotificationType::EXTENSIONS_READY, + Source<ExtensionsService>(this), + NotificationService::NoDetails()); +} + void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) { scoped_ptr<ExtensionList> cleanup(new_extensions); @@ -412,28 +382,17 @@ void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) { } } - // TODO(erikkay) it would be nice if we could just return and send no - // notification if the list is empty. However, UserScriptMaster depends on - // getting an initial notification after the first extensions have finished - // loading, so even if it's an empty message, we send it anyway. We should - // come up with a way to do a "started" notification that has the semantics - // that UserScriptMaster is looking for. - - NotificationService::current()->Notify( - NotificationType::EXTENSIONS_LOADED, - NotificationService::AllSources(), - Details<ExtensionList>(&enabled_extensions)); + if (enabled_extensions.size()) { + NotificationService::current()->Notify( + NotificationType::EXTENSIONS_LOADED, + NotificationService::AllSources(), + Details<ExtensionList>(&enabled_extensions)); + } } void ExtensionsService::OnExtensionInstalled(Extension* extension, Extension::InstallType install_type) { - std::wstring id = ASCIIToWide(extension->id()); - UpdateExtensionPref(id, kPrefState, - Value::CreateIntegerValue(Extension::ENABLED), false); - UpdateExtensionPref(id, kPrefLocation, - Value::CreateIntegerValue(extension->location()), false); - UpdateExtensionPref(id, kPrefPath, - Value::CreateStringValue(extension->path().value()), true); + extension_prefs_->OnExtensionInstalled(extension); // If the extension is a theme, tell the profile (and therefore ThemeProvider) // to apply it. @@ -474,60 +433,6 @@ Extension* ExtensionsService::GetExtensionByURL(const GURL& url) { return GetExtensionById(host); } -void ExtensionsService::GetExternalExtensions( - DictionaryValue* external_extensions, - std::set<std::string>* killed_extensions) { - const DictionaryValue* dict = prefs_->GetDictionary(kExtensionsPref); - if (!dict || dict->GetSize() == 0) - return; - - for (DictionaryValue::key_iterator i = dict->begin_keys(); - i != dict->end_keys(); ++i) { - std::wstring key_name = *i; - if (!Extension::IdIsValid(WideToASCII(key_name))) { - LOG(WARNING) << "Invalid external extension ID encountered: " - << WideToASCII(key_name); - continue; - } - - DictionaryValue* extension = NULL; - if (!dict->GetDictionary(key_name, &extension)) { - NOTREACHED(); - continue; - } - - // Check to see if the extension has been killed. - int state; - if (extension->GetInteger(kPrefState, &state) && - state == static_cast<int>(Extension::KILLBIT)) { - if (killed_extensions) { - StringToLowerASCII(&key_name); - killed_extensions->insert(WideToASCII(key_name)); - } - } - // Return all extensions found. - if (external_extensions) { - DictionaryValue* result = - static_cast<DictionaryValue*>(extension->DeepCopy()); - StringToLowerASCII(&key_name); - external_extensions->Set(key_name, result); - } - } -} - -DictionaryValue* ExtensionsService::GetOrCreateExtensionPref( - const std::wstring& extension_id) { - DictionaryValue* dict = prefs_->GetMutableDictionary(kExtensionsPref); - DictionaryValue* extension = NULL; - if (!dict->GetDictionary(extension_id, &extension)) { - // Extension pref does not exist, create it. - extension = new DictionaryValue(); - dict->Set(extension_id, extension); - } - - return extension; -} - void ExtensionsService::ClearProvidersForTesting() { backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting)); @@ -540,32 +445,6 @@ void ExtensionsService::SetProviderForTesting( location, test_provider)); } -bool ExtensionsService::UpdateExtensionPref(const std::wstring& extension_id, - const std::wstring& key, - Value* data_value, - bool schedule_save) { - DictionaryValue* extension = GetOrCreateExtensionPref(extension_id); - if (!extension->Set(key, data_value)) { - NOTREACHED() << L"Cannot modify key: '" << key.c_str() - << "' for extension: '" << extension_id.c_str() << "'"; - return false; - } - - if (schedule_save) - prefs_->ScheduleSavePersistentPrefs(); - return true; -} - -void ExtensionsService::DeleteExtensionPrefs( - const std::wstring& extension_id) { - DictionaryValue* dict = prefs_->GetMutableDictionary(kExtensionsPref); - if (dict->HasKey(extension_id)) { - dict->Remove(extension_id, NULL); - prefs_->ScheduleSavePersistentPrefs(); - } -} - - // ExtensionsServicesBackend ExtensionsServiceBackend::ExtensionsServiceBackend( @@ -589,53 +468,20 @@ ExtensionsServiceBackend::ExtensionsServiceBackend( ExtensionsServiceBackend::~ExtensionsServiceBackend() { } -void ExtensionsServiceBackend::LoadExtensionsFromPrefs( +void ExtensionsServiceBackend::LoadInstalledExtensions( scoped_refptr<ExtensionsService> frontend, - DictionaryValue* extension_prefs) { - scoped_ptr<DictionaryValue> prefs(extension_prefs); // for cleanup + InstalledExtensions* installed) { + scoped_ptr<InstalledExtensions> cleanup(installed); frontend_ = frontend; alert_on_error_ = false; - scoped_ptr<ExtensionList> extensions(new ExtensionList); - DictionaryValue::key_iterator extension_id = extension_prefs->begin_keys(); - for (; extension_id != extension_prefs->end_keys(); ++extension_id) { - DictionaryValue* ext; - if (!extension_prefs->GetDictionary(*extension_id, &ext)) { - NOTREACHED(); - continue; - } - FilePath::StringType path; - if (ext->GetString(kPrefPath, &path)) { - Extension::Location location = Extension::INVALID; - int location_value; - DictionaryValue* pref = NULL; - extension_prefs->GetDictionary(*extension_id, &pref); - if (!pref || !pref->GetInteger(kPrefLocation, &location_value)) { - // TODO(erikkay) try to recover? - continue; - } else { - location = static_cast<Extension::Location>(location_value); - } - std::string id = WideToASCII(*extension_id); - if (Extension::IsExternalLocation(location) && - CheckExternalUninstall(extension_prefs, FilePath(path), id)) { - // TODO(erikkay): Possibly defer this operation to avoid slowing initial - // load of extensions. - UninstallExtension(id); - - // No error needs to be reported. The extension effectively doesn't - // exist. - continue; - } - Extension* extension = - LoadExtension(FilePath(path), location, true); // require id - if (extension) - extensions->push_back(extension); - } else { - // TODO(erikkay) bootstrap? - } - } - LOG(INFO) << "Done."; - ReportExtensionsLoaded(extensions.release()); + + // Call LoadInstalledExtension for each extension |installed| knows about. + scoped_ptr<InstalledExtensions::Callback> callback( + NewCallback(this, &ExtensionsServiceBackend::LoadInstalledExtension)); + installed->VisitInstalledExtensions(callback.get()); + + frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod( + frontend_, &ExtensionsService::OnLoadedInstalledExtensions)); } void ExtensionsServiceBackend::GarbageCollectExtensions( @@ -708,7 +554,7 @@ void ExtensionsServiceBackend::LoadSingleExtension( Extension* extension = LoadExtension(extension_path, Extension::LOAD, - false); // don't require ID + false); // Don't require id. if (extension) { ExtensionList* extensions = new ExtensionList; extensions->push_back(extension); @@ -716,6 +562,28 @@ void ExtensionsServiceBackend::LoadSingleExtension( } } +void ExtensionsServiceBackend::LoadInstalledExtension( + const std::string& id, const FilePath& path, Extension::Location location) { + if (CheckExternalUninstall(id, location)) { + // TODO(erikkay): Possibly defer this operation to avoid slowing initial + // load of extensions. + UninstallExtension(id); + + // No error needs to be reported. The extension effectively doesn't exist. + return; + } + + Extension* extension = + LoadExtension(FilePath(path), location, true); // Require id. + + // TODO(erikkay) now we only report a single extension loaded at a time. + // Perhaps we should change the notifications to remove ExtensionList. + ExtensionList* extensions = new ExtensionList; + if (extension) + extensions->push_back(extension); + ReportExtensionsLoaded(extensions); +} + DictionaryValue* ExtensionsServiceBackend::ReadManifest(FilePath manifest_path, std::string* error) { JSONFileValueSerializer serializer(manifest_path); @@ -1331,23 +1199,7 @@ void ExtensionsServiceBackend::CheckForExternalUpdates( } bool ExtensionsServiceBackend::CheckExternalUninstall( - const DictionaryValue* extension_prefs, const FilePath& version_path, - const std::string& id) { - // First check the preferences for the kill-bit. - int location_value = Extension::INVALID; - DictionaryValue* extension = NULL; - if (!extension_prefs->GetDictionary(ASCIIToWide(id), &extension)) - return false; - int state; - if (extension->GetInteger(kPrefLocation, &location_value) && - location_value == Extension::EXTERNAL_PREF) { - return extension->GetInteger(kPrefState, &state) && - state == Extension::KILLBIT; - } - - Extension::Location location = - static_cast<Extension::Location>(location_value); - + const std::string& id, Extension::Location location) { // Check if the providers know about this extension. ProviderMap::const_iterator i = external_extension_providers_.find(location); if (i != external_extension_providers_.end()) { @@ -1355,6 +1207,9 @@ bool ExtensionsServiceBackend::CheckExternalUninstall( version.reset(i->second->RegisteredVersion(id, NULL)); if (version.get()) return false; // Yup, known extension, don't uninstall. + } else { + // Not from an external provider, so it's fine. + return false; } return true; // This is not a known extension, uninstall. diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index 8bf49f8..d8fa0d0 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -15,6 +15,7 @@ #include "base/ref_counted.h" #include "base/tuple.h" #include "base/values.h" +#include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/external_extension_provider.h" #include "chrome/common/extensions/extension.h" @@ -120,29 +121,6 @@ class ExtensionsService // Lookup an extension by |url|. This uses the host of the URL as the id. Extension* GetExtensionByURL(const GURL& url); - // Gets a list of external extensions. If |external_extensions| is non-null, - // a dictionary with all external extensions (including extensions installed - // through the registry on Windows builds) and their preferences are - // returned. If |killed_extensions| is non-null, a set of string IDs - // containing all external extension IDs with the killbit set are returned. - void GetExternalExtensions(DictionaryValue* external_extensions, - std::set<std::string>* killed_extensions); - - // Gets the settings for an extension from preferences. If the key doesn't - // exist, this function creates it (don't need to check return for NULL). - DictionaryValue* GetOrCreateExtensionPref(const std::wstring& extension_id); - - // Writes a preference value for a particular extension |extension_id| under - // the |key| specified. If |schedule_save| is true, it will also ask the - // preference system to schedule a save to disk. - bool UpdateExtensionPref(const std::wstring& extension_id, - const std::wstring& key, - Value* data_value, - bool schedule_save); - - // Removes the entire tree of prefs for |extension_id|. - void DeleteExtensionPrefs(const std::wstring& extension_id); - // Clear all ExternalExtensionProviders. void ClearProvidersForTesting(); @@ -169,6 +147,9 @@ class ExtensionsService // OnExtensionVersionReinstalled. friend class ExtensionsServiceBackend; + // Called by the backend when the initial extension load has completed. + void OnLoadedInstalledExtensions(); + // Called by the backend when extensions have been loaded. void OnExtensionsLoaded(ExtensionList* extensions); @@ -185,7 +166,7 @@ class ExtensionsService static const char* kInstallDirectoryName; // Preferences for the owning profile. - PrefService* prefs_; + scoped_ptr<ExtensionPrefs> extension_prefs_; // The message loop to use with the backend. MessageLoop* backend_loop_; @@ -224,12 +205,11 @@ class ExtensionsServiceBackend virtual ~ExtensionsServiceBackend(); - // Loads extensions from the prefs. + // Loads the installed extensions. // Errors are reported through ExtensionErrorReporter. On completion, // OnExtensionsLoaded() is called with any successfully loaded extensions. - void LoadExtensionsFromPrefs( - scoped_refptr<ExtensionsService> frontend, - DictionaryValue* extension_prefs); + void LoadInstalledExtensions(scoped_refptr<ExtensionsService> frontend, + InstalledExtensions* installed); // Scans the extension installation directory to look for partially installed // or extensions to uninstall. @@ -278,6 +258,10 @@ class ExtensionsServiceBackend class UnpackerClient; friend class UnpackerClient; + // Loads a single installed extension. + void LoadInstalledExtension(const std::string& id, const FilePath& path, + Extension::Location location); + // Utility function to read an extension manifest and return it as a // DictionaryValue. If it fails, NULL is returned and |error| contains an // appropriate message. @@ -379,9 +363,8 @@ class ExtensionsServiceBackend // For the extension in |version_path| with |id|, check to see if it's an // externally managed extension. If so return true if it should be // uninstalled. - bool CheckExternalUninstall(const DictionaryValue* extension_prefs, - const FilePath& version_path, - const std::string& id); + bool CheckExternalUninstall(const std::string& id, + Extension::Location location); // Should an extension of |id| and |version| be installed? // Returns true if no extension of type |id| is installed or if |version| diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc index b3dfe7e..2a4c104 100644 --- a/chrome/browser/extensions/extensions_service_unittest.cc +++ b/chrome/browser/extensions/extensions_service_unittest.cc @@ -999,9 +999,9 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) { ValidatePref(good_crx, L"state", Extension::ENABLED); ValidatePref(good_crx, L"location", Extension::EXTERNAL_PREF); - // Now set the kill bit and watch the extension go away. - SetPref(good_crx, L"state", Extension::KILLBIT); - profile_->GetPrefs()->ScheduleSavePersistentPrefs(); + // Now uninstall the extension and verify that it doesn't get reinstalled. + service_->UninstallExtension(id); + loop_.RunAllPending(); loaded_.clear(); service_->ReloadExtensions(); diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index 5cf3696..0c1a4b0 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -259,10 +259,13 @@ UserScriptMaster::UserScriptMaster(MessageLoop* worker_loop, const FilePath& script_dir) : user_script_dir_(script_dir), worker_loop_(worker_loop), + extensions_service_ready_(false), pending_scan_(false) { if (!user_script_dir_.value().empty()) AddWatchedPath(script_dir); + registrar_.Add(this, NotificationType::EXTENSIONS_READY, + NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSIONS_LOADED, NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, @@ -327,6 +330,10 @@ void UserScriptMaster::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type.value) { + case NotificationType::EXTENSIONS_READY: + extensions_service_ready_ = true; + StartScan(); + break; case NotificationType::EXTENSIONS_LOADED: { // TODO(aa): Fix race here. A page could need a content script on startup, // before the extension has loaded. We need to freeze the renderer in @@ -344,7 +351,8 @@ void UserScriptMaster::Observe(NotificationType type, lone_scripts_.push_back(*iter); } } - StartScan(); + if (!extensions_service_ready_) + StartScan(); break; } diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h index 7a73323..ab87631 100644 --- a/chrome/browser/extensions/user_script_master.h +++ b/chrome/browser/extensions/user_script_master.h @@ -148,6 +148,10 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, // List of scripts outside of script directories we should also load. UserScriptList lone_scripts_; + // If the extensions service has finished loading its initial set of + // extensions. + bool extensions_service_ready_; + // If the script directory is modified while we're rescanning it, we note // that we're currently mid-scan and then start over again once the scan // finishes. This boolean tracks whether another scan is pending. diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 49b2e56..7d198af 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -870,6 +870,8 @@ 'browser/extensions/extension_page_actions_module.h', 'browser/extensions/extension_page_actions_module_constants.cc', 'browser/extensions/extension_page_actions_module_constants.h', + 'browser/extensions/extension_prefs.cc', + 'browser/extensions/extension_prefs.h', 'browser/extensions/extension_process_manager.cc', 'browser/extensions/extension_process_manager.h', 'browser/extensions/extension_protocols.cc', diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h index 678c942..e6efd5d 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -563,6 +563,11 @@ class NotificationType { // Extensions -------------------------------------------------------------- + // Sent when the known installed extensions have all been loaded. In + // testing scenarios this can happen multiple times if extensions are + // unloaded and reloaded. + EXTENSIONS_READY, + // Sent when new extensions are loaded. The details are an ExtensionList*. EXTENSIONS_LOADED, |