gecko-dev/layout/base/nsDisplayList.h

3734 lines
145 KiB
C++

/* -*- 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 "nsCOMPtr.h"
#include "nsContainerFrame.h"
#include "nsPoint.h"
#include "nsRect.h"
#include "plarena.h"
#include "Layers.h"
#include "nsRegion.h"
#include "nsDisplayListInvalidation.h"
#include "DisplayListClipState.h"
#include "LayerState.h"
#include "FrameMetrics.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/UserData.h"
#include <stdint.h>
#include "nsTHashtable.h"
#include <stdlib.h>
#include <algorithm>
class nsIContent;
class nsRenderingContext;
class nsDisplayTableItem;
class nsISelection;
class nsDisplayLayerEventRegions;
class nsDisplayScrollInfoLayer;
class nsCaret;
namespace mozilla {
class FrameLayerBuilder;
namespace layers {
class Layer;
class ImageLayer;
class ImageContainer;
} //namespace
namespace gfx {
class VRHMDInfo;
} //namespace
} //namespace
// 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; }
/**
* 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 {
public:
typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor;
typedef mozilla::FrameLayerBuilder FrameLayerBuilder;
typedef mozilla::DisplayItemClip DisplayItemClip;
typedef mozilla::DisplayListClipState DisplayListClipState;
typedef nsIWidget::ThemeGeometry ThemeGeometry;
typedef mozilla::layers::Layer Layer;
typedef mozilla::layers::FrameMetrics FrameMetrics;
typedef mozilla::layers::FrameMetrics::ViewID ViewID;
/**
* @param aReferenceFrame the frame at the root of the subtree; its origin
* is the origin of the reference coordinate system for this display list
* @param aMode encodes what the builder is being used for.
* @param aBuildCaret whether or not we should include the caret in any
* display lists that we make.
*/
enum Mode {
PAINTING,
EVENT_DELIVERY,
PLUGIN_GEOMETRY,
IMAGE_VISIBILITY,
OTHER
};
nsDisplayListBuilder(nsIFrame* aReferenceFrame, Mode aMode, bool aBuildCaret);
~nsDisplayListBuilder();
void SetWillComputePluginGeometry(bool aWillComputePluginGeometry)
{
mWillComputePluginGeometry = aWillComputePluginGeometry;
}
void SetForPluginGeometry()
{
NS_ASSERTION(mMode == PAINTING, "Can only switch from PAINTING to PLUGIN_GEOMETRY");
NS_ASSERTION(mWillComputePluginGeometry, "Should have signalled this in advance");
mMode = PLUGIN_GEOMETRY;
}
/**
* @return true if the display is being built in order to determine which
* frame is under the mouse position.
*/
bool IsForEventDelivery() { return mMode == 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 == PLUGIN_GEOMETRY; }
/**
* @return true if the display list is being built for painting.
*/
bool IsForPainting() { return mMode == PAINTING; }
/**
* @return true if the display list is being built for determining image
* visibility.
*/
bool IsForImageVisibility() { return mMode == IMAGE_VISIBILITY; }
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);
/**
* 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* FindAnimatedGeometryRootFor(nsIFrame* aFrame, const nsIFrame* aStopAtAncestor = 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;
}
/**
* 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; }
/**
* @return Returns if this builder had to ignore painting suppression on some
* document when building the display list.
*/
bool GetHadToIgnorePaintSuppression() { return mHadToIgnoreSuppression; }
/**
* 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; }
const nsIFrame* GetCurrentAnimatedGeometryRoot() {
return mCurrentAnimatedGeometryRoot;
}
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()
{
if (mMode == PAINTING) {
// Note: this is the only place that gets to query LayoutEventRegionsEnabled
// 'directly' - other code should call this function.
return gfxPrefs::LayoutEventRegionsEnabledDoNotUseDirectly() ||
mAsyncPanZoomEnabled;
}
return false;
}
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 without mozpasspointerevents.
*/
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);
/**
* 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 root
* presshell which is the presshell of a chrome document, or if we're
* building the display list for a popup and have not entered a subdocument
* inside that popup.
*/
bool IsInRootChromeDocumentOrPopup() {
return (mIsInChromePresContext || mIsBuildingForPopup) && !IsInSubdocument();
}
/**
* @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 nsRect& aDirtyRect);
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_MOZ_MAC_UNIFIED_TOOLBAR, 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 nsIntRect& 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.
*/
void AdjustWindowDraggingRegion(nsIFrame* aFrame);
const nsIntRegion& GetWindowDraggingRegion() { return mWindowDraggingRegion; }
/**
* 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 DisplayListClip in the arena. Will be cleaned up
* automatically when the arena goes away.
*/
const DisplayItemClip* AllocateDisplayItemClip(const DisplayItemClip& aOriginal);
/**
* 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,
nsCSSProperty 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),
mPrevAnimatedGeometryRoot(mBuilder->mCurrentAnimatedGeometryRoot),
mPrevLayerEventRegions(aBuilder->mLayerEventRegions),
mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
mPrevDirtyRect(aBuilder->mDirtyRect),
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
mPrevAncestorHasApzAwareEventHandler(aBuilder->mAncestorHasApzAwareEventHandler)
{
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->mCurrentAnimatedGeometryRoot = aForChild;
}
} else {
// Stop at the previous animated geometry root to help cases that
// aren't immediate descendents.
aBuilder->mCurrentAnimatedGeometryRoot =
aBuilder->FindAnimatedGeometryRootFor(aForChild, aBuilder->mCurrentAnimatedGeometryRoot);
}
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;
}
~AutoBuildingDisplayList() {
mBuilder->mCurrentFrame = mPrevFrame;
mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame;
mBuilder->mLayerEventRegions = mPrevLayerEventRegions;
mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset;
mBuilder->mDirtyRect = mPrevDirtyRect;
mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext;
mBuilder->mAncestorHasApzAwareEventHandler = mPrevAncestorHasApzAwareEventHandler;
mBuilder->mCurrentAnimatedGeometryRoot = mPrevAnimatedGeometryRoot;
}
private:
nsDisplayListBuilder* mBuilder;
const nsIFrame* mPrevFrame;
const nsIFrame* mPrevReferenceFrame;
nsIFrame* mPrevAnimatedGeometryRoot;
nsDisplayLayerEventRegions* mPrevLayerEventRegions;
nsPoint mPrevOffset;
nsRect mPrevDirtyRect;
bool mPrevIsAtRootOfPseudoStackingContext;
bool mPrevAncestorHasApzAwareEventHandler;
};
/**
* 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;
};
/**
* 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)
: mBuilder(aBuilder) {
aBuilder->mCurrentScrollbarTarget = aScrollTargetID;
aBuilder->mCurrentScrollbarFlags = aScrollbarFlags;
}
~AutoCurrentScrollbarInfoSetter() {
// No need to restore old values because scrollbars cannot be nested.
mBuilder->mCurrentScrollbarTarget = FrameMetrics::NULL_SCROLL_ID;
mBuilder->mCurrentScrollbarFlags = 0;
}
private:
nsDisplayListBuilder* mBuilder;
};
// Helpers for tables
nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; }
void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; }
struct OutOfFlowDisplayData {
OutOfFlowDisplayData(const DisplayItemClip& aContainingBlockClip,
const nsRect &aDirtyRect)
: mContainingBlockClip(aContainingBlockClip)
, mDirtyRect(aDirtyRect)
{}
explicit OutOfFlowDisplayData(const nsRect &aDirtyRect)
: mDirtyRect(aDirtyRect)
{}
DisplayItemClip mContainingBlockClip;
nsRect mDirtyRect;
};
NS_DECLARE_FRAME_PROPERTY(OutOfFlowDisplayDataProperty,
DeleteValue<OutOfFlowDisplayData>)
NS_DECLARE_FRAME_PROPERTY(Preserve3DDirtyRectProperty, DeleteValue<nsRect>)
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(uint8_t aBlendMode);
void SetContainsBlendModes(const BlendModeSet& aModes) {
mContainedBlendModes = aModes;
}
bool ContainsBlendMode() const { return !mContainedBlendModes.isEmpty(); }
BlendModeSet& ContainedBlendModes() {
return mContainedBlendModes;
}
DisplayListClipState& ClipState() { return mClipState; }
/**
* The will-change budget is calculated during the display list building
* phase for all the frames that want will change on a per-document basis.
* The cost should be fully calculated during the layer building phase
* and a decission to allow or disallow will-change for all frames of
* that document will be made by IsInWillChangeBudget.
*/
void AddToWillChangeBudget(nsIFrame* aFrame, const nsSize& aSize);
bool IsInWillChangeBudget(nsIFrame* aFrame) const;
/**
* Look up the cached animated geometry root for aFrame subject to
* aStopAtAncestor. Store the nsIFrame* result into *aOutResult, and return
* true if the cache was hit. Return false if the cache was not hit.
*/
bool GetCachedAnimatedGeometryRoot(const nsIFrame* aFrame,
const nsIFrame* aStopAtAncestor,
nsIFrame** aOutResult);
void EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage);
void ExitSVGEffectsContents();
bool ShouldBuildScrollInfoItemsForHoisting() const
{ return mSVGEffectsBuildingDepth > 0; }
void AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer* aScrollInfoItem);
/**
* Store the dirty rect of the scrolled contents of aScrollableFrame. This
* is a bound for the extents of the new visible region of the scrolled
* layer.
* @param aScrollableFrame the scrollable frame
* @param aDirty the dirty rect, relative to aScrollableFrame
*/
void StoreDirtyRectForScrolledContents(const nsIFrame* aScrollableFrame, const nsRect& aDirty);
/**
* Retrieve the stored dirty rect for the scrolled contents of aScrollableFrame.
* @param aScrollableFrame the scroll frame
* @return the dirty rect, relative to aScrollableFrame's *reference frame*
*/
nsRect GetDirtyRectForScrolledContents(const nsIFrame* aScrollableFrame) const;
private:
void MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
const nsRect& aDirtyRect);
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 that doesn't have mozpasspointerevents set.
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* mReferenceFrame;
nsIFrame* mIgnoreScrollFrame;
nsDisplayLayerEventRegions* mLayerEventRegions;
PLArenaPool mPool;
nsCOMPtr<nsISelection> mBoundingSelection;
nsAutoTArray<PresShellState,8> mPresShellStates;
nsAutoTArray<nsIFrame*,100> mFramesMarkedForDisplay;
nsAutoTArray<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;
// The animated geometry root for mCurrentFrame.
nsIFrame* mCurrentAnimatedGeometryRoot;
struct AnimatedGeometryRootLookup {
const nsIFrame* mFrame;
const nsIFrame* mStopAtFrame;
AnimatedGeometryRootLookup(const nsIFrame* aFrame, const nsIFrame* aStopAtFrame)
: mFrame(aFrame)
, mStopAtFrame(aStopAtFrame)
{
}
PLDHashNumber Hash() const {
return mozilla::HashBytes(this, sizeof(this));
}
bool operator==(const AnimatedGeometryRootLookup& aOther) const {
return mFrame == aOther.mFrame && mStopAtFrame == aOther.mStopAtFrame;
}
};
// Cache for storing animated geometry roots for arbitrary frames
nsDataHashtable<nsGenericHashKey<AnimatedGeometryRootLookup>, nsIFrame*>
mAnimatedGeometryRootCache;
// will-change budget tracker
nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
mWillChangeBudget;
// Assert that we never check the budget before its fully calculated.
mutable mozilla::DebugOnly<bool> mWillChangeBudgetCalculated;
// rects are relative to the frame's reference frame
nsDataHashtable<nsPtrHashKey<nsIFrame>, nsRect> mDirtyRectForScrolledContents;
// Relative to mCurrentFrame.
nsRect mDirtyRect;
nsRegion mWindowExcludeGlassRegion;
nsRegion mWindowOpaqueRegion;
nsIntRegion mWindowDraggingRegion;
// 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<DisplayItemClip*> mDisplayItemClipsToDestroy;
Mode mMode;
ViewID mCurrentScrollParentId;
ViewID mCurrentScrollbarTarget;
uint32_t mCurrentScrollbarFlags;
BlendModeSet mContainedBlendModes;
int32_t mSVGEffectsBuildingDepth;
bool mBuildCaret;
bool mIgnoreSuppression;
bool mHadToIgnoreSuppression;
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;
};
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.
*
* nsDisplayListItems 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::layers::FrameMetrics FrameMetrics;
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);
/**
* This constructor is only used in rare cases when we need to construct
* temporary items.
*/
explicit nsDisplayItem(nsIFrame* aFrame)
: mFrame(aFrame)
, mClip(nullptr)
, mReferenceFrame(nullptr)
#ifdef MOZ_DUMP_PAINTING
, mPainted(false)
#endif
{
}
virtual ~nsDisplayItem() {}
void* operator new(size_t aSize,
nsDisplayListBuilder* aBuilder) CPP_THROW_NEW {
return aBuilder->Allocate(aSize);
}
// Contains all the type integers for each display list item type
#include "nsDisplayItemTypes.h"
struct HitTestState {
explicit HitTestState() {}
~HitTestState() {
NS_ASSERTION(mItemBuffer.Length() == 0,
"mItemBuffer should have been cleared");
}
nsAutoTArray<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();
}
/**
* If this returns true, then aColor is set to the uniform color
* @return true if the item is guaranteed to paint every pixel in its
* bounds with the same (possibly translucent) color
*/
virtual bool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { return false; }
/**
* @return true if the contents of this item are rendered fixed relative
* to the nearest viewport.
*/
virtual bool ShouldFixToViewport(LayerManager* aManager)
{ return false; }
virtual bool ClearsBackground()
{ return false; }
virtual bool ProvidesFontSmoothingBackgroundColor(nsDisplayListBuilder* aBuilder,
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();
/**
* Returns the maximum number of layers that should be created
* or -1 for no limit. Requires setting the pref layers.max-acitve.
*/
static int32_t MaxActiveLayers();
/**
* @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(nsDisplayListBuilder* aBuilder, 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) {}
/**
* 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; }
/**
* 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);
}
}
protected:
friend class nsDisplayList;
nsDisplayItem() { mAbove = nullptr; }
nsIFrame* mFrame;
const DisplayItemClip* mClip;
// Result of FindReferenceFrameFor(mFrame), if mFrame is non-null
const nsIFrame* mReferenceFrame;
// 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;
#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::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. If the item is null we return
* NS_ERROR_OUT_OF_MEMORY. The intended usage is AppendNewToTop(new ...);
*/
void AppendNewToTop(nsDisplayItem* aItem) {
if (aItem) {
AppendToTop(aItem);
}
}
/**
* Append a new item to the bottom of the list. If the item is null we return
* NS_ERROR_OUT_OF_MEMORY. 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(nsDisplayListBuilder* aBuilder);
/**
* 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(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor);
/**
* Stable sort this list by CSS 'order' property order.
* http://dev.w3.org/csswg/css-flexbox-1/#order-property
* (also applies to CSS Grid although it's in the Flexbox spec ATM)
* It is assumed that the list is already in document content order.
*/
void SortByCSSOrder(nsDisplayListBuilder* aBuilder);
/**
* 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(nsDisplayListBuilder* aBuilder, 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.
* @param aDisplayPortFrame If the item for which this list corresponds is
* within a displayport, the scroll frame for which that display port
* applies. For root scroll frames, you can pass the the root frame instead.
* @return true if any item in the list is visible.
*/
bool ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
const nsRect& aListVisibleBounds,
nsIFrame* aDisplayPortFrame = nullptr);
/**
* 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
* @param aDisplayPortFrame The root scroll frame, if a displayport is set
*/
bool ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
nsIFrame* aDisplayPortFrame = nullptr);
/**
* 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_FLUSH_LAYERS is set, we'll force a completely new layer
* tree to be created for this paint *and* the next paint.
*
* 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_FLUSH_LAYERS = 0x02,
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;
/**
* 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 by ComputeVisibility
nsRect mVisibleRect;
// 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(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor) {
for (int32_t i = 0; i < 6; ++i) {
mLists[i].SortByContentOrder(aBuilder, 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) = 0;
virtual already_AddRefed<ImageContainer> GetContainer(LayerManager* aManager,
nsDisplayListBuilder* aBuilder) = 0;
virtual void ConfigureLayer(ImageLayer* aLayer,
const ContainerLayerParameters& aParameters) = 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 void (* PaintCallback)(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)
, 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 {
mPaint(mFrame, aCtx, mVisibleRect, ToReferenceFrame());
}
NS_DISPLAY_DECL_NAME(mName, mType)
protected:
PaintCallback mPaint;
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);
}
#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:
nsRefPtr<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 nsDisplaySolidColor : public nsDisplayItem {
public:
nsDisplaySolidColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
const nsRect& aBounds, nscolor aColor)
: nsDisplayItem(aBuilder, aFrame), mBounds(aBounds), mColor(aColor)
{
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 nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
bool* aSnap) override {
*aSnap = false;
nsRegion result;
if (NS_GET_A(mColor) == 255) {
result = GetBounds(aBuilder, aSnap);
}
return result;
}
virtual bool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) override
{
*aColor = mColor;
return true;
}
virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
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 void WriteDebugInfo(std::stringstream& aStream) override;
NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)
private:
nsRect mBounds;
nscolor 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.
*/
nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
uint32_t aLayer,
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.
static bool AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
nsDisplayList* aList);
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 bool IsUniform(nsDisplayListBuilder* aBuilder, 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;
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<ImageContainer> GetContainer(LayerManager* aManager,
nsDisplayListBuilder *aBuilder) override;
virtual void ConfigureLayer(ImageLayer* aLayer,
const ContainerLayerParameters& aParameters) override;
static nsRegion GetInsideClipRegion(nsDisplayItem* aItem, nsPresContext* aPresContext, uint8_t aClip,
const nsRect& aRect, bool* aSnap);
virtual bool ShouldFixToViewport(LayerManager* aManager) override;
protected:
typedef class mozilla::layers::ImageContainer ImageContainer;
typedef class mozilla::layers::ImageLayer ImageLayer;
bool TryOptimizeToImageLayer(LayerManager* aManager, nsDisplayListBuilder* aBuilder);
bool IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
const nsRect& aClipRect,
gfxRect* aDestRect);
nsRect GetBoundsInternal(nsDisplayListBuilder* aBuilder);
void PaintInternal(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
const nsRect& aBounds, nsRect* aClipRect);
// Cache the result of nsCSSRendering::FindBackground. Always null if
// mIsThemed is true or if FindBackground returned false.
const nsStyleBackground* mBackgroundStyle;
nsCOMPtr<imgIContainer> mImage;
nsRefPtr<ImageContainer> mImageContainer;
LayoutDeviceRect mDestRect;
/* Bounds of this display item */
nsRect mBounds;
uint32_t mLayer;
};
/**
* A display item to paint the native theme background for a frame.
*/
class nsDisplayThemedBackground : public nsDisplayItem {
public:
nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
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 bool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) override;
virtual bool ProvidesFontSmoothingBackgroundColor(nsDisplayListBuilder* aBuilder,
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 mBounds;
nsITheme::Transparency mThemeTransparency;
uint8_t mAppearance;
};
class nsDisplayBackgroundColor : public nsDisplayItem
{
public:
nsDisplayBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
const nsStyleBackground* aBackgroundStyle,
nscolor aColor)
: nsDisplayItem(aBuilder, aFrame)
, mBackgroundStyle(aBackgroundStyle)
, mColor(gfxRGBA(aColor))
{ }
virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
bool* aSnap) override;
virtual bool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) 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 nsRect(ToReferenceFrame(), Frame()->GetSize());
}
virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
{
return new nsDisplaySolidColorGeometry(this, aBuilder,
NS_RGBA_FROM_GFXRGBA(mColor));
}
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion) override
{
const nsDisplaySolidColorGeometry* geometry = static_cast<const nsDisplaySolidColorGeometry*>(aGeometry);
if (NS_RGBA_FROM_GFXRGBA(mColor) != 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 nsStyleBackground* mBackgroundStyle;
gfxRGBA 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 bool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) override
{
*aColor = NS_RGBA(0, 0, 0, 0);
return true;
}
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);
AddFrame(aBuilder, aFrame);
}
#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);
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; }
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;
};
/**
* 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,
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.GetBounds(aBuilder);
// 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 bool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) override;
virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion) override;
virtual bool TryMerge(nsDisplayListBuilder* aBuilder, 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.MoveElementsFrom(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);
#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(nsDisplayListBuilder* aBuilder, 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 void ApplyOpacity(nsDisplayListBuilder* aBuilder,
float aOpacity,
const DisplayItemClip* aClip) override;
virtual bool CanApplyOpacity() const override;
virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder);
NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
virtual void WriteDebugInfo(std::stringstream& aStream) override;
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
private:
float mOpacity;
};
class nsDisplayMixBlendMode : public nsDisplayWrapList {
public:
nsDisplayMixBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList, uint32_t aFlags = 0);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayMixBlendMode();
#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 LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters) override;
virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion) override;
virtual bool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) override;
virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
return false;
}
NS_DISPLAY_DECL_NAME("MixBlendMode", TYPE_MIX_BLEND_MODE)
};
class nsDisplayBlendContainer : public nsDisplayWrapList {
public:
nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList,
BlendModeSet& aContainedBlendModes);
nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList);
#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
{
if (mCanBeActive && aManager->SupportsMixBlendModes(mContainedBlendModes)) {
return mozilla::LAYER_ACTIVE;
}
return mozilla::LAYER_INACTIVE;
}
virtual bool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) override;
virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
return false;
}
NS_DISPLAY_DECL_NAME("BlendContainer", TYPE_BLEND_CONTAINER)
private:
// The set of all blend modes used by nsDisplayMixBlendMode descendents of this container.
BlendModeSet mContainedBlendModes;
// If this is true, then we should make the layer active if all contained blend
// modes can be supported by the current layer manager.
bool mCanBeActive;
};
/**
* 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);
#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
{
return mozilla::LAYER_ACTIVE_FORCE;
}
virtual bool TryMerge(nsDisplayListBuilder* aBuilder, 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;
};
/**
* 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<FrameMetrics> ComputeFrameMetrics(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 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(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) override;
};
/**
* 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<FrameMetrics> ComputeFrameMetrics(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;
};
/**
* A display item to paint a stacking context with effects
* set by the stacking context root frame's style.
*/
class nsDisplaySVGEffects : public nsDisplayWrapList {
public:
nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList);
#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 nsRect GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) override {
*aSnap = false;
return mEffectsBounds + ToReferenceFrame();
}
virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion) override;
virtual bool TryMerge(nsDisplayListBuilder* aBuilder,
nsDisplayItem* aItem) override;
virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
return false;
}
NS_DISPLAY_DECL_NAME("SVGEffects", TYPE_SVG_EFFECTS)
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters) override;
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters) override;
gfxRect BBoxInUserSpace() const;
gfxPoint UserSpaceOffset() const;
virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
{
return new nsDisplaySVGEffectsGeometry(this, aBuilder);
}
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion) override;
void PaintAsLayer(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx,
LayerManager* aManager);
#ifdef MOZ_DUMP_PAINTING
void PrintEffects(nsACString& aTo);
#endif
private:
// relative to mFrame
nsRect mEffectsBounds;
};
/* 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;
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);
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);
#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 bool IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor) 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(nsDisplayListBuilder *aBuilder, 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();
}
virtual const nsRect& GetVisibleRectForChildren() const override
{
return mChildrenVisibleRect;
}
enum {
INDEX_MAX = UINT32_MAX >> nsDisplayItem::TYPE_BITS
};
const Matrix4x4& GetTransform();
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 nsPoint &aOrigin,
const nsRect* aBoundsOverride = nullptr);
static nsRect TransformRectOut(const nsRect &aUntransformedBounds,
const nsIFrame* aFrame,
const nsPoint &aOrigin,
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,
const nsPoint &aOrigin,
nsRect *aOutRect);
bool UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
nsRect* aOutRect);
static Point3D GetDeltaToTransformOrigin(const nsIFrame* aFrame,
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride);
static Point3D GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
float aAppUnitsPerPixel);
struct FrameTransformProperties
{
FrameTransformProperties(const nsIFrame* aFrame,
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride);
FrameTransformProperties(nsCSSValueSharedList* aTransformList,
const Point3D& aToTransformOrigin,
const Point3D& aToPerspectiveOrigin,
nscoord aChildPerspective)
: mFrame(nullptr)
, mTransformList(aTransformList)
, mToTransformOrigin(aToTransformOrigin)
, mChildPerspective(aChildPerspective)
, mToPerspectiveOrigin(aToPerspectiveOrigin)
{}
const Point3D& GetToPerspectiveOrigin() const
{
MOZ_ASSERT(mChildPerspective > 0, "Only valid with mChildPerspective > 0");
return mToPerspectiveOrigin;
}
const nsIFrame* mFrame;
nsRefPtr<nsCSSValueSharedList> mTransformList;
const Point3D mToTransformOrigin;
nscoord mChildPerspective;
private:
// mToPerspectiveOrigin is only valid if mChildPerspective > 0.
Point3D mToPerspectiveOrigin;
};
/**
* 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 aOffsetByOrigin If true, the resulting matrix will be translated
* by aOrigin. This translation is applied *before* the CSS transform.
*/
static gfx3DMatrix GetResultingTransformMatrix(const nsIFrame* aFrame,
const nsPoint& aOrigin,
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride = nullptr,
nsIFrame** aOutAncestor = nullptr,
bool aOffsetByOrigin = false);
static gfx3DMatrix GetResultingTransformMatrix(const FrameTransformProperties& aProperties,
const nsPoint& aOrigin,
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride = nullptr,
nsIFrame** aOutAncestor = 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 aLogAnimations = false);
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
/**
* This will return if it's possible for this element to be prerendered.
* This should never return false if we're going to prerender.
*/
bool MaybePrerender() const { return mMaybePrerender; }
/**
* Check if this element will be prerendered. This must be done after the
* display list has been fully built.
*/
bool ShouldPrerender(nsDisplayListBuilder* aBuilder);
virtual void WriteDebugInfo(std::stringstream& aStream) override;
private:
void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
void Init(nsDisplayListBuilder* aBuilder);
static gfx3DMatrix GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
const nsPoint& aOrigin,
float aAppUnitsPerPixel,
const nsRect* aBoundsOverride,
nsIFrame** aOutAncestor,
bool aOffsetByOrigin);
nsDisplayWrapList mStoredList;
Matrix4x4 mTransform;
ComputeTransformFunction mTransformGetter;
nsRect mChildrenVisibleRect;
uint32_t mIndex;
// We wont know if we pre-render until the layer building phase where we can
// check layers will-change budget.
bool mMaybePrerender;
};
/**
* 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 ||
t == nsDisplayItem::TYPE_TEXT_DECORATION ||
t == nsDisplayItem::TYPE_TEXT_SHADOW)
? 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;
};
/**
* A wrapper layer that wraps its children in a container, then renders
* everything with an appropriate VR effect based on the HMDInfo.
*/
class nsDisplayVR : public nsDisplayOwnLayer {
public:
nsDisplayVR(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList, mozilla::gfx::VRHMDInfo* aHMD);
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters) override
{
return mozilla::LAYER_ACTIVE;
}
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters) override;
protected:
nsRefPtr<mozilla::gfx::VRHMDInfo> mHMD;
};
#endif /*NSDISPLAYLIST_H_*/