LCOV - code coverage report
Current view: top level - layout/svg - SVGTextFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 2086 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 191 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13