/* -*- 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
 */

#ifndef NSDISPLAYLIST_H_
#define NSDISPLAYLIST_H_

#include "DisplayItemClipChain.h"
#include "DisplayListClipState.h"
#include "FrameMetrics.h"
#include "HitTestInfo.h"
#include "ImgDrawResult.h"
#include "RetainedDisplayListHelpers.h"
#include "Units.h"
#include "gfxContext.h"
#include "mozilla/ArenaAllocator.h"
#include "mozilla/Array.h"
#include "mozilla/ArrayIterator.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EnumSet.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/Logging.h"
#include "mozilla/Maybe.h"
#include "mozilla/MotionPathUtils.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TemplateLib.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/EffectsInfo.h"
#include "mozilla/gfx/UserData.h"
#include "mozilla/layers/BSPTree.h"
#include "mozilla/layers/ScrollableLayerGuid.h"
#include "mozilla/layers/ScrollbarData.h"
#include "nsAutoLayoutPhase.h"
#include "nsCOMPtr.h"
#include "nsCSSRenderingBorders.h"
#include "nsContainerFrame.h"
#include "nsDisplayItemTypes.h"
#include "nsDisplayListInvalidation.h"
#include "nsPoint.h"
#include "nsPresArena.h"
#include "nsRect.h"
#include "nsRegion.h"
#include "nsClassHashtable.h"
#include "nsTHashSet.h"
#include "nsTHashMap.h"

#include <algorithm>
#include <unordered_set>

// XXX Includes that could be avoided by moving function implementations to the
// cpp file.
#include "gfxPlatform.h"

class gfxContext;
class nsIContent;
class nsIScrollableFrame;
class nsSubDocumentFrame;
class nsCaret;
struct WrFiltersHolder;

namespace nsStyleTransformMatrix {
class TransformReferenceBox;
}

namespace mozilla {

enum class nsDisplayOwnLayerFlags;
class nsDisplayCompositorHitTestInfo;
class nsDisplayScrollInfoLayer;
class PresShell;
class StickyScrollContainer;

namespace layers {
struct FrameMetrics;
class RenderRootStateManager;
class Layer;
class ImageContainer;
class StackingContextHelper;
class WebRenderScrollData;
class WebRenderLayerScrollData;
class WebRenderLayerManager;
}  // namespace layers

namespace wr {
class DisplayListBuilder;
}  // namespace wr

namespace dom {
class RemoteBrowser;
class Selection;
}  // namespace dom

enum class DisplayListArenaObjectId {
#define DISPLAY_LIST_ARENA_OBJECT(name_) name_,
#include "nsDisplayListArenaTypes.h"
#undef DISPLAY_LIST_ARENA_OBJECT
  COUNT
};

extern LazyLogModule sContentDisplayListLog;
extern LazyLogModule sParentDisplayListLog;

LazyLogModule& GetLoggerByProcess();

#define DL_LOG(lvl, ...) MOZ_LOG(GetLoggerByProcess(), lvl, (__VA_ARGS__))
#define DL_LOGI(...) DL_LOG(LogLevel::Info, __VA_ARGS__)
#define DL_LOG_TEST(lvl) MOZ_LOG_TEST(GetLoggerByProcess(), lvl)

#ifdef DEBUG
#  define DL_LOGD(...) DL_LOG(LogLevel::Debug, __VA_ARGS__)
#  define DL_LOGV(...) DL_LOG(LogLevel::Verbose, __VA_ARGS__)
#else
// Disable Debug and Verbose logs for release builds.
#  define DL_LOGD(...)
#  define DL_LOGV(...)
#endif

/*
 * An nsIFrame can have many different visual parts. For example an image frame
 * can have a background, border, and outline, the image itself, and a
 * translucent selection overlay. In general these parts can be drawn at
 * discontiguous z-levels; see CSS2.1 appendix E:
 * http://www.w3.org/TR/CSS21/zindex.html
 *
 * We construct a display list for a frame tree that contains one item
 * for each visual part. The display list is itself a tree since some items
 * are containers for other items; however, its structure does not match
 * the structure of its source frame tree. The display list items are sorted
 * by z-order. A display list can be used to paint the frames, to determine
 * which frame is the target of a mouse event, and to determine what areas
 * need to be repainted when scrolling. The display lists built for each task
 * may be different for efficiency; in particular some frames need special
 * display list items only for event handling, and do not create these items
 * when the display list will be used for painting (the common case). For
 * example, when painting we avoid creating nsDisplayBackground items for
 * frames that don't display a visible background, but for event handling
 * we need those backgrounds because they are not transparent to events.
 *
 * We could avoid constructing an explicit display list by traversing the
 * frame tree multiple times in clever ways. However, reifying the display list
 * reduces code complexity and reduces the number of times each frame must be
 * traversed to one, which seems to be good for performance. It also means
 * we can share code for painting, event handling and scroll analysis.
 *
 * Display lists are short-lived; content and frame trees cannot change
 * between a display list being created and destroyed. Display lists should
 * not be created during reflow because the frame tree may be in an
 * inconsistent state (e.g., a frame's stored overflow-area may not include
 * the bounds of all its children). However, it should be fine to create
 * a display list while a reflow is pending, before it starts.
 *
 * A display list covers the "extended" frame tree; the display list for
 * a frame tree containing FRAME/IFRAME elements can include frames from
 * the subdocuments.
 *
 * Display item's coordinates are relative to their nearest reference frame
 * ancestor. Both the display root and any frame with a transform act as a
 * reference frame for their frame subtrees.
 */

/**
 * An active scrolled root (ASR) is similar to an animated geometry root (AGR).
 * The differences are:
 *  - ASRs are only created for async-scrollable scroll frames. This is a
 *    (hopefully) temporary restriction. In the future we will want to create
 *    ASRs for all the things that are currently creating AGRs, and then
 *    replace AGRs with ASRs and rename them from "active scrolled root" to
 *    "animated geometry root".
 *  - ASR objects are created during display list construction by the nsIFrames
 *    that induce ASRs. This is done using AutoCurrentActiveScrolledRootSetter.
 *    The current ASR is returned by
 *    nsDisplayListBuilder::CurrentActiveScrolledRoot().
 *  - There is no way to go from an nsIFrame pointer to the ASR of that frame.
 *    If you need to look up an ASR after display list construction, you need
 *    to store it while the AutoCurrentActiveScrolledRootSetter that creates it
 *    is on the stack.
 */
struct ActiveScrolledRoot {
  static already_AddRefed<ActiveScrolledRoot> CreateASRForFrame(
      const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame,
      bool aIsRetained);

  static const ActiveScrolledRoot* PickAncestor(
      const ActiveScrolledRoot* aOne, const ActiveScrolledRoot* aTwo) {
    MOZ_ASSERT(IsAncestor(aOne, aTwo) || IsAncestor(aTwo, aOne));
    return Depth(aOne) <= Depth(aTwo) ? aOne : aTwo;
  }

  static const ActiveScrolledRoot* PickDescendant(
      const ActiveScrolledRoot* aOne, const ActiveScrolledRoot* aTwo) {
    MOZ_ASSERT(IsAncestor(aOne, aTwo) || IsAncestor(aTwo, aOne));
    return Depth(aOne) >= Depth(aTwo) ? aOne : aTwo;
  }

  static bool IsAncestor(const ActiveScrolledRoot* aAncestor,
                         const ActiveScrolledRoot* aDescendant);
  static bool IsProperAncestor(const ActiveScrolledRoot* aAncestor,
                               const ActiveScrolledRoot* aDescendant);

  static nsCString ToString(const ActiveScrolledRoot* aActiveScrolledRoot);

  // Call this when inserting an ancestor.
  void IncrementDepth() { mDepth++; }

  /**
   * Find the view ID (or generate a new one) for the content element
   * corresponding to the ASR.
   */
  layers::ScrollableLayerGuid::ViewID GetViewId() const {
    if (!mViewId.isSome()) {
      mViewId = Some(ComputeViewId());
    }
    return *mViewId;
  }

  RefPtr<const ActiveScrolledRoot> mParent;
  nsIScrollableFrame* mScrollableFrame;

  NS_INLINE_DECL_REFCOUNTING(ActiveScrolledRoot)

 private:
  ActiveScrolledRoot()
      : mScrollableFrame(nullptr), mDepth(0), mRetained(false) {}

  ~ActiveScrolledRoot();

  static void DetachASR(ActiveScrolledRoot* aASR) {
    aASR->mParent = nullptr;
    aASR->mScrollableFrame = nullptr;
    NS_RELEASE(aASR);
  }
  NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(ActiveScrolledRootCache,
                                      ActiveScrolledRoot, DetachASR)

  static uint32_t Depth(const ActiveScrolledRoot* aActiveScrolledRoot) {
    return aActiveScrolledRoot ? aActiveScrolledRoot->mDepth : 0;
  }

  layers::ScrollableLayerGuid::ViewID ComputeViewId() const;

  // This field is lazily populated in GetViewId(). We don't want to do the
  // work of populating if webrender is disabled, because it is often not
  // needed.
  mutable Maybe<layers::ScrollableLayerGuid::ViewID> mViewId;

  uint32_t mDepth;
  bool mRetained;
};

enum class nsDisplayListBuilderMode : uint8_t {
  Painting,
  PaintForPrinting,
  EventDelivery,
  FrameVisibility,
  GenerateGlyph,
};

using ListArenaAllocator = ArenaAllocator<4096, 8>;

class nsDisplayItem;
class nsPaintedDisplayItem;
class nsDisplayList;
class nsDisplayWrapList;
class nsDisplayTableBackgroundSet;
class nsDisplayTableItem;

class RetainedDisplayList;

/**
 * This manages a display list and is passed as a parameter to
 * nsIFrame::BuildDisplayList.
 * It contains the parameters that don't change from frame to frame and manages
 * the display list memory using an arena. It also establishes the reference
 * coordinate system for all display list items. Some of the parameters are
 * available from the prescontext/presshell, but we copy them into the builder
 * for faster/more convenient access.
 */
class nsDisplayListBuilder {
  /**
   * This manages status of a 3d context to collect visible rects of
   * descendants and passing a dirty rect.
   *
   * Since some transforms maybe singular, passing visible rects or
   * the dirty rect level by level from parent to children may get a
   * wrong result, being different from the result of appling with
   * effective transform directly.
   *
   * nsIFrame::BuildDisplayListForStackingContext() uses
   * AutoPreserves3DContext to install an instance on the builder.
   *
   * \see AutoAccumulateTransform, AutoAccumulateRect,
   *      AutoPreserves3DContext, Accumulate, GetCurrentTransform,
   *      StartRoot.
   */
  class Preserves3DContext {
   public:
    Preserves3DContext()
        : mAccumulatedRectLevels(0), mAllowAsyncAnimation(true) {}

    Preserves3DContext(const Preserves3DContext& aOther)
        : mAccumulatedTransform(),
          mAccumulatedRect(),
          mAccumulatedRectLevels(0),
          mVisibleRect(aOther.mVisibleRect),
          mAllowAsyncAnimation(aOther.mAllowAsyncAnimation) {}

    // Accmulate transforms of ancestors on the preserves-3d chain.
    gfx::Matrix4x4 mAccumulatedTransform;
    // Accmulate visible rect of descendants in the preserves-3d context.
    nsRect mAccumulatedRect;
    // How far this frame is from the root of the current 3d context.
    int mAccumulatedRectLevels;
    nsRect mVisibleRect;
    // Allow async animation for this 3D context.
    bool mAllowAsyncAnimation;
  };

 public:
  using ViewID = layers::ScrollableLayerGuid::ViewID;

  /**
   * @param aReferenceFrame the frame at the root of the subtree; its origin
   * is the origin of the reference coordinate system for this display list
   * @param aMode encodes what the builder is being used for.
   * @param aBuildCaret whether or not we should include the caret in any
   * display lists that we make.
   */
  nsDisplayListBuilder(nsIFrame* aReferenceFrame,
                       nsDisplayListBuilderMode aMode, bool aBuildCaret,
                       bool aRetainingDisplayList = false);
  ~nsDisplayListBuilder();

  void BeginFrame();
  void EndFrame();

  void AddTemporaryItem(nsDisplayItem* aItem) {
    mTemporaryItems.AppendElement(aItem);
  }

  WindowRenderer* GetWidgetWindowRenderer(nsView** aView = nullptr);
  layers::WebRenderLayerManager* GetWidgetLayerManager(
      nsView** aView = nullptr);

  /**
   * @return true if the display is being built in order to determine which
   * frame is under the mouse position.
   */
  bool IsForEventDelivery() const {
    return mMode == nsDisplayListBuilderMode::EventDelivery;
  }

  /**
   * @return true if the display list is being built for painting. This
   * includes both painting to a window or other buffer and painting to
   * a print/pdf destination.
   */
  bool IsForPainting() const {
    return mMode == nsDisplayListBuilderMode::Painting ||
           mMode == nsDisplayListBuilderMode::PaintForPrinting;
  }

  /**
   * @return true if the display list is being built specifically for printing.
   */
  bool IsForPrinting() const {
    return mMode == nsDisplayListBuilderMode::PaintForPrinting;
  }

  /**
   * @return true if the display list is being built for determining frame
   * visibility.
   */
  bool IsForFrameVisibility() const {
    return mMode == nsDisplayListBuilderMode::FrameVisibility;
  }

  /**
   * @return true if the display list is being built for creating the glyph
   * mask from text items.
   */
  bool IsForGenerateGlyphMask() const {
    return mMode == nsDisplayListBuilderMode::GenerateGlyph;
  }

  bool BuildCompositorHitTestInfo() const {
    return mBuildCompositorHitTestInfo;
  }

  /**
   * @return true if "painting is suppressed" during page load and we
   * should paint only the background of the document.
   */
  bool IsBackgroundOnly() {
    NS_ASSERTION(mPresShellStates.Length() > 0,
                 "don't call this if we're not in a presshell");
    return CurrentPresShellState()->mIsBackgroundOnly;
  }

  /**
   * @return the root of given frame's (sub)tree, whose origin
   * establishes the coordinate system for the child display items.
   */
  const nsIFrame* FindReferenceFrameFor(const nsIFrame* aFrame,
                                        nsPoint* aOffset = nullptr) const;

  const Maybe<nsPoint>& AdditionalOffset() const { return mAdditionalOffset; }

  /**
   * @return the root of the display list's frame (sub)tree, whose origin
   * establishes the coordinate system for the display list
   */
  nsIFrame* RootReferenceFrame() const { return mReferenceFrame; }

  /**
   * @return a point pt such that adding pt to a coordinate relative to aFrame
   * makes it relative to ReferenceFrame(), i.e., returns
   * aFrame->GetOffsetToCrossDoc(ReferenceFrame()). The returned point is in
   * the appunits of aFrame.
   */
  const nsPoint ToReferenceFrame(const nsIFrame* aFrame) const {
    nsPoint result;
    FindReferenceFrameFor(aFrame, &result);
    return result;
  }
  /**
   * When building the display list, the scrollframe aFrame will be "ignored"
   * for the purposes of clipping, and its scrollbars will be hidden. We use
   * this to allow RenderOffscreen to render a whole document without beign
   * clipped by the viewport or drawing the viewport scrollbars.
   */
  void SetIgnoreScrollFrame(nsIFrame* aFrame) { mIgnoreScrollFrame = aFrame; }
  /**
   * Get the scrollframe to ignore, if any.
   */
  nsIFrame* GetIgnoreScrollFrame() { return mIgnoreScrollFrame; }
  void SetIsRelativeToLayoutViewport();
  bool IsRelativeToLayoutViewport() const {
    return mIsRelativeToLayoutViewport;
  }
  /**
   * Get the ViewID of the nearest scrolling ancestor frame.
   */
  ViewID GetCurrentScrollParentId() const { return mCurrentScrollParentId; }
  /**
   * Get and set the flag that indicates if scroll parents should have layers
   * forcibly created. This flag is set when a deeply nested scrollframe has
   * a displayport, and for scroll handoff to work properly the ancestor
   * scrollframes should also get their own scrollable layers.
   */
  void ForceLayerForScrollParent() { mForceLayerForScrollParent = true; }
  /**
   * Set the flag that indicates there is a non-minimal display port in the
   * current subtree. This is used to determine display port expiry.
   */
  void SetContainsNonMinimalDisplayPort() {
    mContainsNonMinimalDisplayPort = true;
  }
  /**
   * Get the ViewID and the scrollbar flags corresponding to the scrollbar for
   * which we are building display items at the moment.
   */
  ViewID GetCurrentScrollbarTarget() const { return mCurrentScrollbarTarget; }
  Maybe<layers::ScrollDirection> GetCurrentScrollbarDirection() const {
    return mCurrentScrollbarDirection;
  }
  /**
   * Returns true if building a scrollbar, and the scrollbar will not be
   * layerized.
   */
  bool IsBuildingNonLayerizedScrollbar() const {
    return mIsBuildingScrollbar && !mCurrentScrollbarWillHaveLayer;
  }
  /**
   * Calling this setter makes us include all out-of-flow descendant
   * frames in the display list, wherever they may be positioned (even
   * outside the dirty rects).
   */
  void SetIncludeAllOutOfFlows() { mIncludeAllOutOfFlows = true; }
  bool GetIncludeAllOutOfFlows() const { return mIncludeAllOutOfFlows; }
  /**
   * Calling this setter makes us exclude all leaf frames that aren't
   * selected.
   */
  void SetSelectedFramesOnly() { mSelectedFramesOnly = true; }
  bool GetSelectedFramesOnly() { return mSelectedFramesOnly; }
  /**
   * @return Returns true if we should include the caret in any display lists
   * that we make.
   */
  bool IsBuildingCaret() const { return mBuildCaret; }

  bool IsRetainingDisplayList() const { return mRetainingDisplayList; }

  bool IsPartialUpdate() const { return mPartialUpdate; }
  void SetPartialUpdate(bool aPartial) { mPartialUpdate = aPartial; }

  bool IsBuilding() const { return mIsBuilding; }
  void SetIsBuilding(bool aIsBuilding) { mIsBuilding = aIsBuilding; }

  bool InInvalidSubtree() const { return mInInvalidSubtree; }

  /**
   * Allows callers to selectively override the regular paint suppression
   * checks, so that methods like GetFrameForPoint work when painting is
   * suppressed.
   */
  void IgnorePaintSuppression() { mIgnoreSuppression = true; }
  /**
   * @return Returns if this builder will ignore paint suppression.
   */
  bool IsIgnoringPaintSuppression() { return mIgnoreSuppression; }
  /**
   * Call this if we're doing normal painting to the window.
   */
  void SetPaintingToWindow(bool aToWindow) { mIsPaintingToWindow = aToWindow; }
  bool IsPaintingToWindow() const { return mIsPaintingToWindow; }
  /**
   * Call this if we're using high quality scaling for image decoding.
   * It is also implied by IsPaintingToWindow.
   */
  void SetUseHighQualityScaling(bool aUseHighQualityScaling) {
    mUseHighQualityScaling = aUseHighQualityScaling;
  }
  bool UseHighQualityScaling() const {
    return mIsPaintingToWindow || mUseHighQualityScaling;
  }
  /**
   * Call this if we're doing painting for WebRender
   */
  void SetPaintingForWebRender(bool aForWebRender) {
    mIsPaintingForWebRender = true;
  }
  bool IsPaintingForWebRender() const { return mIsPaintingForWebRender; }
  /**
   * Call this to prevent descending into subdocuments.
   */
  void SetDescendIntoSubdocuments(bool aDescend) {
    mDescendIntoSubdocuments = aDescend;
  }

  bool GetDescendIntoSubdocuments() { return mDescendIntoSubdocuments; }

  /**
   * Get dirty rect relative to current frame (the frame that we're calling
   * BuildDisplayList on right now).
   */
  const nsRect& GetVisibleRect() { return mVisibleRect; }
  const nsRect& GetDirtyRect() { return mDirtyRect; }

  void SetVisibleRect(const nsRect& aVisibleRect) {
    mVisibleRect = aVisibleRect;
  }

  void IntersectVisibleRect(const nsRect& aVisibleRect) {
    mVisibleRect.IntersectRect(mVisibleRect, aVisibleRect);
  }

  void SetDirtyRect(const nsRect& aDirtyRect) { mDirtyRect = aDirtyRect; }

  void IntersectDirtyRect(const nsRect& aDirtyRect) {
    mDirtyRect.IntersectRect(mDirtyRect, aDirtyRect);
  }

  const nsIFrame* GetCurrentFrame() { return mCurrentFrame; }
  const nsIFrame* GetCurrentReferenceFrame() { return mCurrentReferenceFrame; }

  const nsPoint& GetCurrentFrameOffsetToReferenceFrame() const {
    return mCurrentOffsetToReferenceFrame;
  }

  void Check() { mPool.Check(); }

  /*
   * Get the paint sequence number of the current paint.
   */
  static uint32_t GetPaintSequenceNumber() { return sPaintSequenceNumber; }

  /*
   * Increment the paint sequence number.
   */
  static void IncrementPaintSequenceNumber() { ++sPaintSequenceNumber; }

  /**
   * Returns true if merging and flattening of display lists should be
   * performed while computing visibility.
   */
  bool AllowMergingAndFlattening() { return mAllowMergingAndFlattening; }
  void SetAllowMergingAndFlattening(bool aAllow) {
    mAllowMergingAndFlattening = aAllow;
  }

  void SetCompositorHitTestInfo(const gfx::CompositorHitTestInfo& aInfo) {
    mCompositorHitTestInfo = aInfo;
  }

  const gfx::CompositorHitTestInfo& GetCompositorHitTestInfo() const {
    return mCompositorHitTestInfo;
  }

  /**
   * Builds a new nsDisplayCompositorHitTestInfo for the frame |aFrame| if
   * needed, and adds it to the top of |aList|.
   */
  void BuildCompositorHitTestInfoIfNeeded(nsIFrame* aFrame,
                                          nsDisplayList* aList);

  bool IsInsidePointerEventsNoneDoc() {
    return CurrentPresShellState()->mInsidePointerEventsNoneDoc;
  }

  bool IsTouchEventPrefEnabledDoc() {
    return CurrentPresShellState()->mTouchEventPrefEnabledDoc;
  }

  bool GetAncestorHasApzAwareEventHandler() const {
    return mAncestorHasApzAwareEventHandler;
  }

  void SetAncestorHasApzAwareEventHandler(bool aValue) {
    mAncestorHasApzAwareEventHandler = aValue;
  }

  bool HaveScrollableDisplayPort() const { return mHaveScrollableDisplayPort; }
  void SetHaveScrollableDisplayPort() { mHaveScrollableDisplayPort = true; }
  void ClearHaveScrollableDisplayPort() { mHaveScrollableDisplayPort = false; }

  bool SetIsCompositingCheap(bool aCompositingCheap) {
    bool temp = mIsCompositingCheap;
    mIsCompositingCheap = aCompositingCheap;
    return temp;
  }

  bool IsCompositingCheap() const { return mIsCompositingCheap; }
  /**
   * Display the caret if needed.
   */
  bool DisplayCaret(nsIFrame* aFrame, nsDisplayList* aList) {
    nsIFrame* frame = GetCaretFrame();
    if (aFrame == frame && !IsBackgroundOnly()) {
      frame->DisplayCaret(this, aList);
      return true;
    }
    return false;
  }
  /**
   * Get the frame that the caret is supposed to draw in.
   * If the caret is currently invisible, this will be null.
   */
  nsIFrame* GetCaretFrame() { return mCaretFrame; }
  /**
   * Get the rectangle we're supposed to draw the caret into.
   */
  const nsRect& GetCaretRect() { return mCaretRect; }
  /**
   * Get the caret associated with the current presshell.
   */
  nsCaret* GetCaret();

  /**
   * Returns the root scroll frame for the current PresShell, if the PresShell
   * is ignoring viewport scrolling.
   */
  nsIFrame* GetPresShellIgnoreScrollFrame() {
    return CurrentPresShellState()->mPresShellIgnoreScrollFrame;
  }

  /**
   * Notify the display list builder that we're entering a presshell.
   * aReferenceFrame should be a frame in the new presshell.
   * aPointerEventsNoneDoc should be set to true if the frame generating this
   * document is pointer-events:none.
   */
  void EnterPresShell(const nsIFrame* aReferenceFrame,
                      bool aPointerEventsNoneDoc = false);
  /**
   * For print-preview documents, we sometimes need to build display items for
   * the same frames multiple times in the same presentation, with different
   * clipping. Between each such batch of items, call
   * ResetMarkedFramesForDisplayList to make sure that the results of
   * MarkFramesForDisplayList do not carry over between batches.
   */
  void ResetMarkedFramesForDisplayList(const nsIFrame* aReferenceFrame);
  /**
   * Notify the display list builder that we're leaving a presshell.
   */
  void LeavePresShell(const nsIFrame* aReferenceFrame,
                      nsDisplayList* aPaintedContents);

  void IncrementPresShellPaintCount(PresShell* aPresShell);

  /**
   * Returns true if we're currently building a display list that's
   * directly or indirectly under an nsDisplayTransform.
   */
  bool IsInTransform() const { return mInTransform; }

  bool InEventsOnly() const { return mInEventsOnly; }
  /**
   * Indicate whether or not we're directly or indirectly under and
   * nsDisplayTransform or SVG foreignObject.
   */
  void SetInTransform(bool aInTransform) { mInTransform = aInTransform; }

  /**
   * Returns true if we're currently building a display list that's
   * under an nsDisplayFilters.
   */
  bool IsInFilter() const { return mInFilter; }

  /**
   * Return true if we're currently building a display list for a
   * nested presshell.
   */
  bool IsInSubdocument() const { return mPresShellStates.Length() > 1; }

  void SetDisablePartialUpdates(bool aDisable) {
    mDisablePartialUpdates = aDisable;
  }
  bool DisablePartialUpdates() const { return mDisablePartialUpdates; }

  void SetPartialBuildFailed(bool aFailed) { mPartialBuildFailed = aFailed; }
  bool PartialBuildFailed() const { return mPartialBuildFailed; }

  bool IsInActiveDocShell() const { return mIsInActiveDocShell; }
  void SetInActiveDocShell(bool aActive) { mIsInActiveDocShell = aActive; }

  /**
   * Return true if we're currently building a display list for the presshell
   * of a chrome document, or if we're building the display list for a popup.
   */
  bool IsInChromeDocumentOrPopup() const {
    return mIsInChromePresContext || mIsBuildingForPopup;
  }

  /**
   * @return true if images have been set to decode synchronously.
   */
  bool ShouldSyncDecodeImages() const { return mSyncDecodeImages; }

  /**
   * Indicates whether we should synchronously decode images. If true, we decode
   * and draw whatever image data has been loaded. If false, we just draw
   * whatever has already been decoded.
   */
  void SetSyncDecodeImages(bool aSyncDecodeImages) {
    mSyncDecodeImages = aSyncDecodeImages;
  }

  nsDisplayTableBackgroundSet* SetTableBackgroundSet(
      nsDisplayTableBackgroundSet* aTableSet) {
    nsDisplayTableBackgroundSet* old = mTableBackgroundSet;
    mTableBackgroundSet = aTableSet;
    return old;
  }
  nsDisplayTableBackgroundSet* GetTableBackgroundSet() const {
    return mTableBackgroundSet;
  }

  void FreeClipChains();

  /*
   * Frees the temporary display items created during merging.
   */
  void FreeTemporaryItems();

  /**
   * Helper method to generate background painting flags based on the
   * information available in the display list builder.
   */
  uint32_t GetBackgroundPaintFlags();

