Bug 1190663: [webext] Add tests for window.close() in browserAction and pageAction popups. r?rpl draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 27 Feb 2016 15:29:42 -0800
changeset 335898 8f40d953c264255414f020d4e568f3d0ca55da57
parent 335162 aba2c94b1bf8d1b0423205a8b45e442c4b35252d
child 515259 2e6c27c1d9b1da45c375f923258d1b25d7dcd96a
push id11923
push usermaglione.k@gmail.com
push dateTue, 01 Mar 2016 23:08:33 +0000
reviewersrpl
bugs1190663
milestone47.0a1
Bug 1190663: [webext] Add tests for window.close() in browserAction and pageAction popups. r?rpl MozReview-Commit-ID: 8r08YRw7IGr
browser/components/extensions/ext-utils.js
browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
browser/components/extensions/test/browser/head.js
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -125,16 +125,29 @@ global.IconDetails = {
 };
 
 global.makeWidgetId = id => {
   id = id.toLowerCase();
   // FIXME: This allows for collisions.
   return id.replace(/[^a-z0-9_-]/g, "_");
 };
 
+function promisePopupShown(popup) {
+  return new Promise(resolve => {
+    if (popup.state == "open") {
+      resolve();
+    } else {
+      popup.addEventListener("popupshown", function onPopupShown(event) {
+        popup.removeEventListener("popupshown", onPopupShown);
+        resolve();
+      });
+    }
+  });
+}
+
 class BasePopup {
   constructor(extension, viewNode, popupURL) {
     let popupURI = Services.io.newURI(popupURL, null, extension.baseURI);
 
     Services.scriptSecurityManager.checkLoadURIWithPrincipal(
       extension.principal, popupURI,
       Services.scriptSecurityManager.DISALLOW_SCRIPT);
 
@@ -254,16 +267,20 @@ class BasePopup {
       this.browser.addEventListener("load", this, true);
       this.browser.addEventListener("DOMTitleChanged", this, true);
       this.browser.addEventListener("DOMWindowClose", this, true);
     });
   }
 
   // Resizes the browser to match the preferred size of the content.
   resizeBrowser() {
+    if (!this.browser) {
+      return;
+    }
+
     let width, height;
     try {
       let w = {}, h = {};
       this.browser.docShell.contentViewer.getContentSize(w, h);
 
       width = w.value / this.window.devicePixelRatio;
       height = h.value / this.window.devicePixelRatio;
 
@@ -310,17 +327,22 @@ global.PanelPopup = class PanelPopup ext
   }
 
   destroy() {
     super.destroy();
     this.viewNode.remove();
   }
 
   closePopup() {
-    this.viewNode.hidePopup();
+    promisePopupShown(this.viewNode).then(() => {
+      // Make sure we're not already destroyed.
+      if (this.viewNode) {
+        this.viewNode.hidePopup();
+      }
+    });
   }
 };
 
 global.ViewPopup = class ViewPopup extends BasePopup {
   get DESTROY_EVENT() {
     return "ViewHiding";
   }
 
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
@@ -1,35 +1,42 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 function* testInArea(area) {
+  let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head></html>`;
+
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "background": {
         "page": "data/background.html",
       },
       "browser_action": {
         "default_popup": "popup-a.html",
       },
     },
 
     files: {
-      "popup-a.html": `<script src="popup-a.js"></script>`,
+      "popup-a.html": scriptPage("popup-a.js"),
       "popup-a.js": function() {
         browser.runtime.sendMessage("from-popup-a");
+        browser.runtime.onMessage.addListener(msg => {
+          if (msg == "close-popup") {
+            window.close();
+          }
+        });
       },
 
-      "data/popup-b.html": `<script src="popup-b.js"></script>`,
+      "data/popup-b.html": scriptPage("popup-b.js"),
       "data/popup-b.js": function() {
         browser.runtime.sendMessage("from-popup-b");
       },
 
-      "data/background.html": `<script src="background.js"></script>`,
+      "data/background.html": scriptPage("background.js"),
 
       "data/background.js": function() {
         let sendClick;
         let tests = [
           () => {
             sendClick({expectEvent: false, expectPopup: "a"});
           },
           () => {
@@ -46,36 +53,47 @@ function* testInArea(area) {
             browser.browserAction.setPopup({popup: ""});
             sendClick({expectEvent: true, expectPopup: null});
           },
           () => {
             sendClick({expectEvent: true, expectPopup: null});
           },
           () => {
             browser.browserAction.setPopup({popup: "/popup-a.html"});
-            sendClick({expectEvent: false, expectPopup: "a"});
+            sendClick({expectEvent: false, expectPopup: "a", runNextTest: true});
+          },
+          () => {
+            browser.test.sendMessage("next-test", {expectClosed: true});
+            browser.runtime.sendMessage("close-popup");
           },
         ];
 
         let expect = {};
-        sendClick = ({expectEvent, expectPopup}) => {
-          expect = {event: expectEvent, popup: expectPopup};
+        sendClick = ({expectEvent, expectPopup, runNextTest}) => {
+          expect = {event: expectEvent, popup: expectPopup, runNextTest};
           browser.test.sendMessage("send-click");
         };
 
         browser.runtime.onMessage.addListener(msg => {
-          if (expect.popup) {
+          if (msg == "close-popup") {
+            return;
+          } else if (expect.popup) {
             browser.test.assertEq(msg, `from-popup-${expect.popup}`,
                                   "expected popup opened");
           } else {
-            browser.test.fail("unexpected popup");
+            browser.test.fail(`unexpected popup: ${msg}`);
           }
 
           expect.popup = null;
-          browser.test.sendMessage("next-test");
+          if (expect.runNextTest) {
+            expect.runNextTest = false;
+            tests.shift()();
+          } else {
+            browser.test.sendMessage("next-test");
+          }
         });
 
         browser.browserAction.onClicked.addListener(() => {
           if (expect.event) {
             browser.test.succeed("expected click event received");
           } else {
             browser.test.fail("unexpected click event");
           }
@@ -102,23 +120,30 @@ function* testInArea(area) {
     },
   });
 
   extension.onMessage("send-click", () => {
     clickBrowserAction(extension);
   });
 
   let widget;
-  extension.onMessage("next-test", Task.async(function* () {
+  extension.onMessage("next-test", Task.async(function* (expecting = {}) {
     if (!widget) {
       widget = getBrowserActionWidget(extension);
       CustomizableUI.addWidgetToArea(widget.id, area);
     }
-
-    yield closeBrowserAction(extension);
+    if (expecting.expectClosed) {
+      let panel = getBrowserActionPopup(extension);
+      ok(panel, "Expect panel to exist");
+      yield promisePopupShown(panel);
+      yield promisePopupHidden(panel);
+      ok(true, "Panel is closed");
+    } else {
+      yield closeBrowserAction(extension);
+    }
 
     extension.sendMessage("next-test");
   }));
 
   yield Promise.all([extension.startup(), extension.awaitFinish("browseraction-tests-done")]);
 
   yield extension.unload();
 
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
@@ -14,16 +14,21 @@ add_task(function* testPageActionPopup()
         "default_popup": "popup-a.html",
       },
     },
 
     files: {
       "popup-a.html": scriptPage("popup-a.js"),
       "popup-a.js": function() {
         browser.runtime.sendMessage("from-popup-a");
+        browser.runtime.onMessage.addListener(msg => {
+          if (msg == "close-popup") {
+            window.close();
+          }
+        });
       },
 
       "data/popup-b.html": scriptPage("popup-b.js"),
       "data/popup-b.js": function() {
         browser.runtime.sendMessage("from-popup-b");
       },
 
       "data/background.html": scriptPage("background.js"),
@@ -50,36 +55,47 @@ add_task(function* testPageActionPopup()
             browser.pageAction.setPopup({tabId, popup: ""});
             sendClick({expectEvent: true, expectPopup: null});
           },
           () => {
             sendClick({expectEvent: true, expectPopup: null});
           },
           () => {
             browser.pageAction.setPopup({tabId, popup: "/popup-a.html"});
-            sendClick({expectEvent: false, expectPopup: "a"});
+            sendClick({expectEvent: false, expectPopup: "a", runNextTest: true});
+          },
+          () => {
+            browser.test.sendMessage("next-test", {expectClosed: true});
+            browser.runtime.sendMessage("close-popup");
           },
         ];
 
         let expect = {};
-        sendClick = ({expectEvent, expectPopup}) => {
-          expect = {event: expectEvent, popup: expectPopup};
+        sendClick = ({expectEvent, expectPopup, runNextTest}) => {
+          expect = {event: expectEvent, popup: expectPopup, runNextTest};
           browser.test.sendMessage("send-click");
         };
 
         browser.runtime.onMessage.addListener(msg => {
-          if (expect.popup) {
+          if (msg == "close-popup") {
+            return;
+          } else if (expect.popup) {
             browser.test.assertEq(msg, `from-popup-${expect.popup}`,
                                   "expected popup opened");
           } else {
-            browser.test.fail("unexpected popup");
+            browser.test.fail(`unexpected popup: ${msg}`);
           }
 
           expect.popup = null;
-          browser.test.sendMessage("next-test");
+          if (expect.runNextTest) {
+            expect.runNextTest = false;
+            tests.shift()();
+          } else {
+            browser.test.sendMessage("next-test");
+          }
         });
 
         browser.pageAction.onClicked.addListener(() => {
           if (expect.event) {
             browser.test.succeed("expected click event received");
           } else {
             browser.test.fail("unexpected click event");
           }
@@ -113,22 +129,29 @@ add_task(function* testPageActionPopup()
 
   let pageActionId = makeWidgetId(extension.id) + "-page-action";
   let panelId = makeWidgetId(extension.id) + "-panel";
 
   extension.onMessage("send-click", () => {
     clickPageAction(extension);
   });
 
-  extension.onMessage("next-test", Task.async(function* () {
+  extension.onMessage("next-test", Task.async(function* (expecting = {}) {
     let panel = document.getElementById(panelId);
-    if (panel) {
+    if (expecting.expectClosed) {
+      ok(panel, "Expect panel to exist");
+      yield promisePopupShown(panel);
+      yield promisePopupHidden(panel);
+      ok(true, `Panel is closed`);
+    } else if (panel) {
       yield promisePopupShown(panel);
       panel.hidePopup();
+    }
 
+    if (panel) {
       panel = document.getElementById(panelId);
       is(panel, null, "panel successfully removed from document after hiding");
     }
 
     extension.sendMessage("next-test");
   }));
 
 
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -2,17 +2,17 @@
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 /* exported CustomizableUI makeWidgetId focusWindow forceGC
  *          getBrowserActionWidget
  *          clickBrowserAction clickPageAction
  *          getBrowserActionPopup getPageActionPopup
  *          closeBrowserAction closePageAction
- *          promisePopupShown
+ *          promisePopupShown promisePopupHidden
  */
 
 var {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm");
 var {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm");
 
 // Bug 1239884: Our tests occasionally hit a long GC pause at unpredictable
 // times in debug builds, which results in intermittent timeouts. Until we have
 // a better solution, we force a GC after certain strategic tests, which tend to
@@ -54,25 +54,37 @@ function promisePopupShown(popup) {
         popup.removeEventListener("popupshown", onPopupShown);
         resolve();
       };
       popup.addEventListener("popupshown", onPopupShown);
     }
   });
 }
 
+function promisePopupHidden(popup) {
+  return new Promise(resolve => {
+    let onPopupHidden = event => {
+      popup.removeEventListener("popuphidden", onPopupHidden);
+      resolve();
+    };
+    popup.addEventListener("popuphidden", onPopupHidden);
+  });
+}
+
 function getBrowserActionWidget(extension) {
   return CustomizableUI.getWidget(makeWidgetId(extension.id) + "-browser-action");
 }
 
 function getBrowserActionPopup(extension, win = window) {
   let group = getBrowserActionWidget(extension);
 
   if (group.areaType == CustomizableUI.TYPE_TOOLBAR) {
     return win.document.getElementById("customizationui-widget-panel");
+  } else {
+    return win.PanelUI.panel;
   }
   return null;
 }
 
 var clickBrowserAction = Task.async(function* (extension, win = window) {
   let group = getBrowserActionWidget(extension);
   let widget = group.forWindow(win);