// Copyright 2015 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 "components/update_client/action_update_check.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/location.h" #include "base/logging.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "base/version.h" #include "components/update_client/action_update.h" #include "components/update_client/configurator.h" #include "components/update_client/update_checker.h" #include "components/update_client/update_client.h" #include "components/update_client/utils.h" using std::string; using std::vector; namespace update_client { namespace { // Returns true if the |proposed| version is newer than |current| version. bool IsVersionNewer(const Version& current, const std::string& proposed) { Version proposed_ver(proposed); return proposed_ver.IsValid() && current.CompareTo(proposed_ver) < 0; } } // namespace ActionUpdateCheck::ActionUpdateCheck( scoped_ptr update_checker, const base::Version& browser_version, const std::string& extra_request_parameters) : update_checker_(update_checker.Pass()), browser_version_(browser_version), extra_request_parameters_(extra_request_parameters) { } ActionUpdateCheck::~ActionUpdateCheck() { DCHECK(thread_checker_.CalledOnValidThread()); } void ActionUpdateCheck::Run(UpdateContext* update_context, Callback callback) { DCHECK(thread_checker_.CalledOnValidThread()); ActionImpl::Run(update_context, callback); // Calls out to get the corresponding CrxComponent data for the CRXs in this // update context. vector crx_components; update_context_->crx_data_callback.Run(update_context_->ids, &crx_components); update_context_->update_items.reserve(crx_components.size()); for (size_t i = 0; i != crx_components.size(); ++i) { scoped_ptr item(new CrxUpdateItem); const CrxComponent& crx_component = crx_components[i]; item->id = GetCrxComponentID(crx_component); item->component = crx_component; item->last_check = base::Time::Now(); item->crx_urls.clear(); item->crx_diffurls.clear(); item->previous_version = crx_component.version; item->next_version = Version(); item->previous_fp = crx_component.fingerprint; item->next_fp.clear(); item->diff_update_failed = false; item->error_category = 0; item->error_code = 0; item->extra_code1 = 0; item->diff_error_category = 0; item->diff_error_code = 0; item->diff_extra_code1 = 0; item->download_metrics.clear(); ChangeItemState(item.get(), CrxUpdateItem::State::kChecking); update_context_->update_items.push_back(item.release()); } update_checker_->CheckForUpdates( update_context_->update_items, extra_request_parameters_, base::Bind(&ActionUpdateCheck::UpdateCheckComplete, base::Unretained(this))); } void ActionUpdateCheck::UpdateCheckComplete( const GURL& original_url, int error, const std::string& error_message, const UpdateResponse::Results& results) { DCHECK(thread_checker_.CalledOnValidThread()); VLOG(1) << "Update check completed from: " << original_url.spec(); if (!error) OnUpdateCheckSucceeded(results); else OnUpdateCheckFailed(error, error_message); } void ActionUpdateCheck::OnUpdateCheckSucceeded( const UpdateResponse::Results& results) { DCHECK(thread_checker_.CalledOnValidThread()); VLOG(1) << "Update check succeeded."; std::vector::const_iterator it; for (it = results.list.begin(); it != results.list.end(); ++it) { CrxUpdateItem* crx = FindUpdateItemById(it->extension_id); if (!crx) continue; if (crx->state != CrxUpdateItem::State::kChecking) { NOTREACHED(); continue; // Not updating this CRX now. } if (it->manifest.version.empty()) { // No version means no update available. ChangeItemState(crx, CrxUpdateItem::State::kNoUpdate); VLOG(1) << "No update available for CRX: " << crx->id; continue; } if (!IsVersionNewer(crx->component.version, it->manifest.version)) { // The CRX is up to date. ChangeItemState(crx, CrxUpdateItem::State::kUpToDate); VLOG(1) << "Component already up-to-date: " << crx->id; continue; } if (!it->manifest.browser_min_version.empty()) { if (IsVersionNewer(browser_version_, it->manifest.browser_min_version)) { // The CRX is not compatible with this Chrome version. VLOG(1) << "Ignoring incompatible CRX: " << crx->id; ChangeItemState(crx, CrxUpdateItem::State::kNoUpdate); continue; } } if (it->manifest.packages.size() != 1) { // Assume one and only one package per CRX. VLOG(1) << "Ignoring multiple packages for CRX: " << crx->id; ChangeItemState(crx, CrxUpdateItem::State::kNoUpdate); continue; } // Parse the members of the result and queue an upgrade for this CRX. crx->next_version = Version(it->manifest.version); VLOG(1) << "Update found for CRX: " << crx->id; const auto& package(it->manifest.packages[0]); crx->next_fp = package.fingerprint; // Resolve the urls by combining the base urls with the package names. for (size_t i = 0; i != it->crx_urls.size(); ++i) { const GURL url(it->crx_urls[i].Resolve(package.name)); if (url.is_valid()) crx->crx_urls.push_back(url); } for (size_t i = 0; i != it->crx_diffurls.size(); ++i) { const GURL url(it->crx_diffurls[i].Resolve(package.namediff)); if (url.is_valid()) crx->crx_diffurls.push_back(url); } ChangeItemState(crx, CrxUpdateItem::State::kCanUpdate); update_context_->queue.push(crx->id); } // All components that are not included in the update response are // considered up to date. ChangeAllItemsState(CrxUpdateItem::State::kChecking, CrxUpdateItem::State::kUpToDate); if (update_context_->queue.empty()) { VLOG(1) << "Update check completed but no update is needed."; UpdateComplete(0); return; } // Starts the execution flow of updating the CRXs in this context. UpdateCrx(); } void ActionUpdateCheck::OnUpdateCheckFailed(int error, const std::string& error_message) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(error); VLOG(1) << "Update check failed." << error; UpdateComplete(error); } } // namespace update_client