  /**
   * Helper method to generate nsImageRenderer flags based on the information
   * available in the display list builder.
   */
  uint32_t GetImageRendererFlags() const;

  /**
   * Helper method to generate image decoding flags based on the
   * information available in the display list builder.
   */
  uint32_t GetImageDecodeFlags() const;

  /**
   * Subtracts aRegion from *aVisibleRegion. We avoid letting
   * aVisibleRegion become overcomplex by simplifying it if necessary.
   */
  void SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
                                 const nsRegion& aRegion);

  /**
   * Mark the frames in aFrames to be displayed if they intersect aDirtyRect
   * (which is relative to aDirtyFrame). If the frames have placeholders
   * that might not be displayed, we mark the placeholders and their ancestors
   * to ensure that display list construction descends into them
   * anyway. nsDisplayListBuilder will take care of unmarking them when it is
   * destroyed.
   */
  void MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
                                const nsFrameList& aFrames);
  void MarkFrameForDisplay(nsIFrame* aFrame, const nsIFrame* aStopAtFrame);
  void MarkFrameForDisplayIfVisible(nsIFrame* aFrame,
                                    const nsIFrame* aStopAtFrame);
  void AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame);

  void ClearFixedBackgroundDisplayData();
  /**
   * Mark all child frames that Preserve3D() as needing display.
   * Because these frames include transforms set on their parent, dirty rects
   * for intermediate frames may be empty, yet child frames could still be
   * visible.
   */
  void MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame);

  /**
   * Returns true if we need to descend into this frame when building
   * the display list, even though it doesn't intersect the dirty
   * rect, because it may have out-of-flows that do so.
   */
  bool ShouldDescendIntoFrame(nsIFrame* aFrame, bool aVisible) const {
    return aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) ||
           (aVisible && aFrame->ForceDescendIntoIfVisible()) ||
           GetIncludeAllOutOfFlows();
  }

  /**
   * Returns the list of registered theme geometries.
   */
  nsTArray<nsIWidget::ThemeGeometry> GetThemeGeometries() const {
    nsTArray<nsIWidget::ThemeGeometry> geometries;

    for (const auto& data : mThemeGeometries.Values()) {
      geometries.AppendElements(*data);
    }

    return geometries;
  }

  /**
   * Notifies the builder that a particular themed widget exists
   * at the given rectangle within the currently built display list.
   * For certain appearance values (currently only StyleAppearance::Toolbar and
   * StyleAppearance::WindowTitlebar) this gets called during every display list
   * construction, for every themed widget of the right type within the
   * display list, except for themed widgets which are transformed or have
   * effects applied to them (e.g. CSS opacity or filters).
   *
   * @param aWidgetType the -moz-appearance value for the themed widget
   * @param aItem the item associated with the theme geometry
   * @param aRect the device-pixel rect relative to the widget's displayRoot
   * for the themed widget
   */
  void RegisterThemeGeometry(uint8_t aWidgetType, nsDisplayItem* aItem,
                             const LayoutDeviceIntRect& aRect) {
    if (!mIsPaintingToWindow) {
      return;
    }

    nsTArray<nsIWidget::ThemeGeometry>* geometries =
        mThemeGeometries.GetOrInsertNew(aItem);
    geometries->AppendElement(nsIWidget::ThemeGeometry(aWidgetType, aRect));
  }

  /**
   * Removes theme geometries associated with the given display item |aItem|.
   */
  void UnregisterThemeGeometry(nsDisplayItem* aItem) {
    mThemeGeometries.Remove(aItem);
  }

  /**
   * Adjusts mWindowDraggingRegion to take into account aFrame. If aFrame's
   * -moz-window-dragging value is |drag|, its border box is added to the
   * collected dragging region; if the value is |no-drag|, the border box is
   * subtracted from the region; if the value is |default|, that frame does
   * not influence the window dragging region.
   */
  void AdjustWindowDraggingRegion(nsIFrame* aFrame);

  LayoutDeviceIntRegion GetWindowDraggingRegion() const;

  void RemoveModifiedWindowRegions();
  void ClearRetainedWindowRegions();

  const nsTHashMap<nsPtrHashKey<dom::RemoteBrowser>, dom::EffectsInfo>&
  GetEffectUpdates() const {
    return mEffectsUpdates;
  }

  void AddEffectUpdate(dom::RemoteBrowser* aBrowser,
                       const dom::EffectsInfo& aUpdate);

  /**
   * Allocate memory in our arena. It will only be freed when this display list
   * builder is destroyed. This memory holds nsDisplayItems and
   * DisplayItemClipChain objects.
   *
   * Destructors are called as soon as the item is no longer used.
   */
  void* Allocate(size_t aSize, DisplayListArenaObjectId aId) {
    return mPool.Allocate(aId, aSize);
  }
  void* Allocate(size_t aSize, DisplayItemType aType) {
#define DECLARE_DISPLAY_ITEM_TYPE(name_, ...)                \
  static_assert(size_t(DisplayItemType::TYPE_##name_) ==     \
                    size_t(DisplayListArenaObjectId::name_), \
                "");
#include "nsDisplayItemTypesList.h"
    static_assert(size_t(DisplayItemType::TYPE_MAX) ==
                      size_t(DisplayListArenaObjectId::CLIPCHAIN),
                  "");
    static_assert(size_t(DisplayItemType::TYPE_MAX) + 1 ==
                      size_t(DisplayListArenaObjectId::LISTNODE),
                  "");
#undef DECLARE_DISPLAY_ITEM_TYPE
    return Allocate(aSize, DisplayListArenaObjectId(size_t(aType)));
  }

  void Destroy(DisplayListArenaObjectId aId, void* aPtr) {
    return mPool.Free(aId, aPtr);
  }
  void Destroy(DisplayItemType aType, void* aPtr) {
    return Destroy(DisplayListArenaObjectId(size_t(aType)), aPtr);
  }

  /**
   * Allocate a new ActiveScrolledRoot in the arena. Will be cleaned up
   * automatically when the arena goes away.
   */
  ActiveScrolledRoot* AllocateActiveScrolledRoot(
      const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame);

  /**
   * Allocate a new DisplayItemClipChain object in the arena. Will be cleaned
   * up automatically when the arena goes away.
   */
  const DisplayItemClipChain* AllocateDisplayItemClipChain(
      const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
      const DisplayItemClipChain* aParent);

  /**
   * Intersect two clip chains, allocating the new clip chain items in this
   * builder's arena. The result is parented to aAncestor, and no intersections
   * happen past aAncestor's ASR.
   * That means aAncestor has to be living in this builder's arena already.
   * aLeafClip1 and aLeafClip2 only need to outlive the call to this function,
   * their values are copied into the newly-allocated intersected clip chain
   * and this function does not hold on to any pointers to them.
   */
  const DisplayItemClipChain* CreateClipChainIntersection(
      const DisplayItemClipChain* aAncestor,
      const DisplayItemClipChain* aLeafClip1,
      const DisplayItemClipChain* aLeafClip2);

  /**
   * Same as above, except aAncestor is computed as the nearest common
   * ancestor of the two provided clips.
   */
  const DisplayItemClipChain* CreateClipChainIntersection(
      const DisplayItemClipChain* aLeafClip1,
      const DisplayItemClipChain* aLeafClip2);

  /**
   * Clone the supplied clip chain's chain items into this builder's arena.
   */
  const DisplayItemClipChain* CopyWholeChain(
      const DisplayItemClipChain* aClipChain);

  const ActiveScrolledRoot* GetFilterASR() const { return mFilterASR; }

  /**
   * Merges the display items in |aMergedItems| and returns a new temporary
   * display item.
   * The display items in |aMergedItems| have to be mergeable with each other.
   */
  nsDisplayWrapList* MergeItems(nsTArray<nsDisplayItem*>& aItems);

  /**
   * A helper class used to temporarily set nsDisplayListBuilder properties for
   * building display items.
   * aVisibleRect and aDirtyRect are relative to aForChild.
   */
  class AutoBuildingDisplayList {
   public:
    AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
                            const nsRect& aVisibleRect,
                            const nsRect& aDirtyRect)
        : AutoBuildingDisplayList(aBuilder, aForChild, aVisibleRect, aDirtyRect,
                                  aForChild->IsTransformed()) {}

    AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
                            const nsRect& aVisibleRect,
                            const nsRect& aDirtyRect,
                            const bool aIsTransformed);

    void SetReferenceFrameAndCurrentOffset(const nsIFrame* aFrame,
                                           const nsPoint& aOffset) {
      mBuilder->mCurrentReferenceFrame = aFrame;
      mBuilder->mCurrentOffsetToReferenceFrame = aOffset;
    }

    void SetAdditionalOffset(const nsPoint& aOffset) {
      MOZ_ASSERT(!mBuilder->mAdditionalOffset);
      mBuilder->mAdditionalOffset = Some(aOffset);

      mBuilder->mCurrentOffsetToReferenceFrame += aOffset;
    }

    void RestoreBuildingInvisibleItemsValue() {
      mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems;
    }

    ~AutoBuildingDisplayList() {
      mBuilder->mCurrentFrame = mPrevFrame;
      mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame;
      mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset;
      mBuilder->mVisibleRect = mPrevVisibleRect;
      mBuilder->mDirtyRect = mPrevDirtyRect;
      mBuilder->mAncestorHasApzAwareEventHandler =
          mPrevAncestorHasApzAwareEventHandler;
      mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems;
      mBuilder->mInInvalidSubtree = mPrevInInvalidSubtree;
      mBuilder->mAdditionalOffset = mPrevAdditionalOffset;
      mBuilder->mCompositorHitTestInfo = mPrevCompositorHitTestInfo;
    }

   private:
    nsDisplayListBuilder* mBuilder;
    const nsIFrame* mPrevFrame;
    const nsIFrame* mPrevReferenceFrame;
    nsPoint mPrevOffset;
    Maybe<nsPoint> mPrevAdditionalOffset;
    nsRect mPrevVisibleRect;
    nsRect mPrevDirtyRect;
    gfx::CompositorHitTestInfo mPrevCompositorHitTestInfo;
    bool mPrevAncestorHasApzAwareEventHandler;
    bool mPrevBuildingInvisibleItems;
    bool mPrevInInvalidSubtree;
  };

  /**
   * A helper class to temporarily set the value of mInTransform.
   */
  class AutoInTransformSetter {
   public:
    AutoInTransformSetter(nsDisplayListBuilder* aBuilder, bool aInTransform)
        : mBuilder(aBuilder), mOldValue(aBuilder->mInTransform) {
      aBuilder->mInTransform = aInTransform;
    }

    ~AutoInTransformSetter() { mBuilder->mInTransform = mOldValue; }

   private:
    nsDisplayListBuilder* mBuilder;
    bool mOldValue;
  };

  class AutoInEventsOnly {
   public:
    AutoInEventsOnly(nsDisplayListBuilder* aBuilder, bool aInEventsOnly)
        : mBuilder(aBuilder), mOldValue(aBuilder->mInEventsOnly) {
      aBuilder->mInEventsOnly |= aInEventsOnly;
    }

    ~AutoInEventsOnly() { mBuilder->mInEventsOnly = mOldValue; }

   private:
    nsDisplayListBuilder* mBuilder;
    bool mOldValue;
  };

  /**
   * A helper class to temporarily set the value of mFilterASR and
   * mInFilter.
   */
  class AutoEnterFilter {
   public:
    AutoEnterFilter(nsDisplayListBuilder* aBuilder, bool aUsingFilter)
        : mBuilder(aBuilder),
          mOldValue(aBuilder->mFilterASR),
          mOldInFilter(aBuilder->mInFilter) {
      if (!aBuilder->mFilterASR && aUsingFilter) {
        aBuilder->mFilterASR = aBuilder->CurrentActiveScrolledRoot();
        aBuilder->mInFilter = true;
      }
    }

    ~AutoEnterFilter() {
      mBuilder->mFilterASR = mOldValue;
      mBuilder->mInFilter = mOldInFilter;
    }

   private:
    nsDisplayListBuilder* mBuilder;
    const ActiveScrolledRoot* mOldValue;
    bool mOldInFilter;
  };

  /**
   * Used to update the current active scrolled root on the display list
   * builder, and to create new active scrolled roots.
   */
  class AutoCurrentActiveScrolledRootSetter {
   public:
    explicit AutoCurrentActiveScrolledRootSetter(nsDisplayListBuilder* aBuilder)
        : mBuilder(aBuilder),
          mSavedActiveScrolledRoot(aBuilder->mCurrentActiveScrolledRoot),
          mContentClipASR(aBuilder->ClipState().GetContentClipASR()),
          mDescendantsStartIndex(aBuilder->mActiveScrolledRoots.Length()),
          mUsed(false),
          mOldScrollParentId(aBuilder->mCurrentScrollParentId),
          mOldForceLayer(aBuilder->mForceLayerForScrollParent),
          mOldContainsNonMinimalDisplayPort(
              mBuilder->mContainsNonMinimalDisplayPort),
          mCanBeScrollParent(false) {}

    void SetCurrentScrollParentId(ViewID aScrollId) {
      // Update the old scroll parent id.
      mOldScrollParentId = mBuilder->mCurrentScrollParentId;
      // If this AutoCurrentActiveScrolledRootSetter has the same aScrollId as
      // the previous one on the stack, then that means the scrollframe that
      // created this isn't actually scrollable and cannot participate in
      // scroll handoff. We set mCanBeScrollParent to false to indicate this.
      mCanBeScrollParent = (mOldScrollParentId != aScrollId);
      mBuilder->mCurrentScrollParentId = aScrollId;
      mBuilder->mForceLayerForScrollParent = false;
      mBuilder->mContainsNonMinimalDisplayPort = false;
    }

    bool ShouldForceLayerForScrollParent() const {
      // Only scrollframes participating in scroll handoff can be forced to
      // layerize
      return mCanBeScrollParent && mBuilder->mForceLayerForScrollParent;
    }

    bool GetContainsNonMinimalDisplayPort() const {
      // Only for scrollframes participating in scroll handoff can we return
      // true.
      return mCanBeScrollParent && mBuilder->mContainsNonMinimalDisplayPort;
    }

    ~AutoCurrentActiveScrolledRootSetter() {
      mBuilder->mCurrentActiveScrolledRoot = mSavedActiveScrolledRoot;
      mBuilder->mCurrentScrollParentId = mOldScrollParentId;
      if (mCanBeScrollParent) {
        // If this flag is set, caller code is responsible for having dealt
        // with the current value of mBuilder->mForceLayerForScrollParent, so
        // we can just restore the old value.
        mBuilder->mForceLayerForScrollParent = mOldForceLayer;
      } else {
        // Otherwise we need to keep propagating the force-layerization flag
        // upwards to the next ancestor scrollframe that does participate in
        // scroll handoff.
        mBuilder->mForceLayerForScrollParent |= mOldForceLayer;
      }
      mBuilder->mContainsNonMinimalDisplayPort |=
          mOldContainsNonMinimalDisplayPort;
    }

    void SetCurrentActiveScrolledRoot(
        const ActiveScrolledRoot* aActiveScrolledRoot);

    void EnterScrollFrame(nsIScrollableFrame* aScrollableFrame) {
      MOZ_ASSERT(!mUsed);
      ActiveScrolledRoot* asr = mBuilder->AllocateActiveScrolledRoot(
          mBuilder->mCurrentActiveScrolledRoot, aScrollableFrame);
      mBuilder->mCurrentActiveScrolledRoot = asr;
      mUsed = true;
    }

    void InsertScrollFrame(nsIScrollableFrame* aScrollableFrame);

   private:
    nsDisplayListBuilder* mBuilder;
    /**
     * The builder's mCurrentActiveScrolledRoot at construction time which
     * needs to be restored at destruction time.
     */
    const ActiveScrolledRoot* mSavedActiveScrolledRoot;
    /**
     * If there's a content clip on the builder at construction time, then
     * mContentClipASR is that content clip's ASR, otherwise null. The
     * assumption is that the content clip doesn't get relaxed while this
     * object is on the stack.
     */
    const ActiveScrolledRoot* mContentClipASR;
    /**
     * InsertScrollFrame needs to mutate existing ASRs (those that were
     * created while this object was on the stack), and mDescendantsStartIndex
     * makes it easier to skip ASRs that were created in the past.
     */
    size_t mDescendantsStartIndex;
    /**
     * Flag to make sure that only one of SetCurrentActiveScrolledRoot /
     * EnterScrollFrame / InsertScrollFrame is called per instance of this
     * class.
     */
    bool mUsed;
    ViewID mOldScrollParentId;
    bool mOldForceLayer;
    bool mOldContainsNonMinimalDisplayPort;
    bool mCanBeScrollParent;
  };

  /**
   * Keeps track of the innermost ASR that can be used as the ASR for a
   * container item that wraps all items that were created while this
   * object was on the stack.
   * The rule is: all child items of the container item need to have
   * clipped bounds with respect to the container ASR.
   */
  class AutoContainerASRTracker {
   public:
    explicit AutoContainerASRTracker(nsDisplayListBuilder* aBuilder);

    const ActiveScrolledRoot* GetContainerASR() {
      return mBuilder->mCurrentContainerASR;
    }

    ~AutoContainerASRTracker() {
      mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
          mBuilder->mCurrentContainerASR, mSavedContainerASR);
    }

   private:
    nsDisplayListBuilder* mBuilder;
    const ActiveScrolledRoot* mSavedContainerASR;
  };

  /**
   * A helper class to temporarily set the value of mCurrentScrollbarTarget
   * and mCurrentScrollbarFlags.
   */
  class AutoCurrentScrollbarInfoSetter {
   public:
    AutoCurrentScrollbarInfoSetter(
        nsDisplayListBuilder* aBuilder, ViewID aScrollTargetID,
        const Maybe<layers::ScrollDirection>& aScrollbarDirection,
        bool aWillHaveLayer)
        : mBuilder(aBuilder) {
      aBuilder->mIsBuildingScrollbar = true;
      aBuilder->mCurrentScrollbarTarget = aScrollTargetID;
      aBuilder->mCurrentScrollbarDirection = aScrollbarDirection;
      aBuilder->mCurrentScrollbarWillHaveLayer = aWillHaveLayer;
    }

    ~AutoCurrentScrollbarInfoSetter() {
      // No need to restore old values because scrollbars cannot be nested.
      mBuilder->mIsBuildingScrollbar = false;
      mBuilder->mCurrentScrollbarTarget =
          layers::ScrollableLayerGuid::NULL_SCROLL_ID;
      mBuilder->mCurrentScrollbarDirection.reset();
      mBuilder->mCurrentScrollbarWillHaveLayer = false;
    }

   private:
    nsDisplayListBuilder* mBuilder;
  };

  /**
   * A helper class to temporarily set mBuildingExtraPagesForPageNum.
   */
  class MOZ_RAII AutoPageNumberSetter {
   public:
    AutoPageNumberSetter(nsDisplayListBuilder* aBuilder, const uint8_t aPageNum)
        : mBuilder(aBuilder),
          mOldPageNum(aBuilder->GetBuildingExtraPagesForPageNum()) {
      mBuilder->SetBuildingExtraPagesForPageNum(aPageNum);
    }
    ~AutoPageNumberSetter() {
      mBuilder->SetBuildingExtraPagesForPageNum(mOldPageNum);
    }

   private:
    nsDisplayListBuilder* mBuilder;
    uint8_t mOldPageNum;
  };

  /**
   * A helper class to track current effective transform for items.
   *
   * For frames that is Combines3DTransformWithAncestors(), we need to
   * apply all transforms of ancestors on the same preserves3D chain
   * on the bounds of current frame to the coordination of the 3D
   * context root.  The 3D context root computes it's bounds from
   * these transformed bounds.
   */
  class AutoAccumulateTransform {
   public:
    explicit AutoAccumulateTransform(nsDisplayListBuilder* aBuilder)
        : mBuilder(aBuilder),
          mSavedTransform(aBuilder->mPreserves3DCtx.mAccumulatedTransform) {}

    ~AutoAccumulateTransform() {
      mBuilder->mPreserves3DCtx.mAccumulatedTransform = mSavedTransform;
    }

    void Accumulate(const gfx::Matrix4x4& aTransform) {
      mBuilder->mPreserves3DCtx.mAccumulatedTransform =
          aTransform * mBuilder->mPreserves3DCtx.mAccumulatedTransform;
    }

    const gfx::Matrix4x4& GetCurrentTransform() {
      return mBuilder->mPreserves3DCtx.mAccumulatedTransform;
    }

    void StartRoot() {
      mBuilder->mPreserves3DCtx.mAccumulatedTransform = gfx::Matrix4x4();
    }

   private:
    nsDisplayListBuilder* mBuilder;
    gfx::Matrix4x4 mSavedTransform;
  };

  /**
   * A helper class to collect bounds rects of descendants.
   *
   * For a 3D context root, it's bounds is computed from the bounds of
   * descendants.  If we transform bounds frame by frame applying
   * transforms, the bounds may turn to empty for any singular
   * transform on the path, but it is not empty for the accumulated
   * transform.
   */
  class AutoAccumulateRect {
   public:
    explicit AutoAccumulateRect(nsDisplayListBuilder* aBuilder)
        : mBuilder(aBuilder),
          mSavedRect(aBuilder->mPreserves3DCtx.mAccumulatedRect) {
      aBuilder->mPreserves3DCtx.mAccumulatedRect = nsRect();
      aBuilder->mPreserves3DCtx.mAccumulatedRectLevels++;
    }

    ~AutoAccumulateRect() {
      mBuilder->mPreserves3DCtx.mAccumulatedRect = mSavedRect;
      mBuilder->mPreserves3DCtx.mAccumulatedRectLevels--;
    }

   private:
    nsDisplayListBuilder* mBuilder;
    nsRect mSavedRect;
  };

  void AccumulateRect(const nsRect& aRect) {
    mPreserves3DCtx.mAccumulatedRect.UnionRect(mPreserves3DCtx.mAccumulatedRect,
                                               aRect);
  }

  const nsRect& GetAccumulatedRect() {
    return mPreserves3DCtx.mAccumulatedRect;
  }

  /**
   * The level is increased by one for items establishing 3D rendering
   * context and starting a new accumulation.
   */
  int GetAccumulatedRectLevels() {
    return mPreserves3DCtx.mAccumulatedRectLevels;
  }

  struct OutOfFlowDisplayData {
    OutOfFlowDisplayData(
        const DisplayItemClipChain* aContainingBlockClipChain,
        const DisplayItemClipChain* aCombinedClipChain,
        const ActiveScrolledRoot* aContainingBlockActiveScrolledRoot,
        const ViewID& aScrollParentId, const nsRect& aVisibleRect,
        const nsRect& aDirtyRect)
        : mContainingBlockClipChain(aContainingBlockClipChain),
          mCombinedClipChain(aCombinedClipChain),
          mContainingBlockActiveScrolledRoot(
              aContainingBlockActiveScrolledRoot),
          mVisibleRect(aVisibleRect),
          mDirtyRect(aDirtyRect),
          mScrollParentId(aScrollParentId) {}
    const DisplayItemClipChain* mContainingBlockClipChain;
    const DisplayItemClipChain*
        mCombinedClipChain;  // only necessary for the special case of top layer
    const ActiveScrolledRoot* mContainingBlockActiveScrolledRoot;

    // If this OutOfFlowDisplayData is associated with the ViewportFrame
    // of a document that has a resolution (creating separate visual and
    // layout viewports with their own coordinate spaces), these rects
    // are in layout coordinates. Similarly, GetVisibleRectForFrame() in
    // such a case returns a quantity in layout coordinates.
    nsRect mVisibleRect;
    nsRect mDirtyRect;
    ViewID mScrollParentId;

    static nsRect ComputeVisibleRectForFrame(nsDisplayListBuilder* aBuilder,
                                             nsIFrame* aFrame,
                                             const nsRect& aVisibleRect,
                                             const nsRect& aDirtyRect,
                                             nsRect* aOutDirtyRect);

    nsRect GetVisibleRectForFrame(nsDisplayListBuilder* aBuilder,
                                  nsIFrame* aFrame, nsRect* aDirtyRect) {
      return ComputeVisibleRectForFrame(aBuilder, aFrame, mVisibleRect,
                                        mDirtyRect, aDirtyRect);
    }
  };

  NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty,
                                      OutOfFlowDisplayData)

  struct DisplayListBuildingData {
    nsIFrame* mModifiedAGR = nullptr;
    nsRect mDirtyRect;
  };
  NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListBuildingRect,
                                      DisplayListBuildingData)

  NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListBuildingDisplayPortRect,
                                      nsRect)

  static OutOfFlowDisplayData* GetOutOfFlowData(nsIFrame* aFrame) {
    if (!aFrame->GetParent()) {
      return nullptr;
    }
    return aFrame->GetParent()->GetProperty(OutOfFlowDisplayDataProperty());
  }

  nsPresContext* CurrentPresContext();

  OutOfFlowDisplayData* GetCurrentFixedBackgroundDisplayData() {
    auto& displayData = CurrentPresShellState()->mFixedBackgroundDisplayData;
    return displayData ? displayData.ptr() : nullptr;
  }

  /**
   * Accumulates opaque stuff into the window opaque region.
   */
  void AddWindowOpaqueRegion(nsIFrame* aFrame, const nsRect& aBounds) {
    if (IsRetainingDisplayList()) {
      mRetainedWindowOpaqueRegion.Add(aFrame, aBounds);
      return;
    }
    mWindowOpaqueRegion.Or(mWindowOpaqueRegion, aBounds);
  }
  /**
   * Returns the window opaque region built so far. This may be incomplete
   * since the opaque region is built during layer construction.
   */
  const nsRegion GetWindowOpaqueRegion() {
    return IsRetainingDisplayList() ? mRetainedWindowOpaqueRegion.ToRegion()
                                    : mWindowOpaqueRegion;
  }

  /**
   * mContainsBlendMode is true if we processed a display item that
   * has a blend mode attached. We do this so we can insert a
   * nsDisplayBlendContainer in the parent stacking context.
   */
  void SetContainsBlendMode(bool aContainsBlendMode) {
    mContainsBlendMode = aContainsBlendMode;
  }
  bool ContainsBlendMode() const { return mContainsBlendMode; }

  DisplayListClipState& ClipState() { return mClipState; }
  const ActiveScrolledRoot* CurrentActiveScrolledRoot() {
    return mCurrentActiveScrolledRoot;
  }
  const ActiveScrolledRoot* CurrentAncestorASRStackingContextContents() {
    return mCurrentContainerASR;
  }

  /**
   * Add the current frame to the will-change budget if possible and
   * remeber the outcome. Subsequent calls to IsInWillChangeBudget
   * will return the same value as return here.
   */
  bool AddToWillChangeBudget(nsIFrame* aFrame, const nsSize& aSize);

  /**
   * This will add the current frame to the will-change budget the first
   * time it is seen. On subsequent calls this will return the same
   * answer. This effectively implements a first-come, first-served
   * allocation of the will-change budget.
   */
  bool IsInWillChangeBudget(nsIFrame* aFrame, const nsSize& aSize);

  /**
   * Clears the will-change budget status for the given |aFrame|.
   * This will also remove the frame from will-change budgets.
   */
  void ClearWillChangeBudgetStatus(nsIFrame* aFrame);

  /**
   * Removes the given |aFrame| from will-change budgets.
   */
  void RemoveFromWillChangeBudgets(const nsIFrame* aFrame);

  /**
   * Clears the will-change budgets.
   */
  void ClearWillChangeBudgets();

  void EnterSVGEffectsContents(nsIFrame* aEffectsFrame,
                               nsDisplayList* aHoistedItemsStorage);
  void ExitSVGEffectsContents();

  bool ShouldBuildScrollInfoItemsForHoisting() const;

  void AppendNewScrollInfoItemForHoisting(
      nsDisplayScrollInfoLayer* aScrollInfoItem);

  /**
   * A helper class to install/restore nsDisplayListBuilder::mPreserves3DCtx.
   *
   * mPreserves3DCtx is used by class AutoAccumulateTransform &
   * AutoAccumulateRect to passing data between frames in the 3D
   * context.  If a frame create a new 3D context, it should restore
   * the value of mPreserves3DCtx before returning back to the parent.
   * This class do it for the users.
   */
  class AutoPreserves3DContext {
   public:
    explicit AutoPreserves3DContext(nsDisplayListBuilder* aBuilder)
        : mBuilder(aBuilder), mSavedCtx(aBuilder->mPreserves3DCtx) {}

    ~AutoPreserves3DContext() { mBuilder->mPreserves3DCtx = mSavedCtx; }

   private:
    nsDisplayListBuilder* mBuilder;
    Preserves3DContext mSavedCtx;
  };

  const nsRect GetPreserves3DRect() const {
    return mPreserves3DCtx.mVisibleRect;
  }

  void SavePreserves3DRect() { mPreserves3DCtx.mVisibleRect = mVisibleRect; }

  void SavePreserves3DAllowAsyncAnimation(bool aValue) {
    mPreserves3DCtx.mAllowAsyncAnimation = aValue;
  }

  bool GetPreserves3DAllowAsyncAnimation() const {
    return mPreserves3DCtx.mAllowAsyncAnimation;
  }

  bool IsBuildingInvisibleItems() const { return mBuildingInvisibleItems; }

  void SetBuildingInvisibleItems(bool aBuildingInvisibleItems) {
    mBuildingInvisibleItems = aBuildingInvisibleItems;
  }

  void SetBuildingExtraPagesForPageNum(uint8_t aPageNum) {
    mBuildingExtraPagesForPageNum = aPageNum;
  }
  uint8_t GetBuildingExtraPagesForPageNum() const {
    return mBuildingExtraPagesForPageNum;
  }

  bool HitTestIsForVisibility() const { return mVisibleThreshold.isSome(); }

  float VisibilityThreshold() const {
    MOZ_DIAGNOSTIC_ASSERT(HitTestIsForVisibility());
    return mVisibleThreshold.valueOr(1.0f);
  }

  void SetHitTestIsForVisibility(float aVisibleThreshold) {
    mVisibleThreshold = Some(aVisibleThreshold);
  }

  bool ShouldBuildAsyncZoomContainer() const {
    return mBuildAsyncZoomContainer;
  }
  void UpdateShouldBuildAsyncZoomContainer();

  void UpdateShouldBuildBackdropRootContainer();

  bool ShouldRebuildDisplayListDueToPrefChange();

  /**
   * Represents a region composed of frame/rect pairs.
   * WeakFrames are used to track whether a rect still belongs to the region.
   * Modified frames and rects are removed and re-added to the region if needed.
   */
  struct WeakFrameRegion {
    /**
     * A wrapper to store WeakFrame and the pointer to the underlying frame.
     * This is needed because WeakFrame does not store the frame pointer after
     * the frame has been deleted.
     */
    struct WeakFrameWrapper {
      explicit WeakFrameWrapper(nsIFrame* aFrame)
          : mWeakFrame(new WeakFrame(aFrame)), mFrame(aFrame) {}

      UniquePtr<WeakFrame> mWeakFrame;
      void* mFrame;
    };

    nsTHashSet<void*> mFrameSet;
    nsTArray<WeakFrameWrapper> mFrames;
    nsTArray<pixman_box32_t> mRects;

    template <typename RectType>
    void Add(nsIFrame* aFrame, const RectType& aRect) {
      if (mFrameSet.Contains(aFrame)) {
        return;
      }

      mFrameSet.Insert(aFrame);
      mFrames.AppendElement(WeakFrameWrapper(aFrame));
      mRects.AppendElement(nsRegion::RectToBox(aRect));
    }

    void Clear() {
      mFrameSet.Clear();
      mFrames.Clear();
      mRects.Clear();
    }

    void RemoveModifiedFramesAndRects();

    size_t SizeOfExcludingThis(MallocSizeOf) const;

    typedef gfx::ArrayView<pixman_box32_t> BoxArrayView;

    nsRegion ToRegion() const { return nsRegion(BoxArrayView(mRects)); }

    LayoutDeviceIntRegion ToLayoutDeviceIntRegion() const {
      return LayoutDeviceIntRegion(BoxArrayView(mRects));
    }
  };

  void AddScrollFrameToNotify(nsIScrollableFrame* aScrollFrame);
  void NotifyAndClearScrollFrames();

  // Helper class to find what link spec (if any) to associate with a frame,
  // recording it in the builder, and generate the corresponding DisplayItem.
  // This also takes care of generating a named destination for internal links
  // if the element has an id or name attribute.
  class Linkifier {
   public:
    Linkifier(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
              nsDisplayList* aList);

    ~Linkifier() {
      if (mBuilderToReset) {
        mBuilderToReset->mLinkSpec.Truncate(0);
      }
    }

    void MaybeAppendLink(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);

   private:
    nsDisplayListBuilder* mBuilderToReset = nullptr;
    nsDisplayList* mList;
  };

  /**
   * Returns the nearest ancestor frame to aFrame that is considered to have
   * (or will have) animated geometry. This can return aFrame.
   */
  nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame);

  /**
   * Returns true if this is a retained builder and reuse stacking contexts
   * mode is enabled by pref.
   */
  bool IsReusingStackingContextItems() const {
    return mIsReusingStackingContextItems;
  }

  /**
   * Adds display item |aItem| to the reuseable display items set.
   */
  void AddReusableDisplayItem(nsDisplayItem* aItem);

  /**
   * Removes display item |aItem| from the reuseable display items set.
   * This is needed because display items are sometimes deleted during
   * display list building.
   * Called by |nsDisplayItem::Destroy()| when the item has been reused.
   */
  void RemoveReusedDisplayItem(nsDisplayItem* aItem);

  /**
   * Clears the reuseable display items set.
   */
  void ClearReuseableDisplayItems();

  /**
   * Marks the given display item |aItem| as reused, and updates the necessary
   * display list builder state.
   */
  void ReuseDisplayItem(nsDisplayItem* aItem);

 private:
  bool MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
                                    const nsRect& aVisibleRect,
                                    const nsRect& aDirtyRect);

  friend class nsDisplayBackgroundImage;
  friend class RetainedDisplayListBuilder;

  /**
   * Returns whether a frame acts as an animated geometry root, optionally
   * returning the next ancestor to check.
   */
  bool IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent = nullptr);

  struct PresShellState {
    PresShell* mPresShell;
#ifdef DEBUG
    Maybe<nsAutoLayoutPhase> mAutoLayoutPhase;
#endif
    Maybe<OutOfFlowDisplayData> mFixedBackgroundDisplayData;
    uint32_t mFirstFrameMarkedForDisplay;
    uint32_t mFirstFrameWithOOFData;
    bool mIsBackgroundOnly;
    // This is a per-document flag turning off event handling for all content
    // in the document, and is set when we enter a subdocument for a pointer-
    // events:none frame.
    bool mInsidePointerEventsNoneDoc;
    bool mTouchEventPrefEnabledDoc;
    nsIFrame* mPresShellIgnoreScrollFrame;
  };

  PresShellState* CurrentPresShellState() {
    NS_ASSERTION(mPresShellStates.Length() > 0,
                 "Someone forgot to enter a presshell");
    return &mPresShellStates[mPresShellStates.Length() - 1];
  }

  void AddSizeOfExcludingThis(nsWindowSizes&) const;

  struct FrameWillChangeBudget {
    FrameWillChangeBudget() : mPresContext(nullptr), mUsage(0) {}

    FrameWillChangeBudget(const nsPresContext* aPresContext, uint32_t aUsage)
        : mPresContext(aPresContext), mUsage(aUsage) {}

    const nsPresContext* mPresContext;
    uint32_t mUsage;
  };

  // will-change budget tracker
  typedef uint32_t DocumentWillChangeBudget;

  nsIFrame* const mReferenceFrame;
  nsIFrame* mIgnoreScrollFrame;

  const ActiveScrolledRoot* mCurrentActiveScrolledRoot;
  const ActiveScrolledRoot* mCurrentContainerASR;
  // mCurrentFrame is the frame that we're currently calling (or about to call)
  // BuildDisplayList on.
  const nsIFrame* mCurrentFrame;
  // The reference frame for mCurrentFrame.
  const nsIFrame* mCurrentReferenceFrame;

  nsIFrame* mCaretFrame;
  // A temporary list that we append scroll info items to while building
  // display items for the contents of frames with SVG effects.
  // Only non-null when ShouldBuildScrollInfoItemsForHoisting() is true.
  // This is a pointer and not a real nsDisplayList value because the
  // nsDisplayList class is defined below this class, so we can't use it here.
  nsDisplayList* mScrollInfoItemsForHoisting;
  nsTArray<RefPtr<ActiveScrolledRoot>> mActiveScrolledRoots;
  DisplayItemClipChain* mFirstClipChainToDestroy;
  nsTArray<nsDisplayItem*> mTemporaryItems;
  nsDisplayTableBackgroundSet* mTableBackgroundSet;
  ViewID mCurrentScrollParentId;
  ViewID mCurrentScrollbarTarget;

  nsTArray<nsIFrame*> mSVGEffectsFrames;
  // When we are inside a filter, the current ASR at the time we entered the
  // filter. Otherwise nullptr.
  const ActiveScrolledRoot* mFilterASR;
  nsCString mLinkSpec;  // Destination of link currently being emitted, if any.

  // Optimized versions for non-retained display list.
  LayoutDeviceIntRegion mWindowDraggingRegion;
  LayoutDeviceIntRegion mWindowNoDraggingRegion;
  nsRegion mWindowOpaqueRegion;

  nsClassHashtable<nsPtrHashKey<nsDisplayItem>,
                   nsTArray<nsIWidget::ThemeGeometry>>
      mThemeGeometries;
  DisplayListClipState mClipState;
  nsTHashMap<nsPtrHashKey<const nsPresContext>, DocumentWillChangeBudget>
      mDocumentWillChangeBudgets;

  // Any frame listed in this set is already counted in the budget
  // and thus is in-budget.
  nsTHashMap<nsPtrHashKey<const nsIFrame>, FrameWillChangeBudget>
      mFrameWillChangeBudgets;

  nsTHashMap<nsPtrHashKey<dom::RemoteBrowser>, dom::EffectsInfo>
      mEffectsUpdates;

  nsTHashSet<nsCString> mDestinations;  // Destination names emitted.

  // Stores reusable items collected during display list preprocessing.
  nsTHashSet<nsDisplayItem*> mReuseableItems;

  // Tracked regions used for retained display list.
  WeakFrameRegion mRetainedWindowDraggingRegion;
  WeakFrameRegion mRetainedWindowNoDraggingRegion;

  // Window opaque region is calculated during layer building.
  WeakFrameRegion mRetainedWindowOpaqueRegion;

  std::unordered_set<const DisplayItemClipChain*, DisplayItemClipChainHasher,
                     DisplayItemClipChainEqualer>
      mClipDeduplicator;
  std::unordered_set<nsIScrollableFrame*> mScrollFramesToNotify;

  AutoTArray<nsIFrame*, 20> mFramesWithOOFData;
  AutoTArray<nsIFrame*, 40> mFramesMarkedForDisplayIfVisible;
  AutoTArray<PresShellState, 8> mPresShellStates;

  using Arena = nsPresArena<32768, DisplayListArenaObjectId,
                            size_t(DisplayListArenaObjectId::COUNT)>;
  Arena mPool;

  AutoTArray<nsIFrame*, 400> mFramesMarkedForDisplay;

  gfx::CompositorHitTestInfo mCompositorHitTestInfo;

  // The offset from mCurrentFrame to mCurrentReferenceFrame.
  nsPoint mCurrentOffsetToReferenceFrame;

  Maybe<float> mVisibleThreshold;

  Maybe<nsPoint> mAdditionalOffset;

  // Relative to mCurrentFrame.
  nsRect mVisibleRect;
  nsRect mDirtyRect;
  nsRect mCaretRect;

  Preserves3DContext mPreserves3DCtx;

  uint8_t mBuildingExtraPagesForPageNum;

  nsDisplayListBuilderMode mMode;
  static uint32_t sPaintSequenceNumber;

  bool mContainsBlendMode;
  bool mIsBuildingScrollbar;
  bool mCurrentScrollbarWillHaveLayer;
  bool mBuildCaret;
  bool mRetainingDisplayList;
  bool mPartialUpdate;
  bool mIgnoreSuppression;
  bool mIncludeAllOutOfFlows;
  bool mDescendIntoSubdocuments;
  bool mSelectedFramesOnly;
  bool mAllowMergingAndFlattening;
  // True when we're building a display list that's directly or indirectly
  // under an nsDisplayTransform
  bool mInTransform;
  bool mInEventsOnly;
  bool mInFilter;
  bool mInPageSequence;
  bool mIsInChromePresContext;
  bool mSyncDecodeImages;
  bool mIsPaintingToWindow;
  bool mUseHighQualityScaling;
  bool mIsPaintingForWebRender;
  bool mIsCompositingCheap;
  bool mAncestorHasApzAwareEventHandler;
  // True when the first async-scrollable scroll frame for which we build a
  // display list has a display port. An async-scrollable scroll frame is one
  // which WantsAsyncScroll().
  bool mHaveScrollableDisplayPort;
  bool mWindowDraggingAllowed;
  bool mIsBuildingForPopup;
  bool mForceLayerForScrollParent;
  bool mContainsNonMinimalDisplayPort;
  bool mAsyncPanZoomEnabled;
  bool mBuildingInvisibleItems;
  bool mIsBuilding;
  bool mInInvalidSubtree;
  bool mBuildCompositorHitTestInfo;
  bool mDisablePartialUpdates;
  bool mPartialBuildFailed;
  bool mIsInActiveDocShell;
  bool mBuildAsyncZoomContainer;
  bool mIsRelativeToLayoutViewport;
  bool mUseOverlayScrollbars;
  bool mAlwaysLayerizeScrollbars;

  bool mIsReusingStackingContextItems;

  Maybe<layers::ScrollDirection> mCurrentScrollbarDirection;
};

