#include <algorithm>
#include <set>
-#include <vector>
#include "base/bind.h"
+#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/extensions/api/module/module.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/updater/extension_downloader.h"
+#include "chrome/browser/extensions/pending_extension_manager.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
+#include "components/omaha_query_params/omaha_query_params.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"
-#include "extensions/browser/pending_extension_manager.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_constants.h"
using base::RandDouble;
using base::RandInt;
using base::Time;
using base::TimeDelta;
using content::BrowserThread;
+using extensions::Extension;
+using extensions::ExtensionSet;
+using omaha_query_params::OmahaQueryParams;
typedef extensions::ExtensionDownloaderDelegate::Error Error;
typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult;
const int kStartupWaitSeconds = 60 * 5;
// For sanity checking on update frequency - enforced in release mode only.
-#ifdef NDEBUG
+#if defined(NDEBUG)
const int kMinUpdateFrequencySeconds = 30;
#endif
const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days
// checks.
const int kMinUpdateThrottleTime = 5;
+// The installsource query parameter to use when forcing updates due to NaCl
+// arch mismatch.
+const char kWrongMultiCrxInstallSource[] = "wrong_multi_crx";
+
// 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
return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
}
+void RespondWithForcedUpdates(
+ const base::Callback<void(const std::set<std::string>&)>& callback,
+ scoped_ptr<std::set<std::string> > forced_updates) {
+ callback.Run(*forced_updates.get());
+}
+
+void DetermineForcedUpdatesOnBlockingPool(
+ scoped_ptr<std::vector<scoped_refptr<const Extension> > > extensions,
+ const base::Callback<void(const std::set<std::string>&)>& callback) {
+ scoped_ptr<std::set<std::string> > forced_updates(
+ new std::set<std::string>());
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+ for (std::vector<scoped_refptr<const Extension> >::const_iterator iter =
+ extensions->begin();
+ iter != extensions->end();
+ ++iter) {
+ scoped_refptr<const Extension> extension = *iter;
+ base::FilePath platform_specific_path = extension->path().Append(
+ extensions::kPlatformSpecificFolder);
+ if (base::PathExists(platform_specific_path)) {
+ bool force = true;
+ const base::ListValue* platforms;
+ if (extension->manifest()->GetList(extensions::manifest_keys::kPlatforms,
+ &platforms)) {
+ for (size_t i = 0; i < platforms->GetSize(); ++i) {
+ const base::DictionaryValue* p;
+ if (platforms->GetDictionary(i, &p)) {
+ std::string nacl_arch;
+ if (p->GetString(extensions::manifest_keys::kNaClArch,
+ &nacl_arch) &&
+ nacl_arch == OmahaQueryParams::GetNaclArch()) {
+ std::string subpath;
+ if (p->GetString(extensions::manifest_keys::kSubPackagePath,
+ &subpath)) {
+ // _platform_specific is part of the sub_package_path entry.
+ base::FilePath platform_specific_subpath =
+ extension->path().AppendASCII(subpath);
+ if (base::PathExists(platform_specific_subpath)) {
+ force = false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (force)
+ forced_updates->insert(extension->id());
+ }
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&RespondWithForcedUpdates,
+ callback,
+ base::Passed(&forced_updates)));
+}
+
+void CollectExtensionsFromSet(
+ const ExtensionSet& extensions,
+ std::vector<scoped_refptr<const Extension> >* paths) {
+ std::copy(extensions.begin(), extensions.end(), std::back_inserter(*paths));
+}
+
+void DetermineForcedUpdates(
+ content::BrowserContext* browser_context,
+ const base::Callback<void(const std::set<std::string>&)>& callback) {
+ scoped_ptr<std::vector<scoped_refptr<const Extension> > > extensions(
+ new std::vector<scoped_refptr<const Extension> >());
+ const extensions::ExtensionRegistry* registry =
+ extensions::ExtensionRegistry::Get(browser_context);
+ scoped_ptr<ExtensionSet> installed_extensions =
+ registry->GenerateInstalledExtensionsSet();
+ CollectExtensionsFromSet(*installed_extensions.get(), extensions.get());
+ BrowserThread::PostBlockingPoolTask(
+ FROM_HERE,
+ base::Bind(&DetermineForcedUpdatesOnBlockingPool,
+ base::Passed(&extensions),
+ callback));
+}
+
} // namespace
namespace extensions {
const std::string& i,
const base::FilePath& p,
bool file_ownership_passed,
- const GURL& u,
const std::set<int>& request_ids)
: extension_id(i),
path(p),
file_ownership_passed(file_ownership_passed),
- download_url(u),
request_ids(request_ids) {}
ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
- : path(), file_ownership_passed(true), download_url() {}
+ : path(), file_ownership_passed(true) {}
ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
Time check_start;
};
-ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
- ExtensionPrefs* extension_prefs,
- PrefService* prefs,
- Profile* profile,
- int frequency_seconds,
- ExtensionCache* cache)
+ExtensionUpdater::ExtensionUpdater(
+ ExtensionServiceInterface* service,
+ ExtensionPrefs* extension_prefs,
+ PrefService* prefs,
+ Profile* profile,
+ int frequency_seconds,
+ ExtensionCache* cache,
+ const ExtensionDownloader::Factory& downloader_factory)
: alive_(false),
- weak_ptr_factory_(this),
- service_(service), frequency_seconds_(frequency_seconds),
- will_check_soon_(false), extension_prefs_(extension_prefs),
- prefs_(prefs), profile_(profile),
+ service_(service),
+ downloader_factory_(downloader_factory),
+ frequency_seconds_(frequency_seconds),
+ will_check_soon_(false),
+ extension_prefs_(extension_prefs),
+ prefs_(prefs),
+ profile_(profile),
next_request_id_(0),
+ extension_registry_observer_(this),
crx_install_is_running_(false),
- extension_cache_(cache) {
+ extension_cache_(cache),
+ weak_ptr_factory_(this) {
DCHECK_GE(frequency_seconds_, 5);
DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
-#ifdef NDEBUG
+#if defined(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);
- registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
- content::NotificationService::AllBrowserContextsAndSources());
+ extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
}
ExtensionUpdater::~ExtensionUpdater() {
Stop();
}
+void ExtensionUpdater::EnsureDownloaderCreated() {
+ if (!downloader_.get()) {
+ downloader_ = downloader_factory_.Run(this);
+ }
+}
+
// The overall goal here is to balance keeping clients up to date while
// avoiding a thundering herd against update servers.
TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
kStartupWaitSeconds * 8));
}
- // Read the persisted next check time, and use that if it isn't too soon.
- // Otherwise pick something random.
+ // Read the persisted next check time, and use that if it isn't too soon
+ // or too late. Otherwise pick something random.
Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
pref_names::kNextUpdateCheck));
Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
- if (saved_next >= earliest) {
+ Time latest = now + TimeDelta::FromSeconds(frequency_seconds_);
+ if (saved_next >= earliest && saved_next <= latest) {
return saved_next - now;
} else {
return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
return will_check_soon_;
}
+void ExtensionUpdater::SetExtensionCacheForTesting(
+ ExtensionCache* extension_cache) {
+ extension_cache_ = extension_cache;
+}
+
void ExtensionUpdater::DoCheckSoon() {
DCHECK(will_check_soon_);
CheckNow(default_params_);
}
void ExtensionUpdater::CheckNow(const CheckParams& params) {
+ DetermineForcedUpdates(
+ profile_,
+ base::Bind(&ExtensionUpdater::OnForcedUpdatesDetermined,
+ weak_ptr_factory_.GetWeakPtr(),
+ params));
+}
+
+void ExtensionUpdater::OnForcedUpdatesDetermined(
+ const CheckParams& params,
+ const std::set<std::string>& forced_updates) {
int request_id = next_request_id_++;
VLOG(2) << "Starting update check " << request_id;
request.callback = params.callback;
request.install_immediately = params.install_immediately;
- if (!downloader_.get()) {
- downloader_.reset(
- new ExtensionDownloader(this, profile_->GetRequestContext()));
- }
+ EnsureDownloaderCreated();
+
+ forced_updates_ = forced_updates;
// Add fetch records for extensions that should be fetched by an update URL.
// These extensions are not yet installed. They come from group policy
request.in_progress_ids_.push_back(*iter);
}
- AddToDownloader(service_->extensions(), pending_ids, request_id);
- AddToDownloader(service_->disabled_extensions(), pending_ids, request_id);
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
+ AddToDownloader(®istry->enabled_extensions(), pending_ids, request_id);
+ AddToDownloader(®istry->disabled_extensions(), pending_ids, request_id);
} else {
for (std::list<std::string>::const_iterator it = params.ids.begin();
it != params.ids.end(); ++it) {
VLOG(2) << download_url << " written to " << path.value();
- FetchedCRXFile fetched(id, path, file_ownership_passed, download_url,
- request_ids);
+ FetchedCRXFile fetched(id, path, file_ownership_passed, request_ids);
fetched_crx_files_.push(fetched);
// MaybeInstallCRXFile() removes extensions from |in_progress_ids_| after
ping_data->rollcall_days = CalculatePingDays(
extension_prefs_->LastPingDay(id));
ping_data->is_enabled = service_->IsExtensionEnabled(id);
+ if (!ping_data->is_enabled)
+ ping_data->disable_reasons = extension_prefs_->GetDisableReasons(id);
ping_data->active_days =
CalculateActivePingDays(extension_prefs_->LastActivePingDay(id),
extension_prefs_->GetActiveBit(id));
return true;
}
+bool ExtensionUpdater::ShouldForceUpdate(
+ const std::string& extension_id,
+ std::string* source) {
+ bool force = forced_updates_.find(extension_id) != forced_updates_.end();
+ // Currently the only reason to force is a NaCl arch mismatch with the
+ // installed extension contents.
+ if (force) {
+ *source = kWrongMultiCrxInstallSource;
+ }
+ return force;
+}
+
void ExtensionUpdater::UpdatePingData(const std::string& id,
const PingResult& ping_result) {
DCHECK(alive_);
if (service_->UpdateExtension(crx_file.extension_id,
crx_file.path,
crx_file.file_ownership_passed,
- crx_file.download_url,
&installer)) {
crx_install_is_running_ = true;
current_crx_file_ = crx_file;
it != crx_file.request_ids.end(); ++it) {
InProgressCheck& request = requests_in_progress_[*it];
if (request.install_immediately) {
- installer->set_install_wait_for_idle(false);
+ installer->set_install_immediately(true);
break;
}
}
// Source parameter ensures that we only see the completion event for the
// the installer we started.
registrar_.Add(this,
- chrome::NOTIFICATION_CRX_INSTALLER_DONE,
+ extensions::NOTIFICATION_CRX_INSTALLER_DONE,
content::Source<CrxInstaller>(installer));
} else {
for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
void ExtensionUpdater::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
- 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);
- }
+ DCHECK_EQ(type, extensions::NOTIFICATION_CRX_INSTALLER_DONE);
- // If any files are available to update, start one.
- MaybeInstallCRXFile();
- break;
- }
- case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
- const Extension* extension =
- content::Details<const InstalledExtensionInfo>(details)->extension;
- if (extension)
- throttle_info_.erase(extension->id());
- break;
- }
- default:
- NOTREACHED();
+ registrar_.Remove(this, extensions::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();
+}
+
+void ExtensionUpdater::OnExtensionWillBeInstalled(
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ bool is_update,
+ bool from_ephemeral,
+ const std::string& old_name) {
+ throttle_info_.erase(extension->id());
}
void ExtensionUpdater::NotifyStarted() {
content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED,
+ extensions::NOTIFICATION_EXTENSION_UPDATING_STARTED,
content::Source<Profile>(profile_),
content::NotificationService::NoDetails());
}