diff options
Diffstat (limited to 'chromium/extensions/browser/updater')
11 files changed, 467 insertions, 140 deletions
diff --git a/chromium/extensions/browser/updater/extension_cache_fake.cc b/chromium/extensions/browser/updater/extension_cache_fake.cc index 5425a994477..3275bb13ff7 100644 --- a/chromium/extensions/browser/updater/extension_cache_fake.cc +++ b/chromium/extensions/browser/updater/extension_cache_fake.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/stl_util.h" -#include "base/task/post_task.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -17,11 +16,11 @@ ExtensionCacheFake::ExtensionCacheFake() = default; ExtensionCacheFake::~ExtensionCacheFake() = default; void ExtensionCacheFake::Start(const base::Closure& callback) { - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, callback); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, callback); } void ExtensionCacheFake::Shutdown(const base::Closure& callback) { - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, callback); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, callback); } void ExtensionCacheFake::AllowCaching(const std::string& id) { @@ -52,8 +51,8 @@ void ExtensionCacheFake::PutExtension(const std::string& id, if (base::Contains(allowed_extensions_, id)) { cache_[id].first = version; cache_[id].second = file_path; - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(callback, file_path, false)); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(callback, file_path, false)); } else { callback.Run(file_path, true); } diff --git a/chromium/extensions/browser/updater/extension_downloader.cc b/chromium/extensions/browser/updater/extension_downloader.cc index 2806def0117..a8c378d4d6b 100644 --- a/chromium/extensions/browser/updater/extension_downloader.cc +++ b/chromium/extensions/browser/updater/extension_downloader.cc @@ -134,6 +134,10 @@ bool ShouldRetryRequest(const network::SimpleURLLoader* loader) { return response_code >= 500 && response_code < 600; } +bool ShouldRetryRequestForExtensionNotFoundInCache(const int net_error_code) { + return net_error_code == net::ERR_INTERNET_DISCONNECTED; +} + // 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 @@ -203,6 +207,27 @@ ExtensionDownloader::ExtensionFetch::ExtensionFetch( ExtensionDownloader::ExtensionFetch::~ExtensionFetch() = default; +ExtensionDownloader::FetchDataGroupKey::FetchDataGroupKey() = default; + +ExtensionDownloader::FetchDataGroupKey::FetchDataGroupKey( + const FetchDataGroupKey& other) = default; + +ExtensionDownloader::FetchDataGroupKey::FetchDataGroupKey( + const int request_id, + const GURL& update_url, + const bool is_force_installed) + : request_id(request_id), + update_url(update_url), + is_force_installed(is_force_installed) {} + +ExtensionDownloader::FetchDataGroupKey::~FetchDataGroupKey() = default; + +bool ExtensionDownloader::FetchDataGroupKey::operator<( + const FetchDataGroupKey& other) const { + return std::tie(request_id, update_url, is_force_installed) < + std::tie(other.request_id, other.update_url, other.is_force_installed); +} + ExtensionDownloader::ExtraParams::ExtraParams() : is_corrupt_reinstall(false) {} ExtensionDownloader::ExtensionDownloader( @@ -324,6 +349,11 @@ void ExtensionDownloader::set_test_delegate( g_test_delegate = delegate; } +void ExtensionDownloader::SetBackoffPolicyForTesting( + const net::BackoffEntry::Policy* backoff_policy) { + manifests_queue_.set_backoff_policy(backoff_policy); +} + bool ExtensionDownloader::AddExtensionData( const std::string& id, const base::Version& version, @@ -403,8 +433,11 @@ bool ExtensionDownloader::AddExtensionData( // Find or create a ManifestFetchData to add this extension to. bool added = false; - auto existing_iter = - fetches_preparing_.find(std::make_pair(request_id, update_url)); + bool is_new_extension_force_installed = + extension_location == Manifest::Location::EXTERNAL_POLICY_DOWNLOAD; + FetchDataGroupKey key(request_id, update_url, + is_new_extension_force_installed); + auto existing_iter = fetches_preparing_.find(key); if (existing_iter != fetches_preparing_.end() && !existing_iter->second.empty()) { // Try to add to the ManifestFetchData at the end of the list. @@ -421,8 +454,9 @@ bool ExtensionDownloader::AddExtensionData( std::unique_ptr<ManifestFetchData> fetch( CreateManifestFetchData(update_url, request_id, fetch_priority)); ManifestFetchData* fetch_ptr = fetch.get(); - fetches_preparing_[std::make_pair(request_id, update_url)].push_back( - std::move(fetch)); + if (is_new_extension_force_installed) + fetch_ptr->set_is_all_external_policy_download(); + fetches_preparing_[key].push_back(std::move(fetch)); added = fetch_ptr->AddExtension(id, version.GetString(), optional_ping_data, extra.update_url_data, install_source, extension_location, fetch_priority); @@ -456,13 +490,12 @@ void ExtensionDownloader::StartUpdateCheck( g_test_delegate->StartUpdateCheck(this, delegate_, std::move(fetch_data)); return; } - + const ExtensionIdSet extension_ids = fetch_data->GetExtensionIds(); if (!ExtensionsBrowserClient::Get()->IsBackgroundUpdateAllowed()) { NotifyExtensionsDownloadStageChanged( - fetch_data->extension_ids(), - ExtensionDownloaderDelegate::Stage::FINISHED); + extension_ids, ExtensionDownloaderDelegate::Stage::FINISHED); NotifyExtensionsDownloadFailed( - fetch_data->extension_ids(), fetch_data->request_ids(), + extension_ids, fetch_data->request_ids(), ExtensionDownloaderDelegate::Error::DISABLED); return; } @@ -472,7 +505,7 @@ void ExtensionDownloader::StartUpdateCheck( if (fetch_data->full_url() == i->full_url()) { // This url is already scheduled to be fetched. NotifyExtensionsDownloadStageChanged( - fetch_data->extension_ids(), + extension_ids, ExtensionDownloaderDelegate::Stage::QUEUED_FOR_MANIFEST); i->Merge(*fetch_data); return; @@ -482,7 +515,7 @@ void ExtensionDownloader::StartUpdateCheck( if (manifests_queue_.active_request() && manifests_queue_.active_request()->full_url() == fetch_data->full_url()) { NotifyExtensionsDownloadStageChanged( - fetch_data->extension_ids(), + extension_ids, ExtensionDownloaderDelegate::Stage::DOWNLOADING_MANIFEST); manifests_queue_.active_request()->Merge(*fetch_data); } else { @@ -491,8 +524,7 @@ void ExtensionDownloader::StartUpdateCheck( fetch_data->full_url().possibly_invalid_spec().length()); NotifyExtensionsDownloadStageChanged( - fetch_data->extension_ids(), - ExtensionDownloaderDelegate::Stage::QUEUED_FOR_MANIFEST); + extension_ids, ExtensionDownloaderDelegate::Stage::QUEUED_FOR_MANIFEST); manifests_queue_.ScheduleRequest(std::move(fetch_data)); } } @@ -515,12 +547,11 @@ network::mojom::URLLoaderFactory* ExtensionDownloader::GetURLLoaderFactoryToUse( void ExtensionDownloader::CreateManifestLoader() { const ManifestFetchData* active_request = manifests_queue_.active_request(); + const ExtensionIdSet extension_ids = active_request->GetExtensionIds(); NotifyExtensionsDownloadStageChanged( - active_request->extension_ids(), - ExtensionDownloaderDelegate::Stage::DOWNLOADING_MANIFEST); - std::vector<base::StringPiece> id_vector( - active_request->extension_ids().begin(), - active_request->extension_ids().end()); + extension_ids, ExtensionDownloaderDelegate::Stage::DOWNLOADING_MANIFEST); + std::vector<base::StringPiece> id_vector(extension_ids.begin(), + extension_ids.end()); std::string id_list = base::JoinString(id_vector, ","); VLOG(2) << "Fetching " << active_request->full_url() << " for " << id_list; VLOG(2) << "Update interactivity: " @@ -605,10 +636,132 @@ void ExtensionDownloader::CreateManifestLoader() { base::Unretained(this))); } +void ExtensionDownloader::RetryManifestFetchRequest() { + constexpr base::TimeDelta backoff_delay; + NotifyExtensionsDownloadStageChanged( + manifests_queue_.active_request()->GetExtensionIds(), + ExtensionDownloaderDelegate::Stage::DOWNLOADING_MANIFEST_RETRY); + manifests_queue_.RetryRequest(backoff_delay); +} + +void ExtensionDownloader::ReportManifestFetchFailure( + ManifestFetchData* fetch_data, + ExtensionDownloaderDelegate::Error error, + const ExtensionDownloaderDelegate::FailureData& data) { + const ExtensionIdSet extension_ids = fetch_data->GetExtensionIds(); + NotifyExtensionsDownloadStageChanged( + extension_ids, ExtensionDownloaderDelegate::Stage::FINISHED); + NotifyExtensionsDownloadFailedWithFailureData( + extension_ids, fetch_data->request_ids(), error, data); +} + +void ExtensionDownloader::TryFetchingExtensionsFromCache( + ManifestFetchData* fetch_data, + ExtensionDownloaderDelegate::Error error, + const int net_error, + const int response_code, + const base::Optional<ManifestInvalidErrorList>& manifest_invalid_errors) { + const ExtensionIdSet extension_ids = fetch_data->GetExtensionIds(); + ExtensionIdSet extensions_fetched_from_cache; + for (const auto& extension_id : extension_ids) { + // Extension is fetched here only in cases when we fail to fetch the update + // manifest or parsing of update manifest failed. In such cases, we don't + // have expected version and expected hash. Thus, passing empty hash and + // version would not be a problem as we only check for the expected hash and + // version if we have them. + auto extension_fetch_data(std::make_unique<ExtensionFetch>( + extension_id, fetch_data->base_url(), /*hash not fetched*/ "", + /*version not fetched*/ "", fetch_data->request_ids())); + base::Optional<base::FilePath> cached_crx_path = GetCachedExtension( + *extension_fetch_data, /*manifest_fetch_failed*/ true); + if (cached_crx_path) { + delegate_->OnExtensionDownloadStageChanged( + extension_id, ExtensionDownloaderDelegate::Stage::FINISHED); + NotifyDelegateDownloadFinished(std::move(extension_fetch_data), true, + cached_crx_path.value(), false); + extensions_fetched_from_cache.insert(extension_id); + } + } + // All the extensions were found in the cache, no need to retry any request or + // report failure. + if (extensions_fetched_from_cache.size() == extension_ids.size()) + return; + fetch_data->RemoveExtensions(extensions_fetched_from_cache, + manifest_query_params_); + + if (ShouldRetryRequestForExtensionNotFoundInCache(net_error)) { + RetryManifestFetchRequest(); + return; + } + if (error == ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED) { + ExtensionDownloaderDelegate::FailureData failure_data( + -net_error, + response_code > 0 ? base::Optional<int>(response_code) : base::nullopt, + manifests_queue_.active_request_failure_count()); + ReportManifestFetchFailure(fetch_data, error, failure_data); + return; + } + DCHECK(manifest_invalid_errors); + ManifestInvalidErrorList errors_for_remaining_extensions; + for (const auto& manifest_invalid_error : manifest_invalid_errors.value()) { + if (!extensions_fetched_from_cache.count(manifest_invalid_error.first)) + errors_for_remaining_extensions.push_back(manifest_invalid_error); + } + NotifyExtensionsDownloadStageChanged( + fetch_data->GetExtensionIds(), + ExtensionDownloaderDelegate::Stage::FINISHED); + NotifyExtensionsManifestInvalidFailure(errors_for_remaining_extensions, + fetch_data->request_ids()); +} + +void ExtensionDownloader::RetryRequestOrHandleFailureOnManifestFetchFailure( + const network::SimpleURLLoader* loader, + const int response_code) { + bool all_force_installed_extensions = + manifests_queue_.active_request()->is_all_external_policy_download(); + + const int net_error = manifest_loader_->NetError(); + const int request_failure_count = + manifests_queue_.active_request_failure_count(); + // If the device is offline, do not retry for force installed extensions, + // try installing it from cache. Try fetching from cache only on first attempt + // in this case, because we will retry the request only if there was no entry + // in cache corresponding to this extension and there is no point in trying to + // fetch extension from cache again. + if (net_error == net::ERR_INTERNET_DISCONNECTED && + all_force_installed_extensions && request_failure_count == 0) { + TryFetchingExtensionsFromCache( + manifests_queue_.active_request(), + ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED, net_error, + response_code, base::nullopt /*manifest_invalid_errors*/); + return; + } + if (ShouldRetryRequest(loader) && request_failure_count < kMaxRetries) { + RetryManifestFetchRequest(); + return; + } + const GURL url = loader->GetFinalURL(); + RETRY_HISTOGRAM("ManifestFetchFailure", request_failure_count, url); + if (all_force_installed_extensions) { + TryFetchingExtensionsFromCache( + manifests_queue_.active_request(), + ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED, net_error, + response_code, base::nullopt /*manifest_invalid_errors*/); + } else { + ExtensionDownloaderDelegate::FailureData failure_data( + -net_error, + response_code > 0 ? base::Optional<int>(response_code) : base::nullopt, + request_failure_count); + ReportManifestFetchFailure( + manifests_queue_.active_request(), + ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED, + failure_data); + } +} + void ExtensionDownloader::OnManifestLoadComplete( std::unique_ptr<std::string> response_body) { const GURL url = manifest_loader_->GetFinalURL(); - int net_error = manifest_loader_->NetError(); DCHECK(manifests_queue_.active_request()); int response_code = -1; @@ -618,7 +771,6 @@ void ExtensionDownloader::OnManifestLoadComplete( VLOG(2) << response_code << " " << url; - const base::TimeDelta& backoff_delay = base::TimeDelta::FromMilliseconds(0); const int request_failure_count = manifests_queue_.active_request_failure_count(); @@ -628,7 +780,7 @@ void ExtensionDownloader::OnManifestLoadComplete( RETRY_HISTOGRAM("ManifestFetchSuccess", request_failure_count, url); VLOG(2) << "beginning manifest parse for " << url; NotifyExtensionsDownloadStageChanged( - manifests_queue_.active_request()->extension_ids(), + manifests_queue_.active_request()->GetExtensionIds(), ExtensionDownloaderDelegate::Stage::PARSING_MANIFEST); auto callback = base::BindOnce(&ExtensionDownloader::HandleManifestResults, weak_ptr_factory_.GetWeakPtr(), @@ -637,30 +789,8 @@ void ExtensionDownloader::OnManifestLoadComplete( } else { VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec() << "' response code:" << response_code; - const auto* loader = manifest_loader_.get(); - if (ShouldRetryRequest(loader) && request_failure_count < kMaxRetries) { - NotifyExtensionsDownloadStageChanged( - manifests_queue_.active_request()->extension_ids(), - ExtensionDownloaderDelegate::Stage::DOWNLOADING_MANIFEST_RETRY); - manifests_queue_.RetryRequest(backoff_delay); - } else { - RETRY_HISTOGRAM("ManifestFetchFailure", - manifests_queue_.active_request_failure_count(), - url); - NotifyExtensionsDownloadStageChanged( - manifests_queue_.active_request()->extension_ids(), - ExtensionDownloaderDelegate::Stage::FINISHED); - ExtensionDownloaderDelegate::FailureData failure_data( - -net_error, - response_code > 0 ? base::Optional<int>(response_code) - : base::nullopt, - manifests_queue_.active_request_failure_count()); - NotifyExtensionsDownloadFailedWithFailureData( - manifests_queue_.active_request()->extension_ids(), - manifests_queue_.active_request()->request_ids(), - ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED, - failure_data); - } + RetryRequestOrHandleFailureOnManifestFetchFailure(manifest_loader_.get(), + response_code); } manifest_loader_.reset(); file_url_loader_factory_.reset(); @@ -678,18 +808,17 @@ void ExtensionDownloader::HandleManifestResults( VLOG(2) << "parsing manifest failed (" << fetch_data->full_url() << ")"; DCHECK(error.has_value()); ManifestInvalidErrorList manifest_invalid_errors; - manifest_invalid_errors.reserve(fetch_data->extension_ids().size()); + const ExtensionIdSet extension_ids = fetch_data->GetExtensionIds(); + manifest_invalid_errors.reserve(extension_ids.size()); // If the manifest parsing failed for all the extensions with a common // error, add all extensions in the list with that error. - for (const auto& extension_id : fetch_data->extension_ids()) { + for (const auto& extension_id : extension_ids) { manifest_invalid_errors.push_back( std::make_pair(extension_id, error.value().error)); } - NotifyExtensionsDownloadStageChanged( - fetch_data->extension_ids(), - ExtensionDownloaderDelegate::Stage::FINISHED); - NotifyExtensionsManifestInvalidFailure(manifest_invalid_errors, - fetch_data->request_ids()); + TryFetchingExtensionsFromCache( + fetch_data.get(), ExtensionDownloaderDelegate::Error::MANIFEST_INVALID, + 0 /*net_error_code*/, 0 /*response_code*/, manifest_invalid_errors); return; } else { VLOG(2) << "parsing manifest succeeded (" << fetch_data->full_url() << ")"; @@ -698,9 +827,9 @@ void ExtensionDownloader::HandleManifestResults( // Report manifest update check status. NotifyExtensionManifestUpdateCheckStatus(results->update_list); + const ExtensionIdSet extension_ids = fetch_data->GetExtensionIds(); NotifyExtensionsDownloadStageChanged( - fetch_data->extension_ids(), - ExtensionDownloaderDelegate::Stage::MANIFEST_LOADED); + extension_ids, ExtensionDownloaderDelegate::Stage::MANIFEST_LOADED); std::vector<UpdateManifestResult*> to_update; std::set<std::string> no_updates; @@ -713,9 +842,14 @@ void ExtensionDownloader::HandleManifestResults( GURL crx_url = update->crx_url; NotifyUpdateFound(extension_id, update->version); + if (fetch_data->is_all_external_policy_download() && crx_url.is_empty()) { + DCHECK_EQ(fetch_data->fetch_priority(), + ManifestFetchData::FetchPriority::FOREGROUND); + } FetchUpdatedExtension(std::make_unique<ExtensionFetch>( - update->extension_id, crx_url, update->package_hash, update->version, - fetch_data->request_ids())); + extension_id, crx_url, update->package_hash, + update->version, fetch_data->request_ids()), + update->info); } // If the manifest response included a <daystart> element, we want to save @@ -725,7 +859,7 @@ void ExtensionDownloader::HandleManifestResults( Time day_start = Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds); - for (const std::string& id : fetch_data->extension_ids()) { + for (const ExtensionId& id : extension_ids) { ExtensionDownloaderDelegate::PingResult& result = ping_results_[id]; result.did_ping = fetch_data->DidPing(id, ManifestFetchData::ROLLCALL); result.day_start = day_start; @@ -843,9 +977,10 @@ void ExtensionDownloader::DetermineUpdates( // are already inserted into |errors|. ExtensionIdSet extension_errors; + const ExtensionIdSet extension_ids = fetch_data.GetExtensionIds(); // For each extensions in the current batch, greedily find an update from // |possible_updates|. - for (const std::string& extension_id : fetch_data.extension_ids()) { + for (const auto& extension_id : extension_ids) { const auto it = update_groups.find(extension_id); if (it == update_groups.end()) { VLOG(2) << "Manifest doesn't have an update entry for " << extension_id; @@ -893,7 +1028,8 @@ void ExtensionDownloader::DetermineUpdates( } base::Optional<base::FilePath> ExtensionDownloader::GetCachedExtension( - const ExtensionFetch& fetch_data) { + const ExtensionFetch& fetch_data, + bool manifest_fetch_failed) { if (!extension_cache_) { delegate_->OnExtensionDownloadCacheStatusRetrieved( fetch_data.id, @@ -908,8 +1044,9 @@ base::Optional<base::FilePath> ExtensionDownloader::GetCachedExtension( fetch_data.id, ExtensionDownloaderDelegate::CacheStatus::CACHE_MISS); return base::nullopt; } - - if (version != fetch_data.version) { + // If manifest fetch is failed, we need not verify the version of the cache as + // we will try to install the version present in the cache. + if (!manifest_fetch_failed && version != fetch_data.version) { delegate_->OnExtensionDownloadCacheStatusRetrieved( fetch_data.id, ExtensionDownloaderDelegate::CacheStatus::CACHE_OUTDATED); @@ -917,7 +1054,10 @@ base::Optional<base::FilePath> ExtensionDownloader::GetCachedExtension( } delegate_->OnExtensionDownloadCacheStatusRetrieved( - fetch_data.id, ExtensionDownloaderDelegate::CacheStatus::CACHE_HIT); + fetch_data.id, manifest_fetch_failed + ? ExtensionDownloaderDelegate::CacheStatus:: + CACHE_HIT_ON_MANIFEST_FETCH_FAILURE + : ExtensionDownloaderDelegate::CacheStatus::CACHE_HIT); base::FilePath crx_path; // Now get .crx file path. @@ -931,7 +1071,8 @@ base::Optional<base::FilePath> ExtensionDownloader::GetCachedExtension( // Begins (or queues up) download of an updated extension. void ExtensionDownloader::FetchUpdatedExtension( - std::unique_ptr<ExtensionFetch> fetch_data) { + std::unique_ptr<ExtensionFetch> fetch_data, + base::Optional<std::string> info) { if (!fetch_data->url.is_valid()) { // TODO(asargent): This can sometimes be invalid. See crbug.com/130881. DLOG(WARNING) << "Invalid URL: '" << fetch_data->url.possibly_invalid_spec() @@ -939,9 +1080,12 @@ void ExtensionDownloader::FetchUpdatedExtension( delegate_->OnExtensionDownloadStageChanged( fetch_data->id, ExtensionDownloaderDelegate::Stage::FINISHED); if (fetch_data->url.is_empty()) { - NotifyExtensionsDownloadFailed( + // We expect to receive initialised |info| from the manifest parser in + // case of no updates status in the update manifest. + ExtensionDownloaderDelegate::FailureData data(info.value_or("")); + NotifyExtensionsDownloadFailedWithFailureData( {fetch_data->id}, fetch_data->request_ids, - ExtensionDownloaderDelegate::Error::CRX_FETCH_URL_EMPTY); + ExtensionDownloaderDelegate::Error::CRX_FETCH_URL_EMPTY, data); } else { NotifyExtensionsDownloadFailed( {fetch_data->id}, fetch_data->request_ids, @@ -971,7 +1115,7 @@ void ExtensionDownloader::FetchUpdatedExtension( return; } base::Optional<base::FilePath> cached_crx_path = - GetCachedExtension(*fetch_data); + GetCachedExtension(*fetch_data, /*manifest_fetch_failed*/ false); if (cached_crx_path) { delegate_->OnExtensionDownloadStageChanged( fetch_data->id, ExtensionDownloaderDelegate::Stage::FINISHED); @@ -1002,7 +1146,9 @@ void ExtensionDownloader::NotifyDelegateDownloadFinished( CRXFileInfo crx_info(crx_path, required_format); crx_info.expected_hash = package_hash; crx_info.extension_id = id; - crx_info.expected_version = version; + // TODO(https://crbug.com/1076376): Change |version| in ExtensionFetch from + // std::string to base::Version. + crx_info.expected_version = base::Version(version); delegate_->OnExtensionDownloadFinished( crx_info, file_ownership_passed, url, ping_results_[id], request_ids, from_cache ? base::BindRepeating(&ExtensionDownloader::CacheInstallDone, @@ -1140,19 +1286,8 @@ void ExtensionDownloader::OnExtensionLoadComplete(base::FilePath crx_path) { extensions_queue_.reset_active_request(); delegate_->OnExtensionDownloadStageChanged( id, ExtensionDownloaderDelegate::Stage::FINISHED); - if (extension_cache_) { - const std::string& version = fetch_data->version; - const std::string& expected_hash = fetch_data->package_hash; - extension_cache_->PutExtension( - id, expected_hash, crx_path, version, - base::BindRepeating( - &ExtensionDownloader::NotifyDelegateDownloadFinished, - weak_ptr_factory_.GetWeakPtr(), base::Passed(&fetch_data), - false)); - } else { - NotifyDelegateDownloadFinished(std::move(fetch_data), false, crx_path, - true); - } + NotifyDelegateDownloadFinished(std::move(fetch_data), false, crx_path, + true); } else if (IterateFetchCredentialsAfterFailure(&active_request, response_code)) { delegate_->OnExtensionDownloadStageChanged( @@ -1174,8 +1309,6 @@ void ExtensionDownloader::OnExtensionLoadComplete(base::FilePath crx_path) { RETRY_HISTOGRAM("CrxFetchFailure", extensions_queue_.active_request_failure_count(), url); - // net_error is 0 (net::OK) or negative. (See net/base/net_errors.h) - base::UmaHistogramSparse("Extensions.CrxFetchError", -net_error); delegate_->OnExtensionDownloadStageChanged( id, ExtensionDownloaderDelegate::Stage::FINISHED); ExtensionDownloaderDelegate::FailureData failure_data( diff --git a/chromium/extensions/browser/updater/extension_downloader.h b/chromium/extensions/browser/updater/extension_downloader.h index e2059b9a3ad..56ac50ddcab 100644 --- a/chromium/extensions/browser/updater/extension_downloader.h +++ b/chromium/extensions/browser/updater/extension_downloader.h @@ -144,6 +144,11 @@ class ExtensionDownloader { ping_enabled_domain_ = domain; } + // Set backoff policy for manifest queue for testing with less initial delay + // so the tests do not timeout on retries. + void SetBackoffPolicyForTesting( + const net::BackoffEntry::Policy* backoff_policy); + // Sets a test delegate to use by any instances of this class. The |delegate| // should outlive all instances. static void set_test_delegate(ExtensionDownloaderTestDelegate* delegate); @@ -229,6 +234,28 @@ class ExtensionDownloader { ExtraParams(); }; + // We limit the number of extensions grouped together in one batch to avoid + // running into the limits on the length of http GET requests, this represents + // the key for grouping these extensions. + struct FetchDataGroupKey { + FetchDataGroupKey(); + FetchDataGroupKey(const FetchDataGroupKey& other); + FetchDataGroupKey(const int request_id, + const GURL& update_url, + const bool is_force_installed); + ~FetchDataGroupKey(); + + bool operator<(const FetchDataGroupKey& other) const; + + int request_id{0}; + GURL update_url; + // The extensions in current ManifestFetchData are all force installed + // (Manifest::Location::EXTERNAL_POLICY_DOWNLOAD) or not. In a + // ManifestFetchData we would have either all the extensions as force + // installed or we would none extensions as force installed. + bool is_force_installed{false}; + }; + enum class UpdateAvailability { kAvailable, kNoUpdate, @@ -258,6 +285,33 @@ class ExtensionDownloader { // Called by RequestQueue when a new manifest load request is started. void CreateManifestLoader(); + // Retries the active request with some backoff delay. + void RetryManifestFetchRequest(); + + // Reports failures if we failed to fetch the manifest or the fetched manifest + // was invalid. + void ReportManifestFetchFailure( + ManifestFetchData* fetch_data, + ExtensionDownloaderDelegate::Error error, + const ExtensionDownloaderDelegate::FailureData& data); + + // Tries fetching the extension from cache if manifest fetch is failed for + // force installed extensions, and notifies the failure reason for remaining + // extensions. + void TryFetchingExtensionsFromCache( + ManifestFetchData* fetch_data, + ExtensionDownloaderDelegate::Error error, + const int net_error, + const int response_code, + const base::Optional<ManifestInvalidErrorList>& manifest_invalid_errors); + + // Makes a retry attempt, reports failure by calling + // AddFailureDataOnManifestFetchFailed when fetching of update manifest + // failed. + void RetryRequestOrHandleFailureOnManifestFetchFailure( + const network::SimpleURLLoader* loader, + const int response_code); + // Handles the result of a manifest fetch. void OnManifestLoadComplete(std::unique_ptr<std::string> response_body); @@ -283,12 +337,18 @@ class ExtensionDownloader { ManifestInvalidErrorList* errors); // Checks whether extension is presented in cache. If yes, return path to its - // cached CRX, base::nullopt otherwise. + // cached CRX, base::nullopt otherwise. |manifest_fetch_failed| flag indicates + // whether the lookup in cache is performed after the manifest is fetched or + // due to failure while fetching or parsing manifest. base::Optional<base::FilePath> GetCachedExtension( - const ExtensionFetch& fetch_data); + const ExtensionFetch& fetch_data, + bool manifest_fetch_failed); - // Begins (or queues up) download of an updated extension. - void FetchUpdatedExtension(std::unique_ptr<ExtensionFetch> fetch_data); + // Begins (or queues up) download of an updated extension. |info| represents + // additional information about the extension update from the info field in + // the update manifest. + void FetchUpdatedExtension(std::unique_ptr<ExtensionFetch> fetch_data, + base::Optional<std::string> info); // Called by RequestQueue when a new extension load request is started. void CreateExtensionLoader(); @@ -388,13 +448,11 @@ class ExtensionDownloader { // Collects UMA samples that are reported when ReportStats() is called. URLStats url_stats_; - // List of data on fetches we're going to do. We limit the number of - // extensions grouped together in one batch to avoid running into the limits - // on the length of http GET requests, so there might be multiple - // ManifestFetchData* objects with the same base_url. - using FetchMap = std::map<std::pair<int, GURL>, - std::vector<std::unique_ptr<ManifestFetchData>>>; - FetchMap fetches_preparing_; + // We limit the number of extensions grouped together in one batch to avoid + // running into the limits on the length of http GET requests, so there might + // be multiple ManifestFetchData* objects with the same update_url. + std::map<FetchDataGroupKey, std::vector<std::unique_ptr<ManifestFetchData>>> + fetches_preparing_; // Outstanding url loader requests for manifests and updates. std::unique_ptr<network::SimpleURLLoader> manifest_loader_; diff --git a/chromium/extensions/browser/updater/extension_downloader_delegate.cc b/chromium/extensions/browser/updater/extension_downloader_delegate.cc index dc7dd473ca8..02d98fe0a38 100644 --- a/chromium/extensions/browser/updater/extension_downloader_delegate.cc +++ b/chromium/extensions/browser/updater/extension_downloader_delegate.cc @@ -32,6 +32,10 @@ ExtensionDownloaderDelegate::FailureData::FailureData( ManifestInvalidError manifest_invalid_error) : manifest_invalid_error(manifest_invalid_error) {} +ExtensionDownloaderDelegate::FailureData::FailureData( + const std::string& additional_info) + : additional_info(additional_info) {} + ExtensionDownloaderDelegate::FailureData::~FailureData() = default; ExtensionDownloaderDelegate::~ExtensionDownloaderDelegate() = default; diff --git a/chromium/extensions/browser/updater/extension_downloader_delegate.h b/chromium/extensions/browser/updater/extension_downloader_delegate.h index 84d7803b7a0..0ba28d76a3b 100644 --- a/chromium/extensions/browser/updater/extension_downloader_delegate.h +++ b/chromium/extensions/browser/updater/extension_downloader_delegate.h @@ -124,9 +124,13 @@ class ExtensionDownloaderDelegate { // Cache entry is good and will be used. CACHE_HIT = 4, + // Cache entry will be used in case we fail to fetch the manifest for the + // extension. + CACHE_HIT_ON_MANIFEST_FETCH_FAILURE = 5, + // Magic constant used by the histogram macros. // Always update it to the max value. - kMaxValue = CACHE_HIT, + kMaxValue = CACHE_HIT_ON_MANIFEST_FETCH_FAILURE, }; // Passed as an argument to the completion callbacks to signal whether @@ -143,8 +147,9 @@ class ExtensionDownloaderDelegate { base::Time day_start; }; - // Contains the error codes when Force installed extension fail to install - // with error CRX_FETCH_FAILED, MANIFEST_FETCH_FAILED or MANIFEST_INVALID. + // Additional information in case of force installed extension install failure + // due to CRX_FETCH_FAILED, MANIFEST_FETCH_FAILED, MANIFEST_INVALID, + // CRX_FETCH_URL_EMPTY. struct FailureData { FailureData(); FailureData(const FailureData& other); @@ -153,6 +158,7 @@ class ExtensionDownloaderDelegate { const base::Optional<int> response, const int fetch_attempts); explicit FailureData(ManifestInvalidError manifest_invalid_error); + explicit FailureData(const std::string& additional_info); ~FailureData(); // Network error code in case of CRX_FETCH_FAILED or MANIFEST_FETCH_FAILED. @@ -166,6 +172,10 @@ class ExtensionDownloaderDelegate { // errors occurred while parsing the update manifest and the errors in the // internal details of the parsed manifest. const base::Optional<ManifestInvalidError> manifest_invalid_error; + // Info field in the update manifest returned by the server. Currently it is + // only set when no update is available and install fails with the error + // CRX_FETCH_URL_EMPTY. + const base::Optional<std::string> additional_info; }; // A callback that is called to indicate if ExtensionDownloader should ignore diff --git a/chromium/extensions/browser/updater/extension_installer.cc b/chromium/extensions/browser/updater/extension_installer.cc index ce88e3aeb23..60a074809fa 100644 --- a/chromium/extensions/browser/updater/extension_installer.cc +++ b/chromium/extensions/browser/updater/extension_installer.cc @@ -12,7 +12,6 @@ #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" -#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "components/update_client/update_client_errors.h" #include "content/public/browser/browser_task_traits.h" @@ -45,8 +44,7 @@ void ExtensionInstaller::Install( std::unique_ptr<InstallParams> /*install_params*/, ProgressCallback /*progress_callback*/, UpdateClientCallback update_client_callback) { - auto ui_thread = - base::CreateSingleThreadTaskRunner({content::BrowserThread::UI}); + auto ui_thread = content::GetUIThreadTaskRunner({}); DCHECK(ui_thread); DCHECK(!extension_installer_callback_.is_null()); if (base::PathExists(unpack_path)) { diff --git a/chromium/extensions/browser/updater/manifest_fetch_data.cc b/chromium/extensions/browser/updater/manifest_fetch_data.cc index 1246974885a..afb8318e619 100644 --- a/chromium/extensions/browser/updater/manifest_fetch_data.cc +++ b/chromium/extensions/browser/updater/manifest_fetch_data.cc @@ -46,6 +46,23 @@ void AddEnabledStateToPing(std::string* ping_value, } // namespace +ManifestFetchData::ExtensionData::ExtensionData() : version(base::Version()) {} + +ManifestFetchData::ExtensionData::ExtensionData(const ExtensionData& other) = + default; + +ManifestFetchData::ExtensionData::ExtensionData( + const base::Version& version, + const std::string& update_url_data, + const std::string& install_source, + Manifest::Location extension_location) + : version(version), + update_url_data(update_url_data), + install_source(install_source), + extension_location(extension_location) {} + +ManifestFetchData::ExtensionData::~ExtensionData() = default; + // static std::string ManifestFetchData::GetSimpleLocationString(Manifest::Location loc) { std::string result = kInvalidLocation; @@ -87,14 +104,9 @@ ManifestFetchData::ManifestFetchData(const GURL& update_url, full_url_(update_url), brand_code_(brand_code), ping_mode_(ping_mode), - fetch_priority_(fetch_priority) { - std::string query = - full_url_.has_query() ? full_url_.query() + "&" : std::string(); - query += base_query_params; - GURL::Replacements replacements; - replacements.SetQueryStr(query); - full_url_ = full_url_.ReplaceComponents(replacements); - + fetch_priority_(fetch_priority), + is_all_external_policy_download_(false) { + UpdateFullUrl(base_query_params); request_ids_.insert(request_id); } @@ -129,7 +141,9 @@ bool ManifestFetchData::AddExtension(const std::string& id, const std::string& install_source, Manifest::Location extension_location, FetchPriority fetch_priority) { - if (extension_ids_.find(id) != extension_ids_.end()) { + DCHECK(!is_all_external_policy_download_ || + extension_location == Manifest::Location::EXTERNAL_POLICY_DOWNLOAD); + if (extensions_data_.find(id) != extensions_data_.end()) { NOTREACHED() << "Duplicate extension id " << id; return false; } @@ -192,20 +206,60 @@ bool ManifestFetchData::AddExtension(const std::string& id, // Check against our max url size, exempting the first extension added. int new_size = full_url_.possibly_invalid_spec().size() + extra.size(); - if (!extension_ids_.empty() && new_size > kExtensionsManifestMaxURLSize) { + if (!extensions_data_.empty() && new_size > kExtensionsManifestMaxURLSize) { UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1); return false; } UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0); // We have room so go ahead and add the extension. - extension_ids_.insert(id); + extensions_data_[id] = ExtensionData(base::Version(version), update_url_data, + install_source, extension_location); full_url_ = GURL(full_url_.possibly_invalid_spec() + extra); return true; } +void ManifestFetchData::UpdateFullUrl(const std::string& base_query_params) { + std::string query = + full_url_.has_query() ? full_url_.query() + "&" : std::string(); + query += base_query_params; + GURL::Replacements replacements; + replacements.SetQueryStr(query); + full_url_ = full_url_.ReplaceComponents(replacements); +} + +void ManifestFetchData::RemoveExtensions(const ExtensionIdSet& id_to_remove, + const std::string& base_query_params) { + const std::map<ExtensionId, ExtensionData> extensions_data = + std::move(extensions_data_); + extensions_data_.clear(); + full_url_ = base_url_; + UpdateFullUrl(base_query_params); + + for (const auto& data : extensions_data) { + const ExtensionId& extension_id = data.first; + if (id_to_remove.count(extension_id)) + continue; + const ExtensionData& extension_data = data.second; + auto it = pings_.find(extension_id); + const PingData* optional_ping_data = + it != pings_.end() ? &(it->second) : nullptr; + AddExtension(extension_id, extension_data.version.GetString(), + optional_ping_data, extension_data.update_url_data, + extension_data.install_source, + extension_data.extension_location, fetch_priority_); + } +} + +ExtensionIdSet ManifestFetchData::GetExtensionIds() const { + ExtensionIdSet extension_ids; + for (const auto& extension_data : extensions_data_) + extension_ids.insert(extension_data.first); + return extension_ids; +} + bool ManifestFetchData::Includes(const std::string& extension_id) const { - return extension_ids_.find(extension_id) != extension_ids_.end(); + return extensions_data_.find(extension_id) != extensions_data_.end(); } bool ManifestFetchData::DidPing(const std::string& extension_id, @@ -231,4 +285,8 @@ void ManifestFetchData::Merge(const ManifestFetchData& other) { request_ids_.insert(other.request_ids_.begin(), other.request_ids_.end()); } +void ManifestFetchData::set_is_all_external_policy_download() { + is_all_external_policy_download_ = true; +} + } // namespace extensions diff --git a/chromium/extensions/browser/updater/manifest_fetch_data.h b/chromium/extensions/browser/updater/manifest_fetch_data.h index b183e2e5833..0bbb0132f0b 100644 --- a/chromium/extensions/browser/updater/manifest_fetch_data.h +++ b/chromium/extensions/browser/updater/manifest_fetch_data.h @@ -10,6 +10,7 @@ #include <string> #include "base/macros.h" +#include "base/version.h" #include "extensions/common/manifest.h" #include "url/gurl.h" @@ -108,14 +109,22 @@ class ManifestFetchData { const GURL& base_url() const { return base_url_; } const GURL& full_url() const { return full_url_; } - const std::set<std::string>& extension_ids() const { return extension_ids_; } + ExtensionIdSet GetExtensionIds() const; const std::set<int>& request_ids() const { return request_ids_; } bool foreground_check() const { return fetch_priority_ == FOREGROUND; } FetchPriority fetch_priority() const { return fetch_priority_; } + bool is_all_external_policy_download() const { + return is_all_external_policy_download_; + } // Returns true if the given id is included in this manifest fetch. bool Includes(const std::string& extension_id) const; + // Resets the full url to base url and removes |id_to_remove| from + // the ManifestFetchData. + void RemoveExtensions(const ExtensionIdSet& id_to_remove, + const std::string& base_query_params); + // Returns true if a ping parameter for |type| was added to full_url for this // extension id. bool DidPing(const std::string& extension_id, PingType type) const; @@ -126,9 +135,36 @@ class ManifestFetchData { // to this ManifestFetchData). void Merge(const ManifestFetchData& other); + // Assigns true if all the extensions are force installed. + void set_is_all_external_policy_download(); + private: - // The set of extension id's for this ManifestFetchData. - std::set<std::string> extension_ids_; + // Contains supplementary data needed to construct update manifest fetch + // query. + struct ExtensionData { + ExtensionData(); + ExtensionData(const ExtensionData& other); + ExtensionData(const base::Version& version, + const std::string& update_url_data, + const std::string& install_source, + Manifest::Location extension_location); + + ~ExtensionData(); + base::Version version; + std::string update_url_data; + std::string install_source; + Manifest::Location extension_location{Manifest::Location::INTERNAL}; + }; + + const std::map<ExtensionId, ExtensionData>& extensions_data() const { + return extensions_data_; + } + + // Appends query parameters to the full url if any. + void UpdateFullUrl(const std::string& base_query_params); + + // The set of extension data for each extension. + std::map<std::string, ExtensionData> extensions_data_; // The set of ping data we actually sent. std::map<std::string, PingData> pings_; @@ -157,6 +193,10 @@ class ManifestFetchData { // The priority of the update. FetchPriority fetch_priority_; + // The flag is set to true if all the extensions are force installed + // extensions. + bool is_all_external_policy_download_; + DISALLOW_COPY_AND_ASSIGN(ManifestFetchData); }; diff --git a/chromium/extensions/browser/updater/update_data_provider.cc b/chromium/extensions/browser/updater/update_data_provider.cc index ee6f6b221bd..ead0cac88c5 100644 --- a/chromium/extensions/browser/updater/update_data_provider.cc +++ b/chromium/extensions/browser/updater/update_data_provider.cc @@ -12,7 +12,6 @@ #include "base/files/file_util.h" #include "base/optional.h" #include "base/strings/string_util.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "components/crx_file/crx_verifier.h" #include "components/update_client/utils.h" @@ -154,23 +153,20 @@ void UpdateDataProvider::RunInstallCallback( if (!browser_context_) { base::ThreadPool::PostTask( FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()}, - base::BindOnce(base::IgnoreResult(&base::DeleteFile), unpacked_dir, - true)); - base::CreateSingleThreadTaskRunner({content::BrowserThread::UI}) - ->PostTask( - FROM_HERE, - base::BindOnce(std::move(update_client_callback), - update_client::CrxInstaller::Result( - update_client::InstallError::GENERIC_ERROR))); + base::BindOnce(base::GetDeletePathRecursivelyCallback(), unpacked_dir)); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(std::move(update_client_callback), + update_client::CrxInstaller::Result( + update_client::InstallError::GENERIC_ERROR))); return; } - base::CreateSingleThreadTaskRunner({content::BrowserThread::UI}) - ->PostTask( - FROM_HERE, - base::BindOnce(InstallUpdateCallback, browser_context_, extension_id, - public_key, unpacked_dir, install_immediately, - std::move(update_client_callback))); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(InstallUpdateCallback, browser_context_, extension_id, + public_key, unpacked_dir, install_immediately, + std::move(update_client_callback))); } } // namespace extensions diff --git a/chromium/extensions/browser/updater/update_service.cc b/chromium/extensions/browser/updater/update_service.cc index 2c57317abe9..2b62acd4ae9 100644 --- a/chromium/extensions/browser/updater/update_service.cc +++ b/chromium/extensions/browser/updater/update_service.cc @@ -135,14 +135,15 @@ void UpdateService::OnEvent(Events event, const std::string& extension_id) { break; } - base::Value attributes(base::Value::Type::DICTIONARY); - if (should_perform_action_on_omaha_attributes && - base::FeatureList::IsEnabled( - extensions_features::kDisableMalwareExtensionsRemotely)) { - attributes = GetExtensionOmahaAttributes(extension_id); + if (should_perform_action_on_omaha_attributes) { + base::Value attributes(base::Value::Type::DICTIONARY); + if (base::FeatureList::IsEnabled( + extensions_features::kDisableMalwareExtensionsRemotely)) { + attributes = GetExtensionOmahaAttributes(extension_id); + } + ExtensionSystem::Get(browser_context_) + ->PerformActionBasedOnOmahaAttributes(extension_id, attributes); } - ExtensionSystem::Get(browser_context_) - ->PerformActionBasedOnOmahaAttributes(extension_id, attributes); } UpdateService::UpdateService( diff --git a/chromium/extensions/browser/updater/update_service_unittest.cc b/chromium/extensions/browser/updater/update_service_unittest.cc index 8b17ff6a9d8..1dd9e3cc094 100644 --- a/chromium/extensions/browser/updater/update_service_unittest.cc +++ b/chromium/extensions/browser/updater/update_service_unittest.cc @@ -497,6 +497,36 @@ TEST_F(UpdateServiceTest, UninstallPings) { } } +TEST_F(UpdateServiceTest, NoPerformAction) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + extensions_features::kDisableMalwareExtensionsRemotely); + std::string extension_id = "lpcaedmchfhocbbapmcbpinfpgnhiddi"; + ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context()); + scoped_refptr<const Extension> extension1 = + ExtensionBuilder("1").SetVersion("1.2").SetID(extension_id).Build(); + EXPECT_TRUE(registry->AddEnabled(extension1)); + + update_client()->set_is_malware_update_item(); + update_client()->set_delay_update(); + + ExtensionUpdateCheckParams update_check_params; + update_check_params.update_info[extension_id] = ExtensionUpdateData(); + + bool executed = false; + update_service()->StartUpdateCheck( + update_check_params, + base::BindOnce([](bool* executed) { *executed = true; }, &executed)); + EXPECT_FALSE(executed); + + const auto& request = update_client()->update_request(0); + EXPECT_THAT(request.extension_ids, testing::ElementsAre(extension_id)); + + update_client()->RunDelayedUpdate( + 0, UpdateClientEvents::COMPONENT_CHECKING_FOR_UPDATES); + EXPECT_FALSE(registry->disabled_extensions().GetByID(extension_id)); +} + TEST_F(UpdateServiceTest, CheckOmahaAttributes) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( |