/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=2 sw=2 et tw=78:
 * 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 "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EnumSet.h"
#include "mozilla/Maybe.h"
#include "nsCOMPtr.h"
#include "nsContainerFrame.h"
#include "nsPoint.h"
#include "nsRect.h"
#include "plarena.h"
#include "nsRegion.h"
#include "nsDisplayListInvalidation.h"
#include "nsRenderingContext.h"
#include "DisplayListClipState.h"
#include "LayerState.h"
#include "FrameMetrics.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/gfx/UserData.h"

#include <stdint.h>
#include "nsTHashtable.h"

#include <stdlib.h>
#include <algorithm>

class nsIContent;
class nsRenderingContext;
class nsDisplayList;
class nsDisplayTableItem;
class nsISelection;
class nsIScrollableFrame;
class nsDisplayLayerEventRegions;
class nsDisplayScrollInfoLayer;
class nsCaret;

namespace mozilla {
class FrameLayerBuilder;
class DisplayItemScrollClip;
namespace layers {
class Layer;
class ImageLayer;
class ImageContainer;
} // namespace layers
} // namespace mozilla

// A set of blend modes, that never includes OP_OVER (since it's
// considered the default, rather than a specific blend mode).
typedef mozilla::EnumSet<mozilla::gfx::CompositionOp> BlendModeSet;

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

// All types are defined in nsDisplayItemTypes.h
#define NS_DISPLAY_DECL_NAME(n, e) \
  virtual const char* Name() override { return n; } \
  virtual Type GetType() override { return e; }


/**
 * Represents a frame that is considered to have (or will have) "animated geometry"
 * for itself and descendant frames.
 *
 * For example the scrolled frames of scrollframes which are actively being scrolled
 * fall into this category. Frames with certain CSS properties that are being animated
 * (e.g. 'left'/'top' etc) are also placed in this category. Frames with different
 * active geometry roots are in different PaintedLayers, so that we can animate the
 * geometry root by changing its transform (either on the main thread or in the
 * compositor).
 *
 * nsDisplayListBuilder constructs a tree of these (for fast traversals) and assigns
 * one for each display item.
 *
 * The animated geometry root for a display item is required to be a descendant (or
 * equal to) the item's ReferenceFrame(), which means that we will fall back to
 * returning aItem->ReferenceFrame() when we can't find another animated geometry root.
 *
 * The animated geometry root isn't strongly defined for a frame as transforms and
 * background-attachment:fixed can cause it to vary between display items for a given
 * frame.
 */
struct AnimatedGeometryRoot
{
  AnimatedGeometryRoot(nsIFrame* aFrame, AnimatedGeometryRoot* aParent)
    : mFrame(aFrame)
    , mParentAGR(aParent)
  {}

  operator nsIFrame*() { return mFrame; }

  nsIFrame* operator ->() const { return mFrame; }

  void* operator new(size_t aSize,
                     nsDisplayListBuilder* aBuilder);

  nsIFrame* mFrame;
  AnimatedGeometryRoot* mParentAGR;
};

enum class nsDisplayListBuilderMode : uint8_t {
  PAINTING,
  EVENT_DELIVERY,
  PLUGIN_GEOMETRY,
  FRAME_VISIBILITY,
  TRANSFORM_COMPUTATION,
  GENERATE_GLYPH,
  PAINTING_SELECTION_BACKGROUND
};

/**
 * 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 a PLArena. 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 {
  typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
  typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion;

  /**
   * 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.
   *
   * nsFrame::BuildDisplayListForStackingContext() uses
   * AutoPreserves3DContext to install an instance on the builder.
   *
   * \see AutoAccumulateTransform, AutoAccumulateRect,
   *      AutoPreserves3DContext, Accumulate, GetCurrentTransform,
   *      StartRoot.
   */
  class Preserves3DContext {
  public:
    typedef mozilla::gfx::Matrix4x4 Matrix4x4;

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

    // Accmulate transforms of ancestors on the preserves-3d chain.
    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 mDirtyRect;
  };

