Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/layout/painting/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 320 kB image not shown  

Quelle  nsDisplayList.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/.
 */


/*
 * structures that represent things to be painted (ordered in z-order),
 * used during painting and hit testing
 */


#include "nsDisplayList.h"

#include <stdint.h>
#include <algorithm>
#include <limits>

#include "gfxContext.h"
#include "gfxUtils.h"
#include "mozilla/DisplayPortUtils.h"
#include "mozilla/Likely.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/RemoteBrowser.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/ServiceWorkerRegistration.h"
#include "mozilla/dom/SVGElement.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/dom/PerformanceMainThread.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/PresShell.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/ShapeUtils.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_print.h"
#include "mozilla/SVGIntegrationUtils.h"
#include "mozilla/SVGUtils.h"
#include "mozilla/ViewportUtils.h"
#include "nsCSSRendering.h"
#include "nsCSSRenderingGradients.h"
#include "nsCaseTreatment.h"
#include "nsRefreshDriver.h"
#include "nsRegion.h"
#include "nsStyleStructInlines.h"
#include "nsStyleTransformMatrix.h"
#include "nsTransitionManager.h"
#include "gfxMatrix.h"
#include "nsLayoutUtils.h"
#include "nsIFrameInlines.h"
#include "nsStyleConsts.h"
#include "BorderConsts.h"
#include "mozilla/MathAlgorithms.h"

#include "imgIContainer.h"
#include "nsImageFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsViewManager.h"
#include "ImageContainer.h"
#include "nsCanvasFrame.h"
#include "nsSubDocumentFrame.h"
#include "StickyScrollContainer.h"
#include "mozilla/AnimationPerformanceWarning.h"
#include "mozilla/AnimationUtils.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/glean/GfxMetrics.h"
#include "mozilla/HashTable.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/OperatorNewExtensions.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/SVGClipPathFrame.h"
#include "mozilla/SVGMaskFrame.h"
#include "mozilla/SVGObserverUtils.h"
#include "mozilla/Telemetry.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "mozilla/ViewportFrame.h"
#include "mozilla/gfx/gfxVars.h"
#include "ActiveLayerTracker.h"
#include "nsEscape.h"
#include "nsPrintfCString.h"
#include "UnitTransforms.h"
#include "LayerAnimationInfo.h"
#include "mozilla/EventStateManager.h"
#include "nsCaret.h"
#include "nsDOMTokenList.h"
#include "nsCSSProps.h"
#include "nsTableCellFrame.h"
#include "nsTableColFrame.h"
#include "nsTextFrame.h"
#include "nsTextPaintStyle.h"
#include "nsSliderFrame.h"
#include "nsFocusManager.h"
#include "TextDrawTarget.h"
#include "mozilla/layers/AnimationHelper.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/RenderRootStateManager.h"
#include "mozilla/layers/StackingContextHelper.h"
#include "mozilla/layers/TreeTraversal.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/layers/WebRenderMessages.h"
#include "mozilla/layers/WebRenderScrollData.h"

namespace mozilla {

using namespace dom;
using namespace gfx;
using namespace layout;
using namespace layers;
using namespace image;

LazyLogModule sContentDisplayListLog("dl.content");
LazyLogModule sParentDisplayListLog("dl.parent");

LazyLogModule& GetLoggerByProcess() {
  return XRE_IsContentProcess() ? sContentDisplayListLog
                                : sParentDisplayListLog;
}

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
void AssertUniqueItem(nsDisplayItem* aItem) {
  for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
    if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
        i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
      if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
        continue;
      }
      MOZ_DIAGNOSTIC_CRASH("Duplicate display item!");
    }
  }
}
#endif

bool ShouldBuildItemForEvents(const DisplayItemType aType) {
  return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
         aType == DisplayItemType::TYPE_REMOTE ||
         (GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER);
}

static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) {
  switch (aType) {
    case DisplayItemType::TYPE_BACKGROUND:
    case DisplayItemType::TYPE_BACKGROUND_COLOR:
    case DisplayItemType::TYPE_THEMED_BACKGROUND:
      return true;
    default:
      return false;
  }
}

void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
                           nsPaintedDisplayItem* aItem,
                           const DisplayItemType aType) {
  if (ItemTypeSupportsHitTesting(aType)) {
    aItem->InitializeHitTestInfo(aBuilder);
  }
}

/* static */
already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
    const ActiveScrolledRoot* aParent,
    ScrollContainerFrame* aScrollContainerFrame, bool aIsRetained) {
  RefPtr<ActiveScrolledRoot> asr;
  if (aIsRetained) {
    asr = aScrollContainerFrame->GetProperty(ActiveScrolledRootCache());
  }

  if (!asr) {
    asr = new ActiveScrolledRoot();

    if (aIsRetained) {
      RefPtr<ActiveScrolledRoot> ref = asr;
      aScrollContainerFrame->SetProperty(ActiveScrolledRootCache(),
                                         ref.forget().take());
    }
  }
  asr->mParent = aParent;
  asr->mScrollContainerFrame = aScrollContainerFrame;
  asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
  asr->mRetained = aIsRetained;

  return asr.forget();
}

/* static */
bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot* aAncestor,
                                    const ActiveScrolledRoot* aDescendant) {
  if (!aAncestor) {
    // nullptr is the root
    return true;
  }
  if (Depth(aAncestor) > Depth(aDescendant)) {
    return false;
  }
  const ActiveScrolledRoot* asr = aDescendant;
  while (asr) {
    if (asr == aAncestor) {
      return true;
    }
    asr = asr->mParent;
  }
  return false;
}

/* static */
bool ActiveScrolledRoot::IsProperAncestor(
    const ActiveScrolledRoot* aAncestor,
    const ActiveScrolledRoot* aDescendant) {
  return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant);
}

/* static */
nsCString ActiveScrolledRoot::ToString(
    const ActiveScrolledRoot* aActiveScrolledRoot) {
  nsAutoCString str;
  for (const auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) {
    str.AppendPrintf("<0x%p>", asr->mScrollContainerFrame);
    if (asr->mParent) {
      str.AppendLiteral(", ");
    }
  }
  return std::move(str);
}

ScrollableLayerGuid::ViewID ActiveScrolledRoot::ComputeViewId() const {
  nsIContent* content = mScrollContainerFrame->GetScrolledFrame()->GetContent();
  return nsLayoutUtils::FindOrCreateIDFor(content);
}

ActiveScrolledRoot::~ActiveScrolledRoot() {
  if (mScrollContainerFrame && mRetained) {
    mScrollContainerFrame->RemoveProperty(ActiveScrolledRootCache());
  }
}

