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"
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 extensions::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 ExtensionRegistry
* extension_registry
);
110 virtual ~IncognitoProcessManager() {}
111 virtual bool CreateBackgroundHost(const Extension
* extension
,
112 const GURL
& url
) OVERRIDE
;
113 virtual SiteInstance
* GetSiteInstanceForURL(const GURL
& url
) OVERRIDE
;
116 ProcessManager
* original_manager_
;
118 DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager
);
121 static void CreateBackgroundHostForExtensionLoad(
122 ProcessManager
* manager
, const Extension
* extension
) {
123 DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
124 if (BackgroundInfo::HasPersistentBackgroundPage(extension
))
125 manager
->CreateBackgroundHost(extension
,
126 BackgroundInfo::GetBackgroundURL(extension
));
131 class RenderViewHostDestructionObserver
132 : public content::WebContentsObserver
,
133 public content::WebContentsUserData
<RenderViewHostDestructionObserver
> {
135 virtual ~RenderViewHostDestructionObserver() {}
138 explicit RenderViewHostDestructionObserver(WebContents
* web_contents
)
139 : WebContentsObserver(web_contents
) {
140 BrowserContext
* context
= web_contents
->GetBrowserContext();
141 process_manager_
= ExtensionSystem::Get(context
)->process_manager();
144 friend class content::WebContentsUserData
<RenderViewHostDestructionObserver
>;
146 // content::WebContentsObserver overrides.
147 virtual void RenderViewDeleted(RenderViewHost
* render_view_host
) OVERRIDE
{
148 process_manager_
->UnregisterRenderViewHost(render_view_host
);
151 ProcessManager
* process_manager_
;
153 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver
);
156 struct ProcessManager::BackgroundPageData
{
157 // The count of things keeping the lazy background page alive.
158 int lazy_keepalive_count
;
160 // Tracks if an impulse event has occured since the last polling check.
161 bool keepalive_impulse
;
162 bool previous_keepalive_impulse
;
164 // True if the page responded to the ShouldSuspend message and is currently
165 // dispatching the suspend event. During this time any events that arrive will
166 // cancel the suspend process and an onSuspendCanceled event will be
167 // dispatched to the page.
170 // Stores the value of the incremented
171 // ProcessManager::last_background_close_sequence_id_ whenever the extension
172 // is active. A copy of the ID is also passed in the callbacks and IPC
173 // messages leading up to CloseLazyBackgroundPageNow. The process is aborted
174 // if the IDs ever differ due to new activity.
175 uint64 close_sequence_id
;
177 // Keeps track of when this page was last suspended. Used for perf metrics.
178 linked_ptr
<base::ElapsedTimer
> since_suspended
;
181 : lazy_keepalive_count(0),
182 keepalive_impulse(false),
183 previous_keepalive_impulse(false),
185 close_sequence_id(0) {}
193 ProcessManager
* ProcessManager::Create(BrowserContext
* context
) {
194 ExtensionRegistry
* extension_registry
= ExtensionRegistry::Get(context
);
195 ExtensionsBrowserClient
* client
= ExtensionsBrowserClient::Get();
196 if (client
->IsGuestSession(context
)) {
197 // In the guest session, there is a single off-the-record context. Unlike
198 // a regular incognito mode, background pages of extensions must be
199 // created regardless of whether extensions use "spanning" or "split"
200 // incognito behavior.
201 BrowserContext
* original_context
= client
->GetOriginalContext(context
);
202 return new ProcessManager(context
, original_context
, extension_registry
);
205 if (context
->IsOffTheRecord()) {
206 BrowserContext
* original_context
= client
->GetOriginalContext(context
);
207 ProcessManager
* original_manager
=
208 ExtensionSystem::Get(original_context
)->process_manager();
209 return new IncognitoProcessManager(
210 context
, original_context
, original_manager
, extension_registry
);
213 return new ProcessManager(context
, context
, extension_registry
);
217 ProcessManager
* ProcessManager::CreateForTesting(
218 BrowserContext
* context
,
219 ExtensionRegistry
* extension_registry
) {
220 DCHECK(!context
->IsOffTheRecord());
221 return new ProcessManager(context
, context
, extension_registry
);
225 ProcessManager
* ProcessManager::CreateIncognitoForTesting(
226 BrowserContext
* incognito_context
,
227 BrowserContext
* original_context
,
228 ProcessManager
* original_manager
,
229 ExtensionRegistry
* extension_registry
) {
230 DCHECK(incognito_context
->IsOffTheRecord());
231 DCHECK(!original_context
->IsOffTheRecord());
232 return new IncognitoProcessManager(incognito_context
,
238 ProcessManager::ProcessManager(BrowserContext
* context
,
239 BrowserContext
* original_context
,
240 ExtensionRegistry
* extension_registry
)
241 : site_instance_(SiteInstance::Create(context
)),
242 extension_registry_(extension_registry
),
243 startup_background_hosts_created_(false),
244 devtools_callback_(base::Bind(&ProcessManager::OnDevToolsStateChanged
,
245 base::Unretained(this))),
246 last_background_close_sequence_id_(0),
247 weak_ptr_factory_(this) {
248 // ExtensionRegistry is shared between incognito and regular contexts.
249 DCHECK_EQ(original_context
, extension_registry_
->browser_context());
251 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
252 content::Source
<BrowserContext
>(original_context
));
254 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
,
255 content::Source
<BrowserContext
>(original_context
));
257 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
,
258 content::Source
<BrowserContext
>(original_context
));
260 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
,
261 content::Source
<BrowserContext
>(context
));
263 extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE
,
264 content::Source
<BrowserContext
>(context
));
265 registrar_
.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
266 content::NotificationService::AllSources());
267 registrar_
.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED
,
268 content::NotificationService::AllSources());
270 // Note: event_page_idle_time_ must be sufficiently larger (e.g. 2x) than
271 // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
272 event_page_idle_time_
= base::TimeDelta::FromSeconds(10);
273 unsigned idle_time_msec
= 0;
274 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
275 extensions::switches::kEventPageIdleTime
), &idle_time_msec
)) {
276 CHECK_GT(idle_time_msec
, 0u); // OnKeepaliveImpulseCheck requires non zero.
277 event_page_idle_time_
= base::TimeDelta::FromMilliseconds(idle_time_msec
);
279 event_page_suspending_time_
= base::TimeDelta::FromSeconds(5);
280 unsigned suspending_time_msec
= 0;
281 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
282 extensions::switches::kEventPageSuspendingTime
),
283 &suspending_time_msec
)) {
284 event_page_suspending_time_
=
285 base::TimeDelta::FromMilliseconds(suspending_time_msec
);
288 content::DevToolsAgentHost::AddAgentStateCallback(devtools_callback_
);
290 OnKeepaliveImpulseCheck();
293 ProcessManager::~ProcessManager() {
294 CloseBackgroundHosts();
295 DCHECK(background_hosts_
.empty());
296 content::DevToolsAgentHost::RemoveAgentStateCallback(devtools_callback_
);
299 const ProcessManager::ViewSet
ProcessManager::GetAllViews() const {
301 for (ExtensionRenderViews::const_iterator iter
=
302 all_extension_views_
.begin();
303 iter
!= all_extension_views_
.end(); ++iter
) {
304 result
.insert(iter
->first
);
309 void ProcessManager::AddObserver(ProcessManagerObserver
* observer
) {
310 observer_list_
.AddObserver(observer
);
313 void ProcessManager::RemoveObserver(ProcessManagerObserver
* observer
) {
314 observer_list_
.RemoveObserver(observer
);
317 bool ProcessManager::CreateBackgroundHost(const Extension
* extension
,
319 // Hosted apps are taken care of from BackgroundContentsService. Ignore them
321 if (extension
->is_hosted_app())
324 // Don't create hosts if the embedder doesn't allow it.
325 ProcessManagerDelegate
* delegate
=
326 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
327 if (delegate
&& !delegate
->IsBackgroundPageAllowed(GetBrowserContext()))
330 // Don't create multiple background hosts for an extension.
331 if (GetBackgroundHostForExtension(extension
->id()))
332 return true; // TODO(kalman): return false here? It might break things...
334 ExtensionHost
* host
=
335 new ExtensionHost(extension
, GetSiteInstanceForURL(url
), url
,
336 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
);
337 host
->CreateRenderViewSoon();
338 OnBackgroundHostCreated(host
);
342 ExtensionHost
* ProcessManager::GetBackgroundHostForExtension(
343 const std::string
& extension_id
) {
344 for (ExtensionHostSet::iterator iter
= background_hosts_
.begin();
345 iter
!= background_hosts_
.end(); ++iter
) {
346 ExtensionHost
* host
= *iter
;
347 if (host
->extension_id() == extension_id
)
353 std::set
<RenderViewHost
*> ProcessManager::GetRenderViewHostsForExtension(
354 const std::string
& extension_id
) {
355 std::set
<RenderViewHost
*> result
;
357 SiteInstance
* site_instance
= GetSiteInstanceForURL(
358 Extension::GetBaseURLFromExtensionId(extension_id
));
362 // Gather up all the views for that site.
363 for (ExtensionRenderViews::iterator view
= all_extension_views_
.begin();
364 view
!= all_extension_views_
.end(); ++view
) {
365 if (view
->first
->GetSiteInstance() == site_instance
)
366 result
.insert(view
->first
);
372 const Extension
* ProcessManager::GetExtensionForRenderViewHost(
373 RenderViewHost
* render_view_host
) {
374 if (!render_view_host
->GetSiteInstance())
377 return extension_registry_
->enabled_extensions().GetByID(
378 GetExtensionID(render_view_host
));
381 void ProcessManager::UnregisterRenderViewHost(
382 RenderViewHost
* render_view_host
) {
383 ExtensionRenderViews::iterator view
=
384 all_extension_views_
.find(render_view_host
);
385 if (view
== all_extension_views_
.end())
388 OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host
);
389 ViewType view_type
= view
->second
;
390 all_extension_views_
.erase(view
);
392 // Keepalive count, balanced in RegisterRenderViewHost.
393 if (view_type
!= VIEW_TYPE_INVALID
&&
394 view_type
!= VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
) {
395 const Extension
* extension
= GetExtensionForRenderViewHost(
398 DecrementLazyKeepaliveCount(extension
);
402 bool ProcessManager::RegisterRenderViewHost(RenderViewHost
* render_view_host
) {
403 const Extension
* extension
= GetExtensionForRenderViewHost(
408 WebContents
* web_contents
= WebContents::FromRenderViewHost(render_view_host
);
409 all_extension_views_
[render_view_host
] = GetViewType(web_contents
);
411 // Keep the lazy background page alive as long as any non-background-page
412 // extension views are visible. Keepalive count balanced in
413 // UnregisterRenderViewHost.
414 IncrementLazyKeepaliveCountForView(render_view_host
);
418 SiteInstance
* ProcessManager::GetSiteInstanceForURL(const GURL
& url
) {
419 return site_instance_
->GetRelatedSiteInstance(url
);
422 bool ProcessManager::IsBackgroundHostClosing(const std::string
& extension_id
) {
423 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
424 return (host
&& background_page_data_
[extension_id
].is_closing
);
427 int ProcessManager::GetLazyKeepaliveCount(const Extension
* extension
) {
428 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
431 return background_page_data_
[extension
->id()].lazy_keepalive_count
;
434 void ProcessManager::IncrementLazyKeepaliveCount(const Extension
* extension
) {
435 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
438 int& count
= background_page_data_
[extension
->id()].lazy_keepalive_count
;
440 OnLazyBackgroundPageActive(extension
->id());
443 void ProcessManager::DecrementLazyKeepaliveCount(const Extension
* extension
) {
444 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
446 DecrementLazyKeepaliveCount(extension
->id());
449 void ProcessManager::DecrementLazyKeepaliveCount(
450 const std::string
& extension_id
) {
451 int& count
= background_page_data_
[extension_id
].lazy_keepalive_count
;
453 !extension_registry_
->enabled_extensions().Contains(extension_id
));
455 // If we reach a zero keepalive count when the lazy background page is about
456 // to be closed, incrementing close_sequence_id will cancel the close
457 // sequence and cause the background page to linger. So check is_closing
458 // before initiating another close sequence.
459 if (--count
== 0 && !background_page_data_
[extension_id
].is_closing
) {
460 background_page_data_
[extension_id
].close_sequence_id
=
461 ++last_background_close_sequence_id_
;
462 base::MessageLoop::current()->PostDelayedTask(
464 base::Bind(&ProcessManager::OnLazyBackgroundPageIdle
,
465 weak_ptr_factory_
.GetWeakPtr(),
467 last_background_close_sequence_id_
),
468 event_page_idle_time_
);
472 void ProcessManager::IncrementLazyKeepaliveCountForView(
473 RenderViewHost
* render_view_host
) {
474 WebContents
* web_contents
=
475 WebContents::FromRenderViewHost(render_view_host
);
476 ViewType view_type
= GetViewType(web_contents
);
477 if (view_type
!= VIEW_TYPE_INVALID
&&
478 view_type
!= VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
) {
479 const Extension
* extension
= GetExtensionForRenderViewHost(
482 IncrementLazyKeepaliveCount(extension
);
486 // This implementation layers on top of the keepalive count. An impulse sets
487 // a per extension flag. On a regular interval that flag is checked. Changes
488 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
489 void ProcessManager::KeepaliveImpulse(const Extension
* extension
) {
490 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
493 BackgroundPageData
& bd
= background_page_data_
[extension
->id()];
495 if (!bd
.keepalive_impulse
) {
496 bd
.keepalive_impulse
= true;
497 if (!bd
.previous_keepalive_impulse
) {
498 IncrementLazyKeepaliveCount(extension
);
502 if (!keepalive_impulse_callback_for_testing_
.is_null()) {
503 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly
=
504 keepalive_impulse_callback_for_testing_
;
505 callback_may_clear_callbacks_reentrantly
.Run(extension
->id());
510 void ProcessManager::OnKeepaliveFromPlugin(int render_process_id
,
512 const std::string
& extension_id
) {
513 content::RenderFrameHost
* render_frame_host
=
514 content::RenderFrameHost::FromID(render_process_id
, render_frame_id
);
515 if (!render_frame_host
)
518 content::SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
522 BrowserContext
* browser_context
= site_instance
->GetBrowserContext();
523 const Extension
* extension
=
524 ExtensionRegistry::Get(browser_context
)->enabled_extensions().GetByID(
529 ProcessManager
* pm
= ExtensionSystem::Get(browser_context
)->process_manager();
533 pm
->KeepaliveImpulse(extension
);
536 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
537 // have been made for at least event_page_idle_time_. In the best case an
538 // impulse was made just before being cleared, and the decrement will occur
539 // event_page_idle_time_ later, causing a 2 * event_page_idle_time_ total time
540 // for extension to be shut down based on impulses. Worst case is an impulse
541 // just after a clear, adding one check cycle and resulting in 3x total time.
542 void ProcessManager::OnKeepaliveImpulseCheck() {
543 for (BackgroundPageDataMap::iterator i
= background_page_data_
.begin();
544 i
!= background_page_data_
.end();
546 if (i
->second
.previous_keepalive_impulse
&& !i
->second
.keepalive_impulse
) {
547 DecrementLazyKeepaliveCount(i
->first
);
548 if (!keepalive_impulse_decrement_callback_for_testing_
.is_null()) {
549 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly
=
550 keepalive_impulse_decrement_callback_for_testing_
;
551 callback_may_clear_callbacks_reentrantly
.Run(i
->first
);
555 i
->second
.previous_keepalive_impulse
= i
->second
.keepalive_impulse
;
556 i
->second
.keepalive_impulse
= false;
559 // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
560 // tests there will be no message loop. In that event don't schedule tasks.
561 if (base::MessageLoop::current()) {
562 base::MessageLoop::current()->PostDelayedTask(
564 base::Bind(&ProcessManager::OnKeepaliveImpulseCheck
,
565 weak_ptr_factory_
.GetWeakPtr()),
566 event_page_idle_time_
);
570 void ProcessManager::OnLazyBackgroundPageIdle(const std::string
& extension_id
,
571 uint64 sequence_id
) {
572 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
573 if (host
&& !background_page_data_
[extension_id
].is_closing
&&
574 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
575 // Tell the renderer we are about to close. This is a simple ping that the
576 // renderer will respond to. The purpose is to control sequencing: if the
577 // extension remains idle until the renderer responds with an ACK, then we
578 // know that the extension process is ready to shut down. If our
579 // close_sequence_id has already changed, then we would ignore the
580 // ShouldSuspendAck, so we don't send the ping.
581 host
->render_view_host()->Send(new ExtensionMsg_ShouldSuspend(
582 extension_id
, sequence_id
));
586 void ProcessManager::OnLazyBackgroundPageActive(
587 const std::string
& extension_id
) {
588 if (!background_page_data_
[extension_id
].is_closing
) {
589 // Cancel the current close sequence by changing the close_sequence_id,
590 // which causes us to ignore the next ShouldSuspendAck.
591 background_page_data_
[extension_id
].close_sequence_id
=
592 ++last_background_close_sequence_id_
;
596 void ProcessManager::OnShouldSuspendAck(const std::string
& extension_id
,
597 uint64 sequence_id
) {
598 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
600 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
601 host
->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id
));
605 void ProcessManager::OnSuspendAck(const std::string
& extension_id
) {
606 background_page_data_
[extension_id
].is_closing
= true;
607 uint64 sequence_id
= background_page_data_
[extension_id
].close_sequence_id
;
608 base::MessageLoop::current()->PostDelayedTask(
610 base::Bind(&ProcessManager::CloseLazyBackgroundPageNow
,
611 weak_ptr_factory_
.GetWeakPtr(), extension_id
, sequence_id
),
612 event_page_suspending_time_
);
615 void ProcessManager::CloseLazyBackgroundPageNow(const std::string
& extension_id
,
616 uint64 sequence_id
) {
617 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
619 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
620 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
622 CloseBackgroundHost(host
);
626 void ProcessManager::OnNetworkRequestStarted(
627 content::RenderFrameHost
* render_frame_host
) {
628 ExtensionHost
* host
= GetBackgroundHostForExtension(
629 GetExtensionIDFromFrame(render_frame_host
));
630 if (host
&& IsFrameInExtensionHost(host
, render_frame_host
))
631 IncrementLazyKeepaliveCount(host
->extension());
634 void ProcessManager::OnNetworkRequestDone(
635 content::RenderFrameHost
* render_frame_host
) {
636 ExtensionHost
* host
= GetBackgroundHostForExtension(
637 GetExtensionIDFromFrame(render_frame_host
));
638 if (host
&& IsFrameInExtensionHost(host
, render_frame_host
))
639 DecrementLazyKeepaliveCount(host
->extension());
642 void ProcessManager::CancelSuspend(const Extension
* extension
) {
643 bool& is_closing
= background_page_data_
[extension
->id()].is_closing
;
644 ExtensionHost
* host
= GetBackgroundHostForExtension(extension
->id());
645 if (host
&& is_closing
) {
647 host
->render_view_host()->Send(
648 new ExtensionMsg_CancelSuspend(extension
->id()));
649 // This increment / decrement is to simulate an instantaneous event. This
650 // has the effect of invalidating close_sequence_id, preventing any in
651 // progress closes from completing and starting a new close process if
653 IncrementLazyKeepaliveCount(extension
);
654 DecrementLazyKeepaliveCount(extension
);
658 void ProcessManager::CloseBackgroundHosts() {
659 for (ExtensionHostSet::iterator iter
= background_hosts_
.begin();
660 iter
!= background_hosts_
.end();) {
661 ExtensionHostSet::iterator current
= iter
++;
666 content::BrowserContext
* ProcessManager::GetBrowserContext() const {
667 return site_instance_
->GetBrowserContext();
670 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
671 const ImpulseCallbackForTesting
& callback
) {
672 keepalive_impulse_callback_for_testing_
= callback
;
675 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
676 const ImpulseCallbackForTesting
& callback
) {
677 keepalive_impulse_decrement_callback_for_testing_
= callback
;
680 void ProcessManager::Observe(int type
,
681 const content::NotificationSource
& source
,
682 const content::NotificationDetails
& details
) {
684 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
: {
685 // TODO(jamescook): Convert this to use ExtensionSystem::ready() instead
686 // of a notification.
687 MaybeCreateStartupBackgroundHosts();
691 case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
: {
692 BrowserContext
* context
= content::Source
<BrowserContext
>(source
).ptr();
693 ExtensionSystem
* system
= ExtensionSystem::Get(context
);
694 if (system
->ready().is_signaled()) {
695 // The extension system is ready, so create the background host.
696 const Extension
* extension
=
697 content::Details
<const Extension
>(details
).ptr();
698 CreateBackgroundHostForExtensionLoad(this, extension
);
703 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
: {
704 const Extension
* extension
=
705 content::Details
<UnloadedExtensionInfo
>(details
)->extension
;
706 for (ExtensionHostSet::iterator iter
= background_hosts_
.begin();
707 iter
!= background_hosts_
.end(); ++iter
) {
708 ExtensionHost
* host
= *iter
;
709 if (host
->extension_id() == extension
->id()) {
710 CloseBackgroundHost(host
);
714 UnregisterExtension(extension
->id());
718 case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
: {
719 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
720 if (background_hosts_
.erase(host
)) {
721 ClearBackgroundPageData(host
->extension()->id());
722 background_page_data_
[host
->extension()->id()].since_suspended
.reset(
723 new base::ElapsedTimer());
728 case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE
: {
729 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
730 if (host
->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
) {
731 CloseBackgroundHost(host
);
736 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED
: {
737 // We get this notification both for new WebContents and when one
738 // has its RenderViewHost replaced (e.g. when a user does a cross-site
739 // navigation away from an extension URL). For the replaced case, we must
740 // unregister the old RVH so it doesn't count as an active view that would
741 // keep the event page alive.
742 WebContents
* contents
= content::Source
<WebContents
>(source
).ptr();
743 if (contents
->GetBrowserContext() != GetBrowserContext())
746 typedef std::pair
<RenderViewHost
*, RenderViewHost
*> RVHPair
;
747 RVHPair
* switched_details
= content::Details
<RVHPair
>(details
).ptr();
748 if (switched_details
->first
)
749 UnregisterRenderViewHost(switched_details
->first
);
751 // The above will unregister a RVH when it gets swapped out with a new
752 // one. However we need to watch the WebContents to know when a RVH is
753 // deleted because the WebContents has gone away.
754 if (RegisterRenderViewHost(switched_details
->second
)) {
755 RenderViewHostDestructionObserver::CreateForWebContents(contents
);
760 case content::NOTIFICATION_WEB_CONTENTS_CONNECTED
: {
761 WebContents
* contents
= content::Source
<WebContents
>(source
).ptr();
762 if (contents
->GetBrowserContext() != GetBrowserContext())
764 const Extension
* extension
= GetExtensionForRenderViewHost(
765 contents
->GetRenderViewHost());
769 // RegisterRenderViewHost is called too early (before the process is
770 // available), so we need to wait until now to notify.
771 content::NotificationService::current()->Notify(
772 extensions::NOTIFICATION_EXTENSION_VIEW_REGISTERED
,
773 content::Source
<BrowserContext
>(GetBrowserContext()),
774 content::Details
<RenderViewHost
>(contents
->GetRenderViewHost()));
783 void ProcessManager::OnDevToolsStateChanged(
784 content::DevToolsAgentHost
* agent_host
,
786 WebContents
* web_contents
= agent_host
->GetWebContents();
787 // Ignore unrelated notifications.
788 if (!web_contents
|| web_contents
->GetBrowserContext() != GetBrowserContext())
790 if (GetViewType(web_contents
) != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
)
792 const Extension
* extension
=
793 GetExtensionForRenderViewHost(web_contents
->GetRenderViewHost());
797 // Keep the lazy background page alive while it's being inspected.
798 CancelSuspend(extension
);
799 IncrementLazyKeepaliveCount(extension
);
801 DecrementLazyKeepaliveCount(extension
);
805 void ProcessManager::MaybeCreateStartupBackgroundHosts() {
806 if (startup_background_hosts_created_
)
809 // The embedder might disallow background pages entirely.
810 ProcessManagerDelegate
* delegate
=
811 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
812 if (delegate
&& !delegate
->IsBackgroundPageAllowed(GetBrowserContext()))
815 // The embedder might want to defer background page loading. For example,
816 // Chrome defers background page loading when it is launched to show the app
817 // list, then triggers a load later when a browser window opens.
819 delegate
->DeferCreatingStartupBackgroundHosts(GetBrowserContext()))
822 CreateStartupBackgroundHosts();
823 startup_background_hosts_created_
= true;
825 // Background pages should only be loaded once. To prevent any further loads
826 // occurring, we remove the notification listeners.
827 BrowserContext
* original_context
=
828 ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
829 if (registrar_
.IsRegistered(
831 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
832 content::Source
<BrowserContext
>(original_context
))) {
833 registrar_
.Remove(this,
834 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
835 content::Source
<BrowserContext
>(original_context
));
839 void ProcessManager::CreateStartupBackgroundHosts() {
840 DCHECK(!startup_background_hosts_created_
);
841 const ExtensionSet
& enabled_extensions
=
842 extension_registry_
->enabled_extensions();
843 for (ExtensionSet::const_iterator extension
= enabled_extensions
.begin();
844 extension
!= enabled_extensions
.end();
846 CreateBackgroundHostForExtensionLoad(this, extension
->get());
848 FOR_EACH_OBSERVER(ProcessManagerObserver
,
850 OnBackgroundHostStartup(extension
->get()));
854 void ProcessManager::OnBackgroundHostCreated(ExtensionHost
* host
) {
855 DCHECK_EQ(GetBrowserContext(), host
->browser_context());
856 background_hosts_
.insert(host
);
858 if (BackgroundInfo::HasLazyBackgroundPage(host
->extension())) {
859 linked_ptr
<base::ElapsedTimer
> since_suspended(
860 background_page_data_
[host
->extension()->id()].
861 since_suspended
.release());
862 if (since_suspended
.get()) {
863 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
864 since_suspended
->Elapsed());
869 void ProcessManager::CloseBackgroundHost(ExtensionHost
* host
) {
870 CHECK(host
->extension_host_type() ==
871 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
);
873 // |host| should deregister itself from our structures.
874 CHECK(background_hosts_
.find(host
) == background_hosts_
.end());
877 void ProcessManager::UnregisterExtension(const std::string
& extension_id
) {
878 // The lazy_keepalive_count may be greater than zero at this point because
879 // RenderViewHosts are still alive. During extension reloading, they will
880 // decrement the lazy_keepalive_count to negative for the new extension
881 // instance when they are destroyed. Since we are erasing the background page
882 // data for the unloaded extension, unregister the RenderViewHosts too.
883 BrowserContext
* context
= GetBrowserContext();
884 for (ExtensionRenderViews::iterator it
= all_extension_views_
.begin();
885 it
!= all_extension_views_
.end(); ) {
886 if (GetExtensionID(it
->first
) == extension_id
) {
887 OnRenderViewHostUnregistered(context
, it
->first
);
888 all_extension_views_
.erase(it
++);
894 background_page_data_
.erase(extension_id
);
897 void ProcessManager::ClearBackgroundPageData(const std::string
& extension_id
) {
898 background_page_data_
.erase(extension_id
);
900 // Re-register all RenderViews for this extension. We do this to restore
901 // the lazy_keepalive_count (if any) to properly reflect the number of open
903 for (ExtensionRenderViews::const_iterator it
= all_extension_views_
.begin();
904 it
!= all_extension_views_
.end(); ++it
) {
905 if (GetExtensionID(it
->first
) == extension_id
)
906 IncrementLazyKeepaliveCountForView(it
->first
);
911 // IncognitoProcessManager
914 IncognitoProcessManager::IncognitoProcessManager(
915 BrowserContext
* incognito_context
,
916 BrowserContext
* original_context
,
917 ProcessManager
* original_manager
,
918 ExtensionRegistry
* extension_registry
)
919 : ProcessManager(incognito_context
, original_context
, extension_registry
),
920 original_manager_(original_manager
) {
921 DCHECK(incognito_context
->IsOffTheRecord());
923 // The original profile will have its own ProcessManager to
924 // load the background pages of the spanning extensions. This process
925 // manager need only worry about the split mode extensions, which is handled
926 // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler.
927 registrar_
.Remove(this,
928 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
929 content::Source
<BrowserContext
>(original_context
));
932 bool IncognitoProcessManager::CreateBackgroundHost(const Extension
* extension
,
934 if (IncognitoInfo::IsSplitMode(extension
)) {
935 if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
936 extension
->id(), GetBrowserContext()))
937 return ProcessManager::CreateBackgroundHost(extension
, url
);
939 // Do nothing. If an extension is spanning, then its original-profile
940 // background page is shared with incognito, so we don't create another.
945 SiteInstance
* IncognitoProcessManager::GetSiteInstanceForURL(const GURL
& url
) {
946 const Extension
* extension
=
947 extension_registry_
->enabled_extensions().GetExtensionOrAppByURL(url
);
948 if (extension
&& !IncognitoInfo::IsSplitMode(extension
))
949 return original_manager_
->GetSiteInstanceForURL(url
);
951 return ProcessManager::GetSiteInstanceForURL(url
);
954 } // namespace extensions