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

#ifndef nsLayoutUtils_h__
#define nsLayoutUtils_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 "SVGImageContext.h"
#include <limits>
#include <algorithm>
#include "gfxPoint.h"

class gfxContext;
class nsPresContext;
class nsIContent;
class nsAtom;
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 nsPIDOMWindowOuter;
class imgIRequest;
class nsIDocument;
struct nsStyleFont;
struct nsStyleImageOrientation;
struct nsOverflowAreas;

namespace mozilla {
enum class CSSPseudoElementType : uint8_t;
class EventListenerManager;
enum class LayoutFrameType : uint8_t;
struct IntrinsicSize;
struct ContainerLayerParameters;
class WritingMode;
class DisplayItemClip;
class EffectSet;
struct ActiveScrolledRoot;
namespace dom {
class CanvasRenderingContext2D;
class DOMRectList;
class Element;
class HTMLImageElement;
class HTMLCanvasElement;
class HTMLVideoElement;
class OffscreenCanvas;
class Selection;
} // namespace dom
namespace gfx {
struct RectCornerRadii;
enum class ShapedTextFlags : uint16_t;
} // 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
};

// Flags to customize the behavior of nsLayoutUtils::DrawString.
enum class DrawStringFlags {
  eDefault         = 0x0,
  eForceHorizontal = 0x1 // Forces the text to be drawn horizontally.
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DrawStringFlags)

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

  /**
   * 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 view ID (or generate a new one) for the content element
   * corresponding to the ASR.
   */
  static ViewID ViewIDForASR(const mozilla::ActiveScrolledRoot* aASR);

  /**
   * Find the scrollable frame for a given ID.
   */
  static nsIScrollableFrame* FindScrollableFrameFor(ViewID aId);

  /**
   * Find the ID for a given scrollable frame.
   */
  static ViewID FindIDForScrollableFrame(nsIScrollableFrame* aScrollable);

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

  /**
   * 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, nsIFrame* aScrolledFrame = nullptr);

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

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

  /**
   * 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.
   */
  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,
                                         mozilla::LayoutFrameType 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 mozilla::LayoutFrameType::Page or nullptr if no
   *         such ancestor exists
   */
  static nsIFrame* GetPageFrame(nsIFrame* aFrame);

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

  static int32_t DoCompareTreePosition(nsIFrame* aFrame1,
                                       nsIFrame* aFrame2,
                                       nsTArray<nsIFrame*>& 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);

  /**
   * Get the scroll id for the root scrollframe of the presshell of the given
   * prescontext. Returns NULL_SCROLL_ID if it couldn't be found.
   */
  static FrameMetrics::ViewID ScrollIdForRootScrollFrame(nsPresContext* aPresContext);

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

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

  static mozilla::LayoutDeviceIntPoint
    WidgetToWidgetOffset(nsIWidget* aFromWidget, nsIWidget* aToWidget);

  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,
    /**
     * When set, return only content that is actually visible.
     */
    ONLY_VISIBLE = 0x08
  };

  /**
   * 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<nsIFrame*> &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<Matrix4x4> - 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<Matrix4x4>* aMatrixCache = nullptr,
                                             bool aStopAtStackingContextAndDisplayPort = false,
                                             nsIFrame** aOutAncestor = nullptr);


  /**
   * Gets the transform for aFrame relative to aAncestor. Pass null for
   * aAncestor to go up to the root frame. aInCSSUnits set to true will
   * return CSS units, set to false (the default) will return App units.
   */
  static Matrix4x4 GetTransformToAncestor(nsIFrame *aFrame,
                                          const nsIFrame *aAncestor,
                                          uint32_t aFlags = 0,
                                          nsIFrame** aOutAncestor = nullptr);

  /**
   * 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
   * enum HalfCorner constants in gfx/2d/Types.h). This is precise.
   */
  static bool RoundedRectIntersectsRect(const nsRect& aRoundedRect,
                                        const nscoord aRadii[8],
                                        const nsRect& aTestRect);

  static bool MaybeCreateDisplayPortInFirstScrollFrameEncountered(
    nsIFrame* aFrame, nsDisplayListBuilder& aBuilder);


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

  /**
   * Like GetAllInFlowBoxes, but doesn't include continuations.
   */
  static void AddBoxesForFrame(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::Sequence<nsString>* 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 <video>'s poster-image has a dedicated
   *                  anonymous element & child-frame, but we should still use
   *                  the <video>'s 'object-fit' and 'object-position' values.)
   * @param aAnchorPoint [out] A point that should be pixel-aligned by functions
   *                           like nsLayoutUtils::DrawImage. See documentation
   *                           in nsCSSRendering.h for ComputeObjectAnchorPoint.
   * @return The nsRect into which we should render the replaced content (using
   *         the same coordinate space as the passed-in aConstraintRect).
   */
  static nsRect ComputeObjectDestRect(const nsRect& aConstraintRect,
                                      const IntrinsicSize& aIntrinsicSize,
                                      const nsSize& aIntrinsicRatio,
                                      const nsStylePosition* aStylePos,
                                      nsPoint* aAnchorPoint = nullptr);

  /**
   * Get the font metrics corresponding to the frame's style data.
   * @param aFrame the frame
   * @param aSizeInflation number to multiply font size by
   */
  static already_AddRefed<nsFontMetrics> GetFontMetricsForFrame(
    const nsIFrame* aFrame, float aSizeInflation);

  static already_AddRefed<nsFontMetrics>
    GetInflatedFontMetricsForFrame(const nsIFrame* aFrame)
  {
    return GetFontMetricsForFrame(aFrame, FontSizeInflationFor(aFrame));
  }

  /**
   * Get the font metrics corresponding to the given style data.
   * @param aStyleContext the style data
   * @param aSizeInflation number to multiply font size by
   */
  static already_AddRefed<nsFontMetrics> GetFontMetricsForStyleContext(
      nsStyleContext* aStyleContext, float aSizeInflation = 1.0f,
      uint8_t aVariantWidth = NS_FONT_VARIANT_WIDTH_NORMAL);

  /**
   * Get the font metrics of emphasis marks corresponding to the given
   * style data. The result is same as GetFontMetricsForStyleContext
   * except that the font size is scaled down to 50%.
   * @param aStyleContext the style data
   * @param aInflation number to multiple font size by
   */
  static already_AddRefed<nsFontMetrics> GetFontMetricsOfEmphasisMarks(
      nsStyleContext* aStyleContext, float aInflation)
  {
    return GetFontMetricsForStyleContext(aStyleContext, aInflation * 0.5f);
  }

  /**
   * Find the immediate child of aParent whose frame subtree contains
   * aDescendantFrame. Returns null if aDescendantFrame is not a descendant
   * of aParent.
   */
  static nsIFrame* FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame);

  /**
   * Find the nearest ancestor that's a block
   */
  static nsBlockFrame* FindNearestBlockAncestor(nsIFrame* aFrame);

  /**
   * Find the nearest ancestor that's not for generated content. Will return
   * aFrame if aFrame is not for generated content.
   */
  static nsIFrame* GetNonGeneratedAncestor(nsIFrame* aFrame);

  /**
   * Cast aFrame to an nsBlockFrame* or return null if it's not
   * an nsBlockFrame.
   */
  static nsBlockFrame* GetAsBlock(nsIFrame* aFrame);

  /*
   * Whether the frame is an nsBlockFrame which is not a wrapper block.
   */
  static bool IsNonWrapperBlock(nsIFrame* aFrame);

  /**
   * If aFrame is an out of flow frame, return its placeholder, otherwise
   * return its parent.
   */
  static nsIFrame* GetParentOrPlaceholderFor(nsIFrame* aFrame);

  /**
   * If aFrame is an out of flow frame, return its placeholder, otherwise
   * return its (possibly cross-doc) parent.
   */
  static nsIFrame* GetParentOrPlaceholderForCrossDoc(nsIFrame* aFrame);

  /**
   * Get a frame's next-in-flow, or, if it doesn't have one, its
   * block-in-inline-split sibling.
   */
  static nsIFrame*
  GetNextContinuationOrIBSplitSibling(nsIFrame *aFrame);

  /**
   * Get the first frame in the continuation-plus-ib-split-sibling chain
   * containing aFrame.
   */
  static nsIFrame*
  FirstContinuationOrIBSplitSibling(const nsIFrame *aFrame);

  /**
   * Get the last frame in the continuation-plus-ib-split-sibling chain
   * containing aFrame.
   */
  static nsIFrame*
  LastContinuationOrIBSplitSibling(const nsIFrame *aFrame);

  /**
   * Is FirstContinuationOrIBSplitSibling(aFrame) going to return
   * aFrame?
   */
  static bool
  IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame);

  /**
   * Check whether aFrame is a part of the scrollbar or scrollcorner of
   * the root content.
   * @param aFrame the checking frame.
   * @return true if the frame is a part of the scrollbar or scrollcorner of
   *         the root content.
   */
  static bool IsViewportScrollbarFrame(nsIFrame* aFrame);

  /**
   * Get the contribution of aFrame to its containing block's intrinsic
   * size for the given physical axis.  This considers the child's intrinsic
   * width, its 'width', 'min-width', and 'max-width' properties (or 'height'
   * variations if that's what matches aAxis) and its padding, border and margin
   * in the corresponding dimension.
   * @param aPercentageBasis an optional percentage basis (in aFrame's WM).
   *   Pass NS_UNCONSTRAINEDSIZE if the basis is indefinite in either/both axes.
   *   If you pass Nothing() a percentage basis will be calculated from aFrame's
   *   ancestors' computed size in the relevant axis, if needed.
   * @param aMarginBoxMinSizeClamp make the result fit within this margin-box
   * size by reducing the *content size* (flooring at zero).  This is used for:
   * https://drafts.csswg.org/css-grid/#min-size-auto
   */
  enum class IntrinsicISizeType { MIN_ISIZE, PREF_ISIZE };
  static const auto MIN_ISIZE = IntrinsicISizeType::MIN_ISIZE;
  static const auto PREF_ISIZE = IntrinsicISizeType::PREF_ISIZE;
  enum {
    IGNORE_PADDING = 0x01,
    BAIL_IF_REFLOW_NEEDED = 0x02, // returns NS_INTRINSIC_WIDTH_UNKNOWN if so
    MIN_INTRINSIC_ISIZE = 0x04, // use min-width/height instead of width/height
    ADD_PERCENTS = 0x08, // apply AddPercents also for MIN_ISIZE
  };
  static nscoord
  IntrinsicForAxis(mozilla::PhysicalAxis aAxis,
                   gfxContext*           aRenderingContext,
                   nsIFrame*             aFrame,
                   IntrinsicISizeType    aType,
                   const mozilla::Maybe<mozilla::LogicalSize>& aPercentageBasis = mozilla::Nothing(),
                   uint32_t              aFlags = 0,
                   nscoord               aMarginBoxMinSizeClamp = NS_MAXSIZE);
  /**
   * Calls IntrinsicForAxis with aFrame's parent's inline physical axis.
   */
  static nscoord IntrinsicForContainer(gfxContext*         aRenderingContext,
                                       nsIFrame*           aFrame,
                                       IntrinsicISizeType  aType,
                                       uint32_t            aFlags = 0);

  /**
   * Get the definite size contribution of aFrame for the given physical axis.
   * This considers the child's 'min-width' property (or 'min-height' if the
   * given axis is vertical), and its padding, border, and margin in the
   * corresponding dimension.  If the 'min-' property is 'auto' (and 'overflow'
   * is 'visible') and the corresponding 'width'/'height' is definite it returns
   * the "specified size" for:
   * https://drafts.csswg.org/css-grid/#min-size-auto
   * Note that the "transferred size" is not handled here; use IntrinsicForAxis.
   * Note that any percentage in 'width'/'height' makes it count as indefinite.
   * If the 'min-' property is 'auto' and 'overflow' is not 'visible', then it
   * calculates the result as if the 'min-' computed value is zero.
   * Otherwise, return NS_UNCONSTRAINEDSIZE.
   *
   * @note this behavior is specific to Grid/Flexbox (currently) so aFrame
   * should be a grid/flex item.
   */
  static nscoord MinSizeContributionForAxis(mozilla::PhysicalAxis aAxis,
                                            gfxContext*           aRC,
                                            nsIFrame*             aFrame,
                                            IntrinsicISizeType    aType,
                                            uint32_t              aFlags = 0);

  /**
   * This function increases an initial intrinsic size, 'aCurrent', according
   * to the given 'aPercent', such that the size-increase makes up exactly
   * 'aPercent' percent of the returned value.  If 'aPercent' or 'aCurrent' are
   * less than or equal to zero the original 'aCurrent' value is returned.
   * If 'aPercent' is greater than or equal to 1.0 the value nscoord_MAX is
   * returned.
   */
  static nscoord AddPercents(nscoord aCurrent, float aPercent)
  {
    if (aPercent > 0.0f && aCurrent > 0) {
      return MOZ_UNLIKELY(aPercent >= 1.0f) ? nscoord_MAX
        : NSToCoordRound(float(aCurrent) / (1.0f - aPercent));
    }
    return aCurrent;
  }

  /*
   * Convert nsStyleCoord to nscoord when percentages depend on the
   * containing block size.
   * @param aPercentBasis The width or height of the containing block
   * (whichever the client wants to use for resolving percentages).
   */
  static nscoord ComputeCBDependentValue(nscoord aPercentBasis,
                                         const nsStyleCoord& aCoord);

  static nscoord ComputeBSizeDependentValue(
                   nscoord              aContainingBlockBSize,
                   const nsStyleCoord&  aCoord);

  static nscoord ComputeBSizeValue(nscoord aContainingBlockBSize,
                                    nscoord aContentEdgeToBoxSizingBoxEdge,
                                    const nsStyleCoord& aCoord)
  {
    MOZ_ASSERT(aContainingBlockBSize != nscoord_MAX || !aCoord.HasPercent(),
               "caller must deal with %% of unconstrained block-size");
    MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());

    nscoord result =
      nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockBSize);
    // Clamp calc(), and the subtraction for box-sizing.
    return std::max(0, result - aContentEdgeToBoxSizingBoxEdge);
  }

  // XXX to be removed
  static bool IsAutoHeight(const nsStyleCoord &aCoord, nscoord aCBHeight)
  {
    return IsAutoBSize(aCoord, aCBHeight);
  }

  static bool IsAutoBSize(const nsStyleCoord &aCoord, nscoord aCBBSize)
  {
    nsStyleUnit unit = aCoord.GetUnit();
    return unit == eStyleUnit_Auto ||  // only for 'height'
           unit == eStyleUnit_None ||  // only for 'max-height'
           // The enumerated values were originally aimed at inline-size
           // (or width, as it was before logicalization). For now, let them
           // return true here, so that we don't call ComputeBSizeValue with
           // value types that it doesn't understand. (See bug 1113216.)
           //
           // FIXME (bug 567039, bug 527285)
           // This isn't correct for the 'fill' value or for the 'min-*' or
           // 'max-*' properties, which need to be handled differently by
           // the callers of IsAutoBSize().
           unit == eStyleUnit_Enumerated ||
           (aCBBSize == nscoord_MAX && aCoord.HasPercent());
  }

  static bool IsPaddingZero(const nsStyleCoord &aCoord)
  {
    return (aCoord.GetUnit() == eStyleUnit_Coord &&
            aCoord.GetCoordValue() == 0) ||
           (aCoord.GetUnit() == eStyleUnit_Percent &&
            aCoord.GetPercentValue() == 0.0f) ||
           (aCoord.IsCalcUnit() &&
            // clamp negative calc() to 0
            nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) <= 0 &&
            nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) <= 0);
  }

  static bool IsMarginZero(const nsStyleCoord &aCoord)
  {
    return (aCoord.GetUnit() == eStyleUnit_Coord &&
            aCoord.GetCoordValue() == 0) ||
           (aCoord.GetUnit() == eStyleUnit_Percent &&
            aCoord.GetPercentValue() == 0.0f) ||
           (aCoord.IsCalcUnit() &&
            nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) == 0 &&
            nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) == 0);
  }

  static void MarkDescendantsDirty(nsIFrame *aSubtreeRoot);

  static void MarkIntrinsicISizesDirtyIfDependentOnBSize(nsIFrame* aFrame);

  /*
   * Calculate the used values for 'width' and 'height' when width
   * and height are 'auto'. The tentWidth and tentHeight arguments should be
   * the result of applying the rules for computing intrinsic sizes and ratios.
   * as specified by CSS 2.1 sections 10.3.2 and 10.6.2
   */
  static nsSize ComputeAutoSizeWithIntrinsicDimensions(nscoord minWidth, nscoord minHeight,
                                                       nscoord maxWidth, nscoord maxHeight,
                                                       nscoord tentWidth, nscoord tentHeight);

  // Implement nsIFrame::GetPrefISize in terms of nsIFrame::AddInlinePrefISize
  static nscoord PrefISizeFromInline(nsIFrame* aFrame,
                                     gfxContext* aRenderingContext);

  // Implement nsIFrame::GetMinISize in terms of nsIFrame::AddInlineMinISize
  static nscoord MinISizeFromInline(nsIFrame* aFrame,
                                    gfxContext* aRenderingContext);

  // Get a suitable foreground color for painting aColor for aFrame.
  static nscolor DarkenColorIfNeeded(nsIFrame* aFrame, nscolor aColor);

  // Get a suitable foreground color for painting aField for aFrame.
  // Type of aFrame is made a template parameter because nsIFrame is not
  // a complete type in the header. Type-safety is not harmed given that
  // DarkenColorIfNeeded requires an nsIFrame pointer.
  template<typename Frame, typename T, typename S>
  static nscolor GetColor(Frame* aFrame, T S::* aField) {
    nscolor color = aFrame->GetVisitedDependentColor(aField);
    return DarkenColorIfNeeded(aFrame, color);
  }

  // Get a baseline y position in app units that is snapped to device pixels.
  static gfxFloat GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
                                      nscoord aY, nscoord aAscent);
  // Ditto for an x position (for vertical text). Note that for vertical-rl
  // writing mode, the ascent value should be negated by the caller.
  static gfxFloat GetSnappedBaselineX(nsIFrame* aFrame, gfxContext* aContext,
                                      nscoord aX, nscoord aAscent);

  static nscoord AppUnitWidthOfString(char16_t aC,
                                      nsFontMetrics& aFontMetrics,
                                      DrawTarget* aDrawTarget) {
    return AppUnitWidthOfString(&aC, 1, aFontMetrics, aDrawTarget);
  }
  static nscoord AppUnitWidthOfString(const nsString& aString,
                                      nsFontMetrics& aFontMetrics,
                                      DrawTarget* aDrawTarget) {
    return nsLayoutUtils::AppUnitWidthOfString(aString.get(), aString.Length(),
                                               aFontMetrics, aDrawTarget);
  }
  static nscoord AppUnitWidthOfString(const char16_t *aString,
                                      uint32_t aLength,
                                      nsFontMetrics& aFontMetrics,
                                      DrawTarget* aDrawTarget);
  static nscoord AppUnitWidthOfStringBidi(const nsString& aString,
                                          const nsIFrame* aFrame,
                                          nsFontMetrics& aFontMetrics,
                                          gfxContext& aContext) {
    return nsLayoutUtils::AppUnitWidthOfStringBidi(aString.get(),
                                                   aString.Length(), aFrame,
                                                   aFontMetrics, aContext);
  }
  static nscoord AppUnitWidthOfStringBidi(const char16_t* aString,
                                          uint32_t aLength,
                                          const nsIFrame* aFrame,
                                          nsFontMetrics& aFontMetrics,
                                          gfxContext& aContext);

  static bool StringWidthIsGreaterThan(const nsString& aString,
                                       nsFontMetrics& aFontMetrics,
                                       DrawTarget* aDrawTarget,
                                       nscoord aWidth);

  static nsBoundingMetrics AppUnitBoundsOfString(const char16_t* aString,
                                                 uint32_t aLength,
                                                 nsFontMetrics& aFontMetrics,
                                                 DrawTarget* aDrawTarget);

  static void DrawString(const nsIFrame*     aFrame,
                         nsFontMetrics&      aFontMetrics,
                         gfxContext*         aContext,
                         const char16_t*     aString,
                         int32_t             aLength,
                         nsPoint             aPoint,
                         nsStyleContext*     aStyleContext = nullptr,
                         DrawStringFlags     aFlags = DrawStringFlags::eDefault);

  static nsPoint GetBackgroundFirstTilePos(const nsPoint& aDest,
                                           const nsPoint& aFill,
                                           const nsSize& aRepeatSize);

  /**
   * Supports only LTR or RTL. Bidi (mixed direction) is not supported.
   */
  static void DrawUniDirString(const char16_t* aString,
                               uint32_t aLength,
                               nsPoint aPoint,
                               nsFontMetrics& aFontMetrics,
                               gfxContext& aContext);

  /**
   * Helper function for drawing text-shadow. The callback's job
   * is to draw whatever needs to be blurred onto the given context.
   */
  typedef void (* TextShadowCallback)(gfxContext* aCtx,
                                      nsPoint aShadowOffset,
                                      const nscolor& aShadowColor,
                                      void* aData);

  static void PaintTextShadow(const nsIFrame*     aFrame,
                              gfxContext*         aContext,
                              const nsRect&       aTextRect,
                              const nsRect&       aDirtyRect,
                              const nscolor&      aForegroundColor,
                              TextShadowCallback  aCallback,
                              void*               aCallbackData);

  /**
   * Gets the baseline to vertically center text from a font within a
   * line of specified height.
   * aIsInverted: true if the text is inverted relative to the block
   * direction, so that the block-dir "ascent" corresponds to font
   * descent. (Applies to sideways text in vertical-lr mode.)
   *
   * Returns the baseline position relative to the top of the line.
   */
  static nscoord GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
                                         nscoord        aLineHeight,
                                         bool           aIsInverted);

  /**
   * Derive a baseline of |aFrame| (measured from its top border edge)
   * from its first in-flow line box (not descending into anything with
   * 'overflow' not 'visible', potentially including aFrame itself).
   *
   * Returns true if a baseline was found (and fills in aResult).
   * Otherwise returns false.
   */
  static bool GetFirstLineBaseline(mozilla::WritingMode aWritingMode,
                                   const nsIFrame* aFrame, nscoord* aResult);

  /**
   * Just like GetFirstLineBaseline, except also returns the top and
   * bottom of the line with the baseline.
   *
   * Returns true if a line was found (and fills in aResult).
   * Otherwise returns false.
   */
  struct LinePosition {
    nscoord mBStart, mBaseline, mBEnd;

    LinePosition operator+(nscoord aOffset) const {
      LinePosition result;
      result.mBStart = mBStart + aOffset;
      result.mBaseline = mBaseline + aOffset;
      result.mBEnd = mBEnd + aOffset;
      return result;
    }
  };
  static bool GetFirstLinePosition(mozilla::WritingMode aWritingMode,
                                   const nsIFrame* aFrame,
                                   LinePosition* aResult);


  /**
   * Derive a baseline of |aFrame| (measured from its top border edge)
   * from its last in-flow line box (not descending into anything with
   * 'overflow' not 'visible', potentially including aFrame itself).
   *
   * Returns true if a baseline was found (and fills in aResult).
   * Otherwise returns false.
   */
  static bool GetLastLineBaseline(mozilla::WritingMode aWritingMode,
                                  const nsIFrame* aFrame, nscoord* aResult);

  /**
   * Returns a block-dir coordinate relative to this frame's origin that
   * represents the logical block-end of the frame or its visible content,
   * whichever is further from the origin.
   * Relative positioning is ignored and margins and glyph bounds are not
   * considered.
   * This value will be >= mRect.BSize() and <= overflowRect.BEnd() unless
   * relative positioning is applied.
   */
  static nscoord CalculateContentBEnd(mozilla::WritingMode aWritingMode,
                                      nsIFrame* aFrame);

  /**
   * Gets the closest frame (the frame passed in or one of its parents) that
   * qualifies as a "layer"; used in DOM0 methods that depends upon that
   * definition. This is the nearest frame that is either positioned or scrolled
   * (the child of a scroll frame).
   */
  static nsIFrame* GetClosestLayer(nsIFrame* aFrame);

  /**
   * Gets the graphics sampling filter for the frame
   */
  static SamplingFilter GetSamplingFilterForFrame(nsIFrame* aFrame);

  static inline void InitDashPattern(StrokeOptions& aStrokeOptions,
                                     uint8_t aBorderStyle) {
    if (aBorderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
      static Float dot[] = { 1.f, 1.f };
      aStrokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dot);
      aStrokeOptions.mDashPattern = dot;
    } else if (aBorderStyle == NS_STYLE_BORDER_STYLE_DASHED) {
      static Float dash[] = { 5.f, 5.f };
      aStrokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash);
      aStrokeOptions.mDashPattern = dash;
    } else {
      aStrokeOptions.mDashLength = 0;
      aStrokeOptions.mDashPattern = nullptr;
    }
  }

  /**
   * Convert an nsRect to a gfxRect.
   */
  static gfxRect RectToGfxRect(const nsRect& aRect,
                               int32_t aAppUnitsPerDevPixel);

  static gfxPoint PointToGfxPoint(const nsPoint& aPoint,
                                  int32_t aAppUnitsPerPixel) {
    return gfxPoint(gfxFloat(aPoint.x) / aAppUnitsPerPixel,
                    gfxFloat(aPoint.y) / aAppUnitsPerPixel);
  }

  /* N.B. The only difference between variants of the Draw*Image
   * functions below is the type of the aImage argument.
   */

  /**
   * Draw a background image.  The image's dimensions are as specified in aDest;
   * the image itself is not consulted to determine a size.
   * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
   *   @param aContext          The context to draw to, already set up with an
   *                            appropriate scale and transform for drawing in
   *                            app units.
   *   @param aForFrame         The nsIFrame that we're drawing this image for.
   *   @param aImage            The image.
   *   @param aImageSize        The unscaled size of the image being drawn.
   *                            (This might be the image's size if no scaling
   *                            occurs, or it might be the image's size if
   *                            the image is a vector image being rendered at
   *                            that size.)
   *   @param aDest             The position and scaled area where one copy of
   *                            the image should be drawn. This area represents
   *                            the image itself in its correct position as defined
   *                            with the background-position css property.
   *   @param aFill             The area to be filled with copies of the image.
   *   @param aRepeatSize       The distance between the positions of two subsequent
   *                            repeats of the image. Sizes larger than aDest.Size()
   *                            create gaps between the images.
   *   @param aAnchor           A point in aFill which we will ensure is
   *                            pixel-aligned in the output.
   *   @param aDirty            Pixels outside this area may be skipped.
   *   @param aImageFlags       Image flags of the imgIContainer::FLAG_* variety.
   *   @param aExtendMode       How to extend the image over the dest rect.
   */
  static DrawResult DrawBackgroundImage(gfxContext&         aContext,
                                        nsIFrame*           aForFrame,
                                        nsPresContext*      aPresContext,
                                        imgIContainer*      aImage,
                                        const CSSIntSize&   aImageSize,
                                        SamplingFilter      aSamplingFilter,
                                        const nsRect&       aDest,
                                        const nsRect&       aFill,
                                        const nsSize&       aRepeatSize,
                                        const nsPoint&      aAnchor,
                                        const nsRect&       aDirty,
                                        uint32_t            aImageFlags,
                                        ExtendMode          aExtendMode,
                                        float               aOpacity);

  /**
   * Draw an image.
   * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
   *   @param aRenderingContext Where to draw the image, set up with an
   *                            appropriate scale and transform for drawing in
   *                            app units.
   *   @param aStyleContext     The style context of the nsIFrame (or
   *                            pseudo-element) for which this image is being
   *                            drawn.
   *   @param aImage            The image.
   *   @param aDest             Where one copy of the image should mapped to.
   *   @param aFill             The area to be filled with copies of the
   *                            image.
   *   @param aAnchor           A point in aFill which we will ensure is
   *                            pixel-aligned in the output.
   *   @param aDirty            Pixels outside this area may be skipped.
   *   @param aImageFlags       Image flags of the imgIContainer::FLAG_* variety
   */
  static DrawResult DrawImage(gfxContext&         aContext,
                              nsStyleContext*     aStyleContext,
                              nsPresContext*      aPresContext,
                              imgIContainer*      aImage,
                              const SamplingFilter aSamplingFilter,
                              const nsRect&       aDest,
                              const nsRect&       aFill,
                              const nsPoint&      aAnchor,
                              const nsRect&       aDirty,
                              uint32_t            aImageFlags,
                              float               aOpacity = 1.0);

  /**
   * Draw a whole image without scaling or tiling.
   *
   *   @param aRenderingContext Where to draw the image, set up with an
   *                            appropriate scale and transform for drawing in
   *                            app units.
   *   @param aImage            The image.
   *   @param aDest             The top-left where the image should be drawn.
   *   @param aDirty            If non-null, then pixels outside this area may
   *                            be skipped.
   *   @param aImageFlags       Image flags of the imgIContainer::FLAG_* variety
   *   @param aSourceArea       If non-null, this area is extracted from
   *                            the image and drawn at aDest. It's
   *                            in appunits. For best results it should
   *                            be aligned with image pixels.
   */
  static DrawResult DrawSingleUnscaledImage(gfxContext&          aContext,
                                            nsPresContext*       aPresContext,
                                            imgIContainer*       aImage,
                                            const SamplingFilter aSamplingFilter,
                                            const nsPoint&       aDest,
                                            const nsRect*        aDirty,
                                            uint32_t             aImageFlags,
                                            const nsRect*        aSourceArea = nullptr);

  /**
   * Draw a whole image without tiling.
   *
   *   @param aRenderingContext Where to draw the image, set up with an
   *                            appropriate scale and transform for drawing in
   *                            app units.
   *   @param aImage            The image.
   *   @param aDest             The area that the image should fill.
   *   @param aDirty            Pixels outside this area may be skipped.
   *   @param aSVGContext       Optionally provides an SVGImageContext.
   *                            Callers should pass an SVGImageContext with at
   *                            least the viewport size set if aImage may be of
   *                            type imgIContainer::TYPE_VECTOR, or pass
   *                            Nothing() if it is of type
   *                            imgIContainer::TYPE_RASTER (to save cycles
   *                            constructing an SVGImageContext, since this
   *                            argument will be ignored for raster images).
   *   @param aImageFlags       Image flags of the imgIContainer::FLAG_*
   *                            variety.
   *   @param aAnchor           If non-null, a point which we will ensure
   *                            is pixel-aligned in the output.
   *   @param aSourceArea       If non-null, this area is extracted from
   *                            the image and drawn in aDest. It's
   *                            in appunits. For best results it should
   *                            be aligned with image pixels.
   */
  static DrawResult DrawSingleImage(gfxContext&         aContext,
                                    nsPresContext*      aPresContext,
                                    imgIContainer*      aImage,
                                    const SamplingFilter aSamplingFilter,
                                    const nsRect&       aDest,
                                    const nsRect&       aDirty,
                                    const mozilla::Maybe<SVGImageContext>& aSVGContext,
                                    uint32_t            aImageFlags,
                                    const nsPoint*      aAnchorPoint = nullptr,
                                    const nsRect*       aSourceArea = nullptr);

  /**
   * Given an imgIContainer, this method attempts to obtain an intrinsic
   * px-valued height & width for it.  If the imgIContainer has a non-pixel
   * value for either height or width, this method tries to generate a pixel
   * value for that dimension using the intrinsic ratio (if available).  The
   * intrinsic ratio will be assigned to aIntrinsicRatio; if there's no
   * intrinsic ratio then (0, 0) will be assigned.
   *
   * This method will always set aGotWidth and aGotHeight to indicate whether
   * we were able to successfully obtain (or compute) a value for each
   * dimension.
   *
   * NOTE: This method is similar to ComputeSizeWithIntrinsicDimensions.  The
   * difference is that this one is simpler and is suited to places where we
   * have less information about the frame tree.
   */
  static void ComputeSizeForDrawing(imgIContainer* aImage,
                                    CSSIntSize&    aImageSize,
                                    nsSize&        aIntrinsicRatio,
                                    bool&          aGotWidth,
                                    bool&          aGotHeight);

  /**
   * Given an imgIContainer, this method attempts to obtain an intrinsic
   * px-valued height & width for it. If the imgIContainer has a non-pixel
   * value for either height or width, this method tries to generate a pixel
   * value for that dimension using the intrinsic ratio (if available). If,
   * after trying all these methods, no value is available for one or both
   * dimensions, the corresponding dimension of aFallbackSize is used instead.
   */
  static CSSIntSize
  ComputeSizeForDrawingWithFallback(imgIContainer* aImage,
                                    const nsSize&  aFallbackSize);

  /**
   * Given a source area of an image (in appunits) and a destination area
   * that we want to map that source area too, computes the area that
   * would be covered by the whole image. This is useful for passing to
   * the aDest parameter of DrawImage, when we want to draw a subimage
   * of an overall image.
   */
  static nsRect GetWholeImageDestination(const nsSize& aWholeImageSize,
                                         const nsRect& aImageSourceArea,
                                         const nsRect& aDestArea);

  /**
   * Given an image container and an orientation, returns an image container
   * that contains the same image, reoriented appropriately. May return the
   * original image container if no changes are needed.
   *
   * @param aContainer   The image container to apply the orientation to.
   * @param aOrientation The desired orientation.
   */
  static already_AddRefed<imgIContainer>
  OrientImage(imgIContainer* aContainer,
              const nsStyleImageOrientation& aOrientation);

  /**
   * Determine if any corner radius is of nonzero size
   *   @param aCorners the |nsStyleCorners| object to check
   *   @return true unless all the coordinates are 0%, 0 or null.
   *
   * A corner radius with one dimension zero and one nonzero is
   * treated as a nonzero-radius corner, even though it will end up
   * being rendered like a zero-radius corner.  This is because such
   * corners are not expected to appear outside of test cases, and it's
   * simpler to implement the test this way.
   */
  static bool HasNonZeroCorner(const nsStyleCorners& aCorners);

  /**
   * Determine if there is any corner radius on corners adjacent to the
   * given side.
   */
  static bool HasNonZeroCornerOnSide(const nsStyleCorners& aCorners,
                                       mozilla::Side aSide);

  /**
   * Determine if a widget is likely to require transparency or translucency.
   *   @param aBackgroundFrame The frame that the background is set on. For
   *                           <window>s, this will be the canvas frame.
   *   @param aCSSRootFrame    The frame that holds CSS properties affecting
   *                           the widget's transparency. For menupopups,
   *                           aBackgroundFrame and aCSSRootFrame will be the
   *                           same.
   *   @return a value suitable for passing to SetWindowTranslucency.
   */
  static nsTransparencyMode GetFrameTransparency(nsIFrame* aBackgroundFrame,
                                                 nsIFrame* aCSSRootFrame);

  /**
   * A frame is a popup if it has its own floating window. Menus, panels
   * and combobox dropdowns are popups.
   */
  static bool IsPopup(nsIFrame* aFrame);

  /**
   * Find the nearest "display root". This is the nearest enclosing
   * popup frame or the root prescontext's root frame.
   */
  static nsIFrame* GetDisplayRootFrame(nsIFrame* aFrame);

  /**
   * Get the reference frame that would be used when constructing a
   * display item for this frame.  Rather than using their own frame
   * as a reference frame.)
   *
   * This duplicates some of the logic of GetDisplayRootFrame above and
   * of nsDisplayListBuilder::FindReferenceFrameFor.
   *
   * If you have an nsDisplayListBuilder, you should get the reference
   * frame from it instead of calling this.
   */
  static nsIFrame* GetReferenceFrame(nsIFrame* aFrame);

  /**
   * Get textrun construction flags determined by a given style; in particular
   * some combination of:
   * -- TEXT_DISABLE_OPTIONAL_LIGATURES if letter-spacing is in use
   * -- TEXT_OPTIMIZE_SPEED if the text-rendering CSS property and font size
   * and prefs indicate we should be optimizing for speed over quality
   */
  static mozilla::gfx::ShapedTextFlags
  GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
                          const nsStyleFont* aStyleFont,
                          const nsStyleText* aStyleText,
                          nscoord aLetterSpacing);

  /**
   * Get orientation flags for textrun construction.
   */
  static mozilla::gfx::ShapedTextFlags
  GetTextRunOrientFlagsForStyle(nsStyleContext* aStyleContext);

  /**
   * Takes two rectangles whose origins must be the same, and computes
   * the difference between their union and their intersection as two
   * rectangles. (This difference is a superset of the difference
   * between the two rectangles.)
   */
  static void GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
                                      nsRect* aHStrip, nsRect* aVStrip);

  /**
   * Get a device context that can be used to get up-to-date device
   * dimensions for the given window. For some reason, this is more
   * complicated than it ought to be in multi-monitor situations.
   */
  static nsDeviceContext*
  GetDeviceContextForScreenInfo(nsPIDOMWindowOuter* aWindow);

  /**
   * Some frames with 'position: fixed' (nsStyleDisplay::mPosition ==
   * NS_STYLE_POSITION_FIXED) are not really fixed positioned, since
   * they're inside an element with -moz-transform.  This function says
   * whether such an element is a real fixed-pos element.
   */
  static bool IsReallyFixedPos(const nsIFrame* aFrame);

  /**
   * This function says whether `aFrame` would really be a fixed positioned
   * frame if the frame was created with NS_STYLE_POSITION_FIXED.
   *
   * It is effectively the same as IsReallyFixedPos, but without asserting the
   * position value. Use it only when you know what you're doing, like when
   * tearing down the frame tree (where styles may have changed due to
   * ::first-line reparenting and rule changes at the same time).
   */
  static bool MayBeReallyFixedPos(const nsIFrame* aFrame);

  /**
   * Obtain a SourceSurface from the given DOM element, if possible.
   * This obtains the most natural surface from the element; that
   * is, the one that can be obtained with the fewest conversions.
   *
   * The flags below can modify the behaviour of this function.  The
   * result is returned as a SurfaceFromElementResult struct, also
   * defined below.
   *
   * Currently, this will do:
   *  - HTML Canvas elements: will return the underlying canvas surface
   *  - HTML Video elements: will return the current video frame
   *  - Image elements: will return the image
   *
   * The above results are modified by the below flags (copying,
   * forcing image surface, etc.).
   */

  enum {
    /* When creating a new surface, create an image surface */
    SFE_WANT_IMAGE_SURFACE = 1 << 0,
    /* Whether to extract the first frame (as opposed to the
       current frame) in the case that the element is an image. */
    SFE_WANT_FIRST_FRAME_IF_IMAGE = 1 << 1,
    /* Whether we should skip colorspace/gamma conversion */
    SFE_NO_COLORSPACE_CONVERSION = 1 << 2,
    /* Specifies that the caller wants either OPAQUE or NON_PREMULT mAlphaType,
       if this is can be done efficiently. */
    SFE_PREFER_NO_PREMULTIPLY_ALPHA = 1 << 3,
    /* Whether we should skip getting a surface for vector images and
       return a DirectDrawInfo containing an imgIContainer instead. */
    SFE_NO_RASTERIZING_VECTORS = 1 << 4,
    /* If image type is vector, the return surface size will same as
       element size, not image's intrinsic size. */
    SFE_USE_ELEMENT_SIZE_IF_VECTOR = 1 << 5
  };

  struct DirectDrawInfo {
    /* imgIContainer to directly draw to a context */
    nsCOMPtr<imgIContainer> mImgContainer;
    /* which frame to draw */
    uint32_t mWhichFrame;
    /* imgIContainer flags to use when drawing */
    uint32_t mDrawingFlags;
  };

  struct SurfaceFromElementResult {
    friend class mozilla::dom::CanvasRenderingContext2D;
    friend class nsLayoutUtils;

    /* If SFEResult contains a valid surface, it either mLayersImage or mSourceSurface
     * will be non-null, and GetSourceSurface() will not be null.
     *
     * For valid surfaces, mSourceSurface may be null if mLayersImage is non-null, but
     * GetSourceSurface() will create mSourceSurface from mLayersImage when called.
     */

    /* Video elements (at least) often are already decoded as layers::Images. */
    RefPtr<mozilla::layers::Image> mLayersImage;

protected:
    /* GetSourceSurface() fills this and returns its non-null value if this SFEResult
     * was successful. */
    RefPtr<mozilla::gfx::SourceSurface> mSourceSurface;

public:
    /* Contains info for drawing when there is no mSourceSurface. */
    DirectDrawInfo mDrawInfo;

    /* The size of the surface */
    mozilla::gfx::IntSize mSize;
    /* The principal associated with the element whose surface was returned.
       If there is a surface, this will never be null. */
    nsCOMPtr<nsIPrincipal> mPrincipal;
    /* The image request, if the element is an nsIImageLoadingContent */
    nsCOMPtr<imgIRequest> mImageRequest;
    /* Whether the element was "write only", that is, the bits should not be exposed to content */
    bool mIsWriteOnly;
    /* Whether the element was still loading.  Some consumers need to handle
       this case specially. */
    bool mIsStillLoading;
    /* Whether the element has a valid size. */
    bool mHasSize;
    /* Whether the element used CORS when loading. */
    bool mCORSUsed;

    gfxAlphaType mAlphaType;

    // Methods:

    SurfaceFromElementResult();

    // Gets mSourceSurface, or makes a SourceSurface from mLayersImage.
    const RefPtr<mozilla::gfx::SourceSurface>& GetSourceSurface();
  };

  // This function can be called on any thread.
  static SurfaceFromElementResult
  SurfaceFromOffscreenCanvas(mozilla::dom::OffscreenCanvas *aOffscreenCanvas,
                             uint32_t aSurfaceFlags,
                             RefPtr<DrawTarget>& aTarget);
  static SurfaceFromElementResult
  SurfaceFromOffscreenCanvas(mozilla::dom::OffscreenCanvas *aOffscreenCanvas,
                             uint32_t aSurfaceFlags = 0) {
    RefPtr<DrawTarget> target = nullptr;
    return SurfaceFromOffscreenCanvas(aOffscreenCanvas, aSurfaceFlags, target);
  }

  static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::Element *aElement,
                                                     uint32_t aSurfaceFlags,
                                                     RefPtr<DrawTarget>& aTarget);
  static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::Element *aElement,
                                                     uint32_t aSurfaceFlags = 0) {
    RefPtr<DrawTarget> target = nullptr;
    return SurfaceFromElement(aElement, aSurfaceFlags, target);
  }

  static SurfaceFromElementResult SurfaceFromElement(nsIImageLoadingContent *aElement,
                                                     uint32_t aSurfaceFlags,
                                                     RefPtr<DrawTarget>& aTarget);
  // Need an HTMLImageElement overload, because otherwise the
  // nsIImageLoadingContent and mozilla::dom::Element overloads are ambiguous
  // for HTMLImageElement.
  static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::HTMLImageElement *aElement,
                                                     uint32_t aSurfaceFlags,
                                                     RefPtr<DrawTarget>& aTarget);
  static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::HTMLCanvasElement *aElement,
                                                     uint32_t aSurfaceFlags,
                                                     RefPtr<DrawTarget>& aTarget);
  static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::HTMLVideoElement *aElement,
                                                     uint32_t aSurfaceFlags,
                                                     RefPtr<DrawTarget>& aTarget);

  /**
   * When the document is editable by contenteditable attribute of its root
   * content or body content.
   *
   * Be aware, this returns nullptr if it's in designMode.
   *
   * For example:
   *
   *  <html contenteditable="true"><body></body></html>
   *    returns the <html>.
   *
   *  <html><body contenteditable="true"></body></html>
   *  <body contenteditable="true"></body>
   *    With these cases, this returns the <body>.
   *    NOTE: The latter case isn't created normally, however, it can be
   *          created by script with XHTML.
   *
   *  <body><p contenteditable="true"></p></body>
   *    returns nullptr because <body> isn't editable.
   */
  static nsIContent*
    GetEditableRootContentByContentEditable(nsIDocument* aDocument);

  static void AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
                                      nsDisplayList& aList,
                                      nsIFrame* aFrame,
                                      const nsRect& aCanvasArea,
                                      const nsRegion& aVisibleRegion,
                                      nscolor aBackstop);

  /**
   * Returns true if the passed in prescontext needs the dark grey background
   * that goes behind the page of a print preview presentation.
   */
  static bool NeedsPrintPreviewBackground(nsPresContext* aPresContext);

  /**
   * Adds all font faces used in the frame tree starting from aFrame
   * to the list aFontFaceList.
   */
  static nsresult GetFontFacesForFrames(nsIFrame* aFrame,
                                        nsFontFaceList* aFontFaceList);

  /**
   * Adds all font faces used within the specified range of text in aFrame,
   * and optionally its continuations, to the list in aFontFaceList.
   * Pass 0 and INT32_MAX for aStartOffset and aEndOffset to specify the
   * entire text is to be considered.
   */
  static nsresult GetFontFacesForText(nsIFrame* aFrame,
                                      int32_t aStartOffset,
                                      int32_t aEndOffset,
                                      bool aFollowContinuations,
                                      nsFontFaceList* aFontFaceList);

  /**
   * Walks the frame tree starting at aFrame looking for textRuns.
   * If |clear| is true, just clears the TEXT_RUN_MEMORY_ACCOUNTED flag
   * on each textRun found (and |aMallocSizeOf| is not used).
   * If |clear| is false, adds the storage used for each textRun to the
   * total, and sets the TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double-
   * accounting. (Runs with this flag already set will be skipped.)
   * Expected usage pattern is therefore to call twice:
   *    (void)SizeOfTextRunsForFrames(rootFrame, nullptr, true);
   *    total = SizeOfTextRunsForFrames(rootFrame, mallocSizeOf, false);
   */
  static size_t SizeOfTextRunsForFrames(nsIFrame* aFrame,
                                        mozilla::MallocSizeOf aMallocSizeOf,
                                        bool clear);

  /**
   * Returns true if the frame has any current CSS transitions.
   * A current transition is any transition that has not yet finished playing
   * including paused transitions.
   */
  static bool HasCurrentTransitions(const nsIFrame* aFrame);

  /**
   * Returns true if |aFrame| has an animation of |aProperty| regardless of
   * whether the property is overridden by !important rule.
   */
  static bool HasAnimationOfProperty(const nsIFrame* aFrame,
                                     nsCSSPropertyID aProperty);

  /**
   * Returns true if |aEffectSet| has an animation of |aProperty| regardless of
   * whether the property is overridden by !important rule.
   */
  static bool HasAnimationOfProperty(mozilla::EffectSet* aEffectSet,
                                     nsCSSPropertyID aProperty);

  /**
   * Returns true if |aFrame| has an animation of |aProperty| which is
   * not overridden by !important rules.
   */
  static bool HasEffectiveAnimation(const nsIFrame* aFrame,
                                    nsCSSPropertyID aProperty);

  /**
   * Checks if off-main-thread animations are enabled.
   */
  static bool AreAsyncAnimationsEnabled();

  /**
   * Checks if we should warn about animations that can't be async
   */
  static bool IsAnimationLoggingEnabled();

  /**
   * Find a suitable scale for a element (aFrame's content) over the course of any
   * animations and transitions of the CSS transform property on the
   * element that run on the compositor thread.
   * It will check the maximum and minimum scale during the animations and
   * transitions and return a suitable value for performance and quality.
   * Will return scale(1,1) if there are no such animations.
   * Always returns a positive value.
   * @param aVisibleSize is the size of the area we want to paint
   * @param aDisplaySize is the size of the display area of the pres context
   */
  static gfxSize ComputeSuitableScaleForAnimation(const nsIFrame* aFrame,
                                                  const nsSize& aVisibleSize,
                                                  const nsSize& aDisplaySize);

  /**
   * Checks whether we want to use the GPU to scale images when
   * possible.
   */
  static bool GPUImageScalingEnabled();

  /**
   * Checks whether we want to layerize animated images whenever possible.
   */
  static bool AnimatedImageLayersEnabled();

  /**
   * Checks if we should enable parsing for CSS Filters.
   */
  static bool CSSFiltersEnabled();

  /**
   * Checks whether support for the CSS-wide "unset" value is enabled.
   */
  static bool UnsetValueEnabled();

  /**
   * Checks whether support for the CSS grid-template-{columns,rows} 'subgrid X'
   * value is enabled.
   */
  static bool IsGridTemplateSubgridValueEnabled();

  /**
   * Checks whether support for the CSS text-align (and text-align-last)
   * 'true' value is enabled.
   */
  static bool IsTextAlignUnsafeValueEnabled();

  /**
   * Checks whether support for inter-character ruby is enabled.
   */
  static bool IsInterCharacterRubyEnabled();

  static bool InterruptibleReflowEnabled()
  {
    return sInterruptibleReflowEnabled;
  }

  /**
   * Unions the overflow areas of the children of aFrame with aOverflowAreas.
   * aSkipChildLists specifies any child lists that should be skipped.
   * kSelectPopupList and kPopupList are always skipped.
   */
  static void UnionChildOverflow(nsIFrame* aFrame,
                                 nsOverflowAreas& aOverflowAreas,
                                 mozilla::layout::FrameChildListIDs aSkipChildLists =
                                     mozilla::layout::FrameChildListIDs());

  /**
   * Return the font size inflation *ratio* for a given frame.  This is
   * the factor by which font sizes should be inflated; it is never
   * smaller than 1.
   */
  static float FontSizeInflationFor(const nsIFrame *aFrame);

  /**
   * Perform the first half of the computation of FontSizeInflationFor
   * (see above).
   * This includes determining whether inflation should be performed
   * within this container and returning 0 if it should not be.
   *
   * The result is guaranteed not to vary between line participants
   * (inlines, text frames) within a line.
   *
   * The result should not be used directly since font sizes slightly
   * above the minimum should always be adjusted as done by
   * FontSizeInflationInner.
   */
  static nscoord InflationMinFontSizeFor(const nsIFrame *aFrame);

  /**
   * Perform the second half of the computation done by
   * FontSizeInflationFor (see above).
   *
   * aMinFontSize must be the result of one of the
   *   InflationMinFontSizeFor methods above.
   */
  static float FontSizeInflationInner(const nsIFrame *aFrame,
                                      nscoord aMinFontSize);

  static bool FontSizeInflationEnabled(nsPresContext *aPresContext);

  /**
   * See comment above "font.size.inflation.maxRatio" in
   * modules/libpref/src/init/all.js .
   */
  static uint32_t FontSizeInflationMaxRatio() {
    return sFontSizeInflationMaxRatio;
  }

  /**
   * See comment above "font.size.inflation.emPerLine" in
   * modules/libpref/src/init/all.js .
   */
  static uint32_t FontSizeInflationEmPerLine() {
    return sFontSizeInflationEmPerLine;
  }

  /**
   * See comment above "font.size.inflation.minTwips" in
   * modules/libpref/src/init/all.js .
   */
  static uint32_t FontSizeInflationMinTwips() {
    return sFontSizeInflationMinTwips;
  }

  /**
   * See comment above "font.size.inflation.lineThreshold" in
   * modules/libpref/src/init/all.js .
   */
  static uint32_t FontSizeInflationLineThreshold() {
    return sFontSizeInflationLineThreshold;
  }

  static bool FontSizeInflationForceEnabled() {
    return sFontSizeInflationForceEnabled;
  }

  static bool FontSizeInflationDisabledInMasterProcess() {
    return sFontSizeInflationDisabledInMasterProcess;
  }

  /**
   * See comment above "font.size.systemFontScale" in
   * modules/libpref/init/all.js.
   */
  static float SystemFontScale() {
    return sSystemFontScale / 100.0f;
  }

  static float MaxZoom() {
    return sZoomMaxPercent / 100.0f;
  }

  static float MinZoom() {
    return sZoomMinPercent / 100.0f;
  }

  static bool SVGTransformBoxEnabled() {
    return sSVGTransformBoxEnabled;
  }

  static bool TextCombineUprightDigitsEnabled() {
    return sTextCombineUprightDigitsEnabled;
  }

  // Stylo (the Servo backend for Gecko's style system) is generally enabled
  // or disabled at compile-time. However, we provide the additional capability
  // to disable it dynamically in stylo-enabled builds via a pref.
  static bool StyloEnabled() {
#ifdef MOZ_STYLO
    return sStyloEnabled && StyloSupportedInCurrentProcess();
#else
    return false;
#endif
  }

  // Whether Stylo should be allowed to be enabled in this process.
  static bool StyloSupportedInCurrentProcess() {
#ifdef MOZ_STYLO
    if (XRE_IsContentProcess()) {
      return true;
    }
    if (XRE_IsParentProcess()) {
      // If Stylo is enabled for chrome document, we use it in all
      // parent processes, regardless of whether it's e10s parent.
      if (StyloChromeEnabled()) {
        return true;
      }
      // Otherwise we only use stylo on non-e10s parent.
      return !XRE_IsE10sParentProcess();
    }
#endif
    // Stylo is not enabled for any other process.
    MOZ_DIAGNOSTIC_ASSERT(false, "We should not be creating any document "
                          "in processes other than content and parent");
    return false;
  }

