1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/ResizeObserver.h"
9 #include "mozilla/dom/DOMRect.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/SVGUtils.h"
12 #include "nsIContent.h"
13 #include "nsIContentInlines.h"
14 #include "nsIScrollableFrame.h"
15 #include "nsLayoutUtils.h"
18 namespace mozilla::dom
{
21 * Returns the length of the parent-traversal path (in terms of the number of
22 * nodes) to an unparented/root node from aNode. An unparented/root node is
23 * considered to have a depth of 1, its children have a depth of 2, etc.
24 * aNode is expected to be non-null.
25 * Note: The shadow root is not part of the calculation because the caller,
26 * ResizeObserver, doesn't observe the shadow root, and only needs relative
27 * depths among all the observed targets. In other words, we calculate the
28 * depth of the flattened tree.
30 * However, these is a spec issue about how to handle shadow DOM case. We
31 * may need to update this function later:
32 * https://github.com/w3c/csswg-drafts/issues/3840
34 * https://drafts.csswg.org/resize-observer/#calculate-depth-for-node-h
36 static uint32_t GetNodeDepth(nsINode
* aNode
) {
39 MOZ_ASSERT(aNode
, "Node shouldn't be null");
41 // Use GetFlattenedTreeParentNode to bypass the shadow root and cross the
42 // shadow boundary to calculate the node depth without the shadow root.
43 while ((aNode
= aNode
->GetFlattenedTreeParentNode())) {
50 static nsSize
GetContentRectSize(const nsIFrame
& aFrame
) {
51 if (const nsIScrollableFrame
* f
= do_QueryFrame(&aFrame
)) {
52 // We return the scrollport rect for compat with other UAs, see bug 1733042.
53 // But the scrollPort includes padding (but not border!), so remove it.
54 nsRect scrollPort
= f
->GetScrollPortRect();
56 aFrame
.GetUsedPadding().ApplySkipSides(aFrame
.GetSkipSides());
57 scrollPort
.Deflate(padding
);
58 // This can break in some edge cases like when layout overflows sizes or
61 !aFrame
.PresContext()->UseOverlayScrollbars() ||
62 scrollPort
.Size() == aFrame
.GetContentRectRelativeToSelf().Size(),
64 return scrollPort
.Size();
66 return aFrame
.GetContentRectRelativeToSelf().Size();
70 * Returns |aTarget|'s size in the form of gfx::Size (in pixels).
71 * If the target is an SVG that does not participate in CSS layout,
72 * its width and height are determined from bounding box.
74 * https://www.w3.org/TR/resize-observer-1/#calculate-box-size
76 static AutoTArray
<LogicalPixelSize
, 1> CalculateBoxSize(
77 Element
* aTarget
, ResizeObserverBoxOptions aBox
,
78 const ResizeObserver
& aObserver
) {
79 nsIFrame
* frame
= aTarget
->GetPrimaryFrame();
82 // TODO: Should this return an empty array instead?
83 // https://github.com/w3c/csswg-drafts/issues/7734
84 return {LogicalPixelSize()};
87 if (frame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
88 // Per the spec, this target's SVG size is always its bounding box size no
89 // matter what box option you choose, because SVG elements do not use
90 // standard CSS box model.
91 // TODO: what if the SVG is fragmented?
92 // https://github.com/w3c/csswg-drafts/issues/7736
93 const gfxRect bbox
= SVGUtils::GetBBox(frame
);
94 gfx::Size
size(static_cast<float>(bbox
.width
),
95 static_cast<float>(bbox
.height
));
96 const WritingMode wm
= frame
->GetWritingMode();
97 if (aBox
== ResizeObserverBoxOptions::Device_pixel_content_box
) {
98 // Per spec, we calculate the inline/block sizes to target’s bounding box
99 // {inline|block} length, in integral device pixels, so we round the final
101 // https://drafts.csswg.org/resize-observer/#dom-resizeobserverboxoptions-device-pixel-content-box
102 const LayoutDeviceIntSize snappedSize
=
103 RoundedToInt(CSSSize::FromUnknownSize(size
) *
104 frame
->PresContext()->CSSToDevPixelScale());
105 return {LogicalPixelSize(wm
, gfx::Size(snappedSize
.ToUnknownSize()))};
107 return {LogicalPixelSize(wm
, size
)};
110 // Per the spec, non-replaced inline Elements will always have an empty
111 // content rect. Therefore, we always use the same trivially-empty size
112 // for non-replaced inline elements here, and their IsActive() will
113 // always return false. (So its observation won't be fired.)
114 // TODO: Should we use an empty array instead?
115 // https://github.com/w3c/csswg-drafts/issues/7734
116 if (!frame
->IsReplaced() && frame
->IsLineParticipant()) {
117 return {LogicalPixelSize()};
120 auto GetFrameSize
= [aBox
](nsIFrame
* aFrame
) {
122 case ResizeObserverBoxOptions::Border_box
:
123 return CSSPixel::FromAppUnits(aFrame
->GetSize()).ToUnknownSize();
124 case ResizeObserverBoxOptions::Device_pixel_content_box
: {
125 // Simply converting from app units to device units is insufficient - we
126 // need to take subpixel snapping into account. Subpixel snapping
127 // happens with respect to the reference frame, so do the dev pixel
128 // conversion with our rectangle positioned relative to the reference
129 // frame, then get the size from there.
130 const auto* referenceFrame
= nsLayoutUtils::GetReferenceFrame(aFrame
);
131 // GetOffsetToCrossDoc version handles <iframe>s in addition to normal
132 // cases. We don't expect this to tight loop for additional checks to
134 const auto offset
= aFrame
->GetOffsetToCrossDoc(referenceFrame
);
135 const auto contentSize
= GetContentRectSize(*aFrame
);
136 // Casting to double here is deliberate to minimize rounding error in
137 // upcoming operations.
138 const auto appUnitsPerDevPixel
=
139 static_cast<double>(aFrame
->PresContext()->AppUnitsPerDevPixel());
140 // Calculation here is a greatly simplified version of
141 // `NSRectToSnappedRect` as 1) we're not actually drawing (i.e. no draw
142 // target), and 2) transform does not need to be taken into account.
143 gfx::Rect rect
{gfx::Float(offset
.X() / appUnitsPerDevPixel
),
144 gfx::Float(offset
.Y() / appUnitsPerDevPixel
),
145 gfx::Float(contentSize
.Width() / appUnitsPerDevPixel
),
146 gfx::Float(contentSize
.Height() / appUnitsPerDevPixel
)};
147 gfx::Point tl
= rect
.TopLeft().Round();
148 gfx::Point br
= rect
.BottomRight().Round();
150 rect
.SizeTo(gfx::Size(br
.x
- tl
.x
, br
.y
- tl
.y
));
151 rect
.NudgeToIntegers();
152 return rect
.Size().ToUnknownSize();
154 case ResizeObserverBoxOptions::Content_box
:
158 return CSSPixel::FromAppUnits(GetContentRectSize(*aFrame
)).ToUnknownSize();
160 if (!StaticPrefs::dom_resize_observer_support_fragments() &&
161 !aObserver
.HasNativeCallback()) {
162 return {LogicalPixelSize(frame
->GetWritingMode(), GetFrameSize(frame
))};
164 AutoTArray
<LogicalPixelSize
, 1> size
;
165 for (nsIFrame
* cur
= frame
; cur
; cur
= cur
->GetNextContinuation()) {
166 const WritingMode wm
= cur
->GetWritingMode();
167 size
.AppendElement(LogicalPixelSize(wm
, GetFrameSize(cur
)));
172 NS_IMPL_CYCLE_COLLECTION_CLASS(ResizeObservation
)
174 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ResizeObservation
)
175 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget
);
176 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
178 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ResizeObservation
)
179 tmp
->Unlink(RemoveFromObserver::Yes
);
180 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
182 ResizeObservation::ResizeObservation(Element
& aTarget
,
183 ResizeObserver
& aObserver
,
184 ResizeObserverBoxOptions aBox
)
186 mObserver(&aObserver
),
188 mLastReportedSize({LogicalPixelSize(WritingMode(), gfx::Size(-1, -1))}) {
189 aTarget
.BindObject(mObserver
);
192 void ResizeObservation::Unlink(RemoveFromObserver aRemoveFromObserver
) {
193 ResizeObserver
* observer
= std::exchange(mObserver
, nullptr);
194 nsCOMPtr
<Element
> target
= std::move(mTarget
);
195 if (observer
&& target
) {
196 if (aRemoveFromObserver
== RemoveFromObserver::Yes
) {
197 observer
->Unobserve(*target
);
199 target
->UnbindObject(observer
);
203 bool ResizeObservation::IsActive() const {
204 // As detailed in the css-contain specification, if the target is hidden by
205 // `content-visibility` it should not call its ResizeObservation callbacks.
206 nsIFrame
* frame
= mTarget
->GetPrimaryFrame();
207 if (frame
&& frame
->IsHiddenByContentVisibilityOnAnyAncestor()) {
211 return mLastReportedSize
!=
212 CalculateBoxSize(mTarget
, mObservedBox
, *mObserver
);
215 void ResizeObservation::UpdateLastReportedSize(
216 const nsTArray
<LogicalPixelSize
>& aSize
) {
217 mLastReportedSize
.Assign(aSize
);
220 // Only needed for refcounted objects.
221 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ResizeObserver
)
223 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ResizeObserver
)
224 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner
, mDocument
, mActiveTargets
,
226 if (tmp
->mCallback
.is
<RefPtr
<ResizeObserverCallback
>>()) {
227 ImplCycleCollectionTraverse(
228 cb
, tmp
->mCallback
.as
<RefPtr
<ResizeObserverCallback
>>(), "mCallback",
231 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
233 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ResizeObserver
)
235 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner
, mDocument
, mActiveTargets
,
237 if (tmp
->mCallback
.is
<RefPtr
<ResizeObserverCallback
>>()) {
238 ImplCycleCollectionUnlink(
239 tmp
->mCallback
.as
<RefPtr
<ResizeObserverCallback
>>());
241 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
242 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
244 NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserver
)
245 NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserver
)
246 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserver
)
247 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
248 NS_INTERFACE_MAP_ENTRY(nsISupports
)
251 ResizeObserver::ResizeObserver(Document
& aDocument
, NativeCallback aCallback
)
252 : mOwner(aDocument
.GetInnerWindow()),
253 mDocument(&aDocument
),
254 mCallback(aCallback
) {
255 MOZ_ASSERT(mOwner
, "Need a non-null owner window");
256 MOZ_ASSERT(mDocument
== mOwner
->GetExtantDoc());
259 already_AddRefed
<ResizeObserver
> ResizeObserver::Constructor(
260 const GlobalObject
& aGlobal
, ResizeObserverCallback
& aCb
,
262 nsCOMPtr
<nsPIDOMWindowInner
> window
=
263 do_QueryInterface(aGlobal
.GetAsSupports());
265 aRv
.Throw(NS_ERROR_FAILURE
);
269 Document
* doc
= window
->GetExtantDoc();
271 aRv
.Throw(NS_ERROR_FAILURE
);
275 return do_AddRef(new ResizeObserver(std::move(window
), doc
, aCb
));
278 void ResizeObserver::Observe(Element
& aTarget
,
279 const ResizeObserverOptions
& aOptions
) {
280 if (MOZ_UNLIKELY(!mDocument
)) {
281 MOZ_ASSERT_UNREACHABLE("How did we call observe() after unlink?");
285 // NOTE(emilio): Per spec, this is supposed to happen on construction, but the
286 // spec isn't particularly sane here, see
287 // https://github.com/w3c/csswg-drafts/issues/4518
288 if (mObservationList
.isEmpty()) {
289 MOZ_ASSERT(mObservationMap
.IsEmpty());
290 mDocument
->AddResizeObserver(*this);
293 auto& observation
= mObservationMap
.LookupOrInsert(&aTarget
);
295 if (observation
->BoxOptions() == aOptions
.mBox
) {
296 // Already observed this target and the observed box is the same, so
298 // Note: Based on the spec, we should unobserve it first. However,
299 // calling Unobserve() when we observe the same box will remove original
300 // ResizeObservation and then add a new one, this may cause an unexpected
301 // result because ResizeObservation stores the mLastReportedSize which
302 // should be kept to make sure IsActive() returns the correct result.
305 // Remove the pre-existing entry, but without unregistering ourselves from
307 observation
->remove();
308 observation
= nullptr;
311 observation
= new ResizeObservation(aTarget
, *this, aOptions
.mBox
);
312 mObservationList
.insertBack(observation
);
314 // Per the spec, we need to trigger notification in event loop that
315 // contains ResizeObserver observe call even when resize/reflow does
317 mDocument
->ScheduleResizeObserversNotification();
320 void ResizeObserver::Unobserve(Element
& aTarget
) {
321 RefPtr
<ResizeObservation
> observation
;
322 if (!mObservationMap
.Remove(&aTarget
, getter_AddRefs(observation
))) {
326 MOZ_ASSERT(!mObservationList
.isEmpty(),
327 "If ResizeObservation found for an element, observation list "
328 "must be not empty.");
329 observation
->remove();
330 if (mObservationList
.isEmpty()) {
331 if (MOZ_LIKELY(mDocument
)) {
332 mDocument
->RemoveResizeObserver(*this);
337 void ResizeObserver::Disconnect() {
338 const bool registered
= !mObservationList
.isEmpty();
339 while (auto* observation
= mObservationList
.popFirst()) {
340 observation
->Unlink(ResizeObservation::RemoveFromObserver::No
);
342 MOZ_ASSERT(mObservationList
.isEmpty());
343 mObservationMap
.Clear();
344 mActiveTargets
.Clear();
345 if (registered
&& MOZ_LIKELY(mDocument
)) {
346 mDocument
->RemoveResizeObserver(*this);
350 void ResizeObserver::GatherActiveObservations(uint32_t aDepth
) {
351 mActiveTargets
.Clear();
352 mHasSkippedTargets
= false;
354 for (auto* observation
: mObservationList
) {
355 if (!observation
->IsActive()) {
359 uint32_t targetDepth
= GetNodeDepth(observation
->Target());
361 if (targetDepth
> aDepth
) {
362 mActiveTargets
.AppendElement(observation
);
364 // This boolean is only used to indicate we will deliver resize loop error
365 // notification later on. However, we don't want to do that for our
366 // internal observers.
367 if (!HasNativeCallback()) {
368 mHasSkippedTargets
= true;
374 uint32_t ResizeObserver::BroadcastActiveObservations() {
375 uint32_t shallowestTargetDepth
= std::numeric_limits
<uint32_t>::max();
377 if (!HasActiveObservations()) {
378 return shallowestTargetDepth
;
381 Sequence
<OwningNonNull
<ResizeObserverEntry
>> entries
;
383 for (auto& observation
: mActiveTargets
) {
384 Element
* target
= observation
->Target();
387 CalculateBoxSize(target
, ResizeObserverBoxOptions::Border_box
, *this);
388 auto contentBoxSize
=
389 CalculateBoxSize(target
, ResizeObserverBoxOptions::Content_box
, *this);
390 auto devicePixelContentBoxSize
= CalculateBoxSize(
391 target
, ResizeObserverBoxOptions::Device_pixel_content_box
, *this);
392 RefPtr
<ResizeObserverEntry
> entry
=
393 new ResizeObserverEntry(mOwner
, *target
, borderBoxSize
, contentBoxSize
,
394 devicePixelContentBoxSize
);
396 if (!entries
.AppendElement(entry
.forget(), fallible
)) {
401 // Sync the broadcast size of observation so the next size inspection
402 // will be based on the updated size from last delivered observations.
403 switch (observation
->BoxOptions()) {
404 case ResizeObserverBoxOptions::Border_box
:
405 observation
->UpdateLastReportedSize(borderBoxSize
);
407 case ResizeObserverBoxOptions::Device_pixel_content_box
:
408 observation
->UpdateLastReportedSize(devicePixelContentBoxSize
);
410 case ResizeObserverBoxOptions::Content_box
:
412 observation
->UpdateLastReportedSize(contentBoxSize
);
415 uint32_t targetDepth
= GetNodeDepth(observation
->Target());
417 if (targetDepth
< shallowestTargetDepth
) {
418 shallowestTargetDepth
= targetDepth
;
422 if (mCallback
.is
<RefPtr
<ResizeObserverCallback
>>()) {
423 RefPtr
<ResizeObserverCallback
> callback(
424 mCallback
.as
<RefPtr
<ResizeObserverCallback
>>());
425 callback
->Call(this, entries
, *this);
427 mCallback
.as
<NativeCallback
>()(entries
, *this);
430 mActiveTargets
.Clear();
431 mHasSkippedTargets
= false;
433 return shallowestTargetDepth
;
436 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverEntry
, mOwner
, mTarget
,
437 mContentRect
, mBorderBoxSize
,
439 mDevicePixelContentBoxSize
)
440 NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverEntry
)
441 NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverEntry
)
442 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverEntry
)
443 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
444 NS_INTERFACE_MAP_ENTRY(nsISupports
)
447 void ResizeObserverEntry::GetBorderBoxSize(
448 nsTArray
<RefPtr
<ResizeObserverSize
>>& aRetVal
) const {
449 // In the resize-observer-1 spec, there will only be a single
450 // ResizeObserverSize returned in the FrozenArray for now.
452 // Note: the usage of FrozenArray is to support elements that have multiple
453 // fragments, which occur in multi-column scenarios.
454 // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
455 aRetVal
.Assign(mBorderBoxSize
);
458 void ResizeObserverEntry::GetContentBoxSize(
459 nsTArray
<RefPtr
<ResizeObserverSize
>>& aRetVal
) const {
460 // In the resize-observer-1 spec, there will only be a single
461 // ResizeObserverSize returned in the FrozenArray for now.
463 // Note: the usage of FrozenArray is to support elements that have multiple
464 // fragments, which occur in multi-column scenarios.
465 // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
466 aRetVal
.Assign(mContentBoxSize
);
469 void ResizeObserverEntry::GetDevicePixelContentBoxSize(
470 nsTArray
<RefPtr
<ResizeObserverSize
>>& aRetVal
) const {
471 // In the resize-observer-1 spec, there will only be a single
472 // ResizeObserverSize returned in the FrozenArray for now.
474 // Note: the usage of FrozenArray is to support elements that have multiple
475 // fragments, which occur in multi-column scenarios.
476 // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
477 aRetVal
.Assign(mDevicePixelContentBoxSize
);
480 void ResizeObserverEntry::SetBorderBoxSize(
481 const nsTArray
<LogicalPixelSize
>& aSize
) {
482 mBorderBoxSize
.Clear();
483 mBorderBoxSize
.SetCapacity(aSize
.Length());
484 for (const LogicalPixelSize
& size
: aSize
) {
485 mBorderBoxSize
.AppendElement(new ResizeObserverSize(mOwner
, size
));
489 void ResizeObserverEntry::SetContentRectAndSize(
490 const nsTArray
<LogicalPixelSize
>& aSize
) {
491 nsIFrame
* frame
= mTarget
->GetPrimaryFrame();
493 // 1. Update mContentRect.
494 nsMargin padding
= frame
? frame
->GetUsedPadding() : nsMargin();
495 // Per the spec, we need to use the top-left padding offset as the origin of
497 gfx::Size sizeForRect
;
498 MOZ_DIAGNOSTIC_ASSERT(!aSize
.IsEmpty());
499 if (!aSize
.IsEmpty()) {
500 const WritingMode wm
= frame
? frame
->GetWritingMode() : WritingMode();
501 sizeForRect
= aSize
[0].PhysicalSize(wm
);
503 nsRect
rect(nsPoint(padding
.left
, padding
.top
),
504 CSSPixel::ToAppUnits(CSSSize::FromUnknownSize(sizeForRect
)));
505 RefPtr
<DOMRect
> contentRect
= new DOMRect(mOwner
);
506 contentRect
->SetLayoutRect(rect
);
507 mContentRect
= std::move(contentRect
);
509 // 2. Update mContentBoxSize.
510 mContentBoxSize
.Clear();
511 mContentBoxSize
.SetCapacity(aSize
.Length());
512 for (const LogicalPixelSize
& size
: aSize
) {
513 mContentBoxSize
.AppendElement(new ResizeObserverSize(mOwner
, size
));
517 void ResizeObserverEntry::SetDevicePixelContentSize(
518 const nsTArray
<LogicalPixelSize
>& aSize
) {
519 mDevicePixelContentBoxSize
.Clear();
520 mDevicePixelContentBoxSize
.SetCapacity(aSize
.Length());
521 for (const LogicalPixelSize
& size
: aSize
) {
522 mDevicePixelContentBoxSize
.AppendElement(
523 new ResizeObserverSize(mOwner
, size
));
527 static void LastRememberedSizeCallback(
528 const Sequence
<OwningNonNull
<ResizeObserverEntry
>>& aEntries
,
529 ResizeObserver
& aObserver
) {
530 for (const auto& entry
: aEntries
) {
531 Element
* target
= entry
->Target();
532 if (!target
->IsInComposedDoc()) {
533 aObserver
.Unobserve(*target
);
534 target
->RemoveLastRememberedBSize();
535 target
->RemoveLastRememberedISize();
538 nsIFrame
* frame
= target
->GetPrimaryFrame();
540 aObserver
.Unobserve(*target
);
543 MOZ_ASSERT(!frame
->IsLineParticipant() || frame
->IsReplaced(),
544 "Should have unobserved non-replaced inline.");
545 MOZ_ASSERT(!frame
->HidesContent(),
546 "Should have unobserved element skipping its contents.");
547 const nsStylePosition
* stylePos
= frame
->StylePosition();
548 const WritingMode wm
= frame
->GetWritingMode();
549 bool canUpdateBSize
= stylePos
->ContainIntrinsicBSize(wm
).HasAuto();
550 bool canUpdateISize
= stylePos
->ContainIntrinsicISize(wm
).HasAuto();
551 MOZ_ASSERT(canUpdateBSize
|| !target
->HasLastRememberedBSize(),
552 "Should have removed the last remembered block size.");
553 MOZ_ASSERT(canUpdateISize
|| !target
->HasLastRememberedISize(),
554 "Should have removed the last remembered inline size.");
555 MOZ_ASSERT(canUpdateBSize
|| canUpdateISize
,
556 "Should have unobserved if we can't update any size.");
557 AutoTArray
<RefPtr
<ResizeObserverSize
>, 1> contentSizeList
;
558 entry
->GetContentBoxSize(contentSizeList
);
559 MOZ_ASSERT(!contentSizeList
.IsEmpty());
560 if (canUpdateBSize
) {
562 for (const auto& current
: contentSizeList
) {
563 bSize
+= current
->BlockSize();
565 target
->SetLastRememberedBSize(bSize
);
567 if (canUpdateISize
) {
569 for (const auto& current
: contentSizeList
) {
570 iSize
= std::max(iSize
, current
->InlineSize());
572 target
->SetLastRememberedISize(iSize
);
577 /* static */ already_AddRefed
<ResizeObserver
>
578 ResizeObserver::CreateLastRememberedSizeObserver(Document
& aDocument
) {
579 return do_AddRef(new ResizeObserver(aDocument
, LastRememberedSizeCallback
));
582 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverSize
, mOwner
)
583 NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverSize
)
584 NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverSize
)
585 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverSize
)
586 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
587 NS_INTERFACE_MAP_ENTRY(nsISupports
)
590 } // namespace mozilla::dom