public:
  typedef mozilla::FrameLayerBuilder FrameLayerBuilder;
  typedef mozilla::DisplayItemClip DisplayItemClip;
  typedef mozilla::DisplayListClipState DisplayListClipState;
  typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip;
  typedef nsIWidget::ThemeGeometry ThemeGeometry;
  typedef mozilla::layers::Layer Layer;
  typedef mozilla::layers::FrameMetrics FrameMetrics;
  typedef mozilla::layers::FrameMetrics::ViewID ViewID;
  typedef mozilla::gfx::Matrix4x4 Matrix4x4;

  /**
   * @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);
  ~nsDisplayListBuilder();

  void SetWillComputePluginGeometry(bool aWillComputePluginGeometry)
  {
    mWillComputePluginGeometry = aWillComputePluginGeometry;
  }
  void SetForPluginGeometry()
  {
    NS_ASSERTION(mMode == nsDisplayListBuilderMode::PAINTING, "Can only switch from PAINTING to PLUGIN_GEOMETRY");
    NS_ASSERTION(mWillComputePluginGeometry, "Should have signalled this in advance");
    mMode = nsDisplayListBuilderMode::PLUGIN_GEOMETRY;
  }

  mozilla::layers::LayerManager* 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()
  {
    return mMode == nsDisplayListBuilderMode::EVENT_DELIVERY;
  }

  /**
   * Be careful with this. The display list will be built in PAINTING mode
   * first and then switched to PLUGIN_GEOMETRY before a second call to
   * ComputeVisibility.
   * @return true if the display list is being built to compute geometry
   * for plugins.
   */
  bool IsForPluginGeometry()
  {
    return mMode == nsDisplayListBuilderMode::PLUGIN_GEOMETRY;
  }

  /**
   * @return true if the display list is being built for painting.
   */
  bool IsForPainting()
  {
    return mMode == nsDisplayListBuilderMode::PAINTING;
  }

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

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

  /**
   * @return true if the display list is being built for painting selection
   * background.
   */
  bool IsForPaintingSelectionBG()
  {
    return mMode == nsDisplayListBuilderMode::PAINTING_SELECTION_BACKGROUND;
  }

  bool WillComputePluginGeometry() { return mWillComputePluginGeometry; }
  /**
   * @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 true if the currently active BuildDisplayList call is being
   * applied to a frame at the root of a pseudo stacking context. A pseudo
   * stacking context is either a real stacking context or basically what
   * CSS2.1 appendix E refers to with "treat the element as if it created
   * a new stacking context
   */
  bool IsAtRootOfPseudoStackingContext() { return mIsAtRootOfPseudoStackingContext; }

  /**
   * @return the selection that painting should be restricted to (or nullptr
   * in the normal unrestricted case)
   */
  nsISelection* GetBoundingSelection() { return mBoundingSelection; }

  /**
   * @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);

  /**
   * @return the root of the display list's frame (sub)tree, whose origin
   * establishes the coordinate system for the display list
   */
  nsIFrame* RootReferenceFrame() 
  {
    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) {
    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; }
  /**
   * 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; }
  /**
   * Get the ViewID and the scrollbar flags corresponding to the scrollbar for
   * which we are building display items at the moment.
   */
  void GetScrollbarInfo(ViewID* aOutScrollbarTarget, uint32_t* aOutScrollbarFlags)
  {
    *aOutScrollbarTarget = mCurrentScrollbarTarget;
    *aOutScrollbarFlags = mCurrentScrollbarFlags;
  }
  /**
   * 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; }
  /**
   * Calling this setter makes us compute accurate visible regions at the cost
   * of performance if regions get very complex.
   */
  void SetAccurateVisibleRegions() { mAccurateVisibleRegions = true; }
  bool GetAccurateVisibleRegions() { return mAccurateVisibleRegions; }
  /**
   * @return Returns true if we should include the caret in any display lists
   * that we make.
   */
  bool IsBuildingCaret() { return mBuildCaret; }
  /**
   * 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 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& GetDirtyRect() { return mDirtyRect; }
  const nsIFrame* GetCurrentFrame() { return mCurrentFrame; }
  const nsIFrame* GetCurrentReferenceFrame() { return mCurrentReferenceFrame; }
  const nsPoint& GetCurrentFrameOffsetToReferenceFrame() { return mCurrentOffsetToReferenceFrame; }
  AnimatedGeometryRoot* GetCurrentAnimatedGeometryRoot() {
    return mCurrentAGR;
  }
  AnimatedGeometryRoot* GetRootAnimatedGeometryRoot() {
    return &mRootAGR;
  }

  void RecomputeCurrentAnimatedGeometryRoot();

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

  nsDisplayLayerEventRegions* GetLayerEventRegions() { return mLayerEventRegions; }
  void SetLayerEventRegions(nsDisplayLayerEventRegions* aItem)
  {
    mLayerEventRegions = aItem;
  }
  bool IsBuildingLayerEventRegions();
  static bool LayerEventRegionsEnabled();
  bool IsInsidePointerEventsNoneDoc()
  {
    return CurrentPresShellState()->mInsidePointerEventsNoneDoc;
  }

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

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

  bool SetIsCompositingCheap(bool aCompositingCheap) { 
    bool temp = mIsCompositingCheap; 
    mIsCompositingCheap = aCompositingCheap;
    return temp;
  }
  bool IsCompositingCheap() const { return mIsCompositingCheap; }
  /**
   * Display the caret if needed.
   */
  void DisplayCaret(nsIFrame* aFrame, const nsRect& aDirtyRect,
                    nsDisplayList* aList) {
    nsIFrame* frame = GetCaretFrame();
    if (aFrame == frame) {
      frame->DisplayCaret(this, aDirtyRect, aList);
    }
  }
  /**
   * Get the frame that the caret is supposed to draw in.
   * If the caret is currently invisible, this will be null.
   */
  nsIFrame* GetCaretFrame() {
    return CurrentPresShellState()->mCaretFrame;
  }
  /**
   * Get the rectangle we're supposed to draw the caret into.
   */
  const nsRect& GetCaretRect() {
    return CurrentPresShellState()->mCaretRect;
  }
  /**
   * Get the caret associated with the current presshell.
   */
  nsCaret* GetCaret();
  /**
   * 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(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();
  /**
   * Notify the display list builder that we're leaving a presshell.
   */
  void LeavePresShell(nsIFrame* aReferenceFrame, nsDisplayList* aPaintedContents);

  /**
   * Returns true if we're currently building a display list that's
   * directly or indirectly under an nsDisplayTransform.
   */
  bool IsInTransform() const { return mInTransform; }
  /**
   * Indicate whether or not we're directly or indirectly under and
   * nsDisplayTransform or SVG foreignObject.
   */
  void SetInTransform(bool aInTransform) { mInTransform = aInTransform; }

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

  /**
   * 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() {
    return mIsInChromePresContext || mIsBuildingForPopup;
  }

  /**
   * @return true if images have been set to decode synchronously.
   */
  bool ShouldSyncDecodeImages() { 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;
  }

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

  /**
   * Subtracts aRegion from *aVisibleRegion. We avoid letting
   * aVisibleRegion become overcomplex by simplifying it if necessary ---
   * unless mAccurateVisibleRegions is set, in which case we let it
   * get arbitrarily complex.
   */
  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,
                                const nsRect& aDirtyRect);
  /**
   * 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);

  const nsTArray<ThemeGeometry>& GetThemeGeometries() { return mThemeGeometries; }

  /**
   * 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) const {
    return
      (aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) ||
      GetIncludeAllOutOfFlows();
  }

  /**
   * 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 NS_THEME_TOOLBAR and
   * NS_THEME_WINDOW_TITLEBAR) 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 aRect the device-pixel rect relative to the widget's displayRoot
   * for the themed widget
   */
  void RegisterThemeGeometry(uint8_t aWidgetType,
                             const mozilla::LayoutDeviceIntRect& aRect) {
    if (mIsPaintingToWindow) {
      mThemeGeometries.AppendElement(ThemeGeometry(aWidgetType, aRect));
    }
  }

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

  /**
   * Allocate memory in our arena. It will only be freed when this display list
   * builder is destroyed. This memory holds nsDisplayItems. nsDisplayItem
   * destructors are called as soon as the item is no longer used.
   */
  void* Allocate(size_t aSize);

  /**
   * Allocate a new DisplayItemClip in the arena. Will be cleaned up
   * automatically when the arena goes away.
   */
  const DisplayItemClip* AllocateDisplayItemClip(const DisplayItemClip& aOriginal);

  /**
   * Allocate a new DisplayItemScrollClip in the arena. Will be cleaned up
   * automatically when the arena goes away.
   */
  DisplayItemScrollClip* AllocateDisplayItemScrollClip(const DisplayItemScrollClip* aParent,
                                                 nsIScrollableFrame* aScrollableFrame,
                                                 const DisplayItemClip* aClip,
                                                 bool aIsAsyncScrollable);

  /**
   * Transfer off main thread animations to the layer.  May be called
   * with aBuilder and aItem both null, but only if the caller has
   * already checked that off main thread animations should be sent to
   * the layer.  When they are both null, the animations are added to
   * the layer as pending animations.
   */
  static void AddAnimationsAndTransitionsToLayer(Layer* aLayer,
                                                 nsDisplayListBuilder* aBuilder,
                                                 nsDisplayItem* aItem,
                                                 nsIFrame* aFrame,
                                                 nsCSSPropertyID aProperty);

  /**
   * A helper class to temporarily set the value of
   * mIsAtRootOfPseudoStackingContext, and temporarily
   * set mCurrentFrame and related state. Also temporarily sets mDirtyRect.
   * aDirtyRect is relative to aForChild.
   */
  class AutoBuildingDisplayList;
  friend class AutoBuildingDisplayList;
  class AutoBuildingDisplayList {
  public:
    AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder,
                            nsIFrame* aForChild,
                            const nsRect& aDirtyRect, bool aIsRoot)
      : mBuilder(aBuilder),
        mPrevFrame(aBuilder->mCurrentFrame),
        mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
        mPrevLayerEventRegions(aBuilder->mLayerEventRegions),
        mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
        mPrevDirtyRect(aBuilder->mDirtyRect),
        mPrevAGR(aBuilder->mCurrentAGR),
        mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
        mPrevAncestorHasApzAwareEventHandler(aBuilder->mAncestorHasApzAwareEventHandler),
        mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems)
    {
      if (aForChild->IsTransformed()) {
        aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
        aBuilder->mCurrentReferenceFrame = aForChild;
      } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
        aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
      } else {
        aBuilder->mCurrentReferenceFrame =
          aBuilder->FindReferenceFrameFor(aForChild,
              &aBuilder->mCurrentOffsetToReferenceFrame);
      }
      if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
        if (aBuilder->IsAnimatedGeometryRoot(aForChild)) {
          aBuilder->mCurrentAGR = aBuilder->WrapAGRForFrame(aForChild, aBuilder->mCurrentAGR);
        }
      } else if (aForChild != aBuilder->mCurrentFrame) {
        aBuilder->mCurrentAGR = aBuilder->FindAnimatedGeometryRootFor(aForChild);
      }
      MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR));
      aBuilder->mCurrentFrame = aForChild;
      aBuilder->mDirtyRect = aDirtyRect;
      aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot;
    }
    void SetDirtyRect(const nsRect& aRect) {
      mBuilder->mDirtyRect = aRect;
    }
    void SetReferenceFrameAndCurrentOffset(const nsIFrame* aFrame, const nsPoint& aOffset) {
      mBuilder->mCurrentReferenceFrame = aFrame;
      mBuilder->mCurrentOffsetToReferenceFrame = aOffset;
    }
    // Return the previous frame's animated geometry root, whether or not the
    // current frame is an immediate descendant.
    const nsIFrame* GetPrevAnimatedGeometryRoot() const {
      return mPrevAnimatedGeometryRoot;
    }
    bool IsAnimatedGeometryRoot() const {
      return *mBuilder->mCurrentAGR == mBuilder->mCurrentFrame;

    }
    void RestoreBuildingInvisibleItemsValue() {
      mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems;
    }
    ~AutoBuildingDisplayList() {
      mBuilder->mCurrentFrame = mPrevFrame;
      mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame;
      mBuilder->mLayerEventRegions = mPrevLayerEventRegions;
      mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset;
      mBuilder->mDirtyRect = mPrevDirtyRect;
      mBuilder->mCurrentAGR = mPrevAGR;
      mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext;
      mBuilder->mAncestorHasApzAwareEventHandler = mPrevAncestorHasApzAwareEventHandler;
      mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems;
    }
  private:
    nsDisplayListBuilder* mBuilder;
    const nsIFrame*       mPrevFrame;
    const nsIFrame*       mPrevReferenceFrame;
    nsIFrame*             mPrevAnimatedGeometryRoot;
    nsDisplayLayerEventRegions* mPrevLayerEventRegions;
    nsPoint               mPrevOffset;
    nsRect                mPrevDirtyRect;
    AnimatedGeometryRoot* mPrevAGR;
    bool                  mPrevIsAtRootOfPseudoStackingContext;
    bool                  mPrevAncestorHasApzAwareEventHandler;
    bool                  mPrevBuildingInvisibleItems;
  };

  /**
   * A helper class to temporarily set the value of mInTransform.
   */
  class AutoInTransformSetter;
  friend class AutoInTransformSetter;
  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 AutoSaveRestorePerspectiveIndex;
  friend class AutoSaveRestorePerspectiveIndex;
  class AutoSaveRestorePerspectiveIndex {
  public:
    AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
      : mBuilder(nullptr)
    {
      if (aFrame->ChildrenHavePerspective()) {
        mBuilder = aBuilder;
        mCachedItemIndex = aBuilder->mPerspectiveItemIndex;
        aBuilder->mPerspectiveItemIndex = 0;
      }
    }

    ~AutoSaveRestorePerspectiveIndex()
    {
      if (mBuilder) {
        mBuilder->mPerspectiveItemIndex = mCachedItemIndex;
      }
    }

  private:
    nsDisplayListBuilder* mBuilder;
    uint32_t mCachedItemIndex;
  };

  /**
   * A helper class to temporarily set the value of mCurrentScrollParentId.
   */
  class AutoCurrentScrollParentIdSetter;
  friend class AutoCurrentScrollParentIdSetter;
  class AutoCurrentScrollParentIdSetter {
  public:
    AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollId)
      : mBuilder(aBuilder)
      , mOldValue(aBuilder->mCurrentScrollParentId)
      , mOldForceLayer(aBuilder->mForceLayerForScrollParent) {
      // If this AutoCurrentScrollParentIdSetter has the same scrollId 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 = (mOldValue != aScrollId);
      aBuilder->mCurrentScrollParentId = aScrollId;
      aBuilder->mForceLayerForScrollParent = false;
    }
    bool ShouldForceLayerForScrollParent() const {
      // Only scrollframes participating in scroll handoff can be forced to
      // layerize
      return mCanBeScrollParent && mBuilder->mForceLayerForScrollParent;
    };
    ~AutoCurrentScrollParentIdSetter() {
      mBuilder->mCurrentScrollParentId = mOldValue;
      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;
      }
    }
  private:
    nsDisplayListBuilder* mBuilder;
    ViewID                mOldValue;
    bool                  mOldForceLayer;
    bool                  mCanBeScrollParent;
  };

  /**
   * A helper class to temporarily set the value of mCurrentScrollbarTarget
   * and mCurrentScrollbarFlags.
   */
  class AutoCurrentScrollbarInfoSetter;
  friend class AutoCurrentScrollbarInfoSetter;
  class AutoCurrentScrollbarInfoSetter {
  public:
    AutoCurrentScrollbarInfoSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollTargetID,
                                   uint32_t aScrollbarFlags, bool aWillHaveLayer)
     : mBuilder(aBuilder) {
      aBuilder->mIsBuildingScrollbar = true;
      aBuilder->mCurrentScrollbarTarget = aScrollTargetID;
      aBuilder->mCurrentScrollbarFlags = aScrollbarFlags;
      aBuilder->mCurrentScrollbarWillHaveLayer = aWillHaveLayer;
    }
    ~AutoCurrentScrollbarInfoSetter() {
      // No need to restore old values because scrollbars cannot be nested.
      mBuilder->mIsBuildingScrollbar = false;
      mBuilder->mCurrentScrollbarTarget = FrameMetrics::NULL_SCROLL_ID;
      mBuilder->mCurrentScrollbarFlags = 0;
      mBuilder->mCurrentScrollbarWillHaveLayer = false;
    }
  private:
    nsDisplayListBuilder* mBuilder;
  };

  /**
   * 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;
  friend class AutoAccumulateTransform;
  class AutoAccumulateTransform {
  public:
    typedef mozilla::gfx::Matrix4x4 Matrix4x4;

    explicit AutoAccumulateTransform(nsDisplayListBuilder* aBuilder)
      : mBuilder(aBuilder)
      , mSavedTransform(aBuilder->mPreserves3DCtx.mAccumulatedTransform) {}

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

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

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

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

  private:
    nsDisplayListBuilder* mBuilder;
    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;
  friend class AutoAccumulateRect;
  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;
  }

  // Helpers for tables
  nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; }
  void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; }

  struct OutOfFlowDisplayData {
    OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip,
                         const DisplayItemScrollClip* aContainingBlockScrollClip,
                         const nsRect &aDirtyRect)
      : mContainingBlockClip(aContainingBlockClip ? *aContainingBlockClip : DisplayItemClip())
      , mContainingBlockScrollClip(aContainingBlockScrollClip)
      , mDirtyRect(aDirtyRect)
    {}
    DisplayItemClip mContainingBlockClip;
    const DisplayItemScrollClip* mContainingBlockScrollClip;
    nsRect mDirtyRect;
  };

  NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty,
                                      OutOfFlowDisplayData)

  static OutOfFlowDisplayData* GetOutOfFlowData(nsIFrame* aFrame)
  {
    return aFrame->Properties().Get(OutOfFlowDisplayDataProperty());
  }

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

  /**
   * Accumulates the bounds of box frames that have moz-appearance
   * -moz-win-exclude-glass style. Used in setting glass margins on
   * Windows.
   *
   * We set the window opaque region (from which glass margins are computed)
   * to the intersection of the glass region specified here and the opaque
   * region computed during painting. So the excluded glass region actually
   * *limits* the extent of the opaque area reported to Windows. We limit it
   * so that changes to the computed opaque region (which can vary based on
   * region optimizations and the placement of UI elements) outside the
   * -moz-win-exclude-glass area don't affect the glass margins reported to
   * Windows; changing those margins willy-nilly can cause the Windows 7 glass
   * haze effect to jump around disconcertingly.
   */
  void AddWindowExcludeGlassRegion(const nsRegion& bounds) {
    mWindowExcludeGlassRegion.Or(mWindowExcludeGlassRegion, bounds);
  }
  const nsRegion& GetWindowExcludeGlassRegion() {
    return mWindowExcludeGlassRegion;
  }
  /**
   * Accumulates opaque stuff into the window opaque region.
   */
  void AddWindowOpaqueRegion(const nsRegion& bounds) {
    mWindowOpaqueRegion.Or(mWindowOpaqueRegion, bounds);
  }
  /**
   * 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 mWindowOpaqueRegion;
  }
  void SetGlassDisplayItem(nsDisplayItem* aItem) {
    if (mGlassDisplayItem) {
      // Web pages or extensions could trigger this by using
      // -moz-appearance:win-borderless-glass etc on their own elements.
      // Keep the first one, since that will be the background of the root
      // window
      NS_WARNING("Multiple glass backgrounds found?");
    } else {
      mGlassDisplayItem = aItem;
    }
  }
  bool NeedToForceTransparentSurfaceForItem(nsDisplayItem* aItem);

  void SetContainsPluginItem() { mContainsPluginItem = true; }
  bool ContainsPluginItem() { return mContainsPluginItem; }

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

  uint32_t AllocatePerspectiveItemIndex() { return mPerspectiveItemIndex++; }

  DisplayListClipState& ClipState() { return mClipState; }

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

  void EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage);
  void ExitSVGEffectsContents();

  bool ShouldBuildScrollInfoItemsForHoisting() const
  { return mSVGEffectsBuildingDepth > 0; }

  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;
  friend class AutoPreserves3DContext;
  class AutoPreserves3DContext {
  public:
    explicit AutoPreserves3DContext(nsDisplayListBuilder* aBuilder)
      : mBuilder(aBuilder)
      , mSavedCtx(aBuilder->mPreserves3DCtx) {}
    ~AutoPreserves3DContext() {
      mBuilder->mPreserves3DCtx = mSavedCtx;
    }

  private:
    nsDisplayListBuilder* mBuilder;
    Preserves3DContext mSavedCtx;
  };

  const nsRect GetPreserves3DDirtyRect(const nsIFrame *aFrame) const {
    return mPreserves3DCtx.mDirtyRect;
  }
  void SetPreserves3DDirtyRect(const nsRect &aDirtyRect) {
    mPreserves3DCtx.mDirtyRect = aDirtyRect;
  }

  bool IsBuildingInvisibleItems() const { return mBuildingInvisibleItems; }
  void SetBuildingInvisibleItems(bool aBuildingInvisibleItems) {
    mBuildingInvisibleItems = aBuildingInvisibleItems;
  }

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

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

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

  friend class nsDisplayCanvasBackgroundImage;
  friend class nsDisplayBackgroundImage;
  friend class nsDisplayFixedPosition;
  AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsDisplayItem* aItem);

  AnimatedGeometryRoot* WrapAGRForFrame(nsIFrame* aAnimatedGeometryRoot,
                                        AnimatedGeometryRoot* aParent = nullptr);

  friend class nsDisplayItem;
  AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame);

  nsDataHashtable<nsPtrHashKey<nsIFrame>, AnimatedGeometryRoot*> mFrameToAnimatedGeometryRootMap;

  /**
   * Add the current frame to the AGR budget if possible and remember
   * the outcome. Subsequent calls will return the same value as
   * returned here.
   */
  bool AddToAGRBudget(nsIFrame* aFrame);

  struct PresShellState {
    nsIPresShell* mPresShell;
    nsIFrame*     mCaretFrame;
    nsRect        mCaretRect;
    uint32_t      mFirstFrameMarkedForDisplay;
    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;
  };

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

  struct DocumentWillChangeBudget {
    DocumentWillChangeBudget()
      : mBudget(0)
    {}

    uint32_t mBudget;
  };

  nsIFrame* const                mReferenceFrame;
  nsIFrame*                      mIgnoreScrollFrame;
  nsDisplayLayerEventRegions*    mLayerEventRegions;
  PLArenaPool                    mPool;
  nsCOMPtr<nsISelection>         mBoundingSelection;
  AutoTArray<PresShellState,8> mPresShellStates;
  AutoTArray<nsIFrame*,100>    mFramesMarkedForDisplay;
  AutoTArray<ThemeGeometry,2>  mThemeGeometries;
  nsDisplayTableItem*            mCurrentTableItem;
  DisplayListClipState           mClipState;
  // 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;
  // The offset from mCurrentFrame to mCurrentReferenceFrame.
  nsPoint                        mCurrentOffsetToReferenceFrame;

  AnimatedGeometryRoot*          mCurrentAGR;
  AnimatedGeometryRoot           mRootAGR;

  // will-change budget tracker
  nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
                                 mWillChangeBudget;

  // Any frame listed in this set is already counted in the budget
  // and thus is in-budget.
  nsTHashtable<nsPtrHashKey<nsIFrame> > mWillChangeBudgetSet;

  // Area of animated geometry root budget already allocated
  uint32_t mUsedAGRBudget;
  // Set of frames already counted in budget
  nsTHashtable<nsPtrHashKey<nsIFrame> > mAGRBudgetSet;

  // Relative to mCurrentFrame.
  nsRect                         mDirtyRect;
  nsRegion                       mWindowExcludeGlassRegion;
  nsRegion                       mWindowOpaqueRegion;
  LayoutDeviceIntRegion          mWindowDraggingRegion;
  LayoutDeviceIntRegion          mWindowNoDraggingRegion;
  // The display item for the Windows window glass background, if any
  nsDisplayItem*                 mGlassDisplayItem;
  // 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<DisplayItemScrollClip*> mScrollClipsToDestroy;
  nsTArray<DisplayItemClip*>     mDisplayItemClipsToDestroy;
  nsDisplayListBuilderMode       mMode;
  ViewID                         mCurrentScrollParentId;
  ViewID                         mCurrentScrollbarTarget;
  uint32_t                       mCurrentScrollbarFlags;
  Preserves3DContext             mPreserves3DCtx;
  uint32_t                       mPerspectiveItemIndex;
  int32_t                        mSVGEffectsBuildingDepth;
  bool                           mContainsBlendMode;
  bool                           mIsBuildingScrollbar;
  bool                           mCurrentScrollbarWillHaveLayer;
  bool                           mBuildCaret;
  bool                           mIgnoreSuppression;
  bool                           mIsAtRootOfPseudoStackingContext;
  bool                           mIncludeAllOutOfFlows;
  bool                           mDescendIntoSubdocuments;
  bool                           mSelectedFramesOnly;
  bool                           mAccurateVisibleRegions;
  bool                           mAllowMergingAndFlattening;
  bool                           mWillComputePluginGeometry;
  // True when we're building a display list that's directly or indirectly
  // under an nsDisplayTransform
  bool                           mInTransform;
  bool                           mIsInChromePresContext;
  bool                           mSyncDecodeImages;
  bool                           mIsPaintingToWindow;
  bool                           mIsCompositingCheap;
  bool                           mContainsPluginItem;
  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                           mAsyncPanZoomEnabled;
  bool                           mBuildingInvisibleItems;
};

