#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 "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_agent_host.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extensions_browser_client.h"
+#include "extensions/browser/notification_types.h"
+#include "extensions/browser/process_manager_delegate.h"
#include "extensions/browser/process_manager_observer.h"
#include "extensions/browser/view_type_utils.h"
#include "extensions/common/constants.h"
void OnRenderViewHostUnregistered(BrowserContext* context,
RenderViewHost* render_view_host) {
content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED,
+ extensions::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED,
content::Source<BrowserContext>(context),
content::Details<RenderViewHost>(render_view_host));
}
public:
IncognitoProcessManager(BrowserContext* incognito_context,
BrowserContext* original_context,
- ProcessManager* original_manager);
+ ProcessManager* original_manager,
+ ExtensionRegistry* extension_registry);
virtual ~IncognitoProcessManager() {}
virtual bool CreateBackgroundHost(const Extension* extension,
const GURL& url) OVERRIDE;
bool keepalive_impulse;
bool previous_keepalive_impulse;
- // 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;
+ // Stores the value of the incremented
+ // ProcessManager::last_background_close_sequence_id_ whenever the extension
+ // is active. A copy of the ID is also passed in the callbacks and IPC
+ // messages leading up to CloseLazyBackgroundPageNow. The process is aborted
+ // if the IDs ever differ due to new activity.
+ uint64 close_sequence_id;
+
// Keeps track of when this page was last suspended. Used for perf metrics.
linked_ptr<base::ElapsedTimer> since_suspended;
: lazy_keepalive_count(0),
keepalive_impulse(false),
previous_keepalive_impulse(false),
- close_sequence_id(0),
- is_closing(false) {}
+ is_closing(false),
+ close_sequence_id(0) {}
};
//
// static
ProcessManager* ProcessManager::Create(BrowserContext* context) {
+ ExtensionRegistry* extension_registry = ExtensionRegistry::Get(context);
ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get();
if (client->IsGuestSession(context)) {
// In the guest session, there is a single off-the-record context. Unlike
// created regardless of whether extensions use "spanning" or "split"
// incognito behavior.
BrowserContext* original_context = client->GetOriginalContext(context);
- return new ProcessManager(context, original_context);
+ return new ProcessManager(context, original_context, extension_registry);
}
if (context->IsOffTheRecord()) {
ProcessManager* original_manager =
ExtensionSystem::Get(original_context)->process_manager();
return new IncognitoProcessManager(
- context, original_context, original_manager);
+ context, original_context, original_manager, extension_registry);
}
- return new ProcessManager(context, context);
+ return new ProcessManager(context, context, extension_registry);
+}
+
+// static
+ProcessManager* ProcessManager::CreateForTesting(
+ BrowserContext* context,
+ ExtensionRegistry* extension_registry) {
+ DCHECK(!context->IsOffTheRecord());
+ return new ProcessManager(context, context, extension_registry);
}
// static
ProcessManager* ProcessManager::CreateIncognitoForTesting(
BrowserContext* incognito_context,
BrowserContext* original_context,
- ProcessManager* original_manager) {
+ ProcessManager* original_manager,
+ ExtensionRegistry* extension_registry) {
DCHECK(incognito_context->IsOffTheRecord());
DCHECK(!original_context->IsOffTheRecord());
- return new IncognitoProcessManager(
- incognito_context, original_context, original_manager);
+ return new IncognitoProcessManager(incognito_context,
+ original_context,
+ original_manager,
+ extension_registry);
}
ProcessManager::ProcessManager(BrowserContext* context,
- BrowserContext* original_context)
- : site_instance_(SiteInstance::Create(context)),
- 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,
+ BrowserContext* original_context,
+ ExtensionRegistry* extension_registry)
+ : site_instance_(SiteInstance::Create(context)),
+ extension_registry_(extension_registry),
+ startup_background_hosts_created_(false),
+ devtools_callback_(base::Bind(&ProcessManager::OnDevToolsStateChanged,
+ base::Unretained(this))),
+ last_background_close_sequence_id_(0),
+ weak_ptr_factory_(this) {
+ // ExtensionRegistry is shared between incognito and regular contexts.
+ DCHECK_EQ(original_context, extension_registry_->browser_context());
+ registrar_.Add(this,
+ extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
content::Source<BrowserContext>(original_context));
registrar_.Add(this,
- chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
+ extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
content::Source<BrowserContext>(original_context));
- registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
+ registrar_.Add(this,
+ extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
content::Source<BrowserContext>(original_context));
- registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
+ registrar_.Add(this,
+ extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
content::Source<BrowserContext>(context));
- registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
+ registrar_.Add(this,
+ extensions::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));
- }
// Note: event_page_idle_time_ must be sufficiently larger (e.g. 2x) than
// kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
const GURL& url) {
// Hosted apps are taken care of from BackgroundContentsService. Ignore them
// here.
- if (extension->is_hosted_app() ||
- !ExtensionsBrowserClient::Get()->
- IsBackgroundPageAllowed(GetBrowserContext())) {
+ if (extension->is_hosted_app())
+ return false;
+
+ // Don't create hosts if the embedder doesn't allow it.
+ ProcessManagerDelegate* delegate =
+ ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
+ if (delegate && !delegate->IsBackgroundPageAllowed(GetBrowserContext()))
return false;
- }
// Don't create multiple background hosts for an extension.
if (GetBackgroundHostForExtension(extension->id()))
if (!render_view_host->GetSiteInstance())
return NULL;
- ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
- if (!registry)
- return NULL;
-
- return registry->enabled_extensions().GetByID(
+ return extension_registry_->enabled_extensions().GetByID(
GetExtensionID(render_view_host));
}
const std::string& extension_id) {
int& count = background_page_data_[extension_id].lazy_keepalive_count;
DCHECK(count > 0 ||
- !ExtensionRegistry::Get(GetBrowserContext())
- ->enabled_extensions()
- .Contains(extension_id));
+ !extension_registry_->enabled_extensions().Contains(extension_id));
// 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) {
+ background_page_data_[extension_id].close_sequence_id =
+ ++last_background_close_sequence_id_;
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&ProcessManager::OnLazyBackgroundPageIdle,
- weak_ptr_factory_.GetWeakPtr(), extension_id,
- ++background_page_data_[extension_id].close_sequence_id),
+ weak_ptr_factory_.GetWeakPtr(),
+ extension_id,
+ last_background_close_sequence_id_),
event_page_idle_time_);
}
}
}
void ProcessManager::OnLazyBackgroundPageIdle(const std::string& extension_id,
- int sequence_id) {
+ uint64 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) {
void ProcessManager::OnLazyBackgroundPageActive(
const std::string& extension_id) {
- ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
- if (host && !background_page_data_[extension_id].is_closing) {
+ if (!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;
+ background_page_data_[extension_id].close_sequence_id =
+ ++last_background_close_sequence_id_;
}
}
void ProcessManager::OnShouldSuspendAck(const std::string& extension_id,
- int sequence_id) {
+ uint64 sequence_id) {
ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
if (host &&
sequence_id == background_page_data_[extension_id].close_sequence_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;
+ uint64 sequence_id = background_page_data_[extension_id].close_sequence_id;
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&ProcessManager::CloseLazyBackgroundPageNow,
}
void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id,
- int sequence_id) {
+ uint64 sequence_id) {
ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
if (host &&
sequence_id == background_page_data_[extension_id].close_sequence_id) {
}
}
-void ProcessManager::OnBrowserWindowReady() {
- // If the extension system isn't ready yet the background hosts will be
- // created via NOTIFICATION_EXTENSIONS_READY below.
- ExtensionSystem* system = ExtensionSystem::Get(GetBrowserContext());
- if (!system->ready().is_signaled())
- return;
-
- CreateBackgroundHostsForProfileStartup();
+void ProcessManager::CloseBackgroundHosts() {
+ for (ExtensionHostSet::iterator iter = background_hosts_.begin();
+ iter != background_hosts_.end();) {
+ ExtensionHostSet::iterator current = iter++;
+ delete *current;
+ }
}
content::BrowserContext* ProcessManager::GetBrowserContext() const {
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
- case chrome::NOTIFICATION_EXTENSIONS_READY:
- case chrome::NOTIFICATION_PROFILE_CREATED: {
- // 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.
- if (DeferLoadingBackgroundHosts())
- break;
-
- CreateBackgroundHostsForProfileStartup();
+ case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED: {
+ // TODO(jamescook): Convert this to use ExtensionSystem::ready() instead
+ // of a notification.
+ MaybeCreateStartupBackgroundHosts();
break;
}
- case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: {
+ case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: {
BrowserContext* context = content::Source<BrowserContext>(source).ptr();
ExtensionSystem* system = ExtensionSystem::Get(context);
if (system->ready().is_signaled()) {
break;
}
- case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
+ case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
const Extension* extension =
content::Details<UnloadedExtensionInfo>(details)->extension;
for (ExtensionHostSet::iterator iter = background_hosts_.begin();
break;
}
- case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
+ case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
if (background_hosts_.erase(host)) {
ClearBackgroundPageData(host->extension()->id());
break;
}
- case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
+ case extensions::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);
// 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,
+ extensions::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();
+ WebContents* web_contents = agent_host->GetWebContents();
// Ignore unrelated notifications.
- if (!rvh ||
- rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() !=
- GetBrowserContext())
+ if (!web_contents || web_contents->GetBrowserContext() != GetBrowserContext())
return;
- if (GetViewType(WebContents::FromRenderViewHost(rvh)) !=
- VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
+ if (GetViewType(web_contents) != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
return;
- const Extension* extension = GetExtensionForRenderViewHost(rvh);
+ const Extension* extension =
+ GetExtensionForRenderViewHost(web_contents->GetRenderViewHost());
if (!extension)
return;
if (attached) {
}
}
-void ProcessManager::CreateBackgroundHostsForProfileStartup() {
- if (startup_background_hosts_created_ ||
- !ExtensionsBrowserClient::Get()->
- IsBackgroundPageAllowed(GetBrowserContext())) {
+void ProcessManager::MaybeCreateStartupBackgroundHosts() {
+ if (startup_background_hosts_created_)
return;
- }
- const ExtensionSet& enabled_extensions =
- ExtensionRegistry::Get(GetBrowserContext())->enabled_extensions();
- for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
- extension != enabled_extensions.end();
- ++extension) {
- CreateBackgroundHostForExtensionLoad(this, extension->get());
+ // The embedder might disallow background pages entirely.
+ ProcessManagerDelegate* delegate =
+ ExtensionsBrowserClient::Get()->GetProcessManagerDelegate();
+ if (delegate && !delegate->IsBackgroundPageAllowed(GetBrowserContext()))
+ return;
- FOR_EACH_OBSERVER(ProcessManagerObserver,
- observer_list_,
- OnBackgroundHostStartup(*extension));
- }
+ // The embedder might want to defer background page loading. For example,
+ // Chrome defers background page loading when it is launched to show the app
+ // list, then triggers a load later when a browser window opens.
+ if (delegate &&
+ delegate->DeferCreatingStartupBackgroundHosts(GetBrowserContext()))
+ return;
+
+ CreateStartupBackgroundHosts();
startup_background_hosts_created_ = true;
// Background pages should only be loaded once. To prevent any further loads
ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
if (registrar_.IsRegistered(
this,
- chrome::NOTIFICATION_PROFILE_CREATED,
+ extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
content::Source<BrowserContext>(original_context))) {
registrar_.Remove(this,
- chrome::NOTIFICATION_PROFILE_CREATED,
+ extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
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::CreateStartupBackgroundHosts() {
+ DCHECK(!startup_background_hosts_created_);
+ const ExtensionSet& enabled_extensions =
+ extension_registry_->enabled_extensions();
+ for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
+ extension != enabled_extensions.end();
+ ++extension) {
+ CreateBackgroundHostForExtensionLoad(this, extension->get());
+
+ FOR_EACH_OBSERVER(ProcessManagerObserver,
+ observer_list_,
+ OnBackgroundHostStartup(*extension));
}
}
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
}
}
-bool ProcessManager::DeferLoadingBackgroundHosts() const {
- // 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* original_manager)
- : ProcessManager(incognito_context, original_context),
+ ProcessManager* original_manager,
+ ExtensionRegistry* extension_registry)
+ : ProcessManager(incognito_context, original_context, extension_registry),
original_manager_(original_manager) {
DCHECK(incognito_context->IsOffTheRecord());
// 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,
+ registrar_.Remove(this,
+ extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
content::Source<BrowserContext>(original_context));
}
}
SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) {
- ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
- if (registry) {
- const Extension* extension =
- registry->enabled_extensions().GetExtensionOrAppByURL(url);
- if (extension && !IncognitoInfo::IsSplitMode(extension)) {
- return original_manager_->GetSiteInstanceForURL(url);
- }
- }
+ const Extension* extension =
+ extension_registry_->enabled_extensions().GetExtensionOrAppByURL(url);
+ if (extension && !IncognitoInfo::IsSplitMode(extension))
+ return original_manager_->GetSiteInstanceForURL(url);
+
return ProcessManager::GetSiteInstanceForURL(url);
}