Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / extensions / browser / process_manager.cc
blob98e72aa30c2e2c88a3bcf331d7936b57dde91129
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"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/stl_util.h"
14 #include "base/time/time.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/devtools_agent_host.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/site_instance.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/common/url_constants.h"
24 #include "extensions/browser/extension_host.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/extensions_browser_client.h"
28 #include "extensions/browser/lazy_background_task_queue.h"
29 #include "extensions/browser/notification_types.h"
30 #include "extensions/browser/process_manager_delegate.h"
31 #include "extensions/browser/process_manager_factory.h"
32 #include "extensions/browser/process_manager_observer.h"
33 #include "extensions/browser/view_type_utils.h"
34 #include "extensions/common/constants.h"
35 #include "extensions/common/extension.h"
36 #include "extensions/common/extension_messages.h"
37 #include "extensions/common/manifest_handlers/background_info.h"
38 #include "extensions/common/manifest_handlers/incognito_info.h"
39 #include "extensions/common/one_shot_event.h"
41 using content::BrowserContext;
43 namespace extensions {
45 namespace {
47 // The time to delay between an extension becoming idle and
48 // sending a ShouldSuspend message.
49 // Note: Must be sufficiently larger (e.g. 2x) than
50 // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
51 unsigned g_event_page_idle_time_msec = 10000;
53 // The time to delay between sending a ShouldSuspend message and
54 // sending a Suspend message.
55 unsigned g_event_page_suspending_time_msec = 5000;
57 std::string GetExtensionIdForSiteInstance(
58 content::SiteInstance* site_instance) {
59 if (!site_instance)
60 return std::string();
62 // This works for both apps and extensions because the site has been
63 // normalized to the extension URL for hosted apps.
64 const GURL& site_url = site_instance->GetSiteURL();
66 if (!site_url.SchemeIs(kExtensionScheme) &&
67 !site_url.SchemeIs(content::kGuestScheme))
68 return std::string();
70 return site_url.host();
73 std::string GetExtensionID(content::RenderFrameHost* render_frame_host) {
74 CHECK(render_frame_host);
75 return GetExtensionIdForSiteInstance(render_frame_host->GetSiteInstance());
78 bool IsFrameInExtensionHost(ExtensionHost* extension_host,
79 content::RenderFrameHost* render_frame_host) {
80 return content::WebContents::FromRenderFrameHost(render_frame_host) ==
81 extension_host->host_contents();
84 // Incognito profiles use this process manager. It is mostly a shim that decides
85 // whether to fall back on the original profile's ProcessManager based
86 // on whether a given extension uses "split" or "spanning" incognito behavior.
87 // TODO(devlin): Given how little this does and the amount of cruft it adds to
88 // the .h file (in the form of protected members), we should consider just
89 // moving the incognito logic into the base class.
90 class IncognitoProcessManager : public ProcessManager {
91 public:
92 IncognitoProcessManager(BrowserContext* incognito_context,
93 BrowserContext* original_context,
94 ExtensionRegistry* extension_registry);
95 ~IncognitoProcessManager() override {}
96 bool CreateBackgroundHost(const Extension* extension,
97 const GURL& url) override;
98 scoped_refptr<content::SiteInstance> GetSiteInstanceForURL(const GURL& url)
99 override;
101 private:
102 DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager);
105 static void CreateBackgroundHostForExtensionLoad(
106 ProcessManager* manager, const Extension* extension) {
107 DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
108 if (BackgroundInfo::HasPersistentBackgroundPage(extension))
109 manager->CreateBackgroundHost(extension,
110 BackgroundInfo::GetBackgroundURL(extension));
113 void PropagateExtensionWakeResult(const base::Callback<void(bool)>& callback,
114 extensions::ExtensionHost* host) {
115 callback.Run(host != nullptr);
118 } // namespace
120 struct ProcessManager::BackgroundPageData {
121 // The count of things keeping the lazy background page alive.
122 int lazy_keepalive_count;
124 // Tracks if an impulse event has occured since the last polling check.
125 bool keepalive_impulse;
126 bool previous_keepalive_impulse;
128 // True if the page responded to the ShouldSuspend message and is currently
129 // dispatching the suspend event. During this time any events that arrive will
130 // cancel the suspend process and an onSuspendCanceled event will be
131 // dispatched to the page.
132 bool is_closing;
134 // Stores the value of the incremented
135 // ProcessManager::last_background_close_sequence_id_ whenever the extension
136 // is active. A copy of the ID is also passed in the callbacks and IPC
137 // messages leading up to CloseLazyBackgroundPageNow. The process is aborted
138 // if the IDs ever differ due to new activity.
139 uint64 close_sequence_id;
141 // Keeps track of when this page was last suspended. Used for perf metrics.
142 linked_ptr<base::ElapsedTimer> since_suspended;
144 BackgroundPageData()
145 : lazy_keepalive_count(0),
146 keepalive_impulse(false),
147 previous_keepalive_impulse(false),
148 is_closing(false),
149 close_sequence_id(0) {}
152 // Data of a RenderFrameHost associated with an extension.
153 struct ProcessManager::ExtensionRenderFrameData {
154 // The type of the view.
155 extensions::ViewType view_type;
157 // Whether the view is keeping the lazy background page alive or not.
158 bool has_keepalive;
160 ExtensionRenderFrameData()
161 : view_type(VIEW_TYPE_INVALID), has_keepalive(false) {}
163 // Returns whether the view can keep the lazy background page alive or not.
164 bool CanKeepalive() const {
165 switch (view_type) {
166 case VIEW_TYPE_APP_WINDOW:
167 case VIEW_TYPE_BACKGROUND_CONTENTS:
168 case VIEW_TYPE_EXTENSION_DIALOG:
169 case VIEW_TYPE_EXTENSION_POPUP:
170 case VIEW_TYPE_LAUNCHER_PAGE:
171 case VIEW_TYPE_PANEL:
172 case VIEW_TYPE_TAB_CONTENTS:
173 case VIEW_TYPE_VIRTUAL_KEYBOARD:
174 return true;
176 case VIEW_TYPE_INVALID:
177 case VIEW_TYPE_EXTENSION_BACKGROUND_PAGE:
178 return false;
180 NOTREACHED();
181 return false;
186 // ProcessManager
189 // static
190 ProcessManager* ProcessManager::Get(BrowserContext* context) {
191 return ProcessManagerFactory::GetForBrowserContext(context);
194 // static
195 ProcessManager* ProcessManager::Create(BrowserContext* context) {
196 ExtensionRegistry* extension_registry = ExtensionRegistry::Get(context);
197 ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get();
198 if (client->IsGuestSession(context)) {
199 // In the guest session, there is a single off-the-record context. Unlike
200 // a regular incognito mode, background pages of extensions must be
201 // created regardless of whether extensions use "spanning" or "split"
202 // incognito behavior.
203 BrowserContext* original_context = client->GetOriginalContext(context);
204 return new ProcessManager(context, original_context, extension_registry);
207 if (context->IsOffTheRecord()) {
208 BrowserContext* original_context = client->GetOriginalContext(context);
209 return new IncognitoProcessManager(
210 context, original_context, extension_registry);
213 return new ProcessManager(context, context, extension_registry);
216 // static
217 ProcessManager* ProcessManager::CreateForTesting(
218 BrowserContext* context,
219 ExtensionRegistry* extension_registry) {
220 DCHECK(!context->IsOffTheRecord());
221 return new ProcessManager(context, context, extension_registry);
224 // static
225 ProcessManager* ProcessManager::CreateIncognitoForTesting(
226 BrowserContext* incognito_context,
227 BrowserContext* original_context,
228 ExtensionRegistry* extension_registry) {
229 DCHECK(incognito_context->IsOffTheRecord());
230 DCHECK(!original_context->IsOffTheRecord());
231 return new IncognitoProcessManager(incognito_context,
232 original_context,
233 extension_registry);
236 ProcessManager::ProcessManager(BrowserContext* context,
237 BrowserContext* original_context,
238 ExtensionRegistry* extension_registry)
239 : extension_registry_(extension_registry),
240 site_instance_(content::SiteInstance::Create(context)),
241 browser_context_(context),
242 startup_background_hosts_created_(false),
243 last_background_close_sequence_id_(0),
244 weak_ptr_factory_(this) {
245 // ExtensionRegistry is shared between incognito and regular contexts.
246 DCHECK_EQ(original_context, extension_registry_->browser_context());
247 extension_registry_->AddObserver(this);
249 if (!context->IsOffTheRecord()) {
250 // Only the original profile needs to listen for ready to create background
251 // pages for all spanning extensions.
252 registrar_.Add(this,
253 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
254 content::Source<BrowserContext>(original_context));
256 registrar_.Add(this,
257 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
258 content::Source<BrowserContext>(context));
259 registrar_.Add(this,
260 extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
261 content::Source<BrowserContext>(context));
262 devtools_callback_ = base::Bind(&ProcessManager::OnDevToolsStateChanged,
263 weak_ptr_factory_.GetWeakPtr());
264 content::DevToolsAgentHost::AddAgentStateCallback(devtools_callback_);
266 OnKeepaliveImpulseCheck();
269 ProcessManager::~ProcessManager() {
270 extension_registry_->RemoveObserver(this);
271 CloseBackgroundHosts();
272 DCHECK(background_hosts_.empty());
273 content::DevToolsAgentHost::RemoveAgentStateCallback(devtools_callback_);
276 void ProcessManager::RegisterRenderFrameHost(
277 content::WebContents* web_contents,
278 content::RenderFrameHost* render_frame_host,
279 const Extension* extension) {
280 ExtensionRenderFrameData* data = &all_extension_frames_[render_frame_host];
281 data->view_type = GetViewType(web_contents);
283 // Keep the lazy background page alive as long as any non-background-page
284 // extension views are visible. Keepalive count balanced in
285 // UnregisterRenderFrame.
286 AcquireLazyKeepaliveCountForFrame(render_frame_host);
288 FOR_EACH_OBSERVER(ProcessManagerObserver,
289 observer_list_,
290 OnExtensionFrameRegistered(extension->id(),
291 render_frame_host));
294 void ProcessManager::UnregisterRenderFrameHost(
295 content::RenderFrameHost* render_frame_host) {
296 ExtensionRenderFrames::iterator frame =
297 all_extension_frames_.find(render_frame_host);
299 if (frame != all_extension_frames_.end()) {
300 std::string extension_id = GetExtensionID(render_frame_host);
301 // Keepalive count, balanced in RegisterRenderFrame.
302 ReleaseLazyKeepaliveCountForFrame(render_frame_host);
303 all_extension_frames_.erase(frame);
305 FOR_EACH_OBSERVER(ProcessManagerObserver,
306 observer_list_,
307 OnExtensionFrameUnregistered(extension_id,
308 render_frame_host));
312 scoped_refptr<content::SiteInstance> ProcessManager::GetSiteInstanceForURL(
313 const GURL& url) {
314 return make_scoped_refptr(site_instance_->GetRelatedSiteInstance(url));
317 const ProcessManager::FrameSet ProcessManager::GetAllFrames() const {
318 FrameSet result;
319 for (const auto& key_value : all_extension_frames_)
320 result.insert(key_value.first);
321 return result;
324 ProcessManager::FrameSet ProcessManager::GetRenderFrameHostsForExtension(
325 const std::string& extension_id) {
326 FrameSet result;
327 scoped_refptr<content::SiteInstance> site_instance(GetSiteInstanceForURL(
328 Extension::GetBaseURLFromExtensionId(extension_id)));
329 if (!site_instance.get())
330 return result;
332 // Gather up all the frames for that site.
333 for (const auto& key_value : all_extension_frames_) {
334 if (key_value.first->GetSiteInstance() == site_instance)
335 result.insert(key_value.first);
338 return result;
341 void ProcessManager::AddObserver(ProcessManagerObserver* observer) {
342 observer_list_.AddObserver(observer);
345 void ProcessManager::RemoveObserver(ProcessManagerObserver* observer) {
346 observer_list_.RemoveObserver(observer);
349 bool ProcessManager::CreateBackgroundHost(const Extension* extension,
350 const GURL& url) {
351 // Hosted apps are taken care of from BackgroundContentsService. Ignore them
352 // here.
353 if (extension->is_hosted_app())
354 return false;
356 // Don't create hosts if the embedder doesn't allow it.
357 ProcessManagerDelegate* delegate =
358 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
359 if (delegate && !delegate->IsBackgroundPageAllowed(browser_context_))
360 return false;
362 // Don't create multiple background hosts for an extension.
363 if (GetBackgroundHostForExtension(extension->id()))
364 return true; // TODO(kalman): return false here? It might break things...
366 ExtensionHost* host =
367 new ExtensionHost(extension, GetSiteInstanceForURL(url).get(), url,
368 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
369 host->CreateRenderViewSoon();
370 OnBackgroundHostCreated(host);
371 return true;
374 void ProcessManager::MaybeCreateStartupBackgroundHosts() {
375 if (startup_background_hosts_created_)
376 return;
378 // The embedder might disallow background pages entirely.
379 ProcessManagerDelegate* delegate =
380 ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
381 if (delegate && !delegate->IsBackgroundPageAllowed(browser_context_))
382 return;
384 // The embedder might want to defer background page loading. For example,
385 // Chrome defers background page loading when it is launched to show the app
386 // list, then triggers a load later when a browser window opens.
387 if (delegate &&
388 delegate->DeferCreatingStartupBackgroundHosts(browser_context_))
389 return;
391 CreateStartupBackgroundHosts();
392 startup_background_hosts_created_ = true;
394 // Background pages should only be loaded once. To prevent any further loads
395 // occurring, we remove the notification listeners.
396 BrowserContext* original_context =
397 ExtensionsBrowserClient::Get()->GetOriginalContext(browser_context_);
398 if (registrar_.IsRegistered(
399 this,
400 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
401 content::Source<BrowserContext>(original_context))) {
402 registrar_.Remove(this,
403 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
404 content::Source<BrowserContext>(original_context));
408 ExtensionHost* ProcessManager::GetBackgroundHostForExtension(
409 const std::string& extension_id) {
410 for (ExtensionHost* host : background_hosts_) {
411 if (host->extension_id() == extension_id)
412 return host;
414 return nullptr;
417 bool ProcessManager::IsEventPageSuspended(const std::string& extension_id) {
418 return GetBackgroundHostForExtension(extension_id) == nullptr;
421 bool ProcessManager::WakeEventPage(const std::string& extension_id,
422 const base::Callback<void(bool)>& callback) {
423 if (GetBackgroundHostForExtension(extension_id)) {
424 // Run the callback immediately if the extension is already awake.
425 return false;
427 LazyBackgroundTaskQueue* queue =
428 ExtensionSystem::Get(browser_context_)->lazy_background_task_queue();
429 queue->AddPendingTask(browser_context_, extension_id,
430 base::Bind(&PropagateExtensionWakeResult, callback));
431 return true;
434 bool ProcessManager::IsBackgroundHostClosing(const std::string& extension_id) {
435 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
436 return (host && background_page_data_[extension_id].is_closing);
439 const Extension* ProcessManager::GetExtensionForRenderFrameHost(
440 content::RenderFrameHost* render_frame_host) {
441 return extension_registry_->enabled_extensions().GetByID(
442 GetExtensionID(render_frame_host));
445 const Extension* ProcessManager::GetExtensionForWebContents(
446 content::WebContents* web_contents) {
447 if (!web_contents->GetSiteInstance())
448 return nullptr;
449 return extension_registry_->enabled_extensions().GetByID(
450 GetExtensionIdForSiteInstance(web_contents->GetSiteInstance()));
453 int ProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
454 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
455 return 0;
457 return background_page_data_[extension->id()].lazy_keepalive_count;
460 void ProcessManager::IncrementLazyKeepaliveCount(const Extension* extension) {
461 if (BackgroundInfo::HasLazyBackgroundPage(extension)) {
462 int& count = background_page_data_[extension->id()].lazy_keepalive_count;
463 if (++count == 1)
464 OnLazyBackgroundPageActive(extension->id());
468 void ProcessManager::DecrementLazyKeepaliveCount(const Extension* extension) {
469 if (BackgroundInfo::HasLazyBackgroundPage(extension))
470 DecrementLazyKeepaliveCount(extension->id());
473 // This implementation layers on top of the keepalive count. An impulse sets
474 // a per extension flag. On a regular interval that flag is checked. Changes
475 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
476 void ProcessManager::KeepaliveImpulse(const Extension* extension) {
477 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
478 return;
480 BackgroundPageData& bd = background_page_data_[extension->id()];
482 if (!bd.keepalive_impulse) {
483 bd.keepalive_impulse = true;
484 if (!bd.previous_keepalive_impulse) {
485 IncrementLazyKeepaliveCount(extension);
489 if (!keepalive_impulse_callback_for_testing_.is_null()) {
490 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
491 keepalive_impulse_callback_for_testing_;
492 callback_may_clear_callbacks_reentrantly.Run(extension->id());
496 // static
497 void ProcessManager::OnKeepaliveFromPlugin(int render_process_id,
498 int render_frame_id,
499 const std::string& extension_id) {
500 content::RenderFrameHost* render_frame_host =
501 content::RenderFrameHost::FromID(render_process_id, render_frame_id);
502 if (!render_frame_host)
503 return;
505 content::SiteInstance* site_instance = render_frame_host->GetSiteInstance();
506 if (!site_instance)
507 return;
509 BrowserContext* browser_context = site_instance->GetBrowserContext();
510 const Extension* extension =
511 ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID(
512 extension_id);
513 if (!extension)
514 return;
516 ProcessManager::Get(browser_context)->KeepaliveImpulse(extension);
519 void ProcessManager::OnShouldSuspendAck(const std::string& extension_id,
520 uint64 sequence_id) {
521 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
522 if (host &&
523 sequence_id == background_page_data_[extension_id].close_sequence_id) {
524 host->render_process_host()->Send(new ExtensionMsg_Suspend(extension_id));
528 void ProcessManager::OnSuspendAck(const std::string& extension_id) {
529 background_page_data_[extension_id].is_closing = true;
530 uint64 sequence_id = background_page_data_[extension_id].close_sequence_id;
531 base::MessageLoop::current()->PostDelayedTask(
532 FROM_HERE,
533 base::Bind(&ProcessManager::CloseLazyBackgroundPageNow,
534 weak_ptr_factory_.GetWeakPtr(),
535 extension_id,
536 sequence_id),
537 base::TimeDelta::FromMilliseconds(g_event_page_suspending_time_msec));
540 void ProcessManager::OnNetworkRequestStarted(
541 content::RenderFrameHost* render_frame_host,
542 uint64 request_id) {
543 ExtensionHost* host = GetBackgroundHostForExtension(
544 GetExtensionID(render_frame_host));
545 if (host && IsFrameInExtensionHost(host, render_frame_host)) {
546 IncrementLazyKeepaliveCount(host->extension());
547 host->OnNetworkRequestStarted(request_id);
551 void ProcessManager::OnNetworkRequestDone(
552 content::RenderFrameHost* render_frame_host,
553 uint64 request_id) {
554 ExtensionHost* host = GetBackgroundHostForExtension(
555 GetExtensionID(render_frame_host));
556 if (host && IsFrameInExtensionHost(host, render_frame_host)) {
557 host->OnNetworkRequestDone(request_id);
558 DecrementLazyKeepaliveCount(host->extension());
562 void ProcessManager::CancelSuspend(const Extension* extension) {
563 bool& is_closing = background_page_data_[extension->id()].is_closing;
564 ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
565 if (host && is_closing) {
566 is_closing = false;
567 host->render_process_host()->Send(
568 new ExtensionMsg_CancelSuspend(extension->id()));
569 // This increment / decrement is to simulate an instantaneous event. This
570 // has the effect of invalidating close_sequence_id, preventing any in
571 // progress closes from completing and starting a new close process if
572 // necessary.
573 IncrementLazyKeepaliveCount(extension);
574 DecrementLazyKeepaliveCount(extension);
578 void ProcessManager::CloseBackgroundHosts() {
579 STLDeleteElements(&background_hosts_);
582 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
583 const ImpulseCallbackForTesting& callback) {
584 keepalive_impulse_callback_for_testing_ = callback;
587 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
588 const ImpulseCallbackForTesting& callback) {
589 keepalive_impulse_decrement_callback_for_testing_ = callback;
592 // static
593 void ProcessManager::SetEventPageIdleTimeForTesting(unsigned idle_time_msec) {
594 CHECK_GT(idle_time_msec, 0u); // OnKeepaliveImpulseCheck requires non zero.
595 g_event_page_idle_time_msec = idle_time_msec;
598 // static
599 void ProcessManager::SetEventPageSuspendingTimeForTesting(
600 unsigned suspending_time_msec) {
601 g_event_page_suspending_time_msec = suspending_time_msec;
604 ////////////////////////////////////////////////////////////////////////////////
605 // Private
607 void ProcessManager::Observe(int type,
608 const content::NotificationSource& source,
609 const content::NotificationDetails& details) {
610 TRACE_EVENT0("browser,startup", "ProcessManager::Observe");
611 switch (type) {
612 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED: {
613 // TODO(jamescook): Convert this to use ExtensionSystem::ready() instead
614 // of a notification.
615 SCOPED_UMA_HISTOGRAM_TIMER("Extensions.ProcessManagerStartupHostsTime");
616 MaybeCreateStartupBackgroundHosts();
617 break;
619 case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
620 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
621 if (background_hosts_.erase(host)) {
622 ClearBackgroundPageData(host->extension()->id());
623 background_page_data_[host->extension()->id()].since_suspended.reset(
624 new base::ElapsedTimer());
626 break;
628 case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
629 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
630 if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
631 CloseBackgroundHost(host);
633 break;
635 default:
636 NOTREACHED();
640 void ProcessManager::OnExtensionLoaded(BrowserContext* browser_context,
641 const Extension* extension) {
642 if (ExtensionSystem::Get(browser_context)->ready().is_signaled()) {
643 // The extension system is ready, so create the background host.
644 CreateBackgroundHostForExtensionLoad(this, extension);
648 void ProcessManager::OnExtensionUnloaded(
649 BrowserContext* browser_context,
650 const Extension* extension,
651 UnloadedExtensionInfo::Reason reason) {
652 ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
653 if (host != nullptr)
654 CloseBackgroundHost(host);
655 UnregisterExtension(extension->id());
658 void ProcessManager::CreateStartupBackgroundHosts() {
659 DCHECK(!startup_background_hosts_created_);
660 for (const scoped_refptr<const Extension>& extension :
661 extension_registry_->enabled_extensions()) {
662 CreateBackgroundHostForExtensionLoad(this, extension.get());
663 FOR_EACH_OBSERVER(ProcessManagerObserver,
664 observer_list_,
665 OnBackgroundHostStartup(extension.get()));
669 void ProcessManager::OnBackgroundHostCreated(ExtensionHost* host) {
670 DCHECK_EQ(browser_context_, host->browser_context());
671 background_hosts_.insert(host);
673 if (BackgroundInfo::HasLazyBackgroundPage(host->extension())) {
674 linked_ptr<base::ElapsedTimer> since_suspended(
675 background_page_data_[host->extension()->id()].
676 since_suspended.release());
677 if (since_suspended.get()) {
678 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
679 since_suspended->Elapsed());
682 FOR_EACH_OBSERVER(ProcessManagerObserver, observer_list_,
683 OnBackgroundHostCreated(host));
686 void ProcessManager::CloseBackgroundHost(ExtensionHost* host) {
687 ExtensionId extension_id = host->extension_id();
688 CHECK(host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
689 delete host;
690 // |host| should deregister itself from our structures.
691 CHECK(background_hosts_.find(host) == background_hosts_.end());
693 FOR_EACH_OBSERVER(ProcessManagerObserver,
694 observer_list_,
695 OnBackgroundHostClose(extension_id));
698 void ProcessManager::AcquireLazyKeepaliveCountForFrame(
699 content::RenderFrameHost* render_frame_host) {
700 ExtensionRenderFrames::iterator it =
701 all_extension_frames_.find(render_frame_host);
702 if (it == all_extension_frames_.end())
703 return;
705 ExtensionRenderFrameData& data = it->second;
706 if (data.CanKeepalive() && !data.has_keepalive) {
707 const Extension* extension =
708 GetExtensionForRenderFrameHost(render_frame_host);
709 if (extension) {
710 IncrementLazyKeepaliveCount(extension);
711 data.has_keepalive = true;
716 void ProcessManager::ReleaseLazyKeepaliveCountForFrame(
717 content::RenderFrameHost* render_frame_host) {
718 ExtensionRenderFrames::iterator iter =
719 all_extension_frames_.find(render_frame_host);
720 if (iter == all_extension_frames_.end())
721 return;
723 ExtensionRenderFrameData& data = iter->second;
724 if (data.CanKeepalive() && data.has_keepalive) {
725 const Extension* extension =
726 GetExtensionForRenderFrameHost(render_frame_host);
727 if (extension) {
728 DecrementLazyKeepaliveCount(extension);
729 data.has_keepalive = false;
734 void ProcessManager::DecrementLazyKeepaliveCount(
735 const std::string& extension_id) {
736 int& count = background_page_data_[extension_id].lazy_keepalive_count;
737 DCHECK(count > 0 ||
738 !extension_registry_->enabled_extensions().Contains(extension_id));
740 // If we reach a zero keepalive count when the lazy background page is about
741 // to be closed, incrementing close_sequence_id will cancel the close
742 // sequence and cause the background page to linger. So check is_closing
743 // before initiating another close sequence.
744 if (--count == 0 && !background_page_data_[extension_id].is_closing) {
745 background_page_data_[extension_id].close_sequence_id =
746 ++last_background_close_sequence_id_;
747 base::MessageLoop::current()->PostDelayedTask(
748 FROM_HERE,
749 base::Bind(&ProcessManager::OnLazyBackgroundPageIdle,
750 weak_ptr_factory_.GetWeakPtr(),
751 extension_id,
752 last_background_close_sequence_id_),
753 base::TimeDelta::FromMilliseconds(g_event_page_idle_time_msec));
757 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
758 // have been made for at least g_event_page_idle_time_msec. In the best case an
759 // impulse was made just before being cleared, and the decrement will occur
760 // g_event_page_idle_time_msec later, causing a 2 * g_event_page_idle_time_msec
761 // total time for extension to be shut down based on impulses. Worst case is
762 // an impulse just after a clear, adding one check cycle and resulting in 3x
763 // total time.
764 void ProcessManager::OnKeepaliveImpulseCheck() {
765 for (BackgroundPageDataMap::iterator i = background_page_data_.begin();
766 i != background_page_data_.end();
767 ++i) {
768 if (i->second.previous_keepalive_impulse && !i->second.keepalive_impulse) {
769 DecrementLazyKeepaliveCount(i->first);
770 if (!keepalive_impulse_decrement_callback_for_testing_.is_null()) {
771 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
772 keepalive_impulse_decrement_callback_for_testing_;
773 callback_may_clear_callbacks_reentrantly.Run(i->first);
777 i->second.previous_keepalive_impulse = i->second.keepalive_impulse;
778 i->second.keepalive_impulse = false;
781 // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
782 // tests there will be no message loop. In that event don't schedule tasks.
783 if (base::MessageLoop::current()) {
784 base::MessageLoop::current()->PostDelayedTask(
785 FROM_HERE,
786 base::Bind(&ProcessManager::OnKeepaliveImpulseCheck,
787 weak_ptr_factory_.GetWeakPtr()),
788 base::TimeDelta::FromMilliseconds(g_event_page_idle_time_msec));
792 void ProcessManager::OnLazyBackgroundPageIdle(const std::string& extension_id,
793 uint64 sequence_id) {
794 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
795 if (host && !background_page_data_[extension_id].is_closing &&
796 sequence_id == background_page_data_[extension_id].close_sequence_id) {
797 // Tell the renderer we are about to close. This is a simple ping that the
798 // renderer will respond to. The purpose is to control sequencing: if the
799 // extension remains idle until the renderer responds with an ACK, then we
800 // know that the extension process is ready to shut down. If our
801 // close_sequence_id has already changed, then we would ignore the
802 // ShouldSuspendAck, so we don't send the ping.
803 host->render_process_host()->Send(new ExtensionMsg_ShouldSuspend(
804 extension_id, sequence_id));
808 void ProcessManager::OnLazyBackgroundPageActive(
809 const std::string& extension_id) {
810 if (!background_page_data_[extension_id].is_closing) {
811 // Cancel the current close sequence by changing the close_sequence_id,
812 // which causes us to ignore the next ShouldSuspendAck.
813 background_page_data_[extension_id].close_sequence_id =
814 ++last_background_close_sequence_id_;
818 void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id,
819 uint64 sequence_id) {
820 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
821 if (host &&
822 sequence_id == background_page_data_[extension_id].close_sequence_id) {
823 // Close remaining views.
824 std::vector<content::RenderFrameHost*> frames_to_close;
825 for (const auto& key_value : all_extension_frames_) {
826 if (key_value.second.CanKeepalive() &&
827 GetExtensionID(key_value.first) == extension_id) {
828 DCHECK(!key_value.second.has_keepalive);
829 frames_to_close.push_back(key_value.first);
832 for (content::RenderFrameHost* frame : frames_to_close) {
833 frame->GetRenderViewHost()->ClosePage();
834 // RenderViewHost::ClosePage() may result in calling
835 // UnregisterRenderViewHost() asynchronously and may cause race conditions
836 // when the background page is reloaded.
837 // To avoid this, unregister the view now.
838 UnregisterRenderFrameHost(frame);
841 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
842 if (host)
843 CloseBackgroundHost(host);
847 void ProcessManager::OnDevToolsStateChanged(
848 content::DevToolsAgentHost* agent_host,
849 bool attached) {
850 content::WebContents* web_contents = agent_host->GetWebContents();
851 // Ignore unrelated notifications.
852 if (!web_contents || web_contents->GetBrowserContext() != browser_context_)
853 return;
854 if (GetViewType(web_contents) != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
855 return;
856 const Extension* extension =
857 extension_registry_->enabled_extensions().GetByID(
858 GetExtensionIdForSiteInstance(web_contents->GetSiteInstance()));
859 if (!extension)
860 return;
861 if (attached) {
862 // Keep the lazy background page alive while it's being inspected.
863 CancelSuspend(extension);
864 IncrementLazyKeepaliveCount(extension);
865 } else {
866 DecrementLazyKeepaliveCount(extension);
870 void ProcessManager::UnregisterExtension(const std::string& extension_id) {
871 // The lazy_keepalive_count may be greater than zero at this point because
872 // RenderFrameHosts are still alive. During extension reloading, they will
873 // decrement the lazy_keepalive_count to negative for the new extension
874 // instance when they are destroyed. Since we are erasing the background page
875 // data for the unloaded extension, unregister the RenderFrameHosts too.
876 for (ExtensionRenderFrames::iterator it = all_extension_frames_.begin();
877 it != all_extension_frames_.end(); ) {
878 content::RenderFrameHost* host = it->first;
879 if (GetExtensionID(host) == extension_id) {
880 all_extension_frames_.erase(it++);
881 FOR_EACH_OBSERVER(ProcessManagerObserver,
882 observer_list_,
883 OnExtensionFrameUnregistered(extension_id, host));
884 } else {
885 ++it;
889 background_page_data_.erase(extension_id);
892 void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) {
893 background_page_data_.erase(extension_id);
895 // Re-register all RenderViews for this extension. We do this to restore
896 // the lazy_keepalive_count (if any) to properly reflect the number of open
897 // views.
898 for (const auto& key_value : all_extension_frames_) {
899 // Do not increment the count when |has_keepalive| is false
900 // (i.e. ReleaseLazyKeepaliveCountForView() was called).
901 if (GetExtensionID(key_value.first) == extension_id &&
902 key_value.second.has_keepalive) {
903 const Extension* extension =
904 GetExtensionForRenderFrameHost(key_value.first);
905 if (extension)
906 IncrementLazyKeepaliveCount(extension);
912 // IncognitoProcessManager
915 IncognitoProcessManager::IncognitoProcessManager(
916 BrowserContext* incognito_context,
917 BrowserContext* original_context,
918 ExtensionRegistry* extension_registry)
919 : ProcessManager(incognito_context, original_context, extension_registry) {
920 DCHECK(incognito_context->IsOffTheRecord());
923 bool IncognitoProcessManager::CreateBackgroundHost(const Extension* extension,
924 const GURL& url) {
925 if (IncognitoInfo::IsSplitMode(extension)) {
926 if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
927 extension->id(), browser_context()))
928 return ProcessManager::CreateBackgroundHost(extension, url);
929 } else {
930 // Do nothing. If an extension is spanning, then its original-profile
931 // background page is shared with incognito, so we don't create another.
933 return false;
936 scoped_refptr<content::SiteInstance>
937 IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) {
938 const Extension* extension =
939 extension_registry_->enabled_extensions().GetExtensionOrAppByURL(url);
940 if (extension && !IncognitoInfo::IsSplitMode(extension)) {
941 BrowserContext* original_context =
942 ExtensionsBrowserClient::Get()->GetOriginalContext(browser_context());
943 return ProcessManager::Get(original_context)->GetSiteInstanceForURL(url);
946 return ProcessManager::GetSiteInstanceForURL(url);
949 } // namespace extensions