// All types are defined in nsDisplayItemTypes.h
#define NS_DISPLAY_DECL_NAME(n, e)                                           \
  const char* Name() const override { return n; }                            \
  constexpr static DisplayItemType ItemType() { return DisplayItemType::e; } \
                                                                             \
 private:                                                                    \
  void* operator new(size_t aSize, nsDisplayListBuilder* aBuilder) {         \
    return aBuilder->Allocate(aSize, DisplayItemType::e);                    \
  }                                                                          \
                                                                             \
  template <typename T, typename F, typename... Args>                        \
  friend T* mozilla::MakeDisplayItemWithIndex(                               \
      nsDisplayListBuilder* aBuilder, F* aFrame, const uint16_t aIndex,      \
      Args&&... aArgs);                                                      \
                                                                             \
 public:

#define NS_DISPLAY_ALLOW_CLONING()                                          \
  template <typename T>                                                     \
  friend T* mozilla::MakeClone(nsDisplayListBuilder* aBuilder,              \
                               const T* aItem);                             \
                                                                            \
  nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override { \
    return MakeClone(aBuilder, this);                                       \
  }

template <typename T>
MOZ_ALWAYS_INLINE T* MakeClone(nsDisplayListBuilder* aBuilder, const T* aItem) {
  static_assert(std::is_base_of<nsDisplayWrapList, T>::value,
                "Display item type should be derived from nsDisplayWrapList");
  T* item = new (aBuilder) T(aBuilder, *aItem);
  item->SetType(T::ItemType());
  return item;
}

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
void AssertUniqueItem(nsDisplayItem* aItem);
#endif

/**
 * Returns true, if a display item of given |aType| needs to be built within
 * opacity:0 container.
 */
bool ShouldBuildItemForEvents(const DisplayItemType aType);

/**
 * Initializes the hit test information of |aItem| if the item type supports it.
 */
void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
                           nsPaintedDisplayItem* aItem,
                           const DisplayItemType aType);

template <typename T, typename F, typename... Args>
MOZ_ALWAYS_INLINE T* MakeDisplayItemWithIndex(nsDisplayListBuilder* aBuilder,
                                              F* aFrame, const uint16_t aIndex,
                                              Args&&... aArgs) {
  static_assert(std::is_base_of<nsDisplayItem, T>::value,
                "Display item type should be derived from nsDisplayItem");
  static_assert(std::is_base_of<nsIFrame, F>::value,
                "Frame type should be derived from nsIFrame");

  const DisplayItemType type = T::ItemType();
  if (aBuilder->InEventsOnly() && !ShouldBuildItemForEvents(type)) {
    // This item is not needed for events.
    return nullptr;
  }

  T* item = new (aBuilder) T(aBuilder, aFrame, std::forward<Args>(aArgs)...);

  if (type != DisplayItemType::TYPE_GENERIC) {
    item->SetType(type);
  }

  item->SetPerFrameIndex(aIndex);
  item->SetExtraPageForPageNum(aBuilder->GetBuildingExtraPagesForPageNum());

  nsPaintedDisplayItem* paintedItem = item->AsPaintedDisplayItem();
  if (paintedItem) {
    InitializeHitTestInfo(aBuilder, paintedItem, type);
  }

  if (aBuilder->InInvalidSubtree() ||
      item->FrameForInvalidation()->IsFrameModified()) {
    item->SetModifiedFrame(true);
  }

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  if (aBuilder->IsRetainingDisplayList() && aBuilder->IsBuilding()) {
    AssertUniqueItem(item);
  }

  // Verify that InInvalidSubtree matches invalidation frame's modified state.
  if (aBuilder->InInvalidSubtree()) {
    MOZ_DIAGNOSTIC_ASSERT(
        AnyContentAncestorModified(item->FrameForInvalidation()));
  }

  DebugOnly<bool> isContainerType =
      (GetDisplayItemFlagsForType(type) & TYPE_IS_CONTAINER);

  MOZ_ASSERT(item->HasChildren() == isContainerType,
             "Container items must have container display item flag set.");
#endif

  DL_LOGV("Created display item %p (%s) (frame: %p)", item, item->Name(),
          aFrame);

  return item;
}

template <typename T, typename F, typename... Args>
MOZ_ALWAYS_INLINE T* MakeDisplayItem(nsDisplayListBuilder* aBuilder, F* aFrame,
                                     Args&&... aArgs) {
  return MakeDisplayItemWithIndex<T>(aBuilder, aFrame, 0,
                                     std::forward<Args>(aArgs)...);
}

/*
 * nsDisplayItemBase is a base-class for all display items. It is mainly
 * responsible for handling the frame-display item 1:n relationship, as well as
 * storing the state needed for display list merging.
 *
 * Display items are arena-allocated during display list construction.
 *
 * Display items can be containers --- i.e., they can perform hit testing
 * and painting by recursively traversing a list of child items.
 *
 * Display items belong to a list at all times (except temporarily as they
 * move from one list to another).
 */
class nsDisplayItem {
 public:
  using LayerManager = layers::LayerManager;
  using WebRenderLayerManager = layers::WebRenderLayerManager;
  using StackingContextHelper = layers::StackingContextHelper;
  using ViewID = layers::ScrollableLayerGuid::ViewID;

  /**
   * Downcasts this item to nsPaintedDisplayItem, if possible.
   */
  virtual nsPaintedDisplayItem* AsPaintedDisplayItem() { return nullptr; }
  virtual const nsPaintedDisplayItem* AsPaintedDisplayItem() const {
    return nullptr;
  }

  /**
   * Downcasts this item to nsDisplayWrapList, if possible.
   */
  virtual nsDisplayWrapList* AsDisplayWrapList() { return nullptr; }
  virtual const nsDisplayWrapList* AsDisplayWrapList() const { return nullptr; }

  /**
   * Create a clone of this item.
   */
  virtual nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const {
    return nullptr;
  }

  /**
   * Checks if the given display item can be merged with this item.
   * @return true if the merging is possible, otherwise false.
   */
  virtual bool CanMerge(const nsDisplayItem* aItem) const { return false; }

  /**
   * Frees the memory allocated for this display item.
   * The given display list builder must have allocated this display item.
   */
  virtual void Destroy(nsDisplayListBuilder* aBuilder) {
    const DisplayItemType type = GetType();
    DL_LOGV("Destroying display item %p (%s)", this, Name());

    if (IsReusedItem()) {
      aBuilder->RemoveReusedDisplayItem(this);
    }

    this->~nsDisplayItem();
    aBuilder->Destroy(type, this);
  }

  /**
   * Returns the frame that this display item was created for.
   * Never returns null.
   */
  inline nsIFrame* Frame() const {
    MOZ_ASSERT(mFrame, "Trying to use display item after frame deletion!");
    return mFrame;
  }

  /**
   * Called when the display item is prepared for deletion. The display item
   * should not be used after calling this function.
   */
  virtual void RemoveFrame(nsIFrame* aFrame) {
    MOZ_ASSERT(aFrame);

    if (mFrame && aFrame == mFrame) {
      mFrame = nullptr;
      SetDeletedFrame();
    }
  }

  /**
   * A display item can depend on multiple different frames for invalidation.
   */
  virtual nsIFrame* GetDependentFrame() { return nullptr; }

  /**
   * Returns the frame that provides the style data, and should
   * be checked when deciding if this display item can be reused.
   */
  virtual nsIFrame* FrameForInvalidation() const { return Frame(); }

  /**
   * Display items can override this to communicate that they won't
   * contribute any visual information (for example fully transparent).
   */
  virtual bool IsInvisible() const { return false; }

  /**
   * Returns the printable name of this display item.
   */
  virtual const char* Name() const = 0;

  /**
   * Some consecutive items should be rendered together as a unit, e.g.,
   * outlines for the same element. For this, we need a way for items to
   * identify their type. We use the type for other purposes too.
   */
  DisplayItemType GetType() const {
    MOZ_ASSERT(mType != DisplayItemType::TYPE_ZERO,
               "Display item should have a valid type!");
    return mType;
  }

  /**
   * Pairing this with the Frame() pointer gives a key that
   * uniquely identifies this display item in the display item tree.
   */
  uint32_t GetPerFrameKey() const {
    // The top 8 bits are the page index
    // The middle 16 bits of the per frame key uniquely identify the display
    // item when there are more than one item of the same type for a frame.
    // The low 8 bits are the display item type.
    return (static_cast<uint32_t>(mExtraPageForPageNum)
            << (TYPE_BITS + (sizeof(mPerFrameIndex) * 8))) |
           (static_cast<uint32_t>(mPerFrameIndex) << TYPE_BITS) |
           static_cast<uint32_t>(mType);
  }

  /**
   * Returns true if this item was reused during display list merging.
   */
  bool IsReused() const { return mItemFlags.contains(ItemFlag::ReusedItem); }

  void SetReused(bool aReused) { SetItemFlag(ItemFlag::ReusedItem, aReused); }

  /**
   * Returns true if this item can be reused during display list merging.
   */
  bool CanBeReused() const {
    return !mItemFlags.contains(ItemFlag::CantBeReused);
  }

  void SetCantBeReused() { mItemFlags += ItemFlag::CantBeReused; }

  bool CanBeCached() const {
    return !mItemFlags.contains(ItemFlag::CantBeCached);
  }

  void SetCantBeCached() { mItemFlags += ItemFlag::CantBeCached; }

  bool IsOldItem() const { return !!mOldList; }

  /**
   * Returns true if the frame of this display item is in a modified subtree.
   */
  bool HasModifiedFrame() const {
    return mItemFlags.contains(ItemFlag::ModifiedFrame);
  }

  void SetModifiedFrame(bool aModified) {
    SetItemFlag(ItemFlag::ModifiedFrame, aModified);
  }

  bool HasDeletedFrame() const;