class nsDisplayItem;
class nsDisplayList;
/**
 * nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
 * nsDisplayItemLink holds the link. The lists are linked from lowest to
 * highest in z-order.
 */
class nsDisplayItemLink {
  // This is never instantiated directly, so no need to count constructors and
  // destructors.
protected:
  nsDisplayItemLink() : mAbove(nullptr) {}
  nsDisplayItem* mAbove;  
  
  friend class nsDisplayList;
};

/**
 * This is the unit of rendering and event testing. Each instance of this
 * class represents an entity that can be drawn on the screen, e.g., a
 * frame's CSS background, or a frame's text string.
 * 
 * nsDisplayItems can be containers --- i.e., they can perform hit testing
 * and painting by recursively traversing a list of child items.
 * 
 * These are arena-allocated during display list construction. A typical
 * subclass would just have a frame pointer, so its object would be just three
 * pointers (vtable, next-item, frame).
 * 
 * Display items belong to a list at all times (except temporarily as they
 * move from one list to another).
 */
class nsDisplayItem : public nsDisplayItemLink {
public:
  typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
  typedef mozilla::DisplayItemClip DisplayItemClip;
  typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip;
  typedef mozilla::layers::FrameMetrics FrameMetrics;
  typedef mozilla::layers::ScrollMetadata ScrollMetadata;
  typedef mozilla::layers::FrameMetrics::ViewID ViewID;
  typedef mozilla::layers::Layer Layer;
  typedef mozilla::layers::LayerManager LayerManager;
  typedef mozilla::LayerState LayerState;

  // 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 DisplayItemScrollClip* aScrollClip);
  /**
   * This constructor is only used in rare cases when we need to construct
   * temporary items.
   */
  explicit nsDisplayItem(nsIFrame* aFrame)
    : mFrame(aFrame)
    , mClip(nullptr)
    , mScrollClip(nullptr)
    , mReferenceFrame(nullptr)
    , mAnimatedGeometryRoot(nullptr)
    , mForceNotVisible(false)
#ifdef MOZ_DUMP_PAINTING
    , mPainted(false)
#endif
  {
  }
  virtual ~nsDisplayItem() {}

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

// Contains all the type integers for each display list item type
#include "nsDisplayItemTypes.h"

  struct HitTestState {
    explicit HitTestState() : mInPreserves3D(false) {}

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

    // Handling transform items for preserve 3D frames.
    bool mInPreserves3D;
    AutoTArray<nsDisplayItem*, 100> mItemBuffer;
  };

  /**
   * 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.
   */
  virtual Type GetType() = 0;
  /**
   * Pairing this with the GetUnderlyingFrame() pointer gives a key that
   * uniquely identifies this display item in the display item tree.
   * XXX check nsOptionEventGrabberWrapper/nsXULEventRedirectorWrapper
   */
  virtual uint32_t GetPerFrameKey() { return uint32_t(GetType()); }
  /**
   * 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) {}
  /**
   * @return the frame that this display item is based on. This is used to sort
   * items by z-index and content order and for some other uses. Never
   * returns null.
   */
  inline nsIFrame* Frame() 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)
  {
    *aSnap = false;
    return nsRect(ToReferenceFrame(), Frame()->GetSize());
  }
  /**
   * 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)
  {
    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);
  nsRect GetBorderRect() {
    return nsRect(ToReferenceFrame(), Frame()->GetSize());
  }
  nsRect GetPaddingRect() {
    return Frame()->GetPaddingRectRelativeToSelf() + ToReferenceFrame();
  }
  nsRect GetContentRect() {
    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) { 
    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 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)
  {
    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);
      }
    }
  }

  /**
   * Called when the area rendered by this display item has changed (been
   * invalidated or changed geometry) since the last paint. This includes
   * when the display item was not rendered at all in the last paint.
   * It does NOT get called when a display item was being rendered and no
   * longer is, because generally that means there is no display item to
   * call this method on.
   */
  virtual void NotifyRenderingChanged() {}

  /**
   * @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.
   */
  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                   bool* aSnap)
  {
    *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 mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder)
  { return mozilla::Nothing(); }
  /**
   * @return true if the contents of this item are rendered fixed relative
   * to the nearest viewport.
   */
  virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder)
  { return false; }

  virtual bool ClearsBackground()
  { return false; }

  virtual bool ProvidesFontSmoothingBackgroundColor(nscolor* aColor)
  { 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();

  /**
   * @return LAYER_NONE if BuildLayer will return null. In this case
   * there is no layer for the item, and Paint should be called instead
   * to paint the content using Thebes.
   * Return LAYER_INACTIVE if there is a layer --- BuildLayer will
   * not return null (unless there's an error) --- but the layer contents
   * are not changing frequently. In this case it makes sense to composite
   * the layer into a PaintedLayer with other content, so we don't have to
   * recomposite it every time we paint.
   * Note: GetLayerState is only allowed to return LAYER_INACTIVE if all
   * descendant display items returned LAYER_INACTIVE or LAYER_NONE. Also,
   * all descendant display item frames must have an active scrolled root
   * that's either the same as this item's frame's active scrolled root, or
   * a descendant of this item's frame. This ensures that the entire
   * set of display items can be collapsed onto a single PaintedLayer.
   * Return LAYER_ACTIVE if the layer is active, that is, its contents are
   * changing frequently. In this case it makes sense to keep the layer
   * as a separate buffer in VRAM and composite it into the destination
   * every time we paint.
   *
   * Users of GetLayerState should check ForceActiveLayers() and if it returns
   * true, change a returned value of LAYER_INACTIVE to LAYER_ACTIVE.
   */
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters)
  { return mozilla::LAYER_NONE; }
  /**
   * Return true to indicate the layer should be constructed even if it's
   * completely invisible.
   */
  virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
  { return false; }
  /**
   * Actually paint this item to some rendering context.
   * Content outside mVisibleRect need not be painted.
   * aCtx must be set up as for nsDisplayList::Paint.
   */
  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) {}

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

  /**
   * Check if this display item has been painted.
   */
  void SetPainted() { mPainted = true; }
