| Index: components/update_client/update_engine.cc
 | 
| diff --git a/components/update_client/update_engine.cc b/components/update_client/update_engine.cc
 | 
| index b127befe91b3906c8b6277a1b9ad86ceb8886f34..afef9097097c21598b51bcd0e655cda99f66ee3e 100644
 | 
| --- a/components/update_client/update_engine.cc
 | 
| +++ b/components/update_client/update_engine.cc
 | 
| @@ -4,19 +4,23 @@
 | 
|  
 | 
|  #include "components/update_client/update_engine.h"
 | 
|  
 | 
| +#include <utility>
 | 
| +
 | 
|  #include "base/bind.h"
 | 
|  #include "base/bind_helpers.h"
 | 
|  #include "base/location.h"
 | 
| +#include "base/logging.h"
 | 
| +#include "base/memory/ptr_util.h"
 | 
|  #include "base/stl_util.h"
 | 
|  #include "base/threading/thread_task_runner_handle.h"
 | 
| -#include "base/time/time.h"
 | 
|  #include "components/prefs/pref_service.h"
 | 
| -#include "components/update_client/action_update_check.h"
 | 
| +#include "components/update_client/component.h"
 | 
|  #include "components/update_client/configurator.h"
 | 
|  #include "components/update_client/crx_update_item.h"
 | 
|  #include "components/update_client/persisted_data.h"
 | 
|  #include "components/update_client/update_checker.h"
 | 
|  #include "components/update_client/update_client_errors.h"
 | 
| +#include "components/update_client/utils.h"
 | 
|  
 | 
|  namespace update_client {
 | 
|  
 | 
| @@ -27,9 +31,7 @@ UpdateContext::UpdateContext(
 | 
|      const UpdateClient::CrxDataCallback& crx_data_callback,
 | 
|      const UpdateEngine::NotifyObserversCallback& notify_observers_callback,
 | 
|      const UpdateEngine::Callback& callback,
 | 
| -    UpdateChecker::Factory update_checker_factory,
 | 
| -    CrxDownloader::Factory crx_downloader_factory,
 | 
| -    PingManager* ping_manager)
 | 
| +    CrxDownloader::Factory crx_downloader_factory)
 | 
|      : config(config),
 | 
|        is_foreground(is_foreground),
 | 
|        enabled_component_updates(config->EnabledComponentUpdates()),
 | 
| @@ -37,12 +39,12 @@ UpdateContext::UpdateContext(
 | 
|        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()),
 | 
| -      update_checker_factory(update_checker_factory),
 | 
| -      crx_downloader_factory(crx_downloader_factory),
 | 
| -      ping_manager(ping_manager),
 | 
| -      retry_after_sec(0) {}
 | 
| +      crx_downloader_factory(crx_downloader_factory) {
 | 
| +  for (const auto& id : ids)
 | 
| +    components.insert(
 | 
| +        std::make_pair(id, base::MakeUnique<Component>(*this, id)));
 | 
| +}
 | 
|  
 | 
|  UpdateContext::~UpdateContext() {}
 | 
|  
 | 
| @@ -63,20 +65,6 @@ UpdateEngine::~UpdateEngine() {
 | 
|    DCHECK(thread_checker_.CalledOnValidThread());
 | 
|  }
 | 
|  
 | 
| -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 = update_items.find(id);
 | 
| -    if (it != update_items.end()) {
 | 
| -      *update_item = *it->second.get();
 | 
| -      return true;
 | 
| -    }
 | 
| -  }
 | 
| -  return false;
 | 
| -}
 | 
| -
 | 
