summaryrefslogtreecommitdiffstats
path: root/components/update_client
diff options
context:
space:
mode:
Diffstat (limited to 'components/update_client')
-rw-r--r--components/update_client/BUILD.gn15
-rw-r--r--components/update_client/action.cc165
-rw-r--r--components/update_client/action.h98
-rw-r--r--components/update_client/action_update.cc370
-rw-r--r--components/update_client/action_update.h138
-rw-r--r--components/update_client/action_update_check.cc204
-rw-r--r--components/update_client/action_update_check.h56
-rw-r--r--components/update_client/action_wait.cc61
-rw-r--r--components/update_client/action_wait.h36
-rw-r--r--components/update_client/component_patcher.cc2
-rw-r--r--components/update_client/component_patcher.h6
-rw-r--r--components/update_client/component_patcher_operation.cc8
-rw-r--r--components/update_client/component_patcher_operation.h12
-rw-r--r--components/update_client/component_unpacker.cc2
-rw-r--r--components/update_client/component_unpacker.h6
-rw-r--r--components/update_client/configurator.h13
-rw-r--r--components/update_client/crx_downloader.cc16
-rw-r--r--components/update_client/crx_downloader.h17
-rw-r--r--components/update_client/crx_update_item.h8
-rw-r--r--components/update_client/ping_manager.cc6
-rw-r--r--components/update_client/ping_manager.h4
-rw-r--r--components/update_client/task.h30
-rw-r--r--components/update_client/task_update.cc46
-rw-r--r--components/update_client/task_update.h52
-rw-r--r--components/update_client/test/DEPS2
-rw-r--r--components/update_client/test/crx_downloader_unittest.cc8
-rw-r--r--components/update_client/test/ping_manager_unittest.cc19
-rw-r--r--components/update_client/test/request_sender_unittest.cc9
-rw-r--r--components/update_client/test/test_configurator.cc4
-rw-r--r--components/update_client/test/test_configurator.h6
-rw-r--r--components/update_client/test/test_installer.h3
-rw-r--r--components/update_client/test/update_checker_unittest.cc10
-rw-r--r--components/update_client/test/update_client_unittest.cc1417
-rw-r--r--components/update_client/update_checker.h12
-rw-r--r--components/update_client/update_client.cc148
-rw-r--r--components/update_client/update_client.h285
-rw-r--r--components/update_client/update_client_internal.h88
-rw-r--r--components/update_client/update_engine.cc133
-rw-r--r--components/update_client/update_engine.h149
39 files changed, 3558 insertions, 106 deletions
diff --git a/components/update_client/BUILD.gn b/components/update_client/BUILD.gn
index c968b4d..17665721 100644
--- a/components/update_client/BUILD.gn
+++ b/components/update_client/BUILD.gn
@@ -4,6 +4,14 @@
source_set("update_client") {
sources = [
+ "action.cc",
+ "action.h",
+ "action_update.cc",
+ "action_update.h",
+ "action_update_check.cc",
+ "action_update_check.h",
+ "action_wait.cc",
+ "action_wait.h",
"background_downloader_win.cc",
"background_downloader_win.h",
"component_patcher.cc",
@@ -20,10 +28,16 @@ source_set("update_client") {
"ping_manager.h",
"request_sender.cc",
"request_sender.h",
+ "task.h",
+ "task_update.cc",
+ "task_update.h",
"update_checker.cc",
"update_checker.h",
"update_client.cc",
"update_client.h",
+ "update_client_internal.h",
+ "update_engine.cc",
+ "update_engine.h",
"update_query_params.cc",
"update_query_params.h",
"update_query_params_delegate.cc",
@@ -77,6 +91,7 @@ source_set("unit_tests") {
"test/ping_manager_unittest.cc",
"test/request_sender_unittest.cc",
"test/update_checker_unittest.cc",
+ "test/update_client_unittest.cc",
"test/update_response_unittest.cc",
]
diff --git a/components/update_client/action.cc b/components/update_client/action.cc
new file mode 100644
index 0000000..4a11efe
--- /dev/null
+++ b/components/update_client/action.cc
@@ -0,0 +1,165 @@
+// 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.h"
+
+#include <algorithm>
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "components/update_client/action_update.h"
+#include "components/update_client/action_wait.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/update_engine.h"
+#include "components/update_client/utils.h"
+
+namespace update_client {
+
+namespace {
+
+// Returns true if a differential update is available, it has not failed yet,
+// and the configuration allows this update.
+bool CanTryDiffUpdate(const CrxUpdateItem* update_item,
+ const scoped_refptr<Configurator>& config) {
+ return HasDiffUpdate(update_item) && !update_item->diff_update_failed &&
+ config->DeltasEnabled();
+}
+
+} // namespace
+
+ActionImpl::ActionImpl() : update_context_(nullptr) {
+}
+
+ActionImpl::~ActionImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void ActionImpl::Run(UpdateContext* update_context, Action::Callback callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ update_context_ = update_context;
+ callback_ = callback;
+}
+
+CrxUpdateItem* ActionImpl::FindUpdateItemById(const std::string& id) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ const auto it(std::find_if(
+ update_context_->update_items.begin(),
+ update_context_->update_items.end(),
+ [&id](const CrxUpdateItem* item) { return item->id == id; }));
+
+ return it != update_context_->update_items.end() ? *it : nullptr;
+}
+
+void ActionImpl::ChangeItemState(CrxUpdateItem* item, CrxUpdateItem::State to) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ item->state = to;
+
+ using Events = UpdateClient::Observer::Events;
+
+ const std::string& id(item->id);
+ switch (to) {
+ case CrxUpdateItem::State::kChecking:
+ NotifyObservers(Events::COMPONENT_CHECKING_FOR_UPDATES, id);
+ break;
+ case CrxUpdateItem::State::kCanUpdate:
+ NotifyObservers(Events::COMPONENT_UPDATE_FOUND, id);
+ break;
+ case CrxUpdateItem::State::kUpdatingDiff:
+ case CrxUpdateItem::State::kUpdating:
+ NotifyObservers(Events::COMPONENT_UPDATE_READY, id);
+ break;
+ case CrxUpdateItem::State::kUpdated:
+ NotifyObservers(Events::COMPONENT_UPDATED, id);
+ break;
+ case CrxUpdateItem::State::kUpToDate:
+ case CrxUpdateItem::State::kNoUpdate:
+ NotifyObservers(Events::COMPONENT_NOT_UPDATED, id);
+ break;
+ case CrxUpdateItem::State::kNew:
+ case CrxUpdateItem::State::kDownloading:
+ case CrxUpdateItem::State::kDownloadingDiff:
+ case CrxUpdateItem::State::kDownloaded:
+ case CrxUpdateItem::State::kLastStatus:
+ // No notification for these states.
+ break;
+ }
+}
+
+size_t ActionImpl::ChangeAllItemsState(CrxUpdateItem::State from,
+ CrxUpdateItem::State to) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ size_t count = 0;
+ for (auto item : update_context_->update_items) {
+ if (item->state == from) {
+ ChangeItemState(item, to);
+ ++count;
+ }
+ }
+ return count;
+}
+
+void ActionImpl::NotifyObservers(UpdateClient::Observer::Events event,
+ const std::string& id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ update_context_->notify_observers_callback.Run(event, id);
+}
+
+void ActionImpl::UpdateCrx() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!update_context_->queue.empty());
+
+ const std::string& id = update_context_->queue.front();
+ CrxUpdateItem* item = FindUpdateItemById(id);
+ DCHECK(item);
+
+ scoped_ptr<Action> update_action(
+ CanTryDiffUpdate(item, update_context_->config)
+ ? ActionUpdateDiff::Create()
+ : ActionUpdateFull::Create());
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&Action::Run, base::Unretained(update_action.get()),
+ update_context_, callback_));
+
+ update_context_->current_action.reset(update_action.release());
+}
+
+void ActionImpl::UpdateCrxComplete(CrxUpdateItem* item) {
+ update_context_->ping_manager->OnUpdateComplete(item);
+
+ update_context_->queue.pop();
+
+ if (update_context_->queue.empty()) {
+ UpdateComplete(0);
+ } else {
+ // TODO(sorin): the value of timing interval between CRX updates might have
+ // to be injected at the call site of update_client::UpdateClient::Update.
+ const int wait_sec = update_context_->config->UpdateDelay();
+
+ scoped_ptr<ActionWait> action_wait(
+ new ActionWait(base::TimeDelta::FromSeconds(wait_sec)));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&Action::Run, base::Unretained(action_wait.get()),
+ update_context_, callback_));
+
+ update_context_->current_action.reset(action_wait.release());
+ }
+}
+
+void ActionImpl::UpdateComplete(int error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::Bind(callback_, error));
+}
+
+} // namespace update_client
diff --git a/components/update_client/action.h b/components/update_client/action.h
new file mode 100644
index 0000000..d85e868
--- /dev/null
+++ b/components/update_client/action.h
@@ -0,0 +1,98 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_ACTION_H_
+#define COMPONENTS_UPDATE_CLIENT_ACTION_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/update_client.h"
+
+namespace update_client {
+
+class Configurator;
+struct CrxUpdateItem;
+struct UpdateContext;
+
+// Any update can be broken down as a sequence of discrete steps, such as
+// checking for updates, downloading patches, updating, and waiting between
+// successive updates. An action is the smallest unit of work executed by
+// the update engine.
+//
+// Defines an abstract interface for a unit of work, executed by the
+// update engine as part of an update.
+class Action {
+ public:
+ enum class ErrorCategory {
+ kErrorNone = 0,
+ kNetworkError,
+ kUnpackError,
+ kInstallError,
+ kServiceError, // Runtime errors which occur in the service itself.
+ };
+
+ enum class ServiceError {
+ ERROR_WAIT = 1,
+ };
+
+ using Callback = base::Callback<void(int error)>;
+ virtual ~Action() {}
+
+ // Runs the code encapsulated by the action. When an action completes, it can
+ // chain up and transfer the execution flow to another action or it can
+ // invoke the |callback| when this function has completed and there is nothing
+ // else to do.
+ virtual void Run(UpdateContext* update_context, Callback callback) = 0;
+};
+
+// Provides a reusable implementation of common functions needed by actions.
+class ActionImpl {
+ protected:
+ ActionImpl();
+ ~ActionImpl();
+
+ void Run(UpdateContext* update_context, Action::Callback callback);
+
+ // Changes the current state of the |item| to the new state |to|.
+ void ChangeItemState(CrxUpdateItem* item, CrxUpdateItem::State to);
+
+ // Changes the state of all items in |update_context_|. Returns the count
+ // of items affected by the call.
+ size_t ChangeAllItemsState(CrxUpdateItem::State from,
+ CrxUpdateItem::State to);
+
+ // Returns the item associated with the component |id| or nullptr in case
+ // of errors.
+ CrxUpdateItem* FindUpdateItemById(const std::string& id) const;
+
+ void NotifyObservers(UpdateClient::Observer::Events event,
+ const std::string& id);
+
+ // Updates the CRX at the front of the CRX queue in this update context.
+ void UpdateCrx();
+
+ // Completes updating the CRX at the front of the queue, and initiates
+ // the update for the next CRX in the queue, if the queue is not empty.
+ void UpdateCrxComplete(CrxUpdateItem* item);
+
+ // Called when the updates for all CRXs have finished and the execution
+ // flow must return back to the update engine.
+ void UpdateComplete(int error);
+
+ base::ThreadChecker thread_checker_;
+
+ UpdateContext* update_context_; // Not owned by this class.
+ Action::Callback callback_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ActionImpl);
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_ACTION_H_
diff --git a/components/update_client/action_update.cc b/components/update_client/action_update.cc
new file mode 100644
index 0000000..b43820b
--- /dev/null
+++ b/components/update_client/action_update.cc
@@ -0,0 +1,370 @@
+// 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.h"
+
+#include <vector>
+
+#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/time/time.h"
+#include "base/version.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/crx_downloader.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace update_client {
+
+namespace {
+
+void AppendDownloadMetrics(
+ const std::vector<CrxDownloader::DownloadMetrics>& source,
+ std::vector<CrxDownloader::DownloadMetrics>* destination) {
+ destination->insert(destination->end(), source.begin(), source.end());
+}
+
+Action::ErrorCategory UnpackerErrorToErrorCategory(
+ ComponentUnpacker::Error error) {
+ Action::ErrorCategory error_category = Action::ErrorCategory::kErrorNone;
+ switch (error) {
+ case ComponentUnpacker::kNone:
+ break;
+ case ComponentUnpacker::kInstallerError:
+ error_category = Action::ErrorCategory::kInstallError;
+ break;
+ default:
+ error_category = Action::ErrorCategory::kUnpackError;
+ break;
+ }
+ return error_category;
+}
+
+} // namespace
+
+ActionUpdate::ActionUpdate() {
+}
+
+ActionUpdate::~ActionUpdate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void ActionUpdate::Run(UpdateContext* update_context, Callback callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ActionImpl::Run(update_context, callback);
+
+ DCHECK(!update_context_->queue.empty());
+
+ const std::string& id = update_context_->queue.front();
+ CrxUpdateItem* item = FindUpdateItemById(id);
+ DCHECK(item);
+
+ const bool is_background_download(IsBackgroundDownload(item));
+
+ scoped_ptr<CrxDownloader> crx_downloader(
+ (*update_context_->crx_downloader_factory)(
+ is_background_download, update_context_->config->RequestContext(),
+ update_context_->blocking_task_runner,
+ update_context_->single_thread_task_runner));
+ crx_downloader->set_progress_callback(
+ base::Bind(&ActionUpdate::DownloadProgress, base::Unretained(this), id));
+ update_context_->crx_downloader.reset(crx_downloader.release());
+
+ const std::vector<GURL> urls(GetUrls(item));
+ OnDownloadStart(item);
+
+ update_context_->crx_downloader->StartDownload(
+ urls,
+ base::Bind(&ActionUpdate::DownloadComplete, base::Unretained(this), id));
+}
+
+void ActionUpdate::DownloadProgress(
+ const std::string& crx_id,
+ const CrxDownloader::Result& download_result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(crx_id == update_context_->queue.front());
+
+ using Events = UpdateClient::Observer::Events;
+ NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING, crx_id);
+}
+
+void ActionUpdate::DownloadComplete(
+ const std::string& crx_id,
+ const CrxDownloader::Result& download_result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(crx_id == update_context_->queue.front());
+
+ CrxUpdateItem* item = FindUpdateItemById(crx_id);
+ DCHECK(item);
+
+ AppendDownloadMetrics(update_context_->crx_downloader->download_metrics(),
+ &item->download_metrics);
+
+ if (download_result.error) {
+ OnDownloadError(item, download_result);
+ } else {
+ OnDownloadSuccess(item, download_result);
+ update_context_->main_task_runner->PostDelayedTask(
+ FROM_HERE, base::Bind(&ActionUpdate::Install, base::Unretained(this),
+ crx_id, download_result.response),
+ base::TimeDelta::FromMilliseconds(
+ update_context_->config->StepDelay()));
+ }
+
+ update_context_->crx_downloader.reset();
+}
+
+void ActionUpdate::Install(const std::string& crx_id,
+ const base::FilePath& crx_path) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(crx_id == update_context_->queue.front());
+
+ CrxUpdateItem* item = FindUpdateItemById(crx_id);
+ DCHECK(item);
+
+ OnInstallStart(item);
+
+ update_context_->blocking_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(&ActionUpdate::DoInstallOnBlockingTaskRunner,
+ base::Unretained(this), update_context_, item, crx_path));
+}
+
+void ActionUpdate::DoInstallOnBlockingTaskRunner(
+ UpdateContext* update_context,
+ CrxUpdateItem* item,
+ const base::FilePath& crx_path) {
+ update_context->unpacker = new ComponentUnpacker(
+ item->component.pk_hash, crx_path, item->component.fingerprint,
+ item->component.installer,
+ update_context->config->CreateOutOfProcessPatcher(),
+ update_context->blocking_task_runner);
+ update_context->unpacker->Unpack(
+ base::Bind(&ActionUpdate::EndUnpackingOnBlockingTaskRunner,
+ base::Unretained(this), update_context, item, crx_path));
+}
+
+void ActionUpdate::EndUnpackingOnBlockingTaskRunner(
+ UpdateContext* update_context,
+ CrxUpdateItem* item,
+ const base::FilePath& crx_path,
+ ComponentUnpacker::Error error,
+ int extended_error) {
+ update_client::DeleteFileAndEmptyParentDirectory(crx_path);
+ update_context->main_task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ActionUpdate::DoneInstalling, base::Unretained(this),
+ item->id, error, extended_error),
+ base::TimeDelta::FromMilliseconds(update_context->config->StepDelay()));
+
+ // Reset the unpacker last, otherwise we free our own arguments.
+ update_context->unpacker = nullptr;
+}
+
+void ActionUpdate::DoneInstalling(const std::string& crx_id,
+ ComponentUnpacker::Error error,
+ int extended_error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(crx_id == update_context_->queue.front());
+
+ CrxUpdateItem* item = FindUpdateItemById(crx_id);
+ DCHECK(item);
+
+ if (error == ComponentUnpacker::kNone)
+ OnInstallSuccess(item);
+ else
+ OnInstallError(item, error, extended_error);
+}
+
+ActionUpdateDiff::ActionUpdateDiff() {
+}
+
+ActionUpdateDiff::~ActionUpdateDiff() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+scoped_ptr<Action> ActionUpdateDiff::Create() {
+ return scoped_ptr<Action>(new ActionUpdateDiff);
+}
+
+void ActionUpdateDiff::TryUpdateFull() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ scoped_ptr<Action> update_action(ActionUpdateFull::Create());
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&Action::Run, base::Unretained(update_action.get()),
+ update_context_, callback_));
+
+ update_context_->current_action.reset(update_action.release());
+}
+
+bool ActionUpdateDiff::IsBackgroundDownload(const CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return false;
+}
+
+std::vector<GURL> ActionUpdateDiff::GetUrls(const CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return item->crx_diffurls;
+}
+
+void ActionUpdateDiff::OnDownloadStart(CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kCanUpdate);
+
+ ChangeItemState(item, CrxUpdateItem::State::kDownloadingDiff);
+}
+
+void ActionUpdateDiff::OnDownloadSuccess(
+ CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kDownloadingDiff);
+
+ ChangeItemState(item, CrxUpdateItem::State::kDownloaded);
+}
+
+void ActionUpdateDiff::OnDownloadError(
+ CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kDownloadingDiff);
+
+ item->diff_error_category = static_cast<int>(ErrorCategory::kNetworkError);
+ item->diff_error_code = download_result.error;
+ item->diff_update_failed = true;
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&ActionUpdateDiff::TryUpdateFull, base::Unretained(this)));
+}
+
+void ActionUpdateDiff::OnInstallStart(CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ ChangeItemState(item, CrxUpdateItem::State::kUpdatingDiff);
+}
+
+void ActionUpdateDiff::OnInstallSuccess(CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kUpdatingDiff);
+
+ item->component.version = item->next_version;
+ item->component.fingerprint = item->next_fp;
+ ChangeItemState(item, CrxUpdateItem::State::kUpdated);
+
+ UpdateCrxComplete(item);
+}
+
+void ActionUpdateDiff::OnInstallError(CrxUpdateItem* item,
+ ComponentUnpacker::Error error,
+ int extended_error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ item->diff_error_category =
+ static_cast<int>(UnpackerErrorToErrorCategory(error));
+ item->diff_error_code = error;
+ item->diff_extra_code1 = extended_error;
+ item->diff_update_failed = true;
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&ActionUpdateDiff::TryUpdateFull, base::Unretained(this)));
+}
+
+ActionUpdateFull::ActionUpdateFull() {
+}
+
+ActionUpdateFull::~ActionUpdateFull() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+scoped_ptr<Action> ActionUpdateFull::Create() {
+ return scoped_ptr<Action>(new ActionUpdateFull);
+}
+
+bool ActionUpdateFull::IsBackgroundDownload(const CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // On demand component updates are always downloaded in foreground.
+ return !item->on_demand && item->component.allow_background_download &&
+ update_context_->config->UseBackgroundDownloader();
+}
+
+std::vector<GURL> ActionUpdateFull::GetUrls(const CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return item->crx_urls;
+}
+
+void ActionUpdateFull::OnDownloadStart(CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kCanUpdate ||
+ item->diff_update_failed);
+
+ ChangeItemState(item, CrxUpdateItem::State::kDownloading);
+}
+
+void ActionUpdateFull::OnDownloadSuccess(
+ CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kDownloading);
+
+ ChangeItemState(item, CrxUpdateItem::State::kDownloaded);
+}
+
+void ActionUpdateFull::OnDownloadError(
+ CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kDownloading);
+
+ item->error_category = static_cast<int>(ErrorCategory::kNetworkError);
+ item->error_code = download_result.error;
+ ChangeItemState(item, CrxUpdateItem::State::kNoUpdate);
+
+ UpdateCrxComplete(item);
+}
+
+void ActionUpdateFull::OnInstallStart(CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kDownloaded);
+
+ ChangeItemState(item, CrxUpdateItem::State::kUpdating);
+}
+
+void ActionUpdateFull::OnInstallSuccess(CrxUpdateItem* item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kUpdating);
+
+ item->component.version = item->next_version;
+ item->component.fingerprint = item->next_fp;
+ ChangeItemState(item, CrxUpdateItem::State::kUpdated);
+
+ UpdateCrxComplete(item);
+}
+
+void ActionUpdateFull::OnInstallError(CrxUpdateItem* item,
+ ComponentUnpacker::Error error,
+ int extended_error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(item->state == CrxUpdateItem::State::kUpdating);
+
+ item->error_category = static_cast<int>(UnpackerErrorToErrorCategory(error));
+ item->error_code = error;
+ item->extra_code1 = extended_error;
+ ChangeItemState(item, CrxUpdateItem::State::kNoUpdate);
+
+ UpdateCrxComplete(item);
+}
+
+} // namespace update_client
diff --git a/components/update_client/action_update.h b/components/update_client/action_update.h
new file mode 100644
index 0000000..8b33444
--- /dev/null
+++ b/components/update_client/action_update.h
@@ -0,0 +1,138 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_ACTION_UPDATE_H_
+#define COMPONENTS_UPDATE_CLIENT_ACTION_UPDATE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/version.h"
+#include "components/update_client/action.h"
+#include "components/update_client/component_unpacker.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_engine.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+class UpdateChecker;
+
+// Defines a template method design pattern for ActionUpdate. This class
+// implements the common code for updating a CRX using either differential or
+// full updates algorithm.
+class ActionUpdate : public Action, protected ActionImpl {
+ public:
+ ActionUpdate();
+ ~ActionUpdate() override;
+
+ // Action overrides.
+ void Run(UpdateContext* update_context, Callback callback) override;
+
+ private:
+ virtual bool IsBackgroundDownload(const CrxUpdateItem* item) = 0;
+ virtual std::vector<GURL> GetUrls(const CrxUpdateItem* item) = 0;
+ virtual void OnDownloadStart(CrxUpdateItem* item) = 0;
+ virtual void OnDownloadSuccess(
+ CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) = 0;
+ virtual void OnDownloadError(
+ CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) = 0;
+ virtual void OnInstallStart(CrxUpdateItem* item) = 0;
+ virtual void OnInstallSuccess(CrxUpdateItem* item) = 0;
+ virtual void OnInstallError(CrxUpdateItem* item,
+ ComponentUnpacker::Error error,
+ int extended_error) = 0;
+
+ // Called when progress is being made downloading a CRX. The progress may
+ // not monotonically increase due to how the CRX downloader switches between
+ // different downloaders and fallback urls.
+ void DownloadProgress(const std::string& crx_id,
+ const CrxDownloader::Result& download_result);
+
+ // Called when the CRX package has been downloaded to a temporary location.
+ void DownloadComplete(const std::string& crx_id,
+ const CrxDownloader::Result& download_result);
+
+ void Install(const std::string& crx_id, const base::FilePath& crx_path);
+
+ // TODO(sorin): refactor the public interface of ComponentUnpacker so
+ // that these calls can run on the main thread.
+ void DoInstallOnBlockingTaskRunner(UpdateContext* update_context,
+ CrxUpdateItem* item,
+ const base::FilePath& crx_path);
+
+ void EndUnpackingOnBlockingTaskRunner(UpdateContext* update_context,
+ CrxUpdateItem* item,
+ const base::FilePath& crx_path,
+ ComponentUnpacker::Error error,
+ int extended_error);
+
+ void DoneInstalling(const std::string& crx_id,
+ ComponentUnpacker::Error error,
+ int extended_error);
+
+ DISALLOW_COPY_AND_ASSIGN(ActionUpdate);
+};
+
+class ActionUpdateDiff : public ActionUpdate {
+ public:
+ static scoped_ptr<Action> Create();
+
+ private:
+ ActionUpdateDiff();
+ ~ActionUpdateDiff() override;
+
+ void TryUpdateFull();
+
+ // ActionUpdate overrides.
+ bool IsBackgroundDownload(const CrxUpdateItem* item) override;
+ std::vector<GURL> GetUrls(const CrxUpdateItem* item) override;
+ void OnDownloadStart(CrxUpdateItem* item) override;
+ void OnDownloadSuccess(CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) override;
+ void OnDownloadError(CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) override;
+ void OnInstallStart(CrxUpdateItem* item) override;
+ void OnInstallSuccess(CrxUpdateItem* item) override;
+ void OnInstallError(CrxUpdateItem* item,
+ ComponentUnpacker::Error error,
+ int extended_error) override;
+
+ DISALLOW_COPY_AND_ASSIGN(ActionUpdateDiff);
+};
+
+class ActionUpdateFull : ActionUpdate {
+ public:
+ static scoped_ptr<Action> Create();
+
+ private:
+ ActionUpdateFull();
+ ~ActionUpdateFull() override;
+
+ // ActionUpdate overrides.
+ bool IsBackgroundDownload(const CrxUpdateItem* item) override;
+ std::vector<GURL> GetUrls(const CrxUpdateItem* item) override;
+ void OnDownloadStart(CrxUpdateItem* item) override;
+ void OnDownloadSuccess(CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) override;
+ void OnDownloadError(CrxUpdateItem* item,
+ const CrxDownloader::Result& download_result) override;
+ void OnInstallStart(CrxUpdateItem* item) override;
+ void OnInstallSuccess(CrxUpdateItem* item) override;
+ void OnInstallError(CrxUpdateItem* item,
+ ComponentUnpacker::Error error,
+ int extended_error) override;
+
+ DISALLOW_COPY_AND_ASSIGN(ActionUpdateFull);
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_ACTION_UPDATE_H_
diff --git a/components/update_client/action_update_check.cc b/components/update_client/action_update_check.cc
new file mode 100644
index 0000000..c480de8
--- /dev/null
+++ b/components/update_client/action_update_check.cc
@@ -0,0 +1,204 @@
+// 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<UpdateChecker> 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<CrxComponent> 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<CrxUpdateItem> 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<UpdateResponse::Result>::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
diff --git a/components/update_client/action_update_check.h b/components/update_client/action_update_check.h
new file mode 100644
index 0000000..b5f1d24
--- /dev/null
+++ b/components/update_client/action_update_check.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_ACTION_UPDATE_CHECK_H_
+#define COMPONENTS_UPDATE_CLIENT_ACTION_UPDATE_CHECK_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/version.h"
+#include "components/update_client/action.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_engine.h"
+#include "components/update_client/update_response.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+class UpdateChecker;
+
+// Implements an update check for the CRXs in an update context.
+class ActionUpdateCheck : public Action, private ActionImpl {
+ public:
+ ActionUpdateCheck(scoped_ptr<UpdateChecker> update_checker,
+ const base::Version& browser_version,
+ const std::string& extra_request_parameters);
+
+ ~ActionUpdateCheck() override;
+
+ void Run(UpdateContext* update_context, Callback callback) override;
+
+ private:
+ void UpdateCheckComplete(const GURL& original_url,
+ int error,
+ const std::string& error_message,
+ const UpdateResponse::Results& results);
+
+ void OnUpdateCheckSucceeded(const UpdateResponse::Results& results);
+ void OnUpdateCheckFailed(int error, const std::string& error_message);
+
+ scoped_ptr<UpdateChecker> update_checker_;
+ const base::Version browser_version_;
+ const std::string extra_request_parameters_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActionUpdateCheck);
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_ACTION_UPDATE_CHECK_H_
diff --git a/components/update_client/action_wait.cc b/components/update_client/action_wait.cc
new file mode 100644
index 0000000..1e8f8059
--- /dev/null
+++ b/components/update_client/action_wait.cc
@@ -0,0 +1,61 @@
+// 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_wait.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "components/update_client/update_engine.h"
+
+namespace update_client {
+
+ActionWait::ActionWait(const base::TimeDelta& time_delta)
+ : time_delta_(time_delta) {
+}
+
+ActionWait::~ActionWait() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void ActionWait::Run(UpdateContext* update_context, Callback callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(update_context);
+
+ ActionImpl::Run(update_context, callback);
+
+ const bool result = base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, base::Bind(&ActionWait::WaitComplete, base::Unretained(this)),
+ time_delta_);
+
+ if (!result) {
+ // Move all items pending updates to the |kNoUpdate| state then return the
+ // control flow to the update engine, as the updates in this context are
+ // completed with an error.
+ while (!update_context->queue.empty()) {
+ const auto item = FindUpdateItemById(update_context->queue.front());
+ if (!item) {
+ item->error_category = static_cast<int>(ErrorCategory::kServiceError);
+ item->error_code = static_cast<int>(ServiceError::ERROR_WAIT);
+ ChangeItemState(item, CrxUpdateItem::State::kNoUpdate);
+ } else {
+ NOTREACHED();
+ }
+ update_context->queue.pop();
+ }
+ callback.Run(static_cast<int>(ServiceError::ERROR_WAIT));
+ }
+
+ NotifyObservers(UpdateClient::Observer::Events::COMPONENT_WAIT,
+ update_context_->queue.front());
+}
+
+void ActionWait::WaitComplete() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ UpdateCrx();
+}
+
+} // namespace update_client
diff --git a/components/update_client/action_wait.h b/components/update_client/action_wait.h
new file mode 100644
index 0000000..d54062b
--- /dev/null
+++ b/components/update_client/action_wait.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_ACTION_WAIT_H_
+#define COMPONENTS_UPDATE_CLIENT_ACTION_WAIT_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+
+#include "components/update_client/action.h"
+
+namespace update_client {
+
+// Implements a wait between handling updates for the CRXs in this context.
+// To avoid thrashing of local computing resources, updates are applied one
+// at a time, with a delay between them.
+class ActionWait : public Action, protected ActionImpl {
+ public:
+ explicit ActionWait(const base::TimeDelta& time_delta);
+ ~ActionWait() override;
+
+ void Run(UpdateContext* update_context, Callback callback) override;
+
+ private:
+ void WaitComplete();
+
+ const base::TimeDelta time_delta_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActionWait);
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_ACTION_WAIT_H_
diff --git a/components/update_client/component_patcher.cc b/components/update_client/component_patcher.cc
index 29ce774..0525882 100644
--- a/components/update_client/component_patcher.cc
+++ b/components/update_client/component_patcher.cc
@@ -43,7 +43,7 @@ base::ListValue* ReadCommands(const base::FilePath& unpack_path) {
ComponentPatcher::ComponentPatcher(
const base::FilePath& input_dir,
const base::FilePath& unpack_dir,
- scoped_refptr<ComponentInstaller> installer,
+ scoped_refptr<CrxInstaller> installer,
scoped_refptr<OutOfProcessPatcher> out_of_process_patcher,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: input_dir_(input_dir),
diff --git a/components/update_client/component_patcher.h b/components/update_client/component_patcher.h
index 394edfe..23c16c8 100644
--- a/components/update_client/component_patcher.h
+++ b/components/update_client/component_patcher.h
@@ -40,7 +40,7 @@ class FilePath;
namespace update_client {
-class ComponentInstaller;
+class CrxInstaller;
class DeltaUpdateOp;
class OutOfProcessPatcher;
@@ -61,7 +61,7 @@ class ComponentPatcher : public base::RefCountedThreadSafe<ComponentPatcher> {
// out-of-process.
ComponentPatcher(const base::FilePath& input_dir,
const base::FilePath& unpack_dir,
- scoped_refptr<ComponentInstaller> installer,
+ scoped_refptr<CrxInstaller> installer,
scoped_refptr<OutOfProcessPatcher> out_of_process_patcher,
scoped_refptr<base::SequencedTaskRunner> task_runner);
@@ -86,7 +86,7 @@ class ComponentPatcher : public base::RefCountedThreadSafe<ComponentPatcher> {
const base::FilePath input_dir_;
const base::FilePath unpack_dir_;
- scoped_refptr<ComponentInstaller> installer_;
+ scoped_refptr<CrxInstaller> installer_;
scoped_refptr<OutOfProcessPatcher> out_of_process_patcher_;
ComponentUnpacker::Callback callback_;
scoped_ptr<base::ListValue> commands_;
diff --git a/components/update_client/component_patcher_operation.cc b/components/update_client/component_patcher_operation.cc
index c5e7437..e971533 100644
--- a/components/update_client/component_patcher_operation.cc
+++ b/components/update_client/component_patcher_operation.cc
@@ -64,7 +64,7 @@ void DeltaUpdateOp::Run(
const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
const base::FilePath& unpack_dir,
- const scoped_refptr<ComponentInstaller>& installer,
+ const scoped_refptr<CrxInstaller>& installer,
const ComponentUnpacker::Callback& callback,
const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
callback_ = callback;
@@ -141,7 +141,7 @@ DeltaUpdateOpCopy::~DeltaUpdateOpCopy() {
ComponentUnpacker::Error DeltaUpdateOpCopy::DoParseArguments(
const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
- const scoped_refptr<ComponentInstaller>& installer) {
+ const scoped_refptr<CrxInstaller>& installer) {
std::string input_rel_path;
if (!command_args->GetString(kInput, &input_rel_path))
return ComponentUnpacker::kDeltaBadCommands;
@@ -168,7 +168,7 @@ DeltaUpdateOpCreate::~DeltaUpdateOpCreate() {
ComponentUnpacker::Error DeltaUpdateOpCreate::DoParseArguments(
const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
- const scoped_refptr<ComponentInstaller>& installer) {
+ const scoped_refptr<CrxInstaller>& installer) {
std::string patch_rel_path;
if (!command_args->GetString(kPatch, &patch_rel_path))
return ComponentUnpacker::kDeltaBadCommands;
@@ -199,7 +199,7 @@ DeltaUpdateOpPatch::~DeltaUpdateOpPatch() {
ComponentUnpacker::Error DeltaUpdateOpPatch::DoParseArguments(
const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
- const scoped_refptr<ComponentInstaller>& installer) {
+ const scoped_refptr<CrxInstaller>& installer) {
std::string patch_rel_path;
std::string input_rel_path;
if (!command_args->GetString(kPatch, &patch_rel_path) ||
diff --git a/components/update_client/component_patcher_operation.h b/components/update_client/component_patcher_operation.h
index 0771757..1cb09e3 100644
--- a/components/update_client/component_patcher_operation.h
+++ b/components/update_client/component_patcher_operation.h
@@ -26,7 +26,7 @@ extern const char kCourgette[];
extern const char kInput[];
extern const char kPatch[];
-class ComponentInstaller;
+class CrxInstaller;
class DeltaUpdateOp : public base::RefCountedThreadSafe<DeltaUpdateOp> {
public:
@@ -37,7 +37,7 @@ class DeltaUpdateOp : public base::RefCountedThreadSafe<DeltaUpdateOp> {
void Run(const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
const base::FilePath& unpack_dir,
- const scoped_refptr<ComponentInstaller>& installer,
+ const scoped_refptr<CrxInstaller>& installer,
const ComponentUnpacker::Callback& callback,
const scoped_refptr<base::SequencedTaskRunner>& task_runner);
@@ -60,7 +60,7 @@ class DeltaUpdateOp : public base::RefCountedThreadSafe<DeltaUpdateOp> {
virtual ComponentUnpacker::Error DoParseArguments(
const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
- const scoped_refptr<ComponentInstaller>& installer) = 0;
+ const scoped_refptr<CrxInstaller>& installer) = 0;
// Subclasses must override DoRun to actually perform the patching operation.
// They must call the provided callback when they have completed their
@@ -92,7 +92,7 @@ class DeltaUpdateOpCopy : public DeltaUpdateOp {
ComponentUnpacker::Error DoParseArguments(
const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
- const scoped_refptr<ComponentInstaller>& installer) override;
+ const scoped_refptr<CrxInstaller>& installer) override;
void DoRun(const ComponentUnpacker::Callback& callback) override;
@@ -116,7 +116,7 @@ class DeltaUpdateOpCreate : public DeltaUpdateOp {
ComponentUnpacker::Error DoParseArguments(
const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
- const scoped_refptr<ComponentInstaller>& installer) override;
+ const scoped_refptr<CrxInstaller>& installer) override;
void DoRun(const ComponentUnpacker::Callback& callback) override;
@@ -160,7 +160,7 @@ class DeltaUpdateOpPatch : public DeltaUpdateOp {
ComponentUnpacker::Error DoParseArguments(
const base::DictionaryValue* command_args,
const base::FilePath& input_dir,
- const scoped_refptr<ComponentInstaller>& installer) override;
+ const scoped_refptr<CrxInstaller>& installer) override;
void DoRun(const ComponentUnpacker::Callback& callback) override;
diff --git a/components/update_client/component_unpacker.cc b/components/update_client/component_unpacker.cc
index efde910..cabc4cb 100644
--- a/components/update_client/component_unpacker.cc
+++ b/components/update_client/component_unpacker.cc
@@ -102,7 +102,7 @@ ComponentUnpacker::ComponentUnpacker(
const std::vector<uint8_t>& pk_hash,
const base::FilePath& path,
const std::string& fingerprint,
- const scoped_refptr<ComponentInstaller>& installer,
+ const scoped_refptr<CrxInstaller>& installer,
const scoped_refptr<OutOfProcessPatcher>& oop_patcher,
const scoped_refptr<base::SequencedTaskRunner>& task_runner)
: pk_hash_(pk_hash),
diff --git a/components/update_client/component_unpacker.h b/components/update_client/component_unpacker.h
index 033fd33..c1c6c9e 100644
--- a/components/update_client/component_unpacker.h
+++ b/components/update_client/component_unpacker.h
@@ -19,7 +19,7 @@
namespace update_client {
-class ComponentInstaller;
+class CrxInstaller;
class ComponentPatcher;
class OutOfProcessPatcher;
@@ -99,7 +99,7 @@ class ComponentUnpacker : public base::RefCountedThreadSafe<ComponentUnpacker> {
const std::vector<uint8_t>& pk_hash,
const base::FilePath& path,
const std::string& fingerprint,
- const scoped_refptr<ComponentInstaller>& installer,
+ const scoped_refptr<CrxInstaller>& installer,
const scoped_refptr<OutOfProcessPatcher>& oop_patcher,
const scoped_refptr<base::SequencedTaskRunner>& task_runner);
@@ -148,7 +148,7 @@ class ComponentUnpacker : public base::RefCountedThreadSafe<ComponentUnpacker> {
bool is_delta_;
std::string fingerprint_;
scoped_refptr<ComponentPatcher> patcher_;
- scoped_refptr<ComponentInstaller> installer_;
+ scoped_refptr<CrxInstaller> installer_;
Callback callback_;
scoped_refptr<OutOfProcessPatcher> oop_patcher_;
Error error_;
diff --git a/components/update_client/configurator.h b/components/update_client/configurator.h
index b9a86fa..c4c763f 100644
--- a/components/update_client/configurator.h
+++ b/components/update_client/configurator.h
@@ -31,10 +31,8 @@ class OutOfProcessPatcher;
// TODO(sorin): this class will be split soon in two. One class controls
// the behavior of the update client, and the other class controls the
// behavior of the component updater.
-class Configurator {
+class Configurator : public base::RefCountedThreadSafe<Configurator> {
public:
- virtual ~Configurator() {}
-
// Delay in seconds from calling Start() to the first update check.
virtual int InitialDelay() const = 0;
@@ -57,6 +55,10 @@ class Configurator {
// for the same component.
virtual int OnDemandDelay() const = 0;
+ // The time delay in seconds between applying updates for different
+ // components.
+ virtual int UpdateDelay() const = 0;
+
// The URLs for the update checks. The URLs are tried in order, the first one
// that succeeds wins.
virtual std::vector<GURL> UpdateUrl() const = 0;
@@ -112,6 +114,11 @@ class Configurator {
// initialized for use of COM objects.
virtual scoped_refptr<base::SingleThreadTaskRunner>
GetSingleThreadTaskRunner() const = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<Configurator>;
+
+ virtual ~Configurator() {}
};
} // namespace update_client
diff --git a/components/update_client/crx_downloader.cc b/components/update_client/crx_downloader.cc
index ecbf301..9bb7e18 100644
--- a/components/update_client/crx_downloader.cc
+++ b/components/update_client/crx_downloader.cc
@@ -29,22 +29,22 @@ CrxDownloader::DownloadMetrics::DownloadMetrics()
// On Windows, the first downloader in the chain is a background downloader,
// which uses the BITS service.
-CrxDownloader* CrxDownloader::Create(
+scoped_ptr<CrxDownloader> CrxDownloader::Create(
bool is_background_download,
net::URLRequestContextGetter* context_getter,
- scoped_refptr<base::SequencedTaskRunner> url_fetcher_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) {
- scoped_ptr<CrxDownloader> url_fetcher_downloader(
+ const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& background_task_runner) {
+ scoped_ptr<CrxDownloader> url_fetcher_downloader(scoped_ptr<CrxDownloader>(
new UrlFetcherDownloader(scoped_ptr<CrxDownloader>().Pass(),
- context_getter, url_fetcher_task_runner));
+ context_getter, url_fetcher_task_runner)));
#if defined(OS_WIN)
if (is_background_download) {
- return new BackgroundDownloader(url_fetcher_downloader.Pass(),
- context_getter, background_task_runner);
+ return scoped_ptr<CrxDownloader>(new BackgroundDownloader(
+ url_fetcher_downloader.Pass(), context_getter, background_task_runner));
}
#endif
- return url_fetcher_downloader.release();
+ return url_fetcher_downloader;
}
CrxDownloader::CrxDownloader(scoped_ptr<CrxDownloader> successor)
diff --git a/components/update_client/crx_downloader.h b/components/update_client/crx_downloader.h
index 661b5c8..7d7720f 100644
--- a/components/update_client/crx_downloader.h
+++ b/components/update_client/crx_downloader.h
@@ -79,13 +79,19 @@ class CrxDownloader {
// download. The callback interface can be extended if needed to provide
// more visibility into how the download has been handled, including
// specific error codes and download metrics.
- typedef base::Callback<void(const Result& result)> DownloadCallback;
+ using DownloadCallback = base::Callback<void(const Result& result)>;
// The callback may fire 0 or many times during a download. Since this
// class implements a chain of responsibility, the callback can fire for
// different urls and different downloaders. The number of actual downloaded
// bytes is not guaranteed to monotonically increment over time.
- typedef base::Callback<void(const Result& result)> ProgressCallback;
+ using ProgressCallback = base::Callback<void(const Result& result)>;
+
+ using Factory = scoped_ptr<CrxDownloader>(*)(
+ bool,
+ net::URLRequestContextGetter*,
+ const scoped_refptr<base::SequencedTaskRunner>&,
+ const scoped_refptr<base::SingleThreadTaskRunner>&);
// Factory method to create an instance of this class and build the
// chain of responsibility. |is_background_download| specifies that a
@@ -93,11 +99,12 @@ class CrxDownloader {
// |url_fetcher_task_runner| should be an IO capable task runner able to
// support UrlFetcherDownloader. |background_task_runner| should be an
// IO capable thread able to support BackgroundDownloader.
- static CrxDownloader* Create(
+ static scoped_ptr<CrxDownloader> Create(
bool is_background_download,
net::URLRequestContextGetter* context_getter,
- scoped_refptr<base::SequencedTaskRunner> url_fetcher_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> background_task_runner);
+ const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ background_task_runner);
virtual ~CrxDownloader();
void set_progress_callback(const ProgressCallback& progress_callback);
diff --git a/components/update_client/crx_update_item.h b/components/update_client/crx_update_item.h
index 09af62f..f066ca2 100644
--- a/components/update_client/crx_update_item.h
+++ b/components/update_client/crx_update_item.h
@@ -52,13 +52,17 @@ namespace update_client {
// | error V |
// +------------------------------------------ kUpdating ->----+ success
//
+// TODO(sorin): this data structure will be further refactored once
+// the new update service is in place. For the time being, it remains as-is,
+// since it is used by the old component update service.
struct CrxUpdateItem {
- enum Status {
+ enum class State {
kNew,
kChecking,
kCanUpdate,
kDownloadingDiff,
kDownloading,
+ kDownloaded,
kUpdatingDiff,
kUpdating,
kUpdated,
@@ -69,7 +73,7 @@ struct CrxUpdateItem {
// Call CrxUpdateService::ChangeItemState to change |status|. The function may
// enforce conditions or notify observers of the change.
- Status status;
+ State state;
// True if the component was recently unregistered and will be uninstalled
// soon (after the currently operation is finished, if there is one).
diff --git a/components/update_client/ping_manager.cc b/components/update_client/ping_manager.cc
index d3eb83c..24afde6 100644
--- a/components/update_client/ping_manager.cc
+++ b/components/update_client/ping_manager.cc
@@ -81,13 +81,13 @@ std::string BuildDownloadCompleteEventElements(const CrxUpdateItem* item) {
// Returns a string representing one ping event xml element for an update item.
std::string BuildUpdateCompleteEventElement(const CrxUpdateItem* item) {
- DCHECK(item->status == CrxUpdateItem::kNoUpdate ||
- item->status == CrxUpdateItem::kUpdated);
+ DCHECK(item->state == CrxUpdateItem::State::kNoUpdate ||
+ item->state == CrxUpdateItem::State::kUpdated);
using base::StringAppendF;
std::string ping_event("<event eventtype=\"3\"");
- const int event_result = item->status == CrxUpdateItem::kUpdated;
+ const int event_result = item->state == CrxUpdateItem::State::kUpdated;
StringAppendF(&ping_event, " eventresult=\"%d\"", event_result);
if (item->error_category)
StringAppendF(&ping_event, " errorcat=\"%d\"", item->error_category);
diff --git a/components/update_client/ping_manager.h b/components/update_client/ping_manager.h
index df2fa44..664142b 100644
--- a/components/update_client/ping_manager.h
+++ b/components/update_client/ping_manager.h
@@ -17,9 +17,9 @@ struct CrxUpdateItem;
class PingManager {
public:
explicit PingManager(const Configurator& config);
- ~PingManager();
+ virtual ~PingManager();
- void OnUpdateComplete(const CrxUpdateItem* item);
+ virtual void OnUpdateComplete(const CrxUpdateItem* item);
private:
const Configurator& config_;
diff --git a/components/update_client/task.h b/components/update_client/task.h
new file mode 100644
index 0000000..51b6890
--- /dev/null
+++ b/components/update_client/task.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_TASK_H_
+#define COMPONENTS_UPDATE_CLIENT_TASK_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/update_client/update_client.h"
+
+namespace update_client {
+
+class Task;
+
+// Defines an abstraction for a unit of work done by the update client.
+// Each invocation of the update client API results in a task being created and
+// run. In most cases, a task corresponds to a set of CRXs, which are updated
+// together.
+class Task {
+ public:
+ using Callback = base::Callback<void(Task* task, int error)>;
+ virtual ~Task() {}
+
+ virtual void Run(const Callback& callback) = 0;
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_TASK_H_
diff --git a/components/update_client/task_update.cc b/components/update_client/task_update.cc
new file mode 100644
index 0000000..2cbf001
--- /dev/null
+++ b/components/update_client/task_update.cc
@@ -0,0 +1,46 @@
+// 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/task_update.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "components/update_client/update_engine.h"
+
+namespace update_client {
+
+TaskUpdate::TaskUpdate(UpdateEngine* update_engine,
+ const std::vector<std::string>& ids,
+ const UpdateClient::CrxDataCallback& crx_data_callback)
+ : update_engine_(update_engine),
+ ids_(ids),
+ crx_data_callback_(crx_data_callback) {
+}
+
+TaskUpdate::~TaskUpdate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void TaskUpdate::Run(const Callback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ callback_ = callback;
+
+ if (ids_.empty())
+ RunComplete(-1);
+
+ update_engine_->Update(
+ ids_, crx_data_callback_,
+ base::Bind(&TaskUpdate::RunComplete, base::Unretained(this)));
+}
+
+void TaskUpdate::RunComplete(int error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ callback_.Run(this, error);
+}
+
+} // namespace update_client
diff --git a/components/update_client/task_update.h b/components/update_client/task_update.h
new file mode 100644
index 0000000..7a91c28
--- /dev/null
+++ b/components/update_client/task_update.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_
+#define COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/task.h"
+#include "components/update_client/update_client.h"
+
+namespace update_client {
+
+class UpdateEngine;
+
+// Defines a specialized task for updating a group of CRXs.
+class TaskUpdate : public Task {
+ public:
+ TaskUpdate(UpdateEngine* update_engine,
+ const std::vector<std::string>& ids,
+ const UpdateClient::CrxDataCallback& crx_data_callback);
+ ~TaskUpdate() override;
+
+ void Run(const Callback& callback) override;
+
+ private:
+ // Called when the Run function associated with this task has completed.
+ void RunComplete(int error);
+
+ base::ThreadChecker thread_checker_;
+
+ UpdateEngine* update_engine_; // Not owned by this class.
+
+ const std::vector<std::string> ids_;
+ const UpdateClient::CrxDataCallback crx_data_callback_;
+
+ // Called to return the execution flow back to the caller of the
+ // Run function when this task has completed .
+ Callback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskUpdate);
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_
diff --git a/components/update_client/test/DEPS b/components/update_client/test/DEPS
index 60dbcf4..d0c36c4 100644
--- a/components/update_client/test/DEPS
+++ b/components/update_client/test/DEPS
@@ -1,3 +1,5 @@
include_rules = [
"+content",
+ "+courgette",
+ "+net",
]
diff --git a/components/update_client/test/crx_downloader_unittest.cc b/components/update_client/test/crx_downloader_unittest.cc
index 5b01b42..4b80f13 100644
--- a/components/update_client/test/crx_downloader_unittest.cc
+++ b/components/update_client/test/crx_downloader_unittest.cc
@@ -113,10 +113,10 @@ void CrxDownloaderTest::SetUp() {
num_progress_calls_ = 0;
download_progress_result_ = CrxDownloader::Result();
- crx_downloader_.reset(CrxDownloader::Create(
- false, // Do not use the background downloader in these tests.
- context_.get(), base::MessageLoopProxy::current(),
- NULL)); // No |background_task_runner| because no background downloader.
+ // Do not use the background downloader in these tests.
+ crx_downloader_.reset(CrxDownloader::Create(false, context_.get(),
+ base::MessageLoopProxy::current(),
+ NULL).release());
crx_downloader_->set_progress_callback(progress_callback_);
get_interceptor_.reset(new GetInterceptor(base::MessageLoopProxy::current(),
diff --git a/components/update_client/test/ping_manager_unittest.cc b/components/update_client/test/ping_manager_unittest.cc
index 95539f03..23308ac 100644
--- a/components/update_client/test/ping_manager_unittest.cc
+++ b/components/update_client/test/ping_manager_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
@@ -29,25 +30,25 @@ class ComponentUpdaterPingManagerTest : public testing::Test {
void TearDown() override;
protected:
- scoped_ptr<TestConfigurator> config_;
+ scoped_refptr<TestConfigurator> config_;
scoped_ptr<PingManager> ping_manager_;
private:
base::MessageLoopForIO loop_;
};
-ComponentUpdaterPingManagerTest::ComponentUpdaterPingManagerTest()
- : config_(new TestConfigurator(base::MessageLoopProxy::current(),
- base::MessageLoopProxy::current())) {
+ComponentUpdaterPingManagerTest::ComponentUpdaterPingManagerTest() {
}
void ComponentUpdaterPingManagerTest::SetUp() {
+ config_ = new TestConfigurator(base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current());
ping_manager_.reset(new PingManager(*config_));
}
void ComponentUpdaterPingManagerTest::TearDown() {
ping_manager_.reset();
- config_.reset();
+ config_ = nullptr;
}
void ComponentUpdaterPingManagerTest::RunThreadsUntilIdle() {
@@ -65,7 +66,7 @@ TEST_F(ComponentUpdaterPingManagerTest, DISABLED_PingManagerTest) {
// Test eventresult="1" is sent for successful updates.
CrxUpdateItem item;
item.id = "abc";
- item.status = CrxUpdateItem::kUpdated;
+ item.state = CrxUpdateItem::State::kUpdated;
item.previous_version = base::Version("1.0");
item.next_version = base::Version("2.0");
@@ -83,7 +84,7 @@ TEST_F(ComponentUpdaterPingManagerTest, DISABLED_PingManagerTest) {
// Test eventresult="0" is sent for failed updates.
item = CrxUpdateItem();
item.id = "abc";
- item.status = CrxUpdateItem::kNoUpdate;
+ item.state = CrxUpdateItem::State::kNoUpdate;
item.previous_version = base::Version("1.0");
item.next_version = base::Version("2.0");
@@ -101,7 +102,7 @@ TEST_F(ComponentUpdaterPingManagerTest, DISABLED_PingManagerTest) {
// Test the error values and the fingerprints.
item = CrxUpdateItem();
item.id = "abc";
- item.status = CrxUpdateItem::kNoUpdate;
+ item.state = CrxUpdateItem::State::kNoUpdate;
item.previous_version = base::Version("1.0");
item.next_version = base::Version("2.0");
item.previous_fp = "prev fp";
@@ -133,7 +134,7 @@ TEST_F(ComponentUpdaterPingManagerTest, DISABLED_PingManagerTest) {
// Test the download metrics.
item = CrxUpdateItem();
item.id = "abc";
- item.status = CrxUpdateItem::kUpdated;
+ item.state = CrxUpdateItem::State::kUpdated;
item.previous_version = base::Version("1.0");
item.next_version = base::Version("2.0");
diff --git a/components/update_client/test/request_sender_unittest.cc b/components/update_client/test/request_sender_unittest.cc
index f8ad042..35d4487 100644
--- a/components/update_client/test/request_sender_unittest.cc
+++ b/components/update_client/test/request_sender_unittest.cc
@@ -4,6 +4,7 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
@@ -40,7 +41,7 @@ class RequestSenderTest : public testing::Test {
void RunThreads();
void RunThreadsUntilIdle();
- scoped_ptr<TestConfigurator> config_;
+ scoped_refptr<TestConfigurator> config_;
scoped_ptr<RequestSender> request_sender_;
scoped_ptr<InterceptorFactory> interceptor_factory_;
@@ -66,8 +67,8 @@ RequestSenderTest::~RequestSenderTest() {
}
void RequestSenderTest::SetUp() {
- config_.reset(new TestConfigurator(base::MessageLoopProxy::current(),
- base::MessageLoopProxy::current()));
+ config_ = new TestConfigurator(base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current());
interceptor_factory_.reset(
new InterceptorFactory(base::MessageLoopProxy::current()));
post_interceptor_1 =
@@ -88,7 +89,7 @@ void RequestSenderTest::TearDown() {
interceptor_factory_.reset();
- config_.reset();
+ config_ = nullptr;
RunThreadsUntilIdle();
}
diff --git a/components/update_client/test/test_configurator.cc b/components/update_client/test/test_configurator.cc
index 27aaf5f..23eaeff 100644
--- a/components/update_client/test/test_configurator.cc
+++ b/components/update_client/test/test_configurator.cc
@@ -68,6 +68,10 @@ int TestConfigurator::OnDemandDelay() const {
return ondemand_time_;
}
+int TestConfigurator::UpdateDelay() const {
+ return 1;
+}
+
std::vector<GURL> TestConfigurator::UpdateUrl() const {
return MakeDefaultUrls();
}
diff --git a/components/update_client/test/test_configurator.h b/components/update_client/test/test_configurator.h
index 4943cca..6c2ed0e 100644
--- a/components/update_client/test/test_configurator.h
+++ b/components/update_client/test/test_configurator.h
@@ -55,7 +55,6 @@ class TestConfigurator : public Configurator {
TestConfigurator(
const scoped_refptr<base::SequencedTaskRunner>& worker_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner);
- ~TestConfigurator() override;
// Overrrides for Configurator.
int InitialDelay() const override;
@@ -64,6 +63,7 @@ class TestConfigurator : public Configurator {
int StepDelayMedium() override;
int MinimumReCheckWait() const override;
int OnDemandDelay() const override;
+ int UpdateDelay() const override;
std::vector<GURL> UpdateUrl() const override;
std::vector<GURL> PingUrl() const override;
base::Version GetBrowserVersion() const override;
@@ -88,6 +88,10 @@ class TestConfigurator : public Configurator {
void SetInitialDelay(int seconds);
private:
+ friend class base::RefCountedThreadSafe<TestConfigurator>;
+
+ ~TestConfigurator() override;
+
scoped_refptr<base::SequencedTaskRunner> worker_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
diff --git a/components/update_client/test/test_installer.h b/components/update_client/test/test_installer.h
index a31c499..e15ae0a 100644
--- a/components/update_client/test/test_installer.h
+++ b/components/update_client/test/test_installer.h
@@ -17,9 +17,10 @@ class DictionaryValue;
namespace update_client {
+// TODO(sorin): consider reducing the number of the installer mocks.
// A TestInstaller is an installer that does nothing for installation except
// increment a counter.
-class TestInstaller : public ComponentInstaller {
+class TestInstaller : public CrxInstaller {
public:
TestInstaller();
diff --git a/components/update_client/test/update_checker_unittest.cc b/components/update_client/test/update_checker_unittest.cc
index 20d3e79..ce6922a 100644
--- a/components/update_client/test/update_checker_unittest.cc
+++ b/components/update_client/test/update_checker_unittest.cc
@@ -60,7 +60,7 @@ class UpdateCheckerTest : public testing::Test {
CrxUpdateItem BuildCrxUpdateItem();
- scoped_ptr<TestConfigurator> config_;
+ scoped_refptr<TestConfigurator> config_;
scoped_ptr<UpdateChecker> update_checker_;
@@ -86,8 +86,8 @@ UpdateCheckerTest::~UpdateCheckerTest() {
}
void UpdateCheckerTest::SetUp() {
- config_.reset(new TestConfigurator(base::MessageLoopProxy::current(),
- base::MessageLoopProxy::current()));
+ config_ = new TestConfigurator(base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current());
interceptor_factory_.reset(
new InterceptorFactory(base::MessageLoopProxy::current()));
post_interceptor_ = interceptor_factory_->CreateInterceptor();
@@ -106,7 +106,7 @@ void UpdateCheckerTest::TearDown() {
post_interceptor_ = NULL;
interceptor_factory_.reset();
- config_.reset();
+ config_ = nullptr;
// The PostInterceptor requires the message loop to run to destruct correctly.
// TODO(sorin): This is fragile and should be fixed.
@@ -155,7 +155,7 @@ CrxUpdateItem UpdateCheckerTest::BuildCrxUpdateItem() {
crx_component.fingerprint = "fp1";
CrxUpdateItem crx_update_item;
- crx_update_item.status = CrxUpdateItem::kNew;
+ crx_update_item.state = CrxUpdateItem::State::kNew;
crx_update_item.id = "jebgalgnebhfojomionfpkfelancnnkf";
crx_update_item.component = crx_component;
diff --git a/components/update_client/test/update_client_unittest.cc b/components/update_client/test/update_client_unittest.cc
new file mode 100644
index 0000000..dbff673
--- /dev/null
+++ b/components/update_client/test/update_client_unittest.cc
@@ -0,0 +1,1417 @@
+// 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 "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/ping_manager.h"
+#include "components/update_client/test/test_configurator.h"
+#include "components/update_client/test/test_installer.h"
+#include "components/update_client/update_checker.h"
+#include "components/update_client/update_client_internal.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+namespace {
+
+using base::FilePath;
+
+// Makes a copy of the file specified by |from_path| in a temporary directory
+// and returns the path of the copy. Returns true if successful. Cleans up if
+// there was an error creating the copy.
+bool MakeTestFile(const FilePath& from_path, FilePath* to_path) {
+ FilePath path;
+ bool result = CreateTemporaryFile(&path);
+ if (!result)
+ return false;
+
+ result = CopyFile(from_path, path);
+ if (!result) {
+ DeleteFile(path, false);
+ return result;
+ }
+
+ *to_path = path;
+ return true;
+}
+
+using Events = UpdateClient::Observer::Events;
+
+class MockObserver : public UpdateClient::Observer {
+ public:
+ MOCK_METHOD2(OnEvent, void(Events event, const std::string&));
+};
+
+class FakePingManagerImpl : public PingManager {
+ public:
+ explicit FakePingManagerImpl(const Configurator& config);
+ ~FakePingManagerImpl() override;
+
+ void OnUpdateComplete(const CrxUpdateItem* item) override;
+
+ const std::vector<CrxUpdateItem>& items() const;
+
+ private:
+ std::vector<CrxUpdateItem> items_;
+ DISALLOW_COPY_AND_ASSIGN(FakePingManagerImpl);
+};
+
+FakePingManagerImpl::FakePingManagerImpl(const Configurator& config)
+ : PingManager(config) {
+}
+
+FakePingManagerImpl::~FakePingManagerImpl() {
+}
+
+void FakePingManagerImpl::OnUpdateComplete(const CrxUpdateItem* item) {
+ items_.push_back(*item);
+}
+
+const std::vector<CrxUpdateItem>& FakePingManagerImpl::items() const {
+ return items_;
+}
+
+} // namespace
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Mock;
+using ::testing::Return;
+
+using content::BrowserThread;
+
+using std::string;
+
+class UpdateClientTest : public testing::Test {
+ public:
+ UpdateClientTest();
+ ~UpdateClientTest() override;
+
+ void SetUp() override;
+ void TearDown() override;
+
+ protected:
+ void RunThreads();
+
+ // Returns the full path to a test file.
+ static base::FilePath TestFilePath(const char* file);
+
+ content::TestBrowserThreadBundle thread_bundle_;
+
+ base::RunLoop runloop_;
+ base::Closure quit_closure_;
+
+ scoped_refptr<update_client::TestConfigurator> config_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UpdateClientTest);
+};
+
+UpdateClientTest::UpdateClientTest()
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ config_(new TestConfigurator(
+ BrowserThread::GetBlockingPool()
+ ->GetSequencedTaskRunnerWithShutdownBehavior(
+ BrowserThread::GetBlockingPool()->GetSequenceToken(),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))) {
+}
+
+UpdateClientTest::~UpdateClientTest() {
+}
+
+void UpdateClientTest::SetUp() {
+ quit_closure_ = runloop_.QuitClosure();
+}
+
+void UpdateClientTest::TearDown() {
+}
+
+void UpdateClientTest::RunThreads() {
+ runloop_.Run();
+}
+
+base::FilePath UpdateClientTest::TestFilePath(const char* file) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return path.AppendASCII("components")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("update_client")
+ .AppendASCII(file);
+}
+
+// Tests the scenario where one update check is done for one CRX. The CRX
+// has no update.
+TEST_F(UpdateClientTest, OneCrxNoUpdate) {
+ class DataCallbackFake {
+ public:
+ static void Callback(const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components) {
+ CrxComponent crx;
+ crx.name = "test_jebg";
+ crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+ crx.version = Version("0.9");
+ crx.installer = new TestInstaller;
+ components->push_back(crx);
+ }
+ };
+
+ class CompletionCallbackFake {
+ public:
+ static void Callback(const base::Closure& quit_closure, int error) {
+ EXPECT_EQ(0, error);
+ quit_closure.Run();
+ }
+ };
+
+ class FakeUpdateChecker : public UpdateChecker {
+ public:
+ static scoped_ptr<UpdateChecker> Create(const Configurator& config) {
+ return scoped_ptr<UpdateChecker>(new FakeUpdateChecker());
+ }
+
+ bool CheckForUpdates(
+ const std::vector<CrxUpdateItem*>& items_to_check,
+ const std::string& additional_attributes,
+ const UpdateCheckCallback& update_check_callback) override {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "",
+ UpdateResponse::Results()));
+ return true;
+ }
+ };
+
+ class FakeCrxDownloader : public CrxDownloader {
+ public:
+ static scoped_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ net::URLRequestContextGetter* context_getter,
+ const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ background_task_runner) {
+ return scoped_ptr<CrxDownloader>(new FakeCrxDownloader());
+ }
+
+ private:
+ FakeCrxDownloader() : CrxDownloader(scoped_ptr<CrxDownloader>().Pass()) {}
+ ~FakeCrxDownloader() override {}
+
+ void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+ };
+
+ class FakePingManager : public FakePingManagerImpl {
+ public:
+ explicit FakePingManager(const Configurator& config)
+ : FakePingManagerImpl(config) {}
+ ~FakePingManager() override { EXPECT_TRUE(items().empty()); }
+ };
+
+ scoped_ptr<PingManager> ping_manager(new FakePingManager(*config_));
+ scoped_ptr<UpdateClient> update_client(new UpdateClientImpl(
+ config_, ping_manager.Pass(), &FakeUpdateChecker::Create,
+ &FakeCrxDownloader::Create));
+
+ MockObserver observer;
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+
+ update_client->AddObserver(&observer);
+
+ std::vector<std::string> ids;
+ ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf"));
+
+ update_client->Update(
+ ids, base::Bind(&DataCallbackFake::Callback),
+ base::Bind(&CompletionCallbackFake::Callback, quit_closure_));
+
+ RunThreads();
+
+ update_client->RemoveObserver(&observer);
+}
+
+// Tests the scenario where two CRXs are checked for updates. On CRX has
+// an update, the other CRX does not.
+TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
+ class DataCallbackFake {
+ public:
+ static void Callback(const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components) {
+ CrxComponent crx1;
+ crx1.name = "test_jebg";
+ crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+ crx1.version = Version("0.9");
+ crx1.installer = new TestInstaller;
+
+ CrxComponent crx2;
+ crx2.name = "test_abag";
+ crx2.pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash));
+ crx2.version = Version("2.2");
+ crx2.installer = new TestInstaller;
+
+ components->push_back(crx1);
+ components->push_back(crx2);
+ }
+ };
+
+ class CompletionCallbackFake {
+ public:
+ static void Callback(const base::Closure& quit_closure, int error) {
+ EXPECT_EQ(0, error);
+ quit_closure.Run();
+ }
+ };
+
+ class FakeUpdateChecker : public UpdateChecker {
+ public:
+ static scoped_ptr<UpdateChecker> Create(const Configurator& config) {
+ return scoped_ptr<UpdateChecker>(new FakeUpdateChecker());
+ }
+
+ bool CheckForUpdates(
+ const std::vector<CrxUpdateItem*>& items_to_check,
+ const std::string& additional_attributes,
+ const UpdateCheckCallback& update_check_callback) override {
+ /*
+ Fake the following response:
+
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.0'>
+ <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ </urls>
+ <manifest version='1.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ */
+ UpdateResponse::Result::Manifest::Package package;
+ package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+
+ UpdateResponse::Result result;
+ result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf";
+ result.crx_urls.push_back(GURL("http://localhost/download/"));
+ result.manifest.version = "1.0";
+ result.manifest.browser_min_version = "11.0.1.0";
+ result.manifest.packages.push_back(package);
+
+ UpdateResponse::Results results;
+ results.list.push_back(result);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results));
+ return true;
+ }
+ };
+
+ class FakeCrxDownloader : public CrxDownloader {
+ public:
+ static scoped_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ net::URLRequestContextGetter* context_getter,
+ const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ background_task_runner) {
+ return scoped_ptr<CrxDownloader>(new FakeCrxDownloader());
+ }
+
+ private:
+ FakeCrxDownloader() : CrxDownloader(scoped_ptr<CrxDownloader>().Pass()) {}
+ ~FakeCrxDownloader() override {}
+
+ void DoStartDownload(const GURL& url) override {
+ DownloadMetrics download_metrics;
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 1843;
+ download_metrics.total_bytes = 1843;
+ download_metrics.download_time_ms = 1000;
+
+ FilePath path;
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+ Result result;
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 1843;
+ result.total_bytes = 1843;
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress,
+ base::Unretained(this), result));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeCrxDownloader::OnDownloadComplete,
+ base::Unretained(this), true, result, download_metrics));
+ }
+ };
+
+ class FakePingManager : public FakePingManagerImpl {
+ public:
+ explicit FakePingManager(const Configurator& config)
+ : FakePingManagerImpl(config) {}
+ ~FakePingManager() override {
+ const auto& ping_items = items();
+ EXPECT_EQ(1U, ping_items.size());
+ EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id);
+ EXPECT_TRUE(base::Version("0.9").Equals(ping_items[0].previous_version));
+ EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version));
+ EXPECT_EQ(0, ping_items[0].error_category);
+ EXPECT_EQ(0, ping_items[0].error_code);
+ }
+ };
+
+ scoped_ptr<PingManager> ping_manager(new FakePingManager(*config_));
+ scoped_ptr<UpdateClient> update_client(new UpdateClientImpl(
+ config_, ping_manager.Pass(), &FakeUpdateChecker::Create,
+ &FakeCrxDownloader::Create));
+
+ MockObserver observer;
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "abagagagagagagagagagagagagagagag")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+ "abagagagagagagagagagagagagagagag")).Times(1);
+ }
+
+ update_client->AddObserver(&observer);
+
+ std::vector<std::string> ids;
+ ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf"));
+ ids.push_back(std::string("abagagagagagagagagagagagagagagag"));
+
+ update_client->Update(
+ ids, base::Bind(&DataCallbackFake::Callback),
+ base::Bind(&CompletionCallbackFake::Callback, quit_closure_));
+
+ RunThreads();
+
+ update_client->RemoveObserver(&observer);
+}
+
+// Tests the update check for two CRXs scenario. Both CRXs have updates.
+TEST_F(UpdateClientTest, TwoCrxUpdate) {
+ class DataCallbackFake {
+ public:
+ static void Callback(const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components) {
+ CrxComponent crx1;
+ crx1.name = "test_jebg";
+ crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+ crx1.version = Version("0.9");
+ crx1.installer = new TestInstaller;
+
+ CrxComponent crx2;
+ crx2.name = "test_ihfo";
+ crx2.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash));
+ crx2.version = Version("0.8");
+ crx2.installer = new TestInstaller;
+
+ components->push_back(crx1);
+ components->push_back(crx2);
+ }
+ };
+
+ class CompletionCallbackFake {
+ public:
+ static void Callback(const base::Closure& quit_closure, int error) {
+ EXPECT_EQ(0, error);
+ quit_closure.Run();
+ }
+ };
+
+ class FakeUpdateChecker : public UpdateChecker {
+ public:
+ static scoped_ptr<UpdateChecker> Create(const Configurator& config) {
+ return scoped_ptr<UpdateChecker>(new FakeUpdateChecker());
+ }
+
+ bool CheckForUpdates(
+ const std::vector<CrxUpdateItem*>& items_to_check,
+ const std::string& additional_attributes,
+ const UpdateCheckCallback& update_check_callback) override {
+ /*
+ Fake the following response:
+
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.0'>
+ <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ </urls>
+ <manifest version='1.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ </urls>
+ <manifest version='1.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ */
+ UpdateResponse::Result::Manifest::Package package1;
+ package1.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+
+ UpdateResponse::Result result1;
+ result1.extension_id = "jebgalgnebhfojomionfpkfelancnnkf";
+ result1.crx_urls.push_back(GURL("http://localhost/download/"));
+ result1.manifest.version = "1.0";
+ result1.manifest.browser_min_version = "11.0.1.0";
+ result1.manifest.packages.push_back(package1);
+
+ UpdateResponse::Result::Manifest::Package package2;
+ package2.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
+
+ UpdateResponse::Result result2;
+ result2.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+ result2.crx_urls.push_back(GURL("http://localhost/download/"));
+ result2.manifest.version = "1.0";
+ result2.manifest.browser_min_version = "11.0.1.0";
+ result2.manifest.packages.push_back(package2);
+
+ UpdateResponse::Results results;
+ results.list.push_back(result1);
+ results.list.push_back(result2);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results));
+ return true;
+ }
+ };
+
+ class FakeCrxDownloader : public CrxDownloader {
+ public:
+ static scoped_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ net::URLRequestContextGetter* context_getter,
+ const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ background_task_runner) {
+ return scoped_ptr<CrxDownloader>(new FakeCrxDownloader());
+ }
+
+ private:
+ FakeCrxDownloader() : CrxDownloader(scoped_ptr<CrxDownloader>().Pass()) {}
+ ~FakeCrxDownloader() override {}
+
+ void DoStartDownload(const GURL& url) override {
+ DownloadMetrics download_metrics;
+ FilePath path;
+ Result result;
+ if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") {
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 1843;
+ download_metrics.total_bytes = 1843;
+ download_metrics.download_time_ms = 1000;
+
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 1843;
+ result.total_bytes = 1843;
+ } else if (url.path() ==
+ "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") {
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 53638;
+ download_metrics.total_bytes = 53638;
+ download_metrics.download_time_ms = 2000;
+
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path));
+
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 53638;
+ result.total_bytes = 53638;
+ } else {
+ NOTREACHED();
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress,
+ base::Unretained(this), result));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeCrxDownloader::OnDownloadComplete,
+ base::Unretained(this), true, result, download_metrics));
+ }
+ };
+
+ class FakePingManager : public FakePingManagerImpl {
+ public:
+ explicit FakePingManager(const Configurator& config)
+ : FakePingManagerImpl(config) {}
+ ~FakePingManager() override {
+ const auto& ping_items = items();
+ EXPECT_EQ(2U, ping_items.size());
+ EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id);
+ EXPECT_TRUE(base::Version("0.9").Equals(ping_items[0].previous_version));
+ EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version));
+ EXPECT_EQ(0, ping_items[0].error_category);
+ EXPECT_EQ(0, ping_items[0].error_code);
+ EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[1].id);
+ EXPECT_TRUE(base::Version("0.8").Equals(ping_items[1].previous_version));
+ EXPECT_TRUE(base::Version("1.0").Equals(ping_items[1].next_version));
+ EXPECT_EQ(0, ping_items[1].error_category);
+ EXPECT_EQ(0, ping_items[1].error_code);
+ }
+ };
+
+ scoped_ptr<FakePingManager> ping_manager(new FakePingManager(*config_));
+ scoped_ptr<UpdateClient> update_client(new UpdateClientImpl(
+ config_, ping_manager.Pass(), &FakeUpdateChecker::Create,
+ &FakeCrxDownloader::Create));
+
+ MockObserver observer;
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_WAIT,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ }
+
+ update_client->AddObserver(&observer);
+
+ std::vector<std::string> ids;
+ ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf"));
+ ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc"));
+
+ update_client->Update(
+ ids, base::Bind(&DataCallbackFake::Callback),
+ base::Bind(&CompletionCallbackFake::Callback, quit_closure_));
+
+ RunThreads();
+
+ update_client->RemoveObserver(&observer);
+}
+
+// Tests the differential update scenario for one CRX.
+TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
+ class DataCallbackFake {
+ public:
+ static void Callback(const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components) {
+ static int num_calls = 0;
+
+ // Must use the same stateful installer object.
+ static scoped_refptr<CrxInstaller> installer(
+ new VersionedTestInstaller());
+
+ ++num_calls;
+
+ CrxComponent crx;
+ crx.name = "test_ihfo";
+ crx.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash));
+ crx.installer = installer;
+ if (num_calls == 1) {
+ crx.version = Version("0.8");
+ } else if (num_calls == 2) {
+ crx.version = Version("1.0");
+ } else {
+ NOTREACHED();
+ }
+
+ components->push_back(crx);
+ }
+ };
+
+ class CompletionCallbackFake {
+ public:
+ static void Callback(const base::Closure& quit_closure, int error) {
+ EXPECT_EQ(0, error);
+ quit_closure.Run();
+ }
+ };
+
+ class FakeUpdateChecker : public UpdateChecker {
+ public:
+ static scoped_ptr<UpdateChecker> Create(const Configurator& config) {
+ return scoped_ptr<UpdateChecker>(new FakeUpdateChecker());
+ }
+
+ bool CheckForUpdates(
+ const std::vector<CrxUpdateItem*>& items_to_check,
+ const std::string& additional_attributes,
+ const UpdateCheckCallback& update_check_callback) override {
+ static int num_call = 0;
+ ++num_call;
+
+ UpdateResponse::Results results;
+
+ if (num_call == 1) {
+ /*
+ Fake the following response:
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.0'>
+ <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ </urls>
+ <manifest version='1.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ */
+ UpdateResponse::Result::Manifest::Package package;
+ package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
+ package.fingerprint = "1";
+ UpdateResponse::Result result;
+ result.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+ result.crx_urls.push_back(GURL("http://localhost/download/"));
+ result.manifest.version = "1.0";
+ result.manifest.browser_min_version = "11.0.1.0";
+ result.manifest.packages.push_back(package);
+ results.list.push_back(result);
+ } else if (num_call == 2) {
+ /*
+ Fake the following response:
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.0'>
+ <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ <url codebasediff='http://localhost/download/'/>
+ </urls>
+ <manifest version='2.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='ihfokbkgjpifnbbojhneepfflplebdkc_2.crx'
+ namediff='ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx'
+ fp='22'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ */
+ UpdateResponse::Result::Manifest::Package package;
+ package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx";
+ package.namediff = "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx";
+ package.fingerprint = "22";
+ UpdateResponse::Result result;
+ result.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+ result.crx_urls.push_back(GURL("http://localhost/download/"));
+ result.crx_diffurls.push_back(GURL("http://localhost/download/"));
+ result.manifest.version = "2.0";
+ result.manifest.browser_min_version = "11.0.1.0";
+ result.manifest.packages.push_back(package);
+ results.list.push_back(result);
+ } else {
+ NOTREACHED();
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results));
+ return true;
+ }
+ };
+
+ class FakeCrxDownloader : public CrxDownloader {
+ public:
+ static scoped_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ net::URLRequestContextGetter* context_getter,
+ const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ background_task_runner) {
+ return scoped_ptr<CrxDownloader>(new FakeCrxDownloader());
+ }
+
+ private:
+ FakeCrxDownloader() : CrxDownloader(scoped_ptr<CrxDownloader>().Pass()) {}
+ ~FakeCrxDownloader() override {}
+
+ void DoStartDownload(const GURL& url) override {
+ DownloadMetrics download_metrics;
+ FilePath path;
+ Result result;
+ if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") {
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 53638;
+ download_metrics.total_bytes = 53638;
+ download_metrics.download_time_ms = 2000;
+
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path));
+
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 53638;
+ result.total_bytes = 53638;
+ } else if (url.path() ==
+ "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx") {
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 2105;
+ download_metrics.total_bytes = 2105;
+ download_metrics.download_time_ms = 1000;
+
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), &path));
+
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 2105;
+ result.total_bytes = 2105;
+ } else {
+ NOTREACHED();
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress,
+ base::Unretained(this), result));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeCrxDownloader::OnDownloadComplete,
+ base::Unretained(this), true, result, download_metrics));
+ }
+ };
+
+ class FakePingManager : public FakePingManagerImpl {
+ public:
+ explicit FakePingManager(const Configurator& config)
+ : FakePingManagerImpl(config) {}
+ ~FakePingManager() override {
+ const auto& ping_items = items();
+ EXPECT_EQ(2U, ping_items.size());
+ EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[0].id);
+ EXPECT_TRUE(base::Version("0.8").Equals(ping_items[0].previous_version));
+ EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version));
+ EXPECT_EQ(0, ping_items[0].error_category);
+ EXPECT_EQ(0, ping_items[0].error_code);
+ EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[1].id);
+ EXPECT_TRUE(base::Version("1.0").Equals(ping_items[1].previous_version));
+ EXPECT_TRUE(base::Version("2.0").Equals(ping_items[1].next_version));
+ EXPECT_EQ(0, ping_items[1].diff_error_category);
+ EXPECT_EQ(0, ping_items[1].diff_error_code);
+ }
+ };
+
+ scoped_ptr<FakePingManager> ping_manager(new FakePingManager(*config_));
+ scoped_ptr<UpdateClient> update_client(new UpdateClientImpl(
+ config_, ping_manager.Pass(), &FakeUpdateChecker::Create,
+ &FakeCrxDownloader::Create));
+
+ MockObserver observer;
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ }
+
+ update_client->AddObserver(&observer);
+
+ std::vector<std::string> ids;
+ ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc"));
+
+ {
+ base::RunLoop runloop;
+ update_client->Update(
+ ids, base::Bind(&DataCallbackFake::Callback),
+ base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure()));
+ runloop.Run();
+ }
+
+ {
+ base::RunLoop runloop;
+ update_client->Update(
+ ids, base::Bind(&DataCallbackFake::Callback),
+ base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure()));
+ runloop.Run();
+ }
+
+ update_client->RemoveObserver(&observer);
+}
+
+// Tests the update scenario for one CRX where the CRX installer returns
+// an error.
+TEST_F(UpdateClientTest, OneCrxInstallError) {
+ class MockInstaller : public CrxInstaller {
+ public:
+ MOCK_METHOD1(OnUpdateError, void(int error));
+ MOCK_METHOD2(Install,
+ bool(const base::DictionaryValue& manifest,
+ const base::FilePath& unpack_path));
+ MOCK_METHOD2(GetInstalledFile,
+ bool(const std::string& file, base::FilePath* installed_file));
+ MOCK_METHOD0(Uninstall, bool());
+
+ static void OnInstall(const base::DictionaryValue& manifest,
+ const base::FilePath& unpack_path) {
+ base::DeleteFile(unpack_path, true);
+ }
+
+ protected:
+ ~MockInstaller() override {}
+ };
+
+ class DataCallbackFake {
+ public:
+ static void Callback(const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components) {
+ scoped_refptr<MockInstaller> installer(new MockInstaller());
+
+ EXPECT_CALL(*installer, OnUpdateError(_)).Times(0);
+ EXPECT_CALL(*installer, Install(_, _))
+ .WillOnce(DoAll(Invoke(MockInstaller::OnInstall), Return(false)));
+ EXPECT_CALL(*installer, GetInstalledFile(_, _)).Times(0);
+ EXPECT_CALL(*installer, Uninstall()).Times(0);
+
+ CrxComponent crx;
+ crx.name = "test_jebg";
+ crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+ crx.version = Version("0.9");
+ crx.installer = installer;
+ components->push_back(crx);
+ }
+ };
+
+ class CompletionCallbackFake {
+ public:
+ static void Callback(const base::Closure& quit_closure, int error) {
+ EXPECT_EQ(0, error);
+ quit_closure.Run();
+ }
+ };
+
+ class FakeUpdateChecker : public UpdateChecker {
+ public:
+ static scoped_ptr<UpdateChecker> Create(const Configurator& config) {
+ return scoped_ptr<UpdateChecker>(new FakeUpdateChecker());
+ }
+
+ bool CheckForUpdates(
+ const std::vector<CrxUpdateItem*>& items_to_check,
+ const std::string& additional_attributes,
+ const UpdateCheckCallback& update_check_callback) override {
+ /*
+ Fake the following response:
+
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.0'>
+ <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ </urls>
+ <manifest version='1.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ */
+ UpdateResponse::Result::Manifest::Package package;
+ package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+
+ UpdateResponse::Result result;
+ result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf";
+ result.crx_urls.push_back(GURL("http://localhost/download/"));
+ result.manifest.version = "1.0";
+ result.manifest.browser_min_version = "11.0.1.0";
+ result.manifest.packages.push_back(package);
+
+ UpdateResponse::Results results;
+ results.list.push_back(result);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results));
+ return true;
+ }
+ };
+
+ class FakeCrxDownloader : public CrxDownloader {
+ public:
+ static scoped_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ net::URLRequestContextGetter* context_getter,
+ const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ background_task_runner) {
+ return scoped_ptr<CrxDownloader>(new FakeCrxDownloader());
+ }
+
+ private:
+ FakeCrxDownloader() : CrxDownloader(scoped_ptr<CrxDownloader>().Pass()) {}
+ ~FakeCrxDownloader() override {}
+
+ void DoStartDownload(const GURL& url) override {
+ DownloadMetrics download_metrics;
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 1843;
+ download_metrics.total_bytes = 1843;
+ download_metrics.download_time_ms = 1000;
+
+ FilePath path;
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+ Result result;
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 1843;
+ result.total_bytes = 1843;
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress,
+ base::Unretained(this), result));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeCrxDownloader::OnDownloadComplete,
+ base::Unretained(this), true, result, download_metrics));
+ }
+ };
+
+ class FakePingManager : public FakePingManagerImpl {
+ public:
+ explicit FakePingManager(const Configurator& config)
+ : FakePingManagerImpl(config) {}
+ ~FakePingManager() override {
+ const auto& ping_items = items();
+ EXPECT_EQ(1U, ping_items.size());
+ EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id);
+ EXPECT_TRUE(base::Version("0.9").Equals(ping_items[0].previous_version));
+ EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version));
+ EXPECT_EQ(3, ping_items[0].error_category); // kInstallError.
+ EXPECT_EQ(9, ping_items[0].error_code); // kInstallerError.
+ }
+ };
+
+ scoped_ptr<PingManager> ping_manager(new FakePingManager(*config_));
+ scoped_ptr<UpdateClient> update_client(new UpdateClientImpl(
+ config_, ping_manager.Pass(), &FakeUpdateChecker::Create,
+ &FakeCrxDownloader::Create));
+
+ MockObserver observer;
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+ "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+ }
+
+ update_client->AddObserver(&observer);
+
+ std::vector<std::string> ids;
+ ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf"));
+
+ update_client->Update(
+ ids, base::Bind(&DataCallbackFake::Callback),
+ base::Bind(&CompletionCallbackFake::Callback, quit_closure_));
+
+ RunThreads();
+
+ update_client->RemoveObserver(&observer);
+}
+
+// Tests the fallback from differential to full update scenario for one CRX.
+TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) {
+ class DataCallbackFake {
+ public:
+ static void Callback(const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components) {
+ static int num_calls = 0;
+
+ // Must use the same stateful installer object.
+ static scoped_refptr<CrxInstaller> installer(
+ new VersionedTestInstaller());
+
+ ++num_calls;
+
+ CrxComponent crx;
+ crx.name = "test_ihfo";
+ crx.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash));
+ crx.installer = installer;
+ if (num_calls == 1) {
+ crx.version = Version("0.8");
+ } else if (num_calls == 2) {
+ crx.version = Version("1.0");
+ } else {
+ NOTREACHED();
+ }
+
+ components->push_back(crx);
+ }
+ };
+
+ class CompletionCallbackFake {
+ public:
+ static void Callback(const base::Closure& quit_closure, int error) {
+ EXPECT_EQ(0, error);
+ quit_closure.Run();
+ }
+ };
+
+ class FakeUpdateChecker : public UpdateChecker {
+ public:
+ static scoped_ptr<UpdateChecker> Create(const Configurator& config) {
+ return scoped_ptr<UpdateChecker>(new FakeUpdateChecker());
+ }
+
+ bool CheckForUpdates(
+ const std::vector<CrxUpdateItem*>& items_to_check,
+ const std::string& additional_attributes,
+ const UpdateCheckCallback& update_check_callback) override {
+ static int num_call = 0;
+ ++num_call;
+
+ UpdateResponse::Results results;
+
+ if (num_call == 1) {
+ /*
+ Fake the following response:
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.0'>
+ <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ </urls>
+ <manifest version='1.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ */
+ UpdateResponse::Result::Manifest::Package package;
+ package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
+ package.fingerprint = "1";
+ UpdateResponse::Result result;
+ result.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+ result.crx_urls.push_back(GURL("http://localhost/download/"));
+ result.manifest.version = "1.0";
+ result.manifest.browser_min_version = "11.0.1.0";
+ result.manifest.packages.push_back(package);
+ results.list.push_back(result);
+ } else if (num_call == 2) {
+ /*
+ Fake the following response:
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.0'>
+ <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ <url codebasediff='http://localhost/download/'/>
+ </urls>
+ <manifest version='2.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='ihfokbkgjpifnbbojhneepfflplebdkc_2.crx'
+ namediff='ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx'
+ fp='22'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ */
+ UpdateResponse::Result::Manifest::Package package;
+ package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx";
+ package.namediff = "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx";
+ package.fingerprint = "22";
+ UpdateResponse::Result result;
+ result.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+ result.crx_urls.push_back(GURL("http://localhost/download/"));
+ result.crx_diffurls.push_back(GURL("http://localhost/download/"));
+ result.manifest.version = "2.0";
+ result.manifest.browser_min_version = "11.0.1.0";
+ result.manifest.packages.push_back(package);
+ results.list.push_back(result);
+ } else {
+ NOTREACHED();
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results));
+ return true;
+ }
+ };
+
+ class FakeCrxDownloader : public CrxDownloader {
+ public:
+ static scoped_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ net::URLRequestContextGetter* context_getter,
+ const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ background_task_runner) {
+ return scoped_ptr<CrxDownloader>(new FakeCrxDownloader());
+ }
+
+ private:
+ FakeCrxDownloader() : CrxDownloader(scoped_ptr<CrxDownloader>().Pass()) {}
+ ~FakeCrxDownloader() override {}
+
+ void DoStartDownload(const GURL& url) override {
+ DownloadMetrics download_metrics;
+ FilePath path;
+ Result result;
+ if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") {
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 53638;
+ download_metrics.total_bytes = 53638;
+ download_metrics.download_time_ms = 2000;
+
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path));
+
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 53638;
+ result.total_bytes = 53638;
+ } else if (url.path() ==
+ "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx") {
+ // A download error is injected on this execution path.
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = -1;
+ download_metrics.downloaded_bytes = 0;
+ download_metrics.total_bytes = 2105;
+ download_metrics.download_time_ms = 1000;
+
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), &path));
+
+ result.error = -1;
+ result.response = path;
+ result.downloaded_bytes = 0;
+ result.total_bytes = 2105;
+ } else if (url.path() ==
+ "/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx") {
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 53855;
+ download_metrics.total_bytes = 53855;
+ download_metrics.download_time_ms = 1000;
+
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"), &path));
+
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 53855;
+ result.total_bytes = 53855;
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress,
+ base::Unretained(this), result));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeCrxDownloader::OnDownloadComplete,
+ base::Unretained(this), true, result, download_metrics));
+ }
+ };
+
+ class FakePingManager : public FakePingManagerImpl {
+ public:
+ explicit FakePingManager(const Configurator& config)
+ : FakePingManagerImpl(config) {}
+ ~FakePingManager() override {
+ const auto& ping_items = items();
+ EXPECT_EQ(2U, ping_items.size());
+ EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[0].id);
+ EXPECT_TRUE(base::Version("0.8").Equals(ping_items[0].previous_version));
+ EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version));
+ EXPECT_EQ(0, ping_items[0].error_category);
+ EXPECT_EQ(0, ping_items[0].error_code);
+ EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[1].id);
+ EXPECT_TRUE(base::Version("1.0").Equals(ping_items[1].previous_version));
+ EXPECT_TRUE(base::Version("2.0").Equals(ping_items[1].next_version));
+ EXPECT_TRUE(ping_items[1].diff_update_failed);
+ EXPECT_EQ(1, ping_items[1].diff_error_category); // kNetworkError.
+ EXPECT_EQ(-1, ping_items[1].diff_error_code);
+ }
+ };
+
+ scoped_ptr<FakePingManager> ping_manager(new FakePingManager(*config_));
+ scoped_ptr<UpdateClient> update_client(new UpdateClientImpl(
+ config_, ping_manager.Pass(), &FakeUpdateChecker::Create,
+ &FakeCrxDownloader::Create));
+
+ MockObserver observer;
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+ "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+ }
+
+ update_client->AddObserver(&observer);
+
+ std::vector<std::string> ids;
+ ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc"));
+
+ {
+ base::RunLoop runloop;
+ update_client->Update(
+ ids, base::Bind(&DataCallbackFake::Callback),
+ base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure()));
+ runloop.Run();
+ }
+
+ {
+ base::RunLoop runloop;
+ update_client->Update(
+ ids, base::Bind(&DataCallbackFake::Callback),
+ base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure()));
+ runloop.Run();
+ }
+
+ update_client->RemoveObserver(&observer);
+}
+
+} // namespace update_client
diff --git a/components/update_client/update_checker.h b/components/update_client/update_checker.h
index 1ac6426..2f4b868 100644
--- a/components/update_client/update_checker.h
+++ b/components/update_client/update_checker.h
@@ -27,11 +27,13 @@ struct CrxUpdateItem;
class UpdateChecker {
public:
- typedef base::Callback<void(const GURL& original_url,
- int error,
- const std::string& error_message,
- const UpdateResponse::Results& results)>
- UpdateCheckCallback;
+ using UpdateCheckCallback =
+ base::Callback<void(const GURL& original_url,
+ int error,
+ const std::string& error_message,
+ const UpdateResponse::Results& results)>;
+
+ using Factory = scoped_ptr<UpdateChecker>(*)(const Configurator& config);
virtual ~UpdateChecker() {}
diff --git a/components/update_client/update_client.cc b/components/update_client/update_client.cc
index 35cc5bb..ce857e1 100644
--- a/components/update_client/update_client.cc
+++ b/components/update_client/update_client.cc
@@ -4,12 +4,40 @@
#include "components/update_client/update_client.h"
+#include <algorithm>
+#include <queue>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/configurator.h"
#include "components/update_client/crx_update_item.h"
+#include "components/update_client/ping_manager.h"
+#include "components/update_client/task_update.h"
+#include "components/update_client/update_checker.h"
+#include "components/update_client/update_client_internal.h"
+#include "components/update_client/update_engine.h"
+#include "components/update_client/update_response.h"
+#include "components/update_client/utils.h"
+#include "url/gurl.h"
namespace update_client {
CrxUpdateItem::CrxUpdateItem()
- : status(kNew),
+ : state(State::kNew),
unregistered(false),
on_demand(false),
diff_update_failed(false),
@@ -30,4 +58,122 @@ CrxComponent::CrxComponent() : allow_background_download(true) {
CrxComponent::~CrxComponent() {
}
+UpdateClientImpl::UpdateClientImpl(
+ const scoped_refptr<Configurator>& config,
+ scoped_ptr<PingManager> ping_manager,
+ UpdateChecker::Factory update_checker_factory,
+ CrxDownloader::Factory crx_downloader_factory)
+ : config_(config),
+ ping_manager_(ping_manager.Pass()),
+ update_engine_(
+ new UpdateEngine(config,
+ update_checker_factory,
+ crx_downloader_factory,
+ ping_manager_.get(),
+ base::Bind(&UpdateClientImpl::NotifyObservers,
+ base::Unretained(this)))) {
+}
+
+UpdateClientImpl::~UpdateClientImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ config_ = nullptr;
+}
+
+void UpdateClientImpl::Install(const std::string& id,
+ const CrxDataCallback& crx_data_callback,
+ const CompletionCallback& completion_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (update_engine_->IsUpdating(id)) {
+ completion_callback.Run(Error::ERROR_UPDATE_IN_PROGRESS);
+ return;
+ }
+
+ std::vector<std::string> ids;
+ ids.push_back(id);
+
+ scoped_ptr<TaskUpdate> task(
+ new TaskUpdate(update_engine_.get(), ids, crx_data_callback));
+
+ auto it = tasks_.insert(task.release()).first;
+ RunTask(*it, completion_callback);
+}
+
+void UpdateClientImpl::Update(const std::vector<std::string>& ids,
+ const CrxDataCallback& crx_data_callback,
+ const CompletionCallback& completion_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ scoped_ptr<TaskUpdate> task(
+ new TaskUpdate(update_engine_.get(), ids, crx_data_callback));
+ if (tasks_.empty()) {
+ auto it = tasks_.insert(task.release()).first;
+ RunTask(*it, completion_callback);
+ } else {
+ task_queue_.push(task.release());
+ }
+}
+
+void UpdateClientImpl::RunTask(Task* task,
+ const CompletionCallback& completion_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&Task::Run, base::Unretained(task),
+ base::Bind(&UpdateClientImpl::OnTaskComplete,
+ base::Unretained(this), completion_callback)));
+}
+
+void UpdateClientImpl::OnTaskComplete(
+ const CompletionCallback& completion_callback,
+ Task* task,
+ int error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(task);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(completion_callback, error));
+
+ tasks_.erase(task);
+ delete task;
+
+ if (!task_queue_.empty()) {
+ RunTask(task_queue_.front(), completion_callback);
+ task_queue_.pop();
+ }
+}
+
+void UpdateClientImpl::AddObserver(Observer* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ observer_list_.AddObserver(observer);
+}
+
+void UpdateClientImpl::RemoveObserver(Observer* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ observer_list_.RemoveObserver(observer);
+}
+
+void UpdateClientImpl::NotifyObservers(Observer::Events event,
+ const std::string& id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnEvent(event, id));
+}
+
+bool UpdateClientImpl::GetCrxUpdateState(const std::string& id,
+ CrxUpdateItem* update_item) const {
+ return update_engine_->GetUpdateState(id, update_item);
+}
+
+bool UpdateClientImpl::IsUpdating(const std::string& id) const {
+ return update_engine_->IsUpdating(id);
+}
+
+scoped_ptr<UpdateClient> UpdateClientFactory(
+ const scoped_refptr<Configurator>& config) {
+ scoped_ptr<PingManager> ping_manager(new PingManager(*config));
+ return scoped_ptr<UpdateClient>(
+ new UpdateClientImpl(config, ping_manager.Pass(), &UpdateChecker::Create,
+ &CrxDownloader::Create));
+}
+
} // namespace update_client
diff --git a/components/update_client/update_client.h b/components/update_client/update_client.h
index 35b3cca..100bbcc 100644
--- a/components/update_client/update_client.h
+++ b/components/update_client/update_client.h
@@ -9,9 +9,126 @@
#include <string>
#include <vector>
+#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/version.h"
+// The UpdateClient class is a facade with a simple interface. The interface
+// exposes a few APIs to install a CRX or update a group of CRXs.
+//
+// The difference between a CRX install and a CRX update is relatively minor.
+// The terminology going forward will use the word "update" to cover both
+// install and update scenarios, except where details regarding the install
+// case are relevant.
+//
+// Handling an update consists of a series of actions such as sending an update
+// check to the server, followed by parsing the server response, identifying
+// the CRXs that require an update, downloading the differential update if
+// it is available, unpacking and patching the differential update, then
+// falling back to trying a similar set of actions using the full update.
+// At the end of this process, completion pings are sent to the server,
+// as needed, for the CRXs which had updates.
+//
+// As a general idea, this code handles the action steps needed to update
+// a group of components serially, one step at a time. However, concurrent
+// execution of calls to UpdateClient::Update is possible, therefore,
+// queuing of updates could happen in some cases. More below.
+//
+// The UpdateClient class features a subject-observer interface to observe
+// the CRX state changes during an update.
+//
+// The threading model for this code assumes that most of the code in the
+// public interface runs on a SingleThreadTaskRunner.
+// This task runner corresponds to the browser UI thread in many cases. There
+// are parts of the installer interface that run on blocking task runners, which
+// are usually threads in a thread pool.
+//
+// Using the UpdateClient is relatively easy. This assumes that the client
+// of this code has already implemented the observer interface as needed, and
+// can provide an installer, as described below.
+//
+// scoped_ptr<UpdateClient> update_client(UpdateClientFactory(...));
+// update_client->AddObserver(&observer);
+// std::vector<std::string> ids;
+// ids.push_back(...));
+// update_client->Update(ids, base::Bind(...), base::Bind(...));
+//
+// UpdateClient::Update takes two callbacks as parameters. First callback
+// allows the client of this code to provide an instance of CrxComponent
+// data structure that specifies additional parameters of the update.
+// CrxComponent has a CrxInstaller data member, which must be provided by the
+// callers of this class. The second callback indicates that this non-blocking
+// call has completed.
+//
+// There could be several ways of triggering updates for a CRX, user-initiated,
+// or timer-based. Since the execution of updates is concurrent, the parameters
+// for the update must be provided right before the update is handled.
+// Otherwise, the version of the CRX set in the CrxComponent may not be correct.
+//
+// The UpdateClient public interface includes two functions: Install and
+// Update. These functions correspond to installing one CRX immediately as a
+// foreground activity (Install), and updating a group of CRXs silently in the
+// background (Update). This distinction is important. Background updates are
+// queued up and their actions run serially, one at a time, for the purpose of
+// conserving local resources such as CPU, network, and I/O.
+// On the other hand, installs are never queued up but run concurrently, as
+// requested by the user.
+//
+// The update client introduces a runtime constraint regarding interleaving
+// updates and installs. If installs or updates for a given CRX are in progress,
+// then installs for the same CRX will fail with a specific error.
+//
+// Implementation details.
+//
+// The implementation details below are not relevant to callers of this
+// code. However, these design notes are relevant to the owners and maintainers
+// of this module.
+//
+// The design for the update client consists of a number of abstractions
+// such as: task, update engine, update context, and action.
+// The execution model for these abstractions is simple. They usually expose
+// a public, non-blocking Run function, and they invoke a callback when
+// the Run function has completed.
+//
+// A task is the unit of work for the UpdateClient. A task is associated
+// with a single call of the Update function. A task represents a group
+// of CRXs that are updated together.
+//
+// The UpdateClient is responsible for the queuing of tasks, if queuing is
+// needed.
+//
+// When the task runs, it calls the update engine to handle the updates for
+// the CRXs associated with the task. The UpdateEngine is the abstraction
+// responsible for breaking down the update in a set of discrete steps, which
+// are implemented as actions, and running the actions.
+//
+// The UpdateEngine maintains a set of UpdateContext instances. Each of
+// these instances maintains the update state for all the CRXs belonging to
+// a given task. The UpdateContext contains a queue of CRX ids.
+// The UpdateEngine will handle updates for the CRXs in the order they appear
+// in the queue, until the queue is empty.
+//
+// The update state for each CRX is maintained in a container of CrxUpdateItem*.
+// As actions run, each action updates the CRX state, represented by one of
+// these CrxUpdateItem* instances.
+//
+// Although the UpdateEngine can and will run update tasks concurrently, the
+// actions of a task are run sequentially.
+//
+// The Action is a polymorphic type. There is some code reuse for convenience,
+// implemented as a mixin. The polymorphic behavior of some of the actions
+// is achieved using a template method.
+//
+// State changes of a CRX could generate events, which are observed using a
+// subject-observer interface.
+//
+// The actions chain up. In some sense, the actions implement a state machine,
+// as the CRX undergoes a series of state transitions in the process of
+// being checked for updates and applying the update.
+
+class ComponentsUI;
+
namespace base {
class DictionaryValue;
class FilePath;
@@ -19,63 +136,161 @@ class FilePath;
namespace update_client {
-// Component specific installers must derive from this class and implement
-// OnUpdateError() and Install(). A valid instance of this class must be
-// given to ComponentUpdateService::RegisterComponent().
-class ComponentInstaller
- : public base::RefCountedThreadSafe<ComponentInstaller> {
+class Configurator;
+struct CrxUpdateItem;
+
+enum Error {
+ ERROR_UPDATE_IN_PROGRESS = 1,
+};
+
+// Defines an interface for a generic CRX installer.
+class CrxInstaller : public base::RefCountedThreadSafe<CrxInstaller> {
public:
- // Called by the component updater on the main thread when there was a
- // problem unpacking or verifying the component. |error| is a non-zero
- // value which is only meaningful to the component updater.
+ // Called on the main thread when there was a problem unpacking or
+ // verifying the CRX. |error| is a non-zero value which is only meaningful
+ // to the caller.
virtual void OnUpdateError(int error) = 0;
- // Called by the component updater when a component has been unpacked
- // and is ready to be installed. |manifest| contains the CRX manifest
- // json dictionary and |unpack_path| contains the temporary directory
- // with all the unpacked CRX files. This method may be called from
- // a thread other than the main thread.
+ // Called by the update service when a CRX has been unpacked
+ // and it is ready to be installed. |manifest| contains the CRX manifest
+ // as a json dictionary.|unpack_path| contains the temporary directory
+ // with all the unpacked CRX files.
+ // This method may be called from a thread other than the main thread.
virtual bool Install(const base::DictionaryValue& manifest,
const base::FilePath& unpack_path) = 0;
- // Set |installed_file| to the full path to the installed |file|. |file| is
- // the filename of the file in this component's CRX. Returns false if this is
+ // Sets |installed_file| to the full path to the installed |file|. |file| is
+ // the filename of the file in this CRX. Returns false if this is
// not possible (the file has been removed or modified, or its current
- // location is unknown). Otherwise, returns true.
+ // location is unknown). Otherwise, it returns true.
virtual bool GetInstalledFile(const std::string& file,
base::FilePath* installed_file) = 0;
- // Called by the component updater when a component has been unregistered and
- // all versions should be uninstalled from disk. Returns true if
- // uninstallation is supported, false otherwise.
+ // Called when a CRX has been unregistered and all versions should
+ // be uninstalled from disk. Returns true if uninstallation is supported,
+ // and false otherwise.
virtual bool Uninstall() = 0;
protected:
- friend class base::RefCountedThreadSafe<ComponentInstaller>;
+ friend class base::RefCountedThreadSafe<CrxInstaller>;
- virtual ~ComponentInstaller() {}
+ virtual ~CrxInstaller() {}
};
-// Describes a particular component that can be installed or updated. This
-// structure is required to register a component with the component updater.
-// |pk_hash| is the SHA256 hash of the component's public key. If the component
-// is to be installed then version should be "0" or "0.0", else it should be
-// the current version. |fingerprint|, and |name| are optional.
-// |allow_background_download| specifies that the component can be background
-// downloaded in some cases. The default for this value is |true| and the value
-// can be overriden at the registration time. This is a temporary change until
-// the issue 340448 is resolved.
+// TODO(sorin): this structure will be refactored soon.
struct CrxComponent {
+ CrxComponent();
+ ~CrxComponent();
+
+ // SHA256 hash of the CRX's public key.
std::vector<uint8_t> pk_hash;
- scoped_refptr<ComponentInstaller> installer;
+ scoped_refptr<CrxInstaller> installer;
+
+ // The current version if the CRX is updated. Otherwise, "0" or "0.0" if
+ // the CRX is installed.
Version version;
- std::string fingerprint;
- std::string name;
+
+ std::string fingerprint; // Optional.
+ std::string name; // Optional.
+
+ // Specifies that the CRX can be background-downloaded in some cases.
+ // The default for this value is |true| and the value can be overriden at
+ // the registration time. This is a temporary change until the issue
+ // crbug/340448 is resolved.
bool allow_background_download;
- CrxComponent();
- ~CrxComponent();
};
+// All methods are safe to call only from the browser's main thread.
+class UpdateClient {
+ public:
+ using CrxDataCallback =
+ base::Callback<void(const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components)>;
+ using CompletionCallback = base::Callback<void(int error)>;
+
+ // Defines an interface to observe the UpdateClient. It provides
+ // notifications when state changes occur for the service itself or for the
+ // registered CRXs.
+ class Observer {
+ public:
+ enum class Events {
+ // Sent before the update client does an update check.
+ COMPONENT_CHECKING_FOR_UPDATES,
+
+ // Sent when there is a new version of a registered CRX. After
+ // the notification is sent the CRX will be downloaded unless the
+ // update client inserts a
+ COMPONENT_UPDATE_FOUND,
+
+ // Sent when a CRX is in the update queue but it can't be acted on
+ // right away, because the update client spaces out CRX updates due to a
+ // throttling policy.
+ COMPONENT_WAIT,
+
+ // Sent after the new CRX has been downloaded but before the install
+ // or the upgrade is attempted.
+ COMPONENT_UPDATE_READY,
+
+ // Sent when a CRX has been successfully updated.
+ COMPONENT_UPDATED,
+
+ // Sent when a CRX has not been updated following an update check:
+ // either there was no update available, or the update failed.
+ COMPONENT_NOT_UPDATED,
+
+ // Sent when CRX bytes are being downloaded.
+ COMPONENT_UPDATE_DOWNLOADING,
+ };
+
+ virtual ~Observer() {}
+
+ // Called by the update client when a state change happens.
+ // If an |id| is specified, then the event is fired on behalf of the
+ // specific CRX. The implementors of this interface are
+ // expected to filter the relevant events based on the id of the CRX.
+ virtual void OnEvent(Events event, const std::string& id) = 0;
+ };
+
+ // Adds an observer for this class. An observer should not be added more
+ // than once. The caller retains the ownership of the observer object.
+ virtual void AddObserver(Observer* observer) = 0;
+
+ // Removes an observer. It is safe for an observer to be removed while
+ // the observers are being notified.
+ virtual void RemoveObserver(Observer* observer) = 0;
+
+ // Installs the specified CRX. Calls back after the install has been handled.
+ // Calls back on |completion_callback| after the update has been handled. The
+ // |error| parameter of the |completion_callback| contains an error code in
+ // the case of a run-time error, or 0 if the Install has been handled
+ // successfully.
+ virtual void Install(const std::string& id,
+ const CrxDataCallback& crx_data_callback,
+ const CompletionCallback& completion_callback) = 0;
+
+ // Updates the specified CRXs. Calls back on |crx_data_callback| before the
+ // update is attempted to give the caller the opportunity to provide the
+ // instances of CrxComponent to be used for this update.
+ virtual void Update(const std::vector<std::string>& ids,
+ const CrxDataCallback& crx_data_callback,
+ const CompletionCallback& completion_callback) = 0;
+
+ // Returns status details about a CRX update. The function returns true in
+ // case of success and false in case of errors, such as |id| was
+ // invalid or not known.
+ virtual bool GetCrxUpdateState(const std::string& id,
+ CrxUpdateItem* update_item) const = 0;
+
+ virtual bool IsUpdating(const std::string& id) const = 0;
+
+ virtual ~UpdateClient() {}
+};
+
+// Creates an instance of the update client.
+// TODO(sorin): make UpdateClient a ref counted type.
+scoped_ptr<UpdateClient> UpdateClientFactory(
+ const scoped_refptr<Configurator>& config);
+
} // namespace update_client
#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_H_
diff --git a/components/update_client/update_client_internal.h b/components/update_client/update_client_internal.h
new file mode 100644
index 0000000..94caf04
--- /dev/null
+++ b/components/update_client/update_client_internal.h
@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_
+
+#include "components/update_client/update_client.h"
+
+#include <queue>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/crx_downloader.h"
+#include "components/update_client/update_checker.h"
+
+namespace base {
+class SequencedTaskRunner;
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace update_client {
+
+class Configurator;
+class PingManager;
+class Task;
+struct TaskContext;
+class UpdateEngine;
+
+class UpdateClientImpl : public UpdateClient {
+ public:
+ UpdateClientImpl(const scoped_refptr<Configurator>& config,
+ scoped_ptr<PingManager> ping_manager,
+ UpdateChecker::Factory update_checker_factory,
+ CrxDownloader::Factory crx_downloader_factory);
+ ~UpdateClientImpl() override;
+
+ // Overrides for UpdateClient.
+ void AddObserver(Observer* observer) override;
+ void RemoveObserver(Observer* observer) override;
+ void Install(const std::string& id,
+ const CrxDataCallback& crx_data_callback,
+ const CompletionCallback& completion_callback) override;
+ void Update(const std::vector<std::string>& ids,
+ const CrxDataCallback& crx_data_callback,
+ const CompletionCallback& completion_callback) override;
+ bool GetCrxUpdateState(const std::string& id,
+ CrxUpdateItem* update_item) const override;
+ bool IsUpdating(const std::string& id) const override;
+
+ private:
+ void RunTask(Task* task, const CompletionCallback& completion_callback);
+ void OnTaskComplete(const CompletionCallback& completion_callback,
+ Task* task,
+ int error);
+
+ void NotifyObservers(Observer::Events event, const std::string& id);
+
+ base::ThreadChecker thread_checker_;
+
+ scoped_refptr<Configurator> config_;
+
+ // Contains the tasks that are queued up.
+ std::queue<Task*> task_queue_;
+
+ // Contains all tasks in progress.
+ std::set<Task*> tasks_;
+
+ // TODO(sorin): try to make the ping manager an observer of the service.
+ scoped_ptr<PingManager> ping_manager_;
+ scoped_ptr<UpdateEngine> update_engine_;
+
+ ObserverList<Observer> observer_list_;
+
+ // Used to post responses back to the main thread.
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateClientImpl);
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_
diff --git a/components/update_client/update_engine.cc b/components/update_client/update_engine.cc
new file mode 100644
index 0000000..4c64fde
--- /dev/null
+++ b/components/update_client/update_engine.cc
@@ -0,0 +1,133 @@
+// 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/update_engine.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "components/update_client/action_update_check.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/update_checker.h"
+
+namespace update_client {
+
+UpdateContext::UpdateContext(
+ const scoped_refptr<Configurator>& config,
+ const std::vector<std::string>& ids,
+ const UpdateClient::CrxDataCallback& crx_data_callback,
+ const UpdateEngine::NotifyObserversCallback& notify_observers_callback,
+ const UpdateEngine::CompletionCallback& callback,
+ UpdateChecker::Factory update_checker_factory,
+ CrxDownloader::Factory crx_downloader_factory,
+ PingManager* ping_manager)
+ : config(config),
+ ids(ids),
+ crx_data_callback(crx_data_callback),
+ notify_observers_callback(notify_observers_callback),
+ callback(callback),
+ main_task_runner(base::ThreadTaskRunnerHandle::Get()),
+ blocking_task_runner(config->GetSequencedTaskRunner()),
+ single_thread_task_runner(config->GetSingleThreadTaskRunner()),
+ update_checker_factory(update_checker_factory),
+ crx_downloader_factory(crx_downloader_factory),
+ ping_manager(ping_manager) {
+}
+
+UpdateContext::~UpdateContext() {
+ STLDeleteElements(&update_items);
+}
+
+UpdateEngine::UpdateEngine(
+ const scoped_refptr<Configurator>& config,
+ UpdateChecker::Factory update_checker_factory,
+ CrxDownloader::Factory crx_downloader_factory,
+ PingManager* ping_manager,
+ const NotifyObserversCallback& notify_observers_callback)
+ : config_(config),
+ update_checker_factory_(update_checker_factory),
+ crx_downloader_factory_(crx_downloader_factory),
+ ping_manager_(ping_manager),
+ notify_observers_callback_(notify_observers_callback) {
+}
+
+UpdateEngine::~UpdateEngine() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+bool UpdateEngine::IsUpdating(const std::string& id) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (const auto& context : update_contexts_) {
+ const auto& ids = context->ids;
+ const auto it = std::find_if(
+ ids.begin(), ids.end(),
+ [id](const std::string& this_id) { return id == this_id; });
+ if (it != ids.end()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool UpdateEngine::GetUpdateState(const std::string& id,
+ CrxUpdateItem* update_item) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (const auto& context : update_contexts_) {
+ const auto& update_items = context->update_items;
+ const auto it = std::find_if(update_items.begin(), update_items.end(),
+ [id](const CrxUpdateItem* update_item) {
+ return id == update_item->id;
+ });
+ if (it != update_items.end()) {
+ *update_item = **it;
+ return true;
+ }
+ }
+ return false;
+}
+
+void UpdateEngine::Update(
+ const std::vector<std::string>& ids,
+ const UpdateClient::CrxDataCallback& crx_data_callback,
+ const CompletionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ scoped_ptr<UpdateContext> update_context(new UpdateContext(
+ config_, ids, crx_data_callback, notify_observers_callback_, callback,
+ update_checker_factory_, crx_downloader_factory_, ping_manager_));
+
+ CrxUpdateItem update_item;
+ scoped_ptr<ActionUpdateCheck> update_check_action(new ActionUpdateCheck(
+ (*update_context->update_checker_factory)(*config_).Pass(),
+ config_->GetBrowserVersion(), config_->ExtraRequestParams()));
+
+ update_context->current_action.reset(update_check_action.release());
+ update_contexts_.insert(update_context.get());
+
+ update_context->current_action->Run(
+ update_context.get(),
+ base::Bind(&UpdateEngine::UpdateComplete, base::Unretained(this),
+ update_context.get()));
+
+ ignore_result(update_context.release());
+}
+
+void UpdateEngine::UpdateComplete(UpdateContext* update_context, int error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(update_contexts_.find(update_context) != update_contexts_.end());
+
+ auto callback = update_context->callback;
+
+ update_contexts_.erase(update_context);
+ delete update_context;
+
+ callback.Run(error);
+}
+
+} // namespace update_client
diff --git a/components/update_client/update_engine.h b/components/update_client/update_engine.h
new file mode 100644
index 0000000..e902a54
--- /dev/null
+++ b/components/update_client/update_engine.h
@@ -0,0 +1,149 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_
+
+#include <list>
+#include <queue>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/action.h"
+#include "components/update_client/component_patcher_operation.h"
+#include "components/update_client/component_unpacker.h"
+#include "components/update_client/crx_downloader.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/ping_manager.h"
+#include "components/update_client/update_checker.h"
+#include "components/update_client/update_client.h"
+
+namespace base {
+class SequencedTaskRunner;
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace update_client {
+
+class Configurator;
+class CrxDownloader;
+struct CrxUpdateItem;
+class PingManager;
+struct UpdateContext;
+
+// Handles updates for a group of components. Updates for different groups
+// are run concurrently but within the same group of components, updates are
+// applied one at a time.
+class UpdateEngine {
+ public:
+ using CompletionCallback = base::Callback<void(int error)>;
+ using NotifyObserversCallback =
+ base::Callback<void(UpdateClient::Observer::Events event,
+ const std::string& id)>;
+ using CrxDataCallback = UpdateClient::CrxDataCallback;
+
+ UpdateEngine(const scoped_refptr<Configurator>& config,
+ UpdateChecker::Factory update_checker_factory,
+ CrxDownloader::Factory crx_downloader_factory,
+ PingManager* ping_manager,
+ const NotifyObserversCallback& notify_observers_callback);
+ ~UpdateEngine();
+
+ // Returns true is the CRX identified by the given |id| is being updated.
+ bool IsUpdating(const std::string& id) const;
+
+ bool GetUpdateState(const std::string& id, CrxUpdateItem* update_state);
+
+ void Update(const std::vector<std::string>& ids,
+ const UpdateClient::CrxDataCallback& crx_data_callback,
+ const CompletionCallback& update_callback);
+
+ private:
+ void UpdateComplete(UpdateContext* update_context, int error);
+
+ base::ThreadChecker thread_checker_;
+
+ scoped_refptr<Configurator> config_;
+
+ UpdateChecker::Factory update_checker_factory_;
+ CrxDownloader::Factory crx_downloader_factory_;
+
+ // TODO(sorin): refactor as a ref counted class.
+ PingManager* ping_manager_; // Not owned by this class.
+
+ // Called when CRX state changes occur.
+ const NotifyObserversCallback notify_observers_callback_;
+
+ // Contains the contexts associated with each update in progress.
+ std::set<UpdateContext*> update_contexts_;
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateEngine);
+};
+
+// TODO(sorin): consider making this a ref counted type.
+struct UpdateContext {
+ UpdateContext(
+ const scoped_refptr<Configurator>& config,
+ const std::vector<std::string>& ids,
+ const UpdateClient::CrxDataCallback& crx_data_callback,
+ const UpdateEngine::NotifyObserversCallback& notify_observers_callback,
+ const UpdateEngine::CompletionCallback& callback,
+ UpdateChecker::Factory update_checker_factory,
+ CrxDownloader::Factory crx_downloader_factory,
+ PingManager* ping_manager);
+
+ ~UpdateContext();
+
+ scoped_refptr<Configurator> config;
+
+ // Contains the ids of all CRXs in this context.
+ const std::vector<std::string> ids;
+
+ // Called before an update check, when update metadata is needed.
+ const UpdateEngine::CrxDataCallback& crx_data_callback;
+
+ // Called when there is a state change for any update in this context.
+ const UpdateEngine::NotifyObserversCallback notify_observers_callback;
+
+ // Called when the all updates associated with this context have completed.
+ const UpdateEngine::CompletionCallback callback;
+
+ // Posts replies back to the main thread.
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner;
+
+ // Runs tasks in a blocking thread pool.
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
+
+ // Runs tasks in the same single thread.
+ scoped_refptr<base::SingleThreadTaskRunner> single_thread_task_runner;
+
+ // Creates instances of UpdateChecker;
+ UpdateChecker::Factory update_checker_factory;
+
+ // Creates instances of CrxDownloader;
+ CrxDownloader::Factory crx_downloader_factory;
+
+ PingManager* ping_manager; // Not owned by this class.
+
+ scoped_ptr<Action> current_action;
+
+ // TODO(sorin): use a map instead of vector.
+ std::vector<CrxUpdateItem*> update_items;
+
+ // Contains the ids of the items to update.
+ std::queue<std::string> queue;
+
+ scoped_ptr<CrxDownloader> crx_downloader;
+ scoped_refptr<ComponentUnpacker> unpacker;
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_