Bug 286746 - Invoke port.onDisconnect if there are no onConnect listeners draft
authorRob Wu <rob@robwu.nl>
Fri, 15 Jul 2016 17:46:00 -0700
changeset 388535 f6937a105f5ce1759ab892c2dd15576f4a386ab1
parent 388534 c60b97b335486e047178ac0f1964e0ba11c61be8
child 525571 63370503adcf97eecb64eff71c9b3b9e7e04ce09
push id23199
push userbmo:rob@robwu.nl
push dateSat, 16 Jul 2016 05:01:30 +0000
bugs286746
milestone50.0a1
Bug 286746 - Invoke port.onDisconnect if there are no onConnect listeners MozReview-Commit-ID: DPs36oFm25J
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/test/mochitest/mochitest.ini
toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_no_receiver.html
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -1027,26 +1027,30 @@ Port.prototype = {
   handleDisconnection() {
     this.messageManager.removeMessageListener(this.disconnectName, this);
     this.context.forgetOnClose(this);
     this.disconnected = true;
   },
 
   receiveMessage(msg) {
     if (msg.name == this.disconnectName) {
-      if (this.disconnected) {
-        return;
-      }
+      this.disconnectByOtherEnd();
+    }
+  },
 
-      for (let listener of this.disconnectListeners) {
-        listener();
-      }
+  disconnectByOtherEnd() {
+    if (this.disconnected) {
+      return;
+    }
 
-      this.handleDisconnection();
+    for (let listener of this.disconnectListeners) {
+      listener();
     }
+
+    this.handleDisconnection();
   },
 
   disconnect() {
     if (this.disconnected) {
       throw new this.context.contentWindow.Error("Attempt to disconnect() a disconnected port");
     }
     this.handleDisconnection();
     this.messageManager.sendAsyncMessage(this.disconnectName);
@@ -1156,18 +1160,22 @@ Messenger.prototype = {
       };
     }).api();
   },
 
   connect(messageManager, name, recipient) {
     let portId = nextPortId++;
     let port = new Port(this.context, messageManager, name, portId, null);
     let msg = {name, portId};
-    // TODO: Disconnect the port if no response?
-    this._sendMessage(messageManager, "Extension:Connect", msg, recipient);
+    this._sendMessage(messageManager, "Extension:Connect", msg, recipient)
+      .catch(e => {
+        if (e.result == MessageChannel.RESULT_NO_HANDLER) {
+          port.disconnectByOtherEnd();
+        }
+      });
     return port.api();
   },
 
   onConnect(name) {
     return new SingletonEventManager(this.context, name, callback => {
       let listener = {
         messageFilterPermissive: this.filter,
 
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -55,16 +55,17 @@ skip-if = buildapp == 'b2g' # runat != d
 [test_ext_idle.html]
 [test_ext_localStorage.html]
 [test_ext_onmessage_removelistener.html]
 [test_ext_notifications.html]
 [test_ext_permission_xhr.html]
 skip-if = buildapp == 'b2g' # JavaScript error: jar:remoteopenfile:///data/local/tmp/generated-extension.xpi!/content.js, line 46: NS_ERROR_ILLEGAL_VALUE:
 [test_ext_runtime_connect.html]
 skip-if = (os == 'android' || buildapp == 'b2g') # port.sender.tab is undefined on b2g. Bug 1258975 on android.
+[test_ext_runtime_connect_no_receiver.html]
 [test_ext_runtime_connect2.html]
 skip-if = (os == 'android' || buildapp == 'b2g') # port.sender.tab is undefined on b2g. Bug 1258975 on android.
 [test_ext_runtime_disconnect.html]
 [test_ext_runtime_getPlatformInfo.html]
 [test_ext_runtime_id.html]
 [test_ext_runtime_sendMessage.html]
 [test_ext_runtime_sendMessage_no_receiver.html]
 [test_ext_sandbox_var.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_no_receiver.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>WebExtension test</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+
+add_task(function* test_connect_without_listener() {
+  function backgroundScript() {
+    let port = browser.runtime.connect();
+    port.onDisconnect.addListener(() => {
+      browser.test.notifyPass("port.onDisconnect was called");
+    });
+  }
+  let extensionData = {
+    background: `(${backgroundScript})();`,
+    manifest: {},
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
+  yield extension.startup();
+  info("extension loaded");
+
+  yield extension.awaitFinish("port.onDisconnect was called");
+
+  yield extension.unload();
+  info("extension unloaded");
+});
+</script>
+</body>