Provide a repeating callback for UpdateClient::{Install,Update}

This is mechanical change for the component updater, because the
component updater gets state change notifications using the observer
interface of update client.

//chrome/updater code is not changed in the CL but the in the future,
a valid CrxStateChangeCallback is going to be provided when invoking
UpdateClient::{Install,Update}.


Bug: 1058858
Change-Id: Iba086a5b7a6e955125de4b5b114d6010c8526c55
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2092422
Commit-Queue: Sorin Jianu <[email protected]>
Reviewed-by: Joshua Pawlicki <[email protected]>
Cr-Commit-Position: refs/heads/master@{#748517}
diff --git a/chrome/updater/update_service_in_process.cc b/chrome/updater/update_service_in_process.cc
index 593d7ce3..73f6ca9 100644
--- a/chrome/updater/update_service_in_process.cc
+++ b/chrome/updater/update_service_in_process.cc
@@ -70,7 +70,7 @@
             return components;
           },
           persisted_data_),
-      false, std::move(callback));
+      {}, false, std::move(callback));
 }
 
 void UpdateServiceInProcess::Update(
diff --git a/chrome/updater/win/install_app.cc b/chrome/updater/win/install_app.cc
index 318e065..1950637 100644
--- a/chrome/updater/win/install_app.cc
+++ b/chrome/updater/win/install_app.cc
@@ -531,6 +531,7 @@
                         ->MakeCrxComponent()};
           },
           persisted_data_),
