Split ExtensionUpdater into two smaller classes.
The new ExtensionDownloader takes care of fetching manifests, parsing them,
and downloading crx files. These are then passed to a Delegate.
The ExtensionUpdater is one such Delegate, and does the same as before.
This makes it possible for other components to download extensions given an ID
and update URL; in particular, the retail-mode AppPack for chromeos.
BUG=chromium-os:25463
TEST=Exactly everything about installing/updating extensions works exactly the same. All tests pass.
Review URL: https://chromiumcodereview.appspot.com/9695019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127206 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index 20c0dbfae..2688231 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -33,7 +33,6 @@
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/infobars/infobar_tab_helper.h"
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index df2dc3f..772243d 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -55,8 +55,8 @@
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/importer/importer_host.h"
#include "chrome/browser/importer/importer_list.h"
@@ -6825,7 +6825,7 @@
return;
}
- ExtensionUpdater* updater = service->updater();
+ extensions::ExtensionUpdater* updater = service->updater();
if (!updater) {
AutomationJSONReply(this, reply_message).SendError(
"No updater for extensions service.");
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index 8f6584a..f35cb0c7 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -22,6 +22,10 @@
class ExtensionService;
class SkBitmap;
+namespace extensions {
+class ExtensionUpdaterTest;
+}
+
// This class installs a crx file into a profile.
//
// Installing a CRX is a multi-step process, including unpacking the crx,
@@ -185,7 +189,7 @@
Profile* profile() { return profile_; }
private:
- friend class ExtensionUpdaterTest;
+ friend class extensions::ExtensionUpdaterTest;
CrxInstaller(base::WeakPtr<ExtensionService> frontend_weak,
ExtensionInstallUI* client);
diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc
index a9e7c23..7841c420 100644
--- a/chrome/browser/extensions/extension_browsertests_misc.cc
+++ b/chrome/browser/extensions/extension_browsertests_misc.cc
@@ -13,7 +13,6 @@
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc
index 647e37e..ede37af6 100644
--- a/chrome/browser/extensions/extension_management_api.cc
+++ b/chrome/browser/extensions/extension_management_api.cc
@@ -17,7 +17,6 @@
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_management_api_constants.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
diff --git a/chrome/browser/extensions/extension_management_browsertest.cc b/chrome/browser/extensions/extension_management_browsertest.cc
index 8ef75a6..6d532c1 100644
--- a/chrome/browser/extensions/extension_management_browsertest.cc
+++ b/chrome/browser/extensions/extension_management_browsertest.cc
@@ -9,7 +9,7 @@
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
-#include "chrome/browser/extensions/extension_updater.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/infobars/infobar_tab_helper.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index c0252ad9..c0a403e73 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -1770,7 +1770,7 @@
prefs->RegisterDictionaryPref(kExtensionsPref, PrefService::UNSYNCABLE_PREF);
prefs->RegisterListPref(kExtensionToolbar, PrefService::UNSYNCABLE_PREF);
prefs->RegisterIntegerPref(prefs::kExtensionToolbarSize,
- -1,
+ -1, // default value
PrefService::UNSYNCABLE_PREF);
prefs->RegisterDictionaryPref(kExtensionsBlacklistUpdate,
PrefService::UNSYNCABLE_PREF);
@@ -1781,6 +1781,15 @@
prefs->RegisterListPref(prefs::kExtensionInstallForceList,
PrefService::UNSYNCABLE_PREF);
prefs->RegisterStringPref(kWebStoreLogin,
- std::string() /* default_value */,
+ std::string(), // default value
PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterStringPref(prefs::kExtensionBlacklistUpdateVersion,
+ "0", // default value
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterInt64Pref(prefs::kLastExtensionsUpdateCheck,
+ 0, // default value
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterInt64Pref(prefs::kNextExtensionsUpdateCheck,
+ 0, // default value
+ PrefService::UNSYNCABLE_PREF);
}
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 3c0eb11..af61980d2 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -52,7 +52,6 @@
#include "chrome/browser/extensions/extension_sorting.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/browser/extensions/extension_sync_data.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/extensions/extension_web_ui.h"
#include "chrome/browser/extensions/extension_webnavigation_api.h"
#include "chrome/browser/extensions/external_extension_provider_impl.h"
@@ -62,6 +61,7 @@
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/settings/settings_frontend.h"
#include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/history/history_extension_api.h"
#include "chrome/browser/net/chrome_url_request_context.h"
#include "chrome/browser/prefs/pref_service.h"
@@ -417,11 +417,11 @@
switches::kExtensionsUpdateFrequency),
&update_frequency);
}
- updater_.reset(new ExtensionUpdater(this,
- extension_prefs,
- profile->GetPrefs(),
- profile,
- update_frequency));
+ updater_.reset(new extensions::ExtensionUpdater(this,
+ extension_prefs,
+ profile->GetPrefs(),
+ profile,
+ update_frequency));
}
component_loader_.reset(
@@ -1223,7 +1223,7 @@
return ready_;
}
-ExtensionUpdater* ExtensionService::updater() {
+extensions::ExtensionUpdater* ExtensionService::updater() {
return updater_.get();
}
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index c090504..4c133d8a 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -59,7 +59,6 @@
class ExtensionPreferenceEventRouter;
class ExtensionSyncData;
class ExtensionToolbarModel;
-class ExtensionUpdater;
class ExtensionWebNavigationEventRouter;
class HistoryExtensionEventRouter;
class GURL;
@@ -75,6 +74,7 @@
namespace extensions {
class APIResourceController;
class ComponentLoader;
+class ExtensionUpdater;
class RulesRegistryService;
class SettingsFrontend;
}
@@ -460,7 +460,7 @@
}
// Note that this may return NULL if autoupdate is not turned on.
- ExtensionUpdater* updater();
+ extensions::ExtensionUpdater* updater();
ExtensionToolbarModel* toolbar_model() { return &toolbar_model_; }
@@ -753,7 +753,7 @@
bool ready_;
// Our extension updater, if updates are turned on.
- scoped_ptr<ExtensionUpdater> updater_;
+ scoped_ptr<extensions::ExtensionUpdater> updater_;
// The model that tracks extensions with BrowserAction buttons.
ExtensionToolbarModel toolbar_model_;
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 58c161b..65df9afe 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -36,7 +36,6 @@
#include "chrome/browser/extensions/extension_sorting.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/browser/extensions/extension_sync_data.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/extensions/external_extension_provider_impl.h"
#include "chrome/browser/extensions/external_extension_provider_interface.h"
#include "chrome/browser/extensions/external_pref_extension_loader.h"
@@ -45,6 +44,7 @@
#include "chrome/browser/extensions/pending_extension_info.h"
#include "chrome/browser/extensions/pending_extension_manager.h"
#include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/plugin_prefs_factory.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/prefs/pref_service_mock_builder.h"
diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc
deleted file mode 100644
index 8d8483f..0000000
--- a/chrome/browser/extensions/extension_updater.cc
+++ /dev/null
@@ -1,1329 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/extension_updater.h"
-
-#include <algorithm>
-#include <set>
-
-#include "base/bind.h"
-#include "base/compiler_specific.h"
-#include "base/file_util.h"
-#include "base/logging.h"
-#include "base/memory/scoped_handle.h"
-#include "base/metrics/histogram.h"
-#include "base/rand_util.h"
-#include "base/stl_util.h"
-#include "base/string_number_conversions.h"
-#include "base/string_split.h"
-#include "base/string_util.h"
-#include "base/threading/thread.h"
-#include "base/time.h"
-#include "base/version.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/extensions/crx_installer.h"
-#include "chrome/browser/extensions/extension_error_reporter.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/google/google_util.h"
-#include "chrome/browser/metrics/metrics_service.h"
-#include "chrome/browser/prefs/pref_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_notification_types.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/chrome_utility_messages.h"
-#include "chrome/common/chrome_version_info.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/extensions/extension_file_util.h"
-#include "chrome/common/pref_names.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/resource_dispatcher_host.h"
-#include "content/public/browser/utility_process_host.h"
-#include "content/public/common/url_fetcher.h"
-#include "crypto/sha2.h"
-#include "googleurl/src/gurl.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/url_request/url_request_status.h"
-
-#if defined(OS_MACOSX)
-#include "base/sys_string_conversions.h"
-#endif
-
-#define SEND_ACTIVE_PINGS 1
-
-using base::RandDouble;
-using base::RandInt;
-using base::Time;
-using base::TimeDelta;
-using content::BrowserThread;
-using content::UtilityProcessHost;
-using content::UtilityProcessHostClient;
-using prefs::kExtensionBlacklistUpdateVersion;
-using prefs::kLastExtensionsUpdateCheck;
-using prefs::kNextExtensionsUpdateCheck;
-
-// Update AppID for extension blacklist.
-const char ExtensionUpdater::kBlacklistAppID[] = "com.google.crx.blacklist";
-
-const char kNotFromWebstoreInstallSource[] = "notfromwebstore";
-const char kDefaultInstallSource[] = "";
-
-namespace {
-
-// 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;
-
-// For sanity checking on update frequency - enforced in release mode only.
-static const int kMinUpdateFrequencySeconds = 30;
-static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days
-
-// Maximum length of an extension manifest update check url, since it is a GET
-// request. We want to stay under 2K because of proxies, etc.
-static const int kExtensionsManifestMaxURLSize = 2000;
-
-// TODO(skerner): It would be nice to know if the file system failure
-// happens when creating a temp file or when writing to it. Knowing this
-// will require changes to URLFetcher.
-enum FileWriteResult {
- SUCCESS = 0,
- CANT_CREATE_OR_WRITE_TEMP_CRX,
- CANT_READ_CRX_FILE,
- NUM_FILE_WRITE_RESULTS
-};
-
-// Prototypes allow the functions to be defined in the order they run.
-void CheckThatCRXIsReadable(const FilePath& crx_path);
-void RecordFileUpdateHistogram(FileWriteResult file_write_result);
-
-// Record the result of writing a CRX file. Will be used to understand
-// high failure rates of CRX installs in the field. If |success| is
-// true, |crx_path| should be set to the path to the CRX file.
-void RecordCRXWriteHistogram(bool success, const FilePath& crx_path) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (!success) {
- // We know there was an error writing the file.
- RecordFileUpdateHistogram(CANT_CREATE_OR_WRITE_TEMP_CRX);
-
- } else {
- // Test that the file can be read. Based on histograms in
- // SandboxExtensionUnpacker, we know that many CRX files
- // can not be read. Try reading.
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&CheckThatCRXIsReadable, crx_path));
- }
-}
-
-void CheckThatCRXIsReadable(const FilePath& crx_path) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
- FileWriteResult file_write_result = SUCCESS;
-
- // Open the file in the same way
- // SandboxExtensionUnpacker::ValidateSigniture() will.
- ScopedStdioHandle file(file_util::OpenFile(crx_path, "rb"));
- if (!file.get()) {
- LOG(ERROR) << "Can't read CRX file written for update at path "
- << crx_path.value().c_str();
- file_write_result = CANT_READ_CRX_FILE;
- }
-
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&RecordFileUpdateHistogram, file_write_result));
-}
-
-void RecordFileUpdateHistogram(FileWriteResult file_write_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- UMA_HISTOGRAM_ENUMERATION("Extensions.UpdaterWriteCrxAsFile",
- file_write_result,
- NUM_FILE_WRITE_RESULTS);
-}
-
-} // namespace
-
-ManifestFetchData::ManifestFetchData(const GURL& update_url)
- : base_url_(update_url),
- full_url_(update_url) {
-}
-
-ManifestFetchData::~ManifestFetchData() {}
-
-// The format for request parameters in update checks is:
-//
-// ?x=EXT1_INFO&x=EXT2_INFO
-//
-// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
-//
-// id=EXTENSION_ID&v=VERSION&uc
-//
-// Additionally, we may include the parameter ping=PING_DATA where PING_DATA
-// looks like r=DAYS or a=DAYS for extensions in the Chrome extensions gallery.
-// ('r' refers to 'roll call' ie installation, and 'a' refers to 'active').
-// These values will each be present at most once every 24 hours, and indicate
-// the number of days since the last time it was present in an update check.
-//
-// So for two extensions like:
-// Extension 1- id:aaaa version:1.1
-// Extension 2- id:bbbb version:2.0
-//
-// the full update url would be:
-// http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
-//
-// (Note that '=' is %3D and '&' is %26 when urlencoded.)
-bool ManifestFetchData::AddExtension(std::string id, std::string version,
- const PingData& ping_data,
- const std::string& update_url_data,
- const std::string& install_source) {
- if (extension_ids_.find(id) != extension_ids_.end()) {
- NOTREACHED() << "Duplicate extension id " << id;
- return false;
- }
-
- // Compute the string we'd append onto the full_url_, and see if it fits.
- std::vector<std::string> parts;
- parts.push_back("id=" + id);
- parts.push_back("v=" + version);
- if (!install_source.empty())
- parts.push_back("installsource=" + install_source);
- parts.push_back("uc");
-
- if (!update_url_data.empty()) {
- // Make sure the update_url_data string is escaped before using it so that
- // there is no chance of overriding the id or v other parameter value
- // we place into the x= value.
- parts.push_back("ap=" + net::EscapeQueryParamValue(update_url_data, true));
- }
-
- // Append brand code, rollcall and active ping parameters.
- if (base_url_.DomainIs("google.com")) {
-#if defined(GOOGLE_CHROME_BUILD)
- std::string brand;
- google_util::GetBrand(&brand);
- if (!brand.empty() && !google_util::IsOrganic(brand))
- parts.push_back("brand=" + brand);
-#endif
-
- std::string ping_value;
- pings_[id] = PingData(0, 0);
-
- if (ping_data.rollcall_days == kNeverPinged ||
- ping_data.rollcall_days > 0) {
- ping_value += "r=" + base::IntToString(ping_data.rollcall_days);
- pings_[id].rollcall_days = ping_data.rollcall_days;
- }
-#if SEND_ACTIVE_PINGS
- if (ping_data.active_days == kNeverPinged || ping_data.active_days > 0) {
- if (!ping_value.empty())
- ping_value += "&";
- ping_value += "a=" + base::IntToString(ping_data.active_days);
- pings_[id].active_days = ping_data.active_days;
- }
-#endif // SEND_ACTIVE_PINGS
- if (!ping_value.empty())
- parts.push_back("ping=" + net::EscapeQueryParamValue(ping_value, true));
- }
-
- std::string extra = full_url_.has_query() ? "&" : "?";
- extra += "x=" + net::EscapeQueryParamValue(JoinString(parts, '&'), true);
-
- // Check against our max url size, exempting the first extension added.
- int new_size = full_url_.possibly_invalid_spec().size() + extra.size();
- if (!extension_ids_.empty() && new_size > kExtensionsManifestMaxURLSize) {
- UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1);
- return false;
- }
- UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0);
-
- // We have room so go ahead and add the extension.
- extension_ids_.insert(id);
- full_url_ = GURL(full_url_.possibly_invalid_spec() + extra);
- return true;
-}
-
-bool ManifestFetchData::Includes(const std::string& extension_id) const {
- return extension_ids_.find(extension_id) != extension_ids_.end();
-}
-
-bool ManifestFetchData::DidPing(std::string extension_id, PingType type) const {
- std::map<std::string, PingData>::const_iterator i = pings_.find(extension_id);
- if (i == pings_.end())
- return false;
- int value = 0;
- if (type == ROLLCALL)
- value = i->second.rollcall_days;
- else if (type == ACTIVE)
- value = i->second.active_days;
- else
- NOTREACHED();
- return value == kNeverPinged || value > 0;
-}
-
-namespace {
-
-// When we've computed a days value, we want to make sure we don't send a
-// negative value (due to the system clock being set backwards, etc.), since -1
-// is a special sentinel value that means "never pinged", and other negative
-// values don't make sense.
-static int SanitizeDays(int days) {
- if (days < 0)
- return 0;
- return days;
-}
-
-// Calculates the value to use for the ping days parameter.
-static int CalculatePingDays(const Time& last_ping_day) {
- int days = ManifestFetchData::kNeverPinged;
- if (!last_ping_day.is_null()) {
- days = SanitizeDays((Time::Now() - last_ping_day).InDays());
- }
- return days;
-}
-
-static int CalculateActivePingDays(const Time& last_active_ping_day,
- bool hasActiveBit) {
- if (!hasActiveBit)
- return 0;
- if (last_active_ping_day.is_null())
- return ManifestFetchData::kNeverPinged;
- return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
-}
-
-} // namespace
-
-ManifestFetchesBuilder::ManifestFetchesBuilder(
- ExtensionServiceInterface* service,
- ExtensionPrefs* prefs)
- : service_(service), prefs_(prefs) {
- DCHECK(service_);
- DCHECK(prefs_);
-}
-
-ManifestFetchesBuilder::~ManifestFetchesBuilder() {}
-
-void ManifestFetchesBuilder::AddExtension(const Extension& extension) {
- // Skip extensions with empty update URLs converted from user
- // scripts.
- if (extension.converted_from_user_script() &&
- extension.update_url().is_empty()) {
- return;
- }
-
- // If the extension updates itself from the gallery, ignore any update URL
- // data. At the moment there is no extra data that an extension can
- // communicate to the the gallery update servers.
- std::string update_url_data;
- if (!extension.UpdatesFromGallery())
- update_url_data = prefs_->GetUpdateUrlData(extension.id());
-
- AddExtensionData(extension.location(),
- extension.id(),
- *extension.version(),
- extension.GetType(),
- extension.update_url(), update_url_data);
-}
-
-void ManifestFetchesBuilder::AddPendingExtension(
- const std::string& id,
- Extension::Location install_source,
- const GURL& update_url) {
- // Use a zero version to ensure that a pending extension will always
- // be updated, and thus installed (assuming all extensions have
- // non-zero versions).
- Version version("0.0.0.0");
- DCHECK(version.IsValid());
-
- AddExtensionData(install_source, id, version,
- Extension::TYPE_UNKNOWN, update_url, "");
-}
-
-void ManifestFetchesBuilder::ReportStats() const {
- UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension",
- url_stats_.extension_count);
- UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
- url_stats_.theme_count);
- UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp",
- url_stats_.app_count);
- UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending",
- url_stats_.pending_count);
- UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
- url_stats_.google_url_count);
- UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
- url_stats_.other_url_count);
- UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
- url_stats_.no_url_count);
-}
-
-std::vector<ManifestFetchData*> ManifestFetchesBuilder::GetFetches() {
- std::vector<ManifestFetchData*> fetches;
- fetches.reserve(fetches_.size());
- for (std::multimap<GURL, ManifestFetchData*>::iterator it =
- fetches_.begin(); it != fetches_.end(); ++it) {
- fetches.push_back(it->second);
- }
- fetches_.clear();
- url_stats_ = URLStats();
- return fetches;
-}
-
-void ManifestFetchesBuilder::AddExtensionData(
- Extension::Location location,
- const std::string& id,
- const Version& version,
- Extension::Type extension_type,
- GURL update_url,
- const std::string& update_url_data) {
- if (!Extension::IsAutoUpdateableLocation(location)) {
- return;
- }
-
- // Skip extensions with non-empty invalid update URLs.
- if (!update_url.is_empty() && !update_url.is_valid()) {
- LOG(WARNING) << "Extension " << id << " has invalid update url "
- << update_url;
- return;
- }
-
- // Skip extensions with empty IDs.
- if (id.empty()) {
- LOG(WARNING) << "Found extension with empty ID";
- return;
- }
-
- if (update_url.DomainIs("google.com")) {
- url_stats_.google_url_count++;
- } else if (update_url.is_empty()) {
- url_stats_.no_url_count++;
- // Fill in default update URL.
- //
- // TODO(akalin): Figure out if we should use the HTTPS version.
- update_url = extension_urls::GetWebstoreUpdateUrl(false);
- } else {
- url_stats_.other_url_count++;
- }
-
- switch (extension_type) {
- case Extension::TYPE_THEME:
- ++url_stats_.theme_count;
- break;
- case Extension::TYPE_EXTENSION:
- case Extension::TYPE_USER_SCRIPT:
- ++url_stats_.extension_count;
- break;
- case Extension::TYPE_HOSTED_APP:
- case Extension::TYPE_PACKAGED_APP:
- ++url_stats_.app_count;
- break;
- case Extension::TYPE_UNKNOWN:
- default:
- ++url_stats_.pending_count;
- break;
- }
-
- std::vector<GURL> update_urls;
- update_urls.push_back(update_url);
- // If UMA is enabled, also add to ManifestFetchData for the
- // webstore update URL.
- if (!extension_urls::IsWebstoreUpdateUrl(update_url) &&
- MetricsServiceHelper::IsMetricsReportingEnabled()) {
- update_urls.push_back(extension_urls::GetWebstoreUpdateUrl(false));
- }
-
- for (size_t i = 0; i < update_urls.size(); ++i) {
- DCHECK(!update_urls[i].is_empty());
- DCHECK(update_urls[i].is_valid());
-
- std::string install_source = i == 0 ?
- kDefaultInstallSource : kNotFromWebstoreInstallSource;
-
- ManifestFetchData* fetch = NULL;
- std::multimap<GURL, ManifestFetchData*>::iterator existing_iter =
- fetches_.find(update_urls[i]);
-
- // Find or create a ManifestFetchData to add this extension to.
- ManifestFetchData::PingData ping_data;
- ping_data.rollcall_days = CalculatePingDays(prefs_->LastPingDay(id));
- ping_data.active_days =
- CalculateActivePingDays(prefs_->LastActivePingDay(id),
- prefs_->GetActiveBit(id));
- while (existing_iter != fetches_.end()) {
- if (existing_iter->second->AddExtension(
- id, version.GetString(), ping_data, update_url_data,
- install_source)) {
- fetch = existing_iter->second;
- break;
- }
- existing_iter++;
- }
- if (!fetch) {
- fetch = new ManifestFetchData(update_urls[i]);
- fetches_.insert(std::make_pair(update_urls[i], fetch));
- bool added = fetch->AddExtension(
- id, version.GetString(), ping_data, update_url_data, install_source);
- DCHECK(added);
- }
- }
-}
-
-ExtensionUpdater::ExtensionFetch::ExtensionFetch()
- : id(""),
- url(),
- package_hash(""),
- version("") {}
-
-ExtensionUpdater::ExtensionFetch::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) {}
-
-ExtensionUpdater::ExtensionFetch::~ExtensionFetch() {}
-
-ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(const std::string& i,
- const FilePath& p,
- const GURL& u)
- : id(i),
- path(p),
- download_url(u) {}
-
-ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
- : id(""),
- path(),
- download_url() {}
-
-ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
-
-ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
- ExtensionPrefs* extension_prefs,
- PrefService* prefs,
- Profile* profile,
- int frequency_seconds)
- : alive_(false),
- weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
- service_(service), frequency_seconds_(frequency_seconds),
- will_check_soon_(false), extension_prefs_(extension_prefs),
- prefs_(prefs), profile_(profile), blacklist_checks_enabled_(true),
- crx_install_is_running_(false) {
- Init();
-}
-
-void ExtensionUpdater::Init() {
- DCHECK_GE(frequency_seconds_, 5);
- DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
-#ifdef NDEBUG
- // In Release mode we enforce that update checks don't happen too often.
- frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
-#endif
- frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
-}
-
-ExtensionUpdater::~ExtensionUpdater() {
- Stop();
-}
-
-static void EnsureInt64PrefRegistered(PrefService* prefs,
- const char name[]) {
- if (!prefs->FindPreference(name))
- prefs->RegisterInt64Pref(name, 0, PrefService::UNSYNCABLE_PREF);
-}
-
-static void EnsureBlacklistVersionPrefRegistered(PrefService* prefs) {
- if (!prefs->FindPreference(kExtensionBlacklistUpdateVersion)) {
- prefs->RegisterStringPref(kExtensionBlacklistUpdateVersion,
- "0",
- PrefService::UNSYNCABLE_PREF);
- }
-}
-
-// The overall goal here is to balance keeping clients up to date while
-// avoiding a thundering herd against update servers.
-TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
- DCHECK(alive_);
- // If someone's testing with a quick frequency, just allow it.
- if (frequency_seconds_ < kStartupWaitSeconds)
- return TimeDelta::FromSeconds(frequency_seconds_);
-
- // If we've never scheduled a check before, start at frequency_seconds_.
- if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck))
- return TimeDelta::FromSeconds(frequency_seconds_);
-
- // If it's been a long time since our last actual check, we want to do one
- // relatively soon.
- Time now = Time::Now();
- Time last = Time::FromInternalValue(prefs_->GetInt64(
- kLastExtensionsUpdateCheck));
- int days = (now - last).InDays();
- if (days >= 30) {
- // Wait 5-10 minutes.
- return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
- kStartupWaitSeconds * 2));
- } else if (days >= 14) {
- // Wait 10-20 minutes.
- return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
- kStartupWaitSeconds * 4));
- } else if (days >= 3) {
- // Wait 20-40 minutes.
- return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
- kStartupWaitSeconds * 8));
- }
-
- // Read the persisted next check time, and use that if it isn't too soon.
- // Otherwise pick something random.
- Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
- kNextExtensionsUpdateCheck));
- Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
- if (saved_next >= earliest) {
- return saved_next - now;
- } else {
- return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
- frequency_seconds_));
- }
-}
-
-void ExtensionUpdater::Start() {
- DCHECK(!alive_);
- // If these are NULL, then that means we've been called after Stop()
- // has been called.
- DCHECK(service_);
- DCHECK(extension_prefs_);
- DCHECK(prefs_);
- DCHECK(profile_);
- DCHECK(!weak_ptr_factory_.HasWeakPtrs());
- alive_ = true;
- // Make sure our prefs are registered, then schedule the first check.
- EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck);
- EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck);
- EnsureBlacklistVersionPrefRegistered(prefs_);
- ScheduleNextCheck(DetermineFirstCheckDelay());
-}
-
-void ExtensionUpdater::Stop() {
- weak_ptr_factory_.InvalidateWeakPtrs();
- alive_ = false;
- service_ = NULL;
- extension_prefs_ = NULL;
- prefs_ = NULL;
- profile_ = NULL;
- timer_.Stop();
- will_check_soon_ = false;
- manifest_fetcher_.reset();
- extension_fetcher_.reset();
- STLDeleteElements(&manifests_pending_);
- manifests_pending_.clear();
- extensions_pending_.clear();
-}
-
-void ExtensionUpdater::OnURLFetchComplete(const content::URLFetcher* source) {
- // Stop() destroys all our URLFetchers, which means we shouldn't be
- // called after Stop() is called.
- DCHECK(alive_);
-
- VLOG(2) << source->GetResponseCode() << " " << source->GetURL();
-
- if (source == manifest_fetcher_.get()) {
- std::string data;
- source->GetResponseAsString(&data);
- OnManifestFetchComplete(source->GetURL(),
- source->GetStatus(),
- source->GetResponseCode(),
- data);
- } else if (source == extension_fetcher_.get()) {
- OnCRXFetchComplete(source,
- source->GetURL(),
- source->GetStatus(),
- source->GetResponseCode());
- } else {
- NOTREACHED();
- }
- NotifyIfFinished();
-}
-
-// Utility class to handle doing xml parsing in a sandboxed utility process.
-class SafeManifestParser : public UtilityProcessHostClient {
- public:
- // Takes ownership of |fetch_data|.
- SafeManifestParser(const std::string& xml, ManifestFetchData* fetch_data,
- base::WeakPtr<ExtensionUpdater> updater)
- : xml_(xml), updater_(updater) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- fetch_data_.reset(fetch_data);
- }
-
- // Posts a task over to the IO loop to start the parsing of xml_ in a
- // utility process.
- void Start() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (!BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&SafeManifestParser::ParseInSandbox, this))) {
- NOTREACHED();
- }
- }
-
- // Creates the sandboxed utility process and tells it to start parsing.
- void ParseInSandbox() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- // TODO(asargent) we shouldn't need to do this branch here - instead
- // UtilityProcessHost should handle it for us. (http://crbug.com/19192)
- bool use_utility_process = content::ResourceDispatcherHost::Get() &&
- !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
- if (use_utility_process) {
- UtilityProcessHost* host = UtilityProcessHost::Create(
- this, BrowserThread::UI);
- host->EnableZygote();
- host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml_));
- } else {
- UpdateManifest manifest;
- if (manifest.Parse(xml_)) {
- if (!BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(
- &SafeManifestParser::OnParseUpdateManifestSucceeded, this,
- manifest.results()))) {
- NOTREACHED();
- }
- } else {
- if (!BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(
- &SafeManifestParser::OnParseUpdateManifestFailed, this,
- manifest.errors()))) {
- NOTREACHED();
- }
- }
- }
- }
-
- // UtilityProcessHostClient
- virtual bool OnMessageReceived(const IPC::Message& message) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(SafeManifestParser, message)
- IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
- OnParseUpdateManifestSucceeded)
- IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
- OnParseUpdateManifestFailed)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
- }
-
- void OnParseUpdateManifestSucceeded(
- const UpdateManifest::Results& results) {
- VLOG(2) << "parsing manifest succeeded (" << fetch_data_->full_url()
- << ")";
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (!updater_) {
- return;
- }
- updater_->HandleManifestResults(*fetch_data_, &results);
- }
-
- void OnParseUpdateManifestFailed(const std::string& error_message) {
- VLOG(2) << "parsing manifest failed (" << fetch_data_->full_url() << ")";
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (!updater_) {
- return;
- }
- LOG(WARNING) << "Error parsing update manifest:\n" << error_message;
- updater_->HandleManifestResults(*fetch_data_, NULL);
- }
-
- private:
- ~SafeManifestParser() {
- // If we're using UtilityProcessHost, we may not be destroyed on
- // the UI or IO thread.
- }
-
- const std::string xml_;
-
- // Should be accessed only on UI thread.
- scoped_ptr<ManifestFetchData> fetch_data_;
- base::WeakPtr<ExtensionUpdater> updater_;
-};
-
-
-void ExtensionUpdater::OnManifestFetchComplete(
- const GURL& url,
- const net::URLRequestStatus& status,
- int response_code,
- const std::string& data) {
- // We want to try parsing the manifest, and if it indicates updates are
- // available, we want to fire off requests to fetch those updates.
- if (status.status() == net::URLRequestStatus::SUCCESS &&
- (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
- VLOG(2) << "beginning manifest parse for " << url;
- scoped_refptr<SafeManifestParser> safe_parser(
- new SafeManifestParser(data, current_manifest_fetch_.release(),
- weak_ptr_factory_.GetWeakPtr()));
- safe_parser->Start();
- } else {
- // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546).
- VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
- << "' response code:" << response_code;
- RemoveFromInProgress(current_manifest_fetch_->extension_ids());
- }
- manifest_fetcher_.reset();
- current_manifest_fetch_.reset();
-
- // If we have any pending manifest requests, fire off the next one.
- if (!manifests_pending_.empty()) {
- ManifestFetchData* manifest_fetch = manifests_pending_.front();
- manifests_pending_.pop_front();
- StartUpdateCheck(manifest_fetch);
- }
-}
-
-void ExtensionUpdater::HandleManifestResults(
- const ManifestFetchData& fetch_data,
- const UpdateManifest::Results* results) {
- DCHECK(alive_);
-
- // Remove all the ids's from in_progress_ids_ (we will add them back in
- // below if they actually have updates we need to fetch and install).
- RemoveFromInProgress(fetch_data.extension_ids());
-
- if (!results) {
- NotifyIfFinished();
- return;
- }
-
- // Examine the parsed manifest and kick off fetches of any new crx files.
- std::vector<int> updates = DetermineUpdates(fetch_data, *results);
- for (size_t i = 0; i < updates.size(); i++) {
- const UpdateManifest::Result* update = &(results->list.at(updates[i]));
- const std::string& id = update->extension_id;
- in_progress_ids_.insert(id);
- if (id != std::string(kBlacklistAppID)) {
- NotifyUpdateFound(update->extension_id);
- } else {
- // The URL of the blacklist file is returned by the server and we need to
- // be sure that we continue to be able to reliably detect whether a URL
- // references a blacklist file.
- DCHECK(extension_urls::IsBlacklistUpdateUrl(update->crx_url))
- << update->crx_url;
- }
- FetchUpdatedExtension(update->extension_id, update->crx_url,
- update->package_hash, update->version);
- }
-
- // If the manifest response included a <daystart> element, we want to save
- // that value for any extensions which had sent a ping in the request.
- if (fetch_data.base_url().DomainIs("google.com") &&
- results->daystart_elapsed_seconds >= 0) {
- Time daystart =
- Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds);
-
- const std::set<std::string>& extension_ids = fetch_data.extension_ids();
- std::set<std::string>::const_iterator i;
- for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
- if (fetch_data.DidPing(*i, ManifestFetchData::ROLLCALL)) {
- if (*i == kBlacklistAppID) {
- extension_prefs_->SetBlacklistLastPingDay(daystart);
- } else if (service_->GetExtensionById(*i, true) != NULL) {
- extension_prefs_->SetLastPingDay(*i, daystart);
- }
- }
- if (extension_prefs_->GetActiveBit(*i)) {
- extension_prefs_->SetActiveBit(*i, false);
- extension_prefs_->SetLastActivePingDay(*i, daystart);
- }
- }
- }
- NotifyIfFinished();
-}
-
-void ExtensionUpdater::ProcessBlacklist(const std::string& data) {
- DCHECK(alive_);
- // Verify sha256 hash value.
- char sha256_hash_value[crypto::kSHA256Length];
- crypto::SHA256HashString(data, sha256_hash_value, crypto::kSHA256Length);
- std::string hash_in_hex = base::HexEncode(sha256_hash_value,
- crypto::kSHA256Length);
-
- 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;
- base::SplitString(data, '\n', &blacklist);
-
- // Tell ExtensionService to update prefs.
- service_->UpdateExtensionBlacklist(blacklist);
-
- // Update the pref value for blacklist version
- prefs_->SetString(kExtensionBlacklistUpdateVersion,
- current_extension_fetch_.version);
-}
-
-void ExtensionUpdater::OnCRXFetchComplete(
- const content::URLFetcher* source,
- const GURL& url,
- const net::URLRequestStatus& status,
- int response_code) {
-
- base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
- if (source->FileErrorOccurred(&error_code)) {
- LOG(ERROR) << "Failed to write update CRX with id "
- << current_extension_fetch_.id << ". "
- << "Error code is "<< error_code;
-
- RecordCRXWriteHistogram(false, FilePath());
- OnCRXFileWriteError(current_extension_fetch_.id);
-
- } else if (status.status() == net::URLRequestStatus::SUCCESS &&
- (response_code == 200 || url.SchemeIsFile())) {
- if (current_extension_fetch_.id == kBlacklistAppID) {
- std::string data;
- source->GetResponseAsString(&data);
- ProcessBlacklist(data);
- in_progress_ids_.erase(current_extension_fetch_.id);
- } else {
- FilePath crx_path;
- // Take ownership of the file at |crx_path|.
- CHECK(source->GetResponseAsFilePath(true, &crx_path));
- RecordCRXWriteHistogram(true, crx_path);
- OnCRXFileWritten(current_extension_fetch_.id, crx_path, url);
- }
- } else {
- // TODO(asargent) do things like exponential backoff, handling
- // 503 Service Unavailable / Retry-After headers, etc. here.
- // (http://crbug.com/12546).
- VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
- << "' response code:" << response_code;
- }
- extension_fetcher_.reset();
- current_extension_fetch_ = ExtensionFetch();
-
- // If there are any pending downloads left, start the next one.
- if (!extensions_pending_.empty()) {
- ExtensionFetch next = extensions_pending_.front();
- extensions_pending_.pop_front();
- FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
- }
-}
-
-void ExtensionUpdater::OnCRXFileWritten(const std::string& id,
- const FilePath& path,
- const GURL& download_url) {
- DCHECK(alive_);
-
- VLOG(2) << download_url << " written to " << path.value();
-
- FetchedCRXFile fetched(id, path, download_url);
- fetched_crx_files_.push(fetched);
-
- MaybeInstallCRXFile();
-}
-
-bool ExtensionUpdater::MaybeInstallCRXFile() {
- if (crx_install_is_running_)
- return false;
-
- while (!fetched_crx_files_.empty() && !crx_install_is_running_) {
- const FetchedCRXFile& crx_file = fetched_crx_files_.top();
-
- VLOG(2) << "updating " << crx_file.id << " with " << crx_file.path.value();
-
- // The ExtensionService is now responsible for cleaning up the temp file
- // at |extension_file.path|.
- CrxInstaller* installer = NULL;
- if (service_->UpdateExtension(crx_file.id,
- crx_file.path,
- crx_file.download_url,
- &installer)) {
- crx_install_is_running_ = true;
-
- // Source parameter ensures that we only see the completion event for the
- // the installer we started.
- registrar_.Add(this,
- chrome::NOTIFICATION_CRX_INSTALLER_DONE,
- content::Source<CrxInstaller>(installer));
- }
- in_progress_ids_.erase(crx_file.id);
- fetched_crx_files_.pop();
- }
-
- // If an updater is running, it was started above.
- return crx_install_is_running_;
-}
-
-void ExtensionUpdater::Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- DCHECK(type == chrome::NOTIFICATION_CRX_INSTALLER_DONE);
-
- // No need to listen for CRX_INSTALLER_DONE anymore.
- registrar_.Remove(this,
- chrome::NOTIFICATION_CRX_INSTALLER_DONE,
- source);
- crx_install_is_running_ = false;
- // If any files are available to update, start one.
- MaybeInstallCRXFile();
-}
-
-void ExtensionUpdater::OnCRXFileWriteError(const std::string& id) {
- DCHECK(alive_);
- in_progress_ids_.erase(id);
-}
-
-void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
- DCHECK(alive_);
- DCHECK(!timer_.IsRunning());
- DCHECK(target_delay >= TimeDelta::FromSeconds(1));
-
- // Add +/- 10% random jitter.
- double delay_ms = target_delay.InMillisecondsF();
- double jitter_factor = (RandDouble() * .2) - 0.1;
- delay_ms += delay_ms * jitter_factor;
- TimeDelta actual_delay = TimeDelta::FromMilliseconds(
- static_cast<int64>(delay_ms));
-
- // Save the time of next check.
- Time next = Time::Now() + actual_delay;
- prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue());
-
- timer_.Start(FROM_HERE, actual_delay, this, &ExtensionUpdater::TimerFired);
-}
-
-void ExtensionUpdater::TimerFired() {
- DCHECK(alive_);
- CheckNow();
-
- // If the user has overridden the update frequency, don't bother reporting
- // this.
- if (frequency_seconds_ == ExtensionService::kDefaultUpdateFrequencySeconds) {
- Time last = Time::FromInternalValue(prefs_->GetInt64(
- kLastExtensionsUpdateCheck));
- if (last.ToInternalValue() != 0) {
- // Use counts rather than time so we can use minutes rather than millis.
- UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
- (Time::Now() - last).InMinutes(),
- base::TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
- base::TimeDelta::FromDays(40).InMinutes(),
- 50); // 50 buckets seems to be the default.
- }
- }
-
- // Save the last check time, and schedule the next check.
- int64 now = Time::Now().ToInternalValue();
- prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
- ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
-}
-
-void ExtensionUpdater::CheckSoon() {
- DCHECK(alive_);
- if (will_check_soon_) {
- return;
- }
- if (BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&ExtensionUpdater::DoCheckSoon,
- weak_ptr_factory_.GetWeakPtr()))) {
- will_check_soon_ = true;
- } else {
- NOTREACHED();
- }
-}
-
-bool ExtensionUpdater::WillCheckSoon() const {
- return will_check_soon_;
-}
-
-void ExtensionUpdater::DoCheckSoon() {
- DCHECK(will_check_soon_);
- CheckNow();
- will_check_soon_ = false;
-}
-
-void ExtensionUpdater::CheckNow() {
- VLOG(2) << "Starting update check";
- DCHECK(alive_);
- NotifyStarted();
- ManifestFetchesBuilder fetches_builder(service_, extension_prefs_);
-
- // Add fetch records for extensions that should be fetched by an update URL.
- // These extensions are not yet installed. They come from group policy
- // and external install sources.
- const PendingExtensionManager* pending_extension_manager =
- service_->pending_extension_manager();
-
- std::set<std::string> pending_ids;
- pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_ids);
-
- std::set<std::string>::const_iterator iter;
- for (iter = pending_ids.begin(); iter != pending_ids.end(); ++iter) {
- PendingExtensionInfo info;
- bool found_id = pending_extension_manager->GetById(*iter, &info);
- DCHECK(found_id);
- if (!found_id)
- continue;
-
- fetches_builder.AddPendingExtension(
- *iter,
- info.install_source(),
- info.update_url());
- }
-
- // Add fetch records for extensions that are installed and have an
- // update URL.
- const ExtensionSet* extensions = service_->extensions();
- for (ExtensionSet::const_iterator iter = extensions->begin();
- iter != extensions->end(); ++iter) {
- // An extension might be overwritten by policy, and have its update url
- // changed. Make sure existing extensions aren't fetched again, if a
- // pending fetch for an extension with the same id already exists.
- if (!ContainsKey(pending_ids, (*iter)->id())) {
- fetches_builder.AddExtension(**iter);
- }
- }
-
- fetches_builder.ReportStats();
-
- std::vector<ManifestFetchData*> fetches(fetches_builder.GetFetches());
-
- // Start a fetch of the blacklist if needed.
- if (blacklist_checks_enabled_) {
- // Note: it is very important that we use the https version of the update
- // url here to avoid DNS hijacking of the blacklist, which is not validated
- // by a public key signature like .crx files are.
- ManifestFetchData* blacklist_fetch =
- new ManifestFetchData(extension_urls::GetWebstoreUpdateUrl(true));
- std::string version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
- ManifestFetchData::PingData ping_data;
- ping_data.rollcall_days =
- CalculatePingDays(extension_prefs_->BlacklistLastPingDay());
- blacklist_fetch->AddExtension(
- kBlacklistAppID, version, ping_data, "", kDefaultInstallSource);
- StartUpdateCheck(blacklist_fetch);
- }
-
- // Now start fetching regular extension updates
- for (std::vector<ManifestFetchData*>::const_iterator it = fetches.begin();
- it != fetches.end(); ++it) {
- // StartUpdateCheck makes sure the url isn't already downloading or
- // scheduled, so we don't need to check before calling it. Ownership of
- // fetch is transferred here.
- StartUpdateCheck(*it);
- }
- // We don't want to use fetches after this since StartUpdateCheck()
- // takes ownership of its argument.
- fetches.clear();
-
- NotifyIfFinished();
-}
-
-bool ExtensionUpdater::GetExistingVersion(const std::string& id,
- std::string* version) {
- DCHECK(alive_);
- if (id == kBlacklistAppID) {
- *version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
- return true;
- }
- const Extension* extension = service_->GetExtensionById(id, false);
- if (!extension) {
- return false;
- }
- *version = extension->version()->GetString();
- return true;
-}
-
-std::vector<int> ExtensionUpdater::DetermineUpdates(
- const ManifestFetchData& fetch_data,
- const UpdateManifest::Results& possible_updates) {
- DCHECK(alive_);
- std::vector<int> result;
-
- // This will only be valid if one of possible_updates specifies
- // browser_min_version.
- Version browser_version;
- PendingExtensionManager* pending_extension_manager =
- service_->pending_extension_manager();
-
- for (size_t i = 0; i < possible_updates.list.size(); i++) {
- const UpdateManifest::Result* update = &possible_updates.list[i];
-
- if (!fetch_data.Includes(update->extension_id)) {
- VLOG(2) << "Ignoring " << update->extension_id << " from this manifest";
- continue;
- }
-
- if (VLOG_IS_ON(2)) {
- if (update->version.empty())
- VLOG(2) << "manifest indicates " << update->extension_id
- << " has no update";
- else
- VLOG(2) << "manifest indicates " << update->extension_id
- << " latest version is '" << update->version << "'";
- }
-
- if (!pending_extension_manager->IsIdPending(update->extension_id)) {
- // If we're not installing pending extension, and the update
- // version is the same or older than what's already installed,
- // we don't want it.
- std::string version;
- if (!GetExistingVersion(update->extension_id, &version)) {
- VLOG(2) << update->extension_id << " is not installed";
- continue;
- }
-
- VLOG(2) << update->extension_id << " is at '" << version << "'";
-
- Version existing_version(version);
- Version update_version(update->version);
-
- if (!update_version.IsValid() ||
- update_version.CompareTo(existing_version) <= 0) {
- continue;
- }
- }
-
- // If the update specifies a browser minimum version, do we qualify?
- if (update->browser_min_version.length() > 0) {
- // First determine the browser version if we haven't already.
- if (!browser_version.IsValid()) {
- chrome::VersionInfo version_info;
- if (version_info.is_valid())
- browser_version = Version(version_info.Version());
- }
- Version browser_min_version(update->browser_min_version);
- if (browser_version.IsValid() && browser_min_version.IsValid() &&
- browser_min_version.CompareTo(browser_version) > 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 " << update->extension_id
- << " available, but requires chrome version "
- << update->browser_min_version;
- continue;
- }
- }
- VLOG(2) << "will try to update " << update->extension_id;
- result.push_back(i);
- }
- return result;
-}
-
-void ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) {
- AddToInProgress(fetch_data->extension_ids());
-
- scoped_ptr<ManifestFetchData> scoped_fetch_data(fetch_data);
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableBackgroundNetworking))
- return;
-
- std::deque<ManifestFetchData*>::const_iterator i;
- for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) {
- if (fetch_data->full_url() == (*i)->full_url()) {
- // This url is already scheduled to be fetched.
- return;
- }
- }
-
- if (manifest_fetcher_.get() != NULL) {
- if (manifest_fetcher_->GetURL() != fetch_data->full_url()) {
- manifests_pending_.push_back(scoped_fetch_data.release());
- }
- } else {
- UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
- fetch_data->full_url().possibly_invalid_spec().length());
-
- if (VLOG_IS_ON(2)) {
- const std::set<std::string>& id_set = fetch_data->extension_ids();
- std::vector<std::string> id_vector(id_set.begin(), id_set.end());
- std::string id_list = JoinString(id_vector, ',');
- VLOG(2) << "Fetching " << fetch_data->full_url() << " for "
- << id_list;
- }
-
- current_manifest_fetch_.swap(scoped_fetch_data);
- manifest_fetcher_.reset(content::URLFetcher::Create(
- kManifestFetcherId, fetch_data->full_url(), content::URLFetcher::GET,
- this));
- manifest_fetcher_->SetRequestContext(profile_->GetRequestContext());
- manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DISABLE_CACHE);
- manifest_fetcher_->Start();
- }
-}
-
-void ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
- 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) {
- if (iter->id == id || iter->url == url) {
- return; // already scheduled
- }
- }
-
- if (extension_fetcher_.get() != NULL) {
- if (extension_fetcher_->GetURL() != url) {
- extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
- }
- } else {
- extension_fetcher_.reset(content::URLFetcher::Create(
- kExtensionFetcherId, url, content::URLFetcher::GET, this));
- extension_fetcher_->SetRequestContext(
- profile_->GetRequestContext());
- extension_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DISABLE_CACHE);
- // Download CRX files to a temp file. The blacklist is small and will be
- // processed in memory, so it is fetched into a string.
- if (id != ExtensionUpdater::kBlacklistAppID) {
- extension_fetcher_->SaveResponseToTemporaryFile(
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
- }
-
- VLOG(2) << "Starting fetch of " << url << " for " << id;
-
- extension_fetcher_->Start();
- current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
- }
-}
-
-void ExtensionUpdater::NotifyStarted() {
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED,
- content::Source<Profile>(profile_),
- content::NotificationService::NoDetails());
-}
-
-void ExtensionUpdater::NotifyUpdateFound(const std::string& extension_id) {
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
- content::Source<Profile>(profile_),
- content::Details<const std::string>(&extension_id));
-}
-
-void ExtensionUpdater::NotifyIfFinished() {
- if (in_progress_ids_.empty()) {
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_EXTENSION_UPDATING_FINISHED,
- content::Source<Profile>(profile_),
- content::NotificationService::NoDetails());
- VLOG(1) << "Sending EXTENSION_UPDATING_FINISHED";
- }
-}
-
-void ExtensionUpdater::AddToInProgress(const std::set<std::string>& ids) {
- std::set<std::string>::const_iterator i;
- for (i = ids.begin(); i != ids.end(); ++i)
- in_progress_ids_.insert(*i);
-}
-
-void ExtensionUpdater::RemoveFromInProgress(const std::set<std::string>& ids) {
- std::set<std::string>::const_iterator i;
- for (i = ids.begin(); i != ids.end(); ++i)
- in_progress_ids_.erase(*i);
-}
diff --git a/chrome/browser/extensions/extension_updater.h b/chrome/browser/extensions/extension_updater.h
deleted file mode 100644
index d61438d..0000000
--- a/chrome/browser/extensions/extension_updater.h
+++ /dev/null
@@ -1,397 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_
-#define CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_
-#pragma once
-
-#include <deque>
-#include <map>
-#include <set>
-#include <stack>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_temp_dir.h"
-#include "base/time.h"
-#include "base/timer.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/common/extensions/update_manifest.h"
-#include "content/public/common/url_fetcher_delegate.h"
-#include "googleurl/src/gurl.h"
-
-class Extension;
-class ExtensionPrefs;
-class ExtensionUpdaterTest;
-class PrefService;
-class Profile;
-class SafeManifestParser;
-
-namespace net {
-class URLRequestStatus;
-}
-
-// To save on server resources we can request updates for multiple extensions
-// in one manifest check. This class helps us keep track of the id's for a
-// given fetch, building up the actual URL, and what if anything to include
-// in the ping parameter.
-class ManifestFetchData {
- public:
- static const int kNeverPinged = -1;
-
- // Each ping type is sent at most once per day.
- enum PingType {
- // Used for counting total installs of an extension/app/theme.
- ROLLCALL,
-
- // Used for counting number of active users of an app, where "active" means
- // the app was launched at least once since the last active ping.
- ACTIVE
- };
-
- struct PingData {
- // The number of days it's been since our last rollcall or active ping,
- // respectively. These are calculated based on the start of day from the
- // server's perspective.
- int rollcall_days;
- int active_days;
-
- PingData() : rollcall_days(0), active_days(0) {}
- PingData(int rollcall, int active)
- : rollcall_days(rollcall), active_days(active) {}
- };
-
- explicit ManifestFetchData(const GURL& update_url);
- ~ManifestFetchData();
-
- // Returns true if this extension information was successfully added. If the
- // return value is false it means the full_url would have become too long, and
- // this ManifestFetchData object remains unchanged.
- bool AddExtension(std::string id, std::string version,
- const PingData& ping_data,
- const std::string& update_url_data,
- const std::string& install_source);
-
- const GURL& base_url() const { return base_url_; }
- const GURL& full_url() const { return full_url_; }
- int extension_count() { return extension_ids_.size(); }
- const std::set<std::string>& extension_ids() const { return extension_ids_; }
-
- // Returns true if the given id is included in this manifest fetch.
- bool Includes(const std::string& extension_id) const;
-
- // Returns true if a ping parameter for |type| was added to full_url for this
- // extension id.
- bool DidPing(std::string extension_id, PingType type) const;
-
- private:
- // The set of extension id's for this ManifestFetchData.
- std::set<std::string> extension_ids_;
-
- // The set of ping data we actually sent.
- std::map<std::string, PingData> pings_;
-
- // The base update url without any arguments added.
- GURL base_url_;
-
- // The base update url plus arguments indicating the id, version, etc.
- // information about each extension.
- GURL full_url_;
-
- DISALLOW_COPY_AND_ASSIGN(ManifestFetchData);
-};
-
-// A class for building a set of ManifestFetchData objects from
-// extensions and pending extensions.
-class ManifestFetchesBuilder {
- public:
- ManifestFetchesBuilder(ExtensionServiceInterface* service,
- ExtensionPrefs* prefs);
- ~ManifestFetchesBuilder();
-
- void AddExtension(const Extension& extension);
-
- void AddPendingExtension(const std::string& id,
- Extension::Location install_source,
- const GURL& update_url);
-
- // Adds all recorded stats taken so far to histogram counts.
- void ReportStats() const;
-
- // Caller takes ownership of the returned ManifestFetchData
- // objects. Clears all recorded stats.
- std::vector<ManifestFetchData*> GetFetches();
-
- private:
- struct URLStats {
- URLStats()
- : no_url_count(0),
- google_url_count(0),
- other_url_count(0),
- extension_count(0),
- theme_count(0),
- app_count(0),
- pending_count(0) {}
-
- int no_url_count, google_url_count, other_url_count;
- int extension_count, theme_count, app_count, pending_count;
- };
-
- void AddExtensionData(Extension::Location location,
- const std::string& id,
- const Version& version,
- Extension::Type extension_type,
- GURL update_url,
- const std::string& update_url_data);
- ExtensionServiceInterface* const service_;
- ExtensionPrefs* const prefs_;
-
- // List of data on fetches we're going to do. We limit the number of
- // extensions grouped together in one batch to avoid running into the limits
- // on the length of http GET requests, so there might be multiple
- // ManifestFetchData* objects with the same base_url.
- std::multimap<GURL, ManifestFetchData*> fetches_;
-
- URLStats url_stats_;
-
- DISALLOW_COPY_AND_ASSIGN(ManifestFetchesBuilder);
-};
-
-// A class for doing auto-updates of installed Extensions. Used like this:
-//
-// ExtensionUpdater* updater = new ExtensionUpdater(my_extensions_service,
-// pref_service,
-// update_frequency_secs);
-// updater->Start();
-// ....
-// updater->Stop();
-class ExtensionUpdater : public content::URLFetcherDelegate,
- public content::NotificationObserver {
- public:
- // Holds a pointer to the passed |service|, using it for querying installed
- // extensions and installing updated ones. The |frequency_seconds| parameter
- // controls how often update checks are scheduled.
- ExtensionUpdater(ExtensionServiceInterface* service,
- ExtensionPrefs* extension_prefs,
- PrefService* prefs,
- Profile* profile,
- int frequency_seconds);
-
- virtual ~ExtensionUpdater();
-
- // Starts the updater running. Should be called at most once.
- void Start();
-
- // Stops the updater running, cancelling any outstanding update manifest and
- // crx downloads. Does not cancel any in-progress installs.
- void Stop();
-
- // Posts a task to do an update check. Does nothing if there is
- // already a pending task that has not yet run.
- void CheckSoon();
-
- // Starts an update check right now, instead of waiting for the next
- // regularly scheduled check or a pending check from CheckSoon().
- void CheckNow();
-
- // Set blacklist checks on or off.
- void set_blacklist_checks_enabled(bool enabled) {
- blacklist_checks_enabled_ = enabled;
- }
-
- // Returns true iff CheckSoon() has been called but the update check
- // hasn't been performed yet. This is used mostly by tests; calling
- // code should just call CheckSoon().
- bool WillCheckSoon() const;
-
- private:
- friend class ExtensionUpdaterTest;
- friend class ExtensionUpdaterFileHandler;
- friend class SafeManifestParser;
-
- // We need to keep track of some information associated with a url
- // when doing a fetch.
- struct ExtensionFetch {
- ExtensionFetch();
- ExtensionFetch(const std::string& i, const GURL& u,
- const std::string& h, const std::string& v);
- ~ExtensionFetch();
-
- std::string id;
- GURL url;
- std::string package_hash;
- std::string version;
- };
-
- // FetchedCRXFile holds information about a CRX file we fetched to disk,
- // but have not yet installed.
- struct FetchedCRXFile {
- FetchedCRXFile();
- FetchedCRXFile(const std::string& id,
- const FilePath& path,
- const GURL& download_url);
- ~FetchedCRXFile();
-
- std::string id;
- FilePath path;
- GURL download_url;
- };
-
- // These are needed for unit testing, to help identify the correct mock
- // URLFetcher objects.
- static const int kManifestFetcherId = 1;
- static const int kExtensionFetcherId = 2;
-
- static const char kBlacklistAppID[];
-
- // Does common work from constructors.
- void Init();
-
- // Computes when to schedule the first update check.
- base::TimeDelta DetermineFirstCheckDelay();
-
- // content::URLFetcherDelegate interface.
- virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE;
-
- // These do the actual work when a URL fetch completes.
- virtual void OnManifestFetchComplete(const GURL& url,
- const net::URLRequestStatus& status,
- int response_code,
- const std::string& data);
-
- virtual void OnCRXFetchComplete(const content::URLFetcher* source,
- const GURL& url,
- const net::URLRequestStatus& status,
- int response_code);
-
- // Called when a crx file has been written into a temp file, and is ready
- // to be installed.
- void OnCRXFileWritten(const std::string& id,
- const FilePath& path,
- const GURL& download_url);
-
- // Called when we encountered an error writing a crx file to a temp file.
- void OnCRXFileWriteError(const std::string& id);
-
- // 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
- // browser restart.
- void ScheduleNextCheck(const base::TimeDelta& target_delay);
-
- // BaseTimer::ReceiverMethod callback.
- void TimerFired();
-
- // Posted by CheckSoon().
- void DoCheckSoon();
-
- // Begins an update check. Takes ownership of |fetch_data|.
- void StartUpdateCheck(ManifestFetchData* fetch_data);
-
- // Begins (or queues up) download of an updated extension.
- void FetchUpdatedExtension(const std::string& id, const GURL& url,
- const std::string& hash, const std::string& version);
-
- // Once a manifest is parsed, this starts fetches of any relevant crx files.
- // If |results| is null, it means something went wrong when parsing it.
- void HandleManifestResults(const ManifestFetchData& fetch_data,
- const UpdateManifest::Results* results);
-
- // Determines the version of an existing extension.
- // Returns true on success and false on failures.
- bool GetExistingVersion(const std::string& id, std::string* version);
-
- // Given a list of potential updates, returns the indices of the ones that are
- // applicable (are actually a new version, etc.) in |result|.
- std::vector<int> DetermineUpdates(const ManifestFetchData& fetch_data,
- const UpdateManifest::Results& possible_updates);
-
- // Send a notification that update checks are starting.
- void NotifyStarted();
-
- // Send a notification that an update was found for extension_id that we'll
- // attempt to download and install.
- void NotifyUpdateFound(const std::string& extension_id);
-
- // Send a notification if we're finished updating.
- void NotifyIfFinished();
-
- // Adds a set of ids to in_progress_ids_.
- void AddToInProgress(const std::set<std::string>& ids);
-
- // Removes a set of ids from in_progress_ids_.
- void RemoveFromInProgress(const std::set<std::string>& ids);
-
- // If a CRX file has been fetched but not installed, and no install is
- // currently running, start installing. Returns true if an install was
- // started.
- bool MaybeInstallCRXFile();
-
- // content::NotificationObserver implementation.
- virtual void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) OVERRIDE;
-
- // Whether Start() has been called but not Stop().
- bool alive_;
-
- base::WeakPtrFactory<ExtensionUpdater> weak_ptr_factory_;
-
- // Outstanding url fetch requests for manifests and updates.
- scoped_ptr<content::URLFetcher> manifest_fetcher_;
- scoped_ptr<content::URLFetcher> extension_fetcher_;
-
- // Pending manifests and extensions to be fetched when the appropriate fetcher
- // is available.
- std::deque<ManifestFetchData*> manifests_pending_;
- std::deque<ExtensionFetch> extensions_pending_;
-
- // The manifest currently being fetched (if any).
- scoped_ptr<ManifestFetchData> current_manifest_fetch_;
-
- // The extension currently being fetched (if any).
- ExtensionFetch current_extension_fetch_;
-
- // Pointer back to the service that owns this ExtensionUpdater.
- ExtensionServiceInterface* service_;
-
- base::OneShotTimer<ExtensionUpdater> timer_;
- int frequency_seconds_;
-
- bool will_check_soon_;
-
- ExtensionPrefs* extension_prefs_;
- PrefService* prefs_;
- Profile* profile_;
- bool blacklist_checks_enabled_;
-
- // The ids of extensions that have in-progress update checks.
- std::set<std::string> in_progress_ids_;
-
- // Observes CRX installs we initiate.
- content::NotificationRegistrar registrar_;
-
- // True when a CrxInstaller is doing an install. Used in MaybeUpdateCrxFile()
- // to keep more than one install from running at once.
- bool crx_install_is_running_;
-
- // Fetched CRX files waiting to be installed.
- std::stack<FetchedCRXFile> fetched_crx_files_;
-
- FRIEND_TEST_ALL_PREFIXES(ExtensionUpdaterTest, TestStartUpdateCheckMemory);
- FRIEND_TEST_ALL_PREFIXES(ExtensionUpdaterTest, TestAfterStopBehavior);
-
- DISALLOW_COPY_AND_ASSIGN(ExtensionUpdater);
-};
-
-#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_
diff --git a/chrome/browser/extensions/pending_extension_manager.h b/chrome/browser/extensions/pending_extension_manager.h
index 098d5f1..859bc860 100644
--- a/chrome/browser/extensions/pending_extension_manager.h
+++ b/chrome/browser/extensions/pending_extension_manager.h
@@ -14,6 +14,14 @@
class ExtensionServiceInterface;
class GURL;
+class PendingExtensionManager;
+
+namespace extensions {
+class ExtensionUpdaterTest;
+void SetupPendingExtensionManagerForTest(
+ int count, const GURL& update_url,
+ PendingExtensionManager* pending_extension_manager);
+}
// Class PendingExtensionManager manages the set of extensions which are
// being installed or updated. In general, installation and updates take
@@ -110,8 +118,8 @@
FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest,
UpdatePendingExtensionAlreadyInstalled);
- friend class ExtensionUpdaterTest;
- friend void SetupPendingExtensionManagerForTest(
+ friend class extensions::ExtensionUpdaterTest;
+ friend void extensions::SetupPendingExtensionManagerForTest(
int count, const GURL& update_url,
PendingExtensionManager* pending_extension_manager);
diff --git a/chrome/browser/extensions/updater/OWNERS b/chrome/browser/extensions/updater/OWNERS
new file mode 100644
index 0000000..f5720f0
--- /dev/null
+++ b/chrome/browser/extensions/updater/OWNERS
@@ -0,0 +1,3 @@
[email protected]
[email protected]
[email protected]
diff --git a/chrome/browser/extensions/updater/extension_downloader.cc b/chrome/browser/extensions/updater/extension_downloader.cc
new file mode 100644
index 0000000..fbf960f
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_downloader.cc
@@ -0,0 +1,649 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/extension_downloader.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_handle.h"
+#include "base/metrics/histogram.h"
+#include "base/platform_file.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/version.h"
+#include "chrome/browser/extensions/updater/safe_manifest_parser.h"
+#include "chrome/browser/metrics/metrics_service.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/common/url_fetcher.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_request_status.h"
+
+using base::Time;
+using base::TimeDelta;
+using content::BrowserThread;
+
+namespace extensions {
+
+const char ExtensionDownloader::kBlacklistAppID[] = "com.google.crx.blacklist";
+
+namespace {
+
+const char kNotFromWebstoreInstallSource[] = "notfromwebstore";
+const char kDefaultInstallSource[] = "";
+
+// TODO(skerner): It would be nice to know if the file system failure
+// happens when creating a temp file or when writing to it. Knowing this
+// will require changes to URLFetcher.
+enum FileWriteResult {
+ SUCCESS = 0,
+ CANT_CREATE_OR_WRITE_TEMP_CRX,
+ CANT_READ_CRX_FILE,
+ NUM_FILE_WRITE_RESULTS,
+};
+
+void RecordFileUpdateHistogram(FileWriteResult file_write_result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ UMA_HISTOGRAM_ENUMERATION("Extensions.UpdaterWriteCrxAsFile",
+ file_write_result,
+ NUM_FILE_WRITE_RESULTS);
+}
+
+void CheckThatCRXIsReadable(const FilePath& crx_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ FileWriteResult file_write_result = SUCCESS;
+
+ // Open the file in the same way
+ // SandboxExtensionUnpacker::ValidateSigniture() will.
+ ScopedStdioHandle file(file_util::OpenFile(crx_path, "rb"));
+ if (!file.get()) {
+ LOG(ERROR) << "Can't read CRX file written for update at path "
+ << crx_path.value().c_str();
+ file_write_result = CANT_READ_CRX_FILE;
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&RecordFileUpdateHistogram, file_write_result));
+}
+
+// Record the result of writing a CRX file. Will be used to understand
+// high failure rates of CRX installs in the field. If |success| is
+// true, |crx_path| should be set to the path to the CRX file.
+void RecordCRXWriteHistogram(bool success, const FilePath& crx_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!success) {
+ // We know there was an error writing the file.
+ RecordFileUpdateHistogram(CANT_CREATE_OR_WRITE_TEMP_CRX);
+ } else {
+ // Test that the file can be read. Based on histograms in
+ // SandboxExtensionUnpacker, we know that many CRX files
+ // can not be read. Try reading.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&CheckThatCRXIsReadable, crx_path));
+ }
+}
+
+} // namespace
+
+ExtensionDownloader::ExtensionFetch::ExtensionFetch()
+ : id(""),
+ url(),
+ package_hash(""),
+ version("") {}
+
+ExtensionDownloader::ExtensionFetch::ExtensionFetch(
+ const std::string& id,
+ const GURL& url,
+ const std::string& package_hash,
+ const std::string& version)
+ : id(id), url(url), package_hash(package_hash), version(version) {}
+
+ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {}
+
+ExtensionDownloader::ExtensionDownloader(
+ ExtensionDownloaderDelegate* delegate,
+ net::URLRequestContextGetter* request_context)
+ : delegate_(delegate),
+ request_context_(request_context),
+ weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ DCHECK(delegate_);
+ DCHECK(request_context_);
+}
+
+ExtensionDownloader::~ExtensionDownloader() {
+ for (FetchMap::iterator it = fetches_preparing_.begin();
+ it != fetches_preparing_.end(); ++it) {
+ STLDeleteElements(&it->second);
+ }
+ STLDeleteElements(&manifests_pending_);
+}
+
+bool ExtensionDownloader::AddExtension(const Extension& extension) {
+ // Skip extensions with empty update URLs converted from user
+ // scripts.
+ if (extension.converted_from_user_script() &&
+ extension.update_url().is_empty()) {
+ return false;
+ }
+
+ // If the extension updates itself from the gallery, ignore any update URL
+ // data. At the moment there is no extra data that an extension can
+ // communicate to the the gallery update servers.
+ std::string update_url_data;
+ if (!extension.UpdatesFromGallery())
+ update_url_data = delegate_->GetUpdateUrlData(extension.id());
+
+ return AddExtensionData(extension.id(), *extension.version(),
+ extension.GetType(), extension.update_url(),
+ update_url_data);
+}
+
+bool ExtensionDownloader::AddPendingExtension(const std::string& id,
+ const GURL& update_url) {
+ // Use a zero version to ensure that a pending extension will always
+ // be updated, and thus installed (assuming all extensions have
+ // non-zero versions).
+ Version version("0.0.0.0");
+ DCHECK(version.IsValid());
+
+ return AddExtensionData(id, version, Extension::TYPE_UNKNOWN, update_url, "");
+}
+
+void ExtensionDownloader::StartAllPending() {
+ ReportStats();
+ url_stats_ = URLStats();
+
+ for (FetchMap::iterator it = fetches_preparing_.begin();
+ it != fetches_preparing_.end(); ++it) {
+ const std::vector<ManifestFetchData*>& list = it->second;
+ for (size_t i = 0; i < list.size(); ++i) {
+ StartUpdateCheck(list[i]);
+ }
+ }
+ fetches_preparing_.clear();
+}
+
+void ExtensionDownloader::StartBlacklistUpdate(
+ const std::string& version,
+ const ManifestFetchData::PingData& ping_data) {
+ // Note: it is very important that we use the https version of the update
+ // url here to avoid DNS hijacking of the blacklist, which is not validated
+ // by a public key signature like .crx files are.
+ ManifestFetchData* blacklist_fetch =
+ new ManifestFetchData(extension_urls::GetWebstoreUpdateUrl(true));
+ blacklist_fetch->AddExtension(kBlacklistAppID, version, &ping_data, "",
+ kDefaultInstallSource);
+ StartUpdateCheck(blacklist_fetch);
+}
+
+bool ExtensionDownloader::AddExtensionData(const std::string& id,
+ const Version& version,
+ Extension::Type extension_type,
+ GURL update_url,
+ const std::string& update_url_data) {
+ // Skip extensions with non-empty invalid update URLs.
+ if (!update_url.is_empty() && !update_url.is_valid()) {
+ LOG(WARNING) << "Extension " << id << " has invalid update url "
+ << update_url;
+ return false;
+ }
+
+ // Skip extensions with empty IDs.
+ if (id.empty()) {
+ LOG(WARNING) << "Found extension with empty ID";
+ return false;
+ }
+
+ if (update_url.DomainIs("google.com")) {
+ url_stats_.google_url_count++;
+ } else if (update_url.is_empty()) {
+ url_stats_.no_url_count++;
+ // Fill in default update URL.
+ //
+ // TODO(akalin): Figure out if we should use the HTTPS version.
+ update_url = extension_urls::GetWebstoreUpdateUrl(false);
+ } else {
+ url_stats_.other_url_count++;
+ }
+
+ switch (extension_type) {
+ case Extension::TYPE_THEME:
+ ++url_stats_.theme_count;
+ break;
+ case Extension::TYPE_EXTENSION:
+ case Extension::TYPE_USER_SCRIPT:
+ ++url_stats_.extension_count;
+ break;
+ case Extension::TYPE_HOSTED_APP:
+ case Extension::TYPE_PACKAGED_APP:
+ ++url_stats_.app_count;
+ break;
+ case Extension::TYPE_UNKNOWN:
+ default:
+ ++url_stats_.pending_count;
+ break;
+ }
+
+ std::vector<GURL> update_urls;
+ update_urls.push_back(update_url);
+ // If UMA is enabled, also add to ManifestFetchData for the
+ // webstore update URL.
+ if (!extension_urls::IsWebstoreUpdateUrl(update_url) &&
+ MetricsServiceHelper::IsMetricsReportingEnabled()) {
+ update_urls.push_back(extension_urls::GetWebstoreUpdateUrl(false));
+ }
+
+ for (size_t i = 0; i < update_urls.size(); ++i) {
+ DCHECK(!update_urls[i].is_empty());
+ DCHECK(update_urls[i].is_valid());
+
+ std::string install_source = i == 0 ?
+ kDefaultInstallSource : kNotFromWebstoreInstallSource;
+
+ ManifestFetchData::PingData ping_data;
+ ManifestFetchData::PingData* optional_ping_data = NULL;
+ if (delegate_->GetPingDataForExtension(id, &ping_data))
+ optional_ping_data = &ping_data;
+
+ // Find or create a ManifestFetchData to add this extension to.
+ ManifestFetchData* fetch = NULL;
+ FetchMap::iterator existing_iter = fetches_preparing_.find(update_urls[i]);
+ if (existing_iter != fetches_preparing_.end() &&
+ !existing_iter->second.empty()) {
+ // Try to add to the ManifestFetchData at the end of the list.
+ ManifestFetchData* existing_fetch = existing_iter->second.back();
+ if (existing_fetch->AddExtension(id, version.GetString(),
+ optional_ping_data, update_url_data,
+ install_source)) {
+ fetch = existing_fetch;
+ }
+ }
+ if (!fetch) {
+ // Otherwise add a new element to the list, if the list doesn't exist or
+ // if its last element is already full.
+ fetch = new ManifestFetchData(update_urls[i]);
+ fetches_preparing_[update_urls[i]].push_back(fetch);
+ bool added = fetch->AddExtension(id, version.GetString(),
+ optional_ping_data,
+ update_url_data,
+ install_source);
+ DCHECK(added);
+ }
+ }
+
+ return true;
+}
+
+void ExtensionDownloader::ReportStats() const {
+ UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension",
+ url_stats_.extension_count);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
+ url_stats_.theme_count);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp",
+ url_stats_.app_count);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending",
+ url_stats_.pending_count);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
+ url_stats_.google_url_count);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
+ url_stats_.other_url_count);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
+ url_stats_.no_url_count);
+}
+
+void ExtensionDownloader::StartUpdateCheck(ManifestFetchData* fetch_data) {
+ scoped_ptr<ManifestFetchData> scoped_fetch_data(fetch_data);
+ const std::set<std::string>& id_set(fetch_data->extension_ids());
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableBackgroundNetworking)) {
+ NotifyExtensionsDownloadFailed(id_set,
+ ExtensionDownloaderDelegate::DISABLED);
+ return;
+ }
+
+ std::deque<ManifestFetchData*>::const_iterator i;
+ for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) {
+ if (fetch_data->full_url() == (*i)->full_url()) {
+ // This url is already scheduled to be fetched.
+ return;
+ }
+ }
+
+ if (manifest_fetcher_.get() != NULL) {
+ if (manifest_fetcher_->GetURL() != fetch_data->full_url()) {
+ manifests_pending_.push_back(scoped_fetch_data.release());
+ }
+ } else {
+ UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
+ fetch_data->full_url().possibly_invalid_spec().length());
+
+ if (VLOG_IS_ON(2)) {
+ std::vector<std::string> id_vector(id_set.begin(), id_set.end());
+ std::string id_list = JoinString(id_vector, ',');
+ VLOG(2) << "Fetching " << fetch_data->full_url() << " for "
+ << id_list;
+ }
+
+ current_manifest_fetch_.swap(scoped_fetch_data);
+ manifest_fetcher_.reset(content::URLFetcher::Create(
+ kManifestFetcherId, fetch_data->full_url(), content::URLFetcher::GET,
+ this));
+ manifest_fetcher_->SetRequestContext(request_context_);
+ manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DISABLE_CACHE);
+ manifest_fetcher_->Start();
+ }
+}
+
+void ExtensionDownloader::OnURLFetchComplete(
+ const content::URLFetcher* source) {
+ VLOG(2) << source->GetResponseCode() << " " << source->GetURL();
+
+ if (source == manifest_fetcher_.get()) {
+ std::string data;
+ source->GetResponseAsString(&data);
+ OnManifestFetchComplete(source->GetURL(),
+ source->GetStatus(),
+ source->GetResponseCode(),
+ data);
+ } else if (source == extension_fetcher_.get()) {
+ OnCRXFetchComplete(source,
+ source->GetURL(),
+ source->GetStatus(),
+ source->GetResponseCode());
+ } else {
+ NOTREACHED();
+ }
+}
+
+void ExtensionDownloader::OnManifestFetchComplete(
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const std::string& data) {
+ // We want to try parsing the manifest, and if it indicates updates are
+ // available, we want to fire off requests to fetch those updates.
+ if (status.status() == net::URLRequestStatus::SUCCESS &&
+ (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
+ VLOG(2) << "beginning manifest parse for " << url;
+ scoped_refptr<SafeManifestParser> safe_parser(
+ new SafeManifestParser(
+ data,
+ current_manifest_fetch_.release(),
+ base::Bind(&ExtensionDownloader::HandleManifestResults,
+ weak_ptr_factory_.GetWeakPtr())));
+ safe_parser->Start();
+ } else {
+ // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546).
+ VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
+ << "' response code:" << response_code;
+ NotifyExtensionsDownloadFailed(
+ current_manifest_fetch_->extension_ids(),
+ ExtensionDownloaderDelegate::MANIFEST_FETCH_FAILED);
+ }
+ manifest_fetcher_.reset();
+ current_manifest_fetch_.reset();
+
+ // If we have any pending manifest requests, fire off the next one.
+ if (!manifests_pending_.empty()) {
+ ManifestFetchData* manifest_fetch = manifests_pending_.front();
+ manifests_pending_.pop_front();
+ StartUpdateCheck(manifest_fetch);
+ }
+}
+
+void ExtensionDownloader::HandleManifestResults(
+ const ManifestFetchData& fetch_data,
+ const UpdateManifest::Results* results) {
+ // Keep a list of extensions that will not be updated, so that the |delegate_|
+ // can be notified once we're done here.
+ std::set<std::string> not_updated(fetch_data.extension_ids());
+
+ if (!results) {
+ NotifyExtensionsDownloadFailed(
+ not_updated,
+ ExtensionDownloaderDelegate::MANIFEST_INVALID);
+ return;
+ }
+
+ // Examine the parsed manifest and kick off fetches of any new crx files.
+ std::vector<int> updates;
+ DetermineUpdates(fetch_data, *results, &updates);
+ for (size_t i = 0; i < updates.size(); i++) {
+ const UpdateManifest::Result* update = &(results->list.at(updates[i]));
+ const std::string& id = update->extension_id;
+ not_updated.erase(id);
+ if (id != kBlacklistAppID) {
+ NotifyUpdateFound(update->extension_id);
+ } else {
+ // The URL of the blacklist file is returned by the server and we need to
+ // be sure that we continue to be able to reliably detect whether a URL
+ // references a blacklist file.
+ DCHECK(extension_urls::IsBlacklistUpdateUrl(update->crx_url))
+ << update->crx_url;
+ }
+ FetchUpdatedExtension(update->extension_id, update->crx_url,
+ update->package_hash, update->version);
+ }
+
+ // If the manifest response included a <daystart> element, we want to save
+ // that value for any extensions which had sent a ping in the request.
+ if (fetch_data.base_url().DomainIs("google.com") &&
+ results->daystart_elapsed_seconds >= 0) {
+ Time day_start =
+ Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds);
+
+ const std::set<std::string>& extension_ids = fetch_data.extension_ids();
+ std::set<std::string>::const_iterator i;
+ for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
+ const std::string& id = *i;
+ ExtensionDownloaderDelegate::PingResult& result = ping_results_[id];
+ result.did_ping = fetch_data.DidPing(id, ManifestFetchData::ROLLCALL);
+ result.day_start = day_start;
+ }
+ }
+
+ NotifyExtensionsDownloadFailed(
+ not_updated,
+ ExtensionDownloaderDelegate::NO_UPDATE_AVAILABLE);
+}
+
+void ExtensionDownloader::DetermineUpdates(
+ const ManifestFetchData& fetch_data,
+ const UpdateManifest::Results& possible_updates,
+ std::vector<int>* result) {
+ // This will only be valid if one of possible_updates specifies
+ // browser_min_version.
+ Version browser_version;
+
+ for (size_t i = 0; i < possible_updates.list.size(); i++) {
+ const UpdateManifest::Result* update = &possible_updates.list[i];
+ const std::string& id = update->extension_id;
+
+ if (!fetch_data.Includes(id)) {
+ VLOG(2) << "Ignoring " << id << " from this manifest";
+ continue;
+ }
+
+ if (VLOG_IS_ON(2)) {
+ if (update->version.empty())
+ VLOG(2) << "manifest indicates " << id << " has no update";
+ else
+ VLOG(2) << "manifest indicates " << id
+ << " latest version is '" << update->version << "'";
+ }
+
+ if (!delegate_->IsExtensionPending(id)) {
+ // If we're not installing pending extension, and the update
+ // version is the same or older than what's already installed,
+ // we don't want it.
+ std::string version;
+ if (!delegate_->GetExtensionExistingVersion(id, &version)) {
+ VLOG(2) << id << " is not installed";
+ continue;
+ }
+
+ VLOG(2) << id << " is at '" << version << "'";
+
+ Version existing_version(version);
+ Version update_version(update->version);
+
+ if (!update_version.IsValid() ||
+ update_version.CompareTo(existing_version) <= 0) {
+ continue;
+ }
+ }
+
+ // If the update specifies a browser minimum version, do we qualify?
+ if (update->browser_min_version.length() > 0) {
+ // First determine the browser version if we haven't already.
+ if (!browser_version.IsValid()) {
+ chrome::VersionInfo version_info;
+ if (version_info.is_valid())
+ browser_version = Version(version_info.Version());
+ }
+ Version browser_min_version(update->browser_min_version);
+ if (browser_version.IsValid() && browser_min_version.IsValid() &&
+ browser_min_version.CompareTo(browser_version) > 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 " << id
+ << " available, but requires chrome version "
+ << update->browser_min_version;
+ continue;
+ }
+ }
+ VLOG(2) << "will try to update " << id;
+ result->push_back(i);
+ }
+}
+
+ // Begins (or queues up) download of an updated extension.
+void ExtensionDownloader::FetchUpdatedExtension(const std::string& id,
+ 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) {
+ if (iter->id == id || iter->url == url) {
+ return; // already scheduled
+ }
+ }
+
+ if (extension_fetcher_.get() != NULL) {
+ if (extension_fetcher_->GetURL() != url) {
+ extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
+ }
+ } else {
+ extension_fetcher_.reset(content::URLFetcher::Create(
+ kExtensionFetcherId, url, content::URLFetcher::GET, this));
+ extension_fetcher_->SetRequestContext(request_context_);
+ extension_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DISABLE_CACHE);
+ // Download CRX files to a temp file. The blacklist is small and will be
+ // processed in memory, so it is fetched into a string.
+ if (id != kBlacklistAppID) {
+ extension_fetcher_->SaveResponseToTemporaryFile(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
+ }
+
+ VLOG(2) << "Starting fetch of " << url << " for " << id;
+
+ extension_fetcher_->Start();
+ current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
+ }
+}
+
+void ExtensionDownloader::OnCRXFetchComplete(
+ const content::URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code) {
+ const std::string& id = current_extension_fetch_.id;
+ const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id];
+
+ base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
+ if (source->FileErrorOccurred(&error_code)) {
+ LOG(ERROR) << "Failed to write update CRX with id " << id << ". "
+ << "Error code is "<< error_code;
+ RecordCRXWriteHistogram(false, FilePath());
+ delegate_->OnExtensionDownloadFailed(
+ id, ExtensionDownloaderDelegate::CRX_FETCH_FAILED, ping);
+ } else if (status.status() == net::URLRequestStatus::SUCCESS &&
+ (response_code == 200 || url.SchemeIsFile())) {
+ if (id == kBlacklistAppID) {
+ std::string data;
+ source->GetResponseAsString(&data);
+ // TODO(asargent): try to get rid of this special case for the blacklist
+ // to simplify the delegate's interface.
+ delegate_->OnBlacklistDownloadFinished(
+ data, current_extension_fetch_.package_hash,
+ current_extension_fetch_.version, ping);
+ } else {
+ FilePath crx_path;
+ // Take ownership of the file at |crx_path|.
+ CHECK(source->GetResponseAsFilePath(true, &crx_path));
+ RecordCRXWriteHistogram(true, crx_path);
+ delegate_->OnExtensionDownloadFinished(id, crx_path, url, ping);
+ }
+ } else {
+ // TODO(asargent) do things like exponential backoff, handling
+ // 503 Service Unavailable / Retry-After headers, etc. here.
+ // (http://crbug.com/12546).
+ VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
+ << "' response code:" << response_code;
+ delegate_->OnExtensionDownloadFailed(
+ id, ExtensionDownloaderDelegate::CRX_FETCH_FAILED, ping);
+ }
+
+ extension_fetcher_.reset();
+ current_extension_fetch_ = ExtensionFetch();
+ ping_results_.erase(id);
+
+ // If there are any pending downloads left, start the next one.
+ if (!extensions_pending_.empty()) {
+ ExtensionFetch next = extensions_pending_.front();
+ extensions_pending_.pop_front();
+ FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
+ }
+}
+
+void ExtensionDownloader::NotifyExtensionsDownloadFailed(
+ const std::set<std::string>& extension_ids,
+ ExtensionDownloaderDelegate::Error error) {
+ for (std::set<std::string>::const_iterator it = extension_ids.begin();
+ it != extension_ids.end(); ++it) {
+ delegate_->OnExtensionDownloadFailed(*it, error, ping_results_[*it]);
+ ping_results_.erase(*it);
+ }
+}
+
+void ExtensionDownloader::NotifyUpdateFound(const std::string& id) {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
+ content::NotificationService::AllBrowserContextsAndSources(),
+ content::Details<const std::string>(&id));
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_downloader.h b/chrome/browser/extensions/updater/extension_downloader.h
new file mode 100644
index 0000000..b59c1259
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_downloader.h
@@ -0,0 +1,205 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_H_
+#pragma once
+
+#include <deque>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/update_manifest.h"
+#include "content/public/common/url_fetcher_delegate.h"
+#include "googleurl/src/gurl.h"
+
+class Version;
+
+namespace net {
+class URLRequestContextGetter;
+class URLRequestStatus;
+}
+
+namespace extensions {
+
+class ExtensionUpdaterTest;
+
+// A class that checks for updates of a given list of extensions, and downloads
+// the crx file when updates are found. It uses a |ExtensionDownloaderDelegate|
+// that takes ownership of the downloaded crx files, and handles events during
+// the update check.
+class ExtensionDownloader : public content::URLFetcherDelegate {
+ public:
+ // |delegate| is stored as a raw pointer and must outlive the
+ // ExtensionDownloader.
+ ExtensionDownloader(ExtensionDownloaderDelegate* delegate,
+ net::URLRequestContextGetter* request_context);
+ virtual ~ExtensionDownloader();
+
+ // Adds |extension| to the list of extensions to check for updates.
+ // Returns false if the |extension| can't be updated due to invalid details.
+ // In that case, no callbacks will be performed on the |delegate_|.
+ bool AddExtension(const Extension& extension);
+
+ // Adds extension |id| to the list of extensions to check for updates.
+ // Returns false if the |id| can't be updated due to invalid details.
+ // In that case, no callbacks will be performed on the |delegate_|.
+ bool AddPendingExtension(const std::string& id, const GURL& update_url);
+
+ // Schedules a fetch of the manifest of all the extensions added with
+ // AddExtension() and AddPendingExtension().
+ void StartAllPending();
+
+ // Schedules an update check of the blacklist.
+ void StartBlacklistUpdate(const std::string& version,
+ const ManifestFetchData::PingData& ping_data);
+
+ // These are needed for unit testing, to help identify the correct mock
+ // URLFetcher objects.
+ static const int kManifestFetcherId = 1;
+ static const int kExtensionFetcherId = 2;
+
+ // Update AppID for extension blacklist.
+ static const char kBlacklistAppID[];
+
+ private:
+ friend class ExtensionUpdaterTest;
+
+ // These counters are bumped as extensions are added to be fetched. They
+ // are then recorded as UMA metrics when all the extensions have been added.
+ struct URLStats {
+ URLStats()
+ : no_url_count(0),
+ google_url_count(0),
+ other_url_count(0),
+ extension_count(0),
+ theme_count(0),
+ app_count(0),
+ pending_count(0) {}
+
+ int no_url_count, google_url_count, other_url_count;
+ int extension_count, theme_count, app_count, pending_count;
+ };
+
+ // We need to keep track of some information associated with a url
+ // when doing a fetch.
+ struct ExtensionFetch {
+ ExtensionFetch();
+ ExtensionFetch(const std::string& id, const GURL& url,
+ const std::string& package_hash, const std::string& version);
+ ~ExtensionFetch();
+
+ std::string id;
+ GURL url;
+ std::string package_hash;
+ std::string version;
+ };
+
+ // Helper for AddExtension() and AddPendingExtension().
+ bool AddExtensionData(const std::string& id,
+ const Version& version,
+ Extension::Type extension_type,
+ GURL update_url,
+ const std::string& update_url_data);
+
+ // Adds all recorded stats taken so far to histogram counts.
+ void ReportStats() const;
+
+ // Begins an update check. Takes ownership of |fetch_data|.
+ void StartUpdateCheck(ManifestFetchData* fetch_data);
+
+ // content::URLFetcherDelegate implementation.
+ virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE;
+
+ // Handles the result of a manifest fetch.
+ void OnManifestFetchComplete(const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const std::string& data);
+
+ // Once a manifest is parsed, this starts fetches of any relevant crx files.
+ // If |results| is null, it means something went wrong when parsing it.
+ void HandleManifestResults(const ManifestFetchData& fetch_data,
+ const UpdateManifest::Results* results);
+
+ // Given a list of potential updates, returns the indices of the ones that are
+ // applicable (are actually a new version, etc.) in |result|.
+ void DetermineUpdates(const ManifestFetchData& fetch_data,
+ const UpdateManifest::Results& possible_updates,
+ std::vector<int>* result);
+
+ // Begins (or queues up) download of an updated extension.
+ void FetchUpdatedExtension(const std::string& id,
+ const GURL& url,
+ const std::string& hash,
+ const std::string& version);
+
+ // Handles the result of a crx fetch.
+ void OnCRXFetchComplete(const content::URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code);
+
+ // Invokes OnExtensionDownloadFailed() on the |delegate_| for each extension
+ // in the set, with |error| as the reason for failure.
+ void NotifyExtensionsDownloadFailed(const std::set<std::string>& id_set,
+ ExtensionDownloaderDelegate::Error error);
+
+ // Send a notification that an update was found for |id| that we'll
+ // attempt to download.
+ void NotifyUpdateFound(const std::string& id);
+
+ // The delegate that receives the crx files downloaded by the
+ // ExtensionDownloader, and that fills in optional ping and update url data.
+ ExtensionDownloaderDelegate* delegate_;
+
+ // The request context to use for the URLFetchers.
+ net::URLRequestContextGetter* request_context_;
+
+ // Used to create WeakPtrs to |this|.
+ base::WeakPtrFactory<ExtensionDownloader> weak_ptr_factory_;
+
+ // Collects UMA samples that are reported when ReportStats() is called.
+ URLStats url_stats_;
+
+ // List of data on fetches we're going to do. We limit the number of
+ // extensions grouped together in one batch to avoid running into the limits
+ // on the length of http GET requests, so there might be multiple
+ // ManifestFetchData* objects with the same base_url.
+ typedef std::map<GURL, std::vector<ManifestFetchData*> > FetchMap;
+ FetchMap fetches_preparing_;
+
+ // Outstanding url fetch requests for manifests and updates.
+ scoped_ptr<content::URLFetcher> manifest_fetcher_;
+ scoped_ptr<content::URLFetcher> extension_fetcher_;
+
+ // Pending manifests and extensions to be fetched when the appropriate fetcher
+ // is available.
+ std::deque<ManifestFetchData*> manifests_pending_;
+ std::deque<ExtensionFetch> extensions_pending_;
+
+ // The manifest currently being fetched (if any).
+ scoped_ptr<ManifestFetchData> current_manifest_fetch_;
+
+ // The extension currently being fetched (if any).
+ ExtensionFetch current_extension_fetch_;
+
+ // Maps an extension-id to its PingResult data.
+ std::map<std::string, ExtensionDownloaderDelegate::PingResult> ping_results_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionDownloader);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_H_
diff --git a/chrome/browser/extensions/updater/extension_downloader_delegate.cc b/chrome/browser/extensions/updater/extension_downloader_delegate.cc
new file mode 100644
index 0000000..3d3eba9
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_downloader_delegate.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+
+namespace extensions {
+
+ExtensionDownloaderDelegate::PingResult::PingResult() : did_ping(false) {}
+
+ExtensionDownloaderDelegate::PingResult::~PingResult() {}
+
+ExtensionDownloaderDelegate::~ExtensionDownloaderDelegate() {}
+
+void ExtensionDownloaderDelegate::OnExtensionDownloadFailed(
+ const std::string& id,
+ ExtensionDownloaderDelegate::Error error,
+ const ExtensionDownloaderDelegate::PingResult& ping_result) {}
+
+bool ExtensionDownloaderDelegate::GetPingDataForExtension(
+ const std::string& id,
+ ManifestFetchData::PingData* ping) {
+ return false;
+}
+
+std::string ExtensionDownloaderDelegate::GetUpdateUrlData(
+ const std::string& id) {
+ return std::string();
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_downloader_delegate.h b/chrome/browser/extensions/updater/extension_downloader_delegate.h
new file mode 100644
index 0000000..4171eebd
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_downloader_delegate.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_DELEGATE_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_DELEGATE_H_
+#pragma once
+
+#include <string>
+
+#include "base/time.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+
+class FilePath;
+class GURL;
+
+namespace extensions {
+
+class ExtensionDownloaderDelegate {
+ public:
+ virtual ~ExtensionDownloaderDelegate();
+
+ // Passed as an argument to ExtensionDownloader::OnExtensionDownloadFailed()
+ // to detail the reason for the failure.
+ enum Error {
+ // Background networking is disabled.
+ DISABLED,
+
+ // Failed to fetch the manifest for this extension.
+ MANIFEST_FETCH_FAILED,
+
+ // The manifest couldn't be parsed.
+ MANIFEST_INVALID,
+
+ // The manifest was fetched and parsed, and there are no updates for
+ // this extension.
+ NO_UPDATE_AVAILABLE,
+
+ // There was an update for this extension but the download of the crx
+ // failed.
+ CRX_FETCH_FAILED,
+ };
+
+ // Passed as an argument to the completion callbacks to signal whether
+ // the extension update sent a ping.
+ struct PingResult {
+ PingResult();
+ ~PingResult();
+
+ // Whether a ping was sent.
+ bool did_ping;
+
+ // The start of day, from the server's perspective. This is only valid
+ // when |did_ping| is true.
+ base::Time day_start;
+ };
+
+ // One of the following 3 methods is always invoked for a given extension
+ // id, if AddExtension() or AddPendingExtension() returned true when that
+ // extension was added to the ExtensionDownloader.
+
+ // Invoked if the extension couldn't be downloaded. |error| contains the
+ // failure reason.
+ virtual void OnExtensionDownloadFailed(const std::string& id,
+ Error error,
+ const PingResult& ping_result);
+
+ // Invoked if the extension had an update available and its crx was
+ // successfully downloaded to |path|. Ownership if that file is transferred
+ // to the delegate.
+ virtual void OnExtensionDownloadFinished(const std::string& id,
+ const FilePath& path,
+ const GURL& download_url,
+ const PingResult& ping_result) = 0;
+
+ // Same as OnExtensionDownloadFinished() but only for the kBlacklistAppID
+ // extension, which passes different data to the delegate.
+ virtual void OnBlacklistDownloadFinished(const std::string& data,
+ const std::string& package_hash,
+ const std::string& version,
+ const PingResult& ping_result) = 0;
+
+ // The remaining methods are used by the ExtensionDownloader to retrieve
+ // information about extensions from the delegate.
+
+ // Invoked to fill the PingData for the given extension id. Returns false
+ // if PingData should not be included for this extension's update check
+ // (this is the default).
+ virtual bool GetPingDataForExtension(const std::string& id,
+ ManifestFetchData::PingData* ping);
+
+ // Invoked to get the update url data for this extension's update url, if
+ // there is any. The default implementation returns an empty string.
+ virtual std::string GetUpdateUrlData(const std::string& id);
+
+ // Invoked to determine whether extension |id| is currently
+ // pending installation.
+ virtual bool IsExtensionPending(const std::string& id) = 0;
+
+ // Invoked to get the current version of extension |id|. Returns false if
+ // that extension is not installed.
+ virtual bool GetExtensionExistingVersion(const std::string& id,
+ std::string* version) = 0;
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_DELEGATE_H_
diff --git a/chrome/browser/extensions/updater/extension_updater.cc b/chrome/browser/extensions/updater/extension_updater.cc
new file mode 100644
index 0000000..ff3fdaf
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_updater.cc
@@ -0,0 +1,505 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/extension_updater.h"
+
+#include <algorithm>
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/pending_extension_manager.h"
+#include "chrome/browser/extensions/updater/extension_downloader.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_set.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "crypto/sha2.h"
+
+using base::RandDouble;
+using base::RandInt;
+using base::Time;
+using base::TimeDelta;
+using content::BrowserThread;
+using prefs::kExtensionBlacklistUpdateVersion;
+using prefs::kLastExtensionsUpdateCheck;
+using prefs::kNextExtensionsUpdateCheck;
+
+typedef extensions::ExtensionDownloaderDelegate::Error Error;
+typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult;
+
+namespace {
+
+// 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;
+
+// For sanity checking on update frequency - enforced in release mode only.
+const int kMinUpdateFrequencySeconds = 30;
+const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days
+
+// When we've computed a days value, we want to make sure we don't send a
+// negative value (due to the system clock being set backwards, etc.), since -1
+// is a special sentinel value that means "never pinged", and other negative
+// values don't make sense.
+int SanitizeDays(int days) {
+ if (days < 0)
+ return 0;
+ return days;
+}
+
+// Calculates the value to use for the ping days parameter.
+int CalculatePingDays(const Time& last_ping_day) {
+ int days = extensions::ManifestFetchData::kNeverPinged;
+ if (!last_ping_day.is_null()) {
+ days = SanitizeDays((Time::Now() - last_ping_day).InDays());
+ }
+ return days;
+}
+
+int CalculateActivePingDays(const Time& last_active_ping_day,
+ bool hasActiveBit) {
+ if (!hasActiveBit)
+ return 0;
+ if (last_active_ping_day.is_null())
+ return extensions::ManifestFetchData::kNeverPinged;
+ return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
+}
+
+} // namespace
+
+namespace extensions {
+
+ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(const std::string& i,
+ const FilePath& p,
+ const GURL& u)
+ : id(i),
+ path(p),
+ download_url(u) {}
+
+ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
+ : id(""),
+ path(),
+ download_url() {}
+
+ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
+
+ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
+ ExtensionPrefs* extension_prefs,
+ PrefService* prefs,
+ Profile* profile,
+ int frequency_seconds)
+ : alive_(false),
+ weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ service_(service), frequency_seconds_(frequency_seconds),
+ will_check_soon_(false), extension_prefs_(extension_prefs),
+ prefs_(prefs), profile_(profile), blacklist_checks_enabled_(true),
+ crx_install_is_running_(false) {
+ DCHECK_GE(frequency_seconds_, 5);
+ DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
+#ifdef NDEBUG
+ // In Release mode we enforce that update checks don't happen too often.
+ frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
+#endif
+ frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
+}
+
+ExtensionUpdater::~ExtensionUpdater() {
+ Stop();
+}
+
+// The overall goal here is to balance keeping clients up to date while
+// avoiding a thundering herd against update servers.
+TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
+ DCHECK(alive_);
+ // If someone's testing with a quick frequency, just allow it.
+ if (frequency_seconds_ < kStartupWaitSeconds)
+ return TimeDelta::FromSeconds(frequency_seconds_);
+
+ // If we've never scheduled a check before, start at frequency_seconds_.
+ if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck))
+ return TimeDelta::FromSeconds(frequency_seconds_);
+
+ // If it's been a long time since our last actual check, we want to do one
+ // relatively soon.
+ Time now = Time::Now();
+ Time last = Time::FromInternalValue(prefs_->GetInt64(
+ kLastExtensionsUpdateCheck));
+ int days = (now - last).InDays();
+ if (days >= 30) {
+ // Wait 5-10 minutes.
+ return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
+ kStartupWaitSeconds * 2));
+ } else if (days >= 14) {
+ // Wait 10-20 minutes.
+ return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
+ kStartupWaitSeconds * 4));
+ } else if (days >= 3) {
+ // Wait 20-40 minutes.
+ return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
+ kStartupWaitSeconds * 8));
+ }
+
+ // Read the persisted next check time, and use that if it isn't too soon.
+ // Otherwise pick something random.
+ Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
+ kNextExtensionsUpdateCheck));
+ Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
+ if (saved_next >= earliest) {
+ return saved_next - now;
+ } else {
+ return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
+ frequency_seconds_));
+ }
+}
+
+void ExtensionUpdater::Start() {
+ DCHECK(!alive_);
+ // If these are NULL, then that means we've been called after Stop()
+ // has been called.
+ DCHECK(service_);
+ DCHECK(extension_prefs_);
+ DCHECK(prefs_);
+ DCHECK(profile_);
+ DCHECK(!weak_ptr_factory_.HasWeakPtrs());
+ alive_ = true;
+ // Make sure our prefs are registered, then schedule the first check.
+ ScheduleNextCheck(DetermineFirstCheckDelay());
+}
+
+void ExtensionUpdater::Stop() {
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ alive_ = false;
+ service_ = NULL;
+ extension_prefs_ = NULL;
+ prefs_ = NULL;
+ profile_ = NULL;
+ timer_.Stop();
+ will_check_soon_ = false;
+ downloader_.reset();
+}
+
+void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
+ DCHECK(alive_);
+ DCHECK(!timer_.IsRunning());
+ DCHECK(target_delay >= TimeDelta::FromSeconds(1));
+
+ // Add +/- 10% random jitter.
+ double delay_ms = target_delay.InMillisecondsF();
+ double jitter_factor = (RandDouble() * .2) - 0.1;
+ delay_ms += delay_ms * jitter_factor;
+ TimeDelta actual_delay = TimeDelta::FromMilliseconds(
+ static_cast<int64>(delay_ms));
+
+ // Save the time of next check.
+ Time next = Time::Now() + actual_delay;
+ prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue());
+
+ timer_.Start(FROM_HERE, actual_delay, this, &ExtensionUpdater::TimerFired);
+}
+
+void ExtensionUpdater::TimerFired() {
+ DCHECK(alive_);
+ CheckNow();
+
+ // If the user has overridden the update frequency, don't bother reporting
+ // this.
+ if (frequency_seconds_ == ExtensionService::kDefaultUpdateFrequencySeconds) {
+ Time last = Time::FromInternalValue(prefs_->GetInt64(
+ kLastExtensionsUpdateCheck));
+ if (last.ToInternalValue() != 0) {
+ // Use counts rather than time so we can use minutes rather than millis.
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
+ (Time::Now() - last).InMinutes(),
+ TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
+ TimeDelta::FromDays(40).InMinutes(),
+ 50); // 50 buckets seems to be the default.
+ }
+ }
+
+ // Save the last check time, and schedule the next check.
+ int64 now = Time::Now().ToInternalValue();
+ prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
+ ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
+}
+
+void ExtensionUpdater::CheckSoon() {
+ DCHECK(alive_);
+ if (will_check_soon_)
+ return;
+ if (BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&ExtensionUpdater::DoCheckSoon,
+ weak_ptr_factory_.GetWeakPtr()))) {
+ will_check_soon_ = true;
+ } else {
+ NOTREACHED();
+ }
+}
+
+bool ExtensionUpdater::WillCheckSoon() const {
+ return will_check_soon_;
+}
+
+void ExtensionUpdater::DoCheckSoon() {
+ DCHECK(will_check_soon_);
+ CheckNow();
+ will_check_soon_ = false;
+}
+
+void ExtensionUpdater::CheckNow() {
+ VLOG(2) << "Starting update check";
+ DCHECK(alive_);
+ NotifyStarted();
+
+ if (!downloader_.get()) {
+ downloader_.reset(
+ new ExtensionDownloader(this, profile_->GetRequestContext()));
+ }
+
+ // Add fetch records for extensions that should be fetched by an update URL.
+ // These extensions are not yet installed. They come from group policy
+ // and external install sources.
+ const PendingExtensionManager* pending_extension_manager =
+ service_->pending_extension_manager();
+
+ std::set<std::string> pending_ids;
+ pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_ids);
+
+ std::set<std::string>::const_iterator iter;
+ for (iter = pending_ids.begin(); iter != pending_ids.end(); ++iter) {
+ PendingExtensionInfo info;
+ bool found_id = pending_extension_manager->GetById(*iter, &info);
+ DCHECK(found_id);
+ if (!found_id)
+ continue;
+ if (!Extension::IsAutoUpdateableLocation(info.install_source())) {
+ VLOG(2) << "Extension " << *iter << " is not auto updateable";
+ continue;
+ }
+ if (downloader_->AddPendingExtension(*iter, info.update_url()))
+ in_progress_ids_.insert(*iter);
+ }
+
+ // Add fetch records for extensions that are installed and have an
+ // update URL.
+ const ExtensionSet* extensions = service_->extensions();
+ for (ExtensionSet::const_iterator iter = extensions->begin();
+ iter != extensions->end(); ++iter) {
+ const Extension& extension = **iter;
+ if (!Extension::IsAutoUpdateableLocation(extension.location())) {
+ VLOG(2) << "Extension " << extension.id() << " is not auto updateable";
+ continue;
+ }
+ // An extension might be overwritten by policy, and have its update url
+ // changed. Make sure existing extensions aren't fetched again, if a
+ // pending fetch for an extension with the same id already exists.
+ if (!ContainsKey(pending_ids, extension.id())) {
+ if (downloader_->AddExtension(extension))
+ in_progress_ids_.insert(extension.id());
+ }
+ }
+
+ // Start a fetch of the blacklist if needed.
+ if (blacklist_checks_enabled_) {
+ ManifestFetchData::PingData ping_data;
+ ping_data.rollcall_days =
+ CalculatePingDays(extension_prefs_->BlacklistLastPingDay());
+ downloader_->StartBlacklistUpdate(
+ prefs_->GetString(kExtensionBlacklistUpdateVersion), ping_data);
+ }
+
+ // StartAllPending() will call OnExtensionUpdateCheckStarted() for each
+ // extension that is going to be checked.
+ downloader_->StartAllPending();
+
+ NotifyIfFinished();
+}
+
+void ExtensionUpdater::OnExtensionDownloadFailed(const std::string& id,
+ Error error,
+ const PingResult& ping) {
+ DCHECK(alive_);
+ UpdatePingData(id, ping);
+ in_progress_ids_.erase(id);
+ NotifyIfFinished();
+}
+
+void ExtensionUpdater::OnExtensionDownloadFinished(const std::string& id,
+ const FilePath& path,
+ const GURL& download_url,
+ const PingResult& ping) {
+ DCHECK(alive_);
+ UpdatePingData(id, ping);
+
+ VLOG(2) << download_url << " written to " << path.value();
+
+ FetchedCRXFile fetched(id, path, download_url);
+ fetched_crx_files_.push(fetched);
+
+ // MaybeInstallCRXFile() removes extensions from |in_progress_ids_| after
+ // starting the crx installer.
+ MaybeInstallCRXFile();
+}
+
+void ExtensionUpdater::OnBlacklistDownloadFinished(
+ const std::string& data,
+ const std::string& package_hash,
+ const std::string& version,
+ const PingResult& ping) {
+ DCHECK(alive_);
+ UpdatePingData(ExtensionDownloader::kBlacklistAppID, ping);
+ in_progress_ids_.erase(ExtensionDownloader::kBlacklistAppID);
+ NotifyIfFinished();
+
+ // Verify sha256 hash value.
+ char sha256_hash_value[crypto::kSHA256Length];
+ crypto::SHA256HashString(data, sha256_hash_value, crypto::kSHA256Length);
+ std::string hash_in_hex = base::HexEncode(sha256_hash_value,
+ crypto::kSHA256Length);
+
+ if (package_hash != hash_in_hex) {
+ NOTREACHED() << "Fetched blacklist checksum is not as expected. "
+ << "Expected: " << package_hash << " Actual: " << hash_in_hex;
+ return;
+ }
+ std::vector<std::string> blacklist;
+ base::SplitString(data, '\n', &blacklist);
+
+ // Tell ExtensionService to update prefs.
+ service_->UpdateExtensionBlacklist(blacklist);
+
+ // Update the pref value for blacklist version
+ prefs_->SetString(kExtensionBlacklistUpdateVersion, version);
+}
+
+bool ExtensionUpdater::GetPingDataForExtension(
+ const std::string& id,
+ ManifestFetchData::PingData* ping_data) {
+ DCHECK(alive_);
+ ping_data->rollcall_days = CalculatePingDays(
+ extension_prefs_->LastPingDay(id));
+ ping_data->active_days =
+ CalculateActivePingDays(extension_prefs_->LastActivePingDay(id),
+ extension_prefs_->GetActiveBit(id));
+ return true;
+}
+
+std::string ExtensionUpdater::GetUpdateUrlData(const std::string& id) {
+ DCHECK(alive_);
+ return extension_prefs_->GetUpdateUrlData(id);
+}
+
+bool ExtensionUpdater::IsExtensionPending(const std::string& id) {
+ DCHECK(alive_);
+ return service_->pending_extension_manager()->IsIdPending(id);
+}
+
+bool ExtensionUpdater::GetExtensionExistingVersion(const std::string& id,
+ std::string* version) {
+ DCHECK(alive_);
+ if (id == ExtensionDownloader::kBlacklistAppID) {
+ *version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
+ return true;
+ }
+ const Extension* extension = service_->GetExtensionById(id, false);
+ if (!extension)
+ return false;
+ *version = extension->version()->GetString();
+ return true;
+}
+
+void ExtensionUpdater::UpdatePingData(const std::string& id,
+ const PingResult& ping_result) {
+ DCHECK(alive_);
+ if (ping_result.did_ping) {
+ if (id == ExtensionDownloader::kBlacklistAppID) {
+ extension_prefs_->SetBlacklistLastPingDay(ping_result.day_start);
+ } else if (service_->GetExtensionById(id, true) != NULL) {
+ extension_prefs_->SetLastPingDay(id, ping_result.day_start);
+ }
+ }
+ if (extension_prefs_->GetActiveBit(id)) {
+ extension_prefs_->SetActiveBit(id, false);
+ extension_prefs_->SetLastActivePingDay(id, ping_result.day_start);
+ }
+}
+
+void ExtensionUpdater::MaybeInstallCRXFile() {
+ if (crx_install_is_running_ || fetched_crx_files_.empty())
+ return;
+
+ while (!fetched_crx_files_.empty() && !crx_install_is_running_) {
+ const FetchedCRXFile& crx_file = fetched_crx_files_.top();
+
+ VLOG(2) << "updating " << crx_file.id << " with " << crx_file.path.value();
+
+ // The ExtensionService is now responsible for cleaning up the temp file
+ // at |crx_file.path|.
+ CrxInstaller* installer = NULL;
+ if (service_->UpdateExtension(crx_file.id,
+ crx_file.path,
+ crx_file.download_url,
+ &installer)) {
+ crx_install_is_running_ = true;
+
+ // Source parameter ensures that we only see the completion event for the
+ // the installer we started.
+ registrar_.Add(this,
+ chrome::NOTIFICATION_CRX_INSTALLER_DONE,
+ content::Source<CrxInstaller>(installer));
+ }
+ in_progress_ids_.erase(crx_file.id);
+ fetched_crx_files_.pop();
+ }
+
+ NotifyIfFinished();
+}
+
+void ExtensionUpdater::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK(type == chrome::NOTIFICATION_CRX_INSTALLER_DONE);
+
+ // No need to listen for CRX_INSTALLER_DONE anymore.
+ registrar_.Remove(this,
+ chrome::NOTIFICATION_CRX_INSTALLER_DONE,
+ source);
+ crx_install_is_running_ = false;
+ // If any files are available to update, start one.
+ MaybeInstallCRXFile();
+}
+
+void ExtensionUpdater::NotifyStarted() {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED,
+ content::Source<Profile>(profile_),
+ content::NotificationService::NoDetails());
+}
+
+void ExtensionUpdater::NotifyIfFinished() {
+ if (in_progress_ids_.empty()) {
+ VLOG(1) << "Sending EXTENSION_UPDATING_FINISHED";
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_UPDATING_FINISHED,
+ content::Source<Profile>(profile_),
+ content::NotificationService::NoDetails());
+ }
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_updater.h b/chrome/browser/extensions/updater/extension_updater.h
new file mode 100644
index 0000000..53d4719
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_updater.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_UPDATER_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_UPDATER_H_
+#pragma once
+
+#include <set>
+#include <stack>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "base/timer.h"
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "googleurl/src/gurl.h"
+
+class ExtensionPrefs;
+class ExtensionServiceInterface;
+class PrefService;
+class Profile;
+
+namespace extensions {
+
+class ExtensionDownloader;
+class ExtensionUpdaterTest;
+
+// A class for doing auto-updates of installed Extensions. Used like this:
+//
+// ExtensionUpdater* updater = new ExtensionUpdater(my_extensions_service,
+// extension_prefs,
+// pref_service,
+// profile,
+// update_frequency_secs);
+// updater->Start();
+// ....
+// updater->Stop();
+class ExtensionUpdater : public ExtensionDownloaderDelegate,
+ public content::NotificationObserver {
+ public:
+ // Holds a pointer to the passed |service|, using it for querying installed
+ // extensions and installing updated ones. The |frequency_seconds| parameter
+ // controls how often update checks are scheduled.
+ ExtensionUpdater(ExtensionServiceInterface* service,
+ ExtensionPrefs* extension_prefs,
+ PrefService* prefs,
+ Profile* profile,
+ int frequency_seconds);
+
+ virtual ~ExtensionUpdater();
+
+ // Starts the updater running. Should be called at most once.
+ void Start();
+
+ // Stops the updater running, cancelling any outstanding update manifest and
+ // crx downloads. Does not cancel any in-progress installs.
+ void Stop();
+
+ // Posts a task to do an update check. Does nothing if there is
+ // already a pending task that has not yet run.
+ void CheckSoon();
+
+ // Starts an update check right now, instead of waiting for the next
+ // regularly scheduled check or a pending check from CheckSoon().
+ void CheckNow();
+
+ // Set blacklist checks on or off.
+ void set_blacklist_checks_enabled(bool enabled) {
+ blacklist_checks_enabled_ = enabled;
+ }
+
+ // Returns true iff CheckSoon() has been called but the update check
+ // hasn't been performed yet. This is used mostly by tests; calling
+ // code should just call CheckSoon().
+ bool WillCheckSoon() const;
+
+ private:
+ friend class ExtensionUpdaterTest;
+ friend class ExtensionUpdaterFileHandler;
+
+ // FetchedCRXFile holds information about a CRX file we fetched to disk,
+ // but have not yet installed.
+ struct FetchedCRXFile {
+ FetchedCRXFile();
+ FetchedCRXFile(const std::string& id,
+ const FilePath& path,
+ const GURL& download_url);
+ ~FetchedCRXFile();
+
+ std::string id;
+ FilePath path;
+ GURL download_url;
+ };
+
+ // Computes when to schedule the first update check.
+ base::TimeDelta DetermineFirstCheckDelay();
+
+ // 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
+ // browser restart.
+ void ScheduleNextCheck(const base::TimeDelta& target_delay);
+
+ // BaseTimer::ReceiverMethod callback.
+ void TimerFired();
+
+ // Posted by CheckSoon().
+ void DoCheckSoon();
+
+ // Implenentation of ExtensionDownloaderDelegate.
+ virtual void OnExtensionDownloadFailed(const std::string& id,
+ Error error,
+ const PingResult& ping) OVERRIDE;
+
+ virtual void OnExtensionDownloadFinished(const std::string& id,
+ const FilePath& path,
+ const GURL& download_url,
+ const PingResult& ping) OVERRIDE;
+
+ virtual void OnBlacklistDownloadFinished(const std::string& data,
+ const std::string& package_hash,
+ const std::string& version,
+ const PingResult& ping) OVERRIDE;
+ virtual bool GetPingDataForExtension(
+ const std::string& id,
+ ManifestFetchData::PingData* ping_data) OVERRIDE;
+
+ virtual std::string GetUpdateUrlData(const std::string& id) OVERRIDE;
+
+ virtual bool IsExtensionPending(const std::string& id) OVERRIDE;
+
+ virtual bool GetExtensionExistingVersion(const std::string& id,
+ std::string* version) OVERRIDE;
+
+ void UpdatePingData(const std::string& id, const PingResult& ping_result);
+
+ // Starts installing a crx file that has been fetched but not installed yet.
+ void MaybeInstallCRXFile();
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // Send a notification that update checks are starting.
+ void NotifyStarted();
+
+ // Send a notification if we're finished updating.
+ void NotifyIfFinished();
+
+ // Whether Start() has been called but not Stop().
+ bool alive_;
+
+ base::WeakPtrFactory<ExtensionUpdater> weak_ptr_factory_;
+
+ // Pointer back to the service that owns this ExtensionUpdater.
+ ExtensionServiceInterface* service_;
+
+ // Fetches the crx files for the extensions that have an available update.
+ scoped_ptr<ExtensionDownloader> downloader_;
+
+ base::OneShotTimer<ExtensionUpdater> timer_;
+ int frequency_seconds_;
+ bool will_check_soon_;
+
+ ExtensionPrefs* extension_prefs_;
+ PrefService* prefs_;
+ Profile* profile_;
+ bool blacklist_checks_enabled_;
+
+ // The ids of extensions that have in-progress update checks.
+ std::set<std::string> in_progress_ids_;
+
+ // Observes CRX installs we initiate.
+ content::NotificationRegistrar registrar_;
+
+ // True when a CrxInstaller is doing an install. Used in MaybeUpdateCrxFile()
+ // to keep more than one install from running at once.
+ bool crx_install_is_running_;
+
+ // Fetched CRX files waiting to be installed.
+ std::stack<FetchedCRXFile> fetched_crx_files_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionUpdater);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_UPDATER_H_
diff --git a/chrome/browser/extensions/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc
similarity index 63%
rename from chrome/browser/extensions/extension_updater_unittest.cc
rename to chrome/browser/extensions/updater/extension_updater_unittest.cc
index b7cdcce..825921d 100644
--- a/chrome/browser/extensions/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -3,6 +3,8 @@
// found in the LICENSE file.
#include <map>
+#include <set>
+#include <vector>
#include "base/compiler_specific.h"
#include "base/file_util.h"
@@ -18,32 +20,52 @@
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_error_reporter.h"
#include "chrome/browser/extensions/extension_sync_data.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/extensions/test_extension_prefs.h"
#include "chrome/browser/extensions/test_extension_service.h"
+#include "chrome/browser/extensions/updater/extension_downloader.h"
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
#include "content/test/test_browser_thread.h"
#include "content/test/test_url_fetcher_factory.h"
#include "libxml/globals.h"
#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::Time;
using base::TimeDelta;
using content::BrowserThread;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::_;
+
+namespace extensions {
+
+typedef ExtensionDownloaderDelegate::Error Error;
+typedef ExtensionDownloaderDelegate::PingResult PingResult;
namespace {
const char kEmptyUpdateUrlData[] = "";
-int expected_load_flags =
+int kExpectedLoadFlags =
net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DISABLE_CACHE;
@@ -51,13 +73,94 @@
const ManifestFetchData::PingData kNeverPingedData(
ManifestFetchData::kNeverPinged, ManifestFetchData::kNeverPinged);
+class MockExtensionDownloaderDelegate : public ExtensionDownloaderDelegate {
+ public:
+ MOCK_METHOD3(OnExtensionDownloadFailed,
+ void(const std::string&, Error, const PingResult&));
+ MOCK_METHOD4(OnExtensionDownloadFinished, void(const std::string&,
+ const FilePath&,
+ const GURL&,
+ const PingResult&));
+ MOCK_METHOD4(OnBlacklistDownloadFinished, void(const std::string&,
+ const std::string&,
+ const std::string&,
+ const PingResult&));
+ MOCK_METHOD2(GetPingDataForExtension,
+ bool(const std::string&, ManifestFetchData::PingData*));
+ MOCK_METHOD1(GetUpdateUrlData, std::string(const std::string&));
+ MOCK_METHOD1(IsExtensionPending, bool(const std::string&));
+ MOCK_METHOD2(GetExtensionExistingVersion,
+ bool(const std::string&, std::string*));
+};
+
+const int kNotificationsObserved[] = {
+ chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED,
+ chrome::NOTIFICATION_EXTENSION_UPDATING_FINISHED,
+ chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
+};
+
+// A class that observes the notifications sent by the ExtensionUpdater and
+// the ExtensionDownloader.
+class NotificationsObserver : public content::NotificationObserver {
+ public:
+ NotificationsObserver() {
+ for (size_t i = 0; i < arraysize(kNotificationsObserved); ++i) {
+ count_[i] = 0;
+ registrar_.Add(this,
+ kNotificationsObserved[i],
+ content::NotificationService::AllSources());
+ }
+ }
+
+ virtual ~NotificationsObserver() {
+ for (size_t i = 0; i < arraysize(kNotificationsObserved); ++i) {
+ registrar_.Remove(this,
+ kNotificationsObserved[i],
+ content::NotificationService::AllSources());
+ }
+ }
+
+ size_t StartedCount() { return count_[0]; }
+ size_t FinishedCount() { return count_[1]; }
+ size_t UpdatedCount() { return count_[2]; }
+
+ bool Updated(const std::string& id) {
+ return updated_.find(id) != updated_.end();
+ }
+
+ private:
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE {
+ for (size_t i = 0; i < arraysize(kNotificationsObserved); ++i) {
+ if (kNotificationsObserved[i] == type) {
+ count_[i]++;
+ if (type == chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND) {
+ updated_.insert(
+ *(content::Details<const std::string>(details).ptr()));
+ }
+ return;
+ }
+ }
+ NOTREACHED();
+ }
+
+ content::NotificationRegistrar registrar_;
+ size_t count_[arraysize(kNotificationsObserved)];
+ std::set<std::string> updated_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotificationsObserver);
+};
+
} // namespace
// Base class for further specialized test classes.
class MockService : public TestExtensionService {
public:
MockService()
- : pending_extension_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(*this)) {}
+ : pending_extension_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(*this)) {
+ profile_.CreateRequestContext();
+ }
virtual ~MockService() {}
virtual PendingExtensionManager* pending_extension_manager() OVERRIDE {
@@ -68,6 +171,10 @@
Profile* profile() { return &profile_; }
+ net::URLRequestContextGetter* request_context() {
+ return profile_.GetRequestContext();
+ }
+
ExtensionPrefs* extension_prefs() { return prefs_.prefs(); }
PrefService* pref_service() { return prefs_.pref_service(); }
@@ -287,24 +394,33 @@
// inside this class (which is a friend to ExtensionUpdater).
class ExtensionUpdaterTest : public testing::Test {
public:
- static void SimulateTimerFired(ExtensionUpdater* updater) {
+ ExtensionUpdaterTest()
+ : ui_thread_(BrowserThread::UI, &loop_),
+ file_thread_(BrowserThread::FILE, &loop_),
+ io_thread_(BrowserThread::IO, &loop_) {}
+
+ virtual void TearDown() OVERRIDE {
+ // Some tests create URLRequestContextGetters, whose destruction must run
+ // on the IO thread. Make sure the IO loop spins before shutdown so that
+ // those objects are released.
+ loop_.RunAllPending();
+ }
+
+ void RunAllPending() {
+ loop_.RunAllPending();
+ }
+
+ void SimulateTimerFired(ExtensionUpdater* updater) {
EXPECT_TRUE(updater->timer_.IsRunning());
updater->timer_.Stop();
updater->TimerFired();
}
- static void SimulateCheckSoon(const ExtensionUpdater& updater,
- MessageLoop* message_loop) {
- EXPECT_TRUE(updater.will_check_soon_);
- message_loop->RunAllPending();
- }
-
// Adds a Result with the given data to results.
- static void AddParseResult(
- const std::string& id,
- const std::string& version,
- const std::string& url,
- UpdateManifest::Results* results) {
+ void AddParseResult(const std::string& id,
+ const std::string& version,
+ const std::string& url,
+ UpdateManifest::Results* results) {
UpdateManifest::Result result;
result.extension_id = id;
result.version = version;
@@ -312,13 +428,23 @@
results->list.push_back(result);
}
- static void TestExtensionUpdateCheckRequests(bool pending) {
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
- content::TestBrowserThread io_thread(BrowserThread::IO);
- io_thread.Start();
+ void ResetDownloader(ExtensionUpdater* updater,
+ ExtensionDownloader* downloader) {
+ EXPECT_FALSE(updater->downloader_.get());
+ updater->downloader_.reset(downloader);
+ }
+ void StartUpdateCheck(ExtensionDownloader* downloader,
+ ManifestFetchData* fetch_data) {
+ downloader->StartUpdateCheck(fetch_data);
+ }
+
+ size_t ManifestFetchersCount(ExtensionDownloader* downloader) {
+ return downloader->manifests_pending_.size() +
+ (downloader->manifest_fetcher_.get() ? 1 : 0);
+ }
+
+ void TestExtensionUpdateCheckRequests(bool pending) {
// Create an extension with an update_url.
ServiceForManifestTests service;
std::string update_url("http://foo.com/bar");
@@ -349,7 +475,7 @@
// Get the url our mock fetcher was asked to fetch.
TestURLFetcher* fetcher =
- factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+ factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
const GURL& url = fetcher->GetOriginalURL();
EXPECT_FALSE(url.is_empty());
EXPECT_TRUE(url.is_valid());
@@ -378,14 +504,9 @@
EXPECT_EQ("", params["uc"]);
}
- static void TestBlacklistUpdateCheckRequests() {
- ServiceForManifestTests service;
-
+ void TestBlacklistUpdateCheckRequests() {
// Setup and start the updater.
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- content::TestBrowserThread io_thread(BrowserThread::IO);
- io_thread.Start();
+ ServiceForManifestTests service;
TestURLFetcherFactory factory;
ExtensionUpdater updater(
@@ -398,7 +519,7 @@
// Get the url our mock fetcher was asked to fetch.
TestURLFetcher* fetcher =
- factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+ factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
ASSERT_FALSE(fetcher == NULL);
const GURL& url = fetcher->GetOriginalURL();
@@ -425,142 +546,138 @@
EXPECT_TRUE(ContainsKey(params, "ping"));
}
- static void TestUpdateUrlDataEmpty() {
+ void TestUpdateUrlDataEmpty() {
const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const std::string version = "1.0";
// Make sure that an empty update URL data string does not cause a ap=
// option to appear in the x= parameter.
ManifestFetchData fetch_data(GURL("http://localhost/foo"));
- fetch_data.AddExtension(id, version,
- kNeverPingedData, "", "");
+ fetch_data.AddExtension(id, version, &kNeverPingedData, "", "");
EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"%26v%3D1.0%26uc",
fetch_data.full_url().spec());
}
- static void TestUpdateUrlDataSimple() {
+ void TestUpdateUrlDataSimple() {
const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const std::string version = "1.0";
// Make sure that an update URL data string causes an appropriate ap=
// option to appear in the x= parameter.
ManifestFetchData fetch_data(GURL("http://localhost/foo"));
- fetch_data.AddExtension(id, version,
- kNeverPingedData, "bar", "");
+ fetch_data.AddExtension(id, version, &kNeverPingedData, "bar", "");
EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"%26v%3D1.0%26uc%26ap%3Dbar",
fetch_data.full_url().spec());
}
- static void TestUpdateUrlDataCompound() {
+ void TestUpdateUrlDataCompound() {
const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const std::string version = "1.0";
// Make sure that an update URL data string causes an appropriate ap=
// option to appear in the x= parameter.
ManifestFetchData fetch_data(GURL("http://localhost/foo"));
- fetch_data.AddExtension(id, version,
- kNeverPingedData, "a=1&b=2&c", "");
+ fetch_data.AddExtension(id, version, &kNeverPingedData, "a=1&b=2&c", "");
EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"%26v%3D1.0%26uc%26ap%3Da%253D1%2526b%253D2%2526c",
fetch_data.full_url().spec());
}
- static void TestUpdateUrlDataFromGallery(const std::string& gallery_url) {
+ void TestUpdateUrlDataFromGallery(const std::string& gallery_url) {
+ TestURLFetcherFactory factory;
+
MockService service;
- ManifestFetchesBuilder builder(&service, service.extension_prefs());
+ MockExtensionDownloaderDelegate delegate;
+ ExtensionDownloader downloader(&delegate, service.request_context());
ExtensionList extensions;
std::string url(gallery_url);
service.CreateTestExtensions(1, 1, &extensions, &url, Extension::INTERNAL);
- builder.AddExtension(*extensions[0]);
- std::vector<ManifestFetchData*> fetches = builder.GetFetches();
- EXPECT_EQ(1u, fetches.size());
- scoped_ptr<ManifestFetchData> fetch(fetches[0]);
- fetches.clear();
+ const std::string& id = extensions[0]->id();
+ EXPECT_CALL(delegate, GetPingDataForExtension(id, _));
+
+ downloader.AddExtension(*extensions[0]);
+ downloader.StartAllPending();
+ TestURLFetcher* fetcher =
+ factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
+ ASSERT_TRUE(fetcher);
// Make sure that extensions that update from the gallery ignore any
// update URL data.
- const std::string& update_url = fetch->full_url().spec();
+ const std::string& update_url = fetcher->GetOriginalURL().spec();
std::string::size_type x = update_url.find("x=");
EXPECT_NE(std::string::npos, x);
std::string::size_type ap = update_url.find("ap%3D", x);
EXPECT_EQ(std::string::npos, ap);
}
- static void TestInstallSource() {
+ void TestInstallSource() {
const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const std::string version = "1.0";
const std::string install_source = "instally";
// Make sure that an installsource= appears in the x= parameter.
ManifestFetchData fetch_data(GURL("http://localhost/foo"));
- fetch_data.AddExtension(id, version, kNeverPingedData,
+ fetch_data.AddExtension(id, version, &kNeverPingedData,
kEmptyUpdateUrlData, install_source);
EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"%26v%3D1.0%26installsource%3Dinstally%26uc",
fetch_data.full_url().spec());
}
- static void TestDetermineUpdates() {
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
- // Create a set of test extensions
- ServiceForManifestTests service;
- ExtensionList tmp;
- service.CreateTestExtensions(1, 3, &tmp, NULL, Extension::INTERNAL);
- service.set_extensions(tmp);
-
- ExtensionUpdater updater(
- &service, service.extension_prefs(), service.pref_service(),
- service.profile(), kUpdateFrequencySecs);
- updater.Start();
+ void TestDetermineUpdates() {
+ TestingProfile profile;
+ profile.CreateRequestContext();
+ MockExtensionDownloaderDelegate delegate;
+ ExtensionDownloader downloader(&delegate, profile.GetRequestContext());
// Check passing an empty list of parse results to DetermineUpdates
ManifestFetchData fetch_data(GURL("http://localhost/foo"));
UpdateManifest::Results updates;
- std::vector<int> updateable = updater.DetermineUpdates(fetch_data,
- updates);
+ std::vector<int> updateable;
+ downloader.DetermineUpdates(fetch_data, updates, &updateable);
EXPECT_TRUE(updateable.empty());
// Create two updates - expect that DetermineUpdates will return the first
// one (v1.0 installed, v1.1 available) but not the second one (both
// installed and available at v2.0).
- scoped_ptr<Version> one(Version::GetVersionFromString("1.0"));
- EXPECT_TRUE(tmp[0]->version()->Equals(*one));
- fetch_data.AddExtension(tmp[0]->id(), tmp[0]->VersionString(),
- kNeverPingedData,
- kEmptyUpdateUrlData,
- "");
- AddParseResult(tmp[0]->id(),
- "1.1", "http://localhost/e1_1.1.crx", &updates);
- fetch_data.AddExtension(tmp[1]->id(), tmp[1]->VersionString(),
- kNeverPingedData,
- kEmptyUpdateUrlData,
- "");
- AddParseResult(tmp[1]->id(),
- tmp[1]->VersionString(), "http://localhost/e2_2.0.crx", &updates);
- updateable = updater.DetermineUpdates(fetch_data, updates);
+ const std::string id1 = GenerateId("1");
+ const std::string id2 = GenerateId("2");
+ fetch_data.AddExtension(id1, "1.0.0.0",
+ &kNeverPingedData, kEmptyUpdateUrlData, "");
+ AddParseResult(id1, "1.1",
+ "http://localhost/e1_1.1.crx", &updates);
+ fetch_data.AddExtension(id2, "2.0.0.0",
+ &kNeverPingedData, kEmptyUpdateUrlData, "");
+ AddParseResult(id2, "2.0.0.0",
+ "http://localhost/e2_2.0.crx", &updates);
+
+ EXPECT_CALL(delegate, IsExtensionPending(_)).WillRepeatedly(Return(false));
+ EXPECT_CALL(delegate, GetExtensionExistingVersion(id1, _))
+ .WillOnce(DoAll(SetArgPointee<1>("1.0.0.0"),
+ Return(true)));
+ EXPECT_CALL(delegate, GetExtensionExistingVersion(id2, _))
+ .WillOnce(DoAll(SetArgPointee<1>("2.0.0.0"),
+ Return(true)));
+
+ downloader.DetermineUpdates(fetch_data, updates, &updateable);
EXPECT_EQ(1u, updateable.size());
EXPECT_EQ(0, updateable[0]);
}
- static void TestDetermineUpdatesPending() {
+ void TestDetermineUpdatesPending() {
// Create a set of test extensions
ServiceForManifestTests service;
PendingExtensionManager* pending_extension_manager =
service.pending_extension_manager();
SetupPendingExtensionManagerForTest(3, GURL(), pending_extension_manager);
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- ExtensionUpdater updater(
- &service, service.extension_prefs(), service.pref_service(),
- service.profile(), kUpdateFrequencySecs);
- updater.Start();
+ TestingProfile profile;
+ profile.CreateRequestContext();
+ MockExtensionDownloaderDelegate delegate;
+ ExtensionDownloader downloader(&delegate, profile.GetRequestContext());
ManifestFetchData fetch_data(GURL("http://localhost/foo"));
UpdateManifest::Results updates;
@@ -572,15 +689,17 @@
std::set<std::string>::const_iterator it;
for (it = ids_for_update_check.begin();
it != ids_for_update_check.end(); ++it) {
- fetch_data.AddExtension(*it,
- "1.0.0.0",
- kNeverPingedData,
- kEmptyUpdateUrlData,
- "");
+ fetch_data.AddExtension(*it, "1.0.0.0",
+ &kNeverPingedData, kEmptyUpdateUrlData, "");
AddParseResult(*it, "1.1", "http://localhost/e1_1.1.crx", &updates);
}
- std::vector<int> updateable =
- updater.DetermineUpdates(fetch_data, updates);
+
+ // The delegate will tell the downloader that all the extensions are
+ // pending.
+ EXPECT_CALL(delegate, IsExtensionPending(_)).WillRepeatedly(Return(true));
+
+ std::vector<int> updateable;
+ downloader.DetermineUpdates(fetch_data, updates, &updateable);
// All the apps should be updateable.
EXPECT_EQ(3u, updateable.size());
for (std::vector<int>::size_type i = 0; i < updateable.size(); ++i) {
@@ -588,93 +707,120 @@
}
}
- static void TestMultipleManifestDownloading() {
- MessageLoop ui_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &ui_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE);
- file_thread.Start();
- content::TestBrowserThread io_thread(BrowserThread::IO);
- io_thread.Start();
-
+ void TestMultipleManifestDownloading() {
TestURLFetcherFactory factory;
TestURLFetcher* fetcher = NULL;
- scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests);
- ExtensionUpdater updater(service.get(),
- service->extension_prefs(),
- service->pref_service(),
- service->profile(),
- kUpdateFrequencySecs);
- updater.Start();
+ NotificationsObserver observer;
+ MockService service;
+ MockExtensionDownloaderDelegate delegate;
+ ExtensionDownloader downloader(&delegate, service.request_context());
- GURL url1("http://localhost/manifest1");
- GURL url2("http://localhost/manifest2");
+ GURL kUpdateUrl("http://localhost/manifest1");
- // Request 2 update checks - the first should begin immediately and the
- // second one should be queued up.
- ManifestFetchData* fetch1 = new ManifestFetchData(url1);
- ManifestFetchData* fetch2 = new ManifestFetchData(url2);
+ ManifestFetchData* fetch1 = new ManifestFetchData(kUpdateUrl);
+ ManifestFetchData* fetch2 = new ManifestFetchData(kUpdateUrl);
+ ManifestFetchData* fetch3 = new ManifestFetchData(kUpdateUrl);
+ ManifestFetchData* fetch4 = new ManifestFetchData(kUpdateUrl);
ManifestFetchData::PingData zeroDays(0, 0);
- fetch1->AddExtension("1111", "1.0", zeroDays, kEmptyUpdateUrlData, "");
- fetch2->AddExtension("12345", "2.0", kNeverPingedData,
- kEmptyUpdateUrlData, "");
- updater.StartUpdateCheck(fetch1);
- updater.StartUpdateCheck(fetch2);
+ fetch1->AddExtension("1111", "1.0", &zeroDays, kEmptyUpdateUrlData, "");
+ fetch2->AddExtension("2222", "2.0", &zeroDays, kEmptyUpdateUrlData, "");
+ fetch3->AddExtension("3333", "3.0", &zeroDays, kEmptyUpdateUrlData, "");
+ fetch4->AddExtension("4444", "4.0", &zeroDays, kEmptyUpdateUrlData, "");
- std::string invalid_xml = "invalid xml";
- fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+ // This will start the first fetcher and queue the others. The next in queue
+ // is started as each fetcher receives its response.
+ downloader.StartUpdateCheck(fetch1);
+ downloader.StartUpdateCheck(fetch2);
+ downloader.StartUpdateCheck(fetch3);
+ downloader.StartUpdateCheck(fetch4);
+ RunAllPending();
+
+ // The first fetch will fail.
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
- EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+ EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
+ EXPECT_CALL(delegate, OnExtensionDownloadFailed(
+ "1111", ExtensionDownloaderDelegate::MANIFEST_FETCH_FAILED, _));
+ fetcher->set_url(kUpdateUrl);
+ fetcher->set_status(net::URLRequestStatus());
+ fetcher->set_response_code(400);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ RunAllPending();
+ Mock::VerifyAndClearExpectations(&delegate);
- fetcher->set_url(url1);
+ // The second fetch gets invalid data.
+ const std::string kInvalidXml = "invalid xml";
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
+ EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
+ EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
+ EXPECT_CALL(delegate, OnExtensionDownloadFailed(
+ "2222", ExtensionDownloaderDelegate::MANIFEST_INVALID, _));
+ fetcher->set_url(kUpdateUrl);
fetcher->set_status(net::URLRequestStatus());
fetcher->set_response_code(200);
- fetcher->SetResponseString(invalid_xml);
+ fetcher->SetResponseString(kInvalidXml);
fetcher->delegate()->OnURLFetchComplete(fetcher);
+ RunAllPending();
+ Mock::VerifyAndClearExpectations(&delegate);
- // Now that the first request is complete, make sure the second one has
- // been started.
- const std::string kValidXml =
+ // The third fetcher doesn't have an update available.
+ const std::string kNoUpdate =
"<?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' />"
+ " <app appid='3333'>"
+ " <updatecheck codebase='http://example.com/extension_3.0.0.0.crx'"
+ " version='3.0.0.0' prodversionmin='3.0.0.0' />"
" </app>"
"</gupdate>";
- fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
- EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
-
- fetcher->set_url(url2);
+ EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
+ EXPECT_CALL(delegate, IsExtensionPending("3333")).WillOnce(Return(false));
+ EXPECT_CALL(delegate, GetExtensionExistingVersion("3333", _))
+ .WillOnce(DoAll(SetArgPointee<1>("3.0.0.0"),
+ Return(true)));
+ EXPECT_CALL(delegate, OnExtensionDownloadFailed(
+ "3333", ExtensionDownloaderDelegate::NO_UPDATE_AVAILABLE, _));
+ fetcher->set_url(kUpdateUrl);
fetcher->set_status(net::URLRequestStatus());
fetcher->set_response_code(200);
- fetcher->SetResponseString(kValidXml);
+ fetcher->SetResponseString(kNoUpdate);
fetcher->delegate()->OnURLFetchComplete(fetcher);
+ RunAllPending();
+ Mock::VerifyAndClearExpectations(&delegate);
- // This should run the manifest parsing, then we want to make sure that our
- // service was called with GetExtensionById with the matching id from
- // kValidXml.
- file_thread.Stop();
- io_thread.Stop();
- ui_loop.RunAllPending();
- EXPECT_EQ("12345", service->last_inquired_extension_id());
- xmlCleanupGlobals();
+ // The last fetcher has an update.
+ const std::string kUpdateAvailable =
+ "<?xml version='1.0' encoding='UTF-8'?>"
+ "<gupdate xmlns='http://www.google.com/update2/response'"
+ " protocol='2.0'>"
+ " <app appid='4444'>"
+ " <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'"
+ " version='4.0.42.0' prodversionmin='4.0.42.0' />"
+ " </app>"
+ "</gupdate>";
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
+ EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
+ EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
+ EXPECT_CALL(delegate, IsExtensionPending("4444")).WillOnce(Return(false));
+ EXPECT_CALL(delegate, GetExtensionExistingVersion("4444", _))
+ .WillOnce(DoAll(SetArgPointee<1>("4.0.0.0"),
+ Return(true)));
+ fetcher->set_url(kUpdateUrl);
+ fetcher->set_status(net::URLRequestStatus());
+ fetcher->set_response_code(200);
+ fetcher->SetResponseString(kUpdateAvailable);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ RunAllPending();
+ Mock::VerifyAndClearExpectations(&delegate);
- // The FILE thread is needed for |service|'s cleanup,
- // because of ImportantFileWriter.
- file_thread.Start();
- service.reset();
+ // Verify that the downloader decided to update this extension.
+ EXPECT_EQ(1u, observer.UpdatedCount());
+ EXPECT_TRUE(observer.Updated("4444"));
}
- static void TestSingleExtensionDownloading(bool pending) {
- MessageLoop ui_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &ui_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE);
- file_thread.Start();
- content::TestBrowserThread io_thread(BrowserThread::IO);
- io_thread.Start();
-
+ void TestSingleExtensionDownloading(bool pending) {
TestURLFetcherFactory factory;
TestURLFetcher* fetcher = NULL;
scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests);
@@ -683,6 +829,9 @@
service->profile(),
kUpdateFrequencySecs);
updater.Start();
+ ResetDownloader(
+ &updater,
+ new ExtensionDownloader(&updater, service->request_context()));
GURL test_url("http://localhost/extension.crx");
@@ -690,7 +839,8 @@
std::string hash = "";
scoped_ptr<Version> version(Version::GetVersionFromString("0.0.1"));
ASSERT_TRUE(version.get());
- updater.FetchUpdatedExtension(id, test_url, hash, version->GetString());
+ updater.downloader_->FetchUpdatedExtension(
+ id, test_url, hash, version->GetString());
if (pending) {
const bool kIsFromSync = true;
@@ -706,9 +856,9 @@
// Call back the ExtensionUpdater with a 200 response and some test data
FilePath extension_file_path(FILE_PATH_LITERAL("/whatever"));
- fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId);
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kExtensionFetcherId);
EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
- EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+ EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
fetcher->set_url(test_url);
fetcher->set_status(net::URLRequestStatus());
@@ -716,8 +866,7 @@
fetcher->SetResponseFilePath(extension_file_path);
fetcher->delegate()->OnURLFetchComplete(fetcher);
- file_thread.Stop();
- ui_loop.RunAllPending();
+ RunAllPending();
// Expect that ExtensionUpdater asked the mock extensions service to install
// a file with the test data for the right id.
@@ -726,20 +875,9 @@
EXPECT_FALSE(tmpfile_path.empty());
EXPECT_EQ(test_url, service->download_url());
EXPECT_EQ(extension_file_path, tmpfile_path);
-
- // The FILE thread is needed for |service|'s cleanup,
- // because of ImportantFileWriter.
- file_thread.Start();
- service.reset();
}
- static void TestBlacklistDownloading() {
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
- content::TestBrowserThread io_thread(BrowserThread::IO);
- io_thread.Start();
-
+ void TestBlacklistDownloading() {
TestURLFetcherFactory factory;
TestURLFetcher* fetcher = NULL;
ServiceForBlacklistTests service;
@@ -747,6 +885,9 @@
&service, service.extension_prefs(), service.pref_service(),
service.profile(), kUpdateFrequencySecs);
updater.Start();
+ ResetDownloader(
+ &updater,
+ new ExtensionDownloader(&updater, service.request_context()));
GURL test_url("http://localhost/extension.crx");
std::string id = "com.google.crx.blacklist";
@@ -755,14 +896,14 @@
"2CE109E9D0FAF820B2434E166297934E6177B65AB9951DBC3E204CAD4689B39C";
std::string version = "0.0.1";
- updater.FetchUpdatedExtension(id, test_url, hash, version);
+ updater.downloader_->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);
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kExtensionFetcherId);
EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
- EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+ EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
fetcher->set_url(test_url);
fetcher->set_status(net::URLRequestStatus());
@@ -770,7 +911,7 @@
fetcher->SetResponseString(extension_data);
fetcher->delegate()->OnURLFetchComplete(fetcher);
- message_loop.RunAllPending();
+ RunAllPending();
// The updater should have called extension service to process the
// blacklist.
@@ -784,13 +925,7 @@
// mock extensions service has UpdateExtension(...) return true, and
// the test is responsible for creating fake CrxInstallers. Otherwise,
// UpdateExtension() returns false, signaling install failures.
- static void TestMultipleExtensionDownloading(bool updates_start_running) {
- MessageLoopForUI message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
- content::TestBrowserThread io_thread(BrowserThread::IO);
- io_thread.Start();
-
+ void TestMultipleExtensionDownloading(bool updates_start_running) {
TestURLFetcherFactory factory;
TestURLFetcher* fetcher = NULL;
ServiceForDownloadTests service;
@@ -798,6 +933,9 @@
&service, service.extension_prefs(), service.pref_service(),
service.profile(), kUpdateFrequencySecs);
updater.Start();
+ ResetDownloader(
+ &updater,
+ new ExtensionDownloader(&updater, service.request_context()));
EXPECT_FALSE(updater.crx_install_is_running_);
@@ -813,15 +951,15 @@
std::string version1 = "0.1";
std::string version2 = "0.1";
// Start two fetches
- updater.FetchUpdatedExtension(id1, url1, hash1, version1);
- updater.FetchUpdatedExtension(id2, url2, hash2, version2);
+ updater.downloader_->FetchUpdatedExtension(id1, url1, hash1, version1);
+ updater.downloader_->FetchUpdatedExtension(id2, url2, hash2, version2);
// Make the first fetch complete.
FilePath extension_file_path(FILE_PATH_LITERAL("/whatever"));
- fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId);
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kExtensionFetcherId);
EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
- EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+ EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
// We need some CrxInstallers, and CrxInstallers require a real
// ExtensionService. Create one on the testing profile. Any action
@@ -856,28 +994,28 @@
fetcher->SetResponseFilePath(extension_file_path);
fetcher->delegate()->OnURLFetchComplete(fetcher);
- message_loop.RunAllPending();
+ RunAllPending();
// Expect that the service was asked to do an install with the right data.
FilePath tmpfile_path = service.install_path();
EXPECT_FALSE(tmpfile_path.empty());
EXPECT_EQ(id1, service.extension_id());
EXPECT_EQ(url1, service.download_url());
- message_loop.RunAllPending();
+ RunAllPending();
// Make sure the second fetch finished and asked the service to do an
// update.
FilePath extension_file_path2(FILE_PATH_LITERAL("/whatever2"));
- fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId);
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kExtensionFetcherId);
EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
- EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+ EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
fetcher->set_url(url2);
fetcher->set_status(net::URLRequestStatus());
fetcher->set_response_code(200);
fetcher->SetResponseFilePath(extension_file_path2);
fetcher->delegate()->OnURLFetchComplete(fetcher);
- message_loop.RunAllPending();
+ RunAllPending();
if (updates_start_running) {
EXPECT_TRUE(updater.crx_install_is_running_);
@@ -908,7 +1046,7 @@
EXPECT_FALSE(updater.crx_install_is_running_);
}
- static void TestGalleryRequestsWithBrand(bool use_organic_brand_code) {
+ void TestGalleryRequestsWithBrand(bool use_organic_brand_code) {
google_util::BrandForTesting brand_for_testing(
use_organic_brand_code ? "GGLS" : "TEST");
@@ -930,9 +1068,8 @@
continue;
bool active_bit = k > 0;
- ExtensionUpdaterTest::TestGalleryRequests(
- rollcall_ping_days, active_ping_days, active_bit,
- !use_organic_brand_code);
+ TestGalleryRequests(rollcall_ping_days, active_ping_days, active_bit,
+ !use_organic_brand_code);
ASSERT_FALSE(HasFailure()) <<
" rollcall_ping_days=" << ping_cases[i] <<
" active_ping_days=" << ping_cases[j] <<
@@ -948,14 +1085,10 @@
// value should be present at most once per day, and can be calculated based
// on the delta between now and the last ping time (or in the case of active
// pings, that delta plus whether the app has been active).
- static void TestGalleryRequests(int rollcall_ping_days,
- int active_ping_days,
- bool active_bit,
- bool expect_brand_code) {
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
+ void TestGalleryRequests(int rollcall_ping_days,
+ int active_ping_days,
+ bool active_bit,
+ bool expect_brand_code) {
TestURLFetcherFactory factory;
// Set up 2 mock extensions, one with a google.com update url and one
@@ -998,15 +1131,15 @@
ExtensionUpdater updater(
&service, service.extension_prefs(), service.pref_service(),
service.profile(), kUpdateFrequencySecs);
- updater.Start();
updater.set_blacklist_checks_enabled(false);
+ updater.Start();
+ updater.CheckNow();
// Make the updater do manifest fetching, and note the urls it tries to
// fetch.
std::vector<GURL> fetched_urls;
- updater.CheckNow();
TestURLFetcher* fetcher =
- factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+ factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
fetched_urls.push_back(fetcher->GetOriginalURL());
@@ -1016,7 +1149,7 @@
fetcher->SetResponseString("");
fetcher->delegate()->OnURLFetchComplete(fetcher);
- fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+ fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
fetched_urls.push_back(fetcher->GetOriginalURL());
// The urls could have been fetched in either order, so use the host to
@@ -1078,37 +1211,42 @@
// of a <daystart> tag from a manifest fetch in one of two cases: 1) This is
// the first time we fetched the extension, or 2) We sent a ping value of
// >= 1 day for the extension.
- static void TestHandleManifestResults() {
+ void TestHandleManifestResults() {
ServiceForManifestTests service;
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- ExtensionUpdater updater(
- &service, service.extension_prefs(), service.pref_service(),
- service.profile(), kUpdateFrequencySecs);
- updater.Start();
-
GURL update_url("http://www.google.com/manifest");
ExtensionList tmp;
service.CreateTestExtensions(1, 1, &tmp, &update_url.spec(),
Extension::INTERNAL);
service.set_extensions(tmp);
+ ExtensionUpdater updater(
+ &service, service.extension_prefs(), service.pref_service(),
+ service.profile(), kUpdateFrequencySecs);
+ updater.Start();
+ ResetDownloader(
+ &updater,
+ new ExtensionDownloader(&updater, service.request_context()));
+
ManifestFetchData fetch_data(update_url);
const Extension* extension = tmp[0];
fetch_data.AddExtension(extension->id(), extension->VersionString(),
- kNeverPingedData,
- kEmptyUpdateUrlData,
- "");
+ &kNeverPingedData, kEmptyUpdateUrlData, "");
UpdateManifest::Results results;
results.daystart_elapsed_seconds = 750;
- updater.HandleManifestResults(fetch_data, &results);
+ updater.downloader_->HandleManifestResults(fetch_data, &results);
Time last_ping_day =
service.extension_prefs()->LastPingDay(extension->id());
EXPECT_FALSE(last_ping_day.is_null());
int64 seconds_diff = (Time::Now() - last_ping_day).InSeconds();
EXPECT_LT(seconds_diff - results.daystart_elapsed_seconds, 5);
}
+
+ private:
+ MessageLoop loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread file_thread_;
+ content::TestBrowserThread io_thread_;
};
// Because we test some private methods of ExtensionUpdater, it's easer for the
@@ -1116,169 +1254,191 @@
// subclasses where friendship with ExtenionUpdater is not inherited.
TEST_F(ExtensionUpdaterTest, TestExtensionUpdateCheckRequests) {
- ExtensionUpdaterTest::TestExtensionUpdateCheckRequests(false);
+ TestExtensionUpdateCheckRequests(false);
}
TEST_F(ExtensionUpdaterTest, TestExtensionUpdateCheckRequestsPending) {
- ExtensionUpdaterTest::TestExtensionUpdateCheckRequests(true);
+ TestExtensionUpdateCheckRequests(true);
}
TEST_F(ExtensionUpdaterTest, TestBlacklistUpdateCheckRequests) {
- ExtensionUpdaterTest::TestBlacklistUpdateCheckRequests();
+ TestBlacklistUpdateCheckRequests();
}
TEST_F(ExtensionUpdaterTest, TestUpdateUrlData) {
- MessageLoop message_loop;
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
- ExtensionUpdaterTest::TestUpdateUrlDataEmpty();
- ExtensionUpdaterTest::TestUpdateUrlDataSimple();
- ExtensionUpdaterTest::TestUpdateUrlDataCompound();
- ExtensionUpdaterTest::TestUpdateUrlDataFromGallery(
+ TestUpdateUrlDataEmpty();
+ TestUpdateUrlDataSimple();
+ TestUpdateUrlDataCompound();
+ TestUpdateUrlDataFromGallery(
extension_urls::GetWebstoreUpdateUrl(false).spec());
- ExtensionUpdaterTest::TestUpdateUrlDataFromGallery(
+ TestUpdateUrlDataFromGallery(
extension_urls::GetWebstoreUpdateUrl(true).spec());
}
TEST_F(ExtensionUpdaterTest, TestInstallSource) {
- ExtensionUpdaterTest::TestInstallSource();
+ TestInstallSource();
}
TEST_F(ExtensionUpdaterTest, TestDetermineUpdates) {
- ExtensionUpdaterTest::TestDetermineUpdates();
+ TestDetermineUpdates();
}
TEST_F(ExtensionUpdaterTest, TestDetermineUpdatesPending) {
- ExtensionUpdaterTest::TestDetermineUpdatesPending();
+ TestDetermineUpdatesPending();
}
TEST_F(ExtensionUpdaterTest, TestMultipleManifestDownloading) {
- ExtensionUpdaterTest::TestMultipleManifestDownloading();
+ TestMultipleManifestDownloading();
}
TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloading) {
- ExtensionUpdaterTest::TestSingleExtensionDownloading(false);
+ TestSingleExtensionDownloading(false);
}
TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloadingPending) {
- ExtensionUpdaterTest::TestSingleExtensionDownloading(true);
+ TestSingleExtensionDownloading(true);
}
TEST_F(ExtensionUpdaterTest, TestBlacklistDownloading) {
- ExtensionUpdaterTest::TestBlacklistDownloading();
+ TestBlacklistDownloading();
}
TEST_F(ExtensionUpdaterTest, TestMultipleExtensionDownloadingUpdatesFail) {
- ExtensionUpdaterTest::TestMultipleExtensionDownloading(false);
+ TestMultipleExtensionDownloading(false);
}
TEST_F(ExtensionUpdaterTest, TestMultipleExtensionDownloadingUpdatesSucceed) {
- ExtensionUpdaterTest::TestMultipleExtensionDownloading(true);
+ TestMultipleExtensionDownloading(true);
}
TEST_F(ExtensionUpdaterTest, TestGalleryRequestsWithOrganicBrand) {
- ExtensionUpdaterTest::TestGalleryRequestsWithBrand(true);
+ TestGalleryRequestsWithBrand(true);
}
TEST_F(ExtensionUpdaterTest, TestGalleryRequestsWithNonOrganicBrand) {
- ExtensionUpdaterTest::TestGalleryRequestsWithBrand(false);
+ TestGalleryRequestsWithBrand(false);
}
TEST_F(ExtensionUpdaterTest, TestHandleManifestResults) {
- ExtensionUpdaterTest::TestHandleManifestResults();
+ TestHandleManifestResults();
+}
+
+TEST_F(ExtensionUpdaterTest, TestNonAutoUpdateableLocations) {
+ TestURLFetcherFactory factory;
+ ServiceForManifestTests service;
+ ExtensionUpdater updater(&service, service.extension_prefs(),
+ service.pref_service(), service.profile(),
+ kUpdateFrequencySecs);
+ MockExtensionDownloaderDelegate delegate;
+ // Set the downloader directly, so that all its events end up in the mock
+ // |delegate|.
+ ExtensionDownloader* downloader =
+ new ExtensionDownloader(&delegate, service.request_context());
+ ResetDownloader(&updater, downloader);
+
+ // Non-internal non-external extensions should be rejected.
+ ExtensionList extensions;
+ service.CreateTestExtensions(1, 1, &extensions, NULL, Extension::INVALID);
+ service.CreateTestExtensions(2, 1, &extensions, NULL, Extension::INTERNAL);
+ ASSERT_EQ(2u, extensions.size());
+ const std::string& id = extensions[1]->id();
+
+ // These expectations fail if the delegate's methods are invoked for the
+ // first extension, which has a non-matching id.
+ EXPECT_CALL(delegate, GetUpdateUrlData(id)).WillOnce(Return(""));
+ EXPECT_CALL(delegate, GetPingDataForExtension(id, _));
+
+ service.set_extensions(extensions);
+ updater.set_blacklist_checks_enabled(false);
+ updater.Start();
+ updater.CheckNow();
}
TEST_F(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) {
- MessageLoop message_loop;
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
+ TestURLFetcherFactory factory;
MockService service;
- ManifestFetchesBuilder builder(&service, service.extension_prefs());
+ MockExtensionDownloaderDelegate delegate;
+ scoped_ptr<ExtensionDownloader> downloader(
+ new ExtensionDownloader(&delegate, service.request_context()));
+ EXPECT_EQ(0u, ManifestFetchersCount(downloader.get()));
- // Non-internal non-external extensions should be rejected.
- {
- ExtensionList extensions;
- service.CreateTestExtensions(1, 1, &extensions, NULL, Extension::INVALID);
- ASSERT_FALSE(extensions.empty());
- builder.AddExtension(*extensions[0]);
- EXPECT_TRUE(builder.GetFetches().empty());
- }
+ // First, verify that adding valid extensions does invoke the callbacks on
+ // the delegate.
+ std::string id = GenerateId("foo");
+ EXPECT_CALL(delegate, GetPingDataForExtension(id, _)).WillOnce(Return(false));
+ EXPECT_TRUE(
+ downloader->AddPendingExtension(id, GURL("http://example.com/update")));
+ downloader->StartAllPending();
+ Mock::VerifyAndClearExpectations(&delegate);
+ EXPECT_EQ(1u, ManifestFetchersCount(downloader.get()));
// Extensions with invalid update URLs should be rejected.
- builder.AddPendingExtension(
- GenerateId("foo"),
- Extension::INTERNAL,
- GURL("http:google.com:foo"));
- EXPECT_TRUE(builder.GetFetches().empty());
+ id = GenerateId("foo2");
+ EXPECT_FALSE(
+ downloader->AddPendingExtension(id, GURL("http:google.com:foo")));
+ downloader->StartAllPending();
+ EXPECT_EQ(1u, ManifestFetchersCount(downloader.get()));
// Extensions with empty IDs should be rejected.
- builder.AddPendingExtension(
- "",
- Extension::INTERNAL,
- GURL());
- EXPECT_TRUE(builder.GetFetches().empty());
+ EXPECT_FALSE(downloader->AddPendingExtension("", GURL()));
+ downloader->StartAllPending();
+ EXPECT_EQ(1u, ManifestFetchersCount(downloader.get()));
// TODO(akalin): Test that extensions with empty update URLs
// converted from user scripts are rejected.
+ // Reset the ExtensionDownloader so that it drops the current fetcher.
+ downloader.reset(
+ new ExtensionDownloader(&delegate, service.request_context()));
+ EXPECT_EQ(0u, ManifestFetchersCount(downloader.get()));
+
// Extensions with empty update URLs should have a default one
// filled in.
- builder.AddPendingExtension(
- GenerateId("foo"),
- Extension::INTERNAL,
- GURL());
+ id = GenerateId("foo3");
+ EXPECT_CALL(delegate, GetPingDataForExtension(id, _)).WillOnce(Return(false));
+ EXPECT_TRUE(downloader->AddPendingExtension(id, GURL()));
+ downloader->StartAllPending();
+ EXPECT_EQ(1u, ManifestFetchersCount(downloader.get()));
- std::vector<ManifestFetchData*> fetches = builder.GetFetches();
- ASSERT_EQ(1u, fetches.size());
- scoped_ptr<ManifestFetchData> fetch(fetches[0]);
- fetches.clear();
- EXPECT_FALSE(fetch->base_url().is_empty());
- EXPECT_FALSE(fetch->full_url().is_empty());
+ TestURLFetcher* fetcher =
+ factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
+ ASSERT_TRUE(fetcher);
+ EXPECT_FALSE(fetcher->GetOriginalURL().is_empty());
}
TEST_F(ExtensionUpdaterTest, TestStartUpdateCheckMemory) {
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
+ TestURLFetcherFactory factory;
+ MockService service;
+ MockExtensionDownloaderDelegate delegate;
+ ExtensionDownloader downloader(&delegate, service.request_context());
- ServiceForManifestTests service;
- TestURLFetcherFactory factory;
- ExtensionUpdater updater(
- &service, service.extension_prefs(), service.pref_service(),
- service.profile(), kUpdateFrequencySecs);
- updater.Start();
- updater.StartUpdateCheck(new ManifestFetchData(GURL()));
- // This should delete the newly-created ManifestFetchData.
- updater.StartUpdateCheck(new ManifestFetchData(GURL()));
- // This should add into |manifests_pending_|.
- updater.StartUpdateCheck(new ManifestFetchData(
- GURL("http://www.google.com")));
- // This should clear out |manifests_pending_|.
- updater.Stop();
+ StartUpdateCheck(&downloader, new ManifestFetchData(GURL()));
+ // This should delete the newly-created ManifestFetchData.
+ StartUpdateCheck(&downloader, new ManifestFetchData(GURL()));
+ // This should add into |manifests_pending_|.
+ StartUpdateCheck(&downloader, new ManifestFetchData(GURL(
+ GURL("http://www.google.com"))));
+ // The dtor of |downloader| should delete the pending fetchers.
}
TEST_F(ExtensionUpdaterTest, TestCheckSoon) {
- MessageLoop message_loop;
- content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
- content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
- ServiceForManifestTests service;
- TestURLFetcherFactory factory;
- ExtensionUpdater updater(
- &service, service.extension_prefs(), service.pref_service(),
- service.profile(), kUpdateFrequencySecs);
- EXPECT_FALSE(updater.WillCheckSoon());
- updater.Start();
- EXPECT_FALSE(updater.WillCheckSoon());
- updater.CheckSoon();
- EXPECT_TRUE(updater.WillCheckSoon());
- updater.CheckSoon();
- EXPECT_TRUE(updater.WillCheckSoon());
- ExtensionUpdaterTest::SimulateCheckSoon(updater, &message_loop);
- EXPECT_FALSE(updater.WillCheckSoon());
- updater.CheckSoon();
- EXPECT_TRUE(updater.WillCheckSoon());
- updater.Stop();
- EXPECT_FALSE(updater.WillCheckSoon());
+ ServiceForManifestTests service;
+ TestURLFetcherFactory factory;
+ ExtensionUpdater updater(
+ &service, service.extension_prefs(), service.pref_service(),
+ service.profile(), kUpdateFrequencySecs);
+ EXPECT_FALSE(updater.WillCheckSoon());
+ updater.Start();
+ EXPECT_FALSE(updater.WillCheckSoon());
+ updater.CheckSoon();
+ EXPECT_TRUE(updater.WillCheckSoon());
+ updater.CheckSoon();
+ EXPECT_TRUE(updater.WillCheckSoon());
+ RunAllPending();
+ EXPECT_FALSE(updater.WillCheckSoon());
+ updater.CheckSoon();
+ EXPECT_TRUE(updater.WillCheckSoon());
+ updater.Stop();
+ EXPECT_FALSE(updater.WillCheckSoon());
}
// TODO(asargent) - (http://crbug.com/12780) add tests for:
@@ -1290,3 +1450,5 @@
// -An extension gets manually updated to v3 while we're downloading v2 (ie
// you don't get downgraded accidentally)
// -An update manifest mentions multiple updates
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/updater/manifest_fetch_data.cc b/chrome/browser/extensions/updater/manifest_fetch_data.cc
new file mode 100644
index 0000000..a5f8dda9
--- /dev/null
+++ b/chrome/browser/extensions/updater/manifest_fetch_data.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "chrome/browser/google/google_util.h"
+#include "net/base/escape.h"
+
+namespace {
+
+// Maximum length of an extension manifest update check url, since it is a GET
+// request. We want to stay under 2K because of proxies, etc.
+const int kExtensionsManifestMaxURLSize = 2000;
+
+} // namespace
+
+namespace extensions {
+
+ManifestFetchData::ManifestFetchData(const GURL& update_url)
+ : base_url_(update_url),
+ full_url_(update_url) {
+}
+
+ManifestFetchData::~ManifestFetchData() {}
+
+// The format for request parameters in update checks is:
+//
+// ?x=EXT1_INFO&x=EXT2_INFO
+//
+// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
+//
+// id=EXTENSION_ID&v=VERSION&uc
+//
+// Additionally, we may include the parameter ping=PING_DATA where PING_DATA
+// looks like r=DAYS or a=DAYS for extensions in the Chrome extensions gallery.
+// ('r' refers to 'roll call' ie installation, and 'a' refers to 'active').
+// These values will each be present at most once every 24 hours, and indicate
+// the number of days since the last time it was present in an update check.
+//
+// So for two extensions like:
+// Extension 1- id:aaaa version:1.1
+// Extension 2- id:bbbb version:2.0
+//
+// the full update url would be:
+// http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
+//
+// (Note that '=' is %3D and '&' is %26 when urlencoded.)
+bool ManifestFetchData::AddExtension(std::string id, std::string version,
+ const PingData* ping_data,
+ const std::string& update_url_data,
+ const std::string& install_source) {
+ if (extension_ids_.find(id) != extension_ids_.end()) {
+ NOTREACHED() << "Duplicate extension id " << id;
+ return false;
+ }
+
+ // Compute the string we'd append onto the full_url_, and see if it fits.
+ std::vector<std::string> parts;
+ parts.push_back("id=" + id);
+ parts.push_back("v=" + version);
+ if (!install_source.empty())
+ parts.push_back("installsource=" + install_source);
+ parts.push_back("uc");
+
+ if (!update_url_data.empty()) {
+ // Make sure the update_url_data string is escaped before using it so that
+ // there is no chance of overriding the id or v other parameter value
+ // we place into the x= value.
+ parts.push_back("ap=" + net::EscapeQueryParamValue(update_url_data, true));
+ }
+
+ // Append brand code, rollcall and active ping parameters.
+ if (base_url_.DomainIs("google.com")) {
+#if defined(GOOGLE_CHROME_BUILD)
+ std::string brand;
+ google_util::GetBrand(&brand);
+ if (!brand.empty() && !google_util::IsOrganic(brand))
+ parts.push_back("brand=" + brand);
+#endif
+
+ std::string ping_value;
+ pings_[id] = PingData(0, 0);
+
+ if (ping_data) {
+ if (ping_data->rollcall_days == kNeverPinged ||
+ ping_data->rollcall_days > 0) {
+ ping_value += "r=" + base::IntToString(ping_data->rollcall_days);
+ pings_[id].rollcall_days = ping_data->rollcall_days;
+ }
+ if (ping_data->active_days == kNeverPinged ||
+ ping_data->active_days > 0) {
+ if (!ping_value.empty())
+ ping_value += "&";
+ ping_value += "a=" + base::IntToString(ping_data->active_days);
+ pings_[id].active_days = ping_data->active_days;
+ }
+ }
+ if (!ping_value.empty())
+ parts.push_back("ping=" + net::EscapeQueryParamValue(ping_value, true));
+ }
+
+ std::string extra = full_url_.has_query() ? "&" : "?";
+ extra += "x=" + net::EscapeQueryParamValue(JoinString(parts, '&'), true);
+
+ // Check against our max url size, exempting the first extension added.
+ int new_size = full_url_.possibly_invalid_spec().size() + extra.size();
+ if (!extension_ids_.empty() && new_size > kExtensionsManifestMaxURLSize) {
+ UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1);
+ return false;
+ }
+ UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0);
+
+ // We have room so go ahead and add the extension.
+ extension_ids_.insert(id);
+ full_url_ = GURL(full_url_.possibly_invalid_spec() + extra);
+ return true;
+}
+
+bool ManifestFetchData::Includes(const std::string& extension_id) const {
+ return extension_ids_.find(extension_id) != extension_ids_.end();
+}
+
+bool ManifestFetchData::DidPing(std::string extension_id, PingType type) const {
+ std::map<std::string, PingData>::const_iterator i = pings_.find(extension_id);
+ if (i == pings_.end())
+ return false;
+ int value = 0;
+ if (type == ROLLCALL)
+ value = i->second.rollcall_days;
+ else if (type == ACTIVE)
+ value = i->second.active_days;
+ else
+ NOTREACHED();
+ return value == kNeverPinged || value > 0;
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/updater/manifest_fetch_data.h b/chrome/browser/extensions/updater/manifest_fetch_data.h
new file mode 100644
index 0000000..6468c1e
--- /dev/null
+++ b/chrome/browser/extensions/updater/manifest_fetch_data.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_MANIFEST_FETCH_DATA_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_MANIFEST_FETCH_DATA_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "googleurl/src/gurl.h"
+
+namespace extensions {
+
+// To save on server resources we can request updates for multiple extensions
+// in one manifest check. This class helps us keep track of the id's for a
+// given fetch, building up the actual URL, and what if anything to include
+// in the ping parameter.
+class ManifestFetchData {
+ public:
+ static const int kNeverPinged = -1;
+
+ // Each ping type is sent at most once per day.
+ enum PingType {
+ // Used for counting total installs of an extension/app/theme.
+ ROLLCALL,
+
+ // Used for counting number of active users of an app, where "active" means
+ // the app was launched at least once since the last active ping.
+ ACTIVE,
+ };
+
+ struct PingData {
+ // The number of days it's been since our last rollcall or active ping,
+ // respectively. These are calculated based on the start of day from the
+ // server's perspective.
+ int rollcall_days;
+ int active_days;
+
+ PingData() : rollcall_days(0), active_days(0) {}
+ PingData(int rollcall, int active)
+ : rollcall_days(rollcall), active_days(active) {}
+ };
+
+ explicit ManifestFetchData(const GURL& update_url);
+ ~ManifestFetchData();
+
+ // Returns true if this extension information was successfully added. If the
+ // return value is false it means the full_url would have become too long, and
+ // this ManifestFetchData object remains unchanged.
+ bool AddExtension(std::string id, std::string version,
+ const PingData* ping_data,
+ const std::string& update_url_data,
+ const std::string& install_source);
+
+ const GURL& base_url() const { return base_url_; }
+ const GURL& full_url() const { return full_url_; }
+ int extension_count() { return extension_ids_.size(); }
+ const std::set<std::string>& extension_ids() const { return extension_ids_; }
+
+ // Returns true if the given id is included in this manifest fetch.
+ bool Includes(const std::string& extension_id) const;
+
+ // Returns true if a ping parameter for |type| was added to full_url for this
+ // extension id.
+ bool DidPing(std::string extension_id, PingType type) const;
+
+ private:
+ // The set of extension id's for this ManifestFetchData.
+ std::set<std::string> extension_ids_;
+
+ // The set of ping data we actually sent.
+ std::map<std::string, PingData> pings_;
+
+ // The base update url without any arguments added.
+ GURL base_url_;
+
+ // The base update url plus arguments indicating the id, version, etc.
+ // information about each extension.
+ GURL full_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(ManifestFetchData);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_UPDATER_MANIFEST_FETCH_DATA_H_
diff --git a/chrome/browser/extensions/updater/safe_manifest_parser.cc b/chrome/browser/extensions/updater/safe_manifest_parser.cc
new file mode 100644
index 0000000..c633dafa
--- /dev/null
+++ b/chrome/browser/extensions/updater/safe_manifest_parser.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/safe_manifest_parser.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "chrome/common/chrome_utility_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/browser/utility_process_host.h"
+#include "content/public/common/content_switches.h"
+#include "ipc/ipc_message_macros.h"
+
+using content::BrowserThread;
+
+namespace extensions {
+
+SafeManifestParser::SafeManifestParser(const std::string& xml,
+ ManifestFetchData* fetch_data,
+ const UpdateCallback& update_callback)
+ : xml_(xml),
+ fetch_data_(fetch_data),
+ update_callback_(update_callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+SafeManifestParser::~SafeManifestParser() {
+ // If we're using UtilityProcessHost, we may not be destroyed on
+ // the UI or IO thread.
+}
+
+void SafeManifestParser::Start() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&SafeManifestParser::ParseInSandbox, this))) {
+ NOTREACHED();
+ }
+}
+
+void SafeManifestParser::ParseInSandbox() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // TODO(asargent) we shouldn't need to do this branch here - instead
+ // UtilityProcessHost should handle it for us. (http://crbug.com/19192)
+ bool use_utility_process = content::ResourceDispatcherHost::Get() &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
+ if (use_utility_process) {
+ content::UtilityProcessHost* host = content::UtilityProcessHost::Create(
+ this, BrowserThread::UI);
+ host->EnableZygote();
+ host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml_));
+ } else {
+ UpdateManifest manifest;
+ if (manifest.Parse(xml_)) {
+ if (!BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &SafeManifestParser::OnParseUpdateManifestSucceeded, this,
+ manifest.results()))) {
+ NOTREACHED();
+ }
+ } else {
+ if (!BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &SafeManifestParser::OnParseUpdateManifestFailed, this,
+ manifest.errors()))) {
+ NOTREACHED();
+ }
+ }
+ }
+}
+
+bool SafeManifestParser::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(SafeManifestParser, message)
+ IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
+ OnParseUpdateManifestSucceeded)
+ IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
+ OnParseUpdateManifestFailed)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void SafeManifestParser::OnParseUpdateManifestSucceeded(
+ const UpdateManifest::Results& results) {
+ VLOG(2) << "parsing manifest succeeded (" << fetch_data_->full_url() << ")";
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ update_callback_.Run(*fetch_data_, &results);
+}
+
+void SafeManifestParser::OnParseUpdateManifestFailed(
+ const std::string& error_message) {
+ VLOG(2) << "parsing manifest failed (" << fetch_data_->full_url() << ")";
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ LOG(WARNING) << "Error parsing update manifest:\n" << error_message;
+ update_callback_.Run(*fetch_data_, NULL);
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/updater/safe_manifest_parser.h b/chrome/browser/extensions/updater/safe_manifest_parser.h
new file mode 100644
index 0000000..9440e7eab
--- /dev/null
+++ b/chrome/browser/extensions/updater/safe_manifest_parser.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_SAFE_MANIFEST_PARSER_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_SAFE_MANIFEST_PARSER_H_
+#pragma once
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+#include "chrome/common/extensions/update_manifest.h"
+#include "content/public/browser/utility_process_host_client.h"
+
+namespace extensions {
+
+// Utility class to handle doing xml parsing in a sandboxed utility process.
+class SafeManifestParser : public content::UtilityProcessHostClient {
+ public:
+ // Callback that is invoked when the manifest results are ready.
+ typedef base::Callback<void(const ManifestFetchData&,
+ const UpdateManifest::Results*)> UpdateCallback;
+
+ // Takes ownership of |fetch_data|.
+ SafeManifestParser(const std::string& xml,
+ ManifestFetchData* fetch_data,
+ const UpdateCallback& update_callback);
+
+ virtual ~SafeManifestParser();
+
+ // Posts a task over to the IO loop to start the parsing of xml_ in a
+ // utility process.
+ void Start();
+
+ private:
+ // Creates the sandboxed utility process and tells it to start parsing.
+ void ParseInSandbox();
+
+ // UtilityProcessHostClient implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& results);
+ void OnParseUpdateManifestFailed(const std::string& error_message);
+
+ const std::string xml_;
+
+ // Should be accessed only on UI thread.
+ scoped_ptr<ManifestFetchData> fetch_data_;
+ UpdateCallback update_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeManifestParser);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_UPDATER_SAFE_MANIFEST_PARSER_H_
diff --git a/chrome/browser/first_run/first_run_win.cc b/chrome/browser/first_run/first_run_win.cc
index 0725819..dd8186d 100644
--- a/chrome/browser/first_run/first_run_win.cc
+++ b/chrome/browser/first_run/first_run_win.cc
@@ -22,7 +22,7 @@
#include "base/win/windows_version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_updater.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/first_run/first_run_import_observer.h"
#include "chrome/browser/first_run/first_run_internal.h"
#include "chrome/browser/importer/importer_host.h"
diff --git a/chrome/browser/metrics/metrics_service.h b/chrome/browser/metrics/metrics_service.h
index 91eb0ead..4761f5d2 100644
--- a/chrome/browser/metrics/metrics_service.h
+++ b/chrome/browser/metrics/metrics_service.h
@@ -32,7 +32,6 @@
class BookmarkModel;
class BookmarkNode;
-class ManifestFetchesBuilder;
class MetricsReportingScheduler;
class PrefService;
class Profile;
@@ -47,6 +46,10 @@
class RenderProcessHost;
}
+namespace extensions {
+class ExtensionDownloader;
+}
+
namespace prerender {
bool IsOmniboxEnabled(Profile* profile);
}
@@ -434,7 +437,7 @@
private:
friend class InstantFieldTrial;
friend bool prerender::IsOmniboxEnabled(Profile* profile);
- friend class ManifestFetchesBuilder;
+ friend class extensions::ExtensionDownloader;
// Returns true if prefs::kMetricsReportingEnabled is set.
static bool IsMetricsReportingEnabled();
diff --git a/chrome/browser/sync/glue/theme_util.cc b/chrome/browser/sync/glue/theme_util.cc
index 03a1ca1..b60b13e 100644
--- a/chrome/browser/sync/glue/theme_util.cc
+++ b/chrome/browser/sync/glue/theme_util.cc
@@ -9,7 +9,6 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
diff --git a/chrome/browser/ui/webui/options/extension_settings_handler.cc b/chrome/browser/ui/webui/options/extension_settings_handler.cc
index 6ea248df..f04b6a5 100644
--- a/chrome/browser/ui/webui/options/extension_settings_handler.cc
+++ b/chrome/browser/ui/webui/options/extension_settings_handler.cc
@@ -18,9 +18,9 @@
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_disabled_infobar_delegate.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/extensions/extension_warning_set.h"
#include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab_contents/background_contents.h"
@@ -453,7 +453,7 @@
}
void ExtensionSettingsHandler::HandleAutoUpdateMessage(const ListValue* args) {
- ExtensionUpdater* updater = extension_service_->updater();
+ extensions::ExtensionUpdater* updater = extension_service_->updater();
if (updater)
updater->CheckNow();
}