1 // Copyright 2013 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 "extensions/browser/process_manager.h"
8 #include "base/command_line.h"
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/api/runtime/runtime_api.h"
18 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/devtools_agent_host.h"
21 #include "content/public/browser/devtools_manager.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/render_frame_host.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/site_instance.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_delegate.h"
29 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/browser/web_contents_user_data.h"
31 #include "content/public/common/renderer_preferences.h"
32 #include "content/public/common/url_constants.h"
33 #include "extensions/browser/extension_host.h"
34 #include "extensions/browser/extension_registry.h"
35 #include "extensions/browser/extension_system.h"
36 #include "extensions/browser/extensions_browser_client.h"
37 #include "extensions/browser/view_type_utils.h"
38 #include "extensions/common/constants.h"
39 #include "extensions/common/extension.h"
40 #include "extensions/common/extension_messages.h"
41 #include "extensions/common/manifest_handlers/background_info.h"
42 #include "extensions/common/manifest_handlers/incognito_info.h"
43 #include "extensions/common/one_shot_event.h"
44 #include "extensions/common/switches.h"
46 using content::BrowserContext;
47 using content::RenderViewHost;
48 using content::SiteInstance;
49 using content::WebContents;
51 namespace extensions {
52 class RenderViewHostDestructionObserver;
54 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
55 extensions::RenderViewHostDestructionObserver);
57 namespace extensions {
61 std::string GetExtensionID(RenderViewHost* render_view_host) {
62 // This works for both apps and extensions because the site has been
63 // normalized to the extension URL for hosted apps.
64 content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
68 const GURL& site_url = site_instance->GetSiteURL();
70 if (!site_url.SchemeIs(kExtensionScheme) &&
71 !site_url.SchemeIs(content::kGuestScheme))
74 return site_url.host();
77 std::string GetExtensionIDFromFrame(
78 content::RenderFrameHost* render_frame_host) {
79 // This works for both apps and extensions because the site has been
80 // normalized to the extension URL for apps.
81 if (!render_frame_host->GetSiteInstance())
84 return render_frame_host->GetSiteInstance()->GetSiteURL().host();
87 bool IsFrameInExtensionHost(ExtensionHost* extension_host,
88 content::RenderFrameHost* render_frame_host) {
89 return WebContents::FromRenderFrameHost(render_frame_host) ==
90 extension_host->host_contents();
93 void OnRenderViewHostUnregistered(BrowserContext* context,
94 RenderViewHost* render_view_host) {
95 content::NotificationService::current()->Notify(
96 chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED,
97 content::Source<BrowserContext>(context),
98 content::Details<RenderViewHost>(render_view_host));
101 // Incognito profiles use this process manager. It is mostly a shim that decides
102 // whether to fall back on the original profile's ProcessManager based
103 // on whether a given extension uses "split" or "spanning" incognito behavior.
104 class IncognitoProcessManager : public ProcessManager {
106 IncognitoProcessManager(BrowserContext* incognito_context,
107 BrowserContext* original_context,
108 ProcessManager* original_manager);
109 virtual ~IncognitoProcessManager() {}
110 virtual bool CreateBackgroundHost(const Extension* extension,
111 const GURL& url) OVERRIDE;
112 virtual SiteInstance* GetSiteInstanceForURL(const GURL& url) OVERRIDE;
115 ProcessManager* original_manager_;
117 DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager);
120 static void CreateBackgroundHostForExtensionLoad(
121 ProcessManager* manager, const Extension* extension) {
122 DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
123 if (BackgroundInfo::HasPersistentBackgroundPage(extension))
124 manager->CreateBackgroundHost(extension,
125 BackgroundInfo::GetBackgroundURL(extension));
130 class RenderViewHostDestructionObserver
131 : public content::WebContentsObserver,
132 public content::WebContentsUserData<RenderViewHostDestructionObserver> {
134 virtual ~RenderViewHostDestructionObserver() {}
137 explicit RenderViewHostDestructionObserver(WebContents* web_contents)
138 : WebContentsObserver(web_contents) {
139 BrowserContext* context = web_contents->GetBrowserContext();
140 process_manager_ = ExtensionSystem::Get(context)->process_manager();
143 friend class content::WebContentsUserData<RenderViewHostDestructionObserver>;
145 // content::WebContentsObserver overrides.
146 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
147 process_manager_->UnregisterRenderViewHost(render_view_host);
150 ProcessManager* process_manager_;
152 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver);
155 struct ProcessManager::BackgroundPageData {
156 // The count of things keeping the lazy background page alive.
157 int lazy_keepalive_count;
159 // Tracks if an impulse event has occured since the last polling check.
160 bool keepalive_impulse;
161 bool previous_keepalive_impulse;
163 // This is used with the ShouldSuspend message, to ensure that the extension
164 // remained idle between sending the message and receiving the ack.
165 int close_sequence_id;
167 // True if the page responded to the ShouldSuspend message and is currently
168 // dispatching the suspend event. During this time any events that arrive will
169 // cancel the suspend process and an onSuspendCanceled event will be
170 // dispatched to the page.
173 // Keeps track of when this page was last suspended. Used for perf metrics.
174 linked_ptr<base::ElapsedTimer> since_suspended;
177 : lazy_keepalive_count(0),
178 keepalive_impulse(false),
179 previous_keepalive_impulse(false),
180 close_sequence_id(0),
189 ProcessManager* ProcessManager::Create(BrowserContext* context) {
190 ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get();
191 if (client->IsGuestSession(context)) {
192 // In the guest session, there is a single off-the-record context. Unlike
193 // a regular incognito mode, background pages of extensions must be
194 // created regardless of whether extensions use "spanning" or "split"
195 // incognito behavior.
196 BrowserContext* original_context = client->GetOriginalContext(context);
197 return new ProcessManager(context, original_context);
200 if (context->IsOffTheRecord()) {
201 BrowserContext* original_context = client->GetOriginalContext(context);
202 ProcessManager* original_manager =
203 ExtensionSystem::Get(original_context)->process_manager();
204 return new IncognitoProcessManager(
205 context, original_context, original_manager);
208 return new ProcessManager(context, context);
212 ProcessManager* ProcessManager::CreateIncognitoForTesting(
213 BrowserContext* incognito_context,
214 BrowserContext* original_context,
215 ProcessManager* original_manager) {
216 DCHECK(incognito_context->IsOffTheRecord());
217 DCHECK(!original_context->IsOffTheRecord());
218 return new IncognitoProcessManager(
219 incognito_context, original_context, original_manager);
222 ProcessManager::ProcessManager(BrowserContext* context,
223 BrowserContext* original_context)
224 : site_instance_(SiteInstance::Create(context)),
225 startup_background_hosts_created_(false),
226 devtools_callback_(base::Bind(
227 &ProcessManager::OnDevToolsStateChanged,
228 base::Unretained(this))),
229 weak_ptr_factory_(this) {
230 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
231 content::Source<BrowserContext>(original_context));
232 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
233 content::Source<BrowserContext>(original_context));
234 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
235 content::Source<BrowserContext>(original_context));
236 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
237 content::Source<BrowserContext>(context));
238 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
239 content::Source<BrowserContext>(context));
240 registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
241 content::NotificationService::AllSources());
242 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
243 content::NotificationService::AllSources());
244 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
245 content::Source<BrowserContext>(original_context));
246 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
247 content::Source<BrowserContext>(context));
248 if (context->IsOffTheRecord()) {
249 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
250 content::Source<BrowserContext>(original_context));
253 // Note: event_page_idle_time_ must be sufficiently larger (e.g. 2x) than
254 // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
255 event_page_idle_time_ = base::TimeDelta::FromSeconds(10);
256 unsigned idle_time_msec = 0;
257 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
258 extensions::switches::kEventPageIdleTime), &idle_time_msec)) {
259 CHECK(idle_time_msec > 0); // OnKeepaliveImpulseCheck requires non zero.
260 event_page_idle_time_ = base::TimeDelta::FromMilliseconds(idle_time_msec);
262 event_page_suspending_time_ = base::TimeDelta::FromSeconds(5);
263 unsigned suspending_time_msec = 0;
264 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
265 extensions::switches::kEventPageSuspendingTime),
266 &suspending_time_msec)) {
267 event_page_suspending_time_ =
268 base::TimeDelta::FromMilliseconds(suspending_time_msec);
271 content::DevToolsManager::GetInstance()->AddAgentStateCallback(
274 OnKeepaliveImpulseCheck();
277 ProcessManager::~ProcessManager() {
278 CloseBackgroundHosts();
279 DCHECK(background_hosts_.empty());
280 content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
284 const ProcessManager::ViewSet ProcessManager::GetAllViews() const {
286 for (ExtensionRenderViews::const_iterator iter =
287 all_extension_views_.begin();
288 iter != all_extension_views_.end(); ++iter) {
289 result.insert(iter->first);
294 bool ProcessManager::CreateBackgroundHost(const Extension* extension,
296 // Hosted apps are taken care of from BackgroundContentsService. Ignore them
298 if (extension->is_hosted_app() ||
299 !ExtensionsBrowserClient::Get()->
300 IsBackgroundPageAllowed(GetBrowserContext())) {
304 // Don't create multiple background hosts for an extension.
305 if (GetBackgroundHostForExtension(extension->id()))
306 return true; // TODO(kalman): return false here? It might break things...
308 ExtensionHost* host =
309 new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
310 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
311 host->CreateRenderViewSoon();
312 OnBackgroundHostCreated(host);
316 ExtensionHost* ProcessManager::GetBackgroundHostForExtension(
317 const std::string& extension_id) {
318 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
319 iter != background_hosts_.end(); ++iter) {
320 ExtensionHost* host = *iter;
321 if (host->extension_id() == extension_id)
327 std::set<RenderViewHost*> ProcessManager::GetRenderViewHostsForExtension(
328 const std::string& extension_id) {
329 std::set<RenderViewHost*> result;
331 SiteInstance* site_instance = GetSiteInstanceForURL(
332 Extension::GetBaseURLFromExtensionId(extension_id));
336 // Gather up all the views for that site.
337 for (ExtensionRenderViews::iterator view = all_extension_views_.begin();
338 view != all_extension_views_.end(); ++view) {
339 if (view->first->GetSiteInstance() == site_instance)
340 result.insert(view->first);
346 const Extension* ProcessManager::GetExtensionForRenderViewHost(
347 RenderViewHost* render_view_host) {
348 if (!render_view_host->GetSiteInstance())
351 ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
355 return registry->enabled_extensions().GetByID(
356 GetExtensionID(render_view_host));
359 void ProcessManager::UnregisterRenderViewHost(
360 RenderViewHost* render_view_host) {
361 ExtensionRenderViews::iterator view =
362 all_extension_views_.find(render_view_host);
363 if (view == all_extension_views_.end())
366 OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host);
367 ViewType view_type = view->second;
368 all_extension_views_.erase(view);
370 // Keepalive count, balanced in RegisterRenderViewHost.
371 if (view_type != VIEW_TYPE_INVALID &&
372 view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
373 const Extension* extension = GetExtensionForRenderViewHost(
376 DecrementLazyKeepaliveCount(extension);
380 bool ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) {
381 const Extension* extension = GetExtensionForRenderViewHost(
386 WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
387 all_extension_views_[render_view_host] = GetViewType(web_contents);
389 // Keep the lazy background page alive as long as any non-background-page
390 // extension views are visible. Keepalive count balanced in
391 // UnregisterRenderViewHost.
392 IncrementLazyKeepaliveCountForView(render_view_host);
396 SiteInstance* ProcessManager::GetSiteInstanceForURL(const GURL& url) {
397 return site_instance_->GetRelatedSiteInstance(url);
400 bool ProcessManager::IsBackgroundHostClosing(const std::string& extension_id) {
401 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
402 return (host && background_page_data_[extension_id].is_closing);
405 int ProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
406 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
409 return background_page_data_[extension->id()].lazy_keepalive_count;
412 void ProcessManager::IncrementLazyKeepaliveCount(const Extension* extension) {
413 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
416 int& count = background_page_data_[extension->id()].lazy_keepalive_count;
418 OnLazyBackgroundPageActive(extension->id());
421 void ProcessManager::DecrementLazyKeepaliveCount(const Extension* extension) {
422 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
424 DecrementLazyKeepaliveCount(extension->id());
427 void ProcessManager::DecrementLazyKeepaliveCount(
428 const std::string& extension_id) {
429 int& count = background_page_data_[extension_id].lazy_keepalive_count;
432 // If we reach a zero keepalive count when the lazy background page is about
433 // to be closed, incrementing close_sequence_id will cancel the close
434 // sequence and cause the background page to linger. So check is_closing
435 // before initiating another close sequence.
436 if (--count == 0 && !background_page_data_[extension_id].is_closing) {
437 base::MessageLoop::current()->PostDelayedTask(
439 base::Bind(&ProcessManager::OnLazyBackgroundPageIdle,
440 weak_ptr_factory_.GetWeakPtr(), extension_id,
441 ++background_page_data_[extension_id].close_sequence_id),
442 event_page_idle_time_);
446 void ProcessManager::IncrementLazyKeepaliveCountForView(
447 RenderViewHost* render_view_host) {
448 WebContents* web_contents =
449 WebContents::FromRenderViewHost(render_view_host);
450 ViewType view_type = GetViewType(web_contents);
451 if (view_type != VIEW_TYPE_INVALID &&
452 view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
453 const Extension* extension = GetExtensionForRenderViewHost(
456 IncrementLazyKeepaliveCount(extension);
460 // This implementation layers on top of the keepalive count. An impulse sets
461 // a per extension flag. On a regular interval that flag is checked. Changes
462 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
463 void ProcessManager::KeepaliveImpulse(const Extension* extension) {
464 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
467 BackgroundPageData& bd = background_page_data_[extension->id()];
469 if (!bd.keepalive_impulse) {
470 bd.keepalive_impulse = true;
471 if (!bd.previous_keepalive_impulse) {
472 IncrementLazyKeepaliveCount(extension);
476 if (!keepalive_impulse_callback_for_testing_.is_null()) {
477 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
478 keepalive_impulse_callback_for_testing_;
479 callback_may_clear_callbacks_reentrantly.Run(extension->id());
483 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
484 // have been made for at least event_page_idle_time_. In the best case an
485 // impulse was made just before being cleared, and the decrement will occur
486 // event_page_idle_time_ later, causing a 2 * event_page_idle_time_ total time
487 // for extension to be shut down based on impulses. Worst case is an impulse
488 // just after a clear, adding one check cycle and resulting in 3x total time.
489 void ProcessManager::OnKeepaliveImpulseCheck() {
490 for (BackgroundPageDataMap::iterator i = background_page_data_.begin();
491 i != background_page_data_.end();
493 if (i->second.previous_keepalive_impulse && !i->second.keepalive_impulse) {
494 DecrementLazyKeepaliveCount(i->first);
495 if (!keepalive_impulse_decrement_callback_for_testing_.is_null()) {
496 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
497 keepalive_impulse_decrement_callback_for_testing_;
498 callback_may_clear_callbacks_reentrantly.Run(i->first);
502 i->second.previous_keepalive_impulse = i->second.keepalive_impulse;
503 i->second.keepalive_impulse = false;
506 // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
507 // tests there will be no message loop. In that event don't schedule tasks.
508 if (base::MessageLoop::current()) {
509 base::MessageLoop::current()->PostDelayedTask(
511 base::Bind(&ProcessManager::OnKeepaliveImpulseCheck,
512 weak_ptr_factory_.GetWeakPtr()),
513 event_page_idle_time_);
517 void ProcessManager::OnLazyBackgroundPageIdle(const std::string& extension_id,
519 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
520 if (host && !background_page_data_[extension_id].is_closing &&
521 sequence_id == background_page_data_[extension_id].close_sequence_id) {
522 // Tell the renderer we are about to close. This is a simple ping that the
523 // renderer will respond to. The purpose is to control sequencing: if the
524 // extension remains idle until the renderer responds with an ACK, then we
525 // know that the extension process is ready to shut down. If our
526 // close_sequence_id has already changed, then we would ignore the
527 // ShouldSuspendAck, so we don't send the ping.
528 host->render_view_host()->Send(new ExtensionMsg_ShouldSuspend(
529 extension_id, sequence_id));
533 void ProcessManager::OnLazyBackgroundPageActive(
534 const std::string& extension_id) {
535 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
536 if (host && !background_page_data_[extension_id].is_closing) {
537 // Cancel the current close sequence by changing the close_sequence_id,
538 // which causes us to ignore the next ShouldSuspendAck.
539 ++background_page_data_[extension_id].close_sequence_id;
543 void ProcessManager::OnShouldSuspendAck(const std::string& extension_id,
545 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
547 sequence_id == background_page_data_[extension_id].close_sequence_id) {
548 host->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id));
552 void ProcessManager::OnSuspendAck(const std::string& extension_id) {
553 background_page_data_[extension_id].is_closing = true;
554 int sequence_id = background_page_data_[extension_id].close_sequence_id;
555 base::MessageLoop::current()->PostDelayedTask(
557 base::Bind(&ProcessManager::CloseLazyBackgroundPageNow,
558 weak_ptr_factory_.GetWeakPtr(), extension_id, sequence_id),
559 event_page_suspending_time_);
562 void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id,
564 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
566 sequence_id == background_page_data_[extension_id].close_sequence_id) {
567 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
569 CloseBackgroundHost(host);
573 void ProcessManager::OnNetworkRequestStarted(
574 content::RenderFrameHost* render_frame_host) {
575 ExtensionHost* host = GetBackgroundHostForExtension(
576 GetExtensionIDFromFrame(render_frame_host));
577 if (host && IsFrameInExtensionHost(host, render_frame_host))
578 IncrementLazyKeepaliveCount(host->extension());
581 void ProcessManager::OnNetworkRequestDone(
582 content::RenderFrameHost* render_frame_host) {
583 ExtensionHost* host = GetBackgroundHostForExtension(
584 GetExtensionIDFromFrame(render_frame_host));
585 if (host && IsFrameInExtensionHost(host, render_frame_host))
586 DecrementLazyKeepaliveCount(host->extension());
589 void ProcessManager::CancelSuspend(const Extension* extension) {
590 bool& is_closing = background_page_data_[extension->id()].is_closing;
591 ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
592 if (host && is_closing) {
594 host->render_view_host()->Send(
595 new ExtensionMsg_CancelSuspend(extension->id()));
596 // This increment / decrement is to simulate an instantaneous event. This
597 // has the effect of invalidating close_sequence_id, preventing any in
598 // progress closes from completing and starting a new close process if
600 IncrementLazyKeepaliveCount(extension);
601 DecrementLazyKeepaliveCount(extension);
605 void ProcessManager::OnBrowserWindowReady() {
606 // If the extension system isn't ready yet the background hosts will be
607 // created via NOTIFICATION_EXTENSIONS_READY below.
608 ExtensionSystem* system = ExtensionSystem::Get(GetBrowserContext());
609 if (!system->ready().is_signaled())
612 CreateBackgroundHostsForProfileStartup();
615 content::BrowserContext* ProcessManager::GetBrowserContext() const {
616 return site_instance_->GetBrowserContext();
619 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
620 const ImpulseCallbackForTesting& callback) {
621 keepalive_impulse_callback_for_testing_ = callback;
624 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
625 const ImpulseCallbackForTesting& callback) {
626 keepalive_impulse_decrement_callback_for_testing_ = callback;
629 void ProcessManager::Observe(int type,
630 const content::NotificationSource& source,
631 const content::NotificationDetails& details) {
633 case chrome::NOTIFICATION_EXTENSIONS_READY:
634 case chrome::NOTIFICATION_PROFILE_CREATED: {
635 // Don't load background hosts now if the loading should be deferred.
636 // Instead they will be loaded when a browser window for this profile
637 // (or an incognito profile from this profile) is ready.
638 if (DeferLoadingBackgroundHosts())
641 CreateBackgroundHostsForProfileStartup();
645 case chrome::NOTIFICATION_EXTENSION_LOADED: {
646 BrowserContext* context = content::Source<BrowserContext>(source).ptr();
647 ExtensionSystem* system = ExtensionSystem::Get(context);
648 if (system->ready().is_signaled()) {
649 // The extension system is ready, so create the background host.
650 const Extension* extension =
651 content::Details<const Extension>(details).ptr();
652 CreateBackgroundHostForExtensionLoad(this, extension);
657 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
658 const Extension* extension =
659 content::Details<UnloadedExtensionInfo>(details)->extension;
660 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
661 iter != background_hosts_.end(); ++iter) {
662 ExtensionHost* host = *iter;
663 if (host->extension_id() == extension->id()) {
664 CloseBackgroundHost(host);
668 UnregisterExtension(extension->id());
672 case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
673 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
674 if (background_hosts_.erase(host)) {
675 ClearBackgroundPageData(host->extension()->id());
676 background_page_data_[host->extension()->id()].since_suspended.reset(
677 new base::ElapsedTimer());
682 case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
683 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
684 if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
685 CloseBackgroundHost(host);
690 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
691 // We get this notification both for new WebContents and when one
692 // has its RenderViewHost replaced (e.g. when a user does a cross-site
693 // navigation away from an extension URL). For the replaced case, we must
694 // unregister the old RVH so it doesn't count as an active view that would
695 // keep the event page alive.
696 WebContents* contents = content::Source<WebContents>(source).ptr();
697 if (contents->GetBrowserContext() != GetBrowserContext())
700 typedef std::pair<RenderViewHost*, RenderViewHost*> RVHPair;
701 RVHPair* switched_details = content::Details<RVHPair>(details).ptr();
702 if (switched_details->first)
703 UnregisterRenderViewHost(switched_details->first);
705 // The above will unregister a RVH when it gets swapped out with a new
706 // one. However we need to watch the WebContents to know when a RVH is
707 // deleted because the WebContents has gone away.
708 if (RegisterRenderViewHost(switched_details->second)) {
709 RenderViewHostDestructionObserver::CreateForWebContents(contents);
714 case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: {
715 WebContents* contents = content::Source<WebContents>(source).ptr();
716 if (contents->GetBrowserContext() != GetBrowserContext())
718 const Extension* extension = GetExtensionForRenderViewHost(
719 contents->GetRenderViewHost());
723 // RegisterRenderViewHost is called too early (before the process is
724 // available), so we need to wait until now to notify.
725 content::NotificationService::current()->Notify(
726 chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED,
727 content::Source<BrowserContext>(GetBrowserContext()),
728 content::Details<RenderViewHost>(contents->GetRenderViewHost()));
732 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
733 // Close background hosts when the last browser is closed so that they
734 // have time to shutdown various objects on different threads. Our
735 // destructor is called too late in the shutdown sequence.
736 CloseBackgroundHosts();
745 void ProcessManager::OnDevToolsStateChanged(
746 content::DevToolsAgentHost* agent_host,
748 RenderViewHost* rvh = agent_host->GetRenderViewHost();
749 // Ignore unrelated notifications.
751 rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() !=
754 if (GetViewType(WebContents::FromRenderViewHost(rvh)) !=
755 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
757 const Extension* extension = GetExtensionForRenderViewHost(rvh);
761 // Keep the lazy background page alive while it's being inspected.
762 CancelSuspend(extension);
763 IncrementLazyKeepaliveCount(extension);
765 DecrementLazyKeepaliveCount(extension);
769 void ProcessManager::CreateBackgroundHostsForProfileStartup() {
770 if (startup_background_hosts_created_ ||
771 !ExtensionsBrowserClient::Get()->
772 IsBackgroundPageAllowed(GetBrowserContext())) {
776 const ExtensionSet& enabled_extensions =
777 ExtensionRegistry::Get(GetBrowserContext())->enabled_extensions();
778 for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
779 extension != enabled_extensions.end();
781 CreateBackgroundHostForExtensionLoad(this, extension->get());
783 RuntimeEventRouter::DispatchOnStartupEvent(GetBrowserContext(),
786 startup_background_hosts_created_ = true;
788 // Background pages should only be loaded once. To prevent any further loads
789 // occurring, we remove the notification listeners.
790 BrowserContext* original_context =
791 ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
792 if (registrar_.IsRegistered(
794 chrome::NOTIFICATION_PROFILE_CREATED,
795 content::Source<BrowserContext>(original_context))) {
796 registrar_.Remove(this,
797 chrome::NOTIFICATION_PROFILE_CREATED,
798 content::Source<BrowserContext>(original_context));
800 if (registrar_.IsRegistered(
802 chrome::NOTIFICATION_EXTENSIONS_READY,
803 content::Source<BrowserContext>(original_context))) {
804 registrar_.Remove(this,
805 chrome::NOTIFICATION_EXTENSIONS_READY,
806 content::Source<BrowserContext>(original_context));
810 void ProcessManager::OnBackgroundHostCreated(ExtensionHost* host) {
811 DCHECK_EQ(GetBrowserContext(), host->browser_context());
812 background_hosts_.insert(host);
814 if (BackgroundInfo::HasLazyBackgroundPage(host->extension())) {
815 linked_ptr<base::ElapsedTimer> since_suspended(
816 background_page_data_[host->extension()->id()].
817 since_suspended.release());
818 if (since_suspended.get()) {
819 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
820 since_suspended->Elapsed());
825 void ProcessManager::CloseBackgroundHost(ExtensionHost* host) {
826 CHECK(host->extension_host_type() ==
827 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
829 // |host| should deregister itself from our structures.
830 CHECK(background_hosts_.find(host) == background_hosts_.end());
833 void ProcessManager::CloseBackgroundHosts() {
834 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
835 iter != background_hosts_.end(); ) {
836 ExtensionHostSet::iterator current = iter++;
841 void ProcessManager::UnregisterExtension(const std::string& extension_id) {
842 // The lazy_keepalive_count may be greater than zero at this point because
843 // RenderViewHosts are still alive. During extension reloading, they will
844 // decrement the lazy_keepalive_count to negative for the new extension
845 // instance when they are destroyed. Since we are erasing the background page
846 // data for the unloaded extension, unregister the RenderViewHosts too.
847 BrowserContext* context = GetBrowserContext();
848 for (ExtensionRenderViews::iterator it = all_extension_views_.begin();
849 it != all_extension_views_.end(); ) {
850 if (GetExtensionID(it->first) == extension_id) {
851 OnRenderViewHostUnregistered(context, it->first);
852 all_extension_views_.erase(it++);
858 background_page_data_.erase(extension_id);
861 void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) {
862 background_page_data_.erase(extension_id);
864 // Re-register all RenderViews for this extension. We do this to restore
865 // the lazy_keepalive_count (if any) to properly reflect the number of open
867 for (ExtensionRenderViews::const_iterator it = all_extension_views_.begin();
868 it != all_extension_views_.end(); ++it) {
869 if (GetExtensionID(it->first) == extension_id)
870 IncrementLazyKeepaliveCountForView(it->first);
874 bool ProcessManager::DeferLoadingBackgroundHosts() const {
875 // The extensions embedder may have special rules about background hosts.
876 return ExtensionsBrowserClient::Get()->DeferLoadingBackgroundHosts(
877 GetBrowserContext());
881 // IncognitoProcessManager
884 IncognitoProcessManager::IncognitoProcessManager(
885 BrowserContext* incognito_context,
886 BrowserContext* original_context,
887 ProcessManager* original_manager)
888 : ProcessManager(incognito_context, original_context),
889 original_manager_(original_manager) {
890 DCHECK(incognito_context->IsOffTheRecord());
892 // The original profile will have its own ProcessManager to
893 // load the background pages of the spanning extensions. This process
894 // manager need only worry about the split mode extensions, which is handled
895 // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler.
896 registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY,
897 content::Source<BrowserContext>(original_context));
898 registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_CREATED,
899 content::Source<BrowserContext>(original_context));
902 bool IncognitoProcessManager::CreateBackgroundHost(const Extension* extension,
904 if (IncognitoInfo::IsSplitMode(extension)) {
905 if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
906 extension->id(), GetBrowserContext()))
907 return ProcessManager::CreateBackgroundHost(extension, url);
909 // Do nothing. If an extension is spanning, then its original-profile
910 // background page is shared with incognito, so we don't create another.
915 SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) {
916 ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
918 const Extension* extension =
919 registry->enabled_extensions().GetExtensionOrAppByURL(url);
920 if (extension && !IncognitoInfo::IsSplitMode(extension)) {
921 return original_manager_->GetSiteInstanceForURL(url);
924 return ProcessManager::GetSiteInstanceForURL(url);
927 } // namespace extensions