1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/updater/extension_updater.h"
11 #include "base/bind.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/rand_util.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_split.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/extensions/api/module/module.h"
22 #include "chrome/browser/extensions/crx_installer.h"
23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/browser/extensions/pending_extension_manager.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "components/update_client/update_query_params.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/notification_details.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/notification_source.h"
31 #include "crypto/sha2.h"
32 #include "extensions/browser/extension_prefs.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/pref_names.h"
35 #include "extensions/common/constants.h"
36 #include "extensions/common/extension.h"
37 #include "extensions/common/extension_set.h"
38 #include "extensions/common/manifest.h"
39 #include "extensions/common/manifest_constants.h"
41 using base::RandDouble
;
44 using base::TimeDelta
;
45 using content::BrowserThread
;
46 using extensions::Extension
;
47 using extensions::ExtensionSet
;
48 using update_client::UpdateQueryParams
;
50 typedef extensions::ExtensionDownloaderDelegate::Error Error
;
51 typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult
;
55 // Wait at least 5 minutes after browser startup before we do any checks. If you
56 // change this value, make sure to update comments where it is used.
57 const int kStartupWaitSeconds
= 60 * 5;
59 // For sanity checking on update frequency - enforced in release mode only.
61 const int kMinUpdateFrequencySeconds
= 30;
63 const int kMaxUpdateFrequencySeconds
= 60 * 60 * 24 * 7; // 7 days
65 // Require at least 5 seconds between consecutive non-succesful extension update
67 const int kMinUpdateThrottleTime
= 5;
69 // The installsource query parameter to use when forcing updates due to NaCl
71 const char kWrongMultiCrxInstallSource
[] = "wrong_multi_crx";
73 // When we've computed a days value, we want to make sure we don't send a
74 // negative value (due to the system clock being set backwards, etc.), since -1
75 // is a special sentinel value that means "never pinged", and other negative
76 // values don't make sense.
77 int SanitizeDays(int days
) {
83 // Calculates the value to use for the ping days parameter.
84 int CalculatePingDays(const Time
& last_ping_day
) {
85 int days
= extensions::ManifestFetchData::kNeverPinged
;
86 if (!last_ping_day
.is_null()) {
87 days
= SanitizeDays((Time::Now() - last_ping_day
).InDays());
92 int CalculateActivePingDays(const Time
& last_active_ping_day
,
96 if (last_active_ping_day
.is_null())
97 return extensions::ManifestFetchData::kNeverPinged
;
98 return SanitizeDays((Time::Now() - last_active_ping_day
).InDays());
101 void RespondWithForcedUpdates(
102 const base::Callback
<void(const std::set
<std::string
>&)>& callback
,
103 scoped_ptr
<std::set
<std::string
> > forced_updates
) {
104 callback
.Run(*forced_updates
.get());
107 void DetermineForcedUpdatesOnBlockingPool(
108 scoped_ptr
<std::vector
<scoped_refptr
<const Extension
> > > extensions
,
109 const base::Callback
<void(const std::set
<std::string
>&)>& callback
) {
110 scoped_ptr
<std::set
<std::string
> > forced_updates(
111 new std::set
<std::string
>());
112 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
113 for (std::vector
<scoped_refptr
<const Extension
> >::const_iterator iter
=
115 iter
!= extensions
->end();
117 scoped_refptr
<const Extension
> extension
= *iter
;
118 base::FilePath platform_specific_path
= extension
->path().Append(
119 extensions::kPlatformSpecificFolder
);
120 if (base::PathExists(platform_specific_path
)) {
122 const base::ListValue
* platforms
;
123 if (extension
->manifest()->GetList(extensions::manifest_keys::kPlatforms
,
125 for (size_t i
= 0; i
< platforms
->GetSize(); ++i
) {
126 const base::DictionaryValue
* p
;
127 if (platforms
->GetDictionary(i
, &p
)) {
128 std::string nacl_arch
;
129 if (p
->GetString(extensions::manifest_keys::kNaClArch
,
131 nacl_arch
== UpdateQueryParams::GetNaclArch()) {
133 if (p
->GetString(extensions::manifest_keys::kSubPackagePath
,
135 // _platform_specific is part of the sub_package_path entry.
136 base::FilePath platform_specific_subpath
=
137 extension
->path().AppendASCII(subpath
);
138 if (base::PathExists(platform_specific_subpath
)) {
148 forced_updates
->insert(extension
->id());
151 BrowserThread::PostTask(
154 base::Bind(&RespondWithForcedUpdates
,
156 base::Passed(&forced_updates
)));
159 void CollectExtensionsFromSet(
160 const ExtensionSet
& extensions
,
161 std::vector
<scoped_refptr
<const Extension
> >* paths
) {
162 std::copy(extensions
.begin(), extensions
.end(), std::back_inserter(*paths
));
165 void DetermineForcedUpdates(
166 content::BrowserContext
* browser_context
,
167 const base::Callback
<void(const std::set
<std::string
>&)>& callback
) {
168 scoped_ptr
<std::vector
<scoped_refptr
<const Extension
> > > extensions(
169 new std::vector
<scoped_refptr
<const Extension
> >());
170 const extensions::ExtensionRegistry
* registry
=
171 extensions::ExtensionRegistry::Get(browser_context
);
172 scoped_ptr
<ExtensionSet
> installed_extensions
=
173 registry
->GenerateInstalledExtensionsSet();
174 CollectExtensionsFromSet(*installed_extensions
.get(), extensions
.get());
175 BrowserThread::PostBlockingPoolTask(
177 base::Bind(&DetermineForcedUpdatesOnBlockingPool
,
178 base::Passed(&extensions
),
184 namespace extensions
{
186 ExtensionUpdater::CheckParams::CheckParams()
187 : install_immediately(false) {}
189 ExtensionUpdater::CheckParams::~CheckParams() {}
191 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(
192 const CRXFileInfo
& file
,
193 bool file_ownership_passed
,
194 const std::set
<int>& request_ids
,
195 const InstallCallback
& callback
)
197 file_ownership_passed(file_ownership_passed
),
198 request_ids(request_ids
),
202 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
203 : file_ownership_passed(true) {
206 ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
208 ExtensionUpdater::InProgressCheck::InProgressCheck()
209 : install_immediately(false) {}
211 ExtensionUpdater::InProgressCheck::~InProgressCheck() {}
213 struct ExtensionUpdater::ThrottleInfo
{
216 throttle_delay(kMinUpdateThrottleTime
),
217 check_start(Time::Now()) {}
224 ExtensionUpdater::ExtensionUpdater(
225 ExtensionServiceInterface
* service
,
226 ExtensionPrefs
* extension_prefs
,
229 int frequency_seconds
,
230 ExtensionCache
* cache
,
231 const ExtensionDownloader::Factory
& downloader_factory
)
234 downloader_factory_(downloader_factory
),
235 frequency_seconds_(frequency_seconds
),
236 will_check_soon_(false),
237 extension_prefs_(extension_prefs
),
241 extension_registry_observer_(this),
242 crx_install_is_running_(false),
243 extension_cache_(cache
),
244 weak_ptr_factory_(this) {
245 DCHECK_GE(frequency_seconds_
, 5);
246 DCHECK_LE(frequency_seconds_
, kMaxUpdateFrequencySeconds
);
248 // In Release mode we enforce that update checks don't happen too often.
249 frequency_seconds_
= std::max(frequency_seconds_
, kMinUpdateFrequencySeconds
);
251 frequency_seconds_
= std::min(frequency_seconds_
, kMaxUpdateFrequencySeconds
);
253 extension_registry_observer_
.Add(ExtensionRegistry::Get(profile
));
256 ExtensionUpdater::~ExtensionUpdater() {
260 void ExtensionUpdater::EnsureDownloaderCreated() {
261 if (!downloader_
.get()) {
262 downloader_
= downloader_factory_
.Run(this);
266 // The overall goal here is to balance keeping clients up to date while
267 // avoiding a thundering herd against update servers.
268 TimeDelta
ExtensionUpdater::DetermineFirstCheckDelay() {
270 // If someone's testing with a quick frequency, just allow it.
271 if (frequency_seconds_
< kStartupWaitSeconds
)
272 return TimeDelta::FromSeconds(frequency_seconds_
);
274 // If we've never scheduled a check before, start at frequency_seconds_.
275 if (!prefs_
->HasPrefPath(pref_names::kNextUpdateCheck
))
276 return TimeDelta::FromSeconds(frequency_seconds_
);
278 // If it's been a long time since our last actual check, we want to do one
280 Time now
= Time::Now();
281 Time last
= Time::FromInternalValue(prefs_
->GetInt64(
282 pref_names::kLastUpdateCheck
));
283 int days
= (now
- last
).InDays();
285 // Wait 5-10 minutes.
286 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds
,
287 kStartupWaitSeconds
* 2));
288 } else if (days
>= 14) {
289 // Wait 10-20 minutes.
290 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds
* 2,
291 kStartupWaitSeconds
* 4));
292 } else if (days
>= 3) {
293 // Wait 20-40 minutes.
294 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds
* 4,
295 kStartupWaitSeconds
* 8));
298 // Read the persisted next check time, and use that if it isn't too soon
299 // or too late. Otherwise pick something random.
300 Time saved_next
= Time::FromInternalValue(prefs_
->GetInt64(
301 pref_names::kNextUpdateCheck
));
302 Time earliest
= now
+ TimeDelta::FromSeconds(kStartupWaitSeconds
);
303 Time latest
= now
+ TimeDelta::FromSeconds(frequency_seconds_
);
304 if (saved_next
>= earliest
&& saved_next
<= latest
) {
305 return saved_next
- now
;
307 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds
,
308 frequency_seconds_
));
312 void ExtensionUpdater::Start() {
314 // If these are NULL, then that means we've been called after Stop()
317 DCHECK(extension_prefs_
);
320 DCHECK(!weak_ptr_factory_
.HasWeakPtrs());
322 // Make sure our prefs are registered, then schedule the first check.
323 ScheduleNextCheck(DetermineFirstCheckDelay());
326 void ExtensionUpdater::Stop() {
327 weak_ptr_factory_
.InvalidateWeakPtrs();
330 extension_prefs_
= NULL
;
334 will_check_soon_
= false;
338 void ExtensionUpdater::ScheduleNextCheck(const TimeDelta
& target_delay
) {
340 DCHECK(!timer_
.IsRunning());
341 DCHECK(target_delay
>= TimeDelta::FromSeconds(1));
343 // Add +/- 10% random jitter.
344 double delay_ms
= target_delay
.InMillisecondsF();
345 double jitter_factor
= (RandDouble() * .2) - 0.1;
346 delay_ms
+= delay_ms
* jitter_factor
;
347 TimeDelta actual_delay
= TimeDelta::FromMilliseconds(
348 static_cast<int64
>(delay_ms
));
350 // Save the time of next check.
351 Time next
= Time::Now() + actual_delay
;
352 prefs_
->SetInt64(pref_names::kNextUpdateCheck
, next
.ToInternalValue());
354 timer_
.Start(FROM_HERE
, actual_delay
, this, &ExtensionUpdater::TimerFired
);
357 void ExtensionUpdater::TimerFired() {
359 CheckNow(default_params_
);
361 // If the user has overridden the update frequency, don't bother reporting
363 if (frequency_seconds_
== extensions::kDefaultUpdateFrequencySeconds
) {
364 Time last
= Time::FromInternalValue(prefs_
->GetInt64(
365 pref_names::kLastUpdateCheck
));
366 if (last
.ToInternalValue() != 0) {
367 // Use counts rather than time so we can use minutes rather than millis.
368 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
369 (Time::Now() - last
).InMinutes(),
370 TimeDelta::FromSeconds(kStartupWaitSeconds
).InMinutes(),
371 TimeDelta::FromDays(40).InMinutes(),
372 50); // 50 buckets seems to be the default.
376 // Save the last check time, and schedule the next check.
377 int64 now
= Time::Now().ToInternalValue();
378 prefs_
->SetInt64(pref_names::kLastUpdateCheck
, now
);
379 ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_
));
382 void ExtensionUpdater::CheckSoon() {
384 if (will_check_soon_
)
386 if (BrowserThread::PostTask(
387 BrowserThread::UI
, FROM_HERE
,
388 base::Bind(&ExtensionUpdater::DoCheckSoon
,
389 weak_ptr_factory_
.GetWeakPtr()))) {
390 will_check_soon_
= true;
396 bool ExtensionUpdater::WillCheckSoon() const {
397 return will_check_soon_
;
400 void ExtensionUpdater::SetExtensionCacheForTesting(
401 ExtensionCache
* extension_cache
) {
402 extension_cache_
= extension_cache
;
405 void ExtensionUpdater::StopTimerForTesting() {
409 void ExtensionUpdater::DoCheckSoon() {
410 DCHECK(will_check_soon_
);
411 CheckNow(default_params_
);
412 will_check_soon_
= false;
415 void ExtensionUpdater::AddToDownloader(
416 const ExtensionSet
* extensions
,
417 const std::list
<std::string
>& pending_ids
,
419 InProgressCheck
& request
= requests_in_progress_
[request_id
];
420 for (ExtensionSet::const_iterator extension_iter
= extensions
->begin();
421 extension_iter
!= extensions
->end(); ++extension_iter
) {
422 const Extension
& extension
= *extension_iter
->get();
423 if (!Manifest::IsAutoUpdateableLocation(extension
.location())) {
424 VLOG(2) << "Extension " << extension
.id() << " is not auto updateable";
427 // An extension might be overwritten by policy, and have its update url
428 // changed. Make sure existing extensions aren't fetched again, if a
429 // pending fetch for an extension with the same id already exists.
430 std::list
<std::string
>::const_iterator pending_id_iter
= std::find(
431 pending_ids
.begin(), pending_ids
.end(), extension
.id());
432 if (pending_id_iter
== pending_ids
.end()) {
433 if (downloader_
->AddExtension(extension
, request_id
))
434 request
.in_progress_ids_
.push_back(extension
.id());
439 void ExtensionUpdater::CheckNow(const CheckParams
& params
) {
440 DetermineForcedUpdates(
442 base::Bind(&ExtensionUpdater::OnForcedUpdatesDetermined
,
443 weak_ptr_factory_
.GetWeakPtr(),
447 void ExtensionUpdater::OnForcedUpdatesDetermined(
448 const CheckParams
& params
,
449 const std::set
<std::string
>& forced_updates
) {
450 int request_id
= next_request_id_
++;
452 VLOG(2) << "Starting update check " << request_id
;
453 if (params
.ids
.empty())
458 InProgressCheck
& request
= requests_in_progress_
[request_id
];
459 request
.callback
= params
.callback
;
460 request
.install_immediately
= params
.install_immediately
;
462 EnsureDownloaderCreated();
464 forced_updates_
= forced_updates
;
466 // Add fetch records for extensions that should be fetched by an update URL.
467 // These extensions are not yet installed. They come from group policy
468 // and external install sources.
469 const PendingExtensionManager
* pending_extension_manager
=
470 service_
->pending_extension_manager();
472 std::list
<std::string
> pending_ids
;
474 if (params
.ids
.empty()) {
475 // If no extension ids are specified, check for updates for all extensions.
476 pending_extension_manager
->GetPendingIdsForUpdateCheck(&pending_ids
);
478 std::list
<std::string
>::const_iterator iter
;
479 for (iter
= pending_ids
.begin(); iter
!= pending_ids
.end(); ++iter
) {
480 const PendingExtensionInfo
* info
= pending_extension_manager
->GetById(
482 if (!Manifest::IsAutoUpdateableLocation(info
->install_source())) {
483 VLOG(2) << "Extension " << *iter
<< " is not auto updateable";
486 if (downloader_
->AddPendingExtension(*iter
, info
->update_url(),
488 request
.in_progress_ids_
.push_back(*iter
);
491 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
492 AddToDownloader(®istry
->enabled_extensions(), pending_ids
, request_id
);
493 AddToDownloader(®istry
->disabled_extensions(), pending_ids
, request_id
);
495 for (std::list
<std::string
>::const_iterator it
= params
.ids
.begin();
496 it
!= params
.ids
.end(); ++it
) {
497 const Extension
* extension
= service_
->GetExtensionById(*it
, true);
499 if (downloader_
->AddExtension(*extension
, request_id
))
500 request
.in_progress_ids_
.push_back(extension
->id());
504 // StartAllPending() might call OnExtensionDownloadFailed/Finished before
505 // it returns, which would cause NotifyIfFinished to incorrectly try to
506 // send out a notification. So check before we call StartAllPending if any
507 // extensions are going to be updated, and use that to figure out if
508 // NotifyIfFinished should be called.
509 bool noChecks
= request
.in_progress_ids_
.empty();
511 // StartAllPending() will call OnExtensionDownloadFailed or
512 // OnExtensionDownloadFinished for each extension that was checked.
513 downloader_
->StartAllPending(extension_cache_
);
516 NotifyIfFinished(request_id
);
519 bool ExtensionUpdater::CheckExtensionSoon(const std::string
& extension_id
,
520 const FinishedCallback
& callback
) {
521 bool have_throttle_info
= ContainsKey(throttle_info_
, extension_id
);
522 ThrottleInfo
& info
= throttle_info_
[extension_id
];
523 if (have_throttle_info
) {
524 // We already had a ThrottleInfo object for this extension, check if the
525 // update check request should be allowed.
527 // If another check is in progress, don't start a new check.
528 if (info
.in_progress
)
531 Time now
= Time::Now();
532 Time last
= info
.check_start
;
533 // If somehow time moved back, we don't want to infinitely keep throttling.
536 info
.check_start
= now
;
538 Time earliest
= last
+ TimeDelta::FromSeconds(info
.throttle_delay
);
539 // If check is too soon, throttle.
543 // TODO(mek): Somehow increase time between allowing checks when checks
544 // are repeatedly throttled and don't result in updates being installed.
546 // It's okay to start a check, update values.
547 info
.check_start
= now
;
548 info
.in_progress
= true;
552 params
.ids
.push_back(extension_id
);
553 params
.callback
= base::Bind(&ExtensionUpdater::ExtensionCheckFinished
,
554 weak_ptr_factory_
.GetWeakPtr(),
555 extension_id
, callback
);
560 void ExtensionUpdater::ExtensionCheckFinished(
561 const std::string
& extension_id
,
562 const FinishedCallback
& callback
) {
563 std::map
<std::string
, ThrottleInfo
>::iterator it
=
564 throttle_info_
.find(extension_id
);
565 if (it
!= throttle_info_
.end()) {
566 it
->second
.in_progress
= false;
571 void ExtensionUpdater::OnExtensionDownloadFailed(
572 const std::string
& id
,
574 const PingResult
& ping
,
575 const std::set
<int>& request_ids
) {
577 UpdatePingData(id
, ping
);
578 bool install_immediately
= false;
579 for (std::set
<int>::const_iterator it
= request_ids
.begin();
580 it
!= request_ids
.end(); ++it
) {
581 InProgressCheck
& request
= requests_in_progress_
[*it
];
582 install_immediately
|= request
.install_immediately
;
583 request
.in_progress_ids_
.remove(id
);
584 NotifyIfFinished(*it
);
587 // This method is called if no updates were found. However a previous update
588 // check might have queued an update for this extension already. If a
589 // current update check has |install_immediately| set the previously
590 // queued update should be installed now.
591 if (install_immediately
&& service_
->GetPendingExtensionUpdate(id
))
592 service_
->FinishDelayedInstallation(id
);
595 void ExtensionUpdater::OnExtensionDownloadFinished(
596 const CRXFileInfo
& file
,
597 bool file_ownership_passed
,
598 const GURL
& download_url
,
599 const std::string
& version
,
600 const PingResult
& ping
,
601 const std::set
<int>& request_ids
,
602 const InstallCallback
& callback
) {
604 UpdatePingData(file
.extension_id
, ping
);
606 VLOG(2) << download_url
<< " written to " << file
.path
.value();
608 FetchedCRXFile
fetched(file
, file_ownership_passed
, request_ids
, callback
);
609 fetched_crx_files_
.push(fetched
);
611 // MaybeInstallCRXFile() removes extensions from |in_progress_ids_| after
612 // starting the crx installer.
613 MaybeInstallCRXFile();
616 bool ExtensionUpdater::GetPingDataForExtension(
617 const std::string
& id
,
618 ManifestFetchData::PingData
* ping_data
) {
620 ping_data
->rollcall_days
= CalculatePingDays(
621 extension_prefs_
->LastPingDay(id
));
622 ping_data
->is_enabled
= service_
->IsExtensionEnabled(id
);
623 if (!ping_data
->is_enabled
)
624 ping_data
->disable_reasons
= extension_prefs_
->GetDisableReasons(id
);
625 ping_data
->active_days
=
626 CalculateActivePingDays(extension_prefs_
->LastActivePingDay(id
),
627 extension_prefs_
->GetActiveBit(id
));
631 std::string
ExtensionUpdater::GetUpdateUrlData(const std::string
& id
) {
633 return extension::GetUpdateURLData(extension_prefs_
, id
);
636 bool ExtensionUpdater::IsExtensionPending(const std::string
& id
) {
638 return service_
->pending_extension_manager()->IsIdPending(id
);
641 bool ExtensionUpdater::GetExtensionExistingVersion(const std::string
& id
,
642 std::string
* version
) {
644 const Extension
* extension
= service_
->GetExtensionById(id
, true);
647 const Extension
* update
= service_
->GetPendingExtensionUpdate(id
);
649 *version
= update
->VersionString();
651 *version
= extension
->VersionString();
655 bool ExtensionUpdater::ShouldForceUpdate(
656 const std::string
& extension_id
,
657 std::string
* source
) {
658 bool force
= forced_updates_
.find(extension_id
) != forced_updates_
.end();
659 // Currently the only reason to force is a NaCl arch mismatch with the
660 // installed extension contents.
662 *source
= kWrongMultiCrxInstallSource
;
667 void ExtensionUpdater::UpdatePingData(const std::string
& id
,
668 const PingResult
& ping_result
) {
670 if (ping_result
.did_ping
)
671 extension_prefs_
->SetLastPingDay(id
, ping_result
.day_start
);
672 if (extension_prefs_
->GetActiveBit(id
)) {
673 extension_prefs_
->SetActiveBit(id
, false);
674 extension_prefs_
->SetLastActivePingDay(id
, ping_result
.day_start
);
678 void ExtensionUpdater::MaybeInstallCRXFile() {
679 if (crx_install_is_running_
|| fetched_crx_files_
.empty())
682 std::set
<int> request_ids
;
684 while (!fetched_crx_files_
.empty() && !crx_install_is_running_
) {
685 const FetchedCRXFile
& crx_file
= fetched_crx_files_
.top();
687 VLOG(2) << "updating " << crx_file
.info
.extension_id
688 << " with " << crx_file
.info
.path
.value();
690 // The ExtensionService is now responsible for cleaning up the temp file
691 // at |crx_file.info.path|.
692 CrxInstaller
* installer
= NULL
;
693 if (service_
->UpdateExtension(crx_file
.info
,
694 crx_file
.file_ownership_passed
,
696 crx_install_is_running_
= true;
697 current_crx_file_
= crx_file
;
699 for (std::set
<int>::const_iterator it
= crx_file
.request_ids
.begin();
700 it
!= crx_file
.request_ids
.end(); ++it
) {
701 InProgressCheck
& request
= requests_in_progress_
[*it
];
702 if (request
.install_immediately
) {
703 installer
->set_install_immediately(true);
708 // Source parameter ensures that we only see the completion event for the
709 // the installer we started.
711 extensions::NOTIFICATION_CRX_INSTALLER_DONE
,
712 content::Source
<CrxInstaller
>(installer
));
714 for (std::set
<int>::const_iterator it
= crx_file
.request_ids
.begin();
715 it
!= crx_file
.request_ids
.end(); ++it
) {
716 InProgressCheck
& request
= requests_in_progress_
[*it
];
717 request
.in_progress_ids_
.remove(crx_file
.info
.extension_id
);
719 request_ids
.insert(crx_file
.request_ids
.begin(),
720 crx_file
.request_ids
.end());
722 fetched_crx_files_
.pop();
725 for (std::set
<int>::const_iterator it
= request_ids
.begin();
726 it
!= request_ids
.end(); ++it
) {
727 NotifyIfFinished(*it
);
731 void ExtensionUpdater::Observe(int type
,
732 const content::NotificationSource
& source
,
733 const content::NotificationDetails
& details
) {
734 DCHECK_EQ(type
, extensions::NOTIFICATION_CRX_INSTALLER_DONE
);
736 registrar_
.Remove(this, extensions::NOTIFICATION_CRX_INSTALLER_DONE
, source
);
737 crx_install_is_running_
= false;
739 // If installing this file didn't succeed, we may need to re-download it.
740 const Extension
* extension
= content::Details
<const Extension
>(details
).ptr();
741 extensions::CrxInstaller
* installer
=
742 content::Source
<extensions::CrxInstaller
>(source
).ptr();
743 const FetchedCRXFile
& crx_file
= current_crx_file_
;
744 if (!extension
&& installer
->hash_check_failed() &&
745 !crx_file
.callback
.is_null()) {
746 // If extension downloader asked us to notify it about failed installations,
747 // it will resume a pending download from the manifest data it has already
748 // fetched and call us again with the same request_id's (with either
749 // OnExtensionDownloadFailed or OnExtensionDownloadFinished). For that
750 // reason we don't notify finished requests yet.
751 crx_file
.callback
.Run(true);
753 for (std::set
<int>::const_iterator it
= crx_file
.request_ids
.begin();
754 it
!= crx_file
.request_ids
.end(); ++it
) {
755 InProgressCheck
& request
= requests_in_progress_
[*it
];
756 request
.in_progress_ids_
.remove(crx_file
.info
.extension_id
);
757 NotifyIfFinished(*it
);
759 if (!crx_file
.callback
.is_null()) {
760 crx_file
.callback
.Run(false);
764 // If any files are available to update, start one.
765 MaybeInstallCRXFile();
768 void ExtensionUpdater::OnExtensionWillBeInstalled(
769 content::BrowserContext
* browser_context
,
770 const Extension
* extension
,
773 const std::string
& old_name
) {
774 throttle_info_
.erase(extension
->id());
777 void ExtensionUpdater::NotifyStarted() {
778 content::NotificationService::current()->Notify(
779 extensions::NOTIFICATION_EXTENSION_UPDATING_STARTED
,
780 content::Source
<Profile
>(profile_
),
781 content::NotificationService::NoDetails());
784 void ExtensionUpdater::NotifyIfFinished(int request_id
) {
785 DCHECK(ContainsKey(requests_in_progress_
, request_id
));
786 const InProgressCheck
& request
= requests_in_progress_
[request_id
];
787 if (request
.in_progress_ids_
.empty()) {
788 VLOG(2) << "Finished update check " << request_id
;
789 if (!request
.callback
.is_null())
790 request
.callback
.Run();
791 requests_in_progress_
.erase(request_id
);
795 } // namespace extensions