Bug 1293287: Close extension popups on extension shutdown. r=aswan
authorKris Maglione <maglione.k@gmail.com>
Wed, 24 Aug 2016 16:26:35 -0700
changeset 311283 c79543918cee752a50ce9c351d3ad15b72b658e8
parent 311282 e6ac4dbe34be40f339ad752fe42d7e9ea9c6197e
child 311284 0238732bceb1be76e050ad170aa008c89fef3e83
push id20395
push userryanvm@gmail.com
push dateFri, 26 Aug 2016 13:37:30 +0000
treeherderfx-team@a65b35c8e5b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1293287
milestone51.0a1
Bug 1293287: Close extension popups on extension shutdown. r=aswan MozReview-Commit-ID: 5lpIk6XrdOM
browser/components/extensions/ext-utils.js
browser/components/extensions/test/browser/browser.ini
browser/components/extensions/test/browser/browser_ext_popup_shutdown.js
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -86,16 +86,24 @@ XPCOMUtils.defineLazyGetter(this, "stand
     let styleSheetURI = NetUtil.newURI("chrome://browser/content/extension-win-panel.css");
     let winStyleSheet = styleSheetService.preloadSheet(styleSheetURI,
                                                        styleSheetService.AGENT_SHEET);
     stylesheets.push(winStyleSheet);
   }
   return stylesheets;
 });
 
+/* eslint-disable mozilla/balanced-listeners */
+extensions.on("page-shutdown", (type, context) => {
+  if (context.type == "popup" && context.active) {
+    context.contentWindow.close();
+  }
+});
+/* eslint-enable mozilla/balanced-listeners */
+
 class BasePopup {
   constructor(extension, viewNode, popupURL, browserStyle, fixedWidth = false) {
     this.extension = extension;
     this.popupURL = popupURL;
     this.viewNode = viewNode;
     this.browserStyle = browserStyle;
     this.window = viewNode.ownerGlobal;
     this.destroyed = false;
@@ -124,18 +132,20 @@ class BasePopup {
     this.browserLoadedDeferred.reject(new Error("Popup destroyed"));
     return this.browserReady.then(() => {
       this.destroyBrowser(this.browser);
       this.browser.remove();
 
       this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
       this.viewNode.style.maxHeight = "";
 
-      this.panel.style.removeProperty("--panel-arrowcontent-background");
-      this.panel.style.removeProperty("--panel-arrow-image-vertical");
+      if (this.panel) {
+        this.panel.style.removeProperty("--panel-arrowcontent-background");
+        this.panel.style.removeProperty("--panel-arrow-image-vertical");
+      }
 
       this.browser = null;
       this.viewNode = null;
     });
   }
 
   destroyBrowser(browser) {
     browser.removeEventListener("DOMWindowCreated", this, true);
@@ -149,17 +159,17 @@ class BasePopup {
   // Returns the name of the event fired on `viewNode` when the popup is being
   // destroyed. This must be implemented by every subclass.
   get DESTROY_EVENT() {
     throw new Error("Not implemented");
   }
 
   get panel() {
     let panel = this.viewNode;
-    while (panel.localName != "panel") {
+    while (panel && panel.localName != "panel") {
       panel = panel.parentNode;
     }
     return panel;
   }
 
   handleEvent(event) {
     switch (event.type) {
       case this.DESTROY_EVENT:
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -44,16 +44,17 @@ tags = webextensions
 [browser_ext_pageAction_context.js]
 [browser_ext_pageAction_popup.js]
 [browser_ext_pageAction_popup_resize.js]
 [browser_ext_pageAction_simple.js]
 [browser_ext_pageAction_title.js]
 [browser_ext_popup_api_injection.js]
 [browser_ext_popup_background.js]
 [browser_ext_popup_corners.js]
+[browser_ext_popup_shutdown.js]
 [browser_ext_runtime_openOptionsPage.js]
 [browser_ext_runtime_openOptionsPage_uninstall.js]
 [browser_ext_runtime_setUninstallURL.js]
 [browser_ext_simple.js]
 [browser_ext_tab_runtimeConnect.js]
 [browser_ext_tabs_audio.js]
 [browser_ext_tabs_captureVisibleTab.js]
 [browser_ext_tabs_create.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_popup_shutdown.js
@@ -0,0 +1,74 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+let getExtension = () => {
+  return ExtensionTestUtils.loadExtension({
+    background() {
+      browser.tabs.query({active: true, currentWindow: true}, tabs => {
+        browser.pageAction.show(tabs[0].id);
+      });
+    },
+
+    manifest: {
+      "browser_action": {
+        "default_popup": "popup.html",
+        "browser_style": false,
+      },
+
+      "page_action": {
+        "default_popup": "popup.html",
+        "browser_style": false,
+      },
+    },
+
+    files: {
+      "popup.html": `<!DOCTYPE html>
+        <html><head><meta charset="utf-8"></head></html>`,
+    },
+  });
+};
+
+add_task(function* testStandaloneBrowserAction() {
+  info("Test stand-alone browserAction popup");
+
+  let extension = getExtension();
+  yield extension.startup();
+
+  clickBrowserAction(extension);
+  let browser = yield awaitExtensionPanel(extension);
+  let panel = getPanelForNode(browser);
+
+  yield extension.unload();
+
+  is(panel.parentNode, null, "Panel should be removed from the document");
+});
+
+add_task(function* testMenuPanelBrowserAction() {
+  let extension = getExtension();
+  yield extension.startup();
+
+  let widget = getBrowserActionWidget(extension);
+  CustomizableUI.addWidgetToArea(widget.id, CustomizableUI.AREA_PANEL);
+
+  clickBrowserAction(extension);
+  let browser = yield awaitExtensionPanel(extension);
+  let panel = getPanelForNode(browser);
+
+  yield extension.unload();
+
+  is(panel.state, "closed", "Panel should be closed");
+});
+
+add_task(function* testPageAction() {
+  let extension = getExtension();
+  yield extension.startup();
+
+  clickPageAction(extension);
+  let browser = yield awaitExtensionPanel(extension);
+  let panel = getPanelForNode(browser);
+
+  yield extension.unload();
+
+  is(panel.parentNode, null, "Panel should be removed from the document");
+});