зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1262332, fix select popup and invalid form popup position when inside a transformed frame, r=felipe
This commit is contained in:
Родитель
405358d4b3
Коммит
bbc0cc12e6
|
@ -39,6 +39,14 @@ const PAGECONTENT_SMALL =
|
|||
" <option value='Six'>Six</option>" +
|
||||
"</select></body></html>";
|
||||
|
||||
const PAGECONTENT_TRANSLATED =
|
||||
"<html><body>" +
|
||||
"<div id='div'>" +
|
||||
"<iframe id='frame' width='320' height='295' style='border: none;'" +
|
||||
" src='data:text/html,<select id=select autofocus><option>he he he</option><option>boo boo</option><option>baz baz</option></select>'" +
|
||||
"</iframe>" +
|
||||
"</div></body></html>";
|
||||
|
||||
function openSelectPopup(selectPopup, withMouse, selector = "select")
|
||||
{
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
|
||||
|
@ -48,7 +56,7 @@ function openSelectPopup(selectPopup, withMouse, selector = "select")
|
|||
BrowserTestUtils.synthesizeMouseAtCenter(selector, { }, gBrowser.selectedBrowser)]);
|
||||
}
|
||||
|
||||
setTimeout(() => EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, code: "ArrowDown" }), 1500);
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, code: "ArrowDown" });
|
||||
return popupShownPromise;
|
||||
}
|
||||
|
||||
|
@ -202,3 +210,68 @@ add_task(function*() {
|
|||
|
||||
ok(true, "Popup hidden when tab is closed");
|
||||
});
|
||||
|
||||
// This test opens a select popup that is isn't a frame and has some translations applied.
|
||||
add_task(function*() {
|
||||
const pageUrl = "data:text/html," + escape(PAGECONTENT_TRANSLATED);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
|
||||
|
||||
let menulist = document.getElementById("ContentSelectDropdown");
|
||||
let selectPopup = menulist.menupopup;
|
||||
|
||||
// First, get the position of the select popup when no translations have been applied.
|
||||
yield openSelectPopup(selectPopup, false);
|
||||
|
||||
let rect = selectPopup.getBoundingClientRect();
|
||||
let expectedX = rect.left;
|
||||
let expectedY = rect.top;
|
||||
|
||||
yield hideSelectPopup(selectPopup);
|
||||
|
||||
// Iterate through a set of steps which each add more translation to the select's expected position.
|
||||
let steps = [
|
||||
[ "div", "transform: translateX(7px) translateY(13px);", 7, 13 ],
|
||||
[ "frame", "border-top: 5px solid green; border-left: 10px solid red; border-right: 35px solid blue;", 10, 5 ],
|
||||
[ "frame", "border: none; padding-left: 6px; padding-right: 12px; padding-top: 2px;", -4, -3 ],
|
||||
[ "select", "margin: 9px; transform: translateY(-3px);", 9, 6 ],
|
||||
];
|
||||
|
||||
for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
|
||||
let step = steps[stepIndex];
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, step, function*(step) {
|
||||
return new Promise(resolve => {
|
||||
let changedWin = content;
|
||||
|
||||
let elem;
|
||||
if (step[0] == "select") {
|
||||
changedWin = content.document.getElementById("frame").contentWindow;
|
||||
elem = changedWin.document.getElementById("select");
|
||||
}
|
||||
else {
|
||||
elem = content.document.getElementById(step[0]);
|
||||
}
|
||||
|
||||
changedWin.addEventListener("MozAfterPaint", function onPaint() {
|
||||
changedWin.removeEventListener("MozAfterPaint", onPaint);
|
||||
resolve();
|
||||
});
|
||||
|
||||
elem.style = step[1];
|
||||
});
|
||||
});
|
||||
|
||||
yield openSelectPopup(selectPopup, false);
|
||||
|
||||
expectedX += step[2];
|
||||
expectedY += step[3];
|
||||
|
||||
let rect = selectPopup.getBoundingClientRect();
|
||||
is(rect.left, expectedX, "step " + (stepIndex + 1) + " x");
|
||||
is(rect.top, expectedY, "step " + (stepIndex + 1) + " y");
|
||||
|
||||
yield hideSelectPopup(selectPopup);
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
|
|
@ -190,7 +190,7 @@ FormSubmitObserver.prototype =
|
|||
|
||||
// Note, this is relative to the browser and needs to be translated
|
||||
// in chrome.
|
||||
panelData.contentRect = this._msgRect(aElement);
|
||||
panelData.contentRect = BrowserUtils.getElementBoundingRect(aElement);
|
||||
|
||||
// We want to show the popup at the middle of checkbox and radio buttons
|
||||
// and where the content begin for the other elements.
|
||||
|
@ -232,21 +232,5 @@ FormSubmitObserver.prototype =
|
|||
(target.ownerDocument && target.ownerDocument == this._content.document));
|
||||
},
|
||||
|
||||
/*
|
||||
* Return a message manager rect for the element's bounding client rect
|
||||
* in top level browser coords.
|
||||
*/
|
||||
_msgRect: function (aElement) {
|
||||
let domRect = aElement.getBoundingClientRect();
|
||||
let zoomFactor = this._getWindowUtils().fullZoom;
|
||||
let { offsetX, offsetY } = BrowserUtils.offsetToTopLevelWindow(this._content, aElement);
|
||||
return {
|
||||
left: (domRect.left + offsetX) * zoomFactor,
|
||||
top: (domRect.top + offsetY) * zoomFactor,
|
||||
width: domRect.width * zoomFactor,
|
||||
height: domRect.height * zoomFactor
|
||||
};
|
||||
},
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver])
|
||||
};
|
||||
|
|
|
@ -108,15 +108,44 @@ this.BrowserUtils = {
|
|||
* the coordinates are relative to the user's screen.
|
||||
*/
|
||||
getElementBoundingScreenRect: function(aElement) {
|
||||
return this.getElementBoundingRect(aElement, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* For a given DOM element, returns its position as an offset from the topmost
|
||||
* window. In a content process, the coordinates returned will be relative to
|
||||
* the left/top of the topmost content area. If aInScreenCoords is true,
|
||||
* screen coordinates will be returned instead.
|
||||
*/
|
||||
getElementBoundingRect: function(aElement, aInScreenCoords) {
|
||||
let rect = aElement.getBoundingClientRect();
|
||||
let window = aElement.ownerDocument.defaultView;
|
||||
let win = aElement.ownerDocument.defaultView;
|
||||
|
||||
let x = rect.left, y = rect.top;
|
||||
|
||||
// We need to compensate for any iframes that might shift things
|
||||
// over. We also need to compensate for zooming.
|
||||
let fullZoom = window.getInterface(Ci.nsIDOMWindowUtils).fullZoom;
|
||||
let parentFrame = win.frameElement;
|
||||
while (parentFrame) {
|
||||
win = parentFrame.ownerDocument.defaultView;
|
||||
let cstyle = win.getComputedStyle(parentFrame, "");
|
||||
|
||||
let framerect = parentFrame.getBoundingClientRect();
|
||||
x += framerect.left + parseFloat(cstyle.borderLeftWidth) + parseFloat(cstyle.paddingLeft);
|
||||
y += framerect.top + parseFloat(cstyle.borderTopWidth) + parseFloat(cstyle.paddingTop);
|
||||
|
||||
parentFrame = win.frameElement;
|
||||
}
|
||||
|
||||
if (aInScreenCoords) {
|
||||
x += win.mozInnerScreenX;
|
||||
y += win.mozInnerScreenY;
|
||||
}
|
||||
|
||||
let fullZoom = win.getInterface(Ci.nsIDOMWindowUtils).fullZoom;
|
||||
rect = {
|
||||
left: (rect.left + window.mozInnerScreenX) * fullZoom,
|
||||
top: (rect.top + window.mozInnerScreenY) * fullZoom,
|
||||
left: x * fullZoom,
|
||||
top: y * fullZoom,
|
||||
width: rect.width * fullZoom,
|
||||
height: rect.height * fullZoom
|
||||
};
|
||||
|
@ -124,34 +153,6 @@ this.BrowserUtils = {
|
|||
return rect;
|
||||
},
|
||||
|
||||
/**
|
||||
* Given an element potentially within a subframe, calculate the offsets
|
||||
* up to the top level browser.
|
||||
*
|
||||
* @param aTopLevelWindow content window to calculate offsets to.
|
||||
* @param aElement The element in question.
|
||||
* @return [targetWindow, offsetX, offsetY]
|
||||
*/
|
||||
offsetToTopLevelWindow: function (aTopLevelWindow, aElement) {
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
let element = aElement;
|
||||
while (element &&
|
||||
element.ownerDocument &&
|
||||
element.ownerDocument.defaultView != aTopLevelWindow) {
|
||||
element = element.ownerDocument.defaultView.frameElement;
|
||||
let rect = element.getBoundingClientRect();
|
||||
offsetX += rect.left;
|
||||
offsetY += rect.top;
|
||||
}
|
||||
let win = null;
|
||||
if (element == aElement)
|
||||
win = aTopLevelWindow;
|
||||
else
|
||||
win = element.contentDocument.defaultView;
|
||||
return { targetWindow: win, offsetX: offsetX, offsetY: offsetY };
|
||||
},
|
||||
|
||||
onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
|
||||
// Don't modify non-default targets or targets that aren't in top-level app
|
||||
// tab docshells (isAppTab will be false for app tab subframes).
|
||||
|
|
Загрузка…
Ссылка в новой задаче