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 draft
authorJared Wein <jwein@mozilla.com>
Tue, 21 Mar 2017 10:51:40 -0400
changeset 502453 67ba3ed31cdd9fd49eb771dbc1cb37d6e52bcbbc
parent 501522 8d967436d696d1f8e3fb33cf7e3d32a72457ffa6
child 550156 a700596438a258c826529731a89c7b1d6fa23a73
push id50278
push userjwein@mozilla.com
push dateTue, 21 Mar 2017 19:42:33 +0000
reviewersmossop
bugs1348617
milestone55.0a1
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 &&