Bug 1287245 - Ensure globally unique internal port IDs r=billm
☠☠ backed out by a10527539339 ☠ ☠
authorRob Wu <rob@robwu.nl>
Fri, 15 Jul 2016 21:44:03 -0700
changeset 345567 7c026e26932d9e32d0a7c7fdef330917c52a9620
parent 345566 4425ad9e484976de55891ae77e17d7c1ac0b5ec2
child 345568 e8e2132fa883f916ed763f890c9cbdfddbf8cdf1
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1287245
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1287245 - Ensure globally unique internal port IDs r=billm MozReview-Commit-ID: FXNP5MadlMx
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/test/mochitest/mochitest.ini
toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_twoway.html
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -949,17 +949,17 @@ function promiseDocumentReady(doc) {
     }, true);
   });
 }
 
 /*
  * Messaging primitives.
  */
 
-var nextPortId = 1;
+let gNextPortId = 1;
 
 // Abstraction for a Port object in the extension API. Each port has a unique ID.
 function Port(context, messageManager, name, id, sender) {
   this.context = context;
   this.messageManager = messageManager;
   this.name = name;
   this.id = id;
   this.listenerName = `Extension:Port-${this.id}`;
@@ -1157,17 +1157,18 @@ Messenger.prototype = {
       MessageChannel.addListener(this.messageManagers, "Extension:Message", listener);
       return () => {
         MessageChannel.removeListener(this.messageManagers, "Extension:Message", listener);
       };
     }).api();
   },
 
   connect(messageManager, name, recipient) {
-    let portId = nextPortId++;
+    // 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);
     return port.api();
   },
 
   onConnect(name) {
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -55,16 +55,18 @@ 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_twoway.html]
+skip-if = (os == 'android' || buildapp == 'b2g') # port.sender.tab is undefined on b2g. Bug 1258975 on android.
 [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_sandbox_var.html]
 [test_ext_sendmessage_reply.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_twoway.html
@@ -0,0 +1,127 @@
+<!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_bidirectionally_and_postMessage() {
+  function backgroundScript() {
+    let onConnectCount = 0;
+    browser.runtime.onConnect.addListener(port => {
+      // 3. onConnect by connect() from CS.
+      browser.test.assertEq("from-cs", port.name);
+      browser.test.assertEq(1, ++onConnectCount,
+          "BG onConnect should be called once");
+
+      let tabId = port.sender.tab.id;
+      browser.test.assertTrue(tabId, "content script must have a tab ID");
+
+      let port2;
+      let postMessageCount1 = 0;
+      port.onMessage.addListener(msg => {
+        // 11. port.onMessage by port.postMessage in CS.
+        browser.test.assertEq("from CS to port", msg);
+        browser.test.assertEq(1, ++postMessageCount1,
+          "BG port.onMessage should be called once");
+
+        // 12. should trigger port2.onMessage in CS.
+        port2.postMessage("from BG to port2");
+      });
+
+      // 4. Should trigger onConnect in CS.
+      port2 = browser.tabs.connect(tabId, {name: "from-bg"});
+      let postMessageCount2 = 0;
+      port2.onMessage.addListener(msg => {
+        // 7. onMessage by port2.postMessage in CS.
+        browser.test.assertEq("from CS to port2", msg);
+        browser.test.assertEq(1, ++postMessageCount2,
+          "BG port2.onMessage should be called once");
+
+        // 8. Should trigger port.onMessage in CS.
+        port.postMessage("from BG to port");
+      });
+    });
+
+    // 1. Notify test runner to create a new tab.
+    browser.test.sendMessage("ready");
+  }
+
+  function contentScript() {
+    let onConnectCount = 0;
+    let port;
+    browser.runtime.onConnect.addListener(port2 => {
+      // 5. onConnect by connect() from BG.
+      browser.test.assertEq("from-bg", port2.name);
+      browser.test.assertEq(1, ++onConnectCount,
+        "CS onConnect should be called once");
+
+      let postMessageCount2 = 0;
+      port2.onMessage.addListener(msg => {
+        // 12. port2.onMessage by port2.postMessage in BG.
+        browser.test.assertEq("from BG to port2", msg);
+        browser.test.assertEq(1, ++postMessageCount2,
+          "CS port2.onMessage should be called once");
+
+        // TODO(robwu): Do not explicitly disconnect, it should not be a problem
+        // if we keep the ports open. However, not closing the ports causes the
+        // test to fail with NS_ERROR_NOT_INITIALIZED in ExtensionUtils.jsm, in
+        // Port.prototype.disconnect (nsIMessageSender.sendAsyncMessage).
+        port.disconnect();
+        port2.disconnect();
+        browser.test.notifyPass("ping pong done");
+      });
+      // 6. should trigger port2.onMessage in BG.
+      port2.postMessage("from CS to port2");
+    });
+
+    // 2. should trigger onConnect in BG.
+    port = browser.runtime.connect({name: "from-cs"});
+    let postMessageCount1 = 0;
+    port.onMessage.addListener(msg => {
+      // 9. onMessage by port.postMessage in BG.
+      browser.test.assertEq("from BG to port", msg);
+      browser.test.assertEq(1, ++postMessageCount1,
+        "CS port.onMessage should be called once");
+
+      // 10. should trigger port.onMessage in BG.
+      port.postMessage("from CS to port");
+    });
+  }
+
+  let extensionData = {
+    background: `(${backgroundScript})();`,
+    manifest: {
+      content_scripts: [{
+        js: ["contentscript.js"],
+        matches: ["http://mochi.test/*/file_sample.html"],
+      }],
+    },
+    files: {
+      "contentscript.js": `(${contentScript})();`,
+    },
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
+  yield extension.startup();
+  info("extension loaded");
+
+  yield extension.awaitMessage("ready");
+
+  let win = window.open("file_sample.html");
+  yield extension.awaitFinish("ping pong done");
+  win.close();
+
+  yield extension.unload();
+  info("extension unloaded");
+});
+</script>
+</body>