summaryrefslogtreecommitdiffstats
path: root/extensions/browser/process_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/browser/process_manager.cc')
-rw-r--r--extensions/browser/process_manager.cc836
1 files changed, 836 insertions, 0 deletions
diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc
new file mode 100644
index 0000000..f5134d1
--- /dev/null
+++ b/extensions/browser/process_manager.cc
@@ -0,0 +1,836 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/process_manager.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/api/runtime/runtime_api.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/common/extensions/background_info.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_messages.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_manager.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "content/public/common/renderer_preferences.h"
+#include "extensions/browser/extensions_browser_client.h"
+#include "extensions/browser/view_type_utils.h"
+#include "extensions/common/manifest_handlers/incognito_info.h"
+#include "extensions/common/switches.h"
+
+#if defined(OS_MACOSX)
+#include "chrome/browser/extensions/extension_host_mac.h"
+#endif
+
+using content::BrowserContext;
+using content::RenderViewHost;
+using content::SiteInstance;
+using content::WebContents;
+
+namespace extensions {
+class RenderViewHostDestructionObserver;
+}
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(
+ extensions::RenderViewHostDestructionObserver);
+
+namespace extensions {
+
+namespace {
+
+std::string GetExtensionID(RenderViewHost* render_view_host) {
+ // This works for both apps and extensions because the site has been
+ // normalized to the extension URL for apps.
+ if (!render_view_host->GetSiteInstance())
+ return std::string();
+
+ return render_view_host->GetSiteInstance()->GetSiteURL().host();
+}
+
+void OnRenderViewHostUnregistered(BrowserContext* context,
+ RenderViewHost* render_view_host) {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED,
+ content::Source<BrowserContext>(context),
+ content::Details<RenderViewHost>(render_view_host));
+}
+
+// Incognito profiles use this process manager. It is mostly a shim that decides
+// whether to fall back on the original profile's ProcessManager based
+// on whether a given extension uses "split" or "spanning" incognito behavior.
+class IncognitoProcessManager : public ProcessManager {
+ public:
+ IncognitoProcessManager(BrowserContext* incognito_context,
+ BrowserContext* original_context);
+ virtual ~IncognitoProcessManager();
+ virtual ExtensionHost* CreateBackgroundHost(const Extension* extension,
+ const GURL& url) OVERRIDE;
+ virtual SiteInstance* GetSiteInstanceForURL(const GURL& url) OVERRIDE;
+
+ private:
+ // Returns true if the extension is allowed to run in incognito mode.
+ bool IsIncognitoEnabled(const Extension* extension);
+
+ ProcessManager* original_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager);
+};
+
+static void CreateBackgroundHostForExtensionLoad(
+ ProcessManager* manager, const Extension* extension) {
+ DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
+ if (BackgroundInfo::HasPersistentBackgroundPage(extension))
+ manager->CreateBackgroundHost(extension,
+ BackgroundInfo::GetBackgroundURL(extension));
+}
+
+} // namespace
+
+class RenderViewHostDestructionObserver
+ : public content::WebContentsObserver,
+ public content::WebContentsUserData<RenderViewHostDestructionObserver> {
+ public:
+ virtual ~RenderViewHostDestructionObserver() {}
+
+ private:
+ explicit RenderViewHostDestructionObserver(WebContents* web_contents)
+ : WebContentsObserver(web_contents) {
+ BrowserContext* context = web_contents->GetBrowserContext();
+ process_manager_ =
+ ExtensionSystem::GetForBrowserContext(context)->process_manager();
+ }
+
+ friend class content::WebContentsUserData<RenderViewHostDestructionObserver>;
+
+ // content::WebContentsObserver overrides.
+ virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
+ process_manager_->UnregisterRenderViewHost(render_view_host);
+ }
+
+ ProcessManager* process_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver);
+};
+
+struct ProcessManager::BackgroundPageData {
+ // The count of things keeping the lazy background page alive.
+ int lazy_keepalive_count;
+
+ // This is used with the ShouldSuspend message, to ensure that the extension
+ // remained idle between sending the message and receiving the ack.
+ int close_sequence_id;
+
+ // True if the page responded to the ShouldSuspend message and is currently
+ // dispatching the suspend event. During this time any events that arrive will
+ // cancel the suspend process and an onSuspendCanceled event will be
+ // dispatched to the page.
+ bool is_closing;
+
+ // Keeps track of when this page was last suspended. Used for perf metrics.
+ linked_ptr<base::ElapsedTimer> since_suspended;
+
+ BackgroundPageData()
+ : lazy_keepalive_count(0), close_sequence_id(0), is_closing(false) {}
+};
+
+//
+// ProcessManager
+//
+
+// static
+ProcessManager* ProcessManager::Create(BrowserContext* context) {
+ if (context->IsOffTheRecord()) {
+ BrowserContext* original_context =
+ ExtensionsBrowserClient::Get()->GetOriginalContext(context);
+ return new IncognitoProcessManager(context, original_context);
+ }
+ return new ProcessManager(context, context);
+}
+
+ProcessManager::ProcessManager(BrowserContext* context,
+ BrowserContext* original_context)
+ : site_instance_(SiteInstance::Create(context)),
+ defer_background_host_creation_(false),
+ startup_background_hosts_created_(false),
+ devtools_callback_(base::Bind(
+ &ProcessManager::OnDevToolsStateChanged,
+ base::Unretained(this))),
+ weak_ptr_factory_(this) {
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
+ content::Source<BrowserContext>(original_context));
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
+ content::Source<BrowserContext>(original_context));
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
+ content::Source<BrowserContext>(original_context));
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
+ content::Source<BrowserContext>(context));
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
+ content::Source<BrowserContext>(context));
+ registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
+ content::NotificationService::AllSources());
+ registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
+ content::NotificationService::AllSources());
+ registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
+ content::Source<BrowserContext>(original_context));
+ registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
+ content::Source<BrowserContext>(context));
+ if (context->IsOffTheRecord()) {
+ registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
+ content::Source<BrowserContext>(original_context));
+ }
+
+ event_page_idle_time_ = base::TimeDelta::FromSeconds(10);
+ unsigned idle_time_sec = 0;
+ if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ extensions::switches::kEventPageIdleTime), &idle_time_sec)) {
+ event_page_idle_time_ = base::TimeDelta::FromSeconds(idle_time_sec);
+ }
+ event_page_suspending_time_ = base::TimeDelta::FromSeconds(5);
+ unsigned suspending_time_sec = 0;
+ if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ extensions::switches::kEventPageSuspendingTime),
+ &suspending_time_sec)) {
+ event_page_suspending_time_ =
+ base::TimeDelta::FromSeconds(suspending_time_sec);
+ }
+
+ content::DevToolsManager::GetInstance()->AddAgentStateCallback(
+ devtools_callback_);
+}
+
+ProcessManager::~ProcessManager() {
+ CloseBackgroundHosts();
+ DCHECK(background_hosts_.empty());
+ content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
+ devtools_callback_);
+}
+
+const ProcessManager::ViewSet ProcessManager::GetAllViews() const {
+ ViewSet result;
+ for (ExtensionRenderViews::const_iterator iter =
+ all_extension_views_.begin();
+ iter != all_extension_views_.end(); ++iter) {
+ result.insert(iter->first);
+ }
+ return result;
+}
+
+ExtensionHost* ProcessManager::CreateBackgroundHost(const Extension* extension,
+ const GURL& url) {
+ DVLOG(1) << "CreateBackgroundHost " << url.spec();
+ // Hosted apps are taken care of from BackgroundContentsService. Ignore them
+ // here.
+ if (extension->is_hosted_app())
+ return NULL;
+
+ // Don't create multiple background hosts for an extension.
+ if (ExtensionHost* host = GetBackgroundHostForExtension(extension->id()))
+ return host; // TODO(kalman): return NULL here? It might break things...
+
+ ExtensionHost* host =
+#if defined(OS_MACOSX)
+ new ExtensionHostMac(
+ extension, GetSiteInstanceForURL(url), url,
+ VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
+#else
+ new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
+ VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
+#endif
+
+ host->CreateRenderViewSoon();
+ OnBackgroundHostCreated(host);
+ return host;
+}
+
+ExtensionHost* ProcessManager::GetBackgroundHostForExtension(
+ const std::string& extension_id) {
+ for (ExtensionHostSet::iterator iter = background_hosts_.begin();
+ iter != background_hosts_.end(); ++iter) {
+ ExtensionHost* host = *iter;
+ if (host->extension_id() == extension_id)
+ return host;
+ }
+ return NULL;
+}
+
+std::set<RenderViewHost*> ProcessManager::GetRenderViewHostsForExtension(
+ const std::string& extension_id) {
+ std::set<RenderViewHost*> result;
+
+ SiteInstance* site_instance = GetSiteInstanceForURL(
+ Extension::GetBaseURLFromExtensionId(extension_id));
+ if (!site_instance)
+ return result;
+
+ // Gather up all the views for that site.
+ for (ExtensionRenderViews::iterator view = all_extension_views_.begin();
+ view != all_extension_views_.end(); ++view) {
+ if (view->first->GetSiteInstance() == site_instance)
+ result.insert(view->first);
+ }
+
+ return result;
+}
+
+const Extension* ProcessManager::GetExtensionForRenderViewHost(
+ RenderViewHost* render_view_host) {
+ if (!render_view_host->GetSiteInstance())
+ return NULL;
+
+ ExtensionService* service = ExtensionSystem::GetForBrowserContext(
+ GetBrowserContext())->extension_service();
+ if (!service)
+ return NULL;
+
+ return service->extensions()->GetByID(GetExtensionID(render_view_host));
+}
+
+void ProcessManager::UnregisterRenderViewHost(
+ RenderViewHost* render_view_host) {
+ ExtensionRenderViews::iterator view =
+ all_extension_views_.find(render_view_host);
+ if (view == all_extension_views_.end())
+ return;
+
+ OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host);
+ ViewType view_type = view->second;
+ all_extension_views_.erase(view);
+
+ // Keepalive count, balanced in RegisterRenderViewHost.
+ if (view_type != VIEW_TYPE_INVALID &&
+ view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
+ const Extension* extension = GetExtensionForRenderViewHost(
+ render_view_host);
+ if (extension)
+ DecrementLazyKeepaliveCount(extension);
+ }
+}
+
+void ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) {
+ const Extension* extension = GetExtensionForRenderViewHost(
+ render_view_host);
+ if (!extension)
+ return;
+
+ WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
+ all_extension_views_[render_view_host] = GetViewType(web_contents);
+
+ // Keep the lazy background page alive as long as any non-background-page
+ // extension views are visible. Keepalive count balanced in
+ // UnregisterRenderViewHost.
+ IncrementLazyKeepaliveCountForView(render_view_host);
+}
+
+SiteInstance* ProcessManager::GetSiteInstanceForURL(const GURL& url) {
+ return site_instance_->GetRelatedSiteInstance(url);
+}
+
+bool ProcessManager::IsBackgroundHostClosing(const std::string& extension_id) {
+ ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
+ return (host && background_page_data_[extension_id].is_closing);
+}
+
+int ProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
+ if (!BackgroundInfo::HasLazyBackgroundPage(extension))
+ return 0;
+
+ return background_page_data_[extension->id()].lazy_keepalive_count;
+}
+
+int ProcessManager::IncrementLazyKeepaliveCount(const Extension* extension) {
+ if (!BackgroundInfo::HasLazyBackgroundPage(extension))
+ return 0;
+
+ int& count = background_page_data_[extension->id()].lazy_keepalive_count;
+ if (++count == 1)
+ OnLazyBackgroundPageActive(extension->id());
+
+ return count;
+}
+
+int ProcessManager::DecrementLazyKeepaliveCount(const Extension* extension) {
+ if (!BackgroundInfo::HasLazyBackgroundPage(extension))
+ return 0;
+
+ int& count = background_page_data_[extension->id()].lazy_keepalive_count;
+ DCHECK_GT(count, 0);
+
+ // If we reach a zero keepalive count when the lazy background page is about
+ // to be closed, incrementing close_sequence_id will cancel the close
+ // sequence and cause the background page to linger. So check is_closing
+ // before initiating another close sequence.
+ if (--count == 0 && !background_page_data_[extension->id()].is_closing) {
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ProcessManager::OnLazyBackgroundPageIdle,
+ weak_ptr_factory_.GetWeakPtr(), extension->id(),
+ ++background_page_data_[extension->id()].close_sequence_id),
+ event_page_idle_time_);
+ }
+
+ return count;
+}
+
+void ProcessManager::IncrementLazyKeepaliveCountForView(
+ RenderViewHost* render_view_host) {
+ WebContents* web_contents =
+ WebContents::FromRenderViewHost(render_view_host);
+ ViewType view_type = GetViewType(web_contents);
+ if (view_type != VIEW_TYPE_INVALID &&
+ view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
+ const Extension* extension = GetExtensionForRenderViewHost(
+ render_view_host);
+ if (extension)
+ IncrementLazyKeepaliveCount(extension);
+ }
+}
+
+void ProcessManager::OnLazyBackgroundPageIdle(const std::string& extension_id,
+ int sequence_id) {
+ ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
+ if (host && !background_page_data_[extension_id].is_closing &&
+ sequence_id == background_page_data_[extension_id].close_sequence_id) {
+ // Tell the renderer we are about to close. This is a simple ping that the
+ // renderer will respond to. The purpose is to control sequencing: if the
+ // extension remains idle until the renderer responds with an ACK, then we
+ // know that the extension process is ready to shut down. If our
+ // close_sequence_id has already changed, then we would ignore the
+ // ShouldSuspendAck, so we don't send the ping.
+ host->render_view_host()->Send(new ExtensionMsg_ShouldSuspend(
+ extension_id, sequence_id));
+ }
+}
+
+void ProcessManager::OnLazyBackgroundPageActive(
+ const std::string& extension_id) {
+ ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
+ if (host && !background_page_data_[extension_id].is_closing) {
+ // Cancel the current close sequence by changing the close_sequence_id,
+ // which causes us to ignore the next ShouldSuspendAck.
+ ++background_page_data_[extension_id].close_sequence_id;
+ }
+}
+
+void ProcessManager::OnShouldSuspendAck(const std::string& extension_id,
+ int sequence_id) {
+ ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
+ if (host &&
+ sequence_id == background_page_data_[extension_id].close_sequence_id) {
+ host->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id));
+ }
+}
+
+void ProcessManager::OnSuspendAck(const std::string& extension_id) {
+ background_page_data_[extension_id].is_closing = true;
+ int sequence_id = background_page_data_[extension_id].close_sequence_id;
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ProcessManager::CloseLazyBackgroundPageNow,
+ weak_ptr_factory_.GetWeakPtr(), extension_id, sequence_id),
+ event_page_suspending_time_);
+}
+
+void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id,
+ int sequence_id) {
+ ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
+ if (host &&
+ sequence_id == background_page_data_[extension_id].close_sequence_id) {
+ ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
+ if (host)
+ CloseBackgroundHost(host);
+ }
+}
+
+void ProcessManager::OnNetworkRequestStarted(RenderViewHost* render_view_host) {
+ ExtensionHost* host = GetBackgroundHostForExtension(
+ GetExtensionID(render_view_host));
+ if (host && host->render_view_host() == render_view_host)
+ IncrementLazyKeepaliveCount(host->extension());
+}
+
+void ProcessManager::OnNetworkRequestDone(RenderViewHost* render_view_host) {
+ ExtensionHost* host = GetBackgroundHostForExtension(
+ GetExtensionID(render_view_host));
+ if (host && host->render_view_host() == render_view_host)
+ DecrementLazyKeepaliveCount(host->extension());
+}
+
+void ProcessManager::CancelSuspend(const Extension* extension) {
+ bool& is_closing = background_page_data_[extension->id()].is_closing;
+ ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
+ if (host && is_closing) {
+ is_closing = false;
+ host->render_view_host()->Send(
+ new ExtensionMsg_CancelSuspend(extension->id()));
+ // This increment / decrement is to simulate an instantaneous event. This
+ // has the effect of invalidating close_sequence_id, preventing any in
+ // progress closes from completing and starting a new close process if
+ // necessary.
+ IncrementLazyKeepaliveCount(extension);
+ DecrementLazyKeepaliveCount(extension);
+ }
+}
+
+void ProcessManager::DeferBackgroundHostCreation(bool defer) {
+ bool previous = defer_background_host_creation_;
+ defer_background_host_creation_ = defer;
+
+ // If we were deferred, and we switch to non-deferred, then create the
+ // background hosts.
+ if (previous && !defer_background_host_creation_)
+ CreateBackgroundHostsForProfileStartup();
+}
+
+void ProcessManager::OnBrowserWindowReady() {
+ ExtensionService* service = ExtensionSystem::GetForBrowserContext(
+ GetBrowserContext())->extension_service();
+ // On Chrome OS, a login screen is implemented as a browser.
+ // This browser has no extension service. In this case,
+ // service will be NULL.
+ if (!service || !service->is_ready())
+ return;
+
+ CreateBackgroundHostsForProfileStartup();
+}
+
+content::BrowserContext* ProcessManager::GetBrowserContext() const {
+ return site_instance_->GetBrowserContext();
+}
+
+void ProcessManager::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_EXTENSIONS_READY:
+ case chrome::NOTIFICATION_PROFILE_CREATED: {
+ CreateBackgroundHostsForProfileStartup();
+ break;
+ }
+
+ case chrome::NOTIFICATION_EXTENSION_LOADED: {
+ BrowserContext* context = content::Source<BrowserContext>(source).ptr();
+ ExtensionService* service =
+ ExtensionSystem::GetForBrowserContext(context)->extension_service();
+ if (service->is_ready()) {
+ const Extension* extension =
+ content::Details<const Extension>(details).ptr();
+ CreateBackgroundHostForExtensionLoad(this, extension);
+ }
+ break;
+ }
+
+ case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
+ const Extension* extension =
+ content::Details<UnloadedExtensionInfo>(details)->extension;
+ for (ExtensionHostSet::iterator iter = background_hosts_.begin();
+ iter != background_hosts_.end(); ++iter) {
+ ExtensionHost* host = *iter;
+ if (host->extension_id() == extension->id()) {
+ CloseBackgroundHost(host);
+ break;
+ }
+ }
+ UnregisterExtension(extension->id());
+ break;
+ }
+
+ case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
+ ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
+ if (background_hosts_.erase(host)) {
+ ClearBackgroundPageData(host->extension()->id());
+ background_page_data_[host->extension()->id()].since_suspended.reset(
+ new base::ElapsedTimer());
+ }
+ break;
+ }
+
+ case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
+ ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
+ if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
+ CloseBackgroundHost(host);
+ }
+ break;
+ }
+
+ case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
+ // We get this notification both for new WebContents and when one
+ // has its RenderViewHost replaced (e.g. when a user does a cross-site
+ // navigation away from an extension URL). For the replaced case, we must
+ // unregister the old RVH so it doesn't count as an active view that would
+ // keep the event page alive.
+ WebContents* contents = content::Source<WebContents>(source).ptr();
+ if (contents->GetBrowserContext() != GetBrowserContext())
+ break;
+
+ typedef std::pair<RenderViewHost*, RenderViewHost*> RVHPair;
+ RVHPair* switched_details = content::Details<RVHPair>(details).ptr();
+ if (switched_details->first)
+ UnregisterRenderViewHost(switched_details->first);
+
+ // The above will unregister a RVH when it gets swapped out with a new
+ // one. However we need to watch the WebContents to know when a RVH is
+ // deleted because the WebContents has gone away.
+ RenderViewHostDestructionObserver::CreateForWebContents(contents);
+ RegisterRenderViewHost(switched_details->second);
+ break;
+ }
+
+ case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: {
+ WebContents* contents = content::Source<WebContents>(source).ptr();
+ if (contents->GetBrowserContext() != GetBrowserContext())
+ break;
+ const Extension* extension = GetExtensionForRenderViewHost(
+ contents->GetRenderViewHost());
+ if (!extension)
+ return;
+
+ // RegisterRenderViewHost is called too early (before the process is
+ // available), so we need to wait until now to notify.
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED,
+ content::Source<BrowserContext>(GetBrowserContext()),
+ content::Details<RenderViewHost>(contents->GetRenderViewHost()));
+ break;
+ }
+
+ case chrome::NOTIFICATION_PROFILE_DESTROYED: {
+ // Close background hosts when the last browser is closed so that they
+ // have time to shutdown various objects on different threads. Our
+ // destructor is called too late in the shutdown sequence.
+ CloseBackgroundHosts();
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void ProcessManager::OnDevToolsStateChanged(
+ content::DevToolsAgentHost* agent_host,
+ bool attached) {
+ RenderViewHost* rvh = agent_host->GetRenderViewHost();
+ // Ignore unrelated notifications.
+ if (!rvh ||
+ rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() !=
+ GetBrowserContext())
+ return;
+ if (GetViewType(WebContents::FromRenderViewHost(rvh)) !=
+ VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
+ return;
+ const Extension* extension = GetExtensionForRenderViewHost(rvh);
+ if (!extension)
+ return;
+ if (attached) {
+ // Keep the lazy background page alive while it's being inspected.
+ CancelSuspend(extension);
+ IncrementLazyKeepaliveCount(extension);
+ } else {
+ DecrementLazyKeepaliveCount(extension);
+ }
+}
+
+void ProcessManager::CreateBackgroundHostsForProfileStartup() {
+ if (startup_background_hosts_created_)
+ return;
+
+ // Don't load background hosts now if the loading should be deferred.
+ // Instead they will be loaded when a browser window for this profile
+ // (or an incognito profile from this profile) is ready, or when
+ // DeferBackgroundHostCreation is called with false.
+ if (DeferLoadingBackgroundHosts())
+ return;
+
+ ExtensionService* service = ExtensionSystem::GetForBrowserContext(
+ GetBrowserContext())->extension_service();
+ DCHECK(service);
+ for (ExtensionSet::const_iterator extension = service->extensions()->begin();
+ extension != service->extensions()->end(); ++extension) {
+ CreateBackgroundHostForExtensionLoad(this, extension->get());
+
+ RuntimeEventRouter::DispatchOnStartupEvent(GetBrowserContext(),
+ (*extension)->id());
+ }
+ startup_background_hosts_created_ = true;
+
+ // Background pages should only be loaded once. To prevent any further loads
+ // occurring, we remove the notification listeners.
+ BrowserContext* original_context =
+ ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
+ if (registrar_.IsRegistered(
+ this,
+ chrome::NOTIFICATION_PROFILE_CREATED,
+ content::Source<BrowserContext>(original_context))) {
+ registrar_.Remove(this,
+ chrome::NOTIFICATION_PROFILE_CREATED,
+ content::Source<BrowserContext>(original_context));
+ }
+ if (registrar_.IsRegistered(
+ this,
+ chrome::NOTIFICATION_EXTENSIONS_READY,
+ content::Source<BrowserContext>(original_context))) {
+ registrar_.Remove(this,
+ chrome::NOTIFICATION_EXTENSIONS_READY,
+ content::Source<BrowserContext>(original_context));
+ }
+}
+
+void ProcessManager::OnBackgroundHostCreated(ExtensionHost* host) {
+ DCHECK_EQ(GetBrowserContext(), host->browser_context());
+ background_hosts_.insert(host);
+
+ if (BackgroundInfo::HasLazyBackgroundPage(host->extension())) {
+ linked_ptr<base::ElapsedTimer> since_suspended(
+ background_page_data_[host->extension()->id()].
+ since_suspended.release());
+ if (since_suspended.get()) {
+ UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
+ since_suspended->Elapsed());
+ }
+ }
+}
+
+void ProcessManager::CloseBackgroundHost(ExtensionHost* host) {
+ CHECK(host->extension_host_type() ==
+ VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
+ delete host;
+ // |host| should deregister itself from our structures.
+ CHECK(background_hosts_.find(host) == background_hosts_.end());
+}
+
+void ProcessManager::CloseBackgroundHosts() {
+ for (ExtensionHostSet::iterator iter = background_hosts_.begin();
+ iter != background_hosts_.end(); ) {
+ ExtensionHostSet::iterator current = iter++;
+ delete *current;
+ }
+}
+
+void ProcessManager::UnregisterExtension(const std::string& extension_id) {
+ // The lazy_keepalive_count may be greater than zero at this point because
+ // RenderViewHosts are still alive. During extension reloading, they will
+ // decrement the lazy_keepalive_count to negative for the new extension
+ // instance when they are destroyed. Since we are erasing the background page
+ // data for the unloaded extension, unregister the RenderViewHosts too.
+ BrowserContext* context = GetBrowserContext();
+ for (ExtensionRenderViews::iterator it = all_extension_views_.begin();
+ it != all_extension_views_.end(); ) {
+ if (GetExtensionID(it->first) == extension_id) {
+ OnRenderViewHostUnregistered(context, it->first);
+ all_extension_views_.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+
+ background_page_data_.erase(extension_id);
+}
+
+void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) {
+ background_page_data_.erase(extension_id);
+
+ // Re-register all RenderViews for this extension. We do this to restore
+ // the lazy_keepalive_count (if any) to properly reflect the number of open
+ // views.
+ for (ExtensionRenderViews::const_iterator it = all_extension_views_.begin();
+ it != all_extension_views_.end(); ++it) {
+ if (GetExtensionID(it->first) == extension_id)
+ IncrementLazyKeepaliveCountForView(it->first);
+ }
+}
+
+bool ProcessManager::DeferLoadingBackgroundHosts() const {
+ // Don't load background hosts now if the loading should be deferred.
+ if (defer_background_host_creation_)
+ return true;
+
+ // The extensions embedder may have special rules about background hosts.
+ return ExtensionsBrowserClient::Get()->DeferLoadingBackgroundHosts(
+ GetBrowserContext());
+}
+
+//
+// IncognitoProcessManager
+//
+
+IncognitoProcessManager::IncognitoProcessManager(
+ BrowserContext* incognito_context,
+ BrowserContext* original_context)
+ : ProcessManager(incognito_context, original_context),
+ original_manager_(ExtensionSystem::GetForBrowserContext(
+ original_context)->process_manager()) {
+ DCHECK(incognito_context->IsOffTheRecord());
+
+ // The original profile will have its own ProcessManager to
+ // load the background pages of the spanning extensions. This process
+ // manager need only worry about the split mode extensions, which is handled
+ // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler.
+ registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY,
+ content::Source<BrowserContext>(original_context));
+ registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_CREATED,
+ content::Source<BrowserContext>(original_context));
+}
+
+IncognitoProcessManager::~IncognitoProcessManager() {
+ // TODO(yoz): This cleanup code belongs in the MenuManager.
+ // Remove "incognito" "split" mode context menu items.
+ ExtensionService* service = ExtensionSystem::GetForBrowserContext(
+ GetBrowserContext())->extension_service();
+ if (service)
+ service->menu_manager()->RemoveAllIncognitoContextItems();
+}
+
+ExtensionHost* IncognitoProcessManager::CreateBackgroundHost(
+ const Extension* extension, const GURL& url) {
+ if (IncognitoInfo::IsSplitMode(extension)) {
+ if (IsIncognitoEnabled(extension))
+ return ProcessManager::CreateBackgroundHost(extension, url);
+ } else {
+ // Do nothing. If an extension is spanning, then its original-profile
+ // background page is shared with incognito, so we don't create another.
+ }
+ return NULL;
+}
+
+SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) {
+ ExtensionService* service = ExtensionSystem::GetForBrowserContext(
+ GetBrowserContext())->extension_service();
+ if (service) {
+ const Extension* extension =
+ service->extensions()->GetExtensionOrAppByURL(url);
+ if (extension && !IncognitoInfo::IsSplitMode(extension)) {
+ return original_manager_->GetSiteInstanceForURL(url);
+ }
+ }
+ return ProcessManager::GetSiteInstanceForURL(url);
+}
+
+bool IncognitoProcessManager::IsIncognitoEnabled(const Extension* extension) {
+ // Keep in sync with duplicate in extension_info_map.cc.
+ ExtensionService* service = ExtensionSystem::GetForBrowserContext(
+ GetBrowserContext())->extension_service();
+ return extension_util::IsIncognitoEnabled(extension->id(), service);
+}
+
+} // namespace extensions