/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * structures that represent things to be painted (ordered in z-order), * used during painting and hit testing */ #ifndef NSDISPLAYLIST_H_ #define NSDISPLAYLIST_H_ #include "mozilla/Attributes.h" #include "gfxContext.h" #include "mozilla/ArenaAllocator.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/Array.h" #include "mozilla/DebugOnly.h" #include "mozilla/EnumSet.h" #include "mozilla/Maybe.h" #include "mozilla/TemplateLib.h" // mozilla::tl::Max #include "nsCOMPtr.h" #include "nsContainerFrame.h" #include "nsPoint.h" #include "nsRect.h" #include "nsRegion.h" #include "nsDisplayListInvalidation.h" #include "DisplayItemClipChain.h" #include "DisplayListClipState.h" #include "LayerState.h" #include "FrameMetrics.h" #include "ImgDrawResult.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" #include "mozilla/TimeStamp.h" #include "mozilla/gfx/UserData.h" #include "mozilla/layers/LayerAttributes.h" #include "nsCSSRenderingBorders.h" #include "nsPresArena.h" #include "nsAutoLayoutPhase.h" #include "nsDisplayItemTypes.h" #include #include "nsTHashtable.h" #include #include class gfxContext; class nsIContent; class nsDisplayList; class nsDisplayTableItem; class nsISelection; class nsIScrollableFrame; class nsSubDocumentFrame; class nsDisplayCompositorHitTestInfo; class nsDisplayLayerEventRegions; class nsDisplayScrollInfoLayer; class nsCaret; enum class nsDisplayOwnLayerFlags; namespace mozilla { class FrameLayerBuilder; namespace layers { class Layer; class ImageLayer; class ImageContainer; class StackingContextHelper; class WebRenderCommand; class WebRenderScrollData; class WebRenderLayerScrollData; } // namespace layers namespace wr { class DisplayListBuilder; } // namespace wr } // namespace mozilla // A set of blend modes, that never includes OP_OVER (since it's // considered the default, rather than a specific blend mode). typedef mozilla::EnumSet 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() const override { return n; } \ virtual DisplayItemType GetType() const override { \ return DisplayItemType::e; \ } \ \ private: \ void* operator new(size_t aSize, nsDisplayListBuilder* aBuilder) { \ return aBuilder->Allocate(aSize, DisplayItemType::e); \ } \ template \ friend T* ::MakeDisplayItem(nsDisplayListBuilder* aBuilder, \ Args&&... aArgs); \ \ public: /** * Represents a frame that is considered to have (or will have) "animated * geometry" for itself and descendant frames. * * For example the scrolled frames of scrollframes which are actively being * scrolled fall into this category. Frames with certain CSS properties that are * being animated (e.g. 'left'/'top' etc) are also placed in this category. * Frames with different active geometry roots are in different PaintedLayers, * so that we can animate the geometry root by changing its transform (either on * the main thread or in the compositor). * * nsDisplayListBuilder constructs a tree of these (for fast traversals) and * assigns one for each display item. * * The animated geometry root for a display item is required to be a descendant * (or equal to) the item's ReferenceFrame(), which means that we will fall back * to returning aItem->ReferenceFrame() when we can't find another animated * geometry root. * * The animated geometry root isn't strongly defined for a frame as transforms * and background-attachment:fixed can cause it to vary between display items * for a given frame. */ struct AnimatedGeometryRoot { static already_AddRefed CreateAGRForFrame( nsIFrame* aFrame, AnimatedGeometryRoot* aParent, bool aIsAsync, bool aIsRetained) { RefPtr result; if (aIsRetained) { result = aFrame->GetProperty(AnimatedGeometryRootCache()); } if (result) { result->mParentAGR = aParent; result->mIsAsync = aIsAsync; } else { result = new AnimatedGeometryRoot(aFrame, aParent, aIsAsync, aIsRetained); } return result.forget(); } operator nsIFrame*() { return mFrame; } nsIFrame* operator->() const { return mFrame; } AnimatedGeometryRoot* GetAsyncAGR() { AnimatedGeometryRoot* agr = this; while (!agr->mIsAsync && agr->mParentAGR) { agr = agr->mParentAGR; } return agr; } NS_INLINE_DECL_REFCOUNTING(AnimatedGeometryRoot) nsIFrame* mFrame; RefPtr mParentAGR; bool mIsAsync; bool mIsRetained; protected: static void DetachAGR(AnimatedGeometryRoot* aAGR) { aAGR->mFrame = nullptr; aAGR->mParentAGR = nullptr; NS_RELEASE(aAGR); } NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(AnimatedGeometryRootCache, AnimatedGeometryRoot, DetachAGR) AnimatedGeometryRoot(nsIFrame* aFrame, AnimatedGeometryRoot* aParent, bool aIsAsync, bool aIsRetained) : mFrame(aFrame), mParentAGR(aParent), mIsAsync(aIsAsync), mIsRetained(aIsRetained) { MOZ_ASSERT(mParentAGR || mIsAsync, "The root AGR should always be treated as an async AGR."); if (mIsRetained) { NS_ADDREF(this); aFrame->SetProperty(AnimatedGeometryRootCache(), this); } } ~AnimatedGeometryRoot() { if (mFrame && mIsRetained) { mFrame->DeleteProperty(AnimatedGeometryRootCache()); } } }; namespace mozilla { /** * An active scrolled root (ASR) is similar to an animated geometry root (AGR). * The differences are: * - ASRs are only created for async-scrollable scroll frames. This is a * (hopefully) temporary restriction. In the future we will want to create * ASRs for all the things that are currently creating AGRs, and then * replace AGRs with ASRs and rename them from "active scrolled root" to * "animated geometry root". * - ASR objects are created during display list construction by the nsIFrames * that induce ASRs. This is done using AutoCurrentActiveScrolledRootSetter. * The current ASR is returned by * nsDisplayListBuilder::CurrentActiveScrolledRoot(). * - There is no way to go from an nsIFrame pointer to the ASR of that frame. * If you need to look up an ASR after display list construction, you need * to store it while the AutoCurrentActiveScrolledRootSetter that creates it * is on the stack. */ struct ActiveScrolledRoot { static already_AddRefed CreateASRForFrame( const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame, bool aIsRetained) { nsIFrame* f = do_QueryFrame(aScrollableFrame); RefPtr asr; if (aIsRetained) { asr = f->GetProperty(ActiveScrolledRootCache()); } if (!asr) { asr = new ActiveScrolledRoot(); if (aIsRetained) { RefPtr ref = asr; f->SetProperty(ActiveScrolledRootCache(), ref.forget().take()); } } asr->mParent = aParent; asr->mScrollableFrame = aScrollableFrame; asr->mViewId = Nothing(); asr->mDepth = aParent ? aParent->mDepth + 1 : 1; asr->mRetained = aIsRetained; return asr.forget(); } static const ActiveScrolledRoot* PickAncestor( const ActiveScrolledRoot* aOne, const ActiveScrolledRoot* aTwo) { MOZ_ASSERT(IsAncestor(aOne, aTwo) || IsAncestor(aTwo, aOne)); return Depth(aOne) <= Depth(aTwo) ? aOne : aTwo; } static const ActiveScrolledRoot* PickDescendant( const ActiveScrolledRoot* aOne, const ActiveScrolledRoot* aTwo) { MOZ_ASSERT(IsAncestor(aOne, aTwo) || IsAncestor(aTwo, aOne)); return Depth(aOne) >= Depth(aTwo) ? aOne : aTwo; } static bool IsAncestor(const ActiveScrolledRoot* aAncestor, const ActiveScrolledRoot* aDescendant); static nsCString ToString( const mozilla::ActiveScrolledRoot* aActiveScrolledRoot); // Call this when inserting an ancestor. void IncrementDepth() { mDepth++; } /** * Find the view ID (or generate a new one) for the content element * corresponding to the ASR. */ mozilla::layers::FrameMetrics::ViewID GetViewId() const { if (!mViewId.isSome()) { nsIContent* content = mScrollableFrame->GetScrolledFrame()->GetContent(); mViewId = Some(nsLayoutUtils::FindOrCreateIDFor(content)); } return *mViewId; } RefPtr mParent; nsIScrollableFrame* mScrollableFrame; NS_INLINE_DECL_REFCOUNTING(ActiveScrolledRoot) private: ActiveScrolledRoot() {} ~ActiveScrolledRoot() { if (mScrollableFrame && mRetained) { nsIFrame* f = do_QueryFrame(mScrollableFrame); f->DeleteProperty(ActiveScrolledRootCache()); } } static void DetachASR(ActiveScrolledRoot* aASR) { aASR->mParent = nullptr; aASR->mScrollableFrame = nullptr; NS_RELEASE(aASR); } NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(ActiveScrolledRootCache, ActiveScrolledRoot, DetachASR) static uint32_t Depth(const ActiveScrolledRoot* aActiveScrolledRoot) { return aActiveScrolledRoot ? aActiveScrolledRoot->mDepth : 0; } // This field is lazily populated in GetViewId(). We don't want to do the // work of populating if webrender is disabled, because it is often not // needed. mutable Maybe mViewId; uint32_t mDepth; bool mRetained; }; } // namespace mozilla enum class nsDisplayListBuilderMode : uint8_t { PAINTING, EVENT_DELIVERY, PLUGIN_GEOMETRY, FRAME_VISIBILITY, TRANSFORM_COMPUTATION, GENERATE_GLYPH, PAINTING_SELECTION_BACKGROUND }; /** * This manages a display list and is passed as a parameter to * nsIFrame::BuildDisplayList. * It contains the parameters that don't change from frame to frame and manages * the display list memory using an arena. It also establishes the reference * coordinate system for all display list items. Some of the parameters are * available from the prescontext/presshell, but we copy them into the builder * for faster/more convenient access. */ class nsDisplayListBuilder { typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion; /** * This manages status of a 3d context to collect visible rects of * descendants and passing a dirty rect. * * Since some transforms maybe singular, passing visible rects or * the dirty rect level by level from parent to children may get a * wrong result, being different from the result of appling with * effective transform directly. * * nsFrame::BuildDisplayListForStackingContext() uses * AutoPreserves3DContext to install an instance on the builder. * * \see AutoAccumulateTransform, AutoAccumulateRect, * AutoPreserves3DContext, Accumulate, GetCurrentTransform, * StartRoot. */ class Preserves3DContext { public: typedef mozilla::gfx::Matrix4x4 Matrix4x4; Preserves3DContext() : mAccumulatedRectLevels(0) {} Preserves3DContext(const Preserves3DContext& aOther) : mAccumulatedTransform(), mAccumulatedRect(), mAccumulatedRectLevels(0), mVisibleRect(aOther.mVisibleRect) {} // Accmulate transforms of ancestors on the preserves-3d chain. Matrix4x4 mAccumulatedTransform; // Accmulate visible rect of descendants in the preserves-3d context. nsRect mAccumulatedRect; // How far this frame is from the root of the current 3d context. int mAccumulatedRectLevels; nsRect mVisibleRect; }; /** * A frame can be in one of three states of AGR. * AGR_NO means the frame is not an AGR for now. * AGR_YES means the frame is an AGR for now. * AGR_MAYBE means the frame is not an AGR for now, but a transition * to AGR_YES without restyling is possible. */ enum AGRState { AGR_NO, AGR_YES, AGR_MAYBE }; public: typedef mozilla::FrameLayerBuilder FrameLayerBuilder; typedef mozilla::DisplayItemClip DisplayItemClip; typedef mozilla::DisplayItemClipChain DisplayItemClipChain; typedef mozilla::DisplayItemClipChainHasher DisplayItemClipChainHasher; typedef mozilla::DisplayItemClipChainEqualer DisplayItemClipChainEqualer; typedef mozilla::DisplayListClipState DisplayListClipState; typedef mozilla::ActiveScrolledRoot ActiveScrolledRoot; typedef nsIWidget::ThemeGeometry ThemeGeometry; typedef mozilla::layers::Layer Layer; typedef mozilla::layers::FrameMetrics FrameMetrics; typedef mozilla::layers::FrameMetrics::ViewID ViewID; typedef mozilla::gfx::Matrix4x4 Matrix4x4; /** * @param aReferenceFrame the frame at the root of the subtree; its origin * is the origin of the reference coordinate system for this display list * @param aMode encodes what the builder is being used for. * @param aBuildCaret whether or not we should include the caret in any * display lists that we make. */ nsDisplayListBuilder(nsIFrame* aReferenceFrame, nsDisplayListBuilderMode aMode, bool aBuildCaret, bool aRetainingDisplayList = false); ~nsDisplayListBuilder(); void BeginFrame(); void EndFrame(); void AddTemporaryItem(nsDisplayItem* aItem) { mTemporaryItems.AppendElement(aItem); } void SetWillComputePluginGeometry(bool aWillComputePluginGeometry) { mWillComputePluginGeometry = aWillComputePluginGeometry; } void SetForPluginGeometry(bool aForPlugin) { if (aForPlugin) { NS_ASSERTION(mMode == nsDisplayListBuilderMode::PAINTING, "Can only switch from PAINTING to PLUGIN_GEOMETRY"); NS_ASSERTION(mWillComputePluginGeometry, "Should have signalled this in advance"); mMode = nsDisplayListBuilderMode::PLUGIN_GEOMETRY; } else { NS_ASSERTION(mMode == nsDisplayListBuilderMode::PLUGIN_GEOMETRY, "Can only switch from PAINTING to PLUGIN_GEOMETRY"); mMode = nsDisplayListBuilderMode::PAINTING; } } mozilla::layers::LayerManager* GetWidgetLayerManager( nsView** aView = nullptr); /** * @return true if the display is being built in order to determine which * frame is under the mouse position. */ bool IsForEventDelivery() { return mMode == nsDisplayListBuilderMode::EVENT_DELIVERY; } /** * Be careful with this. The display list will be built in PAINTING mode * first and then switched to PLUGIN_GEOMETRY before a second call to * ComputeVisibility. * @return true if the display list is being built to compute geometry * for plugins. */ bool IsForPluginGeometry() { return mMode == nsDisplayListBuilderMode::PLUGIN_GEOMETRY; } /** * @return true if the display list is being built for painting. */ bool IsForPainting() { return mMode == nsDisplayListBuilderMode::PAINTING; } /** * @return true if the display list is being built for determining frame * visibility. */ bool IsForFrameVisibility() { return mMode == nsDisplayListBuilderMode::FRAME_VISIBILITY; } /** * @return true if the display list is being built for creating the glyph * mask from text items. */ bool IsForGenerateGlyphMask() { return mMode == nsDisplayListBuilderMode::GENERATE_GLYPH; } /** * @return true if the display list is being built for painting selection * background. */ bool IsForPaintingSelectionBG() { return mMode == nsDisplayListBuilderMode::PAINTING_SELECTION_BACKGROUND; } bool BuildCompositorHitTestInfo() { return mBuildCompositorHitTestInfo; } 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) const; /** * @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) const { nsPoint result; FindReferenceFrameFor(aFrame, &result); return result; } /** * When building the display list, the scrollframe aFrame will be "ignored" * for the purposes of clipping, and its scrollbars will be hidden. We use * this to allow RenderOffscreen to render a whole document without beign * clipped by the viewport or drawing the viewport scrollbars. */ void SetIgnoreScrollFrame(nsIFrame* aFrame) { mIgnoreScrollFrame = aFrame; } /** * Get the scrollframe to ignore, if any. */ nsIFrame* GetIgnoreScrollFrame() { return mIgnoreScrollFrame; } /** * 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. */ ViewID GetCurrentScrollbarTarget() const { return mCurrentScrollbarTarget; } nsDisplayOwnLayerFlags GetCurrentScrollbarFlags() const { return mCurrentScrollbarFlags; } /** * Returns true if building a scrollbar, and the scrollbar will not be * layerized. */ bool IsBuildingNonLayerizedScrollbar() const { return mIsBuildingScrollbar && !mCurrentScrollbarWillHaveLayer; } /** * Calling this setter makes us include all out-of-flow descendant * frames in the display list, wherever they may be positioned (even * outside the dirty rects). */ void SetIncludeAllOutOfFlows() { mIncludeAllOutOfFlows = true; } bool GetIncludeAllOutOfFlows() const { return mIncludeAllOutOfFlows; } /** * Calling this setter makes us exclude all leaf frames that aren't * selected. */ void SetSelectedFramesOnly() { mSelectedFramesOnly = true; } bool GetSelectedFramesOnly() { return mSelectedFramesOnly; } /** * Calling this setter makes us compute accurate visible regions at the cost * of performance if regions get very complex. */ bool GetAccurateVisibleRegions() { return mMode == nsDisplayListBuilderMode::PLUGIN_GEOMETRY; } /** * @return Returns true if we should include the caret in any display lists * that we make. */ bool IsBuildingCaret() const { return mBuildCaret; } bool IsRetainingDisplayList() const { return mRetainingDisplayList; } bool IsPartialUpdate() const { return mPartialUpdate; } void SetPartialUpdate(bool aPartial) { mPartialUpdate = aPartial; } bool IsBuilding() const { return mIsBuilding; } void SetIsBuilding(bool aIsBuilding) { mIsBuilding = aIsBuilding; for (nsIFrame* f : mModifiedFramesDuringBuilding) { f->SetFrameIsModified(false); } mModifiedFramesDuringBuilding.Clear(); } bool InInvalidSubtree() const { return mInInvalidSubtree; } /** * Allows callers to selectively override the regular paint suppression * checks, so that methods like GetFrameForPoint work when painting is * suppressed. */ void IgnorePaintSuppression() { mIgnoreSuppression = true; } /** * @return Returns if this builder will ignore paint suppression. */ bool IsIgnoringPaintSuppression() { return mIgnoreSuppression; } /** * Call this if we're doing normal painting to the window. */ void SetPaintingToWindow(bool aToWindow) { mIsPaintingToWindow = aToWindow; } bool IsPaintingToWindow() const { return mIsPaintingToWindow; } /** * Call this to prevent descending into subdocuments. */ void SetDescendIntoSubdocuments(bool aDescend) { mDescendIntoSubdocuments = aDescend; } bool GetDescendIntoSubdocuments() { return mDescendIntoSubdocuments; } /** * Get dirty rect relative to current frame (the frame that we're calling * BuildDisplayList on right now). */ const nsRect& GetVisibleRect() { return mVisibleRect; } const nsRect& GetDirtyRect() { return mDirtyRect; } void SetVisibleRect(const nsRect& aVisibleRect) { mVisibleRect = aVisibleRect; } void IntersectVisibleRect(const nsRect& aVisibleRect) { mVisibleRect.IntersectRect(mVisibleRect, aVisibleRect); } void SetDirtyRect(const nsRect& aDirtyRect) { mDirtyRect = aDirtyRect; } void IntersectDirtyRect(const nsRect& aDirtyRect) { mDirtyRect.IntersectRect(mDirtyRect, aDirtyRect); } const nsIFrame* GetCurrentFrame() { return mCurrentFrame; } const nsIFrame* GetCurrentReferenceFrame() { return mCurrentReferenceFrame; } const nsPoint& GetCurrentFrameOffsetToReferenceFrame() { return mCurrentOffsetToReferenceFrame; } AnimatedGeometryRoot* GetCurrentAnimatedGeometryRoot() { return mCurrentAGR; } AnimatedGeometryRoot* GetRootAnimatedGeometryRoot() { return mRootAGR; } void RecomputeCurrentAnimatedGeometryRoot(); MOZ_NEVER_INLINE void Check() { mPool.Check(); } /** * 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; } /** * Sets the current compositor hit test info to |aHitTestInfo|. * This is used during display list building to determine if the parent frame * hit test info contains the same information that child frame needs. */ void SetCompositorHitTestInfo(nsDisplayCompositorHitTestInfo* aHitTestInfo) { mCompositorHitTestInfo = aHitTestInfo; } /** * Builds a new nsDisplayCompositorHitTestInfo for the frame |aFrame| if * needed, and adds it to the top of |aList|. If |aBuildNew| is true, the * previous hit test info will not be reused. */ void BuildCompositorHitTestInfoIfNeeded(nsIFrame* aFrame, nsDisplayList* aList, const bool aBuildNew); bool IsBuildingLayerEventRegions(); static bool LayerEventRegionsEnabled(); bool IsInsidePointerEventsNoneDoc() { return CurrentPresShellState()->mInsidePointerEventsNoneDoc; } bool GetAncestorHasApzAwareEventHandler() const { return mAncestorHasApzAwareEventHandler; } void SetAncestorHasApzAwareEventHandler(bool aValue) { mAncestorHasApzAwareEventHandler = aValue; } bool HaveScrollableDisplayPort() const { return mHaveScrollableDisplayPort; } void SetHaveScrollableDisplayPort() { mHaveScrollableDisplayPort = true; } void ClearHaveScrollableDisplayPort() { mHaveScrollableDisplayPort = false; } bool SetIsCompositingCheap(bool aCompositingCheap) { bool temp = mIsCompositingCheap; mIsCompositingCheap = aCompositingCheap; return temp; } bool IsCompositingCheap() const { return mIsCompositingCheap; } /** * Display the caret if needed. */ bool DisplayCaret(nsIFrame* aFrame, nsDisplayList* aList) { nsIFrame* frame = GetCaretFrame(); if (aFrame == frame) { frame->DisplayCaret(this, aList); return true; } return false; } /** * Get the frame that the caret is supposed to draw in. * If the caret is currently invisible, this will be null. */ nsIFrame* GetCaretFrame() { return CurrentPresShellState()->mCaretFrame; } /** * Get the rectangle we're supposed to draw the caret into. */ const nsRect& GetCaretRect() { return CurrentPresShellState()->mCaretRect; } /** * Get the caret associated with the current presshell. */ nsCaret* GetCaret(); /** * Notify the display list builder that we're entering a presshell. * aReferenceFrame should be a frame in the new presshell. * aPointerEventsNoneDoc should be set to true if the frame generating this * document is pointer-events:none. */ void EnterPresShell(nsIFrame* aReferenceFrame, bool aPointerEventsNoneDoc = false); /** * For print-preview documents, we sometimes need to build display items for * the same frames multiple times in the same presentation, with different * clipping. Between each such batch of items, call * ResetMarkedFramesForDisplayList to make sure that the results of * MarkFramesForDisplayList do not carry over between batches. */ void ResetMarkedFramesForDisplayList(nsIFrame* aReferenceFrame); /** * Notify the display list builder that we're leaving a presshell. */ void LeavePresShell(nsIFrame* aReferenceFrame, nsDisplayList* aPaintedContents); void IncrementPresShellPaintCount(nsIPresShell* aPresShell); /** * Returns true if we're currently building a display list that's * directly or indirectly under an nsDisplayTransform. */ bool IsInTransform() const { return mInTransform; } /** * Indicate whether or not we're directly or indirectly under and * nsDisplayTransform or SVG foreignObject. */ void SetInTransform(bool aInTransform) { mInTransform = aInTransform; } /** * Return true if we're currently building a display list for a * nested presshell. */ bool IsInSubdocument() { return mPresShellStates.Length() > 1; } /** * Return true if we're currently building a display list for the presshell * of a chrome document, or if we're building the display list for a popup. */ bool IsInChromeDocumentOrPopup() { return mIsInChromePresContext || mIsBuildingForPopup; } /** * @return true if images have been set to decode synchronously. */ bool ShouldSyncDecodeImages() { return mSyncDecodeImages; } /** * Indicates whether we should synchronously decode images. If true, we decode * and draw whatever image data has been loaded. If false, we just draw * whatever has already been decoded. */ void SetSyncDecodeImages(bool aSyncDecodeImages) { mSyncDecodeImages = aSyncDecodeImages; } void FreeClipChains(); /* * Frees the temporary display items created during merging. */ void FreeTemporaryItems(); /** * Helper method to generate background painting flags based on the * information available in the display list builder. Currently only * accounts for mSyncDecodeImages. */ uint32_t GetBackgroundPaintFlags(); /** * Subtracts aRegion from *aVisibleRegion. We avoid letting * aVisibleRegion become overcomplex by simplifying it if necessary. */ void SubtractFromVisibleRegion(nsRegion* aVisibleRegion, const nsRegion& aRegion); /** * Mark the frames in aFrames to be displayed if they intersect aDirtyRect * (which is relative to aDirtyFrame). If the frames have placeholders * that might not be displayed, we mark the placeholders and their ancestors * to ensure that display list construction descends into them * anyway. nsDisplayListBuilder will take care of unmarking them when it is * destroyed. */ void MarkFramesForDisplayList(nsIFrame* aDirtyFrame, const nsFrameList& aFrames); void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame); void MarkFrameForDisplayIfVisible(nsIFrame* aFrame, nsIFrame* aStopAtFrame); void ClearFixedBackgroundDisplayData(); /** * Mark all child frames that Preserve3D() as needing display. * Because these frames include transforms set on their parent, dirty rects * for intermediate frames may be empty, yet child frames could still be * visible. */ void MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame); /** * Returns true if we need to descend into this frame when building * the display list, even though it doesn't intersect the dirty * rect, because it may have out-of-flows that do so. */ bool ShouldDescendIntoFrame(nsIFrame* aFrame, bool aVisible) const { return (aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) || (aVisible && aFrame->ForceDescendIntoIfVisible()) || GetIncludeAllOutOfFlows(); } /** * Returns the list of registered theme geometries. */ nsTArray GetThemeGeometries() const { nsTArray geometries; for (auto iter = mThemeGeometries.ConstIter(); !iter.Done(); iter.Next()) { geometries.AppendElements(*iter.Data()); } return geometries; } /** * Notifies the builder that a particular themed widget exists * at the given rectangle within the currently built display list. * For certain appearance values (currently only 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 aItem the item associated with the theme geometry * @param aRect the device-pixel rect relative to the widget's displayRoot * for the themed widget */ void RegisterThemeGeometry(uint8_t aWidgetType, nsDisplayItem* aItem, const mozilla::LayoutDeviceIntRect& aRect) { if (!mIsPaintingToWindow) { return; } nsTArray* geometries = mThemeGeometries.LookupOrAdd(aItem); geometries->AppendElement(ThemeGeometry(aWidgetType, aRect)); } /** * Removes theme geometries associated with the given display item |aItem|. */ void UnregisterThemeGeometry(nsDisplayItem* aItem) { mThemeGeometries.Remove(aItem); } /** * Adjusts mWindowDraggingRegion to take into account aFrame. If aFrame's * -moz-window-dragging value is |drag|, its border box is added to the * collected dragging region; if the value is |no-drag|, the border box is * subtracted from the region; if the value is |default|, that frame does * not influence the window dragging region. */ void AdjustWindowDraggingRegion(nsIFrame* aFrame); LayoutDeviceIntRegion GetWindowDraggingRegion() const; void RemoveModifiedWindowRegions(); void ClearRetainedWindowRegions(); /** * 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, DisplayItemType aType); void Destroy(DisplayItemType aType, void* aPtr); /** * Allocate a new ActiveScrolledRoot in the arena. Will be cleaned up * automatically when the arena goes away. */ ActiveScrolledRoot* AllocateActiveScrolledRoot( const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame); /** * Allocate a new DisplayItemClipChain object in the arena. Will be cleaned * up automatically when the arena goes away. */ const DisplayItemClipChain* AllocateDisplayItemClipChain( const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR, const DisplayItemClipChain* aParent); /** * Intersect two clip chains, allocating the new clip chain items in this * builder's arena. The result is parented to aAncestor, and no intersections * happen past aAncestor's ASR. * That means aAncestor has to be living in this builder's arena already. * aLeafClip1 and aLeafClip2 only need to outlive the call to this function, * their values are copied into the newly-allocated intersected clip chain * and this function does not hold on to any pointers to them. */ const DisplayItemClipChain* CreateClipChainIntersection( const DisplayItemClipChain* aAncestor, const DisplayItemClipChain* aLeafClip1, const DisplayItemClipChain* aLeafClip2); /** * Clone the supplied clip chain's chain items into this builder's arena. */ const DisplayItemClipChain* CopyWholeChain( const DisplayItemClipChain* aClipChain); /** * Only used for containerful root scrolling. This is a workaround. */ void SetActiveScrolledRootForRootScrollframe(const ActiveScrolledRoot* aASR) { mActiveScrolledRootForRootScrollframe = aASR; } const ActiveScrolledRoot* ActiveScrolledRootForRootScrollframe() const { return mActiveScrolledRootForRootScrollframe; } /** * Transfer off main thread animations to the layer. May be called * with aBuilder and aItem both null, but only if the caller has * already checked that off main thread animations should be sent to * the layer. When they are both null, the animations are added to * the layer as pending animations. */ static void AddAnimationsAndTransitionsToLayer(Layer* aLayer, nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, nsIFrame* aFrame, nsCSSPropertyID aProperty); /** * Merges the display items in |aMergedItems| and returns a new temporary * display item. * The display items in |aMergedItems| have to be mergeable with each other. */ nsDisplayItem* MergeItems(nsTArray& aMergedItems); /** * 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 { public: AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild, const nsRect& aVisibleRect, const nsRect& aDirtyRect, bool aIsRoot) : mBuilder(aBuilder), mPrevFrame(aBuilder->mCurrentFrame), mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame), mPrevLayerEventRegions(aBuilder->mLayerEventRegions), mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo), mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame), mPrevVisibleRect(aBuilder->mVisibleRect), mPrevDirtyRect(aBuilder->mDirtyRect), mPrevAGR(aBuilder->mCurrentAGR), mPrevIsAtRootOfPseudoStackingContext( aBuilder->mIsAtRootOfPseudoStackingContext), mPrevAncestorHasApzAwareEventHandler( aBuilder->mAncestorHasApzAwareEventHandler), mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems), mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) { 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); } bool isAsync; mCurrentAGRState = aBuilder->IsAnimatedGeometryRoot(aForChild, isAsync); if (aBuilder->mCurrentFrame == aForChild->GetParent()) { if (mCurrentAGRState == AGR_YES) { aBuilder->mCurrentAGR = aBuilder->WrapAGRForFrame( aForChild, isAsync, aBuilder->mCurrentAGR); } } else if (aForChild != aBuilder->mCurrentFrame) { aBuilder->mCurrentAGR = aBuilder->FindAnimatedGeometryRootFor(aForChild); } MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc( aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR)); aBuilder->mCurrentFrame = aForChild; aBuilder->mVisibleRect = aVisibleRect; aBuilder->mDirtyRect = aDirtyRect; aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot; aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree || aForChild->IsFrameModified(); } void SetReferenceFrameAndCurrentOffset(const nsIFrame* aFrame, const nsPoint& aOffset) { mBuilder->mCurrentReferenceFrame = aFrame; mBuilder->mCurrentOffsetToReferenceFrame = aOffset; } bool IsAnimatedGeometryRoot() const { return mCurrentAGRState == AGR_YES; } bool MaybeAnimatedGeometryRoot() const { return mCurrentAGRState == AGR_MAYBE; } void RestoreBuildingInvisibleItemsValue() { mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems; } ~AutoBuildingDisplayList() { mBuilder->mCurrentFrame = mPrevFrame; mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame; mBuilder->mLayerEventRegions = mPrevLayerEventRegions; mBuilder->mCompositorHitTestInfo = mPrevCompositorHitTestInfo; mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset; mBuilder->mVisibleRect = mPrevVisibleRect; mBuilder->mDirtyRect = mPrevDirtyRect; mBuilder->mCurrentAGR = mPrevAGR; mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext; mBuilder->mAncestorHasApzAwareEventHandler = mPrevAncestorHasApzAwareEventHandler; mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems; mBuilder->mInInvalidSubtree = mPrevInInvalidSubtree; } private: nsDisplayListBuilder* mBuilder; AGRState mCurrentAGRState; const nsIFrame* mPrevFrame; const nsIFrame* mPrevReferenceFrame; nsDisplayLayerEventRegions* mPrevLayerEventRegions; nsDisplayCompositorHitTestInfo* mPrevCompositorHitTestInfo; nsPoint mPrevOffset; nsRect mPrevVisibleRect; nsRect mPrevDirtyRect; RefPtr mPrevAGR; bool mPrevIsAtRootOfPseudoStackingContext; bool mPrevAncestorHasApzAwareEventHandler; bool mPrevBuildingInvisibleItems; bool mPrevInInvalidSubtree; }; /** * A helper class to temporarily set the value of mInTransform. */ class AutoInTransformSetter { public: AutoInTransformSetter(nsDisplayListBuilder* aBuilder, bool aInTransform) : mBuilder(aBuilder), mOldValue(aBuilder->mInTransform) { aBuilder->mInTransform = aInTransform; } ~AutoInTransformSetter() { mBuilder->mInTransform = mOldValue; } private: nsDisplayListBuilder* mBuilder; bool mOldValue; }; /** * A helper class to temporarily set the value of mFilterASR. */ class AutoFilterASRSetter { public: AutoFilterASRSetter(nsDisplayListBuilder* aBuilder, bool aUsingFilter) : mBuilder(aBuilder), mOldValue(aBuilder->mFilterASR) { if (!aBuilder->mFilterASR && aUsingFilter) { aBuilder->mFilterASR = aBuilder->CurrentActiveScrolledRoot(); } } ~AutoFilterASRSetter() { mBuilder->mFilterASR = mOldValue; } private: nsDisplayListBuilder* mBuilder; const ActiveScrolledRoot* mOldValue; }; class AutoSaveRestorePerspectiveIndex { public: AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : mBuilder(nullptr) { if (aFrame->ChildrenHavePerspective()) { mBuilder = aBuilder; mCachedItemIndex = aBuilder->mPerspectiveItemIndex; aBuilder->mPerspectiveItemIndex = 0; } } ~AutoSaveRestorePerspectiveIndex() { if (mBuilder) { mBuilder->mPerspectiveItemIndex = mCachedItemIndex; } } private: nsDisplayListBuilder* mBuilder; uint32_t mCachedItemIndex; }; /** * A helper class to temporarily set the value of mCurrentScrollParentId. */ class AutoCurrentScrollParentIdSetter { 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; }; /** * Used to update the current active scrolled root on the display list * builder, and to create new active scrolled roots. */ class AutoCurrentActiveScrolledRootSetter { public: explicit AutoCurrentActiveScrolledRootSetter(nsDisplayListBuilder* aBuilder) : mBuilder(aBuilder), mSavedActiveScrolledRoot(aBuilder->mCurrentActiveScrolledRoot), mContentClipASR(aBuilder->ClipState().GetContentClipASR()), mDescendantsStartIndex(aBuilder->mActiveScrolledRoots.Length()), mUsed(false) {} ~AutoCurrentActiveScrolledRootSetter() { mBuilder->mCurrentActiveScrolledRoot = mSavedActiveScrolledRoot; } void SetCurrentActiveScrolledRoot( const ActiveScrolledRoot* aActiveScrolledRoot); void EnterScrollFrame(nsIScrollableFrame* aScrollableFrame) { MOZ_ASSERT(!mUsed); ActiveScrolledRoot* asr = mBuilder->AllocateActiveScrolledRoot( mBuilder->mCurrentActiveScrolledRoot, aScrollableFrame); mBuilder->mCurrentActiveScrolledRoot = asr; mUsed = true; } void InsertScrollFrame(nsIScrollableFrame* aScrollableFrame); private: nsDisplayListBuilder* mBuilder; /** * The builder's mCurrentActiveScrolledRoot at construction time which * needs to be restored at destruction time. */ const ActiveScrolledRoot* mSavedActiveScrolledRoot; /** * If there's a content clip on the builder at construction time, then * mContentClipASR is that content clip's ASR, otherwise null. The * assumption is that the content clip doesn't get relaxed while this * object is on the stack. */ const ActiveScrolledRoot* mContentClipASR; /** * InsertScrollFrame needs to mutate existing ASRs (those that were * created while this object was on the stack), and mDescendantsStartIndex * makes it easier to skip ASRs that were created in the past. */ size_t mDescendantsStartIndex; /** * Flag to make sure that only one of SetCurrentActiveScrolledRoot / * EnterScrollFrame / InsertScrollFrame is called per instance of this * class. */ bool mUsed; }; /** * Keeps track of the innermost ASR that can be used as the ASR for a * container item that wraps all items that were created while this * object was on the stack. * The rule is: all child items of the container item need to have * clipped bounds with respect to the container ASR. */ class AutoContainerASRTracker { public: explicit AutoContainerASRTracker(nsDisplayListBuilder* aBuilder) : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) { mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickDescendant( mBuilder->ClipState().GetContentClipASR(), mBuilder->mCurrentActiveScrolledRoot); } const ActiveScrolledRoot* GetContainerASR() { return mBuilder->mCurrentContainerASR; } ~AutoContainerASRTracker() { mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor( mBuilder->mCurrentContainerASR, mSavedContainerASR); } private: nsDisplayListBuilder* mBuilder; const ActiveScrolledRoot* mSavedContainerASR; }; /** * A helper class to temporarily set the value of mCurrentScrollbarTarget * and mCurrentScrollbarFlags. */ class AutoCurrentScrollbarInfoSetter { public: AutoCurrentScrollbarInfoSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollTargetID, nsDisplayOwnLayerFlags aScrollbarFlags, bool aWillHaveLayer) : mBuilder(aBuilder) { aBuilder->mIsBuildingScrollbar = true; aBuilder->mCurrentScrollbarTarget = aScrollTargetID; aBuilder->mCurrentScrollbarFlags = aScrollbarFlags; aBuilder->mCurrentScrollbarWillHaveLayer = aWillHaveLayer; } ~AutoCurrentScrollbarInfoSetter() { // No need to restore old values because scrollbars cannot be nested. mBuilder->mIsBuildingScrollbar = false; mBuilder->mCurrentScrollbarTarget = FrameMetrics::NULL_SCROLL_ID; mBuilder->mCurrentScrollbarFlags = (nsDisplayOwnLayerFlags)0; mBuilder->mCurrentScrollbarWillHaveLayer = false; } private: nsDisplayListBuilder* mBuilder; }; /** * A helper class to track current effective transform for items. * * For frames that is Combines3DTransformWithAncestors(), we need to * apply all transforms of ancestors on the same preserves3D chain * on the bounds of current frame to the coordination of the 3D * context root. The 3D context root computes it's bounds from * these transformed bounds. */ class AutoAccumulateTransform { public: typedef mozilla::gfx::Matrix4x4 Matrix4x4; explicit AutoAccumulateTransform(nsDisplayListBuilder* aBuilder) : mBuilder(aBuilder), mSavedTransform(aBuilder->mPreserves3DCtx.mAccumulatedTransform) {} ~AutoAccumulateTransform() { mBuilder->mPreserves3DCtx.mAccumulatedTransform = mSavedTransform; } void Accumulate(const Matrix4x4& aTransform) { mBuilder->mPreserves3DCtx.mAccumulatedTransform = aTransform * mBuilder->mPreserves3DCtx.mAccumulatedTransform; } const Matrix4x4& GetCurrentTransform() { return mBuilder->mPreserves3DCtx.mAccumulatedTransform; } void StartRoot() { mBuilder->mPreserves3DCtx.mAccumulatedTransform = Matrix4x4(); } private: nsDisplayListBuilder* mBuilder; Matrix4x4 mSavedTransform; }; /** * A helper class to collect bounds rects of descendants. * * For a 3D context root, it's bounds is computed from the bounds of * descendants. If we transform bounds frame by frame applying * transforms, the bounds may turn to empty for any singular * transform on the path, but it is not empty for the accumulated * transform. */ class AutoAccumulateRect { public: explicit AutoAccumulateRect(nsDisplayListBuilder* aBuilder) : mBuilder(aBuilder), mSavedRect(aBuilder->mPreserves3DCtx.mAccumulatedRect) { aBuilder->mPreserves3DCtx.mAccumulatedRect = nsRect(); aBuilder->mPreserves3DCtx.mAccumulatedRectLevels++; } ~AutoAccumulateRect() { mBuilder->mPreserves3DCtx.mAccumulatedRect = mSavedRect; mBuilder->mPreserves3DCtx.mAccumulatedRectLevels--; } private: nsDisplayListBuilder* mBuilder; nsRect mSavedRect; }; void AccumulateRect(const nsRect& aRect) { mPreserves3DCtx.mAccumulatedRect.UnionRect(mPreserves3DCtx.mAccumulatedRect, aRect); } const nsRect& GetAccumulatedRect() { return mPreserves3DCtx.mAccumulatedRect; } /** * The level is increased by one for items establishing 3D rendering * context and starting a new accumulation. */ int GetAccumulatedRectLevels() { return mPreserves3DCtx.mAccumulatedRectLevels; } // Helpers for tables nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; } void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; } struct OutOfFlowDisplayData { OutOfFlowDisplayData( const DisplayItemClipChain* aContainingBlockClipChain, const DisplayItemClipChain* aCombinedClipChain, const ActiveScrolledRoot* aContainingBlockActiveScrolledRoot, const nsRect& aVisibleRect, const nsRect& aDirtyRect) : mContainingBlockClipChain(aContainingBlockClipChain), mCombinedClipChain(aCombinedClipChain), mContainingBlockActiveScrolledRoot( aContainingBlockActiveScrolledRoot), mVisibleRect(aVisibleRect), mDirtyRect(aDirtyRect) {} const DisplayItemClipChain* mContainingBlockClipChain; const DisplayItemClipChain* mCombinedClipChain; // only necessary for the special case of top layer const ActiveScrolledRoot* mContainingBlockActiveScrolledRoot; nsRect mVisibleRect; nsRect mDirtyRect; static bool AnyContentAncestorModified(nsIFrame* aFrame, nsIFrame* aStopAtFrame = nullptr) { for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { if (f->IsFrameModified()) { return true; } if (aStopAtFrame && f == aStopAtFrame) { break; } } return false; } static nsRect ComputeVisibleRectForFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aVisibleRect, const nsRect& aDirtyRect, nsRect* aOutDirtyRect) { nsRect visible = aVisibleRect; nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect; if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame) && aBuilder->IsPaintingToWindow()) { // position: fixed items are reflowed into and only drawn inside the // viewport, or the scroll position clamping scrollport size, if one is // set. nsIPresShell* ps = aFrame->PresShell(); if (ps->IsScrollPositionClampingScrollPortSizeSet()) { dirtyRectRelativeToDirtyFrame = nsRect( nsPoint(0, 0), ps->GetScrollPositionClampingScrollPortSize()); visible = dirtyRectRelativeToDirtyFrame; #ifdef MOZ_WIDGET_ANDROID } else { dirtyRectRelativeToDirtyFrame = nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize()); visible = dirtyRectRelativeToDirtyFrame; #endif } } *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition(); visible -= aFrame->GetPosition(); nsRect overflowRect = aFrame->GetVisualOverflowRect(); if (aFrame->IsTransformed() && mozilla::EffectCompositor::HasAnimationsForCompositor( aFrame, eCSSProperty_transform)) { /** * Add a fuzz factor to the overflow rectangle so that elements only * just out of view are pulled into the display list, so they can be * prerendered if necessary. */ overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32)); } visible.IntersectRect(visible, overflowRect); aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect); // If the nearest stacking context for the modified frame is an ancestor // of of it, and if the stacking context is a descendant of the containing // block of this OOF frame, we override the dirty rect to ensure that the // frame will get marked. if (AnyContentAncestorModified(aFrame, aFrame->GetParent())) { *aOutDirtyRect = visible; } return visible; } nsRect GetVisibleRectForFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect) { return ComputeVisibleRectForFrame(aBuilder, aFrame, mVisibleRect, mDirtyRect, aDirtyRect); } }; NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty, OutOfFlowDisplayData) struct DisplayListBuildingData { RefPtr mModifiedAGR = nullptr; nsRect mDirtyRect; }; NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListBuildingRect, DisplayListBuildingData) NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListBuildingDisplayPortRect, nsRect) static OutOfFlowDisplayData* GetOutOfFlowData(nsIFrame* aFrame) { if (!aFrame->GetParent()) { return nullptr; } return aFrame->GetParent()->GetProperty(OutOfFlowDisplayDataProperty()); } nsPresContext* CurrentPresContext() { return CurrentPresShellState()->mPresShell->GetPresContext(); } OutOfFlowDisplayData* GetCurrentFixedBackgroundDisplayData() { auto& displayData = CurrentPresShellState()->mFixedBackgroundDisplayData; return displayData ? displayData.ptr() : nullptr; } /** * 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(nsIFrame* aFrame, const nsRect& aBounds) { mWindowExcludeGlassRegion.Add(aFrame, aBounds); } /** * Returns the window exclude glass region. */ nsRegion GetWindowExcludeGlassRegion() const { return mWindowExcludeGlassRegion.ToRegion(); } /** * 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; } /** * Clears the window opaque region. */ void ClearWindowOpaqueRegion() { mWindowOpaqueRegion.SetEmpty(); } void SetGlassDisplayItem(nsDisplayItem* aItem) { if (mGlassDisplayItem) { // Web pages or extensions could trigger this by using // -moz-appearance:win-borderless-glass etc on their own elements. // Keep the first one, since that will be the background of the root // window NS_WARNING("Multiple glass backgrounds found?"); } else { mGlassDisplayItem = aItem; } } bool NeedToForceTransparentSurfaceForItem(nsDisplayItem* aItem); void SetContainsPluginItem() { mContainsPluginItem = true; } bool ContainsPluginItem() { return mContainsPluginItem; } /** * mContainsBlendMode is true if we processed a display item that * has a blend mode attached. We do this so we can insert a * nsDisplayBlendContainer in the parent stacking context. */ void SetContainsBlendMode(bool aContainsBlendMode) { mContainsBlendMode = aContainsBlendMode; } bool ContainsBlendMode() const { return mContainsBlendMode; } void AllocatePerspectiveItemIndex() { ++mPerspectiveItemIndex; } uint32_t PerspectiveItemIndex() const { return mPerspectiveItemIndex; } DisplayListClipState& ClipState() { return mClipState; } const ActiveScrolledRoot* CurrentActiveScrolledRoot() { return mCurrentActiveScrolledRoot; } const ActiveScrolledRoot* CurrentAncestorASRStackingContextContents() { return mCurrentContainerASR; } /** * Add the current frame to the will-change budget if possible and * remeber the outcome. Subsequent calls to IsInWillChangeBudget * will return the same value as return here. */ bool AddToWillChangeBudget(nsIFrame* aFrame, const nsSize& aSize); /** * This will add the current frame to the will-change budget the first * time it is seen. On subsequent calls this will return the same * answer. This effectively implements a first-come, first-served * allocation of the will-change budget. */ bool IsInWillChangeBudget(nsIFrame* aFrame, const nsSize& aSize); void ClearWillChangeBudget(nsIFrame* aFrame); void EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage); void ExitSVGEffectsContents(); /** * Note: if changing the conditions under which scroll info layers * are created, make a corresponding change to * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp. */ bool ShouldBuildScrollInfoItemsForHoisting() const { return mSVGEffectsBuildingDepth > 0; } void AppendNewScrollInfoItemForHoisting( nsDisplayScrollInfoLayer* aScrollInfoItem); /** * A helper class to install/restore nsDisplayListBuilder::mPreserves3DCtx. * * mPreserves3DCtx is used by class AutoAccumulateTransform & * AutoAccumulateRect to passing data between frames in the 3D * context. If a frame create a new 3D context, it should restore * the value of mPreserves3DCtx before returning back to the parent. * This class do it for the users. */ class AutoPreserves3DContext { public: explicit AutoPreserves3DContext(nsDisplayListBuilder* aBuilder) : mBuilder(aBuilder), mSavedCtx(aBuilder->mPreserves3DCtx) {} ~AutoPreserves3DContext() { mBuilder->mPreserves3DCtx = mSavedCtx; } private: nsDisplayListBuilder* mBuilder; Preserves3DContext mSavedCtx; }; const nsRect GetPreserves3DRect() const { return mPreserves3DCtx.mVisibleRect; } void SavePreserves3DRect() { mPreserves3DCtx.mVisibleRect = mVisibleRect; } bool IsBuildingInvisibleItems() const { return mBuildingInvisibleItems; } void SetBuildingInvisibleItems(bool aBuildingInvisibleItems) { mBuildingInvisibleItems = aBuildingInvisibleItems; } bool MarkFrameModifiedDuringBuilding(nsIFrame* aFrame) { if (!aFrame->IsFrameModified()) { mModifiedFramesDuringBuilding.AppendElement(aFrame); aFrame->SetFrameIsModified(true); return true; } return false; } bool MarkCurrentFrameModifiedDuringBuilding() { if (MarkFrameModifiedDuringBuilding(const_cast(mCurrentFrame))) { mInInvalidSubtree = true; return true; } return false; } /** * This is a convenience function to ease the transition until AGRs and ASRs * are unified. */ AnimatedGeometryRoot* AnimatedGeometryRootForASR( const ActiveScrolledRoot* aASR); bool HitTestIsForVisibility() const { return mHitTestIsForVisibility; } void SetHitTestIsForVisibility(bool aHitTestIsForVisibility) { mHitTestIsForVisibility = aHitTestIsForVisibility; } /** * Represents a region composed of frame/rect pairs. * WeakFrames are used to track whether a rect still belongs to the region. * Modified frames and rects are removed and re-added to the region if needed. * nsDisplayLayerEventRegions::FrameRects implements the same functionality * with nsIFrames. */ struct WeakFrameRegion { std::vector mFrames; nsTArray mRects; void Add(nsIFrame* aFrame, const nsRect& aRect) { mFrames.emplace_back(aFrame); mRects.AppendElement(nsRegion::RectToBox(aRect)); } void Add(nsIFrame* aFrame, const mozilla::gfx::IntRect& aRect) { mFrames.emplace_back(aFrame); mRects.AppendElement(nsRegion::RectToBox(aRect)); } void Clear() { mFrames.clear(); mRects.Clear(); } typedef mozilla::gfx::ArrayView BoxArrayView; nsRegion ToRegion() const { return nsRegion(BoxArrayView(mRects)); } LayoutDeviceIntRegion ToLayoutDeviceIntRegion() const { return LayoutDeviceIntRegion(BoxArrayView(mRects)); } }; private: bool MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame); /** * Returns whether a frame acts as an animated geometry root, optionally * returning the next ancestor to check. */ AGRState IsAnimatedGeometryRoot(nsIFrame* aFrame, bool& aIsAsync, nsIFrame** aParent = nullptr); /** * Returns the nearest ancestor frame to aFrame that is considered to have * (or will have) animated geometry. This can return aFrame. */ nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame, bool& aIsAsync); /** * Returns true if nsDisplayCompositorHitTestInfo item should be build for * |aFrame|. Otherwise returns false. If |aBuildNew| is true, reusing the * previous hit test info will not be considered. */ bool ShouldBuildCompositorHitTestInfo( const nsIFrame* aFrame, const mozilla::gfx::CompositorHitTestInfo& aInfo, const bool aBuildNew) const; friend class nsDisplayCanvasBackgroundImage; friend class nsDisplayBackgroundImage; friend class nsDisplayFixedPosition; AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsDisplayItem* aItem); friend class nsDisplayItem; friend class nsDisplayOwnLayer; friend struct RetainedDisplayListBuilder; AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame); AnimatedGeometryRoot* WrapAGRForFrame( nsIFrame* aAnimatedGeometryRoot, bool aIsAsync, AnimatedGeometryRoot* aParent = nullptr); nsDataHashtable, RefPtr> mFrameToAnimatedGeometryRootMap; /** * Add the current frame to the AGR budget if possible and remember * the outcome. Subsequent calls will return the same value as * returned here. */ bool AddToAGRBudget(nsIFrame* aFrame); struct PresShellState { nsIPresShell* mPresShell; #ifdef DEBUG mozilla::Maybe mAutoLayoutPhase; #endif nsIFrame* mCaretFrame; nsRect mCaretRect; mozilla::Maybe mFixedBackgroundDisplayData; uint32_t mFirstFrameMarkedForDisplay; uint32_t mFirstFrameWithOOFData; bool mIsBackgroundOnly; // This is a per-document flag turning off event handling for all content // in the document, and is set when we enter a subdocument for a pointer- // events:none frame. bool mInsidePointerEventsNoneDoc; }; 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; }; struct FrameWillChangeBudget { FrameWillChangeBudget(nsIFrame* aFrame, uint32_t aUsage) : mFrame(aFrame), mUsage(aUsage) {} nsIFrame* mFrame; uint32_t mUsage; }; nsIFrame* const mReferenceFrame; nsIFrame* mIgnoreScrollFrame; nsDisplayLayerEventRegions* mLayerEventRegions; nsDisplayCompositorHitTestInfo* mCompositorHitTestInfo; nsPresArena mPool; nsCOMPtr mBoundingSelection; AutoTArray mPresShellStates; AutoTArray mFramesMarkedForDisplay; AutoTArray mFramesMarkedForDisplayIfVisible; AutoTArray mFramesWithOOFData; nsClassHashtable, nsTArray> mThemeGeometries; nsDisplayTableItem* mCurrentTableItem; DisplayListClipState mClipState; const ActiveScrolledRoot* mCurrentActiveScrolledRoot; const ActiveScrolledRoot* mCurrentContainerASR; // mCurrentFrame is the frame that we're currently calling (or about to call) // BuildDisplayList on. const nsIFrame* mCurrentFrame; // The reference frame for mCurrentFrame. const nsIFrame* mCurrentReferenceFrame; // The offset from mCurrentFrame to mCurrentReferenceFrame. nsPoint mCurrentOffsetToReferenceFrame; RefPtr mRootAGR; RefPtr mCurrentAGR; // will-change budget tracker nsDataHashtable, DocumentWillChangeBudget> mWillChangeBudget; // Any frame listed in this set is already counted in the budget // and thus is in-budget. nsDataHashtable, uint32_t> mWillChangeBudgetSet; // Area of animated geometry root budget already allocated uint32_t mUsedAGRBudget; // Set of frames already counted in budget nsTHashtable> mAGRBudgetSet; nsTArray mModifiedFramesDuringBuilding; // Relative to mCurrentFrame. nsRect mVisibleRect; nsRect mDirtyRect; // Tracked regions used for retained display list. WeakFrameRegion mWindowExcludeGlassRegion; WeakFrameRegion mRetainedWindowDraggingRegion; WeakFrameRegion mRetainedWindowNoDraggingRegion; // Optimized versions for non-retained display list. LayoutDeviceIntRegion mWindowDraggingRegion; LayoutDeviceIntRegion mWindowNoDraggingRegion; // Window opaque region is calculated during layer building. nsRegion mWindowOpaqueRegion; // 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> mActiveScrolledRoots; std::unordered_set mClipDeduplicator; std::list mClipChainsToDestroy; nsTArray mTemporaryItems; const ActiveScrolledRoot* mActiveScrolledRootForRootScrollframe; nsDisplayListBuilderMode mMode; ViewID mCurrentScrollParentId; ViewID mCurrentScrollbarTarget; nsDisplayOwnLayerFlags mCurrentScrollbarFlags; Preserves3DContext mPreserves3DCtx; uint32_t mPerspectiveItemIndex; int32_t mSVGEffectsBuildingDepth; // When we are inside a filter, the current ASR at the time we entered the // filter. Otherwise nullptr. const ActiveScrolledRoot* mFilterASR; bool mContainsBlendMode; bool mIsBuildingScrollbar; bool mCurrentScrollbarWillHaveLayer; bool mBuildCaret; bool mRetainingDisplayList; bool mPartialUpdate; bool mIgnoreSuppression; bool mIsAtRootOfPseudoStackingContext; bool mIncludeAllOutOfFlows; bool mDescendIntoSubdocuments; bool mSelectedFramesOnly; bool mAllowMergingAndFlattening; bool mWillComputePluginGeometry; // True when we're building a display list that's directly or indirectly // under an nsDisplayTransform bool mInTransform; bool mIsInChromePresContext; bool mSyncDecodeImages; bool mIsPaintingToWindow; bool mIsCompositingCheap; bool mContainsPluginItem; bool mAncestorHasApzAwareEventHandler; // True when the first async-scrollable scroll frame for which we build a // display list has a display port. An async-scrollable scroll frame is one // which WantsAsyncScroll(). bool mHaveScrollableDisplayPort; bool mWindowDraggingAllowed; bool mIsBuildingForPopup; bool mForceLayerForScrollParent; bool mAsyncPanZoomEnabled; bool mBuildingInvisibleItems; bool mHitTestIsForVisibility; bool mIsBuilding; bool mInInvalidSubtree; bool mBuildCompositorHitTestInfo; bool mLessEventRegionItems; }; 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) {} nsDisplayItemLink(const nsDisplayItemLink&) : mAbove(nullptr) {} nsDisplayItem* mAbove; friend class nsDisplayList; }; class nsDisplayWrapList; template MOZ_ALWAYS_INLINE T* MakeDisplayItem(nsDisplayListBuilder* aBuilder, Args&&... aArgs) { T* item = new (aBuilder) T(aBuilder, mozilla::Forward(aArgs)...); const mozilla::SmallPointerArray& array = item->Frame()->DisplayItemData(); for (uint32_t i = 0; i < array.Length(); i++) { mozilla::DisplayItemData* did = array.ElementAt(i); if (did->GetDisplayItemKey() == item->GetPerFrameKey()) { if (!did->HasMergedFrames()) { item->SetDisplayItemData(did); } break; } } return item; } /** * This is the unit of rendering and event testing. Each instance of this * class represents an entity that can be drawn on the screen, e.g., a * frame's CSS background, or a frame's text string. * * nsDisplayItems can be containers --- i.e., they can perform hit testing * and painting by recursively traversing a list of child items. * * These are arena-allocated during display list construction. A typical * subclass would just have a frame pointer, so its object would be just three * pointers (vtable, next-item, frame). * * Display items belong to a list at all times (except temporarily as they * move from one list to another). */ class nsDisplayItem : public nsDisplayItemLink { public: typedef mozilla::ContainerLayerParameters ContainerLayerParameters; typedef mozilla::DisplayItemClip DisplayItemClip; typedef mozilla::DisplayItemClipChain DisplayItemClipChain; typedef mozilla::ActiveScrolledRoot ActiveScrolledRoot; typedef mozilla::layers::FrameMetrics FrameMetrics; typedef mozilla::layers::ScrollMetadata ScrollMetadata; typedef mozilla::layers::FrameMetrics::ViewID ViewID; typedef mozilla::layers::Layer Layer; typedef mozilla::layers::LayerManager LayerManager; typedef mozilla::layers::StackingContextHelper StackingContextHelper; typedef mozilla::layers::WebRenderCommand WebRenderCommand; typedef mozilla::layers::WebRenderParentCommand WebRenderParentCommand; typedef mozilla::LayerState LayerState; typedef mozilla::image::imgDrawingParams imgDrawingParams; typedef mozilla::image::ImgDrawResult ImgDrawResult; typedef class mozilla::gfx::DrawTarget DrawTarget; // This is never instantiated directly (it has pure virtual methods), so no // need to count constructors and destructors. nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame); nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const ActiveScrolledRoot* aActiveScrolledRoot); /** * This constructor is only used in rare cases when we need to construct * temporary items. */ explicit nsDisplayItem(nsIFrame* aFrame) : mFrame(aFrame), mClipChain(nullptr), mClip(nullptr), mActiveScrolledRoot(nullptr), mReferenceFrame(nullptr), mAnimatedGeometryRoot(nullptr), mForceNotVisible(false), mDisableSubpixelAA(false), mReusedItem(false), mBackfaceHidden(mFrame->In3DContextAndBackfaceIsHidden()) #ifdef MOZ_DUMP_PAINTING , mPainted(false) #endif { MOZ_COUNT_CTOR(nsDisplayItem); } protected: virtual ~nsDisplayItem() { MOZ_COUNT_DTOR(nsDisplayItem); if (mFrame) { mFrame->RemoveDisplayItem(this); } } public: virtual void Destroy(nsDisplayListBuilder* aBuilder) { DisplayItemType type = GetType(); this->~nsDisplayItem(); aBuilder->Destroy(type, this); } virtual void RestoreState() { mVisibleRect = mState.mVisibleRect; mClipChain = mState.mClipChain; mClip = mState.mClip; mDisableSubpixelAA = false; } virtual void RemoveFrame(nsIFrame* aFrame) { if (mFrame && aFrame == mFrame) { MOZ_ASSERT(!mFrame->HasDisplayItem(this)); mFrame = nullptr; mDisplayItemData = nullptr; } } /** * Downcasts this item to nsDisplayWrapList, if possible. */ virtual const nsDisplayWrapList* AsDisplayWrapList() const { return nullptr; } virtual nsDisplayWrapList* AsDisplayWrapList() { return nullptr; } /** * Create a clone of this item. */ virtual nsDisplayItem* Clone(nsDisplayListBuilder* aBuilder) const { return nullptr; } nsDisplayItem(const nsDisplayItem&) = delete; /** * The custom copy-constructor is implemented to prevent copying the saved * state of the item. * This is currently only used when creating temporary items for merging. */ nsDisplayItem(nsDisplayListBuilder* aBuilder, const nsDisplayItem& aOther) : mFrame(aOther.mFrame), mClipChain(aOther.mClipChain), mClip(aOther.mClip), mActiveScrolledRoot(aOther.mActiveScrolledRoot), mReferenceFrame(aOther.mReferenceFrame), mAnimatedGeometryRoot(aOther.mAnimatedGeometryRoot), mToReferenceFrame(aOther.mToReferenceFrame), mVisibleRect(aOther.mVisibleRect), mForceNotVisible(aOther.mForceNotVisible), mDisableSubpixelAA(aOther.mDisableSubpixelAA), mReusedItem(false), mBackfaceHidden(mFrame->In3DContextAndBackfaceIsHidden()) #ifdef MOZ_DUMP_PAINTING , mPainted(false) #endif { MOZ_COUNT_CTOR(nsDisplayItem); } struct HitTestState { explicit HitTestState() : mInPreserves3D(false) {} ~HitTestState() { NS_ASSERTION(mItemBuffer.Length() == 0, "mItemBuffer should have been cleared"); } // Handling transform items for preserve 3D frames. bool mInPreserves3D; AutoTArray 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 DisplayItemType GetType() const = 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() const { return uint32_t(GetType()); } uint8_t GetFlags() { return GetDisplayItemFlagsForType(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* 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 { MOZ_ASSERT(mFrame, "Trying to use display item after deletion!"); return mFrame; } /** * @return the nsIFrame that provides the style data, and should * be checked when deciding if this display item can be reused. */ virtual nsIFrame* FrameForInvalidation() const { return mFrame; } virtual bool HasDeletedFrame() const { return !mFrame; } virtual nsIFrame* StyleFrame() const { return mFrame; } /** * Compute the used z-index of our frame; returns zero for elements to which * z-index does not apply, and for z-index:auto. * @note This can be overridden, @see nsDisplayWrapList::SetOverrideZIndex. */ virtual int32_t ZIndex() const; /** * The default bounds is the frame border rect. * @param aSnap *aSnap is set to true if the returned rect will be * snapped to nearest device pixel edges during actual drawing. * It might be set to false and snap anyway, so code computing the set of * pixels affected by this display item needs to round outwards to pixel * boundaries when *aSnap is set to false. * This does not take the item's clipping into account. * @return a rectangle relative to aBuilder->ReferenceFrame() that * contains the area drawn by this display item */ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const { *aSnap = false; return nsRect(ToReferenceFrame(), Frame()->GetSize()); } virtual nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const { *aSnap = false; return nsRegion(); } /** * Returns true if nothing will be rendered inside aRect, false if uncertain. * aRect is assumed to be contained in this item's bounds. */ virtual bool IsInvisibleInRect(const nsRect& aRect) const { return false; } /** * Returns the result of GetBounds intersected with the item's clip. * The intersection is approximate since rounded corners are not taking into * account. */ nsRect GetClippedBounds(nsDisplayListBuilder* aBuilder) const; nsRect GetBorderRect() const { return nsRect(ToReferenceFrame(), Frame()->GetSize()); } nsRect GetPaddingRect() const { return Frame()->GetPaddingRectRelativeToSelf() + ToReferenceFrame(); } nsRect GetContentRect() const { return Frame()->GetContentRectRelativeToSelf() + ToReferenceFrame(); } /** * Checks if the frame(s) owning this display item have been marked as * invalid, and needing repainting. */ virtual bool IsInvalid(nsRect& aRect) const { bool result = mFrame ? mFrame->IsInvalid(aRect) : false; aRect += ToReferenceFrame(); return result; } /** * Creates and initializes an nsDisplayItemGeometry object that retains the * current areas covered by this display item. These need to retain enough * information such that they can be compared against a future nsDisplayItem * of the same type, and determine if repainting needs to happen. * * Subclasses wishing to store more information need to override both this * and ComputeInvalidationRegion, as well as implementing an * nsDisplayItemGeometry subclass. * * The default implementation tracks both the display item bounds, and the * frame's border rect. */ virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) { return new nsDisplayItemGenericGeometry(this, aBuilder); } /** * Compares an nsDisplayItemGeometry object from a previous paint against the * current item. Computes if the geometry of the item has changed, and the * invalidation area required for correct repainting. * * The existing geometry will have been created from a display item with a * matching GetPerFrameKey()/mFrame pair to the current item. * * The default implementation compares the display item bounds, and the * frame's border rect, and invalidates the entire bounds if either rect * changes. * * @param aGeometry The geometry of the matching display item from the * previous paint. * @param aInvalidRegion Output param, the region to invalidate, or * unchanged if none. */ virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const { const nsDisplayItemGenericGeometry* geometry = static_cast(aGeometry); bool snap; if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) || !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) { aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds); } } /** * An alternative default implementation of ComputeInvalidationRegion, * that instead invalidates only the changed area between the two items. */ void ComputeInvalidationRegionDifference( nsDisplayListBuilder* aBuilder, const nsDisplayItemBoundsGeometry* aGeometry, nsRegion* aInvalidRegion) const { bool snap; nsRect bounds = GetBounds(aBuilder, &snap); if (!aGeometry->mBounds.IsEqualInterior(bounds)) { nscoord radii[8]; if (aGeometry->mHasRoundedCorners || Frame()->GetBorderRadii(radii)) { aInvalidRegion->Or(aGeometry->mBounds, bounds); } else { aInvalidRegion->Xor(aGeometry->mBounds, bounds); } } } /** * @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) const { *aSnap = false; return nsRegion(); } /** * @return Some(nscolor) if the item is guaranteed to paint every pixel in its * bounds with the same (possibly translucent) color */ virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const { return mozilla::Nothing(); } /** * @return true if the contents of this item are rendered fixed relative * to the nearest viewport. */ virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) const { return false; } virtual bool ClearsBackground() const { return false; } /** * Returns true if all layers that can be active should be forced to be * active. Requires setting the pref layers.force-active=true. */ static bool ForceActiveLayers(); /** * @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) const { 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, gfxContext* aCtx) {} #ifdef MOZ_DUMP_PAINTING /** * Mark this display item as being painted via * FrameLayerBuilder::DrawPaintedLayer. */ bool Painted() const { 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 BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) { return nullptr; } /** * Function to create the WebRenderCommands. * We should check if the layer state is * active first and have an early return if the layer state is * not active. * * @return true if successfully creating webrender commands. */ virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) { return false; } /** * Updates the provided aLayerData with any APZ-relevant scroll data * that is specific to this display item. This is stuff that would normally * be put on the layer during BuildLayer, but this is only called in * layers-free webrender mode, where we don't have layers. * * This function returns true if and only if it has APZ-relevant scroll data * to provide. Note that the arguments passed in may be nullptr, in which case * the function should still return true if and only if it has APZ-relevant * scroll data, but obviously in this case it can't actually put the * data onto aLayerData, because there isn't one. * * This function assumes that aData and aLayerData will either both be null, * or will both be non-null. The caller is responsible for enforcing this. */ virtual bool UpdateScrollData( mozilla::layers::WebRenderScrollData* aData, mozilla::layers::WebRenderLayerScrollData* aLayerData) { return false; } /** * Builds a DisplayItemLayer and sets the display item to this. */ already_AddRefed BuildDisplayItemLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters); /** * 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); /** * Checks if the given display item can be merged with this item. * @return true if the merging is possible, otherwise false. */ virtual bool CanMerge(const nsDisplayItem* aItem) const { return false; } /** * Try to merge with the other item (which is below us in the display * list). This gets used by nsDisplayClip to coalesce clipping operations * (optimization), by nsDisplayOpacity to merge rendering for the same * content element into a single opacity group (correctness), and will be * used by nsDisplayOutline to merge multiple outlines for the same element * (also for correctness). */ virtual void Merge(const nsDisplayItem* aItem) {} /** * Merges the given display list to this item. */ virtual void MergeDisplayListFromItem(nsDisplayListBuilder* aBuilder, const nsDisplayItem* aItem) {} /** * 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* aFrames) const {} virtual bool HasMergedFrames() const { return false; } /** * 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; } /** * Some items such as those calling into the native themed widget machinery * have to be painted on the content process. In this case it is best to avoid * allocating layers that serializes and forwards the work to the compositor. */ virtual bool MustPaintOnContentSide() const { return false; } /** * If this has a child list where the children are in the same coordinate * system as this item (i.e., they have the same reference frame), * return the list. */ virtual nsDisplayList* GetSameCoordinateSystemChildren() const { return nullptr; } virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) {} /** * Do UpdateBounds() for items with frames establishing or extending * 3D rendering context. * * This function is called by UpdateBoundsFor3D() of * nsDisplayTransform(), and it is called by * BuildDisplayListForStackingContext() on transform items * establishing 3D rendering context. * * The bounds of a transform item with the frame establishing 3D * rendering context should be computed by calling * DoUpdateBoundsPreserves3D() on all descendants that participate * the same 3d rendering context. */ virtual void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) {} /** * If this has a child list, return it, even if the children are in * a different coordinate system to this item. */ virtual nsDisplayList* GetChildren() const { return nullptr; } /** * Returns the visible rect. */ const nsRect& GetVisibleRect() const { return mVisibleRect; } void SetVisibleRect(const nsRect& aVisibleRect, bool aStore) { mVisibleRect = aVisibleRect; if (aStore) { mState.mVisibleRect = 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 DisplayItemClipChain* 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; } bool ForceNotVisible() const { return mForceNotVisible; } /** * For debugging and stuff */ virtual const char* Name() const = 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, bool aUseClipBounds = true); /** * Returns the result of aBuilder->ToReferenceFrame(GetUnderlyingFrame()) */ const nsPoint& ToReferenceFrame() const { NS_ASSERTION(mFrame, "No frame?"); return mToReferenceFrame; } /** * @return the root of the display list's frame (sub)tree, whose origin * establishes the coordinate system for the display list */ const nsIFrame* ReferenceFrame() const { return mReferenceFrame; } /** * Returns the reference frame for display item children of this item. */ virtual const nsIFrame* ReferenceFrameForChildren() const { return mReferenceFrame; } AnimatedGeometryRoot* GetAnimatedGeometryRoot() const { MOZ_ASSERT(mAnimatedGeometryRoot, "Must have cached AGR before accessing it!"); return mAnimatedGeometryRoot; } virtual struct AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const { return GetAnimatedGeometryRoot(); } /** * Checks if this display item (or any children) contains content that might * be rendered with component alpha (e.g. subpixel antialiasing). Returns the * bounds of the area that needs component alpha, or an empty rect if nothing * in the item does. */ virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const { return nsRect(); } /** * Disable usage of component alpha. Currently only relevant for items that * have text. */ void DisableComponentAlpha() { mDisableSubpixelAA = true; } bool IsSubpixelAADisabled() const { return mDisableSubpixelAA; } /** * Check if we can add async animations to the layer for this display item. */ virtual bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) { return false; } virtual bool SupportsOptimizingToImage() const { return false; } const DisplayItemClip& GetClip() const { return mClip ? *mClip : DisplayItemClip::NoClip(); } void IntersectClip(nsDisplayListBuilder* aBuilder, const DisplayItemClipChain* aOther, bool aStore); virtual void SetActiveScrolledRoot( const ActiveScrolledRoot* aActiveScrolledRoot) { mActiveScrolledRoot = aActiveScrolledRoot; } const ActiveScrolledRoot* GetActiveScrolledRoot() const { return mActiveScrolledRoot; } virtual void SetClipChain(const DisplayItemClipChain* aClipChain, bool aStore); const DisplayItemClipChain* GetClipChain() const { return mClipChain; } /** * Intersect all clips in our clip chain up to (and including) aASR and set * set the intersection as this item's clip. */ void FuseClipChainUpTo(nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR); bool BackfaceIsHidden() const { return mFrame->BackfaceIsHidden(); } bool In3DContextAndBackfaceIsHidden() { return mBackfaceHidden; } bool HasSameTypeAndClip(const nsDisplayItem* aOther) const { return GetType() == aOther->GetType() && GetClipChain() == aOther->GetClipChain(); } bool HasSameContent(const nsDisplayItem* aOther) const { return mFrame->GetContent() == aOther->Frame()->GetContent(); } bool IsReused() const { return mReusedItem; } void SetReused(bool aReused) { mReusedItem = aReused; } virtual bool CanBeReused() const { return true; } virtual nsIFrame* GetDependentFrame() { return nullptr; } virtual mozilla::Maybe GetClipWithRespectToASR( nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const; void SetDisplayItemData(mozilla::DisplayItemData* aDID) { mDisplayItemData = aDID; } mozilla::DisplayItemData* GetDisplayItemData() { return mDisplayItemData; } protected: nsDisplayItem() = delete; typedef bool (*PrefFunc)(void); bool ShouldUseAdvancedLayer(LayerManager* aManager, PrefFunc aFunc) const; bool CanUseAdvancedLayer(LayerManager* aManager) const; nsIFrame* mFrame; RefPtr mClipChain; const DisplayItemClip* mClip; RefPtr mActiveScrolledRoot; // Result of FindReferenceFrameFor(mFrame), if mFrame is non-null const nsIFrame* mReferenceFrame; RefPtr mAnimatedGeometryRoot; // Result of ToReferenceFrame(mFrame), if mFrame is non-null nsPoint mToReferenceFrame; RefPtr mDisplayItemData; // This is the rectangle that needs to be painted. // Display item construction sets this to the dirty rect. // nsDisplayList::ComputeVisibility sets this to the visible region // of the item by intersecting the current visible region with the bounds // of the item. Paint implementations can use this to limit their drawing. // Guaranteed to be contained in GetBounds(). nsRect mVisibleRect; bool mForceNotVisible; bool mDisableSubpixelAA; bool mReusedItem; bool mBackfaceHidden; #ifdef MOZ_DUMP_PAINTING // True if this frame has been painted. bool mPainted; #endif struct { nsRect mVisibleRect; RefPtr mClipChain; const DisplayItemClip* mClip; } mState; }; /** * 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::ActiveScrolledRoot ActiveScrolledRoot; typedef mozilla::layers::Layer Layer; typedef mozilla::layers::LayerManager LayerManager; typedef mozilla::layers::PaintedLayer PaintedLayer; /** * Create an empty list. */ nsDisplayList() : mLength(0), mIsOpaque(false), mForceTransparentSurface(false) { mTop = &mSentinel; mSentinel.mAbove = nullptr; } ~nsDisplayList() { if (mSentinel.mAbove) { NS_WARNING("Nonempty list left over?"); } } /** * 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) { MOZ_ASSERT(aItem, "No item to append!"); MOZ_ASSERT(!aItem->mAbove, "Already in a list!"); mTop->mAbove = aItem; mTop = aItem; mLength++; } /** * 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) { MOZ_ASSERT(aItem, "No item to append!"); MOZ_ASSERT(!aItem->mAbove, "Already in a list!"); aItem->mAbove = mSentinel.mAbove; mSentinel.mAbove = aItem; if (mTop == &mSentinel) { mTop = aItem; } mLength++; } /** * 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; mLength += aList->mLength; aList->mLength = 0; } } /** * 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; mLength += aList->mLength; aList->mLength = 0; } } /** * 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(nsDisplayListBuilder* aBuilder); /** * @return the item at the top of the list, or null if the list is empty */ nsDisplayItem* GetTop() const { return mTop != &mSentinel ? static_cast(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; } /** * @return the number of items in the list */ uint32_t Count() const { return mLength; } /** * Stable sort the list by the z-order of GetUnderlyingFrame() on * each item. 'auto' is counted as zero. * It is assumed that the list is already in content document order. */ void SortByZOrder(); /** * Stable sort the list by the tree order of the content of * GetUnderlyingFrame() on each item. z-index is ignored. * @param aCommonAncestor a common ancestor of all the content elements * associated with the display items, for speeding up tree order * checks, or nullptr if not known; it's only a hint, if it is not an * ancestor of some elements, then we lose performance but not correctness */ void SortByContentOrder(nsIContent* aCommonAncestor); /** * Sort the display list using a stable sort. Take care, because some of the * items might be nsDisplayLists themselves. * aComparator(Item item1, Item item2) should return true if item1 should go * before item2. * We sort the items into increasing order. */ template void Sort(const Comparator& aComparator) { // Some casual local browsing testing suggests that a local preallocated // array of 20 items should be able to avoid a lot of dynamic allocations // here. AutoTArray items; while (nsDisplayItem* item = RemoveBottom()) { items.AppendElement(Item(item)); } std::stable_sort(items.begin(), items.end(), aComparator); for (Item& item : items) { AppendToTop(item); } } /** * Compute visiblity for the items in the list. * We put this logic here so it can be shared by top-level * painting and also display items that maintain child lists. * This is also a good place to put ComputeVisibility-related logic * that must be applied to every display item. In particular, this * sets mVisibleRect on each display item. * This sets mIsOpaque if the entire visible area of this list has * been removed from aVisibleRegion when we return. * This does not remove any items from the list, so we can recompute * visiblity with different regions later (see * FrameLayerBuilder::DrawPaintedLayer). * This method needs to be idempotent. * * @param aVisibleRegion the area that is visible, relative to the * reference frame; on return, this contains the area visible under the list. * I.e., opaque contents of this list are subtracted from aVisibleRegion. * @param aListVisibleBounds must be equal to the bounds of the intersection * of aVisibleRegion and GetBounds() for this list. * @return true if any item in the list is visible. */ bool ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, const nsRect& aListVisibleBounds); /** * As ComputeVisibilityForSublist, but computes visibility for a root * list (a list that does not belong to an nsDisplayItem). * This method needs to be idempotent. * * @param aVisibleRegion the area that is visible */ bool ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion); /** * Returns true if the visible region output from ComputeVisiblity was * empty, i.e. everything visible in this list is opaque. */ bool IsOpaque() const { return mIsOpaque; } /** * Returns true if any display item requires the surface to be transparent. */ bool NeedsTransparentSurface() const { return mForceTransparentSurface; } /** * Paint the list to the rendering context. We assume that (0,0) in aCtx * corresponds to the origin of the reference frame. For best results, * aCtx's current transform should make (0,0) pixel-aligned. The * rectangle in aDirtyRect is painted, which *must* be contained in the * dirty rect used to construct the display list. * * If aFlags contains PAINT_USE_WIDGET_LAYERS and * ShouldUseWidgetLayerManager() is set, then we will paint using * the reference frame's widget's layer manager (and ctx may be null), * otherwise we will use a temporary BasicLayerManager and ctx must * not be null. * * If PAINT_EXISTING_TRANSACTION is set, the reference frame's widget's * layer manager has already had BeginTransaction() called on it and * we should not call it again. * * If PAINT_COMPRESSED is set, the FrameLayerBuilder should be set to * compressed mode to avoid short cut optimizations. * * This must only be called on the root display list of the display list * tree. * * We return the layer manager used for painting --- mainly so that * callers can dump its layer tree if necessary. */ enum { PAINT_DEFAULT = 0, PAINT_USE_WIDGET_LAYERS = 0x01, PAINT_EXISTING_TRANSACTION = 0x04, PAINT_NO_COMPOSITE = 0x08, PAINT_COMPRESSED = 0x10, PAINT_IDENTICAL_DISPLAY_LIST = 0x20 }; already_AddRefed PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, uint32_t aFlags); mozilla::FrameLayerBuilder* BuildLayers(nsDisplayListBuilder* aBuilder, LayerManager* aLayerManager, uint32_t aFlags, bool aIsWidgetTransaction); /** * Get the bounds. Takes the union of the bounds of all children. * The result is not cached. */ nsRect GetBounds(nsDisplayListBuilder* aBuilder) const; /** * Get this list's bounds, respecting clips relative to aASR. The result is * the union of each item's clipped bounds with respect to aASR. That means * that if an item can move asynchronously with an ASR that is a descendant * of aASR, then the clipped bounds with respect to aASR will be the clip of * that item for aASR, because the item can move anywhere inside that clip. * If there is an item in this list which is not bounded with respect to * aASR (i.e. which does not have "finite bounds" with respect to aASR), * then this method trigger an assertion failure. * The optional aVisibleRect out argument can be set to non-null if the * caller is also interested to know the visible rect. This can be used * to get the visible rect efficiently without traversing the display list * twice. */ nsRect GetClippedBoundsWithRespectToASR(nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR, nsRect* aVisibleRect = nullptr) 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* 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; } void RestoreState() { mIsOpaque = false; mForceTransparentSurface = false; } private: nsDisplayItemLink mSentinel; nsDisplayItemLink* mTop; uint32_t mLength; // 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; } void DeleteAll(nsDisplayListBuilder* aBuilder) { BorderBackground()->DeleteAll(aBuilder); BlockBorderBackgrounds()->DeleteAll(aBuilder); Floats()->DeleteAll(aBuilder); PositionedDescendants()->DeleteAll(aBuilder); Outlines()->DeleteAll(aBuilder); Content()->DeleteAll(aBuilder); } 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 { explicit nsDisplayListCollection(nsDisplayListBuilder* aBuilder) : nsDisplayListSet(&mLists[0], &mLists[1], &mLists[2], &mLists[3], &mLists[4], &mLists[5]) {} explicit nsDisplayListCollection(nsDisplayListBuilder* aBuilder, nsDisplayList* aBorderBackground) : nsDisplayListSet(aBorderBackground, &mLists[1], &mLists[2], &mLists[3], &mLists[4], &mLists[5]) {} /** * Sort all lists by content order. */ void SortAllByContentOrder(nsIContent* aCommonAncestor) { for (int32_t i = 0; i < 6; ++i) { mLists[i].SortByContentOrder(aCommonAncestor); } } private: // This class is only used on stack, so we don't have to worry about leaking // it. Don't let us be heap-allocated! void* operator new(size_t sz) CPP_THROW_NEW; nsDisplayList mLists[6]; }; class nsDisplayImageContainer : public nsDisplayItem { public: typedef mozilla::LayerIntPoint LayerIntPoint; typedef mozilla::LayoutDeviceRect LayoutDeviceRect; typedef mozilla::layers::ImageContainer ImageContainer; typedef mozilla::layers::ImageLayer ImageLayer; nsDisplayImageContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : nsDisplayItem(aBuilder, aFrame) {} /** * @return true if this display item can be optimized into an image layer. * It is an error to call GetContainer() unless you've called * CanOptimizeToImageLayer() first and it returned true. */ virtual bool CanOptimizeToImageLayer(LayerManager* aManager, nsDisplayListBuilder* aBuilder); already_AddRefed GetContainer(LayerManager* aManager, nsDisplayListBuilder* aBuilder); void ConfigureLayer(ImageLayer* aLayer, const ContainerLayerParameters& aParameters); virtual void UpdateDrawResult(mozilla::image::ImgDrawResult aResult) = 0; virtual already_AddRefed GetImage() = 0; virtual nsRect GetDestRect() const = 0; virtual bool SupportsOptimizingToImage() const 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, DrawTarget* aDrawTarget, const nsRect& aDirtyRect, nsPoint aFramePt); // XXX: should be removed eventually typedef void (*OldPaintCallback)(nsIFrame* aFrame, gfxContext* aCtx, const nsRect& aDirtyRect, nsPoint aFramePt); nsDisplayGeneric(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, PaintCallback aPaint, const char* aName, DisplayItemType aType) : nsDisplayItem(aBuilder, aFrame), mPaint(aPaint), mOldPaint(nullptr), mName(aName), mType(aType) { MOZ_COUNT_CTOR(nsDisplayGeneric); } // XXX: should be removed eventually nsDisplayGeneric(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, OldPaintCallback aOldPaint, const char* aName, DisplayItemType aType) : nsDisplayItem(aBuilder, aFrame), mPaint(nullptr), mOldPaint(aOldPaint), mName(aName), mType(aType) { MOZ_COUNT_CTOR(nsDisplayGeneric); } #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayGeneric() { MOZ_COUNT_DTOR(nsDisplayGeneric); } #endif virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override { MOZ_ASSERT(!!mPaint != !!mOldPaint); if (mPaint) { mPaint(mFrame, aCtx->GetDrawTarget(), mVisibleRect, ToReferenceFrame()); } else { mOldPaint(mFrame, aCtx, mVisibleRect, ToReferenceFrame()); } } virtual const char* Name() const override { return mName; } virtual DisplayItemType GetType() const override { return mType; } // This override is needed because GetType() for nsDisplayGeneric subclasses // does not match TYPE_GENERIC that was used to allocate the object. virtual void Destroy(nsDisplayListBuilder* aBuilder) override { this->~nsDisplayGeneric(); aBuilder->Destroy(DisplayItemType::TYPE_GENERIC, this); } protected: void* operator new(size_t aSize, nsDisplayListBuilder* aBuilder) { return aBuilder->Allocate(aSize, DisplayItemType::TYPE_GENERIC); } template friend T* MakeDisplayItem(nsDisplayListBuilder* aBuilder, Args&&... aArgs); PaintCallback mPaint; OldPaintCallback mOldPaint; // XXX: should be removed eventually const char* mName; DisplayItemType mType; }; #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, gfxContext* aCtx) override { mFrame->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() && \ PresShell()->IsPaintingFrameCounts()) { \ aLists.Outlines()->AppendToTop( \ MakeDisplayItem(aBuilder, this, _name)); \ } \ PR_END_MACRO #define DO_GLOBAL_REFLOW_COUNT_DSP_COLOR(_name, _color) \ PR_BEGIN_MACRO \ if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \ PresShell()->IsPaintingFrameCounts()) { \ aLists.Outlines()->AppendToTop( \ MakeDisplayItem(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) const override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; NS_DISPLAY_DECL_NAME("Caret", TYPE_CARET) virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; protected: RefPtr 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) const override; virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* 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) const override; virtual nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = true; return CalculateBounds(*mFrame->StyleBorder()); } protected: void CreateBorderImageWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResource, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder); template T CalculateBounds(const nsStyleBorder& aStyleBorder) const { nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize()); if (aStyleBorder.IsBorderImageLoaded()) { borderBounds.Inflate(aStyleBorder.GetImageOutset()); return borderBounds; } else { nsMargin border = aStyleBorder.GetComputedBorder(); T result; if (border.top > 0) { result = nsRect(borderBounds.X(), borderBounds.Y(), borderBounds.Width(), border.top); } if (border.right > 0) { result.OrWith(nsRect(borderBounds.XMost() - border.right, borderBounds.Y(), border.right, borderBounds.Height())); } if (border.bottom > 0) { result.OrWith(nsRect(borderBounds.X(), borderBounds.YMost() - border.bottom, borderBounds.Width(), border.bottom)); } if (border.left > 0) { result.OrWith(nsRect(borderBounds.X(), borderBounds.Y(), border.left, borderBounds.Height())); } nscoord radii[8]; if (mFrame->GetBorderRadii(radii)) { if (border.left > 0 || border.top > 0) { nsSize cornerSize(radii[mozilla::eCornerTopLeftX], radii[mozilla::eCornerTopLeftY]); result.OrWith(nsRect(borderBounds.TopLeft(), cornerSize)); } if (border.top > 0 || border.right > 0) { nsSize cornerSize(radii[mozilla::eCornerTopRightX], radii[mozilla::eCornerTopRightY]); result.OrWith( nsRect(borderBounds.TopRight() - nsPoint(cornerSize.width, 0), cornerSize)); } if (border.right > 0 || border.bottom > 0) { nsSize cornerSize(radii[mozilla::eCornerBottomRightX], radii[mozilla::eCornerBottomRightY]); result.OrWith(nsRect(borderBounds.BottomRight() - nsPoint(cornerSize.width, cornerSize.height), cornerSize)); } if (border.bottom > 0 || border.left > 0) { nsSize cornerSize(radii[mozilla::eCornerBottomLeftX], radii[mozilla::eCornerBottomLeftY]); result.OrWith( nsRect(borderBounds.BottomLeft() - nsPoint(0, cornerSize.height), cornerSize)); } } return result; } } mozilla::Array mColors; mozilla::Array mWidths; mozilla::Array mCorners; mozilla::Array mBorderStyles; mozilla::LayerRect mRect; mozilla::Maybe mBorderRenderer; mozilla::Maybe mBorderImageRenderer; nsRect mBounds; bool mBorderIsEmpty; }; /** * A simple display item that just renders a solid color across the * specified bounds. For canvas frames (in the CSS sense) we split off the * drawing of the background color into this class (from nsDisplayBackground * via nsDisplayCanvasBackground). This is done so that we can always draw a * background color to avoid ugly flashes of white when we can't draw a full * frame tree (ie when a page is loading). The bounds can differ from the * frame's bounds -- this is needed when a frame/iframe is loading and there * is not yet a frame tree to go in the frame/iframe so we use the subdoc * frame of the parent document as a standin. */ class nsDisplaySolidColorBase : public nsDisplayItem { public: nsDisplaySolidColorBase(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nscolor aColor) : nsDisplayItem(aBuilder, aFrame), mColor(aColor) {} virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override { return new nsDisplaySolidColorGeometry(this, aBuilder, mColor); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override { const nsDisplaySolidColorGeometry* geometry = static_cast(aGeometry); if (mColor != geometry->mColor) { bool dummy; aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &dummy)); return; } ComputeInvalidationRegionDifference(aBuilder, geometry, aInvalidRegion); } virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = false; nsRegion result; if (NS_GET_A(mColor) == 255) { result = GetBounds(aBuilder, aSnap); } return result; } virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const override { return mozilla::Some(mColor); } protected: nscolor mColor; }; class nsDisplaySolidColor : public nsDisplaySolidColorBase { public: nsDisplaySolidColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBounds, nscolor aColor, bool aCanBeReused = true) : nsDisplaySolidColorBase(aBuilder, aFrame, aColor), mBounds(aBounds), mCanBeReused(aCanBeReused) { 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) const override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; virtual void WriteDebugInfo(std::stringstream& aStream) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR) virtual bool CanBeReused() const override { return mCanBeReused; } int32_t ZIndex() const override { if (mOverrideZIndex) { return mOverrideZIndex.value(); } return nsDisplaySolidColorBase::ZIndex(); } void SetOverrideZIndex(int32_t aZIndex) { mOverrideZIndex = mozilla::Some(aZIndex); } private: nsRect mBounds; bool mCanBeReused; mozilla::Maybe mOverrideZIndex; }; /** * A display item that renders a solid color over a region. This is not * exposed through CSS, its only purpose is efficient invalidation of * the find bar highlighter dimmer. */ class nsDisplaySolidColorRegion : public nsDisplayItem { typedef mozilla::gfx::Color Color; public: nsDisplaySolidColorRegion(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRegion& aRegion, nscolor aColor) : nsDisplayItem(aBuilder, aFrame), mRegion(aRegion), mColor(Color::FromABGR(aColor)) { NS_ASSERTION(NS_GET_A(aColor) > 0, "Don't create invisible nsDisplaySolidColorRegions!"); MOZ_COUNT_CTOR(nsDisplaySolidColorRegion); } #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplaySolidColorRegion() { MOZ_COUNT_DTOR(nsDisplaySolidColorRegion); } #endif virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override { return new nsDisplaySolidColorRegionGeometry(this, aBuilder, mRegion, mColor); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override { const nsDisplaySolidColorRegionGeometry* geometry = static_cast(aGeometry); if (mColor == geometry->mColor) { aInvalidRegion->Xor(geometry->mRegion, mRegion); } else { aInvalidRegion->Or(geometry->mRegion.GetBounds(), mRegion.GetBounds()); } } virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; NS_DISPLAY_DECL_NAME("SolidColorRegion", TYPE_SOLID_COLOR_REGION) protected: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; virtual void WriteDebugInfo(std::stringstream& aStream) override; private: nsRegion mRegion; Color mColor; }; /** * A display item to paint one background-image for a frame. Each background * image layer gets its own nsDisplayBackgroundImage. */ class nsDisplayBackgroundImage : public nsDisplayImageContainer { public: typedef mozilla::StyleGeometryBox StyleGeometryBox; struct InitData { nsDisplayListBuilder* builder; nsIFrame* frame; const nsStyleBackground* backgroundStyle; nsCOMPtr image; nsRect backgroundRect; nsRect fillArea; nsRect destArea; uint32_t layer; bool isRasterImage; bool shouldFixToViewport; }; /** * aLayer signifies which background layer this item represents. * aIsThemed should be the value of aFrame->IsThemed. * aBackgroundStyle should be the result of * nsCSSRendering::FindBackground, or null if FindBackground returned false. * aBackgroundRect is relative to aFrame. */ static InitData GetInitData(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, uint32_t aLayer, const nsRect& aBackgroundRect, const nsStyleBackground* aBackgroundStyle); explicit nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder, const InitData& aInitData, nsIFrame* aFrameForBounds = nullptr); virtual ~nsDisplayBackgroundImage(); // This will create and append new items for all the layers of the // background. Returns whether we appended a themed background. // aAllowWillPaintBorderOptimization should usually be left at true, unless // aFrame has special border drawing that causes opaque borders to not // actually be opaque. static bool AppendBackgroundItemsToTop( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBackgroundRect, nsDisplayList* aList, bool aAllowWillPaintBorderOptimization = true, nsStyleContext* aStyleContext = nullptr, const nsRect& aBackgroundOriginRect = nsRect(), nsIFrame* aSecondaryReferenceFrame = nullptr); virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const override; /** * GetBounds() returns the background painting area. */ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; virtual uint32_t GetPerFrameKey() const override { return (mLayer << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } 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() const; /** * Returns true if existing rendered pixels of this display item may need * to be redrawn if the positioning area size changes but its position does * not. * If false, only the changed painting area needs to be redrawn when the * positioning area size changes but its position does not. */ bool RenderingMightDependOnPositioningAreaSizeChange() const; virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override { return new nsDisplayBackgroundGeometry(this, aBuilder); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override; virtual bool CanOptimizeToImageLayer(LayerManager* aManager, nsDisplayListBuilder* aBuilder) override; virtual already_AddRefed GetImage() override; virtual nsRect GetDestRect() const override; virtual void UpdateDrawResult( mozilla::image::ImgDrawResult aResult) override { nsDisplayBackgroundGeometry::UpdateDrawResult(this, aResult); } static nsRegion GetInsideClipRegion(const nsDisplayItem* aItem, StyleGeometryBox aClip, const nsRect& aRect, const nsRect& aBackgroundRect); virtual bool ShouldFixToViewport( nsDisplayListBuilder* aBuilder) const override { return mShouldFixToViewport; } virtual nsIFrame* GetDependentFrame() override { return mDependentFrame; } void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { if (!aBuilder->IsRetainingDisplayList()) { return; } mDependentFrame = aFrame; if (aFrame) { mDependentFrame->AddDisplayItem(this); } } virtual void RemoveFrame(nsIFrame* aFrame) override { if (aFrame == mDependentFrame) { mDependentFrame = nullptr; } nsDisplayItem::RemoveFrame(aFrame); } protected: typedef class mozilla::layers::ImageContainer ImageContainer; typedef class mozilla::layers::ImageLayer ImageLayer; bool CanBuildWebRenderDisplayItems(LayerManager* aManager); bool TryOptimizeToImageLayer(LayerManager* aManager, nsDisplayListBuilder* aBuilder); nsRect GetBoundsInternal(nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds = nullptr); void PaintInternal(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, const nsRect& aBounds, nsRect* aClipRect); // Determine whether we want to be separated into our own layer, independent // of whether this item can actually be layerized. enum ImageLayerization { WHENEVER_POSSIBLE, ONLY_FOR_SCALING, NO_LAYER_NEEDED }; ImageLayerization ShouldCreateOwnLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager); // Cache the result of nsCSSRendering::FindBackground. Always null if // mIsThemed is true or if FindBackground returned false. const nsStyleBackground* mBackgroundStyle; nsCOMPtr mImage; nsIFrame* mDependentFrame; nsRect mBackgroundRect; // relative to the reference frame nsRect mFillRect; nsRect mDestRect; /* Bounds of this display item */ nsRect mBounds; uint32_t mLayer; bool mIsRasterImage; /* Whether the image should be treated as fixed to the viewport. */ bool mShouldFixToViewport; uint32_t mImageFlags; }; enum class TableType : uint8_t { TABLE, TABLE_COL, TABLE_COL_GROUP, TABLE_ROW, TABLE_ROW_GROUP, TABLE_CELL, TABLE_TYPE_MAX }; enum class TableTypeBits : uint8_t { COUNT = 3 }; static_assert(static_cast(TableType::TABLE_TYPE_MAX) < (1 << (static_cast(TableTypeBits::COUNT) + 1)), "TableType cannot fit with TableTypeBits::COUNT"); TableType GetTableTypeFromFrame(nsIFrame* aFrame); /** * A display item to paint background image for table. For table parts, such * as row, row group, col, col group, when drawing its background, we'll * create separate background image display item for its containning cell. * Those background image display items will reference to same DisplayItemData * if we keep the mFrame point to cell's ancestor frame. We don't want to this * happened bacause share same DisplatItemData will cause many bugs. So that * we let mFrame point to cell frame and store the table type of the ancestor * frame. And use mFrame and table type as key to generate DisplayItemData to * avoid sharing DisplayItemData. * * Also store ancestor frame as mStyleFrame for all rendering informations. */ class nsDisplayTableBackgroundImage : public nsDisplayBackgroundImage { public: nsDisplayTableBackgroundImage(nsDisplayListBuilder* aBuilder, const InitData& aInitData, nsIFrame* aCellFrame); virtual uint32_t GetPerFrameKey() const override { return (mLayer << (TYPE_BITS + static_cast(TableTypeBits::COUNT))) | (static_cast(mTableType) << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } virtual bool IsInvalid(nsRect& aRect) const override; virtual nsIFrame* FrameForInvalidation() const override { return mStyleFrame; } NS_DISPLAY_DECL_NAME("TableBackgroundImage", TYPE_TABLE_BACKGROUND_IMAGE) protected: virtual nsIFrame* StyleFrame() const override { return mStyleFrame; } nsIFrame* mStyleFrame; TableType mTableType; }; /** * A display item to paint the native theme background for a frame. */ class nsDisplayThemedBackground : public nsDisplayItem { public: nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBackgroundRect); virtual ~nsDisplayThemedBackground(); void Init(nsDisplayListBuilder* aBuilder); void Destroy(nsDisplayListBuilder* aBuilder) override { aBuilder->UnregisterThemeGeometry(this); nsDisplayItem::Destroy(aBuilder); } virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual bool MustPaintOnContentSide() const override { return true; } /** * GetBounds() returns the background painting area. */ virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* 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() const; /** * Return whether our frame's document does not have the state * NS_DOCUMENT_STATE_WINDOW_INACTIVE. */ bool IsWindowActive() const; virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override { return new nsDisplayThemedBackgroundGeometry(this, aBuilder); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override; virtual void WriteDebugInfo(std::stringstream& aStream) override; protected: nsRect GetBoundsInternal(); void PaintInternal(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, const nsRect& aBounds, nsRect* aClipRect); nsRect mBackgroundRect; nsRect mBounds; nsITheme::Transparency mThemeTransparency; uint8_t mAppearance; }; class nsDisplayTableThemedBackground : public nsDisplayThemedBackground { public: nsDisplayTableThemedBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBackgroundRect, nsIFrame* aAncestorFrame) : nsDisplayThemedBackground(aBuilder, aFrame, aBackgroundRect), mAncestorFrame(aAncestorFrame), mTableType(GetTableTypeFromFrame(aAncestorFrame)) {} virtual uint32_t GetPerFrameKey() const override { return (static_cast(mTableType) << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } NS_DISPLAY_DECL_NAME("TableThemedBackground", TYPE_TABLE_THEMED_BACKGROUND_IMAGE) protected: virtual nsIFrame* StyleFrame() const override { return mAncestorFrame; } nsIFrame* mAncestorFrame; TableType mTableType; }; class nsDisplayBackgroundColor : public nsDisplayItem { typedef mozilla::gfx::Color Color; public: nsDisplayBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBackgroundRect, const nsStyleBackground* aBackgroundStyle, nscolor aColor) : nsDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect), mBackgroundStyle(aBackgroundStyle), mDependentFrame(nullptr), mColor(Color::FromABGR(aColor)) { mState.mColor = mColor; } virtual ~nsDisplayBackgroundColor() { if (mDependentFrame) { mDependentFrame->RemoveDisplayItem(this); } } virtual void RestoreState() override { nsDisplayItem::RestoreState(); mColor = mState.mColor; } virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const override; virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity, const DisplayItemClipChain* aClip) override; virtual bool CanApplyOpacity() const override; virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = true; return mBackgroundRect; } virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override { return new nsDisplaySolidColorGeometry(this, aBuilder, mColor.ToABGR()); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override { const nsDisplaySolidColorGeometry* geometry = static_cast(aGeometry); if (mColor.ToABGR() != geometry->mColor) { bool dummy; aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &dummy)); return; } ComputeInvalidationRegionDifference(aBuilder, geometry, aInvalidRegion); } virtual nsIFrame* GetDependentFrame() override { return mDependentFrame; } void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { if (!aBuilder->IsRetainingDisplayList()) { return; } mDependentFrame = aFrame; if (aFrame) { mDependentFrame->AddDisplayItem(this); } } virtual void RemoveFrame(nsIFrame* aFrame) override { if (aFrame == mDependentFrame) { mDependentFrame = nullptr; } nsDisplayItem::RemoveFrame(aFrame); } NS_DISPLAY_DECL_NAME("BackgroundColor", TYPE_BACKGROUND_COLOR) virtual void WriteDebugInfo(std::stringstream& aStream) override; protected: const nsRect mBackgroundRect; const nsStyleBackground* mBackgroundStyle; nsIFrame* mDependentFrame; mozilla::gfx::Color mColor; struct { mozilla::gfx::Color mColor; } mState; }; class nsDisplayTableBackgroundColor : public nsDisplayBackgroundColor { public: nsDisplayTableBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBackgroundRect, const nsStyleBackground* aBackgroundStyle, nscolor aColor, nsIFrame* aAncestorFrame) : nsDisplayBackgroundColor(aBuilder, aFrame, aBackgroundRect, aBackgroundStyle, aColor), mAncestorFrame(aAncestorFrame), mTableType(GetTableTypeFromFrame(aAncestorFrame)) {} virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } virtual uint32_t GetPerFrameKey() const override { return (static_cast(mTableType) << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } NS_DISPLAY_DECL_NAME("TableBackgroundColor", TYPE_TABLE_BACKGROUND_COLOR) protected: nsIFrame* mAncestorFrame; TableType mTableType; }; class nsDisplayClearBackground : public nsDisplayItem { public: nsDisplayClearBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : nsDisplayItem(aBuilder, aFrame) {} virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = true; return nsRect(ToReferenceFrame(), Frame()->GetSize()); } virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = false; return GetBounds(aBuilder, aSnap); } virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const override { return mozilla::Some(NS_RGBA(0, 0, 0, 0)); } virtual bool ClearsBackground() const override { return true; } virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override { return mozilla::LAYER_ACTIVE_FORCE; } virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) 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.0f) { MOZ_COUNT_CTOR(nsDisplayBoxShadowOuter); mBounds = GetBoundsInternal(); } #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayBoxShadowOuter() { MOZ_COUNT_DTOR(nsDisplayBoxShadowOuter); } #endif virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual bool IsInvisibleInRect(const nsRect& aRect) const 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) const override; virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity, const DisplayItemClipChain* aClip) override { NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed"); mOpacity = aOpacity; IntersectClip(aBuilder, aClip, false); } virtual bool CanApplyOpacity() const override { return true; } virtual void RestoreState() override { nsDisplayItem::RestoreState(); mVisibleRegion.SetEmpty(); mOpacity = 1.0f; } virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override { return new nsDisplayBoxShadowOuterGeometry(this, aBuilder, mOpacity); } virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; bool CanBuildWebRenderDisplayItems(); virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; 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 RestoreState() override { nsDisplayItem::RestoreState(); mVisibleRegion.SetEmpty(); } virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* 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) const override { const nsDisplayBoxShadowInnerGeometry* geometry = static_cast(aGeometry); if (!geometry->mPaddingRect.IsEqualInterior(GetPaddingRect())) { // nsDisplayBoxShadowInner is based around the padding rect, but it can // touch pixels outside of this. We should invalidate the entire bounds. bool snap; aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap)); } } static bool CanCreateWebRenderCommands(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsPoint aReferencePoint); static void CreateInsetBoxShadowWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc, nsRegion& aVisibleRegion, nsIFrame* aFrame, const nsRect aBorderRect); virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; 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 LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual bool IsInvisibleInRect(const nsRect& aRect) const override; virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; NS_DISPLAY_DECL_NAME("Outline", TYPE_OUTLINE) mozilla::Maybe mBorderRenderer; }; /** * 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 void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; NS_DISPLAY_DECL_NAME("EventReceiver", TYPE_EVENT_RECEIVER) }; /** * Similar to nsDisplayEventReceiver in that it is used for hit-testing. However * this gets built when we're doing widget painting and we need to send the * compositor some hit-test info for a frame. This is effectively a dummy item * whose sole purpose is to carry the hit-test info to the compositor. */ class nsDisplayCompositorHitTestInfo : public nsDisplayEventReceiver { public: nsDisplayCompositorHitTestInfo( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, mozilla::gfx::CompositorHitTestInfo aHitTestInfo, uint32_t aIndex = 0, const mozilla::Maybe& aArea = mozilla::Nothing()); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayCompositorHitTestInfo() { MOZ_COUNT_DTOR(nsDisplayCompositorHitTestInfo); } #endif mozilla::gfx::CompositorHitTestInfo HitTestInfo() const { return mHitTestInfo; } bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; void WriteDebugInfo(std::stringstream& aStream) override; uint32_t GetPerFrameKey() const override; int32_t ZIndex() const override; void SetOverrideZIndex(int32_t aZIndex); /** * Returns the hit test area of this item. */ const nsRect& Area() const { return mArea; } /** * ApplyOpacity() is overriden for opacity flattening. */ void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity, const DisplayItemClipChain* aClip) override {} /** * CanApplyOpacity() is overriden for opacity flattening. */ bool CanApplyOpacity() const override { return true; } nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = false; return nsRect(); } NS_DISPLAY_DECL_NAME("CompositorHitTestInfo", TYPE_COMPOSITOR_HITTEST_INFO) private: mozilla::gfx::CompositorHitTestInfo mHitTestInfo; mozilla::Maybe mScrollTarget; nsRect mArea; uint32_t mIndex; mozilla::Maybe mOverrideZIndex; int32_t mAppUnitsPerDevPixel; }; /** * 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, uint32_t aIndex = 0) : nsDisplayItem(aBuilder, aFrame), mIndex(aIndex) { MOZ_COUNT_CTOR(nsDisplayLayerEventRegions); } virtual void Destroy(nsDisplayListBuilder* aBuilder) override { if (!aBuilder->IsRetainingDisplayList()) { nsDisplayItem::Destroy(aBuilder); return; } RemoveItemFromFrames(mHitRegion); RemoveItemFromFrames(mMaybeHitRegion); RemoveItemFromFrames(mDispatchToContentHitRegion); RemoveItemFromFrames(mNoActionRegion); RemoveItemFromFrames(mHorizontalPanRegion); RemoveItemFromFrames(mVerticalPanRegion); nsDisplayItem::Destroy(aBuilder); } virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = false; return nsRect(); } nsRect GetHitRegionBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { *aSnap = false; // TODO: This constructs the two regions, but we're also doing the same // work in AccumulateEventRegions. We should avoid doing it twice. return HitRegion().GetBounds().Union(MaybeHitRegion().GetBounds()); } virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity, const DisplayItemClipChain* 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(nsIFrame* aFrame, const nsRect& aRect); bool IsEmpty() const; int32_t ZIndex() const override; void SetOverrideZIndex(int32_t aZIndex); virtual uint32_t GetPerFrameKey() const override { return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } const nsRegion HitRegion() { return nsRegion(mozilla::gfx::ArrayView(mHitRegion.mBoxes)); } const nsRegion MaybeHitRegion() { nsRegion result( mozilla::gfx::ArrayView(mMaybeHitRegion.mBoxes)); // Avoid quadratic performance as a result of the region growing to include // an arbitrarily large number of rects, which can happen on some pages. // TODO: It would be nice if we could ask the initial construction above // to include simplification. result.SimplifyOutward(8); return result; } const nsRegion DispatchToContentHitRegion() { nsRegion result(mozilla::gfx::ArrayView( mDispatchToContentHitRegion.mBoxes)); // If this frame has touch-action areas, and there were already // touch-action areas from some other element on this same event regions, // then all we know is that there are multiple elements with touch-action // properties. In particular, we don't know what the relationship is // between those elements in terms of DOM ancestry, and so we don't know // how to combine the regions properly. Instead, we just add all the areas // to the dispatch-to-content region, so that the APZ knows to check with // the main thread. XXX we need to come up with a better way to do this, // see bug 1287829. uint32_t touchActionCount = mNoActionRegion.mBoxes.Length() + mHorizontalPanRegion.mBoxes.Length() + mVerticalPanRegion.mBoxes.Length(); if (touchActionCount > 1) { result.OrWith(NoActionRegion()); result.OrWith(HorizontalPanRegion()); result.OrWith(VerticalPanRegion()); } result.SimplifyOutward(8); return result; } const nsRegion NoActionRegion() { return nsRegion( mozilla::gfx::ArrayView(mNoActionRegion.mBoxes)); } const nsRegion HorizontalPanRegion() { return nsRegion( mozilla::gfx::ArrayView(mHorizontalPanRegion.mBoxes)); } const nsRegion VerticalPanRegion() { return nsRegion( mozilla::gfx::ArrayView(mVerticalPanRegion.mBoxes)); } nsRegion CombinedTouchActionRegion(); virtual void WriteDebugInfo(std::stringstream& aStream) override; // TODO: nsTArray (vector) might not be a great data structure // choice since we need to remove elements from the middle. // Should profile and try figure out the best approach // here. struct FrameRects { void Add(nsIFrame* aFrame, const nsRect& aRect) { mBoxes.AppendElement(nsRegion::RectToBox(aRect)); mFrames.AppendElement(aFrame); } void Add(nsIFrame* aFrame, const pixman_box32& aBox) { mBoxes.AppendElement(aBox); mFrames.AppendElement(aFrame); } void Add(const FrameRects& aOther) { mBoxes.AppendElements(aOther.mBoxes); mFrames.AppendElements(aOther.mFrames); } bool IsEmpty() const { return mBoxes.IsEmpty(); } nsTArray mBoxes; nsTArray mFrames; }; virtual void RemoveFrame(nsIFrame* aFrame) override; private: virtual ~nsDisplayLayerEventRegions() { MOZ_COUNT_DTOR(nsDisplayLayerEventRegions); } void RemoveItemFromFrames(FrameRects& aFrameRects) { for (nsIFrame* f : aFrameRects.mFrames) { if (f != mFrame) { f->RemoveDisplayItem(this); } } } friend bool MergeLayerEventRegions(nsDisplayItem*, nsDisplayItem*); // Relative to aFrame's reference frame. // These are the points that are definitely in the hit region. FrameRects 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). FrameRects mMaybeHitRegion; // These are points that need to be dispatched to the content thread for // resolution. Always contained in the union of mHitRegion and // mMaybeHitRegion. FrameRects mDispatchToContentHitRegion; // These are points where panning is disabled, as determined by the // touch-action property. Always contained in the union of mHitRegion and // mMaybeHitRegion. FrameRects mNoActionRegion; // These are points where panning is horizontal, as determined by the // touch-action property. Always contained in the union of mHitRegion and // mMaybeHitRegion. FrameRects mHorizontalPanRegion; // These are points where panning is vertical, as determined by the // touch-action property. Always contained in the union of mHitRegion and // mMaybeHitRegion. FrameRects mVerticalPanRegion; // If these event regions are for an inactive scroll frame, the z-index of // this display item is overridden to be the largest z-index of the content // in the scroll frame. This ensures that the event regions item remains on // top of the content after sorting items by z-index. mozilla::Maybe mOverrideZIndex; uint32_t mIndex; }; /** * A class that lets you wrap a display list as a display item. * * GetUnderlyingFrame() is troublesome for wrapped lists because if the wrapped * list has many items, it's not clear which one has the 'underlying frame'. * Thus we force the creator to specify what the underlying frame is. The * underlying frame should be the root of a stacking context, because sorting * a list containing this item will not get at the children. * * In some cases (e.g., clipping) we want to wrap a list but we don't have a * particular underlying frame that is a stacking context root. In that case * we allow the frame to be nullptr. Callers to GetUnderlyingFrame must * detect and handle this case. */ class nsDisplayWrapList : public nsDisplayItem { public: /** * Takes all the items from aList and puts them in our list. */ nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList); nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain = false); nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayItem* aItem); nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : nsDisplayItem(aBuilder, aFrame), mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()), mOverrideZIndex(0), mHasZIndexOverride(false) { MOZ_COUNT_CTOR(nsDisplayWrapList); mBaseVisibleRect = mVisibleRect; mListPtr = &mList; } /** * A custom copy-constructor that does not copy mList, as this would mutate * the other item. */ nsDisplayWrapList(const nsDisplayWrapList& aOther) = delete; nsDisplayWrapList(nsDisplayListBuilder* aBuilder, const nsDisplayWrapList& aOther) : nsDisplayItem(aBuilder, aOther), mListPtr(&mList), mFrameActiveScrolledRoot(aOther.mFrameActiveScrolledRoot), mMergedFrames(aOther.mMergedFrames), mBounds(aOther.mBounds), mBaseVisibleRect(aOther.mBaseVisibleRect), mOverrideZIndex(aOther.mOverrideZIndex), mHasZIndexOverride(aOther.mHasZIndexOverride), mClearingClipChain(aOther.mClearingClipChain) { MOZ_COUNT_CTOR(nsDisplayWrapList); } virtual ~nsDisplayWrapList(); virtual const nsDisplayWrapList* AsDisplayWrapList() const override { return this; } virtual nsDisplayWrapList* AsDisplayWrapList() override { return this; } virtual void Destroy(nsDisplayListBuilder* aBuilder) override { mList.DeleteAll(aBuilder); nsDisplayItem::Destroy(aBuilder); } /** * Creates a new nsDisplayWrapList that holds a pointer to the display list * owned by the given nsDisplayItem. The new nsDisplayWrapList will be added * to the bottom of this item's contents. */ virtual void MergeDisplayListFromItem(nsDisplayListBuilder* aBuilder, const nsDisplayItem* aItem) override; /** * Call this if the wrapped list is changed. */ virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override { // Clear the clip chain up to the asr, but don't store it, so that we'll // recover it when we reuse the item. if (mClearingClipChain) { const DisplayItemClipChain* clip = mState.mClipChain; while (clip && ActiveScrolledRoot::IsAncestor(GetActiveScrolledRoot(), clip->mASR)) { clip = clip->mParent; } SetClipChain(clip, false); } nsRect visibleRect; mBounds = mListPtr->GetClippedBoundsWithRespectToASR( aBuilder, mActiveScrolledRoot, &visibleRect); // 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, visibleRect); mState.mVisibleRect = mVisibleRect; } virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const override; virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; virtual bool CanMerge(const nsDisplayItem* aItem) const override { return false; } virtual void Merge(const nsDisplayItem* aItem) override { MOZ_ASSERT(CanMerge(aItem)); MergeFromTrackingMergedFrames(static_cast(aItem)); } virtual void GetMergedFrames(nsTArray* aFrames) const override { aFrames->AppendElements(mMergedFrames); } virtual bool HasMergedFrames() const override { return !mMergedFrames.IsEmpty(); } virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { return true; } virtual bool IsInvalid(nsRect& aRect) const override { if (mFrame->IsInvalid(aRect) && aRect.IsEmpty()) { return true; } nsRect temp; for (uint32_t i = 0; i < mMergedFrames.Length(); i++) { if (mMergedFrames[i]->IsInvalid(temp) && temp.IsEmpty()) { aRect.SetEmpty(); return true; } aRect = aRect.Union(temp); } aRect += ToReferenceFrame(); return !aRect.IsEmpty(); } NS_DISPLAY_DECL_NAME("WrapList", TYPE_WRAP_LIST) virtual nsRect GetComponentAlphaBounds( nsDisplayListBuilder* aBuilder) const override; virtual nsDisplayList* GetSameCoordinateSystemChildren() const override { NS_ASSERTION( mListPtr->IsEmpty() || !ReferenceFrame() || !mListPtr->GetBottom()->ReferenceFrame() || mListPtr->GetBottom()->ReferenceFrame() == ReferenceFrame(), "Children must have same reference frame"); return mListPtr; } virtual nsDisplayList* GetChildren() const override { return mListPtr; } virtual int32_t ZIndex() const override { return (mHasZIndexOverride) ? mOverrideZIndex : nsDisplayItem::ZIndex(); } void SetOverrideZIndex(int32_t aZIndex) { mHasZIndexOverride = true; mOverrideZIndex = aZIndex; } 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; } virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; const ActiveScrolledRoot* GetFrameActiveScrolledRoot() { return mFrameActiveScrolledRoot; } protected: nsDisplayWrapList() = delete; void MergeFromTrackingMergedFrames(const nsDisplayWrapList* aOther) { mBounds.UnionRect(mBounds, aOther->mBounds); mVisibleRect.UnionRect(mVisibleRect, aOther->mVisibleRect); mMergedFrames.AppendElement(aOther->mFrame); mMergedFrames.AppendElements(aOther->mMergedFrames); } nsDisplayList mList; nsDisplayList* mListPtr; // The active scrolled root for the frame that created this // wrap list. RefPtr mFrameActiveScrolledRoot; // The frames from items that have been merged into this item, excluding // this item's own frame. nsTArray 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; bool mClearingClipChain = false; }; /** * 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() = default; }; /** * The standard display item to paint a stacking context with translucency * set by the stacking context root frame's 'opacity' style. */ class nsDisplayOpacity : public nsDisplayWrapList { public: nsDisplayOpacity(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsAndPluginsOnly); nsDisplayOpacity(nsDisplayListBuilder* aBuilder, const nsDisplayOpacity& aOther) : nsDisplayWrapList(aBuilder, aOther), mOpacity(aOther.mOpacity), mForEventsAndPluginsOnly(aOther.mForEventsAndPluginsOnly) {} #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayOpacity(); #endif virtual void RestoreState() override { nsDisplayItem::RestoreState(); mOpacity = mState.mOpacity; } virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { MOZ_COUNT_CTOR(nsDisplayOpacity); return MakeDisplayItem(aBuilder, *this); } virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual already_AddRefed 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 CanMerge(const nsDisplayItem* aItem) const override { // items for the same content element should be merged into a single // compositing group // aItem->GetUnderlyingFrame() returns non-null because it's // nsDisplayOpacity return HasSameTypeAndClip(aItem) && HasSameContent(aItem); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override { // We don't need to compute an invalidation region since we have // LayerTreeInvalidation } virtual bool IsInvalid(nsRect& aRect) const override { if (mForEventsAndPluginsOnly) { return false; } return nsDisplayWrapList::IsInvalid(aRect); } virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity, const DisplayItemClipChain* aClip) override; virtual bool CanApplyOpacity() const override; virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override; static bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame); NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY) virtual void WriteDebugInfo(std::stringstream& aStream) override; bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; private: float mOpacity; bool mForEventsAndPluginsOnly; struct { float mOpacity; } mState; }; class nsDisplayBlendMode : public nsDisplayWrapList { public: nsDisplayBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, uint8_t aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot, uint32_t aIndex = 0); nsDisplayBlendMode(nsDisplayListBuilder* aBuilder, const nsDisplayBlendMode& aOther) : nsDisplayWrapList(aBuilder, aOther), mBlendMode(aOther.mBlendMode), mIndex(aOther.mIndex) {} #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayBlendMode(); #endif virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { MOZ_COUNT_CTOR(nsDisplayBlendMode); return MakeDisplayItem(aBuilder, *this); } nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override { // We don't need to compute an invalidation region since we have // LayerTreeInvalidation } virtual uint32_t GetPerFrameKey() const override { return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; virtual bool CanMerge(const nsDisplayItem* aItem) const override; virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { return false; } NS_DISPLAY_DECL_NAME("BlendMode", TYPE_BLEND_MODE) protected: uint8_t mBlendMode; uint32_t mIndex; }; class nsDisplayTableBlendMode : public nsDisplayBlendMode { public: nsDisplayTableBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, uint8_t aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot, uint32_t aIndex, nsIFrame* aAncestorFrame) : nsDisplayBlendMode(aBuilder, aFrame, aList, aBlendMode, aActiveScrolledRoot, aIndex), mAncestorFrame(aAncestorFrame), mTableType(GetTableTypeFromFrame(aAncestorFrame)) {} nsDisplayTableBlendMode(nsDisplayListBuilder* aBuilder, const nsDisplayTableBlendMode& aOther) : nsDisplayBlendMode(aBuilder, aOther), mAncestorFrame(aOther.mAncestorFrame), mTableType(aOther.mTableType) {} virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { return MakeDisplayItem(aBuilder, *this); } virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } virtual uint32_t GetPerFrameKey() const override { return (mIndex << (TYPE_BITS + static_cast(TableTypeBits::COUNT))) | (static_cast(mTableType) << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } NS_DISPLAY_DECL_NAME("TableBlendMode", TYPE_TABLE_BLEND_MODE) protected: nsIFrame* mAncestorFrame; TableType mTableType; }; class nsDisplayBlendContainer : public nsDisplayWrapList { public: static nsDisplayBlendContainer* CreateForMixBlendMode( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot); static nsDisplayBlendContainer* CreateForBackgroundBlendMode( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayBlendContainer(); #endif virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { MOZ_COUNT_CTOR(nsDisplayBlendContainer); return MakeDisplayItem(aBuilder, *this); } virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual bool CanMerge(const nsDisplayItem* aItem) const override { // Items for the same content element should be merged into a single // compositing group. return HasSameTypeAndClip(aItem) && HasSameContent(aItem) && mIsForBackground == static_cast(aItem) ->mIsForBackground; } virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { return false; } virtual uint32_t GetPerFrameKey() const override { return (mIsForBackground ? 1 << TYPE_BITS : 0) | nsDisplayItem::GetPerFrameKey(); } NS_DISPLAY_DECL_NAME("BlendContainer", TYPE_BLEND_CONTAINER) protected: nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground); nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, const nsDisplayBlendContainer& aOther) : nsDisplayWrapList(aBuilder, aOther), mIsForBackground(aOther.mIsForBackground) {} // Used to distinguish containers created at building stacking // context or appending background. bool mIsForBackground; }; class nsDisplayTableBlendContainer : public nsDisplayBlendContainer { public: static nsDisplayTableBlendContainer* CreateForBackgroundBlendMode( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, nsIFrame* aAncestorFrame); virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { return MakeDisplayItem(aBuilder, *this); } virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } virtual uint32_t GetPerFrameKey() const override { return (static_cast(mTableType) << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } NS_DISPLAY_DECL_NAME("TableBlendContainer", TYPE_TABLE_BLEND_CONTAINER) protected: nsDisplayTableBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground, nsIFrame* aAncestorFrame) : nsDisplayBlendContainer(aBuilder, aFrame, aList, aActiveScrolledRoot, aIsForBackground), mAncestorFrame(aAncestorFrame), mTableType(GetTableTypeFromFrame(aAncestorFrame)) {} nsDisplayTableBlendContainer(nsDisplayListBuilder* aBuilder, const nsDisplayTableBlendContainer& aOther) : nsDisplayBlendContainer(aBuilder, aOther), mAncestorFrame(aOther.mAncestorFrame), mTableType(aOther.mTableType) {} nsIFrame* mAncestorFrame; TableType mTableType; }; /** * nsDisplayOwnLayer constructor flags. If we nest this class inside * nsDisplayOwnLayer then we can't forward-declare it up at the top of this * file and that makes it hard to use in all the places that we need to use it. */ enum class nsDisplayOwnLayerFlags { eNone = 0, eGenerateSubdocInvalidations = 1 << 0, eVerticalScrollbar = 1 << 1, eHorizontalScrollbar = 1 << 2, eGenerateScrollableLayer = 1 << 3, eScrollbarContainer = 1 << 4, }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsDisplayOwnLayerFlags) /** * A display item that has no purpose but to ensure its contents get * their own layer. */ class nsDisplayOwnLayer : public nsDisplayWrapList { public: typedef mozilla::layers::ScrollThumbData ScrollThumbData; /** * @param aFlags eGenerateSubdocInvalidations : * Add UserData to the created ContainerLayer, so that invalidations * for this layer are send to our nsPresContext. * eGenerateScrollableLayer : only valid on nsDisplaySubDocument (and * subclasses), indicates this layer is to be a scrollable layer, so call * ComputeFrameMetrics, etc. * @param aScrollTarget when eVerticalScrollbar or eHorizontalScrollbar * is set in the flags, this parameter should be the ViewID of the * scrollable content this scrollbar is for. */ nsDisplayOwnLayer( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayOwnLayerFlags aFlags = nsDisplayOwnLayerFlags::eNone, ViewID aScrollTarget = mozilla::layers::FrameMetrics::NULL_SCROLL_ID, const ScrollThumbData& aThumbData = ScrollThumbData{}, bool aForceActive = true, bool aClearClipChain = false); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayOwnLayer(); #endif nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, const nsDisplayOwnLayer& aOther) : nsDisplayWrapList(aBuilder, aOther), mFlags(aOther.mFlags), mScrollTarget(aOther.mScrollTarget), mThumbData(aOther.mThumbData), mForceActive(aOther.mForceActive), mWrAnimationId(aOther.mWrAnimationId) { MOZ_COUNT_CTOR(nsDisplayOwnLayer); } virtual bool ShouldBuildLayerEvenIfInvisible( nsDisplayListBuilder* aBuilder) const override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual bool UpdateScrollData( mozilla::layers::WebRenderScrollData* aData, mozilla::layers::WebRenderLayerScrollData* aLayerData) override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual bool CanMerge(const nsDisplayItem* aItem) const override { // Don't allow merging, each sublist must have its own layer return false; } virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { return false; } void WriteDebugInfo(std::stringstream& aStream) override; nsDisplayOwnLayerFlags GetFlags() { return mFlags; } bool IsScrollThumbLayer() const; NS_DISPLAY_DECL_NAME("OwnLayer", TYPE_OWN_LAYER) protected: nsDisplayOwnLayerFlags mFlags; ViewID mScrollTarget; // If this nsDisplayOwnLayer represents a scroll thumb layer, mThumbData // stores information about the scroll thumb. Otherwise, mThumbData will be // default-constructed (in particular with mDirection == Nothing()) // and can be ignored. ScrollThumbData mThumbData; bool mForceActive; uint64_t mWrAnimationId; }; /** * A display item for subdocuments. This is more or less the same as * nsDisplayOwnLayer, except that it always populates the FrameMetrics instance * on the ContainerLayer it builds. */ class nsDisplaySubDocument : public nsDisplayOwnLayer { public: nsDisplaySubDocument(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsSubDocumentFrame* aSubDocFrame, nsDisplayList* aList, nsDisplayOwnLayerFlags aFlags); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplaySubDocument(); #endif virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual nsSubDocumentFrame* SubDocumentFrame() { return mSubDocFrame; } virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; virtual bool ShouldBuildLayerEvenIfInvisible( nsDisplayListBuilder* aBuilder) const override; virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { return mShouldFlatten; } void SetShouldFlattenAway(bool aShouldFlatten) { mShouldFlatten = aShouldFlatten; } virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override { if (mShouldFlatten) { return mozilla::LAYER_NONE; } return nsDisplayOwnLayer::GetLayerState(aBuilder, aManager, aParameters); } virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; NS_DISPLAY_DECL_NAME("SubDocument", TYPE_SUBDOCUMENT) mozilla::UniquePtr ComputeScrollMetadata( LayerManager* aLayerManager, const ContainerLayerParameters& aContainerParameters); protected: ViewID mScrollParentId; bool mForceDispatchToContentRegion; bool mShouldFlatten; nsSubDocumentFrame* mSubDocFrame; }; /** * 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, nsDisplayOwnLayerFlags aFlags); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayResolution(); #endif virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; virtual already_AddRefed 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, const ActiveScrolledRoot* aActiveScrolledRoot); nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, const nsDisplayStickyPosition& aOther) : nsDisplayOwnLayer(aBuilder, aOther) {} #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayStickyPosition(); #endif void SetClipChain(const DisplayItemClipChain* aClipChain, bool aStore) override; virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { MOZ_COUNT_CTOR(nsDisplayStickyPosition); return MakeDisplayItem(aBuilder, *this); } virtual already_AddRefed 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 CanMerge(const nsDisplayItem* aItem) const override { // Items with the same fixed position frame can be merged. return HasSameTypeAndClip(aItem) && mFrame == aItem->Frame(); } virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; }; class nsDisplayFixedPosition : public nsDisplayOwnLayer { public: nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot); nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, const nsDisplayFixedPosition& aOther) : nsDisplayOwnLayer(aBuilder, aOther), mAnimatedGeometryRootForScrollMetadata( aOther.mAnimatedGeometryRootForScrollMetadata), mIndex(aOther.mIndex), mIsFixedBackground(aOther.mIsFixedBackground) { MOZ_COUNT_CTOR(nsDisplayFixedPosition); } static nsDisplayFixedPosition* CreateForFixedBackground( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayBackgroundImage* aImage, uint32_t aIndex); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayFixedPosition(); #endif virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { return MakeDisplayItem(aBuilder, *this); } virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; NS_DISPLAY_DECL_NAME("FixedPosition", TYPE_FIXED_POSITION) virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override { return mozilla::LAYER_ACTIVE_FORCE; } virtual bool CanMerge(const nsDisplayItem* aItem) const override { // Items with the same fixed position frame can be merged. return HasSameTypeAndClip(aItem) && mFrame == aItem->Frame(); } virtual bool ShouldFixToViewport( nsDisplayListBuilder* aBuilder) const override { return mIsFixedBackground; } virtual uint32_t GetPerFrameKey() const override { return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const override { return mAnimatedGeometryRootForScrollMetadata; } virtual bool UpdateScrollData( mozilla::layers::WebRenderScrollData* aData, mozilla::layers::WebRenderLayerScrollData* aLayerData) override; protected: // For background-attachment:fixed nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, uint32_t aIndex); void Init(nsDisplayListBuilder* aBuilder); RefPtr mAnimatedGeometryRootForScrollMetadata; uint32_t mIndex; bool mIsFixedBackground; }; class nsDisplayTableFixedPosition : public nsDisplayFixedPosition { public: static nsDisplayTableFixedPosition* CreateForFixedBackground( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayBackgroundImage* aImage, uint32_t aIndex, nsIFrame* aAncestorFrame); virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { return MakeDisplayItem(aBuilder, *this); } virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; } virtual uint32_t GetPerFrameKey() const override { return (mIndex << (TYPE_BITS + static_cast(TableTypeBits::COUNT))) | (static_cast(mTableType) << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } NS_DISPLAY_DECL_NAME("TableFixedPosition", TYPE_TABLE_FIXED_POSITION) protected: nsDisplayTableFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, uint32_t aIndex, nsIFrame* aAncestorFrame); nsDisplayTableFixedPosition(nsDisplayListBuilder* aBuilder, const nsDisplayTableFixedPosition& aOther) : nsDisplayFixedPosition(aBuilder, aOther), mAncestorFrame(aOther.mAncestorFrame), mTableType(aOther.mTableType) {} nsIFrame* mAncestorFrame; TableType mTableType; }; /** * 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 BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool ShouldBuildLayerEvenIfInvisible( nsDisplayListBuilder* aBuilder) const override { return true; } virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const 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 ComputeScrollMetadata( LayerManager* aLayerManager, const ContainerLayerParameters& aContainerParameters); virtual bool UpdateScrollData( mozilla::layers::WebRenderScrollData* aData, mozilla::layers::WebRenderLayerScrollData* aLayerData) override; 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 eGenerateSubdocInvalidations : * 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, nsDisplayOwnLayerFlags aFlags = nsDisplayOwnLayerFlags::eNone); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayZoom(); #endif virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override { return mozilla::LAYER_ACTIVE; } NS_DISPLAY_DECL_NAME("Zoom", TYPE_ZOOM) // Get the app units per dev pixel ratio of the child document. int32_t GetChildAppUnitsPerDevPixel() { return mAPD; } // Get the app units per dev pixel ratio of the parent document. int32_t GetParentAppUnitsPerDevPixel() { return mParentAPD; } private: int32_t mAPD, mParentAPD; }; class nsDisplaySVGEffects : public nsDisplayWrapList { public: nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, bool aHandleOpacity, const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain = false); nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, bool aHandleOpacity); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplaySVGEffects(); #endif nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, const nsDisplaySVGEffects& aOther) : nsDisplayWrapList(aBuilder, aOther), mEffectsBounds(aOther.mEffectsBounds), mHandleOpacity(aOther.mHandleOpacity) { MOZ_COUNT_CTOR(nsDisplaySVGEffects); } virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { return false; } bool ShouldHandleOpacity() { return mHandleOpacity; } gfxRect BBoxInUserSpace() const; gfxPoint UserSpaceOffset() const; virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override; protected: bool ValidateSVGFrame(); // relative to mFrame nsRect mEffectsBounds; // True if we need to handle css opacity in this display item. bool mHandleOpacity; }; /** * A display item to paint a stacking context with mask and clip effects * set by the stacking context root frame's style. */ class nsDisplayMask : public nsDisplaySVGEffects { public: typedef mozilla::layers::ImageLayer ImageLayer; nsDisplayMask(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, bool aHandleOpacity, const ActiveScrolledRoot* aActiveScrolledRoot); nsDisplayMask(nsDisplayListBuilder* aBuilder, const nsDisplayMask& aOther) : nsDisplaySVGEffects(aBuilder, aOther), mDestRects(aOther.mDestRects) {} #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayMask(); #endif virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { MOZ_COUNT_CTOR(nsDisplayMask); return MakeDisplayItem(aBuilder, *this); } NS_DISPLAY_DECL_NAME("Mask", TYPE_MASK) virtual bool CanMerge(const nsDisplayItem* aItem) const override; virtual void Merge(const nsDisplayItem* aItem) override { nsDisplayWrapList::Merge(aItem); const nsDisplayMask* other = static_cast(aItem); mEffectsBounds.UnionRect( mEffectsBounds, other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame)); } virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override { return new nsDisplayMaskGeometry(this, aBuilder); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override; #ifdef MOZ_DUMP_PAINTING void PrintEffects(nsACString& aTo); #endif void PaintAsLayer(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, LayerManager* aManager); /* * Paint mask onto aMaskContext in mFrame's coordinate space and * return whether the mask layer was painted successfully. */ bool PaintMask(nsDisplayListBuilder* aBuilder, gfxContext* aMaskContext); const nsTArray& GetDestRects() { return mDestRects; } virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual mozilla::Maybe GetClipWithRespectToASR( nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const override; private: // According to mask property and the capability of aManager, determine // whether we can paint the mask onto a dedicate mask layer. bool CanPaintOnMaskLayer(LayerManager* aManager); nsTArray mDestRects; }; /** * A display item to paint a stacking context with filter effects set by the * stacking context root frame's style. */ class nsDisplayFilter : public nsDisplaySVGEffects { public: nsDisplayFilter(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, bool aHandleOpacity); nsDisplayFilter(nsDisplayListBuilder* aBuilder, const nsDisplayFilter& aOther) : nsDisplaySVGEffects(aBuilder, aOther), mEffectsBounds(aOther.mEffectsBounds) {} #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayFilter(); #endif virtual nsDisplayWrapList* Clone( nsDisplayListBuilder* aBuilder) const override { MOZ_COUNT_CTOR(nsDisplayFilter); return MakeDisplayItem(aBuilder, *this); } NS_DISPLAY_DECL_NAME("Filter", TYPE_FILTER) virtual bool CanMerge(const nsDisplayItem* aItem) const override { // Items for the same content element should be merged into a single // compositing group. return HasSameTypeAndClip(aItem) && HasSameContent(aItem); } virtual void Merge(const nsDisplayItem* aItem) override { nsDisplayWrapList::Merge(aItem); const nsDisplayFilter* other = static_cast(aItem); mEffectsBounds.UnionRect( mEffectsBounds, other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame)); } virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = false; return mEffectsBounds + ToReferenceFrame(); } virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override { return new nsDisplayFilterGeometry(this, aBuilder); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override; #ifdef MOZ_DUMP_PAINTING void PrintEffects(nsACString& aTo); #endif void PaintAsLayer(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, LayerManager* aManager); virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; 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 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::Matrix4x4Flagged Matrix4x4Flagged; typedef mozilla::gfx::Point3D Point3D; /* * Avoid doing UpdateBounds() during construction. */ class StoreList : public nsDisplayWrapList { public: StoreList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList) : nsDisplayWrapList(aBuilder, aFrame, aList) {} StoreList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayItem* aItem) : nsDisplayWrapList(aBuilder, aFrame, aItem) {} virtual ~StoreList() {} virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override { // For extending 3d rendering context, the bounds would be // updated by DoUpdateBoundsPreserves3D(), not here. if (!mFrame->Extend3DContext()) { nsDisplayWrapList::UpdateBounds(aBuilder); } } void ForceUpdateBounds(nsDisplayListBuilder* aBuilder) { nsDisplayWrapList::UpdateBounds(aBuilder); } virtual void DoUpdateBoundsPreserves3D( nsDisplayListBuilder* aBuilder) override { for (nsDisplayItem* i = mList.GetBottom(); i; i = i->GetAbove()) { i->DoUpdateBoundsPreserves3D(aBuilder); } nsDisplayWrapList::UpdateBounds(aBuilder); } }; public: enum PrerenderDecision { NoPrerender, FullPrerender, PartialPrerender }; /** * Returns a matrix (in pixels) for the current frame. The matrix should be * relative to the current frame's coordinate space. * * @param aFrame The frame to compute the transform for. * @param aAppUnitsPerPixel The number of app units per graphics unit. */ typedef Matrix4x4 (*ComputeTransformFunction)(nsIFrame* aFrame, float aAppUnitsPerPixel); /* Constructor accepts a display list, empties it, and wraps it up. It also * ferries the underlying frame to the nsDisplayItem constructor. */ nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const nsRect& aChildrenVisibleRect, uint32_t aIndex = 0, bool aAllowAsyncAnimation = false); nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayItem* aItem, const nsRect& aChildrenVisibleRect, uint32_t aIndex = 0); nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const nsRect& aChildrenVisibleRect, ComputeTransformFunction aTransformGetter, uint32_t aIndex = 0); nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const nsRect& aChildrenVisibleRect, const Matrix4x4& aTransform, uint32_t aIndex = 0); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayTransform() { MOZ_COUNT_DTOR(nsDisplayTransform); } #endif virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override { mHasBounds = false; if (IsTransformSeparator()) { mStoredList.ForceUpdateBounds(aBuilder); return; } mStoredList.UpdateBounds(aBuilder); UpdateBoundsFor3D(aBuilder); } virtual void Destroy(nsDisplayListBuilder* aBuilder) override { mStoredList.GetChildren()->DeleteAll(aBuilder); nsDisplayItem::Destroy(aBuilder); } NS_DISPLAY_DECL_NAME("nsDisplayTransform", TYPE_TRANSFORM) virtual nsRect GetComponentAlphaBounds( nsDisplayListBuilder* aBuilder) const override { if (mStoredList.GetComponentAlphaBounds(aBuilder).IsEmpty()) return nsRect(); bool snap; return GetBounds(aBuilder, &snap); } virtual nsDisplayList* GetChildren() const override { return mStoredList.GetChildren(); } virtual void SetActiveScrolledRoot( const ActiveScrolledRoot* aActiveScrolledRoot) override { nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot); mStoredList.SetActiveScrolledRoot(aActiveScrolledRoot); } virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override; virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual bool UpdateScrollData( mozilla::layers::WebRenderScrollData* aData, mozilla::layers::WebRenderLayerScrollData* aLayerData) override; virtual bool ShouldBuildLayerEvenIfInvisible( nsDisplayListBuilder* aBuilder) const override; virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; virtual bool CanMerge(const nsDisplayItem* aItem) const override { return false; } virtual uint32_t GetPerFrameKey() const override { return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override { // We don't need to compute an invalidation region since we have // LayerTreeInvalidation } virtual const nsIFrame* ReferenceFrameForChildren() const override { // If we were created using a transform-getter, then we don't // belong to a transformed frame, and aren't a reference frame // for our children. if (!mTransformGetter) { return mFrame; } return nsDisplayItem::ReferenceFrameForChildren(); } AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const override { return mAnimatedGeometryRootForScrollMetadata; } virtual const nsRect& GetVisibleRectForChildren() const override { return mChildrenVisibleRect; } enum { INDEX_MAX = UINT32_MAX >> TYPE_BITS }; /** * We include the perspective matrix from our containing block for the * purposes of visibility calculations, but we exclude it from the transform * we set on the layer (for rendering), since there will be an * nsDisplayPerspective created for that. */ const Matrix4x4Flagged& GetTransform() const; Matrix4x4 GetTransformForRendering( mozilla::LayoutDevicePoint* aOutOrigin = nullptr); /** * Return the transform that is aggregation of all transform on the * preserves3d chain. */ const Matrix4x4& GetAccumulatedPreserved3DTransform( nsDisplayListBuilder* aBuilder); float GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint); /** * TransformRect takes in as parameters a rectangle (in aFrame's coordinate * space) and returns the smallest rectangle (in aFrame's coordinate space) * containing the transformed image of that rectangle. That is, it takes * the four corners of the rectangle, transforms them according to the * matrix associated with the specified frame, then returns the smallest * rectangle containing the four transformed points. * * @param untransformedBounds The rectangle (in app units) to transform. * @param aFrame The frame whose transformation should be applied. This * function raises an assertion if aFrame is null or doesn't have a * transform applied to it. * @param aOrigin The origin of the transform relative to aFrame's local * coordinate space. * @param aBoundsOverride (optional) Rather than using the frame's computed * bounding rect as frame bounds, use this rectangle instead. Pass * nullptr (or nothing at all) to use the default. */ static nsRect TransformRect(const nsRect& aUntransformedBounds, const nsIFrame* aFrame, const nsRect* aBoundsOverride = nullptr); /* UntransformRect is like TransformRect, except that it inverts the * transform. */ static bool UntransformRect(const nsRect& aTransformedBounds, const nsRect& aChildBounds, const nsIFrame* aFrame, nsRect* aOutRect); bool UntransformVisibleRect(nsDisplayListBuilder* aBuilder, nsRect* aOutRect) const; static Point3D GetDeltaToTransformOrigin(const nsIFrame* aFrame, float aAppUnitsPerPixel, const nsRect* aBoundsOverride); /* * Returns true if aFrame has perspective applied from its containing * block. * Returns the matrix to append to apply the persective (taking * perspective-origin into account), relative to aFrames coordinate * space). * aOutMatrix is assumed to be the identity matrix, and isn't explicitly * cleared. */ static bool ComputePerspectiveMatrix(const nsIFrame* aFrame, float aAppUnitsPerPixel, Matrix4x4& aOutMatrix); struct FrameTransformProperties { FrameTransformProperties(const nsIFrame* aFrame, float aAppUnitsPerPixel, const nsRect* aBoundsOverride); FrameTransformProperties( RefPtr&& aTransformList, const Point3D& aToTransformOrigin) : mFrame(nullptr), mTransformList(mozilla::Move(aTransformList)), mToTransformOrigin(aToTransformOrigin) {} const nsIFrame* mFrame; const RefPtr mTransformList; const Point3D mToTransformOrigin; }; /** * Given a frame with the -moz-transform property or an SVG transform, * returns the transformation matrix for that frame. * * @param aFrame The frame to get the matrix from. * @param aOrigin Relative to which point this transform should be applied. * @param aAppUnitsPerPixel The number of app units per graphics unit. * @param aBoundsOverride [optional] If this is nullptr (the default), the * computation will use the value of TransformReferenceBox(aFrame). * Otherwise, it will use the value of aBoundsOverride. This is * mostly for internal use and in most cases you will not need to * specify a value. * @param aFlags OFFSET_BY_ORIGIN The resulting matrix will be translated * by aOrigin. This translation is applied *before* the CSS transform. * @param aFlags INCLUDE_PRESERVE3D_ANCESTORS The computed transform will * include the transform of any ancestors participating in the same * 3d rendering context. * @param aFlags INCLUDE_PERSPECTIVE The resulting matrix will include the * perspective transform from the containing block if applicable. */ enum { OFFSET_BY_ORIGIN = 1 << 0, INCLUDE_PRESERVE3D_ANCESTORS = 1 << 1, INCLUDE_PERSPECTIVE = 1 << 2, }; static Matrix4x4 GetResultingTransformMatrix( const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags, const nsRect* aBoundsOverride = nullptr); static Matrix4x4 GetResultingTransformMatrix( const FrameTransformProperties& aProperties, const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags, const nsRect* aBoundsOverride = nullptr); /** * Decide whether we should prerender some or all of the contents of the * transformed frame even when it's not completely visible (yet). * Return FullPrerender if the entire contents should be prerendered, * PartialPrerender if some but not all of the contents should be prerendered, * or NoPrerender if only the visible area should be rendered. * |aDirtyRect| is updated to the area that should be prerendered. */ static PrerenderDecision ShouldPrerenderTransformedContent( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect); bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override; bool MayBeAnimated(nsDisplayListBuilder* aBuilder) const; virtual void WriteDebugInfo(std::stringstream& aStream) override; // Force the layer created for this item not to extend 3D context. // See nsIFrame::BuildDisplayListForStackingContext() void SetNoExtendContext() { mNoExtendContext = true; } virtual void DoUpdateBoundsPreserves3D( nsDisplayListBuilder* aBuilder) override { MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() || IsTransformSeparator()); // Updating is not going through to child 3D context. ComputeBounds(aBuilder); } /** * This function updates bounds for items with a frame establishing * 3D rendering context. * * \see nsDisplayItem::DoUpdateBoundsPreserves3D() */ void UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) { if (!mFrame->Extend3DContext() || mFrame->Combines3DTransformWithAncestors() || IsTransformSeparator()) { // Not an establisher of a 3D rendering context. return; } // Always start updating from an establisher of a 3D rendering context. nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder); nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder); accTransform.StartRoot(); ComputeBounds(aBuilder); mBounds = aBuilder->GetAccumulatedRect(); mHasBounds = true; } /** * This item is an additional item as the boundary between parent * and child 3D rendering context. * \see nsIFrame::BuildDisplayListForStackingContext(). */ bool IsTransformSeparator() { return mIsTransformSeparator; } /** * This item is the boundary between parent and child 3D rendering * context. */ bool IsLeafOf3DContext() { return (IsTransformSeparator() || (!mFrame->Extend3DContext() && mFrame->Combines3DTransformWithAncestors())); } /** * The backing frame of this item participates a 3D rendering * context. */ bool IsParticipating3DContext() { return mFrame->Extend3DContext() || mFrame->Combines3DTransformWithAncestors(); } private: void ComputeBounds(nsDisplayListBuilder* aBuilder); void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder); void Init(nsDisplayListBuilder* aBuilder); static Matrix4x4 GetResultingTransformMatrixInternal( const FrameTransformProperties& aProperties, const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags, const nsRect* aBoundsOverride); StoreList mStoredList; mutable Matrix4x4Flagged mTransform; // Accumulated transform of ancestors on the preserves-3d chain. Matrix4x4 mTransformPreserves3D; ComputeTransformFunction mTransformGetter; RefPtr mAnimatedGeometryRootForChildren; RefPtr mAnimatedGeometryRootForScrollMetadata; nsRect mChildrenVisibleRect; uint32_t mIndex; mutable nsRect mBounds; // True for mBounds is valid. mutable bool mHasBounds; // Be forced not to extend 3D context. Since we don't create a // transform item, a container layer, for every frames in a // preserves3d context, the transform items of a child preserves3d // context may extend the parent context not intented if the root of // the child preserves3d context doesn't create a transform item. // With this flags, we force the item not extending 3D context. bool mNoExtendContext; // This item is a separator between 3D rendering contexts, and // mTransform have been presetted by the constructor. bool mIsTransformSeparator; // True if mTransformPreserves3D have been initialized. bool mTransformPreserves3DInited; // True if async animation of the transform is allowed. bool mAllowAsyncAnimation; }; /* A display item that applies a perspective transformation to a single * nsDisplayTransform child item. We keep this as a separate item since the * perspective-origin is relative to an ancestor of the transformed frame, and * APZ can scroll the child separately. */ class nsDisplayPerspective : public nsDisplayItem { typedef mozilla::gfx::Point3D Point3D; public: NS_DISPLAY_DECL_NAME("nsDisplayPerspective", TYPE_PERSPECTIVE) nsDisplayPerspective(nsDisplayListBuilder* aBuilder, nsIFrame* aTransformFrame, nsIFrame* aPerspectiveFrame, nsDisplayList* aList); ~nsDisplayPerspective() { if (mTransformFrame) { mTransformFrame->RemoveDisplayItem(this); } } virtual uint32_t GetPerFrameKey() const override { return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); } virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray* aOutFrames) override { return mList.HitTest(aBuilder, aRect, aState, aOutFrames); } virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { return mList.GetBounds(aBuilder, aSnap); } virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override { mList.UpdateBounds(aBuilder); } virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const override {} virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { return mList.GetOpaqueRegion(aBuilder, aSnap); } virtual mozilla::Maybe IsUniform( nsDisplayListBuilder* aBuilder) const override { return mList.IsUniform(aBuilder); } virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; virtual bool ShouldBuildLayerEvenIfInvisible( nsDisplayListBuilder* aBuilder) const override { if (!mList.GetChildren()->GetTop()) { return false; } return mList.GetChildren()->GetTop()->ShouldBuildLayerEvenIfInvisible( aBuilder); } virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override { mList.RecomputeVisibility(aBuilder, aVisibleRegion); return true; } virtual nsDisplayList* GetSameCoordinateSystemChildren() const override { return mList.GetChildren(); } virtual nsDisplayList* GetChildren() const override { return mList.GetChildren(); } virtual void SetActiveScrolledRoot( const ActiveScrolledRoot* aActiveScrolledRoot) override { nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot); mList.SetActiveScrolledRoot(aActiveScrolledRoot); } virtual nsRect GetComponentAlphaBounds( nsDisplayListBuilder* aBuilder) const override { return mList.GetComponentAlphaBounds(aBuilder); } nsIFrame* TransformFrame() { return mTransformFrame; } virtual nsIFrame* FrameForInvalidation() const override { return mTransformFrame; } virtual int32_t ZIndex() const override; virtual void DoUpdateBoundsPreserves3D( nsDisplayListBuilder* aBuilder) override { if (mList.GetChildren()->GetTop()) { static_cast(mList.GetChildren()->GetTop()) ->DoUpdateBoundsPreserves3D(aBuilder); } } virtual void Destroy(nsDisplayListBuilder* aBuilder) override { mList.GetChildren()->DeleteAll(aBuilder); nsDisplayItem::Destroy(aBuilder); } virtual bool HasDeletedFrame() const override { return !mTransformFrame || nsDisplayItem::HasDeletedFrame(); } virtual void RemoveFrame(nsIFrame* aFrame) override { if (aFrame == mTransformFrame) { mTransformFrame = nullptr; } nsDisplayItem::RemoveFrame(aFrame); } private: nsDisplayWrapList mList; nsIFrame* mTransformFrame; uint32_t mIndex; }; /** * This class adds basic support for limiting the rendering (in the inline axis * of the writing mode) to the part inside the specified edges. It's a base * class for the display item classes that do the actual work. * The two members, mVisIStartEdge and mVisIEndEdge, are relative to the edges * of the frame's scrollable overflow rectangle and are the amount to suppress * on each side. * * Setting none, both or only one edge is allowed. * The values must be non-negative. * The default value for both edges is zero, which means everything is painted. */ class nsCharClipDisplayItem : public nsDisplayItem { public: nsCharClipDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : nsDisplayItem(aBuilder, aFrame), mVisIStartEdge(0), mVisIEndEdge(0) {} explicit nsCharClipDisplayItem(nsIFrame* aFrame) : nsDisplayItem(aFrame), mVisIStartEdge(0), mVisIEndEdge(0) {} virtual void RestoreState() override { nsDisplayItem::RestoreState(); mIsFrameSelected.reset(); } virtual nsDisplayItemGeometry* AllocateGeometry( nsDisplayListBuilder* aBuilder) override; virtual void ComputeInvalidationRegion( nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) const 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) { DisplayItemType t = aItem->GetType(); return (t == DisplayItemType::TYPE_TEXT || t == DisplayItemType::TYPE_SVG_CHAR_CLIP) ? static_cast(aItem) : nullptr; } // Lengths measured from the visual inline start and end sides // (i.e. left and right respectively in horizontal writing modes, // regardless of bidi directionality; top and bottom in vertical modes). nscoord mVisIStartEdge; nscoord mVisIEndEdge; // Cached result of mFrame->IsSelected(). Only initialized when needed. mutable mozilla::Maybe mIsFrameSelected; }; class FlattenedDisplayItemIterator { public: FlattenedDisplayItemIterator(nsDisplayListBuilder* aBuilder, nsDisplayList* aList) : mBuilder(aBuilder), mNext(aList->GetBottom()) { ResolveFlattening(); } nsDisplayItem* GetNext() { nsDisplayItem* next = mNext; // Advance mNext to the following item if (next) { mNext = mNext->GetAbove(); ResolveFlattening(); } return next; } nsDisplayItem* PeekNext() { return mNext; } private: bool AtEndOfNestedList() { return !mNext && mStack.Length() > 0; } bool ShouldFlattenNextItem() { return mNext && mNext->ShouldFlattenAway(mBuilder); } void ResolveFlattening() { // Handle the case where we reach the end of a nested list, or the current // item should start a new nested list. Repeat this until we find an actual // item, or the very end of the outer list. while (AtEndOfNestedList() || ShouldFlattenNextItem()) { if (AtEndOfNestedList()) { // Pop the last item off the stack. mNext = mStack.LastElement(); mStack.RemoveElementAt(mStack.Length() - 1); // We stored the item that was flattened, so advance to the next. mNext = mNext->GetAbove(); } else { // This item wants to be flattened. Store the current item on the stack, // and use the first item in the child list instead. mStack.AppendElement(mNext); nsDisplayList* childItems = mNext->GetSameCoordinateSystemChildren(); mNext = childItems->GetBottom(); } } } nsDisplayListBuilder* mBuilder; nsDisplayItem* mNext; AutoTArray mStack; }; /** * A display item that for webrender to handle SVG */ class nsDisplaySVGWrapper : public nsDisplayWrapList { public: NS_DISPLAY_DECL_NAME("SVGWrapper", TYPE_SVG_WRAPPER) nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplaySVGWrapper(); #endif virtual already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; virtual LayerState GetLayerState( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) override; virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override; bool CreateWebRenderCommands( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) override; }; namespace mozilla { class PaintTelemetry { public: enum class Metric { DisplayList, Layerization, Rasterization, COUNT, }; class AutoRecord { public: explicit AutoRecord(Metric aMetric); ~AutoRecord(); TimeStamp GetStart() const { return mStart; } private: Metric mMetric; mozilla::TimeStamp mStart; }; class AutoRecordPaint { public: AutoRecordPaint(); ~AutoRecordPaint(); private: mozilla::TimeStamp mStart; }; private: static uint32_t sPaintLevel; static uint32_t sMetricLevel; static mozilla::EnumeratedArray sMetrics; }; } // namespace mozilla #endif /*NSDISPLAYLIST_H_*/