#ifdef MOZ_STYLO
  // Whether Stylo should be used on chrome documents.
  static bool StyloChromeEnabled();
#endif

  static uint32_t IdlePeriodDeadlineLimit() {
    return sIdlePeriodDeadlineLimit;
  }

  static uint32_t QuiescentFramesBeforeIdlePeriod() {
    return sQuiescentFramesBeforeIdlePeriod;
  }

  /**
   * See comment above "font.size.inflation.mappingIntercept" in
   * modules/libpref/src/init/all.js .
   */
  static int32_t FontSizeInflationMappingIntercept() {
    return sFontSizeInflationMappingIntercept;
  }

  /**
   * Returns true if the nglayout.debug.invalidation pref is set to true.
   * Note that sInvalidationDebuggingIsEnabled is declared outside this function to
   * allow it to be accessed an manipulated from breakpoint conditions.
   */
  static bool InvalidationDebuggingIsEnabled() {
    return sInvalidationDebuggingIsEnabled || getenv("MOZ_DUMP_INVALIDATION") != 0;
  }

  static void Initialize();
  static void Shutdown();

#ifdef MOZ_STYLO
  /**
   * Return whether stylo should be used for a given document URI and
   * principal.
   */
  static bool ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal);

  /**
   * Principal-based blocklist for stylo.
   * Check if aPrincipal is blocked by stylo's blocklist and should fallback to
   * use Gecko's style backend. Note that using a document's principal rather
   * than the document URI will let us piggy-back off the existing principal
   * relationships and symmetries.
   */
  static bool IsInStyloBlocklist(nsIPrincipal* aPrincipal);

  /**
   * Add aBlockedDomain to the existing stylo blocklist, i.e., sStyloBlocklist.
   * This function is exposed to nsDOMWindowUtils and only for testing purpose.
   * So, NEVER use this in any other cases.
   */
  static void AddToStyloBlocklist(const nsACString& aBlockedDomain);

  /**
   * Remove aBlockedDomain from the existing stylo blocklist, i.e., sStyloBlocklist.
   * This function is exposed to nsDOMWindowUtils and only for testing purpose.
   * So, NEVER use this in any other cases.
   */
  static void RemoveFromStyloBlocklist(const nsACString& aBlockedDomain);
#else
  static bool ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal) {
    return false;
  }
#endif

  /**
   * Register an imgIRequest object with a refresh driver.
   *
   * @param aPresContext The nsPresContext whose refresh driver we want to
   *        register with.
   * @param aRequest A pointer to the imgIRequest object which the client wants
   *        to register with the refresh driver.
   * @param aRequestRegistered A pointer to a boolean value which indicates
   *        whether the given image request is registered. If
   *        *aRequestRegistered is true, then this request will not be
   *        registered again. If the request is registered by this function,
   *        then *aRequestRegistered will be set to true upon the completion of
   *        this function.
   *
   */
  static void RegisterImageRequest(nsPresContext* aPresContext,
                                   imgIRequest* aRequest,
                                   bool* aRequestRegistered);

  /**
   * Register an imgIRequest object with a refresh driver, but only if the
   * request is for an image that is animated.
   *
   * @param aPresContext The nsPresContext whose refresh driver we want to
   *        register with.
   * @param aRequest A pointer to the imgIRequest object which the client wants
   *        to register with the refresh driver.
   * @param aRequestRegistered A pointer to a boolean value which indicates
   *        whether the given image request is registered. If
   *        *aRequestRegistered is true, then this request will not be
   *        registered again. If the request is registered by this function,
   *        then *aRequestRegistered will be set to true upon the completion of
   *        this function.
   *
   */
  static void RegisterImageRequestIfAnimated(nsPresContext* aPresContext,
                                             imgIRequest* aRequest,
                                             bool* aRequestRegistered);

  /**
   * Deregister an imgIRequest object from a refresh driver.
   *
   * @param aPresContext The nsPresContext whose refresh driver we want to
   *        deregister from.
   * @param aRequest A pointer to the imgIRequest object with which the client
   *        previously registered and now wants to deregister from the refresh
   *        driver.
   * @param aRequestRegistered A pointer to a boolean value which indicates
   *        whether the given image request is registered. If
   *        *aRequestRegistered is false, then this request will not be
   *        deregistered. If the request is deregistered by this function,
   *        then *aRequestRegistered will be set to false upon the completion of
   *        this function.
   */
  static void DeregisterImageRequest(nsPresContext* aPresContext,
                                     imgIRequest* aRequest,
                                     bool* aRequestRegistered);

  /**
   * Shim to nsCSSFrameConstructor::PostRestyleEvent. Exists so that we
   * can avoid including nsCSSFrameConstructor.h and all its dependencies
   * in content files.
   */
  static void PostRestyleEvent(mozilla::dom::Element* aElement,
                               nsRestyleHint aRestyleHint,
                               nsChangeHint aMinChangeHint);

  /**
   * Updates a pair of x and y distances if a given point is closer to a given
   * rectangle than the original distance values.  If aPoint is closer to
   * aRect than aClosestXDistance and aClosestYDistance indicate, then those
   * two variables are updated with the distance between aPoint and aRect,
   * and true is returned.  If aPoint is not closer, then aClosestXDistance
   * and aClosestYDistance are left unchanged, and false is returned.
   *
   * Distances are measured in the two dimensions separately; a closer x
   * distance beats a closer y distance.
   */
  template<typename PointType, typename RectType, typename CoordType>
  static bool PointIsCloserToRect(PointType aPoint, const RectType& aRect,
                                  CoordType& aClosestXDistance,
                                  CoordType& aClosestYDistance);
  /**
   * Computes the box shadow rect for the frame, or returns an empty rect if
   * there are no shadows.
   *
   * @param aFrame Frame to compute shadows for.
   * @param aFrameSize Size of aFrame (in case it hasn't been set yet).
   */
  static nsRect GetBoxShadowRectForFrame(nsIFrame* aFrame, const nsSize& aFrameSize);

