Bug 1310019 - web extension API : chrome.tabs.query doesn’t get tabs title at first call, r?kmag draft
authorBob Silverberg <bsilverberg@mozilla.com>
Mon, 31 Oct 2016 17:30:16 -0400
changeset 436617 aefa87ed04b976f74303e16076d81692aaa80e50
parent 436616 ae3b6bfb810476141647ad681b796ed118062412
child 536403 32a266b8f89394e0d0557561e92f3f55b5bc59c3
push id35151
push userbmo:bob.silverberg@gmail.com
push dateWed, 09 Nov 2016 13:14:58 +0000
reviewerskmag
bugs1310019
milestone51.0a2
Bug 1310019 - web extension API : chrome.tabs.query doesn’t get tabs title at first call, r?kmag Add permission to the active tab on mousedown in ext-browserAction.js MozReview-Commit-ID: H0oXQElppo7
browser/components/extensions/ext-browserAction.js
browser/components/extensions/ext-utils.js
browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -144,16 +144,23 @@ BrowserAction.prototype = {
         if (event.button == 0) {
           // Begin pre-loading the browser for the popup, so it's more likely to
           // be ready by the time we get a complete click.
           let tab = window.gBrowser.selectedTab;
           let popupURL = this.getProperty(tab, "popup");
           let enabled = this.getProperty(tab, "enabled");
 
           if (popupURL && enabled) {
+            // Add permission for the active tab so it will exist for the popup.
+            // Store the tab to revoke the permission during clearPopup.
+            if (!this.pendingPopup && !this.tabManager.hasActiveTabPermission(tab)) {
+              this.tabManager.addActiveTabPermission(tab);
+              this.tabToRevokeDuringClearPopup = tab;
+            }
+
             this.pendingPopup = this.getPopup(window, popupURL);
             window.addEventListener("mouseup", this, true);
           } else {
             this.clearPopup();
           }
         }
         break;
 
@@ -206,16 +213,20 @@ BrowserAction.prototype = {
   },
 
   /**
    * Clears any pending pre-loaded popup and related timeouts.
    */
   clearPopup() {
     this.clearPopupTimeout();
     if (this.pendingPopup) {
+      if (this.tabToRevokeDuringClearPopup) {
+        this.tabManager.revokeActiveTabPermission(this.tabToRevokeDuringClearPopup);
+        this.tabToRevokeDuringClearPopup = null;
+      }
       this.pendingPopup.destroy();
       this.pendingPopup = null;
     }
   },
 
   /**
    * Clears any pending timeouts to clear stale, pre-loaded popups.
    */
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -645,16 +645,20 @@ ExtensionTabManager.prototype = {
       // Note that, unlike Chrome, we don't currently clear this permission with
       // the tab navigates. If the inner window is revived from BFCache before
       // we've granted this permission to a new inner window, the extension
       // maintains its permissions for it.
       this.hasTabPermissionFor.set(tab, tab.linkedBrowser.innerWindowID);
     }
   },
 
+  revokeActiveTabPermission(tab = TabManager.activeTab) {
+    this.hasTabPermissionFor.delete(tab);
+  },
+
   // Returns true if the extension has the "activeTab" permission for this tab.
   // This is somewhat more permissive than the generic "tabs" permission, as
   // checked by |hasTabPermission|, in that it also allows programmatic script
   // injection without an explicit host permission.
   hasActiveTabPermission(tab) {
     // This check is redundant with addTabPermission, but cheap.
     if (this.extension.hasPermission("activeTab")) {
       return (this.hasTabPermissionFor.has(tab) &&
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
@@ -1,15 +1,15 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head><body>${url}</body></html>`;
+
 function* testInArea(area) {
-  let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head><body>${url}</body></html>`;
-
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "background": {
         "page": "data/background.html",
       },
       "browser_action": {
         "default_popup": "popup-a.html",
         "browser_style": true,
@@ -196,45 +196,53 @@ add_task(function* testBrowserActionInPa
 
 add_task(function* testBrowserActionClickCanceled() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {
         "default_popup": "popup.html",
         "browser_style": true,
       },
+      "permissions": ["activeTab"],
     },
 
     files: {
       "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8"></head></html>`,
     },
   });
 
   yield extension.startup();
 
   const {GlobalManager, Management: {global: {browserActionFor}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
 
   let ext = GlobalManager.extensionMap.get(extension.id);
   let browserAction = browserActionFor(ext);
 
   let widget = getBrowserActionWidget(extension).forWindow(window);
+  let tab = window.gBrowser.selectedTab;
 
   // Test canceled click.
   EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mousedown", button: 0}, window);
 
   isnot(browserAction.pendingPopup, null, "Have pending popup");
   is(browserAction.pendingPopup.window, window, "Have pending popup for the correct window");
 
   is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout");
 
+  is(browserAction.tabToRevokeDuringClearPopup, tab, "Tab to revoke was saved");
+  is(browserAction.tabManager.hasActiveTabPermission(tab), true, "Active tab was granted permission");
+
   EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mouseup", button: 0}, window);
 
   is(browserAction.pendingPopup, null, "Pending popup was cleared");
   is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout");
 
+  is(browserAction.tabToRevokeDuringClearPopup, null, "Tab to revoke was removed");
+  is(browserAction.tabManager.hasActiveTabPermission(tab), false, "Permission was revoked from tab");
+
   // Test completed click.
   EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mousedown", button: 0}, window);
 
   isnot(browserAction.pendingPopup, null, "Have pending popup");
   is(browserAction.pendingPopup.window, window, "Have pending popup for the correct window");
 
   is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout");
 
@@ -325,8 +333,49 @@ add_task(function* testBrowserActionDisa
   is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout");
 
   // Give the popup a chance to load and trigger a failure, if it was
   // erroneously opened.
   yield new Promise(resolve => setTimeout(resolve, 250));
 
   yield extension.unload();
 });
+
+add_task(function* testBrowserActionTabPopulation() {
+  // Note: This test relates to https://bugzilla.mozilla.org/show_bug.cgi?id=1310019
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "browser_action": {
+        "default_popup": "popup.html",
+        "browser_style": true,
+      },
+      "permissions": ["activeTab"],
+    },
+
+    files: {
+      "popup.html": scriptPage("popup.js"),
+      "popup.js": function() {
+        browser.tabs.query({active: true, currentWindow: true}).then(tabs => {
+          browser.test.assertEq("mochitest index /",
+                                tabs[0].title,
+                                "Tab has the expected title on first click");
+          browser.test.sendMessage("tabTitle");
+        });
+      },
+    },
+  });
+
+  let win = yield BrowserTestUtils.openNewBrowserWindow();
+  yield BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "http://example.com/");
+  yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
+
+  yield extension.startup();
+
+  let widget = getBrowserActionWidget(extension).forWindow(win);
+  EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mousedown", button: 0}, win);
+
+  yield extension.awaitMessage("tabTitle");
+
+  EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mouseup", button: 0}, win);
+
+  yield extension.unload();
+  yield BrowserTestUtils.closeWindow(win);
+});