summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/extensions_service.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/extensions/extensions_service.cc')
-rw-r--r--chrome/browser/extensions/extensions_service.cc288
1 files changed, 148 insertions, 140 deletions
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 23c37fe..db14670 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -21,13 +21,12 @@
#include "chrome/browser/extensions/extension_browser_event_router.h"
#include "chrome/browser/extensions/extension_error_reporter.h"
#include "chrome/browser/extensions/extension_process_manager.h"
-#include "chrome/browser/extensions/user_script_master.h"
-#include "chrome/browser/plugin_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/utility_process_host.h"
#include "chrome/common/extensions/extension_unpacker.h"
#include "chrome/common/json_value_serializer.h"
#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_service.h"
#include "chrome/common/unzip.h"
#include "chrome/common/url_constants.h"
@@ -55,10 +54,17 @@ struct ExtensionHeader {
const size_t kZipHashBytes = 32; // SHA-256
const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size.
-#if defined(OS_WIN)
+// A preference that keeps track of external extensions the user has
+// uninstalled.
+const wchar_t kUninstalledExternalPref[] =
+ L"extensions.uninstalled_external_ids";
// Registry key where registry defined extension installers live.
-const wchar_t kRegistryExtensions[] = L"Software\\Google\\Chrome\\Extensions";
+// TODO(port): Assuming this becomes a similar key into the appropriate
+// platform system.
+const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";
+
+#if defined(OS_WIN)
// Registry value of of that key that defines the path to the .crx file.
const wchar_t kRegistryExtensionPath[] = L"path";
@@ -178,12 +184,16 @@ class ExtensionsServiceBackend::UnpackerClient
};
ExtensionsService::ExtensionsService(Profile* profile,
- UserScriptMaster* user_script_master)
- : message_loop_(MessageLoop::current()),
+ MessageLoop* frontend_loop,
+ MessageLoop* backend_loop,
+ const std::string& registry_path)
+ : prefs_(profile->GetPrefs()),
+ backend_loop_(backend_loop),
install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)),
backend_(new ExtensionsServiceBackend(
- install_directory_, g_browser_process->resource_dispatcher_host())),
- user_script_master_(user_script_master) {
+ install_directory_, g_browser_process->resource_dispatcher_host(),
+ frontend_loop, registry_path)) {
+ prefs_->RegisterListPref(kUninstalledExternalPref);
}
ExtensionsService::~ExtensionsService() {
@@ -198,39 +208,42 @@ bool ExtensionsService::Init() {
ExtensionBrowserEventRouter::GetInstance()->Init();
#if defined(OS_WIN)
- // TODO(port): ExtensionsServiceBackend::CheckForExternalUpdates depends on
- // the Windows registry.
+
+ std::set<std::string> uninstalled_external_ids;
+ const ListValue* list = prefs_->GetList(kUninstalledExternalPref);
+ if (list) {
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ std::string val;
+ bool ok = list->GetString(i, &val);
+ DCHECK(ok);
+ DCHECK(uninstalled_external_ids.find(val) ==
+ uninstalled_external_ids.end());
+ uninstalled_external_ids.insert(val);
+ }
+ }
+
// TODO(erikkay): Should we monitor the registry during run as well?
- g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(backend_.get(),
- &ExtensionsServiceBackend::CheckForExternalUpdates,
- scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
+ backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
+ &ExtensionsServiceBackend::CheckForExternalUpdates,
+ uninstalled_external_ids, scoped_refptr<ExtensionsService>(this)));
+#else
+
+ // TODO(port)
+
#endif
- // TODO(aa): This message loop should probably come from a backend
- // interface, similar to how the message loop for the frontend comes
- // from the frontend interface.
- g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(backend_.get(),
- &ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory,
- scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
+ backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
+ &ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory,
+ scoped_refptr<ExtensionsService>(this)));
return true;
}
-MessageLoop* ExtensionsService::GetMessageLoop() {
- return message_loop_;
-}
-
void ExtensionsService::InstallExtension(const FilePath& extension_path) {
- // TODO(aa): This message loop should probably come from a backend
- // interface, similar to how the message loop for the frontend comes
- // from the frontend interface.
- g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(backend_.get(),
- &ExtensionsServiceBackend::InstallExtension,
- extension_path,
- scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
+ backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
+ &ExtensionsServiceBackend::InstallExtension,
+ extension_path,
+ scoped_refptr<ExtensionsService>(this)));
}
void ExtensionsService::UninstallExtension(const std::string& extension_id) {
@@ -244,70 +257,48 @@ void ExtensionsService::UninstallExtension(const std::string& extension_id) {
}
}
+ // Callers should not send us nonexistant extensions.
+ CHECK(extension);
+
// Remove the extension from our list.
extensions_.erase(iter);
- // Callers should check existence and that the extension is internal before
- // calling us.
- DCHECK(extension);
- DCHECK(extension->is_uninstallable());
-
// Tell other services the extension is gone.
NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
NotificationService::AllSources(),
Details<Extension>(extension));
- // Tell the file thread to start deleting.
- g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(backend_.get(),
- &ExtensionsServiceBackend::UninstallExtension, extension_id));
+ // For external extensions, we save a preference reminding ourself not to try
+ // and install the extension anymore.
+ if (extension->location() == Extension::EXTERNAL) {
+ ListValue* list = prefs_->GetMutableList(kUninstalledExternalPref);
+ list->Append(Value::CreateStringValue(extension->id()));
+ prefs_->ScheduleSavePersistentPrefs();
+ }
+
+ // Tell the backend to start deleting installed extensions on the file thread.
+ if (extension->location() == Extension::INTERNAL ||
+ extension->location() == Extension::EXTERNAL) {
+ backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
+ &ExtensionsServiceBackend::UninstallExtension, extension_id));
+ }
delete extension;
}
void ExtensionsService::LoadExtension(const FilePath& extension_path) {
- g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(backend_.get(),
- &ExtensionsServiceBackend::LoadSingleExtension,
- extension_path,
- scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
+ backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
+ &ExtensionsServiceBackend::LoadSingleExtension,
+ extension_path, scoped_refptr<ExtensionsService>(this)));
}
void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
extensions_.insert(extensions_.end(), new_extensions->begin(),
new_extensions->end());
-
- // TODO: Fix race here. A page could need a user script on startup, before
- // the user script is loaded. We need to freeze the renderer in that case.
- // TODO(mpcomplete): We also need to force a renderer to refresh its cache of
- // the plugin list when we inject user scripts, since it could have a stale
- // version by the time extensions are loaded.
-
- for (ExtensionList::iterator extension = extensions_.begin();
- extension != extensions_.end(); ++extension) {
- // Tell NPAPI about any plugins in the loaded extensions.
- if (!(*extension)->plugins_dir().empty()) {
- PluginService::GetInstance()->AddExtraPluginDir(
- (*extension)->plugins_dir());
- }
-
- // Tell UserScriptMaster about any scripts in the loaded extensions.
- const UserScriptList& scripts = (*extension)->content_scripts();
- for (UserScriptList::const_iterator script = scripts.begin();
- script != scripts.end(); ++script) {
- user_script_master_->AddLoneScript(*script);
- }
- }
-
- // Since user scripts may have changed, tell UserScriptMaster to kick off
- // a scan.
- user_script_master_->StartScan();
-
NotificationService::current()->Notify(
NotificationType::EXTENSIONS_LOADED,
NotificationService::AllSources(),
Details<ExtensionList>(new_extensions));
-
delete new_extensions;
}
@@ -350,8 +341,21 @@ Extension* ExtensionsService::GetExtensionByID(std::string id) {
// ExtensionsServicesBackend
+ExtensionsServiceBackend::ExtensionsServiceBackend(
+ const FilePath& install_directory, ResourceDispatcherHost* rdh,
+ MessageLoop* frontend_loop, const std::string& registry_path)
+ : install_directory_(install_directory),
+ resource_dispatcher_host_(rdh),
+ frontend_loop_(frontend_loop),
+ registry_path_(registry_path) {
+ // Default the registry path if unspecified.
+ if (registry_path_.empty()) {
+ registry_path_ = kRegistryExtensions;
+ }
+}
+
void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+ scoped_refptr<ExtensionsService> frontend) {
frontend_ = frontend;
alert_on_error_ = false;
@@ -392,7 +396,25 @@ void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
extension_path = enumerator.Next()) {
std::string extension_id = WideToASCII(
extension_path.BaseName().ToWStringHack());
- if (CheckExternalUninstall(extension_path, extension_id)) {
+
+ // If there is no Current Version file, just delete the directory and move
+ // on. This can legitimately happen when an uninstall does not complete, for
+ // example, when a plugin is in use at uninstall time.
+ FilePath current_version_path = extension_path.AppendASCII(
+ ExtensionsService::kCurrentVersionFileName);
+ if (!file_util::PathExists(current_version_path)) {
+ LOG(INFO) << "Deleting incomplete install for directory "
+ << WideToASCII(extension_path.ToWStringHack()) << ".";
+ file_util::Delete(extension_path, true); // recursive;
+ continue;
+ }
+
+ std::string current_version;
+ if (!ReadCurrentVersion(extension_path, &current_version))
+ continue;
+
+ FilePath version_path = extension_path.AppendASCII(current_version);
+ if (CheckExternalUninstall(version_path, extension_id)) {
// TODO(erikkay): Possibly defer this operation to avoid slowing initial
// load of extensions.
UninstallExtension(extension_id);
@@ -402,7 +424,7 @@ void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
continue;
}
- Extension* extension = LoadExtensionCurrentVersion(extension_path);
+ Extension* extension = LoadExtension(version_path, true); // require id
if (extension)
extensions->push_back(extension);
}
@@ -412,8 +434,7 @@ void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
}
void ExtensionsServiceBackend::LoadSingleExtension(
- const FilePath& path_in,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+ const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
frontend_ = frontend;
// Explicit UI loads are always noisy.
@@ -436,24 +457,6 @@ void ExtensionsServiceBackend::LoadSingleExtension(
}
}
-Extension* ExtensionsServiceBackend::LoadExtensionCurrentVersion(
- const FilePath& extension_path) {
- std::string version_str;
- if (!ReadCurrentVersion(extension_path, &version_str)) {
- ReportExtensionLoadError(extension_path,
- StringPrintf("Could not read '%s' file.",
- ExtensionsService::kCurrentVersionFileName));
- return NULL;
- }
-
- LOG(INFO) << " " <<
- WideToASCII(extension_path.BaseName().ToWStringHack()) <<
- " version: " << version_str;
-
- return LoadExtension(extension_path.AppendASCII(version_str),
- true); // require id
-}
-
Extension* ExtensionsServiceBackend::LoadExtension(
const FilePath& extension_path, bool require_id) {
FilePath manifest_path =
@@ -529,10 +532,8 @@ void ExtensionsServiceBackend::ReportExtensionLoadError(
void ExtensionsServiceBackend::ReportExtensionsLoaded(
ExtensionList* extensions) {
- frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
- frontend_,
- &ExtensionsServiceFrontendInterface::OnExtensionsLoaded,
- extensions));
+ frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
}
// The extension file format is a header, followed by the manifest, followed
@@ -775,8 +776,7 @@ bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
}
void ExtensionsServiceBackend::InstallExtension(
- const FilePath& extension_path,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+ const FilePath& extension_path, scoped_refptr<ExtensionsService> frontend) {
LOG(INFO) << "Installing extension " << extension_path.value();
frontend_ = frontend;
@@ -855,7 +855,27 @@ void ExtensionsServiceBackend::OnExtensionUnpacked(
file_util::WriteFile(marker, NULL, 0);
}
- ReportExtensionInstalled(dest_dir, was_update);
+ // Load the extension immediately and then report installation success. We
+ // don't load extensions for external installs because external installation
+ // occurs before the normal startup so we just let startup pick them up. We
+ // don't notify installation because there is no UI or external install so
+ // there is nobody to notify.
+ if (!from_external) {
+ Extension* extension = LoadExtension(version_dir, true); // require id
+ CHECK(extension);
+
+ frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend_, &ExtensionsService::OnExtensionInstalled, extension,
+ was_update));
+
+ // Only one extension, but ReportExtensionsLoaded can handle multiple,
+ // so we need to construct a list.
+ scoped_ptr<ExtensionList> extensions(new ExtensionList);
+ extensions->push_back(extension);
+ LOG(INFO) << "Done.";
+ // Hand off ownership of the loaded extensions to the frontend.
+ ReportExtensionsLoaded(extensions.release());
+ }
}
void ExtensionsServiceBackend::ReportExtensionInstallError(
@@ -871,31 +891,8 @@ void ExtensionsServiceBackend::ReportExtensionInstallError(
void ExtensionsServiceBackend::ReportExtensionVersionReinstalled(
const std::string& id) {
- frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
- frontend_,
- &ExtensionsServiceFrontendInterface::OnExtensionVersionReinstalled,
- id));
-}
-
-void ExtensionsServiceBackend::ReportExtensionInstalled(
- const FilePath& path, bool update) {
- // After it's installed, load it right away with the same settings.
- Extension* extension = LoadExtensionCurrentVersion(path);
- CHECK(extension);
-
- frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
- frontend_,
- &ExtensionsServiceFrontendInterface::OnExtensionInstalled,
- extension,
- update));
-
- // Only one extension, but ReportExtensionsLoaded can handle multiple,
- // so we need to construct a list.
- scoped_ptr<ExtensionList> extensions(new ExtensionList);
- extensions->push_back(extension);
- LOG(INFO) << "Done.";
- // Hand off ownership of the loaded extensions to the frontend.
- ReportExtensionsLoaded(extensions.release());
+ frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend_, &ExtensionsService::OnExtensionVersionReinstalled, id));
}
// Some extensions will autoupdate themselves externally from Chrome. These
@@ -905,7 +902,8 @@ void ExtensionsServiceBackend::ReportExtensionInstalled(
// check that location for a .crx file, which it will then install locally if
// a new version is available.
void ExtensionsServiceBackend::CheckForExternalUpdates(
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+ std::set<std::string> ids_to_ignore,
+ scoped_refptr<ExtensionsService> frontend) {
// Note that this installation is intentionally silent (since it didn't
// go through the front-end). Extensions that are registered in this
@@ -919,16 +917,23 @@ void ExtensionsServiceBackend::CheckForExternalUpdates(
// TODO(port): Pull this out into an interface. That will also allow us to
// test the behavior of external extensions.
HKEY reg_root = HKEY_LOCAL_MACHINE;
- RegistryKeyIterator iterator(reg_root, kRegistryExtensions);
+ RegistryKeyIterator iterator(reg_root, ASCIIToWide(registry_path_).c_str());
while (iterator.Valid()) {
+ // Fold
+ std::string id = StringToLowerASCII(WideToASCII(iterator.Name()));
+ if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
+ LOG(INFO) << "Skipping uninstalled external extension " << id;
+ ++iterator;
+ continue;
+ }
+
RegKey key;
- std::wstring key_path = kRegistryExtensions;
+ std::wstring key_path = ASCIIToWide(registry_path_);
key_path.append(L"\\");
key_path.append(iterator.Name());
if (key.Open(reg_root, key_path.c_str())) {
std::wstring extension_path;
if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
- std::string id = WideToASCII(iterator.Name());
std::wstring extension_version;
if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
if (ShouldInstall(id, WideToASCII(extension_version))) {
@@ -954,21 +959,21 @@ void ExtensionsServiceBackend::CheckForExternalUpdates(
#endif
}
-bool ExtensionsServiceBackend::CheckExternalUninstall(const FilePath& path,
+bool ExtensionsServiceBackend::CheckExternalUninstall(const FilePath& version_path,
const std::string& id) {
- FilePath external_file = path.AppendASCII(kExternalInstallFile);
+ FilePath external_file = version_path.AppendASCII(kExternalInstallFile);
if (file_util::PathExists(external_file)) {
#if defined(OS_WIN)
HKEY reg_root = HKEY_LOCAL_MACHINE;
RegKey key;
- std::wstring key_path = kRegistryExtensions;
+ std::wstring key_path = ASCIIToWide(registry_path_);
key_path.append(L"\\");
key_path.append(ASCIIToWide(id));
// If the key doesn't exist, then we should uninstall.
return !key.Open(reg_root, key_path.c_str());
#else
- NOTREACHED();
+ // TODO(port)
#endif
}
return false;
@@ -999,7 +1004,10 @@ void ExtensionsServiceBackend::UninstallExtension(
}
}
- // Ok, now try and delete the entire rest of the directory.
+ // Ok, now try and delete the entire rest of the directory. One major place
+ // this can fail is if the extension contains a plugin (stupid plugins). It's
+ // not a big deal though, because we'll notice next time we startup that the
+ // Current Version file is gone and finish the delete then.
if (!file_util::Delete(extension_directory, true)) {
LOG(WARNING) << "Could not delete directory for extension "
<< extension_id;
@@ -1011,7 +1019,7 @@ bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
FilePath dir(install_directory_.AppendASCII(id.c_str()));
std::string current_version;
if (ReadCurrentVersion(dir, &current_version)) {
- return !CheckCurrentVersion(version, current_version, dir);
+ return CheckCurrentVersion(version, current_version, dir);
}
return true;
}