Skip to content

Commit

Permalink
Bug 890156 - patch 6 - Update window placement code to work with desk…
Browse files Browse the repository at this point in the history
…top pixels, for per-monitor DPI support on Windows. r=emk
  • Loading branch information
jfkthame committed Dec 3, 2015
1 parent d178c65 commit 46b5136
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 119 deletions.
6 changes: 3 additions & 3 deletions browser/components/sessionstore/SessionStore.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -3476,16 +3476,16 @@ var SessionStoreInternal = {
}

// only modify those aspects which aren't correct yet
if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
aWindow.moveTo(aLeft, aTop);
}
if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height"))) {
// Don't resize the window if it's currently maximized and we would
// maximize it again shortly after.
if (aSizeMode != "maximized" || win_("sizemode") != "maximized") {
aWindow.resizeTo(aWidth, aHeight);
}
}
if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
aWindow.moveTo(aLeft, aTop);
}
if (aSizeMode && win_("sizemode") != aSizeMode)
{
switch (aSizeMode)
Expand Down
29 changes: 18 additions & 11 deletions dom/base/nsGlobalWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5029,33 +5029,38 @@ nsGlobalWindow::SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
aValue, "outerHeight", aError);
}

nsIntPoint
DesktopIntPoint
nsGlobalWindow::GetScreenXY(ErrorResult& aError)
{
MOZ_ASSERT(IsOuterWindow());

// When resisting fingerprinting, always return (0,0)
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
return nsIntPoint(0, 0);
return DesktopIntPoint(0, 0);
}

nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
if (!treeOwnerAsWin) {
aError.Throw(NS_ERROR_FAILURE);
return nsIntPoint(0, 0);
return DesktopIntPoint(0, 0);
}

int32_t x = 0, y = 0;
aError = treeOwnerAsWin->GetPosition(&x, &y);
return nsIntPoint(x, y);

nsCOMPtr<nsIWidget> widget = GetMainWidget();
DesktopToLayoutDeviceScale scale = widget ? widget->GetDesktopToDeviceScale()
: DesktopToLayoutDeviceScale(1.0);
DesktopPoint pt = LayoutDeviceIntPoint(x, y) / scale;
return DesktopIntPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
}

int32_t
nsGlobalWindow::GetScreenXOuter(ErrorResult& aError)
{
MOZ_RELEASE_ASSERT(IsOuterWindow());

return DevToCSSIntPixels(GetScreenXY(aError).x);
return GetScreenXY(aError).x;
}

int32_t
Expand Down Expand Up @@ -5299,7 +5304,7 @@ nsGlobalWindow::GetScreenYOuter(ErrorResult& aError)
{
MOZ_RELEASE_ASSERT(IsOuterWindow());

return DevToCSSIntPixels(GetScreenXY(aError).y);
return GetScreenXY(aError).y;
}

int32_t
Expand Down Expand Up @@ -7002,13 +7007,15 @@ nsGlobalWindow::MoveToOuter(int32_t aXPos, int32_t aYPos, ErrorResult& aError, b
return;
}

// Mild abuse of a "size" object so we don't need more helper functions.
nsIntSize cssPos(aXPos, aYPos);
CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height, aCallerIsChrome);
DesktopIntPoint pt(aXPos, aYPos);
CheckSecurityLeftAndTop(&pt.x, &pt.y, aCallerIsChrome);

nsIntSize devPos = CSSToDevIntPixels(cssPos);
nsCOMPtr<nsIWidget> widget = GetMainWidget();
DesktopToLayoutDeviceScale scale = widget ? widget->GetDesktopToDeviceScale()
: DesktopToLayoutDeviceScale(1.0);
LayoutDevicePoint devPos = pt * scale;

aError = treeOwnerAsWin->SetPosition(devPos.width, devPos.height);
aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
}

void
Expand Down
4 changes: 2 additions & 2 deletions dom/base/nsGlobalWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -1606,8 +1606,8 @@ class nsGlobalWindow : public mozilla::dom::EventTarget,
// Outer windows only.
void PreloadLocalStorage();

// Returns device pixels. Outer windows only.
nsIntPoint GetScreenXY(mozilla::ErrorResult& aError);
// Returns desktop pixels. Outer windows only.
mozilla::DesktopIntPoint GetScreenXY(mozilla::ErrorResult& aError);

nsGlobalWindow* InnerForSetTimeoutOrInterval(mozilla::ErrorResult& aError);

Expand Down
2 changes: 1 addition & 1 deletion gfx/layers/apz/util/APZEventState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ APZEventState::ProcessSingleTap(const CSSPoint& aPoint,

LayoutDevicePoint currentPoint =
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid)
* widget->GetDefaultScale();;
* widget->GetDefaultScale();
if (!mActiveElementManager->ActiveElementUsesStyle()) {
// If the active element isn't visually affected by the :active style, we
// have no need to wait the extra sActiveDurationMs to make the activation
Expand Down
2 changes: 2 additions & 0 deletions gfx/src/nsDeviceContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,8 @@ nsDeviceContext::FindScreen(nsIScreen** outScreen)
return;
}

CheckDPIChange();

if (mWidget->GetOwningTabChild()) {
mScreenManager->ScreenForNativeWidget((void *)mWidget->GetOwningTabChild(),
outScreen);
Expand Down
4 changes: 4 additions & 0 deletions layout/base/Units.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ struct LayoutDevicePixel {
return LayoutDeviceIntRect::FromUnknownRect(aRect.ToNearestPixels(aAppUnitsPerDevPixel));
}

static LayoutDeviceIntRect FromAppUnitsToInside(const nsRect& aRect, nscoord aAppUnitsPerDevPixel) {
return LayoutDeviceIntRect::FromUnknownRect(aRect.ToInsidePixels(aAppUnitsPerDevPixel));
}

static LayoutDeviceIntSize FromAppUnitsRounded(const nsSize& aSize, nscoord aAppUnitsPerDevPixel) {
return LayoutDeviceIntSize(
NSAppUnitsToIntPixels(aSize.width, aAppUnitsPerDevPixel),
Expand Down
42 changes: 23 additions & 19 deletions layout/xul/nsMenuPopupFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1428,8 +1428,17 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS

// If a panel is being moved or has flip="none", don't constrain or flip it. But always do this for
// content shells, so that the popup doesn't extend outside the containing frame.
if (mInContentShell || (mFlip != FlipType_None && (!aIsMove || mPopupType != ePopupTypePanel))) {
nsRect screenRect = GetConstraintRect(anchorRect, rootScreenRect, popupLevel);
if (mInContentShell || (mFlip != FlipType_None &&
(!aIsMove || mPopupType != ePopupTypePanel))) {
int32_t appPerDev = presContext->AppUnitsPerDevPixel();
LayoutDeviceIntRect anchorRectDevPix =
LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRect, appPerDev);
LayoutDeviceIntRect rootScreenRectDevPix =
LayoutDeviceIntRect::FromAppUnitsToNearest(rootScreenRect, appPerDev);
LayoutDeviceIntRect screenRectDevPix =
GetConstraintRect(anchorRectDevPix, rootScreenRectDevPix, popupLevel);
nsRect screenRect =
LayoutDeviceIntRect::ToAppUnits(screenRectDevPix, appPerDev);

// Ensure that anchorRect is on screen.
anchorRect = anchorRect.Intersect(screenRect);
Expand Down Expand Up @@ -1527,13 +1536,12 @@ nsMenuPopupFrame::GetCurrentMenuItem()
return mCurrentMenu;
}

nsRect
nsMenuPopupFrame::GetConstraintRect(const nsRect& aAnchorRect,
const nsRect& aRootScreenRect,
LayoutDeviceIntRect
nsMenuPopupFrame::GetConstraintRect(const LayoutDeviceIntRect& aAnchorRect,
const LayoutDeviceIntRect& aRootScreenRect,
nsPopupLevel aPopupLevel)
{
nsIntRect screenRectPixels;
nsPresContext* presContext = PresContext();
LayoutDeviceIntRect screenRectPixels;

// determine the available screen space. It will be reduced by the OS chrome
// such as menubars. It addition, for content shells, it will be the area of
Expand All @@ -1545,34 +1553,30 @@ nsMenuPopupFrame::GetConstraintRect(const nsRect& aAnchorRect,
// This is because we need to constrain the content to this content area,
// so we should use the same screen. Otherwise, use the screen where the
// anchor is located.
nsRect rect = mInContentShell ? aRootScreenRect : aAnchorRect;
// nsIScreenManager::ScreenForRect wants the coordinates in CSS pixels
int32_t width = std::max(1, nsPresContext::AppUnitsToIntCSSPixels(rect.width));
int32_t height = std::max(1, nsPresContext::AppUnitsToIntCSSPixels(rect.height));
sm->ScreenForRect(nsPresContext::AppUnitsToIntCSSPixels(rect.x),
nsPresContext::AppUnitsToIntCSSPixels(rect.y),
width, height, getter_AddRefs(screen));
LayoutDeviceIntRect rect = mInContentShell ? aRootScreenRect : aAnchorRect;
int32_t width = std::max(1, rect.width);
int32_t height = std::max(1, rect.height);
sm->ScreenForRect(rect.x, rect.y, width, height, getter_AddRefs(screen));
if (screen) {
// Non-top-level popups (which will always be panels)
// should never overlap the OS bar:
bool dontOverlapOSBar = aPopupLevel != ePopupLevelTop;
// get the total screen area if the popup is allowed to overlap it.
if (!dontOverlapOSBar && mMenuCanOverlapOSBar && !mInContentShell)
screen->GetRect(&screenRectPixels.x, &screenRectPixels.y,
&screenRectPixels.width, &screenRectPixels.height);
&screenRectPixels.width, &screenRectPixels.height);
else
screen->GetAvailRect(&screenRectPixels.x, &screenRectPixels.y,
&screenRectPixels.width, &screenRectPixels.height);
&screenRectPixels.width, &screenRectPixels.height);
}
}

nsRect screenRect = ToAppUnits(screenRectPixels, presContext->AppUnitsPerDevPixel());
if (mInContentShell) {
// for content shells, clip to the client area rather than the screen area
screenRect.IntersectRect(screenRect, aRootScreenRect);
screenRectPixels.IntersectRect(screenRectPixels, aRootScreenRect);
}

return screenRect;
return screenRectPixels;
}

void nsMenuPopupFrame::CanAdjustEdges(int8_t aHorizontalSide,
Expand Down
7 changes: 5 additions & 2 deletions layout/xul/nsMenuPopupFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,11 @@ class nsMenuPopupFrame final : public nsBoxFrame, public nsMenuParent,
// For non-toplevel popups (which will always be panels), we will also
// constrain them to the available screen rect, ie they will not fall
// underneath the taskbar, dock or other fixed OS elements.
nsRect GetConstraintRect(const nsRect& aAnchorRect, const nsRect& aRootScreenRect,
nsPopupLevel aPopupLevel);
// This operates in device pixels.
mozilla::LayoutDeviceIntRect
GetConstraintRect(const mozilla::LayoutDeviceIntRect& aAnchorRect,
const mozilla::LayoutDeviceIntRect& aRootScreenRect,
nsPopupLevel aPopupLevel);

// Determines whether the given edges of the popup may be moved, where
// aHorizontalSide and aVerticalSide are one of the NS_SIDE_* constants, or
Expand Down
15 changes: 9 additions & 6 deletions layout/xul/nsResizerFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,15 @@ nsResizerFrame::HandleEvent(nsPresContext* aPresContext,
nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits();

nsPopupLevel popupLevel = menuPopupFrame->PopupLevel();
nsRect screenRect = menuPopupFrame->GetConstraintRect(frameRect, rootScreenRect, popupLevel);
// round using ToInsidePixels as it's better to be a pixel too small
// than be too large. If the popup is too large it could get flipped
// to the opposite side of the anchor point while resizing.
nsIntRect screenRectPixels = screenRect.ToInsidePixels(aPresContext->AppUnitsPerDevPixel());
rect.IntersectRect(rect, LayoutDeviceIntRect::FromUnknownRect(screenRectPixels));
int32_t appPerDev = aPresContext->AppUnitsPerDevPixel();
LayoutDeviceIntRect screenRect = menuPopupFrame->GetConstraintRect
(LayoutDeviceIntRect::FromAppUnitsToNearest(frameRect, appPerDev),
// round using ...ToInside as it's better to be a pixel too small
// than be too large. If the popup is too large it could get flipped
// to the opposite side of the anchor point while resizing.
LayoutDeviceIntRect::FromAppUnitsToInside(rootScreenRect, appPerDev),
popupLevel);
rect.IntersectRect(rect, screenRect);
}

if (contentToResize) {
Expand Down
51 changes: 25 additions & 26 deletions view/nsView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,44 +336,43 @@ void nsView::DoResetWidgetBounds(bool aMoveOnly,

// Child views are never attached to top level widgets, this is safe.

// Coordinates are converted to display pixels for window Move/Resize APIs,
// Coordinates are converted to desktop pixels for window Move/Resize APIs,
// because of the potential for device-pixel coordinate spaces for mixed
// hidpi/lodpi screens to overlap each other and result in bad placement
// (bug 814434).
double invScale;

// Bug 861270: for correct widget manipulation at arbitrary scale factors,
// prefer to base scaling on widget->GetDefaultScale(). But only do this if
// it matches the view manager's device context scale after allowing for the
// quantization to app units, because of OS X multiscreen issues (where the
// only two scales are 1.0 or 2.0, and so the quantization doesn't actually
// cause problems anyhow).
// In the case of a mismatch, fall back to scaling based on the dev context's
// AppUnitsPerDevPixelAtUnitFullZoom value. On platforms where the device-pixel
// scale is uniform across all displays (currently all except OS X), we'll
// always use the precise value from mWindow->GetDefaultScale here.
CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
if (NSToIntRound(60.0 / scale.scale) == dx->AppUnitsPerDevPixelAtUnitFullZoom()) {
invScale = 1.0 / scale.scale;
} else {
invScale = dx->AppUnitsPerDevPixelAtUnitFullZoom() / 60.0;
DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScale();

#ifdef XP_MACOSX
// On OS X, this can be called before Cocoa has updated the backing scale
// factor of our widget, in which case |scale| is wrong here. To work
// around this, we check the device context and override |scale| if it
// doesn't match. (This happens when a popup window that has previously
// been created and hidden is being moved between hi- and lo-dpi screens,
// but is not currently visible; Cocoa doesn't notify it of the scale
// factor change until it gets shown on the new screen, which is too late
// for us because we'll have already done the computations involving scale
// here to move/size it.)
// It might be better to avoid this by keeping calculations such as
// CalcWidgetBounds entirely in appUnits, rather than using device pixels,
// but that seems like a more extensive and potentially risky change.
int32_t appPerDev = dx->AppUnitsPerDevPixelAtUnitFullZoom();
if (NSToIntRound(60.0 / scale.scale) != appPerDev) {
scale = DesktopToLayoutDeviceScale(60.0 / appPerDev);
}
#endif

DesktopRect deskRect = newBounds / scale;
if (changedPos) {
if (changedSize && !aMoveOnly) {
widget->ResizeClient(newBounds.x * invScale,
newBounds.y * invScale,
newBounds.width * invScale,
newBounds.height * invScale,
widget->ResizeClient(deskRect.x, deskRect.y,
deskRect.width, deskRect.height,
aInvalidateChangedSize);
} else {
widget->MoveClient(newBounds.x * invScale,
newBounds.y * invScale);
widget->MoveClient(deskRect.x, deskRect.y);
}
} else {
if (changedSize && !aMoveOnly) {
widget->ResizeClient(newBounds.width * invScale,
newBounds.height * invScale,
widget->ResizeClient(deskRect.width, deskRect.height,
aInvalidateChangedSize);
} // else do nothing!
}
Expand Down
Loading

0 comments on commit 46b5136

Please sign in to comment.