static uint64_t AddAnimationsForWebRender(
    nsDisplayItem* aItem, RenderRootStateManager* aManager,
    nsDisplayListBuilder* aDisplayListBuilder,
    const Maybe<LayoutDevicePoint>& aPosition = Nothing()) {
  auto* effects = EffectSet::GetForFrame(aItem->Frame(), aItem->GetType());
  if (!effects || effects->IsEmpty()) {
    // If there is no animation on the nsIFrame, that means
    //  1) we've never created any animations on this frame or
    //  2) the frame was reconstruced or
    //  3) all animations on the frame have finished
    // in such cases we don't need do anything here.
    //
    // Even if there is a WebRenderAnimationData for the display item type on
    // this frame, it's going to be discarded since it's not marked as being
    // used.
    return 0;
  }

  RefPtr<WebRenderAnimationData> animationData =
      aManager->CommandBuilder()
          .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem);
  AnimationInfo& animationInfo = animationData->GetAnimationInfo();
  nsIFrame* frame = aItem->Frame();
  animationInfo.AddAnimationsForDisplayItem(
      frame, aDisplayListBuilder, aItem, aItem->GetType(),
      aManager->LayerManager(), aPosition);

  // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
  // are no active animations.
  uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
  if (!animationInfo.GetAnimations().IsEmpty()) {
    OpAddCompositorAnimations anim(
        CompositorAnimations(animationInfo.GetAnimations(), animationsId));
    aManager->WrBridge()->AddWebRenderParentCommand(anim);
    aManager->AddActiveCompositorAnimationId(animationsId);
  } else if (animationsId) {
    aManager->AddCompositorAnimationsIdForDiscard(animationsId);
    animationsId = 0;
  }

  return animationsId;
}

static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
                                    const nsRect& aFillRect,
                                    nsDisplayListBuilder* aBuilder) {
  if (aBuilder->IsForGenerateGlyphMask()) {
    return false;
  }

  SVGObserverUtils::GetAndObserveBackgroundClip(aFrame);

  // The main function of enabling background-clip:text property value.
  // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
  // this function to
  // 1. Generate a mask by all descendant text frames
  // 2. Push the generated mask into aContext.

  gfxContext* sourceCtx = aContext;
  LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
      aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());

  // Create a mask surface.
  RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
  RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget(
      bounds.ToUnknownRect(), SurfaceFormat::A8);
  if (!maskDT || !maskDT->IsValid()) {
    return false;
  }
  gfxContext maskCtx(maskDT, /* aPreserveTransform */ true);
  maskCtx.Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()));

  // Shade text shape into mask A8 surface.
  nsLayoutUtils::PaintFrame(
      &maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
      NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph);

  // Push the generated mask into aContext, so that the caller can pop and
  // blend with it.

  Matrix currentMatrix = sourceCtx->CurrentMatrix();
  Matrix invCurrentMatrix = currentMatrix;
  invCurrentMatrix.Invert();

  RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
  sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
                                   maskSurface, invCurrentMatrix);

  return true;
}

nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy(
    nsDisplayListBuilder* aBuilder) {
  const nsDisplayWrapList* wrappedItem = AsDisplayWrapList();
  MOZ_ASSERT(wrappedItem);

  // Create a new nsDisplayWrapList using a copy-constructor. This is done
  // to preserve the information about bounds.
  nsDisplayWrapper* wrapper =
      new (aBuilder) nsDisplayWrapper(aBuilder, *wrappedItem);
  wrapper->SetType(nsDisplayWrapper::ItemType());
  MOZ_ASSERT(wrapper);

  // Set the display list pointer of the new wrapper item to the display list
  // of the wrapped item.
  wrapper->mListPtr = wrappedItem->mListPtr;
  return wrapper;
}

nsDisplayWrapList* nsDisplayListBuilder::MergeItems(
    nsTArray<nsDisplayItem*>& aItems) {
  // For merging, we create a temporary item by cloning the last item of the
  // mergeable items list. This ensures that the temporary item will have the
  // correct frame and bounds.
  nsDisplayWrapList* last = aItems.PopLastElement()->AsDisplayWrapList();
  MOZ_ASSERT(last);
  nsDisplayWrapList* merged = last->Clone(this);
  MOZ_ASSERT(merged);
  AddTemporaryItem(merged);

  // Create nsDisplayWrappers that point to the internal display lists of the
  // items we are merging. These nsDisplayWrappers are added to the display list
  // of the temporary item.
  for (nsDisplayItem* item : aItems) {
    MOZ_ASSERT(item);
    MOZ_ASSERT(merged->CanMerge(item));
    merged->Merge(item);
    MOZ_ASSERT(item->AsDisplayWrapList());
    merged->GetChildren()->AppendToTop(
        static_cast<nsDisplayWrapList*>(item)->CreateShallowCopy(this));
  }

  merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this));

  return merged;
}

// FIXME(emilio): This whole business should ideally not be needed at all, but
// there are a variety of hard-to-deal-with caret invalidation issues, like
// bug 1888583, and caret changes are relatively uncommon, enough that it
// probably isn't worth chasing all them down.
void nsDisplayListBuilder::InvalidateCaretFramesIfNeeded() {
  if (mPaintedCarets.IsEmpty()) {
    return;
  }
  size_t i = mPaintedCarets.Length();
  while (i--) {
    nsCaret* caret = mPaintedCarets[i];
    nsIFrame* oldCaret = caret->GetLastPaintedFrame();
    nsIFrame* currentCaret = caret->GetPaintGeometry();
    if (oldCaret == currentCaret) {
      // Keep tracking this caret, it hasn't changed.
      continue;
    }
    if (oldCaret) {
      oldCaret->MarkNeedsDisplayItemRebuild();
    }
    if (currentCaret) {
      currentCaret->MarkNeedsDisplayItemRebuild();
    }
    // If / when we paint this caret, we'll track it again.
    caret->SetLastPaintedFrame(nullptr);
    mPaintedCarets.RemoveElementAt(i);
  }
}