+      {},
       base::BindOnce(
           [](InstallAppController* install_app_controller,
              update_client::Error error) {
@@ -555,6 +556,8 @@
   update_client_ = nullptr;
 }
 
+// TODO(sorin): receive the state changes using a callback instead of events.
+// crbug/1059938.
 void InstallAppController::OnEvent(Events event, const std::string& id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(install_progress_observer_ipc_);
diff --git a/components/component_updater/component_installer_unittest.cc b/components/component_updater/component_installer_unittest.cc
index 1833fd5..0698297 100644
--- a/components/component_updater/component_installer_unittest.cc
+++ b/components/component_updater/component_installer_unittest.cc
@@ -76,6 +76,7 @@
 
   void Install(const std::string& id,
                CrxDataCallback crx_data_callback,
+               CrxStateChangeCallback crx_state_change_callback,
                Callback callback) override {
     DoInstall(id, std::move(crx_data_callback));
     std::move(callback).Run(update_client::Error::NONE);
@@ -83,6 +84,7 @@
 
   void Update(const std::vector<std::string>& ids,
               CrxDataCallback crx_data_callback,
+              CrxStateChangeCallback crx_state_change_callback,
               bool is_foreground,
               Callback callback) override {
     DoUpdate(ids, std::move(crx_data_callback));
diff --git a/components/component_updater/component_updater_service.cc b/components/component_updater/component_updater_service.cc
index 9a51e3e..eb4ce71 100644
--- a/components/component_updater/component_updater_service.cc
+++ b/components/component_updater/component_updater_service.cc
@@ -307,10 +307,10 @@
       std::move(callback), base::TimeTicks::Now());
 
   if (priority == Priority::FOREGROUND)
-    update_client_->Install(id, std::move(crx_data_callback),
+    update_client_->Install(id, std::move(crx_data_callback), {},
                             std::move(update_complete_callback));
   else if (priority == Priority::BACKGROUND)
-    update_client_->Update({id}, std::move(crx_data_callback), false,
+    update_client_->Update({id}, std::move(crx_data_callback), {}, false,
                            std::move(update_complete_callback));
   else
     NOTREACHED();
@@ -354,7 +354,7 @@
         unsecure_ids,
         base::BindOnce(&CrxUpdateService::GetCrxComponents,
                        base::Unretained(this)),
-        false,
+        {}, false,
         base::BindOnce(
             &CrxUpdateService::OnUpdateComplete, base::Unretained(this),
             secure_ids.empty() ? std::move(on_finished_callback) : Callback(),
@@ -366,7 +366,7 @@
         secure_ids,
         base::BindOnce(&CrxUpdateService::GetCrxComponents,
                        base::Unretained(this)),
-        false,
+        {}, false,
         base::BindOnce(&CrxUpdateService::OnUpdateComplete,
                        base::Unretained(this), std::move(on_finished_callback),
                        base::TimeTicks::Now()));
diff --git a/components/component_updater/component_updater_service_unittest.cc b/components/component_updater/component_updater_service_unittest.cc
index 02bfcc232..a376c30 100644
--- a/components/component_updater/component_updater_service_unittest.cc
+++ b/components/component_updater/component_updater_service_unittest.cc
@@ -78,6 +78,7 @@
   // move semantics. This function is a shim to work around it.
   void Install(const std::string& id,
                CrxDataCallback crx_data_callback,
+               CrxStateChangeCallback crx_state_change_callback,
                Callback callback) override {
     DoInstall(id);
     std::move(callback).Run(update_client::Error::NONE);
@@ -85,6 +86,7 @@
 
   void Update(const std::vector<std::string>& ids,
               CrxDataCallback crx_data_callback,
+              CrxStateChangeCallback crx_state_change_callback,
               bool is_foreground,
               Callback callback) override {
     // All update calls initiated by the component update service are
@@ -142,10 +144,6 @@
   ComponentUpdaterTest();
   ~ComponentUpdaterTest() override;
 
-  void SetUp() override;
-
-  void TearDown() override;
-
   // Makes the full path to a component updater test file.
   const base::FilePath test_file(const char* file);
 
@@ -192,23 +190,12 @@
   update_client::Error error_ = update_client::Error::NONE;
 };
 
-MockInstaller::MockInstaller() {
-}
-
-MockInstaller::~MockInstaller() {
-}
-
-MockUpdateClient::MockUpdateClient() {
-}
-
-MockUpdateClient::~MockUpdateClient() {
-}
-
-MockServiceObserver::MockServiceObserver() {
-}
-
-MockServiceObserver::~MockServiceObserver() {
-}
+MockInstaller::MockInstaller() = default;
+MockInstaller::~MockInstaller() = default;
+MockUpdateClient::MockUpdateClient() = default;
+MockUpdateClient::~MockUpdateClient() = default;
+MockServiceObserver::MockServiceObserver() = default;
+MockServiceObserver::~MockServiceObserver() = default;
 
 void OnDemandTester::OnDemand(ComponentUpdateService* cus,
                               const std::string& id,
@@ -246,12 +233,6 @@
   component_updater_.reset();
 }
 
-void ComponentUpdaterTest::SetUp() {
-}
-
-void ComponentUpdaterTest::TearDown() {
-}
-
 void ComponentUpdaterTest::RunThreads() {
   runloop_.Run();
 }
diff --git a/components/update_client/component.cc b/components/update_client/component.cc
index 779af64..04b3ca9 100644
--- a/components/update_client/component.cc
+++ b/components/update_client/component.cc
@@ -192,7 +192,7 @@
       state_(std::make_unique<StateNew>(this)),
       update_context_(update_context) {}
 
-Component::~Component() {}
+Component::~Component() = default;
 
 scoped_refptr<Configurator> Component::config() const {
   return update_context_.config;
@@ -317,6 +317,11 @@
       FROM_HERE, std::move(update_check_complete_));
 }
 
+void Component::NotifyWait() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  NotifyObservers(Events::COMPONENT_WAIT);
+}
+
 bool Component::CanDoBackgroundDownload() const {
   // Foreground component updates are always downloaded in foreground.
   return !is_foreground() &&
@@ -330,6 +335,15 @@
 
 void Component::NotifyObservers(UpdateClient::Observer::Events event) const {
   DCHECK(thread_checker_.CalledOnValidThread());
+
+  // There is no corresponding component state for the COMPONENT_WAIT event.
+  if (update_context_.crx_state_change_callback &&
+      event != UpdateClient::Observer::Events::COMPONENT_WAIT) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindRepeating(update_context_.crx_state_change_callback,
+                            GetCrxUpdateItem()));
+  }
   update_context_.notify_observers_callback.Run(event, id_);
 }
 
diff --git a/components/update_client/component.h b/components/update_client/component.h
index e3e968b7..b360fd1e 100644
--- a/components/update_client/component.h
+++ b/components/update_client/component.h
@@ -36,8 +36,8 @@
 struct CrxUpdateItem;
 struct UpdateContext;
 
-// Describes a CRX component managed by the UpdateEngine. Each |Component| is
-// associated with an UpdateContext.
+// Describes a CRX component managed by the UpdateEngine. Each instance of
+// this class is associated with one instance of UpdateContext.
 class Component {
  public:
   using Events = UpdateClient::Observer::Events;
@@ -62,6 +62,10 @@
       ErrorCategory error_category,
       int error);
 
+  // Called by the UpdateEngine when a component enters a wait for throttling
+  // purposes.
+  void NotifyWait();
+
   // Returns true if the component has reached a final state and no further
   // handling and state transitions are possible.
   bool IsHandled() const { return is_handled_; }
@@ -370,6 +374,9 @@
   void ChangeState(std::unique_ptr<State> next_state);
 
   // Notifies registered observers about changes in the state of the component.
+  // If an UpdateClient::CrxStateChangeCallback is provided as an argument to
+  // UpdateClient::Install or UpdateClient::Update function calls, then the
+  // callback is invoked as well.
   void NotifyObservers(Events event) const;
 
   void SetParseResult(const ProtocolParser::Result& result);
diff --git a/components/update_client/ping_manager_unittest.cc b/components/update_client/ping_manager_unittest.cc
index cd4fb159b..9bc5699 100644
--- a/components/update_client/ping_manager_unittest.cc
+++ b/components/update_client/ping_manager_unittest.cc
@@ -103,8 +103,9 @@
 scoped_refptr<UpdateContext> PingManagerTest::MakeMockUpdateContext() const {
   return base::MakeRefCounted<UpdateContext>(
       config_, false, std::vector<std::string>(),
-      UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(),
-      UpdateEngine::Callback(), nullptr, nullptr);
+      UpdateClient::CrxDataCallback(), UpdateClient::CrxStateChangeCallback(),
+      UpdateEngine::NotifyObserversCallback(), UpdateEngine::Callback(),
+      nullptr, nullptr);
 }
 
 // This test is parameterized for using JSON or XML serialization. |true| means
diff --git a/components/update_client/task_update.cc b/components/update_client/task_update.cc
index ab445b58..733649b5 100644
--- a/components/update_client/task_update.cc
+++ b/components/update_client/task_update.cc
@@ -13,15 +13,18 @@
 
 namespace update_client {
 
-TaskUpdate::TaskUpdate(scoped_refptr<UpdateEngine> update_engine,
-                       bool is_foreground,
-                       const std::vector<std::string>& ids,
-                       UpdateClient::CrxDataCallback crx_data_callback,
-                       Callback callback)
+TaskUpdate::TaskUpdate(
+    scoped_refptr<UpdateEngine> update_engine,
+    bool is_foreground,
+    const std::vector<std::string>& ids,
+    UpdateClient::CrxDataCallback crx_data_callback,
+    UpdateClient::CrxStateChangeCallback crx_state_change_callback,
+    Callback callback)
     : update_engine_(update_engine),
       is_foreground_(is_foreground),
       ids_(ids),
       crx_data_callback_(std::move(crx_data_callback)),
+      crx_state_change_callback_(crx_state_change_callback),
       callback_(std::move(callback)) {}
 
 TaskUpdate::~TaskUpdate() {
@@ -37,6 +40,7 @@
   }
 
   update_engine_->Update(is_foreground_, ids_, std::move(crx_data_callback_),
+                         std::move(crx_state_change_callback_),
                          base::BindOnce(&TaskUpdate::TaskComplete, this));
 }
 
@@ -54,8 +58,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback_),
-                                scoped_refptr<TaskUpdate>(this), error));
+      FROM_HERE,
+      base::BindOnce(std::move(callback_), base::WrapRefCounted(this), error));
 }
 
 }  // namespace update_client
diff --git a/components/update_client/task_update.h b/components/update_client/task_update.h
index 4df796b6..79b07c8 100644
--- a/components/update_client/task_update.h
+++ b/components/update_client/task_update.h
@@ -9,8 +9,8 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/threading/thread_checker.h"
 #include "components/update_client/task.h"
 #include "components/update_client/update_client.h"
@@ -36,12 +36,14 @@
              bool is_foreground,
              const std::vector<std::string>& ids,
              UpdateClient::CrxDataCallback crx_data_callback,
+             UpdateClient::CrxStateChangeCallback crx_state_change_callback,
              Callback callback);
+  TaskUpdate(const TaskUpdate&) = delete;
+  TaskUpdate& operator=(const TaskUpdate&) = delete;
 
+  // Overrides for Task.
   void Run() override;
-
   void Cancel() override;
-
   std::vector<std::string> GetIds() const override;
 
  private:
@@ -56,9 +58,8 @@
   const bool is_foreground_;
   const std::vector<std::string> ids_;
   UpdateClient::CrxDataCallback crx_data_callback_;
+  UpdateClient::CrxStateChangeCallback crx_state_change_callback_;
   Callback callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(TaskUpdate);
 };
 
 }  // namespace update_client
