summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-18 22:56:58 +0000
committermpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-18 22:56:58 +0000
commit1f70f0ca51d1c61d3a775507b2b69dcdf60e77df (patch)
tree41659daa24b3e9af8996b2782dffead89fdcc63c /chrome
parent11de3e98153ad8dcb9e6628e527f7bfb2ca0a8ed (diff)
downloadchromium_src-1f70f0ca51d1c61d3a775507b2b69dcdf60e77df.zip
chromium_src-1f70f0ca51d1c61d3a775507b2b69dcdf60e77df.tar.gz
chromium_src-1f70f0ca51d1c61d3a775507b2b69dcdf60e77df.tar.bz2
Send port-closed notification when a frame with ports unloads.
Also add onLoad and onUnload chrome Event to our bindings, so we can add listeners to these events without needing a DOM. These don't hook into the window "unload" event, so we no longer prevent Chrome's sudden termination of tabs on shutdown. BUG=12686 TEST=no Review URL: http://codereview.chromium.org/125280 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18765 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/automation/extension_port_container.cc2
-rw-r--r--chrome/browser/extensions/extension_message_service.cc33
-rw-r--r--chrome/browser/extensions/extension_message_service.h37
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc17
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.h7
-rw-r--r--chrome/common/render_messages_internal.h5
-rw-r--r--chrome/renderer/extensions/event_bindings.cc31
-rw-r--r--chrome/renderer/extensions/event_bindings.h8
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.cc13
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.h1
-rw-r--r--chrome/renderer/extensions/renderer_extension_bindings.cc13
-rw-r--r--chrome/renderer/render_view.cc28
-rw-r--r--chrome/renderer/render_view.h2
-rw-r--r--chrome/renderer/render_widget.h2
-rw-r--r--chrome/renderer/renderer_resources.grd2
-rw-r--r--chrome/renderer/resources/event_bindings.js31
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js54
-rw-r--r--chrome/renderer/resources/greasemonkey_api.js4
-rw-r--r--chrome/renderer/resources/renderer_extension_bindings.js13
19 files changed, 206 insertions, 97 deletions
diff --git a/chrome/browser/automation/extension_port_container.cc b/chrome/browser/automation/extension_port_container.cc
index 15c2c8e..581da75 100644
--- a/chrome/browser/automation/extension_port_container.cc
+++ b/chrome/browser/automation/extension_port_container.cc
@@ -34,7 +34,7 @@ ExtensionPortContainer::~ExtensionPortContainer() {
DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
if (port_id_ != -1)
- service_->CloseAutomationChannel(port_id_);
+ service_->CloseChannel(port_id_);
}
bool ExtensionPortContainer::PostResponseToExternalPort(
diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc
index 33fbcf2..85f8146 100644
--- a/chrome/browser/extensions/extension_message_service.cc
+++ b/chrome/browser/extensions/extension_message_service.cc
@@ -291,13 +291,28 @@ int ExtensionMessageService::OpenAutomationChannelToExtension(
return port2_id;
}
-void ExtensionMessageService::CloseAutomationChannel(int port_id) {
+void ExtensionMessageService::CloseChannel(int port_id) {
DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
- // TODO(siggi): Cleanup from the tab seems to beat this to the punch.
- // DCHECK(channels_[GET_CHANNEL_ID(port_id)].port1 != NULL);
- // TODO(siggi): should we notify the other side of the port?
- channels_.erase(GET_CHANNEL_ID(port_id));
+ // Note: The channel might be gone already, if the other side closed first.
+ MessageChannelMap::iterator it = channels_.find(GET_CHANNEL_ID(port_id));
+ if (it != channels_.end())
+ CloseChannelImpl(it, port_id);
+}
+
+void ExtensionMessageService::CloseChannelImpl(
+ MessageChannelMap::iterator channel_iter, int port_id) {
+ DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
+
+ // Notify the other side.
+ if (port_id == GET_CHANNEL_PORT1(channel_iter->first)) {
+ DispatchOnDisconnect(channel_iter->second.port2, port_id);
+ } else {
+ DCHECK_EQ(port_id, GET_CHANNEL_PORT2(channel_iter->first));
+ DispatchOnDisconnect(channel_iter->second.port1, port_id);
+ }
+
+ channels_.erase(channel_iter);
}
void ExtensionMessageService::PostMessageFromRenderer(
@@ -366,13 +381,9 @@ void ExtensionMessageService::Observe(NotificationType type,
it != channels_.end(); ) {
MessageChannelMap::iterator current = it++;
if (current->second.port1 == renderer) {
- DispatchOnDisconnect(current->second.port2,
- GET_CHANNEL_PORT1(current->first));
- channels_.erase(current);
+ CloseChannelImpl(current, GET_CHANNEL_PORT1(current->first));
} else if (current->second.port2 == renderer) {
- DispatchOnDisconnect(current->second.port1,
- GET_CHANNEL_PORT2(current->first));
- channels_.erase(current);
+ CloseChannelImpl(current, GET_CHANNEL_PORT2(current->first));
}
}
}
diff --git a/chrome/browser/extensions/extension_message_service.h b/chrome/browser/extensions/extension_message_service.h
index dde7280..f128103 100644
--- a/chrome/browser/extensions/extension_message_service.h
+++ b/chrome/browser/extensions/extension_message_service.h
@@ -54,22 +54,17 @@ class ExtensionMessageService : public NotificationObserver {
void AddEventListener(std::string event_name, int render_process_id);
void RemoveEventListener(std::string event_name, int render_process_id);
- // Closes an extension channel for test automation.
- void CloseAutomationChannel(int port_id);
+ // Closes the message channel associated with the given port, and notifies
+ // the other side.
+ void CloseChannel(int port_id);
// Sends a message from a renderer to the given port.
- // TODO(mpcomplete): include the source tab.
void PostMessageFromRenderer(int port_id, const std::string& message);
// Send an event to every registered extension renderer.
void DispatchEventToRenderers(
const std::string& event_name, const std::string& event_args);
- // NotificationObserver interface.
- void Observe(NotificationType type,
- const NotificationSource& source,
- const NotificationDetails& details);
-
// Given an extension's ID, opens a channel between the given automation
// "port" and that extension. Returns a channel ID to be used for posting
// messages between the processes, or -1 if the extension doesn't exist.
@@ -78,6 +73,11 @@ class ExtensionMessageService : public NotificationObserver {
const std::string& extension_id,
IPC::Message::Sender* source);
+ // NotificationObserver interface.
+ void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
// --- IO thread only:
// Given an extension's ID, opens a channel between the given renderer "port"
@@ -89,6 +89,16 @@ class ExtensionMessageService : public NotificationObserver {
ResourceMessageFilter* source);
private:
+ // The connection between two ports. It is possible that both ports
+ // refer to the same renderer.
+ struct MessageChannel {
+ IPC::Message::Sender* port1;
+ IPC::Message::Sender* port2;
+ };
+
+ // A map of channel ID to its channel object.
+ typedef std::map<int, MessageChannel> MessageChannelMap;
+
// Allocates a pair of port ids.
// NOTE: this can be called from any thread.
void AllocatePortIdPair(int* port1, int* port2);
@@ -97,6 +107,8 @@ class ExtensionMessageService : public NotificationObserver {
// NOTE: this can be called from any thread.
int GetProcessIdForExtension(const std::string& extension_id);
+ void CloseChannelImpl(MessageChannelMap::iterator channel_iter, int port_id);
+
int OpenChannelToExtensionImpl(const std::string& extension_id,
IPC::Message::Sender* source);
@@ -136,15 +148,6 @@ class ExtensionMessageService : public NotificationObserver {
int source_routing_id, int source_port_id, IPC::Message::Sender* source,
int dest_port_id, int dest_process_id, int source_process_id);
- // The connection between two ports. It is possible that both ports
- // refer to the same renderer.
- struct MessageChannel {
- IPC::Message::Sender* port1;
- IPC::Message::Sender* port2;
- };
-
- // A map of channel ID to its channel object.
- typedef std::map<int, MessageChannel> MessageChannelMap;
MessageChannelMap channels_;
// True if Init has been called.
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc
index 06405ed..2f0d07a 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -641,6 +641,8 @@ void BrowserRenderProcessHost::OnMessageReceived(const IPC::Message& msg) {
OnExtensionAddListener)
IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionRemoveListener,
OnExtensionRemoveListener)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionCloseChannel,
+ OnExtensionCloseChannel)
IPC_MESSAGE_UNHANDLED_ERROR()
IPC_END_MESSAGE_MAP_EX()
@@ -857,14 +859,17 @@ void BrowserRenderProcessHost::Observe(NotificationType type,
void BrowserRenderProcessHost::OnExtensionAddListener(
const std::string& event_name) {
- URLRequestContext* context = profile()->GetRequestContext();
- ExtensionMessageService* ems = ExtensionMessageService::GetInstance(context);
- ems->AddEventListener(event_name, pid());
+ ExtensionMessageService::GetInstance(profile()->GetRequestContext())->
+ AddEventListener(event_name, pid());
}
void BrowserRenderProcessHost::OnExtensionRemoveListener(
const std::string& event_name) {
- URLRequestContext* context = profile()->GetRequestContext();
- ExtensionMessageService* ems = ExtensionMessageService::GetInstance(context);
- ems->RemoveEventListener(event_name, pid());
+ ExtensionMessageService::GetInstance(profile()->GetRequestContext())->
+ RemoveEventListener(event_name, pid());
+}
+
+void BrowserRenderProcessHost::OnExtensionCloseChannel(int port_id) {
+ ExtensionMessageService::GetInstance(profile()->GetRequestContext())->
+ CloseChannel(port_id);
}
diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h
index 6c259f0..2fd0d05 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.h
+++ b/chrome/browser/renderer_host/browser_render_process_host.h
@@ -86,16 +86,15 @@ class BrowserRenderProcessHost : public RenderProcessHost,
const NotificationSource& source,
const NotificationDetails& details);
- // An extension process started or stopped listening to an event.
- void OnExtensionAddListener(const std::string& event_name);
- void OnExtensionRemoveListener(const std::string& event_name);
-
private:
// Control message handlers.
void OnPageContents(const GURL& url, int32 page_id,
const std::wstring& contents);
void OnUpdatedCacheStats(const WebKit::WebCache::UsageStats& stats);
void SuddenTerminationChanged(bool enabled);
+ void OnExtensionAddListener(const std::string& event_name);
+ void OnExtensionRemoveListener(const std::string& event_name);
+ void OnExtensionCloseChannel(int port_id);
// Initialize support for visited links. Send the renderer process its initial
// set of visited links.
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index bd3c059..e0ba76e 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -1401,6 +1401,11 @@ IPC_BEGIN_MESSAGES(ViewHost)
int /* port_id */,
std::string /* message */)
+ // Send a message to an extension process. The handle is the value returned
+ // by ViewHostMsg_OpenChannelToExtension.
+ IPC_MESSAGE_CONTROL1(ViewHostMsg_ExtensionCloseChannel,
+ int /* port_id */)
+
// Message to show a popup menu using native cocoa controls (Mac only).
IPC_MESSAGE_ROUTED1(ViewHostMsg_ShowPopup,
ViewHostMsg_ShowPopup_Params)
diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc
index 576beba..e412245 100644
--- a/chrome/renderer/extensions/event_bindings.cc
+++ b/chrome/renderer/extensions/event_bindings.cc
@@ -12,16 +12,17 @@
#include "chrome/renderer/js_only_v8_extensions.h"
#include "chrome/renderer/render_thread.h"
#include "grit/renderer_resources.h"
+#include "webkit/api/public/WebScriptSource.h"
+#include "webkit/glue/webframe.h"
+
+using WebKit::WebScriptSource;
+using WebKit::WebString;
namespace {
// Keep a local cache of RenderThread so that we can mock it out for unit tests.
static RenderThreadBase* render_thread = NULL;
-static RenderThreadBase* GetRenderThread() {
- return render_thread ? render_thread : RenderThread::current();
-}
-
// Keep a list of contexts that have registered themselves with us. This lets
// us know where to dispatch events when we receive them.
typedef std::list< v8::Persistent<v8::Context> > ContextList;
@@ -91,7 +92,7 @@ class ExtensionImpl : public v8::Extension {
if (args[0]->IsString()) {
std::string event_name(*v8::String::AsciiValue(args[0]));
if (EventIncrementListenerCount(event_name) == 1) {
- GetRenderThread()->Send(
+ EventBindings::GetRenderThread()->Send(
new ViewHostMsg_ExtensionAddListener(event_name));
}
}
@@ -122,7 +123,7 @@ class ExtensionImpl : public v8::Extension {
if (args[0]->IsString()) {
std::string event_name(*v8::String::AsciiValue(args[0]));
if (EventDecrementListenerCount(event_name) == 0) {
- GetRenderThread()->Send(
+ EventBindings::GetRenderThread()->Send(
new ViewHostMsg_ExtensionRemoveListener(event_name));
}
}
@@ -164,6 +165,24 @@ void EventBindings::SetRenderThread(RenderThreadBase* thread) {
render_thread = thread;
}
+// static
+RenderThreadBase* EventBindings::GetRenderThread() {
+ return render_thread ? render_thread : RenderThread::current();
+}
+
+// static
+void EventBindings::HandleDocumentReady(WebFrame* frame) {
+ frame->ExecuteScript(WebScriptSource(WebString::fromUTF8(
+ "chrome.dispatchOnLoad_();")));
+}
+
+// static
+void EventBindings::HandleDocumentClose(WebFrame* frame) {
+ frame->ExecuteScript(WebScriptSource(WebString::fromUTF8(
+ "chrome.dispatchOnUnload_();")));
+}
+
+// static
void EventBindings::CallFunction(const std::string& function_name,
int argc, v8::Handle<v8::Value>* argv) {
for (ContextList::iterator it = GetRegisteredContexts().begin();
diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h
index 0ca59c0..9e31d76 100644
--- a/chrome/renderer/extensions/event_bindings.h
+++ b/chrome/renderer/extensions/event_bindings.h
@@ -10,6 +10,7 @@
#include <string>
class RenderThreadBase;
+class WebFrame;
// This class deals with the javascript bindings related to Event objects.
class EventBindings {
@@ -19,6 +20,13 @@ class EventBindings {
// Allow RenderThread to be mocked out.
static void SetRenderThread(RenderThreadBase* thread);
+ static RenderThreadBase* GetRenderThread();
+
+ // Notify script that the document object is ready. We don't use the DOM
+ // unload event because that causes Chrome's "sudden termination" to be
+ // disabled for this renderer, meaning we'll slow down shutdown.
+ static void HandleDocumentReady(WebFrame* frame);
+ static void HandleDocumentClose(WebFrame* frame);
// Calls the given function in each registered context which is listening
// for events. The function can be an object property, ie:
diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc
index 1ce7ea2..907f4c4 100644
--- a/chrome/renderer/extensions/extension_process_bindings.cc
+++ b/chrome/renderer/extensions/extension_process_bindings.cc
@@ -14,12 +14,8 @@
#include "chrome/renderer/js_only_v8_extensions.h"
#include "chrome/renderer/render_view.h"
#include "grit/renderer_resources.h"
-#include "webkit/api/public/WebScriptSource.h"
#include "webkit/glue/webframe.h"
-using WebKit::WebScriptSource;
-using WebKit::WebString;
-
namespace {
const char kExtensionName[] = "chrome/ExtensionProcessBindings";
@@ -105,7 +101,9 @@ class ExtensionImpl : public v8::Extension {
RenderView* renderview = GetRenderViewForCurrentContext();
DCHECK(renderview);
GURL url = renderview->webview()->GetMainFrame()->GetURL();
- DCHECK(url.scheme() == chrome::kExtensionScheme);
+ // Don't check the URL scheme in unit tests.
+ if (RenderThread::current())
+ DCHECK(url.scheme() == chrome::kExtensionScheme);
v8::Persistent<v8::Context> current_context =
v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
@@ -218,11 +216,6 @@ void ExtensionProcessBindings::SetFunctionNames(
ExtensionImpl::SetFunctionNames(names);
}
-void ExtensionProcessBindings::RegisterExtensionContext(WebFrame* frame) {
- frame->ExecuteScript(WebScriptSource(WebString::fromUTF8(
- "chrome.self.register_();")));
-}
-
void ExtensionProcessBindings::HandleResponse(int request_id, bool success,
const std::string& response,
const std::string& error) {
diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h
index 9639090..fc62a73 100644
--- a/chrome/renderer/extensions/extension_process_bindings.h
+++ b/chrome/renderer/extensions/extension_process_bindings.h
@@ -18,7 +18,6 @@ class ExtensionProcessBindings {
public:
static void SetFunctionNames(const std::vector<std::string>& names);
static v8::Extension* Get();
- static void RegisterExtensionContext(WebFrame* frame);
static void HandleResponse(int request_id, bool success,
const std::string& response,
const std::string& error);
diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc
index 0931c8c..b250ff5 100644
--- a/chrome/renderer/extensions/renderer_extension_bindings.cc
+++ b/chrome/renderer/extensions/renderer_extension_bindings.cc
@@ -43,6 +43,8 @@ class ExtensionImpl : public v8::Extension {
return v8::FunctionTemplate::New(OpenChannelToExtension);
} else if (name->Equals(v8::String::New("PostMessage"))) {
return v8::FunctionTemplate::New(PostMessage);
+ } else if (name->Equals(v8::String::New("CloseChannel"))) {
+ return v8::FunctionTemplate::New(CloseChannel);
}
return v8::Handle<v8::FunctionTemplate>();
}
@@ -80,6 +82,17 @@ class ExtensionImpl : public v8::Extension {
}
return v8::Undefined();
}
+
+ // Sends a message along the given channel.
+ static v8::Handle<v8::Value> CloseChannel(const v8::Arguments& args) {
+ if (args.Length() >= 1 && args[0]->IsInt32()) {
+ int port_id = args[0]->Int32Value();
+ // Send via the RenderThread because the RenderView might be closing.
+ EventBindings::GetRenderThread()->Send(
+ new ViewHostMsg_ExtensionCloseChannel(port_id));
+ }
+ return v8::Undefined();
+ }
};
// Convert a ListValue to a vector of V8 values.
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 6151878..33b50d6 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -35,6 +35,7 @@
#include "chrome/renderer/debug_message_handler.h"
#include "chrome/renderer/devtools_agent.h"
#include "chrome/renderer/devtools_client.h"
+#include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/extensions/extension_process_bindings.h"
#include "chrome/renderer/localized_error.h"
#include "chrome/renderer/media/audio_renderer_impl.h"
@@ -325,6 +326,22 @@ void RenderView::Init(gfx::NativeViewId parent_hwnd,
render_thread_->AddFilter(audio_message_filter_);
}
+// Recursively calls WillCloseFrame on every frame in the hierarchy.
+static void WillCloseFrameTree(RenderView* renderview, WebFrame* frame) {
+ renderview->WillCloseFrame(renderview->webview(), frame);
+
+ for (WebFrame* child = frame->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ WillCloseFrameTree(renderview, child);
+ }
+}
+
+void RenderView::Close() {
+ if (webview() && webview()->GetMainFrame())
+ WillCloseFrameTree(this, webview()->GetMainFrame());
+ RenderWidget::Close();
+}
+
void RenderView::OnMessageReceived(const IPC::Message& message) {
WebFrame* main_frame = webview() ? webview()->GetMainFrame() : NULL;
renderer_logging::ScopedActiveRenderingURLSetter url_setter(
@@ -1395,17 +1412,18 @@ void RenderView::DocumentElementAvailable(WebFrame* frame) {
if (frame->GetURL().SchemeIs(chrome::kExtensionScheme))
frame->GrantUniversalAccess();
- // Tell extensions to self-register their js contexts.
- // TODO(rafaelw): This is kind of gross. We need a way to call through
- // the glue layer to retrieve the current v8::Context.
- if (frame->GetURL().SchemeIs(chrome::kExtensionScheme))
- ExtensionProcessBindings::RegisterExtensionContext(frame);
+ // Tell bindings that the DOM is ready.
+ EventBindings::HandleDocumentReady(frame);
if (RenderThread::current()) // Will be NULL during unit tests.
RenderThread::current()->user_script_slave()->InjectScripts(
frame, UserScript::DOCUMENT_START);
}
+void RenderView::WillCloseFrame(WebView* webview, WebFrame* frame) {
+ EventBindings::HandleDocumentClose(frame);
+}
+
WindowOpenDisposition RenderView::DispositionForNavigationAction(
WebView* webview,
WebFrame* frame,
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index 5e9d75b..7e593e8 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -225,6 +225,7 @@ class RenderView : public RenderWidget,
virtual void WindowObjectCleared(WebFrame* webframe);
virtual void DocumentElementAvailable(WebFrame* webframe);
+ virtual void WillCloseFrame(WebView* webview, WebFrame* frame);
virtual WindowOpenDisposition DispositionForNavigationAction(
WebView* webview,
@@ -406,6 +407,7 @@ class RenderView : public RenderWidget,
const WebPreferences& webkit_prefs,
SharedRenderViewCounter* counter,
int32 routing_id);
+ virtual void Close();
void UpdateURL(WebFrame* frame);
void UpdateTitle(WebFrame* frame, const std::wstring& title);
diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h
index 440a7e2..780671a 100644
--- a/chrome/renderer/render_widget.h
+++ b/chrome/renderer/render_widget.h
@@ -96,7 +96,7 @@ class RenderWidget : public IPC::Channel::Listener,
void GenerateFullRepaint();
// Close the underlying WebWidget.
- void Close();
+ virtual void Close();
protected:
// Friend RefCounted so that the dtor can be non-public. Using this class
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index c59e98c..cda253b 100644
--- a/chrome/renderer/renderer_resources.grd
+++ b/chrome/renderer/renderer_resources.grd
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This comment is only here because changes to resources are not picked up
-without changes to the corresponding grd file. -->
+without changes to the corresponding grd file. -->
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/renderer_resources.h" type="rc_header">
diff --git a/chrome/renderer/resources/event_bindings.js b/chrome/renderer/resources/event_bindings.js
index f11ec78..54dd108 100644
--- a/chrome/renderer/resources/event_bindings.js
+++ b/chrome/renderer/resources/event_bindings.js
@@ -1,5 +1,5 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
+// Copyright (c) 2009 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.
// -----------------------------------------------------------------------------
@@ -29,6 +29,9 @@ var chrome = chrome || {};
// A map of event names to the event object that is registered to that name.
chrome.Event.attached_ = {};
+ // An array of all attached event objects, used for detaching on unload.
+ chrome.Event.allAttached_ = [];
+
// Dispatches a named event with the given JSON array, which is deserialized
// before dispatch. The JSON array is the list of arguments that will be
// sent with the event callback.
@@ -105,8 +108,7 @@ var chrome = chrome || {};
// name.
chrome.Event.prototype.attach_ = function() {
AttachEvent(this.eventName_);
- this.unloadHandler_ = this.detach_.bind(this);
- window.addEventListener('unload', this.unloadHandler_, false);
+ chrome.Event.allAttached_[chrome.Event.allAttached_.length] = this;
if (!this.eventName_)
return;
@@ -120,7 +122,9 @@ var chrome = chrome || {};
// Detaches this event object from its name.
chrome.Event.prototype.detach_ = function() {
- window.removeEventListener('unload', this.unloadHandler_, false);
+ var i = chrome.Event.allAttached_.indexOf(this);
+ if (i >= 0)
+ delete chrome.Event.allAttached_[i];
DetachEvent(this.eventName_);
if (!this.eventName_)
return;
@@ -132,4 +136,21 @@ var chrome = chrome || {};
delete chrome.Event.attached_[this.eventName_];
};
+
+ // Load events. Note that onUnload_ might not always fire, since Chrome will
+ // terminate renderers on shutdown.
+ chrome.onLoad_ = new chrome.Event();
+ chrome.onUnload_ = new chrome.Event();
+
+ // This is called by native code when the DOM is ready.
+ chrome.dispatchOnLoad_ = function() {
+ chrome.onLoad_.dispatch();
+ delete chrome.dispatchOnLoad_;
+ }
+
+ chrome.dispatchOnUnload_ = function() {
+ chrome.onUnload_.dispatch();
+ for (var i in chrome.Event.allAttached_)
+ chrome.Event.allAttached_[i].detach_();
+ }
})();
diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js
index ea6481c..93d9f50 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -51,7 +51,7 @@ var chrome;
validator.validate(args[i], schemas[i]);
if (validator.errors.length == 0)
continue;
-
+
var message = "Invalid value for argument " + i + ". ";
for (var i = 0, err; err = validator.errors[i]; i++) {
if (err.path) {
@@ -83,7 +83,7 @@ var chrome;
console.error("Error during " + name + ": " + error);
return;
}
-
+
if (callbacks[requestId]) {
if (response) {
callbacks[requestId](JSON.parse(response));
@@ -125,7 +125,7 @@ var chrome;
chrome.types.pInt,
chrome.types.fun
];
-
+
chrome.windows.getCurrent = function(callback) {
validate(arguments, arguments.callee.params);
sendRequest(GetCurrentWindow, null, callback);
@@ -134,7 +134,7 @@ var chrome;
chrome.windows.getCurrent.params = [
chrome.types.fun
];
-
+
chrome.windows.getLastFocused = function(callback) {
validate(arguments, arguments.callee.params);
sendRequest(GetLastFocusedWindow, null, callback);
@@ -153,7 +153,7 @@ var chrome;
chrome.types.optBool,
chrome.types.fun
];
-
+
chrome.windows.create = function(createData, callback) {
validate(arguments, arguments.callee.params);
sendRequest(CreateWindow, createData, callback);
@@ -200,7 +200,7 @@ var chrome;
chrome.types.pInt,
chrome.types.optFun
];
-
+
// sends (windowId).
// *WILL* be followed by tab-attached AND then tab-selection-changed.
chrome.windows.onCreated = new chrome.Event("window-created");
@@ -210,7 +210,7 @@ var chrome;
// tab-selection-changed -- one for each tab that was contained in the window
// that closed
chrome.windows.onRemoved = new chrome.Event("window-removed");
-
+
// sends (windowId).
chrome.windows.onFocusChanged =
new chrome.Event("window-focus-changed");
@@ -229,7 +229,7 @@ var chrome;
chrome.types.pInt,
chrome.types.fun
];
-
+
chrome.tabs.getSelected = function(windowId, callback) {
validate(arguments, arguments.callee.params);
sendRequest(GetSelectedTab, windowId, callback);
@@ -250,7 +250,7 @@ var chrome;
chrome.types.fun
];
- chrome.tabs.create = function(tab, callback) {
+ chrome.tabs.create = function(tab, callback) {
validate(arguments, arguments.callee.params);
sendRequest(CreateTab, tab, callback);
};
@@ -301,7 +301,7 @@ var chrome;
},
chrome.types.optFun
];
-
+
chrome.tabs.remove = function(tabId, callback) {
validate(arguments, arguments.callee.params);
sendRequest(RemoveTab, tabId, callback);
@@ -316,26 +316,26 @@ var chrome;
// Will *NOT* be followed by tab-attached - it is implied.
// *MAY* be followed by tab-selection-changed.
chrome.tabs.onCreated = new chrome.Event("tab-created");
-
+
// Sends (tabId, {ChangedProps}).
chrome.tabs.onUpdated = new chrome.Event("tab-updated");
// Sends (tabId, {windowId, fromIndex, toIndex}).
// Tabs can only "move" within a window.
chrome.tabs.onMoved = new chrome.Event("tab-moved");
-
+
// Sends (tabId, {windowId}).
- chrome.tabs.onSelectionChanged =
+ chrome.tabs.onSelectionChanged =
new chrome.Event("tab-selection-changed");
-
+
// Sends (tabId, {newWindowId, newPosition}).
// *MAY* be followed by tab-selection-changed.
chrome.tabs.onAttached = new chrome.Event("tab-attached");
-
+
// Sends (tabId, {oldWindowId, oldPosition}).
// *WILL* be followed by tab-selection-changed.
chrome.tabs.onDetached = new chrome.Event("tab-detached");
-
+
// Sends (tabId).
// *WILL* be followed by tab-selection-changed.
// Will *NOT* be followed or preceded by tab-detached.
@@ -351,7 +351,7 @@ var chrome;
sendRequest(EnablePageAction, [pageActionId, action]);
}
- chrome.pageActions.enableForTab.params = [
+ chrome.pageActions.enableForTab.params = [
chrome.types.str,
{
type: "object",
@@ -380,7 +380,7 @@ var chrome;
chrome.types.singleOrListOf(chrome.types.pInt),
chrome.types.fun
];
-
+
chrome.bookmarks.getChildren = function(id, callback) {
validate(arguments, arguments.callee.params);
sendRequest(GetBookmarkChildren, id, callback);
@@ -390,12 +390,12 @@ var chrome;
chrome.types.pInt,
chrome.types.fun
];
-
+
chrome.bookmarks.getTree = function(callback) {
validate(arguments, arguments.callee.params);
sendRequest(GetBookmarkTree, null, callback);
};
-
+
// TODO(erikkay): allow it to take an optional id as a starting point
// BUG=13727
chrome.bookmarks.getTree.params = [
@@ -497,7 +497,7 @@ var chrome;
// Sends (id, {parentId, index, oldParentId, oldIndex})
chrome.bookmarks.onMoved = new chrome.Event("bookmark-moved");
-
+
// Sends (id, [childrenIds])
chrome.bookmarks.onChildrenReordered =
new chrome.Event("bookmark-children-reordered");
@@ -508,14 +508,14 @@ var chrome;
// Self.
chrome.self = chrome.self || {};
chrome.self.onConnect = new chrome.Event("channel-connect");
-
+
// Register
- chrome.self.register_ = function() {
+ chrome.onLoad_.addListener(function() {
var extensionId = RegisterExtension();
- window.addEventListener('unload', function() {
- UnregisterExtension(extensionId); }, false);
- delete chrome.self.register_;
- }
+ chrome.onUnload_.addListener(function() {
+ UnregisterExtension(extensionId);
+ });
+ });
chrome.self.getViews = function() {
return GetViews();
diff --git a/chrome/renderer/resources/greasemonkey_api.js b/chrome/renderer/resources/greasemonkey_api.js
index 3ce1d38..0ad24f0 100644
--- a/chrome/renderer/resources/greasemonkey_api.js
+++ b/chrome/renderer/resources/greasemonkey_api.js
@@ -1,5 +1,5 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
+// Copyright (c) 2009 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.
// -----------------------------------------------------------------------------
diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js
index 31b97ed..f6222d7 100644
--- a/chrome/renderer/resources/renderer_extension_bindings.js
+++ b/chrome/renderer/resources/renderer_extension_bindings.js
@@ -10,6 +10,7 @@
var chrome = chrome || {};
(function () {
native function OpenChannelToExtension(id);
+ native function CloseChannel(portId);
native function PostMessage(portId, msg);
// Port object. Represents a connection to another script context through
@@ -66,6 +67,12 @@ var chrome = chrome || {};
PostMessage(this.portId_, JSON.stringify(msg));
};
+ // Disconnects the port from the other end.
+ chrome.Port.prototype.disconnect = function() {
+ delete chrome.Port.ports_[this.portId_];
+ CloseChannel(this.portId_);
+ }
+
// Extension object.
chrome.Extension = function(id) {
this.id_ = id;
@@ -85,4 +92,10 @@ var chrome = chrome || {};
chrome.Extension.prototype.getURL = function(path) {
return "chrome-extension://" + this.id_ + "/" + path;
};
+
+ chrome.onUnload_.addListener(function() {
+ for (var i in chrome.Port.ports_) {
+ chrome.Port.ports_[i].disconnect();
+ }
+ });
})();