blob: 4e07cb2c5ea0c6dc554dd01ce7efd7347590c90d [file] [log] [blame]
[email protected]e8f96ff2011-08-03 05:07:331// Copyright (c) 2011 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 "chrome/browser/component_updater/component_updater_service.h"
6
7#include <algorithm>
8#include <vector>
9
10#include "base/at_exit.h"
11#include "base/file_path.h"
12#include "base/file_util.h"
13#include "base/logging.h"
14#include "base/scoped_ptr.h"
15#include "base/stl_util.h"
16#include "base/string_number_conversions.h"
17#include "base/string_util.h"
18#include "base/stringprintf.h"
19#include "base/timer.h"
20#include "chrome/browser/browser_process.h"
21#include "chrome/browser/component_updater/component_unpacker.h"
22#include "chrome/common/chrome_notification_types.h"
23#include "chrome/common/chrome_utility_messages.h"
24#include "chrome/common/chrome_version_info.h"
25#include "chrome/common/extensions/extension.h"
26#include "content/browser/utility_process_host.h"
27#include "content/common/notification_service.h"
28#include "content/common/url_fetcher.h"
29#include "googleurl/src/gurl.h"
30#include "net/base/escape.h"
31#include "net/base/load_flags.h"
32
33namespace {
34// Extends an omaha compatible update check url |query| string. Does
35// not mutate the string if it would be longer than |limit| chars.
36bool AddQueryString(std::string id, std::string version,
37 size_t limit, std::string* query) {
38 std::string additional =
39 base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
40 additional = "x=" + EscapeQueryParamValue(additional, true);
41 if ((additional.size() + query->size() + 1) > limit)
42 return false;
43 query->append(1, query->empty()? '?' : '&');
44 query->append(additional);
45 return true;
46}
47
48// Produces an extension-like friendly |id|. This might be removed in the
49// future if we roll our on packing tools.
50static std::string HexStringToID(const std::string& hexstr) {
51 std::string id;
52 for (size_t i = 0; i < hexstr.size(); ++i) {
53 int val;
54 if (base::HexStringToInt(hexstr.begin() + i, hexstr.begin() + i + 1, &val))
55 id.append(1, val + 'a');
56 else
57 id.append(1, 'a');
58 }
59 DCHECK(Extension::IdIsValid(id));
60 return id;
61}
62
63// Helper to do version check for components.
64bool IsVersionNewer(const Version& current, const std::string& proposed) {
65 Version proposed_ver(proposed);
66 if (!proposed_ver.IsValid())
67 return false;
68 return (current.CompareTo(proposed_ver) < 0);
69}
70
71// Helper template class that allows our main class to have separate
72// OnURLFetchComplete() callbacks for diffent types of url requests
73// they are differentiated by the |Ctx| type.
74template <typename Del, typename Ctx>
75class DelegateWithContext : public URLFetcher::Delegate {
76 public:
77 DelegateWithContext(Del* delegate, Ctx* context)
78 : delegate_(delegate), context_(context) {}
79
80 virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE {
81 delegate_->OnURLFetchComplete(source, context_);
82 delete this;
83 }
84
85 private:
86 ~DelegateWithContext() {}
87
88 Del* delegate_;
89 Ctx* context_;
90};
91// This function creates the right DelegateWithContext using template inference.
92template <typename Del, typename Ctx>
93URLFetcher::Delegate* MakeContextDelegate(Del* delegate, Ctx* context) {
94 return new DelegateWithContext<Del, Ctx>(delegate, context);
95}
96
97// Helper to start a url request using |fetcher| with the common flags.
98void StartFetch(URLFetcher* fetcher,
99 net::URLRequestContextGetter* context_getter,
100 bool save_to_file) {
101 fetcher->set_request_context(context_getter);
102 fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
103 net::LOAD_DO_NOT_SAVE_COOKIES |
104 net::LOAD_DISABLE_CACHE);
105 // TODO(cpu): Define our retry and backoff policy.
106 fetcher->set_automatically_retry_on_5xx(false);
107 if (save_to_file) {
108 fetcher->SaveResponseToTemporaryFile(
109 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
110 }
111 fetcher->Start();
112}
113
114// Returs true if the url request of |fetcher| was succesful.
115bool FetchSuccess(const URLFetcher& fetcher) {
116 return (fetcher.status().status() == net::URLRequestStatus::SUCCESS) &&
117 (fetcher.response_code() == 200);
118}
119
120// This is the one and only per-item state structure. Designed to be hosted
121// in a std::vector or a std::list. The two main members are |component|
122// which is supplied by the the component updater client and |status| which
123// is modified as the item is processed by the update pipeline. The expected
124// transition graph is:
125// error error error
126// +--kNoUpdate<------<-------+------<------+------<------+
127// | | | |
128// V yes | | |
129// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
130// ^ | |
131// | |no |
132// |--kUpToDate<---+ |
133// | success |
134// +--kUpdated<-------------------------------------------+
135//
136struct CrxUpdateItem {
137 enum Status {
138 kNew,
139 kChecking,
140 kCanUpdate,
141 kDownloading,
142 kUpdating,
143 kUpdated,
144 kUpToDate,
145 kNoUpdate,
146 kLastStatus
147 };
148
149 Status status;
150 GURL crx_url;
151 std::string id;
152 base::Time last_check;
153 CrxComponent component;
154
155 CrxUpdateItem() : status(kNew) {}
156
157 // Function object used to find a specific component.
158 class FindById {
159 public:
160 explicit FindById(const std::string& id) : id_(id) {}
161
162 bool operator() (CrxUpdateItem* item) const {
163 return (item->id == id_);
164 }
165 private:
166 const std::string& id_;
167 };
168};
169
170} // namespace.
171
172typedef ComponentUpdateService::Configurator Config;
173
174CrxComponent::CrxComponent() {}
175CrxComponent::~CrxComponent() {}
176
177//////////////////////////////////////////////////////////////////////////////
178// The one and only implementation of the ComponentUpdateService interface. In
179// charge of running the show. The main method is ProcessPendingItems() which
180// is called periodically to do the updgrades/installs or the update checks.
181// An important consideration here is to be as "low impact" as we can to the
182// rest of the browser, so even if we have many components registered and
183// elegible for update, we only do one thing at a time with pauses in between
184// the tasks. Also when we do network requests there is only one |url_fetcher_|
185// in flight at at a time.
186// There are no locks in this code, the main structure |work_items_| is mutated
187// only from the UI thread. The unpack and installation is done in the file
188// thread and the network requests are done in the IO thread and in the file
189// thread.
190class CrxUpdateService : public ComponentUpdateService {
191 public:
192 explicit CrxUpdateService(ComponentUpdateService::Configurator* config);
193
194 virtual ~CrxUpdateService();
195
196 // Overrides for ComponentUpdateService.
197 virtual Status Start() OVERRIDE;
198 virtual Status Stop() OVERRIDE;
199 virtual Status RegisterComponent(const CrxComponent& component) OVERRIDE;
200
201 // The only purpose of this class is to forward the
202 // UtilityProcessHost::Client callbacks so CrxUpdateService does
203 // not have to derive from it because that is refcounted.
204 class ManifestParserBridge : public UtilityProcessHost::Client {
205 public:
206 explicit ManifestParserBridge(CrxUpdateService* service)
207 : service_(service) {}
208
209 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
210 bool handled = true;
211 IPC_BEGIN_MESSAGE_MAP(ManifestParserBridge, message)
212 IPC_MESSAGE_HANDLER(UtilityHostMsg_ParseUpdateManifest_Succeeded,
213 OnParseUpdateManifestSucceeded)
214 IPC_MESSAGE_HANDLER(UtilityHostMsg_ParseUpdateManifest_Failed,
215 OnParseUpdateManifestFailed)
216 IPC_MESSAGE_UNHANDLED(handled = false)
217 IPC_END_MESSAGE_MAP()
218 return handled;
219 }
220
221 private:
222 // Omaha update response XML was succesfuly parsed.
223 void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& r) {
224 service_->OnParseUpdateManifestSucceeded(r);
225 }
226 // Omaha update response XML could not be parsed.
227 void OnParseUpdateManifestFailed(const std::string& e) {
228 service_->OnParseUpdateManifestFailed(e);
229 }
230
231 CrxUpdateService* service_;
232 DISALLOW_COPY_AND_ASSIGN(ManifestParserBridge);
233 };
234
235 // Context for a update check url request. See DelegateWithContext above.
236 struct UpdateContext {
237 base::Time start;
238 UpdateContext() : start(base::Time::Now()) {}
239 };
240
241 // Context for a crx download url request. See DelegateWithContext above.
242 struct CRXContext {
243 ComponentInstaller* installer;
244 std::vector<uint8> pk_hash;
245 std::string id;
246 CRXContext() : installer(NULL) {}
247 };
248
249 void OnURLFetchComplete(const URLFetcher* source, UpdateContext* context);
250
251 void OnURLFetchComplete(const URLFetcher* source, CRXContext* context);
252
253 private:
254 // See ManifestParserBridge.
255 void OnParseUpdateManifestSucceeded(
256 const UpdateManifest::Results& results);
257
258 // See ManifestParserBridge.
259 void OnParseUpdateManifestFailed(
260 const std::string& error_message);
261
262 bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
263
264 void ProcessPendingItems();
265
266 void ScheduleNextRun(bool step_delay);
267
268 void ParseManifest(const std::string& xml);
269
270 void Install(const CRXContext* context, const FilePath& crx_path);
271
272 void DoneInstalling(ComponentUnpacker::Error error);
273
274 size_t ChangeItemStatus(CrxUpdateItem::Status from,
275 CrxUpdateItem::Status to);
276
277 CrxUpdateItem* FindUpdateItemById(const std::string& id);
278
279 scoped_ptr<Config> config_;
280
281 scoped_ptr<URLFetcher> url_fetcher_;
282
283 typedef std::vector<CrxUpdateItem*> UpdateItems;
284 UpdateItems work_items_;
285
286 base::OneShotTimer<CrxUpdateService> timer_;
287
288 Version chrome_version_;
289
290 bool running_;
291
292 DISALLOW_COPY_AND_ASSIGN(CrxUpdateService);
293};
294
295// The component updater is designed to live until process shutdown, besides
296// we can't be refcounted because we are a singleton.
297DISABLE_RUNNABLE_METHOD_REFCOUNT(CrxUpdateService);
298
299//////////////////////////////////////////////////////////////////////////////
300
301CrxUpdateService::CrxUpdateService(
302 ComponentUpdateService::Configurator* config)
303 : config_(config),
304 chrome_version_(chrome::VersionInfo().Version()),
305 running_(false) {
306}
307
308CrxUpdateService::~CrxUpdateService() {
309 // Because we are a singleton, at this point only the UI thread should be
310 // alive, this simplifies the management of the work that could be in
311 // flight in other threads.
312 Stop();
313 STLDeleteElements(&work_items_);
314}
315
316ComponentUpdateService::Status CrxUpdateService::Start() {
317 // Note that RegisterComponent will call Start() when the first
318 // component is registered, so it can be called twice. This way
319 // we avoid scheduling the timer if there is no work to do.
320 running_ = true;
321 if (work_items_.empty())
322 return kOk;
323
324 NotificationService::current()->Notify(
325 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
326 Source<ComponentUpdateService>(this),
327 NotificationService::NoDetails());
328
329 timer_.Start(base::TimeDelta::FromSeconds(config_->InitialDelay()),
330 this, &CrxUpdateService::ProcessPendingItems);
331 return kOk;
332}
333
334// Stop the main check + update loop. In flight operations will be
335// completed.
336ComponentUpdateService::Status CrxUpdateService::Stop() {
337 running_ = false;
338 timer_.Stop();
339 return kOk;
340}
341
342// This function sets the timer which will call ProcessPendingItems() there
343// are two kind of waits, the short one (with step_delay = true) and the
344// long one.
345void CrxUpdateService::ScheduleNextRun(bool step_delay) {
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cf442612011-08-09 20:20:12347 DCHECK(url_fetcher_.get() == NULL);
[email protected]e8f96ff2011-08-03 05:07:33348 CHECK(!timer_.IsRunning());
349 // It could be the case that Stop() had been called while a url request
350 // or unpacking was in flight, if so we arrive here but |running_| is
351 // false. In that case do not loop again.
352 if (!running_)
353 return;
354
[email protected]cf442612011-08-09 20:20:12355 int64 delay = step_delay ? config_->StepDelay() : config_->NextCheckDelay();
356
[email protected]e8f96ff2011-08-03 05:07:33357 if (!step_delay) {
358 NotificationService::current()->Notify(
359 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
360 Source<ComponentUpdateService>(this),
361 NotificationService::NoDetails());
362 // Zero is only used for unit tests.
[email protected]cf442612011-08-09 20:20:12363 if (0 == delay)
[email protected]e8f96ff2011-08-03 05:07:33364 return;
365 }
366
[email protected]e8f96ff2011-08-03 05:07:33367 timer_.Start(base::TimeDelta::FromSeconds(delay),
368 this, &CrxUpdateService::ProcessPendingItems);
369}
370
371// Given a extension-like component id, find the associated component.
372CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
374 CrxUpdateItem::FindById finder(id);
375 UpdateItems::iterator it = std::find_if(work_items_.begin(),
376 work_items_.end(),
377 finder);
378 if (it == work_items_.end())
379 return NULL;
380 return (*it);
381}
382
383// Changes all the components in |work_items_| that have |from| status to
384// |to| statatus and returns how many have been changed.
385size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
386 CrxUpdateItem::Status to) {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388 size_t count = 0;
389 for (UpdateItems::iterator it = work_items_.begin();
390 it != work_items_.end(); ++it) {
391 CrxUpdateItem* item = *it;
392 if (item->status != from)
393 continue;
394 item->status = to;
395 ++count;
396 }
397 return count;
398}
399
400// Adds a component to be checked for upgrades. If the component exists it
401// it will be replaced and the return code is kReplaced.
402//
403// TODO(cpu): Evaluate if we want to support un-registration.
404ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
405 const CrxComponent& component) {
406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
407 if (component.pk_hash.empty() ||
408 !component.version.IsValid() ||
409 !component.installer)
410 return kError;
411
412 std::string id =
413 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
414 component.pk_hash.size()/2)));
415 CrxUpdateItem* uit;
416 uit = FindUpdateItemById(id);
417 if (uit) {
418 uit->component = component;
419 return kReplaced;
420 }
421
422 uit = new CrxUpdateItem;
423 uit->id.swap(id);
424 uit->component = component;
425 work_items_.push_back(uit);
426 // If this is the first component registered we call Start to
427 // schedule the first timer.
428 if (running_ && (work_items_.size() == 1))
429 Start();
430
431 return kOk;
432}
433
434// Sets a component to be checked for updates.
435// The componet to add is |crxit| and the |query| string is modified with the
436// required omaha compatible query. Returns false when the query strings
437// is longer than specified by UrlSizeLimit().
438bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
439 std::string* query) {
440 if (!AddQueryString(item->id,
441 item->component.version.GetString(),
442 config_->UrlSizeLimit(), query))
443 return false;
444 item->status = CrxUpdateItem::kChecking;
445 item->last_check = base::Time::Now();
446 return true;
447}
448
449// Here is where the work gets scheduled. Given that our |work_items_| list
450// is expected to be ten or less items, we simply loop several times.
451void CrxUpdateService::ProcessPendingItems() {
452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453 // First check for ready upgrades and do one. The first
454 // step is to fetch the crx package.
455 for (UpdateItems::const_iterator it = work_items_.begin();
456 it != work_items_.end(); ++it) {
457 CrxUpdateItem* item = *it;
458 if (item->status != CrxUpdateItem::kCanUpdate)
459 continue;
460 // Found component to update, start the process.
461 item->status = CrxUpdateItem::kDownloading;
462 CRXContext* context = new CRXContext;
463 context->pk_hash = item->component.pk_hash;
464 context->id = item->id;
465 context->installer = item->component.installer;
466 url_fetcher_.reset(URLFetcher::Create(0, item->crx_url, URLFetcher::GET,
467 MakeContextDelegate(this, context)));
468 StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
469 return;
470 }
471
472 std::string query;
473 // If no pending upgrades, we check the if there are new
474 // components we have not checked against the server. We
475 // can batch a bunch in a single url request.
476 for (UpdateItems::const_iterator it = work_items_.begin();
477 it != work_items_.end(); ++it) {
478 CrxUpdateItem* item = *it;
479 if (item->status != CrxUpdateItem::kNew)
480 continue;
481 if (!AddItemToUpdateCheck(item, &query))
482 break;
483 }
484
485 // Next we can go back to components we already checked, here
486 // we can also batch them in a single url request, as long as
487 // we have not checked them recently.
488 base::TimeDelta min_delta_time =
489 base::TimeDelta::FromSeconds(config_->MinimumReCheckWait());
490
491 for (UpdateItems::const_iterator it = work_items_.begin();
492 it != work_items_.end(); ++it) {
493 CrxUpdateItem* item = *it;
[email protected]cf442612011-08-09 20:20:12494 if ((item->status != CrxUpdateItem::kNoUpdate) &&
[email protected]e8f96ff2011-08-03 05:07:33495 (item->status != CrxUpdateItem::kUpToDate))
496 continue;
497 base::TimeDelta delta = base::Time::Now() - item->last_check;
498 if (delta < min_delta_time)
499 continue;
500 if (!AddItemToUpdateCheck(item, &query))
501 break;
502 }
503 // Finally, we check components that we already updated.
504 for (UpdateItems::const_iterator it = work_items_.begin();
505 it != work_items_.end(); ++it) {
506 CrxUpdateItem* item = *it;
507 if (item->status != CrxUpdateItem::kUpdated)
508 continue;
509 base::TimeDelta delta = base::Time::Now() - item->last_check;
510 if (delta < min_delta_time)
511 continue;
512 if (!AddItemToUpdateCheck(item, &query))
513 break;
514 }
515
516 if (query.empty()) {
517 // Next check after the long sleep.
518 ScheduleNextRun(false);
519 return;
520 }
521
522 // We got components to check. Start the url request.
523 GURL url(config_->UpdateUrl().spec() + query);
524 url_fetcher_.reset(URLFetcher::Create(0, url, URLFetcher::GET,
525 MakeContextDelegate(this, new UpdateContext())));
526 StartFetch(url_fetcher_.get(), config_->RequestContext(), false);
527}
528
529// Caled when we got a response from the update server. It consists of an xml
530// document following the omaha update scheme.
531void CrxUpdateService::OnURLFetchComplete(const URLFetcher* source,
532 UpdateContext*) {
533 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
534 if (FetchSuccess(*source)) {
535 std::string xml;
536 source->GetResponseAsString(&xml);
[email protected]cf442612011-08-09 20:20:12537 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33538 ParseManifest(xml);
539 } else {
[email protected]cf442612011-08-09 20:20:12540 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33541 CrxUpdateService::OnParseUpdateManifestFailed("network error");
542 }
[email protected]e8f96ff2011-08-03 05:07:33543}
544
545// Parsing the manifest is either done right now for tests or in a sandboxed
546// process for the production environment. This mitigates the case where an
547// attacker was able to feed us a malicious xml string.
548void CrxUpdateService::ParseManifest(const std::string& xml) {
549 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
550 if (config_->InProcess()) {
551 UpdateManifest manifest;
552 if (!manifest.Parse(xml)) {
553 CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
554 } else {
555 CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
556 }
557 } else {
558 UtilityProcessHost* host =
559 new UtilityProcessHost(new ManifestParserBridge(this),
560 BrowserThread::UI);
561 host->Send(new UtilityMsg_ParseUpdateManifest(xml));
562 }
563}
564
565// A valid Omaha update check has arrived, from only the list of components that
566// we are currently upgrading we check for a match in which the server side
567// version is newer, if so we queue them for an upgrade. The next time we call
568// ProcessPendingItems() one of them will be drafted for the upgrade process.
569void CrxUpdateService::OnParseUpdateManifestSucceeded(
570 const UpdateManifest::Results& results) {
571 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
572 int update_pending = 0;
573 std::vector<UpdateManifest::Result>::const_iterator it;
574 for (it = results.list.begin(); it != results.list.end(); ++it) {
575 CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
576 if (!crx)
577 continue;
578
579 if (crx->status != CrxUpdateItem::kChecking)
580 continue; // Not updating this component now.
581
582 if (it->version.empty()) {
[email protected]cf442612011-08-09 20:20:12583 // No version means no update available.
[email protected]e8f96ff2011-08-03 05:07:33584 crx->status = CrxUpdateItem::kNoUpdate;
[email protected]cf442612011-08-09 20:20:12585 continue;
[email protected]e8f96ff2011-08-03 05:07:33586 }
587 if (!IsVersionNewer(crx->component.version, it->version)) {
[email protected]cf442612011-08-09 20:20:12588 // Our component is up to date.
[email protected]e8f96ff2011-08-03 05:07:33589 crx->status = CrxUpdateItem::kUpToDate;
[email protected]cf442612011-08-09 20:20:12590 continue;
[email protected]e8f96ff2011-08-03 05:07:33591 }
592 if (!it->browser_min_version.empty()) {
[email protected]cf442612011-08-09 20:20:12593 if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
594 // Does not apply for this chrome version.
595 crx->status = CrxUpdateItem::kNoUpdate;
596 continue;
597 }
[email protected]e8f96ff2011-08-03 05:07:33598 }
599 // All test passed. Queue an upgrade for this component and fire the
600 // notifications.
601 crx->crx_url = it->crx_url;
602 crx->status = CrxUpdateItem::kCanUpdate;
603 ++update_pending;
604
605 NotificationService::current()->Notify(
606 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
607 Source<std::string>(&crx->id),
608 NotificationService::NoDetails());
609 }
[email protected]cf442612011-08-09 20:20:12610
611 // All the components that are not mentioned in the manifest we
612 // consider them up to date.
613 ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
614
[email protected]e8f96ff2011-08-03 05:07:33615 // If there are updates pending we do a short wait.
616 ScheduleNextRun(update_pending ? true : false);
617}
618
619void CrxUpdateService::OnParseUpdateManifestFailed(
620 const std::string& error_message) {
621 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
622 size_t count = ChangeItemStatus(CrxUpdateItem::kChecking,
623 CrxUpdateItem::kNoUpdate);
624 DCHECK_GT(count, 0ul);
625 ScheduleNextRun(false);
626}
627
628// Called when the CRX package has been downloaded to a temporary location.
629// Here we fire the notifications and schedule the component-specific installer
630// to be called in the file thread.
631void CrxUpdateService::OnURLFetchComplete(const URLFetcher* source,
632 CRXContext* context) {
633 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
634 base::PlatformFileError error_code;
635
636 if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
637 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
638 CrxUpdateItem::kNoUpdate);
639 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12640 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33641 ScheduleNextRun(false);
642 } else {
643 FilePath temp_crx_path;
644 CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
645 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
646 CrxUpdateItem::kUpdating);
647 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12648 url_fetcher_.reset();
649
[email protected]e8f96ff2011-08-03 05:07:33650 NotificationService::current()->Notify(
651 chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
652 Source<std::string>(&context->id),
653 NotificationService::NoDetails());
654
655 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE,
656 NewRunnableMethod(this, &CrxUpdateService::Install,
657 context,
658 temp_crx_path),
659 config_->StepDelay());
660 }
[email protected]e8f96ff2011-08-03 05:07:33661}
662
663// Install consists of digital signature verification, unpacking and then
664// calling the component specific installer. All that is handled by the
665// |unpacker|. If there is an error this function is in charge of deleting
666// the files created.
667void CrxUpdateService::Install(const CRXContext* context,
668 const FilePath& crx_path) {
669 // This function owns the |crx_path| and the |context| object.
670 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
671 ComponentUnpacker
672 unpacker(context->pk_hash, crx_path, context->installer);
673 delete context;
674 if (!file_util::Delete(crx_path, false)) {
675 NOTREACHED() << crx_path.value();
676 }
677
678 BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
679 NewRunnableMethod(this, &CrxUpdateService::DoneInstalling,
680 unpacker.error()),
681 config_->StepDelay());
682}
683
684// Installation has been completed. Adjust the component status and
685// schedule the next check.
686void CrxUpdateService::DoneInstalling(ComponentUnpacker::Error error) {
687 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
688 CrxUpdateItem::Status status =
689 (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
690 CrxUpdateItem::kNoUpdate;
691 size_t count = ChangeItemStatus(CrxUpdateItem::kUpdating, status);
692 DCHECK_EQ(count, 1ul);
693 ScheduleNextRun(false);
694}
695
696// The component update factory. Using the component updater as a singleton
697// is the job of the browser process.
698ComponentUpdateService* ComponentUpdateServiceFactory(
699 ComponentUpdateService::Configurator* config) {
700 DCHECK(config);
701 return new CrxUpdateService(config);
702}
703