tor-browser

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

SVGTextFrame.cpp (190632B)


      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 // Main header first:
      8 #include "SVGTextFrame.h"
      9 
     10 // Keep others in (case-insensitive) order:
     11 #include <algorithm>
     12 #include <cmath>
     13 #include <limits>
     14 
     15 #include "DOMSVGPoint.h"
     16 #include "SVGAnimatedNumberList.h"
     17 #include "SVGContentUtils.h"
     18 #include "SVGContextPaint.h"
     19 #include "SVGLengthList.h"
     20 #include "SVGNumberList.h"
     21 #include "SVGPaintServerFrame.h"
     22 #include "gfx2DGlue.h"
     23 #include "gfxContext.h"
     24 #include "gfxFont.h"
     25 #include "gfxSkipChars.h"
     26 #include "gfxTypes.h"
     27 #include "gfxUtils.h"
     28 #include "mozilla/CaretAssociationHint.h"
     29 #include "mozilla/DisplaySVGItem.h"
     30 #include "mozilla/Likely.h"
     31 #include "mozilla/PresShell.h"
     32 #include "mozilla/SVGObserverUtils.h"
     33 #include "mozilla/SVGOuterSVGFrame.h"
     34 #include "mozilla/SVGUtils.h"
     35 #include "mozilla/dom/DOMPointBinding.h"
     36 #include "mozilla/dom/SVGGeometryElement.h"
     37 #include "mozilla/dom/SVGRect.h"
     38 #include "mozilla/dom/SVGTextContentElementBinding.h"
     39 #include "mozilla/dom/SVGTextPathElement.h"
     40 #include "mozilla/dom/Selection.h"
     41 #include "mozilla/dom/Text.h"
     42 #include "mozilla/gfx/2D.h"
     43 #include "mozilla/gfx/PatternHelpers.h"
     44 #include "nsBidiPresUtils.h"
     45 #include "nsBlockFrame.h"
     46 #include "nsCaret.h"
     47 #include "nsContentUtils.h"
     48 #include "nsFrameSelection.h"
     49 #include "nsGkAtoms.h"
     50 #include "nsLayoutUtils.h"
     51 #include "nsStyleStructInlines.h"
     52 #include "nsTArray.h"
     53 #include "nsTextFrame.h"
     54 
     55 using namespace mozilla::dom;
     56 using namespace mozilla::dom::SVGTextContentElement_Binding;
     57 using namespace mozilla::gfx;
     58 using namespace mozilla::image;
     59 
     60 namespace mozilla {
     61 
     62 // ============================================================================
     63 // Utility functions
     64 
     65 /**
     66 * Using the specified gfxSkipCharsIterator, converts an offset and length
     67 * in original char indexes to skipped char indexes.
     68 *
     69 * @param aIterator The gfxSkipCharsIterator to use for the conversion.
     70 * @param aOriginalOffset The original offset.
     71 * @param aOriginalLength The original length.
     72 */
     73 static gfxTextRun::Range ConvertOriginalToSkipped(
     74    gfxSkipCharsIterator& aIterator, uint32_t aOriginalOffset,
     75    uint32_t aOriginalLength) {
     76  uint32_t start = aIterator.ConvertOriginalToSkipped(aOriginalOffset);
     77  aIterator.AdvanceOriginal(aOriginalLength);
     78  return gfxTextRun::Range(start, aIterator.GetSkippedOffset());
     79 }
     80 
     81 /**
     82 * Converts an nsPoint from app units to user space units using the specified
     83 * nsPresContext and returns it as a gfxPoint.
     84 */
     85 static gfxPoint AppUnitsToGfxUnits(const nsPoint& aPoint,
     86                                   const nsPresContext* aContext) {
     87  return gfxPoint(aContext->AppUnitsToGfxUnits(aPoint.x),
     88                  aContext->AppUnitsToGfxUnits(aPoint.y));
     89 }
     90 
     91 /**
     92 * Converts a nsRect that is in app units to CSS pixels and returns it
     93 * as a gfxRect.
     94 */
     95 static gfxRect AppUnitsToFloatCSSPixels(const nsRect& aRect) {
     96  return gfxRect(nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
     97                 nsPresContext::AppUnitsToFloatCSSPixels(aRect.y),
     98                 nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
     99                 nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
    100 }
    101 
    102 /**
    103 * Gets the measured ascent and descent of the text in the given nsTextFrame
    104 * in app units.
    105 *
    106 * @param aFrame The text frame.
    107 * @param aAscent The ascent in app units (output).
    108 * @param aDescent The descent in app units (output).
    109 */
    110 static void GetAscentAndDescentInAppUnits(nsTextFrame* aFrame,
    111                                          gfxFloat& aAscent,
    112                                          gfxFloat& aDescent) {
    113  gfxSkipCharsIterator it = aFrame->EnsureTextRun(nsTextFrame::eInflated);
    114  gfxTextRun* textRun = aFrame->GetTextRun(nsTextFrame::eInflated);
    115 
    116  gfxTextRun::Range range = ConvertOriginalToSkipped(
    117      it, aFrame->GetContentOffset(), aFrame->GetContentLength());
    118 
    119  textRun->GetLineHeightMetrics(range, aAscent, aDescent);
    120 }
    121 
    122 /**
    123 * Updates an interval by intersecting it with another interval.
    124 * The intervals are specified using a start index and a length.
    125 */
    126 static void IntersectInterval(uint32_t& aStart, uint32_t& aLength,
    127                              uint32_t aStartOther, uint32_t aLengthOther) {
    128  uint32_t aEnd = aStart + aLength;
    129  uint32_t aEndOther = aStartOther + aLengthOther;
    130 
    131  if (aStartOther >= aEnd || aStart >= aEndOther) {
    132    aLength = 0;
    133  } else {
    134    if (aStartOther >= aStart) {
    135      aStart = aStartOther;
    136    }
    137    aLength = std::min(aEnd, aEndOther) - aStart;
    138  }
    139 }
    140 
    141 /**
    142 * Intersects an interval as IntersectInterval does but by taking
    143 * the offset and length of the other interval from a
    144 * nsTextFrame::TrimmedOffsets object.
    145 */
    146 static void TrimOffsets(uint32_t& aStart, uint32_t& aLength,
    147                        const nsTextFrame::TrimmedOffsets& aTrimmedOffsets) {
    148  IntersectInterval(aStart, aLength, aTrimmedOffsets.mStart,
    149                    aTrimmedOffsets.mLength);
    150 }
    151 
    152 /**
    153 * Returns the closest ancestor-or-self node that is not an SVG <a>
    154 * element.
    155 */
    156 static nsIContent* GetFirstNonAAncestor(nsIContent* aContent) {
    157  while (aContent && aContent->IsSVGElement(nsGkAtoms::a)) {
    158    aContent = aContent->GetParent();
    159  }
    160  return aContent;
    161 }
    162 
    163 /**
    164 * Returns whether the given node is a text content element[1], taking into
    165 * account whether it has a valid parent.
    166 *
    167 * For example, in:
    168 *
    169 *   <svg xmlns="http://www.w3.org/2000/svg">
    170 *     <text><a/><text/></text>
    171 *     <tspan/>
    172 *   </svg>
    173 *
    174 * true would be returned for the outer <text> element and the <a> element,
    175 * and false for the inner <text> element (since a <text> is not allowed
    176 * to be a child of another <text>) and the <tspan> element (because it
    177 * must be inside a <text> subtree).
    178 *
    179 * [1] https://svgwg.org/svg2-draft/intro.html#TermTextContentElement
    180 */
    181 static bool IsTextContentElement(nsIContent* aContent) {
    182  if (aContent->IsSVGElement(nsGkAtoms::text)) {
    183    nsIContent* parent = GetFirstNonAAncestor(aContent->GetParent());
    184    return !parent || !IsTextContentElement(parent);
    185  }
    186 
    187  if (aContent->IsSVGElement(nsGkAtoms::textPath)) {
    188    nsIContent* parent = GetFirstNonAAncestor(aContent->GetParent());
    189    return parent && parent->IsSVGElement(nsGkAtoms::text);
    190  }
    191 
    192  return aContent->IsAnyOfSVGElements(nsGkAtoms::a, nsGkAtoms::tspan);
    193 }
    194 
    195 /**
    196 * Returns whether the specified frame is an nsTextFrame that has some text
    197 * content.
    198 */
    199 static bool IsNonEmptyTextFrame(nsIFrame* aFrame) {
    200  nsTextFrame* textFrame = do_QueryFrame(aFrame);
    201  if (!textFrame) {
    202    return false;
    203  }
    204 
    205  return textFrame->GetContentLength() != 0;
    206 }
    207 
    208 /**
    209 * Takes an nsIFrame and if it is a text frame that has some text content,
    210 * returns it as an nsTextFrame and its corresponding Text.
    211 *
    212 * @param aFrame The frame to look at.
    213 * @param aTextFrame aFrame as an nsTextFrame (output).
    214 * @param aTextNode The Text content of aFrame (output).
    215 * @return true if aFrame is a non-empty text frame, false otherwise.
    216 */
    217 static bool GetNonEmptyTextFrameAndNode(nsIFrame* aFrame,
    218                                        nsTextFrame*& aTextFrame,
    219                                        Text*& aTextNode) {
    220  nsTextFrame* text = do_QueryFrame(aFrame);
    221  bool isNonEmptyTextFrame = text && text->GetContentLength() != 0;
    222 
    223  if (isNonEmptyTextFrame) {
    224    nsIContent* content = text->GetContent();
    225    NS_ASSERTION(content && content->IsText(),
    226                 "unexpected content type for nsTextFrame");
    227 
    228    Text* node = content->AsText();
    229    MOZ_ASSERT(node->TextLength() != 0,
    230               "frame's GetContentLength() should be 0 if the text node "
    231               "has no content");
    232 
    233    aTextFrame = text;
    234    aTextNode = node;
    235  }
    236 
    237  MOZ_ASSERT(IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame,
    238             "our logic should agree with IsNonEmptyTextFrame");
    239  return isNonEmptyTextFrame;
    240 }
    241 
    242 /**
    243 * Returns whether the specified atom is for one of the five
    244 * glyph positioning attributes that can appear on SVG text
    245 * elements -- x, y, dx, dy or rotate.
    246 */
    247 static bool IsGlyphPositioningAttribute(nsAtom* aAttribute) {
    248  return aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
    249         aAttribute == nsGkAtoms::dx || aAttribute == nsGkAtoms::dy ||
    250         aAttribute == nsGkAtoms::rotate;
    251 }
    252 
    253 /**
    254 * Returns the position in app units of a given baseline (using an
    255 * SVG dominant-baseline property value) for a given nsTextFrame.
    256 *
    257 * @param aFrame The text frame to inspect.
    258 * @param aTextRun The text run of aFrame.
    259 * @param aDominantBaseline The dominant-baseline value to use.
    260 */
    261 static nscoord GetBaselinePosition(nsTextFrame* aFrame,
    262                                   const gfxTextRun* aTextRun,
    263                                   StyleDominantBaseline aDominantBaseline,
    264                                   float aFontSizeScaleFactor) {
    265  WritingMode writingMode = aFrame->GetWritingMode();
    266  gfxFloat ascent, descent;
    267  aTextRun->GetLineHeightMetrics(ascent, descent);
    268 
    269  auto convertIfVerticalRL = [&](gfxFloat dominantBaseline) {
    270    return writingMode.IsVerticalRL() ? ascent + descent - dominantBaseline
    271                                      : dominantBaseline;
    272  };
    273 
    274  switch (aDominantBaseline) {
    275    case StyleDominantBaseline::Hanging:
    276      return convertIfVerticalRL(ascent * 0.2);
    277    case StyleDominantBaseline::TextBeforeEdge:
    278      return convertIfVerticalRL(0);
    279 
    280    case StyleDominantBaseline::Alphabetic:
    281      return writingMode.IsVerticalRL()
    282                 ? ascent * 0.5
    283                 : aFrame->GetLogicalBaseline(writingMode);
    284 
    285    case StyleDominantBaseline::Auto:
    286      return convertIfVerticalRL(aFrame->GetLogicalBaseline(writingMode));
    287 
    288    case StyleDominantBaseline::Middle:
    289      return convertIfVerticalRL(aFrame->GetLogicalBaseline(writingMode) -
    290                                 SVGContentUtils::GetFontXHeight(aFrame) / 2.0 *
    291                                     AppUnitsPerCSSPixel() *
    292                                     aFontSizeScaleFactor);
    293 
    294    case StyleDominantBaseline::TextAfterEdge:
    295    case StyleDominantBaseline::Ideographic:
    296      return writingMode.IsVerticalLR() ? 0 : ascent + descent;
    297 
    298    case StyleDominantBaseline::Central:
    299      return (ascent + descent) / 2.0;
    300    case StyleDominantBaseline::Mathematical:
    301      return convertIfVerticalRL(ascent / 2.0);
    302  }
    303 
    304  MOZ_ASSERT_UNREACHABLE("unexpected dominant-baseline value");
    305  return convertIfVerticalRL(aFrame->GetLogicalBaseline(writingMode));
    306 }
    307 
    308 /**
    309 * Truncates an array to be at most the length of another array.
    310 *
    311 * @param aArrayToTruncate The array to truncate.
    312 * @param aReferenceArray The array whose length will be used to truncate
    313 *   aArrayToTruncate to.
    314 */
    315 template <typename T, typename U>
    316 static void TruncateTo(nsTArray<T>& aArrayToTruncate,
    317                       const nsTArray<U>& aReferenceArray) {
    318  uint32_t length = aReferenceArray.Length();
    319  if (aArrayToTruncate.Length() > length) {
    320    aArrayToTruncate.TruncateLength(length);
    321  }
    322 }
    323 
    324 /**
    325 * Asserts that the anonymous block child of the SVGTextFrame has been
    326 * reflowed (or does not exist).  Returns null if the child has not been
    327 * reflowed, and the frame otherwise.
    328 *
    329 * We check whether the kid has been reflowed and not the frame itself
    330 * since we sometimes need to call this function during reflow, after the
    331 * kid has been reflowed but before we have cleared the dirty bits on the
    332 * frame itself.
    333 */
    334 static SVGTextFrame* FrameIfAnonymousChildReflowed(SVGTextFrame* aFrame) {
    335  MOZ_ASSERT(aFrame, "aFrame must not be null");
    336  nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
    337  if (kid->IsSubtreeDirty()) {
    338    MOZ_ASSERT(false, "should have already reflowed the anonymous block child");
    339    return nullptr;
    340  }
    341  return aFrame;
    342 }
    343 
    344 // FIXME(emilio): SVG is a special-case where transforms affect layout. We don't
    345 // want that to go outside the SVG stuff (and really we should aim to remove
    346 // that).
    347 static float GetContextScale(SVGTextFrame* aFrame) {
    348  if (aFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    349    // When we are non-display, we could be painted in different coordinate
    350    // spaces, and we don't want to have to reflow for each of these.  We just
    351    // assume that the context scale is 1.0 for them all, so we don't get stuck
    352    // with a font size scale factor based on whichever referencing frame
    353    // happens to reflow first.
    354    return 1.0f;
    355  }
    356  auto matrix = nsLayoutUtils::GetTransformToAncestor(
    357      RelativeTo{aFrame}, RelativeTo{SVGUtils::GetOuterSVGFrame(aFrame)});
    358  Matrix transform2D;
    359  if (!matrix.CanDraw2D(&transform2D)) {
    360    return 1.0f;
    361  }
    362  auto scales = transform2D.ScaleFactors();
    363  return std::max(0.0f, std::max(scales.xScale, scales.yScale));
    364 }
    365 
    366 // ============================================================================
    367 // Utility classes
    368 
    369 // ----------------------------------------------------------------------------
    370 // TextRenderedRun
    371 
    372 /**
    373 * A run of text within a single nsTextFrame whose glyphs can all be painted
    374 * with a single call to nsTextFrame::PaintText.  A text rendered run can
    375 * be created for a sequence of two or more consecutive glyphs as long as:
    376 *
    377 *   - Only the first glyph has (or none of the glyphs have) been positioned
    378 *     with SVG text positioning attributes
    379 *   - All of the glyphs have zero rotation
    380 *   - The glyphs are not on a text path
    381 *   - The glyphs correspond to content within the one nsTextFrame
    382 *
    383 * A TextRenderedRunIterator produces TextRenderedRuns required for painting a
    384 * whole SVGTextFrame.
    385 */
    386 struct TextRenderedRun {
    387  using Range = gfxTextRun::Range;
    388 
    389  /**
    390   * Constructs a TextRenderedRun that is uninitialized except for mFrame
    391   * being null.
    392   */
    393  TextRenderedRun() : mFrame(nullptr) {}
    394 
    395  /**
    396   * Constructs a TextRenderedRun with all of the information required to
    397   * paint it.  See the comments documenting the member variables below
    398   * for descriptions of the arguments.
    399   */
    400  TextRenderedRun(nsTextFrame* aFrame, SVGTextFrame* aSVGTextFrame,
    401                  const gfxPoint& aPosition, double aRotate,
    402                  float aFontSizeScaleFactor, nscoord aBaseline,
    403                  uint32_t aTextFrameContentOffset,
    404                  uint32_t aTextFrameContentLength,
    405                  uint32_t aTextElementCharIndex)
    406      : mFrame(aFrame),
    407        mRoot(aSVGTextFrame),
    408        mPosition(aPosition),
    409        mLengthAdjustScaleFactor(mRoot->mLengthAdjustScaleFactor),
    410        mRotate(static_cast<float>(aRotate)),
    411        mFontSizeScaleFactor(aFontSizeScaleFactor),
    412        mBaseline(aBaseline),
    413        mTextFrameContentOffset(aTextFrameContentOffset),
    414        mTextFrameContentLength(aTextFrameContentLength),
    415        mTextElementCharIndex(aTextElementCharIndex) {}
    416 
    417  /**
    418   * Returns the text run for the text frame that this rendered run is part of.
    419   */
    420  gfxTextRun* GetTextRun() const {
    421    mFrame->EnsureTextRun(nsTextFrame::eInflated);
    422    return mFrame->GetTextRun(nsTextFrame::eInflated);
    423  }
    424 
    425  /**
    426   * Return true if the logical inline direction is reversed compared to
    427   * normal physical coordinates (i.e. if it is leftwards or upwards).
    428   */
    429  bool IsInlineReversed() const { return GetTextRun()->IsInlineReversed(); }
    430 
    431  /**
    432   * Returns whether this rendered run is vertical.
    433   */
    434  bool IsVertical() const { return GetTextRun()->IsVertical(); }
    435 
    436  /**
    437   * Returns the transform that converts from a <text> element's user space into
    438   * the coordinate space that rendered runs can be painted directly in.
    439   *
    440   * The difference between this method and
    441   * GetTransformFromRunUserSpaceToUserSpace is that when calling in to
    442   * nsTextFrame::PaintText, it will already take into account any left clip
    443   * edge (that is, it doesn't just apply a visual clip to the rendered text, it
    444   * shifts the glyphs over so that they are painted with their left edge at the
    445   * x coordinate passed in to it). Thus we need to account for this in our
    446   * transform.
    447   *
    448   *
    449   * Assume that we have:
    450   *
    451   *   <text x="100" y="100" rotate="0 0 1 0 0 * 1">abcdef</text>.
    452   *
    453   * This would result in four text rendered runs:
    454   *
    455   *   - one for "ab"
    456   *   - one for "c"
    457   *   - one for "de"
    458   *   - one for "f"
    459   *
    460   * Assume now that we are painting the third TextRenderedRun.  It will have
    461   * a left clip edge that is the sum of the advances of "abc", and it will
    462   * have a right clip edge that is the advance of "f".  In
    463   * SVGTextFrame::PaintSVG(), we pass in nsPoint() (i.e., the origin)
    464   * as the point at which to paint the text frame, and we pass in the
    465   * clip edge values.  The nsTextFrame will paint the substring of its
    466   * text such that the top-left corner of the "d"'s glyph cell will be at
    467   * (0, 0) in the current coordinate system.
    468   *
    469   * Thus, GetTransformFromUserSpaceForPainting must return a transform from
    470   * whatever user space the <text> element is in to a coordinate space in
    471   * device pixels (as that's what nsTextFrame works in) where the origin is at
    472   * the same position as our user space mPositions[i].mPosition value for
    473   * the "d" glyph, which will be (100 + userSpaceAdvance("abc"), 100).
    474   * The translation required to do this (ignoring the scale to get from
    475   * user space to device pixels, and ignoring the
    476   * (100 + userSpaceAdvance("abc"), 100) translation) is:
    477   *
    478   *   (-leftEdge, -baseline)
    479   *
    480   * where baseline is the distance between the baseline of the text and the top
    481   * edge of the nsTextFrame.  We translate by -leftEdge horizontally because
    482   * the nsTextFrame will already shift the glyphs over by that amount and start
    483   * painting glyphs at x = 0.  We translate by -baseline vertically so that
    484   * painting the top edges of the glyphs at y = 0 will result in their
    485   * baselines being at our desired y position.
    486   *
    487   *
    488   * Now for an example with RTL text.  Assume our content is now
    489   * <text x="100" y="100" rotate="0 0 1 0 0 1">WERBEH</text>.  We'd have
    490   * the following text rendered runs:
    491   *
    492   *   - one for "EH"
    493   *   - one for "B"
    494   *   - one for "ER"
    495   *   - one for "W"
    496   *
    497   * Again, we are painting the third TextRenderedRun.  The left clip edge
    498   * is the advance of the "W" and the right clip edge is the sum of the
    499   * advances of "BEH".  Our translation to get the rendered "ER" glyphs
    500   * in the right place this time is:
    501   *
    502   *   (-frameWidth + rightEdge, -baseline)
    503   *
    504   * which is equivalent to:
    505   *
    506   *   (-(leftEdge + advance("ER")), -baseline)
    507   *
    508   * The reason we have to shift left additionally by the width of the run
    509   * of glyphs we are painting is that although the nsTextFrame is RTL,
    510   * we still supply the top-left corner to paint the frame at when calling
    511   * nsTextFrame::PaintText, even though our user space positions for each
    512   * glyph in mPositions specifies the origin of each glyph, which for RTL
    513   * glyphs is at the right edge of the glyph cell.
    514   *
    515   *
    516   * For any other use of an nsTextFrame in the context of a particular run
    517   * (such as hit testing, or getting its rectangle),
    518   * GetTransformFromRunUserSpaceToUserSpace should be used.
    519   *
    520   * @param aContext The context to use for unit conversions.
    521   */
    522  gfxMatrix GetTransformFromUserSpaceForPainting(
    523      nsPresContext* aContext, const nscoord aVisIStartEdge,
    524      const nscoord aVisIEndEdge) const;
    525 
    526  /**
    527   * Returns the transform that converts from "run user space" to a <text>
    528   * element's user space.  Run user space is a coordinate system that has the
    529   * same size as the <text>'s user space but rotated and translated such that
    530   * (0,0) is the top-left of the rectangle that bounds the text.
    531   *
    532   * @param aContext The context to use for unit conversions.
    533   */
    534  gfxMatrix GetTransformFromRunUserSpaceToUserSpace(
    535      nsPresContext* aContext) const;
    536 
    537  /**
    538   * Returns the transform that converts from "run user space" to float pixels
    539   * relative to the nsTextFrame that this rendered run is a part of.
    540   *
    541   * @param aContext The context to use for unit conversions.
    542   */
    543  gfxMatrix GetTransformFromRunUserSpaceToFrameUserSpace(
    544      nsPresContext* aContext) const;
    545 
    546  /**
    547   * Flag values used for the aFlags arguments of GetRunUserSpaceRect,
    548   * GetFrameUserSpaceRect and GetUserSpaceRect.
    549   */
    550  enum {
    551    // Includes the fill geometry of the text in the returned rectangle.
    552    eIncludeFill = 1,
    553    // Includes the stroke geometry of the text in the returned rectangle.
    554    eIncludeStroke = 2,
    555    // Don't include any horizontal glyph overflow in the returned rectangle.
    556    eNoHorizontalOverflow = 4
    557  };
    558 
    559  /**
    560   * Returns a rectangle that bounds the fill and/or stroke of the rendered run
    561   * in run user space.
    562   *
    563   * @param aFlags A combination of the flags above (eIncludeFill and
    564   *   eIncludeStroke) indicating what parts of the text to include in
    565   *   the rectangle.
    566   */
    567  SVGBBox GetRunUserSpaceRect(uint32_t aFlags) const;
    568 
    569  /**
    570   * Returns a rectangle that covers the fill and/or stroke of the rendered run
    571   * in "frame user space".
    572   *
    573   * Frame user space is a coordinate space of the same scale as the <text>
    574   * element's user space, but with its rotation set to the rotation of
    575   * the glyphs within this rendered run and its origin set to the position
    576   * such that placing the nsTextFrame there would result in the glyphs in
    577   * this rendered run being at their correct positions.
    578   *
    579   * For example, say we have <text x="100 150" y="100">ab</text>.  Assume
    580   * the advance of both the "a" and the "b" is 12 user units, and the
    581   * ascent of the text is 8 user units and its descent is 6 user units,
    582   * and that we are not measuing the stroke of the text, so that we stay
    583   * entirely within the glyph cells.
    584   *
    585   * There will be two text rendered runs, one for "a" and one for "b".
    586   *
    587   * The frame user space for the "a" run will have its origin at
    588   * (100, 100 - 8) in the <text> element's user space and will have its
    589   * axes aligned with the user space (since there is no rotate="" or
    590   * text path involve) and with its scale the same as the user space.
    591   * The rect returned by this method will be (0, 0, 12, 14), since the "a"
    592   * glyph is right at the left of the nsTextFrame.
    593   *
    594   * The frame user space for the "b" run will have its origin at
    595   * (150 - 12, 100 - 8), and scale/rotation the same as above.  The rect
    596   * returned by this method will be (12, 0, 12, 14), since we are
    597   * advance("a") horizontally in to the text frame.
    598   *
    599   * @param aContext The context to use for unit conversions.
    600   * @param aFlags A combination of the flags above (eIncludeFill and
    601   *   eIncludeStroke) indicating what parts of the text to include in
    602   *   the rectangle.
    603   */
    604  SVGBBox GetFrameUserSpaceRect(nsPresContext* aContext, uint32_t aFlags) const;
    605 
    606  /**
    607   * Returns a rectangle that covers the fill and/or stroke of the rendered run
    608   * in the <text> element's user space.
    609   *
    610   * @param aContext The context to use for unit conversions.
    611   * @param aFlags A combination of the flags above indicating what parts of
    612   *   the text to include in the rectangle.
    613   * @param aAdditionalTransform An additional transform to apply to the
    614   *   frame user space rectangle before its bounds are transformed into
    615   *   user space.
    616   */
    617  SVGBBox GetUserSpaceRect(
    618      nsPresContext* aContext, uint32_t aFlags,
    619      const gfxMatrix* aAdditionalTransform = nullptr) const;
    620 
    621  /**
    622   * Gets the app unit amounts to clip from the left and right edges of
    623   * the nsTextFrame in order to paint just this rendered run.
    624   *
    625   * Note that if clip edge amounts land in the middle of a glyph, the
    626   * glyph won't be painted at all.  The clip edges are thus more of
    627   * a selection mechanism for which glyphs will be painted, rather
    628   * than a geometric clip.
    629   */
    630  void GetClipEdges(nscoord& aVisIStartEdge, nscoord& aVisIEndEdge) const;
    631 
    632  /**
    633   * Returns the advance width of the whole rendered run.
    634   */
    635  nscoord GetAdvanceWidth() const;
    636 
    637  /**
    638   * Returns the index of the character into this rendered run whose
    639   * glyph cell contains the given point, or -1 if there is no such
    640   * character.  This does not hit test against any overflow.
    641   *
    642   * @param aContext The context to use for unit conversions.
    643   * @param aPoint The point in the user space of the <text> element.
    644   */
    645  int32_t GetCharNumAtPosition(nsPresContext* aContext,
    646                               const gfxPoint& aPoint) const;
    647 
    648  /**
    649   * The text frame that this rendered run lies within.
    650   */
    651  nsTextFrame* mFrame;
    652 
    653  /**
    654   * The SVGTextFrame to which our text frame belongs.
    655   */
    656  SVGTextFrame* mRoot;
    657 
    658  /**
    659   * The point in user space that the text is positioned at.
    660   *
    661   * For a horizontal run:
    662   * The x coordinate is the left edge of a LTR run of text or the right edge of
    663   * an RTL run.  The y coordinate is the baseline of the text.
    664   * For a vertical run:
    665   * The x coordinate is the baseline of the text.
    666   * The y coordinate is the top edge of a LTR run, or bottom of RTL.
    667   */
    668  gfxPoint mPosition;
    669 
    670  /**
    671   * The horizontal scale factor to apply when painting glyphs to take
    672   * into account textLength="".
    673   */
    674  float mLengthAdjustScaleFactor;
    675 
    676  /**
    677   * The rotation in radians in the user coordinate system that the text has.
    678   */
    679  float mRotate;
    680 
    681  /**
    682   * The scale factor that was used to transform the text run's original font
    683   * size into a sane range for painting and measurement.
    684   */
    685  double mFontSizeScaleFactor;
    686 
    687  /**
    688   * The baseline in app units of this text run.  The measurement is from the
    689   * top of the text frame. (From the left edge if vertical.)
    690   */
    691  nscoord mBaseline;
    692 
    693  /**
    694   * The offset and length in mFrame's content Text that corresponds to
    695   * this text rendered run.  These are original char indexes.
    696   */
    697  uint32_t mTextFrameContentOffset;
    698  uint32_t mTextFrameContentLength;
    699 
    700  /**
    701   * The character index in the whole SVG <text> element that this text rendered
    702   * run begins at.
    703   */
    704  uint32_t mTextElementCharIndex;
    705 };
    706 
    707 gfxMatrix TextRenderedRun::GetTransformFromUserSpaceForPainting(
    708    nsPresContext* aContext, const nscoord aVisIStartEdge,
    709    const nscoord aVisIEndEdge) const {
    710  // We transform to device pixels positioned such that painting the text frame
    711  // at (0,0) with aItem will result in the text being in the right place.
    712 
    713  gfxMatrix m;
    714  if (!mFrame) {
    715    return m;
    716  }
    717 
    718  float cssPxPerDevPx =
    719      nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
    720 
    721  // Glyph position in user space.
    722  m.PreTranslate(mPosition / cssPxPerDevPx);
    723 
    724  // Take into account any font size scaling and scaling due to textLength="".
    725  m.PreScale(1.0 / mFontSizeScaleFactor, 1.0 / mFontSizeScaleFactor);
    726 
    727  // Rotation due to rotate="" or a <textPath>.
    728  m.PreRotate(mRotate);
    729 
    730  // Scale for textLength="" and translate to get the text frame
    731  // to the right place.
    732  nsPoint t;
    733  if (IsVertical()) {
    734    m.PreScale(1.0, mLengthAdjustScaleFactor);
    735    t = nsPoint(-mBaseline, IsInlineReversed()
    736                                ? -mFrame->GetRect().height + aVisIEndEdge
    737                                : -aVisIStartEdge);
    738  } else {
    739    m.PreScale(mLengthAdjustScaleFactor, 1.0);
    740    t = nsPoint(IsInlineReversed() ? -mFrame->GetRect().width + aVisIEndEdge
    741                                   : -aVisIStartEdge,
    742                -mBaseline);
    743  }
    744  m.PreTranslate(AppUnitsToGfxUnits(t, aContext));
    745 
    746  return m;
    747 }
    748 
    749 gfxMatrix TextRenderedRun::GetTransformFromRunUserSpaceToUserSpace(
    750    nsPresContext* aContext) const {
    751  gfxMatrix m;
    752  if (!mFrame) {
    753    return m;
    754  }
    755 
    756  float cssPxPerDevPx =
    757      nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
    758 
    759  nscoord start, end;
    760  GetClipEdges(start, end);
    761 
    762  // Glyph position in user space.
    763  m.PreTranslate(mPosition);
    764 
    765  // Rotation due to rotate="" or a <textPath>.
    766  m.PreRotate(mRotate);
    767 
    768  // Scale for textLength="" and translate to get the text frame
    769  // to the right place.
    770 
    771  nsPoint t;
    772  if (IsVertical()) {
    773    m.PreScale(1.0, mLengthAdjustScaleFactor);
    774    t = nsPoint(-mBaseline, IsInlineReversed()
    775                                ? -mFrame->GetRect().height + start + end
    776                                : 0);
    777  } else {
    778    m.PreScale(mLengthAdjustScaleFactor, 1.0);
    779    t = nsPoint(IsInlineReversed() ? -mFrame->GetRect().width + start + end : 0,
    780                -mBaseline);
    781  }
    782  m.PreTranslate(AppUnitsToGfxUnits(t, aContext) * cssPxPerDevPx /
    783                 mFontSizeScaleFactor);
    784 
    785  return m;
    786 }
    787 
    788 gfxMatrix TextRenderedRun::GetTransformFromRunUserSpaceToFrameUserSpace(
    789    nsPresContext* aContext) const {
    790  gfxMatrix m;
    791  if (!mFrame) {
    792    return m;
    793  }
    794 
    795  nscoord start, end;
    796  GetClipEdges(start, end);
    797 
    798  // Translate by the horizontal distance into the text frame this
    799  // rendered run is.
    800  gfxFloat appPerCssPx = AppUnitsPerCSSPixel();
    801  gfxPoint t = IsVertical() ? gfxPoint(0, start / appPerCssPx)
    802                            : gfxPoint(start / appPerCssPx, 0);
    803  return m.PreTranslate(t);
    804 }
    805 
    806 SVGBBox TextRenderedRun::GetRunUserSpaceRect(uint32_t aFlags) const {
    807  SVGBBox r;
    808  if (!mFrame) {
    809    return r;
    810  }
    811 
    812  // Determine the amount of overflow around frame's mRect.
    813  //
    814  // We need to call InkOverflowRectRelativeToSelf because this includes
    815  // overflowing decorations, which the MeasureText call below does not.
    816  nsRect self = mFrame->InkOverflowRectRelativeToSelf();
    817  nsRect rect = mFrame->GetRect();
    818  bool vertical = IsVertical();
    819  nsMargin inkOverflow(
    820      vertical ? -self.x : -self.y,
    821      vertical ? self.YMost() - rect.height : self.XMost() - rect.width,
    822      vertical ? self.XMost() - rect.width : self.YMost() - rect.height,
    823      vertical ? -self.y : -self.x);
    824 
    825  gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
    826  gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
    827 
    828  // Get the content range for this rendered run.
    829  Range range = ConvertOriginalToSkipped(it, mTextFrameContentOffset,
    830                                         mTextFrameContentLength);
    831  if (range.Length() == 0) {
    832    return r;
    833  }
    834 
    835  auto& provider = mRoot->PropertyProviderFor(mFrame);
    836 
    837  // Measure that range.
    838  gfxTextRun::Metrics metrics = textRun->MeasureText(
    839      range, gfxFont::LOOSE_INK_EXTENTS, nullptr, &provider);
    840  // Make sure it includes the font-box.
    841  gfxRect fontBox(0, -metrics.mAscent, metrics.mAdvanceWidth,
    842                  metrics.mAscent + metrics.mDescent);
    843  metrics.mBoundingBox.UnionRect(metrics.mBoundingBox, fontBox);
    844 
    845  // Determine the rectangle that covers the rendered run's fill,
    846  // taking into account the measured overflow due to decorations.
    847  nscoord baseline =
    848      NSToCoordRoundWithClamp(metrics.mBoundingBox.y + metrics.mAscent);
    849  gfxFloat x, width;
    850  if (aFlags & eNoHorizontalOverflow) {
    851    x = 0.0;
    852    width = textRun->GetAdvanceWidth(range, &provider);
    853    if (width < 0.0) {
    854      x = width;
    855      width = -width;
    856    }
    857  } else {
    858    x = metrics.mBoundingBox.x;
    859    width = metrics.mBoundingBox.width;
    860  }
    861  nsRect fillInAppUnits(NSToCoordRoundWithClamp(x), baseline,
    862                        NSToCoordRoundWithClamp(width),
    863                        NSToCoordRoundWithClamp(metrics.mBoundingBox.height));
    864  fillInAppUnits.Inflate(inkOverflow);
    865  if (textRun->IsVertical()) {
    866    // Swap line-relative textMetrics dimensions to physical coordinates.
    867    std::swap(fillInAppUnits.x, fillInAppUnits.y);
    868    std::swap(fillInAppUnits.width, fillInAppUnits.height);
    869  }
    870 
    871  // Convert the app units rectangle to user units.
    872  gfxRect fill = AppUnitsToFloatCSSPixels(fillInAppUnits);
    873 
    874  // Scale the rectangle up due to any mFontSizeScaleFactor.
    875  fill.Scale(1.0 / mFontSizeScaleFactor);
    876 
    877  // Include the fill if requested.
    878  if (aFlags & eIncludeFill) {
    879    r = fill;
    880  }
    881 
    882  // Include the stroke if requested.
    883  if ((aFlags & eIncludeStroke) && !fill.IsEmpty() &&
    884      SVGUtils::GetStrokeWidth(mFrame) > 0) {
    885    r.UnionEdges(
    886        SVGUtils::PathExtentsToMaxStrokeExtents(fill, mFrame, gfxMatrix()));
    887  }
    888 
    889  return r;
    890 }
    891 
    892 SVGBBox TextRenderedRun::GetFrameUserSpaceRect(nsPresContext* aContext,
    893                                               uint32_t aFlags) const {
    894  SVGBBox r = GetRunUserSpaceRect(aFlags);
    895  if (r.IsEmpty()) {
    896    return r;
    897  }
    898  gfxMatrix m = GetTransformFromRunUserSpaceToFrameUserSpace(aContext);
    899  return m.TransformBounds(r.ToThebesRect());
    900 }
    901 
    902 SVGBBox TextRenderedRun::GetUserSpaceRect(
    903    nsPresContext* aContext, uint32_t aFlags,
    904    const gfxMatrix* aAdditionalTransform) const {
    905  SVGBBox r = GetRunUserSpaceRect(aFlags);
    906  if (r.IsEmpty()) {
    907    return r;
    908  }
    909  gfxMatrix m = GetTransformFromRunUserSpaceToUserSpace(aContext);
    910  if (aAdditionalTransform) {
    911    m *= *aAdditionalTransform;
    912  }
    913  return m.TransformBounds(r.ToThebesRect());
    914 }
    915 
    916 void TextRenderedRun::GetClipEdges(nscoord& aVisIStartEdge,
    917                                   nscoord& aVisIEndEdge) const {
    918  uint32_t contentLength = mFrame->GetContentLength();
    919  if (mTextFrameContentOffset == 0 &&
    920      mTextFrameContentLength == contentLength) {
    921    // If the rendered run covers the entire content, we know we don't need
    922    // to clip without having to measure anything.
    923    aVisIStartEdge = 0;
    924    aVisIEndEdge = 0;
    925    return;
    926  }
    927 
    928  gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
    929  gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
    930  auto& provider = mRoot->PropertyProviderFor(mFrame);
    931 
    932  // Get the covered content offset/length for this rendered run in skipped
    933  // characters, since that is what GetAdvanceWidth expects.
    934  Range runRange = ConvertOriginalToSkipped(it, mTextFrameContentOffset,
    935                                            mTextFrameContentLength);
    936 
    937  // Get the offset/length of the whole nsTextFrame.
    938  uint32_t frameOffset = mFrame->GetContentOffset();
    939  uint32_t frameLength = mFrame->GetContentLength();
    940 
    941  // Trim the whole-nsTextFrame offset/length to remove any leading/trailing
    942  // white space, as the nsTextFrame when painting does not include them when
    943  // interpreting clip edges.
    944  nsTextFrame::TrimmedOffsets trimmedOffsets =
    945      mFrame->GetTrimmedOffsets(mFrame->CharacterDataBuffer());
    946  TrimOffsets(frameOffset, frameLength, trimmedOffsets);
    947 
    948  // Convert the trimmed whole-nsTextFrame offset/length into skipped
    949  // characters.
    950  Range frameRange = ConvertOriginalToSkipped(it, frameOffset, frameLength);
    951 
    952  // Get the advance of aRange, using the aCachedRange if available to
    953  // accelerate textrun measurement.
    954  auto MeasureUsingCache = [&](SVGTextFrame::CachedMeasuredRange& aCachedRange,
    955                               const Range& aRange) -> nscoord {
    956    if (aRange.Intersects(aCachedRange.mRange)) {
    957      // Figure out the deltas between the cached range and the new one at the
    958      // start and end edges.
    959      Range startDelta, endDelta;
    960      int startSign = 0, endSign = 0;
    961      if (aRange.start < aCachedRange.mRange.start) {
    962        // This range extends the cached range at the start.
    963        startSign = 1;
    964        startDelta = Range(aRange.start, aCachedRange.mRange.start);
    965      } else if (aRange.start > aCachedRange.mRange.start) {
    966        // This range trims the cached range at the start.
    967        startSign = -1;
    968        startDelta = Range(aCachedRange.mRange.start, aRange.start);
    969      }
    970      if (aRange.end > aCachedRange.mRange.end) {
    971        // This range extends the cached range at the end.
    972        endSign = 1;
    973        endDelta = Range(aCachedRange.mRange.end, aRange.end);
    974      } else if (aRange.end < aCachedRange.mRange.end) {
    975        // This range trims the cached range at the end.
    976        endSign = -1;
    977        endDelta = Range(aRange.end, aCachedRange.mRange.end);
    978      }
    979      // If the total of the deltas is less than the length of aRange,
    980      // it will be cheaper to measure them and adjust the cached advance
    981      // instead of measuring the whole of aRange.
    982      if (startDelta.Length() + endDelta.Length() < aRange.Length()) {
    983        if (startSign) {
    984          aCachedRange.mAdvance +=
    985              startSign * textRun->GetAdvanceWidth(startDelta, &provider);
    986        }
    987        if (endSign) {
    988          aCachedRange.mAdvance +=
    989              endSign * textRun->GetAdvanceWidth(endDelta, &provider);
    990        }
    991      } else {
    992        aCachedRange.mAdvance = textRun->GetAdvanceWidth(aRange, &provider);
    993      }
    994    } else {
    995      // Just measure the range, and cache the result.
    996      aCachedRange.mAdvance = textRun->GetAdvanceWidth(aRange, &provider);
    997    }
    998    aCachedRange.mRange = aRange;
    999    return aCachedRange.mAdvance;
   1000  };
   1001 
   1002  mRoot->SetCurrentFrameForCaching(mFrame);
   1003  nscoord startEdge =
   1004      MeasureUsingCache(mRoot->CachedRange(SVGTextFrame::WhichRange::Before),
   1005                        Range(frameRange.start, runRange.start));
   1006  nscoord endEdge =
   1007      MeasureUsingCache(mRoot->CachedRange(SVGTextFrame::WhichRange::After),
   1008                        Range(runRange.end, frameRange.end));
   1009 
   1010  if (textRun->IsInlineReversed()) {
   1011    aVisIStartEdge = endEdge;
   1012    aVisIEndEdge = startEdge;
   1013  } else {
   1014    aVisIStartEdge = startEdge;
   1015    aVisIEndEdge = endEdge;
   1016  }
   1017 }
   1018 
   1019 nscoord TextRenderedRun::GetAdvanceWidth() const {
   1020  gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
   1021  gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
   1022  auto& provider = mRoot->PropertyProviderFor(mFrame);
   1023 
   1024  Range range = ConvertOriginalToSkipped(it, mTextFrameContentOffset,
   1025                                         mTextFrameContentLength);
   1026 
   1027  return textRun->GetAdvanceWidth(range, &provider);
   1028 }
   1029 
   1030 int32_t TextRenderedRun::GetCharNumAtPosition(nsPresContext* aContext,
   1031                                              const gfxPoint& aPoint) const {
   1032  if (mTextFrameContentLength == 0) {
   1033    return -1;
   1034  }
   1035 
   1036  float cssPxPerDevPx =
   1037      nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
   1038 
   1039  // Convert the point from user space into run user space, and take
   1040  // into account any mFontSizeScaleFactor.
   1041  gfxMatrix m = GetTransformFromRunUserSpaceToUserSpace(aContext);
   1042  if (!m.Invert()) {
   1043    return -1;
   1044  }
   1045  gfxPoint p = m.TransformPoint(aPoint) / cssPxPerDevPx * mFontSizeScaleFactor;
   1046 
   1047  // First check that the point lies vertically between the top and bottom
   1048  // edges of the text.
   1049  gfxFloat ascent, descent;
   1050  GetAscentAndDescentInAppUnits(mFrame, ascent, descent);
   1051 
   1052  WritingMode writingMode = mFrame->GetWritingMode();
   1053  if (writingMode.IsVertical()) {
   1054    gfxFloat leftEdge = mFrame->GetLogicalBaseline(writingMode) -
   1055                        (writingMode.IsVerticalRL() ? ascent : descent);
   1056    gfxFloat rightEdge = leftEdge + ascent + descent;
   1057    if (p.x < aContext->AppUnitsToGfxUnits(leftEdge) ||
   1058        p.x > aContext->AppUnitsToGfxUnits(rightEdge)) {
   1059      return -1;
   1060    }
   1061  } else {
   1062    gfxFloat topEdge = mFrame->GetLogicalBaseline(writingMode) - ascent;
   1063    gfxFloat bottomEdge = topEdge + ascent + descent;
   1064    if (p.y < aContext->AppUnitsToGfxUnits(topEdge) ||
   1065        p.y > aContext->AppUnitsToGfxUnits(bottomEdge)) {
   1066      return -1;
   1067    }
   1068  }
   1069 
   1070  gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
   1071  gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
   1072  auto& provider = mRoot->PropertyProviderFor(mFrame);
   1073 
   1074  // Next check that the point lies horizontally within the left and right
   1075  // edges of the text.
   1076  Range range = ConvertOriginalToSkipped(it, mTextFrameContentOffset,
   1077                                         mTextFrameContentLength);
   1078  gfxFloat runAdvance =
   1079      aContext->AppUnitsToGfxUnits(textRun->GetAdvanceWidth(range, &provider));
   1080 
   1081  gfxFloat pos = writingMode.IsVertical() ? p.y : p.x;
   1082  if (pos < 0 || pos >= runAdvance) {
   1083    return -1;
   1084  }
   1085 
   1086  // Finally, measure progressively smaller portions of the rendered run to
   1087  // find which glyph it lies within.  This will need to change once we
   1088  // support letter-spacing and word-spacing.
   1089  bool ir = textRun->IsInlineReversed();
   1090  for (int32_t i = mTextFrameContentLength - 1; i >= 0; i--) {
   1091    range = ConvertOriginalToSkipped(it, mTextFrameContentOffset, i);
   1092    gfxFloat advance = aContext->AppUnitsToGfxUnits(
   1093        textRun->GetAdvanceWidth(range, &provider));
   1094    if ((ir && pos < runAdvance - advance) || (!ir && pos >= advance)) {
   1095      return i;
   1096    }
   1097  }
   1098  return -1;
   1099 }
   1100 
   1101 // ----------------------------------------------------------------------------
   1102 // TextNodeIterator
   1103 
   1104 enum SubtreePosition { eBeforeSubtree, eWithinSubtree, eAfterSubtree };
   1105 
   1106 /**
   1107 * An iterator class for Text that are descendants of a given node, the
   1108 * root.  Nodes are iterated in document order.  An optional subtree can be
   1109 * specified, in which case the iterator will track whether the current state of
   1110 * the traversal over the tree is within that subtree or is past that subtree.
   1111 */
   1112 class TextNodeIterator {
   1113 public:
   1114  /**
   1115   * Constructs a TextNodeIterator with the specified root node and optional
   1116   * subtree.
   1117   */
   1118  explicit TextNodeIterator(nsIContent* aRoot, nsIContent* aSubtree = nullptr)
   1119      : mRoot(aRoot),
   1120        mSubtree(aSubtree == aRoot ? nullptr : aSubtree),
   1121        mCurrent(aRoot),
   1122        mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree) {
   1123    NS_ASSERTION(aRoot, "expected non-null root");
   1124    if (!aRoot->IsText()) {
   1125      GetNext();
   1126    }
   1127  }
   1128 
   1129  /**
   1130   * Returns the current Text, or null if the iterator has finished.
   1131   */
   1132  Text* GetCurrent() const { return mCurrent ? mCurrent->AsText() : nullptr; }
   1133 
   1134  /**
   1135   * Advances to the next Text and returns it, or null if the end of
   1136   * iteration has been reached.
   1137   */
   1138  Text* GetNext();
   1139 
   1140  /**
   1141   * Returns whether the iterator is currently within the subtree rooted
   1142   * at mSubtree.  Returns true if we are not tracking a subtree (we consider
   1143   * that we're always within the subtree).
   1144   */
   1145  bool IsWithinSubtree() const { return mSubtreePosition == eWithinSubtree; }
   1146 
   1147  /**
   1148   * Returns whether the iterator is past the subtree rooted at mSubtree.
   1149   * Returns false if we are not tracking a subtree.
   1150   */
   1151  bool IsAfterSubtree() const { return mSubtreePosition == eAfterSubtree; }
   1152 
   1153 private:
   1154  /**
   1155   * The root under which all Text will be iterated over.
   1156   */
   1157  nsIContent* const mRoot;
   1158 
   1159  /**
   1160   * The node rooting the subtree to track.
   1161   */
   1162  nsIContent* const mSubtree;
   1163 
   1164  /**
   1165   * The current node during iteration.
   1166   */
   1167  nsIContent* mCurrent;
   1168 
   1169  /**
   1170   * The current iterator position relative to mSubtree.
   1171   */
   1172  SubtreePosition mSubtreePosition;
   1173 };
   1174 
   1175 Text* TextNodeIterator::GetNext() {
   1176  // Starting from mCurrent, we do a non-recursive traversal to the next
   1177  // Text beneath mRoot, updating mSubtreePosition appropriately if we
   1178  // encounter mSubtree.
   1179  if (mCurrent) {
   1180    do {
   1181      nsIContent* next =
   1182          IsTextContentElement(mCurrent) ? mCurrent->GetFirstChild() : nullptr;
   1183      if (next) {
   1184        mCurrent = next;
   1185        if (mCurrent == mSubtree) {
   1186          mSubtreePosition = eWithinSubtree;
   1187        }
   1188      } else {
   1189        for (;;) {
   1190          if (mCurrent == mRoot) {
   1191            mCurrent = nullptr;
   1192            break;
   1193          }
   1194          if (mCurrent == mSubtree) {
   1195            mSubtreePosition = eAfterSubtree;
   1196          }
   1197          next = mCurrent->GetNextSibling();
   1198          if (next) {
   1199            mCurrent = next;
   1200            if (mCurrent == mSubtree) {
   1201              mSubtreePosition = eWithinSubtree;
   1202            }
   1203            break;
   1204          }
   1205          if (mCurrent == mSubtree) {
   1206            mSubtreePosition = eAfterSubtree;
   1207          }
   1208          mCurrent = mCurrent->GetParent();
   1209        }
   1210      }
   1211    } while (mCurrent && !mCurrent->IsText());
   1212  }
   1213 
   1214  return mCurrent ? mCurrent->AsText() : nullptr;
   1215 }
   1216 
   1217 // ----------------------------------------------------------------------------
   1218 // TextNodeCorrespondenceRecorder
   1219 
   1220 /**
   1221 * TextNodeCorrespondence is used as the value of a frame property that
   1222 * is stored on all its descendant nsTextFrames.  It stores the number of DOM
   1223 * characters between it and the previous nsTextFrame that did not have an
   1224 * nsTextFrame created for them, due to either not being in a correctly
   1225 * parented text content element, or because they were display:none.
   1226 * These are called "undisplayed characters".
   1227 *
   1228 * See also TextNodeCorrespondenceRecorder below, which is what sets the
   1229 * frame property.
   1230 */
   1231 struct TextNodeCorrespondence {
   1232  explicit TextNodeCorrespondence(uint32_t aUndisplayedCharacters)
   1233      : mUndisplayedCharacters(aUndisplayedCharacters) {}
   1234 
   1235  uint32_t mUndisplayedCharacters;
   1236 };
   1237 
   1238 NS_DECLARE_FRAME_PROPERTY_DELETABLE(TextNodeCorrespondenceProperty,
   1239                                    TextNodeCorrespondence)
   1240 
   1241 /**
   1242 * Returns the number of undisplayed characters before the specified
   1243 * nsTextFrame.
   1244 */
   1245 static uint32_t GetUndisplayedCharactersBeforeFrame(nsTextFrame* aFrame) {
   1246  void* value = aFrame->GetProperty(TextNodeCorrespondenceProperty());
   1247  TextNodeCorrespondence* correspondence =
   1248      static_cast<TextNodeCorrespondence*>(value);
   1249  if (!correspondence) {
   1250    // FIXME bug 903785
   1251    NS_ERROR(
   1252        "expected a TextNodeCorrespondenceProperty on nsTextFrame "
   1253        "used for SVG text");
   1254    return 0;
   1255  }
   1256  return correspondence->mUndisplayedCharacters;
   1257 }
   1258 
   1259 /**
   1260 * Traverses the nsTextFrames for an SVGTextFrame and records a
   1261 * TextNodeCorrespondenceProperty on each for the number of undisplayed DOM
   1262 * characters between each frame.  This is done by iterating simultaneously
   1263 * over the Text and nsTextFrames and noting when Text (or
   1264 * parts of them) are skipped when finding the next nsTextFrame.
   1265 */
   1266 class TextNodeCorrespondenceRecorder {
   1267 public:
   1268  /**
   1269   * Entry point for the TextNodeCorrespondenceProperty recording.
   1270   */
   1271  static void RecordCorrespondence(SVGTextFrame* aRoot);
   1272 
   1273 private:
   1274  explicit TextNodeCorrespondenceRecorder(SVGTextFrame* aRoot)
   1275      : mNodeIterator(aRoot->GetContent()),
   1276        mPreviousNode(nullptr),
   1277        mNodeCharIndex(0) {}
   1278 
   1279  void Record(SVGTextFrame* aRoot);
   1280  void TraverseAndRecord(nsIFrame* aFrame);
   1281 
   1282  /**
   1283   * Returns the next non-empty Text.
   1284   */
   1285  Text* NextNode();
   1286 
   1287  /**
   1288   * The iterator over the Text that we use as we simultaneously
   1289   * iterate over the nsTextFrames.
   1290   */
   1291  TextNodeIterator mNodeIterator;
   1292 
   1293  /**
   1294   * The previous Text we iterated over.
   1295   */
   1296  Text* mPreviousNode;
   1297 
   1298  /**
   1299   * The index into the current Text's character content.
   1300   */
   1301  uint32_t mNodeCharIndex;
   1302 };
   1303 
   1304 /* static */
   1305 void TextNodeCorrespondenceRecorder::RecordCorrespondence(SVGTextFrame* aRoot) {
   1306  if (aRoot->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)) {
   1307    // Resolve bidi so that continuation frames are created if necessary:
   1308    aRoot->MaybeResolveBidiForAnonymousBlockChild();
   1309    TextNodeCorrespondenceRecorder recorder(aRoot);
   1310    recorder.Record(aRoot);
   1311    aRoot->RemoveStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY);
   1312  }
   1313 }
   1314 
   1315 void TextNodeCorrespondenceRecorder::Record(SVGTextFrame* aRoot) {
   1316  if (!mNodeIterator.GetCurrent()) {
   1317    // If there are no Text nodes then there is nothing to do.
   1318    return;
   1319  }
   1320 
   1321  // Traverse over all the nsTextFrames and record the number of undisplayed
   1322  // characters.
   1323  TraverseAndRecord(aRoot);
   1324 
   1325  // Find how many undisplayed characters there are after the final nsTextFrame.
   1326  uint32_t undisplayed = 0;
   1327  if (mNodeIterator.GetCurrent()) {
   1328    if (mPreviousNode && mPreviousNode->TextLength() != mNodeCharIndex) {
   1329      // The last nsTextFrame ended part way through a Text node.  The
   1330      // remaining characters count as undisplayed.
   1331      NS_ASSERTION(mNodeCharIndex < mPreviousNode->TextLength(),
   1332                   "incorrect tracking of undisplayed characters in "
   1333                   "text nodes");
   1334      undisplayed += mPreviousNode->TextLength() - mNodeCharIndex;
   1335    }
   1336    // All the remaining Text that we iterate must also be undisplayed.
   1337    for (Text* textNode = mNodeIterator.GetCurrent(); textNode;
   1338         textNode = NextNode()) {
   1339      undisplayed += textNode->TextLength();
   1340    }
   1341  }
   1342 
   1343  // Record the trailing number of undisplayed characters on the
   1344  // SVGTextFrame.
   1345  aRoot->mTrailingUndisplayedCharacters = undisplayed;
   1346 }
   1347 
   1348 Text* TextNodeCorrespondenceRecorder::NextNode() {
   1349  mPreviousNode = mNodeIterator.GetCurrent();
   1350  Text* next;
   1351  do {
   1352    next = mNodeIterator.GetNext();
   1353  } while (next && next->TextLength() == 0);
   1354  return next;
   1355 }
   1356 
   1357 void TextNodeCorrespondenceRecorder::TraverseAndRecord(nsIFrame* aFrame) {
   1358  // Recursively iterate over the frame tree, for frames that correspond
   1359  // to text content elements.
   1360  if (IsTextContentElement(aFrame->GetContent())) {
   1361    for (nsIFrame* f : aFrame->PrincipalChildList()) {
   1362      TraverseAndRecord(f);
   1363    }
   1364    return;
   1365  }
   1366 
   1367  nsTextFrame* frame;  // The current text frame.
   1368  Text* node;          // The text node for the current text frame.
   1369  if (!GetNonEmptyTextFrameAndNode(aFrame, frame, node)) {
   1370    // If this isn't an nsTextFrame, or is empty, nothing to do.
   1371    return;
   1372  }
   1373 
   1374  NS_ASSERTION(frame->GetContentOffset() >= 0,
   1375               "don't know how to handle negative content indexes");
   1376 
   1377  uint32_t undisplayed = 0;
   1378  if (!mPreviousNode) {
   1379    // Must be the very first text frame.
   1380    NS_ASSERTION(mNodeCharIndex == 0,
   1381                 "incorrect tracking of undisplayed "
   1382                 "characters in text nodes");
   1383    if (!mNodeIterator.GetCurrent()) {
   1384      MOZ_ASSERT_UNREACHABLE(
   1385          "incorrect tracking of correspondence between "
   1386          "text frames and text nodes");
   1387    } else {
   1388      // Each whole Text we find before we get to the text node for the
   1389      // first text frame must be undisplayed.
   1390      while (mNodeIterator.GetCurrent() != node) {
   1391        undisplayed += mNodeIterator.GetCurrent()->TextLength();
   1392        NextNode();
   1393      }
   1394      // If the first text frame starts at a non-zero content offset, then those
   1395      // earlier characters are also undisplayed.
   1396      undisplayed += frame->GetContentOffset();
   1397      NextNode();
   1398    }
   1399  } else if (mPreviousNode == node) {
   1400    // Same text node as last time.
   1401    if (static_cast<uint32_t>(frame->GetContentOffset()) != mNodeCharIndex) {
   1402      // We have some characters in the middle of the text node
   1403      // that are undisplayed.
   1404      NS_ASSERTION(
   1405          mNodeCharIndex < static_cast<uint32_t>(frame->GetContentOffset()),
   1406          "incorrect tracking of undisplayed characters in "
   1407          "text nodes");
   1408      undisplayed = frame->GetContentOffset() - mNodeCharIndex;
   1409    }
   1410  } else {
   1411    // Different text node from last time.
   1412    if (mPreviousNode->TextLength() != mNodeCharIndex) {
   1413      NS_ASSERTION(mNodeCharIndex < mPreviousNode->TextLength(),
   1414                   "incorrect tracking of undisplayed characters in "
   1415                   "text nodes");
   1416      // Any trailing characters at the end of the previous Text are
   1417      // undisplayed.
   1418      undisplayed = mPreviousNode->TextLength() - mNodeCharIndex;
   1419    }
   1420    // Each whole Text we find before we get to the text node for
   1421    // the current text frame must be undisplayed.
   1422    while (mNodeIterator.GetCurrent() && mNodeIterator.GetCurrent() != node) {
   1423      undisplayed += mNodeIterator.GetCurrent()->TextLength();
   1424      NextNode();
   1425    }
   1426    // If the current text frame starts at a non-zero content offset, then those
   1427    // earlier characters are also undisplayed.
   1428    undisplayed += frame->GetContentOffset();
   1429    NextNode();
   1430  }
   1431 
   1432  // Set the frame property.
   1433  frame->SetProperty(TextNodeCorrespondenceProperty(),
   1434                     new TextNodeCorrespondence(undisplayed));
   1435 
   1436  // Remember how far into the current Text we are.
   1437  mNodeCharIndex = frame->GetContentEnd();
   1438 }
   1439 
   1440 // ----------------------------------------------------------------------------
   1441 // TextFrameIterator
   1442 
   1443 /**
   1444 * An iterator class for nsTextFrames that are descendants of an
   1445 * SVGTextFrame.  The iterator can optionally track whether the
   1446 * current nsTextFrame is for a descendant of, or past, a given subtree
   1447 * content node or frame.  (This functionality is used for example by the SVG
   1448 * DOM text methods to get only the nsTextFrames for a particular <tspan>.)
   1449 *
   1450 * TextFrameIterator also tracks and exposes other information about the
   1451 * current nsTextFrame:
   1452 *
   1453 *   * how many undisplayed characters came just before it
   1454 *   * its position (in app units) relative to the SVGTextFrame's anonymous
   1455 *     block frame
   1456 *   * what nsInlineFrame corresponding to a <textPath> element it is a
   1457 *     descendant of
   1458 *   * what computed dominant-baseline value applies to it
   1459 *
   1460 * Note that any text frames that are empty -- whose ContentLength() is 0 --
   1461 * will be skipped over.
   1462 */
   1463 class MOZ_STACK_CLASS TextFrameIterator {
   1464 public:
   1465  /**
   1466   * Constructs a TextFrameIterator for the specified SVGTextFrame
   1467   * with an optional frame subtree to restrict iterated text frames to.
   1468   */
   1469  explicit TextFrameIterator(SVGTextFrame* aRoot,
   1470                             const nsIFrame* aSubtree = nullptr)
   1471      : mRootFrame(aRoot),
   1472        mSubtree(aSubtree),
   1473        mCurrentFrame(aRoot),
   1474        mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree) {
   1475    Init();
   1476  }
   1477 
   1478  /**
   1479   * Constructs a TextFrameIterator for the specified SVGTextFrame
   1480   * with an optional frame content subtree to restrict iterated text frames to.
   1481   */
   1482  TextFrameIterator(SVGTextFrame* aRoot, nsIContent* aSubtree)
   1483      : mRootFrame(aRoot),
   1484        mSubtree(aRoot && aSubtree && aSubtree != aRoot->GetContent()
   1485                     ? aSubtree->GetPrimaryFrame()
   1486                     : nullptr),
   1487        mCurrentFrame(aRoot),
   1488        mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree) {
   1489    Init();
   1490  }
   1491 
   1492  /**
   1493   * Returns the root SVGTextFrame this TextFrameIterator is iterating over.
   1494   * (May be nullptr, if nullptr was passed to our constructor.)
   1495   */
   1496  SVGTextFrame* GetRoot() const { return mRootFrame; }
   1497 
   1498  /**
   1499   * Returns the current nsTextFrame, or null if the iterator has finished.
   1500   */
   1501  nsTextFrame* GetCurrent() const { return do_QueryFrame(mCurrentFrame); }
   1502 
   1503  /**
   1504   * Returns the number of undisplayed characters in the DOM just before the
   1505   * current frame.
   1506   */
   1507  uint32_t UndisplayedCharacters() const;
   1508 
   1509  /**
   1510   * Returns the current frame's position, in app units, relative to the
   1511   * root SVGTextFrame's anonymous block frame.
   1512   */
   1513  nsPoint Position() const { return mCurrentPosition; }
   1514 
   1515  /**
   1516   * Advances to the next nsTextFrame and returns it, or null if the end of
   1517   * iteration has been reached.
   1518   */
   1519  nsTextFrame* GetNext();
   1520 
   1521  /**
   1522   * Returns whether the iterator is within the subtree.
   1523   */
   1524  bool IsWithinSubtree() const { return mSubtreePosition == eWithinSubtree; }
   1525 
   1526  /**
   1527   * Returns whether the iterator is past the subtree.
   1528   */
   1529  bool IsAfterSubtree() const { return mSubtreePosition == eAfterSubtree; }
   1530 
   1531  /**
   1532   * Returns the frame corresponding to the <textPath> element, if we
   1533   * are inside one.
   1534   */
   1535  nsIFrame* TextPathFrame() const {
   1536    return mTextPathFrames.IsEmpty() ? nullptr : mTextPathFrames.LastElement();
   1537  }
   1538 
   1539  /**
   1540   * Returns the current frame's computed dominant-baseline value.
   1541   */
   1542  StyleDominantBaseline DominantBaseline() const {
   1543    return mBaselines.LastElement();
   1544  }
   1545 
   1546  /**
   1547   * Finishes the iterator.
   1548   */
   1549  void Close() { mCurrentFrame = nullptr; }
   1550 
   1551 private:
   1552  /**
   1553   * Initializes the iterator and advances to the first item.
   1554   */
   1555  void Init() {
   1556    if (!mRootFrame) {
   1557      return;
   1558    }
   1559 
   1560    mBaselines.AppendElement(mRootFrame->StyleSVG()->mDominantBaseline);
   1561    GetNext();
   1562  }
   1563 
   1564  /**
   1565   * Pushes the specified frame's computed dominant-baseline value.
   1566   * If the value of the property is "auto", then the parent frame's
   1567   * computed value is used.
   1568   */
   1569  void PushBaseline(nsIFrame* aNextFrame);
   1570 
   1571  /**
   1572   * Pops the current dominant-baseline off the stack.
   1573   */
   1574  void PopBaseline();
   1575 
   1576  /**
   1577   * The root frame we are iterating through.
   1578   */
   1579  SVGTextFrame* const mRootFrame;
   1580 
   1581  /**
   1582   * The frame for the subtree we are also interested in tracking.
   1583   */
   1584  const nsIFrame* const mSubtree;
   1585 
   1586  /**
   1587   * The current value of the iterator.
   1588   */
   1589  nsIFrame* mCurrentFrame;
   1590 
   1591  /**
   1592   * The position, in app units, of the current frame relative to mRootFrame.
   1593   */
   1594  nsPoint mCurrentPosition;
   1595 
   1596  /**
   1597   * Stack of frames corresponding to <textPath> elements that are in scope
   1598   * for the current frame.
   1599   */
   1600  AutoTArray<nsIFrame*, 1> mTextPathFrames;
   1601 
   1602  /**
   1603   * Stack of dominant-baseline values to record as we traverse through the
   1604   * frame tree.
   1605   */
   1606  AutoTArray<StyleDominantBaseline, 8> mBaselines;
   1607 
   1608  /**
   1609   * The iterator's current position relative to mSubtree.
   1610   */
   1611  SubtreePosition mSubtreePosition;
   1612 };
   1613 
   1614 uint32_t TextFrameIterator::UndisplayedCharacters() const {
   1615  MOZ_ASSERT(
   1616      !mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY),
   1617      "Text correspondence must be up to date");
   1618 
   1619  if (!mCurrentFrame) {
   1620    return mRootFrame->mTrailingUndisplayedCharacters;
   1621  }
   1622 
   1623  nsTextFrame* frame = do_QueryFrame(mCurrentFrame);
   1624  return GetUndisplayedCharactersBeforeFrame(frame);
   1625 }
   1626 
   1627 nsTextFrame* TextFrameIterator::GetNext() {
   1628  // Starting from mCurrentFrame, we do a non-recursive traversal to the next
   1629  // nsTextFrame beneath mRoot, updating mSubtreePosition appropriately if we
   1630  // encounter mSubtree.
   1631  if (mCurrentFrame) {
   1632    do {
   1633      nsIFrame* next = IsTextContentElement(mCurrentFrame->GetContent())
   1634                           ? mCurrentFrame->PrincipalChildList().FirstChild()
   1635                           : nullptr;
   1636      if (next) {
   1637        // Descend into this frame, and accumulate its position.
   1638        mCurrentPosition += next->GetPosition();
   1639        if (next->GetContent()->IsSVGElement(nsGkAtoms::textPath)) {
   1640          // Record this <textPath> frame.
   1641          mTextPathFrames.AppendElement(next);
   1642        }
   1643        // Record the frame's baseline.
   1644        PushBaseline(next);
   1645        mCurrentFrame = next;
   1646        if (mCurrentFrame == mSubtree) {
   1647          // If the current frame is mSubtree, we have now moved into it.
   1648          mSubtreePosition = eWithinSubtree;
   1649        }
   1650      } else {
   1651        for (;;) {
   1652          // We want to move past the current frame.
   1653          if (mCurrentFrame == mRootFrame) {
   1654            // If we've reached the root frame, we're finished.
   1655            mCurrentFrame = nullptr;
   1656            break;
   1657          }
   1658          // Remove the current frame's position.
   1659          mCurrentPosition -= mCurrentFrame->GetPosition();
   1660          if (mCurrentFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath)) {
   1661            // Pop off the <textPath> frame if this is a <textPath>.
   1662            mTextPathFrames.RemoveLastElement();
   1663          }
   1664          // Pop off the current baseline.
   1665          PopBaseline();
   1666          if (mCurrentFrame == mSubtree) {
   1667            // If this was mSubtree, we have now moved past it.
   1668            mSubtreePosition = eAfterSubtree;
   1669          }
   1670          next = mCurrentFrame->GetNextSibling();
   1671          if (next) {
   1672            // Moving to the next sibling.
   1673            mCurrentPosition += next->GetPosition();
   1674            if (next->GetContent()->IsSVGElement(nsGkAtoms::textPath)) {
   1675              // Record this <textPath> frame.
   1676              mTextPathFrames.AppendElement(next);
   1677            }
   1678            // Record the frame's baseline.
   1679            PushBaseline(next);
   1680            mCurrentFrame = next;
   1681            if (mCurrentFrame == mSubtree) {
   1682              // If the current frame is mSubtree, we have now moved into it.
   1683              mSubtreePosition = eWithinSubtree;
   1684            }
   1685            break;
   1686          }
   1687          if (mCurrentFrame == mSubtree) {
   1688            // If there is no next sibling frame, and the current frame is
   1689            // mSubtree, we have now moved past it.
   1690            mSubtreePosition = eAfterSubtree;
   1691          }
   1692          // Ascend out of this frame.
   1693          mCurrentFrame = mCurrentFrame->GetParent();
   1694        }
   1695      }
   1696    } while (mCurrentFrame && !IsNonEmptyTextFrame(mCurrentFrame));
   1697  }
   1698 
   1699  return GetCurrent();
   1700 }
   1701 
   1702 void TextFrameIterator::PushBaseline(nsIFrame* aNextFrame) {
   1703  StyleDominantBaseline baseline = aNextFrame->StyleSVG()->mDominantBaseline;
   1704  mBaselines.AppendElement(baseline);
   1705 }
   1706 
   1707 void TextFrameIterator::PopBaseline() {
   1708  NS_ASSERTION(!mBaselines.IsEmpty(), "popped too many baselines");
   1709  mBaselines.RemoveLastElement();
   1710 }
   1711 
   1712 // -----------------------------------------------------------------------------
   1713 // TextRenderedRunIterator
   1714 
   1715 /**
   1716 * Iterator for TextRenderedRun objects for the SVGTextFrame.
   1717 */
   1718 class TextRenderedRunIterator {
   1719 public:
   1720  /**
   1721   * Values for the aFilter argument of the constructor, to indicate which
   1722   * frames we should be limited to iterating TextRenderedRun objects for.
   1723   */
   1724  enum RenderedRunFilter {
   1725    // Iterate TextRenderedRuns for all nsTextFrames.
   1726    eAllFrames,
   1727    // Iterate only TextRenderedRuns for nsTextFrames that are
   1728    // visibility:visible.
   1729    eVisibleFrames
   1730  };
   1731 
   1732  /**
   1733   * Constructs a TextRenderedRunIterator with an optional frame subtree to
   1734   * restrict iterated rendered runs to.
   1735   *
   1736   * @param aSVGTextFrame The SVGTextFrame whose rendered runs to iterate
   1737   *   through.
   1738   * @param aFilter Indicates whether to iterate rendered runs for non-visible
   1739   *   nsTextFrames.
   1740   * @param aSubtree An optional frame subtree to restrict iterated rendered
   1741   *   runs to.
   1742   */
   1743  explicit TextRenderedRunIterator(SVGTextFrame* aSVGTextFrame,
   1744                                   RenderedRunFilter aFilter = eAllFrames,
   1745                                   const nsIFrame* aSubtree = nullptr)
   1746      : mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
   1747        mFilter(aFilter),
   1748        mTextElementCharIndex(0),
   1749        mFrameStartTextElementCharIndex(0),
   1750        mFontSizeScaleFactor(aSVGTextFrame->mFontSizeScaleFactor),
   1751        mCurrent(First()) {}
   1752 
   1753  /**
   1754   * Constructs a TextRenderedRunIterator with a content subtree to restrict
   1755   * iterated rendered runs to.
   1756   *
   1757   * @param aSVGTextFrame The SVGTextFrame whose rendered runs to iterate
   1758   *   through.
   1759   * @param aFilter Indicates whether to iterate rendered runs for non-visible
   1760   *   nsTextFrames.
   1761   * @param aSubtree A content subtree to restrict iterated rendered runs to.
   1762   */
   1763  TextRenderedRunIterator(SVGTextFrame* aSVGTextFrame,
   1764                          RenderedRunFilter aFilter, nsIContent* aSubtree)
   1765      : mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
   1766        mFilter(aFilter),
   1767        mTextElementCharIndex(0),
   1768        mFrameStartTextElementCharIndex(0),
   1769        mFontSizeScaleFactor(aSVGTextFrame->mFontSizeScaleFactor),
   1770        mCurrent(First()) {}
   1771 
   1772  /**
   1773   * Ensure any cached PropertyProvider is cleared at the end of the iteration.
   1774   */
   1775  ~TextRenderedRunIterator() {
   1776    if (auto* root = mFrameIterator.GetRoot()) {
   1777      root->ForgetCachedProvider();
   1778    }
   1779  }
   1780 
   1781  /**
   1782   * Returns the current TextRenderedRun.
   1783   */
   1784  TextRenderedRun Current() const { return mCurrent; }
   1785 
   1786  /**
   1787   * Advances to the next TextRenderedRun and returns it.
   1788   */
   1789  TextRenderedRun Next();
   1790 
   1791 private:
   1792  /**
   1793   * Returns the root SVGTextFrame this iterator is for.
   1794   */
   1795  SVGTextFrame* GetRoot() const { return mFrameIterator.GetRoot(); }
   1796 
   1797  /**
   1798   * Advances to the first TextRenderedRun and returns it.
   1799   */
   1800  TextRenderedRun First();
   1801 
   1802  /**
   1803   * The frame iterator to use.
   1804   */
   1805  TextFrameIterator mFrameIterator;
   1806 
   1807  /**
   1808   * The filter indicating which TextRenderedRuns to return.
   1809   */
   1810  RenderedRunFilter mFilter;
   1811 
   1812  /**
   1813   * The character index across the entire <text> element we are currently
   1814   * up to.
   1815   */
   1816  uint32_t mTextElementCharIndex;
   1817 
   1818  /**
   1819   * The character index across the entire <text> for the start of the current
   1820   * frame.
   1821   */
   1822  uint32_t mFrameStartTextElementCharIndex;
   1823 
   1824  /**
   1825   * The font-size scale factor we used when constructing the nsTextFrames.
   1826   */
   1827  double mFontSizeScaleFactor;
   1828 
   1829  /**
   1830   * The current TextRenderedRun.
   1831   */
   1832  TextRenderedRun mCurrent;
   1833 };
   1834 
   1835 TextRenderedRun TextRenderedRunIterator::Next() {
   1836  if (!mFrameIterator.GetCurrent()) {
   1837    // If there are no more frames, then there are no more rendered runs to
   1838    // return.
   1839    mCurrent = TextRenderedRun();
   1840    return mCurrent;
   1841  }
   1842 
   1843  // The values we will use to initialize the TextRenderedRun with.
   1844  nsTextFrame* frame;
   1845  gfxPoint pt;
   1846  double rotate;
   1847  nscoord baseline;
   1848  uint32_t offset, length;
   1849  uint32_t charIndex;
   1850 
   1851  // We loop, because we want to skip over rendered runs that either aren't
   1852  // within our subtree of interest, because they don't match the filter,
   1853  // or because they are hidden due to having fallen off the end of a
   1854  // <textPath>.
   1855  for (;;) {
   1856    if (mFrameIterator.IsAfterSubtree()) {
   1857      mCurrent = TextRenderedRun();
   1858      return mCurrent;
   1859    }
   1860 
   1861    frame = mFrameIterator.GetCurrent();
   1862 
   1863    charIndex = mTextElementCharIndex;
   1864 
   1865    // Find the end of the rendered run, by looking through the
   1866    // SVGTextFrame's positions array until we find one that is recorded
   1867    // as a run boundary.
   1868    uint32_t runStart,
   1869        runEnd;  // XXX Replace runStart with mTextElementCharIndex.
   1870    runStart = mTextElementCharIndex;
   1871    runEnd = runStart + 1;
   1872    while (runEnd < GetRoot()->mPositions.Length() &&
   1873           !GetRoot()->mPositions[runEnd].mRunBoundary) {
   1874      runEnd++;
   1875    }
   1876 
   1877    // Convert the global run start/end indexes into an offset/length into the
   1878    // current frame's Text.
   1879    offset =
   1880        frame->GetContentOffset() + runStart - mFrameStartTextElementCharIndex;
   1881    length = runEnd - runStart;
   1882 
   1883    // If the end of the frame's content comes before the run boundary we found
   1884    // in SVGTextFrame's position array, we need to shorten the rendered run.
   1885    uint32_t contentEnd = frame->GetContentEnd();
   1886    if (offset + length > contentEnd) {
   1887      length = contentEnd - offset;
   1888    }
   1889 
   1890    NS_ASSERTION(offset >= uint32_t(frame->GetContentOffset()),
   1891                 "invalid offset");
   1892    NS_ASSERTION(offset + length <= contentEnd, "invalid offset or length");
   1893 
   1894    // Get the frame's baseline position.
   1895    frame->EnsureTextRun(nsTextFrame::eInflated);
   1896    baseline = GetBaselinePosition(
   1897        frame, frame->GetTextRun(nsTextFrame::eInflated),
   1898        mFrameIterator.DominantBaseline(), mFontSizeScaleFactor);
   1899 
   1900    // Trim the offset/length to remove any leading/trailing white space.
   1901    uint32_t untrimmedOffset = offset;
   1902    uint32_t untrimmedLength = length;
   1903    nsTextFrame::TrimmedOffsets trimmedOffsets =
   1904        frame->GetTrimmedOffsets(frame->CharacterDataBuffer());
   1905    TrimOffsets(offset, length, trimmedOffsets);
   1906    charIndex += offset - untrimmedOffset;
   1907 
   1908    // Get the position and rotation of the character that begins this
   1909    // rendered run.
   1910    pt = GetRoot()->mPositions[charIndex].mPosition;
   1911    rotate = GetRoot()->mPositions[charIndex].mAngle;
   1912 
   1913    // Determine if we should skip this rendered run.
   1914    bool skip = !mFrameIterator.IsWithinSubtree() ||
   1915                GetRoot()->mPositions[mTextElementCharIndex].mHidden;
   1916    if (mFilter == eVisibleFrames) {
   1917      skip = skip || !frame->StyleVisibility()->IsVisible();
   1918    }
   1919 
   1920    // Update our global character index to move past the characters
   1921    // corresponding to this rendered run.
   1922    mTextElementCharIndex += untrimmedLength;
   1923 
   1924    // If we have moved past the end of the current frame's content, we need to
   1925    // advance to the next frame.
   1926    if (offset + untrimmedLength >= contentEnd) {
   1927      mFrameIterator.GetNext();
   1928      mTextElementCharIndex += mFrameIterator.UndisplayedCharacters();
   1929      mFrameStartTextElementCharIndex = mTextElementCharIndex;
   1930    }
   1931 
   1932    if (!mFrameIterator.GetCurrent()) {
   1933      if (skip) {
   1934        // That was the last frame, and we skipped this rendered run.  So we
   1935        // have no rendered run to return.
   1936        mCurrent = TextRenderedRun();
   1937        return mCurrent;
   1938      }
   1939      break;
   1940    }
   1941 
   1942    if (length && !skip) {
   1943      // Only return a rendered run if it didn't get collapsed away entirely
   1944      // (due to it being all white space) and if we don't want to skip it.
   1945      break;
   1946    }
   1947  }
   1948 
   1949  mCurrent = TextRenderedRun(frame, GetRoot(), pt, rotate, mFontSizeScaleFactor,
   1950                             baseline, offset, length, charIndex);
   1951  return mCurrent;
   1952 }
   1953 
   1954 TextRenderedRun TextRenderedRunIterator::First() {
   1955  if (!mFrameIterator.GetCurrent()) {
   1956    return TextRenderedRun();
   1957  }
   1958 
   1959  if (GetRoot()->mPositions.IsEmpty()) {
   1960    mFrameIterator.Close();
   1961    return TextRenderedRun();
   1962  }
   1963 
   1964  // Get the character index for the start of this rendered run, by skipping
   1965  // any undisplayed characters.
   1966  mTextElementCharIndex = mFrameIterator.UndisplayedCharacters();
   1967  mFrameStartTextElementCharIndex = mTextElementCharIndex;
   1968 
   1969  return Next();
   1970 }
   1971 
   1972 // -----------------------------------------------------------------------------
   1973 // CharIterator
   1974 
   1975 /**
   1976 * Iterator for characters within an SVGTextFrame.
   1977 */
   1978 class MOZ_STACK_CLASS CharIterator {
   1979  using Range = gfxTextRun::Range;
   1980 
   1981 public:
   1982  /**
   1983   * Values for the aFilter argument of the constructor, to indicate which
   1984   * characters we should be iterating over.
   1985   */
   1986  enum CharacterFilter {
   1987    // Iterate over all original characters from the DOM that are within valid
   1988    // text content elements.
   1989    eOriginal,
   1990    // Iterate only over characters that are not skipped characters.
   1991    eUnskipped,
   1992    // Iterate only over characters that are addressable by the positioning
   1993    // attributes x="", y="", etc.  This includes all characters after
   1994    // collapsing white space as required by the value of 'white-space'.
   1995    eAddressable,
   1996  };
   1997 
   1998  /**
   1999   * Constructs a CharIterator.
   2000   *
   2001   * @param aSVGTextFrame The SVGTextFrame whose characters to iterate
   2002   *   through.
   2003   * @param aFilter Indicates which characters to iterate over.
   2004   * @param aSubtree A content subtree to track whether the current character
   2005   *   is within.
   2006   */
   2007  CharIterator(SVGTextFrame* aSVGTextFrame, CharacterFilter aFilter,
   2008               nsIContent* aSubtree, bool aPostReflow = true);
   2009 
   2010  /**
   2011   * Ensure any cached PropertyProvider is cleared at the end of the iteration.
   2012   */
   2013  ~CharIterator() {
   2014    if (auto* root = mFrameIterator.GetRoot()) {
   2015      root->ForgetCachedProvider();
   2016    }
   2017  }
   2018 
   2019  /**
   2020   * Returns whether the iterator is finished.
   2021   */
   2022  bool AtEnd() const { return !mFrameIterator.GetCurrent(); }
   2023 
   2024  /**
   2025   * Advances to the next matching character.  Returns true if there was a
   2026   * character to advance to, and false otherwise.
   2027   */
   2028  bool Next();
   2029 
   2030  /**
   2031   * Advances ahead aCount matching characters.  Returns true if there were
   2032   * enough characters to advance past, and false otherwise.
   2033   */
   2034  bool Next(uint32_t aCount);
   2035 
   2036  /**
   2037   * Advances ahead up to aCount matching characters.
   2038   */
   2039  void NextWithinSubtree(uint32_t aCount);
   2040 
   2041  /**
   2042   * Advances to the character with the specified index.  The index is in the
   2043   * space of original characters (i.e., all DOM characters under the <text>
   2044   * that are within valid text content elements).
   2045   */
   2046  bool AdvanceToCharacter(uint32_t aTextElementCharIndex);
   2047 
   2048  /**
   2049   * Advances to the first matching character after the current nsTextFrame.
   2050   */
   2051  bool AdvancePastCurrentFrame();
   2052 
   2053  /**
   2054   * Advances to the first matching character after the frames within
   2055   * the current <textPath>.
   2056   */
   2057  bool AdvancePastCurrentTextPathFrame();
   2058 
   2059  /**
   2060   * Advances to the first matching character of the subtree.  Returns true
   2061   * if we successfully advance to the subtree, or if we are already within
   2062   * the subtree.  Returns false if we are past the subtree.
   2063   */
   2064  bool AdvanceToSubtree();
   2065 
   2066  /**
   2067   * Returns the nsTextFrame for the current character, or null if the end of
   2068   * iteration has been reached.
   2069   */
   2070  nsTextFrame* GetTextFrame() const { return mFrameIterator.GetCurrent(); }
   2071 
   2072  /**
   2073   * Returns whether the iterator is within the subtree.
   2074   */
   2075  bool IsWithinSubtree() const { return mFrameIterator.IsWithinSubtree(); }
   2076 
   2077  /**
   2078   * Returns whether the iterator is past the subtree.
   2079   */
   2080  bool IsAfterSubtree() const { return mFrameIterator.IsAfterSubtree(); }
   2081 
   2082  /**
   2083   * Returns the iterator's computed dominant-baseline value.
   2084   */
   2085  StyleDominantBaseline DominantBaseline() const {
   2086    return mFrameIterator.DominantBaseline();
   2087  }
   2088 
   2089  /**
   2090   * Returns whether the current character is a skipped character.
   2091   */
   2092  bool IsOriginalCharSkipped() const {
   2093    return mSkipCharsIterator.IsOriginalCharSkipped();
   2094  }
   2095 
   2096  /**
   2097   * Returns whether the current character is the start of a cluster and
   2098   * ligature group.
   2099   */
   2100  bool IsClusterAndLigatureGroupStart() const {
   2101    return mTextRun->IsLigatureGroupStart(
   2102               mSkipCharsIterator.GetSkippedOffset()) &&
   2103           mTextRun->IsClusterStart(mSkipCharsIterator.GetSkippedOffset());
   2104  }
   2105 
   2106  /**
   2107   * Returns the glyph run for the current character.
   2108   */
   2109  const gfxTextRun::GlyphRun& GlyphRun() const {
   2110    return *mTextRun->FindFirstGlyphRunContaining(
   2111        mSkipCharsIterator.GetSkippedOffset());
   2112  }
   2113 
   2114  /**
   2115   * Returns whether the current character is trimmed away when painting,
   2116   * due to it being leading/trailing white space.
   2117   */
   2118  bool IsOriginalCharTrimmed() const;
   2119 
   2120  /**
   2121   * Returns whether the current character is unaddressable from the SVG glyph
   2122   * positioning attributes.
   2123   */
   2124  bool IsOriginalCharUnaddressable() const {
   2125    return IsOriginalCharSkipped() || IsOriginalCharTrimmed();
   2126  }
   2127 
   2128  /**
   2129   * Returns the text run for the current character.
   2130   */
   2131  gfxTextRun* TextRun() const { return mTextRun; }
   2132 
   2133  /**
   2134   * Returns the current character index.
   2135   */
   2136  uint32_t TextElementCharIndex() const { return mTextElementCharIndex; }
   2137 
   2138  /**
   2139   * Returns the character index for the start of the cluster/ligature group it
   2140   * is part of.
   2141   */
   2142  uint32_t GlyphStartTextElementCharIndex() const {
   2143    return mGlyphStartTextElementCharIndex;
   2144  }
   2145 
   2146  /**
   2147   * Gets the advance, in user units, of the current character.  If the
   2148   * character is a part of ligature, then the advance returned will be
   2149   * a fraction of the ligature glyph's advance.
   2150   *
   2151   * @param aContext The context to use for unit conversions.
   2152   */
   2153  gfxFloat GetAdvance(nsPresContext* aContext) const;
   2154 
   2155  /**
   2156   * Returns the frame corresponding to the <textPath> that the current
   2157   * character is within.
   2158   */
   2159  nsIFrame* TextPathFrame() const { return mFrameIterator.TextPathFrame(); }
   2160 
   2161 #ifdef DEBUG
   2162  /**
   2163   * Returns the subtree we were constructed with.
   2164   */
   2165  nsIContent* GetSubtree() const { return mSubtree; }
   2166 
   2167  /**
   2168   * Returns the CharacterFilter mode in use.
   2169   */
   2170  CharacterFilter Filter() const { return mFilter; }
   2171 #endif
   2172 
   2173 private:
   2174  /**
   2175   * Advances to the next character without checking it against the filter.
   2176   * Returns true if there was a next character to advance to, or false
   2177   * otherwise.
   2178   */
   2179  bool NextCharacter();
   2180 
   2181  /**
   2182   * Returns whether the current character matches the filter.
   2183   */
   2184  bool MatchesFilter() const;
   2185 
   2186  /**
   2187   * If this is the start of a glyph, record it.
   2188   */
   2189  void UpdateGlyphStartTextElementCharIndex() {
   2190    if (!IsOriginalCharSkipped() && IsClusterAndLigatureGroupStart()) {
   2191      mGlyphStartTextElementCharIndex = mTextElementCharIndex;
   2192    }
   2193  }
   2194 
   2195  /**
   2196   * The filter to use.
   2197   */
   2198  CharacterFilter mFilter;
   2199 
   2200  /**
   2201   * The iterator for text frames.
   2202   */
   2203  TextFrameIterator mFrameIterator;
   2204 
   2205 #ifdef DEBUG
   2206  /**
   2207   * The subtree we were constructed with.
   2208   */
   2209  nsIContent* const mSubtree;
   2210 #endif
   2211 
   2212  /**
   2213   * A gfxSkipCharsIterator for the text frame the current character is
   2214   * a part of.
   2215   */
   2216  gfxSkipCharsIterator mSkipCharsIterator;
   2217 
   2218  // Cache for information computed by IsOriginalCharTrimmed.
   2219  mutable nsTextFrame* mFrameForTrimCheck;
   2220  mutable uint32_t mTrimmedOffset;
   2221  mutable uint32_t mTrimmedLength;
   2222 
   2223  /**
   2224   * The text run the current character is a part of.
   2225   */
   2226  gfxTextRun* mTextRun;
   2227 
   2228  /**
   2229   * The current character's index.
   2230   */
   2231  uint32_t mTextElementCharIndex;
   2232 
   2233  /**
   2234   * The index of the character that starts the cluster/ligature group the
   2235   * current character is a part of.
   2236   */
   2237  uint32_t mGlyphStartTextElementCharIndex;
   2238 
   2239  /**
   2240   * The scale factor to apply to glyph advances returned by
   2241   * GetAdvance etc. to take into account textLength="".
   2242   */
   2243  float mLengthAdjustScaleFactor;
   2244 
   2245  /**
   2246   * Whether the instance of this class is being used after reflow has occurred
   2247   * or not.
   2248   */
   2249  bool mPostReflow;
   2250 };
   2251 
   2252 CharIterator::CharIterator(SVGTextFrame* aSVGTextFrame,
   2253                           CharIterator::CharacterFilter aFilter,
   2254                           nsIContent* aSubtree, bool aPostReflow)
   2255    : mFilter(aFilter),
   2256      mFrameIterator(aSVGTextFrame, aSubtree),
   2257 #ifdef DEBUG
   2258      mSubtree(aSubtree),
   2259 #endif
   2260      mFrameForTrimCheck(nullptr),
   2261      mTrimmedOffset(0),
   2262      mTrimmedLength(0),
   2263      mTextRun(nullptr),
   2264      mTextElementCharIndex(0),
   2265      mGlyphStartTextElementCharIndex(0),
   2266      mLengthAdjustScaleFactor(aSVGTextFrame->mLengthAdjustScaleFactor),
   2267      mPostReflow(aPostReflow) {
   2268  if (!AtEnd()) {
   2269    mSkipCharsIterator = GetTextFrame()->EnsureTextRun(nsTextFrame::eInflated);
   2270    mTextRun = GetTextFrame()->GetTextRun(nsTextFrame::eInflated);
   2271    mTextElementCharIndex = mFrameIterator.UndisplayedCharacters();
   2272    UpdateGlyphStartTextElementCharIndex();
   2273    if (!MatchesFilter()) {
   2274      Next();
   2275    }
   2276  }
   2277 }
   2278 
   2279 bool CharIterator::Next() {
   2280  while (NextCharacter()) {
   2281    if (MatchesFilter()) {
   2282      return true;
   2283    }
   2284  }
   2285  return false;
   2286 }
   2287 
   2288 bool CharIterator::Next(uint32_t aCount) {
   2289  if (aCount == 0 && AtEnd()) {
   2290    return false;
   2291  }
   2292  while (aCount) {
   2293    if (!Next()) {
   2294      return false;
   2295    }
   2296    aCount--;
   2297  }
   2298  return true;
   2299 }
   2300 
   2301 void CharIterator::NextWithinSubtree(uint32_t aCount) {
   2302  while (IsWithinSubtree() && aCount) {
   2303    --aCount;
   2304    if (!Next()) {
   2305      return;
   2306    }
   2307  }
   2308 }
   2309 
   2310 bool CharIterator::AdvanceToCharacter(uint32_t aTextElementCharIndex) {
   2311  while (mTextElementCharIndex < aTextElementCharIndex) {
   2312    if (!Next()) {
   2313      return false;
   2314    }
   2315  }
   2316  return true;
   2317 }
   2318 
   2319 bool CharIterator::AdvancePastCurrentFrame() {
   2320  // XXX Can do this better than one character at a time if it matters.
   2321  nsTextFrame* currentFrame = GetTextFrame();
   2322  do {
   2323    if (!Next()) {
   2324      return false;
   2325    }
   2326  } while (GetTextFrame() == currentFrame);
   2327  return true;
   2328 }
   2329 
   2330 bool CharIterator::AdvancePastCurrentTextPathFrame() {
   2331  nsIFrame* currentTextPathFrame = TextPathFrame();
   2332  NS_ASSERTION(currentTextPathFrame,
   2333               "expected AdvancePastCurrentTextPathFrame to be called only "
   2334               "within a text path frame");
   2335  do {
   2336    if (!AdvancePastCurrentFrame()) {
   2337      return false;
   2338    }
   2339  } while (TextPathFrame() == currentTextPathFrame);
   2340  return true;
   2341 }
   2342 
   2343 bool CharIterator::AdvanceToSubtree() {
   2344  while (!IsWithinSubtree()) {
   2345    if (IsAfterSubtree()) {
   2346      return false;
   2347    }
   2348    if (!AdvancePastCurrentFrame()) {
   2349      return false;
   2350    }
   2351  }
   2352  return true;
   2353 }
   2354 
   2355 bool CharIterator::IsOriginalCharTrimmed() const {
   2356  if (mFrameForTrimCheck != GetTextFrame()) {
   2357    // Since we do a lot of trim checking, we cache the trimmed offsets and
   2358    // lengths while we are in the same frame.
   2359    mFrameForTrimCheck = GetTextFrame();
   2360    uint32_t offset = mFrameForTrimCheck->GetContentOffset();
   2361    uint32_t length = mFrameForTrimCheck->GetContentLength();
   2362    nsTextFrame::TrimmedOffsets trim = mFrameForTrimCheck->GetTrimmedOffsets(
   2363        mFrameForTrimCheck->CharacterDataBuffer(),
   2364        (mPostReflow ? nsTextFrame::TrimmedOffsetFlags::Default
   2365                     : nsTextFrame::TrimmedOffsetFlags::NotPostReflow));
   2366    TrimOffsets(offset, length, trim);
   2367    mTrimmedOffset = offset;
   2368    mTrimmedLength = length;
   2369  }
   2370 
   2371  // A character is trimmed if it is outside the mTrimmedOffset/mTrimmedLength
   2372  // range and it is not a significant newline character.
   2373  uint32_t index = mSkipCharsIterator.GetOriginalOffset();
   2374  return !(
   2375      (index >= mTrimmedOffset && index < mTrimmedOffset + mTrimmedLength) ||
   2376      (index >= mTrimmedOffset + mTrimmedLength &&
   2377       mFrameForTrimCheck->StyleText()->NewlineIsSignificant(
   2378           mFrameForTrimCheck) &&
   2379       mFrameForTrimCheck->CharacterDataBuffer().CharAt(index) == '\n'));
   2380 }
   2381 
   2382 gfxFloat CharIterator::GetAdvance(nsPresContext* aContext) const {
   2383  float cssPxPerDevPx =
   2384      nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
   2385 
   2386  auto& provider =
   2387      mFrameIterator.GetRoot()->PropertyProviderFor(GetTextFrame());
   2388  uint32_t offset = mSkipCharsIterator.GetSkippedOffset();
   2389  gfxFloat advance =
   2390      mTextRun->GetAdvanceWidth(Range(offset, offset + 1), &provider);
   2391  return aContext->AppUnitsToGfxUnits(advance) * mLengthAdjustScaleFactor *
   2392         cssPxPerDevPx;
   2393 }
   2394 
   2395 bool CharIterator::NextCharacter() {
   2396  if (AtEnd()) {
   2397    return false;
   2398  }
   2399 
   2400  mTextElementCharIndex++;
   2401 
   2402  // Advance within the current text run.
   2403  mSkipCharsIterator.AdvanceOriginal(1);
   2404  if (mSkipCharsIterator.GetOriginalOffset() <
   2405      GetTextFrame()->GetContentEnd()) {
   2406    // We're still within the part of the text run for the current text frame.
   2407    UpdateGlyphStartTextElementCharIndex();
   2408    return true;
   2409  }
   2410 
   2411  // Advance to the next frame.
   2412  mFrameIterator.GetNext();
   2413 
   2414  // Skip any undisplayed characters.
   2415  uint32_t undisplayed = mFrameIterator.UndisplayedCharacters();
   2416  mTextElementCharIndex += undisplayed;
   2417  if (!GetTextFrame()) {
   2418    // We're at the end.
   2419    mSkipCharsIterator = gfxSkipCharsIterator();
   2420    return false;
   2421  }
   2422 
   2423  mSkipCharsIterator = GetTextFrame()->EnsureTextRun(nsTextFrame::eInflated);
   2424  mTextRun = GetTextFrame()->GetTextRun(nsTextFrame::eInflated);
   2425  UpdateGlyphStartTextElementCharIndex();
   2426  return true;
   2427 }
   2428 
   2429 bool CharIterator::MatchesFilter() const {
   2430  switch (mFilter) {
   2431    case eOriginal:
   2432      return true;
   2433    case eUnskipped:
   2434      return !IsOriginalCharSkipped();
   2435    case eAddressable:
   2436      return !IsOriginalCharSkipped() && !IsOriginalCharUnaddressable();
   2437  }
   2438  MOZ_ASSERT_UNREACHABLE("Invalid mFilter value");
   2439  return true;
   2440 }
   2441 
   2442 // -----------------------------------------------------------------------------
   2443 // SVGTextDrawPathCallbacks
   2444 
   2445 /**
   2446 * Text frame draw callback class that paints the text and text decoration parts
   2447 * of an nsTextFrame using SVG painting properties, and selection backgrounds
   2448 * and decorations as they would normally.
   2449 *
   2450 * An instance of this class is passed to nsTextFrame::PaintText if painting
   2451 * cannot be done directly (e.g. if we are using an SVG pattern fill, stroking
   2452 * the text, etc.).
   2453 */
   2454 class SVGTextDrawPathCallbacks final : public nsTextFrame::DrawPathCallbacks {
   2455  using imgDrawingParams = image::imgDrawingParams;
   2456 
   2457 public:
   2458  /**
   2459   * Constructs an SVGTextDrawPathCallbacks.
   2460   *
   2461   * @param aSVGTextFrame The ancestor text frame.
   2462   * @param aContextPaint Used by context-fill and context-stroke.
   2463   * @param aContext The context to use for painting.
   2464   * @param aFrame The nsTextFrame to paint.
   2465   * @param aCanvasTM The transformation matrix to set when painting; this
   2466   *   should be the FOR_OUTERSVG_TM canvas TM of the text, so that
   2467   *   paint servers are painted correctly.
   2468   * @param aImgParams Whether we need to synchronously decode images.
   2469   * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
   2470   */
   2471  SVGTextDrawPathCallbacks(SVGTextFrame* aSVGTextFrame,
   2472                           SVGContextPaint* aContextPaint, gfxContext& aContext,
   2473                           nsTextFrame* aFrame, const gfxMatrix& aCanvasTM,
   2474                           imgDrawingParams& aImgParams,
   2475                           bool aShouldPaintSVGGlyphs)
   2476      : DrawPathCallbacks(aShouldPaintSVGGlyphs),
   2477        mSVGTextFrame(aSVGTextFrame),
   2478        mContextPaint(aContextPaint),
   2479        mContext(aContext),
   2480        mFrame(aFrame),
   2481        mCanvasTM(aCanvasTM),
   2482        mImgParams(aImgParams) {}
   2483 
   2484  void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect,
   2485                                          nscolor aColor,
   2486                                          DrawTarget& aDrawTarget) override;
   2487  void PaintDecorationLine(Rect aPath, bool aPaintingShadows,
   2488                           nscolor aColor) override;
   2489  void PaintSelectionDecorationLine(Rect aPath, bool aPaintingShadows,
   2490                                    nscolor aColor) override;
   2491  void NotifyBeforeText(bool aPaintingShadows, nscolor aColor) override;
   2492  void NotifyGlyphPathEmitted() override;
   2493  void NotifyAfterText() override;
   2494 
   2495 private:
   2496  void SetupContext();
   2497 
   2498  bool IsClipPathChild() const {
   2499    return mSVGTextFrame->HasAnyStateBits(NS_STATE_SVG_CLIPPATH_CHILD);
   2500  }
   2501 
   2502  /**
   2503   * Paints a piece of text geometry.  This is called when glyphs
   2504   * or text decorations have been emitted to the gfxContext.
   2505   */
   2506  void HandleTextGeometry();
   2507 
   2508  /**
   2509   * Sets the gfxContext paint to the appropriate color or pattern
   2510   * for filling text geometry.
   2511   */
   2512  void MakeFillPattern(GeneralPattern* aOutPattern);
   2513 
   2514  /**
   2515   * Fills and strokes a piece of text geometry, using group opacity
   2516   * if the selection style requires it.
   2517   */
   2518  void FillAndStrokeGeometry();
   2519 
   2520  /**
   2521   * Fills a piece of text geometry.
   2522   */
   2523  void FillGeometry();
   2524 
   2525  /**
   2526   * Strokes a piece of text geometry.
   2527   */
   2528  void StrokeGeometry();
   2529 
   2530  /*
   2531   * Takes a colour and modifies it to account for opacity properties.
   2532   */
   2533  void ApplyOpacity(sRGBColor& aColor, const StyleSVGPaint& aPaint,
   2534                    const StyleSVGOpacity& aOpacity) const;
   2535 
   2536  SVGTextFrame* const mSVGTextFrame;
   2537  SVGContextPaint* const mContextPaint;
   2538  gfxContext& mContext;
   2539  nsTextFrame* const mFrame;
   2540  const gfxMatrix& mCanvasTM;
   2541  imgDrawingParams& mImgParams;
   2542 
   2543  /**
   2544   * The color that we were last told from one of the path callback functions.
   2545   * This color can be the special NS_SAME_AS_FOREGROUND_COLOR,
   2546   * NS_40PERCENT_FOREGROUND_COLOR and NS_TRANSPARENT colors when we are
   2547   * painting selections or IME decorations.
   2548   */
   2549  nscolor mColor = NS_RGBA(0, 0, 0, 0);
   2550 
   2551  /**
   2552   * Whether we're painting text shadows.
   2553   */
   2554  bool mPaintingShadows = false;
   2555 };
   2556 
   2557 void SVGTextDrawPathCallbacks::NotifySelectionBackgroundNeedsFill(
   2558    const Rect& aBackgroundRect, nscolor aColor, DrawTarget& aDrawTarget) {
   2559  if (IsClipPathChild()) {
   2560    // Don't paint selection backgrounds when in a clip path.
   2561    return;
   2562  }
   2563 
   2564  mColor = aColor;  // currently needed by MakeFillPattern
   2565  mPaintingShadows = false;
   2566 
   2567  GeneralPattern fillPattern;
   2568  MakeFillPattern(&fillPattern);
   2569  if (fillPattern.GetPattern()) {
   2570    DrawOptions drawOptions(aColor == NS_40PERCENT_FOREGROUND_COLOR ? 0.4
   2571                                                                    : 1.0);
   2572    aDrawTarget.FillRect(aBackgroundRect, fillPattern, drawOptions);
   2573  }
   2574 }
   2575 
   2576 void SVGTextDrawPathCallbacks::NotifyBeforeText(bool aPaintingShadows,
   2577                                                nscolor aColor) {
   2578  mColor = aColor;
   2579  mPaintingShadows = aPaintingShadows;
   2580  SetupContext();
   2581  mContext.NewPath();
   2582 }
   2583 
   2584 void SVGTextDrawPathCallbacks::NotifyGlyphPathEmitted() {
   2585  HandleTextGeometry();
   2586  mContext.NewPath();
   2587 }
   2588 
   2589 void SVGTextDrawPathCallbacks::NotifyAfterText() { mContext.Restore(); }
   2590 
   2591 void SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath,
   2592                                                   bool aPaintingShadows,
   2593                                                   nscolor aColor) {
   2594  mColor = aColor;
   2595  mPaintingShadows = aPaintingShadows;
   2596  AntialiasMode aaMode =
   2597      SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering);
   2598 
   2599  mContext.Save();
   2600  mContext.NewPath();
   2601  mContext.SetAntialiasMode(aaMode);
   2602  mContext.Rectangle(ThebesRect(aPath));
   2603  HandleTextGeometry();
   2604  mContext.NewPath();
   2605  mContext.Restore();
   2606 }
   2607 
   2608 void SVGTextDrawPathCallbacks::PaintSelectionDecorationLine(
   2609    Rect aPath, bool aPaintingShadows, nscolor aColor) {
   2610  if (IsClipPathChild()) {
   2611    // Don't paint selection decorations when in a clip path.
   2612    return;
   2613  }
   2614 
   2615  mColor = aColor;
   2616  mPaintingShadows = aPaintingShadows;
   2617 
   2618  mContext.Save();
   2619  mContext.NewPath();
   2620  mContext.Rectangle(ThebesRect(aPath));
   2621  FillAndStrokeGeometry();
   2622  mContext.Restore();
   2623 }
   2624 
   2625 void SVGTextDrawPathCallbacks::SetupContext() {
   2626  mContext.Save();
   2627 
   2628  // XXX This is copied from nsSVGGlyphFrame::Render, but cairo doesn't actually
   2629  // seem to do anything with the antialias mode.  So we can perhaps remove it,
   2630  // or make SetAntialiasMode set cairo text antialiasing too.
   2631  mContext.SetAntialiasMode(
   2632      SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering));
   2633 }
   2634 
   2635 void SVGTextDrawPathCallbacks::HandleTextGeometry() {
   2636  if (IsClipPathChild()) {
   2637    RefPtr<Path> path = mContext.GetPath();
   2638    ColorPattern white(
   2639        DeviceColor(1.f, 1.f, 1.f, 1.f));  // for masking, so no ToDeviceColor
   2640    mContext.GetDrawTarget()->Fill(path, white);
   2641  } else {
   2642    // Normal painting.
   2643    gfxContextMatrixAutoSaveRestore saveMatrix(&mContext);
   2644    mContext.SetMatrixDouble(mCanvasTM);
   2645 
   2646    FillAndStrokeGeometry();
   2647  }
   2648 }
   2649 
   2650 void SVGTextDrawPathCallbacks::ApplyOpacity(
   2651    sRGBColor& aColor, const StyleSVGPaint& aPaint,
   2652    const StyleSVGOpacity& aOpacity) const {
   2653  if (aPaint.kind.tag == StyleSVGPaintKind::Tag::Color) {
   2654    aColor.a *=
   2655        sRGBColor::FromABGR(aPaint.kind.AsColor().CalcColor(*mFrame->Style()))
   2656            .a;
   2657  }
   2658  aColor.a *= SVGUtils::GetOpacity(aOpacity, mContextPaint);
   2659 }
   2660 
   2661 void SVGTextDrawPathCallbacks::MakeFillPattern(GeneralPattern* aOutPattern) {
   2662  if (mColor == NS_SAME_AS_FOREGROUND_COLOR ||
   2663      mColor == NS_40PERCENT_FOREGROUND_COLOR) {
   2664    SVGUtils::MakeFillPatternFor(mFrame, &mContext, aOutPattern, mImgParams,
   2665                                 mContextPaint);
   2666    return;
   2667  }
   2668 
   2669  if (mColor == NS_TRANSPARENT) {
   2670    return;
   2671  }
   2672 
   2673  sRGBColor color(sRGBColor::FromABGR(mColor));
   2674  if (mPaintingShadows) {
   2675    ApplyOpacity(color, mFrame->StyleSVG()->mFill,
   2676                 mFrame->StyleSVG()->mFillOpacity);
   2677  }
   2678  aOutPattern->InitColorPattern(ToDeviceColor(color));
   2679 }
   2680 
   2681 void SVGTextDrawPathCallbacks::FillAndStrokeGeometry() {
   2682  gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&mContext);
   2683  if (mColor == NS_40PERCENT_FOREGROUND_COLOR) {
   2684    autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 0.4f);
   2685  }
   2686 
   2687  uint32_t paintOrder = mFrame->StyleSVG()->mPaintOrder;
   2688  if (!paintOrder) {
   2689    FillGeometry();
   2690    StrokeGeometry();
   2691  } else {
   2692    while (paintOrder) {
   2693      auto component = StylePaintOrder(paintOrder & kPaintOrderMask);
   2694      switch (component) {
   2695        case StylePaintOrder::Fill:
   2696          FillGeometry();
   2697          break;
   2698        case StylePaintOrder::Stroke:
   2699          StrokeGeometry();
   2700          break;
   2701        default:
   2702          MOZ_FALLTHROUGH_ASSERT("Unknown paint-order value");
   2703        case StylePaintOrder::Markers:
   2704        case StylePaintOrder::Normal:
   2705          break;
   2706      }
   2707      paintOrder >>= kPaintOrderShift;
   2708    }
   2709  }
   2710 }
   2711 
   2712 void SVGTextDrawPathCallbacks::FillGeometry() {
   2713  if (mFrame->StyleSVG()->mFill.kind.IsNone()) {
   2714    return;
   2715  }
   2716  GeneralPattern fillPattern;
   2717  MakeFillPattern(&fillPattern);
   2718  if (fillPattern.GetPattern()) {
   2719    RefPtr<Path> path = mContext.GetPath();
   2720    FillRule fillRule = SVGUtils::ToFillRule(mFrame->StyleSVG()->mFillRule);
   2721    if (fillRule != path->GetFillRule()) {
   2722      Path::SetFillRule(path, fillRule);
   2723    }
   2724    mContext.GetDrawTarget()->Fill(path, fillPattern);
   2725  }
   2726 }
   2727 
   2728 void SVGTextDrawPathCallbacks::StrokeGeometry() {
   2729  // We don't paint the stroke when we are filling with a selection color.
   2730  if (!(mColor == NS_SAME_AS_FOREGROUND_COLOR ||
   2731        mColor == NS_40PERCENT_FOREGROUND_COLOR || mPaintingShadows)) {
   2732    return;
   2733  }
   2734 
   2735  if (!SVGUtils::HasStroke(mFrame, mContextPaint)) {
   2736    return;
   2737  }
   2738 
   2739  GeneralPattern strokePattern;
   2740  if (mPaintingShadows) {
   2741    sRGBColor color(sRGBColor::FromABGR(mColor));
   2742    ApplyOpacity(color, mFrame->StyleSVG()->mStroke,
   2743                 mFrame->StyleSVG()->mStrokeOpacity);
   2744    strokePattern.InitColorPattern(ToDeviceColor(color));
   2745  } else {
   2746    SVGUtils::MakeStrokePatternFor(mFrame, &mContext, &strokePattern,
   2747                                   mImgParams, mContextPaint);
   2748  }
   2749  if (strokePattern.GetPattern()) {
   2750    SVGElement* svgOwner =
   2751        SVGElement::FromNode(mFrame->GetParent()->GetContent());
   2752 
   2753    // Apply any stroke-specific transform
   2754    gfxMatrix outerSVGToUser;
   2755    if (SVGUtils::GetNonScalingStrokeTransform(mFrame, &outerSVGToUser) &&
   2756        outerSVGToUser.Invert()) {
   2757      mContext.Multiply(outerSVGToUser);
   2758    }
   2759 
   2760    RefPtr<Path> path = mContext.GetPath();
   2761    SVGContentUtils::AutoStrokeOptions strokeOptions;
   2762    SVGContentUtils::GetStrokeOptions(&strokeOptions, svgOwner, mFrame->Style(),
   2763                                      mContextPaint);
   2764    DrawOptions drawOptions;
   2765    drawOptions.mAntialiasMode =
   2766        SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering);
   2767    mContext.GetDrawTarget()->Stroke(path, strokePattern, strokeOptions);
   2768  }
   2769 }
   2770 
   2771 // ============================================================================
   2772 // SVGTextFrame
   2773 
   2774 // ----------------------------------------------------------------------------
   2775 // Display list item
   2776 
   2777 class DisplaySVGText final : public DisplaySVGItem {
   2778 public:
   2779  DisplaySVGText(nsDisplayListBuilder* aBuilder, SVGTextFrame* aFrame)
   2780      : DisplaySVGItem(aBuilder, aFrame) {
   2781    MOZ_COUNT_CTOR(DisplaySVGText);
   2782  }
   2783 
   2784  MOZ_COUNTED_DTOR_FINAL(DisplaySVGText)
   2785 
   2786  NS_DISPLAY_DECL_NAME("DisplaySVGText", TYPE_SVG_TEXT)
   2787 
   2788  nsDisplayItemGeometry* AllocateGeometry(
   2789      nsDisplayListBuilder* aBuilder) override {
   2790    return new nsDisplayItemGenericGeometry(this, aBuilder);
   2791  }
   2792 
   2793  nsRect GetComponentAlphaBounds(
   2794      nsDisplayListBuilder* aBuilder) const override {
   2795    bool snap;
   2796    return GetBounds(aBuilder, &snap);
   2797  }
   2798 };
   2799 
   2800 // ---------------------------------------------------------------------
   2801 // nsQueryFrame methods
   2802 
   2803 NS_QUERYFRAME_HEAD(SVGTextFrame)
   2804  NS_QUERYFRAME_ENTRY(SVGTextFrame)
   2805 NS_QUERYFRAME_TAIL_INHERITING(SVGDisplayContainerFrame)
   2806 
   2807 }  // namespace mozilla
   2808 
   2809 // ---------------------------------------------------------------------
   2810 // Implementation
   2811 
   2812 nsIFrame* NS_NewSVGTextFrame(mozilla::PresShell* aPresShell,
   2813                             mozilla::ComputedStyle* aStyle) {
   2814  return new (aPresShell)
   2815      mozilla::SVGTextFrame(aStyle, aPresShell->GetPresContext());
   2816 }
   2817 
   2818 namespace mozilla {
   2819 
   2820 NS_IMPL_FRAMEARENA_HELPERS(SVGTextFrame)
   2821 
   2822 // ---------------------------------------------------------------------
   2823 // nsIFrame methods
   2824 
   2825 void SVGTextFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
   2826                        nsIFrame* aPrevInFlow) {
   2827  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::text),
   2828               "Content is not an SVG text");
   2829 
   2830  SVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
   2831  AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
   2832 
   2833  mMutationObserver = new MutationObserver(this);
   2834 
   2835  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   2836    // We're inserting a new <text> element into a non-display context.
   2837    // Ensure that we get reflowed.
   2838    ScheduleReflowSVGNonDisplayText(
   2839        IntrinsicDirty::FrameAncestorsAndDescendants);
   2840  }
   2841 }
   2842 
   2843 void SVGTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
   2844                                    const nsDisplayListSet& aLists) {
   2845  if (IsSubtreeDirty()) {
   2846    // We can sometimes be asked to paint before reflow happens and we
   2847    // have updated mPositions, etc.  In this case, we just avoid
   2848    // painting.
   2849    return;
   2850  }
   2851  if (!IsVisibleForPainting() && aBuilder->IsForPainting()) {
   2852    return;
   2853  }
   2854  DisplayOutline(aBuilder, aLists);
   2855  aLists.Content()->AppendNewToTop<DisplaySVGText>(aBuilder, this);
   2856 }
   2857 
   2858 void SVGTextFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
   2859  SVGDisplayContainerFrame::DidSetComputedStyle(aOldComputedStyle);
   2860  if (StyleSVGReset()->HasNonScalingStroke() &&
   2861      (!aOldComputedStyle ||
   2862       !aOldComputedStyle->StyleSVGReset()->HasNonScalingStroke())) {
   2863    SVGUtils::UpdateNonScalingStrokeStateBit(this);
   2864  }
   2865 }
   2866 
   2867 nsresult SVGTextFrame::AttributeChanged(int32_t aNameSpaceID,
   2868                                        nsAtom* aAttribute, AttrModType) {
   2869  if (aNameSpaceID != kNameSpaceID_None) {
   2870    return NS_OK;
   2871  }
   2872 
   2873  if (aAttribute == nsGkAtoms::transform) {
   2874    // We don't invalidate for transform changes (the layers code does that).
   2875    // Also note that SVGTransformableElement::GetAttributeChangeHint will
   2876    // return nsChangeHint_UpdateOverflow for "transform" attribute changes
   2877    // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
   2878 
   2879    if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW) && mCanvasTM &&
   2880        mCanvasTM->IsSingular()) {
   2881      // We won't have calculated the glyph positions correctly.
   2882      NotifyGlyphMetricsChange(false);
   2883    }
   2884    mCanvasTM = nullptr;
   2885  } else if (IsGlyphPositioningAttribute(aAttribute) ||
   2886             aAttribute == nsGkAtoms::textLength ||
   2887             aAttribute == nsGkAtoms::lengthAdjust) {
   2888    NotifyGlyphMetricsChange(false);
   2889  }
   2890 
   2891  return NS_OK;
   2892 }
   2893 
   2894 void SVGTextFrame::ReflowSVGNonDisplayText() {
   2895  MOZ_ASSERT(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this),
   2896             "only call ReflowSVGNonDisplayText when an outer SVG frame is "
   2897             "under ReflowSVG");
   2898  MOZ_ASSERT(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
   2899             "only call ReflowSVGNonDisplayText if the frame is "
   2900             "NS_FRAME_IS_NONDISPLAY");
   2901 
   2902  // We had a style change, so we mark this frame as dirty so that the next
   2903  // time it is painted, we reflow the anonymous block frame.
   2904  this->MarkSubtreeDirty();
   2905 
   2906  // Finally, we need to actually reflow the anonymous block frame and update
   2907  // mPositions, in case we are being reflowed immediately after a DOM
   2908  // mutation that needs frame reconstruction.
   2909  MaybeReflowAnonymousBlockChild();
   2910  UpdateGlyphPositioning();
   2911 }
   2912 
   2913 void SVGTextFrame::ScheduleReflowSVGNonDisplayText(IntrinsicDirty aReason) {
   2914  MOZ_ASSERT(!SVGUtils::OuterSVGIsCallingReflowSVG(this),
   2915             "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
   2916             "frame is under ReflowSVG");
   2917  MOZ_ASSERT(!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW),
   2918             "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
   2919             "anonymous block child");
   2920 
   2921  // We need to find an ancestor frame that we can call FrameNeedsReflow
   2922  // on that will cause the document to be marked as needing relayout,
   2923  // and for that ancestor (or some further ancestor) to be marked as
   2924  // a root to reflow.  We choose the closest ancestor frame that is not
   2925  // NS_FRAME_IS_NONDISPLAY and which is either an outer SVG frame or a
   2926  // non-SVG frame.  (We don't consider displayed SVG frame ancestors other
   2927  // than SVGOuterSVGFrame, since calling FrameNeedsReflow on those other
   2928  // SVG frames would do a bunch of unnecessary work on the SVG frames up to
   2929  // the SVGOuterSVGFrame.)
   2930 
   2931  nsIFrame* f = this;
   2932  while (f) {
   2933    if (!f->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   2934      if (f->IsSubtreeDirty()) {
   2935        // This is a displayed frame, so if it is already dirty, we will be
   2936        // reflowed soon anyway.  No need to call FrameNeedsReflow again, then.
   2937        return;
   2938      }
   2939      if (!f->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
   2940        break;
   2941      }
   2942      f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
   2943    }
   2944    f = f->GetParent();
   2945  }
   2946 
   2947  MOZ_ASSERT(f, "should have found an ancestor frame to reflow");
   2948 
   2949  PresShell()->FrameNeedsReflow(f, aReason, NS_FRAME_IS_DIRTY);
   2950 }
   2951 
   2952 NS_IMPL_ISUPPORTS(SVGTextFrame::MutationObserver, nsIMutationObserver)
   2953 
   2954 void SVGTextFrame::MutationObserver::ContentAppended(
   2955    nsIContent* aFirstNewContent, const ContentAppendInfo&) {
   2956  mFrame->NotifyGlyphMetricsChange(true);
   2957 }
   2958 
   2959 void SVGTextFrame::MutationObserver::ContentInserted(nsIContent* aChild,
   2960                                                     const ContentInsertInfo&) {
   2961  mFrame->NotifyGlyphMetricsChange(true);
   2962 }
   2963 
   2964 void SVGTextFrame::MutationObserver::ContentWillBeRemoved(
   2965    nsIContent* aChild, const ContentRemoveInfo& aInfo) {
   2966  if (aInfo.mBatchRemovalState && !aInfo.mBatchRemovalState->mIsFirst) {
   2967    return;
   2968  }
   2969  mFrame->NotifyGlyphMetricsChange(true);
   2970 }
   2971 
   2972 void SVGTextFrame::MutationObserver::CharacterDataChanged(
   2973    nsIContent* aContent, const CharacterDataChangeInfo&) {
   2974  mFrame->NotifyGlyphMetricsChange(true);
   2975 }
   2976 
   2977 void SVGTextFrame::MutationObserver::AttributeChanged(
   2978    Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute, AttrModType,
   2979    const nsAttrValue* aOldValue) {
   2980  if (!aElement->IsSVGElement()) {
   2981    return;
   2982  }
   2983 
   2984  // Attribute changes on this element will be handled by
   2985  // SVGTextFrame::AttributeChanged.
   2986  if (aElement == mFrame->GetContent()) {
   2987    return;
   2988  }
   2989 
   2990  mFrame->HandleAttributeChangeInDescendant(aElement, aNameSpaceID, aAttribute);
   2991 }
   2992 
   2993 void SVGTextFrame::HandleAttributeChangeInDescendant(Element* aElement,
   2994                                                     int32_t aNameSpaceID,
   2995                                                     nsAtom* aAttribute) {
   2996  if (aElement->IsSVGElement(nsGkAtoms::textPath)) {
   2997    if (aNameSpaceID == kNameSpaceID_None &&
   2998        (aAttribute == nsGkAtoms::startOffset ||
   2999         aAttribute == nsGkAtoms::path || aAttribute == nsGkAtoms::side)) {
   3000      NotifyGlyphMetricsChange(false);
   3001    } else if ((aNameSpaceID == kNameSpaceID_XLink ||
   3002                aNameSpaceID == kNameSpaceID_None) &&
   3003               aAttribute == nsGkAtoms::href) {
   3004      // Blow away our reference, if any
   3005      nsIFrame* childElementFrame = aElement->GetPrimaryFrame();
   3006      if (childElementFrame) {
   3007        SVGObserverUtils::RemoveTextPathObserver(childElementFrame);
   3008        NotifyGlyphMetricsChange(false);
   3009      }
   3010    }
   3011  } else {
   3012    if (aNameSpaceID == kNameSpaceID_None &&
   3013        IsGlyphPositioningAttribute(aAttribute)) {
   3014      NotifyGlyphMetricsChange(false);
   3015    }
   3016  }
   3017 }
   3018 
   3019 void SVGTextFrame::FindCloserFrameForSelection(
   3020    const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
   3021  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   3022    return;
   3023  }
   3024 
   3025  UpdateGlyphPositioning();
   3026 
   3027  nsPresContext* presContext = PresContext();
   3028 
   3029  // Find the frame that has the closest rendered run rect to aPoint.
   3030  TextRenderedRunIterator it(this);
   3031  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
   3032    uint32_t flags = TextRenderedRun::eIncludeFill |
   3033                     TextRenderedRun::eIncludeStroke |
   3034                     TextRenderedRun::eNoHorizontalOverflow;
   3035    SVGBBox userRect = run.GetUserSpaceRect(presContext, flags);
   3036    float devPxPerCSSPx = presContext->CSSPixelsToDevPixels(1.f);
   3037    userRect.Scale(devPxPerCSSPx);
   3038 
   3039    if (!userRect.IsEmpty()) {
   3040      gfxMatrix m;
   3041      nsRect rect =
   3042          SVGUtils::ToCanvasBounds(userRect.ToThebesRect(), m, presContext);
   3043 
   3044      if (nsLayoutUtils::PointIsCloserToRect(aPoint, rect,
   3045                                             aCurrentBestFrame->mXDistance,
   3046                                             aCurrentBestFrame->mYDistance)) {
   3047        aCurrentBestFrame->mFrame = run.mFrame;
   3048      }
   3049    }
   3050  }
   3051 }
   3052 
   3053 //----------------------------------------------------------------------
   3054 // ISVGDisplayableFrame methods
   3055 
   3056 void SVGTextFrame::NotifySVGChanged(ChangeFlags aFlags) {
   3057  MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) ||
   3058                 aFlags.contains(ChangeFlag::CoordContextChanged),
   3059             "Invalidation logic may need adjusting");
   3060 
   3061  bool needNewBounds = false;
   3062  bool needGlyphMetricsUpdate = false;
   3063  if (aFlags.contains(ChangeFlag::CoordContextChanged) &&
   3064      HasAnyStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES)) {
   3065    needGlyphMetricsUpdate = true;
   3066  }
   3067 
   3068  if (aFlags.contains(ChangeFlag::TransformChanged)) {
   3069    if (mCanvasTM && mCanvasTM->IsSingular()) {
   3070      // We won't have calculated the glyph positions correctly.
   3071      needNewBounds = true;
   3072      needGlyphMetricsUpdate = true;
   3073    }
   3074    mCanvasTM = nullptr;
   3075    if (StyleSVGReset()->HasNonScalingStroke()) {
   3076      // Stroke currently contributes to our mRect, and our stroke depends on
   3077      // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
   3078      needNewBounds = true;
   3079    }
   3080 
   3081    // If the scale at which we computed our mFontSizeScaleFactor has changed by
   3082    // at least a factor of two, reflow the text.  This avoids reflowing text at
   3083    // every tick of a transform animation, but ensures our glyph metrics
   3084    // do not get too far out of sync with the final font size on the screen.
   3085    const float scale = GetContextScale(this);
   3086    if (scale != mLastContextScale) {
   3087      if (mLastContextScale == 0.0f) {
   3088        needNewBounds = true;
   3089        needGlyphMetricsUpdate = true;
   3090      } else {
   3091        float change = scale / mLastContextScale;
   3092        if (change >= 2.0f || change <= 0.5f) {
   3093          needNewBounds = true;
   3094          needGlyphMetricsUpdate = true;
   3095        }
   3096      }
   3097    }
   3098  }
   3099 
   3100  if (needNewBounds) {
   3101    // Ancestor changes can't affect how we render from the perspective of
   3102    // any rendering observers that we may have, so we don't need to
   3103    // invalidate them. We also don't need to invalidate ourself, since our
   3104    // changed ancestor will have invalidated its entire area, which includes
   3105    // our area.
   3106    ScheduleReflowSVG();
   3107  }
   3108 
   3109  if (needGlyphMetricsUpdate) {
   3110    // If we are positioned using percentage values we need to update our
   3111    // position whenever our viewport's dimensions change.  But only do this if
   3112    // we have been reflowed once, otherwise the glyph positioning will be
   3113    // wrong.  (We need to wait until bidi reordering has been done.)
   3114    if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
   3115      NotifyGlyphMetricsChange(false);
   3116    }
   3117  }
   3118 }
   3119 
   3120 /**
   3121 * Gets the offset into a DOM node that the specified caret is positioned at.
   3122 */
   3123 static int32_t GetCaretOffset(nsCaret* aCaret) {
   3124  RefPtr<Selection> selection = aCaret->GetSelection();
   3125  if (!selection) {
   3126    return -1;
   3127  }
   3128 
   3129  return selection->AnchorOffset();
   3130 }
   3131 
   3132 /**
   3133 * Returns whether the caret should be painted for a given TextRenderedRun
   3134 * by checking whether the caret is in the range covered by the rendered run.
   3135 *
   3136 * @param aThisRun The TextRenderedRun to be painted.
   3137 * @param aCaret The caret.
   3138 */
   3139 static bool ShouldPaintCaret(const TextRenderedRun& aThisRun, nsCaret* aCaret) {
   3140  int32_t caretOffset = GetCaretOffset(aCaret);
   3141 
   3142  if (caretOffset < 0) {
   3143    return false;
   3144  }
   3145 
   3146  return uint32_t(caretOffset) >= aThisRun.mTextFrameContentOffset &&
   3147         uint32_t(caretOffset) < aThisRun.mTextFrameContentOffset +
   3148                                     aThisRun.mTextFrameContentLength;
   3149 }
   3150 
   3151 void SVGTextFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
   3152                            imgDrawingParams& aImgParams) {
   3153  DrawTarget& aDrawTarget = *aContext.GetDrawTarget();
   3154  nsIFrame* kid = PrincipalChildList().FirstChild();
   3155  if (!kid) {
   3156    return;
   3157  }
   3158 
   3159  if (IsSubtreeDirty()) {
   3160    return;
   3161  }
   3162 
   3163  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   3164    // Text frames inside <clipPath>, <mask>, etc. will never have had
   3165    // ReflowSVG called on them, so call UpdateGlyphPositioning to do this now.
   3166    UpdateGlyphPositioning();
   3167  }
   3168 
   3169  const float epsilon = 0.0001;
   3170  if (abs(mLengthAdjustScaleFactor) < epsilon) {
   3171    // A zero scale factor can be caused by having forced the text length to
   3172    // zero. In this situation there is nothing to show.
   3173    return;
   3174  }
   3175 
   3176  if (aTransform.IsSingular()) {
   3177    NS_WARNING("Can't render text element!");
   3178    return;
   3179  }
   3180 
   3181  gfxMatrix initialMatrix = aContext.CurrentMatrixDouble();
   3182 
   3183  gfxMatrix matrixForPaintServers = aTransform * initialMatrix;
   3184 
   3185  // SVG frames' PaintSVG methods paint in CSS px, but normally frames paint in
   3186  // dev pixels. Here we multiply a CSS-px-to-dev-pixel factor onto aTransform
   3187  // so our non-SVG nsTextFrame children paint correctly.
   3188  nsPresContext* presContext = PresContext();
   3189  auto auPerDevPx = presContext->AppUnitsPerDevPixel();
   3190  float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(auPerDevPx);
   3191  gfxMatrix canvasTMForChildren = aTransform;
   3192  canvasTMForChildren.PreScale(cssPxPerDevPx, cssPxPerDevPx);
   3193  initialMatrix.PreScale(1 / cssPxPerDevPx, 1 / cssPxPerDevPx);
   3194 
   3195  gfxContextMatrixAutoSaveRestore matSR(&aContext);
   3196  aContext.NewPath();
   3197  aContext.Multiply(canvasTMForChildren);
   3198  gfxMatrix currentMatrix = aContext.CurrentMatrixDouble();
   3199 
   3200  RefPtr<nsCaret> caret = presContext->PresShell()->GetCaret();
   3201  nsIFrame* caretFrame = caret->GetPaintGeometry();
   3202 
   3203  gfxContextAutoSaveRestore ctxSR;
   3204  TextRenderedRunIterator it(this, TextRenderedRunIterator::eVisibleFrames);
   3205  TextRenderedRun run = it.Current();
   3206 
   3207  SVGContextPaint* outerContextPaint =
   3208      SVGContextPaint::GetContextPaint(GetContent());
   3209 
   3210  while (run.mFrame) {
   3211    nsTextFrame* frame = run.mFrame;
   3212 
   3213    auto contextPaint = MakeRefPtr<SVGContextPaintImpl>();
   3214    DrawMode drawMode = contextPaint->Init(&aDrawTarget, initialMatrix, frame,
   3215                                           outerContextPaint, aImgParams);
   3216    if (drawMode & DrawMode::GLYPH_STROKE) {
   3217      ctxSR.EnsureSaved(&aContext);
   3218      // This may change the gfxContext's transform (for non-scaling stroke),
   3219      // in which case this needs to happen before we call SetMatrix() below.
   3220      SVGUtils::SetupStrokeGeometry(frame->GetParent(), &aContext,
   3221                                    outerContextPaint);
   3222    }
   3223 
   3224    nscoord startEdge, endEdge;
   3225    run.GetClipEdges(startEdge, endEdge);
   3226 
   3227    // Set up the transform for painting the text frame for the substring
   3228    // indicated by the run.
   3229    gfxMatrix runTransform = run.GetTransformFromUserSpaceForPainting(
   3230                                 presContext, startEdge, endEdge) *
   3231                             currentMatrix;
   3232    aContext.SetMatrixDouble(runTransform);
   3233 
   3234    if (drawMode != DrawMode(0)) {
   3235      bool paintSVGGlyphs;
   3236      nsTextFrame::PaintTextParams params(&aContext);
   3237      params.framePt = Point();
   3238      params.dirtyRect =
   3239          LayoutDevicePixel::FromAppUnits(frame->InkOverflowRect(), auPerDevPx);
   3240      params.contextPaint = contextPaint;
   3241      bool isSelected;
   3242      if (HasAnyStateBits(NS_STATE_SVG_CLIPPATH_CHILD)) {
   3243        params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
   3244        isSelected = false;
   3245      } else {
   3246        isSelected = frame->IsSelected();
   3247      }
   3248      gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&aContext);
   3249      float opacity = 1.0f;
   3250      nsIFrame* ancestor = frame->GetParent();
   3251      while (ancestor != this) {
   3252        opacity *= ancestor->StyleEffects()->mOpacity;
   3253        ancestor = ancestor->GetParent();
   3254      }
   3255      if (opacity < 1.0f) {
   3256        autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
   3257                                                opacity);
   3258      }
   3259 
   3260      if (ShouldRenderAsPath(frame, outerContextPaint, paintSVGGlyphs)) {
   3261        SVGTextDrawPathCallbacks callbacks(this, outerContextPaint, aContext,
   3262                                           frame, matrixForPaintServers,
   3263                                           aImgParams, paintSVGGlyphs);
   3264        params.callbacks = &callbacks;
   3265        frame->PaintText(params, startEdge, endEdge, nsPoint(), isSelected);
   3266      } else {
   3267        frame->PaintText(params, startEdge, endEdge, nsPoint(), isSelected);
   3268      }
   3269    }
   3270 
   3271    if (frame == caretFrame && ShouldPaintCaret(run, caret)) {
   3272      // XXX Should we be looking at the fill/stroke colours to paint the
   3273      // caret with, rather than using the color property?
   3274      caret->PaintCaret(aDrawTarget, frame, nsPoint());
   3275      aContext.NewPath();
   3276    }
   3277 
   3278    run = it.Next();
   3279  }
   3280 }
   3281 
   3282 nsIFrame* SVGTextFrame::GetFrameForPoint(const gfxPoint& aPoint) {
   3283  NS_ASSERTION(PrincipalChildList().FirstChild(), "must have a child frame");
   3284 
   3285  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   3286    // Text frames inside <clipPath> will never have had ReflowSVG called on
   3287    // them, so call UpdateGlyphPositioning to do this now.  (Text frames
   3288    // inside <mask> and other non-display containers will never need to
   3289    // be hit tested.)
   3290    UpdateGlyphPositioning();
   3291  } else {
   3292    NS_ASSERTION(!IsSubtreeDirty(), "reflow should have happened");
   3293  }
   3294 
   3295  // Hit-testing any clip-path will typically be a lot quicker than the
   3296  // hit-testing of our text frames in the loop below, so we do the former up
   3297  // front to avoid unnecessarily wasting cycles on the latter.
   3298  if (!SVGUtils::HitTestClip(this, aPoint)) {
   3299    return nullptr;
   3300  }
   3301 
   3302  nsPresContext* presContext = PresContext();
   3303 
   3304  // Ideally we'd iterate backwards so that we can just return the first frame
   3305  // that is under aPoint.  In practice this will rarely matter though since it
   3306  // is rare for text in/under an SVG <text> element to overlap (i.e. the first
   3307  // text frame that is hit will likely be the only text frame that is hit).
   3308 
   3309  TextRenderedRunIterator it(this);
   3310  nsIFrame* hit = nullptr;
   3311  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
   3312    uint16_t hitTestFlags = SVGUtils::GetGeometryHitTestFlags(run.mFrame);
   3313    if (!hitTestFlags) {
   3314      continue;
   3315    }
   3316 
   3317    gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
   3318    if (!m.Invert()) {
   3319      return nullptr;
   3320    }
   3321 
   3322    gfxPoint pointInRunUserSpace = m.TransformPoint(aPoint);
   3323    gfxRect frameRect = run.GetRunUserSpaceRect(TextRenderedRun::eIncludeFill |
   3324                                                TextRenderedRun::eIncludeStroke)
   3325                            .ToThebesRect();
   3326 
   3327    if (frameRect.Contains(pointInRunUserSpace)) {
   3328      hit = run.mFrame;
   3329    }
   3330  }
   3331  return hit;
   3332 }
   3333 
   3334 void SVGTextFrame::ReflowSVG() {
   3335  MOZ_ASSERT(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this),
   3336             "This call is probaby a wasteful mistake");
   3337 
   3338  MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
   3339             "ReflowSVG mechanism not designed for this");
   3340 
   3341  if (!SVGUtils::NeedsReflowSVG(this)) {
   3342    MOZ_ASSERT(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
   3343                                NS_STATE_SVG_POSITIONING_DIRTY),
   3344               "How did this happen?");
   3345    return;
   3346  }
   3347 
   3348  MaybeReflowAnonymousBlockChild();
   3349  UpdateGlyphPositioning();
   3350 
   3351  nsPresContext* presContext = PresContext();
   3352 
   3353  SVGBBox r;
   3354  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames);
   3355  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
   3356    uint32_t runFlags = 0;
   3357    if (!run.mFrame->StyleSVG()->mFill.kind.IsNone()) {
   3358      runFlags |= TextRenderedRun::eIncludeFill;
   3359    }
   3360    if (SVGUtils::HasStroke(run.mFrame)) {
   3361      runFlags |= TextRenderedRun::eIncludeStroke;
   3362    }
   3363    // Our "visual" overflow rect needs to be valid for building display lists
   3364    // for hit testing, which means that for certain values of 'pointer-events'
   3365    // it needs to include the geometry of the fill or stroke even when the
   3366    // fill/ stroke don't actually render (e.g. when stroke="none" or
   3367    // stroke-opacity="0"). GetGeometryHitTestFlags accounts for
   3368    // 'pointer-events'. The text-shadow is not part of the hit-test area.
   3369    uint16_t hitTestFlags = SVGUtils::GetGeometryHitTestFlags(run.mFrame);
   3370    if (hitTestFlags & SVG_HIT_TEST_FILL) {
   3371      runFlags |= TextRenderedRun::eIncludeFill;
   3372    }
   3373    if (hitTestFlags & SVG_HIT_TEST_STROKE) {
   3374      runFlags |= TextRenderedRun::eIncludeStroke;
   3375    }
   3376 
   3377    if (runFlags) {
   3378      r.UnionEdges(run.GetUserSpaceRect(presContext, runFlags));
   3379    }
   3380  }
   3381 
   3382  if (r.IsEmpty()) {
   3383    mRect.SetEmpty();
   3384  } else {
   3385    mRect = nsLayoutUtils::RoundGfxRectToAppRect((const Rect&)r,
   3386                                                 AppUnitsPerCSSPixel());
   3387 
   3388    // Due to rounding issues when we have a transform applied, we sometimes
   3389    // don't include an additional row of pixels.  For now, just inflate our
   3390    // covered region.
   3391    if (mLastContextScale != 0.0f) {
   3392      mRect.Inflate(
   3393          ceil(presContext->AppUnitsPerDevPixel() / mLastContextScale));
   3394    }
   3395  }
   3396 
   3397  if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
   3398    // Make sure we have our filter property (if any) before calling
   3399    // FinishAndStoreOverflow (subsequent filter changes are handled off
   3400    // nsChangeHint_UpdateEffects):
   3401    SVGObserverUtils::UpdateEffects(this);
   3402  }
   3403 
   3404  // Now unset the various reflow bits. Do this before calling
   3405  // FinishAndStoreOverflow since FinishAndStoreOverflow can require glyph
   3406  // positions (to resolve transform-origin).
   3407  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
   3408                  NS_FRAME_HAS_DIRTY_CHILDREN);
   3409 
   3410  nsRect overflow = nsRect(nsPoint(0, 0), mRect.Size());
   3411  OverflowAreas overflowAreas(overflow, overflow);
   3412  FinishAndStoreOverflow(overflowAreas, mRect.Size());
   3413 }
   3414 
   3415 /**
   3416 * Converts SVGUtils::eBBox* flags into TextRenderedRun flags appropriate
   3417 * for the specified rendered run.
   3418 */
   3419 static uint32_t TextRenderedRunFlagsForBBoxContribution(
   3420    const TextRenderedRun& aRun, uint32_t aBBoxFlags) {
   3421  uint32_t flags = 0;
   3422  if ((aBBoxFlags & SVGUtils::eBBoxIncludeFillGeometry) ||
   3423      ((aBBoxFlags & SVGUtils::eBBoxIncludeFill) &&
   3424       !aRun.mFrame->StyleSVG()->mFill.kind.IsNone())) {
   3425    flags |= TextRenderedRun::eIncludeFill;
   3426  }
   3427  if ((aBBoxFlags & SVGUtils::eBBoxIncludeStrokeGeometry) ||
   3428      ((aBBoxFlags & SVGUtils::eBBoxIncludeStroke) &&
   3429       SVGUtils::HasStroke(aRun.mFrame))) {
   3430    flags |= TextRenderedRun::eIncludeStroke;
   3431  }
   3432  return flags;
   3433 }
   3434 
   3435 SVGBBox SVGTextFrame::GetBBoxContribution(const Matrix& aToBBoxUserspace,
   3436                                          uint32_t aFlags) {
   3437  NS_ASSERTION(PrincipalChildList().FirstChild(), "must have a child frame");
   3438  SVGBBox bbox;
   3439 
   3440  if (aFlags & SVGUtils::eForGetClientRects) {
   3441    if (!mRect.IsEmpty()) {
   3442      Rect rect = NSRectToRect(mRect, AppUnitsPerCSSPixel());
   3443      bbox = aToBBoxUserspace.TransformBounds(rect);
   3444    }
   3445    return bbox;
   3446  }
   3447 
   3448  nsIFrame* kid = PrincipalChildList().FirstChild();
   3449  if (kid && kid->IsSubtreeDirty()) {
   3450    // Return an empty bbox if our kid's subtree is dirty. This may be called
   3451    // in that situation, e.g. when we're building a display list after an
   3452    // interrupted reflow. This can also be called during reflow before we've
   3453    // been reflowed, e.g. if an earlier sibling is calling
   3454    // FinishAndStoreOverflow and needs our parent's perspective matrix, which
   3455    // depends on the SVG bbox contribution of this frame. In the latter
   3456    // situation, when all siblings have been reflowed, the parent will compute
   3457    // its perspective and rerun FinishAndStoreOverflow for all its children.
   3458    return bbox;
   3459  }
   3460 
   3461  UpdateGlyphPositioning();
   3462 
   3463  nsPresContext* presContext = PresContext();
   3464 
   3465  TextRenderedRunIterator it(this);
   3466  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
   3467    uint32_t flags = TextRenderedRunFlagsForBBoxContribution(run, aFlags);
   3468    gfxMatrix m = ThebesMatrix(aToBBoxUserspace);
   3469    SVGBBox bboxForRun = run.GetUserSpaceRect(presContext, flags, &m);
   3470    bbox.UnionEdges(bboxForRun);
   3471  }
   3472 
   3473  return bbox;
   3474 }
   3475 
   3476 //----------------------------------------------------------------------
   3477 // SVGTextFrame SVG DOM methods
   3478 
   3479 /**
   3480 * Returns whether the specified node has any non-empty Text
   3481 * beneath it.
   3482 */
   3483 static bool HasTextContent(nsIContent* aContent) {
   3484  NS_ASSERTION(aContent, "expected non-null aContent");
   3485 
   3486  TextNodeIterator it(aContent);
   3487  for (Text* text = it.GetCurrent(); text; text = it.GetNext()) {
   3488    if (text->TextLength() != 0) {
   3489      return true;
   3490    }
   3491  }
   3492  return false;
   3493 }
   3494 
   3495 /**
   3496 * Returns the number of DOM characters beneath the specified node.
   3497 */
   3498 static uint32_t GetTextContentLength(nsIContent* aContent) {
   3499  NS_ASSERTION(aContent, "expected non-null aContent");
   3500 
   3501  uint32_t length = 0;
   3502  TextNodeIterator it(aContent);
   3503  for (Text* text = it.GetCurrent(); text; text = it.GetNext()) {
   3504    length += text->TextLength();
   3505  }
   3506  return length;
   3507 }
   3508 
   3509 int32_t SVGTextFrame::ConvertTextElementCharIndexToAddressableIndex(
   3510    int32_t aIndex, nsIContent* aContent) {
   3511  CharIterator it(this, CharIterator::eOriginal, aContent);
   3512  if (!it.AdvanceToSubtree()) {
   3513    return -1;
   3514  }
   3515  int32_t result = 0;
   3516  int32_t textElementCharIndex;
   3517  while (!it.AtEnd() && it.IsWithinSubtree()) {
   3518    bool addressable = !it.IsOriginalCharUnaddressable();
   3519    textElementCharIndex = it.TextElementCharIndex();
   3520    it.Next();
   3521    uint32_t delta = it.TextElementCharIndex() - textElementCharIndex;
   3522    aIndex -= delta;
   3523    if (addressable) {
   3524      if (aIndex < 0) {
   3525        return result;
   3526      }
   3527      result += delta;
   3528    }
   3529  }
   3530  return -1;
   3531 }
   3532 
   3533 /**
   3534 * Implements the SVG DOM GetNumberOfChars method for the specified
   3535 * text content element.
   3536 */
   3537 uint32_t SVGTextFrame::GetNumberOfChars(nsIContent* aContent) {
   3538  nsIFrame* kid = PrincipalChildList().FirstChild();
   3539  if (kid->IsSubtreeDirty()) {
   3540    // We're never reflowed if we're under a non-SVG element that is
   3541    // never reflowed (such as the HTML 'caption' element).
   3542    return 0;
   3543  }
   3544 
   3545  UpdateGlyphPositioning();
   3546 
   3547  uint32_t n = 0;
   3548  CharIterator it(this, CharIterator::eAddressable, aContent);
   3549  if (it.AdvanceToSubtree()) {
   3550    while (!it.AtEnd() && it.IsWithinSubtree()) {
   3551      n++;
   3552      it.Next();
   3553    }
   3554  }
   3555  return n;
   3556 }
   3557 
   3558 /**
   3559 * Implements the SVG DOM GetComputedTextLength method for the specified
   3560 * text child element.
   3561 */
   3562 float SVGTextFrame::GetComputedTextLength(nsIContent* aContent) {
   3563  nsIFrame* kid = PrincipalChildList().FirstChild();
   3564  if (kid->IsSubtreeDirty()) {
   3565    // We're never reflowed if we're under a non-SVG element that is
   3566    // never reflowed (such as the HTML 'caption' element).
   3567    //
   3568    // If we ever decide that we need to return accurate values here,
   3569    // we could do similar work to GetSubStringLength.
   3570    return 0;
   3571  }
   3572 
   3573  UpdateGlyphPositioning();
   3574 
   3575  float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
   3576      PresContext()->AppUnitsPerDevPixel());
   3577 
   3578  nscoord length = 0;
   3579  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
   3580                             aContent);
   3581  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
   3582    length += run.GetAdvanceWidth();
   3583  }
   3584 
   3585  return PresContext()->AppUnitsToGfxUnits(length) * cssPxPerDevPx *
   3586         mLengthAdjustScaleFactor / mFontSizeScaleFactor;
   3587 }
   3588 
   3589 /**
   3590 * Implements the SVG DOM SelectSubString method for the specified
   3591 * text content element.
   3592 */
   3593 void SVGTextFrame::SelectSubString(nsIContent* aContent, uint32_t charnum,
   3594                                   uint32_t nchars, ErrorResult& aRv) {
   3595  nsIFrame* kid = PrincipalChildList().FirstChild();
   3596  if (kid->IsSubtreeDirty()) {
   3597    // We're never reflowed if we're under a non-SVG element that is
   3598    // never reflowed (such as the HTML 'caption' element).
   3599    // XXXbz Should this just return without throwing like the no-frame case?
   3600    aRv.ThrowInvalidStateError("No layout information available for SVG text");
   3601    return;
   3602  }
   3603 
   3604  UpdateGlyphPositioning();
   3605 
   3606  RefPtr<nsIContent> content;
   3607 
   3608  // Ensure the destructor of CharIterator runs before calling HandleClick.
   3609  {
   3610    // Convert charnum/nchars from addressable characters relative to
   3611    // aContent to global character indices.
   3612    CharIterator chit(this, CharIterator::eAddressable, aContent);
   3613    if (!chit.AdvanceToSubtree() || !chit.Next(charnum) ||
   3614        chit.IsAfterSubtree()) {
   3615      aRv.ThrowIndexSizeError("Character index out of range");
   3616      return;
   3617    }
   3618    charnum = chit.TextElementCharIndex();
   3619    content = chit.GetTextFrame()->GetContent();
   3620    chit.NextWithinSubtree(nchars);
   3621    nchars = chit.TextElementCharIndex() - charnum;
   3622  }
   3623 
   3624  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
   3625 
   3626  frameSelection->HandleClick(content, charnum, charnum + nchars,
   3627                              nsFrameSelection::FocusMode::kCollapseToNewPoint,
   3628                              CaretAssociationHint::Before);
   3629 }
   3630 
   3631 /**
   3632 * For some content we cannot (or currently cannot) compute the length
   3633 * without reflowing.  In those cases we need to fall back to using
   3634 * GetSubStringLengthSlowFallback.
   3635 *
   3636 * We fall back for textPath since we need glyph positioning in order to
   3637 * tell if any characters should be ignored due to having fallen off the
   3638 * end of the textPath.
   3639 *
   3640 * We fall back for bidi because GetTrimmedOffsets does not produce the
   3641 * correct results for bidi continuations when passed aPostReflow = false.
   3642 * XXX It may be possible to determine which continuations to trim from (and
   3643 * which sides), but currently we don't do that.  It would require us to
   3644 * identify the visual (rather than logical) start and end of the line, to
   3645 * avoid trimming at line-internal frame boundaries.  Maybe nsBidiPresUtils
   3646 * methods like GetFrameToRightOf and GetFrameToLeftOf would help?
   3647 *
   3648 */
   3649 bool SVGTextFrame::RequiresSlowFallbackForSubStringLength() {
   3650  TextFrameIterator frameIter(this);
   3651  for (nsTextFrame* frame = frameIter.GetCurrent(); frame;
   3652       frame = frameIter.GetNext()) {
   3653    if (frameIter.TextPathFrame() || frame->GetNextContinuation()) {
   3654      return true;
   3655    }
   3656  }
   3657  return false;
   3658 }
   3659 
   3660 /**
   3661 * Implements the SVG DOM GetSubStringLength method for the specified
   3662 * text content element.
   3663 */
   3664 float SVGTextFrame::GetSubStringLengthFastPath(nsIContent* aContent,
   3665                                               uint32_t charnum,
   3666                                               uint32_t nchars,
   3667                                               ErrorResult& aRv) {
   3668  MOZ_ASSERT(!RequiresSlowFallbackForSubStringLength());
   3669 
   3670  // We only need our text correspondence to be up to date (no need to call
   3671  // UpdateGlyphPositioning).
   3672  TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
   3673 
   3674  // Convert charnum/nchars from addressable characters relative to
   3675  // aContent to global character indices.
   3676  CharIterator chit(this, CharIterator::eAddressable, aContent,
   3677                    /* aPostReflow */ false);
   3678  if (!chit.AdvanceToSubtree() || !chit.Next(charnum) ||
   3679      chit.IsAfterSubtree()) {
   3680    aRv.ThrowIndexSizeError("Character index out of range");
   3681    return 0;
   3682  }
   3683 
   3684  // We do this after the ThrowIndexSizeError() bit so JS calls correctly throw
   3685  // when necessary.
   3686  if (nchars == 0) {
   3687    return 0.0f;
   3688  }
   3689 
   3690  charnum = chit.TextElementCharIndex();
   3691  chit.NextWithinSubtree(nchars);
   3692  nchars = chit.TextElementCharIndex() - charnum;
   3693 
   3694  // Sum of the substring advances.
   3695  nscoord textLength = 0;
   3696 
   3697  TextFrameIterator frit(this);  // aSubtree = nullptr
   3698 
   3699  // Index of the first non-skipped char in the frame, and of a subsequent char
   3700  // that we're interested in.  Both are relative to the index of the first
   3701  // non-skipped char in the ancestor <text> element.
   3702  uint32_t frameStartTextElementCharIndex = 0;
   3703  uint32_t textElementCharIndex;
   3704 
   3705  for (nsTextFrame* frame = frit.GetCurrent(); frame; frame = frit.GetNext()) {
   3706    frameStartTextElementCharIndex += frit.UndisplayedCharacters();
   3707    textElementCharIndex = frameStartTextElementCharIndex;
   3708 
   3709    // Offset into frame's Text:
   3710    const uint32_t untrimmedOffset = frame->GetContentOffset();
   3711    const uint32_t untrimmedLength = frame->GetContentEnd() - untrimmedOffset;
   3712 
   3713    // Trim the offset/length to remove any leading/trailing white space.
   3714    uint32_t trimmedOffset = untrimmedOffset;
   3715    uint32_t trimmedLength = untrimmedLength;
   3716    nsTextFrame::TrimmedOffsets trimmedOffsets = frame->GetTrimmedOffsets(
   3717        frame->CharacterDataBuffer(),
   3718        nsTextFrame::TrimmedOffsetFlags::NotPostReflow);
   3719    TrimOffsets(trimmedOffset, trimmedLength, trimmedOffsets);
   3720 
   3721    textElementCharIndex += trimmedOffset - untrimmedOffset;
   3722 
   3723    if (textElementCharIndex >= charnum + nchars) {
   3724      break;  // we're past the end of the substring
   3725    }
   3726 
   3727    uint32_t offset = textElementCharIndex;
   3728 
   3729    // Intersect the substring we are interested in with the range covered by
   3730    // the nsTextFrame.
   3731    IntersectInterval(offset, trimmedLength, charnum, nchars);
   3732 
   3733    if (trimmedLength != 0) {
   3734      // Convert offset into an index into the frame.
   3735      offset += trimmedOffset - textElementCharIndex;
   3736 
   3737      gfxSkipCharsIterator it = frame->EnsureTextRun(nsTextFrame::eInflated);
   3738      gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated);
   3739      auto& provider = PropertyProviderFor(frame);
   3740 
   3741      Range range = ConvertOriginalToSkipped(it, offset, trimmedLength);
   3742 
   3743      // Accumulate the advance.
   3744      textLength += textRun->GetAdvanceWidth(range, &provider);
   3745    }
   3746 
   3747    // Advance, ready for next call:
   3748    frameStartTextElementCharIndex += untrimmedLength;
   3749  }
   3750 
   3751  nsPresContext* presContext = PresContext();
   3752  float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
   3753      presContext->AppUnitsPerDevPixel());
   3754 
   3755  return presContext->AppUnitsToGfxUnits(textLength) * cssPxPerDevPx /
   3756         mFontSizeScaleFactor;
   3757 }
   3758 
   3759 float SVGTextFrame::GetSubStringLengthSlowFallback(nsIContent* aContent,
   3760                                                   uint32_t charnum,
   3761                                                   uint32_t nchars,
   3762                                                   ErrorResult& aRv) {
   3763  UpdateGlyphPositioning();
   3764 
   3765  // Convert charnum/nchars from addressable characters relative to
   3766  // aContent to global character indices.
   3767  CharIterator chit(this, CharIterator::eAddressable, aContent);
   3768  if (!chit.AdvanceToSubtree() || !chit.Next(charnum) ||
   3769      chit.IsAfterSubtree()) {
   3770    aRv.ThrowIndexSizeError("Character index out of range");
   3771    return 0;
   3772  }
   3773 
   3774  if (nchars == 0) {
   3775    return 0.0f;
   3776  }
   3777 
   3778  charnum = chit.TextElementCharIndex();
   3779  chit.NextWithinSubtree(nchars);
   3780  nchars = chit.TextElementCharIndex() - charnum;
   3781 
   3782  // Find each rendered run that intersects with the range defined
   3783  // by charnum/nchars.
   3784  nscoord textLength = 0;
   3785  TextRenderedRunIterator runIter(this, TextRenderedRunIterator::eAllFrames);
   3786  TextRenderedRun run = runIter.Current();
   3787  while (run.mFrame) {
   3788    // If this rendered run is past the substring we are interested in, we
   3789    // are done.
   3790    uint32_t offset = run.mTextElementCharIndex;
   3791    if (offset >= charnum + nchars) {
   3792      break;
   3793    }
   3794 
   3795    // Intersect the substring we are interested in with the range covered by
   3796    // the rendered run.
   3797    uint32_t length = run.mTextFrameContentLength;
   3798    IntersectInterval(offset, length, charnum, nchars);
   3799 
   3800    if (length != 0) {
   3801      // Convert offset into an index into the frame.
   3802      offset += run.mTextFrameContentOffset - run.mTextElementCharIndex;
   3803 
   3804      gfxSkipCharsIterator it =
   3805          run.mFrame->EnsureTextRun(nsTextFrame::eInflated);
   3806      gfxTextRun* textRun = run.mFrame->GetTextRun(nsTextFrame::eInflated);
   3807      auto& provider = PropertyProviderFor(run.mFrame);
   3808 
   3809      Range range = ConvertOriginalToSkipped(it, offset, length);
   3810 
   3811      // Accumulate the advance.
   3812      textLength += textRun->GetAdvanceWidth(range, &provider);
   3813    }
   3814 
   3815    run = runIter.Next();
   3816  }
   3817 
   3818  nsPresContext* presContext = PresContext();
   3819  float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
   3820      presContext->AppUnitsPerDevPixel());
   3821 
   3822  return presContext->AppUnitsToGfxUnits(textLength) * cssPxPerDevPx /
   3823         mFontSizeScaleFactor;
   3824 }
   3825 
   3826 /**
   3827 * Implements the SVG DOM GetCharNumAtPosition method for the specified
   3828 * text content element.
   3829 */
   3830 int32_t SVGTextFrame::GetCharNumAtPosition(nsIContent* aContent,
   3831                                           const DOMPointInit& aPoint) {
   3832  nsIFrame* kid = PrincipalChildList().FirstChild();
   3833  if (kid->IsSubtreeDirty()) {
   3834    // We're never reflowed if we're under a non-SVG element that is
   3835    // never reflowed (such as the HTML 'caption' element).
   3836    return -1;
   3837  }
   3838 
   3839  UpdateGlyphPositioning();
   3840 
   3841  nsPresContext* context = PresContext();
   3842 
   3843  gfxPoint p(aPoint.mX, aPoint.mY);
   3844 
   3845  int32_t result = -1;
   3846 
   3847  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
   3848                             aContent);
   3849  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
   3850    // Hit test this rendered run.  Later runs will override earlier ones.
   3851    int32_t index = run.GetCharNumAtPosition(context, p);
   3852    if (index != -1) {
   3853      result = index + run.mTextElementCharIndex;
   3854    }
   3855  }
   3856 
   3857  if (result == -1) {
   3858    return result;
   3859  }
   3860 
   3861  return ConvertTextElementCharIndexToAddressableIndex(result, aContent);
   3862 }
   3863 
   3864 /**
   3865 * Implements the SVG DOM GetStartPositionOfChar method for the specified
   3866 * text content element.
   3867 */
   3868 already_AddRefed<DOMSVGPoint> SVGTextFrame::GetStartPositionOfChar(
   3869    nsIContent* aContent, uint32_t aCharNum, ErrorResult& aRv) {
   3870  nsIFrame* kid = PrincipalChildList().FirstChild();
   3871  if (kid->IsSubtreeDirty()) {
   3872    // We're never reflowed if we're under a non-SVG element that is
   3873    // never reflowed (such as the HTML 'caption' element).
   3874    aRv.ThrowInvalidStateError("No layout information available for SVG text");
   3875    return nullptr;
   3876  }
   3877 
   3878  UpdateGlyphPositioning();
   3879 
   3880  CharIterator it(this, CharIterator::eAddressable, aContent);
   3881  if (!it.AdvanceToSubtree() || !it.Next(aCharNum)) {
   3882    aRv.ThrowIndexSizeError("Character index out of range");
   3883    return nullptr;
   3884  }
   3885 
   3886  // We need to return the start position of the whole glyph.
   3887  uint32_t startIndex = it.GlyphStartTextElementCharIndex();
   3888 
   3889  return do_AddRef(new DOMSVGPoint(ToPoint(mPositions[startIndex].mPosition)));
   3890 }
   3891 
   3892 /**
   3893 * Returns the advance of the entire glyph whose starting character is at
   3894 * aTextElementCharIndex.
   3895 *
   3896 * aIterator, if provided, must be a CharIterator that already points to
   3897 * aTextElementCharIndex that is restricted to aContent and is using
   3898 * filter mode eAddressable.
   3899 */
   3900 static gfxFloat GetGlyphAdvance(SVGTextFrame* aFrame, nsIContent* aContent,
   3901                                uint32_t aTextElementCharIndex,
   3902                                CharIterator* aIterator) {
   3903  MOZ_ASSERT(!aIterator || (aIterator->Filter() == CharIterator::eAddressable &&
   3904                            aIterator->GetSubtree() == aContent &&
   3905                            aIterator->GlyphStartTextElementCharIndex() ==
   3906                                aTextElementCharIndex),
   3907             "Invalid aIterator");
   3908 
   3909  Maybe<CharIterator> newIterator;
   3910  CharIterator* it = aIterator;
   3911  if (!it) {
   3912    newIterator.emplace(aFrame, CharIterator::eAddressable, aContent);
   3913    if (!newIterator->AdvanceToSubtree()) {
   3914      MOZ_ASSERT_UNREACHABLE("Invalid aContent");
   3915      return 0.0;
   3916    }
   3917    it = newIterator.ptr();
   3918  }
   3919 
   3920  while (it->GlyphStartTextElementCharIndex() != aTextElementCharIndex) {
   3921    if (!it->Next()) {
   3922      MOZ_ASSERT_UNREACHABLE("Invalid aTextElementCharIndex");
   3923      return 0.0;
   3924    }
   3925  }
   3926 
   3927  if (it->AtEnd()) {
   3928    MOZ_ASSERT_UNREACHABLE("Invalid aTextElementCharIndex");
   3929    return 0.0;
   3930  }
   3931 
   3932  nsPresContext* presContext = aFrame->PresContext();
   3933  gfxFloat advance = 0.0;
   3934 
   3935  for (;;) {
   3936    advance += it->GetAdvance(presContext);
   3937    if (!it->Next() ||
   3938        it->GlyphStartTextElementCharIndex() != aTextElementCharIndex) {
   3939      break;
   3940    }
   3941  }
   3942 
   3943  return advance;
   3944 }
   3945 
   3946 /**
   3947 * Implements the SVG DOM GetEndPositionOfChar method for the specified
   3948 * text content element.
   3949 */
   3950 already_AddRefed<DOMSVGPoint> SVGTextFrame::GetEndPositionOfChar(
   3951    nsIContent* aContent, uint32_t aCharNum, ErrorResult& aRv) {
   3952  nsIFrame* kid = PrincipalChildList().FirstChild();
   3953  if (kid->IsSubtreeDirty()) {
   3954    // We're never reflowed if we're under a non-SVG element that is
   3955    // never reflowed (such as the HTML 'caption' element).
   3956    aRv.ThrowInvalidStateError("No layout information available for SVG text");
   3957    return nullptr;
   3958  }
   3959 
   3960  UpdateGlyphPositioning();
   3961 
   3962  CharIterator it(this, CharIterator::eAddressable, aContent);
   3963  if (!it.AdvanceToSubtree() || !it.Next(aCharNum)) {
   3964    aRv.ThrowIndexSizeError("Character index out of range");
   3965    return nullptr;
   3966  }
   3967 
   3968  // We need to return the end position of the whole glyph.
   3969  uint32_t startIndex = it.GlyphStartTextElementCharIndex();
   3970 
   3971  // Get the advance of the glyph.
   3972  gfxFloat advance =
   3973      GetGlyphAdvance(this, aContent, startIndex,
   3974                      it.IsClusterAndLigatureGroupStart() ? &it : nullptr) /
   3975      mFontSizeScaleFactor;
   3976  const gfxTextRun* textRun = it.TextRun();
   3977  if (textRun->IsInlineReversed()) {
   3978    advance = -advance;
   3979  }
   3980  Point p = textRun->IsVertical() ? Point(0, advance) : Point(advance, 0);
   3981 
   3982  // The end position is the start position plus the advance in the direction
   3983  // of the glyph's rotation.
   3984  Matrix m = Matrix::Rotation(mPositions[startIndex].mAngle) *
   3985             Matrix::Translation(ToPoint(mPositions[startIndex].mPosition));
   3986 
   3987  return do_AddRef(new DOMSVGPoint(m.TransformPoint(p)));
   3988 }
   3989 
   3990 /**
   3991 * Implements the SVG DOM GetExtentOfChar method for the specified
   3992 * text content element.
   3993 */
   3994 already_AddRefed<SVGRect> SVGTextFrame::GetExtentOfChar(nsIContent* aContent,
   3995                                                        uint32_t aCharNum,
   3996                                                        ErrorResult& aRv) {
   3997  nsIFrame* kid = PrincipalChildList().FirstChild();
   3998  if (kid->IsSubtreeDirty()) {
   3999    // We're never reflowed if we're under a non-SVG element that is
   4000    // never reflowed (such as the HTML 'caption' element).
   4001    aRv.ThrowInvalidStateError("No layout information available for SVG text");
   4002    return nullptr;
   4003  }
   4004 
   4005  UpdateGlyphPositioning();
   4006 
   4007  // Search for the character whose addressable index is aCharNum.
   4008  CharIterator it(this, CharIterator::eAddressable, aContent);
   4009  if (!it.AdvanceToSubtree() || !it.Next(aCharNum)) {
   4010    aRv.ThrowIndexSizeError("Character index out of range");
   4011    return nullptr;
   4012  }
   4013 
   4014  nsPresContext* presContext = PresContext();
   4015  float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
   4016      presContext->AppUnitsPerDevPixel());
   4017 
   4018  nsTextFrame* textFrame = it.GetTextFrame();
   4019  uint32_t startIndex = it.GlyphStartTextElementCharIndex();
   4020  const gfxTextRun* textRun = it.TextRun();
   4021 
   4022  // Get the glyph advance.
   4023  gfxFloat advance =
   4024      GetGlyphAdvance(this, aContent, startIndex,
   4025                      it.IsClusterAndLigatureGroupStart() ? &it : nullptr);
   4026  gfxFloat x = textRun->IsInlineReversed() ? -advance : 0.0;
   4027 
   4028  // The ascent and descent gives the height of the glyph.
   4029  gfxFloat ascent, descent;
   4030  GetAscentAndDescentInAppUnits(textFrame, ascent, descent);
   4031 
   4032  // The horizontal extent is the origin of the glyph plus the advance
   4033  // in the direction of the glyph's rotation.
   4034  gfxMatrix m;
   4035  m.PreTranslate(mPositions[startIndex].mPosition);
   4036  m.PreRotate(mPositions[startIndex].mAngle);
   4037  m.PreScale(1 / mFontSizeScaleFactor, 1 / mFontSizeScaleFactor);
   4038 
   4039  nscoord baseline = GetBaselinePosition(
   4040      textFrame, textRun, it.DominantBaseline(), mFontSizeScaleFactor);
   4041 
   4042  gfxRect glyphRect;
   4043  if (textRun->IsVertical()) {
   4044    glyphRect = gfxRect(
   4045        -presContext->AppUnitsToGfxUnits(baseline) * cssPxPerDevPx, x,
   4046        presContext->AppUnitsToGfxUnits(ascent + descent) * cssPxPerDevPx,
   4047        advance);
   4048  } else {
   4049    glyphRect = gfxRect(
   4050        x, -presContext->AppUnitsToGfxUnits(baseline) * cssPxPerDevPx, advance,
   4051        presContext->AppUnitsToGfxUnits(ascent + descent) * cssPxPerDevPx);
   4052  }
   4053 
   4054  // Transform the glyph's rect into user space.
   4055  gfxRect r = m.TransformBounds(glyphRect);
   4056 
   4057  return do_AddRef(new SVGRect(aContent, ToRect(r)));
   4058 }
   4059 
   4060 /**
   4061 * Implements the SVG DOM GetRotationOfChar method for the specified
   4062 * text content element.
   4063 */
   4064 float SVGTextFrame::GetRotationOfChar(nsIContent* aContent, uint32_t aCharNum,
   4065                                      ErrorResult& aRv) {
   4066  nsIFrame* kid = PrincipalChildList().FirstChild();
   4067  if (kid->IsSubtreeDirty()) {
   4068    // We're never reflowed if we're under a non-SVG element that is
   4069    // never reflowed (such as the HTML 'caption' element).
   4070    aRv.ThrowInvalidStateError("No layout information available for SVG text");
   4071    return 0;
   4072  }
   4073 
   4074  UpdateGlyphPositioning();
   4075 
   4076  CharIterator it(this, CharIterator::eAddressable, aContent);
   4077  if (!it.AdvanceToSubtree() || !it.Next(aCharNum)) {
   4078    aRv.ThrowIndexSizeError("Character index out of range");
   4079    return 0;
   4080  }
   4081 
   4082  // We need to account for the glyph's underlying orientation.
   4083  const gfxTextRun::GlyphRun& glyphRun = it.GlyphRun();
   4084  int32_t glyphOrientation =
   4085      90 * (glyphRun.IsSidewaysRight() - glyphRun.IsSidewaysLeft());
   4086 
   4087  return mPositions[it.TextElementCharIndex()].mAngle * 180.0 / M_PI +
   4088         glyphOrientation;
   4089 }
   4090 
   4091 //----------------------------------------------------------------------
   4092 // SVGTextFrame text layout methods
   4093 
   4094 /**
   4095 * Given the character position array before values have been filled in
   4096 * to any unspecified positions, and an array of dx/dy values, returns whether
   4097 * a character at a given index should start a new rendered run.
   4098 *
   4099 * @param aPositions The array of character positions before unspecified
   4100 *   positions have been filled in and dx/dy values have been added to them.
   4101 * @param aDeltas The array of dx/dy values.
   4102 * @param aIndex The character index in question.
   4103 */
   4104 static bool ShouldStartRunAtIndex(const nsTArray<CharPosition>& aPositions,
   4105                                  const nsTArray<gfxPoint>& aDeltas,
   4106                                  uint32_t aIndex) {
   4107  if (aIndex == 0) {
   4108    return true;
   4109  }
   4110 
   4111  if (aIndex < aPositions.Length()) {
   4112    // If an explicit x or y value was given, start a new run.
   4113    if (aPositions[aIndex].IsXSpecified() ||
   4114        aPositions[aIndex].IsYSpecified()) {
   4115      return true;
   4116    }
   4117 
   4118    // If a non-zero rotation was given, or the previous character had a non-
   4119    // zero rotation, start a new run.
   4120    if ((aPositions[aIndex].IsAngleSpecified() &&
   4121         aPositions[aIndex].mAngle != 0.0f) ||
   4122        (aPositions[aIndex - 1].IsAngleSpecified() &&
   4123         (aPositions[aIndex - 1].mAngle != 0.0f))) {
   4124      return true;
   4125    }
   4126  }
   4127 
   4128  if (aIndex < aDeltas.Length()) {
   4129    // If a non-zero dx or dy value was given, start a new run.
   4130    if (aDeltas[aIndex].x != 0.0 || aDeltas[aIndex].y != 0.0) {
   4131      return true;
   4132    }
   4133  }
   4134 
   4135  return false;
   4136 }
   4137 
   4138 bool SVGTextFrame::ResolvePositionsForNode(nsIContent* aContent,
   4139                                           uint32_t& aIndex, bool aInTextPath,
   4140                                           bool& aForceStartOfChunk,
   4141                                           nsTArray<gfxPoint>& aDeltas) {
   4142  if (aContent->IsText()) {
   4143    // We found a text node.
   4144    uint32_t length = aContent->AsText()->TextLength();
   4145    if (length) {
   4146      uint32_t end = aIndex + length;
   4147      if (MOZ_UNLIKELY(end > mPositions.Length())) {
   4148        MOZ_ASSERT_UNREACHABLE(
   4149            "length of mPositions does not match characters "
   4150            "found by iterating content");
   4151        return false;
   4152      }
   4153      if (aForceStartOfChunk) {
   4154        // Note this character as starting a new anchored chunk.
   4155        mPositions[aIndex].mStartOfChunk = true;
   4156        aForceStartOfChunk = false;
   4157      }
   4158      while (aIndex < end) {
   4159        // Record whether each of these characters should start a new rendered
   4160        // run.  That is always the case for characters on a text path.
   4161        //
   4162        // Run boundaries due to rotate="" values are handled in
   4163        // DoGlyphPositioning.
   4164        if (aInTextPath || ShouldStartRunAtIndex(mPositions, aDeltas, aIndex)) {
   4165          mPositions[aIndex].mRunBoundary = true;
   4166        }
   4167        aIndex++;
   4168      }
   4169    }
   4170    return true;
   4171  }
   4172 
   4173  // Skip past elements that aren't text content elements.
   4174  if (!IsTextContentElement(aContent)) {
   4175    return true;
   4176  }
   4177 
   4178  if (aContent->IsSVGElement(nsGkAtoms::textPath)) {
   4179    // Any ‘y’ attributes on horizontal <textPath> elements are ignored.
   4180    // Similarly, for vertical <texPath>s x attributes are ignored.
   4181    // <textPath> elements behave as if they have x="0" y="0" on them, but only
   4182    // if there is not a value for the non-ignored coordinate that got inherited
   4183    // from a parent.  We skip this if there is no text content, so that empty
   4184    // <textPath>s don't interrupt the layout of text in the parent element.
   4185    if (HasTextContent(aContent)) {
   4186      if (MOZ_UNLIKELY(aIndex >= mPositions.Length())) {
   4187        MOZ_ASSERT_UNREACHABLE(
   4188            "length of mPositions does not match characters "
   4189            "found by iterating content");
   4190        return false;
   4191      }
   4192      bool vertical = GetWritingMode().IsVertical();
   4193      if (vertical || !mPositions[aIndex].IsXSpecified()) {
   4194        mPositions[aIndex].mPosition.x = 0.0;
   4195      }
   4196      if (!vertical || !mPositions[aIndex].IsYSpecified()) {
   4197        mPositions[aIndex].mPosition.y = 0.0;
   4198      }
   4199      mPositions[aIndex].mStartOfChunk = true;
   4200    }
   4201  } else if (!aContent->IsSVGElement(nsGkAtoms::a)) {
   4202    MOZ_ASSERT(aContent->IsSVGElement());
   4203 
   4204    // We have a text content element that can have x/y/dx/dy/rotate attributes.
   4205    SVGElement* element = static_cast<SVGElement*>(aContent);
   4206 
   4207    // Get x, y, dx, dy.
   4208    SVGUserUnitList x, y, dx, dy;
   4209    element->GetAnimatedLengthListValues(&x, &y, &dx, &dy, nullptr);
   4210 
   4211    // Get rotate.
   4212    const SVGNumberList* rotate = nullptr;
   4213    SVGAnimatedNumberList* animatedRotate =
   4214        element->GetAnimatedNumberList(nsGkAtoms::rotate);
   4215    if (animatedRotate) {
   4216      rotate = &animatedRotate->GetAnimValue();
   4217    }
   4218 
   4219    bool percentages = false;
   4220    uint32_t count = GetTextContentLength(aContent);
   4221 
   4222    if (MOZ_UNLIKELY(aIndex + count > mPositions.Length())) {
   4223      MOZ_ASSERT_UNREACHABLE(
   4224          "length of mPositions does not match characters "
   4225          "found by iterating content");
   4226      return false;
   4227    }
   4228 
   4229    // New text anchoring chunks start at each character assigned a position
   4230    // with x="" or y="", or if we forced one with aForceStartOfChunk due to
   4231    // being just after a <textPath>.
   4232    uint32_t newChunkCount = std::max(x.Length(), y.Length());
   4233    if (!newChunkCount && aForceStartOfChunk) {
   4234      newChunkCount = 1;
   4235    }
   4236    for (uint32_t i = 0, j = 0; i < newChunkCount && j < count; j++) {
   4237      if (!mPositions[aIndex + j].mUnaddressable) {
   4238        mPositions[aIndex + j].mStartOfChunk = true;
   4239        i++;
   4240      }
   4241    }
   4242 
   4243    // Copy dx="" and dy="" values into aDeltas.
   4244    if (!dx.IsEmpty() || !dy.IsEmpty()) {
   4245      // Any unspecified deltas when we grow the array just get left as 0s.
   4246      aDeltas.EnsureLengthAtLeast(aIndex + count);
   4247      for (uint32_t i = 0, j = 0; i < dx.Length() && j < count; j++) {
   4248        if (!mPositions[aIndex + j].mUnaddressable) {
   4249          aDeltas[aIndex + j].x = dx[i];
   4250          percentages = percentages || dx.HasPercentageValueAt(i);
   4251          i++;
   4252        }
   4253      }
   4254      for (uint32_t i = 0, j = 0; i < dy.Length() && j < count; j++) {
   4255        if (!mPositions[aIndex + j].mUnaddressable) {
   4256          aDeltas[aIndex + j].y = dy[i];
   4257          percentages = percentages || dy.HasPercentageValueAt(i);
   4258          i++;
   4259        }
   4260      }
   4261    }
   4262 
   4263    // Copy x="" and y="" values.
   4264    for (uint32_t i = 0, j = 0; i < x.Length() && j < count; j++) {
   4265      if (!mPositions[aIndex + j].mUnaddressable) {
   4266        mPositions[aIndex + j].mPosition.x = x[i];
   4267        percentages = percentages || x.HasPercentageValueAt(i);
   4268        i++;
   4269      }
   4270    }
   4271    for (uint32_t i = 0, j = 0; i < y.Length() && j < count; j++) {
   4272      if (!mPositions[aIndex + j].mUnaddressable) {
   4273        mPositions[aIndex + j].mPosition.y = y[i];
   4274        percentages = percentages || y.HasPercentageValueAt(i);
   4275        i++;
   4276      }
   4277    }
   4278 
   4279    // Copy rotate="" values.
   4280    if (rotate && !rotate->IsEmpty()) {
   4281      uint32_t i = 0, j = 0;
   4282      while (i < rotate->Length() && j < count) {
   4283        if (!mPositions[aIndex + j].mUnaddressable) {
   4284          mPositions[aIndex + j].mAngle = M_PI * (*rotate)[i] / 180.0;
   4285          i++;
   4286        }
   4287        j++;
   4288      }
   4289      // Propagate final rotate="" value to the end of this element.
   4290      while (j < count) {
   4291        mPositions[aIndex + j].mAngle = mPositions[aIndex + j - 1].mAngle;
   4292        j++;
   4293      }
   4294    }
   4295 
   4296    if (percentages) {
   4297      AddStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES);
   4298    }
   4299  }
   4300 
   4301  // Recurse to children.
   4302  bool inTextPath = aInTextPath || aContent->IsSVGElement(nsGkAtoms::textPath);
   4303  for (nsIContent* child = aContent->GetFirstChild(); child;
   4304       child = child->GetNextSibling()) {
   4305    bool ok = ResolvePositionsForNode(child, aIndex, inTextPath,
   4306                                      aForceStartOfChunk, aDeltas);
   4307    if (!ok) {
   4308      return false;
   4309    }
   4310  }
   4311 
   4312  if (aContent->IsSVGElement(nsGkAtoms::textPath)) {
   4313    // Force a new anchored chunk just after a <textPath>.
   4314    aForceStartOfChunk = true;
   4315  }
   4316 
   4317  return true;
   4318 }
   4319 
   4320 bool SVGTextFrame::ResolvePositions(nsTArray<gfxPoint>& aDeltas,
   4321                                    bool aRunPerGlyph) {
   4322  NS_ASSERTION(mPositions.IsEmpty(), "expected mPositions to be empty");
   4323  RemoveStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES);
   4324 
   4325  CharIterator it(this, CharIterator::eOriginal, /* aSubtree */ nullptr);
   4326  if (it.AtEnd()) {
   4327    return false;
   4328  }
   4329 
   4330  // We assume the first character position is (0,0) unless we later see
   4331  // otherwise, and note it as unaddressable if it is.
   4332  bool firstCharUnaddressable = it.IsOriginalCharUnaddressable();
   4333  mPositions.AppendElement(CharPosition::Unspecified(firstCharUnaddressable));
   4334 
   4335  // Fill in unspecified positions for all remaining characters, noting
   4336  // them as unaddressable if they are.
   4337  uint32_t index = 0;
   4338  while (it.Next()) {
   4339    while (++index < it.TextElementCharIndex()) {
   4340      mPositions.AppendElement(CharPosition::Unspecified(false));
   4341    }
   4342    mPositions.AppendElement(
   4343        CharPosition::Unspecified(it.IsOriginalCharUnaddressable()));
   4344  }
   4345  while (++index < it.TextElementCharIndex()) {
   4346    mPositions.AppendElement(CharPosition::Unspecified(false));
   4347  }
   4348 
   4349  // Recurse over the content and fill in character positions as we go.
   4350  bool forceStartOfChunk = false;
   4351  index = 0;
   4352  bool ok = ResolvePositionsForNode(mContent, index, aRunPerGlyph,
   4353                                    forceStartOfChunk, aDeltas);
   4354  return ok && index > 0;
   4355 }
   4356 
   4357 void SVGTextFrame::DetermineCharPositions(nsTArray<nsPoint>& aPositions) {
   4358  NS_ASSERTION(aPositions.IsEmpty(), "expected aPositions to be empty");
   4359 
   4360  nsPoint position;
   4361 
   4362  TextFrameIterator frit(this);
   4363  for (nsTextFrame* frame = frit.GetCurrent(); frame; frame = frit.GetNext()) {
   4364    gfxSkipCharsIterator it = frame->EnsureTextRun(nsTextFrame::eInflated);
   4365    gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated);
   4366    auto& provider = PropertyProviderFor(frame);
   4367 
   4368    // Reset the position to the new frame's position.
   4369    position = frit.Position();
   4370    if (textRun->IsVertical()) {
   4371      if (textRun->IsInlineReversed()) {
   4372        position.y += frame->GetRect().height;
   4373      }
   4374      position.x += GetBaselinePosition(frame, textRun, frit.DominantBaseline(),
   4375                                        mFontSizeScaleFactor);
   4376    } else {
   4377      if (textRun->IsInlineReversed()) {
   4378        position.x += frame->GetRect().width;
   4379      }
   4380      position.y += GetBaselinePosition(frame, textRun, frit.DominantBaseline(),
   4381                                        mFontSizeScaleFactor);
   4382    }
   4383 
   4384    // Any characters not in a frame, e.g. when display:none.
   4385    for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
   4386      aPositions.AppendElement(position);
   4387    }
   4388 
   4389    // Any white space characters trimmed at the start of the line of text.
   4390    nsTextFrame::TrimmedOffsets trimmedOffsets =
   4391        frame->GetTrimmedOffsets(frame->CharacterDataBuffer());
   4392    while (it.GetOriginalOffset() < trimmedOffsets.mStart) {
   4393      aPositions.AppendElement(position);
   4394      it.AdvanceOriginal(1);
   4395    }
   4396 
   4397    // Visible characters in the text frame.
   4398    while (it.GetOriginalOffset() < frame->GetContentEnd()) {
   4399      aPositions.AppendElement(position);
   4400      if (!it.IsOriginalCharSkipped()) {
   4401        // Accumulate partial ligature advance into position.  (We must get
   4402        // partial advances rather than get the advance of the whole ligature
   4403        // group / cluster at once, since the group may span text frames, and
   4404        // the PropertyProvider only has spacing information for the current
   4405        // text frame.)
   4406        uint32_t offset = it.GetSkippedOffset();
   4407        nscoord advance =
   4408            textRun->GetAdvanceWidth(Range(offset, offset + 1), &provider);
   4409        (textRun->IsVertical() ? position.y : position.x) +=
   4410            textRun->IsInlineReversed() ? -advance : advance;
   4411      }
   4412      it.AdvanceOriginal(1);
   4413    }
   4414  }
   4415 
   4416  // Finally any characters at the end that are not in a frame.
   4417  for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
   4418    aPositions.AppendElement(position);
   4419  }
   4420 
   4421  // Clear any cached PropertyProvider, to avoid risk of re-using it after
   4422  // style changes or other mutations may have invalidated it.
   4423  ForgetCachedProvider();
   4424 }
   4425 
   4426 /**
   4427 * Physical text-anchor values.
   4428 */
   4429 enum TextAnchorSide { eAnchorLeft, eAnchorMiddle, eAnchorRight };
   4430 
   4431 /**
   4432 * Converts a logical text-anchor value to its physical value, based on whether
   4433 * it is for an RTL frame.
   4434 */
   4435 static TextAnchorSide ConvertLogicalTextAnchorToPhysical(
   4436    StyleTextAnchor aTextAnchor, bool aIsRightToLeft) {
   4437  NS_ASSERTION(uint8_t(aTextAnchor) <= 3, "unexpected value for aTextAnchor");
   4438  if (!aIsRightToLeft) {
   4439    return TextAnchorSide(uint8_t(aTextAnchor));
   4440  }
   4441  return TextAnchorSide(2 - uint8_t(aTextAnchor));
   4442 }
   4443 
   4444 /**
   4445 * Shifts the recorded character positions for an anchored chunk.
   4446 *
   4447 * @param aCharPositions The recorded character positions.
   4448 * @param aChunkStart The character index the starts the anchored chunk.  This
   4449 *   character's initial position is the anchor point.
   4450 * @param aChunkEnd The character index just after the end of the anchored
   4451 *   chunk.
   4452 * @param aVisIStartEdge The left/top-most edge of any of the glyphs within the
   4453 *   anchored chunk.
   4454 * @param aVisIEndEdge The right/bottom-most edge of any of the glyphs within
   4455 *   the anchored chunk.
   4456 * @param aAnchorSide The direction to anchor.
   4457 */
   4458 static void ShiftAnchoredChunk(nsTArray<CharPosition>& aCharPositions,
   4459                               uint32_t aChunkStart, uint32_t aChunkEnd,
   4460                               gfxFloat aVisIStartEdge, gfxFloat aVisIEndEdge,
   4461                               TextAnchorSide aAnchorSide, bool aVertical) {
   4462  NS_ASSERTION(aVisIStartEdge <= aVisIEndEdge,
   4463               "unexpected anchored chunk edges");
   4464  NS_ASSERTION(aChunkStart < aChunkEnd,
   4465               "unexpected values for aChunkStart and aChunkEnd");
   4466 
   4467  gfxFloat shift = aVertical ? aCharPositions[aChunkStart].mPosition.y
   4468                             : aCharPositions[aChunkStart].mPosition.x;
   4469  switch (aAnchorSide) {
   4470    case eAnchorLeft:
   4471      shift -= aVisIStartEdge;
   4472      break;
   4473    case eAnchorMiddle:
   4474      shift -= (aVisIStartEdge + aVisIEndEdge) / 2;
   4475      break;
   4476    case eAnchorRight:
   4477      shift -= aVisIEndEdge;
   4478      break;
   4479    default:
   4480      MOZ_ASSERT_UNREACHABLE("unexpected value for aAnchorSide");
   4481  }
   4482 
   4483  if (shift != 0.0) {
   4484    if (aVertical) {
   4485      for (uint32_t i = aChunkStart; i < aChunkEnd; i++) {
   4486        aCharPositions[i].mPosition.y += shift;
   4487      }
   4488    } else {
   4489      for (uint32_t i = aChunkStart; i < aChunkEnd; i++) {
   4490        aCharPositions[i].mPosition.x += shift;
   4491      }
   4492    }
   4493  }
   4494 }
   4495 
   4496 void SVGTextFrame::AdjustChunksForLineBreaks() {
   4497  nsBlockFrame* block = do_QueryFrame(PrincipalChildList().FirstChild());
   4498  NS_ASSERTION(block, "expected block frame");
   4499 
   4500  nsBlockFrame::LineIterator line = block->LinesBegin();
   4501 
   4502  CharIterator it(this, CharIterator::eOriginal, /* aSubtree */ nullptr);
   4503  while (!it.AtEnd() && line != block->LinesEnd()) {
   4504    if (it.GetTextFrame() == line->mFirstChild) {
   4505      mPositions[it.TextElementCharIndex()].mStartOfChunk = true;
   4506      line++;
   4507    }
   4508    it.AdvancePastCurrentFrame();
   4509  }
   4510 }
   4511 
   4512 void SVGTextFrame::AdjustPositionsForClusters() {
   4513  nsPresContext* presContext = PresContext();
   4514 
   4515  // Find all of the characters that are in the middle of a cluster or
   4516  // ligature group, and adjust their positions and rotations to match
   4517  // the first character of the cluster/group.
   4518  //
   4519  // Also move the boundaries of text rendered runs and anchored chunks to
   4520  // not lie in the middle of cluster/group.
   4521 
   4522  // The partial advance of the current cluster or ligature group that we
   4523  // have accumulated.
   4524  gfxFloat partialAdvance = 0.0;
   4525 
   4526  CharIterator it(this, CharIterator::eUnskipped, /* aSubtree */ nullptr);
   4527  bool isFirst = true;
   4528  while (!it.AtEnd()) {
   4529    if (it.IsClusterAndLigatureGroupStart() || isFirst) {
   4530      // If we're at the start of a new cluster or ligature group, reset our
   4531      // accumulated partial advance. Also treat the beginning of the text as
   4532      // an anchor, even if it is a combining character and therefore was
   4533      // marked as being a Unicode cluster continuation.
   4534      partialAdvance = 0.0;
   4535      isFirst = false;
   4536    } else {
   4537      // Otherwise, we're in the middle of a cluster or ligature group, and
   4538      // we need to use the currently accumulated partial advance to adjust
   4539      // the character's position and rotation.
   4540 
   4541      // Find the start of the cluster/ligature group.
   4542      uint32_t charIndex = it.TextElementCharIndex();
   4543      uint32_t startIndex = it.GlyphStartTextElementCharIndex();
   4544      MOZ_ASSERT(charIndex != startIndex,
   4545                 "If the current character is in the middle of a cluster or "
   4546                 "ligature group, then charIndex must be different from "
   4547                 "startIndex");
   4548 
   4549      mPositions[charIndex].mClusterOrLigatureGroupMiddle = true;
   4550 
   4551      // Don't allow different rotations on ligature parts.
   4552      bool rotationAdjusted = false;
   4553      double angle = mPositions[startIndex].mAngle;
   4554      if (mPositions[charIndex].mAngle != angle) {
   4555        mPositions[charIndex].mAngle = angle;
   4556        rotationAdjusted = true;
   4557      }
   4558 
   4559      // Update the character position.
   4560      gfxFloat advance = partialAdvance / mFontSizeScaleFactor;
   4561      const gfxTextRun* textRun = it.TextRun();
   4562      gfxPoint direction = gfxPoint(cos(angle), sin(angle)) *
   4563                           (textRun->IsInlineReversed() ? -1.0 : 1.0);
   4564      if (textRun->IsVertical()) {
   4565        std::swap(direction.x, direction.y);
   4566      }
   4567      mPositions[charIndex].mPosition =
   4568          mPositions[startIndex].mPosition + direction * advance;
   4569 
   4570      // Ensure any runs that would end in the middle of a ligature now end just
   4571      // after the ligature.
   4572      if (mPositions[charIndex].mRunBoundary) {
   4573        mPositions[charIndex].mRunBoundary = false;
   4574        if (charIndex + 1 < mPositions.Length()) {
   4575          mPositions[charIndex + 1].mRunBoundary = true;
   4576        }
   4577      } else if (rotationAdjusted) {
   4578        if (charIndex + 1 < mPositions.Length()) {
   4579          mPositions[charIndex + 1].mRunBoundary = true;
   4580        }
   4581      }
   4582 
   4583      // Ensure any anchored chunks that would begin in the middle of a ligature
   4584      // now begin just after the ligature.
   4585      if (mPositions[charIndex].mStartOfChunk) {
   4586        mPositions[charIndex].mStartOfChunk = false;
   4587        if (charIndex + 1 < mPositions.Length()) {
   4588          mPositions[charIndex + 1].mStartOfChunk = true;
   4589        }
   4590      }
   4591    }
   4592 
   4593    // Accumulate the current character's partial advance.
   4594    partialAdvance += it.GetAdvance(presContext);
   4595 
   4596    it.Next();
   4597  }
   4598 }
   4599 
   4600 already_AddRefed<Path> SVGTextFrame::GetTextPath(nsIFrame* aTextPathFrame) {
   4601  nsIContent* content = aTextPathFrame->GetContent();
   4602  SVGTextPathElement* tp = static_cast<SVGTextPathElement*>(content);
   4603  if (tp->mPath.IsRendered()) {
   4604    // This is just an attribute so there's no transform that can apply
   4605    // so we can just return the path directly.
   4606    return tp->mPath.GetAnimValue().BuildPathForMeasuring(
   4607        aTextPathFrame->Style()->EffectiveZoom().ToFloat());
   4608  }
   4609 
   4610  SVGGeometryElement* geomElement =
   4611      SVGObserverUtils::GetAndObserveTextPathsPath(aTextPathFrame);
   4612  if (!geomElement) {
   4613    return nullptr;
   4614  }
   4615 
   4616  RefPtr<Path> path = geomElement->GetOrBuildPathForMeasuring();
   4617  if (!path) {
   4618    return nullptr;
   4619  }
   4620 
   4621  // Apply the geometry element's transform if appropriate.
   4622  auto matrix = geomElement->LocalTransform();
   4623  if (!matrix.IsIdentity()) {
   4624    Path::Transform(path, matrix);
   4625  }
   4626 
   4627  return path.forget();
   4628 }
   4629 
   4630 gfxFloat SVGTextFrame::GetOffsetScale(nsIFrame* aTextPathFrame) {
   4631  nsIContent* content = aTextPathFrame->GetContent();
   4632  SVGTextPathElement* tp = static_cast<SVGTextPathElement*>(content);
   4633  if (tp->mPath.IsRendered()) {
   4634    // A path attribute has no pathLength or transform
   4635    // so we return a unit scale.
   4636    return 1.0;
   4637  }
   4638 
   4639  SVGGeometryElement* geomElement =
   4640      SVGObserverUtils::GetAndObserveTextPathsPath(aTextPathFrame);
   4641  if (!geomElement) {
   4642    return 1.0;
   4643  }
   4644  return geomElement->GetPathLengthScale(SVGGeometryElement::eForTextPath);
   4645 }
   4646 
   4647 gfxFloat SVGTextFrame::GetStartOffset(nsIFrame* aTextPathFrame) {
   4648  SVGTextPathElement* tp =
   4649      static_cast<SVGTextPathElement*>(aTextPathFrame->GetContent());
   4650  SVGAnimatedLength* length =
   4651      &tp->mLengthAttributes[SVGTextPathElement::STARTOFFSET];
   4652 
   4653  if (length->IsPercentage()) {
   4654    if (!std::isfinite(GetOffsetScale(aTextPathFrame))) {
   4655      // Either pathLength="0" for this path or the path has 0 length.
   4656      return 0.0;
   4657    }
   4658    RefPtr<Path> data = GetTextPath(aTextPathFrame);
   4659    return data ? length->GetAnimValInSpecifiedUnits() * data->ComputeLength() /
   4660                      100.0
   4661                : 0.0;
   4662  }
   4663  float lengthValue = length->GetAnimValueWithZoom(tp);
   4664  // If offsetScale is infinity we want to return 0 not NaN
   4665  return lengthValue == 0 ? 0.0 : lengthValue * GetOffsetScale(aTextPathFrame);
   4666 }
   4667 
   4668 void SVGTextFrame::DoTextPathLayout() {
   4669  nsPresContext* context = PresContext();
   4670 
   4671  CharIterator it(this, CharIterator::eOriginal, /* aSubtree */ nullptr);
   4672  while (!it.AtEnd()) {
   4673    nsIFrame* textPathFrame = it.TextPathFrame();
   4674    if (!textPathFrame) {
   4675      // Skip past this frame if we're not in a text path.
   4676      it.AdvancePastCurrentFrame();
   4677      continue;
   4678    }
   4679 
   4680    // Get the path itself.
   4681    RefPtr<Path> path = GetTextPath(textPathFrame);
   4682    if (!path) {
   4683      uint32_t start = it.TextElementCharIndex();
   4684      it.AdvancePastCurrentTextPathFrame();
   4685      uint32_t end = it.TextElementCharIndex();
   4686      for (uint32_t i = start; i < end; i++) {
   4687        mPositions[i].mHidden = true;
   4688      }
   4689      continue;
   4690    }
   4691 
   4692    SVGTextPathElement* textPath =
   4693        static_cast<SVGTextPathElement*>(textPathFrame->GetContent());
   4694    uint16_t side =
   4695        textPath->EnumAttributes()[SVGTextPathElement::SIDE].GetAnimValue();
   4696 
   4697    gfxFloat offset = GetStartOffset(textPathFrame);
   4698    Float pathLength = path->ComputeLength();
   4699 
   4700    // If the first character within the text path is in the middle of a
   4701    // cluster or ligature group, just skip it and don't apply text path
   4702    // positioning.
   4703    while (!it.AtEnd()) {
   4704      if (it.IsOriginalCharSkipped()) {
   4705        it.Next();
   4706        continue;
   4707      }
   4708      if (it.IsClusterAndLigatureGroupStart()) {
   4709        break;
   4710      }
   4711      it.Next();
   4712    }
   4713 
   4714    bool skippedEndOfTextPath = false;
   4715 
   4716    // Loop for each character in the text path.
   4717    while (!it.AtEnd() && it.TextPathFrame() &&
   4718           it.TextPathFrame()->GetContent() == textPath) {
   4719      // The index of the cluster or ligature group's first character.
   4720      uint32_t i = it.TextElementCharIndex();
   4721 
   4722      // The index of the next character of the cluster or ligature.
   4723      // We track this as we loop over the characters below so that we
   4724      // can detect undisplayed characters and append entries into
   4725      // partialAdvances for them.
   4726      uint32_t j = i + 1;
   4727 
   4728      MOZ_ASSERT(!mPositions[i].mClusterOrLigatureGroupMiddle);
   4729 
   4730      const gfxTextRun* textRun = it.TextRun();
   4731      bool vertical = textRun->IsVertical();
   4732 
   4733      // Compute cumulative advances for each character of the cluster or
   4734      // ligature group.
   4735      AutoTArray<gfxFloat, 4> partialAdvances;
   4736      gfxFloat partialAdvance = it.GetAdvance(context);
   4737      partialAdvances.AppendElement(partialAdvance);
   4738      while (it.Next()) {
   4739        // Append entries for any undisplayed characters the CharIterator
   4740        // skipped over.
   4741        MOZ_ASSERT(j <= it.TextElementCharIndex());
   4742        while (j < it.TextElementCharIndex()) {
   4743          partialAdvances.AppendElement(partialAdvance);
   4744          ++j;
   4745        }
   4746        // This loop may end up outside of the current text path, but
   4747        // that's OK; we'll consider any complete cluster or ligature
   4748        // group that begins inside the text path as being affected
   4749        // by it.
   4750        if (it.IsOriginalCharSkipped()) {
   4751          if (!it.TextPathFrame()) {
   4752            skippedEndOfTextPath = true;
   4753            break;
   4754          }
   4755          // Leave partialAdvance unchanged.
   4756        } else if (it.IsClusterAndLigatureGroupStart()) {
   4757          break;
   4758        } else {
   4759          partialAdvance += it.GetAdvance(context);
   4760        }
   4761        partialAdvances.AppendElement(partialAdvance);
   4762      }
   4763 
   4764      if (!skippedEndOfTextPath) {
   4765        // Any final undisplayed characters the CharIterator skipped over.
   4766        MOZ_ASSERT(j <= it.TextElementCharIndex());
   4767        while (j < it.TextElementCharIndex()) {
   4768          partialAdvances.AppendElement(partialAdvance);
   4769          ++j;
   4770        }
   4771      }
   4772 
   4773      gfxFloat halfAdvance =
   4774          partialAdvances.LastElement() / mFontSizeScaleFactor / 2.0;
   4775      if (textRun->IsInlineReversed()) {
   4776        halfAdvance = -halfAdvance;
   4777      }
   4778      gfxFloat midx =
   4779          (vertical ? mPositions[i].mPosition.y : mPositions[i].mPosition.x) +
   4780          halfAdvance + offset;
   4781 
   4782      // Hide the character if it falls off the end of the path.
   4783      mPositions[i].mHidden = midx < 0 || midx > pathLength;
   4784 
   4785      // Position the character on the path at the right angle.
   4786      Point tangent;  // Unit vector tangent to the point we find.
   4787      Point pt;
   4788      if (side == TEXTPATH_SIDETYPE_RIGHT) {
   4789        pt = path->ComputePointAtLength(Float(pathLength - midx), &tangent);
   4790        tangent = -tangent;
   4791      } else {
   4792        pt = path->ComputePointAtLength(Float(midx), &tangent);
   4793      }
   4794      Float rotation = vertical ? atan2f(-tangent.x, tangent.y)
   4795                                : atan2f(tangent.y, tangent.x);
   4796      Point normal(-tangent.y, tangent.x);  // Unit vector normal to the point.
   4797      Point offsetFromPath = normal * (vertical ? -mPositions[i].mPosition.x
   4798                                                : mPositions[i].mPosition.y);
   4799      pt += offsetFromPath;
   4800      Point direction = textRun->IsInlineReversed() ? -tangent : tangent;
   4801      mPositions[i].mPosition =
   4802          ThebesPoint(pt) - ThebesPoint(direction) * halfAdvance;
   4803      mPositions[i].mAngle += rotation;
   4804 
   4805      // Position any characters for a partial ligature.
   4806      for (uint32_t k = i + 1; k < j; k++) {
   4807        gfxPoint partialAdvance = ThebesPoint(direction) *
   4808                                  partialAdvances[k - i] / mFontSizeScaleFactor;
   4809        mPositions[k].mPosition = mPositions[i].mPosition + partialAdvance;
   4810        mPositions[k].mAngle = mPositions[i].mAngle;
   4811        mPositions[k].mHidden = mPositions[i].mHidden;
   4812      }
   4813    }
   4814  }
   4815 }
   4816 
   4817 void SVGTextFrame::DoAnchoring() {
   4818  nsPresContext* presContext = PresContext();
   4819 
   4820  CharIterator it(this, CharIterator::eOriginal, /* aSubtree */ nullptr);
   4821 
   4822  // Don't need to worry about skipped or trimmed characters.
   4823  while (!it.AtEnd() &&
   4824         (it.IsOriginalCharSkipped() || it.IsOriginalCharTrimmed())) {
   4825    it.Next();
   4826  }
   4827 
   4828  bool vertical = GetWritingMode().IsVertical();
   4829  for (uint32_t start = it.TextElementCharIndex(); start < mPositions.Length();
   4830       start = it.TextElementCharIndex()) {
   4831    it.AdvanceToCharacter(start);
   4832    nsTextFrame* chunkFrame = it.GetTextFrame();
   4833 
   4834    // Measure characters in this chunk to find the left-most and right-most
   4835    // edges of all glyphs within the chunk.
   4836    uint32_t index = it.TextElementCharIndex();
   4837    uint32_t end = start;
   4838    gfxFloat left = std::numeric_limits<gfxFloat>::infinity();
   4839    gfxFloat right = -std::numeric_limits<gfxFloat>::infinity();
   4840    do {
   4841      if (!it.IsOriginalCharSkipped() && !it.IsOriginalCharTrimmed()) {
   4842        gfxFloat advance = it.GetAdvance(presContext) / mFontSizeScaleFactor;
   4843        const gfxTextRun* textRun = it.TextRun();
   4844        gfxFloat pos = textRun->IsVertical() ? mPositions[index].mPosition.y
   4845                                             : mPositions[index].mPosition.x;
   4846        if (textRun->IsInlineReversed()) {
   4847          left = std::min(left, pos - advance);
   4848          right = std::max(right, pos);
   4849        } else {
   4850          left = std::min(left, pos);
   4851          right = std::max(right, pos + advance);
   4852        }
   4853      }
   4854      it.Next();
   4855      index = end = it.TextElementCharIndex();
   4856    } while (!it.AtEnd() && !mPositions[end].mStartOfChunk);
   4857 
   4858    if (left != std::numeric_limits<gfxFloat>::infinity()) {
   4859      bool isRTL =
   4860          chunkFrame->StyleVisibility()->mDirection == StyleDirection::Rtl;
   4861      TextAnchorSide anchor = ConvertLogicalTextAnchorToPhysical(
   4862          chunkFrame->StyleSVG()->mTextAnchor, isRTL);
   4863 
   4864      ShiftAnchoredChunk(mPositions, start, end, left, right, anchor, vertical);
   4865    }
   4866  }
   4867 }
   4868 
   4869 void SVGTextFrame::DoGlyphPositioning() {
   4870  mPositions.Clear();
   4871  RemoveStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
   4872 
   4873  nsIFrame* kid = PrincipalChildList().FirstChild();
   4874  if (kid && kid->IsSubtreeDirty()) {
   4875    MOZ_ASSERT(false, "should have already reflowed the kid");
   4876    return;
   4877  }
   4878 
   4879  // Since we can be called directly via GetBBoxContribution, our correspondence
   4880  // may not be up to date.
   4881  TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
   4882 
   4883  // Determine the positions of each character in app units.
   4884  AutoTArray<nsPoint, 64> charPositions;
   4885  DetermineCharPositions(charPositions);
   4886 
   4887  if (charPositions.IsEmpty()) {
   4888    // No characters, so nothing to do.
   4889    return;
   4890  }
   4891 
   4892  // If the textLength="" attribute was specified, then we need ResolvePositions
   4893  // to record that a new run starts with each glyph.
   4894  SVGTextContentElement* element =
   4895      static_cast<SVGTextContentElement*>(GetContent());
   4896  SVGAnimatedLength* textLengthAttr =
   4897      element->GetAnimatedLength(nsGkAtoms::textLength);
   4898  uint16_t lengthAdjust =
   4899      element->EnumAttributes()[SVGTextContentElement::LENGTHADJUST]
   4900          .GetAnimValue();
   4901  bool adjustingTextLength = textLengthAttr->IsExplicitlySet();
   4902  float expectedTextLength = textLengthAttr->GetAnimValueWithZoom(element);
   4903 
   4904  if (adjustingTextLength &&
   4905      (expectedTextLength < 0.0f || lengthAdjust == LENGTHADJUST_UNKNOWN)) {
   4906    // If textLength="" is less than zero or lengthAdjust is unknown, ignore it.
   4907    adjustingTextLength = false;
   4908  }
   4909 
   4910  // Get the x, y, dx, dy, rotate values for the subtree.
   4911  AutoTArray<gfxPoint, 16> deltas;
   4912  if (!ResolvePositions(deltas, adjustingTextLength)) {
   4913    // If ResolvePositions returned false, it means either there were some
   4914    // characters in the DOM but none of them are displayed, or there was
   4915    // an error in processing mPositions.  Clear out mPositions so that we don't
   4916    // attempt to do any painting later.
   4917    mPositions.Clear();
   4918    return;
   4919  }
   4920 
   4921  // XXX We might be able to do less work when there is at most a single
   4922  // x/y/dx/dy position.
   4923 
   4924  // Truncate the positioning arrays to the actual number of characters present.
   4925  TruncateTo(deltas, charPositions);
   4926  TruncateTo(mPositions, charPositions);
   4927 
   4928  // Fill in an unspecified position for the first addressable character.
   4929  uint32_t first = 0;
   4930  while (first + 1 < mPositions.Length() && mPositions[first].mUnaddressable) {
   4931    ++first;
   4932  }
   4933  if (!mPositions[first].IsXSpecified()) {
   4934    mPositions[first].mPosition.x = 0.0;
   4935  }
   4936  if (!mPositions[first].IsYSpecified()) {
   4937    mPositions[first].mPosition.y = 0.0;
   4938  }
   4939  if (!mPositions[first].IsAngleSpecified()) {
   4940    mPositions[first].mAngle = 0.0;
   4941  }
   4942 
   4943  nsPresContext* presContext = PresContext();
   4944  bool vertical = GetWritingMode().IsVertical();
   4945 
   4946  float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
   4947      presContext->AppUnitsPerDevPixel());
   4948  double factor = cssPxPerDevPx / mFontSizeScaleFactor;
   4949 
   4950  // Determine how much to compress or expand glyph positions due to
   4951  // textLength="" and lengthAdjust="".
   4952  double adjustment = 0.0;
   4953  mLengthAdjustScaleFactor = 1.0f;
   4954  if (adjustingTextLength) {
   4955    nscoord frameLength =
   4956        vertical ? PrincipalChildList().FirstChild()->GetRect().height
   4957                 : PrincipalChildList().FirstChild()->GetRect().width;
   4958    float actualTextLength = static_cast<float>(
   4959        presContext->AppUnitsToGfxUnits(frameLength) * factor);
   4960 
   4961    switch (lengthAdjust) {
   4962      case LENGTHADJUST_SPACINGANDGLYPHS:
   4963        // Scale the glyphs and their positions.
   4964        if (actualTextLength > 0) {
   4965          mLengthAdjustScaleFactor = expectedTextLength / actualTextLength;
   4966        }
   4967        break;
   4968 
   4969      default:
   4970        MOZ_ASSERT(lengthAdjust == LENGTHADJUST_SPACING);
   4971        // Just add space between each glyph.
   4972        int32_t adjustableSpaces = 0;
   4973        for (uint32_t i = 1; i < mPositions.Length(); i++) {
   4974          if (!mPositions[i].mUnaddressable) {
   4975            adjustableSpaces++;
   4976          }
   4977        }
   4978        if (adjustableSpaces) {
   4979          adjustment =
   4980              (expectedTextLength - actualTextLength) / adjustableSpaces;
   4981        }
   4982        break;
   4983    }
   4984  }
   4985 
   4986  // Fill in any unspecified character positions based on the positions recorded
   4987  // in charPositions, and also add in the dx/dy values.
   4988  if (!deltas.IsEmpty()) {
   4989    mPositions[0].mPosition += deltas[0];
   4990  }
   4991 
   4992  gfxFloat xLengthAdjustFactor = vertical ? 1.0 : mLengthAdjustScaleFactor;
   4993  gfxFloat yLengthAdjustFactor = vertical ? mLengthAdjustScaleFactor : 1.0;
   4994  for (uint32_t i = 1; i < mPositions.Length(); i++) {
   4995    // Fill in unspecified x position.
   4996    if (!mPositions[i].IsXSpecified()) {
   4997      nscoord d = charPositions[i].x - charPositions[i - 1].x;
   4998      mPositions[i].mPosition.x =
   4999          mPositions[i - 1].mPosition.x +
   5000          presContext->AppUnitsToGfxUnits(d) * factor * xLengthAdjustFactor;
   5001      if (!vertical && !mPositions[i].mUnaddressable) {
   5002        mPositions[i].mPosition.x += adjustment;
   5003      }
   5004    }
   5005    // Fill in unspecified y position.
   5006    if (!mPositions[i].IsYSpecified()) {
   5007      nscoord d = charPositions[i].y - charPositions[i - 1].y;
   5008      mPositions[i].mPosition.y =
   5009          mPositions[i - 1].mPosition.y +
   5010          presContext->AppUnitsToGfxUnits(d) * factor * yLengthAdjustFactor;
   5011      if (vertical && !mPositions[i].mUnaddressable) {
   5012        mPositions[i].mPosition.y += adjustment;
   5013      }
   5014    }
   5015    // Add in dx/dy.
   5016    if (i < deltas.Length()) {
   5017      mPositions[i].mPosition += deltas[i];
   5018    }
   5019    // Fill in unspecified rotation values.
   5020    if (!mPositions[i].IsAngleSpecified()) {
   5021      mPositions[i].mAngle = 0.0f;
   5022    }
   5023  }
   5024 
   5025  MOZ_ASSERT(mPositions.Length() == charPositions.Length());
   5026 
   5027  AdjustChunksForLineBreaks();
   5028  AdjustPositionsForClusters();
   5029  DoAnchoring();
   5030  DoTextPathLayout();
   5031 }
   5032 
   5033 bool SVGTextFrame::ShouldRenderAsPath(nsTextFrame* aFrame,
   5034                                      SVGContextPaint* aContextPaint,
   5035                                      bool& aShouldPaintSVGGlyphs) {
   5036  // Rendering to a clip path.
   5037  if (HasAnyStateBits(NS_STATE_SVG_CLIPPATH_CHILD)) {
   5038    aShouldPaintSVGGlyphs = false;
   5039    return true;
   5040  }
   5041 
   5042  aShouldPaintSVGGlyphs = true;
   5043 
   5044  const nsStyleSVG* style = aFrame->StyleSVG();
   5045 
   5046  // Fill is a non-solid paint or is not opaque.
   5047  if (!(style->mFill.kind.IsNone() ||
   5048        (style->mFill.kind.IsColor() &&
   5049         SVGUtils::GetOpacity(style->mFillOpacity, aContextPaint) == 1.0f))) {
   5050    return true;
   5051  }
   5052 
   5053  // If we're going to need to draw a non-opaque shadow.
   5054  // It's possible nsTextFrame will support non-opaque shadows in the future,
   5055  // in which case this test can be removed.
   5056  if (style->mFill.kind.IsColor() && aFrame->StyleText()->HasTextShadow() &&
   5057      NS_GET_A(style->mFill.kind.AsColor().CalcColor(*aFrame->Style())) !=
   5058          0xFF) {
   5059    return true;
   5060  }
   5061 
   5062  // Text has a stroke.
   5063  if (style->HasStroke()) {
   5064    if (style->mStrokeWidth.IsContextValue()) {
   5065      return true;
   5066    }
   5067    if (SVGContentUtils::CoordToFloat(
   5068            static_cast<SVGElement*>(GetContent()),
   5069            style->mStrokeWidth.AsLengthPercentage()) > 0) {
   5070      return true;
   5071    }
   5072  }
   5073 
   5074  return false;
   5075 }
   5076 
   5077 void SVGTextFrame::ScheduleReflowSVG() {
   5078  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   5079    ScheduleReflowSVGNonDisplayText(
   5080        IntrinsicDirty::FrameAncestorsAndDescendants);
   5081  } else {
   5082    SVGUtils::ScheduleReflowSVG(this);
   5083  }
   5084 }
   5085 
   5086 void SVGTextFrame::NotifyGlyphMetricsChange(bool aUpdateTextCorrespondence) {
   5087  if (aUpdateTextCorrespondence) {
   5088    AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY);
   5089  }
   5090  AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
   5091  nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), RestyleHint{0},
   5092                                  nsChangeHint_InvalidateRenderingObservers);
   5093  ScheduleReflowSVG();
   5094 }
   5095 
   5096 void SVGTextFrame::UpdateGlyphPositioning() {
   5097  nsIFrame* kid = PrincipalChildList().FirstChild();
   5098  if (!kid) {
   5099    return;
   5100  }
   5101 
   5102  if (HasAnyStateBits(NS_STATE_SVG_POSITIONING_DIRTY)) {
   5103    DoGlyphPositioning();
   5104  }
   5105 }
   5106 
   5107 void SVGTextFrame::MaybeResolveBidiForAnonymousBlockChild() {
   5108  nsIFrame* kid = PrincipalChildList().FirstChild();
   5109 
   5110  if (kid && kid->HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
   5111      PresContext()->BidiEnabled()) {
   5112    MOZ_ASSERT(static_cast<nsBlockFrame*>(do_QueryFrame(kid)),
   5113               "Expect anonymous child to be an nsBlockFrame");
   5114    nsBidiPresUtils::Resolve(static_cast<nsBlockFrame*>(kid));
   5115  }
   5116 }
   5117 
   5118 void SVGTextFrame::MaybeReflowAnonymousBlockChild() {
   5119  nsIFrame* kid = PrincipalChildList().FirstChild();
   5120  if (!kid) {
   5121    return;
   5122  }
   5123 
   5124  NS_ASSERTION(!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW),
   5125               "should not be in reflow when about to reflow again");
   5126 
   5127  if (IsSubtreeDirty()) {
   5128    if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
   5129      // If we require a full reflow, ensure our kid is marked fully dirty.
   5130      // (Note that our anonymous nsBlockFrame is not an ISVGDisplayableFrame,
   5131      // so even when we are called via our ReflowSVG this will not be done for
   5132      // us by SVGDisplayContainerFrame::ReflowSVG.)
   5133      kid->MarkSubtreeDirty();
   5134    }
   5135 
   5136    // The RecordCorrespondence and DoReflow calls can result in new text frames
   5137    // being created (due to bidi resolution or reflow).  We set this bit to
   5138    // guard against unnecessarily calling back in to
   5139    // ScheduleReflowSVGNonDisplayText from nsIFrame::DidSetComputedStyle on
   5140    // those new text frames.
   5141    AddStateBits(NS_STATE_SVG_TEXT_IN_REFLOW);
   5142 
   5143    TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
   5144 
   5145    MOZ_ASSERT(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this),
   5146               "should be under ReflowSVG");
   5147    nsPresContext::InterruptPreventer noInterrupts(PresContext());
   5148    DoReflow();
   5149 
   5150    RemoveStateBits(NS_STATE_SVG_TEXT_IN_REFLOW);
   5151  }
   5152 }
   5153 
   5154 void SVGTextFrame::DoReflow() {
   5155  MOZ_ASSERT(HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW));
   5156 
   5157  // Since we are going to reflow the anonymous block frame, we will
   5158  // need to update mPositions.
   5159  // We also mark our text correspondence as dirty since we can end up needing
   5160  // reflow in ways that do not set NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY.
   5161  // (We'd then fail the "expected a TextNodeCorrespondenceProperty" assertion
   5162  // when UpdateGlyphPositioning() is called after we return.)
   5163  AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
   5164               NS_STATE_SVG_POSITIONING_DIRTY);
   5165 
   5166  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   5167    // Normally, these dirty flags would be cleared in ReflowSVG(), but that
   5168    // doesn't get called for non-display frames. We don't want to reflow our
   5169    // descendants every time SVGTextFrame::PaintSVG makes sure that we have
   5170    // valid positions by calling UpdateGlyphPositioning(), so we need to clear
   5171    // these dirty bits. Note that this also breaks an invalidation loop where
   5172    // our descendants invalidate as they reflow, which invalidates rendering
   5173    // observers, which reschedules the frame that is currently painting by
   5174    // referencing us to paint again. See bug 839958 comment 7. Hopefully we
   5175    // will break that loop more convincingly at some point.
   5176    RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
   5177  }
   5178 
   5179  // Forget any cached measurements of one of our children.
   5180  mFrameForCachedRanges = nullptr;
   5181 
   5182  nsPresContext* presContext = PresContext();
   5183  nsIFrame* kid = PrincipalChildList().FirstChild();
   5184  if (!kid) {
   5185    return;
   5186  }
   5187 
   5188  UniquePtr<gfxContext> renderingContext =
   5189      presContext->PresShell()->CreateReferenceRenderingContext();
   5190 
   5191  if (UpdateFontSizeScaleFactor()) {
   5192    // If the font size scale factor changed, we need the block to report
   5193    // an updated preferred width.
   5194    kid->MarkIntrinsicISizesDirty();
   5195  }
   5196 
   5197  const IntrinsicSizeInput input(renderingContext.get(), Nothing(), Nothing());
   5198  nscoord inlineSize = kid->GetPrefISize(input);
   5199  WritingMode wm = kid->GetWritingMode();
   5200  ReflowInput reflowInput(presContext, kid, renderingContext.get(),
   5201                          LogicalSize(wm, inlineSize, NS_UNCONSTRAINEDSIZE));
   5202  ReflowOutput desiredSize(reflowInput);
   5203  nsReflowStatus status;
   5204 
   5205  NS_ASSERTION(
   5206      reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) &&
   5207          reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
   5208      "style system should ensure that :-moz-svg-text "
   5209      "does not get styled");
   5210 
   5211  kid->Reflow(presContext, desiredSize, reflowInput, status);
   5212  kid->DidReflow(presContext, &reflowInput);
   5213  kid->SetSize(wm, desiredSize.Size(wm));
   5214 }
   5215 
   5216 // Usable font size range in devpixels / user-units
   5217 #define CLAMP_MIN_SIZE 8.0
   5218 #define CLAMP_MAX_SIZE 200.0
   5219 #define PRECISE_SIZE 200.0
   5220 
   5221 bool SVGTextFrame::UpdateFontSizeScaleFactor() {
   5222  float contextScale = GetContextScale(this);
   5223  mLastContextScale = contextScale;
   5224 
   5225  double oldFontSizeScaleFactor = mFontSizeScaleFactor;
   5226 
   5227  bool geometricPrecision = false;
   5228  // We may need to invert a matrix with these values later.
   5229  CSSCoord min = std::sqrt(std::numeric_limits<float>::max());
   5230  CSSCoord max = std::sqrt(std::numeric_limits<float>::min());
   5231  bool anyText = false;
   5232 
   5233  // Find the minimum and maximum font sizes used over all the
   5234  // nsTextFrames.
   5235  TextFrameIterator it(this);
   5236  nsTextFrame* f = it.GetCurrent();
   5237  while (f) {
   5238    if (!geometricPrecision) {
   5239      // Unfortunately we can't treat text-rendering:geometricPrecision
   5240      // separately for each text frame.
   5241      geometricPrecision = f->StyleText()->mTextRendering ==
   5242                           StyleTextRendering::Geometricprecision;
   5243    }
   5244    const auto& fontSize = f->StyleFont()->mFont.size;
   5245    if (!fontSize.IsZero()) {
   5246      min = std::min(min, fontSize.ToCSSPixels());
   5247      max = std::max(max, fontSize.ToCSSPixels());
   5248      anyText = true;
   5249    }
   5250    f = it.GetNext();
   5251  }
   5252 
   5253  if (!anyText) {
   5254    // No text, so no need for scaling.
   5255    mFontSizeScaleFactor = 1.0;
   5256    return mFontSizeScaleFactor != oldFontSizeScaleFactor;
   5257  }
   5258 
   5259  if (geometricPrecision) {
   5260    // We want to ensure minSize is scaled to PRECISE_SIZE.
   5261    mFontSizeScaleFactor = PRECISE_SIZE / min;
   5262    return mFontSizeScaleFactor != oldFontSizeScaleFactor;
   5263  }
   5264 
   5265  double minTextRunSize = min * contextScale;
   5266  double maxTextRunSize = max * contextScale;
   5267 
   5268  if (minTextRunSize >= CLAMP_MIN_SIZE && maxTextRunSize <= CLAMP_MAX_SIZE) {
   5269    // We are already in the ideal font size range for all text frames,
   5270    // so we only have to take into account the contextScale.
   5271    mFontSizeScaleFactor = contextScale;
   5272  } else if (max / min > CLAMP_MAX_SIZE / CLAMP_MIN_SIZE) {
   5273    // We can't scale the font sizes so that all of the text frames lie
   5274    // within our ideal font size range.
   5275    // Heuristically, if the maxTextRunSize is within the CLAMP_MAX_SIZE
   5276    // as a reasonable value, it's likely to be the user's intent to
   5277    // get a valid font for the maxTextRunSize one, we should honor it.
   5278    // The same for minTextRunSize.
   5279    if (maxTextRunSize <= CLAMP_MAX_SIZE) {
   5280      mFontSizeScaleFactor = CLAMP_MAX_SIZE / max;
   5281    } else if (minTextRunSize >= CLAMP_MIN_SIZE) {
   5282      mFontSizeScaleFactor = CLAMP_MIN_SIZE / min;
   5283    } else {
   5284      // So maxTextRunSize is too big, minTextRunSize is too small,
   5285      // we can't really do anything for this case, just leave it as is.
   5286      mFontSizeScaleFactor = contextScale;
   5287    }
   5288  } else if (minTextRunSize < CLAMP_MIN_SIZE) {
   5289    mFontSizeScaleFactor = CLAMP_MIN_SIZE / min;
   5290  } else {
   5291    mFontSizeScaleFactor = CLAMP_MAX_SIZE / max;
   5292  }
   5293 
   5294  return mFontSizeScaleFactor != oldFontSizeScaleFactor;
   5295 }
   5296 
   5297 double SVGTextFrame::GetFontSizeScaleFactor() const {
   5298  return mFontSizeScaleFactor;
   5299 }
   5300 
   5301 /**
   5302 * Take aPoint, which is in the <text> element's user space, and convert
   5303 * it to the appropriate frame user space of aChildFrame according to
   5304 * which rendered run the point hits.
   5305 */
   5306 Point SVGTextFrame::TransformFramePointToTextChild(
   5307    const Point& aPoint, const nsIFrame* aChildFrame) {
   5308  NS_ASSERTION(aChildFrame && nsLayoutUtils::GetClosestFrameOfType(
   5309                                  aChildFrame->GetParent(),
   5310                                  LayoutFrameType::SVGText) == this,
   5311               "aChildFrame must be a descendant of this frame");
   5312 
   5313  UpdateGlyphPositioning();
   5314 
   5315  nsPresContext* presContext = PresContext();
   5316 
   5317  // Add in the mRect offset to aPoint, as that will have been taken into
   5318  // account when transforming the point from the ancestor frame down
   5319  // to this one.
   5320  float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
   5321      presContext->AppUnitsPerDevPixel());
   5322  float factor = AppUnitsPerCSSPixel();
   5323  Point framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
   5324                      NSAppUnitsToFloatPixels(mRect.y, factor));
   5325  Point pointInUserSpace = aPoint * cssPxPerDevPx + framePosition;
   5326 
   5327  // Find the closest rendered run for the text frames beneath aChildFrame.
   5328  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
   5329                             aChildFrame);
   5330  TextRenderedRun hit;
   5331  gfxPoint pointInRun;
   5332  nscoord dx = nscoord_MAX;
   5333  nscoord dy = nscoord_MAX;
   5334  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
   5335    uint32_t flags = TextRenderedRun::eIncludeFill |
   5336                     TextRenderedRun::eIncludeStroke |
   5337                     TextRenderedRun::eNoHorizontalOverflow;
   5338    gfxRect runRect = run.GetRunUserSpaceRect(flags).ToThebesRect();
   5339 
   5340    gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
   5341    if (!m.Invert()) {
   5342      return aPoint;
   5343    }
   5344    gfxPoint pointInRunUserSpace =
   5345        m.TransformPoint(ThebesPoint(pointInUserSpace));
   5346 
   5347    if (runRect.Contains(pointInRunUserSpace)) {
   5348      // The point was inside the rendered run's rect, so we choose it.
   5349      dx = 0;
   5350      dy = 0;
   5351      pointInRun = pointInRunUserSpace;
   5352      hit = run;
   5353    } else if (nsLayoutUtils::PointIsCloserToRect(pointInRunUserSpace, runRect,
   5354                                                  dx, dy)) {
   5355      // The point was closer to this rendered run's rect than any others
   5356      // we've seen so far.
   5357      pointInRun.x =
   5358          std::clamp(pointInRunUserSpace.x.value, runRect.X(), runRect.XMost());
   5359      pointInRun.y =
   5360          std::clamp(pointInRunUserSpace.y.value, runRect.Y(), runRect.YMost());
   5361      hit = run;
   5362    }
   5363  }
   5364 
   5365  if (!hit.mFrame) {
   5366    // We didn't find any rendered runs for the frame.
   5367    return aPoint;
   5368  }
   5369 
   5370  // Return the point in user units relative to the nsTextFrame,
   5371  // but taking into account mFontSizeScaleFactor.
   5372  gfxMatrix m = hit.GetTransformFromRunUserSpaceToFrameUserSpace(presContext);
   5373  m.PreScale(mFontSizeScaleFactor, mFontSizeScaleFactor);
   5374  return ToPoint(m.TransformPoint(pointInRun) / cssPxPerDevPx);
   5375 }
   5376 
   5377 /**
   5378 * For each rendered run beneath aChildFrame, translate aRect from
   5379 * aChildFrame to the run's text frame, transform it then into
   5380 * the run's frame user space, intersect it with the run's
   5381 * frame user space rect, then transform it up to user space.
   5382 * The result is the union of all of these.
   5383 */
   5384 gfxRect SVGTextFrame::TransformFrameRectFromTextChild(
   5385    const nsRect& aRect, const nsIFrame* aChildFrame) {
   5386  NS_ASSERTION(aChildFrame && nsLayoutUtils::GetClosestFrameOfType(
   5387                                  aChildFrame->GetParent(),
   5388                                  LayoutFrameType::SVGText) == this,
   5389               "aChildFrame must be a descendant of this frame");
   5390 
   5391  UpdateGlyphPositioning();
   5392 
   5393  nsPresContext* presContext = PresContext();
   5394 
   5395  gfxRect result;
   5396  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
   5397                             aChildFrame);
   5398  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
   5399    // First, translate aRect from aChildFrame to this run's frame.
   5400    nsRect rectInTextFrame = aRect + aChildFrame->GetOffsetTo(run.mFrame);
   5401 
   5402    // Scale it into frame user space.
   5403    gfxRect rectInFrameUserSpace = AppUnitsToFloatCSSPixels(rectInTextFrame);
   5404 
   5405    // Intersect it with the run.
   5406    uint32_t flags =
   5407        TextRenderedRun::eIncludeFill | TextRenderedRun::eIncludeStroke;
   5408 
   5409    if (rectInFrameUserSpace.IntersectRect(
   5410            rectInFrameUserSpace,
   5411            run.GetFrameUserSpaceRect(presContext, flags).ToThebesRect())) {
   5412      // Transform it up to user space of the <text>
   5413      gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
   5414      gfxRect rectInUserSpace = m.TransformRect(rectInFrameUserSpace);
   5415 
   5416      // Union it into the result.
   5417      result.UnionRect(result, rectInUserSpace);
   5418    }
   5419  }
   5420 
   5421  // Subtract the mRect offset from the result, as our user space for
   5422  // this frame is relative to the top-left of mRect.
   5423  float factor = AppUnitsPerCSSPixel();
   5424  gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
   5425                         NSAppUnitsToFloatPixels(mRect.y, factor));
   5426 
   5427  return result - framePosition;
   5428 }
   5429 
   5430 Rect SVGTextFrame::TransformFrameRectFromTextChild(
   5431    const Rect& aRect, const nsIFrame* aChildFrame) {
   5432  nscoord appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
   5433  nsRect r = LayoutDevicePixel::ToAppUnits(
   5434      LayoutDeviceRect::FromUnknownRect(aRect), appUnitsPerDevPixel);
   5435  gfxRect resultCssUnits = TransformFrameRectFromTextChild(r, aChildFrame);
   5436  float devPixelPerCSSPixel =
   5437      float(AppUnitsPerCSSPixel()) / appUnitsPerDevPixel;
   5438  resultCssUnits.Scale(devPixelPerCSSPixel);
   5439  return ToRect(resultCssUnits);
   5440 }
   5441 
   5442 Point SVGTextFrame::TransformFramePointFromTextChild(
   5443    const Point& aPoint, const nsIFrame* aChildFrame) {
   5444  return TransformFrameRectFromTextChild(Rect(aPoint, Size(1, 1)), aChildFrame)
   5445      .TopLeft();
   5446 }
   5447 
   5448 void SVGTextFrame::AppendDirectlyOwnedAnonBoxes(
   5449    nsTArray<OwnedAnonBox>& aResult) {
   5450  MOZ_ASSERT(PrincipalChildList().FirstChild(), "Must have our anon box");
   5451  aResult.AppendElement(OwnedAnonBox(PrincipalChildList().FirstChild()));
   5452 }
   5453 
   5454 }  // namespace mozilla