diff --git a/components/update_client/update_checker_unittest.cc b/components/update_client/update_checker_unittest.cc
index e3d2e26..bbfd1a9 100644
--- a/components/update_client/update_checker_unittest.cc
+++ b/components/update_client/update_checker_unittest.cc
@@ -228,8 +228,9 @@
 scoped_refptr<UpdateContext> UpdateCheckerTest::MakeMockUpdateContext() const {
   return base::MakeRefCounted<UpdateContext>(
       config_, false, std::vector<std::string>(),
-      UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(),
-      UpdateEngine::Callback(), nullptr, nullptr);
+      UpdateClient::CrxDataCallback(), UpdateClient::CrxStateChangeCallback(),
+      UpdateEngine::NotifyObserversCallback(), UpdateEngine::Callback(),
+      nullptr, nullptr);
 }
 
 std::unique_ptr<Component> UpdateCheckerTest::MakeComponent() const {
diff --git a/components/update_client/update_client.cc b/components/update_client/update_client.cc
index 02484ab..07f8454 100644
--- a/components/update_client/update_client.cc
+++ b/components/update_client/update_client.cc
@@ -87,6 +87,7 @@
 
 void UpdateClientImpl::Install(const std::string& id,
                                CrxDataCallback crx_data_callback,
+                               CrxStateChangeCallback crx_state_change_callback,
                                Callback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -102,18 +103,21 @@
   constexpr bool kIsForeground = true;
   RunTask(base::MakeRefCounted<TaskUpdate>(
       update_engine_.get(), kIsForeground, ids, std::move(crx_data_callback),
+      crx_state_change_callback,
       base::BindOnce(&UpdateClientImpl::OnTaskComplete, this,
                      std::move(callback))));
 }
 
 void UpdateClientImpl::Update(const std::vector<std::string>& ids,
                               CrxDataCallback crx_data_callback,
+                              CrxStateChangeCallback crx_state_change_callback,
                               bool is_foreground,
                               Callback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   auto task = base::MakeRefCounted<TaskUpdate>(
       update_engine_.get(), is_foreground, ids, std::move(crx_data_callback),
+      crx_state_change_callback,
       base::BindOnce(&UpdateClientImpl::OnTaskComplete, this,
                      std::move(callback)));
 
@@ -144,6 +148,7 @@
 
   // Remove the task from the set of the running tasks. Only tasks handled by
   // the update engine can be in this data structure.
+  DCHECK_EQ(1u, tasks_.count(task));
   tasks_.erase(task);
 
   if (is_stopped_)
diff --git a/components/update_client/update_client.h b/components/update_client/update_client.h
index c50ca2b..4245f132 100644
--- a/components/update_client/update_client.h
+++ b/components/update_client/update_client.h
@@ -323,6 +323,10 @@
       base::OnceCallback<std::vector<base::Optional<CrxComponent>>(
           const std::vector<std::string>& ids)>;
 
+  // Called when state changes occur during an Install or Update call.
+  using CrxStateChangeCallback =
+      base::RepeatingCallback<void(CrxUpdateItem item)>;
+
   // Defines an interface to observe the UpdateClient. It provides
   // notifications when state changes occur for the service itself or for the
   // registered CRXs.
@@ -380,29 +384,36 @@
   virtual void RemoveObserver(Observer* observer) = 0;
 
   // Installs the specified CRX. Calls back on |callback| after the
-  // update has been handled. The |error| parameter of the |callback|
-  // contains an error code in the case of a run-time error, or 0 if the
-  // install has been handled successfully. Overlapping calls of this function
-  // are executed concurrently, as long as the id parameter is different,
-  // meaning that installs of different components are parallelized.
+  // update has been handled. Provides state change notifications through
+  // invocations of the optional |crx_state_change_callback| callback.
+  // The |error| parameter of the |callback| contains an error code in the case
+  // of a run-time error, or 0 if the install has been handled successfully.
+  // Overlapping calls of this function are executed concurrently, as long as
+  // the id parameter is different, meaning that installs of different
+  // components are parallelized.
   // The |Install| function is intended to be used for foreground installs of
   // one CRX. These cases are usually associated with on-demand install
   // scenarios, which are triggered by user actions. Installs are never
   // queued up.
   virtual void Install(const std::string& id,
                        CrxDataCallback crx_data_callback,
+                       CrxStateChangeCallback crx_state_change_callback,
                        Callback 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. The |Update| function
-  // is intended to be used for background updates of several CRXs. Overlapping
-  // calls to this function result in a queuing behavior, and the execution
-  // of each call is serialized. In addition, updates are always queued up when
-  // installs are running. The |is_foreground| parameter must be set to true if
-  // the invocation of this function is a result of a user initiated update.
+  // instances of CrxComponent to be used for this update. Provides state change
+  // notifications through invocations of the optional
+  // |crx_state_change_callback| callback.
+  // The |Update| function is intended to be used for background updates of
+  // several CRXs. Overlapping calls to this function result in a queuing
+  // behavior, and the execution of each call is serialized. In addition,
+  // updates are always queued up when installs are running. The |is_foreground|
+  // parameter must be set to true if the invocation of this function is a
+  // result of a user initiated update.
   virtual void Update(const std::vector<std::string>& ids,
                       CrxDataCallback crx_data_callback,
+                      CrxStateChangeCallback crx_state_change_callback,
                       bool is_foreground,
                       Callback callback) = 0;
 
diff --git a/components/update_client/update_client_internal.h b/components/update_client/update_client_internal.h
index 2b6158c..676c23a 100644
--- a/components/update_client/update_client_internal.h
+++ b/components/update_client/update_client_internal.h
@@ -39,9 +39,11 @@
   void RemoveObserver(Observer* observer) override;
   void Install(const std::string& id,
                CrxDataCallback crx_data_callback,
+               CrxStateChangeCallback crx_state_change_callback,
                Callback callback) override;
   void Update(const std::vector<std::string>& ids,
               CrxDataCallback crx_data_callback,
+              CrxStateChangeCallback crx_state_change_callback,
               bool is_foreground,
               Callback callback) override;
   bool GetCrxUpdateState(const std::string& id,
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc
index 1dc0f1d..823ef42e 100644
--- a/components/update_client/update_client_unittest.cc
+++ b/components/update_client/update_client_unittest.cc
@@ -85,15 +85,25 @@
 class MockActionHandler : public ActionHandler {
  public:
   MockActionHandler() = default;
+  MockActionHandler(const MockActionHandler&) = delete;
+  MockActionHandler& operator=(const MockActionHandler&) = delete;
 
   MOCK_METHOD3(Handle,
                void(const base::FilePath&, const std::string&, Callback));
 
  private:
   ~MockActionHandler() override = default;
+};
 
-  MockActionHandler(const MockActionHandler&) = delete;
-  MockActionHandler& operator=(const MockActionHandler&) = delete;
+class MockCrxStateChangeReceiver
+    : public base::RefCounted<MockCrxStateChangeReceiver> {
+ public:
+  MOCK_METHOD(void, Receive, (CrxUpdateItem));
+
+ private:
+  friend class base::RefCounted<MockCrxStateChangeReceiver>;
+
+  ~MockCrxStateChangeReceiver() = default;
 };
 
 }  // namespace
@@ -324,21 +334,36 @@
           &MockUpdateChecker::Create, &MockCrxDownloader::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);
+  {
+    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);
+  }
+
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
   update_client->AddObserver(&observer);
-
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), true,
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver), true,
       base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
   RunThreads();
 
