mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 00:32:11 +00:00
Bug 1335483 - Compare the user agent value for the select option styling to determine if content has opted-in to custom styling. r=mconley
MozReview-Commit-ID: 8wqclzJr2si --HG-- extra : rebase_source : 38999f77e1c39ee465f8ddb783bae58138adfce2
This commit is contained in:
parent
f83e36ea12
commit
3de08e5fac
@ -81,11 +81,19 @@ const PAGECONTENT_TRANSLATED =
|
||||
"</div></body></html>";
|
||||
|
||||
const PAGECONTENT_COLORS =
|
||||
"<html><head><style>.blue { color: #fff; background-color: #00f; } .green { color: #800080; background-color: green; }</style>" +
|
||||
"<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": "transparent", "unstyled": "true"}</option>' +
|
||||
' <option value="Five" class="defaultColor">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
|
||||
' <option value="Six" class="defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
|
||||
"</select></body></html>";
|
||||
|
||||
function openSelectPopup(selectPopup, mode = "key", selector = "select", win = window) {
|
||||
@ -738,11 +746,6 @@ add_task(function* test_somehidden() {
|
||||
});
|
||||
|
||||
add_task(function* test_colors_applied_to_popup() {
|
||||
function inverseRGBString(rgbString) {
|
||||
let [, r, g, b] = rgbString.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
||||
return `rgb(${255 - r}, ${255 - g}, ${255 - b})`;
|
||||
}
|
||||
|
||||
const pageUrl = "data:text/html," + escape(PAGECONTENT_COLORS);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
|
||||
|
||||
@ -754,62 +757,39 @@ add_task(function* test_colors_applied_to_popup() {
|
||||
|
||||
// The label contains a JSON string of the expected colors for
|
||||
// `color` and `background-color`.
|
||||
is(selectPopup.parentNode.itemCount, 3, "Correct number of items");
|
||||
is(selectPopup.parentNode.itemCount, 6, "Correct number of items");
|
||||
let child = selectPopup.firstChild;
|
||||
let idx = 1;
|
||||
|
||||
ok(child.selected, "The first child should be selected");
|
||||
while (child) {
|
||||
let expectedColors = JSON.parse(child.label);
|
||||
let expected = JSON.parse(child.label);
|
||||
|
||||
// We need to use Canvas here to get the actual pixel color
|
||||
// because the computedStyle will only tell us the 'color' or
|
||||
// 'backgroundColor' of the element, but not what the displayed
|
||||
// color is due to composition of various CSS rules such as
|
||||
// 'filter' which is applied when elements have custom background
|
||||
// or foreground elements.
|
||||
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
canvas = document.documentElement.appendChild(canvas);
|
||||
let rect = child.getBoundingClientRect();
|
||||
canvas.setAttribute("width", rect.width);
|
||||
canvas.setAttribute("height", rect.height);
|
||||
canvas.mozOpaque = true;
|
||||
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawWindow(window, rect.x + rect.left, rect.y + rect.top, rect.width, rect.height, "#000", ctx.DRAWWINDOW_USE_WIDGET_LAYERS);
|
||||
let frame = ctx.getImageData(0, 0, rect.width, rect.height);
|
||||
|
||||
let pixels = frame.data.length / 4;
|
||||
// Assume the inverse backgroundColor is the color of the first pixel.
|
||||
let [inverseBgR, inverseBgG, inverseBgB] = frame.data;
|
||||
let inverseBackgroundColor = `rgb(${inverseBgR}, ${inverseBgG}, ${inverseBgB})`;
|
||||
// Use the next different pixel color as the foreground color, assuming
|
||||
// no anti-aliasing.
|
||||
let inverseColor = inverseBackgroundColor;
|
||||
for (let i = 0; i < pixels; i++) {
|
||||
if (inverseBgR != frame.data[i * 4 + 0] &&
|
||||
inverseBgG != frame.data[i * 4 + 1] &&
|
||||
inverseBgB != frame.data[i * 4 + 2]) {
|
||||
inverseColor = `rgb(${frame.data[i * 4 + 0]}, ${frame.data[i * 4 + 1]}, ${frame.data[i * 4 + 2]})`;
|
||||
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;
|
||||
}
|
||||
}
|
||||
// The canvas code above isn't getting the right colors for the pixels,
|
||||
// it always returns rgb(255,255,255).
|
||||
todo_is(inverseColor, inverseRGBString(getComputedStyle(child).color),
|
||||
"Item " + (idx) + " has correct inverse foreground color when selected");
|
||||
todo_is(inverseBackgroundColor, inverseRGBString(getComputedStyle(child).backgroundColor),
|
||||
"Item " + (idx) + " has correct inverse background color when selected");
|
||||
|
||||
canvas.remove();
|
||||
|
||||
// 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" });
|
||||
|
||||
is(getComputedStyle(child).color, expectedColors.color,
|
||||
"Item " + (idx) + " has correct foreground color");
|
||||
is(getComputedStyle(child).backgroundColor, expectedColors.backgroundColor,
|
||||
"Item " + (idx++) + " has correct background color");
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -471,7 +471,8 @@
|
||||
|
||||
let zoom = Services.prefs.getBoolPref("browser.zoom.full") ||
|
||||
this.isSyntheticDocument ? this._fullZoom : this._textZoom;
|
||||
this._selectParentHelper.populate(menulist, data.options, data.selectedIndex, zoom);
|
||||
this._selectParentHelper.populate(menulist, data.options, data.selectedIndex,
|
||||
zoom, data.uaBackgroundColor, data.uaColor);
|
||||
this._selectParentHelper.open(this, menulist, data.rect, data.isOpenedViaTouch);
|
||||
break;
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ this.SelectContentHelper = function(aElement, aOptions, aGlobal) {
|
||||
this.global = aGlobal;
|
||||
this.closedWithEnter = false;
|
||||
this.isOpenedViaTouch = aOptions.isOpenedViaTouch;
|
||||
this._uaBackgroundColor = null;
|
||||
this._uaColor = null;
|
||||
this.init();
|
||||
this.showDropDown();
|
||||
this._updateTimer = new DeferredTask(this._update.bind(this), 0);
|
||||
@ -91,7 +93,9 @@ this.SelectContentHelper.prototype = {
|
||||
options: this._buildOptionList(),
|
||||
selectedIndex: this.element.selectedIndex,
|
||||
direction: getComputedStyles(this.element).direction,
|
||||
isOpenedViaTouch: this.isOpenedViaTouch
|
||||
isOpenedViaTouch: this.isOpenedViaTouch,
|
||||
uaBackgroundColor: this.uaBackgroundColor,
|
||||
uaColor: this.uaColor,
|
||||
});
|
||||
gOpen = true;
|
||||
},
|
||||
@ -110,9 +114,37 @@ this.SelectContentHelper.prototype = {
|
||||
this.global.sendAsyncMessage("Forms:UpdateDropDown", {
|
||||
options: this._buildOptionList(),
|
||||
selectedIndex: this.element.selectedIndex,
|
||||
uaBackgroundColor: this.uaBackgroundColor,
|
||||
uaColor: this.uaColor,
|
||||
});
|
||||
},
|
||||
|
||||
// Determine user agent background-color and color.
|
||||
// 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;
|
||||
},
|
||||
|
||||
get uaBackgroundColor() {
|
||||
if (!this._uaBackgroundColor) {
|
||||
this._calculateUAColors();
|
||||
}
|
||||
return this._uaBackgroundColor;
|
||||
},
|
||||
|
||||
get uaColor() {
|
||||
if (!this._uaColor) {
|
||||
this._calculateUAColors();
|
||||
}
|
||||
return this._uaColor;
|
||||
},
|
||||
|
||||
dispatchMouseEvent(win, target, eventName) {
|
||||
let mouseEvent = new win.MouseEvent(eventName, {
|
||||
view: win,
|
||||
|
@ -25,12 +25,13 @@ var closedWithEnter = false;
|
||||
var selectRect;
|
||||
|
||||
this.SelectParentHelper = {
|
||||
populate(menulist, items, selectedIndex, zoom) {
|
||||
populate(menulist, items, selectedIndex, zoom, uaBackgroundColor, uaColor) {
|
||||
// Clear the current contents of the popup
|
||||
menulist.menupopup.textContent = "";
|
||||
currentZoom = zoom;
|
||||
currentMenulist = menulist;
|
||||
populateChildren(menulist, items, selectedIndex, zoom);
|
||||
populateChildren(menulist, items, selectedIndex, zoom,
|
||||
uaBackgroundColor, uaColor);
|
||||
},
|
||||
|
||||
open(browser, menulist, rect, isOpenedViaTouch) {
|
||||
@ -139,7 +140,10 @@ this.SelectParentHelper = {
|
||||
|
||||
let options = msg.data.options;
|
||||
let selectedIndex = msg.data.selectedIndex;
|
||||
this.populate(currentMenulist, options, selectedIndex, currentZoom);
|
||||
let uaBackgroundColor = msg.data.uaBackgroundColor;
|
||||
let uaColor = msg.data.uaColor;
|
||||
this.populate(currentMenulist, options, selectedIndex,
|
||||
currentZoom, uaBackgroundColor, uaColor);
|
||||
}
|
||||
},
|
||||
|
||||
@ -168,15 +172,15 @@ this.SelectParentHelper = {
|
||||
};
|
||||
|
||||
function populateChildren(menulist, options, selectedIndex, zoom,
|
||||
uaBackgroundColor, uaColor,
|
||||
parentElement = null, isGroupDisabled = false,
|
||||
adjustedTextSize = -1, addSearch = true) {
|
||||
let element = menulist.menupopup;
|
||||
let win = element.ownerGlobal;
|
||||
|
||||
// -1 just means we haven't calculated it yet. When we recurse through this function
|
||||
// we will pass in adjustedTextSize to save on recalculations.
|
||||
if (adjustedTextSize == -1) {
|
||||
let win = element.ownerGlobal;
|
||||
|
||||
// Grab the computed text size and multiply it by the remote browser's fullZoom to ensure
|
||||
// the popup's text size is matched with the content's. We can't just apply a CSS transform
|
||||
// here as the popup's preferred size is calculated pre-transform.
|
||||
@ -197,16 +201,21 @@ function populateChildren(menulist, options, selectedIndex, zoom,
|
||||
item.hiddenByContent = item.hidden;
|
||||
item.setAttribute("tooltiptext", option.tooltip);
|
||||
|
||||
if (option.backgroundColor && option.backgroundColor != "transparent") {
|
||||
let customOptionStylingUsed = false;
|
||||
if (option.backgroundColor &&
|
||||
option.backgroundColor != "transparent" &&
|
||||
option.backgroundColor != uaBackgroundColor) {
|
||||
item.style.backgroundColor = option.backgroundColor;
|
||||
customOptionStylingUsed = true;
|
||||
}
|
||||
|
||||
if (option.color && option.color != "transparent") {
|
||||
if (option.color &&
|
||||
option.color != uaColor) {
|
||||
item.style.color = option.color;
|
||||
customOptionStylingUsed = true;
|
||||
}
|
||||
|
||||
if ((option.backgroundColor && option.backgroundColor != "transparent") ||
|
||||
(option.color && option.color != "rgb(0, 0, 0)")) {
|
||||
if (customOptionStylingUsed) {
|
||||
item.setAttribute("customoptionstyling", "true");
|
||||
} else {
|
||||
item.removeAttribute("customoptionstyling");
|
||||
@ -222,6 +231,7 @@ function populateChildren(menulist, options, selectedIndex, zoom,
|
||||
|
||||
if (isOptGroup) {
|
||||
populateChildren(menulist, option.children, selectedIndex, zoom,
|
||||
uaBackgroundColor, uaColor,
|
||||
item, isDisabled, adjustedTextSize, false);
|
||||
} else {
|
||||
if (option.index == selectedIndex) {
|
||||
|
@ -32,6 +32,10 @@ menuitem[_moz-menuactive="true"] {
|
||||
background-color: -moz-menuhover;
|
||||
}
|
||||
|
||||
menuitem[customoptionstyling="true"] {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
menuitem[_moz-menuactive="true"][customoptionstyling="true"] {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user