#ifdef DEBUG
  /**
   * Assert that there are no duplicate continuations of the same frame
   * within aFrameList.  Optimize the tests by assuming that all frames
   * in aFrameList have parent aContainer.
   */
  static void
  AssertNoDuplicateContinuations(nsIFrame* aContainer,
                                 const nsFrameList& aFrameList);

  /**
   * Assert that the frame tree rooted at |aSubtreeRoot| is empty, i.e.,
   * that it contains no first-in-flows.
   */
  static void
  AssertTreeOnlyEmptyNextInFlows(nsIFrame *aSubtreeRoot);
#endif

  /**
   * Helper method to get touch action behaviour from the frame
   */
  static uint32_t
  GetTouchActionFromFrame(nsIFrame* aFrame);

  /**
   * Helper method to transform |aBounds| from aFrame to aAncestorFrame,
   * and combine it with |aPreciseTargetDest| if it is axis-aligned, or
   * combine it with |aImpreciseTargetDest| if not. The transformed rect is
   * clipped to |aClip|; if |aClip| has rounded corners, that also causes
   * the imprecise target to be used.
   */
  static void
  TransformToAncestorAndCombineRegions(
    const nsRegion& aRegion,
    nsIFrame* aFrame,
    const nsIFrame* aAncestorFrame,
    nsRegion* aPreciseTargetDest,
    nsRegion* aImpreciseTargetDest,
    mozilla::Maybe<Matrix4x4>* aMatrixCache,
    const mozilla::DisplayItemClip* aClip);

  /**
   * Populate aOutSize with the size of the content viewer corresponding
   * to the given prescontext. Return true if the size was set, false
   * otherwise.
   */
  static bool
  GetContentViewerSize(nsPresContext* aPresContext,
                       LayoutDeviceIntSize& aOutSize);

 /**
  * Calculate the compostion size for a frame. See FrameMetrics.h for
  * defintion of composition size (or bounds).
  * Note that for the root content document's root scroll frame (RCD-RSF),
  * the returned size does not change as the document's resolution changes,
  * but for all other frames it does. This means that callers that pass in
  * a frame that may or may not be the RCD-RSF (which is most callers),
  * are likely to need special-case handling of the RCD-RSF.
  */
  static nsSize
  CalculateCompositionSizeForFrame(nsIFrame* aFrame, bool aSubtractScrollbars = true);

 /**
  * Calculate the composition size for the root scroll frame of the root
  * content document.
  * @param aFrame A frame in the root content document (or a descendant of it).
  * @param aIsRootContentDocRootScrollFrame Whether aFrame is already the root
  *          scroll frame of the root content document. In this case we just
  *          use aFrame's own composition size.
  * @param aMetrics A partially populated FrameMetrics for aFrame. Must have at
  *          least mCompositionBounds, mCumulativeResolution, and
  *          mDevPixelsPerCSSPixel set.
  */
  static CSSSize
  CalculateRootCompositionSize(nsIFrame* aFrame,
                               bool aIsRootContentDocRootScrollFrame,
                               const FrameMetrics& aMetrics);

 /**
  * Calculate the scrollable rect for a frame. See FrameMetrics.h for
  * defintion of scrollable rect. aScrollableFrame is the scroll frame to calculate
  * the scrollable rect for. If it's null then we calculate the scrollable rect
  * as the rect of the root frame.
  */
  static nsRect
  CalculateScrollableRectForFrame(nsIScrollableFrame* aScrollableFrame, nsIFrame* aRootFrame);

 /**
  * Calculate the expanded scrollable rect for a frame. See FrameMetrics.h for
  * defintion of expanded scrollable rect.
  */
  static nsRect
  CalculateExpandedScrollableRect(nsIFrame* aFrame);

  /**
   * Returns true if the widget owning the given frame uses asynchronous
   * scrolling.
   */
  static bool UsesAsyncScrolling(nsIFrame* aFrame);

  /**
   * Returns true if the widget owning the given frame has builtin APZ support
   * enabled.
   */
  static bool AsyncPanZoomEnabled(nsIFrame* aFrame);

  /**
   * Returns the current APZ Resolution Scale. When Java Pan/Zoom is
   * enabled in Fennec it will always return 1.0.
   */
  static float GetCurrentAPZResolutionScale(nsIPresShell* aShell);

  /**
   * Returns true if we need to disable async scrolling for this particular
   * element. Note that this does a partial disabling - the displayport still
   * exists but uses a very small margin, and the compositor doesn't apply the
   * async transform. However, the content may still be layerized.
   */
  static bool ShouldDisableApzForElement(nsIContent* aContent);

  /**
   * Log a key/value pair for APZ testing during a paint.
   * @param aManager   The data will be written to the APZTestData associated
   *                   with this layer manager.
   * @param aScrollId Identifies the scroll frame to which the data pertains.
   * @param aKey The key under which to log the data.
   * @param aValue The value of the data to be logged.
   */
  static void LogTestDataForPaint(mozilla::layers::LayerManager* aManager,
                                  ViewID aScrollId,
                                  const std::string& aKey,
                                  const std::string& aValue) {
    DoLogTestDataForPaint(aManager, aScrollId, aKey, aValue);
  }

  /**
   * A convenience overload of LogTestDataForPaint() that accepts any type
   * as the value, and passes it through mozilla::ToString() to obtain a string
   * value. The type passed must support streaming to an std::ostream.
   */
  template <typename Value>
  static void LogTestDataForPaint(mozilla::layers::LayerManager* aManager,
                                  ViewID aScrollId,
                                  const std::string& aKey,
                                  const Value& aValue) {
    DoLogTestDataForPaint(aManager, aScrollId, aKey,
                          mozilla::ToString(aValue));
  }

  /**
   * Calculate a basic FrameMetrics with enough fields set to perform some
   * layout calculations. The fields set are dev-to-css ratio, pres shell
   * resolution, cumulative resolution, zoom, composition size, root
   * composition size, scroll offset and scrollable rect.
   *
   * By contrast, ComputeFrameMetrics() computes all the fields, but requires
   * extra inputs and can only be called during frame layer building.
   */
  static FrameMetrics CalculateBasicFrameMetrics(nsIScrollableFrame* aScrollFrame);

  /**
   * 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(nsIScrollableFrame* aScrollFrame,
                                                RepaintMode aRepaintMode);

  /**
   * If |aScrollFrame| 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,
                                     nsIFrame* aScrollFrame,
                                     RepaintMode aRepaintMode);

  static nsIScrollableFrame* GetAsyncScrollableAncestorFrame(nsIFrame* aTarget);

  /**
   * Sets a zero margin display port on all proper ancestors of aFrame that
   * are async scrollable.
   */
  static void SetZeroMarginDisplayPortOnAsyncScrollableAncestors(nsIFrame* aFrame,
                                                                 RepaintMode aRepaintMode);
  /**
   * 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);

  static bool IsOutlineStyleAutoEnabled();

  static void SetBSizeFromFontMetrics(const nsIFrame* aFrame,
                                      mozilla::ReflowOutput& aMetrics,
                                      const mozilla::LogicalMargin& aFramePadding,
                                      mozilla::WritingMode aLineWM,
                                      mozilla::WritingMode aFrameWM);

  static bool HasDocumentLevelListenersForApzAwareEvents(nsIPresShell* aShell);

  /**
   * Set the scroll port size for the purpose of clamping the scroll position
   * for the root scroll frame of this document
   * (see nsIDOMWindowUtils.setScrollPositionClampingScrollPortSize).
   */
  static void SetScrollPositionClampingScrollPortSize(nsIPresShell* aPresShell,
                                                      CSSSize aSize);

  /**
   * Returns true if the given scroll origin is "higher priority" than APZ.
   * In general any content programmatic scrolls (e.g. scrollTo calls) are
   * higher priority, and take precedence over APZ scrolling. This function
   * returns true for those, and returns false for other origins like APZ
   * itself, or scroll position updates from the history restore code.
   */
  static bool CanScrollOriginClobberApz(nsAtom* aScrollOrigin);

  static ScrollMetadata ComputeScrollMetadata(nsIFrame* aForFrame,
                                              nsIFrame* aScrollFrame,
                                              nsIContent* aContent,
                                              const nsIFrame* aReferenceFrame,
                                              mozilla::layers::LayerManager* aLayerManager,
                                              ViewID aScrollParentId,
                                              const nsRect& aViewport,
                                              const mozilla::Maybe<nsRect>& aClipRect,
                                              bool aIsRoot,
                                              const ContainerLayerParameters& aContainerParameters);

  /**
   * Returns the metadata to put onto the root layer of a layer tree, if one is
   * needed. The last argument is a callback function to check if the caller
   * already has a metadata for a given scroll id.
   */
  static mozilla::Maybe<ScrollMetadata> GetRootMetadata(nsDisplayListBuilder* aBuilder,
                                                        mozilla::layers::LayerManager* aLayerManager,
                                                        const ContainerLayerParameters& aContainerParameters,
                                                        const std::function<bool(ViewID& aScrollId)>& aCallback);

  /**
   * If the given scroll frame needs an area excluded from its composition
   * bounds due to scrollbars, return that area, otherwise return an empty
   * margin.
   * There is no need to exclude scrollbars in the following cases:
   *   - If the scroll frame is not the RCD-RSF; in that case, the composition
   *     bounds is calculated based on the scroll port which already excludes
   *     the scrollbar area.
   *   - If the scrollbars are overlay, since then they are drawn on top of the
   *     scrollable content.
   */
  static nsMargin ScrollbarAreaToExcludeFromCompositionBoundsFor(nsIFrame* aScrollFrame);

  /**
   * Looks in the layer subtree rooted at aLayer for a metrics with scroll id
   * aScrollId. Returns true if such is found.
   */
  static bool ContainsMetricsWithId(const Layer* aLayer, const ViewID& aScrollId);

  static bool ShouldUseNoScriptSheet(nsIDocument* aDocument);
  static bool ShouldUseNoFramesSheet(nsIDocument* aDocument);

  /**
   * Get the text content inside the frame. This methods traverse the
   * frame tree and collect the content from text frames. Note that this
   * method is similiar to nsContentUtils::GetNodeTextContent, but it at
   * least differs from that method in the following things:
   * 1. it skips text content inside nodes like style, script, textarea
   *    which don't generate an in-tree text frame for the text;
   * 2. it skips elements with display property set to none;
   * 3. it skips out-of-flow elements;
   * 4. it includes content inside pseudo elements;
   * 5. it may include part of text content of a node if a text frame
   *    inside is split to different continuations.
   */
  static void GetFrameTextContent(nsIFrame* aFrame, nsAString& aResult);

  /**
   * Same as GetFrameTextContent but appends the result rather than sets it.
   */
  static void AppendFrameTextContent(nsIFrame* aFrame, nsAString& aResult);

  /**
   * Takes a selection, and returns selection's bounding rect which is relative
   * to its root frame.
   *
   * @param aSel      Selection to check
   */
  static nsRect GetSelectionBoundingRect(mozilla::dom::Selection* aSel);

  /**
   * Calculate the bounding rect of |aContent|, relative to the origin
   * of the scrolled content of |aRootScrollFrame|.
   * Where the element is contained inside a scrollable subframe, the
   * bounding rect is clipped to the bounds of the subframe.
   */
  static CSSRect GetBoundingContentRect(const nsIContent* aContent,
                                        const nsIScrollableFrame* aRootScrollFrame);

  /**
   * Returns the first ancestor who is a float containing block.
   */
  static nsBlockFrame* GetFloatContainingBlock(nsIFrame* aFrame);

  /**
   * Walks up the frame tree from |aForFrame| up to |aTopFrame|, or to the
   * root of the frame tree if |aTopFrame| is nullptr, and returns true if
   * a transformed frame is encountered.
   */
  static bool IsTransformed(nsIFrame* aForFrame, nsIFrame* aTopFrame = nullptr);

  /**
   * Walk up from aFrame to the cross-doc root, accumulating all the APZ callback
   * transforms on the content elements encountered along the way. Return the
   * accumulated value.
   * XXX: Note that this does not take into account CSS transforms, nor
   * differences in structure between the frame tree and the layer tree (which
   * is probably what we *want* to be computing).
   */
  static CSSPoint GetCumulativeApzCallbackTransform(nsIFrame* aFrame);

  /**
   * Compute a rect to pre-render in cases where we want to render more of
   * something than what is visible (usually to support async transformation).
   * @param aDirtyRect the area that's visible
   * @param aOverflow the total size of the thing we're rendering
   * @param aPrerenderSize how large of an area we're willing to render
   * @return A rectangle that includes |aDirtyRect|, is clamped to |aOverflow|,
   *         and is no larger than |aPrerenderSize| (unless |aPrerenderSize|
   *         is smaller than |aDirtyRect|, in which case the returned rect
   *         will still include |aDirtyRect| and thus be larger than
   *         |aPrerenderSize|).
   */
  static nsRect ComputePartialPrerenderArea(const nsRect& aDirtyRect,
                                            const nsRect& aOverflow,
                                            const nsSize& aPrerenderSize);

  /*
   * Checks whether a node is an invisible break.
   * If not, returns the first frame on the next line if such a next line exists.
   *
   * @return  true if the node is an invisible break.
   *          aNextLineFrame is returned null in this case.
   *          false if the node causes a visible break or if the node is no break.
   *
   * @param   aNextLineFrame  assigned to first frame on the next line if such a
   *                          next line exists, null otherwise.
   */
  static bool IsInvisibleBreak(nsINode* aNode, nsIFrame** aNextLineFrame = nullptr);

  static nsRect ComputeGeometryBox(nsIFrame* aFrame,
                                   StyleGeometryBox aGeometryBox);

  static nsPoint ComputeOffsetToUserSpace(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame);

