Bug 1449933 - Webextension popups that don't define a background-color can be hard to read. r?mixedpuppy draft
authorJared Wein <jwein@mozilla.com>
Fri, 30 Mar 2018 15:29:31 -0700
changeset 779295 183f1cebff2bc253cf790d4531e021dd8003726b
parent 777984 110f32790d38a258cab722064aae40736478ef51
push id105735
push userbmo:jaws@mozilla.com
push dateMon, 09 Apr 2018 16:14:26 +0000
reviewersmixedpuppy
bugs1449933
milestone61.0a1
Bug 1449933 - Webextension popups that don't define a background-color can be hard to read. r?mixedpuppy MozReview-Commit-ID: CVXySkhYaem
browser/components/extensions/ExtensionPopups.jsm
browser/components/extensions/test/browser/browser_ext_popup_background.js
toolkit/components/extensions/ext-browser-content.js
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -119,16 +119,17 @@ class BasePopup {
       if (this.viewNode) {
         this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
         delete this.viewNode.customRectGetter;
       }
 
       let {panel} = this;
       if (panel) {
         panel.style.removeProperty("--arrowpanel-background");
+        panel.style.removeProperty("--arrowpanel-border-color");
         panel.removeAttribute("remote");
       }
 
       this.browser = null;
       this.stack = null;
       this.viewNode = null;
     });
   }
@@ -345,19 +346,29 @@ class BasePopup {
       this.browser.style.height = `${height}px`;
       this.browser.style.minHeight = `${height}px`;
     }
 
     let event = new this.window.CustomEvent("WebExtPopupResized", {detail});
     this.browser.dispatchEvent(event);
   }
 