+  EXPECT_EQ(2u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kUpToDate, items[1].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[1].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -544,16 +569,39 @@
                                   "abagagagagagagagagagagagagagagag")).Times(1);
   }
 
-  update_client->AddObserver(&observer);
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
+  update_client->AddObserver(&observer);
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
                                         "abagagagagagagagagagagagagagagag"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(8u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kChecking, items[1].state);
+  EXPECT_STREQ("abagagagagagagagagagagagagagagag", items[1].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[2].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[3].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[3].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[4].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[4].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdating, items[5].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[5].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdated, items[6].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[6].id.c_str());
+  EXPECT_EQ(ComponentState::kUpToDate, items[7].state);
+  EXPECT_STREQ("abagagagagagagagagagagagagagagag", items[7].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -759,16 +807,39 @@
         }));
   }
 
-  update_client->AddObserver(&observer);
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
+  update_client->AddObserver(&observer);
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
                                         "abagagagagagagagagagagagagagagag"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(8u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kChecking, items[1].state);
+  EXPECT_STREQ("abagagagagagagagagagagagagagagag", items[1].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[2].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[3].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[3].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[4].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[4].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdating, items[5].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[5].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdated, items[6].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[6].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdateError, items[7].state);
+  EXPECT_STREQ("abagagagagagagagagagagagagagagag", items[7].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -956,16 +1027,37 @@
         .Times(1);
   }
 
-  update_client->AddObserver(&observer);
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
+  update_client->AddObserver(&observer);
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
                                         "ihfokbkgjpifnbbojhneepfflplebdkc"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(7u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdateError, items[1].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[1].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[2].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[3].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[3].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[4].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[4].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdating, items[5].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[5].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdated, items[6].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[6].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -1049,16 +1141,27 @@
         .Times(1);
   }
 
-  update_client->AddObserver(&observer);
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
+  update_client->AddObserver(&observer);
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
                                         "ihfokbkgjpifnbbojhneepfflplebdkc"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(2u, items.size());
+  EXPECT_EQ(ComponentState::kUpdateError, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdateError, items[1].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[1].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -1318,17 +1421,45 @@
                                   "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
   }
 
-  update_client->AddObserver(&observer);
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
+  update_client->AddObserver(&observer);
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
                                         "ihfokbkgjpifnbbojhneepfflplebdkc"};
-
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(11u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kChecking, items[1].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[1].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[2].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[3].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[3].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[4].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[4].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdateError, items[5].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[5].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[6].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[6].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[7].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[7].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[8].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[8].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdating, items[9].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[9].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdated, items[10].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[10].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -1604,24 +1735,67 @@
   }
 
   update_client->AddObserver(&observer);
-
   const std::vector<std::string> ids = {"ihfokbkgjpifnbbojhneepfflplebdkc"};
   {
+    std::vector<CrxUpdateItem> items;
+    auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+    EXPECT_CALL(*receiver, Receive(_))
+        .WillRepeatedly(
+            [&items](const CrxUpdateItem& item) { items.push_back(item); });
+
     base::RunLoop runloop;
-    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
-                          false,
-                          base::BindOnce(&CompletionCallbackMock::Callback,
-                                         runloop.QuitClosure()));
+    update_client->Update(
+        ids, base::BindOnce(&DataCallbackMock::Callback),
+        base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+        false,
+        base::BindOnce(&CompletionCallbackMock::Callback,
+                       runloop.QuitClosure()));
     runloop.Run();
+
+    EXPECT_EQ(6u, items.size());
+    EXPECT_EQ(ComponentState::kChecking, items[0].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[0].id.c_str());
+    EXPECT_EQ(ComponentState::kCanUpdate, items[1].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[1].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloading, items[2].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[2].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloading, items[3].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[3].id.c_str());
+    EXPECT_EQ(ComponentState::kUpdating, items[4].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[4].id.c_str());
+    EXPECT_EQ(ComponentState::kUpdated, items[5].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[5].id.c_str());
   }
 
   {
+    std::vector<CrxUpdateItem> items;
+    auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+    EXPECT_CALL(*receiver, Receive(_))
+        .WillRepeatedly(
+            [&items](const CrxUpdateItem& item) { items.push_back(item); });
+
     base::RunLoop runloop;
-    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
-                          false,
-                          base::BindOnce(&CompletionCallbackMock::Callback,
-                                         runloop.QuitClosure()));
+    update_client->Update(
+        ids, base::BindOnce(&DataCallbackMock::Callback),
+        base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+        false,
+        base::BindOnce(&CompletionCallbackMock::Callback,
+                       runloop.QuitClosure()));
     runloop.Run();
+
+    EXPECT_EQ(6u, items.size());
+    EXPECT_EQ(ComponentState::kChecking, items[0].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[0].id.c_str());
+    EXPECT_EQ(ComponentState::kCanUpdate, items[1].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[1].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloadingDiff, items[2].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[2].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloadingDiff, items[3].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[3].id.c_str());
+    EXPECT_EQ(ComponentState::kUpdatingDiff, items[4].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[4].id.c_str());
+    EXPECT_EQ(ComponentState::kUpdated, items[5].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[5].id.c_str());
   }
 
   update_client->RemoveObserver(&observer);
@@ -1842,15 +2016,34 @@
         .Times(1);
   }
 
-  update_client->AddObserver(&observer);
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
+  update_client->AddObserver(&observer);
   std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(6u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[1].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[1].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[2].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[3].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[3].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdating, items[4].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[4].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdateError, items[5].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[5].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -2143,21 +2336,68 @@
   const std::vector<std::string> ids = {"ihfokbkgjpifnbbojhneepfflplebdkc"};
 
   {
+    std::vector<CrxUpdateItem> items;
+    auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+    EXPECT_CALL(*receiver, Receive(_))
+        .WillRepeatedly(
+            [&items](const CrxUpdateItem& item) { items.push_back(item); });
+
     base::RunLoop runloop;
-    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
-                          false,
-                          base::BindOnce(&CompletionCallbackMock::Callback,
-                                         runloop.QuitClosure()));
+    update_client->Update(
+        ids, base::BindOnce(&DataCallbackMock::Callback),
+        base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+        false,
+        base::BindOnce(&CompletionCallbackMock::Callback,
+                       runloop.QuitClosure()));
     runloop.Run();
+    EXPECT_EQ(6u, items.size());
+    EXPECT_EQ(ComponentState::kChecking, items[0].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[0].id.c_str());
+    EXPECT_EQ(ComponentState::kCanUpdate, items[1].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[1].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloading, items[2].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[2].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloading, items[3].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[3].id.c_str());
+    EXPECT_EQ(ComponentState::kUpdating, items[4].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[4].id.c_str());
+    EXPECT_EQ(ComponentState::kUpdated, items[5].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[5].id.c_str());
   }
 
   {
+    std::vector<CrxUpdateItem> items;
+    auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+    EXPECT_CALL(*receiver, Receive(_))
+        .WillRepeatedly(
+            [&items](const CrxUpdateItem& item) { items.push_back(item); });
+
     base::RunLoop runloop;
-    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
-                          false,
-                          base::BindOnce(&CompletionCallbackMock::Callback,
-                                         runloop.QuitClosure()));
+    update_client->Update(
+        ids, base::BindOnce(&DataCallbackMock::Callback),
+        base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+        false,
+        base::BindOnce(&CompletionCallbackMock::Callback,
+                       runloop.QuitClosure()));
     runloop.Run();
+
+    EXPECT_EQ(8u, items.size());
+    EXPECT_EQ(ComponentState::kChecking, items[0].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[0].id.c_str());
+    EXPECT_EQ(ComponentState::kCanUpdate, items[1].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[1].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloadingDiff, items[2].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[2].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloadingDiff, items[3].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[3].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloading, items[4].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[4].id.c_str());
+    EXPECT_EQ(ComponentState::kDownloading, items[5].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[5].id.c_str());
+    EXPECT_EQ(ComponentState::kUpdating, items[6].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[6].id.c_str());
+    EXPECT_EQ(ComponentState::kUpdated, items[7].state);
+    EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[7].id.c_str());
   }
 
   update_client->RemoveObserver(&observer);
@@ -2261,28 +2501,58 @@
           &MockUpdateChecker::Create, &MockCrxDownloader::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);
-  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
-                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
-  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
-                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  {
+    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);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+  }
+
+  std::vector<CrxUpdateItem> items1;
+  auto receiver1 = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver1, Receive(_))
+      .WillRepeatedly(
+          [&items1](const CrxUpdateItem& item) { items1.push_back(item); });
+
+  std::vector<CrxUpdateItem> items2;
+  auto receiver2 = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver2, Receive(_))
+      .WillRepeatedly(
+          [&items2](const CrxUpdateItem& item) { items2.push_back(item); });
 
   update_client->AddObserver(&observer);
-
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver1),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver2),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(2u, items1.size());
+  EXPECT_EQ(ComponentState::kChecking, items1[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items1[0].id.c_str());
+  EXPECT_EQ(ComponentState::kUpToDate, items1[1].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items1[1].id.c_str());
+
+  EXPECT_EQ(2u, items2.size());
+  EXPECT_EQ(ComponentState::kChecking, items2[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items2[0].id.c_str());
+  EXPECT_EQ(ComponentState::kUpToDate, items2[1].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items2[1].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -2457,43 +2727,64 @@
   }
 
   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(AtLeast(1));
-  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
-                                "jebgalgnebhfojomionfpkfelancnnkf"))
-      .Times(1);
-  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
-                                "jebgalgnebhfojomionfpkfelancnnkf"))
-      .Times(1)
-      .WillOnce(Invoke([update_client](Events event, const std::string& id) {
-        CrxUpdateItem update_item;
-        ASSERT_TRUE(update_client->GetCrxUpdateState(id, &update_item));
-        ASSERT_TRUE(update_item.component);
-        const auto* test_installer =
-            static_cast<TestInstaller*>(update_item.component->installer.get());
-        EXPECT_STREQ("UpdaterSetup.exe",
-                     test_installer->install_params()->run.c_str());
-        EXPECT_STREQ("--arg1 --arg2",
-                     test_installer->install_params()->arguments.c_str());
-      }));
+  {
+    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(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1)
+        .WillOnce(Invoke([update_client](Events event, const std::string& id) {
+          CrxUpdateItem update_item;
+          ASSERT_TRUE(update_client->GetCrxUpdateState(id, &update_item));
+          ASSERT_TRUE(update_item.component);
+          const auto* test_installer = static_cast<TestInstaller*>(
+              update_item.component->installer.get());
+          EXPECT_STREQ("UpdaterSetup.exe",
+                       test_installer->install_params()->run.c_str());
+          EXPECT_STREQ("--arg1 --arg2",
+                       test_installer->install_params()->arguments.c_str());
+        }));
+  }
+
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
   update_client->AddObserver(&observer);
-
   update_client->Install(
       std::string("jebgalgnebhfojomionfpkfelancnnkf"),
       base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
       base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
   RunThreads();
 
+  EXPECT_EQ(6u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[1].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[1].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[2].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[3].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[3].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdating, items[4].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[4].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdated, items[5].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[5].id.c_str());
+
   const base::DictionaryValue* dict =
       config()->GetPrefService()->GetDictionary("updateclientdata");
   std::string pv;
@@ -2575,33 +2866,44 @@
           &MockUpdateChecker::Create, &MockCrxDownloader::Create);
 
   MockObserver observer;
-  InSequence seq;
-  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
-                                "jebgalgnebhfojomionfpkfelancnnkf"))
-      .Times(1)
-      .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
-        // Tests that the state of the component when the CrxComponent data
-        // is not provided. In this case, the optional |item.component| instance
-        // is not present.
-        CrxUpdateItem item;
-        EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
-        EXPECT_EQ(ComponentState::kUpdateError, item.state);
-        EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", item.id.c_str());
-        EXPECT_FALSE(item.component);
-        EXPECT_EQ(ErrorCategory::kService, item.error_category);
-        EXPECT_EQ(static_cast<int>(Error::CRX_NOT_FOUND), item.error_code);
-        EXPECT_EQ(0, item.extra_code1);
-      }));
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1)
+        .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+          // Tests that the state of the component when the CrxComponent data
+          // is not provided. In this case, the optional |item.component|
+          // instance is not present.
+          CrxUpdateItem item;
+          EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+          EXPECT_EQ(ComponentState::kUpdateError, item.state);
+          EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", item.id.c_str());
+          EXPECT_FALSE(item.component);
+          EXPECT_EQ(ErrorCategory::kService, item.error_category);
+          EXPECT_EQ(static_cast<int>(Error::CRX_NOT_FOUND), item.error_code);
+          EXPECT_EQ(0, item.extra_code1);
+        }));
+  }
+
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
   update_client->AddObserver(&observer);
