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 "content/public/browser/browser_context.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/devtools_agent_host.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/site_instance.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/web_contents_delegate.h"
26 #include "content/public/browser/web_contents_observer.h"
27 #include "content/public/browser/web_contents_user_data.h"
28 #include "content/public/common/renderer_preferences.h"
29 #include "content/public/common/url_constants.h"
30 #include "extensions/browser/extension_host.h"
31 #include "extensions/browser/extension_registry.h"
32 #include "extensions/browser/extension_system.h"
33 #include "extensions/browser/extensions_browser_client.h"
34 #include "extensions/browser/notification_types.h"
35 #include "extensions/browser/process_manager_delegate.h"
36 #include "extensions/browser/process_manager_observer.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"
45 using content::BrowserContext;
46 using content::RenderViewHost;
47 using content::SiteInstance;
48 using content::WebContents;
50 namespace extensions {
51 class RenderViewHostDestructionObserver;
53 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
54 extensions::RenderViewHostDestructionObserver);
56 namespace extensions {
60 // The time to delay between an extension becoming idle and
61 // sending a ShouldSuspend message.
62 // Note: Must be sufficiently larger (e.g. 2x) than
63 // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
64 unsigned g_event_page_idle_time_msec = 10000;
66 // The time to delay between sending a ShouldSuspend message and
67 // sending a Suspend message.
68 unsigned g_event_page_suspending_time_msec = 5000;
70 std::string GetExtensionID(RenderViewHost* render_view_host) {
71 // This works for both apps and extensions because the site has been
72 // normalized to the extension URL for hosted apps.
73 content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
77 const GURL& site_url = site_instance->GetSiteURL();
79 if (!site_url.SchemeIs(kExtensionScheme) &&
80 !site_url.SchemeIs(content::kGuestScheme))
83 return site_url.host();
86 std::string GetExtensionIDFromFrame(
87 content::RenderFrameHost* render_frame_host) {
88 // This works for both apps and extensions because the site has been
89 // normalized to the extension URL for apps.
90 if (!render_frame_host->GetSiteInstance())
93 return render_frame_host->GetSiteInstance()->GetSiteURL().host();
96 bool IsFrameInExtensionHost(ExtensionHost* extension_host,
97 content::RenderFrameHost* render_frame_host) {
98 return WebContents::FromRenderFrameHost(render_frame_host) ==
99 extension_host->host_contents();
102 void OnRenderViewHostUnregistered(BrowserContext* context,
103 RenderViewHost* render_view_host) {
104 content::NotificationService::current()->Notify(
105 extensions::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED,
106 content::Source<BrowserContext>(context),
107 content::Details<RenderViewHost>(render_view_host));
110 // Incognito profiles use this process manager. It is mostly a shim that decides
111 // whether to fall back on the original profile's ProcessManager based
112 // on whether a given extension uses "split" or "spanning" incognito behavior.
113 class IncognitoProcessManager : public ProcessManager {
115 IncognitoProcessManager(BrowserContext* incognito_context,
116 BrowserContext* original_context,
117 ProcessManager* original_manager,
118 ExtensionRegistry* extension_registry);
119 virtual ~IncognitoProcessManager() {}
120 virtual bool CreateBackgroundHost(const Extension* extension,
121 const GURL& url) OVERRIDE;
122 virtual SiteInstance* GetSiteInstanceForURL(const GURL& url) OVERRIDE;
125 ProcessManager* original_manager_;
127 DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager);
130 static void CreateBackgroundHostForExtensionLoad(
131 ProcessManager* manager, const Extension* extension) {
132 DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
133 if (BackgroundInfo::HasPersistentBackgroundPage(extension))
134 manager->CreateBackgroundHost(extension,
135 BackgroundInfo::GetBackgroundURL(extension));
140 class RenderViewHostDestructionObserver
141 : public content::WebContentsObserver,
142 public content::WebContentsUserData<RenderViewHostDestructionObserver> {
144 virtual ~RenderViewHostDestructionObserver() {}
147 explicit RenderViewHostDestructionObserver(WebContents* web_contents)
148 : WebContentsObserver(web_contents) {
149 BrowserContext* context = web_contents->GetBrowserContext();
150 process_manager_ = ExtensionSystem::Get(context)->process_manager();
153 friend class content::WebContentsUserData<RenderViewHostDestructionObserver>;
155 // content::WebContentsObserver overrides.
156 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
157 process_manager_->UnregisterRenderViewHost(render_view_host);
160 ProcessManager* process_manager_;
162 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver);
165 struct ProcessManager::BackgroundPageData {
166 // The count of things keeping the lazy background page alive.
167 int lazy_keepalive_count;
169 // Tracks if an impulse event has occured since the last polling check.
170 bool keepalive_impulse;
171 bool previous_keepalive_impulse;
173 // True if the page responded to the ShouldSuspend message and is currently
174 // dispatching the suspend event. During this time any events that arrive will
175 // cancel the suspend process and an onSuspendCanceled event will be
176 // dispatched to the page.
179 // Stores the value of the incremented
180 // ProcessManager::last_background_close_sequence_id_ whenever the extension
181 // is active. A copy of the ID is also passed in the callbacks and IPC
182 // messages leading up to CloseLazyBackgroundPageNow. The process is aborted
183 // if the IDs ever differ due to new activity.
184 uint64 close_sequence_id;
186 // Keeps track of when this page was last suspended. Used for perf metrics.
187 linked_ptr<base::ElapsedTimer> since_suspended;
190 : lazy_keepalive_count(0),
191 keepalive_impulse(false),
192 previous_keepalive_impulse(false),
194 close_sequence_id(0) {}
202 ProcessManager* ProcessManager::Create(BrowserContext* context) {
203 ExtensionRegistry* extension_registry = ExtensionRegistry::Get(context);
204 ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get();
205 if (client->IsGuestSession(context)) {
206 // In the guest session, there is a single off-the-record context. Unlike
207 // a regular incognito mode, background pages of extensions must be
208 // created regardless of whether extensions use "spanning" or "split"
209 // incognito behavior.
210 BrowserContext* original_context = client->GetOriginalContext(context);
211 return new ProcessManager(context, original_context, extension_registry);
214 if (context->IsOffTheRecord()) {
215 BrowserContext* original_context = client->GetOriginalContext(context);
216 ProcessManager* original_manager =
217 ExtensionSystem::Get(original_context)->process_manager();
218 return new IncognitoProcessManager(
219 context, original_context, original_manager, extension_registry);
222 return new ProcessManager(context, context, extension_registry);
226 ProcessManager* ProcessManager::CreateForTesting(
227 BrowserContext* context,
228 ExtensionRegistry* extension_registry) {
229 DCHECK(!context->IsOffTheRecord());
230 return new ProcessManager(context, context, extension_registry);
234 ProcessManager* ProcessManager::CreateIncognitoForTesting(
235 BrowserContext* incognito_context,
236 BrowserContext* original_context,
237 ProcessManager* original_manager,
238 ExtensionRegistry* extension_registry) {
239 DCHECK(incognito_context->IsOffTheRecord());
240 DCHECK(!original_context->IsOffTheRecord());
241 return new IncognitoProcessManager(incognito_context,
247 ProcessManager::ProcessManager(BrowserContext* context,
248 BrowserContext* original_context,
249 ExtensionRegistry* extension_registry)
250 : site_instance_(SiteInstance::Create(context)),
251 extension_registry_(extension_registry),
252 startup_background_hosts_created_(false),
253 devtools_callback_(base::Bind(&ProcessManager::OnDevToolsStateChanged,
254 base::Unretained(this))),
255 last_background_close_sequence_id_(0),
256 weak_ptr_factory_(this) {
257 // ExtensionRegistry is shared between incognito and regular contexts.
258 DCHECK_EQ(original_context, extension_registry_->browser_context());
260 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
261 content::Source<BrowserContext>(original_context));
263 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
264 content::Source<BrowserContext>(original_context));
266 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
267 content::Source<BrowserContext>(original_context));
269 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
270 content::Source<BrowserContext>(context));
272 extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
273 content::Source<BrowserContext>(context));
274 registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
275 content::NotificationService::AllSources());
276 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
277 content::NotificationService::AllSources());
279 content::DevToolsAgentHost::AddAgentStateCallback(devtools_callback_);
281 OnKeepaliveImpulseCheck();
284 ProcessManager::~ProcessManager() {
285 CloseBackgroundHosts();
286 DCHECK(background_hosts_.empty());
287 content::DevToolsAgentHost::RemoveAgentStateCallback(devtools_callback_);
290 const ProcessManager::ViewSet ProcessManager::GetAllViews() const {
292 for (ExtensionRenderViews::const_iterator iter =
293 all_extension_views_.begin();
294 iter != all_extension_views_.end(); ++iter) {
295 result.insert(iter->first);
300 void ProcessManager::AddObserver(ProcessManagerObserver* observer) {
301 observer_list_.AddObserver(observer);
304 void ProcessManager::RemoveObserver(ProcessManagerObserver* observer) {
305 observer_list_.RemoveObserver(observer);
308 bool ProcessManager::CreateBackgroundHost(const Extension* extension,
310 // Hosted apps are taken care of from BackgroundContentsService. Ignore them
312 if (extension->is_hosted_app())
315 // Don't create hosts if the embedder doesn't allow it.
316 ProcessManagerDelegate* delegate =
317 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
318 if (delegate && !delegate->IsBackgroundPageAllowed(GetBrowserContext()))
321 // Don't create multiple background hosts for an extension.
322 if (GetBackgroundHostForExtension(extension->id()))
323 return true; // TODO(kalman): return false here? It might break things...
325 ExtensionHost* host =
326 new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
327 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
328 host->CreateRenderViewSoon();
329 OnBackgroundHostCreated(host);
333 ExtensionHost* ProcessManager::GetBackgroundHostForExtension(
334 const std::string& extension_id) {
335 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
336 iter != background_hosts_.end(); ++iter) {
337 ExtensionHost* host = *iter;
338 if (host->extension_id() == extension_id)
344 std::set<RenderViewHost*> ProcessManager::GetRenderViewHostsForExtension(
345 const std::string& extension_id) {
346 std::set<RenderViewHost*> result;
348 SiteInstance* site_instance = GetSiteInstanceForURL(
349 Extension::GetBaseURLFromExtensionId(extension_id));
353 // Gather up all the views for that site.
354 for (ExtensionRenderViews::iterator view = all_extension_views_.begin();
355 view != all_extension_views_.end(); ++view) {
356 if (view->first->GetSiteInstance() == site_instance)
357 result.insert(view->first);
363 const Extension* ProcessManager::GetExtensionForRenderViewHost(
364 RenderViewHost* render_view_host) {
365 if (!render_view_host->GetSiteInstance())
368 return extension_registry_->enabled_extensions().GetByID(
369 GetExtensionID(render_view_host));
372 void ProcessManager::UnregisterRenderViewHost(
373 RenderViewHost* render_view_host) {
374 ExtensionRenderViews::iterator view =
375 all_extension_views_.find(render_view_host);
376 if (view == all_extension_views_.end())
379 OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host);
380 ViewType view_type = view->second;
381 all_extension_views_.erase(view);
383 // Keepalive count, balanced in RegisterRenderViewHost.
384 if (view_type != VIEW_TYPE_INVALID &&
385 view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
386 const Extension* extension = GetExtensionForRenderViewHost(
389 DecrementLazyKeepaliveCount(extension);
393 bool ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) {
394 const Extension* extension = GetExtensionForRenderViewHost(
399 WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
400 all_extension_views_[render_view_host] = GetViewType(web_contents);
402 // Keep the lazy background page alive as long as any non-background-page
403 // extension views are visible. Keepalive count balanced in
404 // UnregisterRenderViewHost.
405 IncrementLazyKeepaliveCountForView(render_view_host);
409 SiteInstance* ProcessManager::GetSiteInstanceForURL(const GURL& url) {
410 return site_instance_->GetRelatedSiteInstance(url);
413 bool ProcessManager::IsBackgroundHostClosing(const std::string& extension_id) {
414 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
415 return (host && background_page_data_[extension_id].is_closing);
418 int ProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
419 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
422 return background_page_data_[extension->id()].lazy_keepalive_count;
425 void ProcessManager::IncrementLazyKeepaliveCount(const Extension* extension) {
426 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
429 int& count = background_page_data_[extension->id()].lazy_keepalive_count;
431 OnLazyBackgroundPageActive(extension->id());
434 void ProcessManager::DecrementLazyKeepaliveCount(const Extension* extension) {
435 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
437 DecrementLazyKeepaliveCount(extension->id());
440 void ProcessManager::DecrementLazyKeepaliveCount(
441 const std::string& extension_id) {
442 int& count = background_page_data_[extension_id].lazy_keepalive_count;
444 !extension_registry_->enabled_extensions().Contains(extension_id));
446 // If we reach a zero keepalive count when the lazy background page is about
447 // to be closed, incrementing close_sequence_id will cancel the close
448 // sequence and cause the background page to linger. So check is_closing
449 // before initiating another close sequence.
450 if (--count == 0 && !background_page_data_[extension_id].is_closing) {
451 background_page_data_[extension_id].close_sequence_id =
452 ++last_background_close_sequence_id_;
453 base::MessageLoop::current()->PostDelayedTask(
455 base::Bind(&ProcessManager::OnLazyBackgroundPageIdle,
456 weak_ptr_factory_.GetWeakPtr(),
458 last_background_close_sequence_id_),
459 base::TimeDelta::FromMilliseconds(g_event_page_idle_time_msec));
463 void ProcessManager::IncrementLazyKeepaliveCountForView(
464 RenderViewHost* render_view_host) {
465 WebContents* web_contents =
466 WebContents::FromRenderViewHost(render_view_host);
467 ViewType view_type = GetViewType(web_contents);
468 if (view_type != VIEW_TYPE_INVALID &&
469 view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
470 const Extension* extension = GetExtensionForRenderViewHost(
473 IncrementLazyKeepaliveCount(extension);
477 // This implementation layers on top of the keepalive count. An impulse sets
478 // a per extension flag. On a regular interval that flag is checked. Changes
479 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
480 void ProcessManager::KeepaliveImpulse(const Extension* extension) {
481 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
484 BackgroundPageData& bd = background_page_data_[extension->id()];
486 if (!bd.keepalive_impulse) {
487 bd.keepalive_impulse = true;
488 if (!bd.previous_keepalive_impulse) {
489 IncrementLazyKeepaliveCount(extension);
493 if (!keepalive_impulse_callback_for_testing_.is_null()) {
494 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
495 keepalive_impulse_callback_for_testing_;
496 callback_may_clear_callbacks_reentrantly.Run(extension->id());
501 void ProcessManager::OnKeepaliveFromPlugin(int render_process_id,
503 const std::string& extension_id) {
504 content::RenderFrameHost* render_frame_host =
505 content::RenderFrameHost::FromID(render_process_id, render_frame_id);
506 if (!render_frame_host)
509 content::SiteInstance* site_instance = render_frame_host->GetSiteInstance();
513 BrowserContext* browser_context = site_instance->GetBrowserContext();
514 const Extension* extension =
515 ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID(
520 ProcessManager* pm = ExtensionSystem::Get(browser_context)->process_manager();
524 pm->KeepaliveImpulse(extension);
527 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
528 // have been made for at least g_event_page_idle_time_msec. In the best case an
529 // impulse was made just before being cleared, and the decrement will occur
530 // g_event_page_idle_time_msec later, causing a 2 * g_event_page_idle_time_msec
531 // total time for extension to be shut down based on impulses. Worst case is
532 // an impulse just after a clear, adding one check cycle and resulting in 3x
534 void ProcessManager::OnKeepaliveImpulseCheck() {
535 for (BackgroundPageDataMap::iterator i = background_page_data_.begin();
536 i != background_page_data_.end();
538 if (i->second.previous_keepalive_impulse && !i->second.keepalive_impulse) {
539 DecrementLazyKeepaliveCount(i->first);
540 if (!keepalive_impulse_decrement_callback_for_testing_.is_null()) {
541 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
542 keepalive_impulse_decrement_callback_for_testing_;
543 callback_may_clear_callbacks_reentrantly.Run(i->first);
547 i->second.previous_keepalive_impulse = i->second.keepalive_impulse;
548 i->second.keepalive_impulse = false;
551 // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
552 // tests there will be no message loop. In that event don't schedule tasks.
553 if (base::MessageLoop::current()) {
554 base::MessageLoop::current()->PostDelayedTask(
556 base::Bind(&ProcessManager::OnKeepaliveImpulseCheck,
557 weak_ptr_factory_.GetWeakPtr()),
558 base::TimeDelta::FromMilliseconds(g_event_page_idle_time_msec));
562 void ProcessManager::OnLazyBackgroundPageIdle(const std::string& extension_id,
563 uint64 sequence_id) {
564 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
565 if (host && !background_page_data_[extension_id].is_closing &&
566 sequence_id == background_page_data_[extension_id].close_sequence_id) {
567 // Tell the renderer we are about to close. This is a simple ping that the
568 // renderer will respond to. The purpose is to control sequencing: if the
569 // extension remains idle until the renderer responds with an ACK, then we
570 // know that the extension process is ready to shut down. If our
571 // close_sequence_id has already changed, then we would ignore the
572 // ShouldSuspendAck, so we don't send the ping.
573 host->render_view_host()->Send(new ExtensionMsg_ShouldSuspend(
574 extension_id, sequence_id));
578 void ProcessManager::OnLazyBackgroundPageActive(
579 const std::string& extension_id) {
580 if (!background_page_data_[extension_id].is_closing) {
581 // Cancel the current close sequence by changing the close_sequence_id,
582 // which causes us to ignore the next ShouldSuspendAck.
583 background_page_data_[extension_id].close_sequence_id =
584 ++last_background_close_sequence_id_;
588 void ProcessManager::OnShouldSuspendAck(const std::string& extension_id,
589 uint64 sequence_id) {
590 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
592 sequence_id == background_page_data_[extension_id].close_sequence_id) {
593 host->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id));
597 void ProcessManager::OnSuspendAck(const std::string& extension_id) {
598 background_page_data_[extension_id].is_closing = true;
599 uint64 sequence_id = background_page_data_[extension_id].close_sequence_id;
600 base::MessageLoop::current()->PostDelayedTask(
602 base::Bind(&ProcessManager::CloseLazyBackgroundPageNow,
603 weak_ptr_factory_.GetWeakPtr(),
606 base::TimeDelta::FromMilliseconds(g_event_page_suspending_time_msec));
609 void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id,
610 uint64 sequence_id) {
611 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
613 sequence_id == background_page_data_[extension_id].close_sequence_id) {
614 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
616 CloseBackgroundHost(host);
620 void ProcessManager::OnNetworkRequestStarted(
621 content::RenderFrameHost* render_frame_host) {
622 ExtensionHost* host = GetBackgroundHostForExtension(
623 GetExtensionIDFromFrame(render_frame_host));
624 if (host && IsFrameInExtensionHost(host, render_frame_host))
625 IncrementLazyKeepaliveCount(host->extension());
628 void ProcessManager::OnNetworkRequestDone(
629 content::RenderFrameHost* render_frame_host) {
630 ExtensionHost* host = GetBackgroundHostForExtension(
631 GetExtensionIDFromFrame(render_frame_host));
632 if (host && IsFrameInExtensionHost(host, render_frame_host))
633 DecrementLazyKeepaliveCount(host->extension());
636 void ProcessManager::CancelSuspend(const Extension* extension) {
637 bool& is_closing = background_page_data_[extension->id()].is_closing;
638 ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
639 if (host && is_closing) {
641 host->render_view_host()->Send(
642 new ExtensionMsg_CancelSuspend(extension->id()));
643 // This increment / decrement is to simulate an instantaneous event. This
644 // has the effect of invalidating close_sequence_id, preventing any in
645 // progress closes from completing and starting a new close process if
647 IncrementLazyKeepaliveCount(extension);
648 DecrementLazyKeepaliveCount(extension);
652 void ProcessManager::CloseBackgroundHosts() {
653 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
654 iter != background_hosts_.end();) {
655 ExtensionHostSet::iterator current = iter++;
660 content::BrowserContext* ProcessManager::GetBrowserContext() const {
661 return site_instance_->GetBrowserContext();
664 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
665 const ImpulseCallbackForTesting& callback) {
666 keepalive_impulse_callback_for_testing_ = callback;
669 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
670 const ImpulseCallbackForTesting& callback) {
671 keepalive_impulse_decrement_callback_for_testing_ = callback;
675 void ProcessManager::SetEventPageIdleTimeForTesting(unsigned idle_time_msec) {
676 CHECK_GT(idle_time_msec, 0u); // OnKeepaliveImpulseCheck requires non zero.
677 g_event_page_idle_time_msec = idle_time_msec;
681 void ProcessManager::SetEventPageSuspendingTimeForTesting(
682 unsigned suspending_time_msec) {
683 g_event_page_suspending_time_msec = suspending_time_msec;
686 void ProcessManager::Observe(int type,
687 const content::NotificationSource& source,
688 const content::NotificationDetails& details) {
690 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED: {
691 // TODO(jamescook): Convert this to use ExtensionSystem::ready() instead
692 // of a notification.
693 MaybeCreateStartupBackgroundHosts();
697 case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: {
698 BrowserContext* context = content::Source<BrowserContext>(source).ptr();
699 ExtensionSystem* system = ExtensionSystem::Get(context);
700 if (system->ready().is_signaled()) {
701 // The extension system is ready, so create the background host.
702 const Extension* extension =
703 content::Details<const Extension>(details).ptr();
704 CreateBackgroundHostForExtensionLoad(this, extension);
709 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
710 const Extension* extension =
711 content::Details<UnloadedExtensionInfo>(details)->extension;
712 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
713 iter != background_hosts_.end(); ++iter) {
714 ExtensionHost* host = *iter;
715 if (host->extension_id() == extension->id()) {
716 CloseBackgroundHost(host);
720 UnregisterExtension(extension->id());
724 case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
725 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
726 if (background_hosts_.erase(host)) {
727 ClearBackgroundPageData(host->extension()->id());
728 background_page_data_[host->extension()->id()].since_suspended.reset(
729 new base::ElapsedTimer());
734 case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
735 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
736 if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
737 CloseBackgroundHost(host);
742 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
743 // We get this notification both for new WebContents and when one
744 // has its RenderViewHost replaced (e.g. when a user does a cross-site
745 // navigation away from an extension URL). For the replaced case, we must
746 // unregister the old RVH so it doesn't count as an active view that would
747 // keep the event page alive.
748 WebContents* contents = content::Source<WebContents>(source).ptr();
749 if (contents->GetBrowserContext() != GetBrowserContext())
752 typedef std::pair<RenderViewHost*, RenderViewHost*> RVHPair;
753 RVHPair* switched_details = content::Details<RVHPair>(details).ptr();
754 if (switched_details->first)
755 UnregisterRenderViewHost(switched_details->first);
757 // The above will unregister a RVH when it gets swapped out with a new
758 // one. However we need to watch the WebContents to know when a RVH is
759 // deleted because the WebContents has gone away.
760 if (RegisterRenderViewHost(switched_details->second)) {
761 RenderViewHostDestructionObserver::CreateForWebContents(contents);
766 case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: {
767 WebContents* contents = content::Source<WebContents>(source).ptr();
768 if (contents->GetBrowserContext() != GetBrowserContext())
770 const Extension* extension = GetExtensionForRenderViewHost(
771 contents->GetRenderViewHost());
775 // RegisterRenderViewHost is called too early (before the process is
776 // available), so we need to wait until now to notify.
777 content::NotificationService::current()->Notify(
778 extensions::NOTIFICATION_EXTENSION_VIEW_REGISTERED,
779 content::Source<BrowserContext>(GetBrowserContext()),
780 content::Details<RenderViewHost>(contents->GetRenderViewHost()));
789 void ProcessManager::OnDevToolsStateChanged(
790 content::DevToolsAgentHost* agent_host,
792 WebContents* web_contents = agent_host->GetWebContents();
793 // Ignore unrelated notifications.
794 if (!web_contents || web_contents->GetBrowserContext() != GetBrowserContext())
796 if (GetViewType(web_contents) != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
798 const Extension* extension =
799 GetExtensionForRenderViewHost(web_contents->GetRenderViewHost());
803 // Keep the lazy background page alive while it's being inspected.
804 CancelSuspend(extension);
805 IncrementLazyKeepaliveCount(extension);
807 DecrementLazyKeepaliveCount(extension);
811 void ProcessManager::MaybeCreateStartupBackgroundHosts() {
812 if (startup_background_hosts_created_)
815 // The embedder might disallow background pages entirely.
816 ProcessManagerDelegate* delegate =
817 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
818 if (delegate && !delegate->IsBackgroundPageAllowed(GetBrowserContext()))
821 // The embedder might want to defer background page loading. For example,
822 // Chrome defers background page loading when it is launched to show the app
823 // list, then triggers a load later when a browser window opens.
825 delegate->DeferCreatingStartupBackgroundHosts(GetBrowserContext()))
828 CreateStartupBackgroundHosts();
829 startup_background_hosts_created_ = true;
831 // Background pages should only be loaded once. To prevent any further loads
832 // occurring, we remove the notification listeners.
833 BrowserContext* original_context =
834 ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
835 if (registrar_.IsRegistered(
837 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
838 content::Source<BrowserContext>(original_context))) {
839 registrar_.Remove(this,
840 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
841 content::Source<BrowserContext>(original_context));
845 void ProcessManager::CreateStartupBackgroundHosts() {
846 DCHECK(!startup_background_hosts_created_);
847 const ExtensionSet& enabled_extensions =
848 extension_registry_->enabled_extensions();
849 for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
850 extension != enabled_extensions.end();
852 CreateBackgroundHostForExtensionLoad(this, extension->get());
854 FOR_EACH_OBSERVER(ProcessManagerObserver,
856 OnBackgroundHostStartup(extension->get()));
860 void ProcessManager::OnBackgroundHostCreated(ExtensionHost* host) {
861 DCHECK_EQ(GetBrowserContext(), host->browser_context());
862 background_hosts_.insert(host);
864 if (BackgroundInfo::HasLazyBackgroundPage(host->extension())) {
865 linked_ptr<base::ElapsedTimer> since_suspended(
866 background_page_data_[host->extension()->id()].
867 since_suspended.release());
868 if (since_suspended.get()) {
869 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
870 since_suspended->Elapsed());
875 void ProcessManager::CloseBackgroundHost(ExtensionHost* host) {
876 CHECK(host->extension_host_type() ==
877 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
879 // |host| should deregister itself from our structures.
880 CHECK(background_hosts_.find(host) == background_hosts_.end());
883 void ProcessManager::UnregisterExtension(const std::string& extension_id) {
884 // The lazy_keepalive_count may be greater than zero at this point because
885 // RenderViewHosts are still alive. During extension reloading, they will
886 // decrement the lazy_keepalive_count to negative for the new extension
887 // instance when they are destroyed. Since we are erasing the background page
888 // data for the unloaded extension, unregister the RenderViewHosts too.
889 BrowserContext* context = GetBrowserContext();
890 for (ExtensionRenderViews::iterator it = all_extension_views_.begin();
891 it != all_extension_views_.end(); ) {
892 if (GetExtensionID(it->first) == extension_id) {
893 OnRenderViewHostUnregistered(context, it->first);
894 all_extension_views_.erase(it++);
900 background_page_data_.erase(extension_id);
903 void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) {
904 background_page_data_.erase(extension_id);
906 // Re-register all RenderViews for this extension. We do this to restore
907 // the lazy_keepalive_count (if any) to properly reflect the number of open
909 for (ExtensionRenderViews::const_iterator it = all_extension_views_.begin();
910 it != all_extension_views_.end(); ++it) {
911 if (GetExtensionID(it->first) == extension_id)
912 IncrementLazyKeepaliveCountForView(it->first);
917 // IncognitoProcessManager
920 IncognitoProcessManager::IncognitoProcessManager(
921 BrowserContext* incognito_context,
922 BrowserContext* original_context,
923 ProcessManager* original_manager,
924 ExtensionRegistry* extension_registry)
925 : ProcessManager(incognito_context, original_context, extension_registry),
926 original_manager_(original_manager) {
927 DCHECK(incognito_context->IsOffTheRecord());
929 // The original profile will have its own ProcessManager to
930 // load the background pages of the spanning extensions. This process
931 // manager need only worry about the split mode extensions, which is handled
932 // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler.
933 registrar_.Remove(this,
934 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
935 content::Source<BrowserContext>(original_context));
938 bool IncognitoProcessManager::CreateBackgroundHost(const Extension* extension,
940 if (IncognitoInfo::IsSplitMode(extension)) {
941 if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
942 extension->id(), GetBrowserContext()))
943 return ProcessManager::CreateBackgroundHost(extension, url);
945 // Do nothing. If an extension is spanning, then its original-profile
946 // background page is shared with incognito, so we don't create another.
951 SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) {
952 const Extension* extension =
953 extension_registry_->enabled_extensions().GetExtensionOrAppByURL(url);
954 if (extension && !IncognitoInfo::IsSplitMode(extension))
955 return original_manager_->GetSiteInstanceForURL(url);
957 return ProcessManager::GetSiteInstanceForURL(url);
960 } // namespace extensions