Bug 1286746 - Invoke port.onDisconnect if there are no onConnect listeners draft
authorRob Wu <rob@robwu.nl>
Fri, 15 Jul 2016 17:46:00 -0700
changeset 394574 79d05c46398cfbc9d732a267cfad2d64892bfc14
parent 394130 5224dd4c03792d416ad664c23248ee6c75523896
child 394575 bc4acc00e74e92b11081ed4c648556859e0c0306
push id24608
push userbmo:rob@robwu.nl
push dateSat, 30 Jul 2016 00:59:55 +0000
bugs1286746
milestone50.0a1
Bug 1286746 - Invoke port.onDisconnect if there are no onConnect listeners MozReview-Commit-ID: DPs36oFm25J
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/test/xpcshell/test_ext_runtime_connect_no_receiver.js
toolkit/components/extensions/test/xpcshell/xpcshell.ini
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -1141,26 +1141,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) {
       // disconnect() may be called without side effects even after the port is
       // closed - https://developer.chrome.com/extensions/runtime#type-Port
       return;
     }
@@ -1275,18 +1279,18 @@ Messenger.prototype = {
     }).api();
   },
 
   connect(messageManager, name, recipient) {
     // TODO(robwu): Use a process ID instead of the process type. bugzil.la/1287626
     let portId = `${gNextPortId++}-${Services.appinfo.processType}`;
     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 => port.disconnectByOtherEnd());
     return port.api();
   },
 
   onConnect(name) {
     return new SingletonEventManager(this.context, name, callback => {
       let listener = {
         messageFilterPermissive: this.filter,
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_connect_no_receiver.js
@@ -0,0 +1,22 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* test_connect_without_listener() {
+  function background() {
+    let port = browser.runtime.connect();
+    port.onDisconnect.addListener(() => {
+      browser.test.notifyPass("port.onDisconnect was called");
+    });
+  }
+  let extensionData = {
+    background,
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
+  yield extension.startup();
+
+  yield extension.awaitFinish("port.onDisconnect was called");
+
+  yield extension.unload();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -28,16 +28,17 @@ skip-if = os == "android"
 skip-if = os == "android"
 [test_ext_extension.js]
 [test_ext_idle.js]
 [test_ext_json_parser.js]
 [test_ext_localStorage.js]
 [test_ext_manifest_content_security_policy.js]
 [test_ext_manifest_incognito.js]
 [test_ext_onmessage_removelistener.js]
+[test_ext_runtime_connect_no_receiver.js]
 [test_ext_runtime_getPlatformInfo.js]
 [test_ext_runtime_sendMessage.js]
 [test_ext_schemas.js]
 [test_ext_simple.js]
 [test_ext_storage.js]
 [test_getAPILevelForWindow.js]
 [test_locale_converter.js]
 [test_locale_data.js]