summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/crx_installer.cc31
-rw-r--r--chrome/browser/extensions/extension_prefs.cc97
-rw-r--r--chrome/browser/extensions/extension_prefs.h21
-rw-r--r--chrome/browser/extensions/extension_updater.cc123
-rw-r--r--chrome/browser/extensions/extension_updater.h35
-rw-r--r--chrome/browser/extensions/extension_updater_unittest.cc165
-rw-r--r--chrome/browser/extensions/extensions_service.cc32
-rw-r--r--chrome/browser/extensions/extensions_service.h7
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc260
-rw-r--r--chrome/common/pref_names.cc3
-rw-r--r--chrome/common/pref_names.h2
11 files changed, 675 insertions, 101 deletions
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index 5fede2e..c6d4590 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -116,14 +116,10 @@ void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir,
expected_id_.c_str()));
return;
}
+ if (client_.get()) DecodeInstallIcon();
- if (client_.get()) {
- DecodeInstallIcon();
- ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
- &CrxInstaller::ConfirmInstall));
- } else {
- CompleteInstall();
- }
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &CrxInstaller::ConfirmInstall));
}
void CrxInstaller::DecodeInstallIcon() {
@@ -146,7 +142,7 @@ void CrxInstaller::DecodeInstallIcon() {
webkit_glue::ImageDecoder decoder;
scoped_ptr<SkBitmap> decoded(new SkBitmap());
*decoded = decoder.Decode(data, file_contents.length());
- if(decoded->empty()) {
+ if (decoded->empty()) {
LOG(ERROR) << "Could not decode icon file: "
<< WideToUTF8(path.ToWStringHack());
return;
@@ -163,9 +159,24 @@ void CrxInstaller::DecodeInstallIcon() {
}
void CrxInstaller::ConfirmInstall() {
- AddRef(); // balanced in ContinueInstall() and AbortInstall().
+ DCHECK(MessageLoop::current() == ui_loop_);
+ if (frontend_->extension_prefs()->IsExtensionBlacklisted(extension_->id())) {
+ LOG(INFO) << "This extension: " << extension_->id()
+ << " is blacklisted. Install failed.";
+ if (client_) {
+ client_->OnInstallFailure("This extension is blacklisted.");
+ }
+ return;
+ }
- client_->ConfirmInstall(this, extension_.get(), install_icon_.get());
+ if (client_) {
+ AddRef(); // balanced in ContinueInstall() and AbortInstall().
+ client_->ConfirmInstall(this, extension_.get(), install_icon_.get());
+ } else {
+ file_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &CrxInstaller::CompleteInstall));
+ }
+ return;
}
void CrxInstaller::ContinueInstall() {
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 7b4d061..bc88831 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -24,6 +24,9 @@ const wchar_t kPrefState[] = L"state";
// The path to the current version's manifest file.
const wchar_t kPrefPath[] = L"path";
+// Indicates if an extension is blacklisted:
+const wchar_t kPrefBlacklist[] = L"blacklist";
+
// 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";
@@ -48,6 +51,17 @@ void InstalledExtensions::VisitInstalledExtensions(
NOTREACHED();
continue;
}
+ if (ext->HasKey(kPrefBlacklist)) {
+ bool is_blacklisted = false;
+ if (!ext->GetBoolean(kPrefBlacklist, &is_blacklisted)) {
+ NOTREACHED() << "Invalid blacklist pref:" << *extension_id;
+ continue;
+ }
+ if (is_blacklisted) {
+ LOG(WARNING) << "Blacklisted extension: " << *extension_id;
+ continue;
+ }
+ }
FilePath::StringType path;
if (!ext->GetString(kPrefPath, &path)) {
LOG(WARNING) << "Missing path pref for extension " << *extension_id;
@@ -131,7 +145,10 @@ void ExtensionPrefs::MakePathsAbsolute(DictionaryValue* dict) {
}
FilePath::StringType path_string;
if (!extension_dict->GetString(kPrefPath, &path_string)) {
- NOTREACHED();
+ if (!IsBlacklistBitSet(extension_dict)) {
+ // We expect the kPrefPath for non-blacklisted extensions.
+ NOTREACHED();
+ }
continue;
}
DCHECK(!FilePath(path_string).IsAbsolute());
@@ -151,6 +168,83 @@ DictionaryValue* ExtensionPrefs::CopyCurrentExtensions() {
return new DictionaryValue;
}
+bool ExtensionPrefs::IsBlacklistBitSet(DictionaryValue* ext) {
+ if (!ext->HasKey(kPrefBlacklist)) return false;
+ bool is_blacklisted = false;
+ if (!ext->GetBoolean(kPrefBlacklist, &is_blacklisted)) {
+ NOTREACHED() << "Failed to fetch blacklist flag.";
+ // In case we could not fetch the flag, we consider the extension
+ // is NOT blacklisted.
+ return false;
+ }
+ return is_blacklisted;
+}
+
+bool ExtensionPrefs::IsExtensionBlacklisted(const std::string& extension_id) {
+ const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref);
+ DCHECK(extensions);
+ DictionaryValue* ext = NULL;
+ if (!extensions->GetDictionary(ASCIIToWide(extension_id), &ext)) {
+ // No such extension yet.
+ return false;
+ }
+ return IsBlacklistBitSet(ext);
+}
+
+void ExtensionPrefs::UpdateBlacklist(
+ const std::set<std::string>& blacklist_set) {
+ std::vector<std::string> remove_pref_ids;
+ std::set<std::string> used_id_set;
+ const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref);
+ DCHECK(extensions);
+ DictionaryValue::key_iterator extension_id = extensions->begin_keys();
+ for (; extension_id != extensions->end_keys(); ++extension_id) {
+ DictionaryValue* ext;
+ std::string id = WideToASCII(*extension_id);
+ if (!extensions->GetDictionary(*extension_id, &ext)) {
+ NOTREACHED() << "Invalid pref for extension " << *extension_id;
+ continue;
+ }
+ if (blacklist_set.find(id) == blacklist_set.end()) {
+ if (!IsBlacklistBitSet(ext)) {
+ // This extension is not in blacklist. And it was not blacklisted
+ // before.
+ continue;
+ } else {
+ if (ext->GetSize() == 1) {
+ // We should remove the entry if the only flag here is blacklist.
+ remove_pref_ids.push_back(id);
+ } else {
+ // Remove the blacklist bit.
+ ext->Remove(kPrefBlacklist, NULL);
+ }
+ }
+ } else {
+ if (!IsBlacklistBitSet(ext)) {
+ // Only set the blacklist if it was not set.
+ ext->SetBoolean(kPrefBlacklist, true);
+ }
+ // Keep the record if this extension is already processed.
+ used_id_set.insert(id);
+ }
+ }
+
+ // Iterate the leftovers to set blacklist in pref
+ std::set<std::string>::const_iterator set_itr = blacklist_set.begin();
+ for (; set_itr != blacklist_set.end(); ++set_itr) {
+ if (used_id_set.find(*set_itr) == used_id_set.end()) {
+ UpdateExtensionPref(*set_itr, kPrefBlacklist,
+ Value::CreateBooleanValue(true));
+ }
+ }
+ for (unsigned int i = 0; i < remove_pref_ids.size(); ++i) {
+ DeleteExtensionPrefs(remove_pref_ids[i]);
+ }
+ // Update persistent registry
+ prefs_->ScheduleSavePersistentPrefs();
+ return;
+}
+
void ExtensionPrefs::GetKilledExtensionIds(std::set<std::string>* killed_ids) {
const DictionaryValue* dict = prefs_->GetDictionary(kExtensionsPref);
if (!dict || dict->GetSize() == 0)
@@ -265,4 +359,3 @@ DictionaryValue* ExtensionPrefs::GetOrCreateExtensionPref(
}
return extension;
}
-
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index 277b9c3..c819bcd 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -2,8 +2,8 @@
// 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
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_H_
#include <set>
#include <string>
@@ -46,6 +46,12 @@ class ExtensionPrefs {
// Returns base extensions install directory.
const FilePath& install_directory() const { return install_directory_; }
+ // Updates the prefs based on the blacklist.
+ void UpdateBlacklist(const std::set<std::string>& blacklist_set);
+
+ // Based on extension id, checks prefs to see if it is blacklisted.
+ bool IsExtensionBlacklisted(const std::string& id);
+
private:
// Converts absolute paths in the pref to paths relative to the
@@ -67,6 +73,11 @@ class ExtensionPrefs {
// Ensures and returns a mutable dictionary for extension |id|'s prefs.
DictionaryValue* GetOrCreateExtensionPref(const std::string& id);
+ // Checks if kPrefBlacklist is set to true in the DictionaryValue.
+ // Return false if the value is false or kPrefBlacklist does not exist.
+ // This is used to decide if an extension is blacklisted.
+ bool IsBlacklistBitSet(DictionaryValue* ext);
+
// The pref service specific to this set of extension prefs.
PrefService* prefs_;
@@ -91,7 +102,8 @@ class InstalledExtensions {
Extension::Location>::Type Callback;
// Runs |callback| for each installed extension with the path to the
- // version directory and the location.
+ // version directory and the location. Blacklisted extensions won't trigger
+ // the callback.
void VisitInstalledExtensions(Callback *callback);
private:
@@ -102,4 +114,5 @@ class InstalledExtensions {
DISALLOW_COPY_AND_ASSIGN(InstalledExtensions);
};
-#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_H
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_H_
+
diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc
index de553c3..6ae956d 100644
--- a/chrome/browser/extensions/extension_updater.cc
+++ b/chrome/browser/extensions/extension_updater.cc
@@ -11,6 +11,7 @@
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/rand_util.h"
+#include "base/sha2.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/thread.h"
@@ -31,6 +32,7 @@ using base::RandDouble;
using base::RandInt;
using base::Time;
using base::TimeDelta;
+using prefs::kExtensionBlacklistUpdateVersion;
using prefs::kLastExtensionsUpdateCheck;
using prefs::kNextExtensionsUpdateCheck;
@@ -38,6 +40,15 @@ const char* ExtensionUpdater::kExpectedGupdateProtocol = "2.0";
const char* ExtensionUpdater::kExpectedGupdateXmlns =
"http://www.google.com/update2/response";
+// NOTE: HTTPS is used here to ensure the response from omaha can be trusted.
+// The response contains a url for fetching the blacklist and a hash value
+// for validation.
+const char* ExtensionUpdater::kBlacklistUpdateUrl =
+ "https://clients2.google.com/service/update2/crx";
+
+// Update AppID for extesnion blacklist.
+const char* ExtensionUpdater::kBlacklistAppID = "com.google.crx.blacklist";
+
// Wait at least 5 minutes after browser startup before we do any checks. If you
// change this value, make sure to update comments where it is used.
const int kStartupWaitSeconds = 60 * 5;
@@ -132,6 +143,10 @@ static void EnsureInt64PrefRegistered(PrefService* prefs,
prefs->RegisterInt64Pref(name, 0);
}
+static void EnsureBlacklistVersionPrefRegistered(PrefService* prefs) {
+ if (!prefs->IsPrefRegistered(kExtensionBlacklistUpdateVersion))
+ prefs->RegisterStringPref(kExtensionBlacklistUpdateVersion, L"0");
+}
// The overall goal here is to balance keeping clients up to date while
// avoiding a thundering herd against update servers.
@@ -181,6 +196,7 @@ void ExtensionUpdater::Start() {
// Make sure our prefs are registered, then schedule the first check.
EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck);
EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck);
+ EnsureBlacklistVersionPrefRegistered(prefs_);
ScheduleNextCheck(DetermineFirstCheckDelay());
}
@@ -196,7 +212,6 @@ void ExtensionUpdater::OnURLFetchComplete(
const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
int response_code, const ResponseCookies& cookies,
const std::string& data) {
-
if (source == manifest_fetcher_.get()) {
OnManifestFetchComplete(url, status, response_code, data);
} else if (source == extension_fetcher_.get()) {
@@ -220,7 +235,8 @@ void ExtensionUpdater::OnManifestFetchComplete(const GURL& url,
std::vector<int> updates = DetermineUpdates(parsed.get());
for (size_t i = 0; i < updates.size(); i++) {
ParseResult* update = parsed[updates[i]];
- FetchUpdatedExtension(update->extension_id, update->crx_url);
+ FetchUpdatedExtension(update->extension_id, update->crx_url,
+ update->package_hash, update->version->GetString());
}
}
} else {
@@ -238,6 +254,30 @@ void ExtensionUpdater::OnManifestFetchComplete(const GURL& url,
}
}
+void ExtensionUpdater::ProcessBlacklist(const std::string& data) {
+ // Verify sha256 hash value.
+ char sha256_hash_value[base::SHA256_LENGTH];
+ base::SHA256HashString(data, sha256_hash_value, base::SHA256_LENGTH);
+ std::string hash_in_hex = HexEncode(sha256_hash_value, base::SHA256_LENGTH);
+
+ if (current_extension_fetch_.package_hash != hash_in_hex) {
+ NOTREACHED() << "Fetched blacklist checksum is not as expected. "
+ << "Expected: " << current_extension_fetch_.package_hash
+ << " Actual: " << hash_in_hex;
+ return;
+ }
+ std::vector<std::string> blacklist;
+ SplitString(data, '\n', &blacklist);
+
+ // Tell ExtensionService to update prefs.
+ service_->UpdateExtensionBlacklist(blacklist);
+
+ // Update the pref value for blacklist version
+ prefs_->SetString(kExtensionBlacklistUpdateVersion,
+ ASCIIToWide(current_extension_fetch_.version));
+ prefs_->ScheduleSavePersistentPrefs();
+}
+
void ExtensionUpdater::OnCRXFetchComplete(const GURL& url,
const URLRequestStatus& status,
int response_code,
@@ -248,11 +288,15 @@ void ExtensionUpdater::OnCRXFetchComplete(const GURL& url,
NOTREACHED();
} else if (status.status() == URLRequestStatus::SUCCESS &&
response_code == 200) {
- // Successfully fetched - now write crx to a file so we can have the
- // ExtensionsService install it.
- file_io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
- file_handler_.get(), &ExtensionUpdaterFileHandler::WriteTempFile,
- current_extension_fetch_.id, data, this));
+ if (current_extension_fetch_.id == kBlacklistAppID) {
+ ProcessBlacklist(data);
+ } else {
+ // Successfully fetched - now write crx to a file so we can have the
+ // ExtensionsService install it.
+ file_io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ file_handler_.get(), &ExtensionUpdaterFileHandler::WriteTempFile,
+ current_extension_fetch_.id, data, this));
+ }
} else {
// TODO(asargent) do things like exponential backoff, handling
// 503 Service Unavailable / Retry-After headers, etc. here.
@@ -267,7 +311,7 @@ void ExtensionUpdater::OnCRXFetchComplete(const GURL& url,
if (extensions_pending_.size() > 0) {
ExtensionFetch next = extensions_pending_.front();
extensions_pending_.pop_front();
- FetchUpdatedExtension(next.id, next.url);
+ FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
}
}
@@ -318,6 +362,14 @@ void AppendExtensionInfo(std::string* str, const Extension& extension) {
str->append("x=" + EscapeQueryParamValue(JoinString(parts, '&')));
}
+// Creates a blacklist update url.
+GURL ExtensionUpdater::GetBlacklistUpdateUrl(const std::wstring& version) {
+ std::string blklist_info = StringPrintf("id=%s&v=%s&uc", kBlacklistAppID,
+ WideToASCII(version).c_str());
+ return GURL(StringPrintf("%s?x=%s", kBlacklistUpdateUrl,
+ EscapeQueryParamValue(blklist_info).c_str()));
+}
+
void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
DCHECK(!timer_.IsRunning());
DCHECK(target_delay >= TimeDelta::FromSeconds(1));
@@ -340,6 +392,11 @@ void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
void ExtensionUpdater::TimerFired() {
// Generate a set of update urls for loaded extensions.
std::set<GURL> urls;
+
+ // We always check blacklist update url
+ urls.insert(GetBlacklistUpdateUrl(
+ prefs_->GetString(kExtensionBlacklistUpdateVersion)));
+
const ExtensionList* extensions = service_->extensions();
for (ExtensionList::const_iterator iter = extensions->begin();
iter != extensions->end(); ++iter) {
@@ -365,7 +422,6 @@ void ExtensionUpdater::TimerFired() {
urls.insert(full_url);
}
}
-
// Now do an update check for each url we found.
for (std::set<GURL>::iterator iter = urls.begin(); iter != urls.end();
++iter) {
@@ -373,7 +429,6 @@ void ExtensionUpdater::TimerFired() {
// scheduled, so we don't need to check before calling it.
StartUpdateCheck(*iter);
}
-
// Save the last check time, and schedule the next check.
int64 now = Time::Now().ToInternalValue();
prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
@@ -518,6 +573,10 @@ class ExtensionUpdater::ParseHelper {
return false;
}
}
+
+ // package_hash is optional. It is only required for blacklist. It is a
+ // sha256 hash of the package in hex format.
+ result->package_hash = GetAttribute(updatecheck, "hash");
return true;
}
};
@@ -577,6 +636,21 @@ bool ExtensionUpdater::Parse(const std::string& manifest_xml,
return true;
}
+bool ExtensionUpdater::GetExistingVersion(const std::string& id,
+ std::string* version) {
+ if (id == kBlacklistAppID) {
+ *version =
+ WideToASCII(prefs_->GetString(kExtensionBlacklistUpdateVersion));
+ return true;
+ }
+ Extension* extension = service_->GetExtensionById(id);
+ if (!extension) {
+ return false;
+ }
+ *version = extension->version()->GetString();
+ return true;
+}
+
std::vector<int> ExtensionUpdater::DetermineUpdates(
const ParseResultList& possible_updates) {
@@ -589,33 +663,36 @@ std::vector<int> ExtensionUpdater::DetermineUpdates(
for (size_t i = 0; i < possible_updates.size(); i++) {
ParseResult* update = possible_updates[i];
- Extension* extension = service_->GetExtensionById(update->extension_id);
- if (!extension)
+ std::string version;
+ if (!GetExistingVersion(update->extension_id, &version)) {
continue;
-
+ }
// If the update version is the same or older than what's already installed,
// we don't want it.
- if (update->version.get()->CompareTo(*(extension->version())) <= 0)
+ scoped_ptr<Version> existing_version(
+ Version::GetVersionFromString(version));
+ if (update->version.get()->CompareTo(*(existing_version.get())) <= 0) {
continue;
+ }
// If the update specifies a browser minimum version, do we qualify?
if (update->browser_min_version.get()) {
// First determine the browser version if we haven't already.
if (!browser_version.get()) {
scoped_ptr<FileVersionInfo> version_info(
- FileVersionInfo::CreateFileVersionInfoForCurrentModule());
+ FileVersionInfo::CreateFileVersionInfoForCurrentModule());
if (version_info.get()) {
browser_version.reset(Version::GetVersionFromString(
- version_info->product_version()));
+ version_info->product_version()));
}
}
if (browser_version.get() &&
update->browser_min_version->CompareTo(*browser_version.get()) > 0) {
// TODO(asargent) - We may want this to show up in the extensions UI
// eventually. (http://crbug.com/12547).
- LOG(WARNING) << "Updated version of extension " << extension->id() <<
- " available, but requires chrome version " <<
- update->browser_min_version->GetString();
+ LOG(WARNING) << "Updated version of extension " << update->extension_id
+ << " available, but requires chrome version "
+ << update->browser_min_version->GetString();
continue;
}
}
@@ -643,7 +720,9 @@ void ExtensionUpdater::StartUpdateCheck(const GURL& url) {
}
void ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
- const GURL& url) {
+ const GURL& url,
+ const std::string& hash,
+ const std::string& version) {
for (std::deque<ExtensionFetch>::const_iterator iter =
extensions_pending_.begin();
iter != extensions_pending_.end(); ++iter) {
@@ -654,7 +733,7 @@ void ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
if (extension_fetcher_.get() != NULL) {
if (extension_fetcher_->url() != url) {
- extensions_pending_.push_back(ExtensionFetch(id, url));
+ extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
}
} else {
extension_fetcher_.reset(
@@ -662,6 +741,6 @@ void ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
extension_fetcher_->set_request_context(
Profile::GetDefaultRequestContext());
extension_fetcher_->Start();
- current_extension_fetch_ = ExtensionFetch(id, url);
+ current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
}
}
diff --git a/chrome/browser/extensions/extension_updater.h b/chrome/browser/extensions/extension_updater.h
index 52b4542..c450e8e 100644
--- a/chrome/browser/extensions/extension_updater.h
+++ b/chrome/browser/extensions/extension_updater.h
@@ -68,7 +68,8 @@ class ExtensionUpdater
// <gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
// <app appid='12345'>
// <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'
- // version='1.2.3.4' prodversionmin='2.0.143.0' />
+ // version='1.2.3.4' prodversionmin='2.0.143.0'
+ // hash="12345"/>
// </app>
// </gupdate>
//
@@ -76,22 +77,29 @@ class ExtensionUpdater
// extension. The "codebase" attribute of the <updatecheck> tag is the url to
// fetch the updated crx file, and the "prodversionmin" attribute refers to
// the minimum version of the chrome browser that the update applies to.
+ // The hash is only required for blacklist. It is a sha256 hash value against
+ // the payload in hex format.
// The result of parsing one <app> tag in an xml update check manifest.
struct ParseResult {
std::string extension_id;
scoped_ptr<Version> version;
scoped_ptr<Version> browser_min_version;
+ std::string package_hash;
GURL crx_url;
};
- // We need to keep track of the extension id associated with a url when
- // doing a fetch.
+ // We need to keep track of some information associated with a url
+ // when doing a fetch.
struct ExtensionFetch {
std::string id;
GURL url;
- ExtensionFetch() : id(""), url() {}
- ExtensionFetch(const std::string& i, const GURL& u) : id(i), url(u) {}
+ std::string package_hash;
+ std::string version;
+ ExtensionFetch() : id(""), url(), package_hash(""), version("") {}
+ ExtensionFetch(const std::string& i, const GURL& u,
+ const std::string& h, const std::string& v)
+ : id(i), url(u), package_hash(h), version(v) {}
};
// These are needed for unit testing, to help identify the correct mock
@@ -103,6 +111,9 @@ class ExtensionUpdater
static const char* kExpectedGupdateProtocol;
static const char* kExpectedGupdateXmlns;
+ static const char* kBlacklistUpdateUrl;
+ static const char* kBlacklistAppID;
+
// Does common work from constructors.
void Init();
@@ -134,6 +145,10 @@ class ExtensionUpdater
// Callback for when ExtensionsService::Install is finished.
void OnExtensionInstallFinished(const FilePath& path, Extension* extension);
+ // Verifies downloaded blacklist. Based on the blacklist, calls extension
+ // service to unload blacklisted extensions and update pref.
+ void ProcessBlacklist(const std::string& data);
+
// Sets the timer to call TimerFired after roughly |target_delay| from now.
// To help spread load evenly on servers, this method adds some random
// jitter. It also saves the scheduled time so it can be reloaded on
@@ -147,7 +162,12 @@ class ExtensionUpdater
void StartUpdateCheck(const GURL& url);
// Begins (or queues up) download of an updated extension.
- void FetchUpdatedExtension(const std::string& id, const GURL& url);
+ void FetchUpdatedExtension(const std::string& id, const GURL& url,
+ const std::string& hash, const std::string& version);
+
+ // Determines the version of an existing extension.
+ // Returns true on success and false on failures.
+ bool GetExistingVersion(const std::string& id, std::string* version);
typedef std::vector<ParseResult*> ParseResultList;
@@ -160,6 +180,9 @@ class ExtensionUpdater
// it returns false and puts nothing into |result|.
static bool Parse(const std::string& manifest_xml, ParseResultList* result);
+ // Creates a blacklist update url.
+ static GURL GetBlacklistUpdateUrl(const std::wstring& version);
+
// Outstanding url fetch requests for manifests and updates.
scoped_ptr<URLFetcher> manifest_fetcher_;
scoped_ptr<URLFetcher> extension_fetcher_;
diff --git a/chrome/browser/extensions/extension_updater_unittest.cc b/chrome/browser/extensions/extension_updater_unittest.cc
index 7c8c2ed..9a55af1 100644
--- a/chrome/browser/extensions/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/extension_updater_unittest.cc
@@ -5,6 +5,7 @@
#include <map>
#include "base/file_util.h"
+#include "base/rand_util.h"
#include "base/string_util.h"
#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/extensions/extensions_service.h"
@@ -12,6 +13,8 @@
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_error_reporter.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
#include "net/base/escape.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,6 +28,16 @@ const char *valid_xml =
" </app>"
"</gupdate>";
+const char *valid_xml_with_hash =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>"
+" <app appid='12345'>"
+" <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'"
+" version='1.2.3.4' prodversionmin='2.0.143.0' "
+" hash='1234'/>"
+" </app>"
+"</gupdate>";
+
const char* missing_appid =
"<?xml version='1.0'?>"
"<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>"
@@ -105,6 +118,10 @@ class MockService : public ExtensionUpdateService {
return NULL;
}
+ virtual void UpdateExtensionBlacklist(
+ const std::vector<std::string>& blacklist) {
+ EXPECT_TRUE(false);
+ }
private:
DISALLOW_COPY_AND_ASSIGN(MockService);
};
@@ -114,7 +131,10 @@ class MockService : public ExtensionUpdateService {
class ScopedTempPrefService {
public:
ScopedTempPrefService() {
- FilePath pref_file = temp_dir_.path().AppendASCII("prefs");
+ // Make sure different tests won't use the same prefs file. It will cause
+ // problem when different tests are running in parallel.
+ FilePath pref_file = temp_dir_.path().AppendASCII(
+ StringPrintf("prefs_%lld", base::RandUint64()));
prefs_.reset(new PrefService(pref_file, NULL));
}
@@ -193,6 +213,27 @@ class ServiceForDownloadTests : public MockService {
FilePath install_path_;
};
+class ServiceForBlacklistTests : public MockService {
+ public:
+ ServiceForBlacklistTests()
+ : MockService(),
+ processed_blacklist_(false) {
+ }
+ virtual void UpdateExtensionBlacklist(
+ const std::vector<std::string>& blacklist) {
+ processed_blacklist_ = true;
+ return;
+ }
+ bool processed_blacklist() { return processed_blacklist_; }
+ const std::string& extension_id() { return extension_id_; }
+
+ private:
+ bool processed_blacklist_;
+ std::string extension_id_;
+ FilePath install_path_;
+};
+
+
static const int kUpdateFrequencySecs = 15;
// Takes a string with KEY=VALUE parameters separated by '&' in |params| and
@@ -260,6 +301,7 @@ class ExtensionUpdaterTest : public testing::Test {
ExtensionUpdater::ParseResult* firstResult = results->at(0);
EXPECT_EQ(GURL("http://example.com/extension_1.2.3.4.crx"),
firstResult->crx_url);
+ EXPECT_TRUE(firstResult->package_hash.empty());
scoped_ptr<Version> version(Version::GetVersionFromString("1.2.3.4"));
EXPECT_EQ(0, version->CompareTo(*firstResult->version.get()));
version.reset(Version::GetVersionFromString("2.0.143.0"));
@@ -269,9 +311,16 @@ class ExtensionUpdaterTest : public testing::Test {
results.reset();
EXPECT_TRUE(ExtensionUpdater::Parse(uses_namespace_prefix, &results.get()));
EXPECT_TRUE(ExtensionUpdater::Parse(similar_tagnames, &results.get()));
+
+ // Parse xml with hash value
+ results.reset();
+ EXPECT_TRUE(ExtensionUpdater::Parse(valid_xml_with_hash, &results.get()));
+ EXPECT_FALSE(results->empty());
+ firstResult = results->at(0);
+ EXPECT_EQ("1234", firstResult->package_hash);
}
- static void TestUpdateCheckRequests() {
+ static void TestExtensionUpdateCheckRequests() {
// Create an extension with an update_url.
ServiceForManifestTests service;
ExtensionList tmp;
@@ -319,6 +368,48 @@ class ExtensionUpdaterTest : public testing::Test {
STLDeleteElements(&tmp);
}
+ static void TestBlacklistUpdateCheckRequests() {
+ ServiceForManifestTests service;
+
+ // Setup and start the updater.
+ TestURLFetcherFactory factory;
+ URLFetcher::set_factory(&factory);
+ MessageLoop message_loop;
+ ScopedTempPrefService prefs;
+ scoped_refptr<ExtensionUpdater> updater =
+ new ExtensionUpdater(&service, prefs.get(), 60*60*24, &message_loop);
+ updater->Start();
+
+ // Tell the updater that it's time to do update checks.
+ SimulateTimerFired(updater.get());
+
+ // Get the url our mock fetcher was asked to fetch.
+ TestURLFetcher* fetcher =
+ factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+ const GURL& url = fetcher->original_url();
+
+ EXPECT_FALSE(url.is_empty());
+ EXPECT_TRUE(url.is_valid());
+ EXPECT_TRUE(url.SchemeIs("https"));
+ EXPECT_EQ("clients2.google.com", url.host());
+ EXPECT_EQ("/service/update2/crx", url.path());
+
+ // Validate the extension request parameters in the query. It should
+ // look something like "?x=id%3D<id>%26v%3D<version>%26uc".
+ EXPECT_TRUE(url.has_query());
+ std::vector<std::string> parts;
+ SplitString(url.query(), '=', &parts);
+ EXPECT_EQ(2u, parts.size());
+ EXPECT_EQ("x", parts[0]);
+ std::string decoded = UnescapeURLComponent(parts[1],
+ UnescapeRule::URL_SPECIAL_CHARS);
+ std::map<std::string, std::string> params;
+ ExtractParameters(decoded, &params);
+ EXPECT_EQ("com.google.crx.blacklist", params["id"]);
+ EXPECT_EQ("0", params["v"]);
+ EXPECT_EQ("", params["uc"]);
+ }
+
static void TestDetermineUpdates() {
// Create a set of test extensions
ServiceForManifestTests service;
@@ -403,7 +494,11 @@ class ExtensionUpdaterTest : public testing::Test {
std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
- updater->FetchUpdatedExtension(id, test_url);
+ std::string hash = "";
+
+ std::string version = "0.0.1";
+
+ updater->FetchUpdatedExtension(id, test_url, hash, version);
// Call back the ExtensionUpdater with a 200 response and some test data
std::string extension_data("whatever");
@@ -427,6 +522,49 @@ class ExtensionUpdaterTest : public testing::Test {
URLFetcher::set_factory(NULL);
}
+ static void TestBlacklistDownloading() {
+ MessageLoop message_loop;
+ TestURLFetcherFactory factory;
+ TestURLFetcher* fetcher = NULL;
+ URLFetcher::set_factory(&factory);
+ ServiceForBlacklistTests service;
+ ScopedTempPrefService prefs;
+ scoped_refptr<ExtensionUpdater> updater =
+ new ExtensionUpdater(&service, prefs.get(), kUpdateFrequencySecs,
+ &message_loop);
+ prefs.get()->
+ RegisterStringPref(prefs::kExtensionBlacklistUpdateVersion, L"0");
+ GURL test_url("http://localhost/extension.crx");
+
+ std::string id = "com.google.crx.blacklist";
+
+ std::string hash =
+ "2CE109E9D0FAF820B2434E166297934E6177B65AB9951DBC3E204CAD4689B39C";
+
+ std::string version = "0.0.1";
+
+ updater->FetchUpdatedExtension(id, test_url, hash, version);
+
+ // Call back the ExtensionUpdater with a 200 response and some test data
+ std::string extension_data("aaabbb");
+ fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId);
+ EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
+ fetcher->delegate()->OnURLFetchComplete(
+ fetcher, test_url, URLRequestStatus(), 200, ResponseCookies(),
+ extension_data);
+
+ message_loop.RunAllPending();
+
+ // The updater should have called extension service to process the
+ // blacklist.
+ EXPECT_TRUE(service.processed_blacklist());
+
+ EXPECT_EQ(version, WideToASCII(prefs.get()->
+ GetString(prefs::kExtensionBlacklistUpdateVersion)));
+
+ URLFetcher::set_factory(NULL);
+ }
+
static void TestMultipleExtensionDownloading() {
MessageLoopForUI message_loop;
TestURLFetcherFactory factory;
@@ -444,9 +582,14 @@ class ExtensionUpdaterTest : public testing::Test {
std::string id1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
std::string id2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
+ std::string hash1 = "";
+ std::string hash2 = "";
+
+ std::string version1 = "0.1";
+ std::string version2 = "0.1";
// Start two fetches
- updater->FetchUpdatedExtension(id1, url1);
- updater->FetchUpdatedExtension(id2, url2);
+ updater->FetchUpdatedExtension(id1, url1, hash1, version1);
+ updater->FetchUpdatedExtension(id2, url2, hash2, version2);
// Make the first fetch complete.
std::string extension_data1("whatever");
@@ -491,8 +634,12 @@ TEST(ExtensionUpdaterTest, TestXmlParsing) {
ExtensionUpdaterTest::TestXmlParsing();
}
-TEST(ExtensionUpdaterTest, TestUpdateCheckRequests) {
- ExtensionUpdaterTest::TestUpdateCheckRequests();
+TEST(ExtensionUpdaterTest, TestExtensionUpdateCheckRequests) {
+ ExtensionUpdaterTest::TestExtensionUpdateCheckRequests();
+}
+
+TEST(ExtensionUpdaterTest, TestBlacklistUpdateCheckRequests) {
+ ExtensionUpdaterTest::TestBlacklistUpdateCheckRequests();
}
TEST(ExtensionUpdaterTest, TestDetermineUpdates) {
@@ -507,6 +654,10 @@ TEST(ExtensionUpdaterTest, TestSingleExtensionDownloading) {
ExtensionUpdaterTest::TestSingleExtensionDownloading();
}
+TEST(ExtensionUpdaterTest, TestBlacklistDownloading) {
+ ExtensionUpdaterTest::TestBlacklistDownloading();
+}
+
TEST(ExtensionUpdaterTest, TestMultipleExtensionDownloading) {
ExtensionUpdaterTest::TestMultipleExtensionDownloading();
}
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 1b83a61..70fa3db 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -63,10 +63,11 @@ ExtensionsService::ExtensionsService(Profile* profile,
show_extensions_prompts_(true),
ready_(false) {
// Figure out if extension installation should be enabled.
- if (command_line->HasSwitch(switches::kEnableExtensions))
+ if (command_line->HasSwitch(switches::kEnableExtensions)) {
extensions_enabled_ = true;
- else if (profile->GetPrefs()->GetBoolean(prefs::kEnableExtensions))
+ } else if (profile->GetPrefs()->GetBoolean(prefs::kEnableExtensions)) {
extensions_enabled_ = true;
+ }
// Set up the ExtensionUpdater
if (autoupdate_enabled) {
@@ -172,6 +173,33 @@ void ExtensionsService::LoadAllExtensions() {
new InstalledExtensions(extension_prefs_.get())));
}
+void ExtensionsService::UpdateExtensionBlacklist(
+ const std::vector<std::string>& blacklist) {
+ // Use this set to indicate if an extension in the blacklist has been used.
+ std::set<std::string> blacklist_set;
+ for (unsigned int i = 0; i < blacklist.size(); ++i) {
+ if (Extension::IdIsValid(blacklist[i])) {
+ blacklist_set.insert(blacklist[i]);
+ }
+ }
+ extension_prefs_->UpdateBlacklist(blacklist_set);
+ std::vector<std::string> to_be_removed;
+ // Loop current extensions, unload installed extensions.
+ for (ExtensionList::const_iterator iter = extensions_.begin();
+ iter != extensions_.end(); ++iter) {
+ Extension* extension = (*iter);
+ if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
+ to_be_removed.push_back(extension->id());
+ }
+ }
+
+ // UnloadExtension will change the extensions_ list. So, we should
+ // call it outside the iterator loop.
+ for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
+ UnloadExtension(to_be_removed[i]);
+ }
+}
+
void ExtensionsService::CheckForExternalUpdates() {
// This installs or updates externally provided extensions.
// TODO(aa): Why pass this list into the provider, why not just filter it
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index 640700e..fe0a545 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -45,6 +45,8 @@ class ExtensionUpdateService {
virtual const ExtensionList* extensions() const = 0;
virtual void UpdateExtension(const std::string& id, const FilePath& path) = 0;
virtual Extension* GetExtensionById(const std::string& id) = 0;
+ virtual void UpdateExtensionBlacklist(
+ const std::vector<std::string>& blacklist) = 0;
};
// Manages installed and running Chromium extensions.
@@ -175,6 +177,11 @@ class ExtensionsService
const FilePath& path,
Extension::Location location);
+ // Go through each extensions in pref, unload blacklisted extensions
+ // and update the blacklist state in pref.
+ virtual void UpdateExtensionBlacklist(
+ const std::vector<std::string>& blacklist);
+
void set_extensions_enabled(bool enabled) { extensions_enabled_ = enabled; }
bool extensions_enabled() { return extensions_enabled_; }
diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc
index 0356ed6..3f464d5 100644
--- a/chrome/browser/extensions/extensions_service_unittest.cc
+++ b/chrome/browser/extensions/extensions_service_unittest.cc
@@ -386,9 +386,49 @@ class ExtensionsServiceTest
EXPECT_EQ(count, dict->GetSize());
}
- void ValidatePref(const std::string& extension_id,
- const std::wstring& pref_path,
- int must_equal) {
+ void ValidateBooleanPref(const std::string& extension_id,
+ const std::wstring& pref_path,
+ bool must_equal) {
+ std::wstring msg = L" while checking: ";
+ msg += ASCIIToWide(extension_id);
+ msg += L" ";
+ msg += pref_path;
+ msg += L" == ";
+ msg += must_equal ? L"true" : L"false";
+
+ const DictionaryValue* dict =
+ prefs_->GetDictionary(L"extensions.settings");
+ ASSERT_TRUE(dict != NULL) << msg;
+ DictionaryValue* pref = NULL;
+ ASSERT_TRUE(dict->GetDictionary(ASCIIToWide(extension_id), &pref)) << msg;
+ EXPECT_TRUE(pref != NULL) << msg;
+ bool val;
+ pref->GetBoolean(pref_path, &val);
+ EXPECT_EQ(must_equal, val) << msg;
+ }
+
+ bool IsPrefExist(const std::string& extension_id,
+ const std::wstring& pref_path) {
+ const DictionaryValue* dict =
+ prefs_->GetDictionary(L"extensions.settings");
+ if (dict == NULL) return false;
+ DictionaryValue* pref = NULL;
+ if (!dict->GetDictionary(ASCIIToWide(extension_id), &pref)) {
+ return false;
+ }
+ if (pref == NULL) {
+ return false;
+ }
+ bool val;
+ if (!pref->GetBoolean(pref_path, &val)) {
+ return false;
+ }
+ return true;
+ }
+
+ void ValidateIntegerPref(const std::string& extension_id,
+ const std::wstring& pref_path,
+ int must_equal) {
std::wstring msg = L" while checking: ";
msg += ASCIIToWide(extension_id);
msg += L" ";
@@ -407,9 +447,9 @@ class ExtensionsServiceTest
EXPECT_EQ(must_equal, val) << msg;
}
- void SetPref(const std::string& extension_id,
- const std::wstring& pref_path,
- int value) {
+ void SetPrefInteg(const std::string& extension_id,
+ const std::wstring& pref_path,
+ int value) {
std::wstring msg = L" while setting: ";
msg += ASCIIToWide(extension_id);
msg += L" ";
@@ -485,12 +525,12 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectorySuccess) {
EXPECT_EQ(3u, service_->extensions()->size());
ValidatePrefKeyCount(3);
- ValidatePref(good0, L"state", Extension::ENABLED);
- ValidatePref(good0, L"location", Extension::INTERNAL);
- ValidatePref(good1, L"state", Extension::ENABLED);
- ValidatePref(good1, L"location", Extension::INTERNAL);
- ValidatePref(good2, L"state", Extension::ENABLED);
- ValidatePref(good2, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(good0, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good0, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(good1, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good1, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(good2, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good2, L"location", Extension::INTERNAL);
Extension* extension = loaded_[0];
const UserScriptList& scripts = extension->content_scripts();
@@ -658,15 +698,15 @@ TEST_F(ExtensionsServiceTest, InstallExtension) {
int pref_count = 0;
ValidatePrefKeyCount(++pref_count);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::INTERNAL);
// An extension with page actions.
path = extensions_path.AppendASCII("page_action.crx");
InstallExtension(path, true);
ValidatePrefKeyCount(++pref_count);
- ValidatePref(page_action, L"state", Extension::ENABLED);
- ValidatePref(page_action, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(page_action, L"state", Extension::ENABLED);
+ ValidateIntegerPref(page_action, L"location", Extension::INTERNAL);
// Bad signature.
path = extensions_path.AppendASCII("bad_signature.crx");
@@ -764,8 +804,8 @@ TEST_F(ExtensionsServiceTest, InstallTheme) {
InstallExtension(path, true);
int pref_count = 0;
ValidatePrefKeyCount(++pref_count);
- ValidatePref(theme_crx, L"state", Extension::ENABLED);
- ValidatePref(theme_crx, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(theme_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(theme_crx, L"location", Extension::INTERNAL);
// A theme when extensions are disabled. Themes can be installed, even when
// extensions are disabled.
@@ -773,8 +813,8 @@ TEST_F(ExtensionsServiceTest, InstallTheme) {
path = extensions_path.AppendASCII("theme2.crx");
InstallExtension(path, true);
ValidatePrefKeyCount(++pref_count);
- ValidatePref(theme2_crx, L"state", Extension::ENABLED);
- ValidatePref(theme2_crx, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(theme2_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(theme2_crx, L"location", Extension::INTERNAL);
// A theme with extension elements. Themes cannot have extension elements so
// this test should fail.
@@ -805,8 +845,8 @@ TEST_F(ExtensionsServiceTest, Reinstall) {
ASSERT_EQ(1u, loaded_.size());
ASSERT_EQ(0u, GetErrors().size());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::INTERNAL);
installed_ = NULL;
loaded_.clear();
@@ -820,8 +860,8 @@ TEST_F(ExtensionsServiceTest, Reinstall) {
ASSERT_EQ(0u, loaded_.size());
ASSERT_EQ(0u, GetErrors().size());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::INTERNAL);
}
// Test upgrading a signed extension.
@@ -948,6 +988,130 @@ TEST_F(ExtensionsServiceTest, UpdateToSameVersionIsNoop) {
UpdateExtension(good_crx, path, false, false);
}
+// Test pref settings for blacklist and unblacklist extensions.
+TEST_F(ExtensionsServiceTest, SetUnsetBlacklistInPrefs) {
+ InitializeEmptyExtensionsService();
+ std::vector<std::string> blacklist;
+ blacklist.push_back(good0);
+ blacklist.push_back("invalid_id"); // an invalid id
+ blacklist.push_back(good1);
+ service_->UpdateExtensionBlacklist(blacklist);
+ // Make sure pref is updated
+ loop_.RunAllPending();
+
+ // blacklist is set for good0,1,2
+ ValidateBooleanPref(good0, L"blacklist", true);
+ ValidateBooleanPref(good1, L"blacklist", true);
+ // invalid_id should not be inserted to pref.
+ EXPECT_FALSE(IsPrefExist("invalid_id", L"blacklist"));
+
+ // remove good1, add good2
+ blacklist.pop_back();
+ blacklist.push_back(good2);
+
+ service_->UpdateExtensionBlacklist(blacklist);
+ // only good0 and good1 should be set
+ ValidateBooleanPref(good0, L"blacklist", true);
+ EXPECT_FALSE(IsPrefExist(good1, L"blacklist"));
+ ValidateBooleanPref(good2, L"blacklist", true);
+ EXPECT_FALSE(IsPrefExist("invalid_id", L"blacklist"));
+}
+
+// Unload installed extension from blacklist.
+TEST_F(ExtensionsServiceTest, UnloadBlacklistedExtension) {
+ InitializeEmptyExtensionsService();
+ FilePath extensions_path;
+ EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
+ extensions_path = extensions_path.AppendASCII("extensions");
+
+ FilePath path = extensions_path.AppendASCII("good.crx");
+
+ InstallExtension(path, true);
+ Extension* good = service_->extensions()->at(0);
+ EXPECT_EQ(good_crx, good->id());
+ UpdateExtension(good_crx, path, false, false);
+
+ std::vector<std::string> blacklist;
+ blacklist.push_back(good_crx);
+ service_->UpdateExtensionBlacklist(blacklist);
+ // Make sure pref is updated
+ loop_.RunAllPending();
+
+ // Now, the good_crx is blacklisted.
+ ValidateBooleanPref(good_crx, L"blacklist", true);
+ EXPECT_EQ(0u, service_->extensions()->size());
+
+ // Remove good_crx from blacklist
+ blacklist.pop_back();
+ service_->UpdateExtensionBlacklist(blacklist);
+ // Make sure pref is updated
+ loop_.RunAllPending();
+ // blacklist value should not be set for good_crx
+ EXPECT_FALSE(IsPrefExist(good_crx, L"blacklist"));
+}
+
+// Unload installed extension from blacklist.
+TEST_F(ExtensionsServiceTest, BlacklistedExtensionWillNotInstall) {
+ InitializeEmptyExtensionsService();
+ std::vector<std::string> blacklist;
+ blacklist.push_back(good_crx);
+ service_->UpdateExtensionBlacklist(blacklist);
+ // Make sure pref is updated
+ loop_.RunAllPending();
+
+ // Now, the good_crx is blacklisted.
+ ValidateBooleanPref(good_crx, L"blacklist", true);
+
+ // We can not install good_crx.
+ FilePath extensions_path;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
+ extensions_path = extensions_path.AppendASCII("extensions");
+ FilePath path = extensions_path.AppendASCII("good.crx");
+ service_->InstallExtension(path);
+ loop_.RunAllPending();
+ EXPECT_EQ(0u, service_->extensions()->size());
+ ValidateBooleanPref(good_crx, L"blacklist", true);
+}
+
+// Test loading extensions from the profile directory, except
+// blacklisted ones.
+TEST_F(ExtensionsServiceTest, WillNotLoadBlacklistedExtensionsFromDirectory) {
+ // Initialize the test dir with a good Preferences/extensions.
+ FilePath source_install_dir;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_install_dir));
+ source_install_dir = source_install_dir
+ .AppendASCII("extensions")
+ .AppendASCII("good")
+ .AppendASCII("Extensions");
+ FilePath pref_path = source_install_dir
+ .DirName()
+ .AppendASCII("Preferences");
+ InitializeInstalledExtensionsService(pref_path, source_install_dir);
+
+ // Blacklist good0.
+ std::vector<std::string> blacklist;
+ blacklist.push_back(good0);
+ service_->UpdateExtensionBlacklist(blacklist);
+ // Make sure pref is updated
+ loop_.RunAllPending();
+
+ ValidateBooleanPref(good0, L"blacklist", true);
+
+ // Load extensions.
+ service_->Init();
+ loop_.RunAllPending();
+
+ std::vector<std::string> errors = GetErrors();
+ for (std::vector<std::string>::iterator err = errors.begin();
+ err != errors.end(); ++err) {
+ LOG(ERROR) << *err;
+ }
+ EXPECT_EQ(2u, loaded_.size());
+
+ EXPECT_NE(std::string(good0), loaded_[0]->id());
+ EXPECT_NE(std::string(good0), loaded_[1]->id());
+}
+
// Tests uninstalling normal extensions
TEST_F(ExtensionsServiceTest, UninstallExtension) {
InitializeEmptyExtensionsService();
@@ -965,8 +1129,8 @@ TEST_F(ExtensionsServiceTest, UninstallExtension) {
EXPECT_TRUE(file_util::PathExists(extension_path));
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::INTERNAL);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::INTERNAL);
// Uninstall it.
service_->UninstallExtension(extension_id, false);
@@ -1113,8 +1277,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
ASSERT_EQ(Extension::EXTERNAL_REGISTRY, loaded_[0]->location());
ASSERT_EQ("1.0.0.0", loaded_[0]->version()->GetString());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
// Reload extensions without changing anything. The extension should be
// loaded again.
@@ -1124,8 +1288,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
ASSERT_EQ(0u, GetErrors().size());
ASSERT_EQ(1u, loaded_.size());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
// Now update the extension with a new version. We should get upgraded.
source_path = source_path.DirName().AppendASCII("good2.crx");
@@ -1138,8 +1302,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
ASSERT_EQ(1u, loaded_.size());
ASSERT_EQ("1.0.0.1", loaded_[0]->version()->GetString());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
// Uninstall the extension and reload. Nothing should happen because the
// preference should prevent us from reinstalling.
@@ -1156,12 +1320,12 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
loop_.RunAllPending();
ASSERT_EQ(0u, loaded_.size());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::KILLBIT); // It is an ex-parrot.
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
+ ValidateIntegerPref(good_crx, L"state", Extension::KILLBIT);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
// Now clear the preference, reinstall, then remove the reg key. The extension
// should be uninstalled.
- SetPref(good_crx, L"state", Extension::ENABLED);
+ SetPrefInteg(good_crx, L"state", Extension::ENABLED);
prefs_->ScheduleSavePersistentPrefs();
loaded_.clear();
@@ -1169,8 +1333,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
loop_.RunAllPending();
ASSERT_EQ(1u, loaded_.size());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
// Now test an externally triggered uninstall (deleting the registry key).
reg_provider->RemoveExtension(good_crx);
@@ -1217,8 +1381,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
ASSERT_EQ(Extension::EXTERNAL_PREF, loaded_[0]->location());
ASSERT_EQ("1.0.0.0", loaded_[0]->version()->GetString());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_PREF);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_PREF);
// Reload extensions without changing anything. The extension should be
// loaded again.
@@ -1228,8 +1392,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
ASSERT_EQ(0u, GetErrors().size());
ASSERT_EQ(1u, loaded_.size());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_PREF);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_PREF);
// Now update the extension with a new version. We should get upgraded.
source_path = source_path.DirName().AppendASCII("good2.crx");
@@ -1242,8 +1406,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
ASSERT_EQ(1u, loaded_.size());
ASSERT_EQ("1.0.0.1", loaded_[0]->version()->GetString());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_PREF);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_PREF);
// Uninstall the extension and reload. Nothing should happen because the
// preference should prevent us from reinstalling.
@@ -1260,11 +1424,11 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
loop_.RunAllPending();
ASSERT_EQ(0u, loaded_.size());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::KILLBIT);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_PREF);
+ ValidateIntegerPref(good_crx, L"state", Extension::KILLBIT);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_PREF);
// Now clear the preference and reinstall.
- SetPref(good_crx, L"state", Extension::ENABLED);
+ SetPrefInteg(good_crx, L"state", Extension::ENABLED);
prefs_->ScheduleSavePersistentPrefs();
loaded_.clear();
@@ -1272,8 +1436,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
loop_.RunAllPending();
ASSERT_EQ(1u, loaded_.size());
ValidatePrefKeyCount(1);
- ValidatePref(good_crx, L"state", Extension::ENABLED);
- ValidatePref(good_crx, L"location", Extension::EXTERNAL_PREF);
+ ValidateIntegerPref(good_crx, L"state", Extension::ENABLED);
+ ValidateIntegerPref(good_crx, L"location", Extension::EXTERNAL_PREF);
// Now test an externally triggered uninstall (deleting id from json file).
pref_provider->RemoveExtension(good_crx);
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 2414dc1..c86671f 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -532,6 +532,9 @@ const wchar_t kLastExtensionsUpdateCheck[] =
L"extensions.autoupdate.last_check";
const wchar_t kNextExtensionsUpdateCheck[] =
L"extensions.autoupdate.next_check";
+// Version number of last blacklist check
+const wchar_t kExtensionBlacklistUpdateVersion[] =
+ L"extensions.blacklistupdate.version";
// New Tab Page URLs that should not be shown as most visited thumbnails.
const wchar_t kNTPMostVisitedURLsBlacklist[] = L"ntp.most_visited_blacklist";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index d620347..94e8a5e 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -200,6 +200,8 @@ extern const wchar_t kEnableUserScripts[];
extern const wchar_t kLastExtensionsUpdateCheck[];
extern const wchar_t kNextExtensionsUpdateCheck[];
+extern const wchar_t kExtensionBlacklistUpdateVersion[];
+
extern const wchar_t kNTPMostVisitedURLsBlacklist[];
extern const wchar_t kNTPMostVisitedPinnedURLs[];
extern const wchar_t kNTPTipsCache[];