private:
  static uint32_t sFontSizeInflationEmPerLine;
  static uint32_t sFontSizeInflationMinTwips;
  static uint32_t sFontSizeInflationLineThreshold;
  static int32_t  sFontSizeInflationMappingIntercept;
  static uint32_t sFontSizeInflationMaxRatio;
  static bool sFontSizeInflationForceEnabled;
  static bool sFontSizeInflationDisabledInMasterProcess;
  static uint32_t sSystemFontScale;
  static uint32_t sZoomMaxPercent;
  static uint32_t sZoomMinPercent;
  static bool sInvalidationDebuggingIsEnabled;
  static bool sInterruptibleReflowEnabled;
  static bool sSVGTransformBoxEnabled;
  static bool sTextCombineUprightDigitsEnabled;
#ifdef MOZ_STYLO
  static bool sStyloEnabled;
  static bool sStyloBlocklistEnabled;
  static nsTArray<nsCString>* sStyloBlocklist;
#endif
  static uint32_t sIdlePeriodDeadlineLimit;
  static uint32_t sQuiescentFramesBeforeIdlePeriod;

  /**
   * Helper function for LogTestDataForPaint().
   */
  static void DoLogTestDataForPaint(mozilla::layers::LayerManager* aManager,
                                    ViewID aScrollId,
                                    const std::string& aKey,
                                    const std::string& aValue);

  static bool IsAPZTestLoggingEnabled();
};

MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsLayoutUtils::PaintFrameFlags)

template<typename PointType, typename RectType, typename CoordType>
/* static */ bool
nsLayoutUtils::PointIsCloserToRect(PointType aPoint, const RectType& aRect,
                                   CoordType& aClosestXDistance,
                                   CoordType& aClosestYDistance)
{
  CoordType fromLeft = aPoint.x - aRect.x;
  CoordType fromRight = aPoint.x - aRect.XMost();

  CoordType xDistance;
  if (fromLeft >= 0 && fromRight <= 0) {
    xDistance = 0;
  } else {
    xDistance = std::min(abs(fromLeft), abs(fromRight));
  }

  if (xDistance <= aClosestXDistance) {
    if (xDistance < aClosestXDistance) {
      aClosestYDistance = std::numeric_limits<CoordType>::max();
    }

    CoordType fromTop = aPoint.y - aRect.y;
    CoordType fromBottom = aPoint.y - aRect.YMost();

    CoordType yDistance;
    if (fromTop >= 0 && fromBottom <= 0) {
      yDistance = 0;
    } else {
      yDistance = std::min(abs(fromTop), abs(fromBottom));
    }

    if (yDistance < aClosestYDistance) {
      aClosestXDistance = xDistance;
      aClosestYDistance = yDistance;
      return true;
    }
  }

  return false;
}