  /**
   * Set the nsDisplayList that this item belongs to, and what index it is
   * within that list.
   * Temporary state for merging used by RetainedDisplayListBuilder.
   */
  void SetOldListIndex(nsDisplayList* aList, OldListIndex aIndex,
                       uint32_t aListKey, uint32_t aNestingDepth) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    mOldListKey = aListKey;
    mOldNestingDepth = aNestingDepth;
#endif
    mOldList = reinterpret_cast<uintptr_t>(aList);
    mOldListIndex = aIndex;
  }

  bool GetOldListIndex(nsDisplayList* aList, uint32_t aListKey,
                       OldListIndex* aOutIndex) {
    if (mOldList != reinterpret_cast<uintptr_t>(aList)) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
      MOZ_CRASH_UNSAFE_PRINTF(
          "Item found was in the wrong list! type %d "
          "(outer type was %d at depth %d, now is %d)",
          GetPerFrameKey(), mOldListKey, mOldNestingDepth, aListKey);
#endif
      return false;
    }
    *aOutIndex = mOldListIndex;
    return true;
  }

  /**
   * Returns the display list containing the children of this display item.
   * The children may be in a different coordinate system than this item.
   */
  virtual RetainedDisplayList* GetChildren() const { return nullptr; }
  bool HasChildren() const { return GetChildren(); }

  /**
   * Display items with children may return true here. This causes the
   * display list iterator to descend into the child display list.
   */
  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
    return false;
  }

  virtual bool CreatesStackingContextHelper() { return false; }

  /**
   * Returns true if this item can be moved asynchronously on the compositor,
   * see RetainedDisplayListBuilder.cpp comments.
   */
  virtual bool CanMoveAsync() { return false; }

 protected:
  // This is never instantiated directly (it has pure virtual methods), so no
  // need to count constructors and destructors.
  nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
  nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                const ActiveScrolledRoot* aActiveScrolledRoot);

  /**
   * The custom copy-constructor is implemented to prevent copying the saved
   * state of the item.
   * This is currently only used when creating temporary items for merging.
   */
  nsDisplayItem(nsDisplayListBuilder* aBuilder, const nsDisplayItem& aOther)
      : mFrame(aOther.mFrame),
        mItemFlags(aOther.mItemFlags),
        mType(aOther.mType),
        mExtraPageForPageNum(aOther.mExtraPageForPageNum),
        mPerFrameIndex(aOther.mPerFrameIndex),
        mBuildingRect(aOther.mBuildingRect),
        mToReferenceFrame(aOther.mToReferenceFrame),
        mActiveScrolledRoot(aOther.mActiveScrolledRoot),
        mClipChain(aOther.mClipChain) {
    MOZ_COUNT_CTOR(nsDisplayItem);
    // TODO: It might be better to remove the flags that aren't copied.
    if (aOther.ForceNotVisible()) {
      mItemFlags += ItemFlag::ForceNotVisible;
    }
    if (mFrame->In3DContextAndBackfaceIsHidden()) {
      mItemFlags += ItemFlag::BackfaceHidden;
    }
    if (aOther.Combines3DTransformWithAncestors()) {
      mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
    }
  }

  virtual ~nsDisplayItem() {
    MOZ_COUNT_DTOR(nsDisplayItem);
    if (mFrame) {
      mFrame->RemoveDisplayItem(this);
    }
  }

  void SetType(const DisplayItemType aType) { mType = aType; }

  void SetPerFrameIndex(const uint16_t aIndex) { mPerFrameIndex = aIndex; }

  // Display list building for printing can build duplicate
  // container display items when they contain a mixture of
  // OOF and normal content that is spread across multiple
  // pages. We include the page number for the duplicates
  // to make our GetPerFrameKey unique.
  void SetExtraPageForPageNum(const uint8_t aPageNum) {
    mExtraPageForPageNum = aPageNum;
  }

  void SetDeletedFrame();

 public:
  nsDisplayItem() = delete;
  nsDisplayItem(const nsDisplayItem&) = delete;

  /**
   * Invalidate cached information that depends on this node's contents, after
   * a mutation of those contents.
   *
   * Specifically, if you mutate an |nsDisplayItem| in a way that would change
   * the WebRender display list items generated for it, you should call this
   * method.
   *
   * If a |RestoreState| method exists to restore some piece of state, that's a
   * good indication that modifications to said state should be accompanied by a
   * call to this method. Opacity flattening's effects on
   * |nsDisplayBackgroundColor| items are one example.
   */
  virtual void InvalidateItemCacheEntry() {}

  struct HitTestState {
    explicit HitTestState() = default;

    ~HitTestState() {
      NS_ASSERTION(mItemBuffer.Length() == 0,
                   "mItemBuffer should have been cleared");
    }

    // Handling transform items for preserve 3D frames.
    bool mInPreserves3D = false;
    // When hit-testing for visibility, we may hit an fully opaque item in a
    // nested display list. We want to stop at that point, without looking
    // further on other items.
    bool mHitOccludingItem = false;

    float mCurrentOpacity = 1.0f;

    AutoTArray<nsDisplayItem*, 100> mItemBuffer;
  };

  uint8_t GetFlags() const { return GetDisplayItemFlagsForType(GetType()); }

  virtual bool IsContentful() const { return GetFlags() & TYPE_IS_CONTENTFUL; }

  /**
   * This is called after we've constructed a display list for event handling.
   * When this is called, we've already ensured that aRect intersects the
   * item's bounds and that clipping has been taking into account.
   *
   * @param aRect the point or rect being tested, relative to the reference
   * frame. If the width and height are both 1 app unit, it indicates we're
   * hit testing a point, not a rect.
   * @param aState must point to a HitTestState. If you don't have one,
   * just create one with the default constructor and pass it in.
   * @param aOutFrames each item appends the frame(s) in this display item that
   * the rect is considered over (if any) to aOutFrames.
   */
  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                       HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) {}

  virtual nsIFrame* StyleFrame() const { return mFrame; }

  /**
   * Compute the used z-index of our frame; returns zero for elements to which
   * z-index does not apply, and for z-index:auto.
   * @note This can be overridden, @see nsDisplayWrapList::SetOverrideZIndex.
   */
  virtual int32_t ZIndex() const;
  /**
   * The default bounds is the frame border rect.
   * @param aSnap *aSnap is set to true if the returned rect will be
   * snapped to nearest device pixel edges during actual drawing.
   * It might be set to false and snap anyway, so code computing the set of
   * pixels affected by this display item needs to round outwards to pixel
   * boundaries when *aSnap is set to false.
   * This does not take the item's clipping into account.
   * @return a rectangle relative to aBuilder->ReferenceFrame() that
   * contains the area drawn by this display item
   */
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const {
    *aSnap = false;
    return nsRect(ToReferenceFrame(), Frame()->GetSize());
  }

  /**
   * Returns the untransformed bounds of this display item.
   */
  virtual nsRect GetUntransformedBounds(nsDisplayListBuilder* aBuilder,
                                        bool* aSnap) const {
    return GetBounds(aBuilder, aSnap);
  }

  virtual nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder,
                                  bool* aSnap) const {
    *aSnap = false;
    return nsRegion();
  }

  /**
   * Returns true if nothing will be rendered inside aRect, false if uncertain.
   * aRect is assumed to be contained in this item's bounds.
   */
  virtual bool IsInvisibleInRect(const nsRect& aRect) const { return false; }

  /**
   * Returns the result of GetBounds intersected with the item's clip.
   * The intersection is approximate since rounded corners are not taking into
   * account.
   */
  nsRect GetClippedBounds(nsDisplayListBuilder* aBuilder) const;

  nsRect GetBorderRect() const {
    return nsRect(ToReferenceFrame(), Frame()->GetSize());
  }

  nsRect GetPaddingRect() const {
    return Frame()->GetPaddingRectRelativeToSelf() + ToReferenceFrame();
  }

  nsRect GetContentRect() const {
    return Frame()->GetContentRectRelativeToSelf() + ToReferenceFrame();
  }

  /**
   * Checks if the frame(s) owning this display item have been marked as
   * invalid, and needing repainting.
   */
  virtual bool IsInvalid(nsRect& aRect) const {
    bool result = mFrame ? mFrame->IsInvalid(aRect) : false;
    aRect += ToReferenceFrame();
    return result;
  }

  /**
   * Creates and initializes an nsDisplayItemGeometry object that retains the
   * current areas covered by this display item. These need to retain enough
   * information such that they can be compared against a future nsDisplayItem
   * of the same type, and determine if repainting needs to happen.
   *
   * Subclasses wishing to store more information need to override both this
   * and ComputeInvalidationRegion, as well as implementing an
   * nsDisplayItemGeometry subclass.
   *
   * The default implementation tracks both the display item bounds, and the
   * frame's border rect.
   */
  virtual nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) {
    return new nsDisplayItemGenericGeometry(this, aBuilder);
  }

  /**
   * Compares an nsDisplayItemGeometry object from a previous paint against the
   * current item. Computes if the geometry of the item has changed, and the
   * invalidation area required for correct repainting.
   *
   * The existing geometry will have been created from a display item with a
   * matching GetPerFrameKey()/mFrame pair to the current item.
   *
   * The default implementation compares the display item bounds, and the
   * frame's border rect, and invalidates the entire bounds if either rect
   * changes.
   *
   * @param aGeometry The geometry of the matching display item from the
   * previous paint.
   * @param aInvalidRegion Output param, the region to invalidate, or
   * unchanged if none.
   */
  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) const {
    const nsDisplayItemGenericGeometry* geometry =
        static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
    bool snap;
    if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
        !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
      aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
    }
  }

  /**
   * An alternative default implementation of ComputeInvalidationRegion,
   * that instead invalidates only the changed area between the two items.
   */
  void ComputeInvalidationRegionDifference(
      nsDisplayListBuilder* aBuilder,
      const nsDisplayItemBoundsGeometry* aGeometry,
      nsRegion* aInvalidRegion) const {
    bool snap;
    nsRect bounds = GetBounds(aBuilder, &snap);

    if (!aGeometry->mBounds.IsEqualInterior(bounds)) {
      nscoord radii[8];
      if (aGeometry->mHasRoundedCorners || Frame()->GetBorderRadii(radii)) {
        aInvalidRegion->Or(aGeometry->mBounds, bounds);
      } else {
        aInvalidRegion->Xor(aGeometry->mBounds, bounds);
      }
    }
  }

  /**
   * This function is called when an item's list of children has been modified
   * by RetainedDisplayListBuilder.
   */
  virtual void InvalidateCachedChildInfo(nsDisplayListBuilder* aBuilder) {}

  virtual void AddSizeOfExcludingThis(nsWindowSizes&) const {}

  /**
   * @param aSnap set to true if the edges of the rectangles of the opaque
   * region would be snapped to device pixels when drawing
   * @return a region of the item that is opaque --- that is, every pixel
   * that is visible is painted with an opaque
   * color. This is useful for determining when one piece
   * of content completely obscures another so that we can do occlusion
   * culling.
   * This does not take clipping into account.
   * This must return a simple region (1 rect) for painting display lists.
   * It is only allowed to be a complex region for hit testing.
   */
  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                   bool* aSnap) const {
    *aSnap = false;
    return nsRegion();
  }
  /**
   * @return Some(nscolor) if the item is guaranteed to paint every pixel in its
   * bounds with the same (possibly translucent) color
   */
  virtual Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const {
    return Nothing();
  }

  /**
   * @return true if the contents of this item are rendered fixed relative
   * to the nearest viewport.
   */
  virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) const {
    return false;
  }

  /**
   * Returns true if all layers that can be active should be forced to be
   * active. Requires setting the pref layers.force-active=true.
   */
  static bool ForceActiveLayers();

#ifdef MOZ_DUMP_PAINTING
  /**
   * Mark this display item as being painted via
   * FrameLayerBuilder::DrawPaintedLayer.
   */
  bool Painted() const { return mItemFlags.contains(ItemFlag::Painted); }

  /**
   * Check if this display item has been painted.
   */
  void SetPainted() { mItemFlags += ItemFlag::Painted; }
#endif

  /**
   * Function to create the WebRenderCommands.
   * We should check if the layer state is
   * active first and have an early return if the layer state is
   * not active.
   *
   * @return true if successfully creating webrender commands.
   */
  virtual bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) {
    return false;
  }

  /**
   * Updates the provided aLayerData with any APZ-relevant scroll data
   * that is specific to this display item. This is stuff that would normally
   * be put on the layer during BuildLayer, but this is only called in
   * layers-free webrender mode, where we don't have layers.
   *
   * This function returns true if and only if it has APZ-relevant scroll data
   * to provide. Note that the arguments passed in may be nullptr, in which case
   * the function should still return true if and only if it has APZ-relevant
   * scroll data, but obviously in this case it can't actually put the
   * data onto aLayerData, because there isn't one.
   *
   * This function assumes that aData and aLayerData will either both be null,
   * or will both be non-null. The caller is responsible for enforcing this.
   */
  virtual bool UpdateScrollData(layers::WebRenderScrollData* aData,
                                layers::WebRenderLayerScrollData* aLayerData) {
    return false;
  }

  /**
   * Returns true if this item needs to have its geometry updated, despite
   * returning empty invalidation region.
   */
  virtual bool NeedsGeometryUpdates() const { return false; }

  /**
   * Some items such as those calling into the native themed widget machinery
   * have to be painted on the content process. In this case it is best to avoid
   * allocating layers that serializes and forwards the work to the compositor.
   */
  virtual bool MustPaintOnContentSide() const { return false; }

  /**
   * If this has a child list where the children are in the same coordinate
   * system as this item (i.e., they have the same reference frame),
   * return the list.
   */
  virtual RetainedDisplayList* GetSameCoordinateSystemChildren() const {
    return nullptr;
  }

  virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) {}
  /**
   * Do UpdateBounds() for items with frames establishing or extending
   * 3D rendering context.
   *
   * This function is called by UpdateBoundsFor3D() of
   * nsDisplayTransform(), and it is called by
   * BuildDisplayListForStackingContext() on transform items
   * establishing 3D rendering context.
   *
   * The bounds of a transform item with the frame establishing 3D
   * rendering context should be computed by calling
   * DoUpdateBoundsPreserves3D() on all descendants that participate
   * the same 3d rendering context.
   */
  virtual void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) {}

  /**
   * Returns the building rectangle used by nsDisplayListBuilder when
   * this item was constructed.
   */
  const nsRect& GetBuildingRect() const { return mBuildingRect; }

  void SetBuildingRect(const nsRect& aBuildingRect) {
    mBuildingRect = aBuildingRect;
  }

  /**
   * Returns the building rect for the children, relative to their
   * reference frame. Can be different from mBuildingRect for
   * nsDisplayTransform, since the reference frame for the children is different
   * from the reference frame for the item itself.
   */
  virtual const nsRect& GetBuildingRectForChildren() const {
    return mBuildingRect;
  }

  virtual void WriteDebugInfo(std::stringstream& aStream) {}

  /**
   * Returns the result of aBuilder->ToReferenceFrame(GetUnderlyingFrame())
   */
  const nsPoint& ToReferenceFrame() const {
    NS_ASSERTION(mFrame, "No frame?");
    return mToReferenceFrame;
  }

  /**
   * Returns the reference frame for display item children of this item.
   */
  virtual const nsIFrame* ReferenceFrameForChildren() const { return nullptr; }

  /**
   * Checks if this display item (or any children) contains content that might
   * be rendered with component alpha (e.g. subpixel antialiasing). Returns the
   * bounds of the area that needs component alpha, or an empty rect if nothing
   * in the item does.
   */
  virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const {
    return nsRect();
  }

  /**
   * Check if we can add async animations to the layer for this display item.
   */
  virtual bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
    return false;
  }

  virtual bool SupportsOptimizingToImage() const { return false; }

  virtual const DisplayItemClip& GetClip() const;
  void IntersectClip(nsDisplayListBuilder* aBuilder,
                     const DisplayItemClipChain* aOther, bool aStore);

  virtual void SetActiveScrolledRoot(
      const ActiveScrolledRoot* aActiveScrolledRoot) {
    mActiveScrolledRoot = aActiveScrolledRoot;
  }
  const ActiveScrolledRoot* GetActiveScrolledRoot() const {
    return mActiveScrolledRoot;
  }

  virtual void SetClipChain(const DisplayItemClipChain* aClipChain,
                            bool aStore);
  const DisplayItemClipChain* GetClipChain() const { return mClipChain; }

  bool BackfaceIsHidden() const {
    return mItemFlags.contains(ItemFlag::BackfaceHidden);
  }

  bool Combines3DTransformWithAncestors() const {
    return mItemFlags.contains(ItemFlag::Combines3DTransformWithAncestors);
  }

  bool ForceNotVisible() const {
    return mItemFlags.contains(ItemFlag::ForceNotVisible);
  }

  bool In3DContextAndBackfaceIsHidden() const {
    return mItemFlags.contains(ItemFlag::BackfaceHidden) &&
           mItemFlags.contains(ItemFlag::Combines3DTransformWithAncestors);
  }

  bool HasDifferentFrame(const nsDisplayItem* aOther) const {
    return mFrame != aOther->mFrame;
  }

  bool HasHitTestInfo() const {
    return mItemFlags.contains(ItemFlag::HasHitTestInfo);
  }

  bool HasSameTypeAndClip(const nsDisplayItem* aOther) const {
    return GetPerFrameKey() == aOther->GetPerFrameKey() &&
           GetClipChain() == aOther->GetClipChain();
  }

  bool HasSameContent(const nsDisplayItem* aOther) const {
    return mFrame->GetContent() == aOther->Frame()->GetContent();
  }

  virtual void NotifyUsed(nsDisplayListBuilder* aBuilder) {}

  virtual Maybe<nsRect> GetClipWithRespectToASR(
      nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const;

  virtual const nsRect& GetUntransformedPaintRect() const {
    return GetBuildingRect();
  }

  nsRect GetPaintRect(nsDisplayListBuilder* aBuilder, gfxContext* aCtx);

  virtual const HitTestInfo& GetHitTestInfo() { return HitTestInfo::Empty(); }

  enum class ReuseState : uint8_t {
    None,
    // Set during display list building.
    Reusable,
    // Set during display list preprocessing.
    PreProcessed,
    // Set during partial display list build.
    Reused,
  };

  void SetReusable() {
    MOZ_ASSERT(mReuseState == ReuseState::None ||
               mReuseState == ReuseState::Reused);
    mReuseState = ReuseState::Reusable;
  }

  bool IsReusable() const { return mReuseState == ReuseState::Reusable; }

  void SetPreProcessed() {
    MOZ_ASSERT(mReuseState == ReuseState::Reusable);
    mReuseState = ReuseState::PreProcessed;
  }

  bool IsPreProcessed() const {
    return mReuseState == ReuseState::PreProcessed;
  }

  void SetReusedItem() {
    MOZ_ASSERT(mReuseState == ReuseState::PreProcessed);
    mReuseState = ReuseState::Reused;
  }

  bool IsReusedItem() const { return mReuseState == ReuseState::Reused; }

  void ResetReuseState() { mReuseState = ReuseState::None; }

  ReuseState GetReuseState() const { return mReuseState; }

  nsIFrame* mFrame;  // 8

 private:
  enum class ItemFlag : uint16_t {
    CantBeReused,
    CantBeCached,
    DeletedFrame,
    ModifiedFrame,
    ReusedItem,
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    MergedItem,
    PreProcessedItem,
#endif
    BackfaceHidden,
    Combines3DTransformWithAncestors,
    ForceNotVisible,
    HasHitTestInfo,
#ifdef MOZ_DUMP_PAINTING
    // True if this frame has been painted.
    Painted,
#endif
  };

  EnumSet<ItemFlag, uint16_t> mItemFlags;              // 2
  DisplayItemType mType = DisplayItemType::TYPE_ZERO;  // 1
  uint8_t mExtraPageForPageNum = 0;                    // 1
  uint16_t mPerFrameIndex = 0;                         // 2
  ReuseState mReuseState = ReuseState::None;
  OldListIndex mOldListIndex;  // 4
  uintptr_t mOldList = 0;      // 8

  // This is the rectangle that nsDisplayListBuilder was using as the visible
  // rect to decide which items to construct.
  nsRect mBuildingRect;

 protected:
  void SetItemFlag(ItemFlag aFlag, const bool aValue) {
    if (aValue) {
      mItemFlags += aFlag;
    } else {
      mItemFlags -= aFlag;
    }
  }

  void SetHasHitTestInfo() { mItemFlags += ItemFlag::HasHitTestInfo; }

  // Result of ToReferenceFrame(mFrame), if mFrame is non-null
  nsPoint mToReferenceFrame;

  RefPtr<const ActiveScrolledRoot> mActiveScrolledRoot;
  RefPtr<const DisplayItemClipChain> mClipChain;

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 public:
  bool IsMergedItem() const {
    return mItemFlags.contains(ItemFlag::MergedItem);
  }

  bool IsPreProcessedItem() const {
    return mItemFlags.contains(ItemFlag::PreProcessedItem);
  }

  void SetMergedPreProcessed(bool aMerged, bool aPreProcessed) {
    SetItemFlag(ItemFlag::MergedItem, aMerged);
    SetItemFlag(ItemFlag::PreProcessedItem, aPreProcessed);
  }

  uint32_t mOldListKey = 0;
  uint32_t mOldNestingDepth = 0;
#endif
};

class nsPaintedDisplayItem : public nsDisplayItem {
 public:
  nsPaintedDisplayItem* AsPaintedDisplayItem() final { return this; }
  const nsPaintedDisplayItem* AsPaintedDisplayItem() const final {
    return this;
  }

  /**
   * Returns true if this display item would return true from ApplyOpacity
   * without actually applying the opacity. Otherwise returns false.
   */
  virtual bool CanApplyOpacity(WebRenderLayerManager* aManager,
                               nsDisplayListBuilder* aBuilder) const {
    return false;
  }

  /**
   * Returns true if this item supports PaintWithClip, where the clipping
   * is used directly as the primitive geometry instead of needing an explicit
   * clip.
   */
  virtual bool CanPaintWithClip(const DisplayItemClip& aClip) { return false; }

  /**
   * Same as |Paint()|, except provides a clip to use the geometry to draw with.
   * Must not be called unless |CanPaintWithClip()| returned true.
   */
  virtual void PaintWithClip(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
                             const DisplayItemClip& aClip) {
    MOZ_ASSERT_UNREACHABLE("PaintWithClip() is not implemented!");
  }

  /**
   * Paint this item to some rendering context.
   */
  virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) = 0;

  /**
   * External storage used by |DisplayItemCache| to avoid hashmap lookups.
   * If an item is reused and has the cache index set, it means that
   * |DisplayItemCache| has assigned a cache slot for the item.
   */
  Maybe<uint16_t>& CacheIndex() { return mCacheIndex; }

  void InvalidateItemCacheEntry() override {
    // |nsPaintedDisplayItem|s may have |DisplayItemCache| entries
    // that no longer match after a mutation. The cache will notice
    // on its own that the entry is no longer in use, and free it.
    mCacheIndex = Nothing();
  }

  const HitTestInfo& GetHitTestInfo() final { return mHitTestInfo; }
  void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder) {
    mHitTestInfo.Initialize(aBuilder, Frame());
    SetHasHitTestInfo();
  }

 protected:
  nsPaintedDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
      : nsPaintedDisplayItem(aBuilder, aFrame,
                             aBuilder->CurrentActiveScrolledRoot()) {}

  nsPaintedDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       const ActiveScrolledRoot* aActiveScrolledRoot)
      : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot) {}

  nsPaintedDisplayItem(nsDisplayListBuilder* aBuilder,
                       const nsPaintedDisplayItem& aOther)
      : nsDisplayItem(aBuilder, aOther), mHitTestInfo(aOther.mHitTestInfo) {}

 protected:
  HitTestInfo mHitTestInfo;
  Maybe<uint16_t> mCacheIndex;
};

template <typename T>
struct MOZ_HEAP_CLASS LinkedListNode {
  explicit LinkedListNode(T aValue) : mNext(nullptr), mValue(aValue) {}
  LinkedListNode* mNext;
  T mValue;
};

template <typename T>
struct LinkedListIterator {
  using iterator_category = std::forward_iterator_tag;
  using difference_type = std::ptrdiff_t;
  using value_type = T;
  using pointer = T*;
  using reference = T&;
  using Node = LinkedListNode<T>;

  explicit LinkedListIterator(Node* aNode = nullptr) : mNode(aNode) {}

  bool HasNext() const { return mNode != nullptr; }

  LinkedListIterator<T>& operator++() {
    MOZ_ASSERT(mNode);
    mNode = mNode->mNext;
    return *this;
  }

  bool operator==(const LinkedListIterator<T>& aOther) const {
    return mNode == aOther.mNode;
  }

  bool operator!=(const LinkedListIterator<T>& aOther) const {
    return mNode != aOther.mNode;
  }

  const T operator*() const {
    MOZ_ASSERT(mNode);
    return mNode->mValue;
  }

  T operator*() {
    MOZ_ASSERT(mNode);
    return mNode->mValue;
  }

  Node* mNode;
};

/**
 * Manages a singly-linked list of display list items.
 *
 * Stepping upward through this list is very fast. Stepping downward is very
 * slow so we don't support it. The methods that need to step downward
 * (HitTest()) internally build a temporary array of all
 * the items while they do the downward traversal, so overall they're still
 * linear time. We have optimized for efficient AppendToTop() of both
 * items and lists, with minimal codesize.
 *
 * Internal linked list nodes are allocated using arena allocator.
 * */
class nsDisplayList {
 public:
  using Node = LinkedListNode<nsDisplayItem*>;
  using iterator = LinkedListIterator<nsDisplayItem*>;
  using const_iterator = iterator;

  iterator begin() { return iterator(mBottom); }
  iterator end() { return iterator(nullptr); }
  const_iterator begin() const { return iterator(mBottom); }
  const_iterator end() const { return iterator(nullptr); }

  explicit nsDisplayList(nsDisplayListBuilder* aBuilder) : mBuilder(aBuilder) {}

  nsDisplayList() = delete;
  nsDisplayList(const nsDisplayList&) = delete;
  nsDisplayList& operator=(const nsDisplayList&) = delete;

  virtual ~nsDisplayList() {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    if (!mAllowNonEmptyDestruction) {
      MOZ_RELEASE_ASSERT(IsEmpty(), "Nonempty list left over?");
    }
#endif

    DeallocateNodes();
  }

  nsDisplayList(nsDisplayList&& aOther)
      : mBottom(aOther.mBottom),
        mTop(aOther.mTop),
        mLength(aOther.mLength),
        mBuilder(aOther.mBuilder) {
    aOther.SetEmpty();
  }

  nsDisplayList& operator=(nsDisplayList&& aOther) {
    MOZ_RELEASE_ASSERT(mBuilder == aOther.mBuilder);

    if (this != &aOther) {
      MOZ_RELEASE_ASSERT(IsEmpty());
      mBottom = std::move(aOther.mBottom);
      mTop = std::move(aOther.mTop);
      mLength = std::move(aOther.mLength);
      aOther.SetEmpty();
    }
    return *this;
  }

  /**
   * Append an item to the top of the list.
   **/
  void AppendToTop(nsDisplayItem* aItem) {
    if (!aItem) {
      return;
    }

    auto* next = Allocate(aItem);
    MOZ_ASSERT(next);

    if (IsEmpty()) {
      mBottom = next;
      mTop = next;
    } else {
      mTop->mNext = next;
      mTop = next;
    }

    mLength++;

    MOZ_ASSERT(mBottom && mTop);
    MOZ_ASSERT(mTop->mNext == nullptr);
  }

  template <typename T, typename F, typename... Args>
  void AppendNewToTop(nsDisplayListBuilder* aBuilder, F* aFrame,
                      Args&&... aArgs) {
    AppendNewToTopWithIndex<T>(aBuilder, aFrame, 0,
                               std::forward<Args>(aArgs)...);
  }

  template <typename T, typename F, typename... Args>
  void AppendNewToTopWithIndex(nsDisplayListBuilder* aBuilder, F* aFrame,
                               const uint16_t aIndex, Args&&... aArgs) {
    nsDisplayItem* item = MakeDisplayItemWithIndex<T>(
        aBuilder, aFrame, aIndex, std::forward<Args>(aArgs)...);
    AppendToTop(item);
  }

  /**
   * Removes all items from aList and appends them to the top of this list.
   */
  void AppendToTop(nsDisplayList* aList) {
    MOZ_ASSERT(aList != this);
    MOZ_RELEASE_ASSERT(mBuilder == aList->mBuilder);

    if (aList->IsEmpty()) {
      return;
    }

    if (IsEmpty()) {
      std::swap(mBottom, aList->mBottom);
      std::swap(mTop, aList->mTop);
      std::swap(mLength, aList->mLength);
    } else {
      MOZ_ASSERT(mTop && mTop->mNext == nullptr);
      mTop->mNext = aList->mBottom;
      mTop = aList->mTop;
      mLength += aList->mLength;

      aList->SetEmpty();
    }
  }

  /**
   * Clears the display list.
   */
  void Clear() {
    DeallocateNodes();
    SetEmpty();
  }

  /**
   * Creates a shallow copy of this display list to |aDestination|.
   */
  void CopyTo(nsDisplayList* aDestination) const {
    for (auto* item : *this) {
      aDestination->AppendToTop(item);
    }
  }

  /**
   * Calls the function |aFn| for each display item in the display list.
   */
  void ForEach(const std::function<void(nsDisplayItem*)>& aFn) {
    for (auto* item : *this) {
      aFn(item);
    }
  }
  /**
   * Remove all items from the list and call their destructors.
   */
  virtual void DeleteAll(nsDisplayListBuilder* aBuilder);

  /**
   * @return the item at the bottom of the list, or null if the list is empty
   */
  nsDisplayItem* GetBottom() const {
    return mBottom ? mBottom->mValue : nullptr;
  }

  /**
   * @return the item at the top of the list, or null if the list is empty
   */
  nsDisplayItem* GetTop() const { return mTop ? mTop->mValue : nullptr; }

  bool IsEmpty() const { return mBottom == nullptr; }

  /**
   * @return the number of items in the list
   */
  size_t Length() const { return mLength; }

  /**
   * Stable sort the list by the z-order of Frame() on
   * each item. 'auto' is counted as zero.
   * It is assumed that the list is already in content document order.
   */
  void SortByZOrder();

  /**
   * Stable sort the list by the tree order of the content of
   * Frame() on each item. z-index is ignored.
   * @param aCommonAncestor a common ancestor of all the content elements
   * associated with the display items, for speeding up tree order
   * checks, or nullptr if not known; it's only a hint, if it is not an
   * ancestor of some elements, then we lose performance but not correctness
   */
  void SortByContentOrder(nsIContent* aCommonAncestor);

