tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGTextFrame.h (23171B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef LAYOUT_SVG_SVGTEXTFRAME_H_
      8 #define LAYOUT_SVG_SVGTEXTFRAME_H_
      9 
     10 #include "gfxMatrix.h"
     11 #include "gfxRect.h"
     12 #include "gfxTextRun.h"
     13 #include "mozilla/Attributes.h"
     14 #include "mozilla/Maybe.h"
     15 #include "mozilla/PresShellForwards.h"
     16 #include "mozilla/RefPtr.h"
     17 #include "mozilla/SVGContainerFrame.h"
     18 #include "mozilla/gfx/2D.h"
     19 #include "nsIContent.h"  // for GetContent
     20 #include "nsStubMutationObserver.h"
     21 #include "nsTextFrame.h"
     22 
     23 class gfxContext;
     24 
     25 namespace mozilla {
     26 
     27 class CharIterator;
     28 class DisplaySVGText;
     29 class SVGContextPaint;
     30 class SVGTextFrame;
     31 class TextFrameIterator;
     32 class TextNodeCorrespondenceRecorder;
     33 struct TextRenderedRun;
     34 class TextRenderedRunIterator;
     35 
     36 namespace dom {
     37 struct DOMPointInit;
     38 class DOMSVGPoint;
     39 class SVGRect;
     40 class SVGGeometryElement;
     41 }  // namespace dom
     42 }  // namespace mozilla
     43 
     44 nsIFrame* NS_NewSVGTextFrame(mozilla::PresShell* aPresShell,
     45                             mozilla::ComputedStyle* aStyle);
     46 
     47 namespace mozilla {
     48 
     49 /**
     50 * Information about the positioning for a single character in an SVG <text>
     51 * element.
     52 *
     53 * During SVG text layout, we use infinity values to represent positions and
     54 * rotations that are not explicitly specified with x/y/rotate attributes.
     55 */
     56 struct CharPosition {
     57  CharPosition()
     58      : mAngle(0),
     59        mHidden(false),
     60        mUnaddressable(false),
     61        mClusterOrLigatureGroupMiddle(false),
     62        mRunBoundary(false),
     63        mStartOfChunk(false) {}
     64 
     65  CharPosition(gfxPoint aPosition, double aAngle)
     66      : mPosition(aPosition),
     67        mAngle(aAngle),
     68        mHidden(false),
     69        mUnaddressable(false),
     70        mClusterOrLigatureGroupMiddle(false),
     71        mRunBoundary(false),
     72        mStartOfChunk(false) {}
     73 
     74  static CharPosition Unspecified(bool aUnaddressable) {
     75    CharPosition cp(UnspecifiedPoint(), UnspecifiedAngle());
     76    cp.mUnaddressable = aUnaddressable;
     77    return cp;
     78  }
     79 
     80  bool IsAngleSpecified() const { return mAngle != UnspecifiedAngle(); }
     81 
     82  bool IsXSpecified() const { return mPosition.x != UnspecifiedCoord(); }
     83 
     84  bool IsYSpecified() const { return mPosition.y != UnspecifiedCoord(); }
     85 
     86  gfxPoint mPosition;
     87  double mAngle;
     88 
     89  // not displayed due to falling off the end of a <textPath>
     90  bool mHidden;
     91 
     92  // skipped in positioning attributes due to being collapsed-away white space
     93  bool mUnaddressable;
     94 
     95  // a preceding character is what positioning attributes address
     96  bool mClusterOrLigatureGroupMiddle;
     97 
     98  // rendering is split here since an explicit position or rotation was given
     99  bool mRunBoundary;
    100 
    101  // an anchored chunk begins here
    102  bool mStartOfChunk;
    103 
    104 private:
    105  static gfxFloat UnspecifiedCoord() {
    106    return std::numeric_limits<gfxFloat>::infinity();
    107  }
    108 
    109  static double UnspecifiedAngle() {
    110    return std::numeric_limits<double>::infinity();
    111  }
    112 
    113  static gfxPoint UnspecifiedPoint() {
    114    return gfxPoint(UnspecifiedCoord(), UnspecifiedCoord());
    115  }
    116 };
    117 
    118 /**
    119 * A runnable to mark glyph positions as needing to be recomputed
    120 * and to invalid the bounds of the SVGTextFrame frame.
    121 */
    122 class GlyphMetricsUpdater : public Runnable {
    123 public:
    124  NS_DECL_NSIRUNNABLE
    125  explicit GlyphMetricsUpdater(SVGTextFrame* aFrame)
    126      : Runnable("GlyphMetricsUpdater"), mFrame(aFrame) {}
    127  static void Run(SVGTextFrame* aFrame);
    128  void Revoke() { mFrame = nullptr; }
    129 
    130 private:
    131  SVGTextFrame* mFrame;
    132 };
    133 
    134 /**
    135 * Frame class for SVG <text> elements.
    136 *
    137 * An SVGTextFrame manages SVG text layout, painting and interaction for
    138 * all descendent text content elements.  The frame tree will look like this:
    139 *
    140 *   SVGTextFrame                     -- for <text>
    141 *     <anonymous block frame>
    142 *       ns{Block,Inline,Text}Frames  -- for text nodes, <tspan>s, <a>s, etc.
    143 *
    144 * SVG text layout is done by:
    145 *
    146 *   1. Reflowing the anonymous block frame.
    147 *   2. Inspecting the (app unit) positions of the glyph for each character in
    148 *      the nsTextFrames underneath the anonymous block frame.
    149 *   3. Determining the (user unit) positions for each character in the <text>
    150 *      using the x/y/dx/dy/rotate attributes on all the text content elements,
    151 *      and using the step 2 results to fill in any gaps.
    152 *   4. Applying any other SVG specific text layout (anchoring and text paths)
    153 *      to the positions computed in step 3.
    154 *
    155 * Rendering of the text is done by splitting up each nsTextFrame into ranges
    156 * that can be contiguously painted.  (For example <text x="10 20">abcd</text>
    157 * would have two contiguous ranges: one for the "a" and one for the "bcd".)
    158 * Each range is called a "text rendered run", represented by a TextRenderedRun
    159 * object.  The TextRenderedRunIterator class performs that splitting and
    160 * returns a TextRenderedRun for each bit of text to be painted separately.
    161 *
    162 * Each rendered run is painted by calling nsTextFrame::PaintText.  If the text
    163 * formatting is simple enough (solid fill, no stroking, etc.), PaintText will
    164 * itself do the painting.  Otherwise, a DrawPathCallback is passed to
    165 * PaintText so that we can fill the text geometry with SVG paint servers.
    166 */
    167 class SVGTextFrame final : public SVGDisplayContainerFrame {
    168  friend nsIFrame* ::NS_NewSVGTextFrame(mozilla::PresShell* aPresShell,
    169                                        ComputedStyle* aStyle);
    170 
    171  friend class CharIterator;
    172  friend class DisplaySVGText;
    173  friend class GlyphMetricsUpdater;
    174  friend class MutationObserver;
    175  friend class TextFrameIterator;
    176  friend class TextNodeCorrespondenceRecorder;
    177  friend struct TextRenderedRun;
    178  friend class TextRenderedRunIterator;
    179 
    180  using Range = gfxTextRun::Range;
    181  using DrawTarget = gfx::DrawTarget;
    182  using Path = gfx::Path;
    183  using Point = gfx::Point;
    184  using Rect = gfx::Rect;
    185 
    186 protected:
    187  explicit SVGTextFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
    188      : SVGDisplayContainerFrame(aStyle, aPresContext, kClassID) {
    189    AddStateBits(NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_SVG_TEXT |
    190                 NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
    191                 NS_STATE_SVG_POSITIONING_DIRTY);
    192  }
    193 
    194  ~SVGTextFrame() = default;
    195 
    196 public:
    197  NS_DECL_QUERYFRAME
    198  NS_DECL_FRAMEARENA_HELPERS(SVGTextFrame)
    199 
    200  // nsIFrame:
    201  void Init(nsIContent* aContent, nsContainerFrame* aParent,
    202            nsIFrame* aPrevInFlow) override;
    203 
    204  void DidSetComputedStyle(ComputedStyle* aOldComputedStyle) override;
    205 
    206  nsresult AttributeChanged(int32_t aNamespaceID, nsAtom* aAttribute,
    207                            AttrModType aModType) override;
    208 
    209  nsContainerFrame* GetContentInsertionFrame() override {
    210    return PrincipalChildList().FirstChild()->GetContentInsertionFrame();
    211  }
    212 
    213  void BuildDisplayList(nsDisplayListBuilder* aBuilder,
    214                        const nsDisplayListSet& aLists) override;
    215 
    216 #ifdef DEBUG_FRAME_DUMP
    217  nsresult GetFrameName(nsAString& aResult) const override {
    218    return MakeFrameName(u"SVGText"_ns, aResult);
    219  }
    220 #endif
    221 
    222  /**
    223   * Finds the nsTextFrame for the closest rendered run to the specified point.
    224   */
    225  void FindCloserFrameForSelection(
    226      const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) override;
    227 
    228  // ISVGDisplayableFrame interface:
    229  void NotifySVGChanged(ChangeFlags aFlags) override;
    230  void PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
    231                imgDrawingParams& aImgParams) override;
    232  nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
    233  void ReflowSVG() override;
    234  SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace,
    235                              uint32_t aFlags) override;
    236 
    237  // SVG DOM text methods:
    238  uint32_t GetNumberOfChars(nsIContent* aContent);
    239  float GetComputedTextLength(nsIContent* aContent);
    240  MOZ_CAN_RUN_SCRIPT_BOUNDARY void SelectSubString(nsIContent* aContent,
    241                                                   uint32_t charnum,
    242                                                   uint32_t nchars,
    243                                                   ErrorResult& aRv);
    244  bool RequiresSlowFallbackForSubStringLength();
    245  float GetSubStringLengthFastPath(nsIContent* aContent, uint32_t charnum,
    246                                   uint32_t nchars, ErrorResult& aRv);
    247  /**
    248   * This fallback version of GetSubStringLength takes
    249   * into account glyph positioning and requires us to have flushed layout
    250   * before calling it. As per the SVG 2 spec, typically glyph
    251   * positioning does not affect the results of getSubStringLength, but one
    252   * exception is text in a textPath where we need to ignore characters that
    253   * fall off the end of the textPath path.
    254   */
    255  float GetSubStringLengthSlowFallback(nsIContent* aContent, uint32_t charnum,
    256                                       uint32_t nchars, ErrorResult& aRv);
    257 
    258  int32_t GetCharNumAtPosition(nsIContent* aContent,
    259                               const dom::DOMPointInit& aPoint);
    260 
    261  already_AddRefed<dom::DOMSVGPoint> GetStartPositionOfChar(
    262      nsIContent* aContent, uint32_t aCharNum, ErrorResult& aRv);
    263  already_AddRefed<dom::DOMSVGPoint> GetEndPositionOfChar(nsIContent* aContent,
    264                                                          uint32_t aCharNum,
    265                                                          ErrorResult& aRv);
    266  already_AddRefed<dom::SVGRect> GetExtentOfChar(nsIContent* aContent,
    267                                                 uint32_t aCharNum,
    268                                                 ErrorResult& aRv);
    269  float GetRotationOfChar(nsIContent* aContent, uint32_t aCharNum,
    270                          ErrorResult& aRv);
    271 
    272  // SVGTextFrame methods:
    273 
    274  /**
    275   * Handles a base or animated attribute value change to a descendant
    276   * text content element.
    277   */
    278  void HandleAttributeChangeInDescendant(dom::Element* aElement,
    279                                         int32_t aNameSpaceID,
    280                                         nsAtom* aAttribute);
    281 
    282  /**
    283   * Calls ScheduleReflowSVGNonDisplayText if this is a non-display frame,
    284   * and SVGUtils::ScheduleReflowSVG otherwise.
    285   */
    286  void ScheduleReflowSVG();
    287 
    288  /**
    289   * Reflows the anonymous block frame of this non-display SVGTextFrame.
    290   *
    291   * When we are under SVGDisplayContainerFrame::ReflowSVG, we need to
    292   * reflow any SVGTextFrame frames in the subtree in case they are
    293   * being observed (by being for example in a <mask>) and the change
    294   * that caused the reflow would not already have caused a reflow.
    295   *
    296   * Note that displayed SVGTextFrames are reflowed as needed, when PaintSVG
    297   * is called or some SVG DOM method is called on the element.
    298   */
    299  void ReflowSVGNonDisplayText();
    300 
    301  /**
    302   * This is a function that behaves similarly to SVGUtils::ScheduleReflowSVG,
    303   * but which will skip over any ancestor non-display container frames on the
    304   * way to the SVGOuterSVGFrame.  It exists for the situation where a
    305   * non-display <text> element has changed and needs to ensure ReflowSVG will
    306   * be called on its closest display container frame, so that
    307   * SVGDisplayContainerFrame::ReflowSVG will call ReflowSVGNonDisplayText on
    308   * it.
    309   *
    310   * We have to do this in two cases: in response to a style change on a
    311   * non-display <text>, where aReason will be
    312   * IntrinsicDirty::FrameAncestorsAndDescendants (the common case), and also in
    313   * response to glyphs changes on non-display <text> (i.e., animated
    314   * SVG-in-OpenType glyphs), in which case aReason will be None, since layout
    315   * doesn't need to be recomputed.
    316   */
    317  void ScheduleReflowSVGNonDisplayText(IntrinsicDirty aReason);
    318 
    319  /**
    320   * Updates the mFontSizeScaleFactor value by looking at the range of
    321   * font-sizes used within the <text>.
    322   *
    323   * @return Whether mFontSizeScaleFactor changed.
    324   */
    325  bool UpdateFontSizeScaleFactor();
    326 
    327  double GetFontSizeScaleFactor() const;
    328 
    329  /**
    330   * Takes a point from the <text> element's user space and
    331   * converts it to the appropriate frame user space of aChildFrame,
    332   * according to which rendered run the point hits.
    333   */
    334  Point TransformFramePointToTextChild(const Point& aPoint,
    335                                       const nsIFrame* aChildFrame);
    336 
    337  /**
    338   * Takes an app unit rectangle in the coordinate space of a given descendant
    339   * frame of this frame, and returns a rectangle in the <text> element's user
    340   * space that covers all parts of rendered runs that intersect with the
    341   * rectangle.
    342   */
    343  gfxRect TransformFrameRectFromTextChild(const nsRect& aRect,
    344                                          const nsIFrame* aChildFrame);
    345 
    346  /** As above, but taking and returning a device px rect. */
    347  Rect TransformFrameRectFromTextChild(const Rect& aRect,
    348                                       const nsIFrame* aChildFrame);
    349 
    350  /** As above, but with a single point */
    351  Point TransformFramePointFromTextChild(const Point& aPoint,
    352                                         const nsIFrame* aChildFrame);
    353 
    354  // Return our ::-moz-svg-text anonymous box.
    355  void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
    356 
    357 private:
    358  /**
    359   * Mutation observer used to watch for text positioning attribute changes
    360   * on descendent text content elements (like <tspan>s).
    361   */
    362  class MutationObserver final : public nsStubMutationObserver {
    363   public:
    364    explicit MutationObserver(SVGTextFrame* aFrame) : mFrame(aFrame) {
    365      MOZ_ASSERT(mFrame, "MutationObserver needs a non-null frame");
    366      mFrame->GetContent()->AddMutationObserver(this);
    367      SetEnabledCallbacks(kCharacterDataChanged | kAttributeChanged |
    368                          kContentAppended | kContentInserted |
    369                          kContentWillBeRemoved);
    370    }
    371 
    372    // nsISupports
    373    NS_DECL_ISUPPORTS
    374 
    375    // nsIMutationObserver
    376    NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
    377    NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
    378    NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
    379    NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
    380    NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
    381 
    382   private:
    383    ~MutationObserver() { mFrame->GetContent()->RemoveMutationObserver(this); }
    384 
    385    SVGTextFrame* const mFrame;
    386  };
    387 
    388  /**
    389   * Resolves Bidi for the anonymous block child if it needs it.
    390   */
    391  void MaybeResolveBidiForAnonymousBlockChild();
    392 
    393  /**
    394   * Reflows the anonymous block child if it is dirty or has dirty
    395   * children, or if the SVGTextFrame itself is dirty.
    396   */
    397  void MaybeReflowAnonymousBlockChild();
    398 
    399  /**
    400   * Performs the actual work of reflowing the anonymous block child.
    401   */
    402  void DoReflow();
    403 
    404  /**
    405   * Schedules mPositions to be recomputed and the covered region to be
    406   * updated.
    407   */
    408  void NotifyGlyphMetricsChange(bool aUpdateTextCorrespondence);
    409 
    410  /**
    411   * Recomputes mPositions by calling DoGlyphPositioning if this information
    412   * is out of date.
    413   */
    414  void UpdateGlyphPositioning();
    415 
    416  /**
    417   * Populates mPositions with positioning information for each character
    418   * within the <text>.
    419   */
    420  void DoGlyphPositioning();
    421 
    422  /**
    423   * Converts the specified index into mPositions to an addressable
    424   * character index (as can be used with the SVG DOM text methods)
    425   * relative to the specified text child content element.
    426   *
    427   * @param aIndex The global character index.
    428   * @param aContent The descendant text child content element that
    429   *   the returned addressable index will be relative to; null
    430   *   means the same as the <text> element.
    431   * @return The addressable index, or -1 if the index cannot be
    432   *   represented as an addressable index relative to aContent.
    433   */
    434  int32_t ConvertTextElementCharIndexToAddressableIndex(int32_t aIndex,
    435                                                        nsIContent* aContent);
    436 
    437  /**
    438   * Recursive helper for ResolvePositions below.
    439   *
    440   * @param aContent The current node.
    441   * @param aIndex (in/out) The current character index.
    442   * @param aInTextPath Whether we are currently under a <textPath> element.
    443   * @param aForceStartOfChunk (in/out) Whether the next character we find
    444   *   should start a new anchored chunk.
    445   * @param aDeltas (in/out) Receives the resolved dx/dy values for each
    446   *   character.
    447   * @return false if we discover that mPositions did not have enough
    448   *   elements; true otherwise.
    449   */
    450  bool ResolvePositionsForNode(nsIContent* aContent, uint32_t& aIndex,
    451                               bool aInTextPath, bool& aForceStartOfChunk,
    452                               nsTArray<gfxPoint>& aDeltas);
    453 
    454  /**
    455   * Initializes mPositions with character position information based on
    456   * x/y/rotate attributes, leaving unspecified values in the array if a
    457   * position was not given for that character.  Also fills aDeltas with values
    458   * based on dx/dy attributes.
    459   *
    460   * @param aDeltas (in/out) Receives the resolved dx/dy values for each
    461   *   character.
    462   * @param aRunPerGlyph Whether mPositions should record that a new run begins
    463   *   at each glyph.
    464   * @return false if we did not record any positions (due to having no
    465   *   displayed characters) or if we discover that mPositions did not have
    466   *   enough elements; true otherwise.
    467   */
    468  bool ResolvePositions(nsTArray<gfxPoint>& aDeltas, bool aRunPerGlyph);
    469 
    470  /**
    471   * Determines the position, in app units, of each character in the <text> as
    472   * laid out by reflow, and appends them to aPositions.  Any characters that
    473   * are undisplayed or trimmed away just get the last position.
    474   */
    475  void DetermineCharPositions(nsTArray<nsPoint>& aPositions);
    476 
    477  /**
    478   * Sets mStartOfChunk to true for each character in mPositions that starts a
    479   * line of text.
    480   */
    481  void AdjustChunksForLineBreaks();
    482 
    483  /**
    484   * Adjusts recorded character positions in mPositions to account for glyph
    485   * boundaries.  Four things are done:
    486   *
    487   *   1. mClusterOrLigatureGroupMiddle is set to true for all such characters.
    488   *
    489   *   2. Any run and anchored chunk boundaries that begin in the middle of a
    490   *      cluster/ligature group get moved to the start of the next
    491   *      cluster/ligature group.
    492   *
    493   *   3. The position of any character in the middle of a cluster/ligature
    494   *      group is updated to take into account partial ligatures and any
    495   *      rotation the glyph as a whole has.  (The values that come out of
    496   *      DetermineCharPositions which then get written into mPositions in
    497   *      ResolvePositions store the same position value for each part of the
    498   *      ligature.)
    499   *
    500   *   4. The rotation of any character in the middle of a cluster/ligature
    501   *      group is set to the rotation of the first character.
    502   */
    503  void AdjustPositionsForClusters();
    504 
    505  /**
    506   * Updates the character positions stored in mPositions to account for
    507   * text anchoring.
    508   */
    509  void DoAnchoring();
    510 
    511  /**
    512   * Updates character positions in mPositions for those characters inside a
    513   * <textPath>.
    514   */
    515  void DoTextPathLayout();
    516 
    517  /**
    518   * Returns whether we need to render the text using
    519   * nsTextFrame::DrawPathCallbacks rather than directly painting
    520   * the text frames.
    521   *
    522   * @param aContextPaint Used by context-fill and context-stroke.
    523   * @param aShouldPaintSVGGlyphs (out) Whether SVG glyphs in the text
    524   *   should be painted.
    525   */
    526  bool ShouldRenderAsPath(nsTextFrame* aFrame, SVGContextPaint* aContextPaint,
    527                          bool& aShouldPaintSVGGlyphs);
    528 
    529  // Methods to get information for a <textPath> frame.
    530  already_AddRefed<Path> GetTextPath(nsIFrame* aTextPathFrame);
    531  gfxFloat GetOffsetScale(nsIFrame* aTextPathFrame);
    532  gfxFloat GetStartOffset(nsIFrame* aTextPathFrame);
    533 
    534  /**
    535   * The MutationObserver we have registered for the <text> element subtree.
    536   */
    537  RefPtr<MutationObserver> mMutationObserver;
    538 
    539  /**
    540   * The number of characters in the DOM after the final nsTextFrame.  For
    541   * example, with
    542   *
    543   *   <text>abcd<tspan display="none">ef</tspan></text>
    544   *
    545   * mTrailingUndisplayedCharacters would be 2.
    546   */
    547  uint32_t mTrailingUndisplayedCharacters = 0;
    548 
    549  /**
    550   * Computed position information for each DOM character within the <text>.
    551   */
    552  nsTArray<CharPosition> mPositions;
    553 
    554  /**
    555   * mFontSizeScaleFactor is used to cause the nsTextFrames to create text
    556   * runs with a font size different from the actual font-size property value.
    557   * This is used so that, for example with:
    558   *
    559   *   <svg>
    560   *     <g transform="scale(2)">
    561   *       <text font-size="10">abc</text>
    562   *     </g>
    563   *   </svg>
    564   *
    565   * a font size of 20 would be used.  It's preferable to use a font size that
    566   * is identical or close to the size that the text will appear on the screen,
    567   * because at very small or large font sizes, text metrics will be computed
    568   * differently due to the limited precision that text runs have.
    569   *
    570   * mFontSizeScaleFactor is the amount the actual font-size property value
    571   * should be multiplied by to cause the text run font size to (a) be within a
    572   * "reasonable" range, and (b) be close to the actual size to be painted on
    573   * screen.  (The "reasonable" range as determined by some #defines in
    574   * SVGTextFrame.cpp is 8..200.)
    575   */
    576  float mFontSizeScaleFactor = 1.0f;
    577 
    578  /**
    579   * The scale of the context that we last used to compute mFontSizeScaleFactor.
    580   * We record this so that we can tell when our scale transform has changed
    581   * enough to warrant reflowing the text.
    582   */
    583  float mLastContextScale = 1.0f;
    584 
    585  /**
    586   * The amount that we need to scale each rendered run to account for
    587   * lengthAdjust="spacingAndGlyphs".
    588   */
    589  float mLengthAdjustScaleFactor = 1.0f;
    590 
    591 public:
    592  struct CachedMeasuredRange {
    593    Range mRange;
    594    nscoord mAdvance;
    595  };
    596 
    597  void SetCurrentFrameForCaching(const nsTextFrame* aFrame) {
    598    if (mFrameForCachedRanges != aFrame) {
    599      PodArrayZero(mCachedRanges);
    600      mFrameForCachedRanges = aFrame;
    601    }
    602  }
    603 
    604  enum WhichRange {
    605    Before,
    606    After,
    607    CachedRangeCount,
    608  };
    609 
    610  CachedMeasuredRange& CachedRange(WhichRange aWhichRange) {
    611    return mCachedRanges[aWhichRange];
    612  }
    613 
    614  // Return a reference to a PropertyProvider for the given textframe;
    615  // the provider is cached by SVGTextFrame to avoid creating it afresh
    616  // for repeated operations involving the same textframe.
    617  nsTextFrame::PropertyProvider& PropertyProviderFor(nsTextFrame* aFrame) {
    618    if (!mCachedProvider || aFrame != mCachedProvider->GetFrame()) {
    619      mCachedProvider.reset();
    620      mCachedProvider.emplace(aFrame,
    621                              aFrame->EnsureTextRun(nsTextFrame::eInflated));
    622    }
    623    return mCachedProvider.ref();
    624  }
    625 
    626  // Forget any cached PropertyProvider. This should be called at the end of
    627  // any method that relied on PropertyProviderFor(), to avoid leaving a
    628  // cached provider that may become invalid.
    629  void ForgetCachedProvider() { mCachedProvider.reset(); }
    630 
    631 private:
    632  const nsTextFrame* mFrameForCachedRanges = nullptr;
    633  CachedMeasuredRange mCachedRanges[CachedRangeCount];
    634 
    635  Maybe<nsTextFrame::PropertyProvider> mCachedProvider;
    636 };
    637 
    638 }  // namespace mozilla
    639 
    640 #endif  // LAYOUT_SVG_SVGTEXTFRAME_H_