summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-02 19:30:12 +0000
committermpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-02 19:30:12 +0000
commit103f19fbcf7d78efd8edc28bfabb3d4915a246de (patch)
tree5bd99fea32252ef037bc8b936ec19f04bd50e868
parent3a7e89dbebd7e48b132ef2dab3a8ff7cf2235634 (diff)
downloadchromium_src-103f19fbcf7d78efd8edc28bfabb3d4915a246de.zip
chromium_src-103f19fbcf7d78efd8edc28bfabb3d4915a246de.tar.gz
chromium_src-103f19fbcf7d78efd8edc28bfabb3d4915a246de.tar.bz2
Add chrome.experimental.extension.onBackgroundPageUnloadingSoon event for when transient background
pages unload. Also restructured the background page data in ExtensionProcessManager and ExtensionHost to be a simple map, instead of scattered all over. BUG=119619 TEST=no Review URL: https://chromiumcodereview.appspot.com/9924024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@130179 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_event_router.cc13
-rw-r--r--chrome/browser/extensions/extension_host.cc22
-rw-r--r--chrome/browser/extensions/extension_host.h19
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc81
-rw-r--r--chrome/browser/extensions/extension_process_manager.h14
-rw-r--r--chrome/browser/extensions/lazy_background_page_apitest.cc17
-rw-r--r--chrome/browser/renderer_host/chrome_render_message_filter.cc18
-rw-r--r--chrome/browser/renderer_host/chrome_render_message_filter.h5
-rw-r--r--chrome/common/extensions/api/experimental.extension.json5
-rw-r--r--chrome/common/extensions/docs/experimental.extension.html17
-rw-r--r--chrome/common/extensions/docs/samples.json1
-rw-r--r--chrome/common/extensions/extension_messages.h22
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.cc29
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.h3
-rw-r--r--chrome/test/data/extensions/api_test/lazy_background_page/on_unload/background.js9
-rw-r--r--chrome/test/data/extensions/api_test/lazy_background_page/on_unload/icon.pngbin0 -> 1578 bytes
-rw-r--r--chrome/test/data/extensions/api_test/lazy_background_page/on_unload/manifest.json15
17 files changed, 193 insertions, 97 deletions
diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc
index 58902c1..bf7bd1c 100644
--- a/chrome/browser/extensions/extension_event_router.cc
+++ b/chrome/browser/extensions/extension_event_router.cc
@@ -428,11 +428,16 @@ void ExtensionEventRouter::IncrementInFlightEvents(
void ExtensionEventRouter::OnExtensionEventAck(
Profile* profile, const std::string& extension_id) {
- const Extension* extension =
- profile->GetExtensionService()->extensions()->GetByID(extension_id);
- if (extension && extension->has_lazy_background_page()) {
+ // Don't decrement the count if the background page has gone away. This can
+ // happen if the event was dispatched while unloading the page.
+ // TODO(mpcomplete): This might be insufficient.. what if the page goes away
+ // and comes back before we get the ack? Then we'll have an imbalanced
+ // keepalive count.
+ ExtensionHost* host = profile->GetExtensionProcessManager()->
+ GetBackgroundHostForExtension(extension_id);
+ if (host && host->extension()->has_lazy_background_page()) {
profile->GetExtensionProcessManager()->DecrementLazyKeepaliveCount(
- extension);
+ host->extension());
}
}
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index d974567..1413472 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -132,8 +132,7 @@ ExtensionHost::ExtensionHost(const Extension* extension,
ALLOW_THIS_IN_INITIALIZER_LIST(
extension_function_dispatcher_(profile_, this)),
extension_host_type_(host_type),
- associated_web_contents_(NULL),
- close_sequence_id_(0) {
+ associated_web_contents_(NULL) {
host_contents_.reset(WebContents::Create(
profile_, site_instance, MSG_ROUTING_NONE, NULL, NULL));
content::WebContentsObserver::Observe(host_contents_.get());
@@ -226,25 +225,6 @@ void ExtensionHost::CreateRenderViewNow() {
}
}
-void ExtensionHost::SendShouldClose() {
- CHECK(extension()->has_lazy_background_page());
- render_view_host()->Send(new ExtensionMsg_ShouldClose(
- extension()->id(), ++close_sequence_id_));
- // TODO(mpcomplete): start timeout
-}
-
-void ExtensionHost::CancelShouldClose() {
- CHECK(extension()->has_lazy_background_page());
- ++close_sequence_id_;
-}
-
-void ExtensionHost::OnShouldCloseAck(int sequence_id) {
- CHECK(extension()->has_lazy_background_page());
- if (sequence_id != close_sequence_id_)
- return;
- Close();
-}
-
const Browser* ExtensionHost::GetBrowser() const {
return view() ? view()->browser() : NULL;
}
diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h
index 37b40a6..2c9de63 100644
--- a/chrome/browser/extensions/extension_host.h
+++ b/chrome/browser/extensions/extension_host.h
@@ -94,21 +94,6 @@ class ExtensionHost : public content::WebContentsDelegate,
// Helper variant of the above for cases where no Browser is present.
void CreateViewWithoutBrowser();
- // Send a message to the renderer to notify it 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.
- void SendShouldClose();
-
- // Cancels the current close sequence. Any future Close ACKs will be ignored
- // (unless SendShouldClose is called again).
- void CancelShouldClose();
-
- // Handles the close ACK. The sequence ID lets us identify whether we have
- // cancelled this close sequence.
- void OnShouldCloseAck(int sequence_id);
-
const Extension* extension() const { return extension_; }
const std::string& extension_id() const { return extension_id_; }
content::WebContents* host_contents() const { return host_contents_.get(); }
@@ -259,10 +244,6 @@ class ExtensionHost : public content::WebContentsDelegate,
// Used to measure how long it's been since the host was created.
PerfTimer since_created_;
- // A unique ID associated with each call to ShouldClose. This allows us
- // to differentiate which ShouldClose message the renderer is responding to.
- int close_sequence_id_;
-
DISALLOW_COPY_AND_ASSIGN(ExtensionHost);
};
diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc
index 970ca27..782f642 100644
--- a/chrome/browser/extensions/extension_process_manager.cc
+++ b/chrome/browser/extensions/extension_process_manager.cc
@@ -17,6 +17,7 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_view_type.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
@@ -39,28 +40,6 @@ using content::SiteInstance;
namespace {
-// Accessors for an extension's lazy_keepalive_count - one for each virtual
-// profile.
-static base::LazyInstance<base::PropertyAccessor<int> >
- g_property_accessor = LAZY_INSTANCE_INITIALIZER;
-static base::LazyInstance<base::PropertyAccessor<int> >
- g_property_accessor_incognito = LAZY_INSTANCE_INITIALIZER;
-
-int& GetLazyKeepaliveCount(Profile* profile, const Extension* extension) {
- base::PropertyBag* bag =
- profile->GetExtensionService()->GetPropertyBag(extension);
-
- base::LazyInstance<base::PropertyAccessor<int> >& accessor =
- profile->IsOffTheRecord() ?
- g_property_accessor_incognito : g_property_accessor;
- int* count = accessor.Get().GetProperty(bag);
- if (!count) {
- accessor.Get().SetProperty(bag, 0);
- count = accessor.Get().GetProperty(bag);
- }
- return *count;
-}
-
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.
@@ -112,6 +91,24 @@ static void CreateBackgroundHostsForProfileStartup(
} // namespace
+struct ExtensionProcessManager::BackgroundPageData {
+ // The count of things keeping the lazy background page alive.
+ int lazy_keepalive_count;
+
+ // This is used with the ShouldUnload 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 ShouldUnload message and is currently
+ // dispatching the unload event. We use this to ignore any activity
+ // generated during the unload event that would otherwise keep the
+ // extension alive.
+ bool is_closing;
+
+ BackgroundPageData()
+ : lazy_keepalive_count(0), close_sequence_id(0), is_closing(false) {}
+};
+
//
// ExtensionProcessManager
//
@@ -380,7 +377,7 @@ int ExtensionProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
if (!extension->has_lazy_background_page())
return 0;
- return ::GetLazyKeepaliveCount(GetProfile(), extension);
+ return background_page_data_[extension->id()].lazy_keepalive_count;
}
int ExtensionProcessManager::IncrementLazyKeepaliveCount(
@@ -388,7 +385,7 @@ int ExtensionProcessManager::IncrementLazyKeepaliveCount(
if (!extension->has_lazy_background_page())
return 0;
- int& count = ::GetLazyKeepaliveCount(GetProfile(), extension);
+ int& count = background_page_data_[extension->id()].lazy_keepalive_count;
if (++count == 1)
OnLazyBackgroundPageActive(extension->id());
@@ -400,7 +397,7 @@ int ExtensionProcessManager::DecrementLazyKeepaliveCount(
if (!extension->has_lazy_background_page())
return 0;
- int& count = ::GetLazyKeepaliveCount(GetProfile(), extension);
+ int& count = background_page_data_[extension->id()].lazy_keepalive_count;
DCHECK_GT(count, 0);
if (--count == 0)
OnLazyBackgroundPageIdle(extension->id());
@@ -411,22 +408,40 @@ int ExtensionProcessManager::DecrementLazyKeepaliveCount(
void ExtensionProcessManager::OnLazyBackgroundPageIdle(
const std::string& extension_id) {
ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
- if (host)
- host->SendShouldClose();
+ if (host && !background_page_data_[extension_id].is_closing) {
+ // 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.
+ host->render_view_host()->Send(new ExtensionMsg_ShouldUnload(
+ extension_id, ++background_page_data_[extension_id].close_sequence_id));
+ }
}
void ExtensionProcessManager::OnLazyBackgroundPageActive(
const std::string& extension_id) {
ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
- if (host)
- host->CancelShouldClose();
+ 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 ShouldUnloadAck.
+ ++background_page_data_[extension_id].close_sequence_id;
+ }
}
-void ExtensionProcessManager::OnShouldCloseAck(
+void ExtensionProcessManager::OnShouldUnloadAck(
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) {
+ background_page_data_[extension_id].is_closing = true;
+ host->render_view_host()->Send(new ExtensionMsg_Unload(extension_id));
+ }
+}
+
+void ExtensionProcessManager::OnUnloadAck(const std::string& extension_id) {
+ ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
if (host)
- host->OnShouldCloseAck(sequence_id);
+ CloseBackgroundHost(host);
}
void ExtensionProcessManager::OnNetworkRequestStarted(
@@ -479,13 +494,15 @@ void ExtensionProcessManager::Observe(
break;
}
}
+ background_page_data_.erase(extension->id());
break;
}
case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
all_hosts_.erase(host);
- background_hosts_.erase(host);
+ if (background_hosts_.erase(host))
+ background_page_data_.erase(host->extension()->id());
break;
}
diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h
index 464dadc..c5a936a 100644
--- a/chrome/browser/extensions/extension_process_manager.h
+++ b/chrome/browser/extensions/extension_process_manager.h
@@ -97,9 +97,12 @@ class ExtensionProcessManager : public content::NotificationObserver {
int IncrementLazyKeepaliveCount(const Extension* extension);
int DecrementLazyKeepaliveCount(const Extension* extension);
- // Handles a response to the ShouldClose message, used for lazy background
+ // Handles a response to the ShouldUnload message, used for lazy background
// pages.
- void OnShouldCloseAck(const std::string& extension_id, int sequence_id);
+ void OnShouldUnloadAck(const std::string& extension_id, int sequence_id);
+
+ // Same as above, for the Unload message.
+ void OnUnloadAck(const std::string& extension_id);
// Tracks network requests for a given RenderViewHost, used to know
// when network activity is idle for lazy background pages.
@@ -143,6 +146,11 @@ class ExtensionProcessManager : public content::NotificationObserver {
scoped_refptr<content::SiteInstance> site_instance_;
private:
+ // Extra information we keep for each extension's background page.
+ struct BackgroundPageData;
+ typedef std::string ExtensionId;
+ typedef std::map<ExtensionId, BackgroundPageData> BackgroundPageDataMap;
+
// Contains all extension-related RenderViewHost instances for all extensions.
// We also keep a cache of the host's view type, because that information
// is not accessible at registration/deregistration time.
@@ -167,6 +175,8 @@ class ExtensionProcessManager : public content::NotificationObserver {
// was not available when the host was first registered.
void UpdateRegisteredRenderView(content::RenderViewHost* render_view_host);
+ BackgroundPageDataMap background_page_data_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionProcessManager);
};
diff --git a/chrome/browser/extensions/lazy_background_page_apitest.cc b/chrome/browser/extensions/lazy_background_page_apitest.cc
index 3f42280..4925cca 100644
--- a/chrome/browser/extensions/lazy_background_page_apitest.cc
+++ b/chrome/browser/extensions/lazy_background_page_apitest.cc
@@ -366,6 +366,23 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, Messaging) {
EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
}
+// Tests that the lazy background page receives the unload event when we
+// close it, and that it can execute simple API calls that don't require an
+// asynchronous response.
+IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnUnload) {
+ ASSERT_TRUE(LoadExtensionAndWait("on_unload"));
+
+ // Lazy Background Page has been shut down.
+ ExtensionProcessManager* pm =
+ browser()->profile()->GetExtensionProcessManager();
+ EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
+
+ // The browser action has a new title.
+ BrowserActionTestUtil browser_action(browser());
+ ASSERT_EQ(1, browser_action.NumberOfBrowserActions());
+ EXPECT_EQ("Success", browser_action.GetTooltip(0));
+}
+
// TODO: background page with timer.
// TODO: background page that interacts with popup.
// TODO: background page with menu.
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 57050b9..b4c797ff 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -96,8 +96,9 @@ bool ChromeRenderMessageFilter::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseChannel, OnExtensionCloseChannel)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestForIOThread,
OnExtensionRequestForIOThread)
- IPC_MESSAGE_HANDLER(ExtensionHostMsg_ShouldCloseAck,
- OnExtensionShouldCloseAck)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_ShouldUnloadAck,
+ OnExtensionShouldUnloadAck)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_UnloadAck, OnExtensionUnloadAck)
#if defined(USE_TCMALLOC)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RendererTcmalloc, OnRendererTcmalloc)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_WriteTcmallocHeapProfile_ACK,
@@ -146,7 +147,8 @@ void ChromeRenderMessageFilter::OverrideThreadForMessage(
case ExtensionHostMsg_RemoveLazyListener::ID:
case ExtensionHostMsg_ExtensionEventAck::ID:
case ExtensionHostMsg_CloseChannel::ID:
- case ExtensionHostMsg_ShouldCloseAck::ID:
+ case ExtensionHostMsg_ShouldUnloadAck::ID:
+ case ExtensionHostMsg_UnloadAck::ID:
case ChromeViewHostMsg_UpdatedCacheStats::ID:
*thread = BrowserThread::UI;
break;
@@ -399,13 +401,19 @@ void ChromeRenderMessageFilter::OnExtensionRequestForIOThread(
weak_ptr_factory_.GetWeakPtr(), routing_id, params);
}
-void ChromeRenderMessageFilter::OnExtensionShouldCloseAck(
+void ChromeRenderMessageFilter::OnExtensionShouldUnloadAck(
const std::string& extension_id, int sequence_id) {
if (profile_->GetExtensionProcessManager())
- profile_->GetExtensionProcessManager()->OnShouldCloseAck(
+ profile_->GetExtensionProcessManager()->OnShouldUnloadAck(
extension_id, sequence_id);
}
+void ChromeRenderMessageFilter::OnExtensionUnloadAck(
+ const std::string& extension_id) {
+ if (profile_->GetExtensionProcessManager())
+ profile_->GetExtensionProcessManager()->OnUnloadAck(extension_id);
+}
+
#if defined(USE_TCMALLOC)
void ChromeRenderMessageFilter::OnRendererTcmalloc(const std::string& output) {
base::ProcessId pid = base::GetProcId(peer_handle());
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h
index 2e6fa44..13947d6 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.h
@@ -122,8 +122,9 @@ class ChromeRenderMessageFilter : public content::BrowserMessageFilter {
void OnExtensionRequestForIOThread(
int routing_id,
const ExtensionHostMsg_Request_Params& params);
- void OnExtensionShouldCloseAck(const std::string& extension_id,
- int sequence_id);
+ void OnExtensionShouldUnloadAck(const std::string& extension_id,
+ int sequence_id);
+ void OnExtensionUnloadAck(const std::string& extension_id);
#if defined(USE_TCMALLOC)
void OnRendererTcmalloc(const std::string& output);
void OnWriteTcmallocHeapProfile(const FilePath::StringType& filename,
diff --git a/chrome/common/extensions/api/experimental.extension.json b/chrome/common/extensions/api/experimental.extension.json
index ab582fc..5929593 100644
--- a/chrome/common/extensions/api/experimental.extension.json
+++ b/chrome/common/extensions/api/experimental.extension.json
@@ -10,6 +10,11 @@
"name": "onInstalled",
"type": "function",
"description": "Fired when the extension is first installed."
+ },
+ {
+ "name": "onBackgroundPageUnloadingSoon",
+ "type": "function",
+ "description": "Sent to the transient background page just before it is unloaded. This gives the extension opportunity to do some clean up. Note that since the page is unloading, any asynchronous operations started while handling this event are not guaranteed to complete."
}
]
}
diff --git a/chrome/common/extensions/docs/experimental.extension.html b/chrome/common/extensions/docs/experimental.extension.html
index 463d634..f997ab8 100644
--- a/chrome/common/extensions/docs/experimental.extension.html
+++ b/chrome/common/extensions/docs/experimental.extension.html
@@ -199,6 +199,8 @@
<ol>
<li>
<a href="#event-onInstalled">onInstalled</a>
+ </li><li>
+ <a href="#event-onUnload">onUnload</a>
</li>
</ol>
</li>
@@ -241,6 +243,21 @@
<dl>
</dl>
</div> <!-- /description -->
+ </div><div class="apiItem">
+ <a name="event-onUnload"></a>
+ <h4>onUnload</h4>
+ <div class="summary">
+ <!-- Note: intentionally longer 80 columns -->
+ <span class="subdued">chrome.experimental.extension.</span><span>onUnload</span><span class="subdued">.addListener</span>(function(<span></span>) <span class="subdued">{...}</span><span></span>);
+ </div>
+ <div class="description">
+ <p>Sent to the transient background page just before it is unloaded. This gives the extension opportunity to do some clean up. Note that since the page is unloading, any asynchronous operations started while handling this event are not guaranteed to complete.</p>
+ <!-- LISTENER PARAMETERS -->
+ <!-- EXTRA PARAMETERS -->
+ <!-- LISTENER RETURN VALUE -->
+ <dl>
+ </dl>
+ </div> <!-- /description -->
</div> <!-- /apiItem -->
</div> <!-- /apiGroup -->
<!-- TYPES -->
diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json
index f515fc6..bf8bbb2 100644
--- a/chrome/common/extensions/docs/samples.json
+++ b/chrome/common/extensions/docs/samples.json
@@ -108,6 +108,7 @@
"chrome.experimental.devtools.console.onMessageAdded": "experimental.devtools.console.html#event-onMessageAdded",
"chrome.experimental.downloads.download": "experimental.downloads.html#method-download",
"chrome.experimental.extension.onInstalled": "experimental.extension.html#event-onInstalled",
+ "chrome.experimental.extension.onUnload": "experimental.extension.html#event-onUnload",
"chrome.experimental.fontSettings.getDefaultFixedFontSize": "experimental.fontSettings.html#method-getDefaultFixedFontSize",
"chrome.experimental.fontSettings.getDefaultFontSize": "experimental.fontSettings.html#method-getDefaultFontSize",
"chrome.experimental.fontSettings.getFontList": "experimental.fontSettings.html#method-getFontList",
diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h
index 7fcd56f..83bbf14 100644
--- a/chrome/common/extensions/extension_messages.h
+++ b/chrome/common/extensions/extension_messages.h
@@ -243,13 +243,19 @@ IPC_MESSAGE_CONTROL3(ExtensionMsg_UsingWebRequestAPI,
bool /* adblock_plus */,
bool /* other_webrequest */)
-// Ask the renderer if it is ready to shutdown. Used for lazy background pages
-// when they are considered idle. The renderer will reply with the same
-// sequence_id so that we can tell which message it is responding to.
-IPC_MESSAGE_CONTROL2(ExtensionMsg_ShouldClose,
+// Ask the lazy background page if it is ready to unload. This is sent when the
+// page is considered idle. The renderer will reply with the same sequence_id
+// so that we can tell which message it is responding to.
+IPC_MESSAGE_CONTROL2(ExtensionMsg_ShouldUnload,
std::string /* extension_id */,
int /* sequence_id */)
+// If we complete a round of ShouldUnload->ShouldUnloadAck messages without the
+// lazy background page becoming active again, we are ready to unload. This
+// message tells the page to dispatch the unload event.
+IPC_MESSAGE_CONTROL1(ExtensionMsg_Unload,
+ std::string /* extension_id */)
+
// Messages sent from the renderer to the browser.
// A renderer sends this message when an extension process starts an API
@@ -367,11 +373,15 @@ IPC_MESSAGE_ROUTED4(ExtensionHostMsg_GetAppNotifyChannel,
IPC_MESSAGE_ROUTED1(ExtensionHostMsg_ResponseAck,
int /* request_id */)
-// Response to ExtensionMsg_ShouldClose.
-IPC_MESSAGE_CONTROL2(ExtensionHostMsg_ShouldCloseAck,
+// Response to ExtensionMsg_ShouldUnload.
+IPC_MESSAGE_CONTROL2(ExtensionHostMsg_ShouldUnloadAck,
std::string /* extension_id */,
int /* sequence_id */)
+// Response to ExtensionMsg_Unload, after we dispatch the unload event.
+IPC_MESSAGE_CONTROL1(ExtensionHostMsg_UnloadAck,
+ std::string /* extension_id */)
+
// Response to the renderer for the above message.
IPC_MESSAGE_ROUTED3(ExtensionMsg_GetAppNotifyChannelResponse,
std::string /* channel_id */,
diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc
index f05b1e7..84c1db5 100644
--- a/chrome/renderer/extensions/extension_dispatcher.cc
+++ b/chrome/renderer/extensions/extension_dispatcher.cc
@@ -81,6 +81,9 @@ namespace {
static const int64 kInitialExtensionIdleHandlerDelayMs = 5*1000;
static const int64 kMaxExtensionIdleHandlerDelayMs = 5*60*1000;
+static const char kEventDispatchFunction[] = "Event.dispatchJSON";
+static const char kOnUnloadEvent[] =
+ "experimental.extension.onBackgroundPageUnloadingSoon";
class ChromeHiddenNativeHandler : public NativeHandler {
public:
@@ -175,7 +178,8 @@ bool ExtensionDispatcher::OnControlMessageReceived(
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts)
IPC_MESSAGE_HANDLER(ExtensionMsg_UsingWebRequestAPI, OnUsingWebRequestAPI)
- IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldClose, OnShouldClose)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldUnload, OnShouldUnload)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_Unload, OnUnload)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@@ -247,7 +251,7 @@ void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id,
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 == "Event.dispatchJSON") { // may always be true
+ function_name == kEventDispatchFunction) {
RenderThread::Get()->Send(
new ExtensionHostMsg_ExtensionEventAck(extension_id));
}
@@ -730,10 +734,25 @@ void ExtensionDispatcher::OnUsingWebRequestAPI(
webrequest_other_ = other;
}
-void ExtensionDispatcher::OnShouldClose(const std::string& extension_id,
- int sequence_id) {
+void ExtensionDispatcher::OnShouldUnload(const std::string& extension_id,
+ int sequence_id) {
RenderThread::Get()->Send(
- new ExtensionHostMsg_ShouldCloseAck(extension_id, sequence_id));
+ new ExtensionHostMsg_ShouldUnloadAck(extension_id, sequence_id));
+}
+
+void ExtensionDispatcher::OnUnload(const std::string& extension_id) {
+ // Dispatch the unload event. This doesn't go through the standard event
+ // dispatch machinery because it requires special handling. We need to let
+ // the browser know when we are starting and stopping the event dispatch, so
+ // that it still considers the extension idle despite any activity the unload
+ // event creates.
+ ListValue args;
+ args.Set(0, Value::CreateStringValue(kOnUnloadEvent));
+ args.Set(1, Value::CreateStringValue("[]"));
+ v8_context_set_.DispatchChromeHiddenMethod(
+ extension_id, kEventDispatchFunction, args, NULL, GURL());
+
+ RenderThread::Get()->Send(new ExtensionHostMsg_UnloadAck(extension_id));
}
Feature::Context ExtensionDispatcher::ClassifyJavaScriptContext(
diff --git a/chrome/renderer/extensions/extension_dispatcher.h b/chrome/renderer/extensions/extension_dispatcher.h
index 9f24aa4..069eec7 100644
--- a/chrome/renderer/extensions/extension_dispatcher.h
+++ b/chrome/renderer/extensions/extension_dispatcher.h
@@ -138,7 +138,8 @@ class ExtensionDispatcher : public content::RenderProcessObserver {
bool adblock,
bool adblock_plus,
bool other_webrequest);
- void OnShouldClose(const std::string& extension_id, int sequence_id);
+ void OnShouldUnload(const std::string& extension_id, int sequence_id);
+ void OnUnload(const std::string& extension_id);
// Update the list of active extensions that will be reported when we crash.
void UpdateActiveExtensions();
diff --git a/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/background.js b/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/background.js
new file mode 100644
index 0000000..71508c6
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/background.js
@@ -0,0 +1,9 @@
+// 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.
+
+chrome.browserAction.setTitle({title: "Failure"});
+chrome.experimental.extension.onBackgroundPageUnloadingSoon.addListener(
+ function() {
+ chrome.browserAction.setTitle({title: "Success"});
+ });
diff --git a/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/icon.png b/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/icon.png
new file mode 100644
index 0000000..e3cdb8a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/icon.png
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/manifest.json b/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/manifest.json
new file mode 100644
index 0000000..7f5a4af
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/lazy_background_page/on_unload/manifest.json
@@ -0,0 +1,15 @@
+{
+ "name": "unload test",
+ "description": "Tests that the onBackgroundPageUnloadingSoon event works",
+ "version": "1",
+ "manifest_version": 2,
+ "permissions": ["tabs", "experimental"],
+ "background": {
+ "scripts": ["background.js"],
+ "transient": true
+ },
+ "browser_action": {
+ "default_icon" : "icon.png",
+ "default_title": "Title"
+ }
+}