|  void UpdateEngine::Update(
 | 
|      bool is_foreground,
 | 
|      const std::vector<std::string>& ids,
 | 
| @@ -90,29 +78,98 @@ void UpdateEngine::Update(
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| -  std::unique_ptr<UpdateContext> update_context(new UpdateContext(
 | 
| +  const auto result = update_contexts_.insert(base::MakeUnique<UpdateContext>(
 | 
|        config_, is_foreground, ids, crx_data_callback,
 | 
| -      notify_observers_callback_, callback, update_checker_factory_,
 | 
| -      crx_downloader_factory_, ping_manager_));
 | 
| +      notify_observers_callback_, callback, crx_downloader_factory_));
 | 
| +
 | 
| +  DCHECK(result.second);
 | 
|  
 | 
| -  std::unique_ptr<ActionUpdateCheck> update_check_action(new ActionUpdateCheck(
 | 
| -      (*update_context->update_checker_factory)(config_, metadata_.get()),
 | 
| -      config_->GetBrowserVersion(), config_->ExtraRequestParams()));
 | 
| +  const auto& it = result.first;
 | 
| +  const auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
|  
 | 
| -  update_context->current_action = std::move(update_check_action);
 | 
| -  update_contexts_.insert(update_context.get());
 | 
| +  // Calls out to get the corresponding CrxComponent data for the CRXs in this
 | 
| +  // update context.
 | 
| +  DCHECK_EQ(ids.size(), update_context->ids.size());
 | 
| +  DCHECK_EQ(update_context->ids.size(), update_context->components.size());
 | 
| +  std::vector<CrxComponent> crx_components;
 | 
| +  update_context->crx_data_callback.Run(update_context->ids, &crx_components);
 | 
| +  DCHECK_EQ(update_context->ids.size(), crx_components.size());
 | 
|  
 | 
| -  update_context->current_action->Run(
 | 
| -      update_context.get(),
 | 
| -      base::Bind(&UpdateEngine::UpdateComplete, base::Unretained(this),
 | 
| -                 update_context.get()));
 | 
| +  for (size_t i = 0; i != update_context->ids.size(); ++i) {
 | 
| +    const auto& id = update_context->ids[i];
 | 
| +    const auto& crx_component = crx_components[i];
 | 
|  
 | 
| -  ignore_result(update_context.release());
 | 
| +    DCHECK_EQ(id, GetCrxComponentID(crx_component));
 | 
| +    DCHECK_EQ(1u, update_context->components.count(id));
 | 
| +    DCHECK(update_context->components.at(id));
 | 
| +
 | 
| +    auto& component = *update_context->components.at(id);
 | 
| +    component.set_on_demand(update_context->is_foreground);
 | 
| +    component.set_crx_component(crx_component);
 | 
| +    component.set_previous_version(crx_component.version);
 | 
| +    component.set_previous_fp(crx_component.fingerprint);
 | 
| +
 | 
| +    // Handle |kNew| state. This will transition the components to |kChecking|.
 | 
| +    component.Handle(base::Bind(&UpdateEngine::ComponentCheckingForUpdatesStart,
 | 
| +                                base::Unretained(this), it,
 | 
| +                                base::ConstRef(component)));
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void UpdateEngine::ComponentCheckingForUpdatesStart(
 | 
| +    const UpdateContextIterator& it,
 | 
| +    const Component& component) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
| +  const auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +
 | 
| +  const auto id = component.id();
 | 
| +  DCHECK_EQ(1u, update_context->components.count(id));
 | 
| +  DCHECK(update_context->components.at(id));
 | 
| +
 | 
| +  // Handle |kChecking| state.
 | 
| +  auto& mutable_component = *update_context->components.at(id);
 | 
| +  mutable_component.Handle(base::Bind(
 | 
| +      &UpdateEngine::ComponentCheckingForUpdatesComplete,
 | 
| +      base::Unretained(this), it, base::ConstRef(mutable_component)));
 | 
| +
 | 
| +  ++update_context->num_components_ready_to_check;
 | 
| +  if (update_context->num_components_ready_to_check <
 | 
| +      update_context->ids.size()) {
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  base::ThreadTaskRunnerHandle::Get()->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&UpdateEngine::DoUpdateCheck, base::Unretained(this), it));
 | 
|  }
 | 
|  
 | 
| -void UpdateEngine::UpdateComplete(UpdateContext* update_context, Error error) {
 | 
| +void UpdateEngine::DoUpdateCheck(const UpdateContextIterator& it) {
 | 
|    DCHECK(thread_checker_.CalledOnValidThread());
 | 
| -  DCHECK(update_contexts_.find(update_context) != update_contexts_.end());
 | 
| +
 | 
| +  auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +
 | 
| +  update_context->update_checker =
 | 
| +      update_checker_factory_(config_, metadata_.get());
 | 
| +
 | 
| +  update_context->update_checker->CheckForUpdates(
 | 
| +      update_context->ids, update_context->components,
 | 
| +      config_->ExtraRequestParams(), update_context->enabled_component_updates,
 | 
| +      base::Bind(&UpdateEngine::UpdateCheckDone, base::Unretained(this), it));
 | 
| +}
 | 
| +
 | 
| +void UpdateEngine::UpdateCheckDone(const UpdateContextIterator& it,
 | 
| +                                   int error,
 | 
| +                                   int retry_after_sec) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
| +  auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +
 | 
| +  update_context->retry_after_sec = retry_after_sec;
 | 
|  
 | 
|    const int throttle_sec(update_context->retry_after_sec);
 | 
|    DCHECK_LE(throttle_sec, 24 * 60 * 60);
 | 
| @@ -120,22 +177,161 @@ void UpdateEngine::UpdateComplete(UpdateContext* update_context, Error error) {
 | 
|    // Only positive values for throttle_sec are effective. 0 means that no
 | 
|    // throttling occurs and has the effect of resetting the member.
 | 
|    // Negative values are not trusted and are ignored.
 | 
| -  if (throttle_sec >= 0)
 | 
| +  if (throttle_sec >= 0) {
 | 
|      throttle_updates_until_ =
 | 
| -        throttle_sec
 | 
| -            ? base::TimeTicks::Now() +
 | 
| -                  base::TimeDelta::FromSeconds(throttle_sec)
 | 
| -            : base::TimeTicks();
 | 
| +        throttle_sec ? base::TimeTicks::Now() +
 | 
| +                           base::TimeDelta::FromSeconds(throttle_sec)
 | 
| +                     : base::TimeTicks();
 | 
| +  }
 | 
| +
 | 
| +  update_context->update_check_error = error;
 | 
|  
 | 
| -  auto callback = update_context->callback;
 | 
| +  for (const auto& id : update_context->ids) {
 | 
| +    DCHECK_EQ(1u, update_context->components.count(id));
 | 
| +    DCHECK(update_context->components.at(id));
 | 
| +
 | 
| +    auto& component = *update_context->components.at(id);
 | 
| +    component.UpdateCheckComplete();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void UpdateEngine::ComponentCheckingForUpdatesComplete(
 | 
| +    const UpdateContextIterator& it,
 | 
| +    const Component& component) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
| +  const auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +
 | 
| +  ++update_context->num_components_checked;
 | 
| +  if (update_context->num_components_checked < update_context->ids.size()) {
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  base::ThreadTaskRunnerHandle::Get()->PostTask(
 | 
| +      FROM_HERE, base::Bind(&UpdateEngine::UpdateCheckComplete,
 | 
| +                            base::Unretained(this), it));
 | 
| +}
 | 
| +
 | 
| +void UpdateEngine::UpdateCheckComplete(const UpdateContextIterator& it) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
| +  const auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +
 | 
| +  if (update_context->update_check_error) {
 | 
| +    base::ThreadTaskRunnerHandle::Get()->PostTask(
 | 
| +        FROM_HERE,
 | 
| +        base::Bind(&UpdateEngine::UpdateComplete, base::Unretained(this), it,
 | 
| +                   Error::UPDATE_CHECK_ERROR));
 | 
| +    return;
 | 
| +  }
 | 
|  
 | 
| -  update_contexts_.erase(update_context);
 | 
| -  delete update_context;
 | 
| +  for (const auto& id : update_context->ids)
 | 
| +    update_context->component_queue.push(id);
 | 
|  
 | 
| -  callback.Run(error);
 | 
| +  base::ThreadTaskRunnerHandle::Get()->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&UpdateEngine::HandleComponent, base::Unretained(this), it));
 | 
| +}
 | 
| +
 | 
| +void UpdateEngine::HandleComponent(const UpdateContextIterator& it) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
| +  auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +
 | 
| +  auto& queue = update_context->component_queue;
 | 
| +
 | 
| +  if (queue.empty()) {
 | 
| +    base::ThreadTaskRunnerHandle::Get()->PostTask(
 | 
| +        FROM_HERE, base::Bind(&UpdateEngine::UpdateComplete,
 | 
| +                              base::Unretained(this), it, Error::NONE));
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  const auto& id = queue.front();
 | 
| +  DCHECK_EQ(1u, update_context->components.count(id));
 | 
| +  const auto& component = update_context->components.at(id);
 | 
| +  DCHECK(component);
 | 
| +
 | 
| +  auto& next_update_delay = (*it)->next_update_delay;
 | 
| +  if (!next_update_delay.is_zero() && component->IsUpdateAvailable()) {
 | 
| +    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
 | 
| +        FROM_HERE,
 | 
| +        base::Bind(&UpdateEngine::HandleComponent, base::Unretained(this), it),
 | 
| +        next_update_delay);
 | 
| +
 | 
| +    next_update_delay = base::TimeDelta();
 | 
| +
 | 
| +    notify_observers_callback_.Run(
 | 
| +        UpdateClient::Observer::Events::COMPONENT_WAIT, id);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  component->Handle(base::Bind(&UpdateEngine::HandleComponentComplete,
 | 
| +                               base::Unretained(this), it));
 | 
| +}
 | 
| +
 | 
| +void UpdateEngine::HandleComponentComplete(const UpdateContextIterator& it) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
| +  auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +
 | 
| +  auto& queue = update_context->component_queue;
 | 
| +  DCHECK(!queue.empty());
 | 
| +
 | 
| +  const auto& id = queue.front();
 | 
| +  DCHECK_EQ(1u, update_context->components.count(id));
 | 
| +  const auto& component = update_context->components.at(id);
 | 
| +  DCHECK(component);
 | 
| +
 | 
| +  if (component->IsHandled()) {
 | 
| +    (*it)->next_update_delay = component->GetUpdateDuration();
 | 
| +
 | 
| +    // Only ping when the server response included an update for this component.
 | 
| +    if (component->CanPing()) {
 | 
| +      ping_manager_->SendPing(*component);
 | 
| +    }
 | 
| +
 | 
| +    queue.pop();
 | 
| +  }
 | 
| +
 | 
| +  base::ThreadTaskRunnerHandle::Get()->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&UpdateEngine::HandleComponent, base::Unretained(this), it));
 | 