void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
    SetCurrentActiveScrolledRoot(
        const ActiveScrolledRoot* aActiveScrolledRoot) {
  MOZ_ASSERT(!mUsed);

  // Set the builder's mCurrentActiveScrolledRoot.
  mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;

  // We also need to adjust the builder's mCurrentContainerASR.
  // mCurrentContainerASR needs to be an ASR that all the container's
  // contents have finite bounds with respect to. If aActiveScrolledRoot
  // is an ancestor ASR of mCurrentContainerASR, that means we need to
  // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
  // the items that will be created with aActiveScrolledRoot wouldn't
  // have finite bounds with respect to mCurrentContainerASR. There's one
  // exception, in the case where there's a content clip on the builder
  // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
  // content clip will clip all items that are created while this
  // AutoCurrentActiveScrolledRootSetter exists. This means that the items
  // created during our lifetime will have finite bounds with respect to
  // the content clip's ASR, even if the items' actual ASR is an ancestor
  // of that. And it also means that mCurrentContainerASR only needs to be
  // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
  // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
  // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.

  // finiteBoundsASR is the leafmost ASR that all items created during
  // object's lifetime have finite bounds with respect to.
  const ActiveScrolledRoot* finiteBoundsASR =
      ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);

  // mCurrentContainerASR is adjusted so that it's still an ancestor of
  // finiteBoundsASR.
  mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
      mBuilder->mCurrentContainerASR, finiteBoundsASR);

  // If we are entering out-of-flow content inside a CSS filter, mark
  // scroll frames wrt. which the content is fixed as containing such content.
  if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
                                  aActiveScrolledRoot, mBuilder->mFilterASR)) {
    for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
         asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
      asr->mScrollContainerFrame->SetHasOutOfFlowContentInsideFilter();
    }
  }

  mUsed = true;
}

void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
    InsertScrollFrame(ScrollContainerFrame* aScrollContainerFrame) {
  MOZ_ASSERT(!mUsed);
  size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
  const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
  const ActiveScrolledRoot* asr =
      mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollContainerFrame);
  mBuilder->mCurrentActiveScrolledRoot = asr;

  // All child ASRs of parentASR that were created while this
  // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
  // now. Reparent them to asr.
  for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
    ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
    if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
      descendantASR->IncrementDepth();
      if (descendantASR->mParent == parentASR) {
        descendantASR->mParent = asr;
      }
    }
  }

  mUsed = true;
}

nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
    nsDisplayListBuilder* aBuilder)
    : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
  mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
}

nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
  return CurrentPresShellState()->mPresShell->GetPresContext();
}

/* static */
nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
    nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
    const nsRect& aVisibleRect, const nsRect& aDirtyRect,
    nsRect* aOutDirtyRect) {
  nsRect visible = aVisibleRect;
  nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;

  bool inPartialUpdate =
      aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate();
  if (MOZ_LIKELY(StaticPrefs::apz_allow_zooming()) &&
      aBuilder->IsPaintingToWindow() && !inPartialUpdate &&
      DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame)) {
    dirtyRectRelativeToDirtyFrame =
        nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());

    // If there's a visual viewport size set, restrict the amount of the
    // fixed-position element we paint to the visual viewport. (In general
    // the fixed-position element can be as large as the layout viewport,
    // which at a high zoom level can cause us to paint too large of an
    // area.)
    PresShell* presShell = aFrame->PresShell();
    if (presShell->IsVisualViewportSizeSet()) {
      dirtyRectRelativeToDirtyFrame =
          nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(),
                 presShell->GetVisualViewportSize());
      // But if we have a displayport, expand it to the displayport, so
      // that async-scrolling the visual viewport within the layout viewport
      // will not checkerboard.
      if (nsIFrame* rootScrollContainerFrame =
              presShell->GetRootScrollContainerFrame()) {
        nsRect displayport;
        // Note that the displayport here is already in the right coordinate
        // space: it's relative to the scroll port (= layout viewport), but
        // covers the visual viewport with some margins around it, which is
        // exactly what we want.
        if (DisplayPortUtils::GetDisplayPort(
                rootScrollContainerFrame->GetContent(), &displayport,
                DisplayPortOptions().With(ContentGeometryType::Fixed))) {
          dirtyRectRelativeToDirtyFrame = displayport;
        }
      }
    }
    visible = dirtyRectRelativeToDirtyFrame;
    if (StaticPrefs::apz_test_logging_enabled() &&
        presShell->GetDocument()->IsContentDocument()) {
      nsLayoutUtils::LogAdditionalTestData(
          aBuilder, "fixedPosDisplayport",
          ToString(CSSSize::FromAppUnits(visible)));
    }
  }

  *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
  visible -= aFrame->GetPosition();

  nsRect overflowRect = aFrame->InkOverflowRect();

  if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
                                     aFrame, DisplayItemType::TYPE_TRANSFORM)) {
    /**
     * Add a fuzz factor to the overflow rectangle so that elements only
     * just out of view are pulled into the display list, so they can be
     * prerendered if necessary.
     */

    overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
  }

  visible.IntersectRect(visible, overflowRect);
  aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);

  return visible;
}

nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
                                           nsIFrame* aFrame,
                                           nsDisplayList* aList)
    : mList(aList) {
  // Find the element that we need to check for link-ness, bailing out if
  // we can't find one.
  Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
  if (!elem) {
    return;
  }

  // If the element has an id and/or name attribute, generate a destination
  // for possible internal linking.
  auto maybeGenerateDest = [&](const nsAtom* aAttr) {
    nsAutoString attrValue;
    elem->GetAttr(aAttr, attrValue);
    if (!attrValue.IsEmpty()) {
      NS_ConvertUTF16toUTF8 dest(attrValue);
      // Ensure that we only emit a given destination once, although there may
      // be multiple frames associated with a given element; we'll simply use
      // the first of them as the target of any links to it.
      // XXX(jfkthame) This prevents emitting duplicate destinations *on the
      // same page*, but does not prevent duplicates on subsequent pages, as
      // each new page is handled by a new temporary DisplayListBuilder. This
      // seems to be harmless in practice, though a bit wasteful of space. To
      // fix, we need to maintain the set of already-seen destinations globally
      // for the print job, rather than attached to the (per-page) builder.
      if (aBuilder->mDestinations.EnsureInserted(dest)) {
        auto* destination = MakeDisplayItem<nsDisplayDestination>(
            aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft());
        mList->AppendToTop(destination);
      }
    }
  };

  if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
    if (elem->HasID()) {
      maybeGenerateDest(nsGkAtoms::id);
    }
    if (elem->HasName()) {
      maybeGenerateDest(nsGkAtoms::name);
    }
  }

  // Links don't nest, so if the builder already has a destination, no need to
  // check for a link element here.
  if (!aBuilder->mLinkURI.IsEmpty() || !aBuilder->mLinkDest.IsEmpty()) {
    return;
  }

  // Check if we have actually found a link.
  if (!elem->IsLink()) {
    return;
  }

  nsCOMPtr<nsIURI> uri = elem->GetHrefURI();
  if (!uri) {
    return;
  }

  // Is it potentially a local (in-document) destination?
  bool hasRef, eqExRef;
  nsIURI* docURI;
  if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
      NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
      (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
      NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
    // Try to get a local destination name. If this fails, we'll leave the
    // mLinkDest string empty, but still try to set mLinkURI below.
    if (NS_FAILED(uri->GetRef(aBuilder->mLinkDest))) {
      aBuilder->mLinkDest.Truncate();
    }
    // The destination name is simply a string; we don't want URL-escaping
    // applied to it.
    if (!aBuilder->mLinkDest.IsEmpty()) {
      NS_UnescapeURL(aBuilder->mLinkDest);
    }
  }

  if (NS_FAILED(uri->GetSpec(aBuilder->mLinkURI))) {
    aBuilder->mLinkURI.Truncate();
  }

  // If we didn't get either kind of destination, we won't try to linkify at
  // this level.
  if (aBuilder->mLinkDest.IsEmpty() && aBuilder->mLinkURI.IsEmpty()) {
    return;
  }

  // Record that we need to reset the builder's state on destruction.
  mBuilderToReset = aBuilder;
}