#endif

  /**
   * Get the layer drawn by this display item. Call this only if
   * GetLayerState() returns something other than LAYER_NONE.
   * If GetLayerState returned LAYER_NONE then Paint will be called
   * instead.
   * This is called while aManager is in the construction phase.
   * 
   * The caller (nsDisplayList) is responsible for setting the visible
   * region of the layer.
   *
   * @param aContainerParameters should be passed to
   * FrameLayerBuilder::BuildContainerLayerFor if a ContainerLayer is
   * constructed.
   */
  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters)
  { return nullptr; }

  /**
   * On entry, aVisibleRegion contains the region (relative to ReferenceFrame())
   * which may be visible. If the display item opaquely covers an area, it
   * can remove that area from aVisibleRegion before returning.
   * nsDisplayList::ComputeVisibility automatically subtracts the region
   * returned by GetOpaqueRegion, and automatically removes items whose bounds
   * do not intersect the visible area, so implementations of
   * nsDisplayItem::ComputeVisibility do not need to do these things.
   * nsDisplayList::ComputeVisibility will already have set mVisibleRect on
   * this item to the intersection of *aVisibleRegion and this item's bounds.
   * We rely on that, so this should only be called by
   * nsDisplayList::ComputeVisibility or nsDisplayItem::RecomputeVisibility.
   * aAllowVisibleRegionExpansion is a rect where we are allowed to
   * expand the visible region and is only used for making sure the
   * background behind a plugin is visible.
   * This method needs to be idempotent.
   *
   * @return true if the item is visible, false if no part of the item
   * is visible.
   */
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion);

  /**
   * 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).
   * @return true if the merge was successful and the other item should be deleted
   */
  virtual bool TryMerge(nsDisplayItem* aItem) {
    return false;
  }

  /**
   * Appends the underlying frames of all display items that have been
   * merged into this one (excluding  this item's own underlying frame)
   * to aFrames.
   */
  virtual void GetMergedFrames(nsTArray<nsIFrame*>* aFrames) {}

  /**
   * During the visibility computation and after TryMerge, display lists may
   * return true here to flatten themselves away, removing them. This
   * flattening is distinctly different from FlattenTo, which occurs before
   * items are merged together.
   */
  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
    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 nsDisplayList* GetSameCoordinateSystemChildren() { 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) {}

  /**
   * If this has a child list, return it, even if the children are in
   * a different coordinate system to this item.
   */
  virtual nsDisplayList* GetChildren() { return nullptr; }

  /**
   * Returns the visible rect.
   */
  const nsRect& GetVisibleRect() const { return mVisibleRect; }

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

  /**
   * Stores the given opacity value to be applied when drawing. It is an error to
   * call this if CanApplyOpacity returned false.
   */
  virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                            float aOpacity,
                            const DisplayItemClip* aClip) {
    NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity not supported on this type");
  }
  /**
   * Returns true if this display item would return true from ApplyOpacity without
   * actually applying the opacity. Otherwise returns false.
   */
  virtual bool CanApplyOpacity() const {
    return false;
  }

  /**
   * For debugging and stuff
   */
  virtual const char* Name() = 0;

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

  nsDisplayItem* GetAbove() { return mAbove; }

  /**
   * Like ComputeVisibility, but does the work that nsDisplayList
   * does per-item:
   * -- Intersects GetBounds with aVisibleRegion and puts the result
   * in mVisibleRect
   * -- Subtracts bounds from aVisibleRegion if the item is opaque
   */
  bool RecomputeVisibility(nsDisplayListBuilder* aBuilder,
                           nsRegion* aVisibleRegion);

  /**
   * Returns the result of aBuilder->ToReferenceFrame(GetUnderlyingFrame())
   */
  const nsPoint& ToReferenceFrame() const {
    NS_ASSERTION(mFrame, "No frame?");
    return mToReferenceFrame;
  }
  /**
   * @return the root of the display list's frame (sub)tree, whose origin
   * establishes the coordinate system for the display list
   */
  const nsIFrame* ReferenceFrame() const { return mReferenceFrame; }

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

  AnimatedGeometryRoot* GetAnimatedGeometryRoot() const {
    MOZ_ASSERT(mAnimatedGeometryRoot, "Must have cached AGR before accessing it!");
    return mAnimatedGeometryRoot;
  }

  virtual struct AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const {
    return GetAnimatedGeometryRoot();
  }

  /**
   * 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) { return nsRect(); }

  /**
   * Disable usage of component alpha. Currently only relevant for items that have text.
   */
  virtual void DisableComponentAlpha() {}

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

  const DisplayItemClip& GetClip()
  {
    return mClip ? *mClip : DisplayItemClip::NoClip();
  }
  void SetClip(nsDisplayListBuilder* aBuilder, const DisplayItemClip& aClip)
  {
    if (!aClip.HasClip()) {
      mClip = nullptr;
      return;
    }
    mClip = aBuilder->AllocateDisplayItemClip(aClip);
  }

  void IntersectClip(nsDisplayListBuilder* aBuilder, const DisplayItemClip& aClip)
  {
    if (mClip) {
      DisplayItemClip temp = *mClip;
      temp.IntersectWith(aClip);
      SetClip(aBuilder, temp);
    } else {
      SetClip(aBuilder, aClip);
    }
  }

  void SetScrollClip(const DisplayItemScrollClip* aScrollClip) { mScrollClip = aScrollClip; }
  const DisplayItemScrollClip* ScrollClip() const { return mScrollClip; }

  bool BackfaceIsHidden() {
    return mFrame->BackfaceIsHidden();
  }

protected:
  friend class nsDisplayList;

  nsDisplayItem() { mAbove = nullptr; }

  nsIFrame* mFrame;
  const DisplayItemClip* mClip;
  const DisplayItemScrollClip* mScrollClip;
  // Result of FindReferenceFrameFor(mFrame), if mFrame is non-null
  const nsIFrame* mReferenceFrame;
  struct AnimatedGeometryRoot* mAnimatedGeometryRoot;
  // Result of ToReferenceFrame(mFrame), if mFrame is non-null
  nsPoint   mToReferenceFrame;
  // This is the rectangle that needs to be painted.
  // Display item construction sets this to the dirty rect.
  // nsDisplayList::ComputeVisibility sets this to the visible region
  // of the item by intersecting the current visible region with the bounds
  // of the item. Paint implementations can use this to limit their drawing.
  // Guaranteed to be contained in GetBounds().
  nsRect    mVisibleRect;
  bool      mForceNotVisible;
#ifdef MOZ_DUMP_PAINTING
  // True if this frame has been painted.
  bool      mPainted;
#endif
};

/**
 * Manages a singly-linked list of display list items.
 * 
 * mSentinel is the sentinel list value, the first value in the null-terminated
 * linked list of items. mTop is the last item in the list (whose 'above'
 * pointer is null). This class has no virtual methods. So list objects are just
 * two pointers.
 * 
 * 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(), ComputeVisibility()) 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. AppendToBottom() is efficient too.
 */
class nsDisplayList {
public:
  typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip;
  typedef mozilla::layers::Layer Layer;
  typedef mozilla::layers::LayerManager LayerManager;
  typedef mozilla::layers::PaintedLayer PaintedLayer;

  /**
   * Create an empty list.
   */
  nsDisplayList()
    : mIsOpaque(false)
    , mForceTransparentSurface(false)
  {
    mTop = &mSentinel;
    mSentinel.mAbove = nullptr;
  }
  ~nsDisplayList() {
    if (mSentinel.mAbove) {
      NS_WARNING("Nonempty list left over?");
    }
    DeleteAll();
  }

  /**
   * Append an item to the top of the list. The item must not currently
   * be in a list and cannot be null.
   */
  void AppendToTop(nsDisplayItem* aItem) {
    NS_ASSERTION(aItem, "No item to append!");
    NS_ASSERTION(!aItem->mAbove, "Already in a list!");
    mTop->mAbove = aItem;
    mTop = aItem;
  }
  
  /**
   * Append a new item to the top of the list. The intended usage is
   * AppendNewToTop(new ...);
   */
  void AppendNewToTop(nsDisplayItem* aItem) {
    if (aItem) {
      AppendToTop(aItem);
    }
  }
  
  /**
   * Append a new item to the bottom of the list. The intended usage is
   * AppendNewToBottom(new ...);
   */
  void AppendNewToBottom(nsDisplayItem* aItem) {
    if (aItem) {
      AppendToBottom(aItem);
    }
  }
  
  /**
   * Append a new item to the bottom of the list. The item must be non-null
   * and not already in a list.
   */
  void AppendToBottom(nsDisplayItem* aItem) {
    NS_ASSERTION(aItem, "No item to append!");
    NS_ASSERTION(!aItem->mAbove, "Already in a list!");
    aItem->mAbove = mSentinel.mAbove;
    mSentinel.mAbove = aItem;
    if (mTop == &mSentinel) {
      mTop = aItem;
    }
  }
  
  /**
   * Removes all items from aList and appends them to the top of this list
   */
  void AppendToTop(nsDisplayList* aList) {
    if (aList->mSentinel.mAbove) {
      mTop->mAbove = aList->mSentinel.mAbove;
      mTop = aList->mTop;
      aList->mTop = &aList->mSentinel;
      aList->mSentinel.mAbove = nullptr;
    }
  }
  
  /**
   * Removes all items from aList and prepends them to the bottom of this list
   */
  void AppendToBottom(nsDisplayList* aList) {
    if (aList->mSentinel.mAbove) {
      aList->mTop->mAbove = mSentinel.mAbove;
      mSentinel.mAbove = aList->mSentinel.mAbove;
      if (mTop == &mSentinel) {
        mTop = aList->mTop;
      }
           
      aList->mTop = &aList->mSentinel;
      aList->mSentinel.mAbove = nullptr;
    }
  }
  
  /**
   * Remove an item from the bottom of the list and return it.
   */
  nsDisplayItem* RemoveBottom();
  
  /**
   * Remove all items from the list and call their destructors.
   */
  void DeleteAll();
  
  /**
   * @return the item at the top of the list, or null if the list is empty
   */
  nsDisplayItem* GetTop() const {
    return mTop != &mSentinel ? static_cast<nsDisplayItem*>(mTop) : nullptr;
  }
  /**
   * @return the item at the bottom of the list, or null if the list is empty
   */
  nsDisplayItem* GetBottom() const { return mSentinel.mAbove; }
  bool IsEmpty() const { return mTop == &mSentinel; }
  
  /**
   * This is *linear time*!
   * @return the number of items in the list
   */
  uint32_t Count() const;
  /**
   * Stable sort the list by the z-order of GetUnderlyingFrame() 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
   * GetUnderlyingFrame() 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);

  /**
   * Generic stable sort. Take care, because some of the items might be nsDisplayLists
   * themselves.
   * aCmp(item1, item2) should return true if item1 <= item2. We sort the items
   * into increasing order.
   */
  typedef bool (* SortLEQ)(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
                             void* aClosure);
  void Sort(SortLEQ aCmp, void* aClosure);

