mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 1386015 - Do not generate styling for each element with inherited color. r=jaws
This patch does a minor refactor of the code used to style popup menu for the <select> element. It improves the custom styling experience on MacOS, preserves the functionality on Windows and removes the unnecessary per-item CSS rules significantly improving the performance of opening the <select> list. MozReview-Commit-ID: 7myXq8aDAWr --HG-- extra : rebase_source : 3ff52f832ec471cca0942e5d8a39961dbc84cff4
This commit is contained in:
parent
e1b628b3c2
commit
a7b00209a5
@ -169,6 +169,19 @@ SELECT_LONG_WITH_TRANSITION +=
|
||||
' <option selected="true">{"end": "true"}</option>' +
|
||||
"</select></body></html>";
|
||||
|
||||
const SELECT_INHERITED_COLORS_ON_OPTIONS_DONT_GET_UNIQUE_RULES_IF_RULE_SET_ON_SELECT = `
|
||||
<html><head><style>
|
||||
select { color: blue; text-shadow: 1px 1px 2px blue; }
|
||||
.redColor { color: red; }
|
||||
.textShadow { text-shadow: 1px 1px 2px black; }
|
||||
</style></head><body><select id='one'>
|
||||
<option>{"color": "rgb(0, 0, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>
|
||||
<option class="redColor">{"color": "rgb(255, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>
|
||||
<option class="textShadow">{"color": "rgb(0, 0, 255)", "textShadow": "rgb(0, 0, 0) 1px 1px 2px", "backgroundColor": "rgba(0, 0, 0, 0)"}</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");
|
||||
@ -472,3 +485,48 @@ add_task(async function test_select_with_transition_doesnt_lose_scroll_position(
|
||||
await hideSelectPopup(selectPopup, "escape");
|
||||
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
add_task(async function test_select_inherited_colors_on_options_dont_get_unique_rules_if_rule_set_on_select() {
|
||||
let options = {
|
||||
selectColor: "rgb(0, 0, 255)",
|
||||
selectBgColor: "rgb(255, 255, 255)",
|
||||
selectTextShadow: "rgb(0, 0, 255) 1px 1px 2px",
|
||||
leaveOpen: true
|
||||
};
|
||||
|
||||
await testSelectColors(SELECT_INHERITED_COLORS_ON_OPTIONS_DONT_GET_UNIQUE_RULES_IF_RULE_SET_ON_SELECT, 4, options);
|
||||
|
||||
let stylesheetEl = document.getElementById("ContentSelectDropdownStylesheet");
|
||||
let sheet = stylesheetEl.sheet;
|
||||
/* Check that there are no rulesets for the first option, but that
|
||||
one exists for the second option and sets the color of that
|
||||
option to "rgb(255, 0, 0)" */
|
||||
|
||||
function hasMatchingRuleForOption(cssRules, index, styles = {}) {
|
||||
for (let rule of cssRules) {
|
||||
if (rule.selectorText.includes(`:nth-child(${index})`)) {
|
||||
if (Object.keys(styles).some(key => rule.style[key] !== styles[key])) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
is(hasMatchingRuleForOption(sheet.cssRules, 1), false,
|
||||
"There should be no rules specific to option1");
|
||||
is(hasMatchingRuleForOption(sheet.cssRules, 2, {
|
||||
color: "rgb(255, 0, 0)"
|
||||
}), true, "There should be a rule specific to option2 and it should have color: red");
|
||||
is(hasMatchingRuleForOption(sheet.cssRules, 3, {
|
||||
"text-shadow": "rgb(0, 0, 0) 1px 1px 2px"
|
||||
}), true, "There should be a rule specific to option3 and it should have text-shadow: rgb(0, 0, 0) 1px 1px 2px");
|
||||
|
||||
|
||||
let menulist = document.getElementById("ContentSelectDropdown");
|
||||
let selectPopup = menulist.menupopup;
|
||||
|
||||
await hideSelectPopup(selectPopup, "escape");
|
||||
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
@ -26,12 +26,40 @@ var selectRect = null;
|
||||
var currentZoom = 1;
|
||||
var closedWithEnter = false;
|
||||
var customStylingEnabled = Services.prefs.getBoolPref("dom.forms.select.customstyling");
|
||||
var usedSelectBackgroundColor;
|
||||
|
||||
this.SelectParentHelper = {
|
||||
/**
|
||||
* `populate` takes the `menulist` element and a list of `items` and generates
|
||||
* a popup list of options.
|
||||
*
|
||||
* If `customStylingEnabled` is set to `true`, the function will alse
|
||||
* style the select and its popup trying to prevent the text
|
||||
* and background to end up in the same color.
|
||||
*
|
||||
* All `ua*` variables represent the color values for the default colors
|
||||
* for their respective form elements used by the user agent.
|
||||
* The `select*` variables represent the color values defined for the
|
||||
* particular <select> element.
|
||||
*
|
||||
* The `customoptionstyling` attribute controls the application of
|
||||
* `-moz-appearance` on the elements and is disabled if the element is
|
||||
* defining its own background-color.
|
||||
*
|
||||
* @param {Element} menulist
|
||||
* @param {Array<Element>} items
|
||||
* @param {Number} selectedIndex
|
||||
* @param {Number} zoom
|
||||
* @param {String} uaBackgroundColor
|
||||
* @param {String} uaColor
|
||||
* @param {String} uaSelectBackgroundColor
|
||||
* @param {String} uaSelectColor
|
||||
* @param {String} selectBackgroundColor
|
||||
* @param {String} selectColor
|
||||
* @param {String} selectTextShadow
|
||||
*/
|
||||
populate(menulist, items, selectedIndex, zoom, uaBackgroundColor, uaColor,
|
||||
uaSelectBackgroundColor, uaSelectColor, selectBackgroundColor, selectColor,
|
||||
selectTextShadow) {
|
||||
uaSelectBackgroundColor, uaSelectColor, selectBackgroundColor,
|
||||
selectColor, selectTextShadow) {
|
||||
// Clear the current contents of the popup
|
||||
menulist.menupopup.textContent = "";
|
||||
let stylesheet = menulist.querySelector("#ContentSelectDropdownStylesheet");
|
||||
@ -50,6 +78,9 @@ this.SelectParentHelper = {
|
||||
}
|
||||
|
||||
let ruleBody = "";
|
||||
let usedSelectBackgroundColor;
|
||||
let usedSelectColor;
|
||||
let selectBackgroundSet = false;
|
||||
|
||||
// Some webpages set the <select> backgroundColor to transparent,
|
||||
// but they don't intend to change the popup to transparent.
|
||||
@ -58,27 +89,41 @@ this.SelectParentHelper = {
|
||||
selectBackgroundColor != "rgba(0, 0, 0, 0)") {
|
||||
ruleBody = `background-image: linear-gradient(${selectBackgroundColor}, ${selectBackgroundColor});`;
|
||||
usedSelectBackgroundColor = selectBackgroundColor;
|
||||
selectBackgroundSet = true;
|
||||
} else {
|
||||
usedSelectBackgroundColor = uaSelectBackgroundColor;
|
||||
}
|
||||
|
||||
if (customStylingEnabled &&
|
||||
selectColor != uaSelectColor &&
|
||||
selectColor != selectBackgroundColor &&
|
||||
(selectBackgroundColor != "rgba(0, 0, 0, 0)" ||
|
||||
selectColor != uaSelectBackgroundColor)) {
|
||||
selectColor != usedSelectBackgroundColor) {
|
||||
ruleBody += `color: ${selectColor};`;
|
||||
usedSelectColor = selectColor;
|
||||
} else {
|
||||
usedSelectColor = uaColor;
|
||||
}
|
||||
|
||||
if (customStylingEnabled &&
|
||||
selectTextShadow != "none") {
|
||||
ruleBody += `text-shadow: ${selectTextShadow};`;
|
||||
sheet.insertRule(`#ContentSelectDropdown > menupopup > [_moz-menuactive="true"] {
|
||||
text-shadow: none;
|
||||
}`, 0);
|
||||
}
|
||||
|
||||
if (ruleBody) {
|
||||
sheet.insertRule(`#ContentSelectDropdown > menupopup {
|
||||
${ruleBody}
|
||||
}`, 0);
|
||||
sheet.insertRule(`#ContentSelectDropdown > menupopup > :not([_moz-menuactive="true"]) {
|
||||
color: inherit;
|
||||
}`, 0);
|
||||
}
|
||||
|
||||
// We only set the `customoptionstyling` if the background has been
|
||||
// manually set. This prevents the overlap between moz-appearance and
|
||||
// background-color. `color` and `text-shadow` do not interfere with it.
|
||||
if (selectBackgroundSet) {
|
||||
menulist.menupopup.setAttribute("customoptionstyling", "true");
|
||||
} else {
|
||||
menulist.menupopup.removeAttribute("customoptionstyling");
|
||||
@ -87,7 +132,7 @@ this.SelectParentHelper = {
|
||||
currentZoom = zoom;
|
||||
currentMenulist = menulist;
|
||||
populateChildren(menulist, items, selectedIndex, zoom,
|
||||
uaBackgroundColor, uaColor, sheet);
|
||||
usedSelectBackgroundColor, usedSelectColor, selectTextShadow, selectBackgroundSet, sheet);
|
||||
},
|
||||
|
||||
open(browser, menulist, rect, isOpenedViaTouch) {
|
||||
@ -253,10 +298,36 @@ this.SelectParentHelper = {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* `populateChildren` creates all <menuitem> elements for the popup menu
|
||||
* based on the list of <option> elements from the <select> element.
|
||||
*
|
||||
* It attempts to intelligently add per-item CSS rules if the single
|
||||
* item values differ from the parent menu values and attempting to avoid
|
||||
* ending up with the same color of text and background.
|
||||
*
|
||||
* @param {Element} menulist
|
||||
* @param {Array<Element>} options
|
||||
* @param {Number} selectedIndex
|
||||
* @param {Number} zoom
|
||||
* @param {String} usedSelectBackgroundColor
|
||||
* @param {String} usedSelectColor
|
||||
* @param {String} selectTextShadow
|
||||
* @param {String} selectBackgroundSet
|
||||
* @param {CSSStyleSheet} sheet
|
||||
* @param {Element} parentElement
|
||||
* @param {Boolean} isGroupDisabled
|
||||
* @param {Number} adjustedTextSize
|
||||
* @param {Boolean} addSearch
|
||||
* @param {Number} nthChildIndex
|
||||
* @returns {Number}
|
||||
*/
|
||||
function populateChildren(menulist, options, selectedIndex, zoom,
|
||||
uaBackgroundColor, uaColor, sheet,
|
||||
usedSelectBackgroundColor, usedSelectColor,
|
||||
selectTextShadow, selectBackgroundSet, sheet,
|
||||
parentElement = null, isGroupDisabled = false,
|
||||
adjustedTextSize = -1, addSearch = true, nthChildIndex = 1) {
|
||||
adjustedTextSize = -1, addSearch = true,
|
||||
nthChildIndex = 1) {
|
||||
let element = menulist.menupopup;
|
||||
let win = element.ownerGlobal;
|
||||
|
||||
@ -284,21 +355,30 @@ function populateChildren(menulist, options, selectedIndex, zoom,
|
||||
item.setAttribute("tooltiptext", option.tooltip);
|
||||
|
||||
let ruleBody = "";
|
||||
let usedBackgroundColor;
|
||||
let optionBackgroundSet = false;
|
||||
|
||||
if (customStylingEnabled &&
|
||||
option.backgroundColor &&
|
||||
option.backgroundColor != "rgba(0, 0, 0, 0)" &&
|
||||
option.backgroundColor != usedSelectBackgroundColor) {
|
||||
ruleBody = `background-color: ${option.backgroundColor};`;
|
||||
usedBackgroundColor = option.backgroundColor;
|
||||
optionBackgroundSet = true;
|
||||
} else {
|
||||
usedBackgroundColor = usedSelectBackgroundColor;
|
||||
}
|
||||
|
||||
if (customStylingEnabled &&
|
||||
option.color &&
|
||||
option.color != uaColor) {
|
||||
option.color != usedBackgroundColor &&
|
||||
option.color != usedSelectColor) {
|
||||
ruleBody += `color: ${option.color};`;
|
||||
}
|
||||
|
||||
if (customStylingEnabled &&
|
||||
option.textShadow) {
|
||||
option.textShadow &&
|
||||
option.textShadow != selectTextShadow) {
|
||||
ruleBody += `text-shadow: ${option.textShadow};`;
|
||||
}
|
||||
|
||||
@ -307,7 +387,7 @@ function populateChildren(menulist, options, selectedIndex, zoom,
|
||||
${ruleBody}
|
||||
}`, 0);
|
||||
|
||||
if (option.textShadow) {
|
||||
if (option.textShadow && option.textShadow != selectTextShadow) {
|
||||
// Need to explicitly disable the possibly inherited
|
||||
// text-shadow rule when _moz-menuactive=true since
|
||||
// _moz-menuactive=true disables custom option styling.
|
||||
@ -315,7 +395,10 @@ function populateChildren(menulist, options, selectedIndex, zoom,
|
||||
text-shadow: none;
|
||||
}`, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (customStylingEnabled &&
|
||||
(optionBackgroundSet || selectBackgroundSet)) {
|
||||
item.setAttribute("customoptionstyling", "true");
|
||||
} else {
|
||||
item.removeAttribute("customoptionstyling");
|
||||
@ -333,7 +416,8 @@ function populateChildren(menulist, options, selectedIndex, zoom,
|
||||
if (isOptGroup) {
|
||||
nthChildIndex =
|
||||
populateChildren(menulist, option.children, selectedIndex, zoom,
|
||||
uaBackgroundColor, uaColor, sheet,
|
||||
usedSelectBackgroundColor, usedSelectColor,
|
||||
selectTextShadow, selectBackgroundSet, sheet,
|
||||
item, isDisabled, adjustedTextSize, false, nthChildIndex);
|
||||
} else {
|
||||
if (option.index == selectedIndex) {
|
||||
|
Loading…
Reference in New Issue
Block a user