Bug 1190663: [webext] Add tests for window.close() in browserAction and pageAction popups. r=rpl
authorKris Maglione <maglione.k@gmail.com>
Sat, 27 Feb 2016 15:29:42 -0800
changeset 322780 afa990f7034a12b78e1cba02b532a254c2d1990f
parent 322779 38d23f0168980b27bb88bf20a8cba7f907bc495e
child 322781 7e43bdd93e439b8c8d6e62d48d9de3e293655560
child 322913 2eb7b3fd4cc0ea0bdf666b63dad25bc74289aba3
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrpl
bugs1190663
milestone47.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
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
@@ -120,16 +120,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);
 
@@ -249,16 +262,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;
 
@@ -305,17 +322,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,50 +53,65 @@ 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});
           },
         ];
 
         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");
           }
 
           expect.event = false;
           browser.test.sendMessage("next-test");
         });
 
         browser.test.onMessage.addListener((msg) => {
+          if (msg == "close-popup") {
+            browser.runtime.sendMessage("close-popup");
+            return;
+          }
+
           if (msg != "next-test") {
             browser.test.fail("Expecting 'next-test' message");
           }
 
           if (tests.length) {
             let test = tests.shift();
             test();
           } else {
@@ -102,23 +124,33 @@ 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);
     }
+    if (expecting.expectClosed) {
+      let panel = getBrowserActionPopup(extension);
+      ok(panel, "Expect panel to exist");
+      yield promisePopupShown(panel);
 
-    yield closeBrowserAction(extension);
+      extension.sendMessage("close-popup");
+
+      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,50 +55,65 @@ 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});
           },
         ];
 
         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");
           }
 
           expect.event = false;
           browser.test.sendMessage("next-test");
         });
 
         browser.test.onMessage.addListener((msg) => {
+          if (msg == "close-popup") {
+            browser.runtime.sendMessage("close-popup");
+            return;
+          }
+
           if (msg != "next-test") {
             browser.test.fail("Expecting 'next-test' message");
           }
 
           if (tests.length) {
             let test = tests.shift();
             test();
           } else {
@@ -113,22 +133,32 @@ 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);
+
+      extension.sendMessage("close-popup");
+
+      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);