blob: ec2b940996a02230d8ab3c85eea8c4ff9dd548d7 [file] [log] [blame]
sorin30474f02017-04-27 00:45:481// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/update_client/component.h"
6
7#include <algorithm>
8#include <utility>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/files/file_util.h"
13#include "base/location.h"
14#include "base/logging.h"
15#include "base/memory/ptr_util.h"
16#include "base/single_thread_task_runner.h"
17#include "base/threading/thread_task_runner_handle.h"
sorincca1c122017-05-11 17:43:2218#include "components/update_client/component_unpacker.h"
sorin30474f02017-04-27 00:45:4819#include "components/update_client/configurator.h"
20#include "components/update_client/update_client.h"
21#include "components/update_client/update_client_errors.h"
22#include "components/update_client/update_engine.h"
23#include "components/update_client/utils.h"
24
25// The state machine representing how a CRX component changes during an update.
26//
27//
28// on-demand on-demand
29// +--------------------------> kNew <---------------+-------------+
30// | | | |
31// | V | |
32// | +---------------0----> kChecking -<-------+---|---<-----+ |
33// | | | | | | |
34// | | error V no | | | |
35// kUpdateError <------------- [update?] ->---- kUpToDate kUpdated
36// ^ | ^
37// | yes | |
38// | V |
39// | kCanUpdate |
40// | | |
41// | V no |
42// | [differential update?]--->----+ |
43// | | | |
44// | yes | | |
45// | V error | |
46// | kDownloadingDiff --->---------+ |
47// | | | |
48// | | | |
49// | V error | |
50// | kUpdatingDiff --->--------+-----------+ success
51// | | |
52// | error V |
53// +----------------------------------------- kDownloading |
54// | | |
55// | error V |
56// +------------------------------------------ kUpdating ->----+ success
57
58namespace update_client {
59
60namespace {
61
62using InstallOnBlockingTaskRunnerCompleteCallback =
63 base::Callback<void(int error_category, int error_code, int extra_code1)>;
64
65CrxInstaller::Result DoInstallOnBlockingTaskRunner(
66 const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
67 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
68 const base::FilePath& unpack_path,
69 const std::string& fingerprint,
70 const scoped_refptr<CrxInstaller>& installer,
71 InstallOnBlockingTaskRunnerCompleteCallback callback) {
72 DCHECK(blocking_task_runner->RunsTasksOnCurrentThread());
73
74 if (static_cast<int>(fingerprint.size()) !=
75 base::WriteFile(
76 unpack_path.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
77 fingerprint.c_str(), base::checked_cast<int>(fingerprint.size()))) {
78 return CrxInstaller::Result(InstallError::FINGERPRINT_WRITE_FAILED);
79 }
80
81 std::unique_ptr<base::DictionaryValue> manifest = ReadManifest(unpack_path);
82 if (!manifest)
83 return CrxInstaller::Result(InstallError::BAD_MANIFEST);
84
85 return installer->Install(*manifest, unpack_path);
86}
87
88void InstallOnBlockingTaskRunner(
89 const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
90 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
91 const base::FilePath& unpack_path,
92 const std::string& fingerprint,
93 const scoped_refptr<CrxInstaller>& installer,
94 InstallOnBlockingTaskRunnerCompleteCallback callback) {
95 DCHECK(blocking_task_runner->RunsTasksOnCurrentThread());
96
97 const auto result = DoInstallOnBlockingTaskRunner(
98 main_task_runner, blocking_task_runner, unpack_path, fingerprint,
99 installer, callback);
100
101 const ErrorCategory error_category =
102 result.error ? ErrorCategory::kInstallError : ErrorCategory::kErrorNone;
103 main_task_runner->PostTask(
104 FROM_HERE,
105 base::Bind(callback, static_cast<int>(error_category),
106 static_cast<int>(result.error), result.extended_error));
107}
108
109void UnpackCompleteOnBlockingTaskRunner(
110 const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
111 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
112 const base::FilePath& crx_path,
113 const std::string& fingerprint,
114 const scoped_refptr<CrxInstaller>& installer,
115 InstallOnBlockingTaskRunnerCompleteCallback callback,
116 const ComponentUnpacker::Result& result) {
117 DCHECK(blocking_task_runner->RunsTasksOnCurrentThread());
118
119 update_client::DeleteFileAndEmptyParentDirectory(crx_path);
120
121 if (result.error != UnpackerError::kNone) {
122 main_task_runner->PostTask(
123 FROM_HERE,
124 base::Bind(callback, static_cast<int>(ErrorCategory::kUnpackError),
125 static_cast<int>(result.error), result.extended_error));
126 return;
127 }
128
129 blocking_task_runner->PostTask(
130 FROM_HERE, base::Bind(&InstallOnBlockingTaskRunner, main_task_runner,
131 blocking_task_runner, result.unpack_path,
132 fingerprint, installer, callback));
133}
134
135void StartInstallOnBlockingTaskRunner(
136 const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
137 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
138 const std::vector<uint8_t>& pk_hash,
139 const base::FilePath& crx_path,
140 const std::string& fingerprint,
141 const scoped_refptr<CrxInstaller>& installer,
142 const scoped_refptr<OutOfProcessPatcher>& oop_patcher,
143 InstallOnBlockingTaskRunnerCompleteCallback callback) {
144 DCHECK(blocking_task_runner->RunsTasksOnCurrentThread());
145
Taiju Tsuiki36c517d2017-05-18 06:45:43146 auto unpacker = base::MakeRefCounted<ComponentUnpacker>(
sorin30474f02017-04-27 00:45:48147 pk_hash, crx_path, installer, oop_patcher, blocking_task_runner);
148
149 unpacker->Unpack(base::Bind(&UnpackCompleteOnBlockingTaskRunner,
150 main_task_runner, blocking_task_runner, crx_path,
151 fingerprint, installer, callback));
152}
153
154} // namespace
155
156Component::Component(const UpdateContext& update_context, const std::string& id)
157 : id_(id),
158 state_(base::MakeUnique<StateNew>(this)),
159 update_context_(update_context) {}
160
161Component::~Component() {}
162
163void Component::Handle(CallbackHandleComplete callback) {
164 DCHECK(thread_checker_.CalledOnValidThread());
165 DCHECK(state_);
166
167 callback_handle_complete_ = callback;
168
169 state_->Handle(base::Bind(&Component::ChangeState, base::Unretained(this)));
170}
171
172void Component::ChangeState(std::unique_ptr<State> next_state) {
173 DCHECK(thread_checker_.CalledOnValidThread());
174
175 if (next_state)
176 state_ = std::move(next_state);
177
178 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
179 callback_handle_complete_);
180}
181
182CrxUpdateItem Component::GetCrxUpdateItem() const {
183 DCHECK(thread_checker_.CalledOnValidThread());
184
185 CrxUpdateItem crx_update_item;
186 crx_update_item.state = state_->state();
187 crx_update_item.id = id_;
188 crx_update_item.component = crx_component_;
189 crx_update_item.last_check = last_check_;
190 crx_update_item.next_version = next_version_;
191 crx_update_item.next_fp = next_fp_;
192
193 return crx_update_item;
194}
195
sorin7cff6e52017-05-17 16:37:23196void Component::SetParseResult(const ProtocolParser::Result& result) {
sorin30474f02017-04-27 00:45:48197 DCHECK(thread_checker_.CalledOnValidThread());
198
199 DCHECK_EQ(0, update_check_error_);
200
201 status_ = result.status;
sorin519656c2017-04-28 22:39:34202 action_run_ = result.action_run;
sorin30474f02017-04-27 00:45:48203
204 if (result.manifest.packages.empty())
205 return;
206
207 next_version_ = base::Version(result.manifest.version);
208 const auto& package = result.manifest.packages.front();
209 next_fp_ = package.fingerprint;
210
211 // Resolve the urls by combining the base urls with the package names.
212 for (const auto& crx_url : result.crx_urls) {
213 const GURL url = crx_url.Resolve(package.name);
214 if (url.is_valid())
215 crx_urls_.push_back(url);
216 }
217 for (const auto& crx_diffurl : result.crx_diffurls) {
218 const GURL url = crx_diffurl.Resolve(package.namediff);
219 if (url.is_valid())
220 crx_diffurls_.push_back(url);
221 }
222
223 hash_sha256_ = package.hash_sha256;
224 hashdiff_sha256_ = package.hashdiff_sha256;
225}
226
227void Component::Uninstall(const base::Version& version, int reason) {
228 DCHECK(thread_checker_.CalledOnValidThread());
229
230 DCHECK_EQ(ComponentState::kNew, state());
231
232 previous_version_ = version;
233 next_version_ = base::Version("0");
234 extra_code1_ = reason;
235
236 state_ = base::MakeUnique<StateUninstalled>(this);
237}
238
239void Component::UpdateCheckComplete() const {
240 DCHECK(thread_checker_.CalledOnValidThread());
241
242 DCHECK_EQ(ComponentState::kChecking, state());
243
244 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
245 update_check_complete_);
246}
247
248bool Component::CanDoBackgroundDownload() const {
249 // On demand component updates are always downloaded in foreground.
250 return !on_demand_ && crx_component_.allows_background_download &&
251 update_context_.config->EnabledBackgroundDownloader();
252}
253
254void Component::AppendDownloadMetrics(
255 const std::vector<CrxDownloader::DownloadMetrics>& download_metrics) {
256 download_metrics_.insert(download_metrics_.end(), download_metrics.begin(),
257 download_metrics.end());
258}
259
260void Component::NotifyObservers(UpdateClient::Observer::Events event) const {
261 DCHECK(thread_checker_.CalledOnValidThread());
262 update_context_.notify_observers_callback.Run(event, id_);
263}
264
265base::TimeDelta Component::GetUpdateDuration() const {
266 DCHECK(thread_checker_.CalledOnValidThread());
267
268 if (update_begin_.is_null())
269 return base::TimeDelta();
270
271 const base::TimeDelta update_cost(base::TimeTicks::Now() - update_begin_);
272 DCHECK_GE(update_cost, base::TimeDelta());
273 const base::TimeDelta max_update_delay =
274 base::TimeDelta::FromSeconds(update_context_.config->UpdateDelay());
275 return std::min(update_cost, max_update_delay);
276}
277
278Component::State::State(Component* component, ComponentState state)
279 : state_(state), component_(*component) {}
280
281Component::State::~State() {}
282
283void Component::State::Handle(CallbackNextState callback) {
284 DCHECK(thread_checker_.CalledOnValidThread());
285
286 callback_ = callback;
287
288 DCHECK(!is_final_);
289 DoHandle();
290}
291
292void Component::State::TransitionState(std::unique_ptr<State> next_state) {
293 if (!next_state)
294 is_final_ = true;
295
296 base::ThreadTaskRunnerHandle::Get()->PostTask(
297 FROM_HERE, base::Bind(callback(), base::Passed(&next_state)));
298}
299
300Component::StateNew::StateNew(Component* component)
301 : State(component, ComponentState::kNew) {}
302
303Component::StateNew::~StateNew() {
304 DCHECK(thread_checker_.CalledOnValidThread());
305}
306
307void Component::StateNew::DoHandle() {
308 DCHECK(thread_checker_.CalledOnValidThread());
309
310 auto& component = State::component();
311
312 TransitionState(base::MakeUnique<StateChecking>(&component));
313}
314
315Component::StateChecking::StateChecking(Component* component)
316 : State(component, ComponentState::kChecking) {}
317
318Component::StateChecking::~StateChecking() {
319 DCHECK(thread_checker_.CalledOnValidThread());
320}
321
322// Unlike how other states are handled, this function does not change the
323// state right away. The state transition happens when the UpdateChecker
324// calls Component::UpdateCheckComplete and |update_check_complete_| is invoked.
325// This is an artifact of how multiple components must be checked for updates
326// together but the state machine defines the transitions for one component
327// at a time.
328void Component::StateChecking::DoHandle() {
329 DCHECK(thread_checker_.CalledOnValidThread());
330
331 auto& component = State::component();
332
333 component.last_check_ = base::TimeTicks::Now();
334 component.update_check_complete_ = base::Bind(
335 &Component::StateChecking::UpdateCheckComplete, base::Unretained(this));
336
337 component.NotifyObservers(Events::COMPONENT_CHECKING_FOR_UPDATES);
338}
339
340void Component::StateChecking::UpdateCheckComplete() {
341 DCHECK(thread_checker_.CalledOnValidThread());
342 auto& component = State::component();
343 if (!component.update_check_error_) {
344 if (component.status_ == "ok") {
345 TransitionState(base::MakeUnique<StateCanUpdate>(&component));
346 return;
347 }
348
349 if (component.status_ == "noupdate") {
350 TransitionState(base::MakeUnique<StateUpToDate>(&component));
351 return;
352 }
353 }
354
355 TransitionState(base::MakeUnique<StateUpdateError>(&component));
356}
357
358Component::StateUpdateError::StateUpdateError(Component* component)
359 : State(component, ComponentState::kUpdateError) {}
360
361Component::StateUpdateError::~StateUpdateError() {
362 DCHECK(thread_checker_.CalledOnValidThread());
363}
364
365void Component::StateUpdateError::DoHandle() {
366 DCHECK(thread_checker_.CalledOnValidThread());
367
368 auto& component = State::component();
369 TransitionState(nullptr);
370 component.NotifyObservers(Events::COMPONENT_NOT_UPDATED);
371}
372
373Component::StateCanUpdate::StateCanUpdate(Component* component)
374 : State(component, ComponentState::kCanUpdate) {}
375
376Component::StateCanUpdate::~StateCanUpdate() {
377 DCHECK(thread_checker_.CalledOnValidThread());
378}
379
380void Component::StateCanUpdate::DoHandle() {
381 DCHECK(thread_checker_.CalledOnValidThread());
382
383 auto& component = State::component();
384
385 component.is_update_available_ = true;
386 component.NotifyObservers(Events::COMPONENT_UPDATE_FOUND);
387
388 if (component.crx_component_.supports_group_policy_enable_component_updates &&
389 !component.update_context_.enabled_component_updates) {
390 component.error_category_ = static_cast<int>(ErrorCategory::kServiceError);
391 component.error_code_ = static_cast<int>(ServiceError::UPDATE_DISABLED);
392 component.extra_code1_ = 0;
393 TransitionState(base::MakeUnique<StateUpdateError>(&component));
394 return;
395 }
396
397 // Start computing the cost of the this update from here on.
398 component.update_begin_ = base::TimeTicks::Now();
399
400 if (CanTryDiffUpdate())
401 TransitionState(base::MakeUnique<StateDownloadingDiff>(&component));
402 else
403 TransitionState(base::MakeUnique<StateDownloading>(&component));
404}
405
406// Returns true if a differential update is available, it has not failed yet,
407// and the configuration allows this update.
408bool Component::StateCanUpdate::CanTryDiffUpdate() const {
409 const auto& component = Component::State::component();
410 return HasDiffUpdate(component) && !component.diff_error_code_ &&
411 component.update_context_.config->EnabledDeltas();
412}
413
414Component::StateUpToDate::StateUpToDate(Component* component)
415 : State(component, ComponentState::kUpToDate) {}
416
417Component::StateUpToDate::~StateUpToDate() {
418 DCHECK(thread_checker_.CalledOnValidThread());
419}
420
421void Component::StateUpToDate::DoHandle() {
422 DCHECK(thread_checker_.CalledOnValidThread());
423
424 auto& component = State::component();
425
426 TransitionState(nullptr);
427 component.NotifyObservers(Events::COMPONENT_NOT_UPDATED);
428}
429
430Component::StateDownloadingDiff::StateDownloadingDiff(Component* component)
431 : State(component, ComponentState::kDownloadingDiff) {}
432
433Component::StateDownloadingDiff::~StateDownloadingDiff() {
434 DCHECK(thread_checker_.CalledOnValidThread());
435}
436
437void Component::StateDownloadingDiff::DoHandle() {
438 DCHECK(thread_checker_.CalledOnValidThread());
439
440 const auto& component = Component::State::component();
441 const auto& update_context = component.update_context_;
442
443 crx_downloader_ = update_context.crx_downloader_factory(
444 component.CanDoBackgroundDownload(),
445 update_context.config->RequestContext(),
446 update_context.blocking_task_runner);
447
448 const auto& id = component.id_;
449 crx_downloader_->set_progress_callback(
450 base::Bind(&Component::StateDownloadingDiff::DownloadProgress,
451 base::Unretained(this), id));
452 crx_downloader_->StartDownload(
453 component.crx_diffurls_, component.hashdiff_sha256_,
454 base::Bind(&Component::StateDownloadingDiff::DownloadComplete,
455 base::Unretained(this), id));
456
457 component.NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING);
458}
459
460// Called when progress is being made downloading a CRX. The progress may
461// not monotonically increase due to how the CRX downloader switches between
462// different downloaders and fallback urls.
463void Component::StateDownloadingDiff::DownloadProgress(
464 const std::string& id,
465 const CrxDownloader::Result& download_result) {
466 DCHECK(thread_checker_.CalledOnValidThread());
467
468 component().NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING);
469}
470
471void Component::StateDownloadingDiff::DownloadComplete(
472 const std::string& id,
473 const CrxDownloader::Result& download_result) {
474 DCHECK(thread_checker_.CalledOnValidThread());
475
476 auto& component = Component::State::component();
477
478 component.AppendDownloadMetrics(crx_downloader_->download_metrics());
479
480 crx_downloader_.reset();
481
482 if (download_result.error) {
483 component.diff_error_category_ =
484 static_cast<int>(ErrorCategory::kNetworkError);
485 component.diff_error_code_ = download_result.error;
486
487 TransitionState(base::MakeUnique<StateDownloading>(&component));
488 return;
489 }
490
491 component.crx_path_ = download_result.response;
492
493 TransitionState(base::MakeUnique<StateUpdatingDiff>(&component));
494}
495
496Component::StateDownloading::StateDownloading(Component* component)
497 : State(component, ComponentState::kDownloading) {}
498
499Component::StateDownloading::~StateDownloading() {
500 DCHECK(thread_checker_.CalledOnValidThread());
501}
502
503void Component::StateDownloading::DoHandle() {
504 DCHECK(thread_checker_.CalledOnValidThread());
505
506 const auto& component = Component::State::component();
507 const auto& update_context = component.update_context_;
508
509 crx_downloader_ = update_context.crx_downloader_factory(
510 component.CanDoBackgroundDownload(),
511 update_context.config->RequestContext(),
512 update_context.blocking_task_runner);
513
514 const auto& id = component.id_;
515 crx_downloader_->set_progress_callback(
516 base::Bind(&Component::StateDownloading::DownloadProgress,
517 base::Unretained(this), id));
518 crx_downloader_->StartDownload(
519 component.crx_urls_, component.hash_sha256_,
520 base::Bind(&Component::StateDownloading::DownloadComplete,
521 base::Unretained(this), id));
522
523 component.NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING);
524}
525
526// Called when progress is being made downloading a CRX. The progress may
527// not monotonically increase due to how the CRX downloader switches between
528// different downloaders and fallback urls.
529void Component::StateDownloading::DownloadProgress(
530 const std::string& id,
531 const CrxDownloader::Result& download_result) {
532 DCHECK(thread_checker_.CalledOnValidThread());
533
534 component().NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING);
535}
536
537void Component::StateDownloading::DownloadComplete(
538 const std::string& id,
539 const CrxDownloader::Result& download_result) {
540 DCHECK(thread_checker_.CalledOnValidThread());
541
542 auto& component = Component::State::component();
543
544 component.AppendDownloadMetrics(crx_downloader_->download_metrics());
545
546 crx_downloader_.reset();
547
548 if (download_result.error) {
549 component.error_category_ = static_cast<int>(ErrorCategory::kNetworkError);
550 component.error_code_ = download_result.error;
551
552 TransitionState(base::MakeUnique<StateUpdateError>(&component));
553 return;
554 }
555
556 component.crx_path_ = download_result.response;
557
558 TransitionState(base::MakeUnique<StateUpdating>(&component));
559}
560
561Component::StateUpdatingDiff::StateUpdatingDiff(Component* component)
562 : State(component, ComponentState::kUpdatingDiff) {}
563
564Component::StateUpdatingDiff::~StateUpdatingDiff() {
565 DCHECK(thread_checker_.CalledOnValidThread());
566}
567
568void Component::StateUpdatingDiff::DoHandle() {
569 DCHECK(thread_checker_.CalledOnValidThread());
570
571 const auto& component = Component::State::component();
572 const auto& update_context = component.update_context_;
573
574 component.NotifyObservers(Events::COMPONENT_UPDATE_READY);
575
576 update_context.blocking_task_runner->PostTask(
577 FROM_HERE,
578 base::Bind(&update_client::StartInstallOnBlockingTaskRunner,
579 base::ThreadTaskRunnerHandle::Get(),
580 update_context.blocking_task_runner,
581 component.crx_component_.pk_hash, component.crx_path_,
582 component.next_fp_, component.crx_component_.installer,
583 update_context.config->CreateOutOfProcessPatcher(),
584 base::Bind(&Component::StateUpdatingDiff::InstallComplete,
585 base::Unretained(this))));
586}
587
588void Component::StateUpdatingDiff::InstallComplete(int error_category,
589 int error_code,
590 int extra_code1) {
591 DCHECK(thread_checker_.CalledOnValidThread());
592
593 auto& component = Component::State::component();
594
595 component.diff_error_category_ = error_category;
596 component.diff_error_code_ = error_code;
597 component.diff_extra_code1_ = extra_code1;
598
599 if (component.diff_error_code_ != 0) {
600 TransitionState(base::MakeUnique<StateDownloading>(&component));
601 return;
602 }
603
604 DCHECK_EQ(static_cast<int>(ErrorCategory::kErrorNone),
605 component.diff_error_category_);
606 DCHECK_EQ(0, component.diff_error_code_);
607 DCHECK_EQ(0, component.diff_extra_code1_);
608
609 DCHECK_EQ(static_cast<int>(ErrorCategory::kErrorNone),
610 component.error_category_);
611 DCHECK_EQ(0, component.error_code_);
612 DCHECK_EQ(0, component.extra_code1_);
613
614 TransitionState(base::MakeUnique<StateUpdated>(&component));
615}
616
617Component::StateUpdating::StateUpdating(Component* component)
sorinff403ef2017-05-16 21:48:42618 : State(component, ComponentState::kUpdating) {}
sorin30474f02017-04-27 00:45:48619
620Component::StateUpdating::~StateUpdating() {
621 DCHECK(thread_checker_.CalledOnValidThread());
622}
623
624void Component::StateUpdating::DoHandle() {
625 DCHECK(thread_checker_.CalledOnValidThread());
626
627 const auto& component = Component::State::component();
628 const auto& update_context = component.update_context_;
629
630 component.NotifyObservers(Events::COMPONENT_UPDATE_READY);
631
632 update_context.blocking_task_runner->PostTask(
633 FROM_HERE,
634 base::Bind(&update_client::StartInstallOnBlockingTaskRunner,
635 base::ThreadTaskRunnerHandle::Get(),
636 update_context.blocking_task_runner,
637 component.crx_component_.pk_hash, component.crx_path_,
638 component.next_fp_, component.crx_component_.installer,
639 update_context.config->CreateOutOfProcessPatcher(),
640 base::Bind(&Component::StateUpdating::InstallComplete,
641 base::Unretained(this))));
642}
643
644void Component::StateUpdating::InstallComplete(int error_category,
645 int error_code,
646 int extra_code1) {
647 DCHECK(thread_checker_.CalledOnValidThread());
648
649 auto& component = Component::State::component();
650
651 component.error_category_ = error_category;
652 component.error_code_ = error_code;
653 component.extra_code1_ = extra_code1;
654
655 if (component.error_code_ != 0) {
656 TransitionState(base::MakeUnique<StateUpdateError>(&component));
657 return;
658 }
659
660 DCHECK_EQ(static_cast<int>(ErrorCategory::kErrorNone),
661 component.error_category_);
662 DCHECK_EQ(0, component.error_code_);
663 DCHECK_EQ(0, component.extra_code1_);
664
665 TransitionState(base::MakeUnique<StateUpdated>(&component));
666}
667
668Component::StateUpdated::StateUpdated(Component* component)
669 : State(component, ComponentState::kUpdated) {
670 DCHECK(thread_checker_.CalledOnValidThread());
671}
672
673Component::StateUpdated::~StateUpdated() {
674 DCHECK(thread_checker_.CalledOnValidThread());
675}
676
677void Component::StateUpdated::DoHandle() {
678 DCHECK(thread_checker_.CalledOnValidThread());
679
680 auto& component = State::component();
681 component.crx_component_.version = component.next_version_;
682 component.crx_component_.fingerprint = component.next_fp_;
683
684 TransitionState(nullptr);
685 component.NotifyObservers(Events::COMPONENT_UPDATED);
686}
687
688Component::StateUninstalled::StateUninstalled(Component* component)
689 : State(component, ComponentState::kUninstalled) {
690 DCHECK(thread_checker_.CalledOnValidThread());
691}
692
693Component::StateUninstalled::~StateUninstalled() {
694 DCHECK(thread_checker_.CalledOnValidThread());
695}
696
697void Component::StateUninstalled::DoHandle() {
698 DCHECK(thread_checker_.CalledOnValidThread());
699 TransitionState(nullptr);
700}
701
702} // namespace update_client