Bug 1348617 - Use the alpha channel of custom styled select backgrounds by applying the requested color on top of the system's background. r=mossop
authorJared Wein <jwein@mozilla.com>
Tue, 21 Mar 2017 10:51:40 -0400
changeset 396831 fc61bbc304d73ccefdb9462fd071bd4320fd805a
parent 396830 85561efb5a00464fd5c7c7b708323005fc86461a
child 396832 81e0dfa05b1966e28d7c817368f46a7047142ae7
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmossop
bugs1348617
milestone55.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 1348617 - Use the alpha channel of custom styled select backgrounds by applying the requested color on top of the system's background. r=mossop This matches parity with Google Chrome Canary Version 59.0.3046.0 (Official Build) canary (64-bit). MozReview-Commit-ID: 3rkhiFv8ezX
browser/base/content/test/forms/browser_selectpopup_colors.js
toolkit/modules/SelectContentHelper.jsm
toolkit/modules/SelectParentHelper.jsm
--- a/browser/base/content/test/forms/browser_selectpopup_colors.js
+++ b/browser/base/content/test/forms/browser_selectpopup_colors.js
@@ -55,16 +55,23 @@ const GENERIC_OPTION_STYLED_AS_IMPORTANT
 
 const TRANSLUCENT_SELECT_BECOMES_OPAQUE =
   "<html><head>" +
   "<body><select id='one' style='background-color: rgba(255,255,255,.55);'>" +
   '  <option value="One">{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
   '  <option value="Two" selected="true">{"end": "true"}</option>' +
   "</select></body></html>";
 
+const TRANSLUCENT_SELECT_APPLIES_ON_BASE_COLOR =
+  "<html><head>" +
+  "<body><select id='one' style='background-color: rgba(255,0,0,.55);'>" +
+  '  <option value="One">{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '  <option value="Two" selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
 const DISABLED_OPTGROUP_AND_OPTIONS =
   "<html><head>" +
   "<body><select id='one'>" +
   "  <optgroup label='{\"color\": \"rgb(0, 0, 0)\", \"backgroundColor\": \"buttonface\"}'>" +
   '    <option disabled="">{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
   '    <option>{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
   '    <option disabled="">{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
   '    <option>{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
@@ -135,18 +142,43 @@ function* testSelectColors(select, itemC
 
   is(selectPopup.parentNode.itemCount, itemCount, "Correct number of items");
   let child = selectPopup.firstChild;
   let idx = 1;
 
   if (!options.skipSelectColorTest) {
     is(getComputedStyle(selectPopup).color, options.selectColor,
       "popup has expected foreground color");
-    is(getComputedStyle(selectPopup).backgroundColor, options.selectBgColor,
-      "popup has expected background color");
+
+    // Combine the select popup's backgroundColor and the
+    // backgroundImage color to get the color that is seen by
+    // the user.
+    let base = getComputedStyle(selectPopup).backgroundColor;
+    let [/* unused */, bR, bG, bB] =
+      base.match(/rgb\((\d+), (\d+), (\d+)\)/);
+    bR = parseInt(bR, 10);
+    bG = parseInt(bG, 10);
+    bB = parseInt(bB, 10);
+    let topCoat = getComputedStyle(selectPopup).backgroundImage;
+    if (topCoat == "none") {
+      is(`rgb(${bR}, ${bG}, ${bB})`, options.selectBgColor,
+        "popup has expected background color");
+    } else {
+      let [/* unused */, /* unused */, tR, tG, tB, tA] =
+        topCoat.match(/(rgba?\((\d+), (\d+), (\d+)(?:, (0\.\d+))?\)), \1/);
+      tR = parseInt(tR, 10);
+      tG = parseInt(tG, 10);
+      tB = parseInt(tB, 10);
+      tA = parseFloat(tA) || 1;
+      let actualR = Math.round(tR * tA + bR * (1 - tA));
+      let actualG = Math.round(tG * tA + bG * (1 - tA));
+      let actualB = Math.round(tB * tA + bB * (1 - tA));
+      is(`rgb(${actualR}, ${actualG}, ${actualB})`, options.selectBgColor,
+        "popup has expected background color");
+    }
   }
 
   ok(!child.selected, "The first child should not be selected");
   while (child) {
     testOptionColors(idx, child, menulist);
     idx++;
     child = child.nextSibling;
   }
@@ -212,24 +244,37 @@ add_task(function* test_select_backgroun
 });
 
 // This test checks when a <select> element has a background set, and the
 // options have their own background set which is equal to the default
 // user-agent background color, but should be used because the select
 // background color has been changed.
 add_task(function* test_translucent_select_becomes_opaque() {
   // The popup is requested to show a translucent background
-  // but we force the alpha channel to 1 on the popup.
+  // but we apply the requested background color on the system's base color.
   let options = {
     selectColor: "rgb(0, 0, 0)",
     selectBgColor: "rgb(255, 255, 255)"
   };
   yield testSelectColors(TRANSLUCENT_SELECT_BECOMES_OPAQUE, 2, options);
 });
 