-  setBackground(background = "") {
-    if (background) {
-      this.panel.style.setProperty("--arrowpanel-background", background);
+  setBackground(background) {
+    // Panels inherit the applied theme (light, dark, etc) and there is a high
+    // likelihood that most extension authors will not have tested with a dark theme.
+    // If they have not set a background-color, we force it to white to ensure visibility
+    // of the extension content. Passing `null` should be treated the same as no argument,
+    // which is why we can't use default parameters here.
+    if (!background) {
+      background = "#fff";
+    }
+    this.panel.style.setProperty("--arrowpanel-background", background);
+    if (background == "#fff") {
+      // Set a usable default color that work with the default background-color.
+      this.panel.style.setProperty("--arrowpanel-border-color", "hsla(210,4%,10%,.05)");
     }
     this.background = background;
   }
 }
 
 /**
  * A map of active popups for a given browser window.
  *
--- a/browser/components/extensions/test/browser/browser_ext_popup_background.js
+++ b/browser/components/extensions/test/browser/browser_ext_popup_background.js
@@ -1,116 +1,125 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 "use strict";
 
-add_task(async function testPopupBackground() {
-  let extension = 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,
-      },
-    },
+async function testPanel(browser, standAlone, initial_background) {
+  let panel = getPanelForNode(browser);
+  let arrowContent = document.getAnonymousElementByAttribute(panel, "class", "panel-arrowcontent");
+  let arrow = document.getAnonymousElementByAttribute(panel, "anonid", "arrow");
 
-    files: {
-      "popup.html": `<!DOCTYPE html>
-        <html>
-          <head>
-            <meta charset="utf-8">
-          </head>
-          <body style="width: 100px; height: 100px; background-color: green;">
-          </body>
-        </html>`,
-    },
-  });
-
-  await extension.startup();
-
-  async function testPanel(browser, standAlone) {
-    let panel = getPanelForNode(browser);
-    let arrowContent = document.getAnonymousElementByAttribute(panel, "class", "panel-arrowcontent");
-    let arrow = document.getAnonymousElementByAttribute(panel, "anonid", "arrow");
-
-    let checkArrow = (background = null) => {
-      if (background == null || !standAlone) {
-        ok(!arrow.style.hasOwnProperty("fill"), "Arrow fill should be the default one");
-        return;
-      }
-
-      is(getComputedStyle(arrowContent).backgroundColor, background, "Arrow content should have correct background");
-      is(getComputedStyle(arrow).fill, background, "Arrow should have correct background");
-    };
-
-    function getBackground(browser) {
-      return ContentTask.spawn(browser, null, async function() {
-        return content.getComputedStyle(content.document.body)
-                      .backgroundColor;
-      });
+  let checkArrow = (background = null) => {
+    if (background == null || !standAlone) {
+      ok(!arrow.style.hasOwnProperty("fill"), "Arrow fill should be the default one");
+      return;
     }
 
-    let setBackground = color => {
-      content.document.body.style.backgroundColor = color;
-    };
-
-    await new Promise(resolve => setTimeout(resolve, 100));
-
-    info("Test that initial background color is applied");
-
-    checkArrow(await getBackground(browser));
+    is(getComputedStyle(arrowContent).backgroundColor, background, "Arrow content should have correct background");
+    is(getComputedStyle(arrow).fill, background, "Arrow should have correct background");
+  };
 
-    info("Test that dynamically-changed background color is applied");
-
-    await alterContent(browser, setBackground, "black");
-
-    checkArrow(await getBackground(browser));
-
-    info("Test that non-opaque background color results in default styling");
-
-    await alterContent(browser, setBackground, "rgba(1, 2, 3, .9)");
-
-    checkArrow(null);
+  function getBackground(browser) {
+    return ContentTask.spawn(browser, null, async function() {
+      return content.getComputedStyle(content.document.body)
+                    .backgroundColor;
+    });
   }
 
-  {
-    info("Test stand-alone browserAction popup");
+  let setBackground = color => {
+    content.document.body.style.backgroundColor = color;
+  };
+
+  await new Promise(resolve => setTimeout(resolve, 100));
+
+  info("Test that initial background color is applied");
+  checkArrow(initial_background);
+
+  info("Test that dynamically-changed background color is applied");
+  await alterContent(browser, setBackground, "black");
+  checkArrow(await getBackground(browser));
+
+  info("Test that non-opaque background color results in default styling");
+  await alterContent(browser, setBackground, "rgba(1, 2, 3, .9)");
+  checkArrow(null);
+}
 
-    clickBrowserAction(extension);
-    let browser = await awaitExtensionPanel(extension);
-    await testPanel(browser, true);
-    await closeBrowserAction(extension);
-  }
+add_task(async function testPopupBackground() {
+  let testCases = [{
+    "browser_style": false,
+    "background": "background-color: green;",
+    "initial_background": "rgb(0, 128, 0)",
+  }, {
+    "browser_style": true,
+    // Use white here instead of transparent, because
+    // when no background is supplied we will fill
+    // with white by default.
+    "initial_background": "rgb(255, 255, 255)",
+  }];
+  for (let testCase of testCases) {
+    info(`Testing browser_style: ${testCase.browser_style} with background? ${!!testCase.background}`);
+    let extension = ExtensionTestUtils.loadExtension({
+      background() {
+        browser.tabs.query({active: true, currentWindow: true}, tabs => {
+          browser.pageAction.show(tabs[0].id);
+        });
+      },
 
-  {
-    info("Test menu panel browserAction popup");
-
-    let widget = getBrowserActionWidget(extension);
-    CustomizableUI.addWidgetToArea(widget.id, getCustomizableUIPanelID());
+      manifest: {
+        "browser_action": {
+          "default_popup": "popup.html",
+          "browser_style": testCase.browser_style,
+        },
 
-    clickBrowserAction(extension);
-    let browser = await awaitExtensionPanel(extension);
-    await testPanel(browser, false);
-    await closeBrowserAction(extension);
-  }
+        "page_action": {
+          "default_popup": "popup.html",
+          "browser_style": testCase.browser_style,
+        },
+      },
 
-  {
-    info("Test pageAction popup");
+      files: {
+        "popup.html": `<!DOCTYPE html>
+          <html>
+            <head>
+              <meta charset="utf-8">
+            </head>
+            <body style="width: 100px; height: 100px; ${testCase.background || ""}">
+            </body>
+          </html>`,
+      },
+    });
+
+    await extension.startup();
+
+    {
+      info("Test stand-alone browserAction popup");
 
-    clickPageAction(extension);
-    let browser = await awaitExtensionPanel(extension);
-    await testPanel(browser, true);
-    await closePageAction(extension);
+      clickBrowserAction(extension);
+      let browser = await awaitExtensionPanel(extension);
+      await testPanel(browser, true, testCase.initial_background);
+      await closeBrowserAction(extension);
+    }
+
+    {
+      info("Test menu panel browserAction popup");
+
+      let widget = getBrowserActionWidget(extension);
+      CustomizableUI.addWidgetToArea(widget.id, getCustomizableUIPanelID());
+
+      clickBrowserAction(extension);
+      let browser = await awaitExtensionPanel(extension);
+      await testPanel(browser, false, testCase.initial_background);
+      await closeBrowserAction(extension);
+    }
+
+    {
+      info("Test pageAction popup");
+
+      clickPageAction(extension);
+      let browser = await awaitExtensionPanel(extension);
+      await testPanel(browser, true, testCase.initial_background);
+      await closePageAction(extension);
+    }
+
+    await extension.unload();
   }
-
-  await extension.unload();
 });
--- a/toolkit/components/extensions/ext-browser-content.js
+++ b/toolkit/components/extensions/ext-browser-content.js
@@ -276,17 +276,18 @@ const BrowserListener = {
       result = {height, detail};
     } else {
       let background = doc.defaultView.getComputedStyle(body).backgroundColor;
       if (!isOpaque(background)) {
         // Ignore non-opaque backgrounds.
         background = null;
       }
 
-      if (background !== this.oldBackground) {
+      if (background === null ||
+          background !== this.oldBackground) {
         sendAsyncMessage("Extension:BrowserBackgroundChanged", {background});
       }
       this.oldBackground = background;
 
 
       // Adjust the size of the browser based on its content's preferred size.
       let {contentViewer} = docShell;
       let ratio = content.devicePixelRatio;