Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_system_impl.cc
1 // Copyright 2014 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/extensions/extension_system_impl.h"
6
7 #include "base/base_switches.h"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_tokenizer.h"
15 #include "base/strings/string_util.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/content_settings/cookie_settings.h"
18 #include "chrome/browser/extensions/component_loader.h"
19 #include "chrome/browser/extensions/declarative_user_script_master.h"
20 #include "chrome/browser/extensions/error_console/error_console.h"
21 #include "chrome/browser/extensions/extension_error_reporter.h"
22 #include "chrome/browser/extensions/extension_management.h"
23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/browser/extensions/extension_system_factory.h"
25 #include "chrome/browser/extensions/extension_util.h"
26 #include "chrome/browser/extensions/install_verifier.h"
27 #include "chrome/browser/extensions/navigation_observer.h"
28 #include "chrome/browser/extensions/shared_module_service.h"
29 #include "chrome/browser/extensions/shared_user_script_master.h"
30 #include "chrome/browser/extensions/state_store_notification_observer.h"
31 #include "chrome/browser/extensions/unpacked_installer.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/profiles/profile_manager.h"
34 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/chrome_version_info.h"
37 #include "chrome/common/extensions/extension_constants.h"
38 #include "chrome/common/extensions/extension_file_util.h"
39 #include "chrome/common/extensions/features/feature_channel.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "content/public/browser/url_data_source.h"
42 #include "extensions/browser/content_verifier.h"
43 #include "extensions/browser/content_verifier_delegate.h"
44 #include "extensions/browser/event_router.h"
45 #include "extensions/browser/extension_pref_store.h"
46 #include "extensions/browser/extension_pref_value_map.h"
47 #include "extensions/browser/extension_pref_value_map_factory.h"
48 #include "extensions/browser/extension_prefs.h"
49 #include "extensions/browser/extension_registry.h"
50 #include "extensions/browser/info_map.h"
51 #include "extensions/browser/lazy_background_task_queue.h"
52 #include "extensions/browser/management_policy.h"
53 #include "extensions/browser/quota_service.h"
54 #include "extensions/browser/runtime_data.h"
55 #include "extensions/browser/state_store.h"
56 #include "extensions/common/constants.h"
57 #include "extensions/common/extension.h"
58 #include "extensions/common/extension_urls.h"
59 #include "extensions/common/manifest.h"
60 #include "extensions/common/manifest_url_handlers.h"
61 #include "net/base/escape.h"
62
63 #if defined(ENABLE_NOTIFICATIONS)
64 #include "chrome/browser/notifications/desktop_notification_service.h"
65 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
66 #include "ui/message_center/notifier_settings.h"
67 #endif
68
69 #if defined(OS_CHROMEOS)
70 #include "chrome/browser/app_mode/app_mode_utils.h"
71 #include "chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.h"
72 #include "chrome/browser/chromeos/policy/device_local_account.h"
73 #include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
74 #include "chromeos/chromeos_switches.h"
75 #include "chromeos/login/login_state.h"
76 #include "components/user_manager/user.h"
77 #include "components/user_manager/user_manager.h"
78 #endif
79
80 using content::BrowserThread;
81
82 namespace {
83
84 const char kContentVerificationExperimentName[] =
85     "ExtensionContentVerification";
86
87 }  // namespace
88
89 namespace extensions {
90
91 //
92 // ExtensionSystemImpl::Shared
93 //
94
95 ExtensionSystemImpl::Shared::Shared(Profile* profile)
96     : profile_(profile) {
97 }
98
99 ExtensionSystemImpl::Shared::~Shared() {
100 }
101
102 void ExtensionSystemImpl::Shared::InitPrefs() {
103   lazy_background_task_queue_.reset(new LazyBackgroundTaskQueue(profile_));
104   event_router_.reset(new EventRouter(profile_, ExtensionPrefs::Get(profile_)));
105   // Two state stores. The latter, which contains declarative rules, must be
106   // loaded immediately so that the rules are ready before we issue network
107   // requests.
108   state_store_.reset(new StateStore(
109       profile_,
110       profile_->GetPath().AppendASCII(extensions::kStateStoreName),
111       true));
112   state_store_notification_observer_.reset(
113       new StateStoreNotificationObserver(state_store_.get()));
114
115   rules_store_.reset(new StateStore(
116       profile_,
117       profile_->GetPath().AppendASCII(extensions::kRulesStoreName),
118       false));
119
120 #if defined(OS_CHROMEOS)
121   const user_manager::User* user =
122       user_manager::UserManager::Get()->GetActiveUser();
123   policy::DeviceLocalAccount::Type device_local_account_type;
124   if (user && policy::IsDeviceLocalAccountUser(user->email(),
125                                                &device_local_account_type)) {
126     device_local_account_management_policy_provider_.reset(
127         new chromeos::DeviceLocalAccountManagementPolicyProvider(
128             device_local_account_type));
129   }
130 #endif  // defined(OS_CHROMEOS)
131 }
132
133 void ExtensionSystemImpl::Shared::RegisterManagementPolicyProviders() {
134   management_policy_->RegisterProviders(
135       ExtensionManagementFactory::GetForBrowserContext(profile_)
136           ->GetProviders());
137
138 #if defined(OS_CHROMEOS)
139   if (device_local_account_management_policy_provider_) {
140     management_policy_->RegisterProvider(
141         device_local_account_management_policy_provider_.get());
142   }
143 #endif  // defined(OS_CHROMEOS)
144
145   management_policy_->RegisterProvider(install_verifier_.get());
146 }
147
148 namespace {
149
150 class ContentVerifierDelegateImpl : public ContentVerifierDelegate {
151  public:
152   explicit ContentVerifierDelegateImpl(ExtensionService* service)
153       : service_(service->AsWeakPtr()), default_mode_(GetDefaultMode()) {}
154
155   ~ContentVerifierDelegateImpl() override {}
156
157   Mode ShouldBeVerified(const Extension& extension) override {
158 #if defined(OS_CHROMEOS)
159     if (ExtensionAssetsManagerChromeOS::IsSharedInstall(&extension))
160       return ContentVerifierDelegate::ENFORCE_STRICT;
161 #endif
162
163     if (!extension.is_extension() && !extension.is_legacy_packaged_app())
164       return ContentVerifierDelegate::NONE;
165     if (!Manifest::IsAutoUpdateableLocation(extension.location()))
166       return ContentVerifierDelegate::NONE;
167
168     if (!ManifestURL::UpdatesFromGallery(&extension)) {
169       // It's possible that the webstore update url was overridden for testing
170       // so also consider extensions with the default (production) update url
171       // to be from the store as well.
172       GURL default_webstore_url = extension_urls::GetDefaultWebstoreUpdateUrl();
173       if (ManifestURL::GetUpdateURL(&extension) != default_webstore_url)
174         return ContentVerifierDelegate::NONE;
175     }
176
177     return default_mode_;
178   }
179
180   const ContentVerifierKey& PublicKey() override {
181     static ContentVerifierKey key(
182         extension_misc::kWebstoreSignaturesPublicKey,
183         extension_misc::kWebstoreSignaturesPublicKeySize);
184     return key;
185   }
186
187   GURL GetSignatureFetchUrl(const std::string& extension_id,
188                             const base::Version& version) override {
189     // TODO(asargent) Factor out common code from the extension updater's
190     // ManifestFetchData class that can be shared for use here.
191     std::vector<std::string> parts;
192     parts.push_back("uc");
193     parts.push_back("installsource=signature");
194     parts.push_back("id=" + extension_id);
195     parts.push_back("v=" + version.GetString());
196     std::string x_value =
197         net::EscapeQueryParamValue(JoinString(parts, "&"), true);
198     std::string query = "response=redirect&x=" + x_value;
199
200     GURL base_url = extension_urls::GetWebstoreUpdateUrl();
201     GURL::Replacements replacements;
202     replacements.SetQuery(query.c_str(), url::Component(0, query.length()));
203     return base_url.ReplaceComponents(replacements);
204   }
205
206   std::set<base::FilePath> GetBrowserImagePaths(
207       const extensions::Extension* extension) override {
208     return extension_file_util::GetBrowserImagePaths(extension);
209   }
210
211   void VerifyFailed(const std::string& extension_id,
212                     ContentVerifyJob::FailureReason reason) override {
213     if (!service_)
214       return;
215     ExtensionRegistry* registry = ExtensionRegistry::Get(service_->profile());
216     const Extension* extension =
217         registry->GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
218     if (!extension)
219       return;
220     Mode mode = ShouldBeVerified(*extension);
221     if (mode >= ContentVerifierDelegate::ENFORCE) {
222       service_->DisableExtension(extension_id, Extension::DISABLE_CORRUPTED);
223       ExtensionPrefs::Get(service_->profile())
224           ->IncrementCorruptedDisableCount();
225       UMA_HISTOGRAM_BOOLEAN("Extensions.CorruptExtensionBecameDisabled", true);
226       UMA_HISTOGRAM_ENUMERATION("Extensions.CorruptExtensionDisabledReason",
227           reason, ContentVerifyJob::FAILURE_REASON_MAX);
228     } else if (!ContainsKey(would_be_disabled_ids_, extension_id)) {
229       UMA_HISTOGRAM_BOOLEAN("Extensions.CorruptExtensionWouldBeDisabled", true);
230       would_be_disabled_ids_.insert(extension_id);
231     }
232   }
233
234   static Mode GetDefaultMode() {
235     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
236
237     Mode experiment_value = NONE;
238     const std::string group = base::FieldTrialList::FindFullName(
239         kContentVerificationExperimentName);
240     if (group == "EnforceStrict")
241       experiment_value = ContentVerifierDelegate::ENFORCE_STRICT;
242     else if (group == "Enforce")
243       experiment_value = ContentVerifierDelegate::ENFORCE;
244     else if (group == "Bootstrap")
245       experiment_value = ContentVerifierDelegate::BOOTSTRAP;
246
247     // The field trial value that normally comes from the server can be
248     // overridden on the command line, which we don't want to allow since
249     // malware can set chrome command line flags. There isn't currently a way
250     // to find out what the server-provided value is in this case, so we
251     // conservatively default to the strictest mode if we detect our experiment
252     // name being overridden.
253     if (command_line->HasSwitch(switches::kForceFieldTrials)) {
254       std::string forced_trials =
255           command_line->GetSwitchValueASCII(switches::kForceFieldTrials);
256       if (forced_trials.find(kContentVerificationExperimentName) !=
257               std::string::npos)
258         experiment_value = ContentVerifierDelegate::ENFORCE_STRICT;
259     }
260
261     Mode cmdline_value = NONE;
262     if (command_line->HasSwitch(switches::kExtensionContentVerification)) {
263       std::string switch_value = command_line->GetSwitchValueASCII(
264           switches::kExtensionContentVerification);
265       if (switch_value == switches::kExtensionContentVerificationBootstrap)
266         cmdline_value = ContentVerifierDelegate::BOOTSTRAP;
267       else if (switch_value == switches::kExtensionContentVerificationEnforce)
268         cmdline_value = ContentVerifierDelegate::ENFORCE;
269       else if (switch_value ==
270               switches::kExtensionContentVerificationEnforceStrict)
271         cmdline_value = ContentVerifierDelegate::ENFORCE_STRICT;
272       else
273         // If no value was provided (or the wrong one), just default to enforce.
274         cmdline_value = ContentVerifierDelegate::ENFORCE;
275     }
276
277     // We don't want to allow the command-line flags to eg disable enforcement
278     // if the experiment group says it should be on, or malware may just modify
279     // the command line flags. So return the more restrictive of the 2 values.
280     return std::max(experiment_value, cmdline_value);
281   }
282
283  private:
284   base::WeakPtr<ExtensionService> service_;
285   ContentVerifierDelegate::Mode default_mode_;
286
287   // For reporting metrics in BOOTSTRAP mode, when an extension would be
288   // disabled if content verification was in ENFORCE mode.
289   std::set<std::string> would_be_disabled_ids_;
290
291   DISALLOW_COPY_AND_ASSIGN(ContentVerifierDelegateImpl);
292 };
293
294 }  // namespace
295
296 void ExtensionSystemImpl::Shared::Init(bool extensions_enabled) {
297   const CommandLine* command_line = CommandLine::ForCurrentProcess();
298
299   navigation_observer_.reset(new NavigationObserver(profile_));
300
301   bool allow_noisy_errors = !command_line->HasSwitch(switches::kNoErrorDialogs);
302   ExtensionErrorReporter::Init(allow_noisy_errors);
303
304   shared_user_script_master_.reset(new SharedUserScriptMaster(profile_));
305
306   // ExtensionService depends on RuntimeData.
307   runtime_data_.reset(new RuntimeData(ExtensionRegistry::Get(profile_)));
308
309   bool autoupdate_enabled = !profile_->IsGuestSession();
310 #if defined(OS_CHROMEOS)
311   if (!extensions_enabled)
312     autoupdate_enabled = false;
313 #endif
314   extension_service_.reset(new ExtensionService(
315       profile_,
316       CommandLine::ForCurrentProcess(),
317       profile_->GetPath().AppendASCII(extensions::kInstallDirectoryName),
318       ExtensionPrefs::Get(profile_),
319       Blacklist::Get(profile_),
320       autoupdate_enabled,
321       extensions_enabled,
322       &ready_));
323
324   // These services must be registered before the ExtensionService tries to
325   // load any extensions.
326   {
327     install_verifier_.reset(
328         new InstallVerifier(ExtensionPrefs::Get(profile_), profile_));
329     install_verifier_->Init();
330     content_verifier_ = new ContentVerifier(
331         profile_, new ContentVerifierDelegateImpl(extension_service_.get()));
332     ContentVerifierDelegate::Mode mode =
333         ContentVerifierDelegateImpl::GetDefaultMode();
334 #if defined(OS_CHROMEOS)
335     mode = std::max(mode, ContentVerifierDelegate::BOOTSTRAP);
336 #endif
337     if (mode >= ContentVerifierDelegate::BOOTSTRAP)
338       content_verifier_->Start();
339     info_map()->SetContentVerifier(content_verifier_.get());
340
341     management_policy_.reset(new ManagementPolicy);
342     RegisterManagementPolicyProviders();
343   }
344
345   bool skip_session_extensions = false;
346 #if defined(OS_CHROMEOS)
347   // Skip loading session extensions if we are not in a user session.
348   skip_session_extensions = !chromeos::LoginState::Get()->IsUserLoggedIn();
349   if (chrome::IsRunningInForcedAppMode()) {
350     extension_service_->component_loader()->
351         AddDefaultComponentExtensionsForKioskMode(skip_session_extensions);
352   } else {
353     extension_service_->component_loader()->AddDefaultComponentExtensions(
354         skip_session_extensions);
355   }
356 #else
357   extension_service_->component_loader()->AddDefaultComponentExtensions(
358       skip_session_extensions);
359 #endif
360   if (command_line->HasSwitch(switches::kLoadComponentExtension)) {
361     CommandLine::StringType path_list = command_line->GetSwitchValueNative(
362         switches::kLoadComponentExtension);
363     base::StringTokenizerT<CommandLine::StringType,
364         CommandLine::StringType::const_iterator> t(path_list,
365                                                    FILE_PATH_LITERAL(","));
366     while (t.GetNext()) {
367       // Load the component extension manifest synchronously.
368       // Blocking the UI thread is acceptable here since
369       // this flag designated for developers.
370       base::ThreadRestrictions::ScopedAllowIO allow_io;
371       extension_service_->component_loader()->AddOrReplace(
372           base::FilePath(t.token()));
373     }
374   }
375   extension_service_->Init();
376
377   // Make the chrome://extension-icon/ resource available.
378   content::URLDataSource::Add(profile_, new ExtensionIconSource(profile_));
379
380   error_console_.reset(new ErrorConsole(profile_));
381   quota_service_.reset(new QuotaService);
382
383   if (extensions_enabled) {
384     // Load any extensions specified with --load-extension.
385     // TODO(yoz): Seems like this should move into ExtensionService::Init.
386     // But maybe it's no longer important.
387     if (command_line->HasSwitch(switches::kLoadExtension)) {
388       CommandLine::StringType path_list = command_line->GetSwitchValueNative(
389           switches::kLoadExtension);
390       base::StringTokenizerT<CommandLine::StringType,
391           CommandLine::StringType::const_iterator> t(path_list,
392                                                      FILE_PATH_LITERAL(","));
393       while (t.GetNext()) {
394         std::string extension_id;
395         UnpackedInstaller::Create(extension_service_.get())->
396             LoadFromCommandLine(base::FilePath(t.token()), &extension_id);
397       }
398     }
399   }
400 }
401
402 void ExtensionSystemImpl::Shared::Shutdown() {
403   if (content_verifier_.get())
404     content_verifier_->Shutdown();
405   if (extension_service_)
406     extension_service_->Shutdown();
407 }
408
409 StateStore* ExtensionSystemImpl::Shared::state_store() {
410   return state_store_.get();
411 }
412
413 StateStore* ExtensionSystemImpl::Shared::rules_store() {
414   return rules_store_.get();
415 }
416
417 ExtensionService* ExtensionSystemImpl::Shared::extension_service() {
418   return extension_service_.get();
419 }
420
421 RuntimeData* ExtensionSystemImpl::Shared::runtime_data() {
422   return runtime_data_.get();
423 }
424
425 ManagementPolicy* ExtensionSystemImpl::Shared::management_policy() {
426   return management_policy_.get();
427 }
428
429 SharedUserScriptMaster*
430 ExtensionSystemImpl::Shared::shared_user_script_master() {
431   return shared_user_script_master_.get();
432 }
433
434 InfoMap* ExtensionSystemImpl::Shared::info_map() {
435   if (!extension_info_map_.get())
436     extension_info_map_ = new InfoMap();
437   return extension_info_map_.get();
438 }
439
440 LazyBackgroundTaskQueue*
441     ExtensionSystemImpl::Shared::lazy_background_task_queue() {
442   return lazy_background_task_queue_.get();
443 }
444
445 EventRouter* ExtensionSystemImpl::Shared::event_router() {
446   return event_router_.get();
447 }
448
449 ErrorConsole* ExtensionSystemImpl::Shared::error_console() {
450   return error_console_.get();
451 }
452
453 InstallVerifier* ExtensionSystemImpl::Shared::install_verifier() {
454   return install_verifier_.get();
455 }
456
457 QuotaService* ExtensionSystemImpl::Shared::quota_service() {
458   return quota_service_.get();
459 }
460
461 ContentVerifier* ExtensionSystemImpl::Shared::content_verifier() {
462   return content_verifier_.get();
463 }
464
465 DeclarativeUserScriptMaster*
466 ExtensionSystemImpl::Shared::GetDeclarativeUserScriptMasterByExtension(
467     const ExtensionId& extension_id) {
468   DCHECK(ready().is_signaled());
469   DeclarativeUserScriptMaster* master = NULL;
470   for (ScopedVector<DeclarativeUserScriptMaster>::iterator it =
471            declarative_user_script_masters_.begin();
472        it != declarative_user_script_masters_.end();
473        ++it) {
474     if ((*it)->extension_id() == extension_id) {
475       master = *it;
476       break;
477     }
478   }
479   if (!master) {
480     master = new DeclarativeUserScriptMaster(profile_, extension_id);
481     declarative_user_script_masters_.push_back(master);
482   }
483   return master;
484 }
485
486 //
487 // ExtensionSystemImpl
488 //
489
490 ExtensionSystemImpl::ExtensionSystemImpl(Profile* profile)
491     : profile_(profile) {
492   shared_ = ExtensionSystemSharedFactory::GetForBrowserContext(profile);
493
494   if (!profile->IsOffTheRecord()) {
495     shared_->InitPrefs();
496   }
497 }
498
499 ExtensionSystemImpl::~ExtensionSystemImpl() {
500 }
501
502 void ExtensionSystemImpl::Shutdown() {
503 }
504
505 void ExtensionSystemImpl::InitForRegularProfile(bool extensions_enabled) {
506   DCHECK(!profile_->IsOffTheRecord());
507   if (shared_user_script_master() || extension_service())
508     return;  // Already initialized.
509
510   // The InfoMap needs to be created before the ProcessManager.
511   shared_->info_map();
512   shared_->Init(extensions_enabled);
513 }
514
515 ExtensionService* ExtensionSystemImpl::extension_service() {
516   return shared_->extension_service();
517 }
518
519 RuntimeData* ExtensionSystemImpl::runtime_data() {
520   return shared_->runtime_data();
521 }
522
523 ManagementPolicy* ExtensionSystemImpl::management_policy() {
524   return shared_->management_policy();
525 }
526
527 SharedUserScriptMaster* ExtensionSystemImpl::shared_user_script_master() {
528   return shared_->shared_user_script_master();
529 }
530
531 StateStore* ExtensionSystemImpl::state_store() {
532   return shared_->state_store();
533 }
534
535 StateStore* ExtensionSystemImpl::rules_store() {
536   return shared_->rules_store();
537 }
538
539 InfoMap* ExtensionSystemImpl::info_map() { return shared_->info_map(); }
540
541 LazyBackgroundTaskQueue* ExtensionSystemImpl::lazy_background_task_queue() {
542   return shared_->lazy_background_task_queue();
543 }
544
545 EventRouter* ExtensionSystemImpl::event_router() {
546   return shared_->event_router();
547 }
548
549 const OneShotEvent& ExtensionSystemImpl::ready() const {
550   return shared_->ready();
551 }
552
553 ErrorConsole* ExtensionSystemImpl::error_console() {
554   return shared_->error_console();
555 }
556
557 InstallVerifier* ExtensionSystemImpl::install_verifier() {
558   return shared_->install_verifier();
559 }
560
561 QuotaService* ExtensionSystemImpl::quota_service() {
562   return shared_->quota_service();
563 }
564
565 ContentVerifier* ExtensionSystemImpl::content_verifier() {
566   return shared_->content_verifier();
567 }
568
569 scoped_ptr<ExtensionSet> ExtensionSystemImpl::GetDependentExtensions(
570     const Extension* extension) {
571   return extension_service()->shared_module_service()->GetDependentExtensions(
572       extension);
573 }
574
575 DeclarativeUserScriptMaster*
576 ExtensionSystemImpl::GetDeclarativeUserScriptMasterByExtension(
577     const ExtensionId& extension_id) {
578   return shared_->GetDeclarativeUserScriptMasterByExtension(extension_id);
579 }
580
581 void ExtensionSystemImpl::RegisterExtensionWithRequestContexts(
582     const Extension* extension) {
583   base::Time install_time;
584   if (extension->location() != Manifest::COMPONENT) {
585     install_time = ExtensionPrefs::Get(profile_)->
586         GetInstallTime(extension->id());
587   }
588   bool incognito_enabled = util::IsIncognitoEnabled(extension->id(), profile_);
589
590   bool notifications_disabled = false;
591 #if defined(ENABLE_NOTIFICATIONS)
592   message_center::NotifierId notifier_id(
593       message_center::NotifierId::APPLICATION,
594       extension->id());
595
596   DesktopNotificationService* notification_service =
597       DesktopNotificationServiceFactory::GetForProfile(profile_);
598   notifications_disabled =
599       !notification_service->IsNotifierEnabled(notifier_id);
600 #endif
601
602   BrowserThread::PostTask(
603       BrowserThread::IO, FROM_HERE,
604       base::Bind(&InfoMap::AddExtension, info_map(),
605                  make_scoped_refptr(extension), install_time,
606                  incognito_enabled, notifications_disabled));
607 }
608
609 void ExtensionSystemImpl::UnregisterExtensionWithRequestContexts(
610     const std::string& extension_id,
611     const UnloadedExtensionInfo::Reason reason) {
612   BrowserThread::PostTask(
613       BrowserThread::IO,
614       FROM_HERE,
615       base::Bind(&InfoMap::RemoveExtension, info_map(), extension_id, reason));
616 }
617
618 }  // namespace extensions