Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4380)

Unified Diff: chrome/browser/extensions/updater/extension_downloader.cc

Issue 654363002: Move ExtensionDownloader to //extensions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/updater/extension_downloader.cc
diff --git a/chrome/browser/extensions/updater/extension_downloader.cc b/chrome/browser/extensions/updater/extension_downloader.cc
deleted file mode 100644
index ef5c2bd4f57d5cfa8c766c3f3086387fc64a2c78..0000000000000000000000000000000000000000
--- a/chrome/browser/extensions/updater/extension_downloader.cc
+++ /dev/null
@@ -1,956 +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/updater/extension_downloader.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_profile.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "base/version.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "extensions/browser/extensions_browser_client.h"
-#include "extensions/browser/notification_types.h"
-#include "extensions/browser/updater/extension_cache.h"
-#include "extensions/browser/updater/request_queue_impl.h"
-#include "extensions/browser/updater/safe_manifest_parser.h"
-#include "extensions/common/extension_urls.h"
-#include "extensions/common/manifest_url_handlers.h"
-#include "google_apis/gaia/identity_provider.h"
-#include "net/base/backoff_entry.h"
-#include "net/base/load_flags.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.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 net::BackoffEntry::Policy kDefaultBackoffPolicy = {
- // Number of initial errors (in sequence) to ignore before applying
- // exponential back-off rules.
- 0,
-
- // Initial delay for exponential back-off in ms.
- 2000,
-
- // Factor by which the waiting time will be multiplied.
- 2,
-
- // Fuzzing percentage. ex: 10% will spread requests randomly
- // between 90%-100% of the calculated time.
- 0.1,
-
- // Maximum amount of time we are willing to delay our request in ms.
- -1,
-
- // Time to keep an entry from being discarded even when it
- // has no significant state, -1 to never discard.
- -1,
-
- // Don't use initial delay unless the last request was an error.
- false,
-};
-
-const char kAuthUserQueryKey[] = "authuser";
-
-const int kMaxAuthUserValue = 10;
-const int kMaxOAuth2Attempts = 3;
-
-const char kNotFromWebstoreInstallSource[] = "notfromwebstore";
-const char kDefaultInstallSource[] = "";
-
-const char kGoogleDotCom[] = "google.com";
-const char kTokenServiceConsumerId[] = "extension_downloader";
-const char kWebstoreOAuth2Scope[] =
- "https://www.googleapis.com/auth/chromewebstore.readonly";
-
-#define RETRY_HISTOGRAM(name, retry_count, url) \
- if ((url).DomainIs(kGoogleDotCom)) { \
- UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountGoogleUrl", \
- retry_count, \
- 1, \
- kMaxRetries, \
- kMaxRetries + 1); \
- } else { \
- UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountOtherUrl", \
- retry_count, \
- 1, \
- kMaxRetries, \
- kMaxRetries + 1); \
- }
-
-bool ShouldRetryRequest(const net::URLRequestStatus& status,
- int response_code) {
- // Retry if the response code is a server error, or the request failed because
- // of network errors as opposed to file errors.
- return ((response_code >= 500 && status.is_success()) ||
- status.status() == net::URLRequestStatus::FAILED);
-}
-
-// This parses and updates a URL query such that the value of the |authuser|
-// query parameter is incremented by 1. If parameter was not present in the URL,
-// it will be added with a value of 1. All other query keys and values are
-// preserved as-is. Returns |false| if the user index exceeds a hard-coded
-// maximum.
-bool IncrementAuthUserIndex(GURL* url) {
- int user_index = 0;
- std::string old_query = url->query();
- std::vector<std::string> new_query_parts;
- url::Component query(0, old_query.length());
- url::Component key, value;
- while (url::ExtractQueryKeyValue(old_query.c_str(), &query, &key, &value)) {
- std::string key_string = old_query.substr(key.begin, key.len);
- std::string value_string = old_query.substr(value.begin, value.len);
- if (key_string == kAuthUserQueryKey) {
- base::StringToInt(value_string, &user_index);
- } else {
- new_query_parts.push_back(base::StringPrintf(
- "%s=%s", key_string.c_str(), value_string.c_str()));
- }
- }
- if (user_index >= kMaxAuthUserValue)
- return false;
- new_query_parts.push_back(
- base::StringPrintf("%s=%d", kAuthUserQueryKey, user_index + 1));
- std::string new_query_string = JoinString(new_query_parts, '&');
- url::Component new_query(0, new_query_string.size());
- url::Replacements<char> replacements;
- replacements.SetQuery(new_query_string.c_str(), new_query);
- *url = url->ReplaceComponents(replacements);
- return true;
-}
-
-} // namespace
-
-UpdateDetails::UpdateDetails(const std::string& id, const Version& version)
- : id(id), version(version) {}
-
-UpdateDetails::~UpdateDetails() {}
-
-ExtensionDownloader::ExtensionFetch::ExtensionFetch()
- : url(), credentials(CREDENTIALS_NONE) {
-}
-
-ExtensionDownloader::ExtensionFetch::ExtensionFetch(
- const std::string& id,
- const GURL& url,
- const std::string& package_hash,
- const std::string& version,
- const std::set<int>& request_ids)
- : id(id),
- url(url),
- package_hash(package_hash),
- version(version),
- request_ids(request_ids),
- credentials(CREDENTIALS_NONE),
- oauth2_attempt_count(0) {
-}
-
-ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {}
-
-ExtensionDownloader::ExtensionDownloader(
- ExtensionDownloaderDelegate* delegate,
- net::URLRequestContextGetter* request_context)
- : OAuth2TokenService::Consumer(kTokenServiceConsumerId),
- delegate_(delegate),
- request_context_(request_context),
- manifests_queue_(&kDefaultBackoffPolicy,
- base::Bind(&ExtensionDownloader::CreateManifestFetcher,
- base::Unretained(this))),
- extensions_queue_(&kDefaultBackoffPolicy,
- base::Bind(&ExtensionDownloader::CreateExtensionFetcher,
- base::Unretained(this))),
- extension_cache_(NULL),
- enable_extra_update_metrics_(false),
- weak_ptr_factory_(this) {
- DCHECK(delegate_);
- DCHECK(request_context_.get());
-}
-
-ExtensionDownloader::~ExtensionDownloader() {}
-
-bool ExtensionDownloader::AddExtension(const Extension& extension,
- int request_id) {
- // Skip extensions with empty update URLs converted from user
- // scripts.
- if (extension.converted_from_user_script() &&
- ManifestURL::GetUpdateURL(&extension).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 (!ManifestURL::UpdatesFromGallery(&extension))
- update_url_data = delegate_->GetUpdateUrlData(extension.id());
-
- std::string install_source;
- bool force_update = delegate_->ShouldForceUpdate(extension.id(),
- &install_source);
- return AddExtensionData(extension.id(),
- *extension.version(),
- extension.GetType(),
- ManifestURL::GetUpdateURL(&extension),
- update_url_data,
- request_id,
- force_update,
- install_source);
-}
-
-bool ExtensionDownloader::AddPendingExtension(const std::string& id,
- const GURL& update_url,
- int request_id) {
- // 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,
- Manifest::TYPE_UNKNOWN,
- update_url,
- std::string(),
- request_id,
- false,
- std::string());
-}
-
-void ExtensionDownloader::StartAllPending(ExtensionCache* cache) {
- if (cache) {
- extension_cache_ = cache;
- extension_cache_->Start(base::Bind(
- &ExtensionDownloader::DoStartAllPending,
- weak_ptr_factory_.GetWeakPtr()));
- } else {
- DoStartAllPending();
- }
-}
-
-void ExtensionDownloader::DoStartAllPending() {
- ReportStats();
- url_stats_ = URLStats();
-
- for (FetchMap::iterator it = fetches_preparing_.begin();
- it != fetches_preparing_.end(); ++it) {
- std::vector<linked_ptr<ManifestFetchData> >& list = it->second;
- for (size_t i = 0; i < list.size(); ++i) {
- StartUpdateCheck(scoped_ptr<ManifestFetchData>(list[i].release()));
- }
- }
- fetches_preparing_.clear();
-}
-
-void ExtensionDownloader::StartBlacklistUpdate(
- const std::string& version,
- const ManifestFetchData::PingData& ping_data,
- int request_id) {
- // 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.
- scoped_ptr<ManifestFetchData> blacklist_fetch(CreateManifestFetchData(
- extension_urls::GetWebstoreUpdateUrl(), request_id));
- DCHECK(blacklist_fetch->base_url().SchemeIsSecure());
- blacklist_fetch->AddExtension(kBlacklistAppID,
- version,
- &ping_data,
- std::string(),
- kDefaultInstallSource,
- false);
- StartUpdateCheck(blacklist_fetch.Pass());
-}
-
-void ExtensionDownloader::SetWebstoreIdentityProvider(
- scoped_ptr<IdentityProvider> identity_provider) {
- identity_provider_.swap(identity_provider);
-}
-
-bool ExtensionDownloader::AddExtensionData(
- const std::string& id,
- const Version& version,
- Manifest::Type extension_type,
- const GURL& extension_update_url,
- const std::string& update_url_data,
- int request_id,
- bool force_update,
- const std::string& install_source_override) {
- GURL update_url(extension_update_url);
- // 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;
- }
-
- // Make sure we use SSL for store-hosted extensions.
- if (extension_urls::IsWebstoreUpdateUrl(update_url) &&
- !update_url.SchemeIsSecure())
- update_url = extension_urls::GetWebstoreUpdateUrl();
-
- // Skip extensions with empty IDs.
- if (id.empty()) {
- LOG(WARNING) << "Found extension with empty ID";
- return false;
- }
-
- if (update_url.DomainIs(kGoogleDotCom)) {
- url_stats_.google_url_count++;
- } else if (update_url.is_empty()) {
- url_stats_.no_url_count++;
- // Fill in default update URL.
- update_url = extension_urls::GetWebstoreUpdateUrl();
- } else {
- url_stats_.other_url_count++;
- }
-
- switch (extension_type) {
- case Manifest::TYPE_THEME:
- ++url_stats_.theme_count;
- break;
- case Manifest::TYPE_EXTENSION:
- case Manifest::TYPE_USER_SCRIPT:
- ++url_stats_.extension_count;
- break;
- case Manifest::TYPE_HOSTED_APP:
- case Manifest::TYPE_LEGACY_PACKAGED_APP:
- ++url_stats_.app_count;
- break;
- case Manifest::TYPE_PLATFORM_APP:
- ++url_stats_.platform_app_count;
- break;
- case Manifest::TYPE_UNKNOWN:
- default:
- ++url_stats_.pending_count;
- break;
- }
-
- std::vector<GURL> update_urls;
- update_urls.push_back(update_url);
- // If metrics are enabled, also add to ManifestFetchData for the
- // webstore update URL.
- if (!extension_urls::IsWebstoreUpdateUrl(update_url) &&
- enable_extra_update_metrics_) {
- update_urls.push_back(extension_urls::GetWebstoreUpdateUrl());
- }
-
- 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;
- if (!install_source_override.empty()) {
- install_source = install_source_override;
- }
-
- 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.
- bool added = false;
- FetchMap::iterator existing_iter = fetches_preparing_.find(
- std::make_pair(request_id, 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().get();
- if (existing_fetch->AddExtension(id, version.GetString(),
- optional_ping_data, update_url_data,
- install_source,
- force_update)) {
- added = true;
- }
- }
- if (!added) {
- // Otherwise add a new element to the list, if the list doesn't exist or
- // if its last element is already full.
- linked_ptr<ManifestFetchData> fetch(
- CreateManifestFetchData(update_urls[i], request_id));
- fetches_preparing_[std::make_pair(request_id, update_urls[i])].
- push_back(fetch);
- added = fetch->AddExtension(id, version.GetString(),
- optional_ping_data,
- update_url_data,
- install_source,
- force_update);
- 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.UpdateCheckPackagedApp",
- url_stats_.platform_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(
- scoped_ptr<ManifestFetchData> fetch_data) {
- const std::set<std::string>& id_set(fetch_data->extension_ids());
-
- if (!ExtensionsBrowserClient::Get()->IsBackgroundUpdateAllowed()) {
- NotifyExtensionsDownloadFailed(id_set,
- fetch_data->request_ids(),
- ExtensionDownloaderDelegate::DISABLED);
- }
-
- RequestQueue<ManifestFetchData>::iterator i;
- for (i = manifests_queue_.begin(); i != manifests_queue_.end(); ++i) {
- if (fetch_data->full_url() == i->full_url()) {
- // This url is already scheduled to be fetched.
- i->Merge(*fetch_data);
- return;
- }
- }
-
- if (manifests_queue_.active_request() &&
- manifests_queue_.active_request()->full_url() == fetch_data->full_url()) {
- manifests_queue_.active_request()->Merge(*fetch_data);
- } else {
- UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
- fetch_data->full_url().possibly_invalid_spec().length());
-
- manifests_queue_.ScheduleRequest(fetch_data.Pass());
- }
-}
-
-void ExtensionDownloader::CreateManifestFetcher() {
- if (VLOG_IS_ON(2)) {
- std::vector<std::string> id_vector(
- manifests_queue_.active_request()->extension_ids().begin(),
- manifests_queue_.active_request()->extension_ids().end());
- std::string id_list = JoinString(id_vector, ',');
- VLOG(2) << "Fetching " << manifests_queue_.active_request()->full_url()
- << " for " << id_list;
- }
-
- manifest_fetcher_.reset(net::URLFetcher::Create(
- kManifestFetcherId, manifests_queue_.active_request()->full_url(),
- net::URLFetcher::GET, this));
- manifest_fetcher_->SetRequestContext(request_context_.get());
- manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DISABLE_CACHE);
- // Update checks can be interrupted if a network change is detected; this is
- // common for the retail mode AppPack on ChromeOS. Retrying once should be
- // enough to recover in those cases; let the fetcher retry up to 3 times
- // just in case. http://crosbug.com/130602
- manifest_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3);
- manifest_fetcher_->Start();
-}
-
-void ExtensionDownloader::OnURLFetchComplete(
- const net::URLFetcher* source) {
- // TODO(vadimt): Remove ScopedProfile below once crbug.com/422577 is fixed.
- tracked_objects::ScopedProfile tracking_profile(
- FROM_HERE_WITH_EXPLICIT_FUNCTION(
- "422577 ExtensionDownloader::OnURLFetchComplete"));
-
- VLOG(2) << source->GetResponseCode() << " " << source->GetURL();
-
- if (source == manifest_fetcher_.get()) {
- std::string data;
- source->GetResponseAsString(&data);
- OnManifestFetchComplete(source->GetURL(),
- source->GetStatus(),
- source->GetResponseCode(),
- source->GetBackoffDelay(),
- data);
- } else if (source == extension_fetcher_.get()) {
- OnCRXFetchComplete(source,
- source->GetURL(),
- source->GetStatus(),
- source->GetResponseCode(),
- source->GetBackoffDelay());
- } else {
- NOTREACHED();
- }
-}
-
-void ExtensionDownloader::OnManifestFetchComplete(
- const GURL& url,
- const net::URLRequestStatus& status,
- int response_code,
- const base::TimeDelta& backoff_delay,
- 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))) {
- RETRY_HISTOGRAM("ManifestFetchSuccess",
- manifests_queue_.active_request_failure_count(), url);
- VLOG(2) << "beginning manifest parse for " << url;
- scoped_refptr<SafeManifestParser> safe_parser(
- new SafeManifestParser(
- data,
- manifests_queue_.reset_active_request().release(),
- base::Bind(&ExtensionDownloader::HandleManifestResults,
- weak_ptr_factory_.GetWeakPtr())));
- safe_parser->Start();
- } else {
- VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
- << "' response code:" << response_code;
- if (ShouldRetryRequest(status, response_code) &&
- manifests_queue_.active_request_failure_count() < kMaxRetries) {
- manifests_queue_.RetryRequest(backoff_delay);
- } else {
- RETRY_HISTOGRAM("ManifestFetchFailure",
- manifests_queue_.active_request_failure_count(), url);
- NotifyExtensionsDownloadFailed(
- manifests_queue_.active_request()->extension_ids(),
- manifests_queue_.active_request()->request_ids(),
- ExtensionDownloaderDelegate::MANIFEST_FETCH_FAILED);
- }
- }
- manifest_fetcher_.reset();
- manifests_queue_.reset_active_request();
-
- // If we have any pending manifest requests, fire off the next one.
- manifests_queue_.StartNextRequest();
-}
-
-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,
- fetch_data.request_ids(),
- 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);
-
- GURL crx_url = update->crx_url;
- if (id != kBlacklistAppID) {
- 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
- // references a blacklist file.
- DCHECK(extension_urls::IsBlacklistUpdateUrl(crx_url)) << crx_url;
-
- // Force https (crbug.com/129587).
- if (!crx_url.SchemeIsSecure()) {
- url::Replacements<char> replacements;
- std::string scheme("https");
- replacements.SetScheme(scheme.c_str(),
- url::Component(0, scheme.size()));
- crx_url = crx_url.ReplaceComponents(replacements);
- }
- }
- scoped_ptr<ExtensionFetch> fetch(new ExtensionFetch(
- update->extension_id, crx_url, update->package_hash,
- update->version, fetch_data.request_ids()));
- FetchUpdatedExtension(fetch.Pass());
- }
-
- // 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(kGoogleDotCom) &&
- 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,
- fetch_data.request_ids(),
- ExtensionDownloaderDelegate::NO_UPDATE_AVAILABLE);
-}
-
-void ExtensionDownloader::DetermineUpdates(
- const ManifestFetchData& fetch_data,
- const UpdateManifest::Results& possible_updates,
- std::vector<int>* result) {
- 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 << "'";
-
- // We should skip the version check if update was forced.
- if (!fetch_data.DidForceUpdate(id)) {
- 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 &&
- !ExtensionsBrowserClient::Get()->IsMinBrowserVersionSupported(
- update->browser_min_version)) {
- // 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(
- scoped_ptr<ExtensionFetch> fetch_data) {
- if (!fetch_data->url.is_valid()) {
- // TODO(asargent): This can sometimes be invalid. See crbug.com/130881.
- LOG(ERROR) << "Invalid URL: '" << fetch_data->url.possibly_invalid_spec()
- << "' for extension " << fetch_data->id;
- return;
- }
-
- for (RequestQueue<ExtensionFetch>::iterator iter =
- extensions_queue_.begin();
- iter != extensions_queue_.end(); ++iter) {
- if (iter->id == fetch_data->id || iter->url == fetch_data->url) {
- iter->request_ids.insert(fetch_data->request_ids.begin(),
- fetch_data->request_ids.end());
- return; // already scheduled
- }
- }
-
- if (extensions_queue_.active_request() &&
- extensions_queue_.active_request()->url == fetch_data->url) {
- extensions_queue_.active_request()->request_ids.insert(
- fetch_data->request_ids.begin(), fetch_data->request_ids.end());
- } else {
- std::string version;
- if (extension_cache_ &&
- extension_cache_->GetExtension(fetch_data->id, NULL, &version) &&
- version == fetch_data->version) {
- base::FilePath crx_path;
- // Now get .crx file path and mark extension as used.
- extension_cache_->GetExtension(fetch_data->id, &crx_path, &version);
- NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, false);
- } else {
- extensions_queue_.ScheduleRequest(fetch_data.Pass());
- }
- }
-}
-
-void ExtensionDownloader::NotifyDelegateDownloadFinished(
- scoped_ptr<ExtensionFetch> fetch_data,
- const base::FilePath& crx_path,
- bool file_ownership_passed) {
- delegate_->OnExtensionDownloadFinished(fetch_data->id, crx_path,
- file_ownership_passed, fetch_data->url, fetch_data->version,
- ping_results_[fetch_data->id], fetch_data->request_ids);
- ping_results_.erase(fetch_data->id);
-}
-
-void ExtensionDownloader::CreateExtensionFetcher() {
- const ExtensionFetch* fetch = extensions_queue_.active_request();
- extension_fetcher_.reset(net::URLFetcher::Create(
- kExtensionFetcherId, fetch->url, net::URLFetcher::GET, this));
- extension_fetcher_->SetRequestContext(request_context_.get());
- extension_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3);
-
- int load_flags = net::LOAD_DISABLE_CACHE;
- bool is_secure = fetch->url.SchemeIsSecure();
- if (fetch->credentials != ExtensionFetch::CREDENTIALS_COOKIES || !is_secure) {
- load_flags |= net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES;
- }
- extension_fetcher_->SetLoadFlags(load_flags);
-
- // 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 (fetch->id != kBlacklistAppID) {
- extension_fetcher_->SaveResponseToTemporaryFile(
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
- }
-
- if (fetch->credentials == ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN &&
- is_secure) {
- if (access_token_.empty()) {
- // We should try OAuth2, but we have no token cached. This
- // ExtensionFetcher will be started once the token fetch is complete,
- // in either OnTokenFetchSuccess or OnTokenFetchFailure.
- DCHECK(identity_provider_.get());
- OAuth2TokenService::ScopeSet webstore_scopes;
- webstore_scopes.insert(kWebstoreOAuth2Scope);
- access_token_request_ =
- identity_provider_->GetTokenService()->StartRequest(
- identity_provider_->GetActiveAccountId(),
- webstore_scopes,
- this);
- return;
- }
- extension_fetcher_->AddExtraRequestHeader(
- base::StringPrintf("%s: Bearer %s",
- net::HttpRequestHeaders::kAuthorization,
- access_token_.c_str()));
- }
-
- VLOG(2) << "Starting fetch of " << fetch->url << " for " << fetch->id;
- extension_fetcher_->Start();
-}
-
-void ExtensionDownloader::OnCRXFetchComplete(
- const net::URLFetcher* source,
- const GURL& url,
- const net::URLRequestStatus& status,
- int response_code,
- const base::TimeDelta& backoff_delay) {
- ExtensionFetch& active_request = *extensions_queue_.active_request();
- const std::string& id = active_request.id;
- if (status.status() == net::URLRequestStatus::SUCCESS &&
- (response_code == 200 || url.SchemeIsFile())) {
- RETRY_HISTOGRAM("CrxFetchSuccess",
- extensions_queue_.active_request_failure_count(), url);
- base::FilePath crx_path;
- // Take ownership of the file at |crx_path|.
- CHECK(source->GetResponseAsFilePath(true, &crx_path));
- scoped_ptr<ExtensionFetch> fetch_data =
- extensions_queue_.reset_active_request();
- if (extension_cache_) {
- const std::string& version = fetch_data->version;
- extension_cache_->PutExtension(id, crx_path, version,
- base::Bind(&ExtensionDownloader::NotifyDelegateDownloadFinished,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(&fetch_data)));
- } else {
- NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, true);
- }
- } else if (IterateFetchCredentialsAfterFailure(
- &active_request,
- status,
- response_code)) {
- extensions_queue_.RetryRequest(backoff_delay);
- } else {
- const std::set<int>& request_ids = active_request.request_ids;
- const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id];
- VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
- << "' response code:" << response_code;
- if (ShouldRetryRequest(status, response_code) &&
- extensions_queue_.active_request_failure_count() < kMaxRetries) {
- extensions_queue_.RetryRequest(backoff_delay);
- } else {
- RETRY_HISTOGRAM("CrxFetchFailure",
- extensions_queue_.active_request_failure_count(), url);
- // status.error() is 0 (net::OK) or negative. (See net/base/net_errors.h)
- UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.CrxFetchError", -status.error());
- delegate_->OnExtensionDownloadFailed(
- id, ExtensionDownloaderDelegate::CRX_FETCH_FAILED, ping, request_ids);
- }
- ping_results_.erase(id);
- extensions_queue_.reset_active_request();
- }
-
- extension_fetcher_.reset();
-
- // If there are any pending downloads left, start the next one.
- extensions_queue_.StartNextRequest();
-}
-
-void ExtensionDownloader::NotifyExtensionsDownloadFailed(
- const std::set<std::string>& extension_ids,
- const std::set<int>& request_ids,
- ExtensionDownloaderDelegate::Error error) {
- for (std::set<std::string>::const_iterator it = extension_ids.begin();
- it != extension_ids.end(); ++it) {
- const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[*it];
- delegate_->OnExtensionDownloadFailed(*it, error, ping, request_ids);
- ping_results_.erase(*it);
- }
-}
-
-void ExtensionDownloader::NotifyUpdateFound(const std::string& id,
- const std::string& version) {
- UpdateDetails updateInfo(id, Version(version));
- content::NotificationService::current()->Notify(
- extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND,
- content::NotificationService::AllBrowserContextsAndSources(),
- content::Details<UpdateDetails>(&updateInfo));
-}
-
-bool ExtensionDownloader::IterateFetchCredentialsAfterFailure(
- ExtensionFetch* fetch,
- const net::URLRequestStatus& status,
- int response_code) {
- bool auth_failure = status.status() == net::URLRequestStatus::CANCELED ||
- (status.status() == net::URLRequestStatus::SUCCESS &&
- (response_code == net::HTTP_UNAUTHORIZED ||
- response_code == net::HTTP_FORBIDDEN));
- if (!auth_failure) {
- return false;
- }
- // Here we decide what to do next if the server refused to authorize this
- // fetch.
- switch (fetch->credentials) {
- case ExtensionFetch::CREDENTIALS_NONE:
- if (fetch->url.DomainIs(kGoogleDotCom) && identity_provider_) {
- fetch->credentials = ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN;
- } else {
- fetch->credentials = ExtensionFetch::CREDENTIALS_COOKIES;
- }
- return true;
- case ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN:
- fetch->oauth2_attempt_count++;
- // OAuth2 may fail due to an expired access token, in which case we
- // should invalidate the token and try again.
- if (response_code == net::HTTP_UNAUTHORIZED &&
- fetch->oauth2_attempt_count <= kMaxOAuth2Attempts) {
- DCHECK(identity_provider_.get());
- OAuth2TokenService::ScopeSet webstore_scopes;
- webstore_scopes.insert(kWebstoreOAuth2Scope);
- identity_provider_->GetTokenService()->InvalidateToken(
- identity_provider_->GetActiveAccountId(),
- webstore_scopes,
- access_token_);
- access_token_.clear();
- return true;
- }
- // Either there is no Gaia identity available, the active identity
- // doesn't have access to this resource, or the server keeps returning
- // 401s and we've retried too many times. Fall back on cookies.
- if (access_token_.empty() ||
- response_code == net::HTTP_FORBIDDEN ||
- fetch->oauth2_attempt_count > kMaxOAuth2Attempts) {
- fetch->credentials = ExtensionFetch::CREDENTIALS_COOKIES;
- return true;
- }
- // Something else is wrong. Time to give up.
- return false;
- case ExtensionFetch::CREDENTIALS_COOKIES:
- if (response_code == net::HTTP_FORBIDDEN) {
- // Try the next session identity, up to some maximum.
- return IncrementAuthUserIndex(&fetch->url);
- }
- return false;
- default:
- NOTREACHED();
- }
- NOTREACHED();
- return false;
-}
-
-void ExtensionDownloader::OnGetTokenSuccess(
- const OAuth2TokenService::Request* request,
- const std::string& access_token,
- const base::Time& expiration_time) {
- access_token_ = access_token;
- extension_fetcher_->AddExtraRequestHeader(
- base::StringPrintf("%s: Bearer %s",
- net::HttpRequestHeaders::kAuthorization,
- access_token_.c_str()));
- extension_fetcher_->Start();
-}
-
-void ExtensionDownloader::OnGetTokenFailure(
- const OAuth2TokenService::Request* request,
- const GoogleServiceAuthError& error) {
- // If we fail to get an access token, kick the pending fetch and let it fall
- // back on cookies.
- extension_fetcher_->Start();
-}
-
-ManifestFetchData* ExtensionDownloader::CreateManifestFetchData(
- const GURL& update_url,
- int request_id) {
- ManifestFetchData::PingMode ping_mode = ManifestFetchData::NO_PING;
- if (update_url.DomainIs(ping_enabled_domain_.c_str())) {
- if (enable_extra_update_metrics_) {
- ping_mode = ManifestFetchData::PING_WITH_METRICS;
- } else {
- ping_mode = ManifestFetchData::PING;
- }
- }
- return new ManifestFetchData(
- update_url, request_id, brand_code_, manifest_query_params_, ping_mode);
-}
-
-} // namespace extensions

Powered by Google App Engine
This is Rietveld 408576698