diff options
author | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-15 10:43:22 +0000 |
---|---|---|
committer | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-15 10:43:22 +0000 |
commit | 921777f2b0b5adeb766419ffbf6538d10c819912 (patch) | |
tree | c7b81951118217e9956a8d0d5744827336d46043 /chrome/renderer | |
parent | 21d90b88fd5b2a054e4825e89d104bfe1aadbdde (diff) | |
download | chromium_src-921777f2b0b5adeb766419ffbf6538d10c819912.zip chromium_src-921777f2b0b5adeb766419ffbf6538d10c819912.tar.gz chromium_src-921777f2b0b5adeb766419ffbf6538d10c819912.tar.bz2 |
Make sure ports are closed when they're no longer used.
It appears to be a common pattern that extensions don't invoke the
responseCallback, and so the port objects leak. Register a callback that
triggers when the responseCallback is finalized to free up these resources.
BUG=114738, 93566
TEST=manual
Review URL: http://codereview.chromium.org/9693048
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126884 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r-- | chrome/renderer/extensions/miscellaneous_bindings.cc | 31 | ||||
-rw-r--r-- | chrome/renderer/resources/extensions/miscellaneous_bindings.js | 14 |
2 files changed, 44 insertions, 1 deletions
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; |