void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
    nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
  // Note that we may generate a link here even if the constructor bailed out
  // without updating aBuilder->mLinkURI/Dest, because it may have been set by
  // an ancestor that was associated with a link element.
  if (!aBuilder->mLinkURI.IsEmpty() || !aBuilder->mLinkDest.IsEmpty()) {
    auto* link = MakeDisplayItem<nsDisplayLink>(
        aBuilder, aFrame, aBuilder->mLinkDest.get(), aBuilder->mLinkURI.get(),
        aFrame->GetRect());
    mList->AppendToTop(link);
  }
}

uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);

nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
                                           nsDisplayListBuilderMode aMode,
                                           bool aBuildCaret,
                                           bool aRetainingDisplayList)
    : mReferenceFrame(aReferenceFrame),
      mIgnoreScrollFrame(nullptr),
      mCurrentActiveScrolledRoot(nullptr),
      mCurrentContainerASR(nullptr),
      mCurrentFrame(aReferenceFrame),
      mCurrentReferenceFrame(aReferenceFrame),
      mScrollInfoItemsForHoisting(nullptr),
      mFirstClipChainToDestroy(nullptr),
      mTableBackgroundSet(nullptr),
      mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
      mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
      mFilterASR(nullptr),
      mDirtyRect(-1, -1, -1, -1),
      mBuildingExtraPagesForPageNum(0),
      mMode(aMode),
      mContainsBlendMode(false),
      mIsBuildingScrollbar(false),
      mCurrentScrollbarWillHaveLayer(false),
      mBuildCaret(aBuildCaret),
      mRetainingDisplayList(aRetainingDisplayList),
      mPartialUpdate(false),
      mIgnoreSuppression(false),
      mIncludeAllOutOfFlows(false),
      mDescendIntoSubdocuments(true),
      mSelectedFramesOnly(false),
      mAllowMergingAndFlattening(true),
      mInTransform(false),
      mInEventsOnly(false),
      mInFilter(false),
      mInPageSequence(false),
      mIsInChromePresContext(false),
      mSyncDecodeImages(false),
      mIsPaintingToWindow(false),
      mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
      mUseHighQualityScaling(false),
      mIsPaintingForWebRender(false),
      mAncestorHasApzAwareEventHandler(false),
      mHaveScrollableDisplayPort(false),
      mWindowDraggingAllowed(false),
      mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
      mForceLayerForScrollParent(false),
      mContainsNonMinimalDisplayPort(false),
      mBuildingInvisibleItems(false),
      mIsBuilding(false),
      mInInvalidSubtree(false),
      mDisablePartialUpdates(false),
      mPartialBuildFailed(false),
      mIsInActiveDocShell(false),
      mBuildAsyncZoomContainer(false),
      mIsRelativeToLayoutViewport(false),
      mUseOverlayScrollbars(false),
      mAlwaysLayerizeScrollbars(false),
      mIsDestroying(false) {
  MOZ_COUNT_CTOR(nsDisplayListBuilder);

  ShouldRebuildDisplayListDueToPrefChange();

  mUseOverlayScrollbars =
      !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);

  mAlwaysLayerizeScrollbars =
      StaticPrefs::layout_scrollbars_always_layerize_track();

  static_assert(
      static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
      "Check TYPE_MAX should not overflow");

  mIsReusingStackingContextItems =
      mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
}

void nsDisplayListBuilder::BeginFrame() {
  nsCSSRendering::BeginFrameTreesLocked();

  mIsPaintingToWindow = false;
  mUseHighQualityScaling = false;
  mIgnoreSuppression = false;
  mInTransform = false;
  mInFilter = false;
  mSyncDecodeImages = false;
}

void nsDisplayListBuilder::EndFrame() {
  NS_ASSERTION(!mInInvalidSubtree,
               "Someone forgot to cleanup mInInvalidSubtree!");
  mCurrentContainerASR = nullptr;
  mActiveScrolledRoots.Clear();
  FreeClipChains();
  FreeTemporaryItems();
  nsCSSRendering::EndFrameTreesLocked();
}

void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
                                               const nsIFrame* aStopAtFrame) {
  mFramesMarkedForDisplay.AppendElement(aFrame);
  for (nsIFrame* f = aFrame; f;
       f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
    if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
      return;
    }
    f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
    if (f == aStopAtFrame) {
      // we've reached a frame that we know will be painted, so we can stop.
      break;
    }
  }
}

void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
  mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
}

static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame,
                                                 const nsIFrame* aStopAtFrame) {
  for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
    if (f->ForceDescendIntoIfVisible()) {
      return;
    }
    f->SetForceDescendIntoIfVisible(true);

    // This condition must match the condition in
    // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
    // nsLayoutUtils::GetDisplayListParent
    if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
      nsIFrame* parent = f->GetParent();
      if (parent && !parent->ForceDescendIntoIfVisible()) {
        // If the GetDisplayListParent call is going to walk to a placeholder,
        // in rare cases the placeholder might be contained in a different
        // continuation from the oof. So we have to make sure to mark the oofs
        // parent. In the common case this doesn't make us do any extra work,
        // just changes the order in which we visit the frames since walking
        // through placeholders will walk through the parent, and we stop when
        // we find a ForceDescendIntoIfVisible bit set.
        MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame);
      }
    }

    if (f == aStopAtFrame) {
      // we've reached a frame that we know will be painted, so we can stop.
      break;
    }
  }
}

void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
    nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
  AddFrameMarkedForDisplayIfVisible(aFrame);

  MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame);
}

void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
  mIsRelativeToLayoutViewport = true;
  UpdateShouldBuildAsyncZoomContainer();
}

