Bug 455164 - Proof-of-concept of applying transform to the popup. This attempts to fix attachment 338465 but the popup is clipped on the parts that extend outside of the normal menupopup rectangle.
MozReview-Commit-ID: 7sWdHEXYDoD
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1081,20 +1081,23 @@
case "Forms:ShowDropDown": {
if (!this._selectParentHelper) {
this._selectParentHelper =
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.selectTextShadow);
+ let sheet =
+ this._selectParentHelper.setupMenuList(menulist, data.uaSelectBackgroundColor,
+ data.uaSelectColor, data.selectBackgroundColor,
+ data.selectColor, data.selectTextShadow,
+ data.selectTransform);
+ this._selectParentHelper.populateChildren(menulist, data.options, data.selectedIndex,
+ zoom, data.uaBackgroundColor, data.uaColor, sheet);
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
@@ -470,20 +470,23 @@
Cu.import("resource://gre/modules/SelectParentHelper.jsm", {}).SelectParentHelper;
}
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.selectTextShadow);
+ let sheet =
+ this._selectParentHelper.setupMenuList(menulist, data.uaSelectBackgroundColor,
+ data.uaSelectColor, data.selectBackgroundColor,
+ data.selectColor, data.selectTextShadow,
+ data.selectTransform);
+ this._selectParentHelper.populateChildren(menulist, data.options, data.selectedIndex,
+ zoom, data.uaBackgroundColor, data.uaColor, sheet);
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
@@ -96,26 +96,28 @@ 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;
+ this._selectTransform = computedStyles.transform;
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,
+ selectTransform: this._selectTransform,
uaBackgroundColor: this.uaBackgroundColor,
uaColor: this.uaColor,
uaSelectBackgroundColor: this.uaSelectBackgroundColor,
uaSelectColor: this.uaSelectColor
});
gOpen = true;
},
@@ -136,23 +138,25 @@ this.SelectContentHelper.prototype = {
// 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;
+ this._selectTransform = computedStyles.transform;
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,
+ selectTransform: this._selectTransform,
uaBackgroundColor: this.uaBackgroundColor,
uaColor: this.uaColor,
uaSelectBackgroundColor: this.uaSelectBackgroundColor,
uaSelectColor: this.uaSelectColor
});
},
// Determine user agent background-color and color.
--- a/toolkit/modules/SelectParentHelper.jsm
+++ b/toolkit/modules/SelectParentHelper.jsm
@@ -22,21 +22,22 @@ var currentBrowser = null;
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,
- selectTextShadow) {
+ setupMenuList(menulist, uaSelectBackgroundColor, uaSelectColor,
+ selectBackgroundColor, selectColor,
+ selectTextShadow, selectTransform) {
// Clear the current contents of the popup
menulist.menupopup.textContent = "";
+ currentMenulist = menulist;
let stylesheet = menulist.querySelector("#ContentSelectDropdownScopedStylesheet");
if (stylesheet) {
stylesheet.remove();
}
let doc = menulist.ownerDocument;
let sheet;
if (customStylingEnabled) {
@@ -69,29 +70,38 @@ this.SelectParentHelper = {
ruleBody += `color: ${selectColor};`;
}
if (customStylingEnabled &&
selectTextShadow != "none") {
ruleBody += `text-shadow: ${selectTextShadow};`;
}
+ if (customStylingEnabled &&
+ selectTransform != "none") {
+ // Set transform-origin to mimic the offset from the
+ // arrow-button on LTR environments.
+ ruleBody += "transform-origin: 16.65px -5px 0;";
+ // Need to set opacity to a value other than 1 to force
+ // transparent windows or else a black box will be drawn
+ // in the area that the popup has been transformed away from.
+ ruleBody += "opacity: .99";
+ ruleBody += `transform: ${selectTransform};`;
+ }
+
if (ruleBody) {
sheet.insertRule(`menupopup {
${ruleBody}
}`, 0);
menulist.menupopup.setAttribute("customoptionstyling", "true");
} else {
menulist.menupopup.removeAttribute("customoptionstyling");
}
- currentZoom = zoom;
- currentMenulist = menulist;
- populateChildren(menulist, items, selectedIndex, zoom,
- uaBackgroundColor, uaColor, sheet);
+ return sheet;
},
open(browser, menulist, rect, isOpenedViaTouch) {
menulist.hidden = false;
currentBrowser = browser;
closedWithEnter = false;
selectRect = rect;
this._registerListeners(browser, menulist.menupopup);
@@ -200,20 +210,24 @@ this.SelectParentHelper = {
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, selectTextShadow);
+ let selectTransform = msg.data.selectTransform;
+
+ let sheet =
+ this.setupMenuList(currentMenulist, uaSelectBackgroundColor,
+ uaSelectColor, selectBackgroundColor, selectColor,
+ selectTextShadow, selectTransform);
+ this.populateChildren(currentMenulist, options, selectedIndex, currentZoom,
+ uaBackgroundColor, uaColor, sheet);
// Restore scroll position to what it was prior to the update.
scrollBox.scrollTop = scrollTop;
}
},
_registerListeners(browser, popup) {
popup.addEventListener("command", this);
@@ -232,24 +246,24 @@ this.SelectParentHelper = {
popup.removeEventListener("mouseover", this);
popup.removeEventListener("mouseout", this);
browser.ownerGlobal.removeEventListener("mouseup", this, true);
browser.ownerGlobal.removeEventListener("keydown", this, true);
browser.ownerGlobal.removeEventListener("fullscreen", this, true);
browser.messageManager.removeMessageListener("Forms:UpdateDropDown", this);
},
-};
-function populateChildren(menulist, options, selectedIndex, zoom,
+ populateChildren(menulist, options, selectedIndex, zoom,
uaBackgroundColor, uaColor, sheet,
parentElement = null, isGroupDisabled = false,
adjustedTextSize = -1, addSearch = true, nthChildIndex = 1) {
let element = menulist.menupopup;
let win = element.ownerGlobal;
+ currentZoom = zoom;
// -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) {
// 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.
let textSize = win.getComputedStyle(element).getPropertyValue("font-size");
@@ -313,17 +327,17 @@ function populateChildren(menulist, opti
// A disabled optgroup disables all of its child options.
let isDisabled = isGroupDisabled || option.disabled;
if (isDisabled) {
item.setAttribute("disabled", "true");
}
if (isOptGroup) {
nthChildIndex =
- populateChildren(menulist, option.children, selectedIndex, zoom,
+ this.populateChildren(menulist, option.children, selectedIndex, zoom,
uaBackgroundColor, uaColor, sheet,
item, isDisabled, adjustedTextSize, false, nthChildIndex);
} else {
if (option.index == selectedIndex) {
// We expect the parent element of the popup to be a <xul:menulist> that
// has the popuponly attribute set to "true". This is necessary in order
// for a <xul:menupopup> to act like a proper <html:select> dropdown, as
// the <xul:menulist> does things like remember state and set the
@@ -394,16 +408,17 @@ function populateChildren(menulist, opti
event.preventDefault();
}, true);
element.insertBefore(searchbox, element.childNodes[0]);
}
return nthChildIndex;
}
+};
function onSearchInput() {
let searchObj = this;
// Get input from search field, set to all lower case for comparison
let input = searchObj.value.toLowerCase();
// Get all items in dropdown (could be options or optgroups)
let menupopup = searchObj.parentElement;