-
   update_client->Install(
       std::string("jebgalgnebhfojomionfpkfelancnnkf"),
       base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
       base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
   RunThreads();
 
+  EXPECT_EQ(1u, items.size());
+  EXPECT_EQ(ComponentState::kUpdateError, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -2714,20 +3016,39 @@
                                 "jebgalgnebhfojomionfpkfelancnnkf"))
       .Times(1);
 
+  std::vector<CrxUpdateItem> items1;
+  auto receiver1 = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver1, Receive(_))
+      .WillRepeatedly(
+          [&items1](const CrxUpdateItem& item) { items1.push_back(item); });
+
+  std::vector<CrxUpdateItem> items2;
+  auto receiver2 = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver2, Receive(_))
+      .WillRepeatedly(
+          [&items2](const CrxUpdateItem& item) { items2.push_back(item); });
+
   update_client->AddObserver(&observer);
-
   update_client->Install(
       std::string("jebgalgnebhfojomionfpkfelancnnkf"),
       base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver1),
       base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
   update_client->Install(
       std::string("jebgalgnebhfojomionfpkfelancnnkf"),
       base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver2),
       base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
   RunThreads();
 
+  EXPECT_EQ(2u, items1.size());
+  EXPECT_EQ(ComponentState::kChecking, items1[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items1[0].id.c_str());
+  EXPECT_EQ(ComponentState::kUpToDate, items1[1].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items1[1].id.c_str());
+
+  EXPECT_TRUE(items2.empty());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -2745,7 +3066,7 @@
   class CompletionCallbackMock {
    public:
     static void Callback(base::OnceClosure quit_closure, Error error) {
-      DCHECK_EQ(Error::INVALID_ARGUMENT, error);
+      EXPECT_EQ(Error::INVALID_ARGUMENT, error);
       std::move(quit_closure).Run();
     }
   };
@@ -2799,7 +3120,7 @@
 
   const std::vector<std::string> empty_id_list;
   update_client->Update(
-      empty_id_list, base::BindOnce(&DataCallbackMock::Callback), false,
+      empty_id_list, base::BindOnce(&DataCallbackMock::Callback), {}, false,
       base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 }
@@ -3020,7 +3341,7 @@
     // The engine handles this Update call but responds with a valid
     // |retry_after_sec|, which causes subsequent calls to fail.
     base::RunLoop runloop;
-    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), {},
                           false,
                           base::BindOnce(&CompletionCallbackMock::Callback,
                                          runloop.QuitClosure()));
@@ -3031,7 +3352,7 @@
     // This call will result in a completion callback invoked with
     // Error::ERROR_UPDATE_RETRY_LATER.
     base::RunLoop runloop;
-    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), {},
                           false,
                           base::BindOnce(&CompletionCallbackMock::Callback,
                                          runloop.QuitClosure()));
@@ -3043,7 +3364,7 @@
     // the value of |retry_after_sec| in the completion callback.
     base::RunLoop runloop;
     update_client->Install(std::string("jebgalgnebhfojomionfpkfelancnnkf"),
-                           base::BindOnce(&DataCallbackMock::Callback),
+                           base::BindOnce(&DataCallbackMock::Callback), {},
                            base::BindOnce(&CompletionCallbackMock::Callback,
                                           runloop.QuitClosure()));
     runloop.Run();
@@ -3052,7 +3373,7 @@
   {
     // This call succeeds.
     base::RunLoop runloop;
-    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback), {},
                           false,
                           base::BindOnce(&CompletionCallbackMock::Callback,
                                          runloop.QuitClosure()));
@@ -3310,16 +3631,41 @@
         .Times(1);
   }
 
-  update_client->AddObserver(&observer);
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
+  update_client->AddObserver(&observer);
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
                                         "ihfokbkgjpifnbbojhneepfflplebdkc"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(9u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kChecking, items[1].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[1].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[2].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdateError, items[3].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[3].id.c_str());
+  EXPECT_EQ(ComponentState::kCanUpdate, items[4].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[4].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[5].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[5].id.c_str());
+  EXPECT_EQ(ComponentState::kDownloading, items[6].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[6].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdating, items[7].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[7].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdated, items[8].state);
+  EXPECT_STREQ("ihfokbkgjpifnbbojhneepfflplebdkc", items[8].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -3404,31 +3750,44 @@
           &MockUpdateChecker::Create, &MockCrxDownloader::Create);
 
   MockObserver observer;
-  InSequence seq;
-  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
-                                "jebgalgnebhfojomionfpkfelancnnkf"))
-      .Times(1);
-  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
-                                "jebgalgnebhfojomionfpkfelancnnkf"))
-      .Times(1)
-      .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
-        CrxUpdateItem item;
-        EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
-        EXPECT_EQ(ComponentState::kUpdateError, item.state);
-        EXPECT_EQ(5, static_cast<int>(item.error_category));
-        EXPECT_EQ(-1, item.error_code);
-        EXPECT_EQ(0, item.extra_code1);
-      }));
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1)
+        .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+          CrxUpdateItem item;
+          EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+          EXPECT_EQ(ComponentState::kUpdateError, item.state);
+          EXPECT_EQ(5, static_cast<int>(item.error_category));
+          EXPECT_EQ(-1, item.error_code);
+          EXPECT_EQ(0, item.extra_code1);
+        }));
+  }
+
+  std::vector<CrxUpdateItem> items;
+  auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>();
+  EXPECT_CALL(*receiver, Receive(_))
+      .WillRepeatedly(
+          [&items](const CrxUpdateItem& item) { items.push_back(item); });
 
   update_client->AddObserver(&observer);
-
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), false,
-      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
-
+      ids, base::BindOnce(&DataCallbackMock::Callback),
+      base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver),
+      false, base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
   RunThreads();
 
+  EXPECT_EQ(2u, items.size());
+  EXPECT_EQ(ComponentState::kChecking, items[0].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id.c_str());
+  EXPECT_EQ(ComponentState::kUpdateError, items[1].state);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", items[1].id.c_str());
+
   update_client->RemoveObserver(&observer);
 }
 
@@ -3636,7 +3995,7 @@
       "jebgalgnebhfojomionfpkfelancnnkf", "abagagagagagagagagagagagagagagag",
       "ihfokbkgjpifnbbojhneepfflplebdkc", "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), true,