void nsDisplayListBuilder::ForceLayerForScrollParent() {
  mForceLayerForScrollParent = true;
  mNumActiveScrollframesEncountered++;
}

void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
  const Document* document = mReferenceFrame->PresContext()->Document();
  mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
                             !document->Fullscreen() &&
                             nsLayoutUtils::AllowZoomingForDocument(document);

  // If mIsRelativeToLayoutViewport == false, hit-testing on this
  // display list will take into account the pres shell resolution.
  // If we're not building an async zoom container (meaning, the
  // resolution will not take effect visually), the resolution better
  // be 1.0, otherwise rendering and hit-testing are out of sync.
#ifdef DEBUG
  if (!mIsRelativeToLayoutViewport && !mBuildAsyncZoomContainer) {
    MOZ_ASSERT(document->GetPresShell()->GetResolution() == 1.0f);
  }
#endif
}

// Certain prefs may cause display list items to be added or removed when they
// are toggled. In those cases, we need to fully rebuild the display list.
bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
  // If we transition between wrapping the RCD-RSF contents into an async
  // zoom container vs. not, we need to rebuild the display list. This only
  // happens when the zooming or container scrolling prefs are toggled
  // (manually by the user, or during test setup).
  bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
  UpdateShouldBuildAsyncZoomContainer();

  bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
  mUseOverlayScrollbars =
      !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);

  bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
  mAlwaysLayerizeScrollbars =
      StaticPrefs::layout_scrollbars_always_layerize_track();

  if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
    return true;
  }

  if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
    return true;
  }

  if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
    return true;
  }

  return false;
}

void nsDisplayListBuilder::AddScrollContainerFrameToNotify(
    ScrollContainerFrame* aScrollContainerFrame) {
  mScrollContainerFramesToNotify.insert(aScrollContainerFrame);
}

void nsDisplayListBuilder::NotifyAndClearScrollContainerFrames() {
  for (const auto& it : mScrollContainerFramesToNotify) {
    it->NotifyApzTransaction();
  }
  mScrollContainerFramesToNotify.clear();
}

bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
    nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
    const nsRect& aDirtyRect) {
  MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
  nsRect dirty;
  nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
      this, aFrame, aVisibleRect, aDirtyRect, &dirty);
  if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
      visible.IsEmpty()) {
    return false;
  }

  // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
  // frame, then it will also mark any outer frames to ensure that building
  // reaches the dirty feame.
  if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
    MarkFrameForDisplay(aFrame, aDirtyFrame);
  }

  return true;
}

static void UnmarkFrameForDisplay(nsIFrame* aFrame,
                                  const nsIFrame* aStopAtFrame) {
  for (nsIFrame* f = aFrame; f;
       f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
    if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
      return;
    }
    f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
    if (f == aStopAtFrame) {
      // we've reached a frame that we know will be painted, so we can stop.
      break;
    }
  }
}

static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
  for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
    if (!f->ForceDescendIntoIfVisible()) {
      return;
    }
    f->SetForceDescendIntoIfVisible(false);

    // This condition must match the condition in
    // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
    // nsLayoutUtils::GetDisplayListParent
    if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
      nsIFrame* parent = f->GetParent();
      if (parent && parent->ForceDescendIntoIfVisible()) {
        // If the GetDisplayListParent call is going to walk to a placeholder,
        // in rare cases the placeholder might be contained in a different
        // continuation from the oof. So we have to make sure to mark the oofs
        // parent. In the common case this doesn't make us do any extra work,
        // just changes the order in which we visit the frames since walking
        // through placeholders will walk through the parent, and we stop when
        // we find a ForceDescendIntoIfVisible bit set.
        UnmarkFrameForDisplayIfVisible(f);
      }
    }
  }
}

nsDisplayListBuilder::~nsDisplayListBuilder() {
  NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
               "All frames should have been unmarked");
  NS_ASSERTION(mFramesWithOOFData.Length() == 0,
               "All OOF data should have been removed");
  NS_ASSERTION(mPresShellStates.Length() == 0,
               "All presshells should have been exited");

  DisplayItemClipChain* c = mFirstClipChainToDestroy;
  while (c) {
    DisplayItemClipChain* next = c->mNextClipChainToDestroy;
    c->DisplayItemClipChain::~DisplayItemClipChain();
    c = next;
  }

  MOZ_COUNT_DTOR(nsDisplayListBuilder);
}

uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
  uint32_t flags = 0;
  if (mSyncDecodeImages) {
    flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
  }
  if (mIsPaintingToWindow) {
    flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
  }
  if (mUseHighQualityScaling) {
    flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
  }
  return flags;
}

// TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
  uint32_t flags = 0;
  if (mSyncDecodeImages) {
    flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
  }
  if (mIsPaintingToWindow) {
    flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
  }
  if (mUseHighQualityScaling) {
    flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
  }
  return flags;
}

uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
  uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
  if (mSyncDecodeImages) {
    flags |= imgIContainer::FLAG_SYNC_DECODE;
  } else {
    flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
  }
  if (mIsPaintingToWindow || mUseHighQualityScaling) {
    flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
  }
  return flags;
}

nsCaret* nsDisplayListBuilder::GetCaret() {
  RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
  return caret;
}

void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
  if (mIsPaintingToWindow) {
    aPresShell->IncrementPaintCount();
  }
}

void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
                                          bool aPointerEventsNoneDoc) {
  PresShellState* state = mPresShellStates.AppendElement();
  state->mPresShell = aReferenceFrame->PresShell();
  state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
  state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();

  ScrollContainerFrame* sf = state->mPresShell->GetRootScrollContainerFrame();
  if (sf && IsInSubdocument()) {
    // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
    // that the canvas background color will be set correctly, and that only one
    // unscrollable item will be created.
    // This is done to avoid, for example, a case where only scrollbar frames
    // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
    // and possibly end up with an extra nsDisplaySolidColor item.
    // We skip this for the root document, since we don't want to use
    // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
    // do it manually there.
    nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
    if (canvasFrame) {
      MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
    }
  }

#ifdef DEBUG
  state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
                                  nsLayoutPhase::DisplayListBuilding);
