Part 2: Implement {set|get}Popup for chrome.pageAction (Bug 1264118) r?kmag draft
authorMatthew Wein <mwein@mozilla.com>
Mon, 30 May 2016 19:42:03 -0700
changeset 376044 43777c1dc4d6196215d0ae5ded00adc51fe8300f
parent 375605 ec0b567c7ea3860960fb7d25f089cc053bae39b0
child 523062 d36fe0d3f4bc3be82c8ce5d9a8009beeeaa36cfe
push id20483
push usermwein@mozilla.com
push dateTue, 07 Jun 2016 08:15:29 +0000
reviewerskmag
bugs1264118
milestone49.0a1
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>