+      ids, base::BindOnce(&DataCallbackMock::Callback), {}, true,
       base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
 
   RunThreads();
@@ -3826,6 +4185,7 @@
         crx.crx_format_requirement = crx_file::VerifierFormat::CRX3;
         return std::vector<base::Optional<CrxComponent>>{crx};
       }),
+      {},
       base::BindOnce(
           [](base::OnceClosure quit_closure, Error error) {
             EXPECT_EQ(Error::NONE, error);
@@ -3989,7 +4349,7 @@
             return std::vector<base::Optional<CrxComponent>>{crx};
           },
           unpack_path),
-      false,
+      {}, false,
       base::BindOnce(
           [](base::OnceClosure quit_closure, Error error) {
             EXPECT_EQ(Error::NONE, error);
@@ -4119,7 +4479,7 @@
 
   const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
   update_client->Update(
-      ids, base::BindOnce(&DataCallbackMock::Callback), true,
+      ids, base::BindOnce(&DataCallbackMock::Callback), {}, true,
       base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
 
   RunThreads();
diff --git a/components/update_client/update_engine.cc b/components/update_client/update_engine.cc
index 49f6500..6888b2ca 100644
--- a/components/update_client/update_engine.cc
+++ b/components/update_client/update_engine.cc
@@ -33,6 +33,7 @@
     bool is_foreground,
     const std::vector<std::string>& ids,
     UpdateClient::CrxDataCallback crx_data_callback,
+    UpdateClient::CrxStateChangeCallback crx_state_change_callback,
     const UpdateEngine::NotifyObserversCallback& notify_observers_callback,
     UpdateEngine::Callback callback,
     CrxDownloader::Factory crx_downloader_factory,
@@ -42,6 +43,7 @@
       enabled_component_updates(config->EnabledComponentUpdates()),
       ids(ids),
       crx_data_callback(std::move(crx_data_callback)),
+      crx_state_change_callback(crx_state_change_callback),
       notify_observers_callback(notify_observers_callback),
       callback(std::move(callback)),
       crx_downloader_factory(crx_downloader_factory),
@@ -74,10 +76,12 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 }
 
-void UpdateEngine::Update(bool is_foreground,
-                          const std::vector<std::string>& ids,
-                          UpdateClient::CrxDataCallback crx_data_callback,
-                          Callback callback) {
+void UpdateEngine::Update(
+    bool is_foreground,
+    const std::vector<std::string>& ids,
+    UpdateClient::CrxDataCallback crx_data_callback,
+    UpdateClient::CrxStateChangeCallback crx_state_change_callback,
+    Callback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (ids.empty()) {
@@ -95,8 +99,8 @@
 
   const auto update_context = base::MakeRefCounted<UpdateContext>(
       config_, is_foreground, ids, std::move(crx_data_callback),
-      notify_observers_callback_, std::move(callback), crx_downloader_factory_,
-      metadata_.get());
+      crx_state_change_callback, notify_observers_callback_,
+      std::move(callback), crx_downloader_factory_, metadata_.get());
   DCHECK(!update_context->session_id.empty());
 
   const auto result = update_contexts_.insert(
@@ -134,15 +138,15 @@
 
   if (update_context->components_to_check_for_updates.empty()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent,
-                                  base::Unretained(this), update_context));
+        FROM_HERE,
+        base::BindOnce(&UpdateEngine::HandleComponent, this, update_context));
     return;
   }
 
   for (const auto& id : update_context->components_to_check_for_updates)
     update_context->components[id]->Handle(
-        base::BindOnce(&UpdateEngine::ComponentCheckingForUpdatesStart,
-                       base::Unretained(this), update_context, id));
+        base::BindOnce(&UpdateEngine::ComponentCheckingForUpdatesStart, this,
+                       update_context, id));
 }
 
 void UpdateEngine::ComponentCheckingForUpdatesStart(
@@ -157,8 +161,8 @@
   // Handle |kChecking| state.
   auto& component = *update_context->components.at(id);
   component.Handle(
-      base::BindOnce(&UpdateEngine::ComponentCheckingForUpdatesComplete,
-                     base::Unretained(this), update_context));
+      base::BindOnce(&UpdateEngine::ComponentCheckingForUpdatesComplete, this,
+                     update_context));
 
   ++update_context->num_components_ready_to_check;
   if (update_context->num_components_ready_to_check <
@@ -167,8 +171,8 @@
   }
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&UpdateEngine::DoUpdateCheck,
-                                base::Unretained(this), update_context));
+      FROM_HERE,
+      base::BindOnce(&UpdateEngine::DoUpdateCheck, this, update_context));
 }
 
 void UpdateEngine::DoUpdateCheck(scoped_refptr<UpdateContext> update_context) {
@@ -183,8 +187,8 @@
       update_context->components_to_check_for_updates,
       update_context->components, config_->ExtraRequestParams(),
       update_context->enabled_component_updates,
-      base::BindOnce(&UpdateEngine::UpdateCheckResultsAvailable,
-                     base::Unretained(this), update_context));
+      base::BindOnce(&UpdateEngine::UpdateCheckResultsAvailable, this,
+                     update_context));
 }
 
 void UpdateEngine::UpdateCheckResultsAvailable(
@@ -275,8 +279,8 @@
   }
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&UpdateEngine::UpdateCheckComplete,
-                                base::Unretained(this), update_context));
+      FROM_HERE,
+      base::BindOnce(&UpdateEngine::UpdateCheckComplete, this, update_context));
 }
 
 void UpdateEngine::UpdateCheckComplete(
@@ -288,8 +292,8 @@
     update_context->component_queue.push(id);
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent,
-                                base::Unretained(this), update_context));
+      FROM_HERE,
+      base::BindOnce(&UpdateEngine::HandleComponent, this, update_context));
 }
 
 void UpdateEngine::HandleComponent(
@@ -305,9 +309,8 @@
                             : Error::NONE;
 
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&UpdateEngine::UpdateComplete, base::Unretained(this),
-                       update_context, error));
+        FROM_HERE, base::BindOnce(&UpdateEngine::UpdateComplete, this,
+                                  update_context, error));
     return;
   }
 
@@ -320,18 +323,15 @@
   if (!next_update_delay.is_zero() && component->IsUpdateAvailable()) {
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
-        base::BindOnce(&UpdateEngine::HandleComponent, base::Unretained(this),
-                       update_context),
+        base::BindOnce(&UpdateEngine::HandleComponent, this, update_context),
         next_update_delay);
     next_update_delay = base::TimeDelta();
