Bug 1753836 - Make window screen and event screen coordinates consistent. r=jfkthame

This basically undoes bug 1246346. The current behavior is pretty bizarre,
the screenX origin / position doesn't match the mouse event coordinates,
because on windows we return device pixels rather than CSS pixels for the
window coordinates.

This makes behavior consistent with how other browsers report these coordinates
at least on Windows in non-mixed DPI mode, and I think is fine.

In mixed DPI mode, there might indeed be overlapping coordinates, but again I
think that's fine, because the CSS coordinate space of the different monitors
is different.  You need to multiply by the devicePixelRatio if you want
coordinates not to overlap.

Depends on D138039

Differential Revision: https://phabricator.services.mozilla.com/D138130
This commit is contained in:
Emilio Cobos Álvarez 2022-02-16 12:18:12 +00:00
Родитель a8883d1cfc
Коммит ba31251df3
4 изменённых файлов: 121 добавлений и 162 удалений

Просмотреть файл

@ -4920,46 +4920,56 @@ var SessionStoreInternal = {
screenWidth,
screenHeight
);
// screenX/Y are based on the origin of the screen's desktop-pixel coordinate space
let screenLeftCss = screenLeft.value;
let screenTopCss = screenTop.value;
// convert screen's device pixel dimensions to CSS px dimensions
screen.GetAvailRect(screenLeft, screenTop, screenWidth, screenHeight);
let cssToDevScale = screen.defaultCSSScaleFactor;
let screenRightCss = screenLeftCss + screenWidth.value / cssToDevScale;
let screenBottomCss = screenTopCss + screenHeight.value / cssToDevScale;
// We store aLeft / aTop (screenX/Y) in desktop pixels, see
// _getWindowDimension.
screenLeft = screenLeft.value;
screenTop = screenTop.value;
screenWidth = screenWidth.value;
screenHeight = screenHeight.value;
let screenBottom = screenTop + screenHeight;
let screenRight = screenLeft + screenWidth;
// NOTE: contentsScaleFactor is the desktopToDeviceScale of the screen.
// Naming could be more consistent here.
let cssToDesktopScale =
screen.defaultCSSScaleFactor / screen.contentsScaleFactor;
let slop = SCREEN_EDGE_SLOP * cssToDesktopScale;
// Pull the window within the screen's bounds (allowing a little slop
// for windows that may be deliberately placed with their border off-screen
// as when Win10 "snaps" a window to the left/right edge -- bug 1276516).
// First, ensure the left edge is large enough...
if (aLeft < screenLeftCss - SCREEN_EDGE_SLOP) {
aLeft = screenLeftCss;
if (aLeft < screenLeft - slop) {
aLeft = screenLeft;
}
// Then check the resulting right edge, and reduce it if necessary.
let right = aLeft + aWidth;
if (right > screenRightCss + SCREEN_EDGE_SLOP) {
right = screenRightCss;
let right = aLeft + aWidth * cssToDesktopScale;
if (right > screenRight + slop) {
right = screenRight;
// See if we can move the left edge leftwards to maintain width.
if (aLeft > screenLeftCss) {
aLeft = Math.max(right - aWidth, screenLeftCss);
if (aLeft > screenLeft) {
aLeft = Math.max(right - aWidth * cssToDesktopScale, screenLeft);
}
}
// Finally, update aWidth to account for the adjusted left and right edges.
aWidth = right - aLeft;
// Finally, update aWidth to account for the adjusted left and right
// edges, and convert it back to CSS pixels on the target screen.
aWidth = (right - aLeft) / cssToDesktopScale;
// And do the same in the vertical dimension.
if (aTop < screenTopCss - SCREEN_EDGE_SLOP) {
aTop = screenTopCss;
if (aTop < screenTop - slop) {
aTop = screenTop;
}
let bottom = aTop + aHeight;
if (bottom > screenBottomCss + SCREEN_EDGE_SLOP) {
bottom = screenBottomCss;
if (aTop > screenTopCss) {
aTop = Math.max(bottom - aHeight, screenTopCss);
let bottom = aTop + aHeight * cssToDesktopScale;
if (bottom > screenBottom + slop) {
bottom = screenBottom;
if (aTop > screenTop) {
aTop = Math.max(bottom - aHeight * cssToDesktopScale, screenTop);
}
}
aHeight = bottom - aTop;
aHeight = (bottom - aTop) / cssToDesktopScale;
}
// Suppress animations.
@ -4973,7 +4983,12 @@ var SessionStoreInternal = {
!isNaN(aTop) &&
(aLeft != win_("screenX") || aTop != win_("screenY"))
) {
aWindow.moveTo(aLeft, aTop);
// moveTo uses CSS pixels relative to aWindow, while aLeft and aRight
// are on desktop pixels, undo the conversion we do in
// _getWindowDimension.
let desktopToCssScale =
aWindow.desktopToDeviceScale / aWindow.devicePixelRatio;
aWindow.moveTo(aLeft * desktopToCssScale, aTop * desktopToCssScale);
}
if (
aWidth &&
@ -5339,6 +5354,17 @@ var SessionStoreInternal = {
return aWindow.outerWidth;
case "height":
return aWindow.outerHeight;
case "screenX":
case "screenY":
// We use desktop pixels rather than CSS pixels to store window
// positions, see bug 1247335. This allows proper multi-monitor
// positioning in mixed-DPI situations.
// screenX/Y are in CSS pixels for the current window, so, convert them
// to desktop pixels.
return (
(aWindow[aAttribute] * aWindow.devicePixelRatio) /
aWindow.desktopToDeviceScale
);
default:
return aAttribute in aWindow ? aWindow[aAttribute] : "";
}

Просмотреть файл

@ -3728,34 +3728,19 @@ CSSIntPoint nsGlobalWindowOuter::GetScreenXY(CallerType aCallerType,
return CSSIntPoint(0, 0);
}
int32_t x = 0, y = 0;
aError = treeOwnerAsWin->GetPosition(&x, &y); // LayoutDevice px values
LayoutDeviceIntPoint windowPos;
aError = treeOwnerAsWin->GetPosition(&windowPos.x, &windowPos.y);
RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
if (!presContext) {
return CSSIntPoint(x, y);
// XXX Fishy LayoutDevice to CSS conversion?
return CSSIntPoint(windowPos.x, windowPos.y);
}
// Find the global desktop coordinate of the top-left of the screen.
// We'll use this as a "fake origin" when converting to CSS px units,
// to avoid overlapping coordinates in cases such as a hi-dpi screen
// placed to the right of a lo-dpi screen on Windows. (Instead, there
// may be "gaps" in the resulting CSS px coordinates in some cases.)
nsDeviceContext* dc = presContext->DeviceContext();
nsRect screenRect;
dc->GetRect(screenRect);
LayoutDeviceRect screenRectDev =
LayoutDevicePixel::FromAppUnits(screenRect, dc->AppUnitsPerDevPixel());
DesktopToLayoutDeviceScale scale = dc->GetDesktopToDeviceScale();
DesktopRect screenRectDesk = screenRectDev / scale;
CSSPoint cssPt = LayoutDevicePoint(x - screenRectDev.x, y - screenRectDev.y) /
presContext->CSSToDevPixelScale();
cssPt.x += screenRectDesk.x;
cssPt.y += screenRectDesk.y;
return CSSIntPoint(NSToIntRound(cssPt.x), NSToIntRound(cssPt.y));
nsDeviceContext* context = presContext->DeviceContext();
auto windowPosAppUnits = LayoutDeviceIntPoint::ToAppUnits(
windowPos, context->AppUnitsPerDevPixel());
return CSSIntPoint::FromAppUnitsRounded(windowPosAppUnits);
}
int32_t nsGlobalWindowOuter::GetScreenXOuter(CallerType aCallerType,
@ -5391,42 +5376,21 @@ void nsGlobalWindowOuter::MoveToOuter(int32_t aXPos, int32_t aYPos,
return;
}
nsCOMPtr<nsIScreenManager> screenMgr =
do_GetService("@mozilla.org/gfx/screenmanager;1");
nsCOMPtr<nsIScreen> screen;
if (screenMgr) {
CSSSize size;
GetInnerSize(size);
screenMgr->ScreenForRect(aXPos, aYPos, std::round(size.width),
std::round(size.height), getter_AddRefs(screen));
// We need to do the same transformation GetScreenXY does.
RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
if (!presContext) {
return;
}
if (screen) {
// On secondary displays, the "CSS px" coordinates are offset so that they
// share their origin with global desktop pixels, to avoid ambiguities in
// the coordinate space when there are displays with different DPIs.
// (See the corresponding code in GetScreenXY() above.)
int32_t screenLeftDeskPx, screenTopDeskPx, w, h;
screen->GetRectDisplayPix(&screenLeftDeskPx, &screenTopDeskPx, &w, &h);
CSSIntPoint cssPos(aXPos - screenLeftDeskPx, aYPos - screenTopDeskPx);
CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);
CSSIntPoint cssPos(aXPos, aYPos);
CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);
double scale;
screen->GetDefaultCSSScaleFactor(&scale);
LayoutDevicePoint devPos = cssPos * CSSToLayoutDeviceScale(scale);
nsDeviceContext* context = presContext->DeviceContext();
screen->GetContentsScaleFactor(&scale);
DesktopPoint deskPos = devPos / DesktopToLayoutDeviceScale(scale);
aError = treeOwnerAsWin->SetPositionDesktopPix(screenLeftDeskPx + deskPos.x,
screenTopDeskPx + deskPos.y);
} else {
// We couldn't find a screen? Just assume a 1:1 mapping.
CSSIntPoint cssPos(aXPos, aXPos);
CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);
LayoutDevicePoint devPos = cssPos * CSSToLayoutDeviceScale(1.0);
aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
}
auto devPos = LayoutDeviceIntPoint::FromAppUnitsRounded(
CSSIntPoint::ToAppUnits(cssPos), context->AppUnitsPerDevPixel());
aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
CheckForDPIChange();
}

Просмотреть файл

@ -80,7 +80,7 @@ nsDeviceContext* nsScreen::GetDeviceContext() {
return nsLayoutUtils::GetDeviceContextForScreenInfo(GetOuter());
}
nsresult nsScreen::GetRect(nsRect& aRect) {
nsresult nsScreen::GetRect(CSSIntRect& aRect) {
// Return window inner rect to prevent fingerprinting.
if (ShouldResistFingerprinting()) {
return GetWindowInnerRect(aRect);
@ -88,14 +88,12 @@ nsresult nsScreen::GetRect(nsRect& aRect) {
// Here we manipulate the value of aRect to represent the screen size,
// if in RDM.
nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
if (owner) {
mozilla::dom::Document* doc = owner->GetExtantDoc();
if (doc) {
Maybe<mozilla::CSSIntSize> deviceSize =
if (nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner()) {
if (Document* doc = owner->GetExtantDoc()) {
Maybe<CSSIntSize> deviceSize =
nsGlobalWindowOuter::GetRDMDeviceSize(*doc);
if (deviceSize.isSome()) {
const mozilla::CSSIntSize& size = deviceSize.value();
const CSSIntSize& size = deviceSize.value();
aRect.SetRect(0, 0, size.width, size.height);
return NS_OK;
}
@ -108,18 +106,40 @@ nsresult nsScreen::GetRect(nsRect& aRect) {
return NS_ERROR_FAILURE;
}
context->GetRect(aRect);
LayoutDevicePoint screenTopLeftDev = LayoutDevicePixel::FromAppUnits(
aRect.TopLeft(), context->AppUnitsPerDevPixel());
DesktopPoint screenTopLeftDesk =
screenTopLeftDev / context->GetDesktopToDeviceScale();
nsRect r;
context->GetRect(r);
aRect = CSSIntRect::FromAppUnitsRounded(r);
return NS_OK;
}
aRect.x = NSToIntRound(screenTopLeftDesk.x);
aRect.y = NSToIntRound(screenTopLeftDesk.y);
nsresult nsScreen::GetAvailRect(CSSIntRect& aRect) {
// Return window inner rect to prevent fingerprinting.
if (ShouldResistFingerprinting()) {
return GetWindowInnerRect(aRect);
}
aRect.SetHeight(nsPresContext::AppUnitsToIntCSSPixels(aRect.Height()));
aRect.SetWidth(nsPresContext::AppUnitsToIntCSSPixels(aRect.Width()));
// Here we manipulate the value of aRect to represent the screen size,
// if in RDM.
if (nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner()) {
if (Document* doc = owner->GetExtantDoc()) {
Maybe<CSSIntSize> deviceSize =
nsGlobalWindowOuter::GetRDMDeviceSize(*doc);
if (deviceSize.isSome()) {
const CSSIntSize& size = deviceSize.value();
aRect.SetRect(0, 0, size.width, size.height);
return NS_OK;
}
}
}
nsDeviceContext* context = GetDeviceContext();
if (!context) {
return NS_ERROR_FAILURE;
}
nsRect r;
context->GetClientRect(r);
aRect = CSSIntRect::FromAppUnitsRounded(r);
return NS_OK;
}
@ -141,57 +161,7 @@ hal::ScreenOrientation nsScreen::GetOrientationType() const {
return s->GetOrientationType();
}
nsresult nsScreen::GetAvailRect(nsRect& aRect) {
// Return window inner rect to prevent fingerprinting.
if (ShouldResistFingerprinting()) {
return GetWindowInnerRect(aRect);
}
// Here we manipulate the value of aRect to represent the screen size,
// if in RDM.
nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
if (owner) {
mozilla::dom::Document* doc = owner->GetExtantDoc();
if (doc) {
Maybe<mozilla::CSSIntSize> deviceSize =
nsGlobalWindowOuter::GetRDMDeviceSize(*doc);
if (deviceSize.isSome()) {
const mozilla::CSSIntSize& size = deviceSize.value();
aRect.SetRect(0, 0, size.width, size.height);
return NS_OK;
}
}
}
nsDeviceContext* context = GetDeviceContext();
if (!context) {
return NS_ERROR_FAILURE;
}
nsRect r;
context->GetRect(r);
LayoutDevicePoint screenTopLeftDev = LayoutDevicePixel::FromAppUnits(
r.TopLeft(), context->AppUnitsPerDevPixel());
DesktopPoint screenTopLeftDesk =
screenTopLeftDev / context->GetDesktopToDeviceScale();
context->GetClientRect(aRect);
aRect.x = NSToIntRound(screenTopLeftDesk.x) +
nsPresContext::AppUnitsToIntCSSPixels(aRect.x - r.x);
aRect.y = NSToIntRound(screenTopLeftDesk.y) +
nsPresContext::AppUnitsToIntCSSPixels(aRect.y - r.y);
aRect.SetHeight(nsPresContext::AppUnitsToIntCSSPixels(aRect.Height()));
aRect.SetWidth(nsPresContext::AppUnitsToIntCSSPixels(aRect.Width()));
return NS_OK;
}
mozilla::dom::ScreenOrientation* nsScreen::Orientation() const {
return mScreenOrientation;
}
ScreenOrientation* nsScreen::Orientation() const { return mScreenOrientation; }
void nsScreen::GetMozOrientation(nsString& aOrientation,
CallerType aCallerType) const {
@ -238,7 +208,7 @@ JSObject* nsScreen::WrapObject(JSContext* aCx,
return Screen_Binding::Wrap(aCx, this, aGivenProto);
}
nsresult nsScreen::GetWindowInnerRect(nsRect& aRect) {
nsresult nsScreen::GetWindowInnerRect(CSSIntRect& aRect) {
aRect.x = 0;
aRect.y = 0;
nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
@ -251,8 +221,6 @@ nsresult nsScreen::GetWindowInnerRect(nsRect& aRect) {
NS_ENSURE_SUCCESS(rv, rv);
rv = win->GetInnerHeight(&height);
NS_ENSURE_SUCCESS(rv, rv);
// FIXME(emilio): This is an nsRect but should really be a CSSIntRect, these
// are CSS pixels!
aRect.SizeTo(std::round(width), std::round(height));
return NS_OK;
}

Просмотреть файл

@ -15,7 +15,8 @@
#include "mozilla/StaticPrefs_media.h"
#include "nsCOMPtr.h"
#include "nsGlobalWindowOuter.h"
#include "nsRect.h"
#include "mozilla/gfx/Rect.h"
#include "Units.h"
class nsDeviceContext;
@ -35,25 +36,25 @@ class nsScreen : public mozilla::DOMEventTargetHelper {
nsPIDOMWindowOuter* GetOuter() const;
int32_t GetTop(ErrorResult& aRv) {
nsRect rect;
mozilla::CSSIntRect rect;
aRv = GetRect(rect);
return rect.y;
}
int32_t GetLeft(ErrorResult& aRv) {
nsRect rect;
mozilla::CSSIntRect rect;
aRv = GetRect(rect);
return rect.x;
}
int32_t GetWidth(ErrorResult& aRv) {
nsRect rect;
mozilla::CSSIntRect rect;
aRv = GetRect(rect);
return rect.Width();
}
int32_t GetHeight(ErrorResult& aRv) {
nsRect rect;
mozilla::CSSIntRect rect;
aRv = GetRect(rect);
return rect.Height();
}
@ -62,25 +63,25 @@ class nsScreen : public mozilla::DOMEventTargetHelper {
int32_t GetColorDepth(ErrorResult& aRv) { return GetPixelDepth(aRv); }
int32_t GetAvailTop(ErrorResult& aRv) {
nsRect rect;
mozilla::CSSIntRect rect;
aRv = GetAvailRect(rect);
return rect.y;
}
int32_t GetAvailLeft(ErrorResult& aRv) {
nsRect rect;
mozilla::CSSIntRect rect;
aRv = GetAvailRect(rect);
return rect.x;
}
int32_t GetAvailWidth(ErrorResult& aRv) {
nsRect rect;
mozilla::CSSIntRect rect;
aRv = GetAvailRect(rect);
return rect.Width();
}
int32_t GetAvailHeight(ErrorResult& aRv) {
nsRect rect;
mozilla::CSSIntRect rect;
aRv = GetAvailRect(rect);
return rect.Height();
}
@ -124,9 +125,9 @@ class nsScreen : public mozilla::DOMEventTargetHelper {
protected:
nsDeviceContext* GetDeviceContext();
nsresult GetRect(nsRect& aRect);
nsresult GetAvailRect(nsRect& aRect);
nsresult GetWindowInnerRect(nsRect& aRect);
nsresult GetRect(mozilla::CSSIntRect& aRect);
nsresult GetAvailRect(mozilla::CSSIntRect& aRect);
nsresult GetWindowInnerRect(mozilla::CSSIntRect& aRect);
private:
explicit nsScreen(nsPIDOMWindowInner* aWindow);