  /**
   * Sort the display list using a stable sort.
   * aComparator(Item item1, Item item2) should return true if item1 should go
   * before item2.
   * We sort the items into increasing order.
   */
  template <typename Item, typename Comparator>
  void Sort(const Comparator& aComparator) {
    if (Length() < 2) {
      // Only sort lists with more than one item.
      return;
    }

    // Some casual local browsing testing suggests that a local preallocated
    // array of 20 items should be able to avoid a lot of dynamic allocations
    // here.
    AutoTArray<Item, 20> items;

    for (nsDisplayItem* item : TakeItems()) {
      items.AppendElement(Item(item));
    }

    std::stable_sort(items.begin(), items.end(), aComparator);

    for (Item& item : items) {
      AppendToTop(item);
    }
  }

  nsDisplayList TakeItems() {
    nsDisplayList list = std::move(*this);
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    list.mAllowNonEmptyDestruction = true;
#endif
    return list;
  }

  nsDisplayItem* RemoveBottom() {
    if (!mBottom) {
      return nullptr;
    }

    nsDisplayItem* bottom = mBottom->mValue;

    auto next = mBottom->mNext;
    Deallocate(mBottom);
    mBottom = next;

    if (!mBottom) {
      // No bottom item means no items at all.
      mTop = nullptr;
    }

    MOZ_ASSERT(mLength > 0);
    mLength--;

    return bottom;
  }

  /**
   * Paint the list to the rendering context. We assume that (0,0) in aCtx
   * corresponds to the origin of the reference frame. For best results,
   * aCtx's current transform should make (0,0) pixel-aligned. The
   * rectangle in aDirtyRect is painted, which *must* be contained in the
   * dirty rect used to construct the display list.
   *
   * If aFlags contains PAINT_USE_WIDGET_LAYERS and
   * ShouldUseWidgetLayerManager() is set, then we will paint using
   * the reference frame's widget's layer manager (and ctx may be null),
   * otherwise we will use a temporary BasicLayerManager and ctx must
   * not be null.
   *
   * If PAINT_EXISTING_TRANSACTION is set, the reference frame's widget's
   * layer manager has already had BeginTransaction() called on it and
   * we should not call it again.
   *
   * This must only be called on the root display list of the display list
   * tree.
   *
   * We return the layer manager used for painting --- mainly so that
   * callers can dump its layer tree if necessary.
   */
  enum {
    PAINT_DEFAULT = 0,
    PAINT_USE_WIDGET_LAYERS = 0x01,
    PAINT_EXISTING_TRANSACTION = 0x04,
    PAINT_IDENTICAL_DISPLAY_LIST = 0x08
  };
  void PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
                 uint32_t aFlags, Maybe<double> aDisplayListBuildTime);

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
             int32_t aAppUnitsPerDevPixel);

  /**
   * Get the bounds. Takes the union of the bounds of all children.
   * The result is not cached.
   */
  nsRect GetClippedBounds(nsDisplayListBuilder* aBuilder) const;

  /**
   * Get this list's bounds, respecting clips relative to aASR. The result is
   * the union of each item's clipped bounds with respect to aASR. That means
   * that if an item can move asynchronously with an ASR that is a descendant
   * of aASR, then the clipped bounds with respect to aASR will be the clip of
   * that item for aASR, because the item can move anywhere inside that clip.
   * If there is an item in this list which is not bounded with respect to
   * aASR (i.e. which does not have "finite bounds" with respect to aASR),
   * then this method trigger an assertion failure.
   * The optional aBuildingRect out argument can be set to non-null if the
   * caller is also interested to know the building rect.  This can be used
   * to get the visible rect efficiently without traversing the display list
   * twice.
   */
  nsRect GetClippedBoundsWithRespectToASR(
      nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
      nsRect* aBuildingRect = nullptr) const;

  /**
   * Returns the opaque region of this display list.
   */
  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder) {
    nsRegion result;
    bool snap;
    for (nsDisplayItem* item : *this) {
      result.OrWith(item->GetOpaqueRegion(aBuilder, &snap));
    }
    return result;
  }

  /**
   * Returns the bounds of the area that needs component alpha.
   */
  nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const {
    nsRect bounds;
    for (nsDisplayItem* item : *this) {
      bounds.UnionRect(bounds, item->GetComponentAlphaBounds(aBuilder));
    }
    return bounds;
  }

  /**
   * Find the topmost display item that returns a non-null frame, and return
   * the frame.
   */
  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               nsDisplayItem::HitTestState* aState,
               nsTArray<nsIFrame*>* aOutFrames) const;
  /**
   * Compute the union of the visible rects of the items in the list. The
   * result is not cached.
   */
  nsRect GetBuildingRect() const;

 private:
  inline Node* Allocate(nsDisplayItem* aItem) {
    void* ptr =
        mBuilder->Allocate(sizeof(Node), DisplayListArenaObjectId::LISTNODE);
    return new (ptr) Node(aItem);
  }

  inline void Deallocate(Node* aNode) {
    aNode->~Node();
    mBuilder->Destroy(DisplayListArenaObjectId::LISTNODE, aNode);
  }

  void DeallocateNodes() {
    Node* current = mBottom;
    Node* next = nullptr;

    while (current) {
      next = current->mNext;
      Deallocate(current);
      current = next;
    }
  }

  inline void SetEmpty() {
    mBottom = nullptr;
    mTop = nullptr;
    mLength = 0;
  }

  Node* mBottom = nullptr;
  Node* mTop = nullptr;
  size_t mLength = 0;
  nsDisplayListBuilder* mBuilder = nullptr;

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  // This checks that the invariant of display lists owning their items is held.
  bool mAllowNonEmptyDestruction = false;
#endif
};

/**
 * This is passed as a parameter to nsIFrame::BuildDisplayList. That method
 * will put any generated items onto the appropriate list given here. It's
 * basically just a collection with one list for each separate stacking layer.
 * The lists themselves are external to this object and thus can be shared
 * with others. Some of the list pointers may even refer to the same list.
 */
class nsDisplayListSet {
 public:
  /**
   * @return a list where one should place the border and/or background for
   * this frame (everything from steps 1 and 2 of CSS 2.1 appendix E)
   */
  nsDisplayList* BorderBackground() const { return mLists[0]; }
  /**
   * @return a list where one should place the borders and/or backgrounds for
   * block-level in-flow descendants (step 4 of CSS 2.1 appendix E)
   */
  nsDisplayList* BlockBorderBackgrounds() const { return mLists[1]; }
  /**
   * @return a list where one should place descendant floats (step 5 of
   * CSS 2.1 appendix E)
   */
  nsDisplayList* Floats() const { return mLists[2]; }
  /**
   * @return a list where one should place the (pseudo) stacking contexts
   * for descendants of this frame (everything from steps 3, 7 and 8
   * of CSS 2.1 appendix E)
   */
  nsDisplayList* PositionedDescendants() const { return mLists[3]; }
  /**
   * @return a list where one should place the outlines
   * for this frame and its descendants (step 9 of CSS 2.1 appendix E)
   */
  nsDisplayList* Outlines() const { return mLists[4]; }
  /**
   * @return a list where one should place all other content
   */
  nsDisplayList* Content() const { return mLists[5]; }

  const std::array<nsDisplayList*, 6>& Lists() const { return mLists; }

  /**
   * Clears all the display lists in the set.
   */
  void Clear() {
    for (auto* list : mLists) {
      MOZ_ASSERT(list);
      list->Clear();
    }
  }

  /**
   * Deletes all the display items in the set.
   */
  void DeleteAll(nsDisplayListBuilder* aBuilder) {
    for (auto* list : mLists) {
      list->DeleteAll(aBuilder);
    }
  }

  nsDisplayListSet(nsDisplayList* aBorderBackground,
                   nsDisplayList* aBlockBorderBackgrounds,
                   nsDisplayList* aFloats, nsDisplayList* aContent,
                   nsDisplayList* aPositionedDescendants,
                   nsDisplayList* aOutlines)
      : mLists{aBorderBackground, aBlockBorderBackgrounds, aFloats,
               aContent,          aPositionedDescendants,  aOutlines} {}

  /**
   * A copy constructor that lets the caller override the BorderBackground
   * list.
   */
  nsDisplayListSet(const nsDisplayListSet& aLists,
                   nsDisplayList* aBorderBackground)
      : mLists(aLists.mLists) {
    mLists[0] = aBorderBackground;
  }

  /**
   * Returns true if all the display lists in the display list set are empty.
   */
  bool IsEmpty() const {
    for (auto* list : mLists) {
      if (!list->IsEmpty()) {
        return false;
      }
    }

    return true;
  }

  /**
   * Calls the function |aFn| for each display item in the display list set.
   */
  void ForEach(const std::function<void(nsDisplayItem*)>& aFn) const {
    for (auto* list : mLists) {
      list->ForEach(aFn);
    }
  }

  /**
   * Creates a shallow copy of this display list set to |aDestination|.
   */
  void CopyTo(const nsDisplayListSet& aDestination) const;

  /**
   * Move all display items in our lists to top of the corresponding lists in
   * the destination.
   */
  void MoveTo(const nsDisplayListSet& aDestination) const;

 private:
  // This class is only used on stack, so we don't have to worry about leaking
  // it.  Don't let us be heap-allocated!
  void* operator new(size_t sz) noexcept(true);

  std::array<nsDisplayList*, 6> mLists;
};

/**
 * A specialization of nsDisplayListSet where the lists are actually internal
 * to the object, and all distinct.
 */
struct nsDisplayListCollection : public nsDisplayListSet {
  explicit nsDisplayListCollection(nsDisplayListBuilder* aBuilder)
      : nsDisplayListSet(&mLists[0], &mLists[1], &mLists[2], &mLists[3],
                         &mLists[4], &mLists[5]),
        mLists{nsDisplayList{aBuilder}, nsDisplayList{aBuilder},
               nsDisplayList{aBuilder}, nsDisplayList{aBuilder},
               nsDisplayList{aBuilder}, nsDisplayList{aBuilder}} {}

  /**
   * Sort all lists by content order.
   */
  void SortAllByContentOrder(nsIContent* aCommonAncestor) {
    for (auto& mList : mLists) {
      mList.SortByContentOrder(aCommonAncestor);
    }
  }

  /**
   * Serialize this display list collection into a display list with the items
   * in the correct Z order.
   * @param aOutList the result display list
   * @param aContent the content element to use for content ordering
   */
  void SerializeWithCorrectZOrder(nsDisplayList* aOutResultList,
                                  nsIContent* aContent);

 private:
  // This class is only used on stack, so we don't have to worry about leaking
  // it.  Don't let us be heap-allocated!
  void* operator new(size_t sz) noexcept(true);

  nsDisplayList mLists[6];
};

/**
 * A display list that also retains the partial build
 * information (in the form of a DAG) used to create it.
 *
 * Display lists built from a partial list aren't necessarily
 * in the same order as a full build, and the DAG retains
 * the information needing to interpret the current
 * order correctly.
 */
class RetainedDisplayList : public nsDisplayList {
 public:
  explicit RetainedDisplayList(nsDisplayListBuilder* aBuilder)
      : nsDisplayList(aBuilder) {}

  RetainedDisplayList(RetainedDisplayList&& aOther)
      : nsDisplayList(std::move(aOther)), mDAG(std::move(aOther.mDAG)) {}

  RetainedDisplayList(const RetainedDisplayList&) = delete;
  RetainedDisplayList& operator=(const RetainedDisplayList&) = delete;

  ~RetainedDisplayList() override {
    MOZ_ASSERT(mOldItems.IsEmpty(), "Must empty list before destroying");
  }

  RetainedDisplayList& operator=(RetainedDisplayList&& aOther) {
    MOZ_ASSERT(IsEmpty(), "Can only move into an empty list!");
    MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");

    nsDisplayList::operator=(std::move(aOther));
    mDAG = std::move(aOther.mDAG);
    mOldItems = std::move(aOther.mOldItems);
    return *this;
  }

  RetainedDisplayList& operator=(nsDisplayList&& aOther) {
    MOZ_ASSERT(IsEmpty(), "Can only move into an empty list!");
    MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");
    nsDisplayList::operator=(std::move(aOther));
    return *this;
  }

  void DeleteAll(nsDisplayListBuilder* aBuilder) override {
    for (OldItemInfo& i : mOldItems) {
      if (i.mItem && i.mOwnsItem) {
        i.mItem->Destroy(aBuilder);
        MOZ_ASSERT(!GetBottom(),
                   "mOldItems should not be owning items if we also have items "
                   "in the normal list");
      }
    }
    mOldItems.Clear();
    mDAG.Clear();
    nsDisplayList::DeleteAll(aBuilder);
  }

  void AddSizeOfExcludingThis(nsWindowSizes&) const;

  DirectedAcyclicGraph<MergedListUnits> mDAG;

  // Temporary state initialized during the preprocess pass
  // of RetainedDisplayListBuilder and then used during merging.
  nsTArray<OldItemInfo> mOldItems;
};

class nsDisplayContainer final : public nsDisplayItem {
 public:
  nsDisplayContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     const ActiveScrolledRoot* aActiveScrolledRoot,
                     nsDisplayList* aList);

  ~nsDisplayContainer() override { MOZ_COUNT_DTOR(nsDisplayContainer); }

  NS_DISPLAY_DECL_NAME("nsDisplayContainer", TYPE_CONTAINER)

  void Destroy(nsDisplayListBuilder* aBuilder) override {
    mChildren.DeleteAll(aBuilder);
    nsDisplayItem::Destroy(aBuilder);
  }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;

  nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override;

  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;

  Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override {
    return Nothing();
  }

  RetainedDisplayList* GetChildren() const override { return &mChildren; }
  RetainedDisplayList* GetSameCoordinateSystemChildren() const override {
    return GetChildren();
  }

  Maybe<nsRect> GetClipWithRespectToASR(
      nsDisplayListBuilder* aBuilder,
      const ActiveScrolledRoot* aASR) const override;

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return true;
  }

  void SetClipChain(const DisplayItemClipChain* aClipChain,
                    bool aStore) override {
    MOZ_ASSERT_UNREACHABLE("nsDisplayContainer does not support clipping");
  }

  void UpdateBounds(nsDisplayListBuilder* aBuilder) override;

 private:
  mutable RetainedDisplayList mChildren;
  nsRect mBounds;
};

/**
 * Use this class to implement not-very-frequently-used display items
 * that are not opaque, do not receive events, and are bounded by a frame's
 * border-rect.
 *
 * This should not be used for display items which are created frequently,
 * because each item is one or two pointers bigger than an item from a
 * custom display item class could be, and fractionally slower. However it does
 * save code size. We use this for infrequently-used item types.
 */
class nsDisplayGeneric : public nsPaintedDisplayItem {
 public:
  typedef void (*PaintCallback)(nsIFrame* aFrame, gfx::DrawTarget* aDrawTarget,
                                const nsRect& aDirtyRect, nsPoint aFramePt);

  // XXX: should be removed eventually
  typedef void (*OldPaintCallback)(nsIFrame* aFrame, gfxContext* aCtx,
                                   const nsRect& aDirtyRect, nsPoint aFramePt);

  nsDisplayGeneric(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                   PaintCallback aPaint, const char* aName,
                   DisplayItemType aType)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mPaint(aPaint),
        mOldPaint(nullptr),
        mName(aName) {
    MOZ_COUNT_CTOR(nsDisplayGeneric);
    SetType(aType);
  }

  // XXX: should be removed eventually
  nsDisplayGeneric(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                   OldPaintCallback aOldPaint, const char* aName,
                   DisplayItemType aType)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mPaint(nullptr),
        mOldPaint(aOldPaint),
        mName(aName) {
    MOZ_COUNT_CTOR(nsDisplayGeneric);
    SetType(aType);
  }

  constexpr static DisplayItemType ItemType() {
    return DisplayItemType::TYPE_GENERIC;
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayGeneric)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
    MOZ_ASSERT(!!mPaint != !!mOldPaint);
    if (mPaint) {
      mPaint(mFrame, aCtx->GetDrawTarget(), GetPaintRect(aBuilder, aCtx),
             ToReferenceFrame());
    } else {
      mOldPaint(mFrame, aCtx, GetPaintRect(aBuilder, aCtx), ToReferenceFrame());
    }
  }

  const char* Name() const override { return mName; }

  // This override is needed because GetType() for nsDisplayGeneric subclasses
  // does not match TYPE_GENERIC that was used to allocate the object.
  void Destroy(nsDisplayListBuilder* aBuilder) override {
    this->~nsDisplayGeneric();
    aBuilder->Destroy(DisplayItemType::TYPE_GENERIC, this);
  }

 protected:
  void* operator new(size_t aSize, nsDisplayListBuilder* aBuilder) {
    return aBuilder->Allocate(aSize, DisplayItemType::TYPE_GENERIC);
  }

  template <typename T, typename F, typename... Args>
  friend T* MakeDisplayItemWithIndex(nsDisplayListBuilder* aBuilder, F* aFrame,
                                     const uint16_t aIndex, Args&&... aArgs);

  PaintCallback mPaint;
  OldPaintCallback mOldPaint;  // XXX: should be removed eventually
  const char* mName;
};

#if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
/**
 * This class implements painting of reflow counts.  Ideally, we would simply
 * make all the frame names be those returned by nsIFrame::GetFrameName
 * (except that tosses in the content tag name!)  and support only one color
 * and eliminate this class altogether in favor of nsDisplayGeneric, but for
 * the time being we can't pass args to a PaintCallback, so just have a
 * separate class to do the right thing.  Sadly, this alsmo means we need to
 * hack all leaf frame classes to handle this.
 *
 * XXXbz the color thing is a bit of a mess, but 0 basically means "not set"
 * here...  I could switch it all to nscolor, but why bother?
 */
class nsDisplayReflowCount : public nsPaintedDisplayItem {
 public:
  nsDisplayReflowCount(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       const char* aFrameName, uint32_t aColor = 0)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mFrameName(aFrameName),
        mColor(aColor) {
    MOZ_COUNT_CTOR(nsDisplayReflowCount);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayReflowCount)

  NS_DISPLAY_DECL_NAME("nsDisplayReflowCount", TYPE_REFLOW_COUNT)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

 protected:
  const char* mFrameName;
  nscolor mColor;
};

#  define DO_GLOBAL_REFLOW_COUNT_DSP(_name)                                 \
    PR_BEGIN_MACRO                                                          \
    if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
        PresShell()->IsPaintingFrameCounts()) {                             \
      aLists.Outlines()->AppendNewToTop<mozilla::nsDisplayReflowCount>(     \
          aBuilder, this, _name);                                           \
    }                                                                       \
    PR_END_MACRO

#  define DO_GLOBAL_REFLOW_COUNT_DSP_COLOR(_name, _color)                   \
    PR_BEGIN_MACRO                                                          \
    if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
        PresShell()->IsPaintingFrameCounts()) {                             \
      aLists.Outlines()->AppendNewToTop<mozilla::nsDisplayReflowCount>(     \
          aBuilder, this, _name, _color);                                   \
    }                                                                       \
    PR_END_MACRO

/*
  Macro to be used for classes that don't actually implement BuildDisplayList
 */
#  define DECL_DO_GLOBAL_REFLOW_COUNT_DSP(_class, _super)     \
    void BuildDisplayList(nsDisplayListBuilder* aBuilder,     \
                          const nsRect& aDirtyRect,           \
                          const nsDisplayListSet& aLists) {   \
      DO_GLOBAL_REFLOW_COUNT_DSP(#_class);                    \
      _super::BuildDisplayList(aBuilder, aDirtyRect, aLists); \
    }

#else  // MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF

#  define DO_GLOBAL_REFLOW_COUNT_DSP(_name)
#  define DO_GLOBAL_REFLOW_COUNT_DSP_COLOR(_name, _color)
#  define DECL_DO_GLOBAL_REFLOW_COUNT_DSP(_class, _super)

#endif  // MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF

class nsDisplayCaret : public nsPaintedDisplayItem {
 public:
  nsDisplayCaret(nsDisplayListBuilder* aBuilder, nsIFrame* aCaretFrame);

#ifdef NS_BUILD_REFCNT_LOGGING
  ~nsDisplayCaret() override;
#endif

  NS_DISPLAY_DECL_NAME("Caret", TYPE_CARET)

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

 protected:
  RefPtr<nsCaret> mCaret;
  nsRect mBounds;
};

/**
 * The standard display item to paint the CSS borders of a frame.
 */
class nsDisplayBorder : public nsPaintedDisplayItem {
 public:
  nsDisplayBorder(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBorder)

  NS_DISPLAY_DECL_NAME("Border", TYPE_BORDER)

  bool IsInvisibleInRect(const nsRect& aRect) const override;
  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override;
  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override;

  nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder,
                          bool* aSnap) const override {
    *aSnap = true;
    return CalculateBounds<nsRegion>(*mFrame->StyleBorder());
  }

 protected:
  template <typename T>
  T CalculateBounds(const nsStyleBorder& aStyleBorder) const {
    nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize());
    if (aStyleBorder.IsBorderImageSizeAvailable()) {
      borderBounds.Inflate(aStyleBorder.GetImageOutset());
      return borderBounds;
    }

    nsMargin border = aStyleBorder.GetComputedBorder();
    T result;
    if (border.top > 0) {
      result = nsRect(borderBounds.X(), borderBounds.Y(), borderBounds.Width(),
                      border.top);
    }
    if (border.right > 0) {
      result.OrWith(nsRect(borderBounds.XMost() - border.right,
                           borderBounds.Y(), border.right,
                           borderBounds.Height()));
    }
    if (border.bottom > 0) {
      result.OrWith(nsRect(borderBounds.X(),
                           borderBounds.YMost() - border.bottom,
                           borderBounds.Width(), border.bottom));
    }
    if (border.left > 0) {
      result.OrWith(nsRect(borderBounds.X(), borderBounds.Y(), border.left,
                           borderBounds.Height()));
    }

    nscoord radii[8];
    if (mFrame->GetBorderRadii(radii)) {
      if (border.left > 0 || border.top > 0) {
        nsSize cornerSize(radii[eCornerTopLeftX], radii[eCornerTopLeftY]);
        result.OrWith(nsRect(borderBounds.TopLeft(), cornerSize));
      }
      if (border.top > 0 || border.right > 0) {
        nsSize cornerSize(radii[eCornerTopRightX], radii[eCornerTopRightY]);
        result.OrWith(
            nsRect(borderBounds.TopRight() - nsPoint(cornerSize.width, 0),
                   cornerSize));
      }
      if (border.right > 0 || border.bottom > 0) {
        nsSize cornerSize(radii[eCornerBottomRightX],
                          radii[eCornerBottomRightY]);
        result.OrWith(nsRect(borderBounds.BottomRight() -
                                 nsPoint(cornerSize.width, cornerSize.height),
                             cornerSize));
      }
      if (border.bottom > 0 || border.left > 0) {
        nsSize cornerSize(radii[eCornerBottomLeftX], radii[eCornerBottomLeftY]);
        result.OrWith(
            nsRect(borderBounds.BottomLeft() - nsPoint(0, cornerSize.height),
                   cornerSize));
      }
    }
    return result;
  }

  nsRect mBounds;
};

/**
 * A simple display item that just renders a solid color across the
 * specified bounds. For canvas frames (in the CSS sense) we split off the
 * drawing of the background color into this class (from nsDisplayBackground
 * via nsDisplayCanvasBackground). This is done so that we can always draw a
 * background color to avoid ugly flashes of white when we can't draw a full
 * frame tree (ie when a page is loading). The bounds can differ from the
 * frame's bounds -- this is needed when a frame/iframe is loading and there
 * is not yet a frame tree to go in the frame/iframe so we use the subdoc
 * frame of the parent document as a standin.
 */
class nsDisplaySolidColorBase : public nsPaintedDisplayItem {
 public:
  nsDisplaySolidColorBase(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                          nscolor aColor)
      : nsPaintedDisplayItem(aBuilder, aFrame), mColor(aColor) {}

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplaySolidColorGeometry(this, aBuilder, mColor);
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override {
    const nsDisplaySolidColorGeometry* geometry =
        static_cast<const nsDisplaySolidColorGeometry*>(aGeometry);
    if (mColor != geometry->mColor) {
      bool dummy;
      aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &dummy));
      return;
    }
    ComputeInvalidationRegionDifference(aBuilder, geometry, aInvalidRegion);
  }

  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override {
    *aSnap = false;
    nsRegion result;
    if (NS_GET_A(mColor) == 255) {
      result = GetBounds(aBuilder, aSnap);
    }
    return result;
  }

  Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override {
    return Some(mColor);
  }

 protected:
  nscolor mColor;
};

class nsDisplaySolidColor : public nsDisplaySolidColorBase {
 public:
  nsDisplaySolidColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      const nsRect& aBounds, nscolor aColor,
                      bool aCanBeReused = true)
      : nsDisplaySolidColorBase(aBuilder, aFrame, aColor),
        mBounds(aBounds),
        mIsCheckerboardBackground(false) {
    NS_ASSERTION(NS_GET_A(aColor) > 0,
                 "Don't create invisible nsDisplaySolidColors!");
    MOZ_COUNT_CTOR(nsDisplaySolidColor);
    if (!aCanBeReused) {
      SetCantBeReused();
    }
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplaySolidColor)

  NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  void WriteDebugInfo(std::stringstream& aStream) override;
  void SetIsCheckerboardBackground() { mIsCheckerboardBackground = true; }
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  int32_t ZIndex() const override {
    if (mOverrideZIndex) {
      return mOverrideZIndex.value();
    }
    return nsDisplaySolidColorBase::ZIndex();
  }

  void SetOverrideZIndex(int32_t aZIndex) { mOverrideZIndex = Some(aZIndex); }

 private:
  nsRect mBounds;
  bool mIsCheckerboardBackground;
  Maybe<int32_t> mOverrideZIndex;
};

/**
 * A display item that renders a solid color over a region. This is not
 * exposed through CSS, its only purpose is efficient invalidation of
 * the find bar highlighter dimmer.
 */
class nsDisplaySolidColorRegion : public nsPaintedDisplayItem {
 public:
  nsDisplaySolidColorRegion(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                            const nsRegion& aRegion, nscolor aColor)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mRegion(aRegion),
        mColor(gfx::sRGBColor::FromABGR(aColor)) {
    NS_ASSERTION(NS_GET_A(aColor) > 0,
                 "Don't create invisible nsDisplaySolidColorRegions!");
    MOZ_COUNT_CTOR(nsDisplaySolidColorRegion);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplaySolidColorRegion)

  NS_DISPLAY_DECL_NAME("SolidColorRegion", TYPE_SOLID_COLOR_REGION)

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplaySolidColorRegionGeometry(this, aBuilder, mRegion,
                                                 mColor);
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override {
    const nsDisplaySolidColorRegionGeometry* geometry =
        static_cast<const nsDisplaySolidColorRegionGeometry*>(aGeometry);
    if (mColor == geometry->mColor) {
      aInvalidRegion->Xor(geometry->mRegion, mRegion);
    } else {
      aInvalidRegion->Or(geometry->mRegion.GetBounds(), mRegion.GetBounds());
    }
  }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

 protected:
  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  void WriteDebugInfo(std::stringstream& aStream) override;

 private:
  nsRegion mRegion;
  gfx::sRGBColor mColor;
};

enum class AppendedBackgroundType : uint8_t {
  None,
  Background,
  ThemedBackground,
};

/**
 * A display item to paint one background-image for a frame. Each background
 * image layer gets its own nsDisplayBackgroundImage.
 */
class nsDisplayBackgroundImage : public nsPaintedDisplayItem {
 public:
  struct InitData {
    nsDisplayListBuilder* builder;
    const ComputedStyle* backgroundStyle;
    nsCOMPtr<imgIContainer> image;
    nsRect backgroundRect;
    nsRect fillArea;
    nsRect destArea;
    uint32_t layer;
    bool isRasterImage;
    bool shouldFixToViewport;
  };

