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/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/stl_util.h"
12 #include "base/time/time.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/devtools_agent_host.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/site_instance.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/common/url_constants.h"
22 #include "extensions/browser/extension_host.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/extensions_browser_client.h"
26 #include "extensions/browser/notification_types.h"
27 #include "extensions/browser/process_manager_delegate.h"
28 #include "extensions/browser/process_manager_factory.h"
29 #include "extensions/browser/process_manager_observer.h"
30 #include "extensions/browser/view_type_utils.h"
31 #include "extensions/common/constants.h"
32 #include "extensions/common/extension.h"
33 #include "extensions/common/extension_messages.h"
34 #include "extensions/common/manifest_handlers/background_info.h"
35 #include "extensions/common/manifest_handlers/incognito_info.h"
36 #include "extensions/common/one_shot_event.h"
38 using content::BrowserContext
;
40 namespace extensions
{
44 // The time to delay between an extension becoming idle and
45 // sending a ShouldSuspend message.
46 // Note: Must be sufficiently larger (e.g. 2x) than
47 // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
48 unsigned g_event_page_idle_time_msec
= 10000;
50 // The time to delay between sending a ShouldSuspend message and
51 // sending a Suspend message.
52 unsigned g_event_page_suspending_time_msec
= 5000;
54 std::string
GetExtensionIdForSiteInstance(
55 content::SiteInstance
* site_instance
) {
59 // This works for both apps and extensions because the site has been
60 // normalized to the extension URL for hosted apps.
61 const GURL
& site_url
= site_instance
->GetSiteURL();
63 if (!site_url
.SchemeIs(kExtensionScheme
) &&
64 !site_url
.SchemeIs(content::kGuestScheme
))
67 return site_url
.host();
70 std::string
GetExtensionID(content::RenderFrameHost
* render_frame_host
) {
71 CHECK(render_frame_host
);
72 return GetExtensionIdForSiteInstance(render_frame_host
->GetSiteInstance());
75 bool IsFrameInExtensionHost(ExtensionHost
* extension_host
,
76 content::RenderFrameHost
* render_frame_host
) {
77 return content::WebContents::FromRenderFrameHost(render_frame_host
) ==
78 extension_host
->host_contents();
81 // Incognito profiles use this process manager. It is mostly a shim that decides
82 // whether to fall back on the original profile's ProcessManager based
83 // on whether a given extension uses "split" or "spanning" incognito behavior.
84 // TODO(devlin): Given how little this does and the amount of cruft it adds to
85 // the .h file (in the form of protected members), we should consider just
86 // moving the incognito logic into the base class.
87 class IncognitoProcessManager
: public ProcessManager
{
89 IncognitoProcessManager(BrowserContext
* incognito_context
,
90 BrowserContext
* original_context
,
91 ExtensionRegistry
* extension_registry
);
92 ~IncognitoProcessManager() override
{}
93 bool CreateBackgroundHost(const Extension
* extension
,
94 const GURL
& url
) override
;
95 scoped_refptr
<content::SiteInstance
> GetSiteInstanceForURL(const GURL
& url
)
99 DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager
);
102 static void CreateBackgroundHostForExtensionLoad(
103 ProcessManager
* manager
, const Extension
* extension
) {
104 DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
105 if (BackgroundInfo::HasPersistentBackgroundPage(extension
))
106 manager
->CreateBackgroundHost(extension
,
107 BackgroundInfo::GetBackgroundURL(extension
));
112 struct ProcessManager::BackgroundPageData
{
113 // The count of things keeping the lazy background page alive.
114 int lazy_keepalive_count
;
116 // Tracks if an impulse event has occured since the last polling check.
117 bool keepalive_impulse
;
118 bool previous_keepalive_impulse
;
120 // True if the page responded to the ShouldSuspend message and is currently
121 // dispatching the suspend event. During this time any events that arrive will
122 // cancel the suspend process and an onSuspendCanceled event will be
123 // dispatched to the page.
126 // Stores the value of the incremented
127 // ProcessManager::last_background_close_sequence_id_ whenever the extension
128 // is active. A copy of the ID is also passed in the callbacks and IPC
129 // messages leading up to CloseLazyBackgroundPageNow. The process is aborted
130 // if the IDs ever differ due to new activity.
131 uint64 close_sequence_id
;
133 // Keeps track of when this page was last suspended. Used for perf metrics.
134 linked_ptr
<base::ElapsedTimer
> since_suspended
;
137 : lazy_keepalive_count(0),
138 keepalive_impulse(false),
139 previous_keepalive_impulse(false),
141 close_sequence_id(0) {}
144 // Data of a RenderFrameHost associated with an extension.
145 struct ProcessManager::ExtensionRenderFrameData
{
146 // The type of the view.
147 extensions::ViewType view_type
;
149 // Whether the view is keeping the lazy background page alive or not.
152 ExtensionRenderFrameData()
153 : view_type(VIEW_TYPE_INVALID
), has_keepalive(false) {}
155 // Returns whether the view can keep the lazy background page alive or not.
156 bool CanKeepalive() const {
158 case VIEW_TYPE_APP_WINDOW
:
159 case VIEW_TYPE_BACKGROUND_CONTENTS
:
160 case VIEW_TYPE_EXTENSION_DIALOG
:
161 case VIEW_TYPE_EXTENSION_POPUP
:
162 case VIEW_TYPE_LAUNCHER_PAGE
:
163 case VIEW_TYPE_PANEL
:
164 case VIEW_TYPE_TAB_CONTENTS
:
165 case VIEW_TYPE_VIRTUAL_KEYBOARD
:
168 case VIEW_TYPE_INVALID
:
169 case VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
:
182 ProcessManager
* ProcessManager::Get(BrowserContext
* context
) {
183 return ProcessManagerFactory::GetForBrowserContext(context
);
187 ProcessManager
* ProcessManager::Create(BrowserContext
* context
) {
188 ExtensionRegistry
* extension_registry
= ExtensionRegistry::Get(context
);
189 ExtensionsBrowserClient
* client
= ExtensionsBrowserClient::Get();
190 if (client
->IsGuestSession(context
)) {
191 // In the guest session, there is a single off-the-record context. Unlike
192 // a regular incognito mode, background pages of extensions must be
193 // created regardless of whether extensions use "spanning" or "split"
194 // incognito behavior.
195 BrowserContext
* original_context
= client
->GetOriginalContext(context
);
196 return new ProcessManager(context
, original_context
, extension_registry
);
199 if (context
->IsOffTheRecord()) {
200 BrowserContext
* original_context
= client
->GetOriginalContext(context
);
201 return new IncognitoProcessManager(
202 context
, original_context
, extension_registry
);
205 return new ProcessManager(context
, context
, extension_registry
);
209 ProcessManager
* ProcessManager::CreateForTesting(
210 BrowserContext
* context
,
211 ExtensionRegistry
* extension_registry
) {
212 DCHECK(!context
->IsOffTheRecord());
213 return new ProcessManager(context
, context
, extension_registry
);
217 ProcessManager
* ProcessManager::CreateIncognitoForTesting(
218 BrowserContext
* incognito_context
,
219 BrowserContext
* original_context
,
220 ExtensionRegistry
* extension_registry
) {
221 DCHECK(incognito_context
->IsOffTheRecord());
222 DCHECK(!original_context
->IsOffTheRecord());
223 return new IncognitoProcessManager(incognito_context
,
228 ProcessManager::ProcessManager(BrowserContext
* context
,
229 BrowserContext
* original_context
,
230 ExtensionRegistry
* extension_registry
)
231 : extension_registry_(extension_registry
),
232 site_instance_(content::SiteInstance::Create(context
)),
233 browser_context_(context
),
234 startup_background_hosts_created_(false),
235 last_background_close_sequence_id_(0),
236 weak_ptr_factory_(this) {
237 // ExtensionRegistry is shared between incognito and regular contexts.
238 DCHECK_EQ(original_context
, extension_registry_
->browser_context());
239 extension_registry_
->AddObserver(this);
241 if (!context
->IsOffTheRecord()) {
242 // Only the original profile needs to listen for ready to create background
243 // pages for all spanning extensions.
245 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
246 content::Source
<BrowserContext
>(original_context
));
249 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
,
250 content::Source
<BrowserContext
>(context
));
252 extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE
,
253 content::Source
<BrowserContext
>(context
));
254 devtools_callback_
= base::Bind(&ProcessManager::OnDevToolsStateChanged
,
255 weak_ptr_factory_
.GetWeakPtr());
256 content::DevToolsAgentHost::AddAgentStateCallback(devtools_callback_
);
258 OnKeepaliveImpulseCheck();
261 ProcessManager::~ProcessManager() {
262 extension_registry_
->RemoveObserver(this);
263 CloseBackgroundHosts();
264 DCHECK(background_hosts_
.empty());
265 content::DevToolsAgentHost::RemoveAgentStateCallback(devtools_callback_
);
268 void ProcessManager::RegisterRenderFrameHost(
269 content::WebContents
* web_contents
,
270 content::RenderFrameHost
* render_frame_host
,
271 const Extension
* extension
) {
272 ExtensionRenderFrameData
* data
= &all_extension_frames_
[render_frame_host
];
273 data
->view_type
= GetViewType(web_contents
);
275 // Keep the lazy background page alive as long as any non-background-page
276 // extension views are visible. Keepalive count balanced in
277 // UnregisterRenderFrame.
278 AcquireLazyKeepaliveCountForFrame(render_frame_host
);
280 FOR_EACH_OBSERVER(ProcessManagerObserver
,
282 OnExtensionFrameRegistered(extension
->id(),
286 void ProcessManager::UnregisterRenderFrameHost(
287 content::RenderFrameHost
* render_frame_host
) {
288 ExtensionRenderFrames::iterator frame
=
289 all_extension_frames_
.find(render_frame_host
);
291 if (frame
!= all_extension_frames_
.end()) {
292 std::string extension_id
= GetExtensionID(render_frame_host
);
293 // Keepalive count, balanced in RegisterRenderFrame.
294 ReleaseLazyKeepaliveCountForFrame(render_frame_host
);
295 all_extension_frames_
.erase(frame
);
297 FOR_EACH_OBSERVER(ProcessManagerObserver
,
299 OnExtensionFrameUnregistered(extension_id
,
304 scoped_refptr
<content::SiteInstance
> ProcessManager::GetSiteInstanceForURL(
306 return make_scoped_refptr(site_instance_
->GetRelatedSiteInstance(url
));
309 const ProcessManager::FrameSet
ProcessManager::GetAllFrames() const {
311 for (const auto& key_value
: all_extension_frames_
)
312 result
.insert(key_value
.first
);
316 ProcessManager::FrameSet
ProcessManager::GetRenderFrameHostsForExtension(
317 const std::string
& extension_id
) {
319 scoped_refptr
<content::SiteInstance
> site_instance(GetSiteInstanceForURL(
320 Extension::GetBaseURLFromExtensionId(extension_id
)));
321 if (!site_instance
.get())
324 // Gather up all the frames for that site.
325 for (const auto& key_value
: all_extension_frames_
) {
326 if (key_value
.first
->GetSiteInstance() == site_instance
)
327 result
.insert(key_value
.first
);
333 void ProcessManager::AddObserver(ProcessManagerObserver
* observer
) {
334 observer_list_
.AddObserver(observer
);
337 void ProcessManager::RemoveObserver(ProcessManagerObserver
* observer
) {
338 observer_list_
.RemoveObserver(observer
);
341 bool ProcessManager::CreateBackgroundHost(const Extension
* extension
,
343 // Hosted apps are taken care of from BackgroundContentsService. Ignore them
345 if (extension
->is_hosted_app())
348 // Don't create hosts if the embedder doesn't allow it.
349 ProcessManagerDelegate
* delegate
=
350 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
351 if (delegate
&& !delegate
->IsBackgroundPageAllowed(browser_context_
))
354 // Don't create multiple background hosts for an extension.
355 if (GetBackgroundHostForExtension(extension
->id()))
356 return true; // TODO(kalman): return false here? It might break things...
358 ExtensionHost
* host
=
359 new ExtensionHost(extension
, GetSiteInstanceForURL(url
).get(), url
,
360 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
);
361 host
->CreateRenderViewSoon();
362 OnBackgroundHostCreated(host
);
366 void ProcessManager::MaybeCreateStartupBackgroundHosts() {
367 if (startup_background_hosts_created_
)
370 // The embedder might disallow background pages entirely.
371 ProcessManagerDelegate
* delegate
=
372 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
373 if (delegate
&& !delegate
->IsBackgroundPageAllowed(browser_context_
))
376 // The embedder might want to defer background page loading. For example,
377 // Chrome defers background page loading when it is launched to show the app
378 // list, then triggers a load later when a browser window opens.
380 delegate
->DeferCreatingStartupBackgroundHosts(browser_context_
))
383 CreateStartupBackgroundHosts();
384 startup_background_hosts_created_
= true;
386 // Background pages should only be loaded once. To prevent any further loads
387 // occurring, we remove the notification listeners.
388 BrowserContext
* original_context
=
389 ExtensionsBrowserClient::Get()->GetOriginalContext(browser_context_
);
390 if (registrar_
.IsRegistered(
392 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
393 content::Source
<BrowserContext
>(original_context
))) {
394 registrar_
.Remove(this,
395 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
396 content::Source
<BrowserContext
>(original_context
));
400 ExtensionHost
* ProcessManager::GetBackgroundHostForExtension(
401 const std::string
& extension_id
) {
402 for (ExtensionHost
* host
: background_hosts_
) {
403 if (host
->extension_id() == extension_id
)
409 bool ProcessManager::IsBackgroundHostClosing(const std::string
& extension_id
) {
410 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
411 return (host
&& background_page_data_
[extension_id
].is_closing
);
414 const Extension
* ProcessManager::GetExtensionForRenderFrameHost(
415 content::RenderFrameHost
* render_frame_host
) {
416 return extension_registry_
->enabled_extensions().GetByID(
417 GetExtensionID(render_frame_host
));
420 const Extension
* ProcessManager::GetExtensionForWebContents(
421 content::WebContents
* web_contents
) {
422 if (!web_contents
->GetSiteInstance())
424 return extension_registry_
->enabled_extensions().GetByID(
425 GetExtensionIdForSiteInstance(web_contents
->GetSiteInstance()));
428 int ProcessManager::GetLazyKeepaliveCount(const Extension
* extension
) {
429 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
432 return background_page_data_
[extension
->id()].lazy_keepalive_count
;
435 void ProcessManager::IncrementLazyKeepaliveCount(const Extension
* extension
) {
436 if (BackgroundInfo::HasLazyBackgroundPage(extension
)) {
437 int& count
= background_page_data_
[extension
->id()].lazy_keepalive_count
;
439 OnLazyBackgroundPageActive(extension
->id());
443 void ProcessManager::DecrementLazyKeepaliveCount(const Extension
* extension
) {
444 if (BackgroundInfo::HasLazyBackgroundPage(extension
))
445 DecrementLazyKeepaliveCount(extension
->id());
448 // This implementation layers on top of the keepalive count. An impulse sets
449 // a per extension flag. On a regular interval that flag is checked. Changes
450 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
451 void ProcessManager::KeepaliveImpulse(const Extension
* extension
) {
452 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
455 BackgroundPageData
& bd
= background_page_data_
[extension
->id()];
457 if (!bd
.keepalive_impulse
) {
458 bd
.keepalive_impulse
= true;
459 if (!bd
.previous_keepalive_impulse
) {
460 IncrementLazyKeepaliveCount(extension
);
464 if (!keepalive_impulse_callback_for_testing_
.is_null()) {
465 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly
=
466 keepalive_impulse_callback_for_testing_
;
467 callback_may_clear_callbacks_reentrantly
.Run(extension
->id());
472 void ProcessManager::OnKeepaliveFromPlugin(int render_process_id
,
474 const std::string
& extension_id
) {
475 content::RenderFrameHost
* render_frame_host
=
476 content::RenderFrameHost::FromID(render_process_id
, render_frame_id
);
477 if (!render_frame_host
)
480 content::SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
484 BrowserContext
* browser_context
= site_instance
->GetBrowserContext();
485 const Extension
* extension
=
486 ExtensionRegistry::Get(browser_context
)->enabled_extensions().GetByID(
491 ProcessManager::Get(browser_context
)->KeepaliveImpulse(extension
);
494 void ProcessManager::OnShouldSuspendAck(const std::string
& extension_id
,
495 uint64 sequence_id
) {
496 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
498 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
499 host
->render_process_host()->Send(new ExtensionMsg_Suspend(extension_id
));
503 void ProcessManager::OnSuspendAck(const std::string
& extension_id
) {
504 background_page_data_
[extension_id
].is_closing
= true;
505 uint64 sequence_id
= background_page_data_
[extension_id
].close_sequence_id
;
506 base::MessageLoop::current()->PostDelayedTask(
508 base::Bind(&ProcessManager::CloseLazyBackgroundPageNow
,
509 weak_ptr_factory_
.GetWeakPtr(),
512 base::TimeDelta::FromMilliseconds(g_event_page_suspending_time_msec
));
515 void ProcessManager::OnNetworkRequestStarted(
516 content::RenderFrameHost
* render_frame_host
,
518 ExtensionHost
* host
= GetBackgroundHostForExtension(
519 GetExtensionID(render_frame_host
));
520 if (host
&& IsFrameInExtensionHost(host
, render_frame_host
)) {
521 IncrementLazyKeepaliveCount(host
->extension());
522 host
->OnNetworkRequestStarted(request_id
);
526 void ProcessManager::OnNetworkRequestDone(
527 content::RenderFrameHost
* render_frame_host
,
529 ExtensionHost
* host
= GetBackgroundHostForExtension(
530 GetExtensionID(render_frame_host
));
531 if (host
&& IsFrameInExtensionHost(host
, render_frame_host
)) {
532 host
->OnNetworkRequestDone(request_id
);
533 DecrementLazyKeepaliveCount(host
->extension());
537 void ProcessManager::CancelSuspend(const Extension
* extension
) {
538 bool& is_closing
= background_page_data_
[extension
->id()].is_closing
;
539 ExtensionHost
* host
= GetBackgroundHostForExtension(extension
->id());
540 if (host
&& is_closing
) {
542 host
->render_process_host()->Send(
543 new ExtensionMsg_CancelSuspend(extension
->id()));
544 // This increment / decrement is to simulate an instantaneous event. This
545 // has the effect of invalidating close_sequence_id, preventing any in
546 // progress closes from completing and starting a new close process if
548 IncrementLazyKeepaliveCount(extension
);
549 DecrementLazyKeepaliveCount(extension
);
553 void ProcessManager::CloseBackgroundHosts() {
554 STLDeleteElements(&background_hosts_
);
557 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
558 const ImpulseCallbackForTesting
& callback
) {
559 keepalive_impulse_callback_for_testing_
= callback
;
562 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
563 const ImpulseCallbackForTesting
& callback
) {
564 keepalive_impulse_decrement_callback_for_testing_
= callback
;
568 void ProcessManager::SetEventPageIdleTimeForTesting(unsigned idle_time_msec
) {
569 CHECK_GT(idle_time_msec
, 0u); // OnKeepaliveImpulseCheck requires non zero.
570 g_event_page_idle_time_msec
= idle_time_msec
;
574 void ProcessManager::SetEventPageSuspendingTimeForTesting(
575 unsigned suspending_time_msec
) {
576 g_event_page_suspending_time_msec
= suspending_time_msec
;
579 ////////////////////////////////////////////////////////////////////////////////
582 void ProcessManager::Observe(int type
,
583 const content::NotificationSource
& source
,
584 const content::NotificationDetails
& details
) {
585 TRACE_EVENT0("browser,startup", "ProcessManager::Observe");
587 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
: {
588 // TODO(jamescook): Convert this to use ExtensionSystem::ready() instead
589 // of a notification.
590 SCOPED_UMA_HISTOGRAM_TIMER("Extensions.ProcessManagerStartupHostsTime");
591 MaybeCreateStartupBackgroundHosts();
594 case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
: {
595 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
596 if (background_hosts_
.erase(host
)) {
597 ClearBackgroundPageData(host
->extension()->id());
598 background_page_data_
[host
->extension()->id()].since_suspended
.reset(
599 new base::ElapsedTimer());
603 case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE
: {
604 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
605 if (host
->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
) {
606 CloseBackgroundHost(host
);
615 void ProcessManager::OnExtensionLoaded(BrowserContext
* browser_context
,
616 const Extension
* extension
) {
617 if (ExtensionSystem::Get(browser_context
)->ready().is_signaled()) {
618 // The extension system is ready, so create the background host.
619 CreateBackgroundHostForExtensionLoad(this, extension
);
623 void ProcessManager::OnExtensionUnloaded(
624 BrowserContext
* browser_context
,
625 const Extension
* extension
,
626 UnloadedExtensionInfo::Reason reason
) {
627 ExtensionHost
* host
= GetBackgroundHostForExtension(extension
->id());
629 CloseBackgroundHost(host
);
630 UnregisterExtension(extension
->id());
633 void ProcessManager::CreateStartupBackgroundHosts() {
634 DCHECK(!startup_background_hosts_created_
);
635 for (const scoped_refptr
<const Extension
>& extension
:
636 extension_registry_
->enabled_extensions()) {
637 CreateBackgroundHostForExtensionLoad(this, extension
.get());
638 FOR_EACH_OBSERVER(ProcessManagerObserver
,
640 OnBackgroundHostStartup(extension
.get()));
644 void ProcessManager::OnBackgroundHostCreated(ExtensionHost
* host
) {
645 DCHECK_EQ(browser_context_
, host
->browser_context());
646 background_hosts_
.insert(host
);
648 if (BackgroundInfo::HasLazyBackgroundPage(host
->extension())) {
649 linked_ptr
<base::ElapsedTimer
> since_suspended(
650 background_page_data_
[host
->extension()->id()].
651 since_suspended
.release());
652 if (since_suspended
.get()) {
653 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
654 since_suspended
->Elapsed());
657 FOR_EACH_OBSERVER(ProcessManagerObserver
, observer_list_
,
658 OnBackgroundHostCreated(host
));
661 void ProcessManager::CloseBackgroundHost(ExtensionHost
* host
) {
662 ExtensionId extension_id
= host
->extension_id();
663 CHECK(host
->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
);
665 // |host| should deregister itself from our structures.
666 CHECK(background_hosts_
.find(host
) == background_hosts_
.end());
668 FOR_EACH_OBSERVER(ProcessManagerObserver
,
670 OnBackgroundHostClose(extension_id
));
673 void ProcessManager::AcquireLazyKeepaliveCountForFrame(
674 content::RenderFrameHost
* render_frame_host
) {
675 ExtensionRenderFrames::iterator it
=
676 all_extension_frames_
.find(render_frame_host
);
677 if (it
== all_extension_frames_
.end())
680 ExtensionRenderFrameData
& data
= it
->second
;
681 if (data
.CanKeepalive() && !data
.has_keepalive
) {
682 const Extension
* extension
=
683 GetExtensionForRenderFrameHost(render_frame_host
);
685 IncrementLazyKeepaliveCount(extension
);
686 data
.has_keepalive
= true;
691 void ProcessManager::ReleaseLazyKeepaliveCountForFrame(
692 content::RenderFrameHost
* render_frame_host
) {
693 ExtensionRenderFrames::iterator iter
=
694 all_extension_frames_
.find(render_frame_host
);
695 if (iter
== all_extension_frames_
.end())
698 ExtensionRenderFrameData
& data
= iter
->second
;
699 if (data
.CanKeepalive() && data
.has_keepalive
) {
700 const Extension
* extension
=
701 GetExtensionForRenderFrameHost(render_frame_host
);
703 DecrementLazyKeepaliveCount(extension
);
704 data
.has_keepalive
= false;
709 void ProcessManager::DecrementLazyKeepaliveCount(
710 const std::string
& extension_id
) {
711 int& count
= background_page_data_
[extension_id
].lazy_keepalive_count
;
713 !extension_registry_
->enabled_extensions().Contains(extension_id
));
715 // If we reach a zero keepalive count when the lazy background page is about
716 // to be closed, incrementing close_sequence_id will cancel the close
717 // sequence and cause the background page to linger. So check is_closing
718 // before initiating another close sequence.
719 if (--count
== 0 && !background_page_data_
[extension_id
].is_closing
) {
720 background_page_data_
[extension_id
].close_sequence_id
=
721 ++last_background_close_sequence_id_
;
722 base::MessageLoop::current()->PostDelayedTask(
724 base::Bind(&ProcessManager::OnLazyBackgroundPageIdle
,
725 weak_ptr_factory_
.GetWeakPtr(),
727 last_background_close_sequence_id_
),
728 base::TimeDelta::FromMilliseconds(g_event_page_idle_time_msec
));
732 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
733 // have been made for at least g_event_page_idle_time_msec. In the best case an
734 // impulse was made just before being cleared, and the decrement will occur
735 // g_event_page_idle_time_msec later, causing a 2 * g_event_page_idle_time_msec
736 // total time for extension to be shut down based on impulses. Worst case is
737 // an impulse just after a clear, adding one check cycle and resulting in 3x
739 void ProcessManager::OnKeepaliveImpulseCheck() {
740 for (BackgroundPageDataMap::iterator i
= background_page_data_
.begin();
741 i
!= background_page_data_
.end();
743 if (i
->second
.previous_keepalive_impulse
&& !i
->second
.keepalive_impulse
) {
744 DecrementLazyKeepaliveCount(i
->first
);
745 if (!keepalive_impulse_decrement_callback_for_testing_
.is_null()) {
746 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly
=
747 keepalive_impulse_decrement_callback_for_testing_
;
748 callback_may_clear_callbacks_reentrantly
.Run(i
->first
);
752 i
->second
.previous_keepalive_impulse
= i
->second
.keepalive_impulse
;
753 i
->second
.keepalive_impulse
= false;
756 // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
757 // tests there will be no message loop. In that event don't schedule tasks.
758 if (base::MessageLoop::current()) {
759 base::MessageLoop::current()->PostDelayedTask(
761 base::Bind(&ProcessManager::OnKeepaliveImpulseCheck
,
762 weak_ptr_factory_
.GetWeakPtr()),
763 base::TimeDelta::FromMilliseconds(g_event_page_idle_time_msec
));
767 void ProcessManager::OnLazyBackgroundPageIdle(const std::string
& extension_id
,
768 uint64 sequence_id
) {
769 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
770 if (host
&& !background_page_data_
[extension_id
].is_closing
&&
771 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
772 // Tell the renderer we are about to close. This is a simple ping that the
773 // renderer will respond to. The purpose is to control sequencing: if the
774 // extension remains idle until the renderer responds with an ACK, then we
775 // know that the extension process is ready to shut down. If our
776 // close_sequence_id has already changed, then we would ignore the
777 // ShouldSuspendAck, so we don't send the ping.
778 host
->render_process_host()->Send(new ExtensionMsg_ShouldSuspend(
779 extension_id
, sequence_id
));
783 void ProcessManager::OnLazyBackgroundPageActive(
784 const std::string
& extension_id
) {
785 if (!background_page_data_
[extension_id
].is_closing
) {
786 // Cancel the current close sequence by changing the close_sequence_id,
787 // which causes us to ignore the next ShouldSuspendAck.
788 background_page_data_
[extension_id
].close_sequence_id
=
789 ++last_background_close_sequence_id_
;
793 void ProcessManager::CloseLazyBackgroundPageNow(const std::string
& extension_id
,
794 uint64 sequence_id
) {
795 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
797 sequence_id
== background_page_data_
[extension_id
].close_sequence_id
) {
798 // Close remaining views.
799 std::vector
<content::RenderFrameHost
*> frames_to_close
;
800 for (const auto& key_value
: all_extension_frames_
) {
801 if (key_value
.second
.CanKeepalive() &&
802 GetExtensionID(key_value
.first
) == extension_id
) {
803 DCHECK(!key_value
.second
.has_keepalive
);
804 frames_to_close
.push_back(key_value
.first
);
807 for (content::RenderFrameHost
* frame
: frames_to_close
) {
808 frame
->GetRenderViewHost()->ClosePage();
809 // RenderViewHost::ClosePage() may result in calling
810 // UnregisterRenderViewHost() asynchronously and may cause race conditions
811 // when the background page is reloaded.
812 // To avoid this, unregister the view now.
813 UnregisterRenderFrameHost(frame
);
816 ExtensionHost
* host
= GetBackgroundHostForExtension(extension_id
);
818 CloseBackgroundHost(host
);
822 void ProcessManager::OnDevToolsStateChanged(
823 content::DevToolsAgentHost
* agent_host
,
825 content::WebContents
* web_contents
= agent_host
->GetWebContents();
826 // Ignore unrelated notifications.
827 if (!web_contents
|| web_contents
->GetBrowserContext() != browser_context_
)
829 if (GetViewType(web_contents
) != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
)
831 const Extension
* extension
=
832 extension_registry_
->enabled_extensions().GetByID(
833 GetExtensionIdForSiteInstance(web_contents
->GetSiteInstance()));
837 // Keep the lazy background page alive while it's being inspected.
838 CancelSuspend(extension
);
839 IncrementLazyKeepaliveCount(extension
);
841 DecrementLazyKeepaliveCount(extension
);
845 void ProcessManager::UnregisterExtension(const std::string
& extension_id
) {
846 // The lazy_keepalive_count may be greater than zero at this point because
847 // RenderFrameHosts are still alive. During extension reloading, they will
848 // decrement the lazy_keepalive_count to negative for the new extension
849 // instance when they are destroyed. Since we are erasing the background page
850 // data for the unloaded extension, unregister the RenderFrameHosts too.
851 for (ExtensionRenderFrames::iterator it
= all_extension_frames_
.begin();
852 it
!= all_extension_frames_
.end(); ) {
853 content::RenderFrameHost
* host
= it
->first
;
854 if (GetExtensionID(host
) == extension_id
) {
855 all_extension_frames_
.erase(it
++);
856 FOR_EACH_OBSERVER(ProcessManagerObserver
,
858 OnExtensionFrameUnregistered(extension_id
, host
));
864 background_page_data_
.erase(extension_id
);
867 void ProcessManager::ClearBackgroundPageData(const std::string
& extension_id
) {
868 background_page_data_
.erase(extension_id
);
870 // Re-register all RenderViews for this extension. We do this to restore
871 // the lazy_keepalive_count (if any) to properly reflect the number of open
873 for (const auto& key_value
: all_extension_frames_
) {
874 // Do not increment the count when |has_keepalive| is false
875 // (i.e. ReleaseLazyKeepaliveCountForView() was called).
876 if (GetExtensionID(key_value
.first
) == extension_id
&&
877 key_value
.second
.has_keepalive
) {
878 const Extension
* extension
=
879 GetExtensionForRenderFrameHost(key_value
.first
);
881 IncrementLazyKeepaliveCount(extension
);
887 // IncognitoProcessManager
890 IncognitoProcessManager::IncognitoProcessManager(
891 BrowserContext
* incognito_context
,
892 BrowserContext
* original_context
,
893 ExtensionRegistry
* extension_registry
)
894 : ProcessManager(incognito_context
, original_context
, extension_registry
) {
895 DCHECK(incognito_context
->IsOffTheRecord());
898 bool IncognitoProcessManager::CreateBackgroundHost(const Extension
* extension
,
900 if (IncognitoInfo::IsSplitMode(extension
)) {
901 if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
902 extension
->id(), browser_context()))
903 return ProcessManager::CreateBackgroundHost(extension
, url
);
905 // Do nothing. If an extension is spanning, then its original-profile
906 // background page is shared with incognito, so we don't create another.
911 scoped_refptr
<content::SiteInstance
>
912 IncognitoProcessManager::GetSiteInstanceForURL(const GURL
& url
) {
913 const Extension
* extension
=
914 extension_registry_
->enabled_extensions().GetExtensionOrAppByURL(url
);
915 if (extension
&& !IncognitoInfo::IsSplitMode(extension
)) {
916 BrowserContext
* original_context
=
917 ExtensionsBrowserClient::Get()->GetOriginalContext(browser_context());
918 return ProcessManager::Get(original_context
)->GetSiteInstanceForURL(url
);
921 return ProcessManager::GetSiteInstanceForURL(url
);
924 } // namespace extensions