Bug 1684795 - Account for desktop zooming when opening content popups. r=tnikkel

In OOP iframes, we don't have desktop zoom value specifically in each iframe
documents, instead we have a transform matrix,
nsIWidget::WidgetToTopLevelWidgetTransform(), on each the __top__ level OOP
iframe document that the matrix includes the desktop zoom scale value along with
translations by ancestor scroll containers, ancestor CSS transforms, etc.

Note that if the document is not in OOP iframes, i.e. it's in the top level
content subtree, the transform, nsIWidget::WidgetToTopLevelWidgetTransform()
doesn't include the destktop zoom value, so for documents in the top level
content document subtree, ViewportUtils::DocumentRelativeLayoutToVisual applies
the desktop zoom value via PresShell::GetResolution().

Differential Revision: https://phabricator.services.mozilla.com/D102219
This commit is contained in:
Neil Deakin 2021-02-07 21:33:39 +00:00
parent 0abab2be63
commit 0507ed1cd2
3 changed files with 60 additions and 43 deletions

View File

@ -1607,6 +1607,53 @@ nsDOMWindowUtils::TransformRectLayoutToVisual(float aX, float aY, float aWidth,
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::ToScreenRect(float aX, float aY, float aWidth, float aHeight,
DOMRect** aResult) {
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
NS_ENSURE_STATE(window);
PresShell* presShell = GetPresShell();
NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIWidget> widget = GetWidget();
NS_ENSURE_TRUE(widget, NS_ERROR_NOT_AVAILABLE);
// Note that if the document is NOT in OOP iframes, i.e. it's in the top level
// content subtree in the same process,
// nsIWidget::WidgetToTopLevelWidgetTransform() doesn't include the desktop
// zoom value, so for documents in the top level content document subtree,
// this ViewportUtils::DocumentRelativeLayoutToVisual call applies the desktop
// zoom value via PresShell::GetResolution() in the function.
CSSRect rect(aX, aY, aWidth, aHeight);
rect = ViewportUtils::DocumentRelativeLayoutToVisual(rect, presShell);
nsPresContext* presContext = presShell->GetPresContext();
MOZ_ASSERT(presContext);
// For OOP iframe documents, we don't have desktop zoom value specifically in
// each iframe documents (i.e. the in-process root presshell's resolution is
// 1.0), instead nsIWidget::WidgetToTopLevelWidgetTransform() includes the
// desktop zoom scale value along with translations by ancestor scroll
// containers, ancestor CSS transforms, etc.
nsRect appUnitsRect = CSSPixel::ToAppUnits(rect);
LayoutDeviceRect devPixelsRect = LayoutDeviceRect::FromAppUnits(
appUnitsRect, presContext->AppUnitsPerDevPixel());
devPixelsRect =
widget->WidgetToTopLevelWidgetTransform().TransformBounds(devPixelsRect) +
widget->TopLevelWidgetToScreenOffset();
appUnitsRect = LayoutDeviceRect::ToAppUnits(
devPixelsRect,
presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
rect = CSSRect::FromAppUnits(appUnitsRect);
RefPtr<DOMRect> outRect = new DOMRect(window);
outRect->SetRect(rect.x, rect.y, rect.width, rect.height);
outRect.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::SetDynamicToolbarMaxHeight(uint32_t aHeightInScreen) {
if (aHeightInScreen > INT32_MAX) {

View File

@ -916,6 +916,13 @@ interface nsIDOMWindowUtils : nsISupports {
DOMRect transformRectLayoutToVisual(in float aX, in float aY,
in float aWidth, in float aHeight);
/**
* Transform a rectangle given in coordinates relative to this document
* into CSS coordinates relative to the screen.
*/
DOMRect toScreenRect(in float aX, in float aY,
in float aWidth, in float aHeight);
/**
* Sets the maximum height of the dynamic toolbar in Screen pixel units.
*/

View File

@ -10,29 +10,17 @@ var EXPORTED_SYMBOLS = ["LayoutUtils"];
var LayoutUtils = {
/**
* For a given DOM element, returns its position in "screen"
* coordinates. In a content process, the coordinates returned will
* be relative to the left/top of the tab. In the chrome process,
* the coordinates are relative to the user's screen.
* coordinates.
*/
getElementBoundingScreenRect(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(aElement, aInScreenCoords) {
let rect = aElement.getBoundingClientRect();
let win = aElement.ownerGlobal;
let x = rect.left;
let y = rect.top;
// We need to compensate for any iframes that might shift things
// over. We also need to compensate for zooming.
// We need to compensate for ancestor iframes in the same process
// that might shift things over.
let parentFrame = win.frameElement;
while (parentFrame) {
win = parentFrame.ownerGlobal;
@ -51,36 +39,11 @@ var LayoutUtils = {
parentFrame = win.frameElement;
}
rect = {
left: x,
top: y,
width: rect.width,
height: rect.height,
};
rect = win.windowUtils.transformRectLayoutToVisual(
rect.left,
rect.top,
return aElement.ownerGlobal.windowUtils.toScreenRect(
x,
y,
rect.width,
rect.height
);
if (aInScreenCoords) {
rect = {
left: rect.left + win.mozInnerScreenX,
top: rect.top + win.mozInnerScreenY,
width: rect.width,
height: rect.height,
};
}
let fullZoom = win.windowUtils.fullZoom;
rect = {
left: rect.left * fullZoom,
top: rect.top * fullZoom,
width: rect.width * fullZoom,
height: rect.height * fullZoom,
};
return rect;
},
};