namespace mozilla {

/**
 * Converts an nsPoint in app units to a Moz2D Point in pixels (whether those
 * are device pixels or CSS px depends on what the caller chooses to pass as
 * aAppUnitsPerPixel).
 */
inline gfx::Point NSPointToPoint(const nsPoint& aPoint,
                                 int32_t aAppUnitsPerPixel) {
  return gfx::Point(gfx::Float(aPoint.x) / aAppUnitsPerPixel,
                    gfx::Float(aPoint.y) / aAppUnitsPerPixel);
}

/**
 * Converts an nsRect in app units to a Moz2D Rect in pixels (whether those
 * are device pixels or CSS px depends on what the caller chooses to pass as
 * aAppUnitsPerPixel).
 */
gfx::Rect NSRectToRect(const nsRect& aRect, double aAppUnitsPerPixel);

/**
 * Converts an nsRect in app units to a Moz2D Rect in pixels (whether those
 * are device pixels or CSS px depends on what the caller chooses to pass as
 * aAppUnitsPerPixel).
 *
 * The passed DrawTarget is used to additionally snap the returned Rect to
 * device pixels, if appropriate (as decided and carried out by Moz2D's
 * MaybeSnapToDevicePixels helper, which this function calls to do any
 * snapping).
 */
gfx::Rect NSRectToSnappedRect(const nsRect& aRect, double aAppUnitsPerPixel,
                              const gfx::DrawTarget& aSnapDT);

/**
* Converts, where possible, an nsRect in app units to a Moz2D Rect in pixels
* (whether those are device pixels or CSS px depends on what the caller
*  chooses to pass as aAppUnitsPerPixel).
*
* If snapping results in a rectangle with zero width or height, the affected
* coordinates are left unsnapped
*/
gfx::Rect NSRectToNonEmptySnappedRect(const nsRect& aRect, double aAppUnitsPerPixel,
                                      const gfx::DrawTarget& aSnapDT);

void StrokeLineWithSnapping(const nsPoint& aP1, const nsPoint& aP2,
                            int32_t aAppUnitsPerDevPixel,
                            gfx::DrawTarget& aDrawTarget,
                            const gfx::Pattern& aPattern,
                            const gfx::StrokeOptions& aStrokeOptions = gfx::StrokeOptions(),
                            const gfx::DrawOptions& aDrawOptions = gfx::DrawOptions());