  /**
   * aLayer signifies which background layer this item represents.
   * aIsThemed should be the value of aFrame->IsThemed.
   * aBackgroundStyle should be the result of
   * nsCSSRendering::FindBackground, or null if FindBackground returned false.
   * aBackgroundRect is relative to aFrame.
   */
  static InitData GetInitData(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                              uint16_t aLayer, const nsRect& aBackgroundRect,
                              const ComputedStyle* aBackgroundStyle);

  explicit nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder,
                                    nsIFrame* aFrame, const InitData& aInitData,
                                    nsIFrame* aFrameForBounds = nullptr);
  ~nsDisplayBackgroundImage() override;

  NS_DISPLAY_DECL_NAME("Background", TYPE_BACKGROUND)

  /**
   * This will create and append new items for all the layers of the
   * background. Returns the type of background that was appended.
   * aAllowWillPaintBorderOptimization should usually be left at true, unless
   * aFrame has special border drawing that causes opaque borders to not
   * actually be opaque.
   */
  static AppendedBackgroundType AppendBackgroundItemsToTop(
      nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
      const nsRect& aBackgroundRect, nsDisplayList* aList,
      bool aAllowWillPaintBorderOptimization = true,
      const nsRect& aBackgroundOriginRect = nsRect(),
      nsIFrame* aSecondaryReferenceFrame = nullptr,
      Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
          aAutoBuildingDisplayList = nullptr);

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;
  Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;

  bool CanApplyOpacity(WebRenderLayerManager* aManager,
                       nsDisplayListBuilder* aBuilder) const override;

  /**
   * GetBounds() returns the background painting area.
   */
  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  /**
   * Return the background positioning area.
   * (GetBounds() returns the background painting area.)
   * Can be called only when mBackgroundStyle is non-null.
   */
  nsRect GetPositioningArea() const;

  /**
   * Returns true if existing rendered pixels of this display item may need
   * to be redrawn if the positioning area size changes but its position does
   * not.
   * If false, only the changed painting area needs to be redrawn when the
   * positioning area size changes but its position does not.
   */
  bool RenderingMightDependOnPositioningAreaSizeChange() const;

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplayBackgroundGeometry(this, aBuilder);
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override;
  bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) const override {
    return mShouldFixToViewport;
  }

  nsRect GetDestRect() const { return mDestRect; }

  nsIFrame* GetDependentFrame() override { return mDependentFrame; }

  void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
    if (!aBuilder->IsRetainingDisplayList() || mDependentFrame == aFrame) {
      return;
    }
    mDependentFrame = aFrame;
    if (aFrame) {
      mDependentFrame->AddDisplayItem(this);
    }
  }

  void RemoveFrame(nsIFrame* aFrame) override {
    if (aFrame == mDependentFrame) {
      mDependentFrame = nullptr;
    }
    nsPaintedDisplayItem::RemoveFrame(aFrame);
  }

  // Match https://w3c.github.io/paint-timing/#contentful-image
  bool IsContentful() const override {
    const auto& styleImage =
        mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mImage;

    return styleImage.IsSizeAvailable() && styleImage.FinalImage().IsUrl();
  }

 protected:
  bool CanBuildWebRenderDisplayItems(layers::WebRenderLayerManager* aManager,
                                     nsDisplayListBuilder* aBuilder) const;
  nsRect GetBoundsInternal(nsDisplayListBuilder* aBuilder,
                           nsIFrame* aFrameForBounds = nullptr);

  void PaintInternal(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
                     const nsRect& aBounds, nsRect* aClipRect);

  // Cache the result of nsCSSRendering::FindBackground. Always null if
  // mIsThemed is true or if FindBackground returned false.
  RefPtr<const ComputedStyle> mBackgroundStyle;
  nsCOMPtr<imgIContainer> mImage;
  nsIFrame* mDependentFrame;
  nsRect mBackgroundRect;  // relative to the reference frame
  nsRect mFillRect;
  nsRect mDestRect;
  /* Bounds of this display item */
  nsRect mBounds;
  uint16_t mLayer;
  bool mIsRasterImage;
  /* Whether the image should be treated as fixed to the viewport. */
  bool mShouldFixToViewport;
};

/**
 * A display item to paint background image for table. For table parts, such
 * as row, row group, col, col group, when drawing its background, we'll
 * create separate background image display item for its containning cell.
 * Those background image display items will reference to same DisplayItemData
 * if we keep the mFrame point to cell's ancestor frame. We don't want to this
 * happened bacause share same DisplatItemData will cause many bugs. So that
 * we let mFrame point to cell frame and store the table type of the ancestor
 * frame. And use mFrame and table type as key to generate DisplayItemData to
 * avoid sharing DisplayItemData.
 *
 * Also store ancestor frame as mStyleFrame for all rendering informations.
 */
class nsDisplayTableBackgroundImage : public nsDisplayBackgroundImage {
 public:
  nsDisplayTableBackgroundImage(nsDisplayListBuilder* aBuilder,
                                nsIFrame* aFrame, const InitData& aData,
                                nsIFrame* aCellFrame);
  ~nsDisplayTableBackgroundImage() override;

  NS_DISPLAY_DECL_NAME("TableBackgroundImage", TYPE_TABLE_BACKGROUND_IMAGE)

  bool IsInvalid(nsRect& aRect) const override;

  nsIFrame* FrameForInvalidation() const override { return mStyleFrame; }

  void RemoveFrame(nsIFrame* aFrame) override {
    if (aFrame == mStyleFrame) {
      mStyleFrame = nullptr;
      SetDeletedFrame();
    }
    nsDisplayBackgroundImage::RemoveFrame(aFrame);
  }

 protected:
  nsIFrame* StyleFrame() const override { return mStyleFrame; }
  nsIFrame* mStyleFrame;
};

/**
 * A display item to paint the native theme background for a frame.
 */
class nsDisplayThemedBackground : public nsPaintedDisplayItem {
 public:
  nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                            const nsRect& aBackgroundRect);

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayThemedBackground)

  NS_DISPLAY_DECL_NAME("ThemedBackground", TYPE_THEMED_BACKGROUND)

  void Init(nsDisplayListBuilder* aBuilder);

  void Destroy(nsDisplayListBuilder* aBuilder) override {
    aBuilder->UnregisterThemeGeometry(this);
    nsPaintedDisplayItem::Destroy(aBuilder);
  }

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;
  Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  bool MustPaintOnContentSide() const override { return true; }

  /**
   * GetBounds() returns the background painting area.
   */
  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  /**
   * Return the background positioning area.
   * (GetBounds() returns the background painting area.)
   * Can be called only when mBackgroundStyle is non-null.
   */
  nsRect GetPositioningArea() const;

  /**
   * Return whether our frame's document does not have the state
   * NS_DOCUMENT_STATE_WINDOW_INACTIVE.
   */
  bool IsWindowActive() const;

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplayThemedBackgroundGeometry(this, aBuilder);
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override;

  void WriteDebugInfo(std::stringstream& aStream) override;

 protected:
  nsRect GetBoundsInternal();

  void PaintInternal(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
                     const nsRect& aBounds, nsRect* aClipRect);

  nsRect mBackgroundRect;
  nsRect mBounds;
  nsITheme::Transparency mThemeTransparency;
  StyleAppearance mAppearance;
};

class nsDisplayTableThemedBackground : public nsDisplayThemedBackground {
 public:
  nsDisplayTableThemedBackground(nsDisplayListBuilder* aBuilder,
                                 nsIFrame* aFrame,
                                 const nsRect& aBackgroundRect,
                                 nsIFrame* aAncestorFrame)
      : nsDisplayThemedBackground(aBuilder, aFrame, aBackgroundRect),
        mAncestorFrame(aAncestorFrame) {
    if (aBuilder->IsRetainingDisplayList()) {
      mAncestorFrame->AddDisplayItem(this);
    }
  }

  ~nsDisplayTableThemedBackground() override {
    if (mAncestorFrame) {
      mAncestorFrame->RemoveDisplayItem(this);
    }
  }

  NS_DISPLAY_DECL_NAME("TableThemedBackground",
                       TYPE_TABLE_THEMED_BACKGROUND_IMAGE)

  nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }

  void RemoveFrame(nsIFrame* aFrame) override {
    if (aFrame == mAncestorFrame) {
      mAncestorFrame = nullptr;
      SetDeletedFrame();
    }
    nsDisplayThemedBackground::RemoveFrame(aFrame);
  }

 protected:
  nsIFrame* StyleFrame() const override { return mAncestorFrame; }
  nsIFrame* mAncestorFrame;
};

class nsDisplayBackgroundColor : public nsPaintedDisplayItem {
 public:
  nsDisplayBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                           const nsRect& aBackgroundRect,
                           const ComputedStyle* aBackgroundStyle,
                           const nscolor& aColor)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mBackgroundRect(aBackgroundRect),
        mHasStyle(aBackgroundStyle),
        mDependentFrame(nullptr),
        mColor(gfx::sRGBColor::FromABGR(aColor)) {
    if (mHasStyle) {
      mBottomLayerClip =
          aBackgroundStyle->StyleBackground()->BottomLayer().mClip;
    } else {
      MOZ_ASSERT(aBuilder->IsForEventDelivery());
    }
  }

  ~nsDisplayBackgroundColor() override {
    if (mDependentFrame) {
      mDependentFrame->RemoveDisplayItem(this);
    }
  }

  NS_DISPLAY_DECL_NAME("BackgroundColor", TYPE_BACKGROUND_COLOR)

  bool HasBackgroundClipText() const {
    MOZ_ASSERT(mHasStyle);
    return mBottomLayerClip == StyleGeometryBox::Text;
  }

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  void PaintWithClip(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
                     const DisplayItemClip& aClip) override;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;
  Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;
  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
  bool CanApplyOpacity(WebRenderLayerManager* aManager,
                       nsDisplayListBuilder* aBuilder) const override;

  float GetOpacity() const { return mColor.a; }

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override {
    *aSnap = true;
    return mBackgroundRect;
  }

  bool CanPaintWithClip(const DisplayItemClip& aClip) override {
    if (HasBackgroundClipText()) {
      return false;
    }

    if (aClip.GetRoundedRectCount() > 1) {
      return false;
    }

    return true;
  }

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplaySolidColorGeometry(this, aBuilder, mColor.ToABGR());
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override {
    const nsDisplaySolidColorGeometry* geometry =
        static_cast<const nsDisplaySolidColorGeometry*>(aGeometry);

    if (mColor.ToABGR() != geometry->mColor) {
      bool dummy;
      aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &dummy));
      return;
    }
    ComputeInvalidationRegionDifference(aBuilder, geometry, aInvalidRegion);
  }

  nsIFrame* GetDependentFrame() override { return mDependentFrame; }

  void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
    if (!aBuilder->IsRetainingDisplayList() || mDependentFrame == aFrame) {
      return;
    }
    mDependentFrame = aFrame;
    if (aFrame) {
      mDependentFrame->AddDisplayItem(this);
    }
  }

  void RemoveFrame(nsIFrame* aFrame) override {
    if (aFrame == mDependentFrame) {
      mDependentFrame = nullptr;
    }

    nsPaintedDisplayItem::RemoveFrame(aFrame);
  }

  void WriteDebugInfo(std::stringstream& aStream) override;

  bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;

 protected:
  const nsRect mBackgroundRect;
  const bool mHasStyle;
  StyleGeometryBox mBottomLayerClip;
  nsIFrame* mDependentFrame;
  gfx::sRGBColor mColor;
};

class nsDisplayTableBackgroundColor : public nsDisplayBackgroundColor {
 public:
  nsDisplayTableBackgroundColor(nsDisplayListBuilder* aBuilder,
                                nsIFrame* aFrame, const nsRect& aBackgroundRect,
                                const ComputedStyle* aBackgroundStyle,
                                const nscolor& aColor, nsIFrame* aAncestorFrame)
      : nsDisplayBackgroundColor(aBuilder, aFrame, aBackgroundRect,
                                 aBackgroundStyle, aColor),
        mAncestorFrame(aAncestorFrame) {
    if (aBuilder->IsRetainingDisplayList()) {
      mAncestorFrame->AddDisplayItem(this);
    }
  }

  ~nsDisplayTableBackgroundColor() override {
    if (mAncestorFrame) {
      mAncestorFrame->RemoveDisplayItem(this);
    }
  }

  NS_DISPLAY_DECL_NAME("TableBackgroundColor", TYPE_TABLE_BACKGROUND_COLOR)

  nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }

  void RemoveFrame(nsIFrame* aFrame) override {
    if (aFrame == mAncestorFrame) {
      mAncestorFrame = nullptr;
      SetDeletedFrame();
    }
    nsDisplayBackgroundColor::RemoveFrame(aFrame);
  }

  bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override {
    return false;
  }

 protected:
  nsIFrame* mAncestorFrame;
};

/**
 * The standard display item to paint the outer CSS box-shadows of a frame.
 */
class nsDisplayBoxShadowOuter final : public nsPaintedDisplayItem {
 public:
  nsDisplayBoxShadowOuter(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
      : nsPaintedDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayBoxShadowOuter);
    mBounds = GetBoundsInternal();
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBoxShadowOuter)

  NS_DISPLAY_DECL_NAME("BoxShadowOuter", TYPE_BOX_SHADOW_OUTER)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  bool IsInvisibleInRect(const nsRect& aRect) const override;
  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override;

  bool CanApplyOpacity(WebRenderLayerManager* aManager,
                       nsDisplayListBuilder* aBuilder) const override {
    return CanBuildWebRenderDisplayItems();
  }

  bool CanBuildWebRenderDisplayItems() const;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  nsRect GetBoundsInternal();

 private:
  nsRect mBounds;
};

/**
 * The standard display item to paint the inner CSS box-shadows of a frame.
 */
class nsDisplayBoxShadowInner : public nsPaintedDisplayItem {
 public:
  nsDisplayBoxShadowInner(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
      : nsPaintedDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayBoxShadowInner);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBoxShadowInner)

  NS_DISPLAY_DECL_NAME("BoxShadowInner", TYPE_BOX_SHADOW_INNER)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplayBoxShadowInnerGeometry(this, aBuilder);
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override {
    const nsDisplayBoxShadowInnerGeometry* geometry =
        static_cast<const nsDisplayBoxShadowInnerGeometry*>(aGeometry);
    if (!geometry->mPaddingRect.IsEqualInterior(GetPaddingRect())) {
      // nsDisplayBoxShadowInner is based around the padding rect, but it can
      // touch pixels outside of this. We should invalidate the entire bounds.
      bool snap;
      aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap));
    }
  }

  static bool CanCreateWebRenderCommands(nsDisplayListBuilder* aBuilder,
                                         nsIFrame* aFrame,
                                         const nsPoint& aReferenceOffset);
  static void CreateInsetBoxShadowWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
      nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect);
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
};

/**
 * The standard display item to paint the CSS outline of a frame.
 */
class nsDisplayOutline final : public nsPaintedDisplayItem {
 public:
  nsDisplayOutline(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
      : nsPaintedDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayOutline);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayOutline)

  NS_DISPLAY_DECL_NAME("Outline", TYPE_OUTLINE)

  bool MustPaintOnContentSide() const override {
    MOZ_ASSERT(IsThemedOutline(),
               "The only fallback path we have is for themed outlines");
    return true;
  }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  bool IsInvisibleInRect(const nsRect& aRect) const override;
  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

 private:
  nsRect GetInnerRect() const;
  bool IsThemedOutline() const;
  bool HasRadius() const;
};

/**
 * A class that lets you receive events within the frame bounds but never
 * paints.
 */
class nsDisplayEventReceiver final : public nsDisplayItem {
 public:
  nsDisplayEventReceiver(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
      : nsDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayEventReceiver);
  }

  MOZ_COUNTED_DTOR_FINAL(nsDisplayEventReceiver)

  NS_DISPLAY_DECL_NAME("EventReceiver", TYPE_EVENT_RECEIVER)

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) final;
};

/**
 * Similar to nsDisplayEventReceiver in that it is used for hit-testing. However
 * this gets built when we're doing widget painting and we need to send the
 * compositor some hit-test info for a frame. This is effectively a dummy item
 * whose sole purpose is to carry the hit-test info to the compositor.
 */
class nsDisplayCompositorHitTestInfo final : public nsDisplayItem {
 public:
  nsDisplayCompositorHitTestInfo(nsDisplayListBuilder* aBuilder,
                                 nsIFrame* aFrame)
      : nsDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo);
    mHitTestInfo.Initialize(aBuilder, aFrame);
    SetHasHitTestInfo();
  }

  nsDisplayCompositorHitTestInfo(
      nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aArea,
      const gfx::CompositorHitTestInfo& aHitTestFlags)
      : nsDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo);
    mHitTestInfo.SetAreaAndInfo(aArea, aHitTestFlags);
    mHitTestInfo.InitializeScrollTarget(aBuilder);
    SetHasHitTestInfo();
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayCompositorHitTestInfo)

  NS_DISPLAY_DECL_NAME("CompositorHitTestInfo", TYPE_COMPOSITOR_HITTEST_INFO)

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  bool isInvisible() const { return true; }

  int32_t ZIndex() const override;
  void SetOverrideZIndex(int32_t aZIndex);

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override {
    *aSnap = false;
    return nsRect();
  }

  const HitTestInfo& GetHitTestInfo() final { return mHitTestInfo; }

 private:
  HitTestInfo mHitTestInfo;
  Maybe<int32_t> mOverrideZIndex;
};

class nsDisplayWrapper;

/**
 * A class that lets you wrap a display list as a display item.
 *
 * GetUnderlyingFrame() is troublesome for wrapped lists because if the wrapped
 * list has many items, it's not clear which one has the 'underlying frame'.
 * Thus we force the creator to specify what the underlying frame is. The
 * underlying frame should be the root of a stacking context, because sorting
 * a list containing this item will not get at the children.
 *
 * In some cases (e.g., clipping) we want to wrap a list but we don't have a
 * particular underlying frame that is a stacking context root. In that case
 * we allow the frame to be nullptr. Callers to GetUnderlyingFrame must
 * detect and handle this case.
 */
class nsDisplayWrapList : public nsPaintedDisplayItem {
 public:
  /**
   * Takes all the items from aList and puts them in our list.
   */
  nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                    nsDisplayList* aList);

  nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                    nsDisplayItem* aItem);

  nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                    nsDisplayList* aList,
                    const ActiveScrolledRoot* aActiveScrolledRoot,
                    bool aClearClipChain = false);

  nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mList(aBuilder),
        mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
        mOverrideZIndex(0),
        mHasZIndexOverride(false) {
    MOZ_COUNT_CTOR(nsDisplayWrapList);
    mBaseBuildingRect = GetBuildingRect();
    mListPtr = &mList;
    mOriginalClipChain = mClipChain;
  }

  nsDisplayWrapList() = delete;

  /**
   * A custom copy-constructor that does not copy mList, as this would mutate
   * the other item.
   */
  nsDisplayWrapList(const nsDisplayWrapList& aOther) = delete;
  nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                    const nsDisplayWrapList& aOther)
      : nsPaintedDisplayItem(aBuilder, aOther),
        mList(aBuilder),
        mListPtr(&mList),
        mFrameActiveScrolledRoot(aOther.mFrameActiveScrolledRoot),
        mMergedFrames(aOther.mMergedFrames.Clone()),
        mBounds(aOther.mBounds),
        mBaseBuildingRect(aOther.mBaseBuildingRect),
        mOriginalClipChain(aOther.mClipChain),
        mOverrideZIndex(aOther.mOverrideZIndex),
        mHasZIndexOverride(aOther.mHasZIndexOverride),
        mClearingClipChain(aOther.mClearingClipChain) {
    MOZ_COUNT_CTOR(nsDisplayWrapList);
  }

  ~nsDisplayWrapList() override;

  const nsDisplayWrapList* AsDisplayWrapList() const final { return this; }
  nsDisplayWrapList* AsDisplayWrapList() final { return this; }

  void Destroy(nsDisplayListBuilder* aBuilder) override {
    mList.DeleteAll(aBuilder);
    nsPaintedDisplayItem::Destroy(aBuilder);
  }

  /**
   * Creates a new nsDisplayWrapper that holds a pointer to the display list
   * owned by the given nsDisplayItem.
   */
  nsDisplayWrapper* CreateShallowCopy(nsDisplayListBuilder* aBuilder);

  /**
   * Call this if the wrapped list is changed.
   */
  void UpdateBounds(nsDisplayListBuilder* aBuilder) override {
    // Clear the clip chain up to the asr, but don't store it, so that we'll
    // recover it when we reuse the item.
    if (mClearingClipChain) {
      const DisplayItemClipChain* clip = mOriginalClipChain;
      while (clip && ActiveScrolledRoot::IsAncestor(GetActiveScrolledRoot(),
                                                    clip->mASR)) {
        clip = clip->mParent;
      }
      SetClipChain(clip, false);
    }

    nsRect buildingRect;
    mBounds = mListPtr->GetClippedBoundsWithRespectToASR(
        aBuilder, mActiveScrolledRoot, &buildingRect);
    // The display list may contain content that's visible outside the visible
    // rect (i.e. the current dirty rect) passed in when the item was created.
    // This happens when the dirty rect has been restricted to the visual
    // overflow rect of a frame for some reason (e.g. when setting up dirty
    // rects in nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay), but that
    // frame contains placeholders for out-of-flows that aren't descendants of
    // the frame.
    buildingRect.UnionRect(mBaseBuildingRect, buildingRect);
    SetBuildingRect(buildingRect);
  }

  void SetClipChain(const DisplayItemClipChain* aClipChain,
                    bool aStore) override {
    nsDisplayItem::SetClipChain(aClipChain, aStore);

    if (aStore) {
      mOriginalClipChain = mClipChain;
    }
  }

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;
  Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;

  /**
   * Try to merge with the other item (which is below us in the display
   * list). This gets used by nsDisplayClip to coalesce clipping operations
   * (optimization), by nsDisplayOpacity to merge rendering for the same
   * content element into a single opacity group (correctness), and will be
   * used by nsDisplayOutline to merge multiple outlines for the same element
   * (also for correctness).
   */
  virtual void Merge(const nsDisplayItem* aItem) {
    MOZ_ASSERT(CanMerge(aItem));
    MOZ_ASSERT(Frame() != aItem->Frame());
    MergeFromTrackingMergedFrames(static_cast<const nsDisplayWrapList*>(aItem));
  }

  /**
   * Returns the underlying frames of all display items that have been
   * merged into this one (excluding this item's own underlying frame)
   * to aFrames.
   */
  const nsTArray<nsIFrame*>& GetMergedFrames() const { return mMergedFrames; }

  bool HasMergedFrames() const { return !mMergedFrames.IsEmpty(); }

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return true;
  }

  bool IsInvalid(nsRect& aRect) const override {
    if (mFrame->IsInvalid(aRect) && aRect.IsEmpty()) {
      return true;
    }
    nsRect temp;
    for (uint32_t i = 0; i < mMergedFrames.Length(); i++) {
      if (mMergedFrames[i]->IsInvalid(temp) && temp.IsEmpty()) {
        aRect.SetEmpty();
        return true;
      }
      aRect = aRect.Union(temp);
    }
    aRect += ToReferenceFrame();
    return !aRect.IsEmpty();
  }

  nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override;

  RetainedDisplayList* GetSameCoordinateSystemChildren() const override {
    return mListPtr;
  }

  RetainedDisplayList* GetChildren() const override { return mListPtr; }

  int32_t ZIndex() const override {
    return (mHasZIndexOverride) ? mOverrideZIndex
                                : nsPaintedDisplayItem::ZIndex();
  }

  void SetOverrideZIndex(int32_t aZIndex) {
    mHasZIndexOverride = true;
    mOverrideZIndex = aZIndex;
  }

  /**
   * This creates a copy of this item, but wrapping aItem instead of
   * our existing list. Only gets called if this item returned nullptr
   * for GetUnderlyingFrame(). aItem is guaranteed to return non-null from
   * GetUnderlyingFrame().
   */
  nsDisplayWrapList* WrapWithClone(nsDisplayListBuilder* aBuilder,
                                   nsDisplayItem* aItem) {
    MOZ_ASSERT_UNREACHABLE("We never returned nullptr for GetUnderlyingFrame!");
    return nullptr;
  }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override {
    return CreateWebRenderCommandsNewClipListOption(
        aBuilder, aResources, aSc, aManager, aDisplayListBuilder, true);
  }

  // Same as the above but with the option to pass the aNewClipList argument to
  // WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList.
  bool CreateWebRenderCommandsNewClipListOption(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList);

  const ActiveScrolledRoot* GetFrameActiveScrolledRoot() {
    return mFrameActiveScrolledRoot;
  }

 protected:
  void MergeFromTrackingMergedFrames(const nsDisplayWrapList* aOther) {
    mBounds.UnionRect(mBounds, aOther->mBounds);
    nsRect buildingRect;
    buildingRect.UnionRect(GetBuildingRect(), aOther->GetBuildingRect());
    SetBuildingRect(buildingRect);
    mMergedFrames.AppendElement(aOther->mFrame);
    mMergedFrames.AppendElements(aOther->mMergedFrames.Clone());
  }

  RetainedDisplayList mList;
  RetainedDisplayList* mListPtr;
  // The active scrolled root for the frame that created this
  // wrap list.
  RefPtr<const ActiveScrolledRoot> mFrameActiveScrolledRoot;
  // The frames from items that have been merged into this item, excluding
  // this item's own frame.
  nsTArray<nsIFrame*> mMergedFrames;
  nsRect mBounds;
  // Displaylist building rect contributed by this display item itself.
  // Our mBuildingRect may include the visible areas of children.
  nsRect mBaseBuildingRect;
  RefPtr<const DisplayItemClipChain> mOriginalClipChain;
  int32_t mOverrideZIndex;
  bool mHasZIndexOverride;
  bool mClearingClipChain = false;
};

class nsDisplayWrapper : public nsDisplayWrapList {
 public:
  NS_DISPLAY_DECL_NAME("WrapList", TYPE_WRAP_LIST)

  nsDisplayWrapper(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                   nsDisplayList* aList,
                   const ActiveScrolledRoot* aActiveScrolledRoot,
                   bool aClearClipChain = false)
      : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
                          aClearClipChain) {}

  nsDisplayWrapper(const nsDisplayWrapper& aOther) = delete;
  nsDisplayWrapper(nsDisplayListBuilder* aBuilder,
                   const nsDisplayWrapList& aOther)
      : nsDisplayWrapList(aBuilder, aOther) {}

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

 private:
  NS_DISPLAY_ALLOW_CLONING()
  friend class nsDisplayListBuilder;
  friend class nsDisplayWrapList;
};

/**
 * We call WrapDisplayList on the in-flow lists: BorderBackground(),
 * BlockBorderBackgrounds() and Content().
 * We call WrapDisplayItem on each item of Outlines(), PositionedDescendants(),
 * and Floats(). This is done to support special wrapping processing for frames
 * that may not be in-flow descendants of the current frame.
 */
class nsDisplayItemWrapper {
 public:
  // This is never instantiated directly (it has pure virtual methods), so no
  // need to count constructors and destructors.

  bool WrapBorderBackground() { return true; }
  virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
                                  nsIFrame* aFrame, nsDisplayList* aList) = 0;
  virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
                                  nsDisplayItem* aItem) = 0;

  nsresult WrapLists(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     const nsDisplayListSet& aIn, const nsDisplayListSet& aOut);
  nsresult WrapListsInPlace(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                            const nsDisplayListSet& aLists);

 protected:
  nsDisplayItemWrapper() = default;
};