  /**
   * Compute visiblity for the items in the list.
   * We put this logic here so it can be shared by top-level
   * painting and also display items that maintain child lists.
   * This is also a good place to put ComputeVisibility-related logic
   * that must be applied to every display item. In particular, this
   * sets mVisibleRect on each display item.
   * This sets mIsOpaque if the entire visible area of this list has
   * been removed from aVisibleRegion when we return.
   * This does not remove any items from the list, so we can recompute
   * visiblity with different regions later (see
   * FrameLayerBuilder::DrawPaintedLayer).
   * This method needs to be idempotent.
   * 
   * @param aVisibleRegion the area that is visible, relative to the
   * reference frame; on return, this contains the area visible under the list.
   * I.e., opaque contents of this list are subtracted from aVisibleRegion.
   * @param aListVisibleBounds must be equal to the bounds of the intersection
   * of aVisibleRegion and GetBounds() for this list.
   * @return true if any item in the list is visible.
   */
  bool ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
                                   nsRegion* aVisibleRegion,
                                   const nsRect& aListVisibleBounds);

  /**
   * As ComputeVisibilityForSublist, but computes visibility for a root
   * list (a list that does not belong to an nsDisplayItem).
   * This method needs to be idempotent.
   *
   * @param aVisibleRegion the area that is visible
   */
  bool ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
                                nsRegion* aVisibleRegion);

  /**
   * Returns true if the visible region output from ComputeVisiblity was
   * empty, i.e. everything visible in this list is opaque.
   */
  bool IsOpaque() const {
    return mIsOpaque;
  }

  /**
   * Returns true if any display item requires the surface to be transparent.
   */
  bool NeedsTransparentSurface() const {
    return mForceTransparentSurface;
  }
  /**
   * 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.
   *
   * If PAINT_COMPRESSED is set, the FrameLayerBuilder should be set to compressed mode
   * to avoid short cut optimizations.
   *
   * 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_NO_COMPOSITE = 0x08,
    PAINT_COMPRESSED = 0x10
  };
  already_AddRefed<LayerManager> PaintRoot(nsDisplayListBuilder* aBuilder,
                                           nsRenderingContext* aCtx,
                                           uint32_t aFlags);
  /**
   * Get the bounds. Takes the union of the bounds of all children.
   * The result is not cached.
   */
  nsRect GetBounds(nsDisplayListBuilder* aBuilder) const;
  /**
   * Return the union of the scroll clipped bounds of all children. To get the
   * scroll clipped bounds of a child item, we start with the item's clipped
   * bounds and walk its scroll clip chain up to (but not including)
   * aIncludeScrollClipsUpTo, and take each scroll clip into account. For
   * scroll clips from async scrollable frames we assume that the item can move
   * anywhere inside that scroll frame.
   * In other words, the return value from this method includes all pixels that
   * could potentially be covered by items in this list under async scrolling.
   */
  nsRect GetScrollClippedBoundsUpTo(nsDisplayListBuilder* aBuilder,
                                    const DisplayItemScrollClip* aIncludeScrollClipsUpTo) const;
  /**
   * 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 GetVisibleRect() const;

  void SetIsOpaque()
  {
    mIsOpaque = true;
  }
  void SetNeedsTransparentSurface()
  {
    mForceTransparentSurface = true;
  }

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) CPP_THROW_NEW;
  
  nsDisplayItemLink  mSentinel;
  nsDisplayItemLink* mTop;

  // This is set to true by FrameLayerBuilder if the final visible region
  // is empty (i.e. everything that was visible is covered by some
  // opaque content in this list).
  bool mIsOpaque;
  // This is set to true by FrameLayerBuilder if any display item in this
  // list needs to force the surface containing this list to be transparent.
  bool mForceTransparentSurface;
};

/**
 * 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 mBorderBackground; }
  /**
   * @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 mBlockBorderBackgrounds; }
  /**
   * @return a list where one should place descendant floats (step 5 of
   * CSS 2.1 appendix E)
   */
  nsDisplayList* Floats() const { return mFloats; }
  /**
   * @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 mPositioned; }
  /**
   * @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 mOutlines; }
  /**
   * @return a list where one should place all other content
   */
  nsDisplayList* Content() const { return mContent; }
  
  nsDisplayListSet(nsDisplayList* aBorderBackground,
                   nsDisplayList* aBlockBorderBackgrounds,
                   nsDisplayList* aFloats,
                   nsDisplayList* aContent,
                   nsDisplayList* aPositionedDescendants,
                   nsDisplayList* aOutlines) :
     mBorderBackground(aBorderBackground),
     mBlockBorderBackgrounds(aBlockBorderBackgrounds),
     mFloats(aFloats),
     mContent(aContent),
     mPositioned(aPositionedDescendants),
     mOutlines(aOutlines) {
  }

  /**
   * A copy constructor that lets the caller override the BorderBackground
   * list.
   */  
  nsDisplayListSet(const nsDisplayListSet& aLists,
                   nsDisplayList* aBorderBackground) :
     mBorderBackground(aBorderBackground),
     mBlockBorderBackgrounds(aLists.BlockBorderBackgrounds()),
     mFloats(aLists.Floats()),
     mContent(aLists.Content()),
     mPositioned(aLists.PositionedDescendants()),
     mOutlines(aLists.Outlines()) {
  }
  
  /**
   * 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) CPP_THROW_NEW;

protected:
  nsDisplayList* mBorderBackground;
  nsDisplayList* mBlockBorderBackgrounds;
  nsDisplayList* mFloats;
  nsDisplayList* mContent;
  nsDisplayList* mPositioned;
  nsDisplayList* mOutlines;
};

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

  /**
   * Sort all lists by content order.
   */                     
  void SortAllByContentOrder(nsIContent* aCommonAncestor) {
    for (int32_t i = 0; i < 6; ++i) {
      mLists[i].SortByContentOrder(aCommonAncestor);
    }
  }

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) CPP_THROW_NEW;

  nsDisplayList mLists[6];
};


class nsDisplayImageContainer : public nsDisplayItem {
public:
  typedef mozilla::LayerIntPoint LayerIntPoint;
  typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
  typedef mozilla::layers::ImageContainer ImageContainer;
  typedef mozilla::layers::ImageLayer ImageLayer;

  nsDisplayImageContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    : nsDisplayItem(aBuilder, aFrame)
  {}

  /**
   * @return true if this display item can be optimized into an image layer.
   * It is an error to call GetContainer() unless you've called
   * CanOptimizeToImageLayer() first and it returned true.
   */
  virtual bool CanOptimizeToImageLayer(LayerManager* aManager,
                                       nsDisplayListBuilder* aBuilder);

  already_AddRefed<ImageContainer> GetContainer(LayerManager* aManager,
                                                nsDisplayListBuilder* aBuilder);
  void ConfigureLayer(ImageLayer* aLayer,
                      const ContainerLayerParameters& aParameters);

  virtual already_AddRefed<imgIContainer> GetImage() = 0;

  virtual nsRect GetDestRect() = 0;

  virtual bool SupportsOptimizingToImage() override { return true; }
};

/**
 * 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 nsDisplayItem {
public:
  typedef class mozilla::gfx::DrawTarget DrawTarget;

  typedef void (* PaintCallback)(nsIFrame* aFrame, DrawTarget* aDrawTarget,
                                 const nsRect& aDirtyRect, nsPoint aFramePt);

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

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

  // XXX: should be removed eventually
  nsDisplayGeneric(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                   OldPaintCallback aOldPaint, const char* aName, Type aType)
    : nsDisplayItem(aBuilder, aFrame)
    , mPaint(nullptr)
    , mOldPaint(aOldPaint)
    , mName(aName)
    , mType(aType)
  {
    MOZ_COUNT_CTOR(nsDisplayGeneric);
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayGeneric() {
    MOZ_COUNT_DTOR(nsDisplayGeneric);
  }
#endif
  
  virtual void Paint(nsDisplayListBuilder* aBuilder,
                     nsRenderingContext* aCtx) override {
    MOZ_ASSERT(!!mPaint != !!mOldPaint);
    if (mPaint) {
      mPaint(mFrame, aCtx->GetDrawTarget(), mVisibleRect, ToReferenceFrame());
    } else {
      mOldPaint(mFrame, aCtx, mVisibleRect, ToReferenceFrame());
    }
  }
  NS_DISPLAY_DECL_NAME(mName, mType)

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

/**
 * Generic display item that can contain overflow. Use this in lieu of
 * nsDisplayGeneric if you have a frame that should use the visual overflow
 * rect of its frame when drawing items, instead of the frame's bounds.
 */
class nsDisplayGenericOverflow : public nsDisplayGeneric {
  public:
    nsDisplayGenericOverflow(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                             PaintCallback aPaint, const char* aName, Type aType)
      : nsDisplayGeneric(aBuilder, aFrame, aPaint, aName, aType)
    {
      MOZ_COUNT_CTOR(nsDisplayGenericOverflow);
    }
    // XXX: should be removed eventually
    nsDisplayGenericOverflow(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                             OldPaintCallback aOldPaint, const char* aName, Type aType)
      : nsDisplayGeneric(aBuilder, aFrame, aOldPaint, aName, aType)
    {
      MOZ_COUNT_CTOR(nsDisplayGenericOverflow);
    }
  #ifdef NS_BUILD_REFCNT_LOGGING
    virtual ~nsDisplayGenericOverflow() {
      MOZ_COUNT_DTOR(nsDisplayGenericOverflow);
    }
  #endif

    /**
     * Returns the frame's visual overflow rect instead of the frame's bounds.
     */
    virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                             bool* aSnap) override
    {
      *aSnap = false;
      return Frame()->GetVisualOverflowRect() + ToReferenceFrame();
    }
};

#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 nsFrame::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 nsDisplayItem {
public:
  nsDisplayReflowCount(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       const char* aFrameName,
                       uint32_t aColor = 0)
    : nsDisplayItem(aBuilder, aFrame),
      mFrameName(aFrameName),
      mColor(aColor)
  {
    MOZ_COUNT_CTOR(nsDisplayReflowCount);
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayReflowCount() {
    MOZ_COUNT_DTOR(nsDisplayReflowCount);
  }
#endif

  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override {
    mFrame->PresContext()->PresShell()->PaintCount(mFrameName, aCtx,
                                                   mFrame->PresContext(),
                                                   mFrame, ToReferenceFrame(),
                                                   mColor);
  }
  NS_DISPLAY_DECL_NAME("nsDisplayReflowCount", TYPE_REFLOW_COUNT)
protected:
  const char* mFrameName;
  nscolor mColor;
};

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

#define DO_GLOBAL_REFLOW_COUNT_DSP_COLOR(_name, _color)                       \
  PR_BEGIN_MACRO                                                              \
    if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() &&   \
        PresContext()->PresShell()->IsPaintingFrameCounts()) {                \
        aLists.Outlines()->AppendNewToTop(                                    \
             new (aBuilder) 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 nsDisplayItem {
public:
  nsDisplayCaret(nsDisplayListBuilder* aBuilder, nsIFrame* aCaretFrame);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayCaret();
#endif

  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
  NS_DISPLAY_DECL_NAME("Caret", TYPE_CARET)

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

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

#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayBorder() {
    MOZ_COUNT_DTOR(nsDisplayBorder);
  }
#endif

  virtual bool IsInvisibleInRect(const nsRect& aRect) override;
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
  NS_DISPLAY_DECL_NAME("Border", TYPE_BORDER)
  
  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;

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

protected:
  nsRect CalculateBounds(const nsStyleBorder& aStyleBorder);

  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 nsDisplayItem {
public:
  nsDisplaySolidColorBase(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nscolor aColor)
    : nsDisplayItem(aBuilder, aFrame), mColor(aColor)
  {}

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

  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) 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);
  }

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

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

protected:
  nscolor mColor;
};

class nsDisplaySolidColor : public nsDisplaySolidColorBase {
public:
  nsDisplaySolidColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      const nsRect& aBounds, nscolor aColor)
    : nsDisplaySolidColorBase(aBuilder, aFrame, aColor), mBounds(aBounds)
  {
    NS_ASSERTION(NS_GET_A(aColor) > 0, "Don't create invisible nsDisplaySolidColors!");
    MOZ_COUNT_CTOR(nsDisplaySolidColor);
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplaySolidColor() {
    MOZ_COUNT_DTOR(nsDisplaySolidColor);
  }
#endif

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

  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;

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

  NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)

private:
  nsRect  mBounds;
};

/**
 * 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 nsDisplayItem {
  typedef mozilla::gfx::Color Color;

public:
  nsDisplaySolidColorRegion(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                            const nsRegion& aRegion, nscolor aColor)
    : nsDisplayItem(aBuilder, aFrame), mRegion(aRegion), mColor(Color::FromABGR(aColor))
  {
    NS_ASSERTION(NS_GET_A(aColor) > 0, "Don't create invisible nsDisplaySolidColorRegions!");
    MOZ_COUNT_CTOR(nsDisplaySolidColorRegion);
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplaySolidColorRegion() {
    MOZ_COUNT_DTOR(nsDisplaySolidColorRegion);
  }
#endif

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

  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) 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());
    }
  }

protected:

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

  NS_DISPLAY_DECL_NAME("SolidColorRegion", TYPE_SOLID_COLOR_REGION)

private:
  nsRegion mRegion;
  Color mColor;
};

/**
 * A display item to paint one background-image for a frame. Each background
 * image layer gets its own nsDisplayBackgroundImage.
 */
class nsDisplayBackgroundImage : public nsDisplayImageContainer {
public:
  /**
   * 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.
   */
  nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                           uint32_t aLayer, const nsRect& aBackgroundRect,
                           const nsStyleBackground* aBackgroundStyle);
  virtual ~nsDisplayBackgroundImage();

  // This will create and append new items for all the layers of the
  // background. Returns whether we appended a themed background.
  // aAllowWillPaintBorderOptimization should usually be left at true, unless
  // aFrame has special border drawing that causes opaque borders to not
  // actually be opaque.
  static bool AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder,
                                         nsIFrame* aFrame,
                                         const nsRect& aBackgroundRect,
                                         nsDisplayList* aList,
                                         bool aAllowWillPaintBorderOptimization = true,
                                         nsStyleContext* aStyleContext = nullptr);

  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;

  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;

  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;
  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                   bool* aSnap) override;
  virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) override;
  /**
   * GetBounds() returns the background painting area.
   */
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
  virtual uint32_t GetPerFrameKey() override;
  NS_DISPLAY_DECL_NAME("Background", TYPE_BACKGROUND)

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

  /**
   * 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();

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

  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) override;
  
  virtual bool CanOptimizeToImageLayer(LayerManager* aManager,
                                       nsDisplayListBuilder* aBuilder) override;
  virtual already_AddRefed<imgIContainer> GetImage() override;
  virtual nsRect GetDestRect() override;

  static nsRegion GetInsideClipRegion(nsDisplayItem* aItem, uint8_t aClip,
                                      const nsRect& aRect, const nsRect& aBackgroundRect);

  virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) override;

protected:
  typedef class mozilla::layers::ImageContainer ImageContainer;
  typedef class mozilla::layers::ImageLayer ImageLayer;

  bool TryOptimizeToImageLayer(LayerManager* aManager, nsDisplayListBuilder* aBuilder);
  bool IsNonEmptyFixedImage() const;
  nsRect GetBoundsInternal(nsDisplayListBuilder* aBuilder);
  bool ShouldTreatAsFixed() const;
  bool ComputeShouldTreatAsFixed(bool isTransformedFixed) const;

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

  // Determine whether we want to be separated into our own layer, independent
  // of whether this item can actually be layerized.
  enum ImageLayerization {
    WHENEVER_POSSIBLE,
    ONLY_FOR_SCALING,
    NO_LAYER_NEEDED
  };
  ImageLayerization ShouldCreateOwnLayer(nsDisplayListBuilder* aBuilder,
                                         LayerManager* aManager);

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


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

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

  /**
   * GetBounds() returns the background painting area.
   */
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
  NS_DISPLAY_DECL_NAME("ThemedBackground", TYPE_THEMED_BACKGROUND)

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

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

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

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

  virtual void WriteDebugInfo(std::stringstream& aStream) override;
