From c939c657ebe2e56a7e9f9062dbe919798c8951b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 24 Feb 2022 12:13:08 +0000 Subject: [PATCH] Bug 1733042 - Use scrollport size rather than content-box size of scrollframes. r=boris I don't really feel our behavior is particularly less correct than Chromium since in our implementation an scrolling element has two boxes rather than one, but in the interest of interop, and given developers find it useful, it seems worth doing this. Differential Revision: https://phabricator.services.mozilla.com/D139551 --- dom/base/ResizeObserver.cpp | 80 ++++++++++--------- .../meta/resize-observer/scrollbars.html.ini | 4 + .../tests/resize-observer/scrollbars.html | 56 +++++++++++++ 3 files changed, 102 insertions(+), 38 deletions(-) create mode 100644 testing/web-platform/meta/resize-observer/scrollbars.html.ini create mode 100644 testing/web-platform/tests/resize-observer/scrollbars.html diff --git a/dom/base/ResizeObserver.cpp b/dom/base/ResizeObserver.cpp index a1fb0ce6daf1..0b7bf9afe5cd 100644 --- a/dom/base/ResizeObserver.cpp +++ b/dom/base/ResizeObserver.cpp @@ -11,6 +11,7 @@ #include "mozilla/SVGUtils.h" #include "nsIContent.h" #include "nsIContentInlines.h" +#include "nsIScrollableFrame.h" #include namespace mozilla::dom { @@ -45,6 +46,14 @@ static uint32_t GetNodeDepth(nsINode* aNode) { return depth; } +static nsSize GetContentRectSize(const nsIFrame& aFrame) { + if (const nsIScrollableFrame* f = do_QueryFrame(&aFrame)) { + // We return the scrollport rect for compat with other UAs, see bug 1733042. + return f->GetScrollPortRect().Size(); + } + return aFrame.GetContentRectRelativeToSelf().Size(); +} + /** * Returns |aTarget|'s size in the form of gfx::Size (in pixels). * If the target is SVG, width and height are determined from bounding box. @@ -75,47 +84,42 @@ static gfx::Size CalculateBoxSize(Element* aTarget, const LayoutDeviceIntSize snappedSize = RoundedToInt(CSSSize::FromUnknownSize(size) * frame->PresContext()->CSSToDevPixelScale()); - size = gfx::Size(snappedSize.ToUnknownSize()); - } - } else { - // Per the spec, non-replaced inline Elements will always have an empty - // content rect. Therefore, we always use the same trivially-empty size - // for non-replaced inline elements here, and their IsActive() will - // always return false. (So its observation won't be fired.) - if (!frame->IsFrameOfType(nsIFrame::eReplaced) && - frame->IsFrameOfType(nsIFrame::eLineParticipant)) { - return size; - } - - switch (aBox) { - case ResizeObserverBoxOptions::Border_box: - // GetSize() includes the content area, borders, and padding. - size = CSSPixel::FromAppUnits(frame->GetSize()).ToUnknownSize(); - break; - case ResizeObserverBoxOptions::Device_pixel_content_box: { - // This is a implementation-dependent for subpixel snapping algorithm. - // Gecko relys on LayoutDevicePixel to convert (and snap) the app units - // into device pixels in painting and gfx code, so here we simply - // convert it into dev pixels and round it. - // - // Note: This size must contain integer values. - // https://drafts.csswg.org/resize-observer/#dom-resizeobserverboxoptions-device-pixel-content-box - const LayoutDeviceIntSize snappedSize = - LayoutDevicePixel::FromAppUnitsRounded( - frame->GetContentRectRelativeToSelf().Size(), - frame->PresContext()->AppUnitsPerDevPixel()); - size = gfx::Size(snappedSize.ToUnknownSize()); - break; - } - case ResizeObserverBoxOptions::Content_box: - default: - size = - CSSPixel::FromAppUnits(frame->GetContentRectRelativeToSelf().Size()) - .ToUnknownSize(); + return gfx::Size(snappedSize.ToUnknownSize()); } + return size; } - return size; + // Per the spec, non-replaced inline Elements will always have an empty + // content rect. Therefore, we always use the same trivially-empty size + // for non-replaced inline elements here, and their IsActive() will + // always return false. (So its observation won't be fired.) + if (!frame->IsFrameOfType(nsIFrame::eReplaced) && + frame->IsFrameOfType(nsIFrame::eLineParticipant)) { + return size; + } + + switch (aBox) { + case ResizeObserverBoxOptions::Border_box: + return CSSPixel::FromAppUnits(frame->GetSize()).ToUnknownSize(); + case ResizeObserverBoxOptions::Device_pixel_content_box: { + // This is a implementation-dependent for subpixel snapping algorithm. + // Gecko relies on LayoutDevicePixel to convert (and snap) the app units + // into device pixels in painting and gfx code, so here we simply + // convert it into dev pixels and round it. + // + // Note: This size must contain integer values. + // https://drafts.csswg.org/resize-observer/#dom-resizeobserverboxoptions-device-pixel-content-box + const LayoutDeviceIntSize snappedSize = + LayoutDevicePixel::FromAppUnitsRounded( + GetContentRectSize(*frame), + frame->PresContext()->AppUnitsPerDevPixel()); + return gfx::Size(snappedSize.ToUnknownSize()); + } + case ResizeObserverBoxOptions::Content_box: + default: + break; + } + return CSSPixel::FromAppUnits(GetContentRectSize(*frame)).ToUnknownSize(); } NS_IMPL_CYCLE_COLLECTION_CLASS(ResizeObservation) diff --git a/testing/web-platform/meta/resize-observer/scrollbars.html.ini b/testing/web-platform/meta/resize-observer/scrollbars.html.ini new file mode 100644 index 000000000000..002f1af5046c --- /dev/null +++ b/testing/web-platform/meta/resize-observer/scrollbars.html.ini @@ -0,0 +1,4 @@ +[scrollbars.html] + bug: expected to fail with overlay scrollbars + expected: + if os == "android": FAIL diff --git a/testing/web-platform/tests/resize-observer/scrollbars.html b/testing/web-platform/tests/resize-observer/scrollbars.html new file mode 100644 index 000000000000..129e74e5cd96 --- /dev/null +++ b/testing/web-platform/tests/resize-observer/scrollbars.html @@ -0,0 +1,56 @@ + +ResizeObserver content-box size and scrollbars + + + + +
+
+
+