/**
 * The standard display item to paint a stacking context with translucency
 * set by the stacking context root frame's 'opacity' style.
 */
class nsDisplayOpacity : public nsDisplayWrapList {
 public:
  nsDisplayOpacity(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                   nsDisplayList* aList,
                   const ActiveScrolledRoot* aActiveScrolledRoot,
                   bool aForEventsOnly, bool aNeedsActiveLayer,
                   bool aWrapsBackdropFilter);

  nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
                   const nsDisplayOpacity& aOther)
      : nsDisplayWrapList(aBuilder, aOther),
        mOpacity(aOther.mOpacity),
        mForEventsOnly(aOther.mForEventsOnly),
        mNeedsActiveLayer(aOther.mNeedsActiveLayer),
        mChildOpacityState(ChildOpacityState::Unknown),
        mWrapsBackdropFilter(aOther.mWrapsBackdropFilter) {
    MOZ_COUNT_CTOR(nsDisplayOpacity);
    // We should not try to merge flattened opacities.
    MOZ_ASSERT(aOther.mChildOpacityState != ChildOpacityState::Applied);
  }

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayOpacity)

  NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)

  void InvalidateCachedChildInfo(nsDisplayListBuilder* aBuilder) override {
    mChildOpacityState = ChildOpacityState::Unknown;
  }

  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  bool CanMerge(const nsDisplayItem* aItem) const override {
    // items for the same content element should be merged into a single
    // compositing group
    // aItem->GetUnderlyingFrame() returns non-null because it's
    // nsDisplayOpacity
    return HasDifferentFrame(aItem) && HasSameTypeAndClip(aItem) &&
           HasSameContent(aItem);
  }

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplayOpacityGeometry(this, aBuilder, mOpacity);
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override;

  bool IsInvalid(nsRect& aRect) const override {
    if (mForEventsOnly) {
      return false;
    }
    return nsDisplayWrapList::IsInvalid(aRect);
  }
  bool CanApplyOpacity(WebRenderLayerManager* aManager,
                       nsDisplayListBuilder* aBuilder) const override;
  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }

  bool CanApplyOpacityToChildren(WebRenderLayerManager* aManager,
                                 nsDisplayListBuilder* aBuilder,
                                 float aInheritedOpacity);

  bool NeedsGeometryUpdates() const override {
    // For flattened nsDisplayOpacity items, ComputeInvalidationRegion() only
    // handles invalidation for changed |mOpacity|. In order to keep track of
    // the current bounds of the item for invalidation, nsDisplayOpacityGeometry
    // for the corresponding DisplayItemData needs to be updated, even if the
    // reported invalidation region is empty.
    return mChildOpacityState == ChildOpacityState::Deferred;
  }

  /**
   * Returns true if ShouldFlattenAway() applied opacity to children.
   */
  bool OpacityAppliedToChildren() const {
    return mChildOpacityState == ChildOpacityState::Applied;
  }

  static bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
                               nsIFrame* aFrame);
  void WriteDebugInfo(std::stringstream& aStream) override;
  bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  float GetOpacity() const { return mOpacity; }

  bool CreatesStackingContextHelper() override { return true; }

 private:
  NS_DISPLAY_ALLOW_CLONING()

  bool CanApplyToChildren(WebRenderLayerManager* aManager,
                          nsDisplayListBuilder* aBuilder);
  bool ApplyToMask();

  float mOpacity;
  bool mForEventsOnly : 1;
  enum class ChildOpacityState : uint8_t {
    // Our child list has changed since the last time ApplyToChildren was
    // called.
    Unknown,
    // Our children defer opacity handling to us.
    Deferred,
    // Opacity is applied to our children.
    Applied
  };
  bool mNeedsActiveLayer : 1;
#ifndef __GNUC__
  ChildOpacityState mChildOpacityState : 2;
#else
  ChildOpacityState mChildOpacityState;
#endif
  bool mWrapsBackdropFilter;
};

class nsDisplayBlendMode : public nsDisplayWrapList {
 public:
  nsDisplayBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList, StyleBlend aBlendMode,
                     const ActiveScrolledRoot* aActiveScrolledRoot,
                     const bool aIsForBackground);
  nsDisplayBlendMode(nsDisplayListBuilder* aBuilder,
                     const nsDisplayBlendMode& aOther)
      : nsDisplayWrapList(aBuilder, aOther),
        mBlendMode(aOther.mBlendMode),
        mIsForBackground(aOther.mIsForBackground) {
    MOZ_COUNT_CTOR(nsDisplayBlendMode);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBlendMode)

  NS_DISPLAY_DECL_NAME("BlendMode", TYPE_BLEND_MODE)

  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;
  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override {
    // We don't need to compute an invalidation region since we have
    // LayerTreeInvalidation
  }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  bool CanMerge(const nsDisplayItem* aItem) const override;

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }

  gfx::CompositionOp BlendMode();

  bool CreatesStackingContextHelper() override { return true; }

 protected:
  StyleBlend mBlendMode;
  bool mIsForBackground;

 private:
  NS_DISPLAY_ALLOW_CLONING()
};

class nsDisplayTableBlendMode : public nsDisplayBlendMode {
 public:
  nsDisplayTableBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                          nsDisplayList* aList, StyleBlend aBlendMode,
                          const ActiveScrolledRoot* aActiveScrolledRoot,
                          nsIFrame* aAncestorFrame, const bool aIsForBackground)
      : nsDisplayBlendMode(aBuilder, aFrame, aList, aBlendMode,
                           aActiveScrolledRoot, aIsForBackground),
        mAncestorFrame(aAncestorFrame) {
    if (aBuilder->IsRetainingDisplayList()) {
      mAncestorFrame->AddDisplayItem(this);
    }
  }

  nsDisplayTableBlendMode(nsDisplayListBuilder* aBuilder,
                          const nsDisplayTableBlendMode& aOther)
      : nsDisplayBlendMode(aBuilder, aOther),
        mAncestorFrame(aOther.mAncestorFrame) {
    if (aBuilder->IsRetainingDisplayList()) {
      mAncestorFrame->AddDisplayItem(this);
    }
  }

  ~nsDisplayTableBlendMode() override {
    if (mAncestorFrame) {
      mAncestorFrame->RemoveDisplayItem(this);
    }
  }

  NS_DISPLAY_DECL_NAME("TableBlendMode", TYPE_TABLE_BLEND_MODE)

  nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }

  void RemoveFrame(nsIFrame* aFrame) override {
    if (aFrame == mAncestorFrame) {
      mAncestorFrame = nullptr;
      SetDeletedFrame();
    }
    nsDisplayBlendMode::RemoveFrame(aFrame);
  }

 protected:
  nsIFrame* mAncestorFrame;

 private:
  NS_DISPLAY_ALLOW_CLONING()
};

class nsDisplayBlendContainer : public nsDisplayWrapList {
 public:
  static nsDisplayBlendContainer* CreateForMixBlendMode(
      nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
      const ActiveScrolledRoot* aActiveScrolledRoot);

  static nsDisplayBlendContainer* CreateForBackgroundBlendMode(
      nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
      nsIFrame* aSecondaryFrame, nsDisplayList* aList,
      const ActiveScrolledRoot* aActiveScrolledRoot);

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBlendContainer)

  NS_DISPLAY_DECL_NAME("BlendContainer", TYPE_BLEND_CONTAINER)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  bool CanMerge(const nsDisplayItem* aItem) const override {
    // Items for the same content element should be merged into a single
    // compositing group.
    return HasDifferentFrame(aItem) && HasSameTypeAndClip(aItem) &&
           HasSameContent(aItem) &&
           mIsForBackground ==
               static_cast<const nsDisplayBlendContainer*>(aItem)
                   ->mIsForBackground;
  }

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }

  bool CreatesStackingContextHelper() override { return true; }

 protected:
  nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                          nsDisplayList* aList,
                          const ActiveScrolledRoot* aActiveScrolledRoot,
                          bool aIsForBackground);
  nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder,
                          const nsDisplayBlendContainer& aOther)
      : nsDisplayWrapList(aBuilder, aOther),
        mIsForBackground(aOther.mIsForBackground) {
    MOZ_COUNT_CTOR(nsDisplayBlendContainer);
  }

  // Used to distinguish containers created at building stacking
  // context or appending background.
  bool mIsForBackground;

 private:
  NS_DISPLAY_ALLOW_CLONING()
};

class nsDisplayTableBlendContainer : public nsDisplayBlendContainer {
 public:
  NS_DISPLAY_DECL_NAME("TableBlendContainer", TYPE_TABLE_BLEND_CONTAINER)

  nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }

  void RemoveFrame(nsIFrame* aFrame) override {
    if (aFrame == mAncestorFrame) {
      mAncestorFrame = nullptr;
      SetDeletedFrame();
    }
    nsDisplayBlendContainer::RemoveFrame(aFrame);
  }

 protected:
  nsDisplayTableBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                               nsDisplayList* aList,
                               const ActiveScrolledRoot* aActiveScrolledRoot,
                               bool aIsForBackground, nsIFrame* aAncestorFrame)
      : nsDisplayBlendContainer(aBuilder, aFrame, aList, aActiveScrolledRoot,
                                aIsForBackground),
        mAncestorFrame(aAncestorFrame) {
    if (aBuilder->IsRetainingDisplayList()) {
      mAncestorFrame->AddDisplayItem(this);
    }
  }

  nsDisplayTableBlendContainer(nsDisplayListBuilder* aBuilder,
                               const nsDisplayTableBlendContainer& aOther)
      : nsDisplayBlendContainer(aBuilder, aOther),
        mAncestorFrame(aOther.mAncestorFrame) {}

  ~nsDisplayTableBlendContainer() override {
    if (mAncestorFrame) {
      mAncestorFrame->RemoveDisplayItem(this);
    }
  }

  nsIFrame* mAncestorFrame;

 private:
  NS_DISPLAY_ALLOW_CLONING()
};

/**
 * nsDisplayOwnLayer constructor flags. If we nest this class inside
 * nsDisplayOwnLayer then we can't forward-declare it up at the top of this
 * file and that makes it hard to use in all the places that we need to use it.
 */
enum class nsDisplayOwnLayerFlags {
  None = 0,
  GenerateSubdocInvalidations = 1 << 0,
  GenerateScrollableLayer = 1 << 1,
};

MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsDisplayOwnLayerFlags)

/**
 * A display item that has no purpose but to ensure its contents get
 * their own layer.
 */
class nsDisplayOwnLayer : public nsDisplayWrapList {
 public:
  enum OwnLayerType {
    OwnLayerForTransformWithRoundedClip,
    OwnLayerForStackingContext,
    OwnLayerForScrollbar,
    OwnLayerForScrollThumb,
    OwnLayerForSubdoc,
    OwnLayerForBoxFrame
  };

  /**
   * @param aFlags eGenerateSubdocInvalidations :
   * Add UserData to the created ContainerLayer, so that invalidations
   * for this layer are send to our nsPresContext.
   * eGenerateScrollableLayer : only valid on nsDisplaySubDocument (and
   * subclasses), indicates this layer is to be a scrollable layer, so call
   * ComputeFrameMetrics, etc.
   * @param aScrollTarget when eVerticalScrollbar or eHorizontalScrollbar
   * is set in the flags, this parameter should be the ViewID of the
   * scrollable content this scrollbar is for.
   */
  nsDisplayOwnLayer(
      nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
      const ActiveScrolledRoot* aActiveScrolledRoot,
      nsDisplayOwnLayerFlags aFlags = nsDisplayOwnLayerFlags::None,
      const layers::ScrollbarData& aScrollbarData = layers::ScrollbarData{},
      bool aForceActive = true, bool aClearClipChain = false);

  nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder,
                    const nsDisplayOwnLayer& aOther)
      : nsDisplayWrapList(aBuilder, aOther),
        mFlags(aOther.mFlags),
        mScrollbarData(aOther.mScrollbarData),
        mForceActive(aOther.mForceActive),
        mWrAnimationId(aOther.mWrAnimationId) {
    MOZ_COUNT_CTOR(nsDisplayOwnLayer);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayOwnLayer)

  NS_DISPLAY_DECL_NAME("OwnLayer", TYPE_OWN_LAYER)

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  bool UpdateScrollData(layers::WebRenderScrollData* aData,
                        layers::WebRenderLayerScrollData* aLayerData) override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
    GetChildren()->Paint(aBuilder, aCtx,
                         mFrame->PresContext()->AppUnitsPerDevPixel());
  }

  bool CanMerge(const nsDisplayItem* aItem) const override {
    // Don't allow merging, each sublist must have its own layer
    return false;
  }

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }

  void WriteDebugInfo(std::stringstream& aStream) override;
  nsDisplayOwnLayerFlags GetFlags() { return mFlags; }
  bool IsScrollThumbLayer() const;
  bool IsScrollbarContainer() const;
  bool IsRootScrollbarContainer() const;
  bool IsScrollbarLayerForRoot() const;
  bool IsZoomingLayer() const;
  bool IsFixedPositionLayer() const;
  bool IsStickyPositionLayer() const;
  bool HasDynamicToolbar() const;
  bool ShouldFixedAndStickyContentGetAnimationIds() const;

  bool CreatesStackingContextHelper() override { return true; }

 protected:
  nsDisplayOwnLayerFlags mFlags;

  /**
   * If this nsDisplayOwnLayer represents a scroll thumb layer or a
   * scrollbar container layer, mScrollbarData stores information
   * about the scrollbar. Otherwise, mScrollbarData will be
   * default-constructed (in particular with mDirection == Nothing())
   * and can be ignored.
   */
  layers::ScrollbarData mScrollbarData;
  bool mForceActive;
  uint64_t mWrAnimationId;
};

/**
 * A display item for subdocuments. This is more or less the same as
 * nsDisplayOwnLayer, except that it always populates the FrameMetrics instance
 * on the ContainerLayer it builds.
 */
class nsDisplaySubDocument : public nsDisplayOwnLayer {
 public:
  nsDisplaySubDocument(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsSubDocumentFrame* aSubDocFrame, nsDisplayList* aList,
                       nsDisplayOwnLayerFlags aFlags);
  ~nsDisplaySubDocument() override;

  NS_DISPLAY_DECL_NAME("SubDocument", TYPE_SUBDOCUMENT)

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;

  virtual nsSubDocumentFrame* SubDocumentFrame() { return mSubDocFrame; }

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return mShouldFlatten;
  }

  void SetShouldFlattenAway(bool aShouldFlatten) {
    mShouldFlatten = aShouldFlatten;
  }

  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;

  nsIFrame* FrameForInvalidation() const override;
  void RemoveFrame(nsIFrame* aFrame) override;

 protected:
  ViewID mScrollParentId;
  bool mForceDispatchToContentRegion{};
  bool mShouldFlatten;
  nsSubDocumentFrame* mSubDocFrame;
};

/**
 * A display item used to represent sticky position elements. The contents
 * gets its own layer and creates a stacking context, and the layer will have
 * position-related metadata set on it.
 */
class nsDisplayStickyPosition : public nsDisplayOwnLayer {
 public:
  nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                          nsDisplayList* aList,
                          const ActiveScrolledRoot* aActiveScrolledRoot,
                          const ActiveScrolledRoot* aContainerASR,
                          bool aClippedToDisplayPort);
  nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder,
                          const nsDisplayStickyPosition& aOther)
      : nsDisplayOwnLayer(aBuilder, aOther),
        mContainerASR(aOther.mContainerASR),
        mClippedToDisplayPort(aOther.mClippedToDisplayPort),
        mShouldFlatten(false) {
    MOZ_COUNT_CTOR(nsDisplayStickyPosition);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayStickyPosition)

  const DisplayItemClip& GetClip() const override {
    return DisplayItemClip::NoClip();
  }
  bool IsClippedToDisplayPort() const { return mClippedToDisplayPort; }

  NS_DISPLAY_DECL_NAME("StickyPosition", TYPE_STICKY_POSITION)
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
    GetChildren()->Paint(aBuilder, aCtx,
                         mFrame->PresContext()->AppUnitsPerDevPixel());
  }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  bool UpdateScrollData(layers::WebRenderScrollData* aData,
                        layers::WebRenderLayerScrollData* aLayerData) override;

  const ActiveScrolledRoot* GetContainerASR() const { return mContainerASR; }

  bool CreatesStackingContextHelper() override { return true; }

  bool CanMoveAsync() override { return true; }

  void SetShouldFlatten(bool aShouldFlatten) {
    mShouldFlatten = aShouldFlatten;
  }

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) final {
    return mShouldFlatten;
  }

 private:
  NS_DISPLAY_ALLOW_CLONING()

  void CalculateLayerScrollRanges(StickyScrollContainer* aStickyScrollContainer,
                                  float aAppUnitsPerDevPixel, float aScaleX,
                                  float aScaleY,
                                  LayerRectAbsolute& aStickyOuter,
                                  LayerRectAbsolute& aStickyInner);

  StickyScrollContainer* GetStickyScrollContainer();

  // This stores the ASR that this sticky container item would have assuming it
  // has no fixed descendants. This may be the same as the ASR returned by
  // GetActiveScrolledRoot(), or it may be a descendant of that.
  RefPtr<const ActiveScrolledRoot> mContainerASR;
  // This flag tracks if this sticky item is just clipped to the enclosing
  // scrollframe's displayport, or if there are additional clips in play. In
  // the former case, we can skip setting the displayport clip as the scrolled-
  // clip of the corresponding layer. This allows sticky items to remain
  // unclipped when the enclosing scrollframe is scrolled past the displayport.
  // i.e. when the rest of the scrollframe checkerboards, the sticky item will
  // not. This makes sense to do because the sticky item has abnormal scrolling
  // behavior and may still be visible even if the rest of the scrollframe is
  // checkerboarded. Note that the sticky item will still be subject to the
  // scrollport clip.
  bool mClippedToDisplayPort;

  // True if this item should be flattened away.
  bool mShouldFlatten;
};

class nsDisplayFixedPosition : public nsDisplayOwnLayer {
 public:
  nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                         nsDisplayList* aList,
                         const ActiveScrolledRoot* aActiveScrolledRoot,
                         const ActiveScrolledRoot* aScrollTargetASR);
  nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder,
                         const nsDisplayFixedPosition& aOther)
      : nsDisplayOwnLayer(aBuilder, aOther),
        mScrollTargetASR(aOther.mScrollTargetASR),
        mIsFixedBackground(aOther.mIsFixedBackground) {
    MOZ_COUNT_CTOR(nsDisplayFixedPosition);
  }

  static nsDisplayFixedPosition* CreateForFixedBackground(
      nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
      nsIFrame* aSecondaryFrame, nsDisplayBackgroundImage* aImage,
      const uint16_t aIndex, const ActiveScrolledRoot* aScrollTargetASR);

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayFixedPosition)

  NS_DISPLAY_DECL_NAME("FixedPosition", TYPE_FIXED_POSITION)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
    GetChildren()->Paint(aBuilder, aCtx,
                         mFrame->PresContext()->AppUnitsPerDevPixel());
  }

  bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) const override {
    return mIsFixedBackground;
  }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  bool UpdateScrollData(layers::WebRenderScrollData* aData,
                        layers::WebRenderLayerScrollData* aLayerData) override;
  void WriteDebugInfo(std::stringstream& aStream) override;

 protected:
  // For background-attachment:fixed
  nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                         nsDisplayList* aList,
                         const ActiveScrolledRoot* aScrollTargetASR);
  ViewID GetScrollTargetId();

  RefPtr<const ActiveScrolledRoot> mScrollTargetASR;
  bool mIsFixedBackground;

 private:
  NS_DISPLAY_ALLOW_CLONING()
};

class nsDisplayTableFixedPosition : public nsDisplayFixedPosition {
 public:
  NS_DISPLAY_DECL_NAME("TableFixedPosition", TYPE_TABLE_FIXED_POSITION)

  nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }

  void RemoveFrame(nsIFrame* aFrame) override {
    if (aFrame == mAncestorFrame) {
      mAncestorFrame = nullptr;
      SetDeletedFrame();
    }
    nsDisplayFixedPosition::RemoveFrame(aFrame);
  }

 protected:
  nsDisplayTableFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                              nsDisplayList* aList, nsIFrame* aAncestorFrame,
                              const ActiveScrolledRoot* aScrollTargetASR);

  nsDisplayTableFixedPosition(nsDisplayListBuilder* aBuilder,
                              const nsDisplayTableFixedPosition& aOther)
      : nsDisplayFixedPosition(aBuilder, aOther),
        mAncestorFrame(aOther.mAncestorFrame) {}

  ~nsDisplayTableFixedPosition() override {
    if (mAncestorFrame) {
      mAncestorFrame->RemoveDisplayItem(this);
    }
  }

  nsIFrame* mAncestorFrame;

 private:
  NS_DISPLAY_ALLOW_CLONING()
};

/**
 * This creates an empty scrollable layer. It has no child layers.
 * It is used to record the existence of a scrollable frame in the layer
 * tree.
 */
class nsDisplayScrollInfoLayer : public nsDisplayWrapList {
 public:
  nsDisplayScrollInfoLayer(nsDisplayListBuilder* aBuilder,
                           nsIFrame* aScrolledFrame, nsIFrame* aScrollFrame,
                           const gfx::CompositorHitTestInfo& aHitInfo,
                           const nsRect& aHitArea);

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayScrollInfoLayer)

  NS_DISPLAY_DECL_NAME("ScrollInfoLayer", TYPE_SCROLL_INFO_LAYER)

  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override {
    *aSnap = false;
    return nsRegion();
  }

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
    return;
  }

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }

  void WriteDebugInfo(std::stringstream& aStream) override;
  UniquePtr<layers::ScrollMetadata> ComputeScrollMetadata(
      nsDisplayListBuilder* aBuilder,
      layers::WebRenderLayerManager* aLayerManager);
  bool UpdateScrollData(layers::WebRenderScrollData* aData,
                        layers::WebRenderLayerScrollData* aLayerData) override;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

 protected:
  nsIFrame* mScrollFrame;
  nsIFrame* mScrolledFrame;
  ViewID mScrollParentId;
  gfx::CompositorHitTestInfo mHitInfo;
  nsRect mHitArea;
};

/**
 * nsDisplayZoom is used for subdocuments that have a different full zoom than
 * their parent documents. This item creates a container layer.
 */
class nsDisplayZoom : public nsDisplaySubDocument {
 public:
  /**
   * @param aFrame is the root frame of the subdocument.
   * @param aList contains the display items for the subdocument.
   * @param aAPD is the app units per dev pixel ratio of the subdocument.
   * @param aParentAPD is the app units per dev pixel ratio of the parent
   * document.
   * @param aFlags eGenerateSubdocInvalidations :
   * Add UserData to the created ContainerLayer, so that invalidations
   * for this layer are send to our nsPresContext.
   */
  nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                nsSubDocumentFrame* aSubDocFrame, nsDisplayList* aList,
                int32_t aAPD, int32_t aParentAPD,
                nsDisplayOwnLayerFlags aFlags = nsDisplayOwnLayerFlags::None);

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayZoom)

  NS_DISPLAY_DECL_NAME("Zoom", TYPE_ZOOM)

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
  // Get the app units per dev pixel ratio of the child document.
  int32_t GetChildAppUnitsPerDevPixel() { return mAPD; }
  // Get the app units per dev pixel ratio of the parent document.
  int32_t GetParentAppUnitsPerDevPixel() { return mParentAPD; }

 private:
  int32_t mAPD, mParentAPD;
};

/**
 * nsDisplayAsyncZoom is used for APZ zooming. It wraps the contents of the
 * root content document's scroll frame, including fixed position content. It
 * does not contain the scroll frame's scrollbars. It is clipped to the scroll
 * frame's scroll port clip. It is not scrolled; only its non-fixed contents
 * are scrolled. This item creates a container layer.
 */
class nsDisplayAsyncZoom : public nsDisplayOwnLayer {
 public:
  nsDisplayAsyncZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList,
                     const ActiveScrolledRoot* aActiveScrolledRoot,
                     layers::FrameMetrics::ViewID aViewID);
  nsDisplayAsyncZoom(nsDisplayListBuilder* aBuilder,
                     const nsDisplayAsyncZoom& aOther)
      : nsDisplayOwnLayer(aBuilder, aOther), mViewID(aOther.mViewID) {
    MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
  }

#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayAsyncZoom();
#endif

  NS_DISPLAY_DECL_NAME("AsyncZoom", TYPE_ASYNC_ZOOM)

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
  bool UpdateScrollData(layers::WebRenderScrollData* aData,
                        layers::WebRenderLayerScrollData* aLayerData) override;

 protected:
  layers::FrameMetrics::ViewID mViewID;
};

/**
 * A base class for different effects types.
 */
class nsDisplayEffectsBase : public nsDisplayWrapList {
 public:
  nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsDisplayList* aList,
                       const ActiveScrolledRoot* aActiveScrolledRoot,
                       bool aClearClipChain = false);
  nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsDisplayList* aList);

  nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
                       const nsDisplayEffectsBase& aOther)
      : nsDisplayWrapList(aBuilder, aOther),
        mEffectsBounds(aOther.mEffectsBounds) {
    MOZ_COUNT_CTOR(nsDisplayEffectsBase);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayEffectsBase)

  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;
  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }

  gfxRect BBoxInUserSpace() const;
  gfxPoint UserSpaceOffset() const;

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override;

 protected:
  bool ValidateSVGFrame();

  // relative to mFrame
  nsRect mEffectsBounds;
};

/**
 * A display item to paint a stacking context with 'mask' and 'clip-path'
 * effects set by the stacking context root frame's style.  The 'mask' and
 * 'clip-path' properties may both contain multiple masks and clip paths,
 * respectively.
 *
 * Note that 'mask' and 'clip-path' may just contain CSS simple-images and CSS
 * basic shapes, respectively.  That is, they don't necessarily reference
 * resources such as SVG 'mask' and 'clipPath' elements.
 */
class nsDisplayMasksAndClipPaths : public nsDisplayEffectsBase {
 public:
  nsDisplayMasksAndClipPaths(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                             nsDisplayList* aList,
                             const ActiveScrolledRoot* aActiveScrolledRoot,
                             bool aWrapsBackdropFilter);
  nsDisplayMasksAndClipPaths(nsDisplayListBuilder* aBuilder,
                             const nsDisplayMasksAndClipPaths& aOther)
      : nsDisplayEffectsBase(aBuilder, aOther),
        mDestRects(aOther.mDestRects.Clone()),
        mWrapsBackdropFilter(aOther.mWrapsBackdropFilter) {
    MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayMasksAndClipPaths)

  NS_DISPLAY_DECL_NAME("Mask", TYPE_MASK)

  bool CanMerge(const nsDisplayItem* aItem) const override;

  void Merge(const nsDisplayItem* aItem) override {
    nsDisplayWrapList::Merge(aItem);

    const nsDisplayMasksAndClipPaths* other =
        static_cast<const nsDisplayMasksAndClipPaths*>(aItem);
    mEffectsBounds.UnionRect(
        mEffectsBounds,
        other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame));
  }

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplayMasksAndClipPathsGeometry(this, aBuilder);
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override;
#ifdef MOZ_DUMP_PAINTING
  void PrintEffects(nsACString& aTo);
