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_factory.h"
37 #include "extensions/browser/process_manager_observer.h"
38 #include "extensions/browser/view_type_utils.h"
39 #include "extensions/common/constants.h"
40 #include "extensions/common/extension.h"
41 #include "extensions/common/extension_messages.h"
42 #include "extensions/common/manifest_handlers/background_info.h"
43 #include "extensions/common/manifest_handlers/incognito_info.h"
44 #include "extensions/common/one_shot_event.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 // The time to delay between an extension becoming idle and
62 // sending a ShouldSuspend message.
63 // Note: Must be sufficiently larger (e.g. 2x) than
64 // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
65 unsigned g_event_page_idle_time_msec
= 10000;
67 // The time to delay between sending a ShouldSuspend message and
68 // sending a Suspend message.
69 unsigned g_event_page_suspending_time_msec
= 5000;
71 std::string
GetExtensionID(RenderViewHost
* render_view_host
) {
72 // This works for both apps and extensions because the site has been
73 // normalized to the extension URL for hosted apps.
74 content::SiteInstance
* site_instance
= render_view_host
->GetSiteInstance();
78 const GURL
& site_url
= site_instance
->GetSiteURL();
80 if (!site_url
.SchemeIs(kExtensionScheme
) &&
81 !site_url
.SchemeIs(content::kGuestScheme
))
84 return site_url
.host();
87 std::string
GetExtensionIDFromFrame(
88 content::RenderFrameHost
* render_frame_host
) {
89 // This works for both apps and extensions because the site has been
90 // normalized to the extension URL for apps.
91 if (!render_frame_host
->GetSiteInstance())
94 return render_frame_host
->GetSiteInstance()->GetSiteURL().host();
97 bool IsFrameInExtensionHost(ExtensionHost
* extension_host
,
98 content::RenderFrameHost
* render_frame_host
) {
99 return WebContents::FromRenderFrameHost(render_frame_host
) ==
100 extension_host
->host_contents();
103 void OnRenderViewHostUnregistered(BrowserContext
* context
,
104 RenderViewHost
* render_view_host
) {
105 content::NotificationService::current()->Notify(
106 extensions::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED
,
107 content::Source
<BrowserContext
>(context
),
108 content::Details
<RenderViewHost
>(render_view_host
));
111 // Incognito profiles use this process manager. It is mostly a shim that decides
112 // whether to fall back on the original profile's ProcessManager based
113 // on whether a given extension uses "split" or "spanning" incognito behavior.
114 class IncognitoProcessManager
: public ProcessManager
{
116 IncognitoProcessManager(BrowserContext
* incognito_context
,
117 BrowserContext
* original_context
,
118 ExtensionRegistry
* extension_registry
);
119 ~IncognitoProcessManager() override
{}
120 bool CreateBackgroundHost(const Extension
* extension
,
121 const GURL
& url
) override
;
122 SiteInstance
* GetSiteInstanceForURL(const GURL
& url
) override
;
125 DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager
);
128 static void CreateBackgroundHostForExtensionLoad(
129 ProcessManager
* manager
, const Extension
* extension
) {
130 DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
131 if (BackgroundInfo::HasPersistentBackgroundPage(extension
))
132 manager
->CreateBackgroundHost(extension
,
133 BackgroundInfo::GetBackgroundURL(extension
));
138 class RenderViewHostDestructionObserver
139 : public content::WebContentsObserver
,
140 public content::WebContentsUserData
<RenderViewHostDestructionObserver
> {
142 ~RenderViewHostDestructionObserver() override
{}
145 explicit RenderViewHostDestructionObserver(WebContents
* web_contents
)
146 : WebContentsObserver(web_contents
) {
147 BrowserContext
* context
= web_contents
->GetBrowserContext();
148 process_manager_
= ProcessManager::Get(context
);
151 friend class content::WebContentsUserData
<RenderViewHostDestructionObserver
>;
153 // content::WebContentsObserver overrides.
154 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
155 process_manager_
->UnregisterRenderViewHost(render_view_host
);
158 ProcessManager
* process_manager_
;
160 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver
);
163 struct ProcessManager::BackgroundPageData
{
164 // The count of things keeping the lazy background page alive.
165 int lazy_keepalive_count
;
167 // Tracks if an impulse event has occured since the last polling check.
168 bool keepalive_impulse
;
169 bool previous_keepalive_impulse
;
171 // True if the page responded to the ShouldSuspend message and is currently
172 // dispatching the suspend event. During this time any events that arrive will
173 // cancel the suspend process and an onSuspendCanceled event will be
174 // dispatched to the page.
177 // Stores the value of the incremented
178 // ProcessManager::last_background_close_sequence_id_ whenever the extension
179 // is active. A copy of the ID is also passed in the callbacks and IPC
180 // messages leading up to CloseLazyBackgroundPageNow. The process is aborted
181 // if the IDs ever differ due to new activity.
182 uint64 close_sequence_id
;
184 // Keeps track of when this page was last suspended. Used for perf metrics.
185 linked_ptr
<base::ElapsedTimer
> since_suspended
;
188 : lazy_keepalive_count(0),
189 keepalive_impulse(false),
190 previous_keepalive_impulse(false),
192 close_sequence_id(0) {}
200 ProcessManager
* ProcessManager::Get(BrowserContext
* context
) {
201 return ProcessManagerFactory::GetForBrowserContext(context
);
205 ProcessManager
* ProcessManager::Create(BrowserContext
* context
) {
206 ExtensionRegistry
* extension_registry
= ExtensionRegistry::Get(context
);
207 ExtensionsBrowserClient
* client
= ExtensionsBrowserClient::Get();
208 if (client
->IsGuestSession(context
)) {
209 // In the guest session, there is a single off-the-record context. Unlike
210 // a regular incognito mode, background pages of extensions must be
211 // created regardless of whether extensions use "spanning" or "split"
212 // incognito behavior.
213 BrowserContext
* original_context
= client
->GetOriginalContext(context
);
214 return new ProcessManager(context
, original_context
, extension_registry
);
217 if (context
->IsOffTheRecord()) {
218 BrowserContext
* original_context
= client
->GetOriginalContext(context
);
219 return new IncognitoProcessManager(
220 context
, original_context
, extension_registry
);
223 return new ProcessManager(context
, context
, extension_registry
);
227 ProcessManager
* ProcessManager::CreateForTesting(
228 BrowserContext
* context
,
229 ExtensionRegistry
* extension_registry
) {
230 DCHECK(!context
->IsOffTheRecord());
231 return new ProcessManager(context
, context
, extension_registry
);
235 ProcessManager
* ProcessManager::CreateIncognitoForTesting(
236 BrowserContext
* incognito_context
,
237 BrowserContext
* original_context
,
238 ExtensionRegistry
* extension_registry
) {
239 DCHECK(incognito_context
->IsOffTheRecord());
240 DCHECK(!original_context
->IsOffTheRecord());
241 return new IncognitoProcessManager(incognito_context
,
246 ProcessManager::ProcessManager(BrowserContext
* context
,
247 BrowserContext
* original_context
,
248 ExtensionRegistry
* extension_registry
)
249 : site_instance_(SiteInstance::Create(context
)),
250 extension_registry_(extension_registry
),
251 startup_background_hosts_created_(false),
252 devtools_callback_(base::Bind(&ProcessManager::OnDevToolsStateChanged
,
253 base::Unretained(this))),
254 last_background_close_sequence_id_(0),
255 weak_ptr_factory_(this) {
256 // ExtensionRegistry is shared between incognito and regular contexts.
257 DCHECK_EQ(original_context
, extension_registry_
->browser_context());
259 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
260 content::Source
<BrowserContext
>(original_context
));
262 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
,
263 content::Source
<BrowserContext
>(original_context
));
265 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
,
266 content::Source
<BrowserContext
>(original_context
));
268 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
,
269 content::Source
<BrowserContext
>(context
));
271 extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE
,
272 content::Source
<BrowserContext
>(context
));
273 registrar_
.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
274 content::NotificationService::AllSources());
275 registrar_
.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED
,
276 content::NotificationService::AllSources());
278 content::DevToolsAgentHost::AddAgentStateCallback(devtools_callback_
);
280 OnKeepaliveImpulseCheck();
283 ProcessManager::~ProcessManager() {
284 CloseBackgroundHosts();
285 DCHECK(background_hosts_
.empty());
286 content::DevToolsAgentHost::RemoveAgentStateCallback(devtools_callback_
);
289 const ProcessManager::ViewSet
ProcessManager::GetAllViews() const {
291 for (ExtensionRenderViews::const_iterator iter
=
292 all_extension_views_
.begin();
293 iter
!= all_extension_views_
.end(); ++iter
) {
294 result
.insert(iter
->first
);
299 void ProcessManager::AddObserver(ProcessManagerObserver
* observer
) {
300 observer_list_
.AddObserver(observer
);
303 void ProcessManager::RemoveObserver(ProcessManagerObserver
* observer
) {
304 observer_list_
.RemoveObserver(observer
);
307 bool ProcessManager::CreateBackgroundHost(const Extension
* extension
,
309 // Hosted apps are taken care of from BackgroundContentsService. Ignore them
311 if (extension
->is_hosted_app())
314 // Don't create hosts if the embedder doesn't allow it.
315 ProcessManagerDelegate
* delegate
=
316 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
317 if (delegate
&& !delegate
->IsBackgroundPageAllowed(GetBrowserContext()))
320 // Don't create multiple background hosts for an extension.
321 if (GetBackgroundHostForExtension(extension
->id()))
322 return true; // TODO(kalman): return false here? It might break things...
324 ExtensionHost
* host
=
325 new ExtensionHost(extension
, GetSiteInstanceForURL(url
), url
,
326 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
);
327 host
->CreateRenderViewSoon();
328 OnBackgroundHostCreated(host
);
332 ExtensionHost
* ProcessManager::GetBackgroundHostForExtension(
333 const std::string
& extension_id
) {
334 for (ExtensionHostSet::iterator iter
= background_hosts_
.begin();
335 iter
!= background_hosts_
.end(); ++iter
) {
336 ExtensionHost
* host
= *iter
;
337 if (host
->extension_id() == extension_id
)
343 std::set
<RenderViewHost
*> ProcessManager::GetRenderViewHostsForExtension(
344 const std::string
& extension_id
) {
345 std::set
<RenderViewHost
*> result
;
347 SiteInstance
* site_instance
= GetSiteInstanceForURL(
348 Extension::GetBaseURLFromExtensionId(extension_id
));
352 // Gather up all the views for that site.
353 for (ExtensionRenderViews::iterator view
= all_extension_views_
.begin();
354 view
!= all_extension_views_
.end(); ++view
) {
355 if (view
->first
->GetSiteInstance() == site_instance
)
356 result
.insert(view
->first
);
362 const Extension
* ProcessManager::GetExtensionForRenderViewHost(
363 RenderViewHost
* render_view_host
) {
364 if (!render_view_host
->GetSiteInstance())
367 return extension_registry_
->enabled_extensions().GetByID(
368 GetExtensionID(render_view_host
));
371 void ProcessManager::UnregisterRenderViewHost(
372 RenderViewHost
* render_view_host
) {
373 ExtensionRenderViews::iterator view
=
374 all_extension_views_
.find(render_view_host
);
375 if (view
== all_extension_views_
.end())
378 OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host
);
379 ViewType view_type
= view
->second
;
380 all_extension_views_
.erase(view
);
382 // Keepalive count, balanced in RegisterRenderViewHost.
383 if (view_type
!= VIEW_TYPE_INVALID
&&
384 view_type
!= VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
) {
385 const Extension
* extension
= GetExtensionForRenderViewHost(
388 DecrementLazyKeepaliveCount(extension
);
392 bool ProcessManager::RegisterRenderViewHost(RenderViewHost
* render_view_host
) {
393 const Extension
* extension
= GetExtensionForRenderViewHost(
398 WebContents
* web_contents
= WebContents::FromRenderViewHost(render_view_host
);
399 all_extension_views_
[render_view_host
] = GetViewType(web_contents
);
401 // Keep the lazy background page alive as long as any non-background-page
402 // extension views are visible. Keepalive count balanced in
403 // UnregisterRenderViewHost.
404 IncrementLazyKeepaliveCountForView(render_view_host
);
408 SiteInstance
* ProcessManager::GetSiteInstanceForURL(const GURL
& url
) {
409 return site_instance_
->GetRelatedSiteInstance(url
);
412 bool ProcessManager::IsBackgroundHostClosing(const std::string
& extension_id
) {
413 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
414 return (host
&& background_page_data_
[extension_id
].is_closing
);
417 int ProcessManager::GetLazyKeepaliveCount(const Extension
* extension
) {
418 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
421 return background_page_data_
[extension
->id()].lazy_keepalive_count
;
424 void ProcessManager::IncrementLazyKeepaliveCount(const Extension
* extension
) {
425 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
428 int& count
= background_page_data_
[extension
->id()].lazy_keepalive_count
;
430 OnLazyBackgroundPageActive(extension
->id());
433 void ProcessManager::DecrementLazyKeepaliveCount(const Extension
* extension
) {
434 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
436 DecrementLazyKeepaliveCount(extension
->id());
439 void ProcessManager::DecrementLazyKeepaliveCount(
440 const std::string
& extension_id
) {
441 int& count
= background_page_data_
[extension_id
].lazy_keepalive_count
;
443 !extension_registry_
->enabled_extensions().Contains(extension_id
));
445 // If we reach a zero keepalive count when the lazy background page is about
446 // to be closed, incrementing close_sequence_id will cancel the close
447 // sequence and cause the background page to linger. So check is_closing
448 // before initiating another close sequence.
449 if (--count
== 0 && !background_page_data_
[extension_id
].is_closing
) {
450 background_page_data_
[extension_id
].close_sequence_id
=
451 ++last_background_close_sequence_id_
;
452 base::MessageLoop::current()->PostDelayedTask(
454 base::Bind(&ProcessManager::OnLazyBackgroundPageIdle
,
455 weak_ptr_factory_
.GetWeakPtr(),
457 last_background_close_sequence_id_
),
458 base::TimeDelta::FromMilliseconds(g_event_page_idle_time_msec
));
462 void ProcessManager::IncrementLazyKeepaliveCountForView(
463 RenderViewHost
* render_view_host
) {
464 WebContents
* web_contents
=
465 WebContents::FromRenderViewHost(render_view_host
);
466 ViewType view_type
= GetViewType(web_contents
);
467 if (view_type
!= VIEW_TYPE_INVALID
&&
468 view_type
!= VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
) {
469 const Extension
* extension
= GetExtensionForRenderViewHost(
472 IncrementLazyKeepaliveCount(extension
);
476 // This implementation layers on top of the keepalive count. An impulse sets
477 // a per extension flag. On a regular interval that flag is checked. Changes
478 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
479 void ProcessManager::KeepaliveImpulse(const Extension
* extension
) {
480 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
483 BackgroundPageData
& bd
= background_page_data_
[extension
->id()];
485 if (!bd
.keepalive_impulse
) {
486 bd
.keepalive_impulse
= true;
487 if (!bd
.previous_keepalive_impulse
) {
488 IncrementLazyKeepaliveCount(extension
);
492 if (!keepalive_impulse_callback_for_testing_
.is_null()) {
493 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly
=
494 keepalive_impulse_callback_for_testing_
;
495 callback_may_clear_callbacks_reentrantly
.Run(extension
->id());
500 void ProcessManager::OnKeepaliveFromPlugin(int render_process_id
,
502 const std::string
& extension_id
) {
503 content::RenderFrameHost
* render_frame_host
=
504 content::RenderFrameHost::FromID(render_process_id
, render_frame_id
);
505 if (!render_frame_host
)
508 content::SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
512 BrowserContext
* browser_context
= site_instance
->GetBrowserContext();
513 const Extension
* extension
=
514 ExtensionRegistry::Get(browser_context
)->enabled_extensions().GetByID(
519 ProcessManager::Get(browser_context
)->KeepaliveImpulse(extension
);
522 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
523 // have been made for at least g_event_page_idle_time_msec. In the best case an
524 // impulse was made just before being cleared, and the decrement will occur
525 // g_event_page_idle_time_msec later, causing a 2 * g_event_page_idle_time_msec
526 // total time for extension to be shut down based on impulses. Worst case is
527 // an impulse just after a clear, adding one check cycle and resulting in 3x
529 void ProcessManager::OnKeepaliveImpulseCheck() {
530 for (BackgroundPageDataMap::iterator i
= background_page_data_
.begin();
531 i
!= background_page_data_
.end();
533 if (i
->second
.previous_keepalive_impulse
&& !i
->second
.keepalive_impulse
) {
534 DecrementLazyKeepaliveCount(i
->first
);
535 if (!keepalive_impulse_decrement_callback_for_testing_
.is_null()) {
536 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly
=
537 keepalive_impulse_decrement_callback_for_testing_
;
538 callback_may_clear_callbacks_reentrantly
.Run(i
->first
);
542 i
->second
.previous_keepalive_impulse
= i
->second
.keepalive_impulse
;
543 i
->second
.keepalive_impulse
= false;
546 // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
547 // tests there will be no message loop. In that event don't schedule tasks.
548 if (base::MessageLoop::current()) {
549 base::MessageLoop::current()->PostDelayedTask(
551 base::Bind(&ProcessManager::OnKeepaliveImpulseCheck
,
552 weak_ptr_factory_
.GetWeakPtr()),
553 base::TimeDelta::FromMilliseconds(g_event_page_idle_time_msec
));
557 void ProcessManager::OnLazyBackgroundPageIdle(const std::string
& extension_id
,
558 uint64 sequence_id
) {
559 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
560 if (host
&& !background_page_data_
[extension_id
].is_closing
&&
561 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
562 // Tell the renderer we are about to close. This is a simple ping that the
563 // renderer will respond to. The purpose is to control sequencing: if the
564 // extension remains idle until the renderer responds with an ACK, then we
565 // know that the extension process is ready to shut down. If our
566 // close_sequence_id has already changed, then we would ignore the
567 // ShouldSuspendAck, so we don't send the ping.
568 host
->render_view_host()->Send(new ExtensionMsg_ShouldSuspend(
569 extension_id
, sequence_id
));
573 void ProcessManager::OnLazyBackgroundPageActive(
574 const std::string
& extension_id
) {
575 if (!background_page_data_
[extension_id
].is_closing
) {
576 // Cancel the current close sequence by changing the close_sequence_id,
577 // which causes us to ignore the next ShouldSuspendAck.
578 background_page_data_
[extension_id
].close_sequence_id
=
579 ++last_background_close_sequence_id_
;
583 void ProcessManager::OnShouldSuspendAck(const std::string
& extension_id
,
584 uint64 sequence_id
) {
585 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
587 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
588 host
->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id
));
592 void ProcessManager::OnSuspendAck(const std::string
& extension_id
) {
593 background_page_data_
[extension_id
].is_closing
= true;
594 uint64 sequence_id
= background_page_data_
[extension_id
].close_sequence_id
;
595 base::MessageLoop::current()->PostDelayedTask(
597 base::Bind(&ProcessManager::CloseLazyBackgroundPageNow
,
598 weak_ptr_factory_
.GetWeakPtr(),
601 base::TimeDelta::FromMilliseconds(g_event_page_suspending_time_msec
));
604 void ProcessManager::CloseLazyBackgroundPageNow(const std::string
& extension_id
,
605 uint64 sequence_id
) {
606 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
608 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
609 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
611 CloseBackgroundHost(host
);
615 void ProcessManager::OnNetworkRequestStarted(
616 content::RenderFrameHost
* render_frame_host
) {
617 ExtensionHost
* host
= GetBackgroundHostForExtension(
618 GetExtensionIDFromFrame(render_frame_host
));
619 if (host
&& IsFrameInExtensionHost(host
, render_frame_host
))
620 IncrementLazyKeepaliveCount(host
->extension());
623 void ProcessManager::OnNetworkRequestDone(
624 content::RenderFrameHost
* render_frame_host
) {
625 ExtensionHost
* host
= GetBackgroundHostForExtension(
626 GetExtensionIDFromFrame(render_frame_host
));
627 if (host
&& IsFrameInExtensionHost(host
, render_frame_host
))
628 DecrementLazyKeepaliveCount(host
->extension());
631 void ProcessManager::CancelSuspend(const Extension
* extension
) {
632 bool& is_closing
= background_page_data_
[extension
->id()].is_closing
;
633 ExtensionHost
* host
= GetBackgroundHostForExtension(extension
->id());
634 if (host
&& is_closing
) {
636 host
->render_view_host()->Send(
637 new ExtensionMsg_CancelSuspend(extension
->id()));
638 // This increment / decrement is to simulate an instantaneous event. This
639 // has the effect of invalidating close_sequence_id, preventing any in
640 // progress closes from completing and starting a new close process if
642 IncrementLazyKeepaliveCount(extension
);
643 DecrementLazyKeepaliveCount(extension
);
647 void ProcessManager::CloseBackgroundHosts() {
648 for (ExtensionHostSet::iterator iter
= background_hosts_
.begin();
649 iter
!= background_hosts_
.end();) {
650 ExtensionHostSet::iterator current
= iter
++;
655 content::BrowserContext
* ProcessManager::GetBrowserContext() const {
656 return site_instance_
->GetBrowserContext();
659 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
660 const ImpulseCallbackForTesting
& callback
) {
661 keepalive_impulse_callback_for_testing_
= callback
;
664 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
665 const ImpulseCallbackForTesting
& callback
) {
666 keepalive_impulse_decrement_callback_for_testing_
= callback
;
670 void ProcessManager::SetEventPageIdleTimeForTesting(unsigned idle_time_msec
) {
671 CHECK_GT(idle_time_msec
, 0u); // OnKeepaliveImpulseCheck requires non zero.
672 g_event_page_idle_time_msec
= idle_time_msec
;
676 void ProcessManager::SetEventPageSuspendingTimeForTesting(
677 unsigned suspending_time_msec
) {
678 g_event_page_suspending_time_msec
= suspending_time_msec
;
681 void ProcessManager::Observe(int type
,
682 const content::NotificationSource
& source
,
683 const content::NotificationDetails
& details
) {
685 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
: {
686 // TODO(jamescook): Convert this to use ExtensionSystem::ready() instead
687 // of a notification.
688 MaybeCreateStartupBackgroundHosts();
692 case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
: {
693 BrowserContext
* context
= content::Source
<BrowserContext
>(source
).ptr();
694 ExtensionSystem
* system
= ExtensionSystem::Get(context
);
695 if (system
->ready().is_signaled()) {
696 // The extension system is ready, so create the background host.
697 const Extension
* extension
=
698 content::Details
<const Extension
>(details
).ptr();
699 CreateBackgroundHostForExtensionLoad(this, extension
);
704 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
: {
705 const Extension
* extension
=
706 content::Details
<UnloadedExtensionInfo
>(details
)->extension
;
707 for (ExtensionHostSet::iterator iter
= background_hosts_
.begin();
708 iter
!= background_hosts_
.end(); ++iter
) {
709 ExtensionHost
* host
= *iter
;
710 if (host
->extension_id() == extension
->id()) {
711 CloseBackgroundHost(host
);
715 UnregisterExtension(extension
->id());
719 case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
: {
720 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
721 if (background_hosts_
.erase(host
)) {
722 ClearBackgroundPageData(host
->extension()->id());
723 background_page_data_
[host
->extension()->id()].since_suspended
.reset(
724 new base::ElapsedTimer());
729 case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE
: {
730 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
731 if (host
->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
) {
732 CloseBackgroundHost(host
);
737 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED
: {
738 // We get this notification both for new WebContents and when one
739 // has its RenderViewHost replaced (e.g. when a user does a cross-site
740 // navigation away from an extension URL). For the replaced case, we must
741 // unregister the old RVH so it doesn't count as an active view that would
742 // keep the event page alive.
743 WebContents
* contents
= content::Source
<WebContents
>(source
).ptr();
744 if (contents
->GetBrowserContext() != GetBrowserContext())
747 typedef std::pair
<RenderViewHost
*, RenderViewHost
*> RVHPair
;
748 RVHPair
* switched_details
= content::Details
<RVHPair
>(details
).ptr();
749 if (switched_details
->first
)
750 UnregisterRenderViewHost(switched_details
->first
);
752 // The above will unregister a RVH when it gets swapped out with a new
753 // one. However we need to watch the WebContents to know when a RVH is
754 // deleted because the WebContents has gone away.
755 if (RegisterRenderViewHost(switched_details
->second
)) {
756 RenderViewHostDestructionObserver::CreateForWebContents(contents
);
761 case content::NOTIFICATION_WEB_CONTENTS_CONNECTED
: {
762 WebContents
* contents
= content::Source
<WebContents
>(source
).ptr();
763 if (contents
->GetBrowserContext() != GetBrowserContext())
765 const Extension
* extension
= GetExtensionForRenderViewHost(
766 contents
->GetRenderViewHost());
770 // RegisterRenderViewHost is called too early (before the process is
771 // available), so we need to wait until now to notify.
772 content::NotificationService::current()->Notify(
773 extensions::NOTIFICATION_EXTENSION_VIEW_REGISTERED
,
774 content::Source
<BrowserContext
>(GetBrowserContext()),
775 content::Details
<RenderViewHost
>(contents
->GetRenderViewHost()));
784 void ProcessManager::OnDevToolsStateChanged(
785 content::DevToolsAgentHost
* agent_host
,
787 WebContents
* web_contents
= agent_host
->GetWebContents();
788 // Ignore unrelated notifications.
789 if (!web_contents
|| web_contents
->GetBrowserContext() != GetBrowserContext())
791 if (GetViewType(web_contents
) != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
)
793 const Extension
* extension
=
794 GetExtensionForRenderViewHost(web_contents
->GetRenderViewHost());
798 // Keep the lazy background page alive while it's being inspected.
799 CancelSuspend(extension
);
800 IncrementLazyKeepaliveCount(extension
);
802 DecrementLazyKeepaliveCount(extension
);
806 void ProcessManager::MaybeCreateStartupBackgroundHosts() {
807 if (startup_background_hosts_created_
)
810 // The embedder might disallow background pages entirely.
811 ProcessManagerDelegate
* delegate
=
812 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
813 if (delegate
&& !delegate
->IsBackgroundPageAllowed(GetBrowserContext()))
816 // The embedder might want to defer background page loading. For example,
817 // Chrome defers background page loading when it is launched to show the app
818 // list, then triggers a load later when a browser window opens.
820 delegate
->DeferCreatingStartupBackgroundHosts(GetBrowserContext()))
823 CreateStartupBackgroundHosts();
824 startup_background_hosts_created_
= true;
826 // Background pages should only be loaded once. To prevent any further loads
827 // occurring, we remove the notification listeners.
828 BrowserContext
* original_context
=
829 ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
830 if (registrar_
.IsRegistered(
832 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
833 content::Source
<BrowserContext
>(original_context
))) {
834 registrar_
.Remove(this,
835 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
836 content::Source
<BrowserContext
>(original_context
));
840 void ProcessManager::CreateStartupBackgroundHosts() {
841 DCHECK(!startup_background_hosts_created_
);
842 const ExtensionSet
& enabled_extensions
=
843 extension_registry_
->enabled_extensions();
844 for (ExtensionSet::const_iterator extension
= enabled_extensions
.begin();
845 extension
!= enabled_extensions
.end();
847 CreateBackgroundHostForExtensionLoad(this, extension
->get());
849 FOR_EACH_OBSERVER(ProcessManagerObserver
,
851 OnBackgroundHostStartup(extension
->get()));
855 void ProcessManager::OnBackgroundHostCreated(ExtensionHost
* host
) {
856 DCHECK_EQ(GetBrowserContext(), host
->browser_context());
857 background_hosts_
.insert(host
);
859 if (BackgroundInfo::HasLazyBackgroundPage(host
->extension())) {
860 linked_ptr
<base::ElapsedTimer
> since_suspended(
861 background_page_data_
[host
->extension()->id()].
862 since_suspended
.release());
863 if (since_suspended
.get()) {
864 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
865 since_suspended
->Elapsed());
870 void ProcessManager::CloseBackgroundHost(ExtensionHost
* host
) {
871 ExtensionId extension_id
= host
->extension_id();
872 CHECK(host
->extension_host_type() ==
873 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
);
875 // |host| should deregister itself from our structures.
876 CHECK(background_hosts_
.find(host
) == background_hosts_
.end());
878 FOR_EACH_OBSERVER(ProcessManagerObserver
,
880 OnBackgroundHostClose(extension_id
));
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 ExtensionRegistry
* extension_registry
)
924 : ProcessManager(incognito_context
, original_context
, extension_registry
) {
925 DCHECK(incognito_context
->IsOffTheRecord());
927 // The original profile will have its own ProcessManager to
928 // load the background pages of the spanning extensions. This process
929 // manager need only worry about the split mode extensions, which is handled
930 // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler.
931 registrar_
.Remove(this,
932 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
933 content::Source
<BrowserContext
>(original_context
));
936 bool IncognitoProcessManager::CreateBackgroundHost(const Extension
* extension
,
938 if (IncognitoInfo::IsSplitMode(extension
)) {
939 if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
940 extension
->id(), GetBrowserContext()))
941 return ProcessManager::CreateBackgroundHost(extension
, url
);
943 // Do nothing. If an extension is spanning, then its original-profile
944 // background page is shared with incognito, so we don't create another.
949 SiteInstance
* IncognitoProcessManager::GetSiteInstanceForURL(const GURL
& url
) {
950 const Extension
* extension
=
951 extension_registry_
->enabled_extensions().GetExtensionOrAppByURL(url
);
952 if (extension
&& !IncognitoInfo::IsSplitMode(extension
)) {
953 BrowserContext
* original_context
=
954 ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
955 return ProcessManager::Get(original_context
)->GetSiteInstanceForURL(url
);
958 return ProcessManager::GetSiteInstanceForURL(url
);
961 } // namespace extensions