Commit 89102011 authored by tessamac@chromium.org's avatar tessamac@chromium.org

Don't close background pages if there is a pending response (callback).

BUG=81752
TEST=


Review URL: http://codereview.chromium.org/8343079

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108170 0039d316-1c4b-4281-b951-d872f2087c98
parent 00b22377
...@@ -288,15 +288,36 @@ void ExtensionEventRouter::DispatchEventImpl( ...@@ -288,15 +288,36 @@ void ExtensionEventRouter::DispatchEventImpl(
DispatchEvent(listener->process, listener->extension_id, DispatchEvent(listener->process, listener->extension_id,
event->event_name, event->cross_incognito_args, event->event_name, event->cross_incognito_args,
event->event_url); event->event_url);
IncrementInFlightEvents(listener->extension_id);
} }
continue; continue;
} }
DispatchEvent(listener->process, listener->extension_id, DispatchEvent(listener->process, listener->extension_id,
event->event_name, event->event_args, event->event_url); event->event_name, event->event_args, event->event_url);
IncrementInFlightEvents(listener->extension_id);
} }
} }
void ExtensionEventRouter::IncrementInFlightEvents(
const std::string& extension_id) {
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableLazyBackgroundPages))
in_flight_events_[extension_id]++;
}
void ExtensionEventRouter::OnExtensionEventAck(
const std::string& extension_id) {
CHECK(CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableLazyBackgroundPages));
CHECK(in_flight_events_[extension_id] > 0);
in_flight_events_[extension_id]--;
}
bool ExtensionEventRouter::HasInFlightEvents(const std::string& extension_id) {
return in_flight_events_[extension_id] > 0;
}
void ExtensionEventRouter::AppendEvent( void ExtensionEventRouter::AppendEvent(
const linked_ptr<ExtensionEvent>& event) { const linked_ptr<ExtensionEvent>& event) {
PendingEventsList* events_list = NULL; PendingEventsList* events_list = NULL;
......
...@@ -88,6 +88,13 @@ class ExtensionEventRouter : public content::NotificationObserver { ...@@ -88,6 +88,13 @@ class ExtensionEventRouter : public content::NotificationObserver {
const std::string& cross_incognito_args, const std::string& cross_incognito_args,
const GURL& event_url); const GURL& event_url);
// Record the Event Ack from the renderer. (One less event in-flight.)
void OnExtensionEventAck(const std::string& extension_id);
// Check if there are any Extension Events that have not yet been acked by
// the renderer.
bool HasInFlightEvents(const std::string& extension_id);
protected: protected:
// The details of an event to be dispatched. // The details of an event to be dispatched.
struct ExtensionEvent; struct ExtensionEvent;
...@@ -132,6 +139,11 @@ class ExtensionEventRouter : public content::NotificationObserver { ...@@ -132,6 +139,11 @@ class ExtensionEventRouter : public content::NotificationObserver {
linked_ptr<PendingEventsList> > PendingEventsPerExtMap; linked_ptr<PendingEventsList> > PendingEventsPerExtMap;
PendingEventsPerExtMap pending_events_; PendingEventsPerExtMap pending_events_;
// Track of the number of dispatched events that have not yet sent an
// ACK from the renderer.
void IncrementInFlightEvents(const std::string& extension_id);
std::map<std::string, int> in_flight_events_;
DISALLOW_COPY_AND_ASSIGN(ExtensionEventRouter); DISALLOW_COPY_AND_ASSIGN(ExtensionEventRouter);
}; };
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
#include "content/browser/browsing_instance.h" #include "content/browser/browsing_instance.h"
#include "chrome/browser/extensions/extension_event_router.h"
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
#include "chrome/browser/extensions/extension_host_mac.h" #include "chrome/browser/extensions/extension_host_mac.h"
#endif #endif
...@@ -429,8 +430,12 @@ bool ExtensionProcessManager::HasExtensionHost(ExtensionHost* host) const { ...@@ -429,8 +430,12 @@ bool ExtensionProcessManager::HasExtensionHost(ExtensionHost* host) const {
void ExtensionProcessManager::OnExtensionIdle(const std::string& extension_id) { void ExtensionProcessManager::OnExtensionIdle(const std::string& extension_id) {
ExtensionHost* host = GetBackgroundHostForExtension(extension_id); ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
if (host && !HasVisibleViews(extension_id)) if (host && !HasVisibleViews(extension_id)) {
CloseBackgroundHost(host); Profile* profile =
Profile::FromBrowserContext(browsing_instance_->browser_context());
if (!profile->GetExtensionEventRouter()->HasInFlightEvents(extension_id))
CloseBackgroundHost(host);
}
} }
bool ExtensionProcessManager::HasVisibleViews(const std::string& extension_id) { bool ExtensionProcessManager::HasVisibleViews(const std::string& extension_id) {
......
...@@ -87,10 +87,9 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, ...@@ -87,10 +87,9 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest,
bg_pg_created.Wait(); bg_pg_created.Wait();
bg_pg_closed.Wait(); bg_pg_closed.Wait();
// Background page is closed before it created a new tab. // Background page is closed after creating a new tab.
// TODO(tessamac): Implement! Close background page after callback.
EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
EXPECT_EQ(num_tabs_before, browser()->tab_count()); EXPECT_EQ(num_tabs_before + 1, browser()->tab_count());
} }
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest,
......
...@@ -130,6 +130,7 @@ bool ChromeRenderMessageFilter::OnMessageReceived(const IPC::Message& message, ...@@ -130,6 +130,7 @@ bool ChromeRenderMessageFilter::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER(ExtensionHostMsg_RemoveListener, IPC_MESSAGE_HANDLER(ExtensionHostMsg_RemoveListener,
OnExtensionRemoveListener) OnExtensionRemoveListener)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExtensionIdle, OnExtensionIdle) IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExtensionIdle, OnExtensionIdle)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExtensionEventAck, OnExtensionEventAck)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseChannel, OnExtensionCloseChannel) IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseChannel, OnExtensionCloseChannel)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestForIOThread, IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestForIOThread,
OnExtensionRequestForIOThread) OnExtensionRequestForIOThread)
...@@ -192,6 +193,7 @@ void ChromeRenderMessageFilter::OverrideThreadForMessage( ...@@ -192,6 +193,7 @@ void ChromeRenderMessageFilter::OverrideThreadForMessage(
case ExtensionHostMsg_AddListener::ID: case ExtensionHostMsg_AddListener::ID:
case ExtensionHostMsg_RemoveListener::ID: case ExtensionHostMsg_RemoveListener::ID:
case ExtensionHostMsg_ExtensionIdle::ID: case ExtensionHostMsg_ExtensionIdle::ID:
case ExtensionHostMsg_ExtensionEventAck::ID:
case ExtensionHostMsg_CloseChannel::ID: case ExtensionHostMsg_CloseChannel::ID:
case ChromeViewHostMsg_UpdatedCacheStats::ID: case ChromeViewHostMsg_UpdatedCacheStats::ID:
*thread = BrowserThread::UI; *thread = BrowserThread::UI;
...@@ -381,6 +383,12 @@ void ChromeRenderMessageFilter::OnExtensionIdle( ...@@ -381,6 +383,12 @@ void ChromeRenderMessageFilter::OnExtensionIdle(
profile_->GetExtensionProcessManager()->OnExtensionIdle(extension_id); profile_->GetExtensionProcessManager()->OnExtensionIdle(extension_id);
} }
void ChromeRenderMessageFilter::OnExtensionEventAck(
const std::string& extension_id) {
if (profile_->GetExtensionEventRouter())
profile_->GetExtensionEventRouter()->OnExtensionEventAck(extension_id);
}
void ChromeRenderMessageFilter::OnExtensionCloseChannel(int port_id) { void ChromeRenderMessageFilter::OnExtensionCloseChannel(int port_id) {
if (!RenderProcessHost::FromID(render_process_id_)) if (!RenderProcessHost::FromID(render_process_id_))
return; // To guard against crash in browser_tests shutdown. return; // To guard against crash in browser_tests shutdown.
......
...@@ -93,6 +93,7 @@ class ChromeRenderMessageFilter : public BrowserMessageFilter { ...@@ -93,6 +93,7 @@ class ChromeRenderMessageFilter : public BrowserMessageFilter {
void OnExtensionRemoveListener(const std::string& extension_id, void OnExtensionRemoveListener(const std::string& extension_id,
const std::string& event_name); const std::string& event_name);
void OnExtensionIdle(const std::string& extension_id); void OnExtensionIdle(const std::string& extension_id);
void OnExtensionEventAck(const std::string& extension_id);
void OnExtensionCloseChannel(int port_id); void OnExtensionCloseChannel(int port_id);
void OnExtensionRequestForIOThread( void OnExtensionRequestForIOThread(
int routing_id, int routing_id,
......
...@@ -273,6 +273,11 @@ IPC_MESSAGE_CONTROL2(ExtensionHostMsg_RemoveListener, ...@@ -273,6 +273,11 @@ IPC_MESSAGE_CONTROL2(ExtensionHostMsg_RemoveListener,
IPC_MESSAGE_CONTROL1(ExtensionHostMsg_ExtensionIdle, IPC_MESSAGE_CONTROL1(ExtensionHostMsg_ExtensionIdle,
std::string /* extension_id */) std::string /* extension_id */)
// Notify the browser that an event has finished being dispatched.
IPC_MESSAGE_CONTROL1(ExtensionHostMsg_ExtensionEventAck,
std::string /* extension_id */)
// Open a channel to all listening contexts owned by the extension with // 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 // the given ID. This always returns a valid port ID which can be used for
// sending messages. If an error occurred, the opener will be notified // sending messages. If an error occurred, the opener will be notified
......
...@@ -154,10 +154,18 @@ void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id, ...@@ -154,10 +154,18 @@ void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id,
kInitialExtensionIdleHandlerDelayS); kInitialExtensionIdleHandlerDelayS);
} }
// Tell the browser process that we're idle. // Tell the browser process that the event is dispatched and we're idle.
if (CommandLine::ForCurrentProcess()->HasSwitch( if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableLazyBackgroundPages) && switches::kEnableLazyBackgroundPages) &&
function_name == "Event.dispatchJSON") // may always be true function_name == "Event.dispatchJSON") { // may always be true
RenderThread::Get()->Send(
new ExtensionHostMsg_ExtensionEventAck(extension_id));
CheckIdleStatus(extension_id);
}
}
void ExtensionDispatcher::CheckIdleStatus(const std::string& extension_id) {
if (!ExtensionProcessBindings::HasPendingRequests(extension_id))
RenderThread::Get()->Send(new ExtensionHostMsg_ExtensionIdle(extension_id)); RenderThread::Get()->Send(new ExtensionHostMsg_ExtensionIdle(extension_id));
} }
......
...@@ -81,6 +81,10 @@ class ExtensionDispatcher : public content::RenderProcessObserver { ...@@ -81,6 +81,10 @@ class ExtensionDispatcher : public content::RenderProcessObserver {
return webrequest_other_; return webrequest_other_;
} }
// If the extension is in fact idle, tell the browser process to close
// the background page.
void CheckIdleStatus(const std::string& extension_id);
private: private:
friend class RenderViewTest; friend class RenderViewTest;
......
...@@ -197,9 +197,14 @@ void ExtensionHelper::OnExtensionResponse(int request_id, ...@@ -197,9 +197,14 @@ void ExtensionHelper::OnExtensionResponse(int request_id,
bool success, bool success,
const std::string& response, const std::string& response,
const std::string& error) { const std::string& error) {
std::string extension_id;
ExtensionProcessBindings::HandleResponse( ExtensionProcessBindings::HandleResponse(
extension_dispatcher_->v8_context_set(), request_id, success, extension_dispatcher_->v8_context_set(), request_id, success,
response, error); response, error, &extension_id);
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableLazyBackgroundPages))
extension_dispatcher_->CheckIdleStatus(extension_id);
} }
void ExtensionHelper::OnExtensionMessageInvoke(const std::string& extension_id, void ExtensionHelper::OnExtensionMessageInvoke(const std::string& extension_id,
......
...@@ -66,11 +66,13 @@ const char* kExtensionDeps[] = { ...@@ -66,11 +66,13 @@ const char* kExtensionDeps[] = {
// Contains info relevant to a pending API request. // Contains info relevant to a pending API request.
struct PendingRequest { struct PendingRequest {
public : public :
PendingRequest(v8::Persistent<v8::Context> context, const std::string& name) PendingRequest(v8::Persistent<v8::Context> context, const std::string& name,
: context(context), name(name) { const std::string& extension_id)
: context(context), name(name), extension_id(extension_id) {
} }
v8::Persistent<v8::Context> context; v8::Persistent<v8::Context> context;
std::string name; std::string name;
std::string extension_id;
}; };
typedef std::map<int, linked_ptr<PendingRequest> > PendingRequestMap; typedef std::map<int, linked_ptr<PendingRequest> > PendingRequestMap;
...@@ -473,7 +475,7 @@ class ExtensionImpl : public ChromeV8Extension { ...@@ -473,7 +475,7 @@ class ExtensionImpl : public ChromeV8Extension {
v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
DCHECK(!v8_context.IsEmpty()); DCHECK(!v8_context.IsEmpty());
g_pending_requests.Get()[request_id].reset(new PendingRequest( g_pending_requests.Get()[request_id].reset(new PendingRequest(
v8_context, name)); v8_context, name, current_context->extension_id()));
ExtensionHostMsg_Request_Params params; ExtensionHostMsg_Request_Params params;
params.name = name; params.name = name;
...@@ -605,7 +607,8 @@ void ExtensionProcessBindings::HandleResponse( ...@@ -605,7 +607,8 @@ void ExtensionProcessBindings::HandleResponse(
int request_id, int request_id,
bool success, bool success,
const std::string& response, const std::string& response,
const std::string& error) { const std::string& error,
std::string* extension_id) {
PendingRequestMap::iterator request = PendingRequestMap::iterator request =
g_pending_requests.Get().find(request_id); g_pending_requests.Get().find(request_id);
if (request == g_pending_requests.Get().end()) { if (request == g_pending_requests.Get().end()) {
...@@ -642,7 +645,21 @@ void ExtensionProcessBindings::HandleResponse( ...@@ -642,7 +645,21 @@ void ExtensionProcessBindings::HandleResponse(
} }
#endif #endif
// Save the extension id before erasing the request.
*extension_id = request->second->extension_id;
request->second->context.Dispose(); request->second->context.Dispose();
request->second->context.Clear(); request->second->context.Clear();
g_pending_requests.Get().erase(request); g_pending_requests.Get().erase(request);
} }
// static
bool ExtensionProcessBindings::HasPendingRequests(
const std::string& extension_id) {
for (PendingRequestMap::const_iterator it = g_pending_requests.Get().begin();
it != g_pending_requests.Get().end(); ++it) {
if (it->second->extension_id == extension_id)
return true;
}
return false;
}
...@@ -27,12 +27,15 @@ class ExtensionProcessBindings { ...@@ -27,12 +27,15 @@ class ExtensionProcessBindings {
public: public:
static v8::Extension* Get(ExtensionDispatcher* extension_dispatcher); static v8::Extension* Get(ExtensionDispatcher* extension_dispatcher);
// Handles a response to an API request. // Handles a response to an API request. Sets |extension_id|.
static void HandleResponse(const ChromeV8ContextSet& contexts, static void HandleResponse(const ChromeV8ContextSet& contexts,
int request_id, int request_id,
bool success, bool success,
const std::string& response, const std::string& response,
const std::string& error); const std::string& error,
std::string* extension_id);
static bool HasPendingRequests(const std::string& extension_id);
}; };
#endif // CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_ #endif // CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment