From f125fdcce4a5f901b289c5bfdaab73477f012ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 15 Apr 2020 01:44:25 +0000 Subject: [PATCH] Bug 1609446 - Make default window-constraints always show the content. r=mats,mstange This code is used to determine the sizes of the top-level windows. However the code doesn't cause quite desirable behavior (see the bug, and comment 15). This patch does two things: * Unifies the html / xul code-paths. This shouldn't change behavior (because GetXULMinSize returns the fixed min-* property if present anyways), but makes the patch a bit simpler. * Makes the min-width of the XUL window be the pref size instead of the min-size (for the cases where you have no explicit min-width). This looks a bit counter intuitive, but it's the only way to guarantee that the content will be shown. This matches the sizing algorithm that dialogs use by default (via calling window.sizeToContent()), while allowing to undersize the window via a fixed min-width property. This in turn makes sizeToContent() work "by default" on XUL windows, avoiding having to make JS listen to everything that possibly could change the layout of the document (like resolution changes). Differential Revision: https://phabricator.services.mozilla.com/D70209 --- .../client/framework/toolbox-window.xhtml | 8 +- gfx/src/nsSize.h | 17 ++++ layout/base/Units.h | 6 ++ layout/base/nsDocumentViewer.cpp | 6 +- layout/generic/nsContainerFrame.cpp | 80 ++++++++++++------- .../tests/chrome/window_screenPosSize.xhtml | 1 + 6 files changed, 85 insertions(+), 33 deletions(-) diff --git a/devtools/client/framework/toolbox-window.xhtml b/devtools/client/framework/toolbox-window.xhtml index 09960681b763..a3ecc39ae41d 100644 --- a/devtools/client/framework/toolbox-window.xhtml +++ b/devtools/client/framework/toolbox-window.xhtml @@ -5,13 +5,19 @@ - + diff --git a/gfx/src/nsSize.h b/gfx/src/nsSize.h index b4125e603585..823630ec9f76 100644 --- a/gfx/src/nsSize.h +++ b/gfx/src/nsSize.h @@ -26,6 +26,10 @@ struct nsSize : public mozilla::gfx::BaseSize { float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; inline mozilla::gfx::IntSize ToNearestPixels(nscoord aAppUnitsPerPixel) const; + inline mozilla::gfx::IntSize ScaleToOutsidePixels( + float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; + inline mozilla::gfx::IntSize ToOutsidePixels(nscoord aAppUnitsPerPixel) const; + /** * Return this size scaled to a different appunits per pixel (APP) ratio. * @param aFromAPP the APP to scale from @@ -44,11 +48,24 @@ inline mozilla::gfx::IntSize nsSize::ScaleToNearestPixels( aYScale)); } +inline mozilla::gfx::IntSize nsSize::ScaleToOutsidePixels( + float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const { + return mozilla::gfx::IntSize( + NSToIntCeil(NSAppUnitsToDoublePixels(width, aAppUnitsPerPixel) * aXScale), + NSToIntCeil(NSAppUnitsToDoublePixels(height, aAppUnitsPerPixel) * + aYScale)); +} + inline mozilla::gfx::IntSize nsSize::ToNearestPixels( nscoord aAppUnitsPerPixel) const { return ScaleToNearestPixels(1.0f, 1.0f, aAppUnitsPerPixel); } +inline mozilla::gfx::IntSize nsSize::ToOutsidePixels( + nscoord aAppUnitsPerPixel) const { + return ScaleToOutsidePixels(1.0f, 1.0f, aAppUnitsPerPixel); +} + inline nsSize nsSize::ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const { if (aFromAPP != aToAPP) { diff --git a/layout/base/Units.h b/layout/base/Units.h index 2240f65c6894..0aa3a5aab18a 100644 --- a/layout/base/Units.h +++ b/layout/base/Units.h @@ -429,6 +429,12 @@ struct LayoutDevicePixel { aRect.ToOutsidePixels(aAppUnitsPerDevPixel)); } + static LayoutDeviceIntSize FromAppUnitsToOutside( + const nsSize& aSize, nscoord aAppUnitsPerDevPixel) { + return LayoutDeviceIntSize::FromUnknownSize( + aSize.ToOutsidePixels(aAppUnitsPerDevPixel)); + } + static LayoutDeviceIntSize FromAppUnitsRounded(const nsSize& aSize, nscoord aAppUnitsPerDevPixel) { return LayoutDeviceIntSize( diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index e82300814b48..cb3e53c9347c 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -3136,8 +3136,10 @@ nsresult nsDocumentViewer::GetContentSizeInternal(int32_t* aWidth, // Ceil instead of rounding here, so we can actually guarantee showing all the // content. - *aWidth = std::ceil(presContext->AppUnitsToFloatDevPixels(shellArea.width)); - *aHeight = std::ceil(presContext->AppUnitsToFloatDevPixels(shellArea.height)); + auto devOuterSize = LayoutDeviceIntSize::FromAppUnitsToOutside( + shellArea, presContext->AppUnitsPerDevPixel()); + *aWidth = devOuterSize.width; + *aHeight = devOuterSize.height; return NS_OK; } diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 549cef84ff16..154d8f6841ca 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -551,18 +551,22 @@ static bool IsTopLevelWidget(nsIWidget* aWidget) { void nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, nsIFrame* aFrame, nsView* aView, gfxContext* aRC, uint32_t aFlags) { -#ifdef MOZ_XUL - if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget()) + if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget()) { return; + } nsCOMPtr windowWidget = GetPresContextContainerWidget(aPresContext); - if (!windowWidget || !IsTopLevelWidget(windowWidget)) return; + if (!windowWidget || !IsTopLevelWidget(windowWidget)) { + return; + } nsViewManager* vm = aView->GetViewManager(); nsView* rootView = vm->GetRootView(); - if (aView != rootView) return; + if (aView != rootView) { + return; + } Element* rootElement = aPresContext->Document()->GetRootElement(); if (!rootElement) { @@ -571,7 +575,9 @@ void nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, nsIFrame* rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame(); - if (!rootFrame) return; + if (!rootFrame) { + return; + } if (aFlags & SET_ASYNC) { aView->SetNeedsWindowPropertiesSync(); @@ -600,7 +606,9 @@ void nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, windowWidget->SetWindowShadowStyle(shadow); } - if (!aRC) return; + if (!aRC) { + return; + } if (!weak.IsAlive()) { return; @@ -608,36 +616,49 @@ void nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, nsSize minSize(0, 0); nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - if (rootElement->IsXULElement()) { - nsBoxLayoutState aState(aPresContext, aRC); - minSize = rootFrame->GetXULMinSize(aState); - maxSize = rootFrame->GetXULMaxSize(aState); - } else { - auto* pos = rootFrame->StylePosition(); - if (pos->mMinWidth.ConvertsToLength()) { - minSize.width = pos->mMinWidth.ToLength(); - } - if (pos->mMinHeight.ConvertsToLength()) { - minSize.height = pos->mMinHeight.ToLength(); - } - if (pos->mMaxWidth.ConvertsToLength()) { - maxSize.width = pos->mMaxWidth.ToLength(); - } - if (pos->mMaxHeight.ConvertsToLength()) { - maxSize.height = pos->mMaxHeight.ToLength(); + + const auto& pos = *rootFrame->StylePosition(); +#ifdef MOZ_XUL + if (rootFrame->IsXULBoxFrame()) { + nsBoxLayoutState xulState(aPresContext, aRC); + minSize = rootFrame->GetXULMinSize(xulState); + const bool isMinContent = + pos.mMinWidth.IsExtremumLength() && + pos.mMinWidth.AsExtremumLength() == StyleExtremumLength::MinContent; + // By default we behave with min-width: max-content, to ensure that all + // content is shown (i.e., the GetXULPrefSize call below). + // + // It can be overridden to min-content (i.e., leave GetXULMinSize()) and + // a fixed width (handled below). + if (!isMinContent && !pos.mMinWidth.ConvertsToLength()) { + minSize.width = rootFrame->GetXULPrefSize(xulState).width; } + maxSize = rootFrame->GetXULMaxSize(xulState); } - SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize); #endif + + if (pos.mMinWidth.ConvertsToLength()) { + minSize.width = pos.mMinWidth.ToLength(); + } + if (pos.mMinHeight.ConvertsToLength()) { + minSize.height = pos.mMinHeight.ToLength(); + } + if (pos.mMaxWidth.ConvertsToLength()) { + maxSize.width = pos.mMaxWidth.ToLength(); + } + if (pos.mMaxHeight.ConvertsToLength()) { + maxSize.height = pos.mMaxHeight.ToLength(); + } + + SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize); } void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext, nsIWidget* aWidget, const nsSize& aMinSize, const nsSize& aMaxSize) { - LayoutDeviceIntSize devMinSize( - aPresContext->AppUnitsToDevPixels(aMinSize.width), - aPresContext->AppUnitsToDevPixels(aMinSize.height)); + auto devMinSize = LayoutDeviceIntSize::FromAppUnitsToOutside( + aMinSize, aPresContext->AppUnitsPerDevPixel()); LayoutDeviceIntSize devMaxSize( aMaxSize.width == NS_UNCONSTRAINEDSIZE ? NS_MAXSIZE @@ -647,9 +668,8 @@ void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext, : aPresContext->AppUnitsToDevPixels(aMaxSize.height)); // MinSize has a priority over MaxSize - if (devMinSize.width > devMaxSize.width) devMaxSize.width = devMinSize.width; - if (devMinSize.height > devMaxSize.height) - devMaxSize.height = devMinSize.height; + devMaxSize.width = std::max(devMinSize.width, devMaxSize.width); + devMaxSize.height = std::max(devMinSize.height, devMaxSize.height); widget::SizeConstraints constraints(devMinSize, devMaxSize); diff --git a/toolkit/content/tests/chrome/window_screenPosSize.xhtml b/toolkit/content/tests/chrome/window_screenPosSize.xhtml index accc10d8f10b..cfb9f38343b1 100644 --- a/toolkit/content/tests/chrome/window_screenPosSize.xhtml +++ b/toolkit/content/tests/chrome/window_screenPosSize.xhtml @@ -8,6 +8,7 @@ screenY="80" height="300" width="300" + style="min-width: 0" persist="screenX screenY height width">