/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 nsLayoutUtils_h__ #define nsLayoutUtils_h__ #include "LayoutConstants.h" #include "mozilla/MemoryReporting.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Maybe.h" #include "mozilla/TypedEnumBits.h" #include "nsBoundingMetrics.h" #include "nsChangeHint.h" #include "nsFrameList.h" #include "mozilla/layout/FrameChildList.h" #include "nsThreadUtils.h" #include "nsIPrincipal.h" #include "FrameMetrics.h" #include "nsIWidget.h" #include "nsCSSPropertyID.h" #include "nsStyleCoord.h" #include "nsStyleConsts.h" #include "nsGkAtoms.h" #include "nsRuleNode.h" #include "imgIContainer.h" #include "mozilla/gfx/2D.h" #include "Units.h" #include "mozilla/ToString.h" #include "mozilla/ReflowOutput.h" #include "ImageContainer.h" #include "gfx2DGlue.h" #include "nsStyleConsts.h" #include #include class nsPresContext; class nsIContent; class nsIAtom; class nsIScrollableFrame; class nsIDOMEvent; class nsRegion; class nsDisplayListBuilder; enum class nsDisplayListBuilderMode : uint8_t; class nsDisplayItem; class nsFontMetrics; class nsFontFaceList; class nsIImageLoadingContent; class nsStyleContext; class nsBlockFrame; class nsContainerFrame; class nsView; class nsIFrame; class nsStyleCoord; class nsStyleCorners; class gfxContext; class nsPIDOMWindowOuter; class imgIRequest; class nsIDocument; struct gfxPoint; struct nsStyleFont; struct nsStyleImageOrientation; struct nsOverflowAreas; namespace mozilla { struct aspectRatio; enum class CSSPseudoElementType : uint8_t; class EventListenerManager; class SVGImageContext; struct IntrinsicSize; struct ContainerLayerParameters; class WritingMode; namespace dom { class CanvasRenderingContext2D; class DOMRectList; class Element; class HTMLImageElement; class HTMLCanvasElement; class HTMLVideoElement; class OffscreenCanvas; class Selection; } // namespace dom namespace gfx { struct RectCornerRadii; } // namespace gfx namespace layers { class Image; class Layer; } // namespace layers } // namespace mozilla namespace mozilla { struct DisplayPortPropertyData { DisplayPortPropertyData(const nsRect& aRect, uint32_t aPriority) : mRect(aRect) , mPriority(aPriority) {} nsRect mRect; uint32_t mPriority; }; struct DisplayPortMarginsPropertyData { DisplayPortMarginsPropertyData(const ScreenMargin& aMargins, uint32_t aPriority) : mMargins(aMargins) , mPriority(aPriority) {} ScreenMargin mMargins; uint32_t mPriority; }; } // namespace mozilla // For GetDisplayPort enum class RelativeTo { ScrollPort, ScrollFrame }; /** * nsLayoutUtils is a namespace class used for various helper * functions that are useful in multiple places in layout. The goal * is not to define multiple copies of the same static helper. */ class nsLayoutUtils { typedef mozilla::AspectRatio AspectRatio; typedef mozilla::dom::DOMRectList DOMRectList; typedef mozilla::layers::Layer Layer; typedef mozilla::ContainerLayerParameters ContainerLayerParameters; typedef mozilla::IntrinsicSize IntrinsicSize; typedef mozilla::gfx::SourceSurface SourceSurface; typedef mozilla::gfx::Color Color; typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::gfx::ExtendMode ExtendMode; typedef mozilla::gfx::SamplingFilter SamplingFilter; typedef mozilla::gfx::Float Float; typedef mozilla::gfx::Point Point; typedef mozilla::gfx::Rect Rect; typedef mozilla::gfx::RectDouble RectDouble; typedef mozilla::gfx::Matrix4x4 Matrix4x4; typedef mozilla::gfx::RectCornerRadii RectCornerRadii; typedef mozilla::gfx::StrokeOptions StrokeOptions; typedef mozilla::image::DrawResult DrawResult; public: typedef mozilla::layers::FrameMetrics FrameMetrics; typedef mozilla::layers::ScrollMetadata ScrollMetadata; typedef FrameMetrics::ViewID ViewID; typedef mozilla::CSSPoint CSSPoint; typedef mozilla::CSSSize CSSSize; typedef mozilla::CSSIntSize CSSIntSize; typedef mozilla::CSSRect CSSRect; typedef mozilla::ScreenMargin ScreenMargin; typedef mozilla::LayoutDeviceIntSize LayoutDeviceIntSize; typedef mozilla::StyleGeometryBox StyleGeometryBox; typedef mozilla::LogicalSize LogicalSize; /** * Finds previously assigned ViewID for the given content element, if any. * Returns whether a ViewID was previously assigned. */ static bool FindIDFor(const nsIContent* aContent, ViewID* aOutViewId); /** * Finds previously assigned or generates a unique ViewID for the given * content element. */ static ViewID FindOrCreateIDFor(nsIContent* aContent); /** * Find content for given ID. */ static nsIContent* FindContentFor(ViewID aId); /** * Find the scrollable frame for a given ID. */ static nsIScrollableFrame* FindScrollableFrameFor(ViewID aId); /** * Get display port for the given element, relative to the specified entity, * defaulting to the scrollport. */ static bool GetDisplayPort(nsIContent* aContent, nsRect *aResult, RelativeTo aRelativeTo = RelativeTo::ScrollPort); /** * Check whether the given element has a displayport. */ static bool HasDisplayPort(nsIContent* aContent); /** * Go through the IPC Channel and update displayport margins for content * elements based on UpdateFrame messages. The messages are left in the * queue and will be fully processed when dequeued. The aim is to paint * the most up-to-date displayport without waiting for these message to * go through the message queue. */ static void UpdateDisplayPortMarginsFromPendingMessages(); /** * @return the display port for the given element which should be used for * visibility testing purposes. * * If low-precision buffers are enabled, this is the critical display port; * otherwise, it's the same display port returned by GetDisplayPort(). */ static bool GetDisplayPortForVisibilityTesting( nsIContent* aContent, nsRect* aResult, RelativeTo aRelativeTo = RelativeTo::ScrollPort); enum class RepaintMode : uint8_t { Repaint, DoNotRepaint }; /** * 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. */ static bool SetDisplayPortMargins(nsIContent* aContent, nsIPresShell* aPresShell, const ScreenMargin& aMargins, 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); /** * Get the critical display port for the given element. */ static bool GetCriticalDisplayPort(nsIContent* aContent, nsRect* aResult); /** * Check whether the given element has a critical display port. */ static bool HasCriticalDisplayPort(nsIContent* aContent); /** * If low-precision painting is turned on, delegates to GetCriticalDisplayPort. * Otherwise, delegates to GetDisplayPort. */ static bool GetHighResolutionDisplayPort(nsIContent* aContent, nsRect* aResult); /** * Remove the displayport for the given element. */ static void RemoveDisplayPort(nsIContent* aContent); /** * Use heuristics to figure out the child list that * aChildFrame is currently in. */ static mozilla::layout::FrameChildListID GetChildListNameFor(nsIFrame* aChildFrame); /** * Returns the ::before pseudo-element for aContent, if any. */ static mozilla::dom::Element* GetBeforePseudo(const nsIContent* aContent); /** * Returns the frame corresponding to the ::before pseudo-element for * aContent, if any. */ static nsIFrame* GetBeforeFrame(const nsIContent* aContent); /** * Returns the ::after pseudo-element for aContent, if any. */ static mozilla::dom::Element* GetAfterPseudo(const nsIContent* aContent); /** * Returns the frame corresponding to the ::after pseudo-element for aContent, * if any. */ static nsIFrame* GetAfterFrame(const nsIContent* aContent); /** * Given a frame, search up the frame tree until we find an * ancestor that (or the frame itself) is of type aFrameType, if any. * * @param aFrame the frame to start at * @param aFrameType the frame type to look for * @param aStopAt a frame to stop at after we checked it * @return a frame of the given type or nullptr if no * such ancestor exists */ static nsIFrame* GetClosestFrameOfType(nsIFrame* aFrame, nsIAtom* aFrameType, nsIFrame* aStopAt = nullptr); /** * Given a frame, search up the frame tree until we find an * ancestor that (or the frame itself) is a "Page" frame, if any. * * @param aFrame the frame to start at * @return a frame of type nsGkAtoms::pageFrame or nullptr if no * such ancestor exists */ static nsIFrame* GetPageFrame(nsIFrame* aFrame) { return GetClosestFrameOfType(aFrame, nsGkAtoms::pageFrame); } /** * Given a frame which is the primary frame for an element, * return the frame that has the non-pseudoelement style context for * the content. * This is aPrimaryFrame itself except for tableWrapper frames. * * Given a non-null input, this will return null if and only if its * argument is a table wrapper frame that is mid-destruction (and its * table frame has been destroyed). */ static nsIFrame* GetStyleFrame(nsIFrame* aPrimaryFrame); /** * Given a content node, * return the frame that has the non-pseudoelement style context for * the content. May return null. * This is aContent->GetPrimaryFrame() except for tableWrapper frames. */ static nsIFrame* GetStyleFrame(const nsIContent* aContent); /** * Gets the real primary frame associated with the content object. * * In the case of absolutely positioned elements and floated elements, * the real primary frame is the frame that is out of the flow and not the * placeholder frame. */ static nsIFrame* GetRealPrimaryFrameFor(const nsIContent* aContent); #ifdef DEBUG // TODO: remove, see bug 598468. static bool gPreventAssertInCompareTreePosition; #endif // DEBUG /** * CompareTreePosition determines whether aContent1 comes before or * after aContent2 in a preorder traversal of the content tree. * * @param aCommonAncestor either null, or a common ancestor of * aContent1 and aContent2. Actually this is * only a hint; if it's not an ancestor of * aContent1 or aContent2, this function will * still work, but it will be slower than * normal. * @return < 0 if aContent1 is before aContent2 * > 0 if aContent1 is after aContent2, * 0 otherwise (meaning they're the same, or they're in * different documents) */ static int32_t CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2, const nsIContent* aCommonAncestor = nullptr) { return DoCompareTreePosition(aContent1, aContent2, -1, 1, aCommonAncestor); } /* * More generic version of |CompareTreePosition|. |aIf1Ancestor| * gives the value to return when 1 is an ancestor of 2, and likewise * for |aIf2Ancestor|. Passing (-1, 1) gives preorder traversal * order, and (1, -1) gives postorder traversal order. */ static int32_t DoCompareTreePosition(nsIContent* aContent1, nsIContent* aContent2, int32_t aIf1Ancestor, int32_t aIf2Ancestor, const nsIContent* aCommonAncestor = nullptr); /** * CompareTreePosition determines whether aFrame1 comes before or * after aFrame2 in a preorder traversal of the frame tree, where out * of flow frames are treated as children of their placeholders. This is * basically the same ordering as DoCompareTreePosition(nsIContent*) except * that it handles anonymous content properly and there are subtleties with * continuations. * * @param aCommonAncestor either null, or a common ancestor of * aContent1 and aContent2. Actually this is * only a hint; if it's not an ancestor of * aContent1 or aContent2, this function will * still work, but it will be slower than * normal. * @return < 0 if aContent1 is before aContent2 * > 0 if aContent1 is after aContent2, * 0 otherwise (meaning they're the same, or they're in * different frame trees) */ static int32_t CompareTreePosition(nsIFrame* aFrame1, nsIFrame* aFrame2, nsIFrame* aCommonAncestor = nullptr) { return DoCompareTreePosition(aFrame1, aFrame2, -1, 1, aCommonAncestor); } static int32_t CompareTreePosition(nsIFrame* aFrame1, nsIFrame* aFrame2, nsTArray& aFrame2Ancestors, nsIFrame* aCommonAncestor = nullptr) { return DoCompareTreePosition(aFrame1, aFrame2, aFrame2Ancestors, -1, 1, aCommonAncestor); } /* * More generic version of |CompareTreePosition|. |aIf1Ancestor| * gives the value to return when 1 is an ancestor of 2, and likewise * for |aIf2Ancestor|. Passing (-1, 1) gives preorder traversal * order, and (1, -1) gives postorder traversal order. */ static int32_t DoCompareTreePosition(nsIFrame* aFrame1, nsIFrame* aFrame2, int32_t aIf1Ancestor, int32_t aIf2Ancestor, nsIFrame* aCommonAncestor = nullptr); static nsIFrame* FillAncestors(nsIFrame* aFrame, nsIFrame* aStopAtAncestor, nsTArray* aAncestors); static int32_t DoCompareTreePosition(nsIFrame* aFrame1, nsIFrame* aFrame2, nsTArray& aFrame2Ancestors, int32_t aIf1Ancestor, int32_t aIf2Ancestor, nsIFrame* aCommonAncestor); /** * LastContinuationWithChild gets the last continuation in aFrame's chain * that has a child, or the first continuation if the frame has no children. */ static nsContainerFrame* LastContinuationWithChild(nsContainerFrame* aFrame); /** * GetLastSibling simply finds the last sibling of aFrame, or returns nullptr if * aFrame is null. */ static nsIFrame* GetLastSibling(nsIFrame* aFrame); /** * FindSiblingViewFor locates the child of aParentView that aFrame's * view should be inserted 'above' (i.e., before in sibling view * order). This is the first child view of aParentView whose * corresponding content is before aFrame's content (view siblings * are in reverse content order). */ static nsView* FindSiblingViewFor(nsView* aParentView, nsIFrame* aFrame); /** * Get the parent of aFrame. If aFrame is the root frame for a document, * and the document has a parent document in the same view hierarchy, then * we try to return the subdocumentframe in the parent document. * @param aExtraOffset [in/out] if non-null, then as we cross documents * an extra offset may be required and it will be added to aCrossDocOffset. * Be careful dealing with this extra offset as it is in app units of the * parent document, which may have a different app units per dev pixel ratio * than the child document. */ static nsIFrame* GetCrossDocParentFrame(const nsIFrame* aFrame, nsPoint* aCrossDocOffset = nullptr); /** * IsProperAncestorFrame checks whether aAncestorFrame is an ancestor * of aFrame and not equal to aFrame. * @param aCommonAncestor nullptr, or a common ancestor of aFrame and * aAncestorFrame. If non-null, this can bound the search and speed up * the function */ static bool IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame, nsIFrame* aCommonAncestor = nullptr); /** * Like IsProperAncestorFrame, but looks across document boundaries. * * Just like IsAncestorFrameCrossDoc, except that it returns false when * aFrame == aAncestorFrame. */ static bool IsProperAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame, nsIFrame* aCommonAncestor = nullptr); /** * IsAncestorFrameCrossDoc checks whether aAncestorFrame is an ancestor * of aFrame or equal to aFrame, looking across document boundaries. * @param aCommonAncestor nullptr, or a common ancestor of aFrame and * aAncestorFrame. If non-null, this can bound the search and speed up * the function. * * Just like IsProperAncestorFrameCrossDoc, except that it returns true when * aFrame == aAncestorFrame. */ static bool IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame, const nsIFrame* aFrame, const nsIFrame* aCommonAncestor = nullptr); /** * Sets the fixed-pos metadata properties on aLayer. * aAnchorRect is the basic anchor rectangle. If aFixedPosFrame is not a viewport * frame, then we pick a corner of aAnchorRect to as the anchor point for the * fixed-pos layer (i.e. the point to remain stable during zooming), based * on which of the fixed-pos frame's CSS absolute positioning offset * properties (top, left, right, bottom) are auto. aAnchorRect is in the * coordinate space of aLayer's container layer (i.e. relative to the reference * frame of the display item which is building aLayer's container layer). */ static void SetFixedPositionLayerData(Layer* aLayer, const nsIFrame* aViewportFrame, const nsRect& aAnchorRect, const nsIFrame* aFixedPosFrame, nsPresContext* aPresContext, const ContainerLayerParameters& aContainerParameters); /** * 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); /** * Store whether aThumbFrame wants its own layer. This sets a property on * the frame. */ static void SetScrollbarThumbLayerization(nsIFrame* aThumbFrame, bool aLayerize); /** * Returns whether aThumbFrame wants its own layer due to having called * SetScrollbarThumbLayerization. */ static bool IsScrollbarThumbLayerized(nsIFrame* aThumbFrame); /** * GetScrollableFrameFor returns the scrollable frame for a scrolled frame */ static nsIScrollableFrame* GetScrollableFrameFor(const nsIFrame *aScrolledFrame); /** * GetNearestScrollableFrameForDirection locates the first ancestor of * aFrame (or aFrame itself) that is scrollable with overflow:scroll or * overflow:auto in the given direction and where either the scrollbar for * that direction is visible or the frame can be scrolled by some * positive amount in that direction. * The search extends across document boundaries. * * @param aFrame the frame to start with * @param aDirection Whether it's for horizontal or vertical scrolling. * @return the nearest scrollable frame or nullptr if not found */ enum Direction { eHorizontal, eVertical }; static nsIScrollableFrame* GetNearestScrollableFrameForDirection(nsIFrame* aFrame, Direction aDirection); enum { /** * If the SCROLLABLE_SAME_DOC flag is set, then we only walk the frame tree * up to the root frame in the current document. */ SCROLLABLE_SAME_DOC = 0x01, /** * If the SCROLLABLE_INCLUDE_HIDDEN flag is set then we allow * overflow:hidden scrollframes to be returned as scrollable frames. */ SCROLLABLE_INCLUDE_HIDDEN = 0x02, /** * If the SCROLLABLE_ONLY_ASYNC_SCROLLABLE flag is set, then we only * want to match scrollable frames for which WantAsyncScroll() returns * true. */ SCROLLABLE_ONLY_ASYNC_SCROLLABLE = 0x04, /** * If the SCROLLABLE_ALWAYS_MATCH_ROOT flag is set, then we will always * return the root scrollable frame for the root document (in the current * process) if we encounter it, whether or not it is async scrollable or * overflow: hidden. */ SCROLLABLE_ALWAYS_MATCH_ROOT = 0x08, /** * If the SCROLLABLE_FIXEDPOS_FINDS_ROOT flag is set, then for fixed-pos * frames that are in the root document (in the current process) return the * root scrollable frame for that document. */ SCROLLABLE_FIXEDPOS_FINDS_ROOT = 0x10 }; /** * GetNearestScrollableFrame locates the first ancestor of aFrame * (or aFrame itself) that is scrollable with overflow:scroll or * overflow:auto in some direction. * * @param aFrame the frame to start with * @param aFlags if SCROLLABLE_SAME_DOC is set, do not search across * document boundaries. If SCROLLABLE_INCLUDE_HIDDEN is set, include * frames scrollable with overflow:hidden. * @return the nearest scrollable frame or nullptr if not found */ static nsIScrollableFrame* GetNearestScrollableFrame(nsIFrame* aFrame, uint32_t aFlags = 0); /** * GetScrolledRect returns the range of allowable scroll offsets * for aScrolledFrame, assuming the scrollable overflow area is * aScrolledFrameOverflowArea and the scrollport size is aScrollPortSize. * aDirection is either NS_STYLE_DIRECTION_LTR or NS_STYLE_DIRECTION_RTL. */ static nsRect GetScrolledRect(nsIFrame* aScrolledFrame, const nsRect& aScrolledFrameOverflowArea, const nsSize& aScrollPortSize, uint8_t aDirection); /** * HasPseudoStyle returns true if aContent (whose primary style * context is aStyleContext) has the aPseudoElement pseudo-style * attached to it; returns false otherwise. * * @param aContent the content node we're looking at * @param aStyleContext aContent's style context * @param aPseudoElement the id of the pseudo style we care about * @param aPresContext the presentation context * @return whether aContent has aPseudoElement style attached to it */ static bool HasPseudoStyle(nsIContent* aContent, nsStyleContext* aStyleContext, mozilla::CSSPseudoElementType aPseudoElement, nsPresContext* aPresContext); /** * If this frame is a placeholder for a float, then return the float, * otherwise return nullptr. aPlaceholder must be a placeholder frame. */ static nsIFrame* GetFloatFromPlaceholder(nsIFrame* aPlaceholder); // Combine aNewBreakType with aOrigBreakType, but limit the break types // to StyleClear::Left, Right, Both. static mozilla::StyleClear CombineBreakType(mozilla::StyleClear aOrigBreakType, mozilla::StyleClear aNewBreakType); /** * Get the coordinates of a given DOM mouse event, relative to a given * frame. Works only for DOM events generated by WidgetGUIEvents. * @param aDOMEvent the event * @param aFrame the frame to make coordinates relative to * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if * for some reason the coordinates for the mouse are not known (e.g., * the event is not a GUI event). */ static nsPoint GetDOMEventCoordinatesRelativeTo(nsIDOMEvent* aDOMEvent, nsIFrame* aFrame); /** * Get the coordinates of a given native mouse event, relative to a given * frame. * @param aEvent the event * @param aFrame the frame to make coordinates relative to * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if * for some reason the coordinates for the mouse are not known (e.g., * the event is not a GUI event). */ static nsPoint GetEventCoordinatesRelativeTo( const mozilla::WidgetEvent* aEvent, nsIFrame* aFrame); /** * Get the coordinates of a given point relative to an event and a * given frame. * @param aEvent the event * @param aPoint the point to get the coordinates relative to * @param aFrame the frame to make coordinates relative to * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if * for some reason the coordinates for the mouse are not known (e.g., * the event is not a GUI event). */ static nsPoint GetEventCoordinatesRelativeTo( const mozilla::WidgetEvent* aEvent, const mozilla::LayoutDeviceIntPoint& aPoint, nsIFrame* aFrame); /** * Get the coordinates of a given point relative to a widget and a * given frame. * @param aWidget the event src widget * @param aPoint the point to get the coordinates relative to * @param aFrame the frame to make coordinates relative to * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if * for some reason the coordinates for the mouse are not known (e.g., * the event is not a GUI event). */ static nsPoint GetEventCoordinatesRelativeTo(nsIWidget* aWidget, const mozilla::LayoutDeviceIntPoint& aPoint, nsIFrame* aFrame); /** * Get the popup frame of a given native mouse event. * @param aPresContext only check popups within aPresContext or a descendant * @param aEvent the event. * @return Null, if there is no popup frame at the point, otherwise, * returns top-most popup frame at the point. */ static nsIFrame* GetPopupFrameForEventCoordinates( nsPresContext* aPresContext, const mozilla::WidgetEvent* aEvent); /** * Translate from widget coordinates to the view's coordinates * @param aPresContext the PresContext for the view * @param aWidget the widget * @param aPt the point relative to the widget * @param aView view to which returned coordinates are relative * @return the point in the view's coordinates */ static nsPoint TranslateWidgetToView(nsPresContext* aPresContext, nsIWidget* aWidget, const mozilla::LayoutDeviceIntPoint& aPt, nsView* aView); /** * Translate from view coordinates to the widget's coordinates. * @param aPresContext the PresContext for the view * @param aView the view * @param aPt the point relative to the view * @param aWidget the widget to which returned coordinates are relative * @return the point in the view's coordinates */ static mozilla::LayoutDeviceIntPoint TranslateViewToWidget(nsPresContext* aPresContext, nsView* aView, nsPoint aPt, nsIWidget* aWidget); enum FrameForPointFlags { /** * When set, paint suppression is ignored, so we'll return non-root page * elements even if paint suppression is stopping them from painting. */ IGNORE_PAINT_SUPPRESSION = 0x01, /** * When set, clipping due to the root scroll frame (and any other viewport- * related clipping) is ignored. */ IGNORE_ROOT_SCROLL_FRAME = 0x02, /** * When set, return only content in the same document as aFrame. */ IGNORE_CROSS_DOC = 0x04 }; /** * Given aFrame, the root frame of a stacking context, find its descendant * frame under the point aPt that receives a mouse event at that location, * or nullptr if there is no such frame. * @param aPt the point, relative to the frame origin * @param aFlags some combination of FrameForPointFlags */ static nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt, uint32_t aFlags = 0); /** * Given aFrame, the root frame of a stacking context, find all descendant * frames under the area of a rectangle that receives a mouse event, * or nullptr if there is no such frame. * @param aRect the rect, relative to the frame origin * @param aOutFrames an array to add all the frames found * @param aFlags some combination of FrameForPointFlags */ static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect, nsTArray &aOutFrames, uint32_t aFlags = 0); /** * Transform aRect relative to aFrame up to the coordinate system of * aAncestor. Computes the bounding-box of the true quadrilateral. * Pass non-null aPreservesAxisAlignedRectangles and it will be set to true if * we only need to use a 2d transform that PreservesAxisAlignedRectangles(). * * |aMatrixCache| allows for optimizations in recomputing the same matrix over * and over. The argument can be one of the following values: * nullptr (the default) - No optimization; the transform matrix is computed on * every call to this function. * non-null pointer to an empty Maybe - Upon return, the Maybe is * filled with the transform matrix that was computed. This can then be passed * in to subsequent calls with the same source and destination frames to avoid * recomputing the matrix. * non-null pointer to a non-empty Matrix4x4 - The provided matrix will be used * as the transform matrix and applied to the rect. */ static nsRect TransformFrameRectToAncestor(nsIFrame* aFrame, const nsRect& aRect, const nsIFrame* aAncestor, bool* aPreservesAxisAlignedRectangles = nullptr, mozilla::Maybe* aMatrixCache = nullptr); /** * Gets the transform for aFrame relative to aAncestor. Pass null for * aAncestor to go up to the root frame. */ static Matrix4x4 GetTransformToAncestor(nsIFrame *aFrame, const nsIFrame *aAncestor); /** * Gets the scale factors of the transform for aFrame relative to the root * frame if this transform is 2D, or the identity scale factors otherwise. */ static gfxSize GetTransformToAncestorScale(nsIFrame* aFrame); /** * Gets the scale factors of the transform for aFrame relative to the root * frame if this transform is 2D, or the identity scale factors otherwise. * If some frame on the path from aFrame to the display root frame may have an * animated scale, returns the identity scale factors. */ static gfxSize GetTransformToAncestorScaleExcludingAnimated(nsIFrame* aFrame); /** * Find the nearest common ancestor frame for aFrame1 and aFrame2. The * ancestor frame could be cross-doc. */ static nsIFrame* FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2); /** * Transforms a list of CSSPoints from aFromFrame to aToFrame, taking into * account all relevant transformations on the frames up to (but excluding) * their nearest common ancestor. * If we encounter a transform that we need to invert but which is * non-invertible, we return NONINVERTIBLE_TRANSFORM. If the frames have * no common ancestor, we return NO_COMMON_ANCESTOR. * If this returns TRANSFORM_SUCCEEDED, the points in aPoints are transformed * in-place, otherwise they are untouched. */ enum TransformResult { TRANSFORM_SUCCEEDED, NO_COMMON_ANCESTOR, NONINVERTIBLE_TRANSFORM }; static TransformResult TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame, uint32_t aPointCount, CSSPoint* aPoints); /** * Same as above function, but transform points in app units and * handle 1 point per call. */ static TransformResult TransformPoint(nsIFrame* aFromFrame, nsIFrame* aToFrame, nsPoint& aPoint); /** * Transforms a rect from aFromFrame to aToFrame. In app units. * Returns the bounds of the actual rect if the transform requires rotation * or anything complex like that. */ static TransformResult TransformRect(nsIFrame* aFromFrame, nsIFrame* aToFrame, nsRect& aRect); /** * Converts app units to pixels (with optional snapping) and appends as a * translation to aTransform. */ static void PostTranslate(Matrix4x4& aTransform, const nsPoint& aOrigin, float aAppUnitsPerPixel, bool aRounded); /** * Get the border-box of aElement's primary frame, transformed it to be * relative to aFrame. */ static nsRect GetRectRelativeToFrame(mozilla::dom::Element* aElement, nsIFrame* aFrame); /** * Returns true if aRect with border inflation of size aInflateSize contains * aPoint. */ static bool ContainsPoint(const nsRect& aRect, const nsPoint& aPoint, nscoord aInflateSize); /** * Clamp aRect relative to aFrame to the scroll frames boundary searching from * aFrame. */ static nsRect ClampRectToScrollFrames(nsIFrame* aFrame, const nsRect& aRect); /** * Return true if a "layer transform" could be computed for aFrame, * and optionally return the computed transform. The returned * transform is what would be set on the layer currently if a layers * transaction were opened at the time this helper is called. */ static bool GetLayerTransformForFrame(nsIFrame* aFrame, Matrix4x4* aTransform); /** * Given a point in the global coordinate space, returns that point expressed * in the coordinate system of aFrame. This effectively inverts all * transforms between this point and the root frame. * * @param aFrame The frame that acts as the coordinate space container. * @param aPoint The point, in the global space, to get in the frame-local space. * @return aPoint, expressed in aFrame's canonical coordinate space. */ static nsPoint TransformRootPointToFrame(nsIFrame* aFrame, const nsPoint &aPoint) { return TransformAncestorPointToFrame(aFrame, aPoint, nullptr); } /** * Transform aPoint relative to aAncestor down to the coordinate system of * aFrame. */ static nsPoint TransformAncestorPointToFrame(nsIFrame* aFrame, const nsPoint& aPoint, nsIFrame* aAncestor); /** * Helper function that, given a rectangle and a matrix, returns the smallest * rectangle containing the image of the source rectangle. * * @param aBounds The rectangle to transform. * @param aMatrix The matrix to transform it with. * @param aFactor The number of app units per graphics unit. * @return The smallest rect that contains the image of aBounds. */ static nsRect MatrixTransformRect(const nsRect &aBounds, const Matrix4x4 &aMatrix, float aFactor); /** * Helper function that, given a point and a matrix, returns the image * of that point under the matrix transform. * * @param aPoint The point to transform. * @param aMatrix The matrix to transform it with. * @param aFactor The number of app units per graphics unit. * @return The image of the point under the transform. */ static nsPoint MatrixTransformPoint(const nsPoint &aPoint, const Matrix4x4 &aMatrix, float aFactor); /** * Given a graphics rectangle in graphics space, return a rectangle in * app space that contains the graphics rectangle, rounding out as necessary. * * @param aRect The graphics rect to round outward. * @param aFactor The number of app units per graphics unit. * @return The smallest rectangle in app space that contains aRect. */ static nsRect RoundGfxRectToAppRect(const Rect &aRect, float aFactor); /** * Given a graphics rectangle in graphics space, return a rectangle in * app space that contains the graphics rectangle, rounding out as necessary. * * @param aRect The graphics rect to round outward. * @param aFactor The number of app units per graphics unit. * @return The smallest rectangle in app space that contains aRect. */ static nsRect RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor); /** * Returns a subrectangle of aContainedRect that is entirely inside the rounded * rect. Complex cases are handled conservatively by returning a smaller * rect than necessary. */ static nsRegion RoundedRectIntersectRect(const nsRect& aRoundedRect, const nscoord aRadii[8], const nsRect& aContainedRect); static nsIntRegion RoundedRectIntersectIntRect(const nsIntRect& aRoundedRect, const RectCornerRadii& aCornerRadii, const nsIntRect& aContainedRect); /** * Return whether any part of aTestRect is inside of the rounded * rectangle formed by aBounds and aRadii (which are indexed by the * NS_CORNER_* constants in nsStyleConsts.h). This is precise. */ static bool RoundedRectIntersectsRect(const nsRect& aRoundedRect, const nscoord aRadii[8], const nsRect& aTestRect); enum class PaintFrameFlags : uint32_t { PAINT_IN_TRANSFORM = 0x01, PAINT_SYNC_DECODE_IMAGES = 0x02, PAINT_WIDGET_LAYERS = 0x04, PAINT_IGNORE_SUPPRESSION = 0x08, PAINT_DOCUMENT_RELATIVE = 0x10, PAINT_HIDE_CARET = 0x20, PAINT_TO_WINDOW = 0x40, PAINT_EXISTING_TRANSACTION = 0x80, PAINT_NO_COMPOSITE = 0x100, PAINT_COMPRESSED = 0x200 }; /** * Given aFrame, the root frame of a stacking context, paint it and its * descendants to aRenderingContext. * @param aRenderingContext a rendering context translated so that (0,0) * is the origin of aFrame; for best results, (0,0) should transform * to pixel-aligned coordinates. This can be null, in which case * aFrame must be a "display root" (root frame for a root document, * or the root of a popup) with an associated widget and we draw using * the layer manager for the frame's widget. * @param aDirtyRegion the region that must be painted, in the coordinates * of aFrame. * @param aBackstop paint the dirty area with this color before drawing * the actual content; pass NS_RGBA(0,0,0,0) to draw no background. * @param aBuilderMode Passed through to the display-list builder. * @param aFlags if PAINT_IN_TRANSFORM is set, then we assume * this is inside a transform or SVG foreignObject. If * PAINT_SYNC_DECODE_IMAGES is set, we force synchronous decode on all * images. If PAINT_WIDGET_LAYERS is set, aFrame must be a display root, * and we will use the frame's widget's layer manager to paint * even if aRenderingContext is non-null. This is useful if you want * to force rendering to use the widget's layer manager for testing * or speed. PAINT_WIDGET_LAYERS must be set if aRenderingContext is null. * If PAINT_DOCUMENT_RELATIVE is used, the visible region is interpreted * as being relative to the document (normally it's relative to the CSS * viewport) and the document is painted as if no scrolling has occured. * Only considered if nsIPresShell::IgnoringViewportScrolling is true. * PAINT_TO_WINDOW sets painting to window to true on the display list * builder even if we can't tell that we are painting to the window. * If PAINT_EXISTING_TRANSACTION is set, then BeginTransaction() has already * been called on aFrame's widget's layer manager and should not be * called again. * If PAINT_COMPRESSED is set, the FrameLayerBuilder should be set to * compressed mode to avoid short cut optimizations. * * So there are three possible behaviours: * 1) PAINT_WIDGET_LAYERS is set and aRenderingContext is null; we paint * by calling BeginTransaction on the widget's layer manager. * 2) PAINT_WIDGET_LAYERS is set and aRenderingContext is non-null; we * paint by calling BeginTransactionWithTarget on the widget's layer * manager. * 3) PAINT_WIDGET_LAYERS is not set and aRenderingContext is non-null; * we paint by construct a BasicLayerManager and calling * BeginTransactionWithTarget on it. This is desirable if we're doing * something like drawWindow in a mode where what gets rendered doesn't * necessarily correspond to what's visible in the window; we don't * want to mess up the widget's layer tree. */ static nsresult PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame, const nsRegion& aDirtyRegion, nscolor aBackstop, nsDisplayListBuilderMode aBuilderMode, PaintFrameFlags aFlags = PaintFrameFlags(0)); /** * Uses a binary search for find where the cursor falls in the line of text * It also keeps track of the part of the string that has already been * measured so it doesn't have to keep measuring the same text over and over. * * @param "aBaseWidth" contains the width in twips of the portion * of the text that has already been measured, and aBaseInx contains * the index of the text that has already been measured. * * @param aTextWidth returns (in twips) the length of the text that falls * before the cursor aIndex contains the index of the text where the cursor * falls. */ static bool BinarySearchForPosition(DrawTarget* aDrawTarget, nsFontMetrics& aFontMetrics, const char16_t* aText, int32_t aBaseWidth, int32_t aBaseInx, int32_t aStartInx, int32_t aEndInx, int32_t aCursorPos, int32_t& aIndex, int32_t& aTextWidth); class BoxCallback { public: BoxCallback() : mIncludeCaptionBoxForTable(true) {} virtual void AddBox(nsIFrame* aFrame) = 0; bool mIncludeCaptionBoxForTable; }; /** * Collect all CSS boxes associated with aFrame and its * continuations, "drilling down" through table wrapper frames and * some anonymous blocks since they're not real CSS boxes. * If aFrame is null, no boxes are returned. * SVG frames return a single box, themselves. */ static void GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback); /** * Find the first frame descendant of aFrame (including aFrame) which is * not an anonymous frame that getBoxQuads/getClientRects should ignore. */ static nsIFrame* GetFirstNonAnonymousFrame(nsIFrame* aFrame); class RectCallback { public: virtual void AddRect(const nsRect& aRect) = 0; }; struct RectAccumulator : public RectCallback { nsRect mResultRect; nsRect mFirstRect; bool mSeenFirstRect; RectAccumulator(); virtual void AddRect(const nsRect& aRect) override; }; struct RectListBuilder : public RectCallback { DOMRectList* mRectList; explicit RectListBuilder(DOMRectList* aList); virtual void AddRect(const nsRect& aRect) override; }; static nsIFrame* GetContainingBlockForClientRect(nsIFrame* aFrame); enum { RECTS_ACCOUNT_FOR_TRANSFORMS = 0x01, // Two bits for specifying which box type to use. // With neither bit set (default), use the border box. RECTS_USE_CONTENT_BOX = 0x02, RECTS_USE_PADDING_BOX = 0x04, RECTS_USE_MARGIN_BOX = 0x06, // both bits set RECTS_WHICH_BOX_MASK = 0x06 // bitmask for these two bits }; /** * Collect all CSS boxes (content, padding, border, or margin) associated * with aFrame and its continuations, "drilling down" through table wrapper * frames and some anonymous blocks since they're not real CSS boxes. * The boxes are positioned relative to aRelativeTo (taking scrolling * into account) and passed to the callback in frame-tree order. * If aFrame is null, no boxes are returned. * For SVG frames, returns one rectangle, the bounding box. * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting * the boxes into aRelativeTo coordinates, transforms (including CSS * and SVG transforms) are taken into account. * If aFlags includes one of RECTS_USE_CONTENT_BOX, RECTS_USE_PADDING_BOX, * or RECTS_USE_MARGIN_BOX, the corresponding type of box is used. * Otherwise (by default), the border box is used. */ static void GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo, RectCallback* aCallback, uint32_t aFlags = 0); static void GetAllInFlowRectsAndTexts(nsIFrame* aFrame, nsIFrame* aRelativeTo, RectCallback* aCallback, mozilla::dom::DOMStringList* aTextList, uint32_t aFlags = 0); /** * Computes the union of all rects returned by GetAllInFlowRects. If * the union is empty, returns the first rect. * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting * the boxes into aRelativeTo coordinates, transforms (including CSS * and SVG transforms) are taken into account. * If aFlags includes one of RECTS_USE_CONTENT_BOX, RECTS_USE_PADDING_BOX, * or RECTS_USE_MARGIN_BOX, the corresponding type of box is used. * Otherwise (by default), the border box is used. */ static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo, uint32_t aFlags = 0); enum { EXCLUDE_BLUR_SHADOWS = 0x01 }; /** * Takes a text-shadow array from the style properties of a given nsIFrame and * computes the union of those shadows along with the given initial rect. * If there are no shadows, the initial rect is returned. */ static nsRect GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect, nsIFrame* aFrame, uint32_t aFlags = 0); /** * Computes the destination rect that a given replaced element should render * into, based on its CSS 'object-fit' and 'object-position' properties. * * @param aConstraintRect The constraint rect that we have at our disposal, * which would e.g. be exactly filled by the image * if we had "object-fit: fill". * @param aIntrinsicSize The replaced content's intrinsic size, as reported * by nsIFrame::GetIntrinsicSize(). * @param aIntrinsicRatio The replaced content's intrinsic ratio, as reported * by nsIFrame::GetIntrinsicRatio(). * @param aStylePos The nsStylePosition struct that contains the 'object-fit' * and 'object-position' values that we should rely on. * (This should usually be the nsStylePosition for the * replaced element in question, but not always. For * example, a