From 7d9b3b83c6edcea3cef65fc927345a3b6dfcf6e9 Mon Sep 17 00:00:00 2001 From: Jared Wein Date: Sat, 4 Feb 2017 00:33:13 -0500 Subject: [PATCH] Bug 1336301 - Styles that are applied directly to the select element should be forwarded to the popup. r=mconley MozReview-Commit-ID: 29DjcUUqEkx --HG-- extra : rebase_source : 97970d2e8f1b6527caa2767b06ddf110a2478195 --- .../test/general/browser_selectpopup.js | 77 ++++++++++++++++++- toolkit/content/widgets/browser.xml | 4 +- toolkit/content/widgets/remote-browser.xml | 4 +- toolkit/modules/SelectContentHelper.jsm | 55 ++++++++++--- toolkit/modules/SelectParentHelper.jsm | 43 +++++++---- toolkit/themes/osx/global/menu.css | 1 + 6 files changed, 156 insertions(+), 28 deletions(-) diff --git a/browser/base/content/test/general/browser_selectpopup.js b/browser/base/content/test/general/browser_selectpopup.js index 6d5f98501b0c..c6c57f61418e 100644 --- a/browser/base/content/test/general/browser_selectpopup.js +++ b/browser/base/content/test/general/browser_selectpopup.js @@ -97,6 +97,17 @@ const PAGECONTENT_COLORS = ' ' + ""; +const PAGECONTENT_COLORS_ON_SELECT = + "" + + ""; + function openSelectPopup(selectPopup, mode = "key", selector = "select", win = window) { let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown"); @@ -746,7 +757,8 @@ add_task(function* test_somehidden() { yield BrowserTestUtils.removeTab(tab); }); -add_task(function* test_colors_applied_to_popup() { +// This test checks when a element has styles applied to itself. +add_task(function* test_colors_applied_to_popup() { + const pageUrl = "data:text/html," + escape(PAGECONTENT_COLORS_ON_SELECT); + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl); + + let menulist = document.getElementById("ContentSelectDropdown"); + let selectPopup = menulist.menupopup; + + let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown"); + yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser); + yield popupShownPromise; + + // The label contains a JSON string of the expected colors for + // `color` and `background-color`. + is(selectPopup.parentNode.itemCount, 4, "Correct number of items"); + let child = selectPopup.firstChild; + let idx = 1; + + is(getComputedStyle(selectPopup).color, "rgb(255, 255, 255)", + "popup has expected foreground color"); + is(getComputedStyle(selectPopup).backgroundColor, "rgb(126, 58, 58)", + "popup has expected background color"); + + ok(!child.selected, "The first child should not be selected"); + while (child) { + let expected = JSON.parse(child.label); + + for (let color of Object.keys(expected)) { + if (color.toLowerCase().includes("color") && + !expected[color].startsWith("rgb")) { + // Need to convert system color to RGB color. + let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea"); + textarea.style.color = expected[color]; + expected[color] = getComputedStyle(textarea).color; + } + } + + // 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", { code: "ArrowDown" }); + + if (expected.end) { + break; + } + + if (expected.unstyled) { + ok(!child.hasAttribute("customoptionstyling"), + `Item ${idx} should not have any custom option styling`); + } else { + is(getComputedStyle(child).color, expected.color, + "Item " + (idx) + " has correct foreground color"); + is(getComputedStyle(child).backgroundColor, expected.backgroundColor, + "Item " + (idx) + " has correct background color"); + } + + idx++; + child = child.nextSibling; + } + + yield hideSelectPopup(selectPopup, "escape"); + yield BrowserTestUtils.removeTab(tab); +}); diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml index c998e1bf112e..537c6f64ed58 100644 --- a/toolkit/content/widgets/browser.xml +++ b/toolkit/content/widgets/browser.xml @@ -1081,7 +1081,9 @@ 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.uaBackgroundColor, data.uaColor, + data.uaSelectBackgroundColor, data.uaSelectColor, + data.selectBackgroundColor, data.selectColor); this._selectParentHelper.open(this, menulist, data.rect, data.isOpenedViaTouch); break; } diff --git a/toolkit/content/widgets/remote-browser.xml b/toolkit/content/widgets/remote-browser.xml index d9b70b69d519..0dde870a5278 100644 --- a/toolkit/content/widgets/remote-browser.xml +++ b/toolkit/content/widgets/remote-browser.xml @@ -472,7 +472,9 @@ 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); + zoom, data.uaBackgroundColor, data.uaColor, + data.uaSelectBackgroundColor, data.uaSelectColor, + data.selectBackgroundColor, data.selectColor); this._selectParentHelper.open(this, menulist, data.rect, data.isOpenedViaTouch); break; } diff --git a/toolkit/modules/SelectContentHelper.jsm b/toolkit/modules/SelectContentHelper.jsm index 45205eadbfe9..190338021870 100644 --- a/toolkit/modules/SelectContentHelper.jsm +++ b/toolkit/modules/SelectContentHelper.jsm @@ -36,8 +36,12 @@ this.SelectContentHelper = function(aElement, aOptions, aGlobal) { this.global = aGlobal; this.closedWithEnter = false; this.isOpenedViaTouch = aOptions.isOpenedViaTouch; + this._selectBackgroundColor = null; + this._selectColor = null; this._uaBackgroundColor = null; this._uaColor = null; + this._uaSelectBackgroundColor = null; + this._uaSelectColor = null; this.init(); this.showDropDown(); this._updateTimer = new DeferredTask(this._update.bind(this), 0); @@ -88,14 +92,21 @@ this.SelectContentHelper.prototype = { showDropDown() { this.element.openInParentProcess = true; let rect = this._getBoundingContentRect(); + let computedStyles = getComputedStyles(this.element); + this._selectBackgroundColor = computedStyles.backgroundColor; + this._selectColor = computedStyles.color; this.global.sendAsyncMessage("Forms:ShowDropDown", { - rect, - options: this._buildOptionList(), - selectedIndex: this.element.selectedIndex, - direction: getComputedStyles(this.element).direction, + direction: computedStyles.direction, isOpenedViaTouch: this.isOpenedViaTouch, + options: this._buildOptionList(), + rect, + selectedIndex: this.element.selectedIndex, + selectBackgroundColor: this._selectBackgroundColor, + selectColor: this._selectColor, uaBackgroundColor: this.uaBackgroundColor, uaColor: this.uaColor, + uaSelectBackgroundColor: this.uaSelectBackgroundColor, + uaSelectColor: this.uaSelectColor }); gOpen = true; }, @@ -114,8 +125,12 @@ this.SelectContentHelper.prototype = { this.global.sendAsyncMessage("Forms:UpdateDropDown", { options: this._buildOptionList(), selectedIndex: this.element.selectedIndex, + selectBackgroundColor: this._selectBackgroundColor, + selectColor: this._selectColor, uaBackgroundColor: this.uaBackgroundColor, uaColor: this.uaColor, + uaSelectBackgroundColor: this.uaSelectBackgroundColor, + uaSelectColor: this.uaSelectColor }); }, @@ -123,12 +138,18 @@ this.SelectContentHelper.prototype = { // This is used to skip applying the custom color if it matches // the user agent values. _calculateUAColors() { - let dummy = this.element.ownerDocument.createElement("option"); - dummy.style.color = "-moz-comboboxtext"; - dummy.style.backgroundColor = "-moz-combobox"; - let dummyCS = this.element.ownerGlobal.getComputedStyle(dummy); - this._uaBackgroundColor = dummyCS.backgroundColor; - this._uaColor = dummyCS.color; + let dummyOption = this.element.ownerDocument.createElement("option"); + dummyOption.style.color = "-moz-comboboxtext"; + dummyOption.style.backgroundColor = "-moz-combobox"; + let optionCS = this.element.ownerGlobal.getComputedStyle(dummyOption); + this._uaBackgroundColor = optionCS.backgroundColor; + this._uaColor = optionCS.color; + let dummySelect = this.element.ownerDocument.createElement("select"); + dummySelect.style.color = "-moz-fieldtext"; + dummySelect.style.backgroundColor = "-moz-field"; + let selectCS = this.element.ownerGlobal.getComputedStyle(dummySelect); + this._uaSelectBackgroundColor = selectCS.backgroundColor; + this._uaSelectColor = selectCS.color; }, get uaBackgroundColor() { @@ -145,6 +166,20 @@ this.SelectContentHelper.prototype = { return this._uaColor; }, + get uaSelectBackgroundColor() { + if (!this._selectBackgroundColor) { + this._calculateUAColors(); + } + return this._uaSelectBackgroundColor; + }, + + get uaSelectColor() { + if (!this._selectBackgroundColor) { + this._calculateUAColors(); + } + return this._uaSelectColor; + }, + dispatchMouseEvent(win, target, eventName) { let mouseEvent = new win.MouseEvent(eventName, { view: win, diff --git a/toolkit/modules/SelectParentHelper.jsm b/toolkit/modules/SelectParentHelper.jsm index eaa89c0b874c..908ff733ae02 100644 --- a/toolkit/modules/SelectParentHelper.jsm +++ b/toolkit/modules/SelectParentHelper.jsm @@ -25,7 +25,8 @@ var closedWithEnter = false; var selectRect; this.SelectParentHelper = { - populate(menulist, items, selectedIndex, zoom, uaBackgroundColor, uaColor) { + populate(menulist, items, selectedIndex, zoom, uaBackgroundColor, uaColor, + uaSelectBackgroundColor, uaSelectColor, selectBackgroundColor, selectColor) { // Clear the current contents of the popup menulist.menupopup.textContent = ""; let stylesheet = menulist.querySelector("#ContentSelectDropdownScopedStylesheet"); @@ -33,10 +34,29 @@ this.SelectParentHelper = { stylesheet.remove(); } + let doc = menulist.ownerDocument; + stylesheet = doc.createElementNS("http://www.w3.org/1999/xhtml", "style"); + stylesheet.setAttribute("id", "ContentSelectDropdownScopedStylesheet"); + stylesheet.scoped = true; + stylesheet.hidden = true; + stylesheet = menulist.appendChild(stylesheet); + + let sheet = stylesheet.sheet; + if (selectBackgroundColor != uaSelectBackgroundColor || + selectColor != uaSelectColor) { + sheet.insertRule(`menupopup { + background-color: ${selectBackgroundColor}; + color: ${selectColor}; + }`, 0); + menulist.menupopup.setAttribute("customoptionstyling", "true"); + } else { + menulist.menupopup.removeAttribute("customoptionstyling"); + } + currentZoom = zoom; currentMenulist = menulist; populateChildren(menulist, items, selectedIndex, zoom, - uaBackgroundColor, uaColor); + uaBackgroundColor, uaColor, sheet); }, open(browser, menulist, rect, isOpenedViaTouch) { @@ -147,8 +167,11 @@ this.SelectParentHelper = { let selectedIndex = msg.data.selectedIndex; let uaBackgroundColor = msg.data.uaBackgroundColor; let uaColor = msg.data.uaColor; + let selectBackgroundColor = msg.data.selectBackgroundColor; + let selectColor = msg.data.selectColor; this.populate(currentMenulist, options, selectedIndex, - currentZoom, uaBackgroundColor, uaColor); + currentZoom, uaBackgroundColor, uaColor, + selectBackgroundColor, selectColor); } }, @@ -177,20 +200,11 @@ this.SelectParentHelper = { }; function populateChildren(menulist, options, selectedIndex, zoom, - uaBackgroundColor, uaColor, + uaBackgroundColor, uaColor, sheet, parentElement = null, isGroupDisabled = false, adjustedTextSize = -1, addSearch = true, nthChildIndex = 1) { let element = menulist.menupopup; let win = element.ownerGlobal; - let scopedStyleSheet = menulist.querySelector("#ContentSelectDropdownScopedStylesheet"); - if (!scopedStyleSheet) { - let doc = element.ownerDocument; - scopedStyleSheet = doc.createElementNS("http://www.w3.org/1999/xhtml", "style"); - scopedStyleSheet.setAttribute("id", "ContentSelectDropdownScopedStylesheet"); - scopedStyleSheet.scoped = true; - scopedStyleSheet.hidden = true; - scopedStyleSheet = menulist.appendChild(scopedStyleSheet); - } // -1 just means we haven't calculated it yet. When we recurse through this function // we will pass in adjustedTextSize to save on recalculations. @@ -228,7 +242,6 @@ function populateChildren(menulist, options, selectedIndex, zoom, } if (ruleBody) { - let sheet = scopedStyleSheet.sheet; sheet.insertRule(`${item.localName}:nth-child(${nthChildIndex}):not([_moz-menuactive="true"]) { ${ruleBody} }`, 0); @@ -250,7 +263,7 @@ function populateChildren(menulist, options, selectedIndex, zoom, if (isOptGroup) { nthChildIndex = populateChildren(menulist, option.children, selectedIndex, zoom, - uaBackgroundColor, uaColor, + uaBackgroundColor, uaColor, sheet, item, isDisabled, adjustedTextSize, false); } else { if (option.index == selectedIndex) { diff --git a/toolkit/themes/osx/global/menu.css b/toolkit/themes/osx/global/menu.css index 3eb84c86efa3..73ec26424895 100644 --- a/toolkit/themes/osx/global/menu.css +++ b/toolkit/themes/osx/global/menu.css @@ -135,6 +135,7 @@ menuitem[_moz-menuactive="true"] { background-color: Highlight; } +menupopup[customoptionstyling="true"], menuitem[customoptionstyling="true"] { -moz-appearance: none; padding-top: 0;