/* -*- 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/. */
/** * 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 ScrollContainerFrame* 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();
}
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<float>(bbox.width), static_cast<float>(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. constauto* referenceFrame = nsLayoutUtils::GetReferenceFrame(aFrame); // GetOffsetToCrossDoc version handles <iframe>s in addition to normal // cases. We don't expect this to tight loop for additional checks to // matter. constauto offset = aFrame->GetOffsetToCrossDoc(referenceFrame); constauto contentSize = GetContentRectSize(*aFrame); // Casting to double here is deliberate to minimize rounding error in // upcoming operations. constauto appUnitsPerDevPixel = static_cast<double>(aFrame->PresContext()->AppUnitsPerDevPixel()); // Calculation here is a greatly simplified version of // `NSRectToSnappedRect` as 1) we're not actually drawing (i.e. no draw // target), and 2) transform does not need to be taken into account.
gfx::Rect rect{gfx::Float(offset.X() / appUnitsPerDevPixel),
gfx::Float(offset.Y() / appUnitsPerDevPixel),
gfx::Float(contentSize.Width() / appUnitsPerDevPixel),
gfx::Float(contentSize.Height() / appUnitsPerDevPixel)};
gfx::Point tl = rect.TopLeft().Round();
gfx::Point br = rect.BottomRight().Round();
bool ResizeObservation::IsActive() const { // As detailed in the css-contain specification, if the target is hidden by // `content-visibility` it should not call its ResizeObservation callbacks.
nsIFrame* frame = mTarget->GetPrimaryFrame(); if (frame && frame->IsHiddenByContentVisibilityOnAnyAncestor()) { returnfalse;
}
void ResizeObserver::Observe(Element& aTarget, const ResizeObserverOptions& aOptions) { if (MOZ_UNLIKELY(!mDocument)) {
MOZ_ASSERT_UNREACHABLE("How did we call observe() after unlink?"); return;
}
// NOTE(emilio): Per spec, this is supposed to happen on construction, but the // spec isn't particularly sane here, see // https://github.com/w3c/csswg-drafts/issues/4518 if (mObservationList.isEmpty()) {
MOZ_ASSERT(mObservationMap.IsEmpty());
mDocument->AddResizeObserver(*this);
}
auto& observation = mObservationMap.LookupOrInsert(&aTarget); if (observation) { if (observation->BoxOptions() == aOptions.mBox) { // Already observed this target and the observed box is the same, so // return. // Note: Based on the spec, we should unobserve it first. However, // calling Unobserve() when we observe the same box will remove original // ResizeObservation and then add a new one, this may cause an unexpected // result because ResizeObservation stores the mLastReportedSize which // should be kept to make sure IsActive() returns the correct result. return;
} // Remove the pre-existing entry, but without unregistering ourselves from // the controller.
observation->remove();
observation = nullptr;
}
observation = new ResizeObservation(aTarget, *this, aOptions.mBox);
mObservationList.insertBack(observation);
// Per the spec, we need to trigger notification in event loop that // contains ResizeObserver observe call even when resize/reflow does // not happen.
mDocument->ScheduleResizeObserversNotification();
}
MOZ_ASSERT(!mObservationList.isEmpty(), "If ResizeObservation found for an element, observation list " "must be not empty.");
observation->remove(); if (mObservationList.isEmpty()) { if (MOZ_LIKELY(mDocument)) {
mDocument->RemoveResizeObserver(*this);
}
}
}
for (auto& observation : mActiveTargets) {
Element* target = observation->Target();
auto borderBoxSize = ResizeObserver::CalculateBoxSize(
target, ResizeObserverBoxOptions::Border_box); auto contentBoxSize = ResizeObserver::CalculateBoxSize(
target, ResizeObserverBoxOptions::Content_box); auto devicePixelContentBoxSize = ResizeObserver::CalculateBoxSize(
target, ResizeObserverBoxOptions::Device_pixel_content_box);
RefPtr<ResizeObserverEntry> entry = new ResizeObserverEntry(mOwner, *target, borderBoxSize, contentBoxSize,
devicePixelContentBoxSize);
if (!entries.AppendElement(entry.forget(), fallible)) { // Out of memory. break;
}
// Sync the broadcast size of observation so the next size inspection // will be based on the updated size from last delivered observations. switch (observation->BoxOptions()) { case ResizeObserverBoxOptions::Border_box:
observation->UpdateLastReportedSize(borderBoxSize); break; case ResizeObserverBoxOptions::Device_pixel_content_box:
observation->UpdateLastReportedSize(devicePixelContentBoxSize); break; case ResizeObserverBoxOptions::Content_box: default:
observation->UpdateLastReportedSize(contentBoxSize);
}
void ResizeObserverEntry::GetBorderBoxSize(
nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const { // In the resize-observer-1 spec, there will only be a single // ResizeObserverSize returned in the FrozenArray for now. // // Note: the usage of FrozenArray is to support elements that have multiple // fragments, which occur in multi-column scenarios. // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
aRetVal.Assign(mBorderBoxSize);
}
void ResizeObserverEntry::GetContentBoxSize(
nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const { // In the resize-observer-1 spec, there will only be a single // ResizeObserverSize returned in the FrozenArray for now. // // Note: the usage of FrozenArray is to support elements that have multiple // fragments, which occur in multi-column scenarios. // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
aRetVal.Assign(mContentBoxSize);
}
void ResizeObserverEntry::GetDevicePixelContentBoxSize(
nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const { // In the resize-observer-1 spec, there will only be a single // ResizeObserverSize returned in the FrozenArray for now. // // Note: the usage of FrozenArray is to support elements that have multiple // fragments, which occur in multi-column scenarios. // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
aRetVal.Assign(mDevicePixelContentBoxSize);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.