summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormek@chromium.org <mek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-14 21:01:06 +0000
committermek@chromium.org <mek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-14 21:01:06 +0000
commit42774e4a2cffae20a5ab38ed43ed79ef9376e2fd (patch)
tree4e15852e85c196f8345b51595676d658fd8a1dbe
parent8116f33fd183f21a50a0da79ff9a8f1a9448754d (diff)
downloadchromium_src-42774e4a2cffae20a5ab38ed43ed79ef9376e2fd.zip
chromium_src-42774e4a2cffae20a5ab38ed43ed79ef9376e2fd.tar.gz
chromium_src-42774e4a2cffae20a5ab38ed43ed79ef9376e2fd.tar.bz2
add chrome.runtime.requestUpdateCheck method.
To prevent bad-behaving extensions from potentially overloading the network, an extension is currently limited to checking for updates ones every 5 seconds. TBR=jhawkins@chromium.org BUG=88945 Review URL: https://chromiumcodereview.appspot.com/11339047 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@167741 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/automation/testing_automation_provider.cc6
-rw-r--r--chrome/browser/extensions/api/management/management_browsertest.cc43
-rw-r--r--chrome/browser/extensions/api/runtime/runtime_api.cc60
-rw-r--r--chrome/browser/extensions/api/runtime/runtime_api.h21
-rw-r--r--chrome/browser/extensions/extension_function_registry.cc1
-rw-r--r--chrome/browser/extensions/extension_service.cc2
-rw-r--r--chrome/browser/extensions/updater/extension_downloader.cc14
-rw-r--r--chrome/browser/extensions/updater/extension_downloader.h11
-rw-r--r--chrome/browser/extensions/updater/extension_updater.cc179
-rw-r--r--chrome/browser/extensions/updater/extension_updater.h49
-rw-r--r--chrome/browser/extensions/updater/extension_updater_unittest.cc21
-rw-r--r--chrome/browser/first_run/first_run_win.cc2
-rw-r--r--chrome/browser/ui/webui/extensions/extension_settings_handler.cc2
-rw-r--r--chrome/common/chrome_notification_types.h5
-rw-r--r--chrome/common/extensions/api/runtime.json32
15 files changed, 366 insertions, 82 deletions
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index d0b2005..c36c16c 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -4145,8 +4145,10 @@ void TestingAutomationProvider::UpdateExtensionsNow(
// been updated). This observer will delete itself.
ExtensionsUpdatedObserver* observer = new ExtensionsUpdatedObserver(
manager, this, reply_message);
- updater->CheckNow(base::Bind(&ExtensionsUpdatedObserver::UpdateCheckFinished,
- base::Unretained(observer)));
+ extensions::ExtensionUpdater::CheckParams params;
+ params.callback = base::Bind(&ExtensionsUpdatedObserver::UpdateCheckFinished,
+ base::Unretained(observer));
+ updater->CheckNow(params);
}
#if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
diff --git a/chrome/browser/extensions/api/management/management_browsertest.cc b/chrome/browser/extensions/api/management/management_browsertest.cc
index 54df1bd..1b9f9e3 100644
--- a/chrome/browser/extensions/api/management/management_browsertest.cc
+++ b/chrome/browser/extensions/api/management/management_browsertest.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
#include "chrome/browser/extensions/updater/extension_updater.h"
+#include "chrome/browser/extensions/updater/extension_downloader.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"
@@ -202,9 +203,9 @@ class NotificationListener : public content::NotificationObserver {
break;
}
case chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND: {
- const std::string* id =
- content::Details<const std::string>(details).ptr();
- updates_.insert(*id);
+ const std::string& id =
+ content::Details<extensions::UpdateDetails>(details)->id;
+ updates_.insert(id);
break;
}
default:
@@ -270,13 +271,15 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, MAYBE_AutoUpdate) {
ASSERT_EQ("1.0", extension->VersionString());
// We don't want autoupdate blacklist checks.
- service->updater()->set_blacklist_checks_enabled(false);
+ extensions::ExtensionUpdater::CheckParams params;
+ params.check_blacklist = false;
+ params.callback =
+ base::Bind(&NotificationListener::OnFinished,
+ base::Unretained(&notification_listener));
// Run autoupdate and make sure version 2 of the extension was installed.
ExtensionTestMessageListener listener2("v2 installed", false);
- service->updater()->CheckNow(
- base::Bind(&NotificationListener::OnFinished,
- base::Unretained(&notification_listener)));
+ service->updater()->CheckNow(params);
ASSERT_TRUE(WaitForExtensionInstall());
listener2.WaitUntilSatisfied();
ASSERT_EQ(size_before + 1, service->extensions()->size());
@@ -297,9 +300,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, MAYBE_AutoUpdate) {
interceptor->SetResponseOnIOThread("http://localhost/autoupdate/v3.crx",
basedir.AppendASCII("v3.crx"));
- service->updater()->CheckNow(
- base::Bind(&NotificationListener::OnFinished,
- base::Unretained(&notification_listener)));
+ service->updater()->CheckNow(params);
ASSERT_TRUE(WaitForExtensionInstallError());
ASSERT_TRUE(notification_listener.started());
ASSERT_TRUE(notification_listener.finished());
@@ -356,14 +357,16 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest,
ASSERT_EQ("1.0", extension->VersionString());
// We don't want autoupdate blacklist checks.
- service->updater()->set_blacklist_checks_enabled(false);
+ extensions::ExtensionUpdater::CheckParams params;
+ params.check_blacklist = false;
+ params.callback =
+ base::Bind(&NotificationListener::OnFinished,
+ base::Unretained(&notification_listener));
ExtensionTestMessageListener listener2("v2 installed", false);
// Run autoupdate and make sure version 2 of the extension was installed but
// is still disabled.
- service->updater()->CheckNow(
- base::Bind(&NotificationListener::OnFinished,
- base::Unretained(&notification_listener)));
+ service->updater()->CheckNow(params);
ASSERT_TRUE(WaitForExtensionInstall());
ASSERT_EQ(disabled_size_before + 1, service->disabled_extensions()->size());
ASSERT_EQ(enabled_size_before, service->extensions()->size());
@@ -390,7 +393,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, ExternalUrlUpdate) {
ExtensionService* service = browser()->profile()->GetExtensionService();
const char* kExtensionId = "ogjcoiohnmldgjemafoockdghcjciccf";
// We don't want autoupdate blacklist checks.
- service->updater()->set_blacklist_checks_enabled(false);
+ extensions::ExtensionUpdater::CheckParams params;
+ params.check_blacklist = false;
FilePath basedir = test_data_dir_.AppendASCII("autoupdate");
@@ -420,7 +424,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, ExternalUrlUpdate) {
Extension::EXTERNAL_PREF_DOWNLOAD));
// Run autoupdate and make sure version 2 of the extension was installed.
- service->updater()->CheckNow(base::Closure());
+ service->updater()->CheckNow(params);
ASSERT_TRUE(WaitForExtensionInstall());
ASSERT_EQ(size_before + 1, service->extensions()->size());
const Extension* extension = service->GetExtensionById(kExtensionId, false);
@@ -474,7 +478,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, ExternalPolicyRefresh) {
ExtensionService* service = browser()->profile()->GetExtensionService();
const char* kExtensionId = "ogjcoiohnmldgjemafoockdghcjciccf";
// We don't want autoupdate blacklist checks.
- service->updater()->set_blacklist_checks_enabled(false);
+ extensions::ExtensionUpdater::CheckParams params;
+ params.check_blacklist = false;
FilePath basedir = test_data_dir_.AppendASCII("autoupdate");
@@ -549,7 +554,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest,
MAYBE_PolicyOverridesUserInstall) {
ExtensionService* service = browser()->profile()->GetExtensionService();
const char* kExtensionId = "ogjcoiohnmldgjemafoockdghcjciccf";
- service->updater()->set_blacklist_checks_enabled(false);
+ extensions::ExtensionUpdater::CheckParams params;
+ params.check_blacklist = false;
+ service->updater()->set_default_check_params(params);
const size_t size_before = service->extensions()->size();
FilePath basedir = test_data_dir_.AppendASCII("autoupdate");
ASSERT_TRUE(service->disabled_extensions()->is_empty());
diff --git a/chrome/browser/extensions/api/runtime/runtime_api.cc b/chrome/browser/extensions/api/runtime/runtime_api.cc
index 1b15857..dfe9f4b 100644
--- a/chrome/browser/extensions/api/runtime/runtime_api.cc
+++ b/chrome/browser/extensions/api/runtime/runtime_api.cc
@@ -11,6 +11,7 @@
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/lazy_background_task_queue.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/extensions/extension.h"
@@ -30,6 +31,10 @@ const char kInstallReasonChromeUpdate[] = "chrome_update";
const char kInstallReasonUpdate[] = "update";
const char kInstallReasonInstall[] = "install";
const char kInstallPreviousVersion[] = "previousVersion";
+const char kUpdatesDisabledError[] = "Autoupdate is not enabled.";
+const char kUpdateFound[] = "update_available";
+const char kUpdateNotFound[] = "no_update";
+const char kUpdateThrottled[] = "throttled";
static void DispatchOnStartupEventImpl(
Profile* profile,
@@ -166,4 +171,59 @@ bool RuntimeReloadFunction::RunImpl() {
return true;
}
+RuntimeRequestUpdateCheckFunction::RuntimeRequestUpdateCheckFunction() {
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
+ content::NotificationService::AllSources());
+}
+
+bool RuntimeRequestUpdateCheckFunction::RunImpl() {
+ ExtensionSystem* system = ExtensionSystem::Get(profile());
+ ExtensionService* service = system->extension_service();
+ ExtensionUpdater* updater = service->updater();
+ if (!updater) {
+ error_ = kUpdatesDisabledError;
+ return false;
+ }
+
+ did_reply_ = false;
+ if (!updater->CheckExtensionSoon(extension_id(), base::Bind(
+ &RuntimeRequestUpdateCheckFunction::CheckComplete, this))) {
+ did_reply_ = true;
+ SetResult(new base::StringValue(kUpdateThrottled));
+ SendResponse(true);
+ }
+ return true;
+}
+
+void RuntimeRequestUpdateCheckFunction::CheckComplete() {
+ if (did_reply_)
+ return;
+
+ did_reply_ = true;
+ SetResult(new base::StringValue(kUpdateNotFound));
+ SendResponse(true);
+}
+
+void RuntimeRequestUpdateCheckFunction::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (did_reply_)
+ return;
+
+ DCHECK(type == chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND);
+ typedef const std::pair<std::string, Version> UpdateDetails;
+ const std::string& id = content::Details<UpdateDetails>(details)->first;
+ const Version& version = content::Details<UpdateDetails>(details)->second;
+ if (id == extension_id()) {
+ did_reply_ = true;
+ results_.reset(new base::ListValue);
+ results_->AppendString(kUpdateFound);
+ base::DictionaryValue* details = new base::DictionaryValue;
+ results_->Append(details);
+ details->SetString("version", version.GetString());
+ SendResponse(true);
+ }
+}
+
} // namespace extensions
diff --git a/chrome/browser/extensions/api/runtime/runtime_api.h b/chrome/browser/extensions/api/runtime/runtime_api.h
index 281cac9..b62b684 100644
--- a/chrome/browser/extensions/api/runtime/runtime_api.h
+++ b/chrome/browser/extensions/api/runtime/runtime_api.h
@@ -54,6 +54,27 @@ class RuntimeReloadFunction : public SyncExtensionFunction {
virtual bool RunImpl() OVERRIDE;
};
+class RuntimeRequestUpdateCheckFunction : public AsyncExtensionFunction,
+ public content::NotificationObserver {
+ public:
+ DECLARE_EXTENSION_FUNCTION_NAME("runtime.requestUpdateCheck");
+
+ RuntimeRequestUpdateCheckFunction();
+ protected:
+ virtual ~RuntimeRequestUpdateCheckFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+
+ // Implements content::NotificationObserver interface.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+ private:
+ void CheckComplete();
+
+ content::NotificationRegistrar registrar_;
+ bool did_reply_;
+};
+
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_RUNTIME_RUNTIME_API_H_
diff --git a/chrome/browser/extensions/extension_function_registry.cc b/chrome/browser/extensions/extension_function_registry.cc
index 7f5a5df..21b9030 100644
--- a/chrome/browser/extensions/extension_function_registry.cc
+++ b/chrome/browser/extensions/extension_function_registry.cc
@@ -515,6 +515,7 @@ void ExtensionFunctionRegistry::ResetFunctions() {
// Runtime
RegisterFunction<extensions::RuntimeGetBackgroundPageFunction>();
RegisterFunction<extensions::RuntimeReloadFunction>();
+ RegisterFunction<extensions::RuntimeRequestUpdateCheckFunction>();
// Generated APIs
extensions::api::GeneratedFunctionRegistry::RegisterAll(this);
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index bafe23c..9499f21 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -1763,7 +1763,7 @@ void ExtensionService::OnAllExternalProvidersReady() {
// Install any pending extensions.
if (update_once_all_providers_are_ready_ && updater()) {
update_once_all_providers_are_ready_ = false;
- updater()->CheckNow(base::Closure());
+ updater()->CheckNow(extensions::ExtensionUpdater::CheckParams());
}
// Uninstall all the unclaimed extensions.
diff --git a/chrome/browser/extensions/updater/extension_downloader.cc b/chrome/browser/extensions/updater/extension_downloader.cc
index e7e9160..8ca659f 100644
--- a/chrome/browser/extensions/updater/extension_downloader.cc
+++ b/chrome/browser/extensions/updater/extension_downloader.cc
@@ -102,6 +102,12 @@ void RecordCRXWriteHistogram(bool success, const FilePath& crx_path) {
} // namespace
+UpdateDetails::UpdateDetails(const std::string& id, const Version& version)
+ : id(id), version(version) {}
+
+UpdateDetails::~UpdateDetails() {}
+
+
ExtensionDownloader::ExtensionFetch::ExtensionFetch()
: id(""),
url(),
@@ -457,7 +463,7 @@ void ExtensionDownloader::HandleManifestResults(
GURL crx_url = update->crx_url;
if (id != kBlacklistAppID) {
- NotifyUpdateFound(update->extension_id);
+ NotifyUpdateFound(update->extension_id, update->version);
} 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
@@ -693,11 +699,13 @@ void ExtensionDownloader::NotifyExtensionsDownloadFailed(
}
}
-void ExtensionDownloader::NotifyUpdateFound(const std::string& id) {
+void ExtensionDownloader::NotifyUpdateFound(const std::string& id,
+ const std::string& version) {
+ UpdateDetails updateInfo(id, Version(version));
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
content::NotificationService::AllBrowserContextsAndSources(),
- content::Details<const std::string>(&id));
+ content::Details<UpdateDetails>(&updateInfo));
}
} // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_downloader.h b/chrome/browser/extensions/updater/extension_downloader.h
index cbc138c..8dd1331 100644
--- a/chrome/browser/extensions/updater/extension_downloader.h
+++ b/chrome/browser/extensions/updater/extension_downloader.h
@@ -16,6 +16,7 @@
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/version.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"
@@ -33,6 +34,14 @@ class URLRequestStatus;
namespace extensions {
+struct UpdateDetails {
+ UpdateDetails(const std::string& id, const Version& version);
+ ~UpdateDetails();
+
+ std::string id;
+ Version version;
+};
+
class ExtensionUpdaterTest;
// A class that checks for updates of a given list of extensions, and downloads
@@ -174,7 +183,7 @@ class ExtensionDownloader : public net::URLFetcherDelegate {
// Send a notification that an update was found for |id| that we'll
// attempt to download.
- void NotifyUpdateFound(const std::string& id);
+ void NotifyUpdateFound(const std::string& id, const std::string& version);
// The delegate that receives the crx files downloaded by the
// ExtensionDownloader, and that fills in optional ping and update url data.
diff --git a/chrome/browser/extensions/updater/extension_updater.cc b/chrome/browser/extensions/updater/extension_updater.cc
index e35f327..d26159d 100644
--- a/chrome/browser/extensions/updater/extension_updater.cc
+++ b/chrome/browser/extensions/updater/extension_updater.cc
@@ -53,6 +53,10 @@ const int kStartupWaitSeconds = 60 * 5;
const int kMinUpdateFrequencySeconds = 30;
const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days
+// Require at least 5 seconds between consecutive non-succesful extension update
+// checks.
+const int kMinUpdateThrottleTime = 5;
+
// 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
@@ -85,6 +89,11 @@ int CalculateActivePingDays(const Time& last_active_ping_day,
namespace extensions {
+ExtensionUpdater::CheckParams::CheckParams()
+ : check_blacklist(true) {}
+
+ExtensionUpdater::CheckParams::~CheckParams() {}
+
ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(
const std::string& i,
const FilePath& p,
@@ -107,6 +116,17 @@ ExtensionUpdater::InProgressCheck::InProgressCheck()
ExtensionUpdater::InProgressCheck::~InProgressCheck() {}
+struct ExtensionUpdater::ThrottleInfo {
+ ThrottleInfo()
+ : in_progress(true),
+ throttle_delay(kMinUpdateThrottleTime),
+ check_start(Time::Now()) {}
+
+ bool in_progress;
+ int throttle_delay;
+ Time check_start;
+};
+
ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
ExtensionPrefs* extension_prefs,
PrefService* prefs,
@@ -116,8 +136,7 @@ ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
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),
- next_request_id_(0),
+ prefs_(prefs), profile_(profile), next_request_id_(0),
crx_install_is_running_(false) {
DCHECK_GE(frequency_seconds_, 5);
DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
@@ -126,6 +145,9 @@ ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
#endif
frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
+
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
+ content::NotificationService::AllBrowserContextsAndSources());
}
ExtensionUpdater::~ExtensionUpdater() {
@@ -224,7 +246,7 @@ void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
void ExtensionUpdater::TimerFired() {
DCHECK(alive_);
- CheckNow(base::Closure());
+ CheckNow(default_params_);
// If the user has overridden the update frequency, don't bother reporting
// this.
@@ -267,7 +289,7 @@ bool ExtensionUpdater::WillCheckSoon() const {
void ExtensionUpdater::DoCheckSoon() {
DCHECK(will_check_soon_);
- CheckNow(base::Closure());
+ CheckNow(default_params_);
will_check_soon_ = false;
}
@@ -294,16 +316,18 @@ void ExtensionUpdater::AddToDownloader(
}
}
-void ExtensionUpdater::CheckNow(const FinishedCallback& callback) {
+void ExtensionUpdater::CheckNow(const CheckParams& params) {
int request_id = next_request_id_++;
VLOG(2) << "Starting update check " << request_id;
+ if (params.ids.empty())
+ NotifyStarted();
+
DCHECK(alive_);
- NotifyStarted();
InProgressCheck* request = &requests_in_progress_[request_id];
request->id = request_id;
- request->callback = callback;
+ request->callback = params.callback;
if (!downloader_.get()) {
downloader_.reset(
@@ -317,25 +341,38 @@ void ExtensionUpdater::CheckNow(const FinishedCallback& callback) {
service_->pending_extension_manager();
std::list<std::string> pending_ids;
- pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_ids);
-
- std::list<std::string>::const_iterator iter;
- for (iter = pending_ids.begin(); iter != pending_ids.end(); ++iter) {
- const PendingExtensionInfo* info = pending_extension_manager->GetById(
- *iter);
- if (!Extension::IsAutoUpdateableLocation(info->install_source())) {
- VLOG(2) << "Extension " << *iter << " is not auto updateable";
- continue;
+
+ if (params.ids.empty()) {
+ // If no extension ids are specified, check for updates for all extensions.
+ pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_ids);
+
+ std::list<std::string>::const_iterator iter;
+ for (iter = pending_ids.begin(); iter != pending_ids.end(); ++iter) {
+ const PendingExtensionInfo* info = pending_extension_manager->GetById(
+ *iter);
+ if (!Extension::IsAutoUpdateableLocation(info->install_source())) {
+ VLOG(2) << "Extension " << *iter << " is not auto updateable";
+ continue;
+ }
+ if (downloader_->AddPendingExtension(*iter, info->update_url(),
+ request_id))
+ request->in_progress_ids_.push_back(*iter);
}
- if (downloader_->AddPendingExtension(*iter, info->update_url(), request_id))
- request->in_progress_ids_.push_back(*iter);
- }
- AddToDownloader(service_->extensions(), pending_ids, request);
- AddToDownloader(service_->disabled_extensions(), pending_ids, request);
+ AddToDownloader(service_->extensions(), pending_ids, request);
+ AddToDownloader(service_->disabled_extensions(), pending_ids, request);
+ } else {
+ for (std::list<std::string>::const_iterator it = params.ids.begin();
+ it != params.ids.end(); ++it) {
+ const Extension* extension = service_->GetExtensionById(*it, true);
+ DCHECK(extension);
+ if (downloader_->AddExtension(*extension, request->id))
+ request->in_progress_ids_.push_back(extension->id());
+ }
+ }
// Start a fetch of the blacklist if needed.
- if (blacklist_checks_enabled_) {
+ if (params.check_blacklist) {
ManifestFetchData::PingData ping_data;
ping_data.rollcall_days =
CalculatePingDays(extension_prefs_->BlacklistLastPingDay());
@@ -361,6 +398,59 @@ void ExtensionUpdater::CheckNow(const FinishedCallback& callback) {
NotifyIfFinished(request_id);
}
+bool ExtensionUpdater::CheckExtensionSoon(const std::string& extension_id,
+ const FinishedCallback& callback) {
+ bool have_throttle_info = ContainsKey(throttle_info_, extension_id);
+ ThrottleInfo& info = throttle_info_[extension_id];
+ if (have_throttle_info) {
+ // We already had a ThrottleInfo object for this extension, check if the
+ // update check request should be allowed.
+
+ // If another check is in progress, don't start a new check.
+ if (info.in_progress)
+ return false;
+
+ Time now = Time::Now();
+ Time last = info.check_start;
+ // If somehow time moved back, we don't want to infinitely keep throttling.
+ if (now < last) {
+ last = now;
+ info.check_start = now;
+ }
+ Time earliest = last + TimeDelta::FromSeconds(info.throttle_delay);
+ // If check is too soon, throttle.
+ if (now < earliest)
+ return false;
+
+ // TODO(mek): Somehow increase time between allowing checks when checks
+ // are repeatedly throttled and don't result in updates being installed.
+
+ // It's okay to start a check, update values.
+ info.check_start = now;
+ info.in_progress = true;
+ }
+
+ CheckParams params;
+ params.ids.push_back(extension_id);
+ params.check_blacklist = false;
+ params.callback = base::Bind(&ExtensionUpdater::ExtensionCheckFinished,
+ weak_ptr_factory_.GetWeakPtr(),
+ extension_id, callback);
+ CheckNow(params);
+ return true;
+}
+
+void ExtensionUpdater::ExtensionCheckFinished(
+ const std::string& extension_id,
+ const FinishedCallback& callback) {
+ std::map<std::string, ThrottleInfo>::iterator it =
+ throttle_info_.find(extension_id);
+ if (it != throttle_info_.end()) {
+ it->second.in_progress = false;
+ }
+ callback.Run();
+}
+
void ExtensionUpdater::OnExtensionDownloadFailed(
const std::string& id,
Error error,
@@ -532,23 +622,36 @@ void ExtensionUpdater::MaybeInstallCRXFile() {
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;
-
- const FetchedCRXFile& crx_file = current_crx_file_;
- for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
- it != crx_file.request_ids.end(); ++it) {
- requests_in_progress_[*it].in_progress_ids_.remove(crx_file.extension_id);
- NotifyIfFinished(*it);
- }
+ switch (type) {
+ case 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;
+
+ const FetchedCRXFile& crx_file = current_crx_file_;
+ for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
+ it != crx_file.request_ids.end(); ++it) {
+ InProgressCheck& request = requests_in_progress_[*it];
+ request.in_progress_ids_.remove(crx_file.extension_id);
+ NotifyIfFinished(*it);
+ }
- // If any files are available to update, start one.
- MaybeInstallCRXFile();
+ // If any files are available to update, start one.
+ MaybeInstallCRXFile();
+ break;
+ }
+ case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
+ const Extension* extension =
+ content::Details<const Extension>(details).ptr();
+ if (extension)
+ throttle_info_.erase(extension->id());
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
}
void ExtensionUpdater::NotifyStarted() {
diff --git a/chrome/browser/extensions/updater/extension_updater.h b/chrome/browser/extensions/updater/extension_updater.h
index 6ecfacf..ae59b7d 100644
--- a/chrome/browser/extensions/updater/extension_updater.h
+++ b/chrome/browser/extensions/updater/extension_updater.h
@@ -51,6 +51,24 @@ class ExtensionUpdater : public ExtensionDownloaderDelegate,
public:
typedef base::Closure FinishedCallback;
+ struct CheckParams {
+ // Creates a default CheckParams instance that checks for all extensions and
+ // the extension blacklist.
+ CheckParams();
+ ~CheckParams();
+
+ // The set of extensions that should be checked for updates. If empty
+ // all extensions will be included in the update check.
+ std::list<std::string> ids;
+
+ // If true, the extension blacklist will also be updated.
+ bool check_blacklist;
+
+ // Callback to call when the update check is complete. Can be null, if
+ // you're not interested in when this happens.
+ FinishedCallback callback;
+ };
+
// 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.
@@ -73,20 +91,27 @@ class ExtensionUpdater : public ExtensionDownloaderDelegate,
// already a pending task that has not yet run.
void CheckSoon();
+ // Starts an update check for the specified extension soon. If a check
+ // is already running, or finished too recently without an update being
+ // installed, this method returns false and the check won't be scheduled.
+ bool CheckExtensionSoon(const std::string& extension_id,
+ const FinishedCallback& callback);
+
// Starts an update check right now, instead of waiting for the next
// regularly scheduled check or a pending check from CheckSoon().
- void CheckNow(const FinishedCallback& callback);
-
- // Set blacklist checks on or off.
- void set_blacklist_checks_enabled(bool enabled) {
- blacklist_checks_enabled_ = enabled;
- }
+ void CheckNow(const CheckParams& params);
// 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;
+ // Changes the params that are used for the automatic periodic update checks,
+ // as well as for explicit calls to CheckSoon.
+ void set_default_check_params(const CheckParams& params) {
+ default_params_ = params;
+ }
+
private:
friend class ExtensionUpdaterTest;
friend class ExtensionUpdaterFileHandler;
@@ -117,6 +142,8 @@ class ExtensionUpdater : public ExtensionDownloaderDelegate,
std::list<std::string> in_progress_ids_;
};
+ struct ThrottleInfo;
+
// Computes when to schedule the first update check.
base::TimeDelta DetermineFirstCheckDelay();
@@ -187,6 +214,9 @@ class ExtensionUpdater : public ExtensionDownloaderDelegate,
// Send a notification if we're finished updating.
void NotifyIfFinished(int request_id);
+ void ExtensionCheckFinished(const std::string& extension_id,
+ const FinishedCallback& callback);
+
// Whether Start() has been called but not Stop().
bool alive_;
@@ -205,7 +235,6 @@ class ExtensionUpdater : public ExtensionDownloaderDelegate,
ExtensionPrefs* extension_prefs_;
PrefService* prefs_;
Profile* profile_;
- bool blacklist_checks_enabled_;
std::map<int, InProgressCheck> requests_in_progress_;
int next_request_id_;
@@ -221,6 +250,12 @@ class ExtensionUpdater : public ExtensionDownloaderDelegate,
std::stack<FetchedCRXFile> fetched_crx_files_;
FetchedCRXFile current_crx_file_;
+ CheckParams default_params_;
+
+ // Keeps track of when an extension tried to update itself, so we can throttle
+ // checks to prevent too many requests from being made.
+ std::map<std::string, ThrottleInfo> throttle_info_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionUpdater);
};
diff --git a/chrome/browser/extensions/updater/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc
index d2bf559..4fd0346 100644
--- a/chrome/browser/extensions/updater/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -144,7 +144,7 @@ class NotificationsObserver : public content::NotificationObserver {
count_[i]++;
if (type == chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND) {
updated_.insert(
- *(content::Details<const std::string>(details).ptr()));
+ content::Details<UpdateDetails>(details)->id);
}
return;
}
@@ -504,7 +504,9 @@ class ExtensionUpdaterTest : public testing::Test {
updater.Start();
// Disable blacklist checks (tested elsewhere) so that we only see the
// update HTTP request.
- updater.set_blacklist_checks_enabled(false);
+ ExtensionUpdater::CheckParams check_params;
+ check_params.check_blacklist = false;
+ updater.set_default_check_params(check_params);
// Tell the update that it's time to do update checks.
SimulateTimerFired(&updater);
@@ -1181,9 +1183,10 @@ class ExtensionUpdaterTest : public testing::Test {
ExtensionUpdater updater(
&service, service.extension_prefs(), service.pref_service(),
service.profile(), kUpdateFrequencySecs);
- updater.set_blacklist_checks_enabled(false);
+ ExtensionUpdater::CheckParams params;
+ params.check_blacklist = false;
updater.Start();
- updater.CheckNow(base::Closure());
+ updater.CheckNow(params);
// Make the updater do manifest fetching, and note the urls it tries to
// fetch.
@@ -1401,9 +1404,10 @@ TEST_F(ExtensionUpdaterTest, TestNonAutoUpdateableLocations) {
EXPECT_CALL(delegate, GetPingDataForExtension(updateable_id, _));
service.set_extensions(extensions);
- updater.set_blacklist_checks_enabled(false);
+ ExtensionUpdater::CheckParams params;
+ params.check_blacklist = false;
updater.Start();
- updater.CheckNow(base::Closure());
+ updater.CheckNow(params);
}
TEST_F(ExtensionUpdaterTest, TestUpdatingDisabledExtensions) {
@@ -1439,9 +1443,10 @@ TEST_F(ExtensionUpdaterTest, TestUpdatingDisabledExtensions) {
service.set_extensions(enabled_extensions);
service.set_disabled_extensions(disabled_extensions);
- updater.set_blacklist_checks_enabled(false);
+ ExtensionUpdater::CheckParams params;
+ params.check_blacklist = false;
updater.Start();
- updater.CheckNow(base::Closure());
+ updater.CheckNow(params);
}
TEST_F(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) {
diff --git a/chrome/browser/first_run/first_run_win.cc b/chrome/browser/first_run/first_run_win.cc
index 0d99c3a..4bd884b 100644
--- a/chrome/browser/first_run/first_run_win.cc
+++ b/chrome/browser/first_run/first_run_win.cc
@@ -98,7 +98,7 @@ class FirstRunDelayedTasks : public content::NotificationObserver {
// extension it will get updated which is the same as get it installed.
void DoExtensionWork(ExtensionService* service) {
if (service)
- service->updater()->CheckNow(base::Closure());
+ service->updater()->CheckNow(extensions::ExtensionUpdater::CheckParams());
}
content::NotificationRegistrar registrar_;
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
index 1e3eb37..b59a21d 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
+++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
@@ -772,7 +772,7 @@ void ExtensionSettingsHandler::HandleShowButtonMessage(const ListValue* args) {
void ExtensionSettingsHandler::HandleAutoUpdateMessage(const ListValue* args) {
ExtensionUpdater* updater = extension_service_->updater();
if (updater)
- updater->CheckNow(base::Closure());
+ updater->CheckNow(extensions::ExtensionUpdater::CheckParams());
}
void ExtensionSettingsHandler::HandleLoadUnpackedExtensionMessage(
diff --git a/chrome/common/chrome_notification_types.h b/chrome/common/chrome_notification_types.h
index df9509a..90e8f18 100644
--- a/chrome/common/chrome_notification_types.h
+++ b/chrome/common/chrome_notification_types.h
@@ -670,8 +670,9 @@ enum NotificationType {
NOTIFICATION_EXTENSION_UPDATING_STARTED,
// The extension updater found an update and will attempt to download and
- // install it. The source is a Profile, and the details are an extension id
- // (const std::string).
+ // install it. The source is a Profile, and the details are an
+ // extensions::UpdateDetails object with the extension id and version of the
+ // found update.
NOTIFICATION_EXTENSION_UPDATE_FOUND,
// An installed app changed notification state (added or removed
diff --git a/chrome/common/extensions/api/runtime.json b/chrome/common/extensions/api/runtime.json
index 36c024bd..fddd130 100644
--- a/chrome/common/extensions/api/runtime.json
+++ b/chrome/common/extensions/api/runtime.json
@@ -86,6 +86,38 @@
"type": "function",
"unprivileged": true,
"parameters": []
+ },
+ {
+ "name": "requestUpdateCheck",
+ "type": "function",
+ "unprivileged": true,
+ "description": "Requests an update check for this app/extension.",
+ "parameters": [
+ {
+ "type": "function",
+ "name": "callback",
+ "parameters": [
+ {
+ "name": "status",
+ "type": "string",
+ "enum": ["throttled", "no_update", "update_available"],
+ "description": "Result of the update check."
+ },
+ {
+ "name": "details",
+ "type": "object",
+ "optional": true,
+ "properties": {
+ "version": {
+ "type": "string",
+ "description": "The version of the available update."
+ }
+ },
+ "description": "If an update is available, this contains more information about the available update."
+ }
+ ]
+ }
+ ]
}
],
"events": [