summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
authorerikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-26 00:04:15 +0000
committererikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-26 00:04:15 +0000
commitb0beaa66afc6a6127440a65b56ce3967c8852e40 (patch)
treec1d7b3e24dcdaa82d9893e7c71432b9b110d3530 /chrome/browser/extensions
parent15fda7175c8ba31974d87099f0cf01eb0281bd1d (diff)
downloadchromium_src-b0beaa66afc6a6127440a65b56ce3967c8852e40.zip
chromium_src-b0beaa66afc6a6127440a65b56ce3967c8852e40.tar.gz
chromium_src-b0beaa66afc6a6127440a65b56ce3967c8852e40.tar.bz2
Auto install and update extensions from a registry defined location. This allows developers to install a chrome extension and manage its update process completely indepdendently of Chrome's built-in mechanism.
Review URL: http://codereview.chromium.org/28040 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10412 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r--chrome/browser/extensions/extensions_service.cc592
-rw-r--r--chrome/browser/extensions/extensions_service.h135
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc31
3 files changed, 488 insertions, 270 deletions
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 17c5afe..1446e7b 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -19,7 +19,9 @@
#include "chrome/common/json_value_serializer.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/unzip.h"
+
#if defined(OS_WIN)
+#include "base/registry.h"
#include "chrome/common/win_util.h"
#endif
@@ -28,8 +30,10 @@
const char* ExtensionsService::kInstallDirectoryName = "Extensions";
const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL";
+
+namespace {
// Chromium Extension magic number
-static const char kExtensionFileMagic[] = "Cr24";
+const char kExtensionFileMagic[] = "Cr24";
struct ExtensionHeader {
char magic[sizeof(kExtensionFileMagic) - 1];
@@ -41,6 +45,25 @@ struct ExtensionHeader {
const size_t kZipHashBytes = 32; // SHA-256
const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size.
+#if defined(OS_WIN)
+
+// Registry key where registry defined extension installers live.
+const wchar_t kRegistryExtensions[] = L"Software\\Google\\Chrome\\Extensions";
+
+// Registry value of of that key that defines the path to the .crx file.
+const wchar_t kRegistryExtensionPath[] = L"path";
+
+// Registry value of that key that defines the current version of the .crx file.
+const wchar_t kRegistryExtensionVersion[] = L"version";
+
+#endif
+
+// A marker file to indicate that an extension was installed from an external
+// source.
+const char kExternalInstallFile[] = "EXTERNAL_INSTALL";
+}
+
+
ExtensionsService::ExtensionsService(const FilePath& profile_directory,
UserScriptMaster* user_script_master)
: message_loop_(MessageLoop::current()),
@@ -57,6 +80,18 @@ ExtensionsService::~ExtensionsService() {
}
bool ExtensionsService::Init() {
+#if defined(OS_WIN)
+
+ // TODO(port): ExtensionsServiceBackend::CheckForExternalUpdates depends on
+ // the Windows registry.
+ // 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,
+ install_directory_,
+ scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
+#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.
@@ -65,7 +100,6 @@ bool ExtensionsService::Init() {
&ExtensionsServiceBackend::LoadExtensionsFromDirectory,
install_directory_,
scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
- // TODO(aa): Load extensions from other registered directories.
return true;
}
@@ -83,6 +117,7 @@ void ExtensionsService::InstallExtension(const FilePath& extension_path) {
&ExtensionsServiceBackend::InstallExtension,
extension_path,
install_directory_,
+ true, // alert_on_error
scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
}
@@ -135,115 +170,125 @@ void ExtensionsService::OnExtensionsLoadedFromDirectory(
delete new_extensions;
}
-void ExtensionsService::OnExtensionLoadError(const std::string& error) {
+void ExtensionsService::OnExtensionLoadError(bool alert_on_error,
+ const std::string& error) {
// TODO(aa): Print the error message out somewhere better. I think we are
// going to need some sort of 'extension inspector'.
LOG(WARNING) << error;
+ if (alert_on_error) {
#if defined(OS_WIN)
- win_util::MessageBox(NULL, UTF8ToWide(error),
- L"Extension load error", MB_OK | MB_SETFOREGROUND);
+ win_util::MessageBox(NULL, UTF8ToWide(error),
+ L"Extension load error", MB_OK | MB_SETFOREGROUND);
#endif
+ }
}
-void ExtensionsService::OnExtensionInstallError(const std::string& error) {
+void ExtensionsService::OnExtensionInstallError(bool alert_on_error,
+ const std::string& error) {
// TODO(erikkay): Print the error message out somewhere better.
LOG(WARNING) << error;
+ if (alert_on_error) {
#if defined(OS_WIN)
- win_util::MessageBox(NULL, UTF8ToWide(error),
- L"Extension load error", MB_OK | MB_SETFOREGROUND);
+ win_util::MessageBox(NULL, UTF8ToWide(error),
+ L"Extension load error", MB_OK | MB_SETFOREGROUND);
#endif
+ }
}
-void ExtensionsService::OnExtensionInstalled(FilePath path) {
+void ExtensionsService::OnExtensionInstalled(FilePath path, bool update) {
NotificationService::current()->Notify(
NotificationType::EXTENSION_INSTALLED,
NotificationService::AllSources(),
Details<FilePath>(&path));
- // Immediately try to load the extension.
- g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(backend_.get(),
- &ExtensionsServiceBackend::LoadSingleExtension,
- path,
- scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
+ // TODO(erikkay): Update UI if appropriate.
}
// ExtensionsServicesBackend
-bool ExtensionsServiceBackend::LoadExtensionsFromDirectory(
+void ExtensionsServiceBackend::LoadExtensionsFromDirectory(
const FilePath& path_in,
scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
FilePath path = path_in;
-
- // Create the <Profile>/Extensions directory if it doesn't exist.
- if (!file_util::DirectoryExists(path))
- file_util::CreateDirectory(path);
+ frontend_ = frontend;
+ alert_on_error_ = false;
if (!file_util::AbsolutePath(&path))
NOTREACHED();
+ scoped_ptr<ExtensionList> extensions(new ExtensionList);
+
+ // Create the <Profile>/Extensions directory if it doesn't exist.
+ if (!file_util::DirectoryExists(path)) {
+ file_util::CreateDirectory(path);
+ LOG(INFO) << "Created Extensions directory. No extensions to install.";
+ ReportExtensionsLoaded(extensions.release());
+ return;
+ }
+
LOG(INFO) << "Loading installed extensions...";
// Find all child directories in the install directory and load their
// manifests. Post errors and results to the frontend.
- scoped_ptr<ExtensionList> extensions(new ExtensionList);
file_util::FileEnumerator enumerator(path,
false, // not recursive
file_util::FileEnumerator::DIRECTORIES);
- for (FilePath child_path = enumerator.Next(); !child_path.value().empty();
- child_path = enumerator.Next()) {
- std::string version_str;
- if (!ReadCurrentVersion(child_path, &version_str)) {
- ReportExtensionLoadError(frontend.get(), child_path, StringPrintf(
- "Could not read '%s' file.",
- ExtensionsService::kCurrentVersionFileName));
- continue;
- }
-
- LOG(INFO) << " " << WideToASCII(child_path.BaseName().ToWStringHack()) <<
- " version: " << version_str;
-
- child_path = child_path.AppendASCII(version_str);
- Extension* extension = LoadExtension(child_path, frontend);
+ for (extension_path_ = enumerator.Next(); !extension_path_.value().empty();
+ extension_path_ = enumerator.Next()) {
+ Extension* extension = LoadExtensionCurrentVersion();
if (extension)
extensions->push_back(extension);
}
LOG(INFO) << "Done.";
-
- ReportExtensionsLoaded(frontend.get(), extensions.release());
- return true;
+ ReportExtensionsLoaded(extensions.release());
}
-bool ExtensionsServiceBackend::LoadSingleExtension(
+void ExtensionsServiceBackend::LoadSingleExtension(
const FilePath& path_in,
scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
- FilePath path = path_in;
- if (!file_util::AbsolutePath(&path))
+ frontend_ = frontend;
+
+ // Explicit UI loads are always noisy.
+ alert_on_error_ = true;
+
+ extension_path_ = path_in;
+ if (!file_util::AbsolutePath(&extension_path_))
NOTREACHED();
LOG(INFO) << "Loading single extension from " <<
- WideToASCII(path.BaseName().ToWStringHack());
+ WideToASCII(extension_path_.BaseName().ToWStringHack());
- Extension* extension = LoadExtension(path, frontend);
+ Extension* extension = LoadExtension();
if (extension) {
ExtensionList* extensions = new ExtensionList;
extensions->push_back(extension);
- ReportExtensionsLoaded(frontend.get(), extensions);
- return true;
+ ReportExtensionsLoaded(extensions);
}
- return false;
}
-Extension* ExtensionsServiceBackend::LoadExtension(
- const FilePath& path,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+Extension* ExtensionsServiceBackend::LoadExtensionCurrentVersion() {
+ std::string version_str;
+ if (!ReadCurrentVersion(extension_path_, &version_str)) {
+ ReportExtensionLoadError(StringPrintf("Could not read '%s' file.",
+ ExtensionsService::kCurrentVersionFileName));
+ return NULL;
+ }
+
+ LOG(INFO) << " " <<
+ WideToASCII(extension_path_.BaseName().ToWStringHack()) <<
+ " version: " << version_str;
+
+ extension_path_ = extension_path_.AppendASCII(version_str);
+ return LoadExtension();
+}
+
+Extension* ExtensionsServiceBackend::LoadExtension() {
FilePath manifest_path =
- path.AppendASCII(Extension::kManifestFilename);
+ extension_path_.AppendASCII(Extension::kManifestFilename);
if (!file_util::PathExists(manifest_path)) {
- ReportExtensionLoadError(frontend.get(), path,
- Extension::kInvalidManifestError);
+ ReportExtensionLoadError(Extension::kInvalidManifestError);
return NULL;
}
@@ -251,21 +296,29 @@ Extension* ExtensionsServiceBackend::LoadExtension(
std::string error;
scoped_ptr<Value> root(serializer.Deserialize(&error));
if (!root.get()) {
- ReportExtensionLoadError(frontend.get(), path,
- error);
+ ReportExtensionLoadError(error);
return NULL;
}
if (!root->IsType(Value::TYPE_DICTIONARY)) {
- ReportExtensionLoadError(frontend.get(), path,
- Extension::kInvalidManifestError);
+ ReportExtensionLoadError(Extension::kInvalidManifestError);
return NULL;
}
- scoped_ptr<Extension> extension(new Extension(path));
+ scoped_ptr<Extension> extension(new Extension(extension_path_));
if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
&error)) {
- ReportExtensionLoadError(frontend.get(), path, error);
+ ReportExtensionLoadError(error);
+ return NULL;
+ }
+
+ if (CheckExternalUninstall(extension_path_, extension->id())) {
+
+ // TODO(erikkay): Possibly defer this operation to avoid slowing initial
+ // load of extensions.
+ UninstallExtension(extension_path_);
+
+ // No error needs to be reported. The extension effectively doesn't exist.
return NULL;
}
@@ -274,9 +327,9 @@ Extension* ExtensionsServiceBackend::LoadExtension(
extension->content_scripts().begin();
iter != extension->content_scripts().end(); ++iter) {
if (!file_util::PathExists(iter->path())) {
- ReportExtensionLoadError(frontend.get(), path, StringPrintf(
- "Could not load content script '%s'.",
- WideToUTF8(iter->path().ToWStringHack()).c_str()));
+ ReportExtensionLoadError(
+ StringPrintf("Could not load content script '%s'.",
+ WideToUTF8(iter->path().ToWStringHack()).c_str()));
return NULL;
}
}
@@ -285,21 +338,21 @@ Extension* ExtensionsServiceBackend::LoadExtension(
}
void ExtensionsServiceBackend::ReportExtensionLoadError(
- ExtensionsServiceFrontendInterface *frontend, const FilePath& path,
const std::string &error) {
- // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
- std::string path_str = WideToASCII(path.ToWStringHack());
+
+ // TODO(port): note that this isn't guaranteed to work properly on Linux.
+ std::string path_str = WideToASCII(extension_path_.ToWStringHack());
std::string message = StringPrintf("Could not load extension from '%s'. %s",
path_str.c_str(), error.c_str());
- frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
- frontend, &ExtensionsServiceFrontendInterface::OnExtensionLoadError,
- message));
+ frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend_, &ExtensionsServiceFrontendInterface::OnExtensionLoadError,
+ alert_on_error_, message));
}
void ExtensionsServiceBackend::ReportExtensionsLoaded(
- ExtensionsServiceFrontendInterface *frontend, ExtensionList* extensions) {
- frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
- frontend,
+ ExtensionList* extensions) {
+ frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend_,
&ExtensionsServiceFrontendInterface::OnExtensionsLoadedFromDirectory,
extensions));
}
@@ -307,37 +360,32 @@ void ExtensionsServiceBackend::ReportExtensionsLoaded(
// The extension file format is a header, followed by the manifest, followed
// by the zip file. The header is a magic number, a version, the size of the
// header, and the size of the manifest. These ints are 4 byte little endian.
-DictionaryValue* ExtensionsServiceBackend::ReadManifest(
- const FilePath& extension_path,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
- ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb"));
+DictionaryValue* ExtensionsServiceBackend::ReadManifest() {
+ ScopedStdioHandle file(file_util::OpenFile(extension_path_, "rb"));
if (!file.get()) {
- ReportExtensionInstallError(frontend, extension_path,
- "no such extension file");
+ ReportExtensionInstallError("no such extension file");
return NULL;
}
// Read and verify the header.
ExtensionHeader header;
size_t len;
+
// TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
// appears that we don't have any endian/alignment aware serialization
// code in the code base. So for now, this assumes that we're running
// on a little endian machine with 4 byte alignment.
len = fread(&header, 1, sizeof(ExtensionHeader), file.get());
if (len < sizeof(ExtensionHeader)) {
- ReportExtensionInstallError(frontend, extension_path,
- "invalid extension header");
+ ReportExtensionInstallError("invalid extension header");
return NULL;
}
if (strncmp(kExtensionFileMagic, header.magic, sizeof(header.magic))) {
- ReportExtensionInstallError(frontend, extension_path,
- "bad magic number");
+ ReportExtensionInstallError("bad magic number");
return NULL;
}
if (header.version != Extension::kExpectedFormatVersion) {
- ReportExtensionInstallError(frontend, extension_path,
- "bad version number");
+ ReportExtensionInstallError("bad version number");
return NULL;
}
if (header.header_size > sizeof(ExtensionHeader))
@@ -360,24 +408,44 @@ DictionaryValue* ExtensionsServiceBackend::ReadManifest(
std::string error;
scoped_ptr<Value> val(json.Deserialize(&error));
if (!val.get()) {
- ReportExtensionInstallError(frontend, extension_path, error);
+ ReportExtensionInstallError(error);
return NULL;
}
if (!val->IsType(Value::TYPE_DICTIONARY)) {
- ReportExtensionInstallError(frontend, extension_path,
- "manifest isn't a JSON dictionary");
+ ReportExtensionInstallError("manifest isn't a JSON dictionary");
return NULL;
}
DictionaryValue* manifest = static_cast<DictionaryValue*>(val.get());
+
+ // Check the version before proceeding. Although we verify the version
+ // again later, checking it here allows us to skip some potentially expensive
+ // work.
+ std::string id;
+ if (!manifest->GetString(Extension::kIdKey, &id)) {
+ ReportExtensionInstallError("missing id key");
+ return NULL;
+ }
+ FilePath dest_dir = install_directory_.AppendASCII(id.c_str());
+ if (file_util::PathExists(dest_dir)) {
+ std::string version;
+ if (!manifest->GetString(Extension::kVersionKey, &version)) {
+ ReportExtensionInstallError("missing version key");
+ return NULL;
+ }
+ std::string current_version;
+ if (ReadCurrentVersion(dest_dir, &current_version)) {
+ if (!CheckCurrentVersion(version, current_version, dest_dir))
+ return NULL;
+ }
+ }
+
std::string zip_hash;
if (!manifest->GetString(Extension::kZipHashKey, &zip_hash)) {
- ReportExtensionInstallError(frontend, extension_path,
- "missing zip_hash key");
+ ReportExtensionInstallError("missing zip_hash key");
return NULL;
}
if (zip_hash.size() != kZipHashHexBytes) {
- ReportExtensionInstallError(frontend, extension_path,
- "invalid zip_hash key");
+ ReportExtensionInstallError("invalid zip_hash key");
return NULL;
}
@@ -394,19 +462,16 @@ DictionaryValue* ExtensionsServiceBackend::ReadManifest(
std::vector<uint8> zip_hash_bytes;
if (!HexStringToBytes(zip_hash, &zip_hash_bytes)) {
- ReportExtensionInstallError(frontend, extension_path,
- "invalid zip_hash key");
+ ReportExtensionInstallError("invalid zip_hash key");
return NULL;
}
if (zip_hash_bytes.size() != kZipHashBytes) {
- ReportExtensionInstallError(frontend, extension_path,
- "invalid zip_hash key");
+ ReportExtensionInstallError("invalid zip_hash key");
return NULL;
}
for (size_t i = 0; i < kZipHashBytes; ++i) {
if (zip_hash_bytes[i] != hash[i]) {
- ReportExtensionInstallError(frontend, extension_path,
- "zip_hash key didn't match zip hash");
+ ReportExtensionInstallError("zip_hash key didn't match zip hash");
return NULL;
}
}
@@ -419,11 +484,10 @@ DictionaryValue* ExtensionsServiceBackend::ReadManifest(
return manifest;
}
-bool ExtensionsServiceBackend::ReadCurrentVersion(
- const FilePath& extension_path,
- std::string* version_string) {
+bool ExtensionsServiceBackend::ReadCurrentVersion(const FilePath& dir,
+ std::string* version_string) {
FilePath current_version =
- extension_path.AppendASCII(ExtensionsService::kCurrentVersionFileName);
+ dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
if (file_util::PathExists(current_version)) {
if (file_util::ReadFileToString(current_version, version_string)) {
TrimWhitespace(*version_string, TRIM_ALL, version_string);
@@ -434,66 +498,38 @@ bool ExtensionsServiceBackend::ReadCurrentVersion(
}
bool ExtensionsServiceBackend::CheckCurrentVersion(
- const FilePath& extension_path,
- const std::string& version,
- const FilePath& dest_dir,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
- std::string version_str;
- if (ReadCurrentVersion(dest_dir, &version_str)) {
- if (version_str == version) {
- FilePath version_dir = dest_dir.AppendASCII(version_str);
- if (file_util::PathExists(version_dir)) {
- ReportExtensionInstallError(frontend, extension_path,
- "Extension version already installed");
- return false;
- }
- // If the existing version_dir doesn't exist, then we'll return true
- // so that we attempt to repair the broken installation.
- } else {
- scoped_ptr<Version> cur_version(
- Version::GetVersionFromString(version_str));
- scoped_ptr<Version> new_version(
- Version::GetVersionFromString(version));
- if (cur_version->CompareTo(*new_version) >= 0) {
- ReportExtensionInstallError(frontend, extension_path,
- "More recent version of extension already installed");
- return false;
- }
+ const std::string& new_version_str,
+ const std::string& current_version_str,
+ const FilePath& dest_dir) {
+ scoped_ptr<Version> current_version(
+ Version::GetVersionFromString(current_version_str));
+ scoped_ptr<Version> new_version(
+ Version::GetVersionFromString(new_version_str));
+ if (current_version->CompareTo(*new_version) >= 0) {
+
+ // Verify that the directory actually exists. If it doesn't we'll return
+ // true so that the install code will repair the broken installation.
+ // TODO(erikkay): A further step would be to verify that the extension
+ // has actually loaded successfully.
+ FilePath version_dir = dest_dir.AppendASCII(current_version_str);
+ if (file_util::PathExists(version_dir)) {
+ ReportExtensionInstallError(
+ "Existing version is already up to date.");
+ return false;
}
}
return true;
}
-bool ExtensionsServiceBackend::UnzipExtension(const FilePath& extension_path,
- const FilePath& temp_dir,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
- // <profile>/Extensions/INSTALL_TEMP/<version>
- if (!file_util::CreateDirectory(temp_dir)) {
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't create version directory.");
- return false;
- }
- if (!Unzip(extension_path, temp_dir, NULL)) {
- // Remove what we just installed.
- file_util::Delete(temp_dir, true);
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't unzip extension.");
- return false;
- }
- return true;
-}
-
-bool ExtensionsServiceBackend::InstallDirSafely(
- const FilePath& extension_path,
- const FilePath& source_dir,
- const FilePath& dest_dir,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+bool ExtensionsServiceBackend::InstallDirSafely(const FilePath& source_dir,
+ const FilePath& dest_dir) {
if (file_util::PathExists(dest_dir)) {
+
// By the time we get here, it should be safe to assume that this directory
// is not currently in use (it's not the current active version).
if (!file_util::Delete(dest_dir, true)) {
- ReportExtensionInstallError(frontend, extension_path,
+ ReportExtensionInstallError(
"Can't delete existing version directory.");
return false;
}
@@ -501,26 +537,21 @@ bool ExtensionsServiceBackend::InstallDirSafely(
FilePath parent = dest_dir.DirName();
if (!file_util::DirectoryExists(parent)) {
if (!file_util::CreateDirectory(parent)) {
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't create extension directory.");
+ ReportExtensionInstallError("Couldn't create extension directory.");
return false;
}
}
}
if (!file_util::Move(source_dir, dest_dir)) {
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't move temporary directory.");
+ ReportExtensionInstallError("Couldn't move temporary directory.");
return false;
}
return true;
}
-bool ExtensionsServiceBackend::SetCurrentVersion(
- const FilePath& extension_path,
- const FilePath& dest_dir,
- std::string version,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
+ std::string version) {
// Write out the new CurrentVersion file.
// <profile>/Extension/<name>/CurrentVersion
FilePath current_version =
@@ -529,15 +560,13 @@ bool ExtensionsServiceBackend::SetCurrentVersion(
current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
if (file_util::PathExists(current_version_old)) {
if (!file_util::Delete(current_version_old, false)) {
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't remove CurrentVersion_old file.");
+ ReportExtensionInstallError("Couldn't remove CurrentVersion_old file.");
return false;
}
}
if (file_util::PathExists(current_version)) {
if (!file_util::Move(current_version, current_version_old)) {
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't move CurrentVersion file.");
+ ReportExtensionInstallError("Couldn't move CurrentVersion file.");
return false;
}
}
@@ -546,103 +575,250 @@ bool ExtensionsServiceBackend::SetCurrentVersion(
if (stream.Open(current_version, flags) != 0)
return false;
if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
+
// Restore the old CurrentVersion.
if (file_util::PathExists(current_version_old)) {
if (!file_util::Move(current_version_old, current_version)) {
LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
" to " << current_version.value();
+
// TODO(erikkay): This is an ugly state to be in. Try harder?
}
}
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't create CurrentVersion file.");
+ ReportExtensionInstallError("Couldn't create CurrentVersion file.");
return false;
}
return true;
}
-bool ExtensionsServiceBackend::InstallExtension(
+void ExtensionsServiceBackend::InstallExtension(
const FilePath& extension_path,
const FilePath& install_dir,
+ bool alert_on_error,
scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
LOG(INFO) << "Installing extension " << extension_path.value();
- // <profile>/Extensions/INSTALL_TEMP
- FilePath temp_dir = install_dir.AppendASCII(kTempExtensionName);
- // Ensure we're starting with a clean slate.
- if (file_util::PathExists(temp_dir)) {
- if (!file_util::Delete(temp_dir, true)) {
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't delete existing temporary directory.");
- return false;
- }
- }
- ScopedTempDir scoped_temp;
- scoped_temp.Set(temp_dir);
- if (!scoped_temp.IsValid()) {
- ReportExtensionInstallError(frontend, extension_path,
- "Couldn't create temporary directory.");
- return false;
- }
+ frontend_ = frontend;
+ alert_on_error_ = alert_on_error;
+ external_install_ = false;
+ extension_path_ = extension_path;
+ install_directory_ = install_dir;
+
+ InstallOrUpdateExtension(std::string());
+}
+
+void ExtensionsServiceBackend::InstallOrUpdateExtension(
+ const std::string& expected_id) {
+ bool update = false;
// Read and verify the extension.
- scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path, frontend));
+ scoped_ptr<DictionaryValue> manifest(ReadManifest());
if (!manifest.get()) {
+
// ReadManifest has already reported the extension error.
- return false;
+ return;
}
DictionaryValue* dict = manifest.get();
Extension extension;
std::string error;
if (!extension.InitFromValue(*dict, &error)) {
- ReportExtensionInstallError(frontend, extension_path,
- "Invalid extension manifest.");
- return false;
+ ReportExtensionInstallError("Invalid extension manifest.");
+ return;
+ }
+
+ // If an expected id was provided, make sure it matches.
+ if (expected_id.length() && expected_id != extension.id()) {
+ ReportExtensionInstallError(
+ "ID in new extension manifest does not match expected ID.");
+ return;
}
// <profile>/Extensions/<id>
- FilePath dest_dir = install_dir.AppendASCII(extension.id());
+ FilePath dest_dir = install_directory_.AppendASCII(extension.id());
std::string version = extension.VersionString();
- if (!CheckCurrentVersion(extension_path, version, dest_dir, frontend))
- return false;
+ std::string current_version;
+ if (ReadCurrentVersion(dest_dir, &current_version)) {
+ if (!CheckCurrentVersion(version, current_version, dest_dir))
+ return;
+ update = true;
+ }
+
+ // <profile>/Extensions/INSTALL_TEMP
+ FilePath temp_dir = install_directory_.AppendASCII(kTempExtensionName);
+
+ // Ensure we're starting with a clean slate.
+ if (file_util::PathExists(temp_dir)) {
+ if (!file_util::Delete(temp_dir, true)) {
+ ReportExtensionInstallError(
+ "Couldn't delete existing temporary directory.");
+ return;
+ }
+ }
+ ScopedTempDir scoped_temp;
+ scoped_temp.Set(temp_dir);
+ if (!scoped_temp.IsValid()) {
+ ReportExtensionInstallError("Couldn't create temporary directory.");
+ return;
+ }
// <profile>/Extensions/INSTALL_TEMP/<version>
FilePath temp_version = temp_dir.AppendASCII(version);
- if (!UnzipExtension(extension_path, temp_version, frontend))
- return false;
+ file_util::CreateDirectory(temp_version);
+ if (!Unzip(extension_path_, temp_version, NULL)) {
+ ReportExtensionInstallError("Couldn't unzip extension.");
+ return;
+ }
// <profile>/Extensions/<dir_name>/<version>
FilePath version_dir = dest_dir.AppendASCII(version);
- if (!InstallDirSafely(extension_path, temp_version, version_dir, frontend))
- return false;
+ if (!InstallDirSafely(temp_version, version_dir))
+ return;
- if (!SetCurrentVersion(extension_path, dest_dir, version, frontend)) {
+ if (!SetCurrentVersion(dest_dir, version)) {
if (!file_util::Delete(version_dir, true))
LOG(WARNING) << "Can't remove " << dest_dir.value();
- return false;
+ return;
}
- ReportExtensionInstalled(frontend, dest_dir);
- return true;
+ if (external_install_) {
+
+ // To mark that this extension was installed from an external source,
+ // create a zero-length file. At load time, this is used to indicate
+ // that the extension should be uninstalled.
+ // TODO(erikkay): move this into per-extension config storage when
+ // it appears.
+ FilePath marker = version_dir.AppendASCII(kExternalInstallFile);
+ file_util::WriteFile(marker, NULL, 0);
+ }
+
+ ReportExtensionInstalled(dest_dir, update);
}
void ExtensionsServiceBackend::ReportExtensionInstallError(
- ExtensionsServiceFrontendInterface *frontend, const FilePath& path,
const std::string &error) {
+
// TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
- std::string path_str = WideToASCII(path.ToWStringHack());
+ std::string path_str = WideToASCII(extension_path_.ToWStringHack());
std::string message =
StringPrintf("Could not install extension from '%s'. %s",
path_str.c_str(), error.c_str());
- frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
- frontend, &ExtensionsServiceFrontendInterface::OnExtensionInstallError,
- message));
+ frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend_, &ExtensionsServiceFrontendInterface::OnExtensionInstallError,
+ alert_on_error_, message));
}
void ExtensionsServiceBackend::ReportExtensionInstalled(
- ExtensionsServiceFrontendInterface *frontend, FilePath path) {
- frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
- frontend,
+ FilePath path, bool update) {
+ frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend_,
&ExtensionsServiceFrontendInterface::OnExtensionInstalled,
- path));
+ path,
+ update));
+
+ // After it's installed, load it right away with the same settings.
+ extension_path_ = path;
+ LoadExtensionCurrentVersion();
+}
+
+// Some extensions will autoupdate themselves externally from Chrome. These
+// are typically part of some larger client application package. To support
+// these, the extension will register its location in the registry on Windows
+// (TODO(port): what about on other platforms?) and this code will periodically
+// check that location for a .crx file, which it will then install locally if
+// a new version is available.
+void ExtensionsServiceBackend::CheckForExternalUpdates(
+ const FilePath& install_dir,
+ scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+
+ // Note that this installation is intentionally silent (since it didn't
+ // go through the front-end). Extensions that are registered in this
+ // way are effectively considered 'pre-bundled', and so implicitly
+ // trusted. In general, if something has HKLM or filesystem access,
+ // they could install an extension manually themselves anyway.
+ alert_on_error_ = false;
+ frontend_ = frontend;
+ external_install_ = true;
+ install_directory_ = install_dir;
+
+#if defined(OS_WIN)
+ HKEY reg_root = HKEY_LOCAL_MACHINE;
+ RegistryKeyIterator iterator(reg_root, kRegistryExtensions);
+ while (iterator.Valid()) {
+ RegKey key;
+ std::wstring key_path = kRegistryExtensions;
+ 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());
+ extension_path_ = FilePath(extension_path);
+ std::wstring extension_version;
+ if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
+ if (ShouldInstall(id, WideToASCII(extension_version)))
+ InstallOrUpdateExtension(id);
+ } else {
+
+ // TODO(erikkay): find a way to get this into about:extensions
+ LOG(WARNING) << "Missing value " << kRegistryExtensionVersion <<
+ " for key " << key_path;
+ }
+ } else {
+
+ // TODO(erikkay): find a way to get this into about:extensions
+ LOG(WARNING) << "Missing value " << kRegistryExtensionPath <<
+ " for key " << key_path;
+ }
+ }
+ ++iterator;
+ }
+#else
+ NOTREACHED();
+#endif
+}
+
+bool ExtensionsServiceBackend::CheckExternalUninstall(const FilePath& path,
+ const std::string& id) {
+ FilePath external_file = 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;
+ 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();
+#endif
+ }
+ return false;
+}
+
+// Assumes that the extension isn't currently loaded or in use.
+void ExtensionsServiceBackend::UninstallExtension(const FilePath& path) {
+ FilePath parent = path.DirName();
+ FilePath version =
+ parent.AppendASCII(ExtensionsService::kCurrentVersionFileName);
+ bool version_exists = file_util::PathExists(version);
+ DCHECK(version_exists);
+ if (!version_exists) {
+ LOG(WARNING) << "Asked to uninstall bogus extension dir " << parent.value();
+ return;
+ }
+ if (!file_util::Delete(parent, true)) {
+ LOG(WARNING) << "Failed to delete " << parent.value();
+ }
+}
+
+bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
+ const std::string& version) {
+ FilePath dir(install_directory_.AppendASCII(id.c_str()));
+ std::string current_version;
+ if (ReadCurrentVersion(dir, &current_version)) {
+ return !CheckCurrentVersion(version, current_version, dir);
+ }
+ return true;
}
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index 8382786..c8f04b0 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSIONS_SERVICE_H_
#define CHROME_BROWSER_EXTENSIONS_EXTENSIONS_SERVICE_H_
+#include <string>
#include <vector>
#include "base/file_path.h"
@@ -27,24 +28,31 @@ class ExtensionsServiceFrontendInterface
// The message loop to invoke the frontend's methods on.
virtual MessageLoop* GetMessageLoop() = 0;
- // Install the extension file at |extension_path|.
+ // Install the extension file at |extension_path|. Will install as an
+ // update if an older version is already installed.
+ // For fresh installs, this method also causes the extension to be
+ // immediately loaded.
virtual void InstallExtension(const FilePath& extension_path) = 0;
// Load the extension from the directory |extension_path|.
virtual void LoadExtension(const FilePath& extension_path) = 0;
// Called when loading an extension fails.
- virtual void OnExtensionLoadError(const std::string& message) = 0;
+ virtual void OnExtensionLoadError(bool alert_on_error,
+ const std::string& message) = 0;
// Called with results from LoadExtensionsFromDirectory(). The frontend
// takes ownership of the list.
virtual void OnExtensionsLoadedFromDirectory(ExtensionList* extensions) = 0;
// Called when installing an extension fails.
- virtual void OnExtensionInstallError(const std::string& message) = 0;
+ virtual void OnExtensionInstallError(bool alert_on_error,
+ const std::string& message) = 0;
// Called with results from InstallExtension().
- virtual void OnExtensionInstalled(FilePath path) = 0;
+ // |is_update| is true if the installation was an update to an existing
+ // installed extension rather than a new installation.
+ virtual void OnExtensionInstalled(FilePath path, bool is_update) = 0;
};
@@ -67,10 +75,12 @@ class ExtensionsService : public ExtensionsServiceFrontendInterface {
virtual MessageLoop* GetMessageLoop();
virtual void InstallExtension(const FilePath& extension_path);
virtual void LoadExtension(const FilePath& extension_path);
- virtual void OnExtensionLoadError(const std::string& message);
+ virtual void OnExtensionLoadError(bool alert_on_error,
+ const std::string& message);
virtual void OnExtensionsLoadedFromDirectory(ExtensionList* extensions);
- virtual void OnExtensionInstallError(const std::string& message);
- virtual void OnExtensionInstalled(FilePath path);
+ virtual void OnExtensionInstallError(bool alert_on_error,
+ const std::string& message);
+ virtual void OnExtensionInstalled(FilePath path, bool is_update);
// The name of the file that the current active version number is stored in.
static const char* kCurrentVersionFileName;
@@ -111,7 +121,7 @@ class ExtensionsServiceBackend
// Errors are reported through OnExtensionLoadError(). On completion,
// OnExtensionsLoadedFromDirectory() is called with any successfully loaded
// extensions.
- bool LoadExtensionsFromDirectory(
+ void LoadExtensionsFromDirectory(
const FilePath &path,
scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
@@ -122,79 +132,110 @@ class ExtensionsServiceBackend
// extensions.
// TODO(erikkay): It might be useful to be able to load a packed extension
// (presumably into memory) without installing it.
- bool LoadSingleExtension(
+ void LoadSingleExtension(
const FilePath &path,
scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
// Install the extension file at extension_path to install_dir.
// ReportExtensionInstallError is called on error.
// ReportExtensionInstalled is called on success.
- bool InstallExtension(
+ void InstallExtension(
const FilePath& extension_path,
const FilePath& install_dir,
+ bool alert_on_error,
+ scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
+
+ // Check externally updated extensions for updates and install if necessary.
+ // ReportExtensionInstallError is called on error.
+ // ReportExtensionInstalled is called on success.
+ void CheckForExternalUpdates(
+ const FilePath& install_dir,
scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
private:
- // Load a single extension from |path| where |path| is the top directory of
+ // Load a single extension from |extension_path_|, the top directory of
// a specific extension where its manifest file lives.
- Extension* LoadExtension(
- const FilePath &path,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
+ Extension* LoadExtension();
+
+ // Load a single extension from |extension_path_|, the top directory of
+ // a versioned extension where its Current Version file lives.
+ Extension* LoadExtensionCurrentVersion();
+
+ // Install a crx file at |extension_path_| into |install_directory_|.
+ // If |expected_id| is not empty, it's verified against the extension's
+ // manifest before installationl. If the extension is already installed,
+ // install the new version only if its version number is greater than the
+ // current installed version.
+ void InstallOrUpdateExtension(const std::string& expected_id);
// Notify a frontend that there was an error loading an extension.
- void ReportExtensionLoadError(ExtensionsServiceFrontendInterface* frontend,
- const FilePath& path,
- const std::string& error);
+ void ReportExtensionLoadError(const std::string& error);
// Notify a frontend that extensions were loaded.
- void ReportExtensionsLoaded(ExtensionsServiceFrontendInterface* frontend,
- ExtensionList* extensions);
+ void ReportExtensionsLoaded(ExtensionList* extensions);
// Notify a frontend that there was an error installing an extension.
- void ReportExtensionInstallError(ExtensionsServiceFrontendInterface* frontend,
- const FilePath& path,
- const std::string& error);
+ void ReportExtensionInstallError(const std::string& error);
// Notify a frontend that extensions were installed.
- void ReportExtensionInstalled(ExtensionsServiceFrontendInterface* frontend,
- FilePath path);
+ // |is_update| is true if this was an update to an existing extension.
+ void ReportExtensionInstalled(FilePath path, bool is_update);
// Read the manifest from the front of the extension file.
// Caller takes ownership of return value.
- DictionaryValue* ReadManifest(const FilePath& extension_path,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
-
- // Reads the Current Version file from |extension_path|.
- bool ReadCurrentVersion(const FilePath& extension_path,
- std::string* version_string);
+ DictionaryValue* ReadManifest();
- // Check that the version to be installed is > the current installed
- // extension.
- bool CheckCurrentVersion(const FilePath& extension_path,
- const std::string& version,
- const FilePath& dest_dir,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
+ // Reads the Current Version file from |dir| into |version_string|.
+ bool ReadCurrentVersion(const FilePath& dir, std::string* version_string);
- // Unzip the extension into |dest_dir|.
- bool UnzipExtension(const FilePath& extension_path,
- const FilePath& dest_dir,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
+ // Check that the version to be installed is greater than the current
+ // installed extension.
+ bool CheckCurrentVersion(const std::string& version,
+ const std::string& current_version,
+ const FilePath& dest_dir);
// Install the extension dir by moving it from |source| to |dest| safely.
- bool InstallDirSafely(const FilePath& extension_path,
- const FilePath& source, const FilePath& dest,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
+ bool InstallDirSafely(const FilePath& source,
+ const FilePath& dest);
// Update the CurrentVersion file in |dest_dir| to |version|.
- bool SetCurrentVersion(const FilePath& extension_path,
- const FilePath& dest_dir,
- std::string version,
- scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
+ bool SetCurrentVersion(const FilePath& dest_dir,
+ std::string version);
+
+ // For the extension at |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 FilePath& path, const std::string& id);
+
+ // Deletes all versions of the extension from the filesystem.
+ // |path| points at a specific extension version dir.
+ void UninstallExtension(const FilePath& path);
+
+ // Should an extension of |id| and |version| be installed?
+ // Returns true if no extension of type |id| is installed or if |version|
+ // is greater than the current installed version.
+ bool ShouldInstall(const std::string& id, const std::string& version);
// The name of a temporary directory to install an extension into for
// validation before finalizing install.
static const char* kTempExtensionName;
+ // This is a naked pointer which is set by each entry point.
+ // The entry point is responsible for ensuring lifetime.
+ ExtensionsServiceFrontendInterface* frontend_;
+
+ // The extension path being loaded or installed.
+ FilePath extension_path_;
+
+ // The top-level extensions directory being installed to.
+ FilePath install_directory_;
+
+ // Whether errors result in noisy alerts.
+ bool alert_on_error_;
+
+ // Whether the current install is from an external source.
+ bool external_install_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceBackend);
};
diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc
index 6040823..62df67c 100644
--- a/chrome/browser/extensions/extensions_service_unittest.cc
+++ b/chrome/browser/extensions/extensions_service_unittest.cc
@@ -73,7 +73,8 @@ class ExtensionsServiceTestFrontend
virtual void LoadExtension(const FilePath& extension_path) {
}
- virtual void OnExtensionLoadError(const std::string& message) {
+ virtual void OnExtensionLoadError(bool alert_on_error,
+ const std::string& message) {
// In the development environment, we get errors when trying to load
// extensions out of the .svn directories.
if (message.find(".svn") != std::string::npos)
@@ -92,11 +93,12 @@ class ExtensionsServiceTestFrontend
std::stable_sort(errors_.begin(), errors_.end());
}
- virtual void OnExtensionInstallError(const std::string& message) {
+ virtual void OnExtensionInstallError(bool alert_on_error,
+ const std::string& message) {
errors_.push_back(message);
}
- virtual void OnExtensionInstalled(FilePath path) {
+ virtual void OnExtensionInstalled(FilePath path, bool is_update) {
installed_.push_back(path);
}
@@ -104,13 +106,12 @@ class ExtensionsServiceTestFrontend
ExtensionsServiceBackend* backend,
bool should_succeed) {
ASSERT_TRUE(file_util::PathExists(path));
- EXPECT_EQ(should_succeed,
- backend->InstallExtension(path, install_dir_,
- scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
+ backend->InstallExtension(path, install_dir_, false,
+ scoped_refptr<ExtensionsServiceFrontendInterface>(this));
message_loop_.RunAllPending();
if (should_succeed) {
EXPECT_EQ(1u, installed_.size());
- EXPECT_EQ(0u, errors_.size());
+ EXPECT_EQ(0u, errors_.size()) << path.value();
} else {
EXPECT_EQ(0u, installed_.size());
EXPECT_EQ(1u, errors_.size());
@@ -143,8 +144,8 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectorySuccess) {
new ExtensionsServiceTestFrontend);
std::vector<Extension*> extensions;
- EXPECT_TRUE(backend->LoadExtensionsFromDirectory(extensions_path,
- scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get())));
+ backend->LoadExtensionsFromDirectory(extensions_path,
+ scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()));
frontend->GetMessageLoop()->RunAllPending();
ASSERT_EQ(3u, frontend->extensions()->size());
@@ -202,8 +203,8 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectoryFail) {
new ExtensionsServiceTestFrontend);
std::vector<Extension*> extensions;
- EXPECT_TRUE(backend->LoadExtensionsFromDirectory(extensions_path,
- scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get())));
+ backend->LoadExtensionsFromDirectory(extensions_path,
+ scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()));
frontend->GetMessageLoop()->RunAllPending();
EXPECT_EQ(4u, frontend->errors()->size());
@@ -275,16 +276,16 @@ TEST_F(ExtensionsServiceTest, LoadExtension) {
FilePath ext1 = extensions_path.AppendASCII("good").AppendASCII("extension1")
.AppendASCII("1");
- EXPECT_TRUE(backend->LoadSingleExtension(ext1,
- scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get())));
+ backend->LoadSingleExtension(ext1,
+ scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()));
frontend->GetMessageLoop()->RunAllPending();
EXPECT_EQ(0u, frontend->errors()->size());
ASSERT_EQ(1u, frontend->extensions()->size());
FilePath no_manifest = extensions_path.AppendASCII("bad")
.AppendASCII("no_manifest").AppendASCII("1");
- EXPECT_FALSE(backend->LoadSingleExtension(no_manifest,
- scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get())));
+ backend->LoadSingleExtension(no_manifest,
+ scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()));
frontend->GetMessageLoop()->RunAllPending();
EXPECT_EQ(1u, frontend->errors()->size());
ASSERT_EQ(1u, frontend->extensions()->size());