Bug 1842773 - Part 5: Add ArrayBuffer.prototype.{maxByteLength,resizable} getters...
[gecko.git] / dom / base / ResizeObserver.cpp
blob77472179c877d90e5b03495824551a7bc40b1ac3
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"
16 #include <limits>
18 namespace mozilla::dom {
20 /**
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) {
37 uint32_t depth = 1;
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())) {
44 ++depth;
47 return depth;
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();
55 nsMargin padding =
56 aFrame.GetUsedPadding().ApplySkipSides(aFrame.GetSkipSides());
57 scrollPort.Deflate(padding);
58 // This can break in some edge cases like when layout overflows sizes or
59 // what not.
60 NS_ASSERTION(
61 !aFrame.PresContext()->UseOverlayScrollbars() ||
62 scrollPort.Size() == aFrame.GetContentRectRelativeToSelf().Size(),
63 "Wrong scrollport?");
64 return scrollPort.Size();
66 return aFrame.GetContentRectRelativeToSelf().Size();
69 /**
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();
81 if (!frame) {
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
100 // result.
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) {
121 switch (aBox) {
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
133 // matter.
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:
155 default:
156 break;
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)));
169 return size;
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)
185 : mTarget(&aTarget),
186 mObserver(&aObserver),
187 mObservedBox(aBox),
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()) {
208 return false;
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,
225 mObservationMap);
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)
234 tmp->Disconnect();
235 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner, mDocument, mActiveTargets,
236 mObservationMap);
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)
249 NS_INTERFACE_MAP_END
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,
261 ErrorResult& aRv) {
262 nsCOMPtr<nsPIDOMWindowInner> window =
263 do_QueryInterface(aGlobal.GetAsSupports());
264 if (!window) {
265 aRv.Throw(NS_ERROR_FAILURE);
266 return nullptr;
269 Document* doc = window->GetExtantDoc();
270 if (!doc) {
271 aRv.Throw(NS_ERROR_FAILURE);
272 return nullptr;
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?");
282 return;
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);
294 if (observation) {
295 if (observation->BoxOptions() == aOptions.mBox) {
296 // Already observed this target and the observed box is the same, so
297 // return.
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.
303 return;
305 // Remove the pre-existing entry, but without unregistering ourselves from
306 // the controller.
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
316 // not happen.
317 mDocument->ScheduleResizeObserversNotification();
320 void ResizeObserver::Unobserve(Element& aTarget) {
321 RefPtr<ResizeObservation> observation;
322 if (!mObservationMap.Remove(&aTarget, getter_AddRefs(observation))) {
323 return;
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()) {
356 continue;
359 uint32_t targetDepth = GetNodeDepth(observation->Target());
361 if (targetDepth > aDepth) {
362 mActiveTargets.AppendElement(observation);
363 } else {
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();
386 auto borderBoxSize =
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)) {
397 // Out of memory.
398 break;
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);
406 break;
407 case ResizeObserverBoxOptions::Device_pixel_content_box:
408 observation->UpdateLastReportedSize(devicePixelContentBoxSize);
409 break;
410 case ResizeObserverBoxOptions::Content_box:
411 default:
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);
426 } else {
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,
438 mContentBoxSize,
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)
445 NS_INTERFACE_MAP_END
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
496 // our contentRect.
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();
536 continue;
538 nsIFrame* frame = target->GetPrimaryFrame();
539 if (!frame) {
540 aObserver.Unobserve(*target);
541 continue;
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) {
561 float bSize = 0;
562 for (const auto& current : contentSizeList) {
563 bSize += current->BlockSize();
565 target->SetLastRememberedBSize(bSize);
567 if (canUpdateISize) {
568 float iSize = 0;
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)
588 NS_INTERFACE_MAP_END
590 } // namespace mozilla::dom