Bug 1377734 - Avoid instantiating a lazy tab from sendMessage, r?mixedpuppy draft
authorTomislav Jovanovic <tomica@gmail.com>
Fri, 11 Aug 2017 02:16:33 +0200
changeset 645143 27c4b75a5603a42b079190611fdb14ba1a8f8a28
parent 643173 4c5fbf49376351679dcc49f4cff26c3c2e055ccc
child 725836 2e0b6f92b98cd5d3c401f5158705dd41c8b666a8
push id73681
push userbmo:tomica@gmail.com
push dateFri, 11 Aug 2017 22:22:08 +0000
reviewersmixedpuppy
bugs1377734
milestone57.0a1
Bug 1377734 - Avoid instantiating a lazy tab from sendMessage, r?mixedpuppy MozReview-Commit-ID: KfYg6hG8KmC
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
toolkit/components/extensions/ExtensionParent.jsm
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -123,16 +123,17 @@ skip-if = debug || asan # Bug 1354681
 [browser_ext_tabs_executeScript_good.js]
 [browser_ext_tabs_executeScript_bad.js]
 [browser_ext_tabs_executeScript_multiple.js]
 [browser_ext_tabs_executeScript_no_create.js]
 [browser_ext_tabs_executeScript_runAt.js]
 [browser_ext_tabs_getCurrent.js]
 [browser_ext_tabs_insertCSS.js]
 [browser_ext_tabs_lastAccessed.js]
+[browser_ext_tabs_lazy.js]
 [browser_ext_tabs_removeCSS.js]
 [browser_ext_tabs_move_array.js]
 [browser_ext_tabs_move_window.js]
 [browser_ext_tabs_move_window_multiple.js]
 [browser_ext_tabs_move_window_pinned.js]
 [browser_ext_tabs_onHighlighted.js]
 [browser_ext_tabs_onUpdated.js]
 [browser_ext_tabs_printPreview.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
@@ -0,0 +1,40 @@
+"use strict";
+
+const SESSION = {
+  windows: [{
+    tabs: [
+      {entries: [{url: "about:blank"}]},
+      {entries: [{url: "https://example.com/"}]},
+    ],
+  }],
+};
+
+add_task(async function() {
+  SessionStore.setBrowserState(JSON.stringify(SESSION));
+  const tab = gBrowser.tabs[1];
+
+  is(tab.getAttribute("pending"), "true", "The tab is pending restore");
+  is(tab.linkedBrowser.isConnected, false, "The tab is lazy");
+
+  async function background() {
+    const [tab] = await browser.tabs.query({url: "https://example.com/"});
+    browser.test.assertRejects(
+      browser.tabs.sendMessage(tab.id, "void"),
+      /Could not establish connection. Receiving end does not exist/,
+      "No recievers in a tab pending restore."
+    );
+    browser.test.notifyPass("lazy");
+  }
+
+  const manifest = {permissions: ["tabs"]};
+  const extension = ExtensionTestUtils.loadExtension({manifest, background});
+
+  await extension.startup();
+  await extension.awaitFinish("lazy");
+  await extension.unload();
+
+  is(tab.getAttribute("pending"), "true", "The tab is still pending restore");
+  is(tab.linkedBrowser.isConnected, false, "The tab is still lazy");
+
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -273,25 +273,32 @@ ProxyMessenger = {
 
   /**
    * @param {object} recipient An object that was passed to
    *     `MessageChannel.sendMessage`.
    * @param {Extension} extension
    * @returns {object|null} The message manager matching the recipient if found.
    */
   getMessageManagerForRecipient(recipient) {
-    let {tabId} = recipient;
     // tabs.sendMessage / tabs.connect
-    if (tabId) {
+    if ("tabId" in recipient) {
       // `tabId` being set implies that the tabs API is supported, so we don't
       // need to check whether `tabTracker` exists.
-      let tab = apiManager.global.tabTracker.getTab(tabId, null);
+      let tab = apiManager.global.tabTracker.getTab(recipient.tabId, null);
       if (!tab) {
         return null;
       }
+
+      // There can be no recipients in a tab pending restore,
+      // So we bail early to avoid instantiating the lazy browser.
+      let node = tab.browser || tab;
+      if (node.getAttribute("pending") === "true") {
+        return null;
+      }
+
       let browser = tab.linkedBrowser || tab.browser;
 
       // Options panels in the add-on manager currently require
       // special-casing, since their message managers aren't currently
       // connected to the tab's top-level message manager. To deal with
       // this, we find the options <browser> for the tab, and use that
       // directly, insteead.
       if (browser.currentURI.cloneIgnoringRef().spec === "about:addons") {