protected:
  nsRect GetBoundsInternal();

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

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

class nsDisplayBackgroundColor : public nsDisplayItem
{
  typedef mozilla::gfx::Color Color;

public:
  nsDisplayBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                           const nsRect& aBackgroundRect,
                           const nsStyleBackground* aBackgroundStyle,
                           nscolor aColor)
    : nsDisplayItem(aBuilder, aFrame)
    , mBackgroundRect(aBackgroundRect)
    , mBackgroundStyle(aBackgroundStyle)
    , mColor(Color::FromABGR(aColor))
  { }

  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;

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

  virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                            float aOpacity,
                            const DisplayItemClip* aClip) override;
  virtual bool CanApplyOpacity() const override;

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

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

  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) 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);
  }

  NS_DISPLAY_DECL_NAME("BackgroundColor", TYPE_BACKGROUND_COLOR)
  virtual void WriteDebugInfo(std::stringstream& aStream) override;

protected:
  const nsRect mBackgroundRect;
  const nsStyleBackground* mBackgroundStyle;
  mozilla::gfx::Color mColor;
};

class nsDisplayClearBackground : public nsDisplayItem
{
public:
  nsDisplayClearBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    : nsDisplayItem(aBuilder, aFrame)
  { }

  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
  {
    *aSnap = true;
    return nsRect(ToReferenceFrame(), Frame()->GetSize());
  }

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

  virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) override
  {
    return mozilla::Some(NS_RGBA(0, 0, 0, 0));
  }

  virtual bool ClearsBackground() override
  {
    return true;
  }

  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override
  {
    return mozilla::LAYER_ACTIVE_FORCE;
  }

  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;

  NS_DISPLAY_DECL_NAME("ClearBackground", TYPE_CLEAR_BACKGROUND)
};

/**
 * The standard display item to paint the outer CSS box-shadows of a frame.
 */
class nsDisplayBoxShadowOuter final : public nsDisplayItem {
public:
  nsDisplayBoxShadowOuter(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    : nsDisplayItem(aBuilder, aFrame)
    , mOpacity(1.0) {
    MOZ_COUNT_CTOR(nsDisplayBoxShadowOuter);
    mBounds = GetBoundsInternal();
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayBoxShadowOuter() {
    MOZ_COUNT_DTOR(nsDisplayBoxShadowOuter);
  }
#endif

  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
  virtual bool IsInvisibleInRect(const nsRect& aRect) override;
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;
  NS_DISPLAY_DECL_NAME("BoxShadowOuter", TYPE_BOX_SHADOW_OUTER)
  
  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) override;
  
  virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                            float aOpacity,
                            const DisplayItemClip* aClip) override
  {
    NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
    mOpacity = aOpacity;
    if (aClip) {
      IntersectClip(aBuilder, *aClip);
    }
  }
  virtual bool CanApplyOpacity() const override
  {
    return true;
  }

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

  nsRect GetBoundsInternal();

private:
  nsRegion mVisibleRegion;
  nsRect mBounds;
  float mOpacity;
};

/**
 * The standard display item to paint the inner CSS box-shadows of a frame.
 */
class nsDisplayBoxShadowInner : public nsDisplayItem {
public:
  nsDisplayBoxShadowInner(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    : nsDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayBoxShadowInner);
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayBoxShadowInner() {
    MOZ_COUNT_DTOR(nsDisplayBoxShadowInner);
  }
#endif

  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;
  NS_DISPLAY_DECL_NAME("BoxShadowInner", TYPE_BOX_SHADOW_INNER)
  
  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
  {
    return new nsDisplayBoxShadowInnerGeometry(this, aBuilder);
  }

  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) 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));
    }
  }

private:
  nsRegion mVisibleRegion;
};

/**
 * The standard display item to paint the CSS outline of a frame.
 */
class nsDisplayOutline : public nsDisplayItem {
public:
  nsDisplayOutline(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) :
    nsDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayOutline);
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayOutline() {
    MOZ_COUNT_DTOR(nsDisplayOutline);
  }
#endif

  virtual bool IsInvisibleInRect(const nsRect& aRect) override;
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
  NS_DISPLAY_DECL_NAME("Outline", TYPE_OUTLINE)
};

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

  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
  NS_DISPLAY_DECL_NAME("EventReceiver", TYPE_EVENT_RECEIVER)
};

/**
 * A display item that tracks event-sensitive regions which will be set
 * on the ContainerLayer that eventually contains this item.
 *
 * One of these is created for each stacking context and pseudo-stacking-context.
 * It accumulates regions for event targets contributed by the border-boxes of
 * frames in its (pseudo) stacking context. A nsDisplayLayerEventRegions
 * eventually contributes its regions to the PaintedLayer it is placed in by
 * FrameLayerBuilder. (We don't create a display item for every frame that
 * could be an event target (i.e. almost all frames), because that would be
 * high overhead.)
 *
 * We always make leaf layers other than PaintedLayers transparent to events.
 * For example, an event targeting a canvas or video will actually target the
 * background of that element, which is logically in the PaintedLayer behind the
 * CanvasFrame or ImageFrame. We only need to create a
 * nsDisplayLayerEventRegions when an element's background could be in front
 * of a lower z-order element with its own layer.
 */
class nsDisplayLayerEventRegions final : public nsDisplayItem {
public:
  nsDisplayLayerEventRegions(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    : nsDisplayItem(aBuilder, aFrame)
  {
    MOZ_COUNT_CTOR(nsDisplayLayerEventRegions);
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayLayerEventRegions() {
    MOZ_COUNT_DTOR(nsDisplayLayerEventRegions);
  }
#endif
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
  {
    *aSnap = false;
    return nsRect();
  }
  nsRect GetHitRegionBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
  {
    *aSnap = false;
    return mHitRegion.GetBounds().Union(mMaybeHitRegion.GetBounds());
  }

  virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                            float aOpacity,
                            const DisplayItemClip* aClip) override
  {
    NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
  }
  virtual bool CanApplyOpacity() const override
  {
    return true;
  }

  NS_DISPLAY_DECL_NAME("LayerEventRegions", TYPE_LAYER_EVENT_REGIONS)

  // Indicate that aFrame's border-box contributes to the event regions for
  // this layer. aFrame must have the same reference frame as mFrame.
  void AddFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);

  // Indicate that an inactive scrollframe's scrollport should be added to the
  // dispatch-to-content region, to ensure that APZ lets content create a
  // displayport.
  void AddInactiveScrollPort(const nsRect& aRect);

  bool IsEmpty() const;

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

  const nsRegion& HitRegion() { return mHitRegion; }
  const nsRegion& MaybeHitRegion() { return mMaybeHitRegion; }
  const nsRegion& DispatchToContentHitRegion() { return mDispatchToContentHitRegion; }
  const nsRegion& NoActionRegion() { return mNoActionRegion; }
  const nsRegion& HorizontalPanRegion() { return mHorizontalPanRegion; }
  const nsRegion& VerticalPanRegion() { return mVerticalPanRegion; }
  nsRegion CombinedTouchActionRegion();

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

private:
  // Relative to aFrame's reference frame.
  // These are the points that are definitely in the hit region.
  nsRegion mHitRegion;
  // These are points that may or may not be in the hit region. Only main-thread
  // event handling can tell for sure (e.g. because complex shapes are present).
  nsRegion mMaybeHitRegion;
  // These are points that need to be dispatched to the content thread for
  // resolution. Always contained in the union of mHitRegion and mMaybeHitRegion.
  nsRegion mDispatchToContentHitRegion;
  // These are points where panning is disabled, as determined by the touch-action
  // property. Always contained in the union of mHitRegion and mMaybeHitRegion.
  nsRegion mNoActionRegion;
  // These are points where panning is horizontal, as determined by the touch-action
  // property. Always contained in the union of mHitRegion and mMaybeHitRegion.
  nsRegion mHorizontalPanRegion;
  // These are points where panning is vertical, as determined by the touch-action
  // property. Always contained in the union of mHitRegion and mMaybeHitRegion.
  nsRegion mVerticalPanRegion;
  // If these event regions are for an inactive scroll frame, the z-index of
  // this display item is overridden to be the largest z-index of the content
  // in the scroll frame. This ensures that the event regions item remains on
  // top of the content after sorting items by z-index.
  mozilla::Maybe<int32_t> mOverrideZIndex;
};

/**
 * 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 nsDisplayItem {
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,
                    nsDisplayList* aList,
                    const DisplayItemScrollClip* aScrollClip);
  nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                    nsDisplayItem* aItem);
  nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    : nsDisplayItem(aBuilder, aFrame), mOverrideZIndex(0), mHasZIndexOverride(false)
  {
    MOZ_COUNT_CTOR(nsDisplayWrapList);
    mBaseVisibleRect = mVisibleRect;
  }
  virtual ~nsDisplayWrapList();
  /**
   * Call this if the wrapped list is changed.
   */
  virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override
  {
    mBounds = mList.GetScrollClippedBoundsUpTo(aBuilder, mScrollClip);
    // 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.
    mVisibleRect.UnionRect(mBaseVisibleRect, mList.GetVisibleRect());
  }
  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                   bool* aSnap) override;
  virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) override;
  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;
  virtual bool TryMerge(nsDisplayItem* aItem) override {
    return false;
  }
  virtual void GetMergedFrames(nsTArray<nsIFrame*>* aFrames) override
  {
    aFrames->AppendElements(mMergedFrames);
  }
  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return true;
  }
  virtual bool IsInvalid(nsRect& aRect) 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();
  }
  NS_DISPLAY_DECL_NAME("WrapList", TYPE_WRAP_LIST)

  virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override;
                                    
  virtual nsDisplayList* GetSameCoordinateSystemChildren() override
  {
    NS_ASSERTION(mList.IsEmpty() || !ReferenceFrame() ||
                 !mList.GetBottom()->ReferenceFrame() ||
                 mList.GetBottom()->ReferenceFrame() == ReferenceFrame(),
                 "Children must have same reference frame");
    return &mList;
  }
  virtual nsDisplayList* GetChildren() override { return &mList; }

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

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

  void SetVisibleRect(const nsRect& aRect);

  void SetReferenceFrame(const nsIFrame* aFrame);

  /**
   * 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().
   */
  virtual nsDisplayWrapList* WrapWithClone(nsDisplayListBuilder* aBuilder,
                                           nsDisplayItem* aItem) {
    NS_NOTREACHED("We never returned nullptr for GetUnderlyingFrame!");
    return nullptr;
  }

protected:
  nsDisplayWrapList() {}

  void MergeFromTrackingMergedFrames(nsDisplayWrapList* aOther)
  {
    mList.AppendToBottom(&aOther->mList);
    mBounds.UnionRect(mBounds, aOther->mBounds);
    mVisibleRect.UnionRect(mVisibleRect, aOther->mVisibleRect);
    mMergedFrames.AppendElement(aOther->mFrame);
    mMergedFrames.AppendElements(mozilla::Move(aOther->mMergedFrames));
  }

  nsDisplayList mList;
  // The frames from items that have been merged into this item, excluding
  // this item's own frame.
  nsTArray<nsIFrame*> mMergedFrames;
  nsRect mBounds;
  // Visible rect contributed by this display item itself.
  // Our mVisibleRect may include the visible areas of children.
  nsRect mBaseVisibleRect;
  int32_t mOverrideZIndex;
  bool mHasZIndexOverride;
};