| +}
 | 
| +
 | 
| +void UpdateEngine::UpdateComplete(const UpdateContextIterator& it,
 | 
| +                                  Error error) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
| +  auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +
 | 
| +  base::ThreadTaskRunnerHandle::Get()->PostTask(
 | 
| +      FROM_HERE, base::Bind(update_context->callback, error));
 | 
| +
 | 
| +  update_contexts_.erase(it);
 | 
| +}
 | 
| +
 | 
| +bool UpdateEngine::GetUpdateState(const std::string& id,
 | 
| +                                  CrxUpdateItem* update_item) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +  for (const auto& context : update_contexts_) {
 | 
| +    const auto it = context->components.find(id);
 | 
| +    if (it != context->components.end()) {
 | 
| +      *update_item = it->second->GetCrxUpdateItem();
 | 
| +      return true;
 | 
| +    }
 | 
| +  }
 | 
| +  return false;
 | 
|  }
 | 
|  
 | 
|  bool UpdateEngine::IsThrottled(bool is_foreground) const {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
|    if (is_foreground || throttle_updates_until_.is_null())
 | 
|      return false;
 | 
|  
 | 
| @@ -147,4 +343,33 @@ bool UpdateEngine::IsThrottled(bool is_foreground) const {
 | 
|           now < throttle_updates_until_;
 | 
|  }
 | 
|  
 | 
| +void UpdateEngine::SendUninstallPing(const std::string& id,
 | 
| +                                     const base::Version& version,
 | 
| +                                     int reason,
 | 
| +                                     const Callback& callback) {
 | 
| +  DCHECK(thread_checker_.CalledOnValidThread());
 | 
| +
 | 
| +  const auto result = update_contexts_.insert(base::MakeUnique<UpdateContext>(
 | 
| +      config_, false, std::vector<std::string>{id},
 | 
| +      UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(),
 | 
| +      callback, nullptr));
 | 
| +
 | 
| +  DCHECK(result.second);
 | 
| +
 | 
| +  const auto& it = result.first;
 | 
| +  const auto& update_context = *it;
 | 
| +  DCHECK(update_context);
 | 
| +  DCHECK_EQ(1u, update_context->ids.size());
 | 
| +  DCHECK_EQ(1u, update_context->components.count(id));
 | 
| +  const auto& component = update_context->components.at(id);
 | 
| +
 | 
| +  component->Uninstall(version, reason);
 | 
| +
 | 
| +  update_context->component_queue.push(id);
 | 
| +
 | 
| +  base::ThreadTaskRunnerHandle::Get()->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&UpdateEngine::HandleComponent, base::Unretained(this), it));
 | 
| +}
 | 
| +
 | 
|  }  // namespace update_client
 | 
| 
 |