/* 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/. */ #ifndef mozilla_DisplayPortUtils_h_ #define mozilla_DisplayPortUtils_h_ #include #include #include "Units.h" #include "nsDisplayList.h" #include "nsRect.h" class nsIContent; class nsIFrame; class nsPresContext; namespace mozilla { class nsDisplayListBuilder; class PresShell; class ScrollContainerFrame; // For GetDisplayPort enum class DisplayportRelativeTo { ScrollPort, ScrollFrame }; // Is the displayport being applied to scrolled content or fixed content? enum class ContentGeometryType { Scrolled, Fixed }; struct DisplayPortOptions { // The default options. DisplayportRelativeTo mRelativeTo = DisplayportRelativeTo::ScrollPort; ContentGeometryType mGeometryType = ContentGeometryType::Scrolled; // Fluent interface for changing the defaults. DisplayPortOptions With(DisplayportRelativeTo aRelativeTo) const { DisplayPortOptions result = *this; result.mRelativeTo = aRelativeTo; return result; } DisplayPortOptions With(ContentGeometryType aGeometryType) const { DisplayPortOptions result = *this; result.mGeometryType = aGeometryType; return result; } }; struct DisplayPortPropertyData { DisplayPortPropertyData(const nsRect& aRect, uint32_t aPriority, bool aPainted) : mRect(aRect), mPriority(aPriority), mPainted(aPainted) {} nsRect mRect; uint32_t mPriority; bool mPainted; }; struct DisplayPortMargins { // The margins relative to the visual scroll offset. ScreenMargin mMargins; // Some information captured at the time the margins are stored. // This ensures that we can express the margins as being relative to // the correct scroll offset when applying them. // APZ's visual scroll offset at the time it requested the margins. CSSPoint mVisualOffset; // The scroll frame's layout scroll offset at the time the margins // were saved. CSSPoint mLayoutOffset; // Create displayport margins requested by APZ, relative to an async visual // offset provided by APZ. static DisplayPortMargins FromAPZ(const ScreenMargin& aMargins, const CSSPoint& aVisualOffset, const CSSPoint& aLayoutOffset); // Create displayport port margins for the given scroll container frame. // This is for use in cases where we don't have async scroll information from // APZ to use to adjust the margins. The visual and layout offset are set // based on the main thread's view of them. static DisplayPortMargins ForScrollContainerFrame( ScrollContainerFrame* aScrollContainerFrame, const ScreenMargin& aMargins); // Convenience version of the above that takes a content element. static DisplayPortMargins ForContent(nsIContent* aContent, const ScreenMargin& aMargins); // Another convenience version that sets empty margins. static DisplayPortMargins Empty(nsIContent* aContent) { return ForContent(aContent, ScreenMargin()); } // Get the margins relative to the layout viewport. // |aGeometryType| tells us whether the margins are being queried for the // purpose of being applied to scrolled content or fixed content. // |aScrollableFrame| is the scroll frame whose content the margins will be // applied to (or, in the case of fixed content), the scroll frame wrt. which // the content is fixed. ScreenMargin GetRelativeToLayoutViewport( ContentGeometryType aGeometryType, ScrollContainerFrame* aScrollContainerFrame, const CSSToScreenScale2D& aDisplayportScale) const; friend std::ostream& operator<<(std::ostream& aOs, const DisplayPortMargins& aMargins); private: CSSPoint ComputeAsyncTranslation( ContentGeometryType aGeometryType, ScrollContainerFrame* aScrollContainerFrame) const; }; struct DisplayPortMarginsPropertyData { DisplayPortMarginsPropertyData(const DisplayPortMargins& aMargins, uint32_t aPriority, bool aPainted) : mMargins(aMargins), mPriority(aPriority), mPainted(aPainted) {} DisplayPortMargins mMargins; uint32_t mPriority; bool mPainted; }; struct FrameAndASRKind { nsIFrame* mFrame; ActiveScrolledRoot::ASRKind mASRKind; bool operator==(const FrameAndASRKind&) const = default; static FrameAndASRKind default_value() { return {nullptr, ActiveScrolledRoot::ASRKind::Scroll}; } }; class DisplayPortUtils { public: /** * Get display port for the given element, relative to the specified entity, * defaulting to the scrollport. */ static bool GetDisplayPort( nsIContent* aContent, nsRect* aResult, const DisplayPortOptions& aOptions = DisplayPortOptions()); /** * Check whether the given element has a displayport. */ static bool HasDisplayPort(nsIContent* aContent); /** * Check whether the given element has a displayport that has already * been sent to the compositor via a layers or WR transaction. */ static bool HasPaintedDisplayPort(nsIContent* aContent); /** * Mark the displayport of a given element as having been sent to * the compositor via a layers or WR transaction. */ static void MarkDisplayPortAsPainted(nsIContent* aContent); /** * Check whether the given frame has a displayport. It returns false * for scrolled frames and true for the corresponding scroll frame. * Optionally pass the child, and it only returns true if the child is the * scrolled frame for the displayport. */ static bool FrameHasDisplayPort(nsIFrame* aFrame, const nsIFrame* aScrolledFrame = nullptr); /** * Check whether the given element has a non-minimal displayport. */ static bool HasNonMinimalDisplayPort(nsIContent* aContent); /** * Check whether the given element has a non-minimal displayport that also has * non-zero margins. A display port rect is considered non-minimal non-zero. */ static bool HasNonMinimalNonZeroDisplayPort(nsIContent* aContent); /** * Check if the given element has a margins based displayport but is missing a * displayport base rect that it needs to properly compute a displayport rect. */ static bool IsMissingDisplayPortBaseRect(nsIContent* aContent); /** * @return the display port for the given element which should be used for * visibility testing purposes, relative to the scroll frame. * * This is the display port computed with a multipler of 1 which is the normal * display port unless low-precision buffers are enabled. If low-precision * buffers are enabled then GetDisplayPort() uses a multiplier to expand the * displayport, so this will differ from GetDisplayPort. */ static bool GetDisplayPortForVisibilityTesting(nsIContent* aContent, nsRect* aResult); enum class RepaintMode : uint8_t { Repaint, DoNotRepaint }; /** * Invalidate for displayport change. */ static void InvalidateForDisplayPortChange( nsIContent* aContent, bool aHadDisplayPort, const nsRect& aOldDisplayPort, const nsRect& aNewDisplayPort, RepaintMode aRepaintMode = RepaintMode::Repaint); /** * Set the display port margins for a content element to be used with a * display port base (see SetDisplayPortBase()). * See also nsIDOMWindowUtils.setDisplayPortMargins. * @param aContent the content element for which to set the margins * @param aPresShell the pres shell for the document containing the element * @param aMargins the margins to set * @param aAlignmentX, alignmentY the amount of pixels to which to align the * displayport built by combining the base * rect with the margins, in either direction * @param aPriority a priority value to determine which margins take effect * when multiple callers specify margins * @param aRepaintMode whether to schedule a paint after setting the margins * @return true if the new margins were applied. */ enum class ClearMinimalDisplayPortProperty { No, Yes }; static bool SetDisplayPortMargins( nsIContent* aContent, PresShell* aPresShell, const DisplayPortMargins& aMargins, ClearMinimalDisplayPortProperty aClearMinimalDisplayPortProperty, uint32_t aPriority = 0, RepaintMode aRepaintMode = RepaintMode::Repaint); /** * Set the display port base rect for given element to be used with display * port margins. * SetDisplayPortBaseIfNotSet is like SetDisplayPortBase except it only sets * the display port base to aBase if no display port base is currently set. */ static void SetDisplayPortBase(nsIContent* aContent, const nsRect& aBase); static void SetDisplayPortBaseIfNotSet(nsIContent* aContent, const nsRect& aBase); /** * Remove the displayport for the given element. */ static void RemoveDisplayPort(nsIContent* aContent); /** * Set minimal display port margins during painting. */ static void SetMinimalDisplayPortDuringPainting(nsIContent* aContent, PresShell* aPresShell); /** * Return true if aPresContext's viewport has a displayport. */ static bool ViewportHasDisplayPort(nsPresContext* aPresContext); /** * Return true if aFrame is a fixed-pos frame and is a child of a viewport * which has a displayport. These frames get special treatment from the * compositor. aDisplayPort, if non-null, is set to the display port rectangle * (relative to the viewport). */ static bool IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame); static bool MaybeCreateDisplayPortInFirstScrollFrameEncountered( nsIFrame* aFrame, nsDisplayListBuilder* aBuilder); /** * Calculate a default set of displayport margins for the given scrollframe * and set them on the scrollframe's content element. The margins are set with * the default priority, which may clobber previously set margins. The repaint * mode provided is passed through to the call to SetDisplayPortMargins. * The |aScrollFrame| parameter must be non-null and queryable to an nsIFrame. * @return true iff the call to SetDisplayPortMargins returned true. */ static bool CalculateAndSetDisplayPortMargins( ScrollContainerFrame* aScrollContainerFrame, RepaintMode aRepaintMode); /** * If |aScrollContainerFrame| WantsAsyncScroll() and we don't have a * scrollable displayport yet (as tracked by |aBuilder|), calculate and set a * displayport. * * If this is called during display list building pass DoNotRepaint in * aRepaintMode. * * Returns true if there is a displayport on an async scrollable scrollframe * after this call, either because one was just added or it already existed. */ static bool MaybeCreateDisplayPort( nsDisplayListBuilder* aBuilder, ScrollContainerFrame* aScrollContainerFrame, RepaintMode aRepaintMode); /** * Sets a zero margin display port on all proper ancestors of aFrame that * are async scrollable. */ static void SetZeroMarginDisplayPortOnAsyncScrollableAncestors( nsIFrame* aFrame); /** * Finds the closest ancestor async scrollable frame from aFrame that has a * displayport and attempts to trigger the displayport expiry on that * ancestor. */ static void ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame); /** * Returns root displayport base rect for |aPresShell|. In the case where * |aPresShell| is in an out-of-process iframe, this function may return * Nothing() if we haven't received the iframe's visible rect from the parent * content. * |aPresShell| should be top level content or in-process root or root in the * browser process. */ static Maybe GetRootDisplayportBase(PresShell* aPresShell); static nsRect GetDisplayportBase(nsIFrame* aFrame); /** * Whether to tell the given element will use empty displayport marings. * NOTE: This function should be called only for the element having any type * of displayports. */ static bool WillUseEmptyDisplayPortMargins(nsIContent* aContent); /** * Step up one frame in the async scrollable ancestor chain, to be used in * conjunction with GetAsyncScrollableAncestorFrame to walk the async * scrollable ancestor chain. Note this doesn't go from one async scrollable * frame to the next. Rather this walks all frame types, taking only one * ancestor step per call. */ static nsIFrame* OneStepInAsyncScrollableAncestorChain(nsIFrame* aFrame); /** * The next two functions (GetASRAncestorFrame and OneStepInASRChain) use * FrameAndASRKind (a pair of a nsIFrame pointer an an ASRKind enum) as a * cursor iterating up the frame tree. Each frame can potential generate two * ASRs: an inner one corresponding to scrolling with the contents of the * frame if it is a scroll frame, and an outer one correspnding to scrolling * with the frame itself if it is a sticky position frame. Its meaning is * different for each of the two functions but is natural when considering * what each function does. When passed into GetASRAncestorFrame it specifies * the first frame and type for the function to check. When returned from * GetASRAncestorFrame it specifies the frame and type of the ASR (because * GetASRAncestorFrame only returns ASRs). When passed into OneStepInASRChain * it specifies the last spot that was checked, and OneStepInASRChain's job is * to move one iteration from that, so it returns the next frame and ASR kind * to be checked (which may not generate an ASR, just that it needs to be * checked because it could generate an ASR). */ /** * Follows the ASR (ActiveScrolledRoot) chain of frames, so that if * f is the frame of an ASR A, then calling this function on * OneStepInASRChain(f) will return the frame of parent ASR of A. Frames that * generate an ASR are scroll frames for which IsMaybeAsynchronouslyScrolled() * returns true (aka mWillBuildScrollableLayer == true) or they are sticky * position frames for which their corresponding scroll frame will generate an * ASR. This function is different from * nsLayoutUtils::GetAsyncScrollableAncestorFrame because * GetAsyncScrollableAncestorFrame looks only for scroll frames that * WantAsyncScroll that that function walks from fixed pos to the root scroll * frame. Because that status (ie mWillBuildScrollableLayer) can change this * should only be called during a paint to the window after BuildDisplayList * has been called on aTarget so that mWillBuildScrollableLayer will have been * updated for this paint already for any frame we need to consult. Or for * some other reason you know that mWillBuildScrollableLayer is up to date for * this paint for any frame that might need to be consulted, ie you just * updated them yourself. Note that a frame returned from this function could * generate two ASRs: an inner one corresponding to an activated scroll frame, * and an outer one corresponding to sticky pos. */ static FrameAndASRKind GetASRAncestorFrame(FrameAndASRKind aFrameAndASRKind, nsDisplayListBuilder* aBuilder); /** * Step up one frame in the ASR chain, to be used in conjunction with * GetASRAncestorFrame to walk the ASR chain. Note this doesn't go from one * ASR frame to the next. Rather this walks all frame types, taking only one * ancestor step per call. Note that a frame returned from this function could * generate two ASRs: an inner one corresponding to an activated scroll frame, * and an outer one corresponding to sticky pos. Returns null if we hit * aLimitAncestor. */ static FrameAndASRKind OneStepInASRChain(FrameAndASRKind aFrameAndASRKind, nsDisplayListBuilder* aBuilder, nsIFrame* aLimitAncestor = nullptr); /** * Calls DecideScrollableLayerEnsureDisplayport on all proper ancestors of * aAnchor that are async scrollable up to but not including aLimitAncestor * (this creates a minimal display port on all async scrollable ancestors if * they don't have a display port) and makes sure that there is an ASR struct * created for all such async scrollable ancestors. * Returns the ASR of aAnchor. * This is a very specific function for anchor positioning and likely not * what you want. In that context, aAnchor is the anchor of an abspos frame f * (not passed to this function because it is not needed) and aLimitAncestor * is the parent/containing block of f. */ static const ActiveScrolledRoot* ActivateDisplayportOnASRAncestors( nsIFrame* aAnchor, nsIFrame* aLimitAncestor, const ActiveScrolledRoot* aASRofLimitAncestor, nsDisplayListBuilder* aBuilder); /** * aFrame is an absolutely positioned frame that is anchor positioned and * compensates for scroll in at least one axis. */ static bool ShouldAsyncScrollWithAnchor(nsIFrame* aFrame, nsIFrame* aAnchor, nsDisplayListBuilder* aBuilder, PhysicalAxes aAxes); }; } // namespace mozilla #endif // mozilla_DisplayPortUtils_h_