-
-    notify_observers_callback_.Run(
-        UpdateClient::Observer::Events::COMPONENT_WAIT, id);
+    component->NotifyWait();
     return;
   }
 
-  component->Handle(base::BindOnce(&UpdateEngine::HandleComponentComplete,
-                                   base::Unretained(this), update_context));
+  component->Handle(base::BindOnce(&UpdateEngine::HandleComponentComplete, this,
+                                   update_context));
 }
 
 void UpdateEngine::HandleComponentComplete(
@@ -359,8 +359,8 @@
   }
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent,
-                                base::Unretained(this), update_context));
+      FROM_HERE,
+      base::BindOnce(&UpdateEngine::HandleComponent, this, update_context));
 }
 
 void UpdateEngine::UpdateComplete(scoped_refptr<UpdateContext> update_context,
@@ -411,8 +411,9 @@
 
   const auto update_context = base::MakeRefCounted<UpdateContext>(
       config_, false, std::vector<std::string>{id},
-      UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(),
-      std::move(callback), nullptr, metadata_.get());
+      UpdateClient::CrxDataCallback(), UpdateClient::CrxStateChangeCallback(),
+      UpdateEngine::NotifyObserversCallback(), std::move(callback), nullptr,
+      metadata_.get());
   DCHECK(!update_context->session_id.empty());
 
   const auto result = update_contexts_.insert(
@@ -429,8 +430,8 @@
   update_context->component_queue.push(id);
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent,
-                                base::Unretained(this), update_context));
+      FROM_HERE,
+      base::BindOnce(&UpdateEngine::HandleComponent, this, update_context));
 }
 
 }  // namespace update_client
diff --git a/components/update_client/update_engine.h b/components/update_client/update_engine.h
index 21a8bf1..cdbfcb8 100644
--- a/components/update_client/update_engine.h
+++ b/components/update_client/update_engine.h
@@ -13,7 +13,6 @@
 
 #include "base/callback.h"
 #include "base/containers/queue.h"
-#include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/threading/thread_checker.h"
@@ -51,6 +50,8 @@
                CrxDownloader::Factory crx_downloader_factory,
                scoped_refptr<PingManager> ping_manager,
                const NotifyObserversCallback& notify_observers_callback);
+  UpdateEngine(const UpdateEngine&) = delete;
+  UpdateEngine& operator=(const UpdateEngine&) = delete;
 
   // Returns true and the state of the component identified by |id|, if the
   // component is found in any update context. Returns false if the component
@@ -60,6 +61,7 @@
   void Update(bool is_foreground,
               const std::vector<std::string>& ids,
               UpdateClient::CrxDataCallback crx_data_callback,
+              UpdateClient::CrxStateChangeCallback crx_state_change_callback,
               Callback update_callback);
 
   void SendUninstallPing(const std::string& id,
@@ -115,8 +117,6 @@
   // a certain time, which is negotiated with the server as part of the
   // update protocol. See the comments for X-Retry-After header.
   base::TimeTicks throttle_updates_until_;
-
-  DISALLOW_COPY_AND_ASSIGN(UpdateEngine);
 };
 
 // Describes a group of components which are installed or updated together.
@@ -126,10 +126,13 @@
       bool is_foreground,
       const std::vector<std::string>& ids,
       UpdateClient::CrxDataCallback crx_data_callback,
+      UpdateClient::CrxStateChangeCallback crx_state_change_callback,
       const UpdateEngine::NotifyObserversCallback& notify_observers_callback,
       UpdateEngine::Callback callback,
       CrxDownloader::Factory crx_downloader_factory,
       PersistedData* persisted_data);
+  UpdateContext(const UpdateContext&) = delete;
+  UpdateContext& operator=(const UpdateContext&) = delete;
 
   scoped_refptr<Configurator> config;
 
@@ -149,6 +152,9 @@
   // Called before an update check, when update metadata is needed.
   UpdateEngine::CrxDataCallback crx_data_callback;
 
+  // Called when the observable state of the CRX component has changed.
+  UpdateClient::CrxStateChangeCallback crx_state_change_callback;
+
   // Called when there is a state change for any update in this context.
   const UpdateEngine::NotifyObserversCallback notify_observers_callback;
 
@@ -197,8 +203,6 @@
  private:
   friend class base::RefCounted<UpdateContext>;
   ~UpdateContext();
-
-  DISALLOW_COPY_AND_ASSIGN(UpdateContext);
 };
 
 }  // namespace update_client
diff --git a/extensions/browser/updater/update_service.cc b/extensions/browser/updater/update_service.cc
index 063c4c8..6e96cc2 100644
--- a/extensions/browser/updater/update_service.cc
+++ b/extensions/browser/updater/update_service.cc
@@ -4,6 +4,9 @@
 
 #include "extensions/browser/updater/update_service.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -139,7 +142,7 @@
   if (complete_event) {
     // The update check for |extension_id| has completed, thus it can be
     // removed from all in-progress update checks.
-    DCHECK(updating_extension_ids_.count(extension_id) > 0);
+    DCHECK_GT(updating_extension_ids_.count(extension_id), 0u);
     updating_extension_ids_.erase(extension_id);
 
     bool install_immediately = false;
@@ -244,7 +247,7 @@
         base::BindOnce(&UpdateDataProvider::GetData, update_data_provider_,
                        update_params.install_immediately,
                        std::move(batch_data)),
-        update_params.priority == ExtensionUpdateCheckParams::FOREGROUND,
+        {}, update_params.priority == ExtensionUpdateCheckParams::FOREGROUND,
         base::BindOnce(&UpdateService::UpdateCheckComplete,
                        weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/extensions/browser/updater/update_service_unittest.cc b/extensions/browser/updater/update_service_unittest.cc
index 250506d..d22b073 100644
--- a/extensions/browser/updater/update_service_unittest.cc
+++ b/extensions/browser/updater/update_service_unittest.cc
@@ -80,9 +80,11 @@
   void RemoveObserver(Observer* observer) override {}
   void Install(const std::string& id,
                CrxDataCallback crx_data_callback,
+               CrxStateChangeCallback crx_state_change_callback,
                update_client::Callback callback) override {}
   void Update(const std::vector<std::string>& ids,
               CrxDataCallback crx_data_callback,
+              CrxStateChangeCallback crx_state_change_callback,
               bool is_foreground,
               update_client::Callback callback) override;
   bool GetCrxUpdateState(
@@ -140,6 +142,7 @@
 
 void FakeUpdateClient::Update(const std::vector<std::string>& ids,
                               CrxDataCallback crx_data_callback,
+                              CrxStateChangeCallback crx_state_change_callback,
                               bool is_foreground,
                               update_client::Callback callback) {
   data_ = std::move(crx_data_callback).Run(ids);