| Index: extensions/renderer/resources/messaging.js
|
| diff --git a/extensions/renderer/resources/messaging.js b/extensions/renderer/resources/messaging.js
|
| index 8ca84ea46559aa91d4688c5a38b0e5f5a162ab27..38ad9447c302b23f607108ad3fa049d7e0236770 100644
|
| --- a/extensions/renderer/resources/messaging.js
|
| +++ b/extensions/renderer/resources/messaging.js
|
| @@ -21,6 +21,7 @@
|
| var kRequestChannel = "chrome.extension.sendRequest";
|
| var kMessageChannel = "chrome.runtime.sendMessage";
|
| var kNativeMessageChannel = "chrome.runtime.sendNativeMessage";
|
| + var kPortClosedError = 'Attempting to use a disconnected port object';
|
|
|
| // Map of port IDs to port object.
|
| var ports = {__proto__: null};
|
| @@ -53,13 +54,15 @@
|
| };
|
| this.onDisconnect = new Event(null, [portSchema], options);
|
| this.onMessage = new Event(null, [messageSchema, portSchema], options);
|
| - this.onDestroy_ = null;
|
| }
|
| $Object.setPrototypeOf(PortImpl.prototype, null);
|
|
|
| // Sends a message asynchronously to the context on the other end of this
|
| // port.
|
| PortImpl.prototype.postMessage = function(msg) {
|
| + if (!$Object.hasOwnProperty(ports, this.portId_))
|
| + throw new Error(kPortClosedError);
|
| +
|
| // JSON.stringify doesn't support a root object which is undefined.
|
| if (msg === undefined)
|
| msg = null;
|
| @@ -80,28 +83,24 @@
|
|
|
| // Disconnects the port from the other end.
|
| PortImpl.prototype.disconnect = function() {
|
| + if (!$Object.hasOwnProperty(ports, this.portId_))
|
| + return; // disconnect() on an already-closed port is a no-op.
|
| messagingNatives.CloseChannel(this.portId_, true);
|
| this.destroy_();
|
| };
|
|
|
| + // Close this specific port without forcing the channel to close. The channel
|
| + // will close if this was the only port at this end of the channel.
|
| + PortImpl.prototype.disconnectSoftly = function() {
|
| + if (!$Object.hasOwnProperty(ports, this.portId_))
|
| + return;
|
| + messagingNatives.CloseChannel(this.portId_, false);
|
| + this.destroy_();
|
| + };
|
| +
|
| PortImpl.prototype.destroy_ = function() {
|
| - if (this.onDestroy_) {
|
| - this.onDestroy_();
|
| - this.onDestroy_ = null;
|
| - }
|
| privates(this.onDisconnect).impl.destroy_();
|
| privates(this.onMessage).impl.destroy_();
|
| - // TODO(robwu): Remove port lifetime management because it is completely
|
| - // handled in the browser. The renderer's only roles are
|
| - // 1) rejecting ports so that the browser knows that the renderer is not
|
| - // interested in the port (this is merely an optimization)
|
| - // 2) acknowledging port creations, so that the browser knows that the port
|
| - // was successfully created (from the perspective of the extension), but
|
| - // then closed for some non-fatal reason.
|
| - // 3) notifying the browser of explicit port closure via .disconnect().
|
| - // In other cases (navigations), the browser automatically cleans up the
|
| - // port.
|
| - messagingNatives.PortRelease(this.portId_);
|
| delete ports[this.portId_];
|
| };
|
|
|
| @@ -109,7 +108,7 @@
|
| // the C++ to avoid creating the javascript message for all the contexts that
|
| // don't care about a particular message.
|
| function hasPort(portId) {
|
| - return portId in ports;
|
| + return $Object.hasOwnProperty(ports, portId);
|
| };
|
|
|
| // Hidden port creation function. We don't want to expose an API that lets
|
| @@ -119,7 +118,6 @@
|
| throw new Error("Port '" + portId + "' already exists.");
|
| var port = new Port(portId, opt_name);
|
| ports[portId] = port;
|
| - messagingNatives.PortAddRef(portId);
|
| return port;
|
| };
|
|
|
| @@ -177,7 +175,11 @@
|
| var responseCallback = function(response) {
|
| if (port) {
|
| port.postMessage(response);
|
| - privates(port).impl.destroy_();
|
| + // TODO(robwu): This can be changed to disconnect() because there is
|
| + // no point in allowing other receivers at this end of the port to
|
| + // keep the channel alive because the opener port can only receive one
|
| + // message.
|
| + privates(port).impl.disconnectSoftly();
|
| port = null;
|
| } else {
|
| // We nulled out port when sending the response, and now the page
|
| @@ -198,7 +200,7 @@
|
| // the context has been destroyed, so there isn't any JS state to clear.
|
| messagingNatives.BindToGC(responseCallback, function() {
|
| if (port) {
|
| - privates(port).impl.destroy_();
|
| + privates(port).impl.disconnectSoftly();
|
| port = null;
|
| }
|
| }, portId);
|
| @@ -209,15 +211,12 @@
|
| if (!responseCallbackPreserved && port) {
|
| // If they didn't access the response callback, they're not
|
| // going to send a response, so clean up the port immediately.
|
| - privates(port).impl.destroy_();
|
| + privates(port).impl.disconnectSoftly();
|
| port = null;
|
| }
|
| }
|
| }
|
|
|
| - privates(port).impl.onDestroy_ = function() {
|
| - port.onMessage.removeListener(messageListener);
|
| - };
|
| port.onMessage.addListener(messageListener);
|
|
|
| var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest";
|
| @@ -314,8 +313,7 @@
|
| function dispatchOnDisconnect(portId, errorMessage) {
|
| var port = ports[portId];
|
| if (port) {
|
| - // Update the renderer's port bookkeeping, without notifying the browser.
|
| - messagingNatives.CloseChannel(portId, false);
|
| + delete ports[portId];
|
| if (errorMessage)
|
| lastError.set('Port', errorMessage, null, chrome);
|
| try {
|
| @@ -398,10 +396,6 @@
|
| }
|
| }
|
|
|
| - privates(port).impl.onDestroy_ = function() {
|
| - port.onDisconnect.removeListener(disconnectListener);
|
| - port.onMessage.removeListener(messageListener);
|
| - };
|
| port.onDisconnect.addListener(disconnectListener);
|
| port.onMessage.addListener(messageListener);
|
| };
|
|
|