1 // 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 "extensions/browser/extension_registrar.h"
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/stl_util.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/devtools_agent_host.h"
15 #include "content/public/browser/notification_service.h"
16 #include "extensions/browser/app_sorting.h"
17 #include "extensions/browser/extension_host.h"
18 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_system.h"
21 #include "extensions/browser/lazy_context_id.h"
22 #include "extensions/browser/lazy_context_task_queue.h"
23 #include "extensions/browser/notification_types.h"
24 #include "extensions/browser/process_manager.h"
25 #include "extensions/browser/renderer_startup_helper.h"
26 #include "extensions/browser/runtime_data.h"
27 #include "extensions/browser/service_worker_task_queue.h"
28 #include "extensions/browser/task_queue_util.h"
29 #include "extensions/common/manifest_handlers/background_info.h"
30 
31 using content::DevToolsAgentHost;
32 
33 namespace extensions {
34 
ExtensionRegistrar(content::BrowserContext * browser_context,Delegate * delegate)35 ExtensionRegistrar::ExtensionRegistrar(content::BrowserContext* browser_context,
36                                        Delegate* delegate)
37     : browser_context_(browser_context),
38       delegate_(delegate),
39       extension_system_(ExtensionSystem::Get(browser_context)),
40       extension_prefs_(ExtensionPrefs::Get(browser_context)),
41       registry_(ExtensionRegistry::Get(browser_context)),
42       renderer_helper_(
43           RendererStartupHelperFactory::GetForBrowserContext(browser_context)) {
44 }
45 
46 ExtensionRegistrar::~ExtensionRegistrar() = default;
47 
AddExtension(scoped_refptr<const Extension> extension)48 void ExtensionRegistrar::AddExtension(
49     scoped_refptr<const Extension> extension) {
50   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
51 
52   bool is_extension_upgrade = false;
53   bool is_extension_loaded = false;
54   const Extension* old = registry_->GetInstalledExtension(extension->id());
55   if (old) {
56     is_extension_loaded = true;
57     int version_compare_result = extension->version().CompareTo(old->version());
58     is_extension_upgrade = version_compare_result > 0;
59     // Other than for unpacked extensions, we should not be downgrading.
60     if (!Manifest::IsUnpackedLocation(extension->location()) &&
61         version_compare_result < 0) {
62       UMA_HISTOGRAM_ENUMERATION(
63           "Extensions.AttemptedToDowngradeVersionLocation",
64           extension->location(), Manifest::NUM_LOCATIONS);
65       UMA_HISTOGRAM_ENUMERATION("Extensions.AttemptedToDowngradeVersionType",
66                                 extension->GetType(), Manifest::NUM_LOAD_TYPES);
67 
68       // TODO(https://crbug.com/810799): It would be awfully nice to CHECK this,
69       // but that's caused problems. There are apparently times when this
70       // happens that we aren't accounting for. We should track those down and
71       // fix them, but it can be tricky.
72       NOTREACHED() << "Attempted to downgrade extension."
73                    << "\nID: " << extension->id()
74                    << "\nOld Version: " << old->version()
75                    << "\nNew Version: " << extension->version()
76                    << "\nLocation: " << extension->location();
77       return;
78     }
79   }
80 
81   // If the extension was disabled for a reload, we will enable it.
82   bool was_reloading = reloading_extensions_.erase(extension->id()) > 0;
83 
84   // Set the upgraded bit; we consider reloads upgrades.
85   extension_system_->runtime_data()->SetBeingUpgraded(
86       extension->id(), is_extension_upgrade || was_reloading);
87 
88   // The extension is now loaded; remove its data from unloaded extension map.
89   unloaded_extension_paths_.erase(extension->id());
90 
91   // If a terminated extension is loaded, remove it from the terminated list.
92   UntrackTerminatedExtension(extension->id());
93 
94   // Notify the delegate we will add the extension.
95   delegate_->PreAddExtension(extension.get(), old);
96 
97   if (was_reloading) {
98     ReplaceReloadedExtension(extension);
99   } else {
100     if (is_extension_loaded) {
101       // To upgrade an extension in place, remove the old one and then activate
102       // the new one. ReloadExtension disables the extension, which is
103       // sufficient.
104       RemoveExtension(extension->id(), UnloadedExtensionReason::UPDATE);
105     }
106     AddNewExtension(extension);
107   }
108 
109   extension_system_->runtime_data()->SetBeingUpgraded(extension->id(), false);
110 }
111 
AddNewExtension(scoped_refptr<const Extension> extension)112 void ExtensionRegistrar::AddNewExtension(
113     scoped_refptr<const Extension> extension) {
114   if (extension_prefs_->IsExtensionBlacklisted(extension->id())) {
115     DCHECK(!Manifest::IsComponentLocation(extension->location()));
116     // Only prefs is checked for the blacklist. We rely on callers to check the
117     // blacklist before calling into here, e.g. CrxInstaller checks before
118     // installation then threads through the install and pending install flow
119     // of this class, and ExtensionService checks when loading installed
120     // extensions.
121     registry_->AddBlacklisted(extension);
122   } else if (delegate_->ShouldBlockExtension(extension.get())) {
123     DCHECK(!Manifest::IsComponentLocation(extension->location()));
124     registry_->AddBlocked(extension);
125   } else if (extension_prefs_->IsExtensionDisabled(extension->id())) {
126     registry_->AddDisabled(extension);
127     // Notify that a disabled extension was added or updated.
128     content::NotificationService::current()->Notify(
129         extensions::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
130         content::Source<content::BrowserContext>(browser_context_),
131         content::Details<const Extension>(extension.get()));
132   } else {  // Extension should be enabled.
133     // All apps that are displayed in the launcher are ordered by their ordinals
134     // so we must ensure they have valid ordinals.
135     if (extension->RequiresSortOrdinal()) {
136       AppSorting* app_sorting = extension_system_->app_sorting();
137       app_sorting->SetExtensionVisible(extension->id(),
138                                        extension->ShouldDisplayInNewTabPage());
139       app_sorting->EnsureValidOrdinals(extension->id(),
140                                        syncer::StringOrdinal());
141     }
142     registry_->AddEnabled(extension);
143     ActivateExtension(extension.get(), true);
144   }
145 }
146 
RemoveExtension(const ExtensionId & extension_id,UnloadedExtensionReason reason)147 void ExtensionRegistrar::RemoveExtension(const ExtensionId& extension_id,
148                                          UnloadedExtensionReason reason) {
149   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
150 
151   int include_mask =
152       ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::TERMINATED;
153   scoped_refptr<const Extension> extension(
154       registry_->GetExtensionById(extension_id, include_mask));
155 
156   // If the extension was already removed, just notify of the new unload reason.
157   // TODO: It's unclear when this needs to be called given that it may be a
158   // duplicate notification. See crbug.com/708230.
159   if (!extension) {
160     extension_system_->UnregisterExtensionWithRequestContexts(extension_id,
161                                                               reason);
162     return;
163   }
164 
165   // Keep information about the extension so that we can reload it later
166   // even if it's not permanently installed.
167   unloaded_extension_paths_[extension->id()] = extension->path();
168 
169   // Stop tracking whether the extension was meant to be enabled after a reload.
170   reloading_extensions_.erase(extension->id());
171 
172   if (registry_->disabled_extensions().Contains(extension_id)) {
173     // The extension is already deactivated.
174     registry_->RemoveDisabled(extension->id());
175     extension_system_->UnregisterExtensionWithRequestContexts(extension_id,
176                                                               reason);
177   } else {
178     // TODO(michaelpg): The extension may be blocked or blacklisted, in which
179     // case it shouldn't need to be "deactivated". Determine whether the removal
180     // notifications are necessary (crbug.com/708230).
181     registry_->RemoveEnabled(extension_id);
182     DeactivateExtension(extension.get(), reason);
183   }
184 
185   content::NotificationService::current()->Notify(
186       extensions::NOTIFICATION_EXTENSION_REMOVED,
187       content::Source<content::BrowserContext>(browser_context_),
188       content::Details<const Extension>(extension.get()));
189 }
190 
EnableExtension(const ExtensionId & extension_id)191 void ExtensionRegistrar::EnableExtension(const ExtensionId& extension_id) {
192   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
193 
194   // If the extension is currently reloading, it will be enabled once the reload
195   // is complete.
196   if (reloading_extensions_.count(extension_id) > 0)
197     return;
198 
199   // First, check that the extension can be enabled.
200   if (IsExtensionEnabled(extension_id) ||
201       extension_prefs_->IsExtensionBlacklisted(extension_id) ||
202       registry_->blocked_extensions().Contains(extension_id)) {
203     return;
204   }
205 
206   const Extension* extension =
207       registry_->disabled_extensions().GetByID(extension_id);
208   if (extension && !delegate_->CanEnableExtension(extension))
209     return;
210 
211   // Now that we know the extension can be enabled, update the prefs.
212   extension_prefs_->SetExtensionEnabled(extension_id);
213 
214   // This can happen if sync enables an extension that is not installed yet.
215   if (!extension)
216     return;
217 
218   // Actually enable the extension.
219   registry_->AddEnabled(extension);
220   registry_->RemoveDisabled(extension->id());
221   ActivateExtension(extension, false);
222 }
223 
DisableExtension(const ExtensionId & extension_id,int disable_reasons)224 void ExtensionRegistrar::DisableExtension(const ExtensionId& extension_id,
225                                           int disable_reasons) {
226   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
227   DCHECK_NE(disable_reason::DISABLE_NONE, disable_reasons);
228 
229   if (extension_prefs_->IsExtensionBlacklisted(extension_id))
230     return;
231 
232   // The extension may have been disabled already. Just add the disable reasons.
233   // TODO(michaelpg): Move this after the policy check, below, to ensure that
234   // disable reasons disallowed by policy are not added here.
235   if (!IsExtensionEnabled(extension_id)) {
236     extension_prefs_->AddDisableReasons(extension_id, disable_reasons);
237     return;
238   }
239 
240   scoped_refptr<const Extension> extension =
241       registry_->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
242 
243   bool is_controlled_extension =
244       !delegate_->CanDisableExtension(extension.get());
245 
246   if (is_controlled_extension) {
247     // Remove disallowed disable reasons.
248     // Certain disable reasons are always allowed, since they are more internal
249     // to the browser (rather than the user choosing to disable the extension).
250     int internal_disable_reason_mask =
251         extensions::disable_reason::DISABLE_RELOAD |
252         extensions::disable_reason::DISABLE_CORRUPTED |
253         extensions::disable_reason::DISABLE_UPDATE_REQUIRED_BY_POLICY |
254         extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY |
255         extensions::disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED;
256     disable_reasons &= internal_disable_reason_mask;
257 
258     if (disable_reasons == disable_reason::DISABLE_NONE)
259       return;
260   }
261 
262   extension_prefs_->SetExtensionDisabled(extension_id, disable_reasons);
263 
264   int include_mask =
265       ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::DISABLED;
266   extension = registry_->GetExtensionById(extension_id, include_mask);
267   if (!extension)
268     return;
269 
270   // The extension is either enabled or terminated.
271   DCHECK(registry_->enabled_extensions().Contains(extension->id()) ||
272          registry_->terminated_extensions().Contains(extension->id()));
273 
274   // Move the extension to the disabled list.
275   registry_->AddDisabled(extension);
276   if (registry_->enabled_extensions().Contains(extension->id())) {
277     registry_->RemoveEnabled(extension->id());
278     DeactivateExtension(extension.get(), UnloadedExtensionReason::DISABLE);
279   } else {
280     // The extension must have been terminated. Don't send additional
281     // notifications for it being disabled.
282     bool removed = registry_->RemoveTerminated(extension->id());
283     DCHECK(removed);
284   }
285 }
286 
ReloadExtension(const ExtensionId extension_id,LoadErrorBehavior load_error_behavior)287 void ExtensionRegistrar::ReloadExtension(
288     const ExtensionId extension_id,  // Passed by value because reloading can
289                                      // invalidate a reference to the ID.
290     LoadErrorBehavior load_error_behavior) {
291   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
292 
293   // If the extension is already reloading, don't reload again.
294   if (extension_prefs_->HasDisableReason(extension_id,
295                                          disable_reason::DISABLE_RELOAD)) {
296     return;
297   }
298 
299   // Ignore attempts to reload a blacklisted or blocked extension. Sometimes
300   // this can happen in a convoluted reload sequence triggered by the
301   // termination of a blacklisted or blocked extension and a naive attempt to
302   // reload it. For an example see http://crbug.com/373842.
303   if (registry_->blacklisted_extensions().Contains(extension_id) ||
304       registry_->blocked_extensions().Contains(extension_id)) {
305     return;
306   }
307 
308   base::FilePath path;
309 
310   const Extension* enabled_extension =
311       registry_->enabled_extensions().GetByID(extension_id);
312 
313   // Disable the extension if it's loaded. It might not be loaded if it crashed.
314   if (enabled_extension) {
315     // If the extension has an inspector open for its background page, detach
316     // the inspector and hang onto a cookie for it, so that we can reattach
317     // later.
318     // TODO(yoz): this is not incognito-safe!
319     ProcessManager* manager = ProcessManager::Get(browser_context_);
320     ExtensionHost* host = manager->GetBackgroundHostForExtension(extension_id);
321     if (host && content::DevToolsAgentHost::HasFor(host->host_contents())) {
322       // Look for an open inspector for the background page.
323       scoped_refptr<content::DevToolsAgentHost> agent_host =
324           content::DevToolsAgentHost::GetOrCreateFor(host->host_contents());
325       agent_host->DisconnectWebContents();
326       orphaned_dev_tools_[extension_id] = agent_host;
327     }
328 
329     path = enabled_extension->path();
330     // BeingUpgraded is set back to false when the extension is added.
331     extension_system_->runtime_data()->SetBeingUpgraded(enabled_extension->id(),
332                                                         true);
333     DisableExtension(extension_id, disable_reason::DISABLE_RELOAD);
334     DCHECK(registry_->disabled_extensions().Contains(extension_id));
335     reloading_extensions_.insert(extension_id);
336   } else {
337     std::map<ExtensionId, base::FilePath>::const_iterator iter =
338         unloaded_extension_paths_.find(extension_id);
339     if (iter == unloaded_extension_paths_.end()) {
340       return;
341     }
342     path = unloaded_extension_paths_[extension_id];
343   }
344 
345   delegate_->LoadExtensionForReload(extension_id, path, load_error_behavior);
346 }
347 
TerminateExtension(const ExtensionId & extension_id)348 void ExtensionRegistrar::TerminateExtension(const ExtensionId& extension_id) {
349   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
350 
351   scoped_refptr<const Extension> extension =
352       registry_->enabled_extensions().GetByID(extension_id);
353   if (!extension)
354     return;
355 
356   // Keep information about the extension so that we can reload it later
357   // even if it's not permanently installed.
358   unloaded_extension_paths_[extension->id()] = extension->path();
359 
360   DCHECK(!base::Contains(reloading_extensions_, extension->id()))
361       << "Enabled extension shouldn't be marked for reloading";
362 
363   registry_->AddTerminated(extension);
364   registry_->RemoveEnabled(extension_id);
365   DeactivateExtension(extension.get(), UnloadedExtensionReason::TERMINATE);
366 }
367 
UntrackTerminatedExtension(const ExtensionId & extension_id)368 void ExtensionRegistrar::UntrackTerminatedExtension(
369     const ExtensionId& extension_id) {
370   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
371 
372   scoped_refptr<const Extension> extension =
373       registry_->terminated_extensions().GetByID(extension_id);
374   if (!extension)
375     return;
376 
377   registry_->RemoveTerminated(extension_id);
378 
379   // TODO(michaelpg): This notification was already sent when the extension was
380   // unloaded as part of being terminated. But we send it again as observers
381   // may be tracking the terminated extension. See crbug.com/708230.
382   content::NotificationService::current()->Notify(
383       extensions::NOTIFICATION_EXTENSION_REMOVED,
384       content::Source<content::BrowserContext>(browser_context_),
385       content::Details<const Extension>(extension.get()));
386 }
387 
IsExtensionEnabled(const ExtensionId & extension_id) const388 bool ExtensionRegistrar::IsExtensionEnabled(
389     const ExtensionId& extension_id) const {
390   if (registry_->enabled_extensions().Contains(extension_id) ||
391       registry_->terminated_extensions().Contains(extension_id)) {
392     return true;
393   }
394 
395   if (registry_->disabled_extensions().Contains(extension_id) ||
396       registry_->blacklisted_extensions().Contains(extension_id) ||
397       registry_->blocked_extensions().Contains(extension_id)) {
398     return false;
399   }
400 
401   if (delegate_->ShouldBlockExtension(nullptr))
402     return false;
403 
404   // If the extension hasn't been loaded yet, check the prefs for it. Assume
405   // enabled unless otherwise noted.
406   return !extension_prefs_->IsExtensionDisabled(extension_id) &&
407          !extension_prefs_->IsExtensionBlacklisted(extension_id) &&
408          !extension_prefs_->IsExternalExtensionUninstalled(extension_id);
409 }
410 
DidCreateRenderViewForBackgroundPage(ExtensionHost * host)411 void ExtensionRegistrar::DidCreateRenderViewForBackgroundPage(
412     ExtensionHost* host) {
413   auto iter = orphaned_dev_tools_.find(host->extension_id());
414   if (iter == orphaned_dev_tools_.end())
415     return;
416   // Keepalive count is reset on extension reload. This re-establishes the
417   // keepalive that was added when the DevTools agent was initially attached.
418   ProcessManager::Get(browser_context_)
419       ->IncrementLazyKeepaliveCount(host->extension(), Activity::DEV_TOOLS,
420                                     std::string());
421   iter->second->ConnectWebContents(host->host_contents());
422   orphaned_dev_tools_.erase(iter);
423 }
424 
ActivateExtension(const Extension * extension,bool is_newly_added)425 void ExtensionRegistrar::ActivateExtension(const Extension* extension,
426                                            bool is_newly_added) {
427   // The URLRequestContexts need to be first to know that the extension
428   // was loaded. Otherwise a race can arise where a renderer that is created
429   // for the extension may try to load an extension URL with an extension id
430   // that the request context doesn't yet know about. The BrowserContext should
431   // ensure its URLRequestContexts appropriately discover the loaded extension.
432   extension_system_->RegisterExtensionWithRequestContexts(
433       extension,
434       base::BindOnce(
435           &ExtensionRegistrar::OnExtensionRegisteredWithRequestContexts,
436           weak_factory_.GetWeakPtr(), WrapRefCounted(extension)));
437 
438   // Activate the extension before calling
439   // RendererStartupHelper::OnExtensionLoaded() below, so that we have
440   // activation information ready while we send ExtensionMsg_Load IPC.
441   //
442   // TODO(lazyboy): We should move all logic that is required to start up an
443   // extension to a separate class, instead of calling adhoc methods like
444   // service worker ones below.
445   ActivateTaskQueueForExtension(browser_context_, extension);
446 
447   renderer_helper_->OnExtensionLoaded(*extension);
448 
449   // Tell subsystems that use the ExtensionRegistryObserver::OnExtensionLoaded
450   // about the new extension.
451   //
452   // NOTE: It is important that this happen after notifying the renderers about
453   // the new extensions so that if we navigate to an extension URL in
454   // ExtensionRegistryObserver::OnExtensionLoaded the renderer is guaranteed to
455   // know about it.
456   registry_->TriggerOnLoaded(extension);
457 
458   delegate_->PostActivateExtension(extension);
459 
460   // When an existing extension is re-enabled, it may be necessary to spin up
461   // its lazy background page.
462   if (!is_newly_added)
463     MaybeSpinUpLazyBackgroundPage(extension);
464 }
465 
DeactivateExtension(const Extension * extension,UnloadedExtensionReason reason)466 void ExtensionRegistrar::DeactivateExtension(const Extension* extension,
467                                              UnloadedExtensionReason reason) {
468   registry_->TriggerOnUnloaded(extension, reason);
469   renderer_helper_->OnExtensionUnloaded(*extension);
470   extension_system_->UnregisterExtensionWithRequestContexts(extension->id(),
471                                                             reason);
472   DeactivateTaskQueueForExtension(browser_context_, extension);
473 
474   delegate_->PostDeactivateExtension(extension);
475 }
476 
ReplaceReloadedExtension(scoped_refptr<const Extension> extension)477 bool ExtensionRegistrar::ReplaceReloadedExtension(
478     scoped_refptr<const Extension> extension) {
479   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
480 
481   // The extension must already be disabled, and the original extension has
482   // been unloaded.
483   CHECK(registry_->disabled_extensions().Contains(extension->id()));
484   if (!delegate_->CanEnableExtension(extension.get()))
485     return false;
486 
487   // TODO(michaelpg): Other disable reasons might have been added after the
488   // reload started. We may want to keep the extension disabled and just remove
489   // the DISABLE_RELOAD reason in that case.
490   extension_prefs_->SetExtensionEnabled(extension->id());
491 
492   // Move it over to the enabled list.
493   CHECK(registry_->RemoveDisabled(extension->id()));
494   CHECK(registry_->AddEnabled(extension));
495 
496   ActivateExtension(extension.get(), false);
497 
498   return true;
499 }
500 
OnExtensionRegisteredWithRequestContexts(scoped_refptr<const Extension> extension)501 void ExtensionRegistrar::OnExtensionRegisteredWithRequestContexts(
502     scoped_refptr<const Extension> extension) {
503   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
504 
505   registry_->AddReady(extension);
506   if (registry_->enabled_extensions().Contains(extension->id()))
507     registry_->TriggerOnReady(extension.get());
508 }
509 
MaybeSpinUpLazyBackgroundPage(const Extension * extension)510 void ExtensionRegistrar::MaybeSpinUpLazyBackgroundPage(
511     const Extension* extension) {
512   if (!BackgroundInfo::HasLazyBackgroundPage(extension))
513     return;
514 
515   // For orphaned devtools, we will reconnect devtools to it later in
516   // DidCreateRenderViewForBackgroundPage().
517   bool has_orphaned_dev_tools =
518       base::Contains(orphaned_dev_tools_, extension->id());
519 
520   // Reloading component extension does not trigger install, so RuntimeAPI won't
521   // be able to detect its loading. Therefore, we need to spin up its lazy
522   // background page.
523   bool is_component_extension =
524       Manifest::IsComponentLocation(extension->location());
525 
526   if (!has_orphaned_dev_tools && !is_component_extension)
527     return;
528 
529   // Wake up the event page by posting a dummy task.
530   const LazyContextId context_id(browser_context_, extension->id());
531   context_id.GetTaskQueue()->AddPendingTask(context_id, base::DoNothing());
532 }
533 
534 }  // namespace extensions
535