#endif

  if (!IsForEventDelivery()) {
    state->mPresShell->UpdateCanvasBackground();
  }

  bool buildCaret = mBuildCaret;
  if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
    state->mIsBackgroundOnly = false;
  } else {
    state->mIsBackgroundOnly = true;
    buildCaret = false;
  }

  bool pointerEventsNone = aPointerEventsNoneDoc;
  if (IsInSubdocument()) {
    pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
                             .mInsidePointerEventsNoneDoc;
  }
  state->mInsidePointerEventsNoneDoc = pointerEventsNone;

  state->mPresShellIgnoreScrollFrame =
      state->mPresShell->IgnoringViewportScrolling()
          ? state->mPresShell->GetRootScrollContainerFrame()
          : nullptr;

  nsPresContext* pc = aReferenceFrame->PresContext();
  mIsInChromePresContext = pc->IsChrome();
  nsIDocShell* docShell = pc->GetDocShell();

  if (docShell) {
    docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
  }

  state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);

  if (!buildCaret) {
    return;
  }

  state->mCaretFrame = [&]() -> nsIFrame* {
    RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
    nsIFrame* currentCaret = caret->GetPaintGeometry(&mCaretRect);
    if (!currentCaret) {
      return nullptr;
    }

    // Check if the display root for the caret matches the display root that
    // we're painting, and only use it if it matches. Likely we only need this
    // for carets inside popups.
    if (nsLayoutUtils::GetDisplayRootFrame(currentCaret) !=
        nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) {
      return nullptr;
    }

    // Caret frames add visual area to their frame, but we don't update the
    // overflow area. Use flags to make sure we build display items for that
    // frame instead.
    MOZ_ASSERT(currentCaret->PresShell() == state->mPresShell);
    MarkFrameForDisplay(currentCaret, aReferenceFrame);
    caret->SetLastPaintedFrame(currentCaret);
    if (!mPaintedCarets.Contains(caret)) {
      mPaintedCarets.AppendElement(std::move(caret));
    }
    return currentCaret;
  }();
}

// A non-blank paint is a paint that does not just contain the canvas
// background.
static bool DisplayListIsNonBlank(nsDisplayList* aList) {
  for (nsDisplayItem* i : *aList) {
    switch (i->GetType()) {
      case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
      case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
      case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
        continue;
      case DisplayItemType::TYPE_SOLID_COLOR:
      case DisplayItemType::TYPE_BACKGROUND:
      case DisplayItemType::TYPE_BACKGROUND_COLOR:
        if (i->Frame()->IsCanvasFrame()) {
          continue;
        }
        return true;
      default:
        return true;
    }
  }
  return false;
}

// A contentful paint is a paint that does contains DOM content (text,
// images, non-blank canvases, SVG): "First Contentful Paint entry
// contains a DOMHighResTimeStamp reporting the time when the browser
// first rendered any text, image (including background images),
// non-white canvas or SVG. This excludes any content of iframes, but
// includes text with pending webfonts. This is the first time users
// could start consuming page content."
static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
                                    nsDisplayList* aList) {
  for (nsDisplayItem* i : *aList) {
    DisplayItemType type = i->GetType();
    nsDisplayList* children = i->GetChildren();

    switch (type) {
      case DisplayItemType::TYPE_SUBDOCUMENT:  // iframes are ignored
        break;
      // CANVASes check if they may have been modified (as a stand-in
      // actually tracking all modifications)
      default:
        if (i->IsContentful()) {
          bool dummy;
          nsRect bound = i->GetBounds(aBuilder, &dummy);
          if (!bound.IsEmpty()) {
            return true;
          }
        }
        if (children) {
          if (DisplayListIsContentful(aBuilder, children)) {
            return true;
          }
        }
        break;
    }
  }
  return false;
}

void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
                                          nsDisplayList* aPaintedContents) {
  NS_ASSERTION(
      CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
      "Presshell mismatch");

  if (mIsPaintingToWindow && aPaintedContents) {
    nsPresContext* pc = aReferenceFrame->PresContext();
    if (!pc->HadNonBlankPaint()) {
      if (!CurrentPresShellState()->mIsBackgroundOnly &&
          DisplayListIsNonBlank(aPaintedContents)) {
        pc->NotifyNonBlankPaint();
      }
    }
    nsRootPresContext* rootPresContext = pc->GetRootPresContext();
    if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
      if (!CurrentPresShellState()->mIsBackgroundOnly) {
        if (pc->HasEverBuiltInvisibleText() ||
            DisplayListIsContentful(this, aPaintedContents)) {
          pc->NotifyContentfulPaint();
        }
      }
    }
  }

  ResetMarkedFramesForDisplayList(aReferenceFrame);
  mPresShellStates.RemoveLastElement();

  if (!mPresShellStates.IsEmpty()) {
    nsPresContext* pc = CurrentPresContext();
    nsIDocShell* docShell = pc->GetDocShell();
    if (docShell) {
      docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
    }
    mIsInChromePresContext = pc->IsChrome();
  } else {
    for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
      UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
    }
    mFramesMarkedForDisplayIfVisible.SetLength(0);
  }
}

void nsDisplayListBuilder::FreeClipChains() {
  // Iterate the clip chains from newest to oldest (forward
  // iteration), so that we destroy descendants first which
  // will drop the ref count on their ancestors.
  DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;

  while (*indirect) {
    if (!(*indirect)->mRefCount) {
      DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;

      mClipDeduplicator.erase(*indirect);
      (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
      Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);

      *indirect = next;
    } else {
      indirect = &(*indirect)->mNextClipChainToDestroy;
    }
  }
}

void nsDisplayListBuilder::FreeTemporaryItems() {
  for (nsDisplayItem* i : mTemporaryItems) {
    // Temporary display items are not added to the frames.
    MOZ_ASSERT(i->Frame());
    i->RemoveFrame(i->Frame());
    i->Destroy(this);
  }

  mTemporaryItems.Clear();
}

void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
    const nsIFrame* aReferenceFrame) {
  // Unmark and pop off the frames marked for display in this pres shell.
  uint32_t firstFrameForShell =
      CurrentPresShellState()->mFirstFrameMarkedForDisplay;
  for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
       ++i) {
    UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
  }
  mFramesMarkedForDisplay.SetLength(firstFrameForShell);

  firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
  for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
    mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
  }
  mFramesWithOOFData.SetLength(firstFrameForShell);
}

void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
  CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
}

void nsDisplayListBuilder::MarkFramesForDisplayList(
    nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
  nsRect visibleRect = GetVisibleRect();
  nsRect dirtyRect = GetDirtyRect();

  // If we are entering content that is fixed to the RCD-RSF, we are
  // crossing the async zoom container boundary, and need to convert from
  // visual to layout coordinates.
  if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
    if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
        viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
      if (viewportFrame->PresShell()->GetRootScrollContainerFrame()) {
#ifdef DEBUG
        for (nsIFrame* f : aFrames) {
          MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
        }
#endif
        visibleRect = ViewportUtils::VisualToLayout(visibleRect,
                                                    viewportFrame->PresShell());
        dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
                                                  viewportFrame->PresShell());
      }