#endif

  bool IsValidMask();

  void PaintWithContentsPaintCallback(
      nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
      const std::function<void()>& aPaintChildren);

  /*
   * Paint mask onto aMaskContext in mFrame's coordinate space and
   * return whether the mask layer was painted successfully.
   */
  bool PaintMask(nsDisplayListBuilder* aBuilder, gfxContext* aMaskContext,
                 bool aHandleOpacity, bool* aMaskPainted = nullptr);

  const nsTArray<nsRect>& GetDestRects() { return mDestRects; }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  Maybe<nsRect> GetClipWithRespectToASR(
      nsDisplayListBuilder* aBuilder,
      const ActiveScrolledRoot* aASR) const override;

  bool CreatesStackingContextHelper() override { return true; }

 private:
  NS_DISPLAY_ALLOW_CLONING()

  nsTArray<nsRect> mDestRects;
  bool mWrapsBackdropFilter;
};

class nsDisplayBackdropFilters : public nsDisplayWrapList {
 public:
  nsDisplayBackdropFilters(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                           nsDisplayList* aList, const nsRect& aBackdropRect,
                           nsIFrame* aStyleFrame)
      : nsDisplayWrapList(aBuilder, aFrame, aList),
        mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
        mBackdropRect(aBackdropRect) {
    MOZ_COUNT_CTOR(nsDisplayBackdropFilters);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBackdropFilters)

  NS_DISPLAY_DECL_NAME("BackdropFilter", TYPE_BACKDROP_FILTER)

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return !aBuilder->IsPaintingForWebRender();
  }

  bool CreatesStackingContextHelper() override { return true; }

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;

 private:
  RefPtr<ComputedStyle> mStyle;
  nsRect mBackdropRect;
};

/**
 * A display item to paint a stacking context with filter effects set by the
 * stacking context root frame's style.
 *
 * Note that the filters may just be simple CSS filter functions.  That is,
 * they won't necessarily be references to SVG 'filter' elements.
 */
class nsDisplayFilters : public nsDisplayEffectsBase {
 public:
  nsDisplayFilters(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                   nsDisplayList* aList, nsIFrame* aStyleFrame,
                   bool aWrapsBackdropFilter);

  nsDisplayFilters(nsDisplayListBuilder* aBuilder,
                   const nsDisplayFilters& aOther)
      : nsDisplayEffectsBase(aBuilder, aOther),
        mStyle(aOther.mStyle),
        mEffectsBounds(aOther.mEffectsBounds),
        mWrapsBackdropFilter(aOther.mWrapsBackdropFilter) {
    MOZ_COUNT_CTOR(nsDisplayFilters);
  }

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayFilters)

  NS_DISPLAY_DECL_NAME("Filter", TYPE_FILTER)

  bool CanMerge(const nsDisplayItem* aItem) const override {
    // Items for the same content element should be merged into a single
    // compositing group.
    return HasDifferentFrame(aItem) && HasSameTypeAndClip(aItem) &&
           HasSameContent(aItem);
  }

  void Merge(const nsDisplayItem* aItem) override {
    nsDisplayWrapList::Merge(aItem);

    const nsDisplayFilters* other = static_cast<const nsDisplayFilters*>(aItem);
    mEffectsBounds.UnionRect(
        mEffectsBounds,
        other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame));
  }

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override {
    *aSnap = false;
    return mEffectsBounds + ToReferenceFrame();
  }

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplaySVGEffectGeometry(this, aBuilder);
  }

#ifdef MOZ_DUMP_PAINTING
  void PrintEffects(nsACString& aTo);
#endif

  void PaintWithContentsPaintCallback(
      nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
      const std::function<void(gfxContext* aContext)>& aPaintChildren);

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  bool CanCreateWebRenderCommands() const;

  bool CanApplyOpacity(WebRenderLayerManager* aManager,
                       nsDisplayListBuilder* aBuilder) const override {
    return CanCreateWebRenderCommands();
  }

  bool CreatesStackingContextHelper() override { return true; }

 private:
  NS_DISPLAY_ALLOW_CLONING()

  RefPtr<ComputedStyle> mStyle;
  // relative to mFrame
  nsRect mEffectsBounds;
  nsRect mVisibleRect;
  bool mWrapsBackdropFilter;
};

/* A display item that applies a transformation to all of its descendant
 * elements.  This wrapper should only be used if there is a transform applied
 * to the root element.
 *
 * The reason that a "bounds" rect is involved in transform calculations is
 * because CSS-transforms allow percentage values for the x and y components
 * of <translation-value>s, where percentages are percentages of the element's
 * border box.
 *
 * INVARIANT: The wrapped frame is transformed or we supplied a transform getter
 * function.
 * INVARIANT: The wrapped frame is non-null.
 */
class nsDisplayTransform : public nsPaintedDisplayItem {
  using Matrix4x4 = gfx::Matrix4x4;
  using Matrix4x4Flagged = gfx::Matrix4x4Flagged;
  using TransformReferenceBox = nsStyleTransformMatrix::TransformReferenceBox;

 public:
  enum class PrerenderDecision : uint8_t { No, Full, Partial };

  enum {
    WithTransformGetter,
  };

  /* Constructor accepts a display list, empties it, and wraps it up.  It also
   * ferries the underlying frame to the nsDisplayItem constructor.
   */
  nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList, const nsRect& aChildrenBuildingRect);

  nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList, const nsRect& aChildrenBuildingRect,
                     PrerenderDecision aPrerenderDecision);

  nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList, const nsRect& aChildrenBuildingRect,
                     decltype(WithTransformGetter));

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayTransform)

  NS_DISPLAY_DECL_NAME("nsDisplayTransform", TYPE_TRANSFORM)

  void UpdateBounds(nsDisplayListBuilder* aBuilder) override;

  /**
   * This function updates bounds for items with a frame establishing
   * 3D rendering context.
   */
  void UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder);

  void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override;

  void Destroy(nsDisplayListBuilder* aBuilder) override {
    GetChildren()->DeleteAll(aBuilder);
    nsPaintedDisplayItem::Destroy(aBuilder);
  }

  nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override;

  RetainedDisplayList* GetChildren() const override { return &mChildren; }

  nsRect GetUntransformedBounds(nsDisplayListBuilder* aBuilder,
                                bool* aSnap) const override {
    *aSnap = false;
    return mChildBounds;
  }

  const nsRect& GetUntransformedPaintRect() const override {
    return mChildrenBuildingRect;
  }

  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
             const Maybe<gfx::Polygon>& aPolygon);
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
  bool UpdateScrollData(layers::WebRenderScrollData* aData,
                        layers::WebRenderLayerScrollData* aLayerData) override;

  nsDisplayItemGeometry* AllocateGeometry(
      nsDisplayListBuilder* aBuilder) override {
    return new nsDisplayTransformGeometry(
        this, aBuilder, GetTransformForRendering(),
        mFrame->PresContext()->AppUnitsPerDevPixel());
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override {
    const nsDisplayTransformGeometry* geometry =
        static_cast<const nsDisplayTransformGeometry*>(aGeometry);

    // This code is only called for flattened, inactive transform items.
    // Only check if the transform has changed. The bounds invalidation should
    // be handled by the children themselves.
    if (!geometry->mTransform.FuzzyEqual(GetTransformForRendering())) {
      bool snap;
      aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
    }
  }

  const nsIFrame* ReferenceFrameForChildren() const override {
    // If we were created using a transform-getter, then we don't
    // belong to a transformed frame, and aren't a reference frame
    // for our children.
    if (!mHasTransformGetter) {
      return mFrame;
    }
    return nsPaintedDisplayItem::ReferenceFrameForChildren();
  }

  const nsRect& GetBuildingRectForChildren() const override {
    return mChildrenBuildingRect;
  }

  enum { INDEX_MAX = UINT32_MAX >> TYPE_BITS };

  /**
   * We include the perspective matrix from our containing block for the
   * purposes of visibility calculations, but we exclude it from the transform
   * we set on the layer (for rendering), since there will be an
   * nsDisplayPerspective created for that.
   */
  const Matrix4x4Flagged& GetTransform() const;
  const Matrix4x4Flagged& GetInverseTransform() const;

  bool ShouldSkipTransform(nsDisplayListBuilder* aBuilder) const;
  Matrix4x4 GetTransformForRendering(
      LayoutDevicePoint* aOutOrigin = nullptr) const;

  /**
   * Return the transform that is aggregation of all transform on the
   * preserves3d chain.
   */
  const Matrix4x4& GetAccumulatedPreserved3DTransform(
      nsDisplayListBuilder* aBuilder);

  float GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
                           const nsPoint& aPoint);
  /**
   * TransformRect takes in as parameters a rectangle (in aFrame's coordinate
   * space) and returns the smallest rectangle (in aFrame's coordinate space)
   * containing the transformed image of that rectangle.  That is, it takes
   * the four corners of the rectangle, transforms them according to the
   * matrix associated with the specified frame, then returns the smallest
   * rectangle containing the four transformed points.
   *
   * @param untransformedBounds The rectangle (in app units) to transform.
   * @param aFrame The frame whose transformation should be applied.  This
   *        function raises an assertion if aFrame is null or doesn't have a
   *        transform applied to it.
   * @param aRefBox the reference box to use, which would usually be just
   *        TransformReferemceBox(aFrame), but callers may override it if
   *        needed.
   */
  static nsRect TransformRect(const nsRect& aUntransformedBounds,
                              const nsIFrame* aFrame,
                              TransformReferenceBox& aRefBox);

  /* UntransformRect is like TransformRect, except that it inverts the
   * transform.
   */
  static bool UntransformRect(const nsRect& aTransformedBounds,
                              const nsRect& aChildBounds,
                              const nsIFrame* aFrame, nsRect* aOutRect);
  static bool UntransformRect(const nsRect& aTransformedBounds,
                              const nsRect& aChildBounds,
                              const Matrix4x4& aMatrix, float aAppUnitsPerPixel,
                              nsRect* aOutRect);

  bool UntransformRect(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                       nsRect* aOutRect) const;

  bool UntransformBuildingRect(nsDisplayListBuilder* aBuilder,
                               nsRect* aOutRect) const {
    return UntransformRect(aBuilder, GetBuildingRect(), aOutRect);
  }

  static gfx::Point3D GetDeltaToTransformOrigin(const nsIFrame* aFrame,
                                                TransformReferenceBox&,
                                                float aAppUnitsPerPixel);

  /*
   * Returns true if aFrame has perspective applied from its containing
   * block.
   * Returns the matrix to append to apply the persective (taking
   * perspective-origin into account), relative to aFrames coordinate
   * space).
   * aOutMatrix is assumed to be the identity matrix, and isn't explicitly
   * cleared.
   */
  static bool ComputePerspectiveMatrix(const nsIFrame* aFrame,
                                       float aAppUnitsPerPixel,
                                       Matrix4x4& aOutMatrix);

  struct MOZ_STACK_CLASS FrameTransformProperties {
    FrameTransformProperties(const nsIFrame* aFrame,
                             TransformReferenceBox& aRefBox,
                             float aAppUnitsPerPixel);
    FrameTransformProperties(const StyleTranslate& aTranslate,
                             const StyleRotate& aRotate,
                             const StyleScale& aScale,
                             const StyleTransform& aTransform,
                             const Maybe<ResolvedMotionPathData>& aMotion,
                             const gfx::Point3D& aToTransformOrigin)
        : mFrame(nullptr),
          mTranslate(aTranslate),
          mRotate(aRotate),
          mScale(aScale),
          mTransform(aTransform),
          mMotion(aMotion),
          mToTransformOrigin(aToTransformOrigin) {}

    bool HasTransform() const {
      return !mTranslate.IsNone() || !mRotate.IsNone() || !mScale.IsNone() ||
             !mTransform.IsNone() || mMotion.isSome();
    }

    const nsIFrame* mFrame;
    const StyleTranslate& mTranslate;
    const StyleRotate& mRotate;
    const StyleScale& mScale;
    const StyleTransform& mTransform;
    const Maybe<ResolvedMotionPathData> mMotion;
    const gfx::Point3D mToTransformOrigin;
  };

  /**
   * Given a frame with the transform property or an SVG transform,
   * returns the transformation matrix for that frame.
   *
   * @param aFrame The frame to get the matrix from.
   * @param aOrigin Relative to which point this transform should be applied.
   * @param aAppUnitsPerPixel The number of app units per graphics unit.
   * @param aBoundsOverride [optional] If this is nullptr (the default), the
   *        computation will use the value of TransformReferenceBox(aFrame).
   *        Otherwise, it will use the value of aBoundsOverride.  This is
   *        mostly for internal use and in most cases you will not need to
   *        specify a value.
   * @param aFlags OFFSET_BY_ORIGIN The resulting matrix will be translated
   *        by aOrigin. This translation is applied *before* the CSS transform.
   * @param aFlags INCLUDE_PRESERVE3D_ANCESTORS The computed transform will
   *        include the transform of any ancestors participating in the same
   *        3d rendering context.
   * @param aFlags INCLUDE_PERSPECTIVE The resulting matrix will include the
   *        perspective transform from the containing block if applicable.
   */
  enum {
    OFFSET_BY_ORIGIN = 1 << 0,
    INCLUDE_PRESERVE3D_ANCESTORS = 1 << 1,
    INCLUDE_PERSPECTIVE = 1 << 2,
  };
  static constexpr uint32_t kTransformRectFlags =
      INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN | INCLUDE_PRESERVE3D_ANCESTORS;
  static Matrix4x4 GetResultingTransformMatrix(const nsIFrame* aFrame,
                                               const nsPoint& aOrigin,
                                               float aAppUnitsPerPixel,
                                               uint32_t aFlags);
  static Matrix4x4 GetResultingTransformMatrix(
      const FrameTransformProperties& aProperties, TransformReferenceBox&,
      float aAppUnitsPerPixel);

  struct PrerenderInfo {
    bool CanUseAsyncAnimations() const {
      return mDecision != PrerenderDecision::No && mHasAnimations;
    }
    PrerenderDecision mDecision = PrerenderDecision::No;
    bool mHasAnimations = true;
  };
  /**
   * Decide whether we should prerender some or all of the contents of the
   * transformed frame even when it's not completely visible (yet).
   * Return PrerenderDecision::Full if the entire contents should be
   * prerendered, PrerenderDecision::Partial if some but not all of the
   * contents should be prerendered, or PrerenderDecision::No if only the
   * visible area should be rendered.
   * |mNoAffectDecisionInPreserve3D| is set if the prerender decision should not
   * affect the decision on other frames in the preserve 3d tree.
   * |aDirtyRect| is updated to the area that should be prerendered.
   */
  static PrerenderInfo ShouldPrerenderTransformedContent(
      nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect);

  bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;

  bool MayBeAnimated(nsDisplayListBuilder* aBuilder) const;

  void WriteDebugInfo(std::stringstream& aStream) override;

  bool CanMoveAsync() override {
    return EffectCompositor::HasAnimationsForCompositor(
        mFrame, DisplayItemType::TYPE_TRANSFORM);
  }

  /**
   * This item is an additional item as the boundary between parent
   * and child 3D rendering context.
   * \see nsIFrame::BuildDisplayListForStackingContext().
   */
  bool IsTransformSeparator() const { return mIsTransformSeparator; }
  /**
   * This item is the boundary between parent and child 3D rendering
   * context.
   */
  bool IsLeafOf3DContext() const {
    return (IsTransformSeparator() ||
            (!mFrame->Extend3DContext() && Combines3DTransformWithAncestors()));
  }
  /**
   * The backing frame of this item participates a 3D rendering
   * context.
   */
  bool IsParticipating3DContext() const {
    return mFrame->Extend3DContext() || Combines3DTransformWithAncestors();
  }

  bool IsPartialPrerender() const {
    return mPrerenderDecision == PrerenderDecision::Partial;
  }

  /**
   * Mark this item as created together with `nsDisplayPerspective`.
   * \see nsIFrame::BuildDisplayListForStackingContext().
   */
  void MarkWithAssociatedPerspective() { mHasAssociatedPerspective = true; }

  void AddSizeOfExcludingThis(nsWindowSizes&) const override;

  bool CreatesStackingContextHelper() override { return true; }

 private:
  void ComputeBounds(nsDisplayListBuilder* aBuilder);
  nsRect TransformUntransformedBounds(nsDisplayListBuilder* aBuilder,
                                      const Matrix4x4Flagged& aMatrix) const;
  void UpdateUntransformedBounds(nsDisplayListBuilder* aBuilder);

  void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
  void Init(nsDisplayListBuilder* aBuilder, nsDisplayList* aChildren);

  static Matrix4x4 GetResultingTransformMatrixInternal(
      const FrameTransformProperties& aProperties,
      TransformReferenceBox& aRefBox, const nsPoint& aOrigin,
      float aAppUnitsPerPixel, uint32_t aFlags);

  void Collect3DTransformLeaves(nsDisplayListBuilder* aBuilder,
                                nsTArray<nsDisplayTransform*>& aLeaves);
  using TransformPolygon = layers::BSPPolygon<nsDisplayTransform>;
  void CollectSorted3DTransformLeaves(nsDisplayListBuilder* aBuilder,
                                      nsTArray<TransformPolygon>& aLeaves);

  mutable RetainedDisplayList mChildren;
  mutable Maybe<Matrix4x4Flagged> mTransform;
  mutable Maybe<Matrix4x4Flagged> mInverseTransform;
  // Accumulated transform of ancestors on the preserves-3d chain.
  UniquePtr<Matrix4x4> mTransformPreserves3D;
  nsRect mChildrenBuildingRect;

  // The untransformed bounds of |mChildren|.
  nsRect mChildBounds;
  // The transformed bounds of this display item.
  nsRect mBounds;
  PrerenderDecision mPrerenderDecision : 8;
  // This item is a separator between 3D rendering contexts, and
  // mTransform have been presetted by the constructor.
  // This also forces us not to extend the 3D context.  Since we don't create a
  // transform item, a container layer, for every frame in a preserves3d
  // context, the transform items of a child preserves3d context may extend the
  // parent context unintendedly if the root of the child preserves3d context
  // doesn't create a transform item.
  bool mIsTransformSeparator : 1;
  // True if we have a transform getter.
  bool mHasTransformGetter : 1;
  // True if this item is created together with `nsDisplayPerspective`
  // from the same CSS stacking context.
  bool mHasAssociatedPerspective : 1;
};

/* A display item that applies a perspective transformation to a single
 * nsDisplayTransform child item. We keep this as a separate item since the
 * perspective-origin is relative to an ancestor of the transformed frame, and
 * APZ can scroll the child separately.
 */
class nsDisplayPerspective : public nsPaintedDisplayItem {
 public:
  nsDisplayPerspective(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsDisplayList* aList);
  ~nsDisplayPerspective() override = default;

  NS_DISPLAY_DECL_NAME("nsDisplayPerspective", TYPE_PERSPECTIVE)

  void Destroy(nsDisplayListBuilder* aBuilder) override {
    mList.DeleteAll(aBuilder);
    nsPaintedDisplayItem::Destroy(aBuilder);
  }

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override {
    return GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
  }

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override {
    *aSnap = false;
    return GetChildren()->GetClippedBoundsWithRespectToASR(aBuilder,
                                                           mActiveScrolledRoot);
  }

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const override {}

  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) const override;

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

  RetainedDisplayList* GetSameCoordinateSystemChildren() const override {
    return &mList;
  }

  RetainedDisplayList* GetChildren() const override { return &mList; }

  nsRect GetComponentAlphaBounds(
      nsDisplayListBuilder* aBuilder) const override {
    return GetChildren()->GetComponentAlphaBounds(aBuilder);
  }

  void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
    if (GetChildren()->GetTop()) {
      static_cast<nsDisplayTransform*>(GetChildren()->GetTop())
          ->DoUpdateBoundsPreserves3D(aBuilder);
    }
  }

  bool CreatesStackingContextHelper() override { return true; }

 private:
  mutable RetainedDisplayList mList;
};

class nsDisplayTextGeometry;

/**
 * This class adds basic support for limiting the rendering (in the inline axis
 * of the writing mode) to the part inside the specified edges.
 * The two members, mVisIStartEdge and mVisIEndEdge, are relative to the edges
 * of the frame's scrollable overflow rectangle and are the amount to suppress
 * on each side.
 *
 * Setting none, both or only one edge is allowed.
 * The values must be non-negative.
 * The default value for both edges is zero, which means everything is painted.
 */
class nsDisplayText final : public nsPaintedDisplayItem {
 public:
  nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame);
  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayText)

  NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT)

  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const final {
    *aSnap = false;
    return mBounds;
  }

  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
               HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) final {
    if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) {
      aOutFrames->AppendElement(mFrame);
    }
  }

  bool CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                               wr::IpcResourceUpdateQueue& aResources,
                               const StackingContextHelper& aSc,
                               layers::RenderRootStateManager* aManager,
                               nsDisplayListBuilder* aDisplayListBuilder) final;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) final;

  nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const final {
    if (gfxPlatform::GetPlatform()->RespectsFontStyleSmoothing()) {
      // On OS X, web authors can turn off subpixel text rendering using the
      // CSS property -moz-osx-font-smoothing. If they do that, we don't need
      // to use component alpha layers for the affected text.
      if (mFrame->StyleFont()->mFont.smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
        return nsRect();
      }
    }
    bool snap;
    return GetBounds(aBuilder, &snap);
  }

  nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) final;

  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayItemGeometry* aGeometry,
                                 nsRegion* aInvalidRegion) const final;

  void RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder,
                       const nsRect& aVisibleRect, float aOpacity = 1.0f,
                       bool aIsRecording = false);

  bool CanApplyOpacity(WebRenderLayerManager* aManager,
                       nsDisplayListBuilder* aBuilder) const final;

  void WriteDebugInfo(std::stringstream& aStream) final;

  static nsDisplayText* CheckCast(nsDisplayItem* aItem) {
    return (aItem->GetType() == DisplayItemType::TYPE_TEXT)
               ? static_cast<nsDisplayText*>(aItem)
               : nullptr;
  }

  nscoord& VisIStartEdge() { return mVisIStartEdge; }
  nscoord& VisIEndEdge() { return mVisIEndEdge; }

 private:
  nsRect mBounds;
  nsRect mVisibleRect;

  // Lengths measured from the visual inline start and end sides
  // (i.e. left and right respectively in horizontal writing modes,
  // regardless of bidi directionality; top and bottom in vertical modes).
  nscoord mVisIStartEdge;
  nscoord mVisIEndEdge;
};

/**
 * A display item that for webrender to handle SVG
 */
class nsDisplaySVGWrapper : public nsDisplayWrapList {
 public:
  nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      nsDisplayList* aList);

  MOZ_COUNTED_DTOR_OVERRIDE(nsDisplaySVGWrapper)

  NS_DISPLAY_DECL_NAME("SVGWrapper", TYPE_SVG_WRAPPER)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
    GetChildren()->Paint(aBuilder, aCtx,
                         mFrame->PresContext()->AppUnitsPerDevPixel());
  }
  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
};

/**
 * A display item for webrender to handle SVG foreign object
 */
class nsDisplayForeignObject : public nsDisplayWrapList {
 public:
  nsDisplayForeignObject(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                         nsDisplayList* aList);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayForeignObject();
#endif

  NS_DISPLAY_DECL_NAME("ForeignObject", TYPE_FOREIGN_OBJECT)

  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
    GetChildren()->Paint(aBuilder, aCtx,
                         mFrame->PresContext()->AppUnitsPerDevPixel());
  }

  bool CreateWebRenderCommands(
      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
      const StackingContextHelper& aSc,
      layers::RenderRootStateManager* aManager,
      nsDisplayListBuilder* aDisplayListBuilder) override;
};

/**
 * A display item to represent a hyperlink.
 */
class nsDisplayLink : public nsPaintedDisplayItem {
 public:
  nsDisplayLink(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                const char* aLinkSpec, const nsRect& aRect)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mLinkSpec(aLinkSpec),
        mRect(aRect) {}

  NS_DISPLAY_DECL_NAME("Link", TYPE_LINK)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

 private:
  nsCString mLinkSpec;
  nsRect mRect;
};

/**
 * A display item to represent a destination within the document.
 */
class nsDisplayDestination : public nsPaintedDisplayItem {
 public:
  nsDisplayDestination(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       const char* aDestinationName, const nsPoint& aPosition)
      : nsPaintedDisplayItem(aBuilder, aFrame),
        mDestinationName(aDestinationName),
        mPosition(aPosition) {}

  NS_DISPLAY_DECL_NAME("Destination", TYPE_DESTINATION)

  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;

 private:
  nsCString mDestinationName;
  nsPoint mPosition;
};

class MOZ_STACK_CLASS FlattenedDisplayListIterator {
 public:
  FlattenedDisplayListIterator(nsDisplayListBuilder* aBuilder,
                               nsDisplayList* aList)
      : mBuilder(aBuilder), mStart(aList->begin()), mEnd(aList->end()) {
    ResolveFlattening();
  }

  bool HasNext() const { return !AtEndOfCurrentList(); }

  nsDisplayItem* GetNextItem() {
    MOZ_ASSERT(HasNext());

    nsDisplayItem* current = NextItem();
    Advance();

    if (!AtEndOfCurrentList() && current->CanMerge(NextItem())) {
      // Since we can merge at least two display items, create an array and
      // collect mergeable display items there.
      AutoTArray<nsDisplayItem*, 2> willMerge{current};

      auto it = mStart;
      while (it != mEnd) {
        nsDisplayItem* next = *it;
        if (current->CanMerge(next)) {
          willMerge.AppendElement(next);
          ++it;
        } else {
          break;
        }
      }
      mStart = it;

      current = mBuilder->MergeItems(willMerge);
    }

    ResolveFlattening();
    return current;
  }

 protected:
  void Advance() { ++mStart; }

  bool AtEndOfNestedList() const {
    return AtEndOfCurrentList() && mStack.Length() > 0;
  }

  bool AtEndOfCurrentList() const { return mStart == mEnd; }

  nsDisplayItem* NextItem() {
    MOZ_ASSERT(HasNext());
    return *mStart;
  }

  bool ShouldFlattenNextItem() {
    return HasNext() && NextItem()->ShouldFlattenAway(mBuilder);
  }

  void ResolveFlattening() {
    // Handle the case where we reach the end of a nested list, or the current
    // item should start a new nested list. Repeat this until we find an actual
    // item, or the very end of the outer list.
    while (AtEndOfNestedList() || ShouldFlattenNextItem()) {
      if (AtEndOfNestedList()) {
        // We reached the end of the list, pop the next list from the stack.
        std::tie(mStart, mEnd) = mStack.PopLastElement();
      } else {
        // The next item wants to be flattened. This means that we will skip the
        // flattened item and directly iterate over its sublist.
        MOZ_ASSERT(ShouldFlattenNextItem());

        nsDisplayList* sublist = NextItem()->GetChildren();
        MOZ_ASSERT(sublist);

        // Skip the flattened item.
        Advance();

        // Store the current position on the stack.
        if (!AtEndOfCurrentList()) {
          mStack.AppendElement(std::make_pair(mStart, mEnd));
        }

        // Iterate over the sublist.
        mStart = sublist->begin();
        mEnd = sublist->end();
      }
    }
  }

 private:
  nsDisplayListBuilder* mBuilder;
  nsDisplayList::iterator mStart;
  nsDisplayList::iterator mEnd;
  AutoTArray<std::pair<nsDisplayList::iterator, nsDisplayList::iterator>, 3>
      mStack;
};

class PaintTelemetry {
 public:
  class AutoRecordPaint {
   public:
    AutoRecordPaint();
    ~AutoRecordPaint();

   private:
    TimeStamp mStart;
  };

 private:
  static uint32_t sPaintLevel;
};

}  // namespace mozilla

#endif /*NSDISPLAYLIST_H_*/
