diff options
author | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-18 22:56:58 +0000 |
---|---|---|
committer | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-18 22:56:58 +0000 |
commit | 1f70f0ca51d1c61d3a775507b2b69dcdf60e77df (patch) | |
tree | 41659daa24b3e9af8996b2782dffead89fdcc63c /chrome | |
parent | 11de3e98153ad8dcb9e6628e527f7bfb2ca0a8ed (diff) | |
download | chromium_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')
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(); + } + }); })(); |