#ifdef DEBUG
      else {
        // This is an edge case that should only happen if we are in a
        // document with a XUL root element so that it does not have a root
        // scroll frame but it has fixed pos content and all of the frames in
        // aFrames are that fixed pos content.
        for (nsIFrame* f : aFrames) {
          MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
                     f->GetParent() == aDirtyFrame &&
                     f->StyleDisplay()->mPosition ==
                         StylePositionProperty::Fixed);
        }
        // There's no root scroll frame so there can't be any zooming or async
        // panning so we don't need to adjust the visible and dirty rects.
      }
#endif
    }
  }

  bool markedFrames = false;
  for (nsIFrame* e : aFrames) {
    // Skip the AccessibleCaret frame when building no caret.
    if (!IsBuildingCaret()) {
      nsIContent* content = e->GetContent();
      if (content && content->IsInNativeAnonymousSubtree() &&
          content->IsElement()) {
        const nsAttrValue* classes = content->AsElement()->GetClasses();
        if (classes &&
            classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
          continue;
        }
      }
    }
    if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
      markedFrames = true;
    }
  }

  if (markedFrames) {
    // mClipState.GetClipChainForContainingBlockDescendants can return pointers
    // to objects on the stack, so we need to clone the chain.
    const DisplayItemClipChain* clipChain =
        CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
    const DisplayItemClipChain* combinedClipChain =
        mClipState.GetCurrentCombinedClipChain(this);
    const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;

    OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
        clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
        visibleRect, dirtyRect);
    aDirtyFrame->SetProperty(
        nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
    mFramesWithOOFData.AppendElement(aDirtyFrame);
  }

  if (!aDirtyFrame->GetParent()) {
    // This is the viewport frame of aDirtyFrame's presshell.
    // Store the current display data so that it can be used for fixed
    // background images.
    NS_ASSERTION(
        CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
        "Presshell mismatch");
    MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
               "already traversed this presshell's root frame?");

    const DisplayItemClipChain* clipChain =
        CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
    const DisplayItemClipChain* combinedClipChain =
        mClipState.GetCurrentCombinedClipChain(this);
    const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
    CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
        clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
        GetVisibleRect(), GetDirtyRect());
  }
}

/**
 * Mark all preserve-3d children with
 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
 * nsIFrame::BuildDisplayListForChild() would visit them.  Also compute
 * dirty rect for preserve-3d children.
 *
 * @param aDirtyFrame is the frame to mark children extending context.
 */

void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
    nsIFrame* aDirtyFrame) {
  for (const auto& childList : aDirtyFrame->ChildLists()) {
    for (nsIFrame* child : childList.mList) {
      if (child->Combines3DTransformWithAncestors()) {
        MarkFrameForDisplay(child, aDirtyFrame);
      }

      if (child->IsBlockWrapper()) {
        // Mark preserve-3d frames inside the block wrapper.
        MarkPreserve3DFramesForDisplayList(child);
      }
    }
  }
}

ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
    const ActiveScrolledRoot* aParent,
    ScrollContainerFrame* aScrollContainerFrame) {
  RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
      aParent, aScrollContainerFrame, IsRetainingDisplayList());
  mActiveScrolledRoots.AppendElement(asr);
  return asr;
}

const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
    const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
    const DisplayItemClipChain* aParent) {
  MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack));
  void* p = Allocate(sizeof(DisplayItemClipChain),
                     DisplayListArenaObjectId::CLIPCHAIN);
  DisplayItemClipChain* c = new (KnownNotNull, p)
      DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
#if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
  c->mOnStack = false;
#endif
  auto result = mClipDeduplicator.insert(c);
  if (!result.second) {
    // An equivalent clip chain item was already created, so let's return that
    // instead. Destroy the one we just created.
    // Note that this can cause clip chains from different coordinate systems to
    // collapse into the same clip chain object, because clip chains do not keep
    // track of the reference frame that they were created in.
    c->DisplayItemClipChain::~DisplayItemClipChain();
    Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
    return *(result.first);
  }
  mFirstClipChainToDestroy = c;
  return c;
}

struct ClipChainItem {
  DisplayItemClip clip;
  const ActiveScrolledRoot* asr;
};

static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
    const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
  for (const ActiveScrolledRoot* asr =
           ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
       asr; asr = asr->mParent) {
    if (aOne == aTwo) {
      return aOne;
    }
    if (aOne->mASR == asr) {
      aOne = aOne->mParent;
    }
    if (aTwo->mASR == asr) {
      aTwo = aTwo->mParent;
    }
    if (!aOne) {
      return aTwo;
    }
    if (!aTwo) {
      return aOne;
    }
  }
  return nullptr;
}

const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
    const DisplayItemClipChain* aAncestor,
    const DisplayItemClipChain* aLeafClip1,
    const DisplayItemClipChain* aLeafClip2) {
  AutoTArray<ClipChainItem, 8> intersectedClips;

  const DisplayItemClipChain* clip1 = aLeafClip1;
  const DisplayItemClipChain* clip2 = aLeafClip2;

  const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
      clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);

  // Build up the intersection from the leaf to the root and put it into
  // intersectedClips. The loop below will convert intersectedClips into an
  // actual DisplayItemClipChain.
  // (We need to do this in two passes because we need the parent clip in order
  // to create the DisplayItemClipChain object, but the parent clip has not
  // been created at that point.)
  while (!aAncestor || asr != aAncestor->mASR) {
    if (clip1 && clip1->mASR == asr) {
      if (clip2 && clip2->mASR == asr) {
        DisplayItemClip intersection = clip1->mClip;
        intersection.IntersectWith(clip2->mClip);
        intersectedClips.AppendElement(ClipChainItem{intersection, asr});
        clip2 = clip2->mParent;
      } else {
        intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
      }
      clip1 = clip1->mParent;
    } else if (clip2 && clip2->mASR == asr) {
      intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
      clip2 = clip2->mParent;
    }
    if (!asr) {
      MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
      break;
    }
    asr = asr->mParent;
  }

  // Convert intersectedClips into a DisplayItemClipChain.
  const DisplayItemClipChain* parentSC = aAncestor;
  for (auto& sc : Reversed(intersectedClips)) {
    parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
  }
  return parentSC;
}

const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
    const DisplayItemClipChain* aLeafClip1,
    const DisplayItemClipChain* aLeafClip2) {
  // aLeafClip2 might be a reference to a clip on the stack. We need to make
  // sure that CreateClipChainIntersection will allocate the actual intersected
  // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
  // we supply nullptr as the common ancestor so that
  // CreateClipChainIntersection clones the whole chain.
  const DisplayItemClipChain* ancestorClip =
      aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
                 : nullptr;

  return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
}

