tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ResizeObserver.cpp (19691B)


      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/. */
      6 
      7 #include "mozilla/dom/ResizeObserver.h"
      8 
      9 #include <limits>
     10 
     11 #include "mozilla/SVGUtils.h"
     12 #include "mozilla/ScrollContainerFrame.h"
     13 #include "mozilla/dom/DOMRect.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "nsIContent.h"
     16 #include "nsIContentInlines.h"
     17 #include "nsLayoutUtils.h"
     18 
     19 namespace mozilla::dom {
     20 
     21 /**
     22 * Returns the length of the parent-traversal path (in terms of the number of
     23 * nodes) to an unparented/root node from aNode. An unparented/root node is
     24 * considered to have a depth of 1, its children have a depth of 2, etc.
     25 * aNode is expected to be non-null.
     26 * Note: The shadow root is not part of the calculation because the caller,
     27 * ResizeObserver, doesn't observe the shadow root, and only needs relative
     28 * depths among all the observed targets. In other words, we calculate the
     29 * depth of the flattened tree.
     30 *
     31 * However, these is a spec issue about how to handle shadow DOM case. We
     32 * may need to update this function later:
     33 *   https://github.com/w3c/csswg-drafts/issues/3840
     34 *
     35 * https://drafts.csswg.org/resize-observer/#calculate-depth-for-node-h
     36 */
     37 static uint32_t GetNodeDepth(nsINode* aNode) {
     38  uint32_t depth = 1;
     39 
     40  MOZ_ASSERT(aNode, "Node shouldn't be null");
     41 
     42  // Use GetFlattenedTreeParentNode to bypass the shadow root and cross the
     43  // shadow boundary to calculate the node depth without the shadow root.
     44  while ((aNode = aNode->GetFlattenedTreeParentNode())) {
     45    ++depth;
     46  }
     47 
     48  return depth;
     49 }
     50 
     51 static nsSize GetContentRectSize(const nsIFrame& aFrame) {
     52  if (const ScrollContainerFrame* f = do_QueryFrame(&aFrame)) {
     53    // We return the scrollport rect for compat with other UAs, see bug 1733042.
     54    // But the scrollPort includes padding (but not border!), so remove it.
     55    nsRect scrollPort = f->GetScrollPortRect();
     56    nsMargin padding =
     57        aFrame.GetUsedPadding().ApplySkipSides(aFrame.GetSkipSides());
     58    scrollPort.Deflate(padding);
     59    // This can break in some edge cases like when layout overflows sizes or
     60    // what not.
     61    NS_ASSERTION(
     62        !aFrame.PresContext()->UseOverlayScrollbars() ||
     63            scrollPort.Size() == aFrame.GetContentRectRelativeToSelf().Size(),
     64        "Wrong scrollport?");
     65    return scrollPort.Size();
     66  }
     67  return aFrame.GetContentRectRelativeToSelf().Size();
     68 }
     69 
     70 AutoTArray<LogicalPixelSize, 1> ResizeObserver::CalculateBoxSize(
     71    Element* aTarget, ResizeObserverBoxOptions aBox,
     72    bool aForceFragmentHandling) {
     73  nsIFrame* frame = aTarget->GetPrimaryFrame();
     74 
     75  if (!frame) {
     76    // TODO: Should this return an empty array instead?
     77    // https://github.com/w3c/csswg-drafts/issues/7734
     78    return {LogicalPixelSize()};
     79  }
     80 
     81  const auto zoom = frame->Style()->EffectiveZoom();
     82  if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
     83    // Per the spec, this target's SVG size is always its bounding box size no
     84    // matter what box option you choose, because SVG elements do not use
     85    // standard CSS box model.
     86    // TODO: what if the SVG is fragmented?
     87    // https://github.com/w3c/csswg-drafts/issues/7736
     88    const gfxRect bbox = SVGUtils::GetBBox(frame);
     89    gfx::Size size(static_cast<float>(bbox.width),
     90                   static_cast<float>(bbox.height));
     91    const WritingMode wm = frame->GetWritingMode();
     92    if (aBox == ResizeObserverBoxOptions::Device_pixel_content_box) {
     93      // Per spec, we calculate the inline/block sizes to target’s bounding box
     94      // {inline|block} length, in integral device pixels, so we round the final
     95      // result.
     96      // https://drafts.csswg.org/resize-observer/#dom-resizeobserverboxoptions-device-pixel-content-box
     97      const LayoutDeviceIntSize snappedSize =
     98          RoundedToInt(CSSSize::FromUnknownSize(size) *
     99                       frame->PresContext()->CSSToDevPixelScale());
    100      return {LogicalPixelSize(wm, gfx::Size(snappedSize.ToUnknownSize()))};
    101    }
    102    size.width = zoom.Unzoom(size.width);
    103    size.height = zoom.Unzoom(size.height);
    104    return {LogicalPixelSize(wm, size)};
    105  }
    106 
    107  // Per the spec, non-replaced inline Elements will always have an empty
    108  // content rect. Therefore, we always use the same trivially-empty size
    109  // for non-replaced inline elements here, and their IsActive() will
    110  // always return false. (So its observation won't be fired.)
    111  // TODO: Should we use an empty array instead?
    112  // https://github.com/w3c/csswg-drafts/issues/7734
    113  if (!frame->IsReplaced() && frame->IsLineParticipant()) {
    114    return {LogicalPixelSize()};
    115  }
    116 
    117  auto GetFrameSize = [aBox, zoom](nsIFrame* aFrame) {
    118    switch (aBox) {
    119      case ResizeObserverBoxOptions::Border_box:
    120        return CSSPixel::FromAppUnits(zoom.Unzoom(aFrame->GetSize()))
    121            .ToUnknownSize();
    122      case ResizeObserverBoxOptions::Device_pixel_content_box: {
    123        // Simply converting from app units to device units is insufficient - we
    124        // need to take subpixel snapping into account. Subpixel snapping
    125        // happens with respect to the reference frame, so do the dev pixel
    126        // conversion with our rectangle positioned relative to the reference
    127        // frame, then get the size from there.
    128        const auto* referenceFrame = nsLayoutUtils::GetReferenceFrame(aFrame);
    129        // GetOffsetToCrossDoc version handles <iframe>s in addition to normal
    130        // cases. We don't expect this to tight loop for additional checks to
    131        // matter.
    132        const auto offset = aFrame->GetOffsetToCrossDoc(referenceFrame);
    133        const auto contentSize = GetContentRectSize(*aFrame);
    134        // Casting to double here is deliberate to minimize rounding error in
    135        // upcoming operations.
    136        const auto appUnitsPerDevPixel =
    137            static_cast<double>(aFrame->PresContext()->AppUnitsPerDevPixel());
    138        // Calculation here is a greatly simplified version of
    139        // `NSRectToSnappedRect` as 1) we're not actually drawing (i.e. no draw
    140        // target), and 2) transform does not need to be taken into account.
    141        gfx::Rect rect{gfx::Float(offset.X() / appUnitsPerDevPixel),
    142                       gfx::Float(offset.Y() / appUnitsPerDevPixel),
    143                       gfx::Float(contentSize.Width() / appUnitsPerDevPixel),
    144                       gfx::Float(contentSize.Height() / appUnitsPerDevPixel)};
    145        gfx::Point tl = rect.TopLeft().Round();
    146        gfx::Point br = rect.BottomRight().Round();
    147 
    148        rect.SizeTo(gfx::Size(br.x - tl.x, br.y - tl.y));
    149        rect.NudgeToIntegers();
    150        return rect.Size().ToUnknownSize();
    151      }
    152      case ResizeObserverBoxOptions::Content_box:
    153      default:
    154        break;
    155    }
    156    return CSSPixel::FromAppUnits(zoom.Unzoom(GetContentRectSize(*aFrame)))
    157        .ToUnknownSize();
    158  };
    159  if (!StaticPrefs::dom_resize_observer_support_fragments() &&
    160      !aForceFragmentHandling) {
    161    return {LogicalPixelSize(frame->GetWritingMode(), GetFrameSize(frame))};
    162  }
    163  AutoTArray<LogicalPixelSize, 1> size;
    164  for (nsIFrame* cur = frame; cur; cur = cur->GetNextContinuation()) {
    165    const WritingMode wm = cur->GetWritingMode();
    166    size.AppendElement(LogicalPixelSize(wm, GetFrameSize(cur)));
    167  }
    168  return size;
    169 }
    170 
    171 NS_IMPL_CYCLE_COLLECTION_CLASS(ResizeObservation)
    172 
    173 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ResizeObservation)
    174  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget);
    175 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    176 
    177 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ResizeObservation)
    178  tmp->Unlink(RemoveFromObserver::Yes);
    179 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    180 
    181 ResizeObservation::ResizeObservation(Element& aTarget,
    182                                     ResizeObserver& aObserver,
    183                                     ResizeObserverBoxOptions aBox)
    184    : mTarget(&aTarget),
    185      mObserver(&aObserver),
    186      mObservedBox(aBox),
    187      mLastReportedSize({LogicalPixelSize(WritingMode(), gfx::Size(-1, -1))}) {
    188  aTarget.BindObject(mObserver);
    189 }
    190 
    191 void ResizeObservation::Unlink(RemoveFromObserver aRemoveFromObserver) {
    192  ResizeObserver* observer = std::exchange(mObserver, nullptr);
    193  nsCOMPtr<Element> target = std::move(mTarget);
    194  if (observer && target) {
    195    if (aRemoveFromObserver == RemoveFromObserver::Yes) {
    196      observer->Unobserve(*target);
    197    }
    198    target->UnbindObject(observer);
    199  }
    200 }
    201 
    202 bool ResizeObservation::IsActive() const {
    203  // As detailed in the css-contain specification, if the target is hidden by
    204  // `content-visibility` it should not call its ResizeObservation callbacks.
    205  nsIFrame* frame = mTarget->GetPrimaryFrame();
    206  if (frame && frame->IsHiddenByContentVisibilityOnAnyAncestor()) {
    207    return false;
    208  }
    209 
    210  return mLastReportedSize !=
    211         ResizeObserver::CalculateBoxSize(mTarget, mObservedBox);
    212 }
    213 
    214 void ResizeObservation::UpdateLastReportedSize(
    215    const nsTArray<LogicalPixelSize>& aSize) {
    216  mLastReportedSize.Assign(aSize);
    217 }
    218 
    219 // Only needed for refcounted objects.
    220 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ResizeObserver)
    221 
    222 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ResizeObserver)
    223  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner, mDocument, mCallback,
    224                                    mActiveTargets, mObservationMap);
    225 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    226 
    227 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ResizeObserver)
    228  tmp->Disconnect();
    229  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner, mDocument, mCallback, mActiveTargets,
    230                                  mObservationMap);
    231  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    232 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    233 
    234 NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserver)
    235 NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserver)
    236 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserver)
    237  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    238  NS_INTERFACE_MAP_ENTRY(nsISupports)
    239 NS_INTERFACE_MAP_END
    240 
    241 already_AddRefed<ResizeObserver> ResizeObserver::Constructor(
    242    const GlobalObject& aGlobal, ResizeObserverCallback& aCb,
    243    ErrorResult& aRv) {
    244  nsCOMPtr<nsPIDOMWindowInner> window =
    245      do_QueryInterface(aGlobal.GetAsSupports());
    246  if (!window) {
    247    aRv.Throw(NS_ERROR_FAILURE);
    248    return nullptr;
    249  }
    250 
    251  Document* doc = window->GetExtantDoc();
    252  if (!doc) {
    253    aRv.Throw(NS_ERROR_FAILURE);
    254    return nullptr;
    255  }
    256 
    257  return do_AddRef(new ResizeObserver(std::move(window), doc, aCb));
    258 }
    259 
    260 void ResizeObserver::Observe(Element& aTarget,
    261                             const ResizeObserverOptions& aOptions) {
    262  if (MOZ_UNLIKELY(!mDocument)) {
    263    MOZ_ASSERT_UNREACHABLE("How did we call observe() after unlink?");
    264    return;
    265  }
    266 
    267  // NOTE(emilio): Per spec, this is supposed to happen on construction, but the
    268  // spec isn't particularly sane here, see
    269  // https://github.com/w3c/csswg-drafts/issues/4518
    270  if (mObservationList.isEmpty()) {
    271    MOZ_ASSERT(mObservationMap.IsEmpty());
    272    mDocument->AddResizeObserver(*this);
    273  }
    274 
    275  auto& observation = mObservationMap.LookupOrInsert(&aTarget);
    276  if (observation) {
    277    if (observation->BoxOptions() == aOptions.mBox) {
    278      // Already observed this target and the observed box is the same, so
    279      // return.
    280      // Note: Based on the spec, we should unobserve it first. However,
    281      // calling Unobserve() when we observe the same box will remove original
    282      // ResizeObservation and then add a new one, this may cause an unexpected
    283      // result because ResizeObservation stores the mLastReportedSize which
    284      // should be kept to make sure IsActive() returns the correct result.
    285      return;
    286    }
    287    // Remove the pre-existing entry, but without unregistering ourselves from
    288    // the controller.
    289    observation->remove();
    290    observation = nullptr;
    291  }
    292 
    293  observation = new ResizeObservation(aTarget, *this, aOptions.mBox);
    294  mObservationList.insertBack(observation);
    295 
    296  // Per the spec, we need to trigger notification in event loop that
    297  // contains ResizeObserver observe call even when resize/reflow does
    298  // not happen.
    299  mDocument->ScheduleResizeObserversNotification();
    300 }
    301 
    302 void ResizeObserver::Unobserve(Element& aTarget) {
    303  RefPtr<ResizeObservation> observation;
    304  if (!mObservationMap.Remove(&aTarget, getter_AddRefs(observation))) {
    305    return;
    306  }
    307 
    308  MOZ_ASSERT(!mObservationList.isEmpty(),
    309             "If ResizeObservation found for an element, observation list "
    310             "must be not empty.");
    311  observation->remove();
    312  if (mObservationList.isEmpty()) {
    313    if (MOZ_LIKELY(mDocument)) {
    314      mDocument->RemoveResizeObserver(*this);
    315    }
    316  }
    317 }
    318 
    319 void ResizeObserver::Disconnect() {
    320  const bool registered = !mObservationList.isEmpty();
    321  while (auto* observation = mObservationList.popFirst()) {
    322    observation->Unlink(ResizeObservation::RemoveFromObserver::No);
    323  }
    324  MOZ_ASSERT(mObservationList.isEmpty());
    325  mObservationMap.Clear();
    326  mActiveTargets.Clear();
    327  if (registered && MOZ_LIKELY(mDocument)) {
    328    mDocument->RemoveResizeObserver(*this);
    329  }
    330 }
    331 
    332 void ResizeObserver::GatherActiveObservations(uint32_t aDepth) {
    333  mActiveTargets.Clear();
    334  mHasSkippedTargets = false;
    335 
    336  for (auto* observation : mObservationList) {
    337    if (!observation->IsActive()) {
    338      continue;
    339    }
    340 
    341    uint32_t targetDepth = GetNodeDepth(observation->Target());
    342 
    343    if (targetDepth > aDepth) {
    344      mActiveTargets.AppendElement(observation);
    345    } else {
    346      mHasSkippedTargets = true;
    347    }
    348  }
    349 }
    350 
    351 uint32_t ResizeObserver::BroadcastActiveObservations() {
    352  uint32_t shallowestTargetDepth = std::numeric_limits<uint32_t>::max();
    353 
    354  if (!HasActiveObservations()) {
    355    return shallowestTargetDepth;
    356  }
    357 
    358  Sequence<OwningNonNull<ResizeObserverEntry>> entries;
    359 
    360  for (auto& observation : mActiveTargets) {
    361    Element* target = observation->Target();
    362 
    363    auto borderBoxSize = ResizeObserver::CalculateBoxSize(
    364        target, ResizeObserverBoxOptions::Border_box);
    365    auto contentBoxSize = ResizeObserver::CalculateBoxSize(
    366        target, ResizeObserverBoxOptions::Content_box);
    367    auto devicePixelContentBoxSize = ResizeObserver::CalculateBoxSize(
    368        target, ResizeObserverBoxOptions::Device_pixel_content_box);
    369    RefPtr<ResizeObserverEntry> entry =
    370        new ResizeObserverEntry(mOwner, *target, borderBoxSize, contentBoxSize,
    371                                devicePixelContentBoxSize);
    372 
    373    if (!entries.AppendElement(entry.forget(), fallible)) {
    374      // Out of memory.
    375      break;
    376    }
    377 
    378    // Sync the broadcast size of observation so the next size inspection
    379    // will be based on the updated size from last delivered observations.
    380    switch (observation->BoxOptions()) {
    381      case ResizeObserverBoxOptions::Border_box:
    382        observation->UpdateLastReportedSize(borderBoxSize);
    383        break;
    384      case ResizeObserverBoxOptions::Device_pixel_content_box:
    385        observation->UpdateLastReportedSize(devicePixelContentBoxSize);
    386        break;
    387      case ResizeObserverBoxOptions::Content_box:
    388      default:
    389        observation->UpdateLastReportedSize(contentBoxSize);
    390    }
    391 
    392    uint32_t targetDepth = GetNodeDepth(observation->Target());
    393 
    394    if (targetDepth < shallowestTargetDepth) {
    395      shallowestTargetDepth = targetDepth;
    396    }
    397  }
    398 
    399  RefPtr<ResizeObserverCallback> callback(mCallback);
    400  callback->Call(this, entries, *this);
    401 
    402  mActiveTargets.Clear();
    403  mHasSkippedTargets = false;
    404 
    405  return shallowestTargetDepth;
    406 }
    407 
    408 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverEntry, mOwner, mTarget,
    409                                      mContentRect, mBorderBoxSize,
    410                                      mContentBoxSize,
    411                                      mDevicePixelContentBoxSize)
    412 NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverEntry)
    413 NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverEntry)
    414 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverEntry)
    415  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    416  NS_INTERFACE_MAP_ENTRY(nsISupports)
    417 NS_INTERFACE_MAP_END
    418 
    419 void ResizeObserverEntry::GetBorderBoxSize(
    420    nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const {
    421  // In the resize-observer-1 spec, there will only be a single
    422  // ResizeObserverSize returned in the FrozenArray for now.
    423  //
    424  // Note: the usage of FrozenArray is to support elements that have multiple
    425  // fragments, which occur in multi-column scenarios.
    426  // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
    427  aRetVal.Assign(mBorderBoxSize);
    428 }
    429 
    430 void ResizeObserverEntry::GetContentBoxSize(
    431    nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const {
    432  // In the resize-observer-1 spec, there will only be a single
    433  // ResizeObserverSize returned in the FrozenArray for now.
    434  //
    435  // Note: the usage of FrozenArray is to support elements that have multiple
    436  // fragments, which occur in multi-column scenarios.
    437  // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
    438  aRetVal.Assign(mContentBoxSize);
    439 }
    440 
    441 void ResizeObserverEntry::GetDevicePixelContentBoxSize(
    442    nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const {
    443  // In the resize-observer-1 spec, there will only be a single
    444  // ResizeObserverSize returned in the FrozenArray for now.
    445  //
    446  // Note: the usage of FrozenArray is to support elements that have multiple
    447  // fragments, which occur in multi-column scenarios.
    448  // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
    449  aRetVal.Assign(mDevicePixelContentBoxSize);
    450 }
    451 
    452 void ResizeObserverEntry::SetBorderBoxSize(
    453    const nsTArray<LogicalPixelSize>& aSize) {
    454  mBorderBoxSize.Clear();
    455  mBorderBoxSize.SetCapacity(aSize.Length());
    456  for (const LogicalPixelSize& size : aSize) {
    457    mBorderBoxSize.AppendElement(new ResizeObserverSize(mOwner, size));
    458  }
    459 }
    460 
    461 void ResizeObserverEntry::SetContentRectAndSize(
    462    const nsTArray<LogicalPixelSize>& aSize) {
    463  nsIFrame* frame = mTarget->GetPrimaryFrame();
    464 
    465  // 1. Update mContentRect.
    466  mContentRect = [&] {
    467    nsMargin padding = frame ? frame->GetUsedPadding() : nsMargin();
    468    const auto zoom = frame ? frame->Style()->EffectiveZoom() : StyleZoom::ONE;
    469    // Per the spec, we need to use the top-left padding offset as the origin of
    470    // our contentRect.
    471    // NOTE(emilio): aSize already has been unzoomed if needed.
    472    const nsPoint origin = zoom.Unzoom(nsPoint(padding.left, padding.top));
    473 
    474    gfx::Size sizeForRect;
    475    MOZ_DIAGNOSTIC_ASSERT(!aSize.IsEmpty());
    476    if (!aSize.IsEmpty()) {
    477      const WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
    478      sizeForRect = aSize[0].PhysicalSize(wm);
    479    }
    480    nsRect rect(origin,
    481                CSSPixel::ToAppUnits(CSSSize::FromUnknownSize(sizeForRect)));
    482    RefPtr<DOMRect> contentRect = new DOMRect(mOwner);
    483    contentRect->SetLayoutRect(rect);
    484    return contentRect.forget();
    485  }();
    486 
    487  // 2. Update mContentBoxSize.
    488  mContentBoxSize.Clear();
    489  mContentBoxSize.SetCapacity(aSize.Length());
    490  for (const LogicalPixelSize& size : aSize) {
    491    mContentBoxSize.AppendElement(new ResizeObserverSize(mOwner, size));
    492  }
    493 }
    494 
    495 void ResizeObserverEntry::SetDevicePixelContentSize(
    496    const nsTArray<LogicalPixelSize>& aSize) {
    497  mDevicePixelContentBoxSize.Clear();
    498  mDevicePixelContentBoxSize.SetCapacity(aSize.Length());
    499  for (const LogicalPixelSize& size : aSize) {
    500    mDevicePixelContentBoxSize.AppendElement(
    501        new ResizeObserverSize(mOwner, size));
    502  }
    503 }
    504 
    505 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverSize, mOwner)
    506 NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverSize)
    507 NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverSize)
    508 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverSize)
    509  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    510  NS_INTERFACE_MAP_ENTRY(nsISupports)
    511 NS_INTERFACE_MAP_END
    512 
    513 }  // namespace mozilla::dom