Bug 1354196 - Forward the text-shadow CSS property to the select popup for styling. r=mossop
authorJared Wein <jwein@mozilla.com>
Mon, 10 Apr 2017 16:03:03 -0400
changeset 400600 7545a6eab53bfd974ba81a9adc8f4c86cd71c2e6
parent 400599 9929b37fea6884a758f3b5fee384bb7ea91792fa
child 400601 64ba6efd97222a83c08d56f0f2e772aec688ec8c
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
bugs1354196
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 1354196 - Forward the text-shadow CSS property to the select popup for styling. r=mossop MozReview-Commit-ID: 3jzZOIiJyXT
browser/base/content/test/forms/browser_selectpopup_colors.js
toolkit/content/widgets/browser.xml
toolkit/content/widgets/remote-browser.xml
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
@@ -134,16 +134,24 @@ const SELECT_STYLE_OF_OPTION_CHANGES_AFT
   "<html><head><style>" +
   "  select { transition: all .1s; }" +
   "  select:focus { background-color: orange; }" +
   "</style></head><body><select id='one'>" +
   '  <option>{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
   '  <option selected="true">{"end": "true"}</option>' +
   "</select></body></html>";
 
+const SELECT_TRANSPARENT_COLOR_WITH_TEXT_SHADOW =
+  "<html><head><style>" +
+  "  select { color: transparent; text-shadow: 0 0 0 #303030; }" +
+  "</style></head><body><select id='one'>" +
+  '  <option>{"color": "rgba(0, 0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)", "textShadow": "rgb(48, 48, 48) 0px 0px 0px"}</option>' +
+  '  <option selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
 function getSystemColor(color) {
   // Need to convert system color to RGB color.
   let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
   textarea.style.color = color;
   return getComputedStyle(textarea).color;
 }
 
 function testOptionColors(index, item, menulist) {
@@ -169,16 +177,20 @@ function testOptionColors(index, item, m
   if (expected.unstyled) {
     ok(!item.hasAttribute("customoptionstyling"),
       `Item ${index} should not have any custom option styling`);
   } else {
     is(getComputedStyle(item).color, expected.color,
        "Item " + (index) + " has correct foreground color");
     is(getComputedStyle(item).backgroundColor, expected.backgroundColor,
        "Item " + (index) + " has correct background color");
+    if (expected.textShadow) {
+      is(getComputedStyle(item).textShadow, expected.textShadow,
+         "Item " + (index) + " has correct text-shadow color");
+    }
   }
 }
 
 function* testSelectColors(select, itemCount, options) {
   const pageUrl = "data:text/html," + escape(select);
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
 
   let menulist = document.getElementById("ContentSelectDropdown");
@@ -200,16 +212,21 @@ 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");
 
+    if (options.selectTextShadow) {
+      is(getComputedStyle(selectPopup).textShadow, options.selectTextShadow,
+        "popup has expected text-shadow 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);
@@ -383,8 +400,17 @@ add_task(function* test_style_of_options
       property: "background-image",
       value: "linear-gradient(rgb(255, 165, 0), rgb(255, 165, 0))"
     }
   };
 
   yield testSelectColors(SELECT_STYLE_OF_OPTION_CHANGES_AFTER_TRANSITIONEND, 2, options);
 });
 
+add_task(function* test_transparent_color_with_text_shadow() {
+  let options = {
+    selectColor: "rgba(0, 0, 0, 0)",
+    selectTextShadow: "rgb(48, 48, 48) 0px 0px 0px",
+    selectBgColor: "rgb(255, 255, 255)"
+  };
+
+  yield testSelectColors(SELECT_TRANSPARENT_COLOR_WITH_TEXT_SHADOW, 2, options);
+});
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1083,17 +1083,17 @@
                   Cu.import("resource://gre/modules/SelectParentHelper.jsm", {}).SelectParentHelper;
               }
 
               let menulist = document.getElementById(this.getAttribute("selectmenulist"));
               menulist.menupopup.style.direction = data.direction;
               this._selectParentHelper.populate(menulist, data.options, data.selectedIndex, this._fullZoom,
                                                 data.uaBackgroundColor, data.uaColor,
                                                 data.uaSelectBackgroundColor, data.uaSelectColor,
-                                                data.selectBackgroundColor, data.selectColor);
+                                                data.selectBackgroundColor, data.selectColor, data.selectTextShadow);
               this._selectParentHelper.open(this, menulist, data.rect, data.isOpenedViaTouch);
               break;
             }
 
             case "Forms:HideDropDown": {
               if (this._selectParentHelper) {
                 let menulist = document.getElementById(this.getAttribute("selectmenulist"));
                 this._selectParentHelper.hide(menulist, this);
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -473,17 +473,17 @@
               let menulist = document.getElementById(this.getAttribute("selectmenulist"));
               menulist.menupopup.style.direction = data.direction;
 
               let zoom = Services.prefs.getBoolPref("browser.zoom.full") ||
                          this.isSyntheticDocument ? this._fullZoom : this._textZoom;
               this._selectParentHelper.populate(menulist, data.options, data.selectedIndex,
                                                 zoom, data.uaBackgroundColor, data.uaColor,
                                                 data.uaSelectBackgroundColor, data.uaSelectColor,
-                                                data.selectBackgroundColor, data.selectColor);
+                                                data.selectBackgroundColor, data.selectColor, data.selectTextShadow);
               this._selectParentHelper.open(this, menulist, data.rect, data.isOpenedViaTouch);
               break;
             }
 
             case "FullZoomChange": {
               this._fullZoom = data.value;
               let event = document.createEvent("Events");
               event.initEvent("FullZoomChange", true, false);
--- a/toolkit/modules/SelectContentHelper.jsm
+++ b/toolkit/modules/SelectContentHelper.jsm
@@ -95,25 +95,27 @@ this.SelectContentHelper.prototype = {
 
   showDropDown() {
     this.element.openInParentProcess = true;
     let rect = this._getBoundingContentRect();
     DOMUtils.addPseudoClassLock(this.element, ":focus");
     let computedStyles = getComputedStyles(this.element);
     this._selectBackgroundColor = computedStyles.backgroundColor;
     this._selectColor = computedStyles.color;
+    this._selectTextShadow = computedStyles.textShadow;
     DOMUtils.clearPseudoClassLocks(this.element);
     this.global.sendAsyncMessage("Forms:ShowDropDown", {
       direction: computedStyles.direction,
       isOpenedViaTouch: this.isOpenedViaTouch,
       options: this._buildOptionList(),
       rect,
       selectedIndex: this.element.selectedIndex,
       selectBackgroundColor: this._selectBackgroundColor,
       selectColor: this._selectColor,
+      selectTextShadow: this._selectTextShadow,
       uaBackgroundColor: this.uaBackgroundColor,
       uaColor: this.uaColor,
       uaSelectBackgroundColor: this.uaSelectBackgroundColor,
       uaSelectColor: this.uaSelectColor
     });
     gOpen = true;
   },
 
@@ -133,22 +135,24 @@ this.SelectContentHelper.prototype = {
     // Let's send up a new list of options.
     // Technically we might not need to set this pseudo-class
     // during _update() since the element should organically
     // have :focus, though it is here for belt-and-suspenders.
     DOMUtils.addPseudoClassLock(this.element, ":focus");
     let computedStyles = getComputedStyles(this.element);
     this._selectBackgroundColor = computedStyles.backgroundColor;
     this._selectColor = computedStyles.color;
+    this._selectTextShadow = computedStyles.textShadow;
     DOMUtils.clearPseudoClassLocks(this.element);
     this.global.sendAsyncMessage("Forms:UpdateDropDown", {
       options: this._buildOptionList(),
       selectedIndex: this.element.selectedIndex,
       selectBackgroundColor: this._selectBackgroundColor,
       selectColor: this._selectColor,
+      selectTextShadow: this._selectTextShadow,
       uaBackgroundColor: this.uaBackgroundColor,
       uaColor: this.uaColor,
       uaSelectBackgroundColor: this.uaSelectBackgroundColor,
       uaSelectColor: this.uaSelectColor
     });
   },
 
   // Determine user agent background-color and color.
@@ -330,16 +334,20 @@ function buildOptionListForChildren(node
         // an individual style set for direction
         textDirection: cs.direction,
         tooltip: child.title,
         backgroundColor: cs.backgroundColor,
         color: cs.color,
         children: tagName == "OPTGROUP" ? buildOptionListForChildren(child) : []
       };
 
+      if (cs.textShadow != "none") {
+        info.textShadow = cs.textShadow;
+      }
+
       // We must wait until all computedStyles have been
       // read before we clear the locks.
       DOMUtils.clearPseudoClassLocks(child);
 
       result.push(info);
     }
   }
   return result;
