Commit 0d475e07 authored by koz@chromium.org's avatar koz@chromium.org

Introduce runtime.onSuspendCanceled() event.

runtime.onSuspendCanceled() is sent after runtime.onSuspend() to indicate that
the extension / app will not be suspended after all.


BUG=136469


Review URL: https://chromiumcodereview.appspot.com/10804020

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148490 0039d316-1c4b-4281-b951-d872f2087c98
parent 6cda04ba
...@@ -357,6 +357,23 @@ void EventRouter::DispatchEventImpl(const std::string& restrict_to_extension_id, ...@@ -357,6 +357,23 @@ void EventRouter::DispatchEventImpl(const std::string& restrict_to_extension_id,
std::set<const EventListener*> listeners( std::set<const EventListener*> listeners(
listeners_.GetEventListeners(*event)); listeners_.GetEventListeners(*event));
// We dispatch events for lazy background pages first because attempting to do
// so will cause those that are being suspended to cancel that suspension.
// As canceling a suspension entails sending an event to the affected
// background page, and as that event needs to be delivered before we dispatch
// the event we are dispatching here, we dispatch to the lazy listeners here
// first.
for (std::set<const EventListener*>::iterator it = listeners.begin();
it != listeners.end(); it++) {
const EventListener* listener = *it;
if (restrict_to_extension_id.empty() ||
restrict_to_extension_id == listener->extension_id) {
if (!listener->process)
DispatchLazyEvent(listener->extension_id, event);
}
}
for (std::set<const EventListener*>::iterator it = listeners.begin(); for (std::set<const EventListener*>::iterator it = listeners.begin();
it != listeners.end(); it++) { it != listeners.end(); it++) {
const EventListener* listener = *it; const EventListener* listener = *it;
...@@ -365,8 +382,6 @@ void EventRouter::DispatchEventImpl(const std::string& restrict_to_extension_id, ...@@ -365,8 +382,6 @@ void EventRouter::DispatchEventImpl(const std::string& restrict_to_extension_id,
if (listener->process) { if (listener->process) {
DispatchEventToProcess(listener->extension_id, listener->process, DispatchEventToProcess(listener->extension_id, listener->process,
event); event);
} else {
DispatchLazyEvent(listener->extension_id, event);
} }
} }
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/message_loop.h" #include "base/message_loop.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
...@@ -111,9 +112,9 @@ struct ExtensionProcessManager::BackgroundPageData { ...@@ -111,9 +112,9 @@ struct ExtensionProcessManager::BackgroundPageData {
int close_sequence_id; int close_sequence_id;
// True if the page responded to the ShouldUnload message and is currently // True if the page responded to the ShouldUnload message and is currently
// dispatching the unload event. We use this to ignore any activity // dispatching the unload event. During this time any events that arrive will
// generated during the unload event that would otherwise keep the // cancel the unload process and an onSuspendCanceled event will be dispatched
// extension alive. // to the page.
bool is_closing; bool is_closing;
// Keeps track of when this page was last unloaded. Used for perf metrics. // Keeps track of when this page was last unloaded. Used for perf metrics.
...@@ -441,6 +442,7 @@ int ExtensionProcessManager::DecrementLazyKeepaliveCount( ...@@ -441,6 +442,7 @@ int ExtensionProcessManager::DecrementLazyKeepaliveCount(
return count; return count;
} }
void ExtensionProcessManager::IncrementLazyKeepaliveCountForView( void ExtensionProcessManager::IncrementLazyKeepaliveCountForView(
RenderViewHost* render_view_host) { RenderViewHost* render_view_host) {
WebContents* web_contents = WebContents* web_contents =
...@@ -491,19 +493,24 @@ void ExtensionProcessManager::OnShouldUnloadAck( ...@@ -491,19 +493,24 @@ void ExtensionProcessManager::OnShouldUnloadAck(
} }
void ExtensionProcessManager::OnUnloadAck(const std::string& extension_id) { void ExtensionProcessManager::OnUnloadAck(const std::string& extension_id) {
background_page_data_[extension_id].is_closing = true;
int sequence_id = background_page_data_[extension_id].close_sequence_id;
MessageLoop::current()->PostDelayedTask( MessageLoop::current()->PostDelayedTask(
FROM_HERE, FROM_HERE,
base::Bind(&ExtensionProcessManager::CloseLazyBackgroundPageNow, base::Bind(&ExtensionProcessManager::CloseLazyBackgroundPageNow,
weak_ptr_factory_.GetWeakPtr(), extension_id), weak_ptr_factory_.GetWeakPtr(), extension_id, sequence_id),
event_page_unloading_time_); event_page_unloading_time_);
} }
void ExtensionProcessManager::CloseLazyBackgroundPageNow( void ExtensionProcessManager::CloseLazyBackgroundPageNow(
const std::string& extension_id) { const std::string& extension_id, int sequence_id) {
background_page_data_[extension_id].is_closing = true;
ExtensionHost* host = GetBackgroundHostForExtension(extension_id); ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
if (host) if (host &&
CloseBackgroundHost(host); sequence_id == background_page_data_[extension_id].close_sequence_id) {
ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
if (host)
CloseBackgroundHost(host);
}
} }
void ExtensionProcessManager::OnNetworkRequestStarted( void ExtensionProcessManager::OnNetworkRequestStarted(
...@@ -522,6 +529,22 @@ void ExtensionProcessManager::OnNetworkRequestDone( ...@@ -522,6 +529,22 @@ void ExtensionProcessManager::OnNetworkRequestDone(
DecrementLazyKeepaliveCount(host->extension()); DecrementLazyKeepaliveCount(host->extension());
} }
void ExtensionProcessManager::CancelSuspend(const Extension* extension) {
bool& is_closing = background_page_data_[extension->id()].is_closing;
ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
if (host && is_closing) {
is_closing = false;
host->render_view_host()->Send(
new ExtensionMsg_CancelUnload(extension->id()));
// This increment / decrement is to simulate an instantaneous event. This
// has the effect of invalidating close_sequence_id, preventing any in
// progress closes from completing and starting a new close process if
// necessary.
IncrementLazyKeepaliveCount(extension);
DecrementLazyKeepaliveCount(extension);
}
}
void ExtensionProcessManager::Observe( void ExtensionProcessManager::Observe(
int type, int type,
const content::NotificationSource& source, const content::NotificationSource& source,
......
...@@ -132,6 +132,10 @@ class ExtensionProcessManager : public content::NotificationObserver { ...@@ -132,6 +132,10 @@ class ExtensionProcessManager : public content::NotificationObserver {
void OnNetworkRequestStarted(content::RenderViewHost* render_view_host); void OnNetworkRequestStarted(content::RenderViewHost* render_view_host);
void OnNetworkRequestDone(content::RenderViewHost* render_view_host); void OnNetworkRequestDone(content::RenderViewHost* render_view_host);
// Prevents |extension|'s background page from being closed and sends the
// onSuspendCanceled() event to it.
void CancelSuspend(const extensions::Extension* extension);
protected: protected:
explicit ExtensionProcessManager(Profile* profile); explicit ExtensionProcessManager(Profile* profile);
...@@ -185,7 +189,8 @@ class ExtensionProcessManager : public content::NotificationObserver { ...@@ -185,7 +189,8 @@ class ExtensionProcessManager : public content::NotificationObserver {
void OnLazyBackgroundPageIdle(const std::string& extension_id, void OnLazyBackgroundPageIdle(const std::string& extension_id,
int sequence_id); int sequence_id);
void OnLazyBackgroundPageActive(const std::string& extension_id); void OnLazyBackgroundPageActive(const std::string& extension_id);
void CloseLazyBackgroundPageNow(const std::string& extension_id); void CloseLazyBackgroundPageNow(const std::string& extension_id,
int sequence_id);
// Updates a potentially-registered RenderViewHost once it has been // Updates a potentially-registered RenderViewHost once it has been
// associated with a WebContents. This allows us to gather information that // associated with a WebContents. This allows us to gather information that
......
...@@ -48,9 +48,10 @@ bool LazyBackgroundTaskQueue::ShouldEnqueueTask( ...@@ -48,9 +48,10 @@ bool LazyBackgroundTaskQueue::ShouldEnqueueTask(
ExtensionSystem::Get(profile)->process_manager(); ExtensionSystem::Get(profile)->process_manager();
ExtensionHost* background_host = ExtensionHost* background_host =
pm->GetBackgroundHostForExtension(extension->id()); 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; return true;
if (pm->IsBackgroundHostClosing(extension->id()))
pm->CancelSuspend(extension);
} }
return false; return false;
......
...@@ -39,7 +39,9 @@ class LazyBackgroundTaskQueue ...@@ -39,7 +39,9 @@ class LazyBackgroundTaskQueue
virtual ~LazyBackgroundTaskQueue(); virtual ~LazyBackgroundTaskQueue();
// Returns true if the task should be added to the queue (that is, if the // Returns true if the task should be added to the queue (that is, if the
// extension has a lazy background page that isn't ready yet). // extension has a lazy background page that isn't ready yet). If the
// extension has a lazy background page that is being suspended this method
// cancels that suspension.
bool ShouldEnqueueTask(Profile* profile, const Extension* extension); bool ShouldEnqueueTask(Profile* profile, const Extension* extension);
// Adds a task to the queue for a given extension. If this is the first // Adds a task to the queue for a given extension. If this is the first
......
...@@ -117,7 +117,12 @@ ...@@ -117,7 +117,12 @@
{ {
"name": "onSuspend", "name": "onSuspend",
"type": "function", "type": "function",
"description": "Sent to the event 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." "description": "Sent to the event 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. If more activity for the event page occurs before it gets unloaded the onSuspendCanceled event will be sent and the page won't be unloaded. "
},
{
"name": "onSuspendCanceled",
"type": "function",
"description": "Sent after onSuspend() to indicate that the app won't be unloaded after all."
} }
] ]
} }
......
...@@ -195,6 +195,8 @@ ...@@ -195,6 +195,8 @@
<a href="#event-onInstalled">onInstalled</a> <a href="#event-onInstalled">onInstalled</a>
</li><li> </li><li>
<a href="#event-onSuspend">onSuspend</a> <a href="#event-onSuspend">onSuspend</a>
</li><li>
<a href="#event-onSuspendCanceled">onSuspendCanceled</a>
</li> </li>
</ol> </ol>
</li> </li>
...@@ -503,7 +505,23 @@ ...@@ -503,7 +505,23 @@
<span class="subdued">chrome.runtime.</span><span>onSuspend</span><span class="subdued">.addListener</span>(function(<span></span>) <span class="subdued">{...}</span><span></span>); <span class="subdued">chrome.runtime.</span><span>onSuspend</span><span class="subdued">.addListener</span>(function(<span></span>) <span class="subdued">{...}</span><span></span>);
</div> </div>
<div class="description"> <div class="description">
<p>Sent to the event 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> <p>Sent to the event 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. If more activity for the event page occurs before it gets unloaded the onSuspendCanceled event will be sent and the page won't be unloaded. </p>
<!-- LISTENER PARAMETERS -->
<!-- EXTRA PARAMETERS -->
<!-- LISTENER RETURN VALUE -->
<dl>
</dl>
</div> <!-- /description -->
<!-- /description -->
</div><div class="apiItem">
<a name="event-onSuspendCanceled"></a>
<h4>onSuspendCanceled</h4>
<div class="summary">
<!-- Note: intentionally longer 80 columns -->
<span class="subdued">chrome.runtime.</span><span>onSuspendCanceled</span><span class="subdued">.addListener</span>(function(<span></span>) <span class="subdued">{...}</span><span></span>);
</div>
<div class="description">
<p>Sent after onSuspend() to indicate that the app won't be unloaded after all.</p>
<!-- LISTENER PARAMETERS --> <!-- LISTENER PARAMETERS -->
<!-- EXTRA PARAMETERS --> <!-- EXTRA PARAMETERS -->
<!-- LISTENER RETURN VALUE --> <!-- LISTENER RETURN VALUE -->
......
...@@ -224,6 +224,8 @@ ...@@ -224,6 +224,8 @@
<a href="#event-onInstalled">onInstalled</a> <a href="#event-onInstalled">onInstalled</a>
</li><li> </li><li>
<a href="#event-onSuspend">onSuspend</a> <a href="#event-onSuspend">onSuspend</a>
</li><li>
<a href="#event-onSuspendCanceled">onSuspendCanceled</a>
</li> </li>
</ol> </ol>
</li> </li>
...@@ -570,7 +572,23 @@ ...@@ -570,7 +572,23 @@
<span class="subdued">chrome.runtime.</span><span>onSuspend</span><span class="subdued">.addListener</span>(function(<span></span>) <span class="subdued">{...}</span><span></span>); <span class="subdued">chrome.runtime.</span><span>onSuspend</span><span class="subdued">.addListener</span>(function(<span></span>) <span class="subdued">{...}</span><span></span>);
</div> </div>
<div class="description"> <div class="description">
<p>Sent to the event 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> <p>Sent to the event 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. If more activity for the event page occurs before it gets unloaded the onSuspendCanceled event will be sent and the page won't be unloaded. </p>
<!-- LISTENER PARAMETERS -->
<!-- EXTRA PARAMETERS -->
<!-- LISTENER RETURN VALUE -->
<dl>
</dl>
</div> <!-- /description -->
<!-- /description -->
</div><div class="apiItem">
<a name="event-onSuspendCanceled"></a>
<h4>onSuspendCanceled</h4>
<div class="summary">
<!-- Note: intentionally longer 80 columns -->
<span class="subdued">chrome.runtime.</span><span>onSuspendCanceled</span><span class="subdued">.addListener</span>(function(<span></span>) <span class="subdued">{...}</span><span></span>);
</div>
<div class="description">
<p>Sent after onSuspend() to indicate that the app won't be unloaded after all.</p>
<!-- LISTENER PARAMETERS --> <!-- LISTENER PARAMETERS -->
<!-- EXTRA PARAMETERS --> <!-- EXTRA PARAMETERS -->
<!-- LISTENER RETURN VALUE --> <!-- LISTENER RETURN VALUE -->
......
...@@ -310,6 +310,7 @@ ...@@ -310,6 +310,7 @@
"chrome.runtime.getURL": "runtime.html#method-getURL", "chrome.runtime.getURL": "runtime.html#method-getURL",
"chrome.runtime.onInstalled": "runtime.html#event-onInstalled", "chrome.runtime.onInstalled": "runtime.html#event-onInstalled",
"chrome.runtime.onSuspend": "runtime.html#event-onSuspend", "chrome.runtime.onSuspend": "runtime.html#event-onSuspend",
"chrome.runtime.onSuspendCanceled": "runtime.html#event-onSuspendCanceled",
"chrome.scriptBadge.getAttention": "scriptBadge.html#method-getAttention", "chrome.scriptBadge.getAttention": "scriptBadge.html#method-getAttention",
"chrome.scriptBadge.getPopup": "scriptBadge.html#method-getPopup", "chrome.scriptBadge.getPopup": "scriptBadge.html#method-getPopup",
"chrome.scriptBadge.onClicked": "scriptBadge.html#event-onClicked", "chrome.scriptBadge.onClicked": "scriptBadge.html#event-onClicked",
......
...@@ -287,6 +287,10 @@ IPC_MESSAGE_CONTROL2(ExtensionMsg_ShouldUnload, ...@@ -287,6 +287,10 @@ IPC_MESSAGE_CONTROL2(ExtensionMsg_ShouldUnload,
IPC_MESSAGE_CONTROL1(ExtensionMsg_Unload, IPC_MESSAGE_CONTROL1(ExtensionMsg_Unload,
std::string /* extension_id */) std::string /* extension_id */)
// The browser changed its mind about unloading this extension.
IPC_MESSAGE_CONTROL1(ExtensionMsg_CancelUnload,
std::string /* extension_id */)
// Send to renderer once the installation mentioned on // Send to renderer once the installation mentioned on
// ExtensionHostMsg_InlineWebstoreInstall is complete. // ExtensionHostMsg_InlineWebstoreInstall is complete.
IPC_MESSAGE_ROUTED3(ExtensionMsg_InlineWebstoreInstallResponse, IPC_MESSAGE_ROUTED3(ExtensionMsg_InlineWebstoreInstallResponse,
......
...@@ -110,6 +110,7 @@ static const int64 kInitialExtensionIdleHandlerDelayMs = 5*1000; ...@@ -110,6 +110,7 @@ static const int64 kInitialExtensionIdleHandlerDelayMs = 5*1000;
static const int64 kMaxExtensionIdleHandlerDelayMs = 5*60*1000; static const int64 kMaxExtensionIdleHandlerDelayMs = 5*60*1000;
static const char kEventDispatchFunction[] = "Event.dispatchJSON"; static const char kEventDispatchFunction[] = "Event.dispatchJSON";
static const char kOnUnloadEvent[] = "runtime.onSuspend"; static const char kOnUnloadEvent[] = "runtime.onSuspend";
static const char kOnSuspendCanceledEvent[] = "runtime.onSuspendCanceled";
class ChromeHiddenNativeHandler : public NativeHandler { class ChromeHiddenNativeHandler : public NativeHandler {
public: public:
...@@ -325,6 +326,7 @@ bool ExtensionDispatcher::OnControlMessageReceived( ...@@ -325,6 +326,7 @@ bool ExtensionDispatcher::OnControlMessageReceived(
IPC_MESSAGE_HANDLER(ExtensionMsg_UsingWebRequestAPI, OnUsingWebRequestAPI) IPC_MESSAGE_HANDLER(ExtensionMsg_UsingWebRequestAPI, OnUsingWebRequestAPI)
IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldUnload, OnShouldUnload) IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldUnload, OnShouldUnload)
IPC_MESSAGE_HANDLER(ExtensionMsg_Unload, OnUnload) IPC_MESSAGE_HANDLER(ExtensionMsg_Unload, OnUnload)
IPC_MESSAGE_HANDLER(ExtensionMsg_CancelUnload, OnCancelUnload)
IPC_MESSAGE_UNHANDLED(handled = false) IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP() IPC_END_MESSAGE_MAP()
...@@ -1001,6 +1003,14 @@ void ExtensionDispatcher::OnUnload(const std::string& extension_id) { ...@@ -1001,6 +1003,14 @@ void ExtensionDispatcher::OnUnload(const std::string& extension_id) {
RenderThread::Get()->Send(new ExtensionHostMsg_UnloadAck(extension_id)); RenderThread::Get()->Send(new ExtensionHostMsg_UnloadAck(extension_id));
} }
void ExtensionDispatcher::OnCancelUnload(const std::string& extension_id) {
ListValue args;
args.Set(0, Value::CreateStringValue(kOnSuspendCanceledEvent));
args.Set(1, Value::CreateStringValue("[]"));
v8_context_set_.DispatchChromeHiddenMethod(
extension_id, kEventDispatchFunction, args, NULL, GURL());
}
Feature::Context ExtensionDispatcher::ClassifyJavaScriptContext( Feature::Context ExtensionDispatcher::ClassifyJavaScriptContext(
const std::string& extension_id, const std::string& extension_id,
int extension_group, int extension_group,
......
...@@ -173,6 +173,7 @@ class ExtensionDispatcher : public content::RenderProcessObserver { ...@@ -173,6 +173,7 @@ class ExtensionDispatcher : public content::RenderProcessObserver {
bool other_webrequest); bool other_webrequest);
void OnShouldUnload(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); void OnUnload(const std::string& extension_id);
void OnCancelUnload(const std::string& extension_id);
// Update the list of active extensions that will be reported when we crash. // Update the list of active extensions that will be reported when we crash.
void UpdateActiveExtensions(); void UpdateActiveExtensions();
......
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