const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
    const DisplayItemClipChain* aClipChain) {
  return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
}

const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
    const nsIFrame* aFrame, nsPoint* aOffset) const {
  auto MaybeApplyAdditionalOffset = [&]() {
    if (auto offset = AdditionalOffset()) {
      *aOffset += *offset;
    }
  };

  if (aFrame == mCurrentFrame) {
    if (aOffset) {
      *aOffset = mCurrentOffsetToReferenceFrame;
    }
    return mCurrentReferenceFrame;
  }

  for (const nsIFrame* f = aFrame; f;
       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
    if (f == mReferenceFrame || f->IsTransformed()) {
      if (aOffset) {
        *aOffset = aFrame->GetOffsetToCrossDoc(f);
        MaybeApplyAdditionalOffset();
      }
      return f;
    }
  }

  if (aOffset) {
    *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
    MaybeApplyAdditionalOffset();
  }

  return mReferenceFrame;
}

// Sticky frames are active if their nearest scrollable frame is also active.
static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
                                nsIFrame* aFrame) {
  MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
             StylePositionProperty::Sticky);

  StickyScrollContainer* stickyScrollContainer =
      StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
  return stickyScrollContainer && stickyScrollContainer->ScrollContainer()
                                      ->IsMaybeAsynchronouslyScrolled();
}

bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
                                                  nsIFrame** aParent) {
  if (aFrame == mReferenceFrame) {
    return true;
  }

  if (!IsPaintingToWindow()) {
    if (aParent) {
      *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
    }
    return false;
  }

  nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
  if (!parent) {
    return true;
  }
  *aParent = parent;

  if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
      IsStickyFrameActive(this, aFrame)) {
    return true;
  }

  if (aFrame->IsTransformed()) {
    if (EffectCompositor::HasAnimationsForCompositor(
            aFrame, DisplayItemType::TYPE_TRANSFORM)) {
      return true;
    }
  }

  if (parent->IsScrollContainerOrSubclass()) {
    ScrollContainerFrame* sf = do_QueryFrame(parent);
    if (sf->GetScrolledFrame() == aFrame) {
      MOZ_ASSERT(!aFrame->IsTransformed());
      return sf->IsMaybeAsynchronouslyScrolled();
    }
  }

  return false;
}

nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
    nsIFrame* aFrame) {
  MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
      RootReferenceFrame(), aFrame));
  nsIFrame* cursor = aFrame;
  while (cursor != RootReferenceFrame()) {
    nsIFrame* next;
    if (IsAnimatedGeometryRoot(cursor, &next)) {
      return cursor;
    }
    cursor = next;
  }
  return cursor;
}

static nsRect ApplyAllClipNonRoundedIntersection(
    const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
  nsRect result = aRect;
  while (aClipChain) {
    result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
    aClipChain = aClipChain->mParent;
  }
  return result;
}

void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
  if (!mWindowDraggingAllowed || !IsForPainting()) {
    return;
  }

  const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
  if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
    // This frame has the default value and doesn't influence the window
    // dragging region.
    return;
  }

  LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;

  // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
  nsIFrame* referenceFrame =
      const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));

  if (IsInTransform()) {
    // Only support 2d rectilinear transforms. Transform support is needed for
    // the horizontal flip transform that's applied to the urlbar textbox in
    // RTL mode - it should be able to exclude itself from the draggable region.
    referenceFrameToRootReferenceFrame =
        ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
            nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
                                                  RelativeTo{mReferenceFrame})
                .GetMatrix());
    Matrix referenceFrameToRootReferenceFrame2d;
    if (!referenceFrameToRootReferenceFrame.Is2D(
            &referenceFrameToRootReferenceFrame2d) ||
        !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
      return;
    }
  } else {
    MOZ_ASSERT(referenceFrame == mReferenceFrame,
               "referenceFrameToRootReferenceFrame needs to be adjusted");
  }

  // We do some basic visibility checking on the frame's border box here.
  // We intersect it both with the current dirty rect and with the current
  // clip. Either one is just a conservative approximation on its own, but
  // their intersection luckily works well enough for our purposes, so that
  // we don't have to do full-blown visibility computations.
  // The most important case we need to handle is the scrolled-off tab:
  // If the tab bar overflows, tab parts that are clipped by the scrollbox
  // should not be allowed to interfere with the window dragging region. Using
  // just the current DisplayItemClip is not enough to cover this case
  // completely because clips are reset while building stacking context
  // contents, so for example we'd fail to clip frames that have a clip path
  // applied to them. But the current dirty rect doesn't get reset in that
  // case, so we use it to make this case work.
  nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
  borderBox += ToReferenceFrame(aFrame);
  const DisplayItemClipChain* clip =
      ClipState().GetCurrentCombinedClipChain(this);
  borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
  if (borderBox.IsEmpty()) {
    return;
  }

  LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
      borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());

  LayoutDeviceRect transformedDevPixelBorderBox =
      TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
  transformedDevPixelBorderBox.Round();
  LayoutDeviceIntRect transformedDevPixelBorderBoxInt;

  if (!transformedDevPixelBorderBox.ToIntRect(
          &transformedDevPixelBorderBoxInt)) {
    return;
  }

  LayoutDeviceIntRegion& region =
      styleUI->mWindowDragging == StyleWindowDragging::Drag
          ? mWindowDraggingRegion
          : mWindowNoDraggingRegion;

  if (!IsRetainingDisplayList()) {
    region.OrWith(transformedDevPixelBorderBoxInt);
    return;
  }

  gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
  if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
    mRetainedWindowDraggingRegion.Add(aFrame, rect);
  } else {
    mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
  }
}

LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
  LayoutDeviceIntRegion result;
  if (!IsRetainingDisplayList()) {
    result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
    return result;
  }

  LayoutDeviceIntRegion dragRegion =
      mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();

  LayoutDeviceIntRegion noDragRegion =
      mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();

  result.Sub(dragRegion, noDragRegion);
  return result;
}

void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
  nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
  aSizes.mLayoutRetainedDisplayListSize +=
      aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
}

void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
  mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);

  size_t n = 0;
  MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
  n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
  n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
  n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
  n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
  n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
  // XXX can't measure mClipDeduplicator since it uses std::unordered_set.

  aSizes.mLayoutRetainedDisplayListSize += n;
}

--> --------------------

--> maximum size reached

--> --------------------

89%


¤ Dauer der Verarbeitung: 0.53 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.