Part 2: Implement {set|get}Popup for chrome.pageAction (Bug 1264118) r=kmag
authorMatthew Wein <mwein@mozilla.com>
Mon, 30 May 2016 19:42:03 -0700
changeset 301183 a80121b022f54351a0a8772a95058ba30c29e1ba
parent 301182 43a83633a789f6078f4807c09b657ccd83ef9cf4
child 301184 5753eb3ec41884030a12d9df006ee8fced481ca5
push id78241
push userkwierso@gmail.com
push dateThu, 09 Jun 2016 00:09:10 +0000
treeherdermozilla-inbound@2c66b75bbb7f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1264118
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
Part 2: Implement {set|get}Popup for chrome.pageAction (Bug 1264118) r=kmag MozReview-Commit-ID: 1ZyQgs8ktic
mobile/android/components/extensions/ext-pageAction.js
mobile/android/components/extensions/schemas/page_action.json
mobile/android/components/extensions/test/mochitest/chrome.ini
mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html
--- a/mobile/android/components/extensions/ext-pageAction.js
+++ b/mobile/android/components/extensions/ext-pageAction.js
@@ -21,26 +21,28 @@ var {
 // WeakMap[Extension -> PageAction]
 var pageActionMap = new WeakMap();
 
 function PageAction(options, extension) {
   this.id = null;
 
   let DEFAULT_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAC4klEQVRYhdWXLWzbQBSADQtDAwsHC1tUhUxqfL67lk2tdn+OJg0ODU0rLByqgqINBY6tmlbn7LMTJ5FaFVVBk1G0oUGjG2jT2Y7jxmmcbU/6iJ+f36fz+e5sGP9riCGm9hB37RG+scd4Yo/wsDXCZyIE2xuXsce4bY+wXkAsQtzYmExrfFgvkJkRbkzo1ehoxx5iXcgI/9iYUGt8WH9MqDXEcmNChmEYrRCf2SHWeYgQx3x0tLNRIeKQLTtEFyJEep4NTuhk8BC+yMrwEE3+iozo42d8gK7FAOkMsRiiN8QhW2ttSK5QTfRRV4QoymVeJMvPvDp7gCZigD613MN6yRFA3SWarow9QB9LCfG+NeF9qCtjAKOSQjCqVKhfVsiHEQ+grgx/lRGqUihAc1uL8EFD+KCRO+GrF4J61phcoRoPoEzkYhZYpykh5sMb7kOdIeY+jHKur4QI4Feh4AFX1nVeLxrAvQchGsBz5ls6wa2QdwcvIcE2863bTH79KOvsz/uUYJsp+J0pSzNlDckVqqVGUAF+n6uS7txcOl6wot4JVy70ufDLy4pWLUQVPE81pRI0mGe9oxLMHSeohHvMs/STUNaUK6vDPCvOyxMFDx4achehRDJmHnydnkPww5OFfLxrGIZBFDyYl4LpMzlTQFIP6AQx86w2UeYBccFpJrcKv5L9eGDtUAU6RIELqsB74uynjy/UBRF1gS5BTFxwQT1wTiXoUg9MH7m/3NZRRoi5IJytUbMgzv4Wc832+oQkiKgEehmyMkkpKsFkQV11QsRJL5rJYBLItQgRaUZEmnoZXsomz3vGiWw+I9KMF9SVFOqZEemZekli1jN3U/UOqhHHvC6oWWGElhfSpGdOk6+O9prdwvtLj5BjRsQxdRnot+Zeifpy/2/0stktKTRNLmbk0mwXyl8253fyojj+8rxOHNAhjjm5n0/5OOCGOKBzkrMO0Z75lvSAzKlrF32Z/3z8BqLAn+yMV7VhAAAAAElFTkSuQmCC";
 
+  this.popupUrl = options.default_popup;
+
   this.options = {
     title: options.default_title || extension.name,
     icon: DEFAULT_ICON,
     id: extension.id,
     clickCallback: () => {
-      if (this.default_popup) {
+      if (this.popupUrl) {
         let win = Services.wm.getMostRecentWindow("navigator:browser");
-        win.BrowserApp.addTab(this.default_popup, {
+        win.BrowserApp.addTab(this.popupUrl, {
           selected: true,
-          parentId: win.BrowserApp.selectedTab.id
+          parentId: win.BrowserApp.selectedTab.id,
         });
       } else {
         this.emit("click");
       }
     },
   };
 
   EventEmitter.decorate(this);
@@ -56,16 +58,26 @@ PageAction.prototype = {
 
   hide(tabId) {
     if (this.id) {
       PageActions.remove(this.id);
       this.id = null;
     }
   },
 
+  setPopup(tab, url) {
+    // TODO: Only set the popup for the specified tab once we have Tabs API support.
+    this.popupUrl = url;
+  },
+
+  getPopup(tab) {
+    // TODO: Only return the popup for the specified tab once we have Tabs API support.
+    return this.popupUrl;
+  },
+
   shutdown() {
     this.hide();
   },
 };
 
 /* eslint-disable mozilla/balanced-listeners */
 extensions.on("manifest_page_action", (type, directive, extension, manifest) => {
   let pageAction = new PageAction(manifest.page_action, extension);
@@ -95,11 +107,25 @@ extensions.registerSchemaAPI("pageAction
 
       show(tabId) {
         pageActionMap.get(extension).show(tabId);
       },
 
       hide(tabId) {
         pageActionMap.get(extension).hide(tabId);
       },
+
+      setPopup(details) {
+        // TODO: Use the Tabs API to get the tab from details.tabId.
+        let tab = null;
+        let url = details.popup && context.uri.resolve(details.popup);
+        pageActionMap.get(extension).setPopup(tab, url);
+      },
+
+      getPopup(details) {
+        // TODO: Use the Tabs API to get the tab from details.tabId.
+        let tab = null;
+        let popup = pageActionMap.get(extension).getPopup(tab);
+        return Promise.resolve(popup);
+      },
     },
   };
 });
--- a/mobile/android/components/extensions/schemas/page_action.json
+++ b/mobile/android/components/extensions/schemas/page_action.json
@@ -155,17 +155,16 @@
             "name": "callback",
             "optional": true,
             "parameters": []
           }
         ]
       },
       {
         "name": "setPopup",
-        "unsupported": true,
         "type": "function",
         "description": "Sets the html document to be opened as a popup when the user clicks on the page action's icon.",
         "parameters": [
           {
             "name": "details",
             "type": "object",
             "properties": {
               "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
@@ -174,17 +173,16 @@
                 "description": "The html file to show in a popup.  If set to the empty string (''), no popup is shown."
               }
             }
           }
         ]
       },
       {
         "name": "getPopup",
-        "unsupported": true,
         "type": "function",
         "description": "Gets the html document set as the popup for this page action.",
         "async": "callback",
         "parameters": [
           {
             "name": "details",
             "type": "object",
             "properties": {
--- a/mobile/android/components/extensions/test/mochitest/chrome.ini
+++ b/mobile/android/components/extensions/test/mochitest/chrome.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
 support-files =
   head.js
 
 [test_ext_pageAction.html]
-[test_ext_pageAction_defaultPopup.html]
\ No newline at end of file
+[test_ext_pageAction_popup.html]
\ No newline at end of file
--- a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html
+++ b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html
@@ -14,87 +14,140 @@
 "use strict";
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 add_task(function* test_contentscript() {
   function backgroundScript() {
     // TODO: Use the Tabs API to obtain the tab ids for showing pageActions.
     let tabId = 1;
-    browser.test.onMessage.addListener(msg => {
+    let onClickedListenerEnabled = false;
+
+    browser.test.onMessage.addListener((msg, details) => {
       if (msg === "page-action-show") {
         // TODO: switch to using .show(tabId).then(...) once bug 1270742 lands.
         browser.pageAction.show(tabId);
         browser.test.sendMessage("page-action-shown");
-      } else if (msg == "page-action-close-popup") {
-        browser.runtime.sendMessage("close-popup");
+      } else if (msg == "page-action-set-popup") {
+        browser.pageAction.setPopup({popup: details.name, tabId: tabId});
+        browser.test.sendMessage("page-action-popup-set");
+      } else if (msg == "page-action-get-popup") {
+        browser.pageAction.getPopup({tabId: tabId}).then(url => {
+          browser.test.sendMessage("page-action-got-popup", url);
+        });
+      } else if (msg == "page-action-enable-onClicked-listener") {
+        onClickedListenerEnabled = true;
+        browser.test.sendMessage("page-action-onClicked-listener-enabled");
+      } else if (msg == "page-action-disable-onClicked-listener") {
+        onClickedListenerEnabled = false;
+        browser.test.sendMessage("page-action-onClicked-listener-disabled");
       }
     });
 
     browser.pageAction.onClicked.addListener(tab => {
-      browser.test.fail(`The onClicked listener should never fire when a popup is shown.`);
+      browser.test.assertTrue(onClickedListenerEnabled, "The onClicked listener should only fire when it is enabled.");
+      browser.test.sendMessage("page-action-onClicked-fired");
     });
 
     browser.test.sendMessage("ready");
   }
 
   function popupScript() {
     window.onload = () => {
-      browser.test.sendMessage("from-page-action-popup-shown");
+      browser.test.sendMessage("page-action-from-popup", location.href);
     };
-    browser.runtime.onMessage.addListener(msg => {
-      if (msg == "close-popup") {
-        window.close();
+    browser.test.onMessage.addListener((msg, details) => {
+      if (msg == "page-action-close-popup") {
+        if (details.location == location.href) {
+          window.close();
+        }
       }
     });
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     background: `(${backgroundScript}())`,
     manifest: {
       "name": "PageAction Extension",
       "page_action": {
         "default_title": "Page Action",
-        "default_popup": "popup.html",
+        "default_popup": "default.html",
       },
     },
     files: {
-      "popup.html": `<html><head><meta charset="utf-8"><script src="popup.js"></${"script"}></head></html>`,
+      "default.html": `<html><head><meta charset="utf-8"><script src="popup.js"></${"script"}></head></html>`,
+      "a.html": `<html><head><meta charset="utf-8"><script src="popup.js"></${"script"}></head></html>`,
+      "b.html": `<html><head><meta charset="utf-8"><script src="popup.js"></${"script"}></head></html>`,
       "popup.js": `(${popupScript})()`,
     },
   });
 
-  let tabClosedPromise = new Promise(resolve => {
-    let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
-    let BrowserApp = chromeWin.BrowserApp;
+  let tabClosedPromise = () => {
+    return new Promise(resolve => {
+      let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+      let BrowserApp = chromeWin.BrowserApp;
 
-    let tabCloseListener = (event) => {
-      BrowserApp.deck.removeEventListener("TabClose", tabCloseListener, false);
-      let browser = event.target;
-      let url = browser.currentURI.spec
-      resolve(url);
+      let tabCloseListener = (event) => {
+        BrowserApp.deck.removeEventListener("TabClose", tabCloseListener, false);
+        let browser = event.target;
+        let url = browser.currentURI.spec;
+        resolve(url);
+      };
+
+      BrowserApp.deck.addEventListener("TabClose", tabCloseListener, false);
+    });
+  };
+
+  function* testPopup(name) {
+    // We don't need to set the popup when testing default_popup.
+    if (name != "default.html") {
+      extension.sendMessage("page-action-set-popup", {name});
+      yield extension.awaitMessage("page-action-popup-set");
     }
 
-    BrowserApp.deck.addEventListener("TabClose", tabCloseListener, false);
-  });
+    extension.sendMessage("page-action-get-popup");
+    let url = yield extension.awaitMessage("page-action-got-popup");
+
+    if (name == "") {
+      ok(url == name, "Calling pageAction.getPopup should return an empty string when the popup is not set.");
+
+      // The onClicked listener should get called when the popup is set to an empty string.
+      extension.sendMessage("page-action-enable-onClicked-listener");
+      yield extension.awaitMessage("page-action-onClicked-listener-enabled");
+
+      clickPageAction(extension.id);
+      yield extension.awaitMessage("page-action-onClicked-fired");
+
+      extension.sendMessage("page-action-disable-onClicked-listener");
+      yield extension.awaitMessage("page-action-onClicked-listener-disabled");
+    } else {
+      ok(url.includes(name), "Calling pageAction.getPopup should return the correct popup URL when the popup is set.");
+
+      clickPageAction(extension.id);
+      let location = yield extension.awaitMessage("page-action-from-popup");
+      ok(location.includes(name), "The popup with the correct URL should be shown.");
+
+      extension.sendMessage("page-action-close-popup", {location});
+
+      url = yield tabClosedPromise();
+      ok(url.includes(name), "The tab for the popup should be closed.");
+    }
+  }
 
   yield extension.startup();
   yield extension.awaitMessage("ready");
 
   extension.sendMessage("page-action-show");
   yield extension.awaitMessage("page-action-shown");
-  ok(isPageActionShown(extension.id), "The PageAction should be shown");
-
-  clickPageAction(extension.id);
-  yield extension.awaitMessage("from-page-action-popup-shown");
+  ok(isPageActionShown(extension.id), "The PageAction should be shown.");
 
-  extension.sendMessage("page-action-close-popup");
-
-  let url = yield tabClosedPromise;
-  ok(url.includes("popup.html"), "The tab for the popup should be closed");
+  yield testPopup("default.html");
+  yield testPopup("a.html");
+  yield testPopup("");
+  yield testPopup("b.html");
 
   yield extension.unload();
-  ok(!isPageActionShown(extension.id), "The PageAction should be removed after unload");
+  ok(!isPageActionShown(extension.id), "The PageAction should be removed after unload.");
 });
 </script>
 
 </body>
 </html>