/**
 * 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 nsDisplayWrapper {
public:
  // This is never instantiated directly (it has pure virtual methods), so no
  // need to count constructors and destructors.

  virtual 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:
  nsDisplayWrapper() {}
};

/**
 * 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 DisplayItemScrollClip* aScrollClip,
                   bool aForEventsAndPluginsOnly);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayOpacity();
#endif

  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                   bool* aSnap) override;
  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;
  virtual bool TryMerge(nsDisplayItem* aItem) override;
  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) override
  {
    // We don't need to compute an invalidation region since we have LayerTreeInvalidation
  }
  virtual bool IsInvalid(nsRect& aRect) override
  {
    if (mForEventsAndPluginsOnly) {
      return false;
    }
    return nsDisplayWrapList::IsInvalid(aRect);
  }
  virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
                            float aOpacity,
                            const DisplayItemClip* aClip) override;
  virtual bool CanApplyOpacity() const override;
  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
  static bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
  NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
  virtual void WriteDebugInfo(std::stringstream& aStream) override;

  bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;

private:
  float mOpacity;
  bool mForEventsAndPluginsOnly;
};

class nsDisplayBlendMode : public nsDisplayWrapList {
public:
  nsDisplayBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                        nsDisplayList* aList, uint8_t aBlendMode,
                        const DisplayItemScrollClip* aScrollClip,
                        uint32_t aIndex = 0);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayBlendMode();
#endif

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

  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) override
  {
    // We don't need to compute an invalidation region since we have LayerTreeInvalidation
  }
  virtual uint32_t GetPerFrameKey() override {
    return (mIndex << nsDisplayItem::TYPE_BITS) |
      nsDisplayItem::GetPerFrameKey();
  }
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;
  virtual bool TryMerge(nsDisplayItem* aItem) override;
  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }
  NS_DISPLAY_DECL_NAME("BlendMode", TYPE_BLEND_MODE)

private:
  uint8_t mBlendMode;
  uint32_t mIndex;
};

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

    static nsDisplayBlendContainer*
    CreateForBackgroundBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                                 nsDisplayList* aList, const DisplayItemScrollClip* aScrollClip);

#ifdef NS_BUILD_REFCNT_LOGGING
    virtual ~nsDisplayBlendContainer();
#endif
    
    virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                               LayerManager* aManager,
                                               const ContainerLayerParameters& aContainerParameters) override;
    virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                     LayerManager* aManager,
                                     const ContainerLayerParameters& aParameters) override;
    virtual bool TryMerge(nsDisplayItem* aItem) override;
    virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
      return false;
    }
    virtual uint32_t GetPerFrameKey() override {
      return (mIsForBackground ? 1 << nsDisplayItem::TYPE_BITS : 0) |
        nsDisplayItem::GetPerFrameKey();
    }
    NS_DISPLAY_DECL_NAME("BlendContainer", TYPE_BLEND_CONTAINER)

private:
    nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                            nsDisplayList* aList,
                            const DisplayItemScrollClip* aScrollClip,
                            bool aIsForBackground);

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

/**
 * A display item that has no purpose but to ensure its contents get
 * their own layer.
 */
class nsDisplayOwnLayer : public nsDisplayWrapList {
public:

  /**
   * nsDisplayOwnLayer constructor flags
   */
  enum {
    GENERATE_SUBDOC_INVALIDATIONS = 0x01,
    VERTICAL_SCROLLBAR = 0x02,
    HORIZONTAL_SCROLLBAR = 0x04,
    GENERATE_SCROLLABLE_LAYER = 0x08,
    SCROLLBAR_CONTAINER = 0x10
  };

  /**
   * @param aFlags GENERATE_SUBDOC_INVALIDATIONS :
   * Add UserData to the created ContainerLayer, so that invalidations
   * for this layer are send to our nsPresContext.
   * GENERATE_SCROLLABLE_LAYER : only valid on nsDisplaySubDocument (and
   * subclasses), indicates this layer is to be a scrollable layer, so call
   * ComputeFrameMetrics, etc.
   * @param aScrollTarget when VERTICAL_SCROLLBAR or HORIZONTAL_SCROLLBAR
   * 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, uint32_t aFlags = 0,
                    ViewID aScrollTarget = mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
                    float aScrollbarThumbRatio = 0.0f,
                    bool aForceActive = true);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayOwnLayer();
#endif
  
  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;
  virtual bool TryMerge(nsDisplayItem* aItem) override
  {
    // Don't allow merging, each sublist must have its own layer
    return false;
  }
  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }
  uint32_t GetFlags() { return mFlags; }
  NS_DISPLAY_DECL_NAME("OwnLayer", TYPE_OWN_LAYER)
protected:
  uint32_t mFlags;
  ViewID mScrollTarget;
  float mScrollbarThumbRatio;
  bool mForceActive;
};

/**
 * 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,
                       nsDisplayList* aList, uint32_t aFlags);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplaySubDocument();
#endif

  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;

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

  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;

  virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) override;

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

  NS_DISPLAY_DECL_NAME("SubDocument", TYPE_SUBDOCUMENT)

  mozilla::UniquePtr<ScrollMetadata> ComputeScrollMetadata(Layer* aLayer,
                                                           const ContainerLayerParameters& aContainerParameters);

protected:
  ViewID mScrollParentId;
  bool mForceDispatchToContentRegion;
};

/**
 * A display item for subdocuments to capture the resolution from the presShell
 * and ensure that it gets applied to all the right elements. This item creates
 * a container layer.
 */
class nsDisplayResolution : public nsDisplaySubDocument {
public:
  nsDisplayResolution(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      nsDisplayList* aList, uint32_t aFlags);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayResolution();
#endif
  virtual void HitTest(nsDisplayListBuilder* aBuilder,
                       const nsRect& aRect,
                       HitTestState* aState,
                       nsTArray<nsIFrame*> *aOutFrames) override;
  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  NS_DISPLAY_DECL_NAME("Resolution", TYPE_RESOLUTION)
};

/**
 * 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);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayStickyPosition();
#endif

  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  NS_DISPLAY_DECL_NAME("StickyPosition", TYPE_STICKY_POSITION)
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override
  {
    return mozilla::LAYER_ACTIVE;
  }
  virtual bool TryMerge(nsDisplayItem* aItem) override;
};

class nsDisplayFixedPosition : public nsDisplayOwnLayer {
public:
  nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                          nsDisplayList* aList);

  static nsDisplayFixedPosition* CreateForFixedBackground(nsDisplayListBuilder* aBuilder,
                                                          nsIFrame* aFrame,
                                                          nsDisplayBackgroundImage* aImage,
                                                          uint32_t aIndex);


#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayFixedPosition();
#endif

  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  NS_DISPLAY_DECL_NAME("FixedPosition", TYPE_FIXED_POSITION)
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override
  {
    return mozilla::LAYER_ACTIVE;
  }
  virtual bool TryMerge(nsDisplayItem* aItem) override;

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

  virtual uint32_t GetPerFrameKey() override { return (mIndex << nsDisplayItem::TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); }

  AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const override {
    return mAnimatedGeometryRootForScrollMetadata;
  }

private:
  // For background-attachment:fixed
  nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                         nsDisplayList* aList, uint32_t aIndex);
  void Init(nsDisplayListBuilder* aBuilder);

  AnimatedGeometryRoot* mAnimatedGeometryRootForScrollMetadata;
  uint32_t mIndex;
  bool mIsFixedBackground;
};

/**
 * 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);
  NS_DISPLAY_DECL_NAME("ScrollInfoLayer", TYPE_SCROLL_INFO_LAYER)


#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayScrollInfoLayer();
#endif

  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;

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

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

  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;

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

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

  mozilla::UniquePtr<ScrollMetadata> ComputeScrollMetadata(Layer* aLayer,
                                                           const ContainerLayerParameters& aContainerParameters);

protected:
  nsIFrame* mScrollFrame;
  nsIFrame* mScrolledFrame;
  ViewID mScrollParentId;
};

/**
 * 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 GENERATE_SUBDOC_INVALIDATIONS :
   * Add UserData to the created ContainerLayer, so that invalidations
   * for this layer are send to our nsPresContext.
   */
  nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                nsDisplayList* aList,
                int32_t aAPD, int32_t aParentAPD,
                uint32_t aFlags = 0);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayZoom();
#endif
  
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override
  {
    return mozilla::LAYER_ACTIVE;
  }
  NS_DISPLAY_DECL_NAME("Zoom", TYPE_ZOOM)

  // 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;
};

class nsDisplaySVGEffects: public nsDisplayWrapList {
public:
  nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      nsDisplayList* aList, bool aHandleOpacity,
                      const DisplayItemScrollClip* aScrollClip);
  nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      nsDisplayList* aList, bool aHandleOpacity);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplaySVGEffects();
#endif

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

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

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

  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) override;
protected:
  bool ValidateSVGFrame();

  // relative to mFrame
  nsRect mEffectsBounds;
  // True if we need to handle css opacity in this display item.
  bool mHandleOpacity;
};

/**
 * A display item to paint a stacking context with mask and clip effects
 * set by the stacking context root frame's style.
 */
class nsDisplayMask : public nsDisplaySVGEffects {
public:
  typedef mozilla::layers::ImageLayer ImageLayer;
  typedef class mozilla::gfx::DrawTarget DrawTarget;

  nsDisplayMask(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                nsDisplayList* aList, bool aHandleOpacity,
                const DisplayItemScrollClip* aScrollClip);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayMask();
#endif

  NS_DISPLAY_DECL_NAME("Mask", TYPE_MASK)

  virtual bool TryMerge(nsDisplayItem* aItem) override;
  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;

  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;

  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
  {
    return new nsDisplayMaskGeometry(this, aBuilder);
  }
  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) override;
#ifdef MOZ_DUMP_PAINTING
  void PrintEffects(nsACString& aTo);
#endif

  void PaintAsLayer(nsDisplayListBuilder* aBuilder,
                    nsRenderingContext* aCtx,
                    LayerManager* aManager);

  /*
   * Paint mask onto aMaskContext in mFrame's coordinate space.
   */
  bool PaintMask(nsDisplayListBuilder* aBuilder, gfxContext* aMaskContext);

  const nsTArray<nsRect>& GetDestRects()
  {
    return mDestRects;
  }
private:
  // According to mask property and the capability of aManager, determine
  // whether paint mask onto a dedicate mask layer.
  bool ShouldPaintOnMaskLayer(LayerManager* aManager);

  nsTArray<nsRect> mDestRects;
};

/**
 * A display item to paint a stacking context with filter effects set by the
 * stacking context root frame's style.
 */
class nsDisplayFilter : public nsDisplaySVGEffects {
public:
  nsDisplayFilter(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                  nsDisplayList* aList, bool aHandleOpacity);
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayFilter();
#endif

  NS_DISPLAY_DECL_NAME("Filter", TYPE_FILTER)

  virtual bool TryMerge(nsDisplayItem* aItem) override;
  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;
  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                           bool* aSnap) override {
    *aSnap = false;
    return mEffectsBounds + ToReferenceFrame();
  }
  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override;
  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
  {
    return new nsDisplayFilterGeometry(this, aBuilder);
  }
  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) override;
#ifdef MOZ_DUMP_PAINTING
  void PrintEffects(nsACString& aTo);
#endif

  void PaintAsLayer(nsDisplayListBuilder* aBuilder,
                    nsRenderingContext* aCtx,
                    LayerManager* aManager);
};

/* 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 nsDisplayItem
{
  typedef mozilla::gfx::Matrix4x4 Matrix4x4;
  typedef mozilla::gfx::Point3D Point3D;

  /*
   * Avoid doing UpdateBounds() during construction.
   */
  class StoreList : public nsDisplayWrapList {
  public:
    StoreList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
              nsDisplayList* aList) :
      nsDisplayWrapList(aBuilder, aFrame, aList) {}
    StoreList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
              nsDisplayItem* aItem) :
      nsDisplayWrapList(aBuilder, aFrame, aItem) {}
    virtual ~StoreList() {}

    virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override {
      // For extending 3d rendering context, the bounds would be
      // updated by DoUpdateBoundsPreserves3D(), not here.
      if (!mFrame->Extend3DContext()) {
        nsDisplayWrapList::UpdateBounds(aBuilder);
      }
    }
    virtual void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
      for (nsDisplayItem *i = mList.GetBottom(); i; i = i->GetAbove()) {
        i->DoUpdateBoundsPreserves3D(aBuilder);
      }
      nsDisplayWrapList::UpdateBounds(aBuilder);
    }
  };