--- a/toolkit/modules/SelectParentHelper.jsm
+++ b/toolkit/modules/SelectParentHelper.jsm
@@ -23,17 +23,18 @@ var currentMenulist = null;
 var currentZoom = 1;
 var closedWithEnter = false;
 var selectRect;
 var customStylingEnabled = Services.prefs.getBoolPref("dom.forms.select.customstyling");
 var usedSelectBackgroundColor;
 
 this.SelectParentHelper = {
   populate(menulist, items, selectedIndex, zoom, uaBackgroundColor, uaColor,
-           uaSelectBackgroundColor, uaSelectColor, selectBackgroundColor, selectColor) {
+           uaSelectBackgroundColor, uaSelectColor, selectBackgroundColor, selectColor,
+           selectTextShadow) {
     // Clear the current contents of the popup
     menulist.menupopup.textContent = "";
     let stylesheet = menulist.querySelector("#ContentSelectDropdownScopedStylesheet");
     if (stylesheet) {
       stylesheet.remove();
     }
 
     let doc = menulist.ownerDocument;
@@ -63,16 +64,21 @@ this.SelectParentHelper = {
     if (customStylingEnabled &&
         selectColor != uaSelectColor &&
         selectColor != selectBackgroundColor &&
         (selectBackgroundColor != "rgba(0, 0, 0, 0)" ||
          selectColor != uaSelectBackgroundColor)) {
       ruleBody += `color: ${selectColor};`;
     }
 
+    if (customStylingEnabled &&
+        selectTextShadow != "none") {
+      ruleBody += `text-shadow: ${selectTextShadow};`;
+    }
+
     if (ruleBody) {
       sheet.insertRule(`menupopup {
         ${ruleBody}
       }`, 0);
       menulist.menupopup.setAttribute("customoptionstyling", "true");
     } else {
       menulist.menupopup.removeAttribute("customoptionstyling");
     }
@@ -190,20 +196,21 @@ this.SelectParentHelper = {
       let options = msg.data.options;
       let selectedIndex = msg.data.selectedIndex;
       let uaBackgroundColor = msg.data.uaBackgroundColor;
       let uaColor = msg.data.uaColor;
       let uaSelectBackgroundColor = msg.data.uaSelectBackgroundColor;
       let uaSelectColor = msg.data.uaSelectColor;
       let selectBackgroundColor = msg.data.selectBackgroundColor;
       let selectColor = msg.data.selectColor;
+      let selectTextShadow = msg.data.selectTextShadow;
       this.populate(currentMenulist, options, selectedIndex,
                     currentZoom, uaBackgroundColor, uaColor,
                     uaSelectBackgroundColor, uaSelectColor,
-                    selectBackgroundColor, selectColor);
+                    selectBackgroundColor, selectColor, selectTextShadow);
     }
   },
 
   _registerListeners(browser, popup) {
     popup.addEventListener("command", this);
     popup.addEventListener("popuphidden", this);
     popup.addEventListener("mouseover", this);
     popup.addEventListener("mouseout", this);
@@ -265,20 +272,35 @@ function populateChildren(menulist, opti
     }
 
     if (customStylingEnabled &&
         option.color &&
         option.color != uaColor) {
       ruleBody += `color: ${option.color};`;
     }
 
+    if (customStylingEnabled &&
+        option.textShadow) {
+      ruleBody += `text-shadow: ${option.textShadow};`;
+    }
+
     if (ruleBody) {
       sheet.insertRule(`menupopup > :nth-child(${nthChildIndex}):not([_moz-menuactive="true"]) {
         ${ruleBody}
       }`, 0);
+
+      if (option.textShadow) {
+        // Need to explicitly disable the possibly inherited
+        // text-shadow rule when _moz-menuactive=true since
+        // _moz-menuactive=true disables custom option styling.
+        sheet.insertRule(`menupopup > :nth-child(${nthChildIndex})[_moz-menuactive="true"] {
+          text-shadow: none;
+        }`, 0);
+      }
+
       item.setAttribute("customoptionstyling", "true");
     } else {
       item.removeAttribute("customoptionstyling");
     }
 
     element.appendChild(item);
     nthChildIndex++;