Bug 1293287: Close extension popups on extension shutdown. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Wed, 24 Aug 2016 16:26:35 -0700
changeset 405167 ba86b6c7516f359a6bb6c38c231ac84332a6cc0b
parent 405166 d0f1300fe232ec853c3aa3f1e9cf14efe76b1078
child 529377 f3e031248a4851179f8ca98c9665c58742c9ad04
push id27419
push usermaglione.k@gmail.com
push dateWed, 24 Aug 2016 23:27:05 +0000
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,80 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* testPopupShutdown() {
+  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>`,
+      },
+    });
+  };
+
+  {
+    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");
+  }
+
+  {
+    info("Test menu panel browserAction popup");
+
+    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");
+  }
+
+  {
+    info("Test pageAction popup");
+
+    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");
+  }
+});