diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /browser/base/content/test/forms/browser_selectpopup_colors.js | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | browser/base/content/test/forms/browser_selectpopup_colors.js | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/browser/base/content/test/forms/browser_selectpopup_colors.js b/browser/base/content/test/forms/browser_selectpopup_colors.js new file mode 100644 index 0000000000..63cece0ce5 --- /dev/null +++ b/browser/base/content/test/forms/browser_selectpopup_colors.js @@ -0,0 +1,867 @@ +const gSelects = { + PAGECONTENT_COLORS: + "<html><head><style>" + + " .blue { color: #fff; background-color: #00f; }" + + " .green { color: #800080; background-color: green; }" + + " .defaultColor { color: -moz-ComboboxText; }" + + " .defaultBackground { background-color: -moz-Combobox; }" + + "</style>" + + "<body><select id='one'>" + + ' <option value="One" style="color: #fff; background-color: #f00;">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(255, 0, 0)"}</option>' + + ' <option value="Two" class="blue">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(0, 0, 255)"}</option>' + + ' <option value="Three" class="green">{"color": "rgb(128, 0, 128)", "backgroundColor": "rgb(0, 128, 0)"}</option>' + + ' <option value="Four" class="defaultColor defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option value="Five" class="defaultColor">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' + + ' <option value="Six" class="defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option value="Seven" selected="true">{"unstyled": "true"}</option>' + + "</select></body></html>", + + PAGECONTENT_COLORS_ON_SELECT: + "<html><head><style>" + + " #one { background-color: #7E3A3A; color: #fff }" + + "</style>" + + "<body><select id='one'>" + + ' <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' + + ' <option value="Two">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' + + ' <option value="Three">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' + + ' <option value="Four" selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + TRANSPARENT_SELECT: + "<html><head><style>" + + " #one { background-color: transparent; }" + + "</style>" + + "<body><select id='one'>" + + ' <option value="One">{"unstyled": "true"}</option>' + + ' <option value="Two" selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + OPTION_COLOR_EQUAL_TO_UABACKGROUND_COLOR_SELECT: + "<html><head><style>" + + " #one { background-color: black; color: white; }" + + "</style>" + + "<body><select id='one'>" + + ' <option value="One" style="background-color: white; color: black;">{"color": "rgb(0, 0, 0)", "backgroundColor": "rgb(255, 255, 255)"}</option>' + + ' <option value="Two" selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + GENERIC_OPTION_STYLED_AS_IMPORTANT: + "<html><head><style>" + + " option { background-color: black !important; color: white !important; }" + + "</style>" + + "<body><select id='one'>" + + ' <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(0, 0, 0)"}</option>' + + ' <option value="Two" selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + 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>", + + 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>", + + DISABLED_OPTGROUP_AND_OPTIONS: + "<html><head>" + + "<body><select id='one'>" + + " <optgroup label='{\"unstyled\": true}'>" + + ' <option disabled="">{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option>{"unstyled": true}</option>' + + ' <option disabled="">{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option>{"unstyled": true}</option>' + + ' <option>{"unstyled": true}</option>' + + ' <option>{"unstyled": true}</option>' + + ' <option>{"unstyled": true}</option>' + + " </optgroup>" + + ' <optgroup label=\'{"color": "GrayText", "backgroundColor": "-moz-Combobox"}\' disabled=\'\'>' + + ' <option>{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option>{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option>{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option>{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option>{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option>{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + ' <option>{"color": "GrayText", "backgroundColor": "-moz-Combobox"}</option>' + + " </optgroup>" + + ' <option value="Two" selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + SELECT_CHANGES_COLOR_ON_FOCUS: + "<html><head><style>" + + " select:focus { background-color: orange; color: black; }" + + "</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>", + + SELECT_BGCOLOR_ON_SELECT_COLOR_ON_OPTIONS: + "<html><head><style>" + + " select { background-color: black; }" + + " option { color: white; }" + + "</style></head>" + + "<body><select id='one'>" + + ' <option>{"colorScheme": "dark", "color": "rgb(255, 255, 255)", "backgroundColor": "rgb(0, 0, 0)"}</option>' + + ' <option selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + SELECT_STYLE_OF_OPTION_IS_BASED_ON_FOCUS_OF_SELECT: + "<html><head><style>" + + " select:focus { background-color: #3a96dd; }" + + " select:focus option { background-color: #fff; }" + + "</style></head>" + + "<body><select id='one'>" + + ' <option>{"color": "-moz-ComboboxText", "backgroundColor": "rgb(255, 255, 255)"}</option>' + + ' <option selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + SELECT_STYLE_OF_OPTION_CHANGES_AFTER_FOCUS_EVENT: + "<html><body><select id='one'>" + + ' <option>{"color": "rgb(255, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' + + ' <option selected="true">{"end": "true"}</option>' + + "</select></body><scr" + + "ipt>" + + " var select = document.getElementById('one');" + + " select.addEventListener('focus', () => select.style.color = 'red');" + + "</script></html>", + + SELECT_COLOR_OF_OPTION_CHANGES_AFTER_TRANSITIONEND: + "<html><head><style>" + + " select { transition: all .1s; }" + + " select:focus { background-color: orange; }" + + "</style></head><body><select id='one'>" + + ' <option>{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' + + ' <option selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + SELECT_TEXTSHADOW_OF_OPTION_CHANGES_AFTER_TRANSITIONEND: + "<html><head><style>" + + " select { transition: all .1s; }" + + " select:focus { text-shadow: 0 0 0 #303030; }" + + " option { color: red; /* It gets the default otherwise, which is fine but we don't have a good way to test for */ }" + + "</style></head><body><select id='one'>" + + ' <option>{"color": "rgb(255, 0, 0)", "backgroundColor": "-moz-Combobox", "textShadow": "rgb(48, 48, 48) 0px 0px 0px"}</option>' + + ' <option selected="true">{"end": "true"}</option>' + + "</select></body></html>", + + 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>", + + SELECT_LONG_WITH_TRANSITION: + "<html><head><style>" + + " select { transition: all .2s linear; }" + + " select:focus { color: purple; }" + + "</style></head><body><select id='one'>" + + (function () { + let rv = ""; + for (let i = 0; i < 75; i++) { + rv += + ' <option>{"color": "rgb(128, 0, 128)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>'; + } + rv += + ' <option selected="true">{"end": "true"}</option>' + + "</select></body></html>"; + return rv; + })(), + + 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>{"color": "rgb(0, 0, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option> + <option>{"color": "rgb(0, 0, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option> + <option class="redColor">{"color": "rgb(255, 0, 0)", "backgroundColor": "-moz-Combobox"}</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> +`, + + SELECT_FONT_INHERITS_TO_OPTION: ` + <html><head><style> + select { font-family: monospace } + </style></head><body><select id='one'> + <option>One</option> + <option style="font-family: sans-serif">Two</option> + </select></body></html> +`, + + SELECT_SCROLLBAR_PROPS: ` + <html><head><style> + select { scrollbar-width: thin; scrollbar-color: red blue } + </style></head><body><select id='one'> + <option>One</option> + <option style="font-family: sans-serif">Two</option> + </select></body></html> +`, + DEFAULT_DARKMODE: ` + <html><body><select id='one'> + <option>{"unstyled": "true"}</option> + <option>{"unstyled": "true"}</option> + <option selected="true">{"end": "true"}</option> + </select></body></html> +`, + + DEFAULT_DARKMODE_DARK: ` + <meta name=color-scheme content=dark> + <select id='one'> + <option>{"color": "MenuText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option> + <option>{"color": "MenuText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option> + <option selected="true">{"end": "true"}</option> + </select> +`, + + SPLIT_FG_BG_OPTION_DARKMODE: ` + <html><head><style> + select { background-color: #fff; } + option { color: #2b2b2b; } + </style></head><body><select id='one'> + <option>{"color": "rgb(43, 43, 43)", "backgroundColor": "rgb(255, 255, 255)"}</option> + <option>{"color": "rgb(43, 43, 43)", "backgroundColor": "rgb(255, 255, 255)"}</option> + <option selected="true">{"end": "true"}</option> + </select></body></html> +`, + + IDENTICAL_BG_DIFF_FG_OPTION_DARKMODE: ` + <html><head><style> + select { background-color: #fff; } + option { color: #2b2b2b; background-color: #fff; } + </style></head><body><select id='one'> + <option>{"colorScheme": "light", "color": "rgb(43, 43, 43)", "backgroundColor": "rgb(255, 255, 255)"}</option> + <option>{"colorScheme": "light", "color": "rgb(43, 43, 43)", "backgroundColor": "rgb(255, 255, 255)"}</option> + <option selected="true">{"end": "true"}</option> + </select></body></html> +`, +}; + +function rgbaToString(parsedColor) { + let { r, g, b, a } = parsedColor; + if (a == 1) { + return `rgb(${r}, ${g}, ${b})`; + } + return `rgba(${r}, ${g}, ${b}, ${a})`; +} + +function testOptionColors(test, index, item, menulist) { + // The label contains a JSON string of the expected colors for + // `color` and `background-color`. + let expected = JSON.parse(item.label); + + // Press Down to move the selected item to the next item in the + // list and check the colors of this item when it's not selected. + EventUtils.synthesizeKey("KEY_ArrowDown"); + + if (expected.end) { + return; + } + + if (expected.unstyled) { + ok( + !item.hasAttribute("customoptionstyling"), + `${test}: Item ${index} should not have any custom option styling: ${item.outerHTML}` + ); + } else { + is( + getComputedStyle(item).color, + expected.color, + `${test}: Item ${index} has correct foreground color` + ); + is( + getComputedStyle(item).backgroundColor, + expected.backgroundColor, + `${test}: Item ${index} has correct background color` + ); + if (expected.textShadow) { + is( + getComputedStyle(item).textShadow, + expected.textShadow, + `${test}: Item ${index} has correct text-shadow color` + ); + } + } +} + +function computeLabels(tab) { + return SpecialPowers.spawn(tab.linkedBrowser, [], function () { + function _rgbaToString(parsedColor) { + let { r, g, b, a } = parsedColor; + if (a == 1) { + return `rgb(${r}, ${g}, ${b})`; + } + return `rgba(${r}, ${g}, ${b}, ${a})`; + } + function computeColors(expected) { + let any = false; + for (let color of Object.keys(expected)) { + if ( + color != "colorScheme" && + color.toLowerCase().includes("color") && + !expected[color].startsWith("rgb") + ) { + any = true; + expected[color] = _rgbaToString( + InspectorUtils.colorToRGBA(expected[color], content.document) + ); + } + } + return any; + } + for (let option of content.document.querySelectorAll("option,optgroup")) { + if (!option.label) { + continue; + } + let expected; + try { + expected = JSON.parse(option.label); + } catch (ex) { + continue; + } + if (computeColors(expected)) { + option.label = JSON.stringify(expected); + } + } + }); +} + +async function openSelectPopup(select) { + const pageUrl = "data:text/html," + escape(select); + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl); + + await computeLabels(tab); + + let popupShownPromise = BrowserTestUtils.waitForSelectPopupShown(window); + await BrowserTestUtils.synthesizeMouseAtCenter( + "#one", + { type: "mousedown" }, + gBrowser.selectedBrowser + ); + let selectPopup = await popupShownPromise; + let menulist = selectPopup.parentNode; + return { tab, menulist, selectPopup }; +} + +async function testSelectColors(selectID, itemCount, options) { + let select = gSelects[selectID]; + let { tab, menulist, selectPopup } = await openSelectPopup(select); + if (options.unstyled) { + ok( + !selectPopup.hasAttribute("customoptionstyling"), + `Shouldn't have custom option styling for ${selectID}` + ); + } + let arrowSB = selectPopup.shadowRoot.querySelector( + ".menupopup-arrowscrollbox" + ); + if (options.waitForComputedStyle) { + let property = options.waitForComputedStyle.property; + let expectedValue = options.waitForComputedStyle.value; + await TestUtils.waitForCondition(() => { + let node = ["background-image", "background-color"].includes(property) + ? arrowSB + : selectPopup; + let value = getComputedStyle(node).getPropertyValue(property); + info(`<${node.localName}> has ${property}: ${value}`); + return value == expectedValue; + }, `${selectID} - Waiting for <select> to have ${property}: ${expectedValue}`); + } + + is(selectPopup.parentNode.itemCount, itemCount, "Correct number of items"); + let child = selectPopup.firstElementChild; + let idx = 1; + + if (typeof options.skipSelectColorTest != "object") { + let skip = !!options.skipSelectColorTest; + options.skipSelectColorTest = { + color: skip, + background: skip, + }; + } + if (!options.skipSelectColorTest.color) { + is( + getComputedStyle(arrowSB).color, + options.selectColor, + selectID + " popup has expected foreground color" + ); + } + + if (options.selectTextShadow) { + is( + getComputedStyle(selectPopup).textShadow, + options.selectTextShadow, + selectID + " popup has expected text-shadow color" + ); + } + + if (!options.skipSelectColorTest.background) { + // Combine the select popup's backgroundColor and the + // backgroundImage color to get the color that is seen by + // the user. + let base = getComputedStyle(arrowSB).backgroundColor; + if (base == "rgba(0, 0, 0, 0)") { + base = getComputedStyle(selectPopup).backgroundColor; + } + info("Parsing background color: " + base); + 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(arrowSB).backgroundImage; + if (topCoat == "none") { + is( + `rgb(${bR}, ${bG}, ${bB})`, + options.selectBgColor, + selectID + " popup has expected background color (top coat)" + ); + } 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, + selectID + " popup has expected background color (no top coat)" + ); + } + } + + ok(!child.selected, "The first child should not be selected"); + while (child) { + testOptionColors(selectID, idx, child, menulist); + idx++; + child = child.nextElementSibling; + } + + if (!options.leaveOpen) { + await hideSelectPopup("escape"); + BrowserTestUtils.removeTab(tab); + } +} + +// System colors may be different in content pages and chrome pages. +let kDefaultSelectStyles = {}; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["dom.forms.select.customstyling", true]], + }); + kDefaultSelectStyles = await BrowserTestUtils.withNewTab( + `data:text/html,<select>`, + function (browser) { + return SpecialPowers.spawn(browser, [], function () { + let cs = content.getComputedStyle( + content.document.querySelector("select") + ); + return { + backgroundColor: cs.backgroundColor, + }; + }); + } + ); +}); + +// This test checks when a <select> element has styles applied to <option>s within it. +add_task(async function test_colors_applied_to_popup_items() { + await testSelectColors("PAGECONTENT_COLORS", 7, { + skipSelectColorTest: true, + }); +}); + +// This test checks when a <select> element has styles applied to itself. +add_task(async function test_colors_applied_to_popup() { + let options = { + selectColor: "rgb(255, 255, 255)", + selectBgColor: "rgb(126, 58, 58)", + }; + await testSelectColors("PAGECONTENT_COLORS_ON_SELECT", 4, options); +}); + +// This test checks when a <select> element has a transparent background applied to itself. +add_task(async function test_transparent_applied_to_popup() { + let options = { + unstyled: true, + skipSelectColorTest: true, + }; + await testSelectColors("TRANSPARENT_SELECT", 2, options); +}); + +// 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(async function test_options_inverted_from_select_background() { + // The popup has a black background and white text, but the + // options inside of it have flipped the colors. + let options = { + selectColor: "rgb(255, 255, 255)", + selectBgColor: "rgb(0, 0, 0)", + }; + await testSelectColors( + "OPTION_COLOR_EQUAL_TO_UABACKGROUND_COLOR_SELECT", + 2, + options + ); +}); + +// This test checks when a <select> element has a background set using !important, +// which was affecting how we calculated the user-agent styling. +add_task(async function test_select_background_using_important() { + await testSelectColors("GENERIC_OPTION_STYLED_AS_IMPORTANT", 2, { + skipSelectColorTest: true, + }); +}); + +// 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(async function test_translucent_select_becomes_opaque() { + // 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, 255, 255)", + }; + await 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(async 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)", + }; + await testSelectColors( + "TRANSLUCENT_SELECT_APPLIES_ON_BASE_COLOR", + 2, + options + ); +}); + +add_task(async function test_disabled_optgroup_and_options() { + await testSelectColors("DISABLED_OPTGROUP_AND_OPTIONS", 17, { + skipSelectColorTest: true, + }); +}); + +add_task(async function test_disabled_optgroup_and_options() { + let options = { + selectColor: "rgb(0, 0, 0)", + selectBgColor: "rgb(255, 165, 0)", + }; + + await testSelectColors("SELECT_CHANGES_COLOR_ON_FOCUS", 2, options); +}); + +add_task(async function test_bgcolor_on_select_color_on_options() { + let options = { + selectColor: "rgb(0, 0, 0)", + selectBgColor: "rgb(0, 0, 0)", + }; + + await testSelectColors( + "SELECT_BGCOLOR_ON_SELECT_COLOR_ON_OPTIONS", + 2, + options + ); +}); + +add_task( + async function test_style_of_options_is_dependent_on_focus_of_select() { + let options = { + selectColor: "rgb(0, 0, 0)", + selectBgColor: "rgb(58, 150, 221)", + }; + + await testSelectColors( + "SELECT_STYLE_OF_OPTION_IS_BASED_ON_FOCUS_OF_SELECT", + 2, + options + ); + } +); + +add_task( + async function test_style_of_options_is_dependent_on_focus_of_select_after_event() { + let options = { + skipSelectColorTest: true, + waitForComputedStyle: { + property: "--panel-color", + value: "rgb(255, 0, 0)", + }, + }; + await testSelectColors( + "SELECT_STYLE_OF_OPTION_CHANGES_AFTER_FOCUS_EVENT", + 2, + options + ); + } +); + +add_task(async function test_color_of_options_is_dependent_on_transitionend() { + let options = { + selectColor: "rgb(0, 0, 0)", + selectBgColor: "rgb(255, 165, 0)", + waitForComputedStyle: { + property: "background-image", + value: "linear-gradient(rgb(255, 165, 0), rgb(255, 165, 0))", + }, + }; + + await testSelectColors( + "SELECT_COLOR_OF_OPTION_CHANGES_AFTER_TRANSITIONEND", + 2, + options + ); +}); + +add_task( + async function test_textshadow_of_options_is_dependent_on_transitionend() { + let options = { + skipSelectColorTest: true, + waitForComputedStyle: { + property: "text-shadow", + value: "rgb(48, 48, 48) 0px 0px 0px", + }, + }; + + await testSelectColors( + "SELECT_TEXTSHADOW_OF_OPTION_CHANGES_AFTER_TRANSITIONEND", + 2, + options + ); + } +); + +add_task(async function test_transparent_color_with_text_shadow() { + let options = { + selectColor: "rgba(0, 0, 0, 0)", + selectTextShadow: "rgb(48, 48, 48) 0px 0px 0px", + selectBgColor: kDefaultSelectStyles.backgroundColor, + }; + + await testSelectColors( + "SELECT_TRANSPARENT_COLOR_WITH_TEXT_SHADOW", + 2, + options + ); +}); + +add_task( + async function test_select_with_transition_doesnt_lose_scroll_position() { + let options = { + selectColor: "rgb(128, 0, 128)", + selectBgColor: kDefaultSelectStyles.backgroundColor, + waitForComputedStyle: { + property: "--panel-color", + value: "rgb(128, 0, 128)", + }, + leaveOpen: true, + }; + + await testSelectColors("SELECT_LONG_WITH_TRANSITION", 76, options); + + let selectPopup = document.getElementById( + "ContentSelectDropdown" + ).menupopup; + let scrollBox = selectPopup.scrollBox; + is( + scrollBox.scrollTop, + scrollBox.scrollTopMax, + "The popup should be scrolled to the bottom of the list (where the selected item is)" + ); + + await hideSelectPopup("escape"); + 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)", + selectTextShadow: "rgb(0, 0, 255) 1px 1px 2px", + selectBgColor: kDefaultSelectStyles.backgroundColor, + leaveOpen: true, + }; + + await testSelectColors( + "SELECT_INHERITED_COLORS_ON_OPTIONS_DONT_GET_UNIQUE_RULES_IF_RULE_SET_ON_SELECT", + 6, + options + ); + + let stylesheetEl = document.getElementById( + "ContentSelectDropdownStylesheet" + ); + + let sheet = stylesheetEl.sheet; + /* Check that the rules are what we expect: There are three different option styles (even though there are 6 options, plus the select rules). */ + let expectedSelectors = [ + "#ContentSelectDropdown .ContentSelectDropdown-item-0", + "#ContentSelectDropdown .ContentSelectDropdown-item-1", + '#ContentSelectDropdown .ContentSelectDropdown-item-1:not([_moz-menuactive="true"])', + "#ContentSelectDropdown .ContentSelectDropdown-item-2", + '#ContentSelectDropdown .ContentSelectDropdown-item-2:not([_moz-menuactive="true"])', + '#ContentSelectDropdown > menupopup > :is(menuitem, menucaption):not([_moz-menuactive="true"])', + '#ContentSelectDropdown > menupopup > :is(menuitem, menucaption)[_moz-menuactive="true"]', + ].sort(); + + let actualSelectors = [...sheet.cssRules].map(r => r.selectorText).sort(); + is( + actualSelectors.length, + expectedSelectors.length, + "Should have the expected number of rules" + ); + for (let i = 0; i < expectedSelectors.length; ++i) { + is( + actualSelectors[i], + expectedSelectors[i], + `Selector ${i} should match` + ); + } + + await hideSelectPopup("escape"); + BrowserTestUtils.removeTab(gBrowser.selectedTab); + } +); + +add_task(async function test_select_font_inherits_to_option() { + let { tab, menulist, selectPopup } = await openSelectPopup( + gSelects.SELECT_FONT_INHERITS_TO_OPTION + ); + + let popupFont = getComputedStyle(selectPopup).fontFamily; + let items = menulist.querySelectorAll("menuitem"); + is(items.length, 2, "Should have two options"); + let firstItemFont = getComputedStyle(items[0]).fontFamily; + let secondItemFont = getComputedStyle(items[1]).fontFamily; + + is( + popupFont, + firstItemFont, + "First menuitem's font should be inherited from the select" + ); + isnot( + popupFont, + secondItemFont, + "Second menuitem's font should be the author specified one" + ); + + await hideSelectPopup("escape"); + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_scrollbar_props() { + let { tab, selectPopup } = await openSelectPopup( + gSelects.SELECT_SCROLLBAR_PROPS + ); + + let popupStyle = getComputedStyle(selectPopup); + is(popupStyle.getPropertyValue("--content-select-scrollbar-width"), "thin"); + is(popupStyle.scrollbarColor, "rgb(255, 0, 0) rgb(0, 0, 255)"); + + let scrollBoxStyle = getComputedStyle(selectPopup.scrollBox.scrollbox); + is(scrollBoxStyle.overflow, "auto", "Should be the scrollable box"); + is(scrollBoxStyle.scrollbarWidth, "thin"); + is(scrollBoxStyle.scrollbarColor, "rgb(255, 0, 0) rgb(0, 0, 255)"); + + await hideSelectPopup("escape"); + BrowserTestUtils.removeTab(tab); +}); + +if (AppConstants.platform == "win") { + add_task(async function test_darkmode() { + let lightSelectColor = rgbaToString( + InspectorUtils.colorToRGBA("MenuText", document) + ); + let lightSelectBgColor = rgbaToString( + InspectorUtils.colorToRGBA("Menu", document) + ); + + // Force dark mode: + let darkModeQuery = matchMedia("(prefers-color-scheme: dark)"); + let darkModeChange = BrowserTestUtils.waitForEvent(darkModeQuery, "change"); + await SpecialPowers.pushPrefEnv({ set: [["ui.systemUsesDarkTheme", 1]] }); + await darkModeChange; + + // Determine colours from the main context menu: + let darkSelectColor = rgbaToString( + InspectorUtils.colorToRGBA("MenuText", document) + ); + let darkSelectBgColor = rgbaToString( + InspectorUtils.colorToRGBA("Menu", document) + ); + + isnot(lightSelectColor, darkSelectColor); + isnot(lightSelectBgColor, darkSelectBgColor); + + let { tab } = await openSelectPopup(gSelects.DEFAULT_DARKMODE); + + await testSelectColors("DEFAULT_DARKMODE", 3, { + selectColor: lightSelectColor, + selectBgColor: lightSelectBgColor, + }); + + await hideSelectPopup("escape"); + + await testSelectColors("DEFAULT_DARKMODE_DARK", 3, { + selectColor: darkSelectColor, + selectBgColor: darkSelectBgColor, + }); + + await hideSelectPopup("escape"); + BrowserTestUtils.removeTab(tab); + + ({ tab } = await openSelectPopup( + gSelects.IDENTICAL_BG_DIFF_FG_OPTION_DARKMODE + )); + + // Custom styling on the options enforces using the select styling, too, + // even if it matched the UA style. They'll be overridden on individual + // options where necessary. + await testSelectColors("IDENTICAL_BG_DIFF_FG_OPTION_DARKMODE", 3, { + selectColor: "rgb(0, 0, 0)", + selectBgColor: "rgb(255, 255, 255)", + }); + + await hideSelectPopup("escape"); + BrowserTestUtils.removeTab(tab); + + ({ tab } = await openSelectPopup(gSelects.SPLIT_FG_BG_OPTION_DARKMODE)); + + // Like the previous case, but here the bg colour is defined on the + // select, and the fg colour on the option. The behaviour should be the + // same. + await testSelectColors("SPLIT_FG_BG_OPTION_DARKMODE", 3, { + selectColor: "rgb(0, 0, 0)", + selectBgColor: "rgb(255, 255, 255)", + }); + + await hideSelectPopup("escape"); + BrowserTestUtils.removeTab(tab); + }); +} |