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 |