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 "extensions/browser/extension_host.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/extension_system.h"
35 #include "extensions/browser/extensions_browser_client.h"
36 #include "extensions/browser/view_type_utils.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/extension_messages.h"
39 #include "extensions/common/manifest_handlers/background_info.h"
40 #include "extensions/common/manifest_handlers/incognito_info.h"
41 #include "extensions/common/one_shot_event.h"
42 #include "extensions/common/switches.h"
44 using content::BrowserContext;
45 using content::RenderViewHost;
46 using content::SiteInstance;
47 using content::WebContents;
49 namespace extensions {
50 class RenderViewHostDestructionObserver;
52 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
53 extensions::RenderViewHostDestructionObserver);
55 namespace extensions {
59 std::string GetExtensionID(RenderViewHost* render_view_host) {
60 // This works for both apps and extensions because the site has been
61 // normalized to the extension URL for apps.
62 if (!render_view_host->GetSiteInstance())
65 return render_view_host->GetSiteInstance()->GetSiteURL().host();
68 std::string GetExtensionIDFromFrame(
69 content::RenderFrameHost* render_frame_host) {
70 // This works for both apps and extensions because the site has been
71 // normalized to the extension URL for apps.
72 if (!render_frame_host->GetSiteInstance())
75 return render_frame_host->GetSiteInstance()->GetSiteURL().host();
78 bool IsFrameInExtensionHost(ExtensionHost* extension_host,
79 content::RenderFrameHost* render_frame_host) {
80 return WebContents::FromRenderFrameHost(render_frame_host) ==
81 extension_host->host_contents();
84 void OnRenderViewHostUnregistered(BrowserContext* context,
85 RenderViewHost* render_view_host) {
86 content::NotificationService::current()->Notify(
87 chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED,
88 content::Source<BrowserContext>(context),
89 content::Details<RenderViewHost>(render_view_host));
92 // Incognito profiles use this process manager. It is mostly a shim that decides
93 // whether to fall back on the original profile's ProcessManager based
94 // on whether a given extension uses "split" or "spanning" incognito behavior.
95 class IncognitoProcessManager : public ProcessManager {
97 IncognitoProcessManager(BrowserContext* incognito_context,
98 BrowserContext* original_context,
99 ProcessManager* original_manager);
100 virtual ~IncognitoProcessManager() {}
101 virtual bool CreateBackgroundHost(const Extension* extension,
102 const GURL& url) OVERRIDE;
103 virtual SiteInstance* GetSiteInstanceForURL(const GURL& url) OVERRIDE;
106 ProcessManager* original_manager_;
108 DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager);
111 static void CreateBackgroundHostForExtensionLoad(
112 ProcessManager* manager, const Extension* extension) {
113 DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
114 if (BackgroundInfo::HasPersistentBackgroundPage(extension))
115 manager->CreateBackgroundHost(extension,
116 BackgroundInfo::GetBackgroundURL(extension));
121 class RenderViewHostDestructionObserver
122 : public content::WebContentsObserver,
123 public content::WebContentsUserData<RenderViewHostDestructionObserver> {
125 virtual ~RenderViewHostDestructionObserver() {}
128 explicit RenderViewHostDestructionObserver(WebContents* web_contents)
129 : WebContentsObserver(web_contents) {
130 BrowserContext* context = web_contents->GetBrowserContext();
131 process_manager_ = ExtensionSystem::Get(context)->process_manager();
134 friend class content::WebContentsUserData<RenderViewHostDestructionObserver>;
136 // content::WebContentsObserver overrides.
137 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
138 process_manager_->UnregisterRenderViewHost(render_view_host);
141 ProcessManager* process_manager_;
143 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver);
146 struct ProcessManager::BackgroundPageData {
147 // The count of things keeping the lazy background page alive.
148 int lazy_keepalive_count;
150 // Tracks if an impulse event has occured since the last polling check.
151 bool keepalive_impulse;
152 bool previous_keepalive_impulse;
154 // This is used with the ShouldSuspend message, to ensure that the extension
155 // remained idle between sending the message and receiving the ack.
156 int close_sequence_id;
158 // True if the page responded to the ShouldSuspend message and is currently
159 // dispatching the suspend event. During this time any events that arrive will
160 // cancel the suspend process and an onSuspendCanceled event will be
161 // dispatched to the page.
164 // Keeps track of when this page was last suspended. Used for perf metrics.
165 linked_ptr<base::ElapsedTimer> since_suspended;
168 : lazy_keepalive_count(0),
169 keepalive_impulse(false),
170 previous_keepalive_impulse(false),
171 close_sequence_id(0),
180 ProcessManager* ProcessManager::Create(BrowserContext* context) {
181 ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get();
182 if (client->IsGuestSession(context)) {
183 // In the guest session, there is a single off-the-record context. Unlike
184 // a regular incognito mode, background pages of extensions must be
185 // created regardless of whether extensions use "spanning" or "split"
186 // incognito behavior.
187 BrowserContext* original_context = client->GetOriginalContext(context);
188 return new ProcessManager(context, original_context);
191 if (context->IsOffTheRecord()) {
192 BrowserContext* original_context = client->GetOriginalContext(context);
193 ProcessManager* original_manager =
194 ExtensionSystem::Get(original_context)->process_manager();
195 return new IncognitoProcessManager(
196 context, original_context, original_manager);
199 return new ProcessManager(context, context);
203 ProcessManager* ProcessManager::CreateIncognitoForTesting(
204 BrowserContext* incognito_context,
205 BrowserContext* original_context,
206 ProcessManager* original_manager) {
207 DCHECK(incognito_context->IsOffTheRecord());
208 DCHECK(!original_context->IsOffTheRecord());
209 return new IncognitoProcessManager(
210 incognito_context, original_context, original_manager);
213 ProcessManager::ProcessManager(BrowserContext* context,
214 BrowserContext* original_context)
215 : site_instance_(SiteInstance::Create(context)),
216 startup_background_hosts_created_(false),
217 devtools_callback_(base::Bind(
218 &ProcessManager::OnDevToolsStateChanged,
219 base::Unretained(this))),
220 weak_ptr_factory_(this) {
221 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
222 content::Source<BrowserContext>(original_context));
223 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
224 content::Source<BrowserContext>(original_context));
225 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
226 content::Source<BrowserContext>(original_context));
227 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
228 content::Source<BrowserContext>(context));
229 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
230 content::Source<BrowserContext>(context));
231 registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
232 content::NotificationService::AllSources());
233 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
234 content::NotificationService::AllSources());
235 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
236 content::Source<BrowserContext>(original_context));
237 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
238 content::Source<BrowserContext>(context));
239 if (context->IsOffTheRecord()) {
240 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
241 content::Source<BrowserContext>(original_context));
244 // Note: event_page_idle_time_ must be sufficiently larger (e.g. 2x) than
245 // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
246 event_page_idle_time_ = base::TimeDelta::FromSeconds(10);
247 unsigned idle_time_msec = 0;
248 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
249 extensions::switches::kEventPageIdleTime), &idle_time_msec)) {
250 CHECK(idle_time_msec > 0); // OnKeepaliveImpulseCheck requires non zero.
251 event_page_idle_time_ = base::TimeDelta::FromMilliseconds(idle_time_msec);
253 event_page_suspending_time_ = base::TimeDelta::FromSeconds(5);
254 unsigned suspending_time_msec = 0;
255 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
256 extensions::switches::kEventPageSuspendingTime),
257 &suspending_time_msec)) {
258 event_page_suspending_time_ =
259 base::TimeDelta::FromMilliseconds(suspending_time_msec);
262 content::DevToolsManager::GetInstance()->AddAgentStateCallback(
265 OnKeepaliveImpulseCheck();
268 ProcessManager::~ProcessManager() {
269 CloseBackgroundHosts();
270 DCHECK(background_hosts_.empty());
271 content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
275 const ProcessManager::ViewSet ProcessManager::GetAllViews() const {
277 for (ExtensionRenderViews::const_iterator iter =
278 all_extension_views_.begin();
279 iter != all_extension_views_.end(); ++iter) {
280 result.insert(iter->first);
285 bool ProcessManager::CreateBackgroundHost(const Extension* extension,
287 // Hosted apps are taken care of from BackgroundContentsService. Ignore them
289 if (extension->is_hosted_app() ||
290 !ExtensionsBrowserClient::Get()->
291 IsBackgroundPageAllowed(GetBrowserContext())) {
295 // Don't create multiple background hosts for an extension.
296 if (GetBackgroundHostForExtension(extension->id()))
297 return true; // TODO(kalman): return false here? It might break things...
299 ExtensionHost* host =
300 new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
301 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
302 host->CreateRenderViewSoon();
303 OnBackgroundHostCreated(host);
307 ExtensionHost* ProcessManager::GetBackgroundHostForExtension(
308 const std::string& extension_id) {
309 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
310 iter != background_hosts_.end(); ++iter) {
311 ExtensionHost* host = *iter;
312 if (host->extension_id() == extension_id)
318 std::set<RenderViewHost*> ProcessManager::GetRenderViewHostsForExtension(
319 const std::string& extension_id) {
320 std::set<RenderViewHost*> result;
322 SiteInstance* site_instance = GetSiteInstanceForURL(
323 Extension::GetBaseURLFromExtensionId(extension_id));
327 // Gather up all the views for that site.
328 for (ExtensionRenderViews::iterator view = all_extension_views_.begin();
329 view != all_extension_views_.end(); ++view) {
330 if (view->first->GetSiteInstance() == site_instance)
331 result.insert(view->first);
337 const Extension* ProcessManager::GetExtensionForRenderViewHost(
338 RenderViewHost* render_view_host) {
339 if (!render_view_host->GetSiteInstance())
342 ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
346 return registry->enabled_extensions().GetByID(
347 GetExtensionID(render_view_host));
350 void ProcessManager::UnregisterRenderViewHost(
351 RenderViewHost* render_view_host) {
352 ExtensionRenderViews::iterator view =
353 all_extension_views_.find(render_view_host);
354 if (view == all_extension_views_.end())
357 OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host);
358 ViewType view_type = view->second;
359 all_extension_views_.erase(view);
361 // Keepalive count, balanced in RegisterRenderViewHost.
362 if (view_type != VIEW_TYPE_INVALID &&
363 view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
364 const Extension* extension = GetExtensionForRenderViewHost(
367 DecrementLazyKeepaliveCount(extension);
371 bool ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) {
372 const Extension* extension = GetExtensionForRenderViewHost(
377 WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
378 all_extension_views_[render_view_host] = GetViewType(web_contents);
380 // Keep the lazy background page alive as long as any non-background-page
381 // extension views are visible. Keepalive count balanced in
382 // UnregisterRenderViewHost.
383 IncrementLazyKeepaliveCountForView(render_view_host);
387 SiteInstance* ProcessManager::GetSiteInstanceForURL(const GURL& url) {
388 return site_instance_->GetRelatedSiteInstance(url);
391 bool ProcessManager::IsBackgroundHostClosing(const std::string& extension_id) {
392 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
393 return (host && background_page_data_[extension_id].is_closing);
396 int ProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
397 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
400 return background_page_data_[extension->id()].lazy_keepalive_count;
403 void ProcessManager::IncrementLazyKeepaliveCount(const Extension* extension) {
404 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
407 int& count = background_page_data_[extension->id()].lazy_keepalive_count;
409 OnLazyBackgroundPageActive(extension->id());
412 void ProcessManager::DecrementLazyKeepaliveCount(const Extension* extension) {
413 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
415 DecrementLazyKeepaliveCount(extension->id());
418 void ProcessManager::DecrementLazyKeepaliveCount(
419 const std::string& extension_id) {
420 int& count = background_page_data_[extension_id].lazy_keepalive_count;
423 // If we reach a zero keepalive count when the lazy background page is about
424 // to be closed, incrementing close_sequence_id will cancel the close
425 // sequence and cause the background page to linger. So check is_closing
426 // before initiating another close sequence.
427 if (--count == 0 && !background_page_data_[extension_id].is_closing) {
428 base::MessageLoop::current()->PostDelayedTask(
430 base::Bind(&ProcessManager::OnLazyBackgroundPageIdle,
431 weak_ptr_factory_.GetWeakPtr(), extension_id,
432 ++background_page_data_[extension_id].close_sequence_id),
433 event_page_idle_time_);
437 void ProcessManager::IncrementLazyKeepaliveCountForView(
438 RenderViewHost* render_view_host) {
439 WebContents* web_contents =
440 WebContents::FromRenderViewHost(render_view_host);
441 ViewType view_type = GetViewType(web_contents);
442 if (view_type != VIEW_TYPE_INVALID &&
443 view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
444 const Extension* extension = GetExtensionForRenderViewHost(
447 IncrementLazyKeepaliveCount(extension);
451 // This implementation layers on top of the keepalive count. An impulse sets
452 // a per extension flag. On a regular interval that flag is checked. Changes
453 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
454 void ProcessManager::KeepaliveImpulse(const Extension* extension) {
455 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
458 BackgroundPageData& bd = background_page_data_[extension->id()];
460 if (!bd.keepalive_impulse) {
461 bd.keepalive_impulse = true;
462 if (!bd.previous_keepalive_impulse) {
463 IncrementLazyKeepaliveCount(extension);
467 if (!keepalive_impulse_callback_for_testing_.is_null()) {
468 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
469 keepalive_impulse_callback_for_testing_;
470 callback_may_clear_callbacks_reentrantly.Run(extension->id());
474 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
475 // have been made for at least event_page_idle_time_. In the best case an
476 // impulse was made just before being cleared, and the decrement will occur
477 // event_page_idle_time_ later, causing a 2 * event_page_idle_time_ total time
478 // for extension to be shut down based on impulses. Worst case is an impulse
479 // just after a clear, adding one check cycle and resulting in 3x total time.
480 void ProcessManager::OnKeepaliveImpulseCheck() {
481 for (BackgroundPageDataMap::iterator i = background_page_data_.begin();
482 i != background_page_data_.end();
484 if (i->second.previous_keepalive_impulse && !i->second.keepalive_impulse) {
485 DecrementLazyKeepaliveCount(i->first);
486 if (!keepalive_impulse_decrement_callback_for_testing_.is_null()) {
487 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
488 keepalive_impulse_decrement_callback_for_testing_;
489 callback_may_clear_callbacks_reentrantly.Run(i->first);
493 i->second.previous_keepalive_impulse = i->second.keepalive_impulse;
494 i->second.keepalive_impulse = false;
497 // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
498 // tests there will be no message loop. In that event don't schedule tasks.
499 if (base::MessageLoop::current()) {
500 base::MessageLoop::current()->PostDelayedTask(
502 base::Bind(&ProcessManager::OnKeepaliveImpulseCheck,
503 weak_ptr_factory_.GetWeakPtr()),
504 event_page_idle_time_);
508 void ProcessManager::OnLazyBackgroundPageIdle(const std::string& extension_id,
510 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
511 if (host && !background_page_data_[extension_id].is_closing &&
512 sequence_id == background_page_data_[extension_id].close_sequence_id) {
513 // Tell the renderer we are about to close. This is a simple ping that the
514 // renderer will respond to. The purpose is to control sequencing: if the
515 // extension remains idle until the renderer responds with an ACK, then we
516 // know that the extension process is ready to shut down. If our
517 // close_sequence_id has already changed, then we would ignore the
518 // ShouldSuspendAck, so we don't send the ping.
519 host->render_view_host()->Send(new ExtensionMsg_ShouldSuspend(
520 extension_id, sequence_id));
524 void ProcessManager::OnLazyBackgroundPageActive(
525 const std::string& extension_id) {
526 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
527 if (host && !background_page_data_[extension_id].is_closing) {
528 // Cancel the current close sequence by changing the close_sequence_id,
529 // which causes us to ignore the next ShouldSuspendAck.
530 ++background_page_data_[extension_id].close_sequence_id;
534 void ProcessManager::OnShouldSuspendAck(const std::string& extension_id,
536 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
538 sequence_id == background_page_data_[extension_id].close_sequence_id) {
539 host->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id));
543 void ProcessManager::OnSuspendAck(const std::string& extension_id) {
544 background_page_data_[extension_id].is_closing = true;
545 int sequence_id = background_page_data_[extension_id].close_sequence_id;
546 base::MessageLoop::current()->PostDelayedTask(
548 base::Bind(&ProcessManager::CloseLazyBackgroundPageNow,
549 weak_ptr_factory_.GetWeakPtr(), extension_id, sequence_id),
550 event_page_suspending_time_);
553 void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id,
555 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
557 sequence_id == background_page_data_[extension_id].close_sequence_id) {
558 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
560 CloseBackgroundHost(host);
564 void ProcessManager::OnNetworkRequestStarted(
565 content::RenderFrameHost* render_frame_host) {
566 ExtensionHost* host = GetBackgroundHostForExtension(
567 GetExtensionIDFromFrame(render_frame_host));
568 if (host && IsFrameInExtensionHost(host, render_frame_host))
569 IncrementLazyKeepaliveCount(host->extension());
572 void ProcessManager::OnNetworkRequestDone(
573 content::RenderFrameHost* render_frame_host) {
574 ExtensionHost* host = GetBackgroundHostForExtension(
575 GetExtensionIDFromFrame(render_frame_host));
576 if (host && IsFrameInExtensionHost(host, render_frame_host))
577 DecrementLazyKeepaliveCount(host->extension());
580 void ProcessManager::CancelSuspend(const Extension* extension) {
581 bool& is_closing = background_page_data_[extension->id()].is_closing;
582 ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
583 if (host && is_closing) {
585 host->render_view_host()->Send(
586 new ExtensionMsg_CancelSuspend(extension->id()));
587 // This increment / decrement is to simulate an instantaneous event. This
588 // has the effect of invalidating close_sequence_id, preventing any in
589 // progress closes from completing and starting a new close process if
591 IncrementLazyKeepaliveCount(extension);
592 DecrementLazyKeepaliveCount(extension);
596 void ProcessManager::OnBrowserWindowReady() {
597 // If the extension system isn't ready yet the background hosts will be
598 // created via NOTIFICATION_EXTENSIONS_READY below.
599 ExtensionSystem* system = ExtensionSystem::Get(GetBrowserContext());
600 if (!system->ready().is_signaled())
603 CreateBackgroundHostsForProfileStartup();
606 content::BrowserContext* ProcessManager::GetBrowserContext() const {
607 return site_instance_->GetBrowserContext();
610 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
611 const ImpulseCallbackForTesting& callback) {
612 keepalive_impulse_callback_for_testing_ = callback;
615 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
616 const ImpulseCallbackForTesting& callback) {
617 keepalive_impulse_decrement_callback_for_testing_ = callback;
620 void ProcessManager::Observe(int type,
621 const content::NotificationSource& source,
622 const content::NotificationDetails& details) {
624 case chrome::NOTIFICATION_EXTENSIONS_READY:
625 case chrome::NOTIFICATION_PROFILE_CREATED: {
626 // Don't load background hosts now if the loading should be deferred.
627 // Instead they will be loaded when a browser window for this profile
628 // (or an incognito profile from this profile) is ready.
629 if (DeferLoadingBackgroundHosts())
632 CreateBackgroundHostsForProfileStartup();
636 case chrome::NOTIFICATION_EXTENSION_LOADED: {
637 BrowserContext* context = content::Source<BrowserContext>(source).ptr();
638 ExtensionSystem* system = ExtensionSystem::Get(context);
639 if (system->ready().is_signaled()) {
640 // The extension system is ready, so create the background host.
641 const Extension* extension =
642 content::Details<const Extension>(details).ptr();
643 CreateBackgroundHostForExtensionLoad(this, extension);
648 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
649 const Extension* extension =
650 content::Details<UnloadedExtensionInfo>(details)->extension;
651 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
652 iter != background_hosts_.end(); ++iter) {
653 ExtensionHost* host = *iter;
654 if (host->extension_id() == extension->id()) {
655 CloseBackgroundHost(host);
659 UnregisterExtension(extension->id());
663 case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
664 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
665 if (background_hosts_.erase(host)) {
666 ClearBackgroundPageData(host->extension()->id());
667 background_page_data_[host->extension()->id()].since_suspended.reset(
668 new base::ElapsedTimer());
673 case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
674 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
675 if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
676 CloseBackgroundHost(host);
681 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
682 // We get this notification both for new WebContents and when one
683 // has its RenderViewHost replaced (e.g. when a user does a cross-site
684 // navigation away from an extension URL). For the replaced case, we must
685 // unregister the old RVH so it doesn't count as an active view that would
686 // keep the event page alive.
687 WebContents* contents = content::Source<WebContents>(source).ptr();
688 if (contents->GetBrowserContext() != GetBrowserContext())
691 typedef std::pair<RenderViewHost*, RenderViewHost*> RVHPair;
692 RVHPair* switched_details = content::Details<RVHPair>(details).ptr();
693 if (switched_details->first)
694 UnregisterRenderViewHost(switched_details->first);
696 // The above will unregister a RVH when it gets swapped out with a new
697 // one. However we need to watch the WebContents to know when a RVH is
698 // deleted because the WebContents has gone away.
699 if (RegisterRenderViewHost(switched_details->second)) {
700 RenderViewHostDestructionObserver::CreateForWebContents(contents);
705 case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: {
706 WebContents* contents = content::Source<WebContents>(source).ptr();
707 if (contents->GetBrowserContext() != GetBrowserContext())
709 const Extension* extension = GetExtensionForRenderViewHost(
710 contents->GetRenderViewHost());
714 // RegisterRenderViewHost is called too early (before the process is
715 // available), so we need to wait until now to notify.
716 content::NotificationService::current()->Notify(
717 chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED,
718 content::Source<BrowserContext>(GetBrowserContext()),
719 content::Details<RenderViewHost>(contents->GetRenderViewHost()));
723 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
724 // Close background hosts when the last browser is closed so that they
725 // have time to shutdown various objects on different threads. Our
726 // destructor is called too late in the shutdown sequence.
727 CloseBackgroundHosts();
736 void ProcessManager::OnDevToolsStateChanged(
737 content::DevToolsAgentHost* agent_host,
739 RenderViewHost* rvh = agent_host->GetRenderViewHost();
740 // Ignore unrelated notifications.
742 rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() !=
745 if (GetViewType(WebContents::FromRenderViewHost(rvh)) !=
746 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
748 const Extension* extension = GetExtensionForRenderViewHost(rvh);
752 // Keep the lazy background page alive while it's being inspected.
753 CancelSuspend(extension);
754 IncrementLazyKeepaliveCount(extension);
756 DecrementLazyKeepaliveCount(extension);
760 void ProcessManager::CreateBackgroundHostsForProfileStartup() {
761 if (startup_background_hosts_created_ ||
762 !ExtensionsBrowserClient::Get()->
763 IsBackgroundPageAllowed(GetBrowserContext())) {
767 const ExtensionSet& enabled_extensions =
768 ExtensionRegistry::Get(GetBrowserContext())->enabled_extensions();
769 for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
770 extension != enabled_extensions.end();
772 CreateBackgroundHostForExtensionLoad(this, extension->get());
774 RuntimeEventRouter::DispatchOnStartupEvent(GetBrowserContext(),
777 startup_background_hosts_created_ = true;
779 // Background pages should only be loaded once. To prevent any further loads
780 // occurring, we remove the notification listeners.
781 BrowserContext* original_context =
782 ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
783 if (registrar_.IsRegistered(
785 chrome::NOTIFICATION_PROFILE_CREATED,
786 content::Source<BrowserContext>(original_context))) {
787 registrar_.Remove(this,
788 chrome::NOTIFICATION_PROFILE_CREATED,
789 content::Source<BrowserContext>(original_context));
791 if (registrar_.IsRegistered(
793 chrome::NOTIFICATION_EXTENSIONS_READY,
794 content::Source<BrowserContext>(original_context))) {
795 registrar_.Remove(this,
796 chrome::NOTIFICATION_EXTENSIONS_READY,
797 content::Source<BrowserContext>(original_context));
801 void ProcessManager::OnBackgroundHostCreated(ExtensionHost* host) {
802 DCHECK_EQ(GetBrowserContext(), host->browser_context());
803 background_hosts_.insert(host);
805 if (BackgroundInfo::HasLazyBackgroundPage(host->extension())) {
806 linked_ptr<base::ElapsedTimer> since_suspended(
807 background_page_data_[host->extension()->id()].
808 since_suspended.release());
809 if (since_suspended.get()) {
810 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
811 since_suspended->Elapsed());
816 void ProcessManager::CloseBackgroundHost(ExtensionHost* host) {
817 CHECK(host->extension_host_type() ==
818 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
820 // |host| should deregister itself from our structures.
821 CHECK(background_hosts_.find(host) == background_hosts_.end());
824 void ProcessManager::CloseBackgroundHosts() {
825 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
826 iter != background_hosts_.end(); ) {
827 ExtensionHostSet::iterator current = iter++;
832 void ProcessManager::UnregisterExtension(const std::string& extension_id) {
833 // The lazy_keepalive_count may be greater than zero at this point because
834 // RenderViewHosts are still alive. During extension reloading, they will
835 // decrement the lazy_keepalive_count to negative for the new extension
836 // instance when they are destroyed. Since we are erasing the background page
837 // data for the unloaded extension, unregister the RenderViewHosts too.
838 BrowserContext* context = GetBrowserContext();
839 for (ExtensionRenderViews::iterator it = all_extension_views_.begin();
840 it != all_extension_views_.end(); ) {
841 if (GetExtensionID(it->first) == extension_id) {
842 OnRenderViewHostUnregistered(context, it->first);
843 all_extension_views_.erase(it++);
849 background_page_data_.erase(extension_id);
852 void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) {
853 background_page_data_.erase(extension_id);
855 // Re-register all RenderViews for this extension. We do this to restore
856 // the lazy_keepalive_count (if any) to properly reflect the number of open
858 for (ExtensionRenderViews::const_iterator it = all_extension_views_.begin();
859 it != all_extension_views_.end(); ++it) {
860 if (GetExtensionID(it->first) == extension_id)
861 IncrementLazyKeepaliveCountForView(it->first);
865 bool ProcessManager::DeferLoadingBackgroundHosts() const {
866 // The extensions embedder may have special rules about background hosts.
867 return ExtensionsBrowserClient::Get()->DeferLoadingBackgroundHosts(
868 GetBrowserContext());
872 // IncognitoProcessManager
875 IncognitoProcessManager::IncognitoProcessManager(
876 BrowserContext* incognito_context,
877 BrowserContext* original_context,
878 ProcessManager* original_manager)
879 : ProcessManager(incognito_context, original_context),
880 original_manager_(original_manager) {
881 DCHECK(incognito_context->IsOffTheRecord());
883 // The original profile will have its own ProcessManager to
884 // load the background pages of the spanning extensions. This process
885 // manager need only worry about the split mode extensions, which is handled
886 // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler.
887 registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY,
888 content::Source<BrowserContext>(original_context));
889 registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_CREATED,
890 content::Source<BrowserContext>(original_context));
893 bool IncognitoProcessManager::CreateBackgroundHost(const Extension* extension,
895 if (IncognitoInfo::IsSplitMode(extension)) {
896 if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
897 extension->id(), GetBrowserContext()))
898 return ProcessManager::CreateBackgroundHost(extension, url);
900 // Do nothing. If an extension is spanning, then its original-profile
901 // background page is shared with incognito, so we don't create another.
906 SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) {
907 ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
909 const Extension* extension =
910 registry->enabled_extensions().GetExtensionOrAppByURL(url);
911 if (extension && !IncognitoInfo::IsSplitMode(extension)) {
912 return original_manager_->GetSiteInstanceForURL(url);
915 return ProcessManager::GetSiteInstanceForURL(url);
918 } // namespace extensions