Bug 1354196 - Forward the text-shadow CSS property to the select popup for styling. r?mossop draft
authorJared Wein <jwein@mozilla.com>
Mon, 10 Apr 2017 16:03:03 -0400
changeset 559909 c4f2f628890440f68faad50d56ace7b1986b89e8
parent 558422 2f33b3e877aa45a052efdae5c4ab0e1589ecc731
child 559919 0dd2225fb65df5c8cc14f78c4aab2f620494f900
child 560614 3c79be17471a0238c14668be586f5cd3da974083
push id53270
push userbmo:jaws@mozilla.com
push dateMon, 10 Apr 2017 20:03:27 +0000
reviewersmossop
bugs1354196
milestone55.0a1
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++;