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, a=gchang
authorJared Wein <jwein@mozilla.com>
Tue, 21 Mar 2017 10:51:40 -0400
changeset 395655 b305ba0b489ba2c9ac32c6896f9da68a24e10357
parent 395654 17f121d897d225a7defa54445dd8bd3000604046
child 395656 94cfa175598827eceb9475a471ca3fe9a82a70f8
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmossop, gchang
bugs1348617
milestone54.0a2
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, a=gchang 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 &&