Bug 1354196 - Forward the text-shadow CSS property to the select popup for styling. r?mossop
MozReview-Commit-ID: 3jzZOIiJyXT
--- 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++;