diff options
4 files changed, 48 insertions, 13 deletions
diff --git a/chrome/common/extensions/docs/messaging.html b/chrome/common/extensions/docs/messaging.html index 951f969b..0123757 100644 --- a/chrome/common/extensions/docs/messaging.html +++ b/chrome/common/extensions/docs/messaging.html @@ -273,9 +273,7 @@ chrome.tabs.getSelected(null, function(tab) { On the receiving end, you need to set up an <a href="extension.html#event-onRequest">chrome.extension.onRequest</a> event listener to handle the message. This looks the same from a content -script or extension page. The request will remain open until you call -sendResponse, so it is good practice to call sendResponse with an empty -object to allow the request to be cleaned up. +script or extension page. </p><pre>chrome.extension.onRequest.addListener( function(request, sender, sendResponse) { console.log(sender.tab ? @@ -283,8 +281,6 @@ object to allow the request to be cleaned up. "from the extension"); if (request.greeting == "hello") sendResponse({farewell: "goodbye"}); - else - sendResponse({}); // snub them. }); </pre> <p class="note"> @@ -379,7 +375,7 @@ methods. Here's an example of each: chrome.extension.onRequestExternal.addListener( function(request, sender, sendResponse) { if (sender.id == blacklistedExtension) - sendResponse({}); // don't allow this extension access + return; // don't allow this extension access else if (request.getTargetData) sendResponse({targetData: targetData}); else if (request.activateLasers) { diff --git a/chrome/common/extensions/docs/static/messaging.html b/chrome/common/extensions/docs/static/messaging.html index 1391e7e..c4774a5 100644 --- a/chrome/common/extensions/docs/static/messaging.html +++ b/chrome/common/extensions/docs/static/messaging.html @@ -63,9 +63,7 @@ chrome.tabs.getSelected(null, function(tab) { On the receiving end, you need to set up an <a href="extension.html#event-onRequest">chrome.extension.onRequest</a> event listener to handle the message. This looks the same from a content -script or extension page. The request will remain open until you call -sendResponse, so it is good practice to call sendResponse with an empty -object to allow the request to be cleaned up. +script or extension page. <pre> chrome.extension.onRequest.addListener( function(request, sender, sendResponse) { @@ -74,8 +72,6 @@ chrome.extension.onRequest.addListener( "from the extension"); if (request.greeting == "hello") sendResponse({farewell: "goodbye"}); - else - sendResponse({}); // snub them. }); </pre> @@ -185,7 +181,7 @@ methods. Here's an example of each: chrome.extension.onRequestExternal.addListener( function(request, sender, sendResponse) { if (sender.id == blacklistedExtension) - sendResponse({}); // don't allow this extension access + return; // don't allow this extension access else if (request.getTargetData) sendResponse({targetData: targetData}); else if (request.activateLasers) { diff --git a/chrome/renderer/extensions/miscellaneous_bindings.cc b/chrome/renderer/extensions/miscellaneous_bindings.cc index 31a8923..8a5f758 100644 --- a/chrome/renderer/extensions/miscellaneous_bindings.cc +++ b/chrome/renderer/extensions/miscellaneous_bindings.cc @@ -84,6 +84,8 @@ class ExtensionImpl : public ChromeV8Extension { return v8::FunctionTemplate::New(PortAddRef); } else if (name->Equals(v8::String::New("PortRelease"))) { return v8::FunctionTemplate::New(PortRelease); + } else if (name->Equals(v8::String::New("BindToGC"))) { + return v8::FunctionTemplate::New(BindToGC); } return ChromeV8Extension::GetNativeFunction(name); } @@ -149,6 +151,35 @@ class ExtensionImpl : public ChromeV8Extension { } return v8::Undefined(); } + + struct GCCallbackArgs { + v8::Persistent<v8::Object> object; + v8::Persistent<v8::Function> callback; + }; + + static void GCCallback(v8::Persistent<v8::Value> object, void* parameter) { + v8::HandleScope handle_scope; + GCCallbackArgs* args = reinterpret_cast<GCCallbackArgs*>(parameter); + args->callback->Call(args->callback->CreationContext()->Global(), 0, NULL); + args->callback.Dispose(); + args->object.Dispose(); + delete args; + } + + // Binds a callback to be invoked when the given object is garbage collected. + static v8::Handle<v8::Value> BindToGC(const v8::Arguments& args) { + if (args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()) { + GCCallbackArgs* context = new GCCallbackArgs; + context->callback = v8::Persistent<v8::Function>::New( + v8::Handle<v8::Function>::Cast(args[1])); + context->object = v8::Persistent<v8::Object>::New( + v8::Handle<v8::Object>::Cast(args[0])); + context->object.MakeWeak(context, GCCallback); + } else { + NOTREACHED(); + } + return v8::Undefined(); + } }; } // namespace diff --git a/chrome/renderer/resources/extensions/miscellaneous_bindings.js b/chrome/renderer/resources/extensions/miscellaneous_bindings.js index 6bcfc11..0895e1d 100644 --- a/chrome/renderer/resources/extensions/miscellaneous_bindings.js +++ b/chrome/renderer/resources/extensions/miscellaneous_bindings.js @@ -15,6 +15,7 @@ var chrome = chrome || {}; native function PostMessage(portId, msg); native function GetChromeHidden(); native function Print(); + native function BindToGC(); var chromeHidden = GetChromeHidden(); var manifestVersion; @@ -123,7 +124,7 @@ var chrome = chrome || {}; if (requestEvent.hasListeners()) { var port = chromeHidden.Port.createPort(portId, channelName); port.onMessage.addListener(function(request) { - requestEvent.dispatch(request, sender, function(response) { + var responseCallback = function(response) { if (port) { port.postMessage(response); port = null; @@ -141,7 +142,18 @@ var chrome = chrome || {}; chrome.extension.lastError = {"message": errorMsg}; console.error("Could not send response: " + errorMsg); } + }; + // In case the extension never invokes the responseCallback, and also + // doesn't keep a reference to it, we need to clean up the port. Do + // so by attaching to the garbage collection of the responseCallback + // using some native hackery. + BindToGC(responseCallback, function() { + if (port) { + port.disconnect(); + port = null; + } }); + requestEvent.dispatch(request, sender, responseCallback); }); } return; |