  namespace layout {

    /**
     * An RAII class which will, for the duration of its lifetime,
     * **if** the frame given is a container for font size inflation,
     * set the current inflation container on the pres context to null
     * (and then, in its destructor, restore the old value).
     */
    class AutoMaybeDisableFontInflation {
    public:
      explicit AutoMaybeDisableFontInflation(nsIFrame *aFrame);

      ~AutoMaybeDisableFontInflation();
    private:
      nsPresContext *mPresContext;
      bool mOldValue;
    };

    void MaybeSetupTransactionIdAllocator(layers::LayerManager* aManager,
                                          nsPresContext* aPresContext);

  } // namespace layout
} // namespace mozilla

class nsSetAttrRunnable : public mozilla::Runnable
{
public:
  nsSetAttrRunnable(nsIContent* aContent, nsAtom* aAttrName,
                    const nsAString& aValue);
  nsSetAttrRunnable(nsIContent* aContent, nsAtom* aAttrName,
                    int32_t aValue);

  NS_DECL_NSIRUNNABLE

  nsCOMPtr<nsIContent> mContent;
  RefPtr<nsAtom> mAttrName;
  nsAutoString mValue;
};

class nsUnsetAttrRunnable : public mozilla::Runnable
{
public:
  nsUnsetAttrRunnable(nsIContent* aContent, nsAtom* aAttrName);

  NS_DECL_NSIRUNNABLE

  nsCOMPtr<nsIContent> mContent;
  RefPtr<nsAtom> mAttrName;
};

// This class allows you to easily set any pointer variable and ensure it's
// set to nullptr when leaving its scope.
template<typename T>
class MOZ_RAII SetAndNullOnExit
{
public:
  SetAndNullOnExit(T* &aVariable, T* aValue) {
    aVariable = aValue;
    mVariable = &aVariable;
  }
  ~SetAndNullOnExit() {
    *mVariable = nullptr;
  }
private:
  T** mVariable;
};

#endif // nsLayoutUtils_h__