public:
  /**
   * Returns a matrix (in pixels) for the current frame. The matrix should be relative to
   * the current frame's coordinate space.
   *
   * @param aFrame The frame to compute the transform for.
   * @param aAppUnitsPerPixel The number of app units per graphics unit.
   */
  typedef Matrix4x4 (* ComputeTransformFunction)(nsIFrame* aFrame, float aAppUnitsPerPixel);

  /* 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& aChildrenVisibleRect,
                     uint32_t aIndex = 0, bool aIsFullyVisible = false);
  nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
                     nsDisplayItem *aItem, const nsRect& aChildrenVisibleRect,
                     uint32_t aIndex = 0);
  nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
                     nsDisplayList *aList, const nsRect& aChildrenVisibleRect,
                     ComputeTransformFunction aTransformGetter, uint32_t aIndex = 0);
  nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
                     nsDisplayList *aList, const nsRect& aChildrenVisibleRect,
                     const Matrix4x4& aTransform, uint32_t aIndex = 0);

#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayTransform()
  {
    MOZ_COUNT_DTOR(nsDisplayTransform);
  }
#endif

  NS_DISPLAY_DECL_NAME("nsDisplayTransform", TYPE_TRANSFORM)

  virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
  {
    if (mStoredList.GetComponentAlphaBounds(aBuilder).IsEmpty())
      return nsRect();
    bool snap;
    return GetBounds(aBuilder, &snap);
  }

  virtual nsDisplayList* GetChildren() override { return mStoredList.GetChildren(); }

  virtual void HitTest(nsDisplayListBuilder *aBuilder, const nsRect& aRect,
                       HitTestState *aState, nsTArray<nsIFrame*> *aOutFrames) override;
  virtual nsRect GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap) override;
  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder *aBuilder,
                                   bool* aSnap) override;
  virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder *aBuilder) override;
  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;
  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;
  virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) override;
  virtual bool ComputeVisibility(nsDisplayListBuilder *aBuilder,
                                 nsRegion *aVisibleRegion) override;
  virtual bool TryMerge(nsDisplayItem *aItem) override;
  
  virtual uint32_t GetPerFrameKey() override { return (mIndex << nsDisplayItem::TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); }
  
  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayItemGeometry* aGeometry,
                                         nsRegion* aInvalidRegion) override
  {
    // We don't need to compute an invalidation region since we have LayerTreeInvalidation
  }

  virtual 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 (!mTransformGetter) {
      return mFrame;
    }
    return nsDisplayItem::ReferenceFrameForChildren(); 
  }

  AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const override {
    return mAnimatedGeometryRootForScrollMetadata;
  }

  virtual const nsRect& GetVisibleRectForChildren() const override
  {
    return mChildrenVisibleRect;
  }

  enum {
    INDEX_MAX = UINT32_MAX >> nsDisplayItem::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 Matrix4x4& GetTransform();
  Matrix4x4 GetTransformForRendering();

  /**
   * 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 aOrigin The origin of the transform relative to aFrame's local
   *        coordinate space.
   * @param aBoundsOverride (optional) Rather than using the frame's computed
   *        bounding rect as frame bounds, use this rectangle instead.  Pass
   *        nullptr (or nothing at all) to use the default.
   */
  static nsRect TransformRect(const nsRect &aUntransformedBounds, 
                              const nsIFrame* aFrame,
                              const nsRect* aBoundsOverride = nullptr);

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

  bool UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
                              nsRect* aOutRect);

  static Point3D GetDeltaToTransformOrigin(const nsIFrame* aFrame,
                                           float aAppUnitsPerPixel,
                                           const nsRect* aBoundsOverride);

  /*
   * 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 FrameTransformProperties
  {
    FrameTransformProperties(const nsIFrame* aFrame,
                             float aAppUnitsPerPixel,
                             const nsRect* aBoundsOverride);
    FrameTransformProperties(nsCSSValueSharedList* aTransformList,
                             const Point3D& aToTransformOrigin)
      : mFrame(nullptr)
      , mTransformList(aTransformList)
      , mToTransformOrigin(aToTransformOrigin)
    {}

    const nsIFrame* mFrame;
    RefPtr<nsCSSValueSharedList> mTransformList;
    const Point3D mToTransformOrigin;
  };

  /**
   * Given a frame with the -moz-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 Matrix4x4 GetResultingTransformMatrix(const nsIFrame* aFrame,
                                               const nsPoint& aOrigin,
                                               float aAppUnitsPerPixel,
                                               uint32_t aFlags,
                                               const nsRect* aBoundsOverride = nullptr);
  static Matrix4x4 GetResultingTransformMatrix(const FrameTransformProperties& aProperties,
                                               const nsPoint& aOrigin,
                                               float aAppUnitsPerPixel,
                                               uint32_t aFlags,
                                               const nsRect* aBoundsOverride = nullptr);
  /**
   * Return true when we should try to prerender the entire contents of the
   * transformed frame even when it's not completely visible (yet).
   */
  static bool ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
                                                nsIFrame* aFrame);
  bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;

  bool MayBeAnimated(nsDisplayListBuilder* aBuilder);

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

  // Force the layer created for this item not to extend 3D context.
  // See nsIFrame::BuildDisplayListForStackingContext()
  void SetNoExtendContext() { mNoExtendContext = true; }

  virtual void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
    MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
               IsTransformSeparator());
    // Updating is not going through to child 3D context.
    ComputeBounds(aBuilder);
  }

  /**
   * This function updates bounds for items with a frame establishing
   * 3D rendering context.
   *
   * \see nsDisplayItem::DoUpdateBoundsPreserves3D()
   */
  void UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
    if (!mFrame->Extend3DContext() ||
        mFrame->Combines3DTransformWithAncestors() ||
        IsTransformSeparator()) {
      // Not an establisher of a 3D rendering context.
      return;
    }
    // Always start updating from an establisher of a 3D rendering context.

    nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
    nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
    accTransform.StartRoot();
    ComputeBounds(aBuilder);
    mBounds = aBuilder->GetAccumulatedRect();
    mHasBounds = true;
  }

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

private:
  void ComputeBounds(nsDisplayListBuilder* aBuilder);
  void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
  void Init(nsDisplayListBuilder* aBuilder);

  static Matrix4x4 GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
                                                       const nsPoint& aOrigin,
                                                       float aAppUnitsPerPixel,
                                                       uint32_t aFlags,
                                                       const nsRect* aBoundsOverride);

  StoreList mStoredList;
  Matrix4x4 mTransform;
  // Accumulated transform of ancestors on the preserves-3d chain.
  Matrix4x4 mTransformPreserves3D;
  ComputeTransformFunction mTransformGetter;
  AnimatedGeometryRoot* mAnimatedGeometryRootForChildren;
  AnimatedGeometryRoot* mAnimatedGeometryRootForScrollMetadata;
  nsRect mChildrenVisibleRect;
  uint32_t mIndex;
  nsRect mBounds;
  // True for mBounds is valid.
  bool mHasBounds;
  // Be forced not to extend 3D context.  Since we don't create a
  // transform item, a container layer, for every frames in a
  // preserves3d context, the transform items of a child preserves3d
  // context may extend the parent context not intented if the root of
  // the child preserves3d context doesn't create a transform item.
  // With this flags, we force the item not extending 3D context.
  bool mNoExtendContext;
  // This item is a separator between 3D rendering contexts, and
  // mTransform have been presetted by the constructor.
  bool mIsTransformSeparator;
  // True if mTransformPreserves3D have been initialized.
  bool mTransformPreserves3DInited;
  // True if the entire untransformed area has been treated as
  // visible during display list construction.
  bool mIsFullyVisible;
};

/* 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 nsDisplayItem
{
  typedef mozilla::gfx::Point3D Point3D;

public:
  NS_DISPLAY_DECL_NAME("nsDisplayPerspective", TYPE_PERSPECTIVE)

  nsDisplayPerspective(nsDisplayListBuilder* aBuilder, nsIFrame* aTransformFrame,
                       nsIFrame* aPerspectiveFrame,
                       nsDisplayList* aList);

  virtual uint32_t GetPerFrameKey() override { return (mIndex << nsDisplayItem::TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); }

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

  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
  {
    return mList.GetBounds(aBuilder, aSnap);
  }

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

  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                   bool* aSnap) override
  {
    return mList.GetOpaqueRegion(aBuilder, aSnap);
  }

  virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) override
  {
    return mList.IsUniform(aBuilder);
  }

  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) override;

  virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) override
  {
    if (!mList.GetChildren()->GetTop()) {
      return false;
    }
    return mList.GetChildren()->GetTop()->ShouldBuildLayerEvenIfInvisible(aBuilder);
  }

  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                             LayerManager* aManager,
                                             const ContainerLayerParameters& aContainerParameters) override;

  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                 nsRegion* aVisibleRegion) override
  {
    mList.RecomputeVisibility(aBuilder, aVisibleRegion);
    return true;
  }
  virtual nsDisplayList* GetSameCoordinateSystemChildren() override { return mList.GetChildren(); }
  virtual nsDisplayList* GetChildren() override { return mList.GetChildren(); }
  virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
  {
    return mList.GetComponentAlphaBounds(aBuilder);
  }

  nsIFrame* TransformFrame() { return mTransformFrame; }

  virtual int32_t ZIndex() const override;

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

private:
  nsDisplayWrapList mList;
  nsIFrame* mTransformFrame;
  uint32_t mIndex;
};

/**
 * This class adds basic support for limiting the rendering (in the inline axis
 * of the writing mode) to the part inside the specified edges.  It's a base
 * class for the display item classes that do the actual work.
 * 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 nsCharClipDisplayItem : public nsDisplayItem {
public:
  nsCharClipDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    : nsDisplayItem(aBuilder, aFrame), mVisIStartEdge(0), mVisIEndEdge(0) {}

  explicit nsCharClipDisplayItem(nsIFrame* aFrame)
    : nsDisplayItem(aFrame) {}

  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;

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

  struct ClipEdges {
    ClipEdges(const nsDisplayItem& aItem,
              nscoord aVisIStartEdge, nscoord aVisIEndEdge) {
      nsRect r = aItem.Frame()->GetScrollableOverflowRect() +
                 aItem.ToReferenceFrame();
      if (aItem.Frame()->GetWritingMode().IsVertical()) {
        mVisIStart = aVisIStartEdge > 0 ? r.y + aVisIStartEdge : nscoord_MIN;
        mVisIEnd =
          aVisIEndEdge > 0 ? std::max(r.YMost() - aVisIEndEdge, mVisIStart)
                           : nscoord_MAX;
      } else {
        mVisIStart = aVisIStartEdge > 0 ? r.x + aVisIStartEdge : nscoord_MIN;
        mVisIEnd =
          aVisIEndEdge > 0 ? std::max(r.XMost() - aVisIEndEdge, mVisIStart)
                           : nscoord_MAX;
      }
    }
    void Intersect(nscoord* aVisIStart, nscoord* aVisISize) const {
      nscoord end = *aVisIStart + *aVisISize;
      *aVisIStart = std::max(*aVisIStart, mVisIStart);
      *aVisISize = std::max(std::min(end, mVisIEnd) - *aVisIStart, 0);
    }
    nscoord mVisIStart;
    nscoord mVisIEnd;
  };

  ClipEdges Edges() const {
    return ClipEdges(*this, mVisIStartEdge, mVisIEndEdge);
  }

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

  // 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;
  // Cached result of mFrame->IsSelected().  Only initialized when needed.
  mutable mozilla::Maybe<bool> mIsFrameSelected;
};

namespace mozilla {

class PaintTelemetry
{
 public:
  enum class Metric {
    DisplayList,
    Layerization,
    Rasterization,
    COUNT,
  };

  class AutoRecord
  {
   public:
    explicit AutoRecord(Metric aMetric);
    ~AutoRecord();
   private:
    Metric mMetric;
    mozilla::TimeStamp mStart;
  };

  class AutoRecordPaint
  {
   public:
    AutoRecordPaint();
    ~AutoRecordPaint();
   private:
    mozilla::TimeStamp mStart;
  };

 private:
  static uint32_t sPaintLevel;
  static uint32_t sMetricLevel;
  static mozilla::EnumeratedArray<Metric, Metric::COUNT, double> sMetrics;
};

} // namespace mozilla

#endif /*NSDISPLAYLIST_H_*/