+// This test checks when a popup has a translucent background color,
+// and that the color painted to the screen of the translucent background
+// matches what the user expects.
+add_task(function* test_translucent_select_applies_on_base_color() {
+  // The popup is requested to show a translucent background
+  // but we apply the requested background color on the system's base color.
+  let options = {
+    selectColor: "rgb(0, 0, 0)",
+    selectBgColor: "rgb(255, 115, 115)"
+  };
+  yield testSelectColors(TRANSLUCENT_SELECT_APPLIES_ON_BASE_COLOR, 2, options);
+});
+
 add_task(function* test_disabled_optgroup_and_options() {
   // The colors used by this test are platform-specific.
   if (AppConstants.platform != "win") {
     return;
   }
 
   yield testSelectColors(DISABLED_OPTGROUP_AND_OPTIONS, 17,
                          {skipSelectColorTest: true});
--- a/toolkit/modules/SelectContentHelper.jsm
+++ b/toolkit/modules/SelectContentHelper.jsm
@@ -137,23 +137,23 @@ this.SelectContentHelper.prototype = {
   },
 
   // Determine user agent background-color and color.
   // This is used to skip applying the custom color if it matches
   // the user agent values.
   _calculateUAColors() {
     let dummyOption = this.element.ownerDocument.createElement("option");
     dummyOption.style.setProperty("color", "-moz-comboboxtext", "important");
-    dummyOption.style.setProperty("backgroundColor", "-moz-combobox", "important");
+    dummyOption.style.setProperty("background-color", "-moz-combobox", "important");
     let optionCS = this.element.ownerGlobal.getComputedStyle(dummyOption);
     this._uaBackgroundColor = optionCS.backgroundColor;
     this._uaColor = optionCS.color;
     let dummySelect = this.element.ownerDocument.createElement("select");
     dummySelect.style.setProperty("color", "-moz-fieldtext", "important");
-    dummySelect.style.setProperty("backgroundColor", "-moz-field", "important");
+    dummySelect.style.setProperty("background-color", "-moz-field", "important");
     let selectCS = this.element.ownerGlobal.getComputedStyle(dummySelect);
     this._uaSelectBackgroundColor = selectCS.backgroundColor;
     this._uaSelectColor = selectCS.color;
   },
 
   get uaBackgroundColor() {
     if (!this._uaBackgroundColor) {
       this._calculateUAColors();
--- a/toolkit/modules/SelectParentHelper.jsm
+++ b/toolkit/modules/SelectParentHelper.jsm
@@ -50,23 +50,17 @@ this.SelectParentHelper = {
     let ruleBody = "";
 
     // Some webpages set the <select> backgroundColor to transparent,
     // but they don't intend to change the popup to transparent.
     if (customStylingEnabled &&
         selectBackgroundColor != uaSelectBackgroundColor &&
         selectBackgroundColor != "rgba(0, 0, 0, 0)" &&
         selectBackgroundColor != selectColor) {
-      let rgba = selectBackgroundColor.match(/rgba\((\d+), (\d+), (\d+),/);
-      if (rgba) {
-        let [, r, g, b] = rgba;
-        ruleBody = `background-color: rgb(${r}, ${g}, ${b});`;
-      } else {
-        ruleBody = `background-color: ${selectBackgroundColor};`;
-      }
+      ruleBody = `background-image: linear-gradient(${selectBackgroundColor}, ${selectBackgroundColor});`;
       usedSelectBackgroundColor = selectBackgroundColor;
     } else {
       usedSelectBackgroundColor = uaSelectBackgroundColor;
     }
 
     if (customStylingEnabled &&
         selectColor != uaSelectColor &&
         selectColor != selectBackgroundColor &&