summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-19 22:57:51 +0000
committermpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-19 22:57:51 +0000
commit7042b688fe403f1aa63e0ee5c44f63f4130e0392 (patch)
tree8fcad02d8a21f09df775f9ca2302fbec8900cb5e /chrome
parent9c46a89faedc01afa43d5332f362d9c01caaea68 (diff)
downloadchromium_src-7042b688fe403f1aa63e0ee5c44f63f4130e0392.zip
chromium_src-7042b688fe403f1aa63e0ee5c44f63f4130e0392.tar.gz
chromium_src-7042b688fe403f1aa63e0ee5c44f63f4130e0392.tar.bz2
Fix bug where transient pages would miss events dispatched while it was
unloading. The fix is a 2-parter: Part 1: lazy_background_queue.* now checks if the background page is unloading before allowing a task to run. If it is unloading, we wait until the page unloads, then reload it. Part 2: Part 1 exposed the problem which I had previously outlined in extension_process_manager.cc:403 - after we reload the page, we occasionally received messages from the renderer to decrement the keepalive count for the old page, throwing the count out of balance. The fix is to make sure we decrement keepalive counts only if the host we incremented them for is still active. BUG=123243 TEST=no Review URL: https://chromiumcodereview.appspot.com/10114015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133079 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/extensions/extension_event_router.cc25
-rw-r--r--chrome/browser/extensions/extension_event_router.h2
-rw-r--r--chrome/browser/extensions/extension_function.cc2
-rw-r--r--chrome/browser/extensions/extension_host.cc27
-rw-r--r--chrome/browser/extensions/extension_host.h3
-rw-r--r--chrome/browser/extensions/extension_message_service.cc86
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc19
-rw-r--r--chrome/browser/extensions/extension_process_manager.h4
-rw-r--r--chrome/browser/extensions/lazy_background_task_queue.cc60
-rw-r--r--chrome/browser/extensions/lazy_background_task_queue.h11
-rw-r--r--chrome/browser/renderer_host/chrome_render_message_filter.cc41
-rw-r--r--chrome/browser/renderer_host/chrome_render_message_filter.h3
-rw-r--r--chrome/common/extensions/extension_messages.h10
-rw-r--r--chrome/renderer/extensions/extension_custom_bindings.cc102
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.cc36
-rw-r--r--chrome/renderer/extensions/extension_helper.cc87
-rw-r--r--chrome/renderer/extensions/extension_helper.h12
17 files changed, 306 insertions, 224 deletions
diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc
index 908c06c..0b957df 100644
--- a/chrome/browser/extensions/extension_event_router.cc
+++ b/chrome/browser/extensions/extension_event_router.cc
@@ -405,20 +405,27 @@ void ExtensionEventRouter::MaybeLoadLazyBackgroundPage(
void ExtensionEventRouter::IncrementInFlightEvents(
Profile* profile, const Extension* extension) {
+ // Only increment in-flight events if the lazy background page is active,
+ // because that's the only time we'll get an ACK.
if (extension->has_lazy_background_page()) {
- profile->GetExtensionProcessManager()->IncrementLazyKeepaliveCount(
- extension);
+ ExtensionProcessManager* pm =
+ ExtensionSystem::Get(profile)->process_manager();
+ ExtensionHost* host = pm->GetBackgroundHostForExtension(extension->id());
+ if (host)
+ pm->IncrementLazyKeepaliveCount(extension);
}
}
-void ExtensionEventRouter::OnExtensionEventAck(
+void ExtensionEventRouter::OnEventAck(
Profile* profile, const std::string& extension_id) {
- const Extension* extension = profile->GetExtensionService()->extensions()->
- GetByID(extension_id);
- if (extension && extension->has_lazy_background_page()) {
- profile->GetExtensionProcessManager()->DecrementLazyKeepaliveCount(
- extension);
- }
+ ExtensionProcessManager* pm =
+ ExtensionSystem::Get(profile)->process_manager();
+ ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_id);
+ // The event ACK is routed to the background host, so this should never be
+ // NULL.
+ CHECK(host);
+ CHECK(host->extension()->has_lazy_background_page());
+ pm->DecrementLazyKeepaliveCount(host->extension());
}
void ExtensionEventRouter::DispatchPendingEvent(
diff --git a/chrome/browser/extensions/extension_event_router.h b/chrome/browser/extensions/extension_event_router.h
index 2b52cc8..faf80e9 100644
--- a/chrome/browser/extensions/extension_event_router.h
+++ b/chrome/browser/extensions/extension_event_router.h
@@ -121,7 +121,7 @@ class ExtensionEventRouter : public content::NotificationObserver {
const GURL& event_url);
// Record the Event Ack from the renderer. (One less event in-flight.)
- void OnExtensionEventAck(Profile* profile, const std::string& extension_id);
+ void OnEventAck(Profile* profile, const std::string& extension_id);
private:
// The details of an event to be dispatched.
diff --git a/chrome/browser/extensions/extension_function.cc b/chrome/browser/extensions/extension_function.cc
index b3b3105..b278df8 100644
--- a/chrome/browser/extensions/extension_function.cc
+++ b/chrome/browser/extensions/extension_function.cc
@@ -152,7 +152,7 @@ UIThreadExtensionFunction::UIThreadExtensionFunction()
}
UIThreadExtensionFunction::~UIThreadExtensionFunction() {
- if (dispatcher())
+ if (dispatcher() && render_view_host())
dispatcher()->OnExtensionFunctionCompleted(GetExtension());
}
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index ed4ee74..4a3900f 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -14,6 +14,7 @@
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_shutdown.h"
+#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
@@ -470,6 +471,11 @@ bool ExtensionHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ExtensionHost, message)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_EventAck, OnEventAck)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_IncrementLazyKeepaliveCount,
+ OnIncrementLazyKeepaliveCount)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_DecrementLazyKeepaliveCount,
+ OnDecrementLazyKeepaliveCount)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -479,6 +485,27 @@ void ExtensionHost::OnRequest(const ExtensionHostMsg_Request_Params& params) {
extension_function_dispatcher_.Dispatch(params, render_view_host());
}
+void ExtensionHost::OnEventAck() {
+ ExtensionEventRouter* router = ExtensionSystem::Get(profile_)->event_router();
+ if (router)
+ router->OnEventAck(profile_, extension_id());
+}
+
+void ExtensionHost::OnIncrementLazyKeepaliveCount() {
+ ExtensionProcessManager* pm =
+ ExtensionSystem::Get(profile_)->process_manager();
+ if (pm)
+ pm->IncrementLazyKeepaliveCount(extension());
+}
+
+void ExtensionHost::OnDecrementLazyKeepaliveCount() {
+ ExtensionProcessManager* pm =
+ ExtensionSystem::Get(profile_)->process_manager();
+ if (pm)
+ pm->DecrementLazyKeepaliveCount(extension());
+}
+
+
void ExtensionHost::RenderViewCreated(RenderViewHost* render_view_host) {
render_view_host_ = render_view_host;
diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h
index 835f62f..be0208a 100644
--- a/chrome/browser/extensions/extension_host.h
+++ b/chrome/browser/extensions/extension_host.h
@@ -188,6 +188,9 @@ class ExtensionHost : public content::WebContentsDelegate,
// Message handlers.
void OnRequest(const ExtensionHostMsg_Request_Params& params);
+ void OnEventAck();
+ void OnIncrementLazyKeepaliveCount();
+ void OnDecrementLazyKeepaliveCount();
// Handles keyboard events that were not handled by HandleKeyboardEvent().
// Platform specific implementation may override this method to handle the
diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc
index ccb555e..d041527 100644
--- a/chrome/browser/extensions/extension_message_service.cc
+++ b/chrome/browser/extensions/extension_message_service.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/lazy_background_task_queue.h"
#include "chrome/browser/extensions/process_map.h"
@@ -47,16 +48,25 @@ using content::WebContents;
struct ExtensionMessageService::MessagePort {
content::RenderProcessHost* process;
int routing_id;
- explicit MessagePort(content::RenderProcessHost* process = NULL,
- int routing_id = MSG_ROUTING_CONTROL)
- : process(process), routing_id(routing_id) {}
+ std::string extension_id;
+ void* background_host_ptr; // used in IncrementLazyKeepaliveCount
+
+ MessagePort()
+ : process(NULL),
+ routing_id(MSG_ROUTING_CONTROL),
+ background_host_ptr(NULL) {}
+ MessagePort(content::RenderProcessHost* process,
+ int routing_id,
+ const std::string& extension_id)
+ : process(process),
+ routing_id(routing_id),
+ extension_id(extension_id),
+ background_host_ptr(NULL) {}
};
struct ExtensionMessageService::MessageChannel {
ExtensionMessageService::MessagePort opener;
ExtensionMessageService::MessagePort receiver;
- std::string source_extension_id;
- std::string target_extension_id;
};
struct ExtensionMessageService::OpenChannelParams {
@@ -124,24 +134,30 @@ static content::RenderProcessHost* GetExtensionProcess(
return site_instance->GetProcess();
}
-static void IncrementLazyKeepaliveCount(content::RenderProcessHost* process,
- const std::string& extension_id) {
- Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
- const Extension* extension = profile->GetExtensionService()->extensions()->
- GetByID(extension_id);
- if (extension)
- profile->GetExtensionProcessManager()->IncrementLazyKeepaliveCount(
- extension);
+static void IncrementLazyKeepaliveCount(
+ ExtensionMessageService::MessagePort* port) {
+ Profile* profile =
+ Profile::FromBrowserContext(port->process->GetBrowserContext());
+ ExtensionProcessManager* pm =
+ ExtensionSystem::Get(profile)->process_manager();
+ ExtensionHost* host = pm->GetBackgroundHostForExtension(port->extension_id);
+ if (host && host->extension()->has_lazy_background_page())
+ pm->IncrementLazyKeepaliveCount(host->extension());
+
+ // Keep track of the background host, so when we decrement, we only do so if
+ // the host hasn't reloaded.
+ port->background_host_ptr = host;
}
-static void DecrementLazyKeepaliveCount(content::RenderProcessHost* process,
- const std::string& extension_id) {
- Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
- const Extension* extension = profile->GetExtensionService()->extensions()->
- GetByID(extension_id);
- if (extension)
- profile->GetExtensionProcessManager()->DecrementLazyKeepaliveCount(
- extension);
+static void DecrementLazyKeepaliveCount(
+ ExtensionMessageService::MessagePort* port) {
+ Profile* profile =
+ Profile::FromBrowserContext(port->process->GetBrowserContext());
+ ExtensionProcessManager* pm =
+ ExtensionSystem::Get(profile)->process_manager();
+ ExtensionHost* host = pm->GetBackgroundHostForExtension(port->extension_id);
+ if (host && host == port->background_host_ptr)
+ pm->DecrementLazyKeepaliveCount(host->extension());
}
} // namespace
@@ -195,7 +211,8 @@ void ExtensionMessageService::OpenChannelToExtension(
// which depends on whether the extension uses spanning or split mode.
MessagePort receiver(
GetExtensionProcess(profile, target_extension_id),
- MSG_ROUTING_CONTROL);
+ MSG_ROUTING_CONTROL,
+ target_extension_id);
WebContents* source_contents = tab_util::GetWebContentsByID(
source_process_id, source_routing_id);
@@ -237,12 +254,13 @@ void ExtensionMessageService::OpenChannelToTab(
receiver.process = contents->web_contents()->GetRenderProcessHost();
receiver.routing_id =
contents->web_contents()->GetRenderViewHost()->GetRoutingID();
+ receiver.extension_id = extension_id;
}
if (contents && contents->web_contents()->GetController().NeedsReload()) {
// The tab isn't loaded yet. Don't attempt to connect. Treat this as a
// disconnect.
- DispatchOnDisconnect(MessagePort(source, MSG_ROUTING_CONTROL),
+ DispatchOnDisconnect(MessagePort(source, MSG_ROUTING_CONTROL, extension_id),
GET_OPPOSITE_PORT_ID(receiver_port_id), true);
return;
}
@@ -269,7 +287,7 @@ bool ExtensionMessageService::OpenChannelImpl(const OpenChannelParams& params) {
if (!params.receiver.process) {
// Treat it as a disconnect.
- DispatchOnDisconnect(MessagePort(params.source, MSG_ROUTING_CONTROL),
+ DispatchOnDisconnect(MessagePort(params.source, MSG_ROUTING_CONTROL, ""),
GET_OPPOSITE_PORT_ID(params.receiver_port_id), true);
return false;
}
@@ -279,10 +297,9 @@ bool ExtensionMessageService::OpenChannelImpl(const OpenChannelParams& params) {
CHECK(params.receiver.process);
MessageChannel* channel(new MessageChannel);
- channel->opener = MessagePort(params.source, MSG_ROUTING_CONTROL);
+ channel->opener = MessagePort(params.source, MSG_ROUTING_CONTROL,
+ params.source_extension_id);
channel->receiver = params.receiver;
- channel->source_extension_id = params.source_extension_id;
- channel->target_extension_id = params.target_extension_id;
CHECK(params.receiver.process);
@@ -300,10 +317,8 @@ bool ExtensionMessageService::OpenChannelImpl(const OpenChannelParams& params) {
params.source_extension_id, params.target_extension_id);
// Keep both ends of the channel alive until the channel is closed.
- IncrementLazyKeepaliveCount(channel->opener.process,
- channel->source_extension_id);
- IncrementLazyKeepaliveCount(channel->receiver.process,
- channel->target_extension_id);
+ IncrementLazyKeepaliveCount(&channel->opener);
+ IncrementLazyKeepaliveCount(&channel->receiver);
return true;
}
@@ -338,10 +353,8 @@ void ExtensionMessageService::CloseChannelImpl(
}
// Balance the addrefs in OpenChannelImpl.
- DecrementLazyKeepaliveCount(channel->opener.process,
- channel->source_extension_id);
- DecrementLazyKeepaliveCount(channel->receiver.process,
- channel->target_extension_id);
+ DecrementLazyKeepaliveCount(&channel->opener);
+ DecrementLazyKeepaliveCount(&channel->receiver);
delete channel_iter->second;
channels_.erase(channel_iter);
@@ -452,6 +465,7 @@ void ExtensionMessageService::PendingOpenChannel(
return;
params.receiver = MessagePort(host->render_process_host(),
- MSG_ROUTING_CONTROL);
+ MSG_ROUTING_CONTROL,
+ params.target_extension_id);
OpenChannelImpl(params);
}
diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc
index 30fcb4d..100059e 100644
--- a/chrome/browser/extensions/extension_process_manager.cc
+++ b/chrome/browser/extensions/extension_process_manager.cc
@@ -373,6 +373,12 @@ bool ExtensionProcessManager::HasExtensionHost(ExtensionHost* host) const {
return all_hosts_.find(host) != all_hosts_.end();
}
+bool ExtensionProcessManager::IsBackgroundHostClosing(
+ const std::string& extension_id) {
+ ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
+ return (host && background_page_data_[extension_id].is_closing);
+}
+
int ExtensionProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
if (!extension->has_lazy_background_page())
return 0;
@@ -397,15 +403,10 @@ int ExtensionProcessManager::DecrementLazyKeepaliveCount(
if (!extension->has_lazy_background_page())
return 0;
- // Don't decrement the count if the background page has gone away. This can
- // happen e.g. if an event was dispatched while unloading the page, or if
- // the process is killed/closed while a message port remains open.
- // TODO(mpcomplete): This might be insufficient.. what if the page goes away
- // and comes back before we get here? Then we'll have an imbalanced
- // keepalive count.
- ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
- if (!host)
- return 0;
+ // This should never be called if the background page isn't active.
+ // Otherwise, the count can get out of sync, because we reset it when the
+ // page unloads.
+ CHECK(GetBackgroundHostForExtension(extension->id()));
int& count = background_page_data_[extension->id()].lazy_keepalive_count;
DCHECK_GT(count, 0);
diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h
index c5a936a..eddf0cf 100644
--- a/chrome/browser/extensions/extension_process_manager.h
+++ b/chrome/browser/extensions/extension_process_manager.h
@@ -89,6 +89,10 @@ class ExtensionProcessManager : public content::NotificationObserver {
// Returns true if |host| is managed by this process manager.
bool HasExtensionHost(ExtensionHost* host) const;
+ // Returns true if the (lazy) background host for the given extension has
+ // already been sent the unload event and is shutting down.
+ bool IsBackgroundHostClosing(const std::string& extension_id);
+
// Getter and setter for the lazy background page's keepalive count. This is
// the count of how many outstanding "things" are keeping the page alive.
// When this reaches 0, we will begin the process of shutting down the page.
diff --git a/chrome/browser/extensions/lazy_background_task_queue.cc b/chrome/browser/extensions/lazy_background_task_queue.cc
index bb1b1a2..dbe0ff6 100644
--- a/chrome/browser/extensions/lazy_background_task_queue.cc
+++ b/chrome/browser/extensions/lazy_background_task_queue.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/extensions/lazy_background_task_queue.h"
#include "base/callback.h"
+#include "base/message_loop.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
@@ -43,10 +44,12 @@ bool LazyBackgroundTaskQueue::ShouldEnqueueTask(
Profile* profile, const Extension* extension) {
DCHECK(extension);
if (extension->has_lazy_background_page()) {
- ExtensionProcessManager* pm = profile->GetExtensionProcessManager();
+ ExtensionProcessManager* pm =
+ ExtensionSystem::Get(profile)->process_manager();
ExtensionHost* background_host =
pm->GetBackgroundHostForExtension(extension->id());
- if (!background_host || !background_host->did_stop_loading())
+ if (!background_host || !background_host->did_stop_loading() ||
+ pm->IsBackgroundHostClosing(extension->id()))
return true;
}
@@ -64,16 +67,10 @@ void LazyBackgroundTaskQueue::AddPendingTask(
tasks_list = new PendingTasksList();
pending_tasks_[key] = linked_ptr<PendingTasksList>(tasks_list);
- // If this is the first enqueued task, ensure the background page
- // is loaded.
- const Extension* extension =
- ExtensionSystem::Get(profile)->extension_service()->
- extensions()->GetByID(extension_id);
- DCHECK(extension->has_lazy_background_page());
- ExtensionProcessManager* pm =
- ExtensionSystem::Get(profile)->process_manager();
- pm->IncrementLazyKeepaliveCount(extension);
- pm->CreateBackgroundHost(extension, extension->GetBackgroundURL());
+ // If this is the first enqueued task, and we're not waiting for the
+ // background page to unload, ensure the background page is loaded.
+ if (pending_page_loads_.count(key) == 0)
+ StartLazyBackgroundPage(profile, extension_id);
} else {
tasks_list = it->second.get();
}
@@ -81,6 +78,26 @@ void LazyBackgroundTaskQueue::AddPendingTask(
tasks_list->push_back(task);
}
+void LazyBackgroundTaskQueue::StartLazyBackgroundPage(
+ Profile* profile, const std::string& extension_id) {
+ ExtensionProcessManager* pm =
+ ExtensionSystem::Get(profile)->process_manager();
+ if (pm->IsBackgroundHostClosing(extension_id)) {
+ // When the background host finishes closing, we will reload it.
+ pending_page_loads_.insert(PendingTasksKey(profile, extension_id));
+ return;
+ }
+
+ const Extension* extension =
+ ExtensionSystem::Get(profile)->extension_service()->
+ extensions()->GetByID(extension_id);
+ DCHECK(extension->has_lazy_background_page());
+ pm->IncrementLazyKeepaliveCount(extension);
+ pm->CreateBackgroundHost(extension, extension->GetBackgroundURL());
+
+ pending_page_loads_.erase(PendingTasksKey(profile, extension_id));
+}
+
void LazyBackgroundTaskQueue::ProcessPendingTasks(
ExtensionHost* host,
Profile* profile,
@@ -130,15 +147,24 @@ void LazyBackgroundTaskQueue::Observe(
break;
}
case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
- // Notify consumers about the load failure when the background host dies.
- // This can happen if the extension crashes. This is not strictly
- // necessary, since we also unload the extension in that case (which
- // dispatches the tasks below), but is a good extra precaution.
Profile* profile = content::Source<Profile>(source).ptr();
ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
if (host->extension_host_type() ==
chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
- ProcessPendingTasks(NULL, profile, host->extension());
+ PendingTasksKey key(profile, host->extension()->id());
+ if (pending_page_loads_.count(key) > 0) {
+ // We were waiting for the background page to unload. We can start it
+ // up again and dispatch any queued events.
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &LazyBackgroundTaskQueue::StartLazyBackgroundPage,
+ AsWeakPtr(), profile, host->extension()->id()));
+ } else {
+ // This may be a load failure (e.g. a crash). In that case, notify
+ // consumers about the load failure. This is not strictly necessary,
+ // since we also unload the extension in that case (which dispatches
+ // the tasks below), but is a good extra precaution.
+ ProcessPendingTasks(NULL, profile, host->extension());
+ }
}
break;
}
diff --git a/chrome/browser/extensions/lazy_background_task_queue.h b/chrome/browser/extensions/lazy_background_task_queue.h
index 12b85ba..abe71b2 100644
--- a/chrome/browser/extensions/lazy_background_task_queue.h
+++ b/chrome/browser/extensions/lazy_background_task_queue.h
@@ -7,11 +7,13 @@
#pragma once
#include <map>
+#include <set>
#include <string>
#include "base/compiler_specific.h"
#include "base/callback_forward.h"
#include "base/memory/linked_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
@@ -27,7 +29,9 @@ namespace extensions {
//
// It is the consumer's responsibility to use this class when appropriate, i.e.
// only with extensions that have not-yet-loaded lazy background pages.
-class LazyBackgroundTaskQueue : public content::NotificationObserver {
+class LazyBackgroundTaskQueue
+ : public content::NotificationObserver,
+ public base::SupportsWeakPtr<LazyBackgroundTaskQueue> {
public:
typedef base::Callback<void(ExtensionHost*)> PendingTask;
@@ -56,6 +60,10 @@ class LazyBackgroundTaskQueue : public content::NotificationObserver {
typedef std::vector<PendingTask> PendingTasksList;
typedef std::map<PendingTasksKey,
linked_ptr<PendingTasksList> > PendingTasksMap;
+ typedef std::set<PendingTasksKey> PendingPageLoadList;
+
+ void StartLazyBackgroundPage(Profile* profile,
+ const std::string& extension_id);
// content::NotificationObserver interface.
virtual void Observe(int type,
@@ -72,6 +80,7 @@ class LazyBackgroundTaskQueue : public content::NotificationObserver {
Profile* profile_;
content::NotificationRegistrar registrar_;
PendingTasksMap pending_tasks_;
+ PendingPageLoadList pending_page_loads_;
};
} // namespace extensions
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index c1a0eb8..095057f 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -91,16 +91,11 @@ bool ChromeRenderMessageFilter::OnMessageReceived(const IPC::Message& message,
OnExtensionAddLazyListener)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_RemoveLazyListener,
OnExtensionRemoveLazyListener)
- IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExtensionEventAck, OnExtensionEventAck)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseChannel, OnExtensionCloseChannel)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestForIOThread,
OnExtensionRequestForIOThread)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_ShouldUnloadAck,
OnExtensionShouldUnloadAck)
- IPC_MESSAGE_HANDLER(ExtensionHostMsg_IncrementLazyKeepaliveCount,
- OnExtensionIncrementLazyKeepaliveCount)
- IPC_MESSAGE_HANDLER(ExtensionHostMsg_DecrementLazyKeepaliveCount,
- OnExtensionDecrementLazyKeepaliveCount)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_GenerateUniqueID,
OnExtensionGenerateUniqueID)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_UnloadAck, OnExtensionUnloadAck)
@@ -150,12 +145,9 @@ void ChromeRenderMessageFilter::OverrideThreadForMessage(
case ExtensionHostMsg_RemoveListener::ID:
case ExtensionHostMsg_AddLazyListener::ID:
case ExtensionHostMsg_RemoveLazyListener::ID:
- case ExtensionHostMsg_ExtensionEventAck::ID:
case ExtensionHostMsg_CloseChannel::ID:
case ExtensionHostMsg_ShouldUnloadAck::ID:
case ExtensionHostMsg_UnloadAck::ID:
- case ExtensionHostMsg_IncrementLazyKeepaliveCount::ID:
- case ExtensionHostMsg_DecrementLazyKeepaliveCount::ID:
case ChromeViewHostMsg_UpdatedCacheStats::ID:
*thread = BrowserThread::UI;
break;
@@ -388,13 +380,6 @@ void ChromeRenderMessageFilter::OnExtensionRemoveLazyListener(
event_name, extension_id);
}
-void ChromeRenderMessageFilter::OnExtensionEventAck(
- const std::string& extension_id) {
- if (profile_->GetExtensionEventRouter())
- profile_->GetExtensionEventRouter()->OnExtensionEventAck(
- profile_, extension_id);
-}
-
void ChromeRenderMessageFilter::OnExtensionCloseChannel(int port_id,
bool connection_error) {
if (!content::RenderProcessHost::FromID(render_process_id_))
@@ -429,32 +414,6 @@ void ChromeRenderMessageFilter::OnExtensionUnloadAck(
profile_->GetExtensionProcessManager()->OnUnloadAck(extension_id);
}
-void ChromeRenderMessageFilter::OnExtensionIncrementLazyKeepaliveCount(
- const std::string& extension_id) {
- ExtensionService* service =
- ExtensionSystem::Get(profile_)->extension_service();
- ExtensionProcessManager* process_manager =
- ExtensionSystem::Get(profile_)->process_manager();
- if (process_manager && service) {
- const Extension* extension = service->extensions()->GetByID(extension_id);
- if (extension)
- process_manager->IncrementLazyKeepaliveCount(extension);
- }
-}
-
-void ChromeRenderMessageFilter::OnExtensionDecrementLazyKeepaliveCount(
- const std::string& extension_id) {
- ExtensionService* service =
- ExtensionSystem::Get(profile_)->extension_service();
- ExtensionProcessManager* process_manager =
- ExtensionSystem::Get(profile_)->process_manager();
- if (process_manager && service) {
- const Extension* extension = service->extensions()->GetByID(extension_id);
- if (extension)
- process_manager->DecrementLazyKeepaliveCount(extension);
- }
-}
-
void ChromeRenderMessageFilter::OnExtensionGenerateUniqueID(int* unique_id) {
static int next_unique_id = 1;
*unique_id = next_unique_id++;
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h
index 5068f94..0dfb1df 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.h
@@ -117,7 +117,6 @@ class ChromeRenderMessageFilter : public content::BrowserMessageFilter {
const std::string& event_name);
void OnExtensionRemoveLazyListener(const std::string& extension_id,
const std::string& event_name);
- void OnExtensionEventAck(const std::string& extension_id);
void OnExtensionCloseChannel(int port_id, bool connection_error);
void OnExtensionRequestForIOThread(
int routing_id,
@@ -125,8 +124,6 @@ class ChromeRenderMessageFilter : public content::BrowserMessageFilter {
void OnExtensionShouldUnloadAck(const std::string& extension_id,
int sequence_id);
void OnExtensionUnloadAck(const std::string& extension_id);
- void OnExtensionIncrementLazyKeepaliveCount(const std::string& extension_id);
- void OnExtensionDecrementLazyKeepaliveCount(const std::string& extension_id);
void OnExtensionGenerateUniqueID(int* unique_id);
#if defined(USE_TCMALLOC)
void OnRendererTcmalloc(const std::string& output);
diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h
index 6cc83ae..ff35b1e 100644
--- a/chrome/common/extensions/extension_messages.h
+++ b/chrome/common/extensions/extension_messages.h
@@ -331,9 +331,7 @@ IPC_MESSAGE_CONTROL2(ExtensionHostMsg_RemoveLazyListener,
std::string /* name */)
// Notify the browser that an event has finished being dispatched.
-IPC_MESSAGE_CONTROL1(ExtensionHostMsg_ExtensionEventAck,
- std::string /* extension_id */)
-
+IPC_MESSAGE_ROUTED0(ExtensionHostMsg_EventAck)
// Open a channel to all listening contexts owned by the extension with
// the given ID. This always returns a valid port ID which can be used for
@@ -417,13 +415,11 @@ IPC_MESSAGE_CONTROL1(ExtensionHostMsg_UnloadAck,
// Informs the browser to increment the keepalive count for the lazy background
// page, keeping it alive.
-IPC_MESSAGE_CONTROL1(ExtensionHostMsg_IncrementLazyKeepaliveCount,
- std::string /* extension_id */)
+IPC_MESSAGE_ROUTED0(ExtensionHostMsg_IncrementLazyKeepaliveCount)
// Informs the browser there is one less thing keeping the lazy background page
// alive.
-IPC_MESSAGE_CONTROL1(ExtensionHostMsg_DecrementLazyKeepaliveCount,
- std::string /* extension_id */)
+IPC_MESSAGE_ROUTED0(ExtensionHostMsg_DecrementLazyKeepaliveCount)
// Fetches a globally unique ID (for the lifetime of the browser) from the
// browser process.
diff --git a/chrome/renderer/extensions/extension_custom_bindings.cc b/chrome/renderer/extensions/extension_custom_bindings.cc
index 7b177e1..047588c 100644
--- a/chrome/renderer/extensions/extension_custom_bindings.cc
+++ b/chrome/renderer/extensions/extension_custom_bindings.cc
@@ -14,97 +14,16 @@
#include "chrome/renderer/extensions/extension_dispatcher.h"
#include "chrome/renderer/extensions/extension_helper.h"
#include "content/public/renderer/render_view.h"
-#include "content/public/renderer/render_view_visitor.h"
#include "grit/renderer_resources.h"
-#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
-#include "v8/include/v8.h"
#include "webkit/glue/webkit_glue.h"
+#include "v8/include/v8.h"
namespace extensions {
namespace {
-// A RenderViewVisitor class that iterates through the set of available
-// views, looking for a view of the given type, in the given browser window
-// and within the given extension.
-// Used to accumulate the list of views associated with an extension.
-class ExtensionViewAccumulator : public content::RenderViewVisitor {
- public:
- ExtensionViewAccumulator(const std::string& extension_id,
- int browser_window_id,
- content::ViewType view_type)
- : extension_id_(extension_id),
- browser_window_id_(browser_window_id),
- view_type_(view_type),
- views_(v8::Array::New()),
- index_(0) {
- }
-
- v8::Local<v8::Array> views() { return views_; }
-
- virtual bool Visit(content::RenderView* render_view) {
- ExtensionHelper* helper = ExtensionHelper::Get(render_view);
- if (!ViewTypeMatches(helper->view_type(), view_type_))
- return true;
-
- GURL url = render_view->GetWebView()->mainFrame()->document().url();
- if (!url.SchemeIs(chrome::kExtensionScheme))
- return true;
- const std::string& extension_id = url.host();
- if (extension_id != extension_id_)
- return true;
-
- if (browser_window_id_ != extension_misc::kUnknownWindowId &&
- helper->browser_window_id() != browser_window_id_) {
- return true;
- }
-
- v8::Local<v8::Context> context =
- render_view->GetWebView()->mainFrame()->mainWorldScriptContext();
- if (!context.IsEmpty()) {
- v8::Local<v8::Value> window = context->Global();
- DCHECK(!window.IsEmpty());
-
- if (!OnMatchedView(window))
- return false;
- }
- return true;
- }
-
- private:
- // Called on each view found matching the search criteria. Returns false
- // to terminate the iteration.
- bool OnMatchedView(v8::Local<v8::Value> view_window) {
- views_->Set(v8::Integer::New(index_), view_window);
- index_++;
-
- if (view_type_ == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
- return false; // There can be only one...
-
- return true;
- }
-
- // Returns true is |type| "isa" |match|.
- static bool ViewTypeMatches(content::ViewType type, content::ViewType match) {
- if (type == match)
- return true;
-
- // INVALID means match all.
- if (match == content::VIEW_TYPE_INVALID)
- return true;
-
- return false;
- }
-
- std::string extension_id_;
- int browser_window_id_;
- content::ViewType view_type_;
- v8::Local<v8::Array> views_;
- int index_;
-};
-
} // namespace
ExtensionCustomBindings::ExtensionCustomBindings(
@@ -159,10 +78,21 @@ v8::Handle<v8::Value> ExtensionCustomBindings::GetExtensionViews(
if (!extension)
return v8::Undefined();
- ExtensionViewAccumulator accumulator(extension->id(), browser_window_id,
- view_type);
- content::RenderView::ForEach(&accumulator);
- return accumulator.views();
+ std::vector<content::RenderView*> views = ExtensionHelper::GetExtensionViews(
+ extension->id(), browser_window_id, view_type);
+ v8::Local<v8::Array> v8_views = v8::Array::New();
+ int v8_index = 0;
+ for (size_t i = 0; i < views.size(); ++i) {
+ v8::Local<v8::Context> context =
+ views[i]->GetWebView()->mainFrame()->mainWorldScriptContext();
+ if (!context.IsEmpty()) {
+ v8::Local<v8::Value> window = context->Global();
+ DCHECK(!window.IsEmpty());
+ v8_views->Set(v8::Integer::New(v8_index++), window);
+ }
+ }
+
+ return v8_views;
}
// static
diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc
index fcfa1e7..9b2744c 100644
--- a/chrome/renderer/extensions/extension_dispatcher.cc
+++ b/chrome/renderer/extensions/extension_dispatcher.cc
@@ -45,6 +45,7 @@
#include "chrome/renderer/native_handler.h"
#include "chrome/renderer/resource_bundle_source_map.h"
#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/render_view.h"
#include "grit/renderer_resources.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
@@ -141,10 +142,12 @@ class LazyBackgroundPageNativeHandler : public ChromeV8Extension {
v8::Handle<v8::Value> IncrementKeepaliveCount(const v8::Arguments& args) {
ChromeV8Context* context =
extension_dispatcher()->v8_context_set().GetCurrent();
- if (IsCurrentContextLazyBackgroundPage(context->extension())) {
- content::RenderThread::Get()->Send(
- new ExtensionHostMsg_IncrementLazyKeepaliveCount(
- context->extension()->id()));
+ if (!context)
+ return v8::Undefined();
+ content::RenderView* render_view = context->GetRenderView();
+ if (IsContextLazyBackgroundPage(render_view, context->extension())) {
+ render_view->Send(new ExtensionHostMsg_IncrementLazyKeepaliveCount(
+ render_view->GetRoutingID()));
}
return v8::Undefined();
}
@@ -152,17 +155,19 @@ class LazyBackgroundPageNativeHandler : public ChromeV8Extension {
v8::Handle<v8::Value> DecrementKeepaliveCount(const v8::Arguments& args) {
ChromeV8Context* context =
extension_dispatcher()->v8_context_set().GetCurrent();
- if (IsCurrentContextLazyBackgroundPage(context->extension())) {
- content::RenderThread::Get()->Send(
- new ExtensionHostMsg_DecrementLazyKeepaliveCount(
- context->extension()->id()));
+ if (!context)
+ return v8::Undefined();
+ content::RenderView* render_view = context->GetRenderView();
+ if (IsContextLazyBackgroundPage(render_view, context->extension())) {
+ render_view->Send(new ExtensionHostMsg_DecrementLazyKeepaliveCount(
+ render_view->GetRoutingID()));
}
return v8::Undefined();
}
private:
- bool IsCurrentContextLazyBackgroundPage(const Extension* extension) {
- content::RenderView* render_view = GetCurrentRenderView();
+ bool IsContextLazyBackgroundPage(content::RenderView* render_view,
+ const Extension* extension) {
if (!render_view)
return false;
@@ -305,12 +310,17 @@ void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id,
kInitialExtensionIdleHandlerDelayMs);
}
+ // Tell the browser process when an event has been dispatched with a lazy
+ // background page active.
const Extension* extension = extensions_.GetByID(extension_id);
- // Tell the browser process that the event is dispatched and we're idle.
if (extension && extension->has_lazy_background_page() &&
function_name == kEventDispatchFunction) {
- RenderThread::Get()->Send(
- new ExtensionHostMsg_ExtensionEventAck(extension_id));
+ content::RenderView* background_view =
+ ExtensionHelper::GetBackgroundPage(extension_id);
+ if (background_view) {
+ background_view->Send(new ExtensionHostMsg_EventAck(
+ background_view->GetRoutingID()));
+ }
}
}
diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc
index 73027de..41fd732 100644
--- a/chrome/renderer/extensions/extension_helper.cc
+++ b/chrome/renderer/extensions/extension_helper.cc
@@ -22,7 +22,9 @@
#include "chrome/renderer/extensions/user_script_scheduler.h"
#include "chrome/renderer/extensions/user_script_slave.h"
#include "content/public/renderer/render_view.h"
+#include "content/public/renderer/render_view_visitor.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRequest.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedUserGesture.h"
@@ -48,6 +50,91 @@ namespace {
typedef std::map<WebFrame*, UserScriptScheduler*> SchedulerMap;
static base::LazyInstance<SchedulerMap> g_schedulers =
LAZY_INSTANCE_INITIALIZER;
+
+// A RenderViewVisitor class that iterates through the set of available
+// views, looking for a view of the given type, in the given browser window
+// and within the given extension.
+// Used to accumulate the list of views associated with an extension.
+class ExtensionViewAccumulator : public content::RenderViewVisitor {
+ public:
+ ExtensionViewAccumulator(const std::string& extension_id,
+ int browser_window_id,
+ content::ViewType view_type)
+ : extension_id_(extension_id),
+ browser_window_id_(browser_window_id),
+ view_type_(view_type) {
+ }
+
+ std::vector<content::RenderView*> views() { return views_; }
+
+ // Returns false to terminate the iteration.
+ virtual bool Visit(content::RenderView* render_view) {
+ ExtensionHelper* helper = ExtensionHelper::Get(render_view);
+ if (!ViewTypeMatches(helper->view_type(), view_type_))
+ return true;
+
+ GURL url = render_view->GetWebView()->mainFrame()->document().url();
+ if (!url.SchemeIs(chrome::kExtensionScheme))
+ return true;
+ const std::string& extension_id = url.host();
+ if (extension_id != extension_id_)
+ return true;
+
+ if (browser_window_id_ != extension_misc::kUnknownWindowId &&
+ helper->browser_window_id() != browser_window_id_) {
+ return true;
+ }
+
+ views_.push_back(render_view);
+
+ if (view_type_ == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
+ return false; // There can be only one...
+ return true;
+ }
+
+ private:
+ // Returns true if |type| "isa" |match|.
+ static bool ViewTypeMatches(content::ViewType type, content::ViewType match) {
+ if (type == match)
+ return true;
+
+ // INVALID means match all.
+ if (match == content::VIEW_TYPE_INVALID)
+ return true;
+
+ return false;
+ }
+
+ std::string extension_id_;
+ int browser_window_id_;
+ content::ViewType view_type_;
+ std::vector<content::RenderView*> views_;
+};
+
+}
+
+// static
+std::vector<content::RenderView*> ExtensionHelper::GetExtensionViews(
+ const std::string& extension_id,
+ int browser_window_id,
+ content::ViewType view_type) {
+ ExtensionViewAccumulator accumulator(
+ extension_id, browser_window_id, view_type);
+ content::RenderView::ForEach(&accumulator);
+ return accumulator.views();
+}
+
+// static
+content::RenderView* ExtensionHelper::GetBackgroundPage(
+ const std::string& extension_id) {
+ ExtensionViewAccumulator accumulator(
+ extension_id, extension_misc::kUnknownWindowId,
+ chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
+ content::RenderView::ForEach(&accumulator);
+ CHECK_LE(accumulator.views().size(), 1u);
+ if (accumulator.views().size() == 0)
+ return NULL;
+ return accumulator.views()[0];
}
ExtensionHelper::ExtensionHelper(content::RenderView* render_view,
diff --git a/chrome/renderer/extensions/extension_helper.h b/chrome/renderer/extensions/extension_helper.h
index 0a0b949..a1ed3ed 100644
--- a/chrome/renderer/extensions/extension_helper.h
+++ b/chrome/renderer/extensions/extension_helper.h
@@ -36,6 +36,18 @@ class ExtensionHelper
: public content::RenderViewObserver,
public content::RenderViewObserverTracker<ExtensionHelper> {
public:
+ // Returns a list of extension RenderViews that match the given filter
+ // criteria. If |browser_window_id| is not extension_misc::kUnknownWindowId,
+ // the list is restricted to views in that browser window.
+ static std::vector<content::RenderView*> GetExtensionViews(
+ const std::string& extension_id,
+ int browser_window_id,
+ content::ViewType view_type);
+
+ // Returns the given extension's background page, or NULL if none.
+ static content::RenderView* GetBackgroundPage(
+ const std::string& extension_id);
+
ExtensionHelper(content::RenderView* render_view,
ExtensionDispatcher* extension_dispatcher);
virtual ~ExtensionHelper();