From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- dom/base/ResizeObserver.cpp | 590 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 590 insertions(+) create mode 100644 dom/base/ResizeObserver.cpp (limited to 'dom/base/ResizeObserver.cpp') diff --git a/dom/base/ResizeObserver.cpp b/dom/base/ResizeObserver.cpp new file mode 100644 index 0000000000..77472179c8 --- /dev/null +++ b/dom/base/ResizeObserver.cpp @@ -0,0 +1,590 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/ResizeObserver.h" + +#include "mozilla/dom/DOMRect.h" +#include "mozilla/dom/Document.h" +#include "mozilla/SVGUtils.h" +#include "nsIContent.h" +#include "nsIContentInlines.h" +#include "nsIScrollableFrame.h" +#include "nsLayoutUtils.h" +#include + +namespace mozilla::dom { + +/** + * Returns the length of the parent-traversal path (in terms of the number of + * nodes) to an unparented/root node from aNode. An unparented/root node is + * considered to have a depth of 1, its children have a depth of 2, etc. + * aNode is expected to be non-null. + * Note: The shadow root is not part of the calculation because the caller, + * ResizeObserver, doesn't observe the shadow root, and only needs relative + * depths among all the observed targets. In other words, we calculate the + * depth of the flattened tree. + * + * However, these is a spec issue about how to handle shadow DOM case. We + * may need to update this function later: + * https://github.com/w3c/csswg-drafts/issues/3840 + * + * https://drafts.csswg.org/resize-observer/#calculate-depth-for-node-h + */ +static uint32_t GetNodeDepth(nsINode* aNode) { + uint32_t depth = 1; + + MOZ_ASSERT(aNode, "Node shouldn't be null"); + + // Use GetFlattenedTreeParentNode to bypass the shadow root and cross the + // shadow boundary to calculate the node depth without the shadow root. + while ((aNode = aNode->GetFlattenedTreeParentNode())) { + ++depth; + } + + 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. + // But the scrollPort includes padding (but not border!), so remove it. + nsRect scrollPort = f->GetScrollPortRect(); + nsMargin padding = + aFrame.GetUsedPadding().ApplySkipSides(aFrame.GetSkipSides()); + scrollPort.Deflate(padding); + // This can break in some edge cases like when layout overflows sizes or + // what not. + NS_ASSERTION( + !aFrame.PresContext()->UseOverlayScrollbars() || + scrollPort.Size() == aFrame.GetContentRectRelativeToSelf().Size(), + "Wrong scrollport?"); + return scrollPort.Size(); + } + return aFrame.GetContentRectRelativeToSelf().Size(); +} + +/** + * Returns |aTarget|'s size in the form of gfx::Size (in pixels). + * If the target is an SVG that does not participate in CSS layout, + * its width and height are determined from bounding box. + * + * https://www.w3.org/TR/resize-observer-1/#calculate-box-size + */ +static AutoTArray CalculateBoxSize( + Element* aTarget, ResizeObserverBoxOptions aBox, + const ResizeObserver& aObserver) { + nsIFrame* frame = aTarget->GetPrimaryFrame(); + + if (!frame) { + // TODO: Should this return an empty array instead? + // https://github.com/w3c/csswg-drafts/issues/7734 + return {LogicalPixelSize()}; + } + + if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { + // Per the spec, this target's SVG size is always its bounding box size no + // matter what box option you choose, because SVG elements do not use + // standard CSS box model. + // TODO: what if the SVG is fragmented? + // https://github.com/w3c/csswg-drafts/issues/7736 + const gfxRect bbox = SVGUtils::GetBBox(frame); + gfx::Size size(static_cast(bbox.width), + static_cast(bbox.height)); + const WritingMode wm = frame->GetWritingMode(); + if (aBox == ResizeObserverBoxOptions::Device_pixel_content_box) { + // Per spec, we calculate the inline/block sizes to target’s bounding box + // {inline|block} length, in integral device pixels, so we round the final + // result. + // https://drafts.csswg.org/resize-observer/#dom-resizeobserverboxoptions-device-pixel-content-box + const LayoutDeviceIntSize snappedSize = + RoundedToInt(CSSSize::FromUnknownSize(size) * + frame->PresContext()->CSSToDevPixelScale()); + return {LogicalPixelSize(wm, gfx::Size(snappedSize.ToUnknownSize()))}; + } + return {LogicalPixelSize(wm, 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.) + // TODO: Should we use an empty array instead? + // https://github.com/w3c/csswg-drafts/issues/7734 + if (!frame->IsReplaced() && frame->IsLineParticipant()) { + return {LogicalPixelSize()}; + } + + auto GetFrameSize = [aBox](nsIFrame* aFrame) { + switch (aBox) { + case ResizeObserverBoxOptions::Border_box: + return CSSPixel::FromAppUnits(aFrame->GetSize()).ToUnknownSize(); + case ResizeObserverBoxOptions::Device_pixel_content_box: { + // Simply converting from app units to device units is insufficient - we + // need to take subpixel snapping into account. Subpixel snapping + // happens with respect to the reference frame, so do the dev pixel + // conversion with our rectangle positioned relative to the reference + // frame, then get the size from there. + const auto* referenceFrame = nsLayoutUtils::GetReferenceFrame(aFrame); + // GetOffsetToCrossDoc version handles