layout/svg/SVGTextFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     6 // Main header first:
     7 #include "SVGTextFrame.h"
     9 // Keep others in (case-insensitive) order:
    10 #include "DOMSVGPoint.h"
    11 #include "gfx2DGlue.h"
    12 #include "gfxFont.h"
    13 #include "gfxSkipChars.h"
    14 #include "gfxTypes.h"
    15 #include "LookAndFeel.h"
    16 #include "mozilla/gfx/2D.h"
    17 #include "nsAlgorithm.h"
    18 #include "nsBlockFrame.h"
    19 #include "nsCaret.h"
    20 #include "nsContentUtils.h"
    21 #include "nsGkAtoms.h"
    22 #include "nsIDOMSVGLength.h"
    23 #include "nsISelection.h"
    24 #include "nsQuickSort.h"
    25 #include "nsRenderingContext.h"
    26 #include "nsSVGEffects.h"
    27 #include "nsSVGOuterSVGFrame.h"
    28 #include "nsSVGPaintServerFrame.h"
    29 #include "mozilla/dom/SVGRect.h"
    30 #include "nsSVGIntegrationUtils.h"
    31 #include "nsSVGUtils.h"
    32 #include "nsTArray.h"
    33 #include "nsTextFrame.h"
    34 #include "nsTextNode.h"
    35 #include "SVGAnimatedNumberList.h"
    36 #include "SVGContentUtils.h"
    37 #include "SVGLengthList.h"
    38 #include "SVGNumberList.h"
    39 #include "SVGPathElement.h"
    40 #include "SVGTextPathElement.h"
    41 #include "nsLayoutUtils.h"
    42 #include <algorithm>
    43 #include <cmath>
    44 #include <limits>
    46 using namespace mozilla;
    47 using namespace mozilla::dom;
    48 using namespace mozilla::gfx;
    50 // ============================================================================
    51 // Utility functions
    53 /**
    54  * Using the specified gfxSkipCharsIterator, converts an offset and length
    55  * in original char indexes to skipped char indexes.
    56  *
    57  * @param aIterator The gfxSkipCharsIterator to use for the conversion.
    58  * @param aOriginalOffset The original offset (input).
    59  * @param aOriginalLength The original length (input).
    60  * @param aSkippedOffset The skipped offset (output).
    61  * @param aSkippedLength The skipped length (output).
    62  */
    63 static void
    64 ConvertOriginalToSkipped(gfxSkipCharsIterator& aIterator,
    65                          uint32_t aOriginalOffset, uint32_t aOriginalLength,
    66                          uint32_t& aSkippedOffset, uint32_t& aSkippedLength)
    67 {
    68   aSkippedOffset = aIterator.ConvertOriginalToSkipped(aOriginalOffset);
    69   aIterator.AdvanceOriginal(aOriginalLength);
    70   aSkippedLength = aIterator.GetSkippedOffset() - aSkippedOffset;
    71 }
    73 /**
    74  * Using the specified gfxSkipCharsIterator, converts an offset and length
    75  * in original char indexes to skipped char indexes in place.
    76  *
    77  * @param aIterator The gfxSkipCharsIterator to use for the conversion.
    78  * @param aOriginalOffset The offset to convert from original to skipped.
    79  * @param aOriginalLength The length to convert from original to skipped.
    80  */
    81 static void
    82 ConvertOriginalToSkipped(gfxSkipCharsIterator& aIterator,
    83                          uint32_t& aOffset, uint32_t& aLength)
    84 {
    85   ConvertOriginalToSkipped(aIterator, aOffset, aLength, aOffset, aLength);
    86 }
    88 /**
    89  * Converts an nsPoint from app units to user space units using the specified
    90  * nsPresContext and returns it as a gfxPoint.
    91  */
    92 static gfxPoint
    93 AppUnitsToGfxUnits(const nsPoint& aPoint, const nsPresContext* aContext)
    94 {
    95   return gfxPoint(aContext->AppUnitsToGfxUnits(aPoint.x),
    96                   aContext->AppUnitsToGfxUnits(aPoint.y));
    97 }
    99 /**
   100  * Converts a gfxRect that is in app units to CSS pixels using the specified
   101  * nsPresContext and returns it as a gfxRect.
   102  */
   103 static gfxRect
   104 AppUnitsToFloatCSSPixels(const gfxRect& aRect, const nsPresContext* aContext)
   105 {
   106   return gfxRect(aContext->AppUnitsToFloatCSSPixels(aRect.x),
   107                  aContext->AppUnitsToFloatCSSPixels(aRect.y),
   108                  aContext->AppUnitsToFloatCSSPixels(aRect.width),
   109                  aContext->AppUnitsToFloatCSSPixels(aRect.height));
   110 }
   112 /**
   113  * Scales a gfxRect around a given point.
   114  *
   115  * @param aRect The rectangle to scale.
   116  * @param aPoint The point around which to scale.
   117  * @param aScale The scale amount.
   118  */
   119 static void
   120 ScaleAround(gfxRect& aRect, const gfxPoint& aPoint, double aScale)
   121 {
   122   aRect.x = aPoint.x - aScale * (aPoint.x - aRect.x);
   123   aRect.y = aPoint.y - aScale * (aPoint.y - aRect.y);
   124   aRect.width *= aScale;
   125   aRect.height *= aScale;
   126 }
   128 /**
   129  * Returns whether a gfxPoint lies within a gfxRect.
   130  */
   131 static bool
   132 Inside(const gfxRect& aRect, const gfxPoint& aPoint)
   133 {
   134   return aPoint.x >= aRect.x &&
   135          aPoint.x < aRect.XMost() &&
   136          aPoint.y >= aRect.y &&
   137          aPoint.y < aRect.YMost();
   138 }
   140 /**
   141  * Gets the measured ascent and descent of the text in the given nsTextFrame
   142  * in app units.
   143  *
   144  * @param aFrame The text frame.
   145  * @param aAscent The ascent in app units (output).
   146  * @param aDescent The descent in app units (output).
   147  */
   148 static void
   149 GetAscentAndDescentInAppUnits(nsTextFrame* aFrame,
   150                               gfxFloat& aAscent, gfxFloat& aDescent)
   151 {
   152   gfxSkipCharsIterator it = aFrame->EnsureTextRun(nsTextFrame::eInflated);
   153   gfxTextRun* textRun = aFrame->GetTextRun(nsTextFrame::eInflated);
   155   uint32_t offset, length;
   156   ConvertOriginalToSkipped(it,
   157                            aFrame->GetContentOffset(),
   158                            aFrame->GetContentLength(),
   159                            offset, length);
   161   gfxTextRun::Metrics metrics =
   162     textRun->MeasureText(offset, length, gfxFont::LOOSE_INK_EXTENTS, nullptr,
   163                          nullptr);
   165   aAscent = metrics.mAscent;
   166   aDescent = metrics.mDescent;
   167 }
   169 /**
   170  * Updates an interval by intersecting it with another interval.
   171  * The intervals are specified using a start index and a length.
   172  */
   173 static void
   174 IntersectInterval(uint32_t& aStart, uint32_t& aLength,
   175                   uint32_t aStartOther, uint32_t aLengthOther)
   176 {
   177   uint32_t aEnd = aStart + aLength;
   178   uint32_t aEndOther = aStartOther + aLengthOther;
   180   if (aStartOther >= aEnd || aStart >= aEndOther) {
   181     aLength = 0;
   182   } else {
   183     if (aStartOther >= aStart)
   184       aStart = aStartOther;
   185     aLength = std::min(aEnd, aEndOther) - aStart;
   186   }
   187 }
   189 /**
   190  * Intersects an interval as IntersectInterval does but by taking
   191  * the offset and length of the other interval from a
   192  * nsTextFrame::TrimmedOffsets object.
   193  */
   194 static void
   195 TrimOffsets(uint32_t& aStart, uint32_t& aLength,
   196             const nsTextFrame::TrimmedOffsets& aTrimmedOffsets)
   197 {
   198   IntersectInterval(aStart, aLength,
   199                     aTrimmedOffsets.mStart, aTrimmedOffsets.mLength);
   200 }
   202 /**
   203  * Returns the closest ancestor-or-self node that is not an SVG <a>
   204  * element.
   205  */
   206 static nsIContent*
   207 GetFirstNonAAncestor(nsIContent* aContent)
   208 {
   209   while (aContent && aContent->IsSVG(nsGkAtoms::a)) {
   210     aContent = aContent->GetParent();
   211   }
   212   return aContent;
   213 }
   215 /**
   216  * Returns whether the given node is a text content element[1], taking into
   217  * account whether it has a valid parent.
   218  *
   219  * For example, in:
   220  *
   221  *   <svg xmlns="http://www.w3.org/2000/svg">
   222  *     <text><a/><text/></text>
   223  *     <tspan/>
   224  *   </svg>
   225  *
   226  * true would be returned for the outer <text> element and the <a> element,
   227  * and false for the inner <text> element (since a <text> is not allowed
   228  * to be a child of another <text>) and the <tspan> element (because it
   229  * must be inside a <text> subtree).
   230  *
   231  * Note that we don't support the <tref> element yet and this function
   232  * returns false for it.
   233  *
   234  * [1] https://svgwg.org/svg2-draft/intro.html#TermTextContentElement
   235  */
   236 static bool
   237 IsTextContentElement(nsIContent* aContent)
   238 {
   239   if (!aContent->IsSVG()) {
   240     return false;
   241   }
   243   if (aContent->Tag() == nsGkAtoms::text) {
   244     nsIContent* parent = GetFirstNonAAncestor(aContent->GetParent());
   245     return !parent || !IsTextContentElement(parent);
   246   }
   248   if (aContent->Tag() == nsGkAtoms::textPath) {
   249     nsIContent* parent = GetFirstNonAAncestor(aContent->GetParent());
   250     return parent && parent->IsSVG(nsGkAtoms::text);
   251   }
   253   if (aContent->Tag() == nsGkAtoms::a ||
   254       aContent->Tag() == nsGkAtoms::tspan ||
   255       aContent->Tag() == nsGkAtoms::altGlyph) {
   256     return true;
   257   }
   259   return false;
   260 }
   262 /**
   263  * Returns whether the specified frame is an nsTextFrame that has some text
   264  * content.
   265  */
   266 static bool
   267 IsNonEmptyTextFrame(nsIFrame* aFrame)
   268 {
   269   nsTextFrame* textFrame = do_QueryFrame(aFrame);
   270   if (!textFrame) {
   271     return false;
   272   }
   274   return textFrame->GetContentLength() != 0;
   275 }
   277 /**
   278  * Takes an nsIFrame and if it is a text frame that has some text content,
   279  * returns it as an nsTextFrame and its corresponding nsTextNode.
   280  *
   281  * @param aFrame The frame to look at.
   282  * @param aTextFrame aFrame as an nsTextFrame (output).
   283  * @param aTextNode The nsTextNode content of aFrame (output).
   284  * @return true if aFrame is a non-empty text frame, false otherwise.
   285  */
   286 static bool
   287 GetNonEmptyTextFrameAndNode(nsIFrame* aFrame,
   288                             nsTextFrame*& aTextFrame,
   289                             nsTextNode*& aTextNode)
   290 {
   291   nsTextFrame* text = do_QueryFrame(aFrame);
   292   if (!text) {
   293     return false;
   294   }
   296   nsIContent* content = text->GetContent();
   297   NS_ASSERTION(content && content->IsNodeOfType(nsINode::eTEXT),
   298                "unexpected content type for nsTextFrame");
   300   nsTextNode* node = static_cast<nsTextNode*>(content);
   301   if (node->TextLength() == 0) {
   302     return false;
   303   }
   305   aTextFrame = text;
   306   aTextNode = node;
   307   return true;
   308 }
   310 /**
   311  * Returns whether the specified atom is for one of the five
   312  * glyph positioning attributes that can appear on SVG text
   313  * elements -- x, y, dx, dy or rotate.
   314  */
   315 static bool
   316 IsGlyphPositioningAttribute(nsIAtom* aAttribute)
   317 {
   318   return aAttribute == nsGkAtoms::x ||
   319          aAttribute == nsGkAtoms::y ||
   320          aAttribute == nsGkAtoms::dx ||
   321          aAttribute == nsGkAtoms::dy ||
   322          aAttribute == nsGkAtoms::rotate;
   323 }
   325 /**
   326  * Returns the position in app units of a given baseline (using an
   327  * SVG dominant-baseline property value) for a given nsTextFrame.
   328  *
   329  * @param aFrame The text frame to inspect.
   330  * @param aTextRun The text run of aFrame.
   331  * @param aDominantBaseline The dominant-baseline value to use.
   332  */
   333 static nscoord
   334 GetBaselinePosition(nsTextFrame* aFrame,
   335                     gfxTextRun* aTextRun,
   336                     uint8_t aDominantBaseline)
   337 {
   338   switch (aDominantBaseline) {
   339     case NS_STYLE_DOMINANT_BASELINE_HANGING:
   340     case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE:
   341       return 0;
   342     case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT:
   343     case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE:
   344     case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE:
   345       // These three should not simply map to 'baseline', but we don't
   346       // support the complex baseline model that SVG 1.1 has and which
   347       // css3-linebox now defines.
   348       // (fall through)
   349     case NS_STYLE_DOMINANT_BASELINE_AUTO:
   350     case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC:
   351       return aFrame->GetBaseline();
   352   }
   354   gfxTextRun::Metrics metrics =
   355     aTextRun->MeasureText(0, aTextRun->GetLength(), gfxFont::LOOSE_INK_EXTENTS,
   356                           nullptr, nullptr);
   358   switch (aDominantBaseline) {
   359     case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE:
   360     case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC:
   361       return metrics.mAscent + metrics.mDescent;
   362     case NS_STYLE_DOMINANT_BASELINE_CENTRAL:
   363     case NS_STYLE_DOMINANT_BASELINE_MIDDLE:
   364     case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL:
   365       return (metrics.mAscent + metrics.mDescent) / 2.0;
   366   }
   368   NS_NOTREACHED("unexpected dominant-baseline value");
   369   return aFrame->GetBaseline();
   370 }
   372 /**
   373  * For a given text run, returns the number of skipped characters that comprise
   374  * the ligature group and/or cluster that includes the character represented
   375  * by the specified gfxSkipCharsIterator.
   376  *
   377  * @param aTextRun The text run to use for determining whether a given character
   378  *   is part of a ligature or cluster.
   379  * @param aIterator The gfxSkipCharsIterator to use for the current position
   380  *   in the text run.
   381  */
   382 static uint32_t
   383 ClusterLength(gfxTextRun* aTextRun, const gfxSkipCharsIterator& aIterator)
   384 {
   385   uint32_t start = aIterator.GetSkippedOffset();
   386   uint32_t end = start + 1;
   387   while (end < aTextRun->GetLength() &&
   388          (!aTextRun->IsLigatureGroupStart(end) ||
   389           !aTextRun->IsClusterStart(end))) {
   390     end++;
   391   }
   392   return end - start;
   393 }
   395 /**
   396  * Truncates an array to be at most the length of another array.
   397  *
   398  * @param aArrayToTruncate The array to truncate.
   399  * @param aReferenceArray The array whose length will be used to truncate
   400  *   aArrayToTruncate to.
   401  */
   402 template<typename T, typename U>
   403 static void
   404 TruncateTo(nsTArray<T>& aArrayToTruncate, const nsTArray<U>& aReferenceArray)
   405 {
   406   uint32_t length = aReferenceArray.Length();
   407   if (aArrayToTruncate.Length() > length) {
   408     aArrayToTruncate.TruncateLength(length);
   409   }
   410 }
   412 /**
   413  * Asserts that the anonymous block child of the SVGTextFrame has been
   414  * reflowed (or does not exist).  Returns null if the child has not been
   415  * reflowed, and the frame otherwise.
   416  *
   417  * We check whether the kid has been reflowed and not the frame itself
   418  * since we sometimes need to call this function during reflow, after the
   419  * kid has been reflowed but before we have cleared the dirty bits on the
   420  * frame itself.
   421  */
   422 static SVGTextFrame*
   423 FrameIfAnonymousChildReflowed(SVGTextFrame* aFrame)
   424 {
   425   NS_PRECONDITION(aFrame, "aFrame must not be null");
   426   nsIFrame* kid = aFrame->GetFirstPrincipalChild();
   427   if (NS_SUBTREE_DIRTY(kid)) {
   428     MOZ_ASSERT(false, "should have already reflowed the anonymous block child");
   429     return nullptr;
   430   }
   431   return aFrame;
   432 }
   434 static double
   435 GetContextScale(const gfxMatrix& aMatrix)
   436 {
   437   // The context scale is the ratio of the length of the transformed
   438   // diagonal vector (1,1) to the length of the untransformed diagonal
   439   // (which is sqrt(2)).
   440   gfxPoint p = aMatrix.Transform(gfxPoint(1, 1)) -
   441                aMatrix.Transform(gfxPoint(0, 0));
   442   return SVGContentUtils::ComputeNormalizedHypotenuse(p.x, p.y);
   443 }
   445 // ============================================================================
   446 // Utility classes
   448 namespace mozilla {
   450 // ----------------------------------------------------------------------------
   451 // TextRenderedRun
   453 /**
   454  * A run of text within a single nsTextFrame whose glyphs can all be painted
   455  * with a single call to nsTextFrame::PaintText.  A text rendered run can
   456  * be created for a sequence of two or more consecutive glyphs as long as:
   457  *
   458  *   - Only the first glyph has (or none of the glyphs have) been positioned
   459  *     with SVG text positioning attributes
   460  *   - All of the glyphs have zero rotation
   461  *   - The glyphs are not on a text path
   462  *   - The glyphs correspond to content within the one nsTextFrame
   463  *
   464  * A TextRenderedRunIterator produces TextRenderedRuns required for painting a
   465  * whole SVGTextFrame.
   466  */
   467 struct TextRenderedRun
   468 {
   469   /**
   470    * Constructs a TextRenderedRun that is uninitialized except for mFrame
   471    * being null.
   472    */
   473   TextRenderedRun()
   474     : mFrame(nullptr)
   475   {
   476   }
   478   /**
   479    * Constructs a TextRenderedRun with all of the information required to
   480    * paint it.  See the comments documenting the member variables below
   481    * for descriptions of the arguments.
   482    */
   483   TextRenderedRun(nsTextFrame* aFrame, const gfxPoint& aPosition,
   484                   float aLengthAdjustScaleFactor, double aRotate,
   485                   float aFontSizeScaleFactor, nscoord aBaseline,
   486                   uint32_t aTextFrameContentOffset,
   487                   uint32_t aTextFrameContentLength,
   488                   uint32_t aTextElementCharIndex)
   489     : mFrame(aFrame),
   490       mPosition(aPosition),
   491       mLengthAdjustScaleFactor(aLengthAdjustScaleFactor),
   492       mRotate(static_cast<float>(aRotate)),
   493       mFontSizeScaleFactor(aFontSizeScaleFactor),
   494       mBaseline(aBaseline),
   495       mTextFrameContentOffset(aTextFrameContentOffset),
   496       mTextFrameContentLength(aTextFrameContentLength),
   497       mTextElementCharIndex(aTextElementCharIndex)
   498   {
   499   }
   501   /**
   502    * Returns the text run for the text frame that this rendered run is part of.
   503    */
   504   gfxTextRun* GetTextRun() const
   505   {
   506     mFrame->EnsureTextRun(nsTextFrame::eInflated);
   507     return mFrame->GetTextRun(nsTextFrame::eInflated);
   508   }
   510   /**
   511    * Returns whether this rendered run is RTL.
   512    */
   513   bool IsRightToLeft() const
   514   {
   515     return GetTextRun()->IsRightToLeft();
   516   }
   518   /**
   519    * Returns the transform that converts from a <text> element's user space into
   520    * the coordinate space that rendered runs can be painted directly in.
   521    *
   522    * The difference between this method and GetTransformFromRunUserSpaceToUserSpace
   523    * is that when calling in to nsTextFrame::PaintText, it will already take
   524    * into account any left clip edge (that is, it doesn't just apply a visual
   525    * clip to the rendered text, it shifts the glyphs over so that they are
   526    * painted with their left edge at the x coordinate passed in to it).
   527    * Thus we need to account for this in our transform.
   528    *
   529    *
   530    * Assume that we have <text x="100" y="100" rotate="0 0 1 0 0 1">abcdef</text>.
   531    * This would result in four text rendered runs:
   532    *
   533    *   - one for "ab"
   534    *   - one for "c"
   535    *   - one for "de"
   536    *   - one for "f"
   537    *
   538    * Assume now that we are painting the third TextRenderedRun.  It will have
   539    * a left clip edge that is the sum of the advances of "abc", and it will
   540    * have a right clip edge that is the advance of "f".  In
   541    * SVGTextFrame::PaintSVG(), we pass in nsPoint() (i.e., the origin)
   542    * as the point at which to paint the text frame, and we pass in the
   543    * clip edge values.  The nsTextFrame will paint the substring of its
   544    * text such that the top-left corner of the "d"'s glyph cell will be at
   545    * (0, 0) in the current coordinate system.
   546    *
   547    * Thus, GetTransformFromUserSpaceForPainting must return a transform from
   548    * whatever user space the <text> element is in to a coordinate space in
   549    * device pixels (as that's what nsTextFrame works in) where the origin is at
   550    * the same position as our user space mPositions[i].mPosition value for
   551    * the "d" glyph, which will be (100 + userSpaceAdvance("abc"), 100).
   552    * The translation required to do this (ignoring the scale to get from
   553    * user space to device pixels, and ignoring the
   554    * (100 + userSpaceAdvance("abc"), 100) translation) is:
   555    *
   556    *   (-leftEdge, -baseline)
   557    *
   558    * where baseline is the distance between the baseline of the text and the top
   559    * edge of the nsTextFrame.  We translate by -leftEdge horizontally because
   560    * the nsTextFrame will already shift the glyphs over by that amount and start
   561    * painting glyphs at x = 0.  We translate by -baseline vertically so that
   562    * painting the top edges of the glyphs at y = 0 will result in their
   563    * baselines being at our desired y position.
   564    *
   565    *
   566    * Now for an example with RTL text.  Assume our content is now
   567    * <text x="100" y="100" rotate="0 0 1 0 0 1">WERBEH</text>.  We'd have
   568    * the following text rendered runs:
   569    *
   570    *   - one for "EH"
   571    *   - one for "B"
   572    *   - one for "ER"
   573    *   - one for "W"
   574    *
   575    * Again, we are painting the third TextRenderedRun.  The left clip edge
   576    * is the advance of the "W" and the right clip edge is the sum of the
   577    * advances of "BEH".  Our translation to get the rendered "ER" glyphs
   578    * in the right place this time is:
   579    *
   580    *   (-frameWidth + rightEdge, -baseline)
   581    *
   582    * which is equivalent to:
   583    *
   584    *   (-(leftEdge + advance("ER")), -baseline)
   585    *
   586    * The reason we have to shift left additionally by the width of the run
   587    * of glyphs we are painting is that although the nsTextFrame is RTL,
   588    * we still supply the top-left corner to paint the frame at when calling
   589    * nsTextFrame::PaintText, even though our user space positions for each
   590    * glyph in mPositions specifies the origin of each glyph, which for RTL
   591    * glyphs is at the right edge of the glyph cell.
   592    *
   593    *
   594    * For any other use of an nsTextFrame in the context of a particular run
   595    * (such as hit testing, or getting its rectangle),
   596    * GetTransformFromRunUserSpaceToUserSpace should be used.
   597    *
   598    * @param aContext The context to use for unit conversions.
   599    * @param aItem The nsCharClipDisplayItem that holds the amount of clipping
   600    *   from the left and right edges of the text frame for this rendered run.
   601    *   An appropriate nsCharClipDisplayItem can be obtained by constructing an
   602    *   SVGCharClipDisplayItem for the TextRenderedRun.
   603    */
   604   gfxMatrix GetTransformFromUserSpaceForPainting(
   605                                       nsPresContext* aContext,
   606                                       const nsCharClipDisplayItem& aItem) const;
   608   /**
   609    * Returns the transform that converts from "run user space" to a <text>
   610    * element's user space.  Run user space is a coordinate system that has the
   611    * same size as the <text>'s user space but rotated and translated such that
   612    * (0,0) is the top-left of the rectangle that bounds the text.
   613    *
   614    * @param aContext The context to use for unit conversions.
   615    */
   616   gfxMatrix GetTransformFromRunUserSpaceToUserSpace(nsPresContext* aContext) const;
   618   /**
   619    * Returns the transform that converts from "run user space" to float pixels
   620    * relative to the nsTextFrame that this rendered run is a part of.
   621    *
   622    * @param aContext The context to use for unit conversions.
   623    */
   624   gfxMatrix GetTransformFromRunUserSpaceToFrameUserSpace(nsPresContext* aContext) const;
   626   /**
   627    * Flag values used for the aFlags arguments of GetRunUserSpaceRect,
   628    * GetFrameUserSpaceRect and GetUserSpaceRect.
   629    */
   630   enum {
   631     // Includes the fill geometry of the text in the returned rectangle.
   632     eIncludeFill = 1,
   633     // Includes the stroke geometry of the text in the returned rectangle.
   634     eIncludeStroke = 2,
   635     // Includes any text shadow in the returned rectangle.
   636     eIncludeTextShadow = 4,
   637     // Don't include any horizontal glyph overflow in the returned rectangle.
   638     eNoHorizontalOverflow = 8
   639   };
   641   /**
   642    * Returns a rectangle that bounds the fill and/or stroke of the rendered run
   643    * in run user space.
   644    *
   645    * @param aContext The context to use for unit conversions.
   646    * @param aFlags A combination of the flags above (eIncludeFill and
   647    *   eIncludeStroke) indicating what parts of the text to include in
   648    *   the rectangle.
   649    */
   650   SVGBBox GetRunUserSpaceRect(nsPresContext* aContext, uint32_t aFlags) const;
   652   /**
   653    * Returns a rectangle that covers the fill and/or stroke of the rendered run
   654    * in "frame user space".
   655    *
   656    * Frame user space is a coordinate space of the same scale as the <text>
   657    * element's user space, but with its rotation set to the rotation of
   658    * the glyphs within this rendered run and its origin set to the position
   659    * such that placing the nsTextFrame there would result in the glyphs in
   660    * this rendered run being at their correct positions.
   661    *
   662    * For example, say we have <text x="100 150" y="100">ab</text>.  Assume
   663    * the advance of both the "a" and the "b" is 12 user units, and the
   664    * ascent of the text is 8 user units and its descent is 6 user units,
   665    * and that we are not measuing the stroke of the text, so that we stay
   666    * entirely within the glyph cells.
   667    *
   668    * There will be two text rendered runs, one for "a" and one for "b".
   669    *
   670    * The frame user space for the "a" run will have its origin at
   671    * (100, 100 - 8) in the <text> element's user space and will have its
   672    * axes aligned with the user space (since there is no rotate="" or
   673    * text path involve) and with its scale the same as the user space.
   674    * The rect returned by this method will be (0, 0, 12, 14), since the "a"
   675    * glyph is right at the left of the nsTextFrame.
   676    *
   677    * The frame user space for the "b" run will have its origin at
   678    * (150 - 12, 100 - 8), and scale/rotation the same as above.  The rect
   679    * returned by this method will be (12, 0, 12, 14), since we are
   680    * advance("a") horizontally in to the text frame.
   681    *
   682    * @param aContext The context to use for unit conversions.
   683    * @param aFlags A combination of the flags above (eIncludeFill and
   684    *   eIncludeStroke) indicating what parts of the text to include in
   685    *   the rectangle.
   686    */
   687   SVGBBox GetFrameUserSpaceRect(nsPresContext* aContext, uint32_t aFlags) const;
   689   /**
   690    * Returns a rectangle that covers the fill and/or stroke of the rendered run
   691    * in the <text> element's user space.
   692    *
   693    * @param aContext The context to use for unit conversions.
   694    * @param aFlags A combination of the flags above indicating what parts of the
   695    *   text to include in the rectangle.
   696    * @param aAdditionalTransform An additional transform to apply to the
   697    *   frame user space rectangle before its bounds are transformed into
   698    *   user space.
   699    */
   700   SVGBBox GetUserSpaceRect(nsPresContext* aContext, uint32_t aFlags,
   701                            const gfxMatrix* aAdditionalTransform = nullptr) const;
   703   /**
   704    * Gets the app unit amounts to clip from the left and right edges of
   705    * the nsTextFrame in order to paint just this rendered run.
   706    *
   707    * Note that if clip edge amounts land in the middle of a glyph, the
   708    * glyph won't be painted at all.  The clip edges are thus more of
   709    * a selection mechanism for which glyphs will be painted, rather
   710    * than a geometric clip.
   711    */
   712   void GetClipEdges(nscoord& aLeftEdge, nscoord& aRightEdge) const;
   714   /**
   715    * Returns the advance width of the whole rendered run.
   716    */
   717   nscoord GetAdvanceWidth() const;
   719   /**
   720    * Returns the index of the character into this rendered run whose
   721    * glyph cell contains the given point, or -1 if there is no such
   722    * character.  This does not hit test against any overflow.
   723    *
   724    * @param aContext The context to use for unit conversions.
   725    * @param aPoint The point in the user space of the <text> element.
   726    */
   727   int32_t GetCharNumAtPosition(nsPresContext* aContext,
   728                                const gfxPoint& aPoint) const;
   730   /**
   731    * The text frame that this rendered run lies within.
   732    */
   733   nsTextFrame* mFrame;
   735   /**
   736    * The point in user space that the text is positioned at.
   737    *
   738    * The x coordinate is the left edge of a LTR run of text or the right edge of
   739    * an RTL run.  The y coordinate is the baseline of the text.
   740    */
   741   gfxPoint mPosition;
   743   /**
   744    * The horizontal scale factor to apply when painting glyphs to take
   745    * into account textLength="".
   746    */
   747   float mLengthAdjustScaleFactor;
   749   /**
   750    * The rotation in radians in the user coordinate system that the text has.
   751    */
   752   float mRotate;
   754   /**
   755    * The scale factor that was used to transform the text run's original font
   756    * size into a sane range for painting and measurement.
   757    */
   758   double mFontSizeScaleFactor;
   760   /**
   761    * The baseline in app units of this text run.  The measurement is from the
   762    * top of the text frame.
   763    */
   764   nscoord mBaseline;
   766   /**
   767    * The offset and length in mFrame's content nsTextNode that corresponds to
   768    * this text rendered run.  These are original char indexes.
   769    */
   770   uint32_t mTextFrameContentOffset;
   771   uint32_t mTextFrameContentLength;
   773   /**
   774    * The character index in the whole SVG <text> element that this text rendered
   775    * run begins at.
   776    */
   777   uint32_t mTextElementCharIndex;
   778 };
   780 gfxMatrix
   781 TextRenderedRun::GetTransformFromUserSpaceForPainting(
   782                                        nsPresContext* aContext,
   783                                        const nsCharClipDisplayItem& aItem) const
   784 {
   785   // We transform to device pixels positioned such that painting the text frame
   786   // at (0,0) with aItem will result in the text being in the right place.
   788   gfxMatrix m;
   789   if (!mFrame) {
   790     return m;
   791   }
   793   float cssPxPerDevPx = aContext->
   794     AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
   796   // Glyph position in user space.
   797   m.Translate(mPosition / cssPxPerDevPx);
   799   // Take into account any font size scaling and scaling due to textLength="".
   800   m.Scale(1.0 / mFontSizeScaleFactor, 1.0 / mFontSizeScaleFactor);
   802   // Rotation due to rotate="" or a <textPath>.
   803   m.Rotate(mRotate);
   805   m.Scale(mLengthAdjustScaleFactor, 1.0);
   807   // Translation to get the text frame in the right place.
   808   nsPoint t(IsRightToLeft() ?
   809               -mFrame->GetRect().width + aItem.mRightEdge :
   810               -aItem.mLeftEdge,
   811             -mBaseline);
   812   m.Translate(AppUnitsToGfxUnits(t, aContext));
   814   return m;
   815 }
   817 gfxMatrix
   818 TextRenderedRun::GetTransformFromRunUserSpaceToUserSpace(
   819                                                   nsPresContext* aContext) const
   820 {
   821   gfxMatrix m;
   822   if (!mFrame) {
   823     return m;
   824   }
   826   float cssPxPerDevPx = aContext->
   827     AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
   829   nscoord left, right;
   830   GetClipEdges(left, right);
   832   // Glyph position in user space.
   833   m.Translate(mPosition);
   835   // Rotation due to rotate="" or a <textPath>.
   836   m.Rotate(mRotate);
   838   // Scale due to textLength="".
   839   m.Scale(mLengthAdjustScaleFactor, 1.0);
   841   // Translation to get the text frame in the right place.
   842   nsPoint t(IsRightToLeft() ?
   843               -mFrame->GetRect().width + left + right :
   844               0,
   845             -mBaseline);
   846   m.Translate(AppUnitsToGfxUnits(t, aContext) *
   847                 cssPxPerDevPx / mFontSizeScaleFactor);
   849   return m;
   850 }
   852 gfxMatrix
   853 TextRenderedRun::GetTransformFromRunUserSpaceToFrameUserSpace(
   854                                                   nsPresContext* aContext) const
   855 {
   856   gfxMatrix m;
   857   if (!mFrame) {
   858     return m;
   859   }
   861   nscoord left, right;
   862   GetClipEdges(left, right);
   864   // Translate by the horizontal distance into the text frame this
   865   // rendered run is.
   866   return m.Translate(gfxPoint(gfxFloat(left) / aContext->AppUnitsPerCSSPixel(),
   867                               0));
   868 }
   870 SVGBBox
   871 TextRenderedRun::GetRunUserSpaceRect(nsPresContext* aContext,
   872                                      uint32_t aFlags) const
   873 {
   874   SVGBBox r;
   875   if (!mFrame) {
   876     return r;
   877   }
   879   // Determine the amount of overflow above and below the frame's mRect.
   880   //
   881   // We need to call GetVisualOverflowRectRelativeToSelf because this includes
   882   // overflowing decorations, which the MeasureText call below does not.  We
   883   // assume here the decorations only overflow above and below the frame, never
   884   // horizontally.
   885   nsRect self = mFrame->GetVisualOverflowRectRelativeToSelf();
   886   nsRect rect = mFrame->GetRect();
   887   nscoord above = -self.y;
   888   nscoord below = self.YMost() - rect.height;
   890   gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
   891   gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
   893   // Get the content range for this rendered run.
   894   uint32_t offset, length;
   895   ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength,
   896                            offset, length);
   898   // Measure that range.
   899   gfxTextRun::Metrics metrics =
   900     textRun->MeasureText(offset, length, gfxFont::LOOSE_INK_EXTENTS,
   901                          nullptr, nullptr);
   903   // Determine the rectangle that covers the rendered run's fill,
   904   // taking into account the measured vertical overflow due to
   905   // decorations.
   906   nscoord baseline = metrics.mBoundingBox.y + metrics.mAscent;
   907   gfxFloat x, width;
   908   if (aFlags & eNoHorizontalOverflow) {
   909     x = 0.0;
   910     width = textRun->GetAdvanceWidth(offset, length, nullptr);
   911   } else {
   912     x = metrics.mBoundingBox.x;
   913     width = metrics.mBoundingBox.width;
   914   }
   915   nsRect fillInAppUnits(x, baseline - above,
   916                         width, metrics.mBoundingBox.height + above + below);
   918   // Account for text-shadow.
   919   if (aFlags & eIncludeTextShadow) {
   920     fillInAppUnits =
   921       nsLayoutUtils::GetTextShadowRectsUnion(fillInAppUnits, mFrame);
   922   }
   924   // Convert the app units rectangle to user units.
   925   gfxRect fill = AppUnitsToFloatCSSPixels(gfxRect(fillInAppUnits.x,
   926                                                   fillInAppUnits.y,
   927                                                   fillInAppUnits.width,
   928                                                   fillInAppUnits.height),
   929                                           aContext);
   931   // Scale the rectangle up due to any mFontSizeScaleFactor.  We scale
   932   // it around the text's origin.
   933   ScaleAround(fill,
   934               gfxPoint(0.0, aContext->AppUnitsToFloatCSSPixels(baseline)),
   935               1.0 / mFontSizeScaleFactor);
   937   // Include the fill if requested.
   938   if (aFlags & eIncludeFill) {
   939     r = fill;
   940   }
   942   // Include the stroke if requested.
   943   if ((aFlags & eIncludeStroke) &&
   944       nsSVGUtils::GetStrokeWidth(mFrame) > 0) {
   945     r.UnionEdges(nsSVGUtils::PathExtentsToMaxStrokeExtents(fill, mFrame,
   946                                                            gfxMatrix()));
   947   }
   949   return r;
   950 }
   952 SVGBBox
   953 TextRenderedRun::GetFrameUserSpaceRect(nsPresContext* aContext,
   954                                        uint32_t aFlags) const
   955 {
   956   SVGBBox r = GetRunUserSpaceRect(aContext, aFlags);
   957   if (r.IsEmpty()) {
   958     return r;
   959   }
   960   gfxMatrix m = GetTransformFromRunUserSpaceToFrameUserSpace(aContext);
   961   return m.TransformBounds(r.ToThebesRect());
   962 }
   964 SVGBBox
   965 TextRenderedRun::GetUserSpaceRect(nsPresContext* aContext,
   966                                   uint32_t aFlags,
   967                                   const gfxMatrix* aAdditionalTransform) const
   968 {
   969   SVGBBox r = GetRunUserSpaceRect(aContext, aFlags);
   970   if (r.IsEmpty()) {
   971     return r;
   972   }
   973   gfxMatrix m = GetTransformFromRunUserSpaceToUserSpace(aContext);
   974   if (aAdditionalTransform) {
   975     m.Multiply(*aAdditionalTransform);
   976   }
   977   return m.TransformBounds(r.ToThebesRect());
   978 }
   980 void
   981 TextRenderedRun::GetClipEdges(nscoord& aLeftEdge, nscoord& aRightEdge) const
   982 {
   983   uint32_t contentLength = mFrame->GetContentLength();
   984   if (mTextFrameContentOffset == 0 &&
   985       mTextFrameContentLength == contentLength) {
   986     // If the rendered run covers the entire content, we know we don't need
   987     // to clip without having to measure anything.
   988     aLeftEdge = 0;
   989     aRightEdge = 0;
   990     return;
   991   }
   993   gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
   994   gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
   996   // Get the covered content offset/length for this rendered run in skipped
   997   // characters, since that is what GetAdvanceWidth expects.
   998   uint32_t runOffset, runLength, frameOffset, frameLength;
   999   ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength,
  1000                                runOffset, runLength);
  1002   // Get the offset/length of the whole nsTextFrame.
  1003   frameOffset = mFrame->GetContentOffset();
  1004   frameLength = mFrame->GetContentLength();
  1006   // Trim the whole-nsTextFrame offset/length to remove any leading/trailing
  1007   // white space, as the nsTextFrame when painting does not include them when
  1008   // interpreting clip edges.
  1009   nsTextFrame::TrimmedOffsets trimmedOffsets =
  1010     mFrame->GetTrimmedOffsets(mFrame->GetContent()->GetText(), true);
  1011   TrimOffsets(frameOffset, frameLength, trimmedOffsets);
  1013   // Convert the trimmed whole-nsTextFrame offset/length into skipped
  1014   // characters.
  1015   ConvertOriginalToSkipped(it, frameOffset, frameLength);
  1017   // Measure the advance width in the text run between the start of
  1018   // frame's content and the start of the rendered run's content,
  1019   nscoord leftEdge =
  1020     textRun->GetAdvanceWidth(frameOffset, runOffset - frameOffset, nullptr);
  1022   // and between the end of the rendered run's content and the end
  1023   // of the frame's content.
  1024   nscoord rightEdge =
  1025     textRun->GetAdvanceWidth(runOffset + runLength,
  1026                              frameOffset + frameLength - (runOffset + runLength),
  1027                              nullptr);
  1029   if (textRun->IsRightToLeft()) {
  1030     aLeftEdge  = rightEdge;
  1031     aRightEdge = leftEdge;
  1032   } else {
  1033     aLeftEdge  = leftEdge;
  1034     aRightEdge = rightEdge;
  1038 nscoord
  1039 TextRenderedRun::GetAdvanceWidth() const
  1041   gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
  1042   gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
  1044   uint32_t offset, length;
  1045   ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength,
  1046                            offset, length);
  1048   return textRun->GetAdvanceWidth(offset, length, nullptr);
  1051 int32_t
  1052 TextRenderedRun::GetCharNumAtPosition(nsPresContext* aContext,
  1053                                       const gfxPoint& aPoint) const
  1055   if (mTextFrameContentLength == 0) {
  1056     return -1;
  1059   float cssPxPerDevPx = aContext->
  1060     AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
  1062   // Convert the point from user space into run user space, and take
  1063   // into account any mFontSizeScaleFactor.
  1064   gfxMatrix m = GetTransformFromRunUserSpaceToUserSpace(aContext).Invert();
  1065   gfxPoint p = m.Transform(aPoint) / cssPxPerDevPx * mFontSizeScaleFactor;
  1067   // First check that the point lies vertically between the top and bottom
  1068   // edges of the text.
  1069   gfxFloat ascent, descent;
  1070   GetAscentAndDescentInAppUnits(mFrame, ascent, descent);
  1072   gfxFloat topEdge = mFrame->GetBaseline() - ascent;
  1073   gfxFloat bottomEdge = topEdge + ascent + descent;
  1075   if (p.y < aContext->AppUnitsToGfxUnits(topEdge) ||
  1076       p.y >= aContext->AppUnitsToGfxUnits(bottomEdge)) {
  1077     return -1;
  1080   gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
  1081   gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
  1083   // Next check that the point lies horizontally within the left and right
  1084   // edges of the text.
  1085   uint32_t offset, length;
  1086   ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength,
  1087                            offset, length);
  1088   gfxFloat runAdvance =
  1089     aContext->AppUnitsToGfxUnits(textRun->GetAdvanceWidth(offset, length,
  1090                                                           nullptr));
  1092   if (p.x < 0 || p.x >= runAdvance) {
  1093     return -1;
  1096   // Finally, measure progressively smaller portions of the rendered run to
  1097   // find which glyph it lies within.  This will need to change once we
  1098   // support letter-spacing and word-spacing.
  1099   bool rtl = textRun->IsRightToLeft();
  1100   for (int32_t i = mTextFrameContentLength - 1; i >= 0; i--) {
  1101     ConvertOriginalToSkipped(it, mTextFrameContentOffset, i, offset, length);
  1102     gfxFloat advance =
  1103       aContext->AppUnitsToGfxUnits(textRun->GetAdvanceWidth(offset, length,
  1104                                                             nullptr));
  1105     if ((rtl && p.x < runAdvance - advance) ||
  1106         (!rtl && p.x >= advance)) {
  1107       return i;
  1110   return -1;
  1113 // ----------------------------------------------------------------------------
  1114 // TextNodeIterator
  1116 enum SubtreePosition
  1118   eBeforeSubtree,
  1119   eWithinSubtree,
  1120   eAfterSubtree
  1121 };
  1123 /**
  1124  * An iterator class for nsTextNodes that are descendants of a given node, the
  1125  * root.  Nodes are iterated in document order.  An optional subtree can be
  1126  * specified, in which case the iterator will track whether the current state of
  1127  * the traversal over the tree is within that subtree or is past that subtree.
  1128  */
  1129 class TextNodeIterator
  1131 public:
  1132   /**
  1133    * Constructs a TextNodeIterator with the specified root node and optional
  1134    * subtree.
  1135    */
  1136   TextNodeIterator(nsIContent* aRoot, nsIContent* aSubtree = nullptr)
  1137     : mRoot(aRoot),
  1138       mSubtree(aSubtree == aRoot ? nullptr : aSubtree),
  1139       mCurrent(aRoot),
  1140       mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree)
  1142     NS_ASSERTION(aRoot, "expected non-null root");
  1143     if (!aRoot->IsNodeOfType(nsINode::eTEXT)) {
  1144       Next();
  1148   /**
  1149    * Returns the current nsTextNode, or null if the iterator has finished.
  1150    */
  1151   nsTextNode* Current() const
  1153     return static_cast<nsTextNode*>(mCurrent);
  1156   /**
  1157    * Advances to the next nsTextNode and returns it, or null if the end of
  1158    * iteration has been reached.
  1159    */
  1160   nsTextNode* Next();
  1162   /**
  1163    * Returns whether the iterator is currently within the subtree rooted
  1164    * at mSubtree.  Returns true if we are not tracking a subtree (we consider
  1165    * that we're always within the subtree).
  1166    */
  1167   bool IsWithinSubtree() const
  1169     return mSubtreePosition == eWithinSubtree;
  1172   /**
  1173    * Returns whether the iterator is past the subtree rooted at mSubtree.
  1174    * Returns false if we are not tracking a subtree.
  1175    */
  1176   bool IsAfterSubtree() const
  1178     return mSubtreePosition == eAfterSubtree;
  1181 private:
  1182   /**
  1183    * The root under which all nsTextNodes will be iterated over.
  1184    */
  1185   nsIContent* mRoot;
  1187   /**
  1188    * The node rooting the subtree to track.
  1189    */
  1190   nsIContent* mSubtree;
  1192   /**
  1193    * The current node during iteration.
  1194    */
  1195   nsIContent* mCurrent;
  1197   /**
  1198    * The current iterator position relative to mSubtree.
  1199    */
  1200   SubtreePosition mSubtreePosition;
  1201 };
  1203 nsTextNode*
  1204 TextNodeIterator::Next()
  1206   // Starting from mCurrent, we do a non-recursive traversal to the next
  1207   // nsTextNode beneath mRoot, updating mSubtreePosition appropriately if we
  1208   // encounter mSubtree.
  1209   if (mCurrent) {
  1210     do {
  1211       nsIContent* next = IsTextContentElement(mCurrent) ?
  1212                            mCurrent->GetFirstChild() :
  1213                            nullptr;
  1214       if (next) {
  1215         mCurrent = next;
  1216         if (mCurrent == mSubtree) {
  1217           mSubtreePosition = eWithinSubtree;
  1219       } else {
  1220         for (;;) {
  1221           if (mCurrent == mRoot) {
  1222             mCurrent = nullptr;
  1223             break;
  1225           if (mCurrent == mSubtree) {
  1226             mSubtreePosition = eAfterSubtree;
  1228           next = mCurrent->GetNextSibling();
  1229           if (next) {
  1230             mCurrent = next;
  1231             if (mCurrent == mSubtree) {
  1232               mSubtreePosition = eWithinSubtree;
  1234             break;
  1236           if (mCurrent == mSubtree) {
  1237             mSubtreePosition = eAfterSubtree;
  1239           mCurrent = mCurrent->GetParent();
  1242     } while (mCurrent && !mCurrent->IsNodeOfType(nsINode::eTEXT));
  1245   return static_cast<nsTextNode*>(mCurrent);
  1248 // ----------------------------------------------------------------------------
  1249 // TextNodeCorrespondenceRecorder
  1251 /**
  1252  * TextNodeCorrespondence is used as the value of a frame property that
  1253  * is stored on all its descendant nsTextFrames.  It stores the number of DOM
  1254  * characters between it and the previous nsTextFrame that did not have an
  1255  * nsTextFrame created for them, due to either not being in a correctly
  1256  * parented text content element, or because they were display:none.
  1257  * These are called "undisplayed characters".
  1259  * See also TextNodeCorrespondenceRecorder below, which is what sets the
  1260  * frame property.
  1261  */
  1262 struct TextNodeCorrespondence
  1264   TextNodeCorrespondence(uint32_t aUndisplayedCharacters)
  1265     : mUndisplayedCharacters(aUndisplayedCharacters)
  1269   uint32_t mUndisplayedCharacters;
  1270 };
  1272 static void DestroyTextNodeCorrespondence(void* aPropertyValue)
  1274   delete static_cast<TextNodeCorrespondence*>(aPropertyValue);
  1277 NS_DECLARE_FRAME_PROPERTY(TextNodeCorrespondenceProperty, DestroyTextNodeCorrespondence)
  1279 /**
  1280  * Returns the number of undisplayed characters before the specified
  1281  * nsTextFrame.
  1282  */
  1283 static uint32_t
  1284 GetUndisplayedCharactersBeforeFrame(nsTextFrame* aFrame)
  1286   void* value = aFrame->Properties().Get(TextNodeCorrespondenceProperty());
  1287   TextNodeCorrespondence* correspondence =
  1288     static_cast<TextNodeCorrespondence*>(value);
  1289   if (!correspondence) {
  1290     NS_NOTREACHED("expected a TextNodeCorrespondenceProperty on nsTextFrame "
  1291                   "used for SVG text");
  1292     return 0;
  1294   return correspondence->mUndisplayedCharacters;
  1297 /**
  1298  * Traverses the nsTextFrames for an SVGTextFrame and records a
  1299  * TextNodeCorrespondenceProperty on each for the number of undisplayed DOM
  1300  * characters between each frame.  This is done by iterating simultaenously
  1301  * over the nsTextNodes and nsTextFrames and noting when nsTextNodes (or
  1302  * parts of them) are skipped when finding the next nsTextFrame.
  1303  */
  1304 class TextNodeCorrespondenceRecorder
  1306 public:
  1307   /**
  1308    * Entry point for the TextNodeCorrespondenceProperty recording.
  1309    */
  1310   static void RecordCorrespondence(SVGTextFrame* aRoot);
  1312 private:
  1313   TextNodeCorrespondenceRecorder(SVGTextFrame* aRoot)
  1314     : mNodeIterator(aRoot->GetContent()),
  1315       mPreviousNode(nullptr),
  1316       mNodeCharIndex(0)
  1320   void Record(SVGTextFrame* aRoot);
  1321   void TraverseAndRecord(nsIFrame* aFrame);
  1323   /**
  1324    * Returns the next non-empty nsTextNode.
  1325    */
  1326   nsTextNode* NextNode();
  1328   /**
  1329    * The iterator over the nsTextNodes that we use as we simultaneously
  1330    * iterate over the nsTextFrames.
  1331    */
  1332   TextNodeIterator mNodeIterator;
  1334   /**
  1335    * The previous nsTextNode we iterated over.
  1336    */
  1337   nsTextNode* mPreviousNode;
  1339   /**
  1340    * The index into the current nsTextNode's character content.
  1341    */
  1342   uint32_t mNodeCharIndex;
  1343 };
  1345 /* static */ void
  1346 TextNodeCorrespondenceRecorder::RecordCorrespondence(SVGTextFrame* aRoot)
  1348   TextNodeCorrespondenceRecorder recorder(aRoot);
  1349   recorder.Record(aRoot);
  1352 void
  1353 TextNodeCorrespondenceRecorder::Record(SVGTextFrame* aRoot)
  1355   if (!mNodeIterator.Current()) {
  1356     // If there are no nsTextNodes then there is nothing to do.
  1357     return;
  1360   // Traverse over all the nsTextFrames and record the number of undisplayed
  1361   // characters.
  1362   TraverseAndRecord(aRoot);
  1364   // Find how many undisplayed characters there are after the final nsTextFrame.
  1365   uint32_t undisplayed = 0;
  1366   if (mNodeIterator.Current()) {
  1367     if (mPreviousNode && mPreviousNode->TextLength() != mNodeCharIndex) {
  1368       // The last nsTextFrame ended part way through an nsTextNode.  The
  1369       // remaining characters count as undisplayed.
  1370       NS_ASSERTION(mNodeCharIndex < mPreviousNode->TextLength(),
  1371                    "incorrect tracking of undisplayed characters in "
  1372                    "text nodes");
  1373       undisplayed += mPreviousNode->TextLength() - mNodeCharIndex;
  1375     // All the remaining nsTextNodes that we iterate must also be undisplayed.
  1376     for (nsTextNode* textNode = mNodeIterator.Current();
  1377          textNode;
  1378          textNode = NextNode()) {
  1379       undisplayed += textNode->TextLength();
  1383   // Record the trailing number of undisplayed characters on the
  1384   // SVGTextFrame.
  1385   aRoot->mTrailingUndisplayedCharacters = undisplayed;
  1388 nsTextNode*
  1389 TextNodeCorrespondenceRecorder::NextNode()
  1391   mPreviousNode = mNodeIterator.Current();
  1392   nsTextNode* next;
  1393   do {
  1394     next = mNodeIterator.Next();
  1395   } while (next && next->TextLength() == 0);
  1396   return next;
  1399 void
  1400 TextNodeCorrespondenceRecorder::TraverseAndRecord(nsIFrame* aFrame)
  1402   // Recursively iterate over the frame tree, for frames that correspond
  1403   // to text content elements.
  1404   if (IsTextContentElement(aFrame->GetContent())) {
  1405     for (nsIFrame* f = aFrame->GetFirstPrincipalChild();
  1406          f;
  1407          f = f->GetNextSibling()) {
  1408       TraverseAndRecord(f);
  1410     return;
  1413   nsTextFrame* frame;  // The current text frame.
  1414   nsTextNode* node;    // The text node for the current text frame.
  1415   if (!GetNonEmptyTextFrameAndNode(aFrame, frame, node)) {
  1416     // If this isn't an nsTextFrame, or is empty, nothing to do.
  1417     return;
  1420   NS_ASSERTION(frame->GetContentOffset() >= 0,
  1421                "don't know how to handle negative content indexes");
  1423   uint32_t undisplayed = 0;
  1424   if (!mPreviousNode) {
  1425     // Must be the very first text frame.
  1426     NS_ASSERTION(mNodeCharIndex == 0, "incorrect tracking of undisplayed "
  1427                                       "characters in text nodes");
  1428     if (!mNodeIterator.Current()) {
  1429       NS_NOTREACHED("incorrect tracking of correspondence between text frames "
  1430                     "and text nodes");
  1431     } else {
  1432       // Each whole nsTextNode we find before we get to the text node for the
  1433       // first text frame must be undisplayed.
  1434       while (mNodeIterator.Current() != node) {
  1435         undisplayed += mNodeIterator.Current()->TextLength();
  1436         NextNode();
  1438       // If the first text frame starts at a non-zero content offset, then those
  1439       // earlier characters are also undisplayed.
  1440       undisplayed += frame->GetContentOffset();
  1441       NextNode();
  1443   } else if (mPreviousNode == node) {
  1444     // Same text node as last time.
  1445     if (static_cast<uint32_t>(frame->GetContentOffset()) != mNodeCharIndex) {
  1446       // We have some characters in the middle of the text node
  1447       // that are undisplayed.
  1448       NS_ASSERTION(mNodeCharIndex <
  1449                      static_cast<uint32_t>(frame->GetContentOffset()),
  1450                    "incorrect tracking of undisplayed characters in "
  1451                    "text nodes");
  1452       undisplayed = frame->GetContentOffset() - mNodeCharIndex;
  1454   } else {
  1455     // Different text node from last time.
  1456     if (mPreviousNode->TextLength() != mNodeCharIndex) {
  1457       NS_ASSERTION(mNodeCharIndex < mPreviousNode->TextLength(),
  1458                    "incorrect tracking of undisplayed characters in "
  1459                    "text nodes");
  1460       // Any trailing characters at the end of the previous nsTextNode are
  1461       // undisplayed.
  1462       undisplayed = mPreviousNode->TextLength() - mNodeCharIndex;
  1464     // Each whole nsTextNode we find before we get to the text node for
  1465     // the current text frame must be undisplayed.
  1466     while (mNodeIterator.Current() != node) {
  1467       undisplayed += mNodeIterator.Current()->TextLength();
  1468       NextNode();
  1470     // If the current text frame starts at a non-zero content offset, then those
  1471     // earlier characters are also undisplayed.
  1472     undisplayed += frame->GetContentOffset();
  1473     NextNode();
  1476   // Set the frame property.
  1477   frame->Properties().Set(TextNodeCorrespondenceProperty(),
  1478                           new TextNodeCorrespondence(undisplayed));
  1480   // Remember how far into the current nsTextNode we are.
  1481   mNodeCharIndex = frame->GetContentEnd();
  1484 // ----------------------------------------------------------------------------
  1485 // TextFrameIterator
  1487 /**
  1488  * An iterator class for nsTextFrames that are descendants of an
  1489  * SVGTextFrame.  The iterator can optionally track whether the
  1490  * current nsTextFrame is for a descendant of, or past, a given subtree
  1491  * content node or frame.  (This functionality is used for example by the SVG
  1492  * DOM text methods to get only the nsTextFrames for a particular <tspan>.)
  1494  * TextFrameIterator also tracks and exposes other information about the
  1495  * current nsTextFrame:
  1497  *   * how many undisplayed characters came just before it
  1498  *   * its position (in app units) relative to the SVGTextFrame's anonymous
  1499  *     block frame
  1500  *   * what nsInlineFrame corresponding to a <textPath> element it is a
  1501  *     descendant of
  1502  *   * what computed dominant-baseline value applies to it
  1504  * Note that any text frames that are empty -- whose ContentLength() is 0 --
  1505  * will be skipped over.
  1506  */
  1507 class TextFrameIterator
  1509 public:
  1510   /**
  1511    * Constructs a TextFrameIterator for the specified SVGTextFrame
  1512    * with an optional frame subtree to restrict iterated text frames to.
  1513    */
  1514   TextFrameIterator(SVGTextFrame* aRoot, nsIFrame* aSubtree = nullptr)
  1515     : mRootFrame(aRoot),
  1516       mSubtree(aSubtree),
  1517       mCurrentFrame(aRoot),
  1518       mCurrentPosition(),
  1519       mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree)
  1521     Init();
  1524   /**
  1525    * Constructs a TextFrameIterator for the specified SVGTextFrame
  1526    * with an optional frame content subtree to restrict iterated text frames to.
  1527    */
  1528   TextFrameIterator(SVGTextFrame* aRoot, nsIContent* aSubtree)
  1529     : mRootFrame(aRoot),
  1530       mSubtree(aRoot && aSubtree && aSubtree != aRoot->GetContent() ?
  1531                  aSubtree->GetPrimaryFrame() :
  1532                  nullptr),
  1533       mCurrentFrame(aRoot),
  1534       mCurrentPosition(),
  1535       mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree)
  1537     Init();
  1540   /**
  1541    * Returns the root SVGTextFrame this TextFrameIterator is iterating over.
  1542    */
  1543   SVGTextFrame* Root() const
  1545     return mRootFrame;
  1548   /**
  1549    * Returns the current nsTextFrame.
  1550    */
  1551   nsTextFrame* Current() const
  1553     return do_QueryFrame(mCurrentFrame);
  1556   /**
  1557    * Returns the number of undisplayed characters in the DOM just before the
  1558    * current frame.
  1559    */
  1560   uint32_t UndisplayedCharacters() const;
  1562   /**
  1563    * Returns the current frame's position, in app units, relative to the
  1564    * root SVGTextFrame's anonymous block frame.
  1565    */
  1566   nsPoint Position() const
  1568     return mCurrentPosition;
  1571   /**
  1572    * Advances to the next nsTextFrame and returns it.
  1573    */
  1574   nsTextFrame* Next();
  1576   /**
  1577    * Returns whether the iterator is within the subtree.
  1578    */
  1579   bool IsWithinSubtree() const
  1581     return mSubtreePosition == eWithinSubtree;
  1584   /**
  1585    * Returns whether the iterator is past the subtree.
  1586    */
  1587   bool IsAfterSubtree() const
  1589     return mSubtreePosition == eAfterSubtree;
  1592   /**
  1593    * Returns the frame corresponding to the <textPath> element, if we
  1594    * are inside one.
  1595    */
  1596   nsIFrame* TextPathFrame() const
  1598     return mTextPathFrames.IsEmpty() ?
  1599              nullptr :
  1600              mTextPathFrames.ElementAt(mTextPathFrames.Length() - 1);
  1603   /**
  1604    * Returns the current frame's computed dominant-baseline value.
  1605    */
  1606   uint8_t DominantBaseline() const
  1608     return mBaselines.ElementAt(mBaselines.Length() - 1);
  1611   /**
  1612    * Finishes the iterator.
  1613    */
  1614   void Close()
  1616     mCurrentFrame = nullptr;
  1619 private:
  1620   /**
  1621    * Initializes the iterator and advances to the first item.
  1622    */
  1623   void Init()
  1625     if (!mRootFrame) {
  1626       return;
  1629     mBaselines.AppendElement(mRootFrame->StyleSVGReset()->mDominantBaseline);
  1630     Next();
  1633   /**
  1634    * Pushes the specified frame's computed dominant-baseline value.
  1635    * If the value of the property is "auto", then the parent frame's
  1636    * computed value is used.
  1637    */
  1638   void PushBaseline(nsIFrame* aNextFrame);
  1640   /**
  1641    * Pops the current dominant-baseline off the stack.
  1642    */
  1643   void PopBaseline();
  1645   /**
  1646    * The root frame we are iterating through.
  1647    */
  1648   SVGTextFrame* mRootFrame;
  1650   /**
  1651    * The frame for the subtree we are also interested in tracking.
  1652    */
  1653   nsIFrame* mSubtree;
  1655   /**
  1656    * The current value of the iterator.
  1657    */
  1658   nsIFrame* mCurrentFrame;
  1660   /**
  1661    * The position, in app units, of the current frame relative to mRootFrame.
  1662    */
  1663   nsPoint mCurrentPosition;
  1665   /**
  1666    * Stack of frames corresponding to <textPath> elements that are in scope
  1667    * for the current frame.
  1668    */
  1669   nsAutoTArray<nsIFrame*, 1> mTextPathFrames;
  1671   /**
  1672    * Stack of dominant-baseline values to record as we traverse through the
  1673    * frame tree.
  1674    */
  1675   nsAutoTArray<uint8_t, 8> mBaselines;
  1677   /**
  1678    * The iterator's current position relative to mSubtree.
  1679    */
  1680   SubtreePosition mSubtreePosition;
  1681 };
  1683 uint32_t
  1684 TextFrameIterator::UndisplayedCharacters() const
  1686   MOZ_ASSERT(!(mRootFrame->GetFirstPrincipalChild() &&
  1687                NS_SUBTREE_DIRTY(mRootFrame->GetFirstPrincipalChild())),
  1688              "should have already reflowed the anonymous block child");
  1690   if (!mCurrentFrame) {
  1691     return mRootFrame->mTrailingUndisplayedCharacters;
  1694   nsTextFrame* frame = do_QueryFrame(mCurrentFrame);
  1695   return GetUndisplayedCharactersBeforeFrame(frame);
  1698 nsTextFrame*
  1699 TextFrameIterator::Next()
  1701   // Starting from mCurrentFrame, we do a non-recursive traversal to the next
  1702   // nsTextFrame beneath mRoot, updating mSubtreePosition appropriately if we
  1703   // encounter mSubtree.
  1704   if (mCurrentFrame) {
  1705     do {
  1706       nsIFrame* next = IsTextContentElement(mCurrentFrame->GetContent()) ?
  1707                          mCurrentFrame->GetFirstPrincipalChild() :
  1708                          nullptr;
  1709       if (next) {
  1710         // Descend into this frame, and accumulate its position.
  1711         mCurrentPosition += next->GetPosition();
  1712         if (next->GetContent()->Tag() == nsGkAtoms::textPath) {
  1713           // Record this <textPath> frame.
  1714           mTextPathFrames.AppendElement(next);
  1716         // Record the frame's baseline.
  1717         PushBaseline(next);
  1718         mCurrentFrame = next;
  1719         if (mCurrentFrame == mSubtree) {
  1720           // If the current frame is mSubtree, we have now moved into it.
  1721           mSubtreePosition = eWithinSubtree;
  1723       } else {
  1724         for (;;) {
  1725           // We want to move past the current frame.
  1726           if (mCurrentFrame == mRootFrame) {
  1727             // If we've reached the root frame, we're finished.
  1728             mCurrentFrame = nullptr;
  1729             break;
  1731           // Remove the current frame's position.
  1732           mCurrentPosition -= mCurrentFrame->GetPosition();
  1733           if (mCurrentFrame->GetContent()->Tag() == nsGkAtoms::textPath) {
  1734             // Pop off the <textPath> frame if this is a <textPath>.
  1735             mTextPathFrames.TruncateLength(mTextPathFrames.Length() - 1);
  1737           // Pop off the current baseline.
  1738           PopBaseline();
  1739           if (mCurrentFrame == mSubtree) {
  1740             // If this was mSubtree, we have now moved past it.
  1741             mSubtreePosition = eAfterSubtree;
  1743           next = mCurrentFrame->GetNextSibling();
  1744           if (next) {
  1745             // Moving to the next sibling.
  1746             mCurrentPosition += next->GetPosition();
  1747             if (next->GetContent()->Tag() == nsGkAtoms::textPath) {
  1748               // Record this <textPath> frame.
  1749               mTextPathFrames.AppendElement(next);
  1751             // Record the frame's baseline.
  1752             PushBaseline(next);
  1753             mCurrentFrame = next;
  1754             if (mCurrentFrame == mSubtree) {
  1755               // If the current frame is mSubtree, we have now moved into it.
  1756               mSubtreePosition = eWithinSubtree;
  1758             break;
  1760           if (mCurrentFrame == mSubtree) {
  1761             // If there is no next sibling frame, and the current frame is
  1762             // mSubtree, we have now moved past it.
  1763             mSubtreePosition = eAfterSubtree;
  1765           // Ascend out of this frame.
  1766           mCurrentFrame = mCurrentFrame->GetParent();
  1769     } while (mCurrentFrame &&
  1770              !IsNonEmptyTextFrame(mCurrentFrame));
  1773   return Current();
  1776 void
  1777 TextFrameIterator::PushBaseline(nsIFrame* aNextFrame)
  1779   uint8_t baseline = aNextFrame->StyleSVGReset()->mDominantBaseline;
  1780   if (baseline == NS_STYLE_DOMINANT_BASELINE_AUTO) {
  1781     baseline = mBaselines.LastElement();
  1783   mBaselines.AppendElement(baseline);
  1786 void
  1787 TextFrameIterator::PopBaseline()
  1789   NS_ASSERTION(!mBaselines.IsEmpty(), "popped too many baselines");
  1790   mBaselines.TruncateLength(mBaselines.Length() - 1);
  1793 // -----------------------------------------------------------------------------
  1794 // TextRenderedRunIterator
  1796 /**
  1797  * Iterator for TextRenderedRun objects for the SVGTextFrame.
  1798  */
  1799 class TextRenderedRunIterator
  1801 public:
  1802   /**
  1803    * Values for the aFilter argument of the constructor, to indicate which frames
  1804    * we should be limited to iterating TextRenderedRun objects for.
  1805    */
  1806   enum RenderedRunFilter {
  1807     // Iterate TextRenderedRuns for all nsTextFrames.
  1808     eAllFrames,
  1809     // Iterate only TextRenderedRuns for nsTextFrames that are
  1810     // visibility:visible.
  1811     eVisibleFrames
  1812   };
  1814   /**
  1815    * Constructs a TextRenderedRunIterator with an optional frame subtree to
  1816    * restrict iterated rendered runs to.
  1818    * @param aSVGTextFrame The SVGTextFrame whose rendered runs to iterate
  1819    *   through.
  1820    * @param aFilter Indicates whether to iterate rendered runs for non-visible
  1821    *   nsTextFrames.
  1822    * @param aSubtree An optional frame subtree to restrict iterated rendered
  1823    *   runs to.
  1824    */
  1825   TextRenderedRunIterator(SVGTextFrame* aSVGTextFrame,
  1826                           RenderedRunFilter aFilter = eAllFrames,
  1827                           nsIFrame* aSubtree = nullptr)
  1828     : mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
  1829       mFilter(aFilter),
  1830       mTextElementCharIndex(0),
  1831       mFrameStartTextElementCharIndex(0),
  1832       mFontSizeScaleFactor(aSVGTextFrame->mFontSizeScaleFactor),
  1833       mCurrent(First())
  1837   /**
  1838    * Constructs a TextRenderedRunIterator with a content subtree to restrict
  1839    * iterated rendered runs to.
  1841    * @param aSVGTextFrame The SVGTextFrame whose rendered runs to iterate
  1842    *   through.
  1843    * @param aFilter Indicates whether to iterate rendered runs for non-visible
  1844    *   nsTextFrames.
  1845    * @param aSubtree A content subtree to restrict iterated rendered runs to.
  1846    */
  1847   TextRenderedRunIterator(SVGTextFrame* aSVGTextFrame,
  1848                           RenderedRunFilter aFilter,
  1849                           nsIContent* aSubtree)
  1850     : mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
  1851       mFilter(aFilter),
  1852       mTextElementCharIndex(0),
  1853       mFrameStartTextElementCharIndex(0),
  1854       mFontSizeScaleFactor(aSVGTextFrame->mFontSizeScaleFactor),
  1855       mCurrent(First())
  1859   /**
  1860    * Returns the current TextRenderedRun.
  1861    */
  1862   TextRenderedRun Current() const
  1864     return mCurrent;
  1867   /**
  1868    * Advances to the next TextRenderedRun and returns it.
  1869    */
  1870   TextRenderedRun Next();
  1872 private:
  1873   /**
  1874    * Returns the root SVGTextFrame this iterator is for.
  1875    */
  1876   SVGTextFrame* Root() const
  1878     return mFrameIterator.Root();
  1881   /**
  1882    * Advances to the first TextRenderedRun and returns it.
  1883    */
  1884   TextRenderedRun First();
  1886   /**
  1887    * The frame iterator to use.
  1888    */
  1889   TextFrameIterator mFrameIterator;
  1891   /**
  1892    * The filter indicating which TextRenderedRuns to return.
  1893    */
  1894   RenderedRunFilter mFilter;
  1896   /**
  1897    * The character index across the entire <text> element we are currently
  1898    * up to.
  1899    */
  1900   uint32_t mTextElementCharIndex;
  1902   /**
  1903    * The character index across the entire <text> for the start of the current
  1904    * frame.
  1905    */
  1906   uint32_t mFrameStartTextElementCharIndex;
  1908   /**
  1909    * The font-size scale factor we used when constructing the nsTextFrames.
  1910    */
  1911   double mFontSizeScaleFactor;
  1913   /**
  1914    * The current TextRenderedRun.
  1915    */
  1916   TextRenderedRun mCurrent;
  1917 };
  1919 TextRenderedRun
  1920 TextRenderedRunIterator::Next()
  1922   if (!mFrameIterator.Current()) {
  1923     // If there are no more frames, then there are no more rendered runs to
  1924     // return.
  1925     mCurrent = TextRenderedRun();
  1926     return mCurrent;
  1929   // The values we will use to initialize the TextRenderedRun with.
  1930   nsTextFrame* frame;
  1931   gfxPoint pt;
  1932   double rotate;
  1933   nscoord baseline;
  1934   uint32_t offset, length;
  1935   uint32_t charIndex;
  1937   // We loop, because we want to skip over rendered runs that either aren't
  1938   // within our subtree of interest, because they don't match the filter,
  1939   // or because they are hidden due to having fallen off the end of a
  1940   // <textPath>.
  1941   for (;;) {
  1942     if (mFrameIterator.IsAfterSubtree()) {
  1943       mCurrent = TextRenderedRun();
  1944       return mCurrent;
  1947     frame = mFrameIterator.Current();
  1949     charIndex = mTextElementCharIndex;
  1951     // Find the end of the rendered run, by looking through the
  1952     // SVGTextFrame's positions array until we find one that is recorded
  1953     // as a run boundary.
  1954     uint32_t runStart, runEnd;  // XXX Replace runStart with mTextElementCharIndex.
  1955     runStart = mTextElementCharIndex;
  1956     runEnd = runStart + 1;
  1957     while (runEnd < Root()->mPositions.Length() &&
  1958            !Root()->mPositions[runEnd].mRunBoundary) {
  1959       runEnd++;
  1962     // Convert the global run start/end indexes into an offset/length into the
  1963     // current frame's nsTextNode.
  1964     offset = frame->GetContentOffset() + runStart -
  1965              mFrameStartTextElementCharIndex;
  1966     length = runEnd - runStart;
  1968     // If the end of the frame's content comes before the run boundary we found
  1969     // in SVGTextFrame's position array, we need to shorten the rendered run.
  1970     uint32_t contentEnd = frame->GetContentEnd();
  1971     if (offset + length > contentEnd) {
  1972       length = contentEnd - offset;
  1975     NS_ASSERTION(offset >= uint32_t(frame->GetContentOffset()), "invalid offset");
  1976     NS_ASSERTION(offset + length <= contentEnd, "invalid offset or length");
  1978     // Get the frame's baseline position.
  1979     frame->EnsureTextRun(nsTextFrame::eInflated);
  1980     baseline = GetBaselinePosition(frame,
  1981                                    frame->GetTextRun(nsTextFrame::eInflated),
  1982                                    mFrameIterator.DominantBaseline());
  1984     // Trim the offset/length to remove any leading/trailing white space.
  1985     uint32_t untrimmedOffset = offset;
  1986     uint32_t untrimmedLength = length;
  1987     nsTextFrame::TrimmedOffsets trimmedOffsets =
  1988       frame->GetTrimmedOffsets(frame->GetContent()->GetText(), true);
  1989     TrimOffsets(offset, length, trimmedOffsets);
  1990     charIndex += offset - untrimmedOffset;
  1992     // Get the position and rotation of the character that begins this
  1993     // rendered run.
  1994     pt = Root()->mPositions[charIndex].mPosition;
  1995     rotate = Root()->mPositions[charIndex].mAngle;
  1997     // Determine if we should skip this rendered run.
  1998     bool skip = !mFrameIterator.IsWithinSubtree() ||
  1999                 Root()->mPositions[mTextElementCharIndex].mHidden;
  2000     if (mFilter == eVisibleFrames) {
  2001       skip = skip || !frame->StyleVisibility()->IsVisible();
  2004     // Update our global character index to move past the characters
  2005     // corresponding to this rendered run.
  2006     mTextElementCharIndex += untrimmedLength;
  2008     // If we have moved past the end of the current frame's content, we need to
  2009     // advance to the next frame.
  2010     if (offset + untrimmedLength >= contentEnd) {
  2011       mFrameIterator.Next();
  2012       mTextElementCharIndex += mFrameIterator.UndisplayedCharacters();
  2013       mFrameStartTextElementCharIndex = mTextElementCharIndex;
  2016     if (!mFrameIterator.Current()) {
  2017       if (skip) {
  2018         // That was the last frame, and we skipped this rendered run.  So we
  2019         // have no rendered run to return.
  2020         mCurrent = TextRenderedRun();
  2021         return mCurrent;
  2023       break;
  2026     if (length && !skip) {
  2027       // Only return a rendered run if it didn't get collapsed away entirely
  2028       // (due to it being all white space) and if we don't want to skip it.
  2029       break;
  2033   mCurrent = TextRenderedRun(frame, pt, Root()->mLengthAdjustScaleFactor,
  2034                              rotate, mFontSizeScaleFactor, baseline,
  2035                              offset, length, charIndex);
  2036   return mCurrent;
  2039 TextRenderedRun
  2040 TextRenderedRunIterator::First()
  2042   if (!mFrameIterator.Current()) {
  2043     return TextRenderedRun();
  2046   if (Root()->mPositions.IsEmpty()) {
  2047     mFrameIterator.Close();
  2048     return TextRenderedRun();
  2051   // Get the character index for the start of this rendered run, by skipping
  2052   // any undisplayed characters.
  2053   mTextElementCharIndex = mFrameIterator.UndisplayedCharacters();
  2054   mFrameStartTextElementCharIndex = mTextElementCharIndex;
  2056   return Next();
  2059 // -----------------------------------------------------------------------------
  2060 // CharIterator
  2062 /**
  2063  * Iterator for characters within an SVGTextFrame.
  2064  */
  2065 class CharIterator
  2067 public:
  2068   /**
  2069    * Values for the aFilter argument of the constructor, to indicate which
  2070    * characters we should be iterating over.
  2071    */
  2072   enum CharacterFilter {
  2073     // Iterate over all original characters from the DOM that are within valid
  2074     // text content elements.
  2075     eOriginal,
  2076     // Iterate only over characters that are addressable by the positioning
  2077     // attributes x="", y="", etc.  This includes all characters after
  2078     // collapsing white space as required by the value of 'white-space'.
  2079     eAddressable,
  2080     // Iterate only over characters that are the first of clusters or ligature
  2081     // groups.
  2082     eClusterAndLigatureGroupStart,
  2083     // Iterate only over characters that are part of a cluster or ligature
  2084     // group but not the first character.
  2085     eClusterOrLigatureGroupMiddle
  2086   };
  2088   /**
  2089    * Constructs a CharIterator.
  2091    * @param aSVGTextFrame The SVGTextFrame whose characters to iterate
  2092    *   through.
  2093    * @param aFilter Indicates which characters to iterate over.
  2094    * @param aSubtree A content subtree to track whether the current character
  2095    *   is within.
  2096    */
  2097   CharIterator(SVGTextFrame* aSVGTextFrame,
  2098                CharacterFilter aFilter,
  2099                nsIContent* aSubtree = nullptr);
  2101   /**
  2102    * Returns whether the iterator is finished.
  2103    */
  2104   bool AtEnd() const
  2106     return !mFrameIterator.Current();
  2109   /**
  2110    * Advances to the next matching character.  Returns true if there was a
  2111    * character to advance to, and false otherwise.
  2112    */
  2113   bool Next();
  2115   /**
  2116    * Advances ahead aCount matching characters.  Returns true if there were
  2117    * enough characters to advance past, and false otherwise.
  2118    */
  2119   bool Next(uint32_t aCount);
  2121   /**
  2122    * Advances ahead up to aCount matching characters.
  2123    */
  2124   void NextWithinSubtree(uint32_t aCount);
  2126   /**
  2127    * Advances to the character with the specified index.  The index is in the
  2128    * space of original characters (i.e., all DOM characters under the <text>
  2129    * that are within valid text content elements).
  2130    */
  2131   bool AdvanceToCharacter(uint32_t aTextElementCharIndex);
  2133   /**
  2134    * Advances to the first matching character after the current nsTextFrame.
  2135    */
  2136   bool AdvancePastCurrentFrame();
  2138   /**
  2139    * Advances to the first matching character after the frames within
  2140    * the current <textPath>.
  2141    */
  2142   bool AdvancePastCurrentTextPathFrame();
  2144   /**
  2145    * Advances to the first matching character of the subtree.  Returns true
  2146    * if we successfully advance to the subtree, or if we are already within
  2147    * the subtree.  Returns false if we are past the subtree.
  2148    */
  2149   bool AdvanceToSubtree();
  2151   /**
  2152    * Returns the nsTextFrame for the current character.
  2153    */
  2154   nsTextFrame* TextFrame() const
  2156     return mFrameIterator.Current();
  2159   /**
  2160    * Returns whether the iterator is within the subtree.
  2161    */
  2162   bool IsWithinSubtree() const
  2164     return mFrameIterator.IsWithinSubtree();
  2167   /**
  2168    * Returns whether the iterator is past the subtree.
  2169    */
  2170   bool IsAfterSubtree() const
  2172     return mFrameIterator.IsAfterSubtree();
  2175   /**
  2176    * Returns whether the current character is a skipped character.
  2177    */
  2178   bool IsOriginalCharSkipped() const
  2180     return mSkipCharsIterator.IsOriginalCharSkipped();
  2183   /**
  2184    * Returns whether the current character is the start of a cluster and
  2185    * ligature group.
  2186    */
  2187   bool IsClusterAndLigatureGroupStart() const;
  2189   /**
  2190    * Returns whether the current character is trimmed away when painting,
  2191    * due to it being leading/trailing white space.
  2192    */
  2193   bool IsOriginalCharTrimmed() const;
  2195   /**
  2196    * Returns whether the current character is unaddressable from the SVG glyph
  2197    * positioning attributes.
  2198    */
  2199   bool IsOriginalCharUnaddressable() const
  2201     return IsOriginalCharSkipped() || IsOriginalCharTrimmed();
  2204   /**
  2205    * Returns the text run for the current character.
  2206    */
  2207   gfxTextRun* TextRun() const
  2209     return mTextRun;
  2212   /**
  2213    * Returns the current character index.
  2214    */
  2215   uint32_t TextElementCharIndex() const
  2217     return mTextElementCharIndex;
  2220   /**
  2221    * Returns the character index for the start of the cluster/ligature group it
  2222    * is part of.
  2223    */
  2224   uint32_t GlyphStartTextElementCharIndex() const
  2226     return mGlyphStartTextElementCharIndex;
  2229   /**
  2230    * Returns the number of undisplayed characters between the beginning of
  2231    * the glyph and the current character.
  2232    */
  2233   uint32_t GlyphUndisplayedCharacters() const
  2235     return mGlyphUndisplayedCharacters;
  2238   /**
  2239    * Gets the original character offsets within the nsTextNode for the
  2240    * cluster/ligature group the current character is a part of.
  2242    * @param aOriginalOffset The offset of the start of the cluster/ligature
  2243    *   group (output).
  2244    * @param aOriginalLength The length of cluster/ligature group (output).
  2245    */
  2246   void GetOriginalGlyphOffsets(uint32_t& aOriginalOffset,
  2247                                uint32_t& aOriginalLength) const;
  2249   /**
  2250    * Gets the advance, in user units, of the glyph the current character is
  2251    * part of.
  2253    * @param aContext The context to use for unit conversions.
  2254    */
  2255   gfxFloat GetGlyphAdvance(nsPresContext* aContext) const;
  2257   /**
  2258    * Gets the advance, in user units, of the current character.  If the
  2259    * character is a part of ligature, then the advance returned will be
  2260    * a fraction of the ligature glyph's advance.
  2262    * @param aContext The context to use for unit conversions.
  2263    */
  2264   gfxFloat GetAdvance(nsPresContext* aContext) const;
  2266   /**
  2267    * Gets the specified partial advance of the glyph the current character is
  2268    * part of.  The partial advance is measured from the first character
  2269    * corresponding to the glyph until the specified part length.
  2271    * The part length value does not include any undisplayed characters in the
  2272    * middle of the cluster/ligature group.  For example, if you have:
  2274    *   <text>f<tspan display="none">x</tspan>i</text>
  2276    * and the "f" and "i" are ligaturized, then calling GetGlyphPartialAdvance
  2277    * with aPartLength values will have the following results:
  2279    *   0 => 0
  2280    *   1 => adv("fi") / 2
  2281    *   2 => adv("fi")
  2283    * @param aPartLength The number of characters in the cluster/ligature group
  2284    *   to measure.
  2285    * @param aContext The context to use for unit conversions.
  2286    */
  2287   gfxFloat GetGlyphPartialAdvance(uint32_t aPartLength,
  2288                                   nsPresContext* aContext) const;
  2290   /**
  2291    * Returns the frame corresponding to the <textPath> that the current
  2292    * character is within.
  2293    */
  2294   nsIFrame* TextPathFrame() const
  2296     return mFrameIterator.TextPathFrame();
  2299 private:
  2300   /**
  2301    * Advances to the next character without checking it against the filter.
  2302    * Returns true if there was a next character to advance to, or false
  2303    * otherwise.
  2304    */
  2305   bool NextCharacter();
  2307   /**
  2308    * Returns whether the current character matches the filter.
  2309    */
  2310   bool MatchesFilter() const;
  2312   /**
  2313    * If this is the start of a glyph, record it.
  2314    */
  2315   void UpdateGlyphStartTextElementCharIndex() {
  2316     if (!IsOriginalCharSkipped() && IsClusterAndLigatureGroupStart()) {
  2317       mGlyphStartTextElementCharIndex = mTextElementCharIndex;
  2318       mGlyphUndisplayedCharacters = 0;
  2322   /**
  2323    * The filter to use.
  2324    */
  2325   CharacterFilter mFilter;
  2327   /**
  2328    * The iterator for text frames.
  2329    */
  2330   TextFrameIterator mFrameIterator;
  2332   /**
  2333    * A gfxSkipCharsIterator for the text frame the current character is
  2334    * a part of.
  2335    */
  2336   gfxSkipCharsIterator mSkipCharsIterator;
  2338   // Cache for information computed by IsOriginalCharTrimmed.
  2339   mutable nsTextFrame* mFrameForTrimCheck;
  2340   mutable uint32_t mTrimmedOffset;
  2341   mutable uint32_t mTrimmedLength;
  2343   /**
  2344    * The text run the current character is a part of.
  2345    */
  2346   gfxTextRun* mTextRun;
  2348   /**
  2349    * The current character's index.
  2350    */
  2351   uint32_t mTextElementCharIndex;
  2353   /**
  2354    * The index of the character that starts the cluster/ligature group the
  2355    * current character is a part of.
  2356    */
  2357   uint32_t mGlyphStartTextElementCharIndex;
  2359   /**
  2360    * If we are iterating in mode eClusterOrLigatureGroupMiddle, then
  2361    * this tracks how many undisplayed characters were encountered
  2362    * between the start of this glyph (at mGlyphStartTextElementCharIndex)
  2363    * and the current character (at mTextElementCharIndex).
  2364    */
  2365   uint32_t mGlyphUndisplayedCharacters;
  2367   /**
  2368    * The scale factor to apply to glyph advances returned by
  2369    * GetGlyphAdvance etc. to take into account textLength="".
  2370    */
  2371   float mLengthAdjustScaleFactor;
  2372 };
  2374 CharIterator::CharIterator(SVGTextFrame* aSVGTextFrame,
  2375                            CharIterator::CharacterFilter aFilter,
  2376                            nsIContent* aSubtree)
  2377   : mFilter(aFilter),
  2378     mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
  2379     mFrameForTrimCheck(nullptr),
  2380     mTrimmedOffset(0),
  2381     mTrimmedLength(0),
  2382     mTextElementCharIndex(0),
  2383     mGlyphStartTextElementCharIndex(0),
  2384     mLengthAdjustScaleFactor(aSVGTextFrame->mLengthAdjustScaleFactor)
  2386   if (!AtEnd()) {
  2387     mSkipCharsIterator = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  2388     mTextRun = TextFrame()->GetTextRun(nsTextFrame::eInflated);
  2389     mTextElementCharIndex = mFrameIterator.UndisplayedCharacters();
  2390     UpdateGlyphStartTextElementCharIndex();
  2391     if (!MatchesFilter()) {
  2392       Next();
  2397 bool
  2398 CharIterator::Next()
  2400   while (NextCharacter()) {
  2401     if (MatchesFilter()) {
  2402       return true;
  2405   return false;
  2408 bool
  2409 CharIterator::Next(uint32_t aCount)
  2411   if (aCount == 0 && AtEnd()) {
  2412     return false;
  2414   while (aCount) {
  2415     if (!Next()) {
  2416       return false;
  2418     aCount--;
  2420   return true;
  2423 void
  2424 CharIterator::NextWithinSubtree(uint32_t aCount)
  2426   while (IsWithinSubtree() && aCount) {
  2427     --aCount;
  2428     if (!Next()) {
  2429       return;
  2434 bool
  2435 CharIterator::AdvanceToCharacter(uint32_t aTextElementCharIndex)
  2437   while (mTextElementCharIndex < aTextElementCharIndex) {
  2438     if (!Next()) {
  2439       return false;
  2442   return true;
  2445 bool
  2446 CharIterator::AdvancePastCurrentFrame()
  2448   // XXX Can do this better than one character at a time if it matters.
  2449   nsTextFrame* currentFrame = TextFrame();
  2450   do {
  2451     if (!Next()) {
  2452       return false;
  2454   } while (TextFrame() == currentFrame);
  2455   return true;
  2458 bool
  2459 CharIterator::AdvancePastCurrentTextPathFrame()
  2461   nsIFrame* currentTextPathFrame = TextPathFrame();
  2462   NS_ASSERTION(currentTextPathFrame,
  2463                "expected AdvancePastCurrentTextPathFrame to be called only "
  2464                "within a text path frame");
  2465   do {
  2466     if (!AdvancePastCurrentFrame()) {
  2467       return false;
  2469   } while (TextPathFrame() == currentTextPathFrame);
  2470   return true;
  2473 bool
  2474 CharIterator::AdvanceToSubtree()
  2476   while (!IsWithinSubtree()) {
  2477     if (IsAfterSubtree()) {
  2478       return false;
  2480     if (!AdvancePastCurrentFrame()) {
  2481       return false;
  2484   return true;
  2487 bool
  2488 CharIterator::IsClusterAndLigatureGroupStart() const
  2490   return mTextRun->IsLigatureGroupStart(mSkipCharsIterator.GetSkippedOffset()) &&
  2491          mTextRun->IsClusterStart(mSkipCharsIterator.GetSkippedOffset());
  2494 bool
  2495 CharIterator::IsOriginalCharTrimmed() const
  2497   if (mFrameForTrimCheck != TextFrame()) {
  2498     // Since we do a lot of trim checking, we cache the trimmed offsets and
  2499     // lengths while we are in the same frame.
  2500     mFrameForTrimCheck = TextFrame();
  2501     uint32_t offset = mFrameForTrimCheck->GetContentOffset();
  2502     uint32_t length = mFrameForTrimCheck->GetContentLength();
  2503     nsIContent* content = mFrameForTrimCheck->GetContent();
  2504     nsTextFrame::TrimmedOffsets trim =
  2505       mFrameForTrimCheck->GetTrimmedOffsets(content->GetText(), true);
  2506     TrimOffsets(offset, length, trim);
  2507     mTrimmedOffset = offset;
  2508     mTrimmedLength = length;
  2511   // A character is trimmed if it is outside the mTrimmedOffset/mTrimmedLength
  2512   // range and it is not a significant newline character.
  2513   uint32_t index = mSkipCharsIterator.GetOriginalOffset();
  2514   return !((index >= mTrimmedOffset &&
  2515             index < mTrimmedOffset + mTrimmedLength) ||
  2516            (index >= mTrimmedOffset + mTrimmedLength &&
  2517             mFrameForTrimCheck->StyleText()->NewlineIsSignificant() &&
  2518             mFrameForTrimCheck->GetContent()->GetText()->CharAt(index) == '\n'));
  2521 void
  2522 CharIterator::GetOriginalGlyphOffsets(uint32_t& aOriginalOffset,
  2523                                       uint32_t& aOriginalLength) const
  2525   gfxSkipCharsIterator it = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  2526   it.SetOriginalOffset(mSkipCharsIterator.GetOriginalOffset() -
  2527                          (mTextElementCharIndex -
  2528                           mGlyphStartTextElementCharIndex -
  2529                           mGlyphUndisplayedCharacters));
  2531   while (it.GetSkippedOffset() > 0 &&
  2532          (!mTextRun->IsClusterStart(it.GetSkippedOffset()) ||
  2533           !mTextRun->IsLigatureGroupStart(it.GetSkippedOffset()))) {
  2534     it.AdvanceSkipped(-1);
  2537   aOriginalOffset = it.GetOriginalOffset();
  2539   // Find the end of the cluster/ligature group.
  2540   it.SetOriginalOffset(mSkipCharsIterator.GetOriginalOffset());
  2541   do {
  2542     it.AdvanceSkipped(1);
  2543   } while (it.GetSkippedOffset() < mTextRun->GetLength() &&
  2544            (!mTextRun->IsClusterStart(it.GetSkippedOffset()) ||
  2545             !mTextRun->IsLigatureGroupStart(it.GetSkippedOffset())));
  2547   aOriginalLength = it.GetOriginalOffset() - aOriginalOffset;
  2550 gfxFloat
  2551 CharIterator::GetGlyphAdvance(nsPresContext* aContext) const
  2553   uint32_t offset, length;
  2554   GetOriginalGlyphOffsets(offset, length);
  2556   gfxSkipCharsIterator it = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  2557   ConvertOriginalToSkipped(it, offset, length);
  2559   float cssPxPerDevPx = aContext->
  2560     AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
  2562   gfxFloat advance = mTextRun->GetAdvanceWidth(offset, length, nullptr);
  2563   return aContext->AppUnitsToGfxUnits(advance) *
  2564          mLengthAdjustScaleFactor * cssPxPerDevPx;
  2567 gfxFloat
  2568 CharIterator::GetAdvance(nsPresContext* aContext) const
  2570   float cssPxPerDevPx = aContext->
  2571     AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
  2573   gfxFloat advance =
  2574     mTextRun->GetAdvanceWidth(mSkipCharsIterator.GetSkippedOffset(), 1, nullptr);
  2575   return aContext->AppUnitsToGfxUnits(advance) *
  2576          mLengthAdjustScaleFactor * cssPxPerDevPx;
  2579 gfxFloat
  2580 CharIterator::GetGlyphPartialAdvance(uint32_t aPartLength,
  2581                                      nsPresContext* aContext) const
  2583   uint32_t offset, length;
  2584   GetOriginalGlyphOffsets(offset, length);
  2586   NS_ASSERTION(aPartLength <= length, "invalid aPartLength value");
  2587   length = aPartLength;
  2589   gfxSkipCharsIterator it = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  2590   ConvertOriginalToSkipped(it, offset, length);
  2592   float cssPxPerDevPx = aContext->
  2593     AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
  2595   gfxFloat advance = mTextRun->GetAdvanceWidth(offset, length, nullptr);
  2596   return aContext->AppUnitsToGfxUnits(advance) *
  2597          mLengthAdjustScaleFactor * cssPxPerDevPx;
  2600 bool
  2601 CharIterator::NextCharacter()
  2603   if (AtEnd()) {
  2604     return false;
  2607   mTextElementCharIndex++;
  2609   // Advance within the current text run.
  2610   mSkipCharsIterator.AdvanceOriginal(1);
  2611   if (mSkipCharsIterator.GetOriginalOffset() < TextFrame()->GetContentEnd()) {
  2612     // We're still within the part of the text run for the current text frame.
  2613     UpdateGlyphStartTextElementCharIndex();
  2614     return true;
  2617   // Advance to the next frame.
  2618   mFrameIterator.Next();
  2620   // Skip any undisplayed characters.
  2621   uint32_t undisplayed = mFrameIterator.UndisplayedCharacters();
  2622   mGlyphUndisplayedCharacters += undisplayed;
  2623   mTextElementCharIndex += undisplayed;
  2624   if (!TextFrame()) {
  2625     // We're at the end.
  2626     mSkipCharsIterator = gfxSkipCharsIterator();
  2627     return false;
  2630   mSkipCharsIterator = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  2631   mTextRun = TextFrame()->GetTextRun(nsTextFrame::eInflated);
  2632   UpdateGlyphStartTextElementCharIndex();
  2633   return true;
  2636 bool
  2637 CharIterator::MatchesFilter() const
  2639   if (mFilter == eOriginal) {
  2640     return true;
  2643   if (IsOriginalCharSkipped()) {
  2644     return false;
  2647   if (mFilter == eAddressable) {
  2648     return !IsOriginalCharUnaddressable();
  2651   return (mFilter == eClusterAndLigatureGroupStart) ==
  2652          IsClusterAndLigatureGroupStart();
  2655 // -----------------------------------------------------------------------------
  2656 // nsCharClipDisplayItem
  2658 /**
  2659  * An nsCharClipDisplayItem that obtains its left and right clip edges from a
  2660  * TextRenderedRun object.
  2661  */
  2662 class SVGCharClipDisplayItem : public nsCharClipDisplayItem {
  2663 public:
  2664   SVGCharClipDisplayItem(const TextRenderedRun& aRun)
  2665     : nsCharClipDisplayItem(aRun.mFrame)
  2667     aRun.GetClipEdges(mLeftEdge, mRightEdge);
  2670   NS_DISPLAY_DECL_NAME("SVGText", TYPE_TEXT)
  2671 };
  2673 // -----------------------------------------------------------------------------
  2674 // SVGTextDrawPathCallbacks
  2676 /**
  2677  * Text frame draw callback class that paints the text and text decoration parts
  2678  * of an nsTextFrame using SVG painting properties, and selection backgrounds
  2679  * and decorations as they would normally.
  2681  * An instance of this class is passed to nsTextFrame::PaintText if painting
  2682  * cannot be done directly (e.g. if we are using an SVG pattern fill, stroking
  2683  * the text, etc.).
  2684  */
  2685 class SVGTextDrawPathCallbacks : public nsTextFrame::DrawPathCallbacks
  2687 public:
  2688   /**
  2689    * Constructs an SVGTextDrawPathCallbacks.
  2691    * @param aContext The context to use for painting.
  2692    * @param aFrame The nsTextFrame to paint.
  2693    * @param aCanvasTM The transformation matrix to set when painting; this
  2694    *   should be the FOR_OUTERSVG_TM canvas TM of the text, so that
  2695    *   paint servers are painted correctly.
  2696    * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
  2697    */
  2698   SVGTextDrawPathCallbacks(nsRenderingContext* aContext,
  2699                            nsTextFrame* aFrame,
  2700                            const gfxMatrix& aCanvasTM,
  2701                            bool aShouldPaintSVGGlyphs)
  2702     : DrawPathCallbacks(aShouldPaintSVGGlyphs),
  2703       gfx(aContext->ThebesContext()),
  2704       mRenderMode(SVGAutoRenderState::GetRenderMode(aContext)),
  2705       mFrame(aFrame),
  2706       mCanvasTM(aCanvasTM)
  2710   void NotifyBeforeText(nscolor aColor) MOZ_OVERRIDE;
  2711   void NotifyGlyphPathEmitted() MOZ_OVERRIDE;
  2712   void NotifyBeforeSVGGlyphPainted() MOZ_OVERRIDE;
  2713   void NotifyAfterSVGGlyphPainted() MOZ_OVERRIDE;
  2714   void NotifyAfterText() MOZ_OVERRIDE;
  2715   void NotifyBeforeSelectionBackground(nscolor aColor) MOZ_OVERRIDE;
  2716   void NotifySelectionBackgroundPathEmitted() MOZ_OVERRIDE;
  2717   void NotifyBeforeDecorationLine(nscolor aColor) MOZ_OVERRIDE;
  2718   void NotifyDecorationLinePathEmitted() MOZ_OVERRIDE;
  2719   void NotifyBeforeSelectionDecorationLine(nscolor aColor) MOZ_OVERRIDE;
  2720   void NotifySelectionDecorationLinePathEmitted() MOZ_OVERRIDE;
  2722 private:
  2723   void FillWithOpacity();
  2725   void SetupContext();
  2727   /**
  2728    * Paints a piece of text geometry.  This is called when glyphs
  2729    * or text decorations have been emitted to the gfxContext.
  2730    */
  2731   void HandleTextGeometry();
  2733   /**
  2734    * Sets the gfxContext paint to the appropriate color or pattern
  2735    * for filling text geometry.
  2736    */
  2737   bool SetFillColor();
  2739   /**
  2740    * Fills and strokes a piece of text geometry, using group opacity
  2741    * if the selection style requires it.
  2742    */
  2743   void FillAndStrokeGeometry();
  2745   /**
  2746    * Fills a piece of text geometry.
  2747    */
  2748   void FillGeometry();
  2750   /**
  2751    * Strokes a piece of text geometry.
  2752    */
  2753   void StrokeGeometry();
  2755   gfxContext* gfx;
  2756   uint16_t mRenderMode;
  2757   nsTextFrame* mFrame;
  2758   const gfxMatrix& mCanvasTM;
  2760   /**
  2761    * The color that we were last told from one of the path callback functions.
  2762    * This color can be the special NS_SAME_AS_FOREGROUND_COLOR,
  2763    * NS_40PERCENT_FOREGROUND_COLOR and NS_TRANSPARENT colors when we are
  2764    * painting selections or IME decorations.
  2765    */
  2766   nscolor mColor;
  2767 };
  2769 void
  2770 SVGTextDrawPathCallbacks::NotifyBeforeText(nscolor aColor)
  2772   mColor = aColor;
  2773   SetupContext();
  2774   gfx->NewPath();
  2777 void
  2778 SVGTextDrawPathCallbacks::NotifyGlyphPathEmitted()
  2780   HandleTextGeometry();
  2781   gfx->NewPath();
  2784 void
  2785 SVGTextDrawPathCallbacks::NotifyBeforeSVGGlyphPainted()
  2787   gfx->Save();
  2790 void
  2791 SVGTextDrawPathCallbacks::NotifyAfterSVGGlyphPainted()
  2793   gfx->Restore();
  2794   gfx->NewPath();
  2797 void
  2798 SVGTextDrawPathCallbacks::NotifyAfterText()
  2800   gfx->Restore();
  2803 void
  2804 SVGTextDrawPathCallbacks::NotifyBeforeSelectionBackground(nscolor aColor)
  2806   if (mRenderMode != SVGAutoRenderState::NORMAL) {
  2807     // Don't paint selection backgrounds when in a clip path.
  2808     return;
  2811   mColor = aColor;
  2812   gfx->Save();
  2815 void
  2816 SVGTextDrawPathCallbacks::NotifySelectionBackgroundPathEmitted()
  2818   if (mRenderMode != SVGAutoRenderState::NORMAL) {
  2819     // Don't paint selection backgrounds when in a clip path.
  2820     return;
  2823   if (SetFillColor()) {
  2824     FillWithOpacity();
  2826   gfx->Restore();
  2829 void
  2830 SVGTextDrawPathCallbacks::NotifyBeforeDecorationLine(nscolor aColor)
  2832   mColor = aColor;
  2833   SetupContext();
  2836 void
  2837 SVGTextDrawPathCallbacks::NotifyDecorationLinePathEmitted()
  2839   HandleTextGeometry();
  2840   gfx->NewPath();
  2841   gfx->Restore();
  2844 void
  2845 SVGTextDrawPathCallbacks::NotifyBeforeSelectionDecorationLine(nscolor aColor)
  2847   if (mRenderMode != SVGAutoRenderState::NORMAL) {
  2848     // Don't paint selection decorations when in a clip path.
  2849     return;
  2852   mColor = aColor;
  2853   gfx->Save();
  2856 void
  2857 SVGTextDrawPathCallbacks::NotifySelectionDecorationLinePathEmitted()
  2859   if (mRenderMode != SVGAutoRenderState::NORMAL) {
  2860     // Don't paint selection decorations when in a clip path.
  2861     return;
  2864   FillAndStrokeGeometry();
  2865   gfx->Restore();
  2868 void
  2869 SVGTextDrawPathCallbacks::FillWithOpacity()
  2871   gfx->FillWithOpacity(mColor == NS_40PERCENT_FOREGROUND_COLOR ? 0.4 : 1.0);
  2874 void
  2875 SVGTextDrawPathCallbacks::SetupContext()
  2877   gfx->Save();
  2879   // XXX This is copied from nsSVGGlyphFrame::Render, but cairo doesn't actually
  2880   // seem to do anything with the antialias mode.  So we can perhaps remove it,
  2881   // or make SetAntialiasMode set cairo text antialiasing too.
  2882   switch (mFrame->StyleSVG()->mTextRendering) {
  2883   case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
  2884     gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
  2885     break;
  2886   default:
  2887     gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
  2888     break;
  2892 void
  2893 SVGTextDrawPathCallbacks::HandleTextGeometry()
  2895   if (mRenderMode != SVGAutoRenderState::NORMAL) {
  2896     // We're in a clip path.
  2897     if (mRenderMode == SVGAutoRenderState::CLIP_MASK) {
  2898       gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
  2899       gfx->Fill();
  2901   } else {
  2902     // Normal painting.
  2903     gfxContextMatrixAutoSaveRestore saveMatrix(gfx);
  2904     gfx->SetMatrix(mCanvasTM);
  2906     FillAndStrokeGeometry();
  2910 bool
  2911 SVGTextDrawPathCallbacks::SetFillColor()
  2913   if (mColor == NS_SAME_AS_FOREGROUND_COLOR ||
  2914       mColor == NS_40PERCENT_FOREGROUND_COLOR) {
  2915     return nsSVGUtils::SetupCairoFillPaint(mFrame, gfx);
  2918   if (mColor == NS_TRANSPARENT) {
  2919     return false;
  2922   gfx->SetColor(gfxRGBA(mColor));
  2923   return true;
  2926 void
  2927 SVGTextDrawPathCallbacks::FillAndStrokeGeometry()
  2929   bool pushedGroup = false;
  2930   if (mColor == NS_40PERCENT_FOREGROUND_COLOR) {
  2931     pushedGroup = true;
  2932     gfx->PushGroup(gfxContentType::COLOR_ALPHA);
  2935   uint32_t paintOrder = mFrame->StyleSVG()->mPaintOrder;
  2936   if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
  2937     FillGeometry();
  2938     StrokeGeometry();
  2939   } else {
  2940     while (paintOrder) {
  2941       uint32_t component =
  2942         paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
  2943       switch (component) {
  2944         case NS_STYLE_PAINT_ORDER_FILL:
  2945           FillGeometry();
  2946           break;
  2947         case NS_STYLE_PAINT_ORDER_STROKE:
  2948           StrokeGeometry();
  2949           break;
  2951       paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
  2955   if (pushedGroup) {
  2956     gfx->PopGroupToSource();
  2957     gfx->Paint(0.4);
  2961 void
  2962 SVGTextDrawPathCallbacks::FillGeometry()
  2964   if (SetFillColor()) {
  2965     gfx->Fill();
  2969 void
  2970 SVGTextDrawPathCallbacks::StrokeGeometry()
  2972   if (mColor == NS_SAME_AS_FOREGROUND_COLOR ||
  2973       mColor == NS_40PERCENT_FOREGROUND_COLOR) {
  2974     // Don't paint the stroke when we are filling with a selection color.
  2975     if (nsSVGUtils::SetupCairoStroke(mFrame, gfx)) {
  2976       gfx->Stroke();
  2981 //----------------------------------------------------------------------
  2982 // SVGTextContextPaint methods:
  2984 already_AddRefed<gfxPattern>
  2985 SVGTextContextPaint::GetFillPattern(float aOpacity,
  2986                                     const gfxMatrix& aCTM)
  2988   return mFillPaint.GetPattern(aOpacity, &nsStyleSVG::mFill, aCTM);
  2991 already_AddRefed<gfxPattern>
  2992 SVGTextContextPaint::GetStrokePattern(float aOpacity,
  2993                                       const gfxMatrix& aCTM)
  2995   return mStrokePaint.GetPattern(aOpacity, &nsStyleSVG::mStroke, aCTM);
  2998 already_AddRefed<gfxPattern>
  2999 SVGTextContextPaint::Paint::GetPattern(float aOpacity,
  3000                                        nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
  3001                                        const gfxMatrix& aCTM)
  3003   nsRefPtr<gfxPattern> pattern;
  3004   if (mPatternCache.Get(aOpacity, getter_AddRefs(pattern))) {
  3005     // Set the pattern matrix just in case it was messed with by a previous
  3006     // caller. We should get the same matrix each time a pattern is constructed
  3007     // so this should be fine.
  3008     pattern->SetMatrix(aCTM * mPatternMatrix);
  3009     return pattern.forget();
  3012   switch (mPaintType) {
  3013   case eStyleSVGPaintType_None:
  3014     pattern = new gfxPattern(gfxRGBA(0.0f, 0.0f, 0.0f, 0.0f));
  3015     mPatternMatrix = gfxMatrix();
  3016     break;
  3017   case eStyleSVGPaintType_Color:
  3018     pattern = new gfxPattern(gfxRGBA(NS_GET_R(mPaintDefinition.mColor) / 255.0,
  3019                                      NS_GET_G(mPaintDefinition.mColor) / 255.0,
  3020                                      NS_GET_B(mPaintDefinition.mColor) / 255.0,
  3021                                      NS_GET_A(mPaintDefinition.mColor) / 255.0 * aOpacity));
  3022     mPatternMatrix = gfxMatrix();
  3023     break;
  3024   case eStyleSVGPaintType_Server:
  3025     pattern = mPaintDefinition.mPaintServerFrame->GetPaintServerPattern(mFrame,
  3026                                                                         mContextMatrix,
  3027                                                                         aFillOrStroke,
  3028                                                                         aOpacity);
  3030       // m maps original-user-space to pattern space
  3031       gfxMatrix m = pattern->GetMatrix();
  3032       gfxMatrix deviceToOriginalUserSpace = mContextMatrix;
  3033       deviceToOriginalUserSpace.Invert();
  3034       // mPatternMatrix maps device space to pattern space via original user space
  3035       mPatternMatrix = deviceToOriginalUserSpace * m;
  3037     pattern->SetMatrix(aCTM * mPatternMatrix);
  3038     break;
  3039   case eStyleSVGPaintType_ContextFill:
  3040     pattern = mPaintDefinition.mContextPaint->GetFillPattern(aOpacity, aCTM);
  3041     // Don't cache this. mContextPaint will have cached it anyway. If we
  3042     // cache it, we'll have to compute mPatternMatrix, which is annoying.
  3043     return pattern.forget();
  3044   case eStyleSVGPaintType_ContextStroke:
  3045     pattern = mPaintDefinition.mContextPaint->GetStrokePattern(aOpacity, aCTM);
  3046     // Don't cache this. mContextPaint will have cached it anyway. If we
  3047     // cache it, we'll have to compute mPatternMatrix, which is annoying.
  3048     return pattern.forget();
  3049   default:
  3050     MOZ_ASSERT(false, "invalid paint type");
  3051     return nullptr;
  3054   mPatternCache.Put(aOpacity, pattern);
  3055   return pattern.forget();
  3058 } // namespace mozilla
  3061 // ============================================================================
  3062 // SVGTextFrame
  3064 // ----------------------------------------------------------------------------
  3065 // Display list item
  3067 class nsDisplaySVGText : public nsDisplayItem {
  3068 public:
  3069   nsDisplaySVGText(nsDisplayListBuilder* aBuilder,
  3070                    SVGTextFrame* aFrame)
  3071     : nsDisplayItem(aBuilder, aFrame),
  3072       mDisableSubpixelAA(false)
  3074     MOZ_COUNT_CTOR(nsDisplaySVGText);
  3075     NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
  3077 #ifdef NS_BUILD_REFCNT_LOGGING
  3078   virtual ~nsDisplaySVGText() {
  3079     MOZ_COUNT_DTOR(nsDisplaySVGText);
  3081 #endif
  3083   NS_DISPLAY_DECL_NAME("nsDisplaySVGText", TYPE_SVG_TEXT)
  3085   virtual void DisableComponentAlpha() MOZ_OVERRIDE {
  3086     mDisableSubpixelAA = true;
  3088   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  3089                        HitTestState* aState,
  3090                        nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE;
  3091   virtual void Paint(nsDisplayListBuilder* aBuilder,
  3092                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
  3093   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE {
  3094     bool snap;
  3095     return GetBounds(aBuilder, &snap);
  3097 private:
  3098   bool mDisableSubpixelAA;
  3099 };
  3101 void
  3102 nsDisplaySVGText::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  3103                           HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
  3105   SVGTextFrame *frame = static_cast<SVGTextFrame*>(mFrame);
  3106   nsPoint pointRelativeToReferenceFrame = aRect.Center();
  3107   // ToReferenceFrame() includes frame->GetPosition(), our user space position.
  3108   nsPoint userSpacePt = pointRelativeToReferenceFrame -
  3109                           (ToReferenceFrame() - frame->GetPosition());
  3111   nsIFrame* target = frame->GetFrameForPoint(userSpacePt);
  3112   if (target) {
  3113     aOutFrames->AppendElement(target);
  3117 void
  3118 nsDisplaySVGText::Paint(nsDisplayListBuilder* aBuilder,
  3119                         nsRenderingContext* aCtx)
  3121   gfxContextAutoDisableSubpixelAntialiasing
  3122     disable(aCtx->ThebesContext(), mDisableSubpixelAA);
  3124   // ToReferenceFrame includes our mRect offset, but painting takes
  3125   // account of that too. To avoid double counting, we subtract that
  3126   // here.
  3127   nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
  3129   aCtx->PushState();
  3130   aCtx->Translate(offset);
  3131   static_cast<SVGTextFrame*>(mFrame)->PaintSVG(aCtx, nullptr);
  3132   aCtx->PopState();
  3135 // ---------------------------------------------------------------------
  3136 // nsQueryFrame methods
  3138 NS_QUERYFRAME_HEAD(SVGTextFrame)
  3139   NS_QUERYFRAME_ENTRY(SVGTextFrame)
  3140 NS_QUERYFRAME_TAIL_INHERITING(SVGTextFrameBase)
  3142 // ---------------------------------------------------------------------
  3143 // Implementation
  3145 nsIFrame*
  3146 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  3148   return new (aPresShell) SVGTextFrame(aContext);
  3151 NS_IMPL_FRAMEARENA_HELPERS(SVGTextFrame)
  3153 // ---------------------------------------------------------------------
  3154 // nsIFrame methods
  3156 void
  3157 SVGTextFrame::Init(nsIContent* aContent,
  3158                    nsIFrame* aParent,
  3159                    nsIFrame* aPrevInFlow)
  3161   NS_ASSERTION(aContent->IsSVG(nsGkAtoms::text), "Content is not an SVG text");
  3163   SVGTextFrameBase::Init(aContent, aParent, aPrevInFlow);
  3164   AddStateBits((aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) |
  3165                NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_SVG_TEXT);
  3167   mMutationObserver.StartObserving(this);
  3170 void
  3171 SVGTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  3172                                const nsRect& aDirtyRect,
  3173                                const nsDisplayListSet& aLists)
  3175   if (NS_SUBTREE_DIRTY(this)) {
  3176     // We can sometimes be asked to paint before reflow happens and we
  3177     // have updated mPositions, etc.  In this case, we just avoid
  3178     // painting.
  3179     return;
  3181   aLists.Content()->AppendNewToTop(
  3182     new (aBuilder) nsDisplaySVGText(aBuilder, this));
  3185 nsresult
  3186 SVGTextFrame::AttributeChanged(int32_t aNameSpaceID,
  3187                                nsIAtom* aAttribute,
  3188                                int32_t aModType)
  3190   if (aNameSpaceID != kNameSpaceID_None)
  3191     return NS_OK;
  3193   if (aAttribute == nsGkAtoms::transform) {
  3194     // We don't invalidate for transform changes (the layers code does that).
  3195     // Also note that SVGTransformableElement::GetAttributeChangeHint will
  3196     // return nsChangeHint_UpdateOverflow for "transform" attribute changes
  3197     // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
  3199     if (!(mState & NS_FRAME_FIRST_REFLOW) &&
  3200         mCanvasTM && mCanvasTM->IsSingular()) {
  3201       // We won't have calculated the glyph positions correctly.
  3202       NotifyGlyphMetricsChange();
  3204     mCanvasTM = nullptr;
  3205   } else if (IsGlyphPositioningAttribute(aAttribute) ||
  3206              aAttribute == nsGkAtoms::textLength ||
  3207              aAttribute == nsGkAtoms::lengthAdjust) {
  3208     NotifyGlyphMetricsChange();
  3211   return NS_OK;
  3214 nsIAtom *
  3215 SVGTextFrame::GetType() const
  3217   return nsGkAtoms::svgTextFrame;
  3220 void
  3221 SVGTextFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
  3223   if (mState & NS_FRAME_IS_NONDISPLAY) {
  3224     // We need this DidSetStyleContext override to handle cases like this:
  3225     //
  3226     //   <defs>
  3227     //     <g>
  3228     //       <mask>
  3229     //         <text>...</text>
  3230     //       </mask>
  3231     //     </g>
  3232     //   </defs>
  3233     //
  3234     // where the <text> is non-display, and a style change occurs on the <defs>,
  3235     // the <g>, the <mask>, or the <text> itself.  If the style change happened
  3236     // on the parent of the <defs>, then in
  3237     // nsSVGDisplayContainerFrame::ReflowSVG, we would find the non-display
  3238     // <defs> container and then call ReflowSVGNonDisplayText on it.  If we do
  3239     // not actually reflow the parent of the <defs>, then without this
  3240     // DidSetStyleContext we would (a) not cause the <text>'s anonymous block
  3241     // child to be reflowed when it is next painted, and (b) not cause the
  3242     // <text> to be repainted anyway since the user of the <mask> would not
  3243     // know it needs to be repainted.
  3244     ScheduleReflowSVGNonDisplayText();
  3248 void
  3249 SVGTextFrame::ReflowSVGNonDisplayText()
  3251   MOZ_ASSERT(nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(this),
  3252              "only call ReflowSVGNonDisplayText when an outer SVG frame is "
  3253              "under ReflowSVG");
  3254   MOZ_ASSERT(mState & NS_FRAME_IS_NONDISPLAY,
  3255              "only call ReflowSVGNonDisplayText if the frame is "
  3256              "NS_FRAME_IS_NONDISPLAY");
  3258   // We had a style change, so we mark this frame as dirty so that the next
  3259   // time it is painted, we reflow the anonymous block frame.
  3260   AddStateBits(NS_FRAME_IS_DIRTY);
  3262   // We also need to call InvalidateRenderingObservers, so that if the <text>
  3263   // element is within a <mask>, say, the element referencing the <mask> will
  3264   // be updated, which will then cause this SVGTextFrame to be painted and
  3265   // in doing so cause the anonymous block frame to be reflowed.
  3266   nsSVGEffects::InvalidateRenderingObservers(this);
  3268   // Finally, we need to actually reflow the anonymous block frame and update
  3269   // mPositions, in case we are being reflowed immediately after a DOM
  3270   // mutation that needs frame reconstruction.
  3271   MaybeReflowAnonymousBlockChild();
  3272   UpdateGlyphPositioning();
  3275 void
  3276 SVGTextFrame::ScheduleReflowSVGNonDisplayText()
  3278   MOZ_ASSERT(!nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
  3279              "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
  3280              "frame is under ReflowSVG");
  3281   MOZ_ASSERT(!(mState & NS_STATE_SVG_TEXT_IN_REFLOW),
  3282              "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
  3283              "anonymous block child");
  3285   // We need to find an ancestor frame that we can call FrameNeedsReflow
  3286   // on that will cause the document to be marked as needing relayout,
  3287   // and for that ancestor (or some further ancestor) to be marked as
  3288   // a root to reflow.  We choose the closest ancestor frame that is not
  3289   // NS_FRAME_IS_NONDISPLAY and which is either an outer SVG frame or a
  3290   // non-SVG frame.  (We don't consider displayed SVG frame ancestors toerh
  3291   // than nsSVGOuterSVGFrame, since calling FrameNeedsReflow on those other
  3292   // SVG frames would do a bunch of unnecessary work on the SVG frames up to
  3293   // the nsSVGOuterSVGFrame.)
  3295   nsIFrame* f = this;
  3296   while (f) {
  3297     if (!(f->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
  3298       if (NS_SUBTREE_DIRTY(f)) {
  3299         // This is a displayed frame, so if it is already dirty, we will be reflowed
  3300         // soon anyway.  No need to call FrameNeedsReflow again, then.
  3301         return;
  3303       if (!f->IsFrameOfType(eSVG) ||
  3304           (f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
  3305         break;
  3307       f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
  3309     f = f->GetParent();
  3312   MOZ_ASSERT(f, "should have found an ancestor frame to reflow");
  3314   PresContext()->PresShell()->FrameNeedsReflow(
  3315     f, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
  3318 NS_IMPL_ISUPPORTS(SVGTextFrame::MutationObserver, nsIMutationObserver)
  3320 void
  3321 SVGTextFrame::MutationObserver::ContentAppended(nsIDocument* aDocument,
  3322                                                 nsIContent* aContainer,
  3323                                                 nsIContent* aFirstNewContent,
  3324                                                 int32_t aNewIndexInContainer)
  3326   mFrame->NotifyGlyphMetricsChange();
  3329 void
  3330 SVGTextFrame::MutationObserver::ContentInserted(
  3331                                         nsIDocument* aDocument,
  3332                                         nsIContent* aContainer,
  3333                                         nsIContent* aChild,
  3334                                         int32_t aIndexInContainer)
  3336   mFrame->NotifyGlyphMetricsChange();
  3339 void
  3340 SVGTextFrame::MutationObserver::ContentRemoved(
  3341                                        nsIDocument *aDocument,
  3342                                        nsIContent* aContainer,
  3343                                        nsIContent* aChild,
  3344                                        int32_t aIndexInContainer,
  3345                                        nsIContent* aPreviousSibling)
  3347   mFrame->NotifyGlyphMetricsChange();
  3350 void
  3351 SVGTextFrame::MutationObserver::CharacterDataChanged(
  3352                                                  nsIDocument* aDocument,
  3353                                                  nsIContent* aContent,
  3354                                                  CharacterDataChangeInfo* aInfo)
  3356   mFrame->NotifyGlyphMetricsChange();
  3359 void
  3360 SVGTextFrame::MutationObserver::AttributeChanged(
  3361                                                 nsIDocument* aDocument,
  3362                                                 mozilla::dom::Element* aElement,
  3363                                                 int32_t aNameSpaceID,
  3364                                                 nsIAtom* aAttribute,
  3365                                                 int32_t aModType)
  3367   if (!aElement->IsSVG()) {
  3368     return;
  3371   // Attribute changes on this element are handled in
  3372   // SVGTextFrame::AttributeChanged.
  3373   if (aElement == mFrame->GetContent()) {
  3374     return;
  3377   // Attributes changes on descendent elements.
  3378   if (aElement->Tag() == nsGkAtoms::textPath) {
  3379     if (aNameSpaceID == kNameSpaceID_None &&
  3380         aAttribute == nsGkAtoms::startOffset) {
  3381       mFrame->NotifyGlyphMetricsChange();
  3382     } else if (aNameSpaceID == kNameSpaceID_XLink &&
  3383                aAttribute == nsGkAtoms::href) {
  3384       // Blow away our reference, if any
  3385       nsIFrame* childElementFrame = aElement->GetPrimaryFrame();
  3386       if (childElementFrame) {
  3387         childElementFrame->Properties().Delete(nsSVGEffects::HrefProperty());
  3388         mFrame->NotifyGlyphMetricsChange();
  3391   } else {
  3392     if (aNameSpaceID == kNameSpaceID_None &&
  3393         IsGlyphPositioningAttribute(aAttribute)) {
  3394       mFrame->NotifyGlyphMetricsChange();
  3399 void
  3400 SVGTextFrame::FindCloserFrameForSelection(
  3401                                  nsPoint aPoint,
  3402                                  nsIFrame::FrameWithDistance* aCurrentBestFrame)
  3404   if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
  3405     return;
  3408   UpdateGlyphPositioning();
  3410   nsPresContext* presContext = PresContext();
  3412   // Find the frame that has the closest rendered run rect to aPoint.
  3413   TextRenderedRunIterator it(this);
  3414   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  3415     uint32_t flags = TextRenderedRun::eIncludeFill |
  3416                      TextRenderedRun::eIncludeStroke |
  3417                      TextRenderedRun::eNoHorizontalOverflow;
  3418     SVGBBox userRect = run.GetUserSpaceRect(presContext, flags);
  3419     if (!userRect.IsEmpty()) {
  3420       nsRect rect = nsSVGUtils::ToCanvasBounds(userRect.ToThebesRect(),
  3421                                                GetCanvasTM(FOR_HIT_TESTING),
  3422                                                presContext);
  3424       if (nsLayoutUtils::PointIsCloserToRect(aPoint, rect,
  3425                                              aCurrentBestFrame->mXDistance,
  3426                                              aCurrentBestFrame->mYDistance)) {
  3427         aCurrentBestFrame->mFrame = run.mFrame;
  3433 //----------------------------------------------------------------------
  3434 // nsISVGChildFrame methods
  3436 void
  3437 SVGTextFrame::NotifySVGChanged(uint32_t aFlags)
  3439   NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
  3440                     "Invalidation logic may need adjusting");
  3442   bool needNewBounds = false;
  3443   bool needGlyphMetricsUpdate = false;
  3444   bool needNewCanvasTM = false;
  3446   if ((aFlags & COORD_CONTEXT_CHANGED) &&
  3447       (mState & NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES)) {
  3448     needGlyphMetricsUpdate = true;
  3451   if (aFlags & TRANSFORM_CHANGED) {
  3452     needNewCanvasTM = true;
  3453     if (mCanvasTM && mCanvasTM->IsSingular()) {
  3454       // We won't have calculated the glyph positions correctly.
  3455       needNewBounds = true;
  3456       needGlyphMetricsUpdate = true;
  3458     if (StyleSVGReset()->mVectorEffect ==
  3459         NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
  3460       // Stroke currently contributes to our mRect, and our stroke depends on
  3461       // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
  3462       needNewBounds = true;
  3466   // If the scale at which we computed our mFontSizeScaleFactor has changed by
  3467   // at least a factor of two, reflow the text.  This avoids reflowing text
  3468   // at every tick of a transform animation, but ensures our glyph metrics
  3469   // do not get too far out of sync with the final font size on the screen.
  3470   if (needNewCanvasTM && mLastContextScale != 0.0f) {
  3471     mCanvasTM = nullptr;
  3472     // If we are a non-display frame, then we don't want to call
  3473     // GetCanvasTM(FOR_OUTERSVG_TM), since the context scale does not use it.
  3474     gfxMatrix newTM =
  3475       (mState & NS_FRAME_IS_NONDISPLAY) ? gfxMatrix() :
  3476                                           GetCanvasTM(FOR_OUTERSVG_TM);
  3477     // Compare the old and new context scales.
  3478     float scale = GetContextScale(newTM);
  3479     float change = scale / mLastContextScale;
  3480     if (change >= 2.0f || change <= 0.5f) {
  3481       needNewBounds = true;
  3482       needGlyphMetricsUpdate = true;
  3486   if (needNewBounds) {
  3487     // Ancestor changes can't affect how we render from the perspective of
  3488     // any rendering observers that we may have, so we don't need to
  3489     // invalidate them. We also don't need to invalidate ourself, since our
  3490     // changed ancestor will have invalidated its entire area, which includes
  3491     // our area.
  3492     ScheduleReflowSVG();
  3495   if (needGlyphMetricsUpdate) {
  3496     // If we are positioned using percentage values we need to update our
  3497     // position whenever our viewport's dimensions change.  But only do this if
  3498     // we have been reflowed once, otherwise the glyph positioning will be
  3499     // wrong.  (We need to wait until bidi reordering has been done.)
  3500     if (!(mState & NS_FRAME_FIRST_REFLOW)) {
  3501       NotifyGlyphMetricsChange();
  3506 /**
  3507  * Gets the offset into a DOM node that the specified caret is positioned at.
  3508  */
  3509 static int32_t
  3510 GetCaretOffset(nsCaret* aCaret)
  3512   nsCOMPtr<nsISelection> selection = aCaret->GetCaretDOMSelection();
  3513   if (!selection) {
  3514     return -1;
  3517   int32_t offset = -1;
  3518   selection->GetAnchorOffset(&offset);
  3519   return offset;
  3522 /**
  3523  * Returns whether the caret should be painted for a given TextRenderedRun
  3524  * by checking whether the caret is in the range covered by the rendered run.
  3526  * @param aThisRun The TextRenderedRun to be painted.
  3527  * @param aCaret The caret.
  3528  */
  3529 static bool
  3530 ShouldPaintCaret(const TextRenderedRun& aThisRun, nsCaret* aCaret)
  3532   int32_t caretOffset = GetCaretOffset(aCaret);
  3534   if (caretOffset < 0) {
  3535     return false;
  3538   if (uint32_t(caretOffset) >= aThisRun.mTextFrameContentOffset &&
  3539       uint32_t(caretOffset) < aThisRun.mTextFrameContentOffset +
  3540                                 aThisRun.mTextFrameContentLength) {
  3541     return true;
  3544   return false;
  3547 nsresult
  3548 SVGTextFrame::PaintSVG(nsRenderingContext* aContext,
  3549                        const nsIntRect *aDirtyRect,
  3550                        nsIFrame* aTransformRoot)
  3552   nsIFrame* kid = GetFirstPrincipalChild();
  3553   if (!kid)
  3554     return NS_OK;
  3556   nsPresContext* presContext = PresContext();
  3558   gfxContext *gfx = aContext->ThebesContext();
  3559   gfxMatrix initialMatrix = gfx->CurrentMatrix();
  3561   if (mState & NS_FRAME_IS_NONDISPLAY) {
  3562     // If we are in a canvas DrawWindow call that used the
  3563     // DRAWWINDOW_DO_NOT_FLUSH flag, then we may still have out
  3564     // of date frames.  Just don't paint anything if they are
  3565     // dirty.
  3566     if (presContext->PresShell()->InDrawWindowNotFlushing() &&
  3567         NS_SUBTREE_DIRTY(this)) {
  3568       return NS_OK;
  3570     // Text frames inside <clipPath>, <mask>, etc. will never have had
  3571     // ReflowSVG called on them, so call UpdateGlyphPositioning to do this now.
  3572     UpdateGlyphPositioning();
  3573   } else if (NS_SUBTREE_DIRTY(this)) {
  3574     // If we are asked to paint before reflow has recomputed mPositions etc.
  3575     // directly via PaintSVG, rather than via a display list, then we need
  3576     // to bail out here too.
  3577     return NS_OK;
  3580   gfxMatrix canvasTM = GetCanvasTM(FOR_PAINTING, aTransformRoot);
  3581   if (canvasTM.IsSingular()) {
  3582     NS_WARNING("Can't render text element!");
  3583     return NS_ERROR_FAILURE;
  3586   gfxMatrix matrixForPaintServers(canvasTM);
  3587   matrixForPaintServers.Multiply(initialMatrix);
  3589   // Check if we need to draw anything.
  3590   if (aDirtyRect) {
  3591     NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
  3592                  (mState & NS_FRAME_IS_NONDISPLAY),
  3593                  "Display lists handle dirty rect intersection test");
  3594     nsRect dirtyRect(aDirtyRect->x, aDirtyRect->y,
  3595                      aDirtyRect->width, aDirtyRect->height);
  3597     gfxFloat appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
  3598     gfxRect frameRect(mRect.x / appUnitsPerDevPixel,
  3599                       mRect.y / appUnitsPerDevPixel,
  3600                       mRect.width / appUnitsPerDevPixel,
  3601                       mRect.height / appUnitsPerDevPixel);
  3603     nsRect canvasRect = nsLayoutUtils::RoundGfxRectToAppRect(
  3604         GetCanvasTM(FOR_OUTERSVG_TM).TransformBounds(frameRect), 1);
  3605     if (!canvasRect.Intersects(dirtyRect)) {
  3606       return NS_OK;
  3610   // SVG paints in CSS px, but normally frames paint in dev pixels. Here we
  3611   // multiply a CSS-px-to-dev-pixel factor onto canvasTM so our children paint
  3612   // correctly.
  3613   float cssPxPerDevPx = presContext->
  3614     AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  3615   gfxMatrix canvasTMForChildren = canvasTM;
  3616   canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx);
  3617   initialMatrix.Scale(1 / cssPxPerDevPx, 1 / cssPxPerDevPx);
  3619   gfxContextAutoSaveRestore save(gfx);
  3620   gfx->NewPath();
  3621   gfx->Multiply(canvasTMForChildren);
  3622   gfxMatrix currentMatrix = gfx->CurrentMatrix();
  3624   nsRefPtr<nsCaret> caret = presContext->PresShell()->GetCaret();
  3625   nsIFrame* caretFrame = caret->GetCaretFrame();
  3627   TextRenderedRunIterator it(this, TextRenderedRunIterator::eVisibleFrames);
  3628   TextRenderedRun run = it.Current();
  3629   while (run.mFrame) {
  3630     nsTextFrame* frame = run.mFrame;
  3632     // Determine how much of the left and right edges of the text frame we
  3633     // need to ignore.
  3634     SVGCharClipDisplayItem item(run);
  3636     // Set up the fill and stroke so that SVG glyphs can get painted correctly
  3637     // when they use context-fill etc.
  3638     gfx->SetMatrix(initialMatrix);
  3639     gfxTextContextPaint *outerContextPaint =
  3640       (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
  3642     nsAutoPtr<gfxTextContextPaint> contextPaint;
  3643     DrawMode drawMode =
  3644       SetupCairoState(gfx, frame, outerContextPaint,
  3645                       getter_Transfers(contextPaint));
  3647     // Set up the transform for painting the text frame for the substring
  3648     // indicated by the run.
  3649     gfxMatrix runTransform =
  3650       run.GetTransformFromUserSpaceForPainting(presContext, item);
  3651     runTransform.Multiply(currentMatrix);
  3652     gfx->SetMatrix(runTransform);
  3654     if (drawMode != DrawMode(0)) {
  3655       nsRect frameRect = frame->GetVisualOverflowRect();
  3656       bool paintSVGGlyphs;
  3657       if (ShouldRenderAsPath(aContext, frame, paintSVGGlyphs)) {
  3658         SVGTextDrawPathCallbacks callbacks(aContext, frame,
  3659                                            matrixForPaintServers,
  3660                                            paintSVGGlyphs);
  3661         frame->PaintText(aContext, nsPoint(), frameRect, item,
  3662                          contextPaint, &callbacks);
  3663       } else {
  3664         frame->PaintText(aContext, nsPoint(), frameRect, item,
  3665                          contextPaint, nullptr);
  3669     if (frame == caretFrame && ShouldPaintCaret(run, caret)) {
  3670       // XXX Should we be looking at the fill/stroke colours to paint the
  3671       // caret with, rather than using the color property?
  3672       caret->PaintCaret(nullptr, aContext, frame, nsPoint());
  3673       gfx->NewPath();
  3676     run = it.Next();
  3679   return NS_OK;
  3682 nsIFrame*
  3683 SVGTextFrame::GetFrameForPoint(const nsPoint& aPoint)
  3685   NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
  3687   if (mState & NS_FRAME_IS_NONDISPLAY) {
  3688     // Text frames inside <clipPath> will never have had ReflowSVG called on
  3689     // them, so call UpdateGlyphPositioning to do this now.  (Text frames
  3690     // inside <mask> and other non-display containers will never need to
  3691     // be hit tested.)
  3692     UpdateGlyphPositioning();
  3693   } else {
  3694     NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "reflow should have happened");
  3697   nsPresContext* presContext = PresContext();
  3699   gfxPoint pointInOuterSVGUserUnits = AppUnitsToGfxUnits(aPoint, presContext);
  3701   TextRenderedRunIterator it(this);
  3702   nsIFrame* hit = nullptr;
  3703   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  3704     uint16_t hitTestFlags = nsSVGUtils::GetGeometryHitTestFlags(run.mFrame);
  3705     if (!(hitTestFlags & (SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE))) {
  3706       continue;
  3709     gfxMatrix m = GetCanvasTM(FOR_HIT_TESTING);
  3710     m.PreMultiply(run.GetTransformFromRunUserSpaceToUserSpace(presContext));
  3711     m.Invert();
  3713     gfxPoint pointInRunUserSpace = m.Transform(pointInOuterSVGUserUnits);
  3714     gfxRect frameRect =
  3715       run.GetRunUserSpaceRect(presContext, TextRenderedRun::eIncludeFill |
  3716                                            TextRenderedRun::eIncludeStroke).ToThebesRect();
  3718     if (Inside(frameRect, pointInRunUserSpace) &&
  3719         nsSVGUtils::HitTestClip(this, aPoint)) {
  3720       hit = run.mFrame;
  3723   return hit;
  3726 nsRect
  3727 SVGTextFrame::GetCoveredRegion()
  3729   return nsSVGUtils::TransformFrameRectToOuterSVG(
  3730            mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
  3733 void
  3734 SVGTextFrame::ReflowSVG()
  3736   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
  3737                "This call is probaby a wasteful mistake");
  3739   NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
  3740                     "ReflowSVG mechanism not designed for this");
  3742   if (!nsSVGUtils::NeedsReflowSVG(this)) {
  3743     NS_ASSERTION(!(mState & NS_STATE_SVG_POSITIONING_DIRTY), "How did this happen?");
  3744     return;
  3747   MaybeReflowAnonymousBlockChild();
  3748   UpdateGlyphPositioning();
  3750   nsPresContext* presContext = PresContext();
  3752   SVGBBox r;
  3753   TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames);
  3754   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  3755     uint32_t runFlags = 0;
  3756     if (run.mFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None) {
  3757       runFlags |= TextRenderedRun::eIncludeFill |
  3758                   TextRenderedRun::eIncludeTextShadow;
  3760     if (nsSVGUtils::HasStroke(run.mFrame)) {
  3761       runFlags |= TextRenderedRun::eIncludeFill |
  3762                   TextRenderedRun::eIncludeTextShadow;
  3764     // Our "visual" overflow rect needs to be valid for building display lists
  3765     // for hit testing, which means that for certain values of 'pointer-events'
  3766     // it needs to include the geometry of the fill or stroke even when the fill/
  3767     // stroke don't actually render (e.g. when stroke="none" or
  3768     // stroke-opacity="0"). GetGeometryHitTestFlags accounts for 'pointer-events'.
  3769     // The text-shadow is not part of the hit-test area.
  3770     uint16_t hitTestFlags = nsSVGUtils::GetGeometryHitTestFlags(run.mFrame);
  3771     if (hitTestFlags & SVG_HIT_TEST_FILL) {
  3772       runFlags |= TextRenderedRun::eIncludeFill;
  3774     if (hitTestFlags & SVG_HIT_TEST_STROKE) {
  3775       runFlags |= TextRenderedRun::eIncludeStroke;
  3778     if (runFlags) {
  3779       r.UnionEdges(run.GetUserSpaceRect(presContext, runFlags));
  3783   if (r.IsEmpty()) {
  3784     mRect.SetEmpty();
  3785   } else {
  3786     mRect =
  3787       nsLayoutUtils::RoundGfxRectToAppRect(r.ToThebesRect(), presContext->AppUnitsPerCSSPixel());
  3789     // Due to rounding issues when we have a transform applied, we sometimes
  3790     // don't include an additional row of pixels.  For now, just inflate our
  3791     // covered region.
  3792     mRect.Inflate(presContext->AppUnitsPerDevPixel());
  3795   if (mState & NS_FRAME_FIRST_REFLOW) {
  3796     // Make sure we have our filter property (if any) before calling
  3797     // FinishAndStoreOverflow (subsequent filter changes are handled off
  3798     // nsChangeHint_UpdateEffects):
  3799     nsSVGEffects::UpdateEffects(this);
  3802   nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
  3803   nsOverflowAreas overflowAreas(overflow, overflow);
  3804   FinishAndStoreOverflow(overflowAreas, mRect.Size());
  3806   // Now unset the various reflow bits:
  3807   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
  3808               NS_FRAME_HAS_DIRTY_CHILDREN);
  3810   // XXX nsSVGContainerFrame::ReflowSVG only looks at its nsISVGChildFrame
  3811   // children, and calls ConsiderChildOverflow on them.  Does it matter
  3812   // that ConsiderChildOverflow won't be called on our children?
  3813   SVGTextFrameBase::ReflowSVG();
  3816 /**
  3817  * Converts nsSVGUtils::eBBox* flags into TextRenderedRun flags appropriate
  3818  * for the specified rendered run.
  3819  */
  3820 static uint32_t
  3821 TextRenderedRunFlagsForBBoxContribution(const TextRenderedRun& aRun,
  3822                                         uint32_t aBBoxFlags)
  3824   uint32_t flags = 0;
  3825   if ((aBBoxFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
  3826       ((aBBoxFlags & nsSVGUtils::eBBoxIncludeFill) &&
  3827        aRun.mFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
  3828     flags |= TextRenderedRun::eIncludeFill;
  3830   if ((aBBoxFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
  3831       ((aBBoxFlags & nsSVGUtils::eBBoxIncludeStroke) &&
  3832        nsSVGUtils::HasStroke(aRun.mFrame))) {
  3833     flags |= TextRenderedRun::eIncludeStroke;
  3835   return flags;
  3838 SVGBBox
  3839 SVGTextFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace,
  3840                                   uint32_t aFlags)
  3842   NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
  3844   UpdateGlyphPositioning();
  3846   SVGBBox bbox;
  3847   nsPresContext* presContext = PresContext();
  3849   TextRenderedRunIterator it(this);
  3850   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  3851     uint32_t flags = TextRenderedRunFlagsForBBoxContribution(run, aFlags);
  3852     gfxMatrix m = ThebesMatrix(aToBBoxUserspace);
  3853     SVGBBox bboxForRun =
  3854       run.GetUserSpaceRect(presContext, flags, &m);
  3855     bbox.UnionEdges(bboxForRun);
  3858   return bbox;
  3861 //----------------------------------------------------------------------
  3862 // nsSVGContainerFrame methods
  3864 gfxMatrix
  3865 SVGTextFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
  3867   if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
  3868       !aTransformRoot) {
  3869     if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
  3870         (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
  3871       return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
  3874   if (!mCanvasTM) {
  3875     NS_ASSERTION(mParent, "null parent");
  3876     NS_ASSERTION(!(aFor == FOR_OUTERSVG_TM &&
  3877                    (GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
  3878                  "should not call GetCanvasTM(FOR_OUTERSVG_TM) when we are "
  3879                  "non-display");
  3881     nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
  3882     dom::SVGTextContentElement *content = static_cast<dom::SVGTextContentElement*>(mContent);
  3884     gfxMatrix tm = content->PrependLocalTransformsTo(
  3885         this == aTransformRoot ? gfxMatrix() :
  3886                                  parent->GetCanvasTM(aFor, aTransformRoot));
  3888     mCanvasTM = new gfxMatrix(tm);
  3890   return *mCanvasTM;
  3893 //----------------------------------------------------------------------
  3894 // SVGTextFrame SVG DOM methods
  3896 /**
  3897  * Returns whether the specified node has any non-empty nsTextNodes
  3898  * beneath it.
  3899  */
  3900 static bool
  3901 HasTextContent(nsIContent* aContent)
  3903   NS_ASSERTION(aContent, "expected non-null aContent");
  3905   TextNodeIterator it(aContent);
  3906   for (nsTextNode* text = it.Current(); text; text = it.Next()) {
  3907     if (text->TextLength() != 0) {
  3908       return true;
  3911   return false;
  3914 /**
  3915  * Returns the number of DOM characters beneath the specified node.
  3916  */
  3917 static uint32_t
  3918 GetTextContentLength(nsIContent* aContent)
  3920   NS_ASSERTION(aContent, "expected non-null aContent");
  3922   uint32_t length = 0;
  3923   TextNodeIterator it(aContent);
  3924   for (nsTextNode* text = it.Current(); text; text = it.Next()) {
  3925     length += text->TextLength();
  3927   return length;
  3930 int32_t
  3931 SVGTextFrame::ConvertTextElementCharIndexToAddressableIndex(
  3932                                                            int32_t aIndex,
  3933                                                            nsIContent* aContent)
  3935   CharIterator it(this, CharIterator::eOriginal, aContent);
  3936   if (!it.AdvanceToSubtree()) {
  3937     return -1;
  3939   int32_t result = 0;
  3940   int32_t textElementCharIndex;
  3941   while (!it.AtEnd() &&
  3942          it.IsWithinSubtree()) {
  3943     bool addressable = !it.IsOriginalCharUnaddressable();
  3944     textElementCharIndex = it.TextElementCharIndex();
  3945     it.Next();
  3946     uint32_t delta = it.TextElementCharIndex() - textElementCharIndex;
  3947     aIndex -= delta;
  3948     if (addressable) {
  3949       if (aIndex < 0) {
  3950         return result;
  3952       result += delta;
  3955   return -1;
  3958 /**
  3959  * Implements the SVG DOM GetNumberOfChars method for the specified
  3960  * text content element.
  3961  */
  3962 uint32_t
  3963 SVGTextFrame::GetNumberOfChars(nsIContent* aContent)
  3965   UpdateGlyphPositioning();
  3967   uint32_t n = 0;
  3968   CharIterator it(this, CharIterator::eAddressable, aContent);
  3969   if (it.AdvanceToSubtree()) {
  3970     while (!it.AtEnd() && it.IsWithinSubtree()) {
  3971       n++;
  3972       it.Next();
  3975   return n;
  3978 /**
  3979  * Implements the SVG DOM GetComputedTextLength method for the specified
  3980  * text child element.
  3981  */
  3982 float
  3983 SVGTextFrame::GetComputedTextLength(nsIContent* aContent)
  3985   UpdateGlyphPositioning();
  3987   float cssPxPerDevPx = PresContext()->
  3988     AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
  3990   nscoord length = 0;
  3991   TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
  3992                              aContent);
  3993   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  3994     length += run.GetAdvanceWidth();
  3997   return PresContext()->AppUnitsToGfxUnits(length) *
  3998            cssPxPerDevPx * mLengthAdjustScaleFactor / mFontSizeScaleFactor;
  4001 /**
  4002  * Implements the SVG DOM SelectSubString method for the specified
  4003  * text content element.
  4004  */
  4005 nsresult
  4006 SVGTextFrame::SelectSubString(nsIContent* aContent,
  4007                               uint32_t charnum, uint32_t nchars)
  4009   UpdateGlyphPositioning();
  4011   // Convert charnum/nchars from addressable characters relative to
  4012   // aContent to global character indices.
  4013   CharIterator chit(this, CharIterator::eAddressable, aContent);
  4014   if (!chit.AdvanceToSubtree() ||
  4015       !chit.Next(charnum) ||
  4016       chit.IsAfterSubtree()) {
  4017     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  4019   charnum = chit.TextElementCharIndex();
  4020   nsIContent* content = chit.TextFrame()->GetContent();
  4021   chit.NextWithinSubtree(nchars);
  4022   nchars = chit.TextElementCharIndex() - charnum;
  4024   nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
  4026   frameSelection->HandleClick(content, charnum, charnum + nchars,
  4027                               false, false, false);
  4028   return NS_OK;
  4031 /**
  4032  * Implements the SVG DOM GetSubStringLength method for the specified
  4033  * text content element.
  4034  */
  4035 nsresult
  4036 SVGTextFrame::GetSubStringLength(nsIContent* aContent,
  4037                                  uint32_t charnum, uint32_t nchars,
  4038                                  float* aResult)
  4040   UpdateGlyphPositioning();
  4042   // Convert charnum/nchars from addressable characters relative to
  4043   // aContent to global character indices.
  4044   CharIterator chit(this, CharIterator::eAddressable, aContent);
  4045   if (!chit.AdvanceToSubtree() ||
  4046       !chit.Next(charnum) ||
  4047       chit.IsAfterSubtree()) {
  4048     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  4051   if (nchars == 0) {
  4052     *aResult = 0.0f;
  4053     return NS_OK;
  4056   charnum = chit.TextElementCharIndex();
  4057   chit.NextWithinSubtree(nchars);
  4058   nchars = chit.TextElementCharIndex() - charnum;
  4060   // Find each rendered run that intersects with the range defined
  4061   // by charnum/nchars.
  4062   nscoord textLength = 0;
  4063   TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames);
  4064   TextRenderedRun run = it.Current();
  4065   while (run.mFrame) {
  4066     // If this rendered run is past the substring we are interested in, we
  4067     // are done.
  4068     uint32_t offset = run.mTextElementCharIndex;
  4069     if (offset >= charnum + nchars) {
  4070       break;
  4073     // Intersect the substring we are interested in with the range covered by
  4074     // the rendered run.
  4075     uint32_t length = run.mTextFrameContentLength;
  4076     IntersectInterval(offset, length, charnum, nchars);
  4078     if (length != 0) {
  4079       // Convert offset into an index into the frame.
  4080       offset += run.mTextFrameContentOffset - run.mTextElementCharIndex;
  4082       gfxSkipCharsIterator it =
  4083         run.mFrame->EnsureTextRun(nsTextFrame::eInflated);
  4084       gfxTextRun* textRun = run.mFrame->GetTextRun(nsTextFrame::eInflated);
  4085       ConvertOriginalToSkipped(it, offset, length);
  4087       // Accumulate the advance.
  4088       textLength += textRun->GetAdvanceWidth(offset, length, nullptr);
  4091     run = it.Next();
  4094   nsPresContext* presContext = PresContext();
  4095   float cssPxPerDevPx = presContext->
  4096     AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  4098   *aResult = presContext->AppUnitsToGfxUnits(textLength) *
  4099                cssPxPerDevPx / mFontSizeScaleFactor;
  4100   return NS_OK;
  4103 /**
  4104  * Implements the SVG DOM GetCharNumAtPosition method for the specified
  4105  * text content element.
  4106  */
  4107 int32_t
  4108 SVGTextFrame::GetCharNumAtPosition(nsIContent* aContent,
  4109                                    mozilla::nsISVGPoint* aPoint)
  4111   UpdateGlyphPositioning();
  4113   nsPresContext* context = PresContext();
  4115   gfxPoint p(aPoint->X(), aPoint->Y());
  4117   int32_t result = -1;
  4119   TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames, aContent);
  4120   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  4121     // Hit test this rendered run.  Later runs will override earlier ones.
  4122     int32_t index = run.GetCharNumAtPosition(context, p);
  4123     if (index != -1) {
  4124       result = index + run.mTextElementCharIndex;
  4128   if (result == -1) {
  4129     return result;
  4132   return ConvertTextElementCharIndexToAddressableIndex(result, aContent);
  4135 /**
  4136  * Implements the SVG DOM GetStartPositionOfChar method for the specified
  4137  * text content element.
  4138  */
  4139 nsresult
  4140 SVGTextFrame::GetStartPositionOfChar(nsIContent* aContent,
  4141                                      uint32_t aCharNum,
  4142                                      mozilla::nsISVGPoint** aResult)
  4144   UpdateGlyphPositioning();
  4146   CharIterator it(this, CharIterator::eAddressable, aContent);
  4147   if (!it.AdvanceToSubtree() ||
  4148       !it.Next(aCharNum)) {
  4149     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  4152   // We need to return the start position of the whole glyph.
  4153   uint32_t startIndex = it.GlyphStartTextElementCharIndex();
  4155   NS_ADDREF(*aResult =
  4156     new DOMSVGPoint(ToPoint(mPositions[startIndex].mPosition)));
  4157   return NS_OK;
  4160 /**
  4161  * Implements the SVG DOM GetEndPositionOfChar method for the specified
  4162  * text content element.
  4163  */
  4164 nsresult
  4165 SVGTextFrame::GetEndPositionOfChar(nsIContent* aContent,
  4166                                    uint32_t aCharNum,
  4167                                    mozilla::nsISVGPoint** aResult)
  4169   UpdateGlyphPositioning();
  4171   CharIterator it(this, CharIterator::eAddressable, aContent);
  4172   if (!it.AdvanceToSubtree() ||
  4173       !it.Next(aCharNum)) {
  4174     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  4177   // We need to return the end position of the whole glyph.
  4178   uint32_t startIndex = it.GlyphStartTextElementCharIndex();
  4180   // Get the advance of the glyph.
  4181   gfxFloat advance = it.GetGlyphAdvance(PresContext());
  4182   if (it.TextRun()->IsRightToLeft()) {
  4183     advance = -advance;
  4186   // The end position is the start position plus the advance in the direction
  4187   // of the glyph's rotation.
  4188   Matrix m =
  4189     Matrix::Rotation(mPositions[startIndex].mAngle) *
  4190     Matrix::Translation(ToPoint(mPositions[startIndex].mPosition));
  4191   Point p = m * Point(advance / mFontSizeScaleFactor, 0);
  4193   NS_ADDREF(*aResult = new DOMSVGPoint(p));
  4194   return NS_OK;
  4197 /**
  4198  * Implements the SVG DOM GetExtentOfChar method for the specified
  4199  * text content element.
  4200  */
  4201 nsresult
  4202 SVGTextFrame::GetExtentOfChar(nsIContent* aContent,
  4203                               uint32_t aCharNum,
  4204                               dom::SVGIRect** aResult)
  4206   UpdateGlyphPositioning();
  4208   CharIterator it(this, CharIterator::eAddressable, aContent);
  4209   if (!it.AdvanceToSubtree() ||
  4210       !it.Next(aCharNum)) {
  4211     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  4214   nsPresContext* presContext = PresContext();
  4216   float cssPxPerDevPx = presContext->
  4217     AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  4219   // We need to return the extent of the whole glyph.
  4220   uint32_t startIndex = it.GlyphStartTextElementCharIndex();
  4222   // The ascent and descent gives the height of the glyph.
  4223   gfxFloat ascent, descent;
  4224   GetAscentAndDescentInAppUnits(it.TextFrame(), ascent, descent);
  4226   // Get the advance of the glyph.
  4227   gfxFloat advance = it.GetGlyphAdvance(presContext);
  4228   gfxFloat x = it.TextRun()->IsRightToLeft() ? -advance : 0.0;
  4230   // The horizontal extent is the origin of the glyph plus the advance
  4231   // in the direction of the glyph's rotation.
  4232   gfxMatrix m;
  4233   m.Translate(mPositions[startIndex].mPosition);
  4234   m.Rotate(mPositions[startIndex].mAngle);
  4235   m.Scale(1 / mFontSizeScaleFactor, 1 / mFontSizeScaleFactor);
  4237   gfxRect glyphRect
  4238     (x, -presContext->AppUnitsToGfxUnits(ascent) * cssPxPerDevPx,
  4239      advance, presContext->AppUnitsToGfxUnits(ascent + descent) * cssPxPerDevPx);
  4241   // Transform the glyph's rect into user space.
  4242   gfxRect r = m.TransformBounds(glyphRect);
  4244   NS_ADDREF(*aResult = new dom::SVGRect(aContent, r.x, r.y, r.width, r.height));
  4245   return NS_OK;
  4248 /**
  4249  * Implements the SVG DOM GetRotationOfChar method for the specified
  4250  * text content element.
  4251  */
  4252 nsresult
  4253 SVGTextFrame::GetRotationOfChar(nsIContent* aContent,
  4254                                 uint32_t aCharNum,
  4255                                 float* aResult)
  4257   UpdateGlyphPositioning();
  4259   CharIterator it(this, CharIterator::eAddressable, aContent);
  4260   if (!it.AdvanceToSubtree() ||
  4261       !it.Next(aCharNum)) {
  4262     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  4265   *aResult = mPositions[it.TextElementCharIndex()].mAngle * 180.0 / M_PI;
  4266   return NS_OK;
  4269 //----------------------------------------------------------------------
  4270 // SVGTextFrame text layout methods
  4272 /**
  4273  * Given the character position array before values have been filled in
  4274  * to any unspecified positions, and an array of dx/dy values, returns whether
  4275  * a character at a given index should start a new rendered run.
  4277  * @param aPositions The array of character positions before unspecified
  4278  *   positions have been filled in and dx/dy values have been added to them.
  4279  * @param aDeltas The array of dx/dy values.
  4280  * @param aIndex The character index in question.
  4281  */
  4282 static bool
  4283 ShouldStartRunAtIndex(const nsTArray<CharPosition>& aPositions,
  4284                       const nsTArray<gfxPoint>& aDeltas,
  4285                       uint32_t aIndex)
  4287   if (aIndex == 0) {
  4288     return true;
  4291   if (aIndex < aPositions.Length()) {
  4292     // If an explicit x or y value was given, start a new run.
  4293     if (aPositions[aIndex].IsXSpecified() ||
  4294         aPositions[aIndex].IsYSpecified()) {
  4295       return true;
  4298     // If a non-zero rotation was given, or the previous character had a non-
  4299     // zero rotation, start a new run.
  4300     if ((aPositions[aIndex].IsAngleSpecified() &&
  4301          aPositions[aIndex].mAngle != 0.0f) ||
  4302         (aPositions[aIndex - 1].IsAngleSpecified() &&
  4303          (aPositions[aIndex - 1].mAngle != 0.0f))) {
  4304       return true;
  4308   if (aIndex < aDeltas.Length()) {
  4309     // If a non-zero dx or dy value was given, start a new run.
  4310     if (aDeltas[aIndex].x != 0.0 ||
  4311         aDeltas[aIndex].y != 0.0) {
  4312       return true;
  4316   return false;
  4319 uint32_t
  4320 SVGTextFrame::ResolvePositions(nsIContent* aContent,
  4321                                uint32_t aIndex,
  4322                                bool aInTextPath,
  4323                                bool& aForceStartOfChunk,
  4324                                nsTArray<gfxPoint>& aDeltas)
  4326   if (aContent->IsNodeOfType(nsINode::eTEXT)) {
  4327     // We found a text node.
  4328     uint32_t length = static_cast<nsTextNode*>(aContent)->TextLength();
  4329     if (length) {
  4330       if (aForceStartOfChunk) {
  4331         // Note this character as starting a new anchored chunk.
  4332         mPositions[aIndex].mStartOfChunk = true;
  4333         aForceStartOfChunk = false;
  4335       uint32_t end = aIndex + length;
  4336       while (aIndex < end) {
  4337         // Record whether each of these characters should start a new rendered
  4338         // run.  That is always the case for characters on a text path.
  4339         //
  4340         // Run boundaries due to rotate="" values are handled in
  4341         // DoGlyphPositioning.
  4342         if (aInTextPath || ShouldStartRunAtIndex(mPositions, aDeltas, aIndex)) {
  4343           mPositions[aIndex].mRunBoundary = true;
  4345         aIndex++;
  4348     return aIndex;
  4351   // Skip past elements that aren't text content elements.
  4352   if (!IsTextContentElement(aContent)) {
  4353     return aIndex;
  4356   if (aContent->Tag() == nsGkAtoms::textPath) {
  4357     // <textPath> elements are as if they are specified with x="0" y="0", but
  4358     // only if they actually have some text content.
  4359     if (HasTextContent(aContent)) {
  4360       mPositions[aIndex].mPosition = gfxPoint();
  4361       mPositions[aIndex].mStartOfChunk = true;
  4363   } else if (aContent->Tag() != nsGkAtoms::a) {
  4364     // We have a text content element that can have x/y/dx/dy/rotate attributes.
  4365     nsSVGElement* element = static_cast<nsSVGElement*>(aContent);
  4367     // Get x, y, dx, dy.
  4368     SVGUserUnitList x, y, dx, dy;
  4369     element->GetAnimatedLengthListValues(&x, &y, &dx, &dy);
  4371     // Get rotate.
  4372     const SVGNumberList* rotate = nullptr;
  4373     SVGAnimatedNumberList* animatedRotate =
  4374       element->GetAnimatedNumberList(nsGkAtoms::rotate);
  4375     if (animatedRotate) {
  4376       rotate = &animatedRotate->GetAnimValue();
  4379     uint32_t count = GetTextContentLength(aContent);
  4380     bool percentages = false;
  4382     // New text anchoring chunks start at each character assigned a position
  4383     // with x="" or y="", or if we forced one with aForceStartOfChunk due to
  4384     // being just after a <textPath>.
  4385     uint32_t newChunkCount = std::max(x.Length(), y.Length());
  4386     if (!newChunkCount && aForceStartOfChunk) {
  4387       newChunkCount = 1;
  4389     for (uint32_t i = 0, j = 0; i < newChunkCount && j < count; j++) {
  4390       if (!mPositions[aIndex + j].mUnaddressable) {
  4391         mPositions[aIndex + j].mStartOfChunk = true;
  4392         i++;
  4396     // Copy dx="" and dy="" values into aDeltas.
  4397     if (!dx.IsEmpty() || !dy.IsEmpty()) {
  4398       // Any unspecified deltas when we grow the array just get left as 0s.
  4399       aDeltas.EnsureLengthAtLeast(aIndex + count);
  4400       for (uint32_t i = 0, j = 0; i < dx.Length() && j < count; j++) {
  4401         if (!mPositions[aIndex + j].mUnaddressable) {
  4402           aDeltas[aIndex + j].x = dx[i];
  4403           percentages = percentages || dx.HasPercentageValueAt(i);
  4404           i++;
  4407       for (uint32_t i = 0, j = 0; i < dy.Length() && j < count; j++) {
  4408         if (!mPositions[aIndex + j].mUnaddressable) {
  4409           aDeltas[aIndex + j].y = dy[i];
  4410           percentages = percentages || dy.HasPercentageValueAt(i);
  4411           i++;
  4416     // Copy x="" and y="" values.
  4417     for (uint32_t i = 0, j = 0; i < x.Length() && j < count; j++) {
  4418       if (!mPositions[aIndex + j].mUnaddressable) {
  4419         mPositions[aIndex + j].mPosition.x = x[i];
  4420         percentages = percentages || x.HasPercentageValueAt(i);
  4421         i++;
  4424     for (uint32_t i = 0, j = 0; i < y.Length() && j < count; j++) {
  4425       if (!mPositions[aIndex + j].mUnaddressable) {
  4426         mPositions[aIndex + j].mPosition.y = y[i];
  4427         percentages = percentages || y.HasPercentageValueAt(i);
  4428         i++;
  4432     // Copy rotate="" values.
  4433     if (rotate && !rotate->IsEmpty()) {
  4434       uint32_t i = 0, j = 0;
  4435       while (i < rotate->Length() && j < count) {
  4436         if (!mPositions[aIndex + j].mUnaddressable) {
  4437           mPositions[aIndex + j].mAngle = M_PI * (*rotate)[i] / 180.0;
  4438           i++;
  4440         j++;
  4442       // Propagate final rotate="" value to the end of this element.
  4443       while (j < count) {
  4444         mPositions[aIndex + j].mAngle = mPositions[aIndex + j - 1].mAngle;
  4445         j++;
  4449     if (percentages) {
  4450       AddStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES);
  4454   // Recurse to children.
  4455   bool inTextPath = aInTextPath || aContent->Tag() == nsGkAtoms::textPath;
  4456   for (nsIContent* child = aContent->GetFirstChild();
  4457        child;
  4458        child = child->GetNextSibling()) {
  4459     aIndex = ResolvePositions(child, aIndex, inTextPath, aForceStartOfChunk,
  4460                               aDeltas);
  4463   if (aContent->Tag() == nsGkAtoms::textPath) {
  4464     // Force a new anchored chunk just after a <textPath>.
  4465     aForceStartOfChunk = true;
  4468   return aIndex;
  4471 bool
  4472 SVGTextFrame::ResolvePositions(nsTArray<gfxPoint>& aDeltas,
  4473                                bool aRunPerGlyph)
  4475   NS_ASSERTION(mPositions.IsEmpty(), "expected mPositions to be empty");
  4476   RemoveStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES);
  4478   CharIterator it(this, CharIterator::eOriginal);
  4479   if (it.AtEnd()) {
  4480     return false;
  4483   // We assume the first character position is (0,0) unless we later see
  4484   // otherwise, and note it as unaddressable if it is.
  4485   bool firstCharUnaddressable = it.IsOriginalCharUnaddressable();
  4486   mPositions.AppendElement(CharPosition::Unspecified(firstCharUnaddressable));
  4488   // Fill in unspecified positions for all remaining characters, noting
  4489   // them as unaddressable if they are.
  4490   uint32_t index = 0;
  4491   while (it.Next()) {
  4492     while (++index < it.TextElementCharIndex()) {
  4493       mPositions.AppendElement(CharPosition::Unspecified(false));
  4495     mPositions.AppendElement(CharPosition::Unspecified(
  4496                                              it.IsOriginalCharUnaddressable()));
  4498   while (++index < it.TextElementCharIndex()) {
  4499     mPositions.AppendElement(CharPosition::Unspecified(false));
  4502   // Recurse over the content and fill in character positions as we go.
  4503   bool forceStartOfChunk = false;
  4504   return ResolvePositions(mContent, 0, aRunPerGlyph,
  4505                           forceStartOfChunk, aDeltas) != 0;
  4508 void
  4509 SVGTextFrame::DetermineCharPositions(nsTArray<nsPoint>& aPositions)
  4511   NS_ASSERTION(aPositions.IsEmpty(), "expected aPositions to be empty");
  4513   nsPoint position, lastPosition;
  4515   TextFrameIterator frit(this);
  4516   for (nsTextFrame* frame = frit.Current(); frame; frame = frit.Next()) {
  4517     gfxSkipCharsIterator it = frame->EnsureTextRun(nsTextFrame::eInflated);
  4518     gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated);
  4520     // Reset the position to the new frame's position.
  4521     position = frit.Position();
  4522     if (textRun->IsRightToLeft()) {
  4523       position.x += frame->GetRect().width;
  4525     position.y += GetBaselinePosition(frame, textRun, frit.DominantBaseline());
  4527     // Any characters not in a frame, e.g. when display:none.
  4528     for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
  4529       aPositions.AppendElement(position);
  4532     // Any white space characters trimmed at the start of the line of text.
  4533     nsTextFrame::TrimmedOffsets trimmedOffsets =
  4534       frame->GetTrimmedOffsets(frame->GetContent()->GetText(), true);
  4535     while (it.GetOriginalOffset() < trimmedOffsets.mStart) {
  4536       aPositions.AppendElement(position);
  4537       it.AdvanceOriginal(1);
  4540     // If a ligature was started in the previous frame, we should record
  4541     // the ligature's start position, not any partial position.
  4542     while (it.GetOriginalOffset() < frame->GetContentEnd() &&
  4543            !it.IsOriginalCharSkipped() &&
  4544            (!textRun->IsLigatureGroupStart(it.GetSkippedOffset()) ||
  4545             !textRun->IsClusterStart(it.GetSkippedOffset()))) {
  4546       nscoord advance = textRun->GetAdvanceWidth(it.GetSkippedOffset(), 1,
  4547                                                  nullptr);
  4548       position.x += textRun->IsRightToLeft() ? -advance : advance;
  4549       aPositions.AppendElement(lastPosition);
  4550       it.AdvanceOriginal(1);
  4553     // The meat of the text frame.
  4554     while (it.GetOriginalOffset() < frame->GetContentEnd()) {
  4555       aPositions.AppendElement(position);
  4556       if (!it.IsOriginalCharSkipped() &&
  4557           textRun->IsLigatureGroupStart(it.GetSkippedOffset()) &&
  4558           textRun->IsClusterStart(it.GetSkippedOffset())) {
  4559         // A real visible character.
  4560         uint32_t length = ClusterLength(textRun, it);
  4561         nscoord advance = textRun->GetAdvanceWidth(it.GetSkippedOffset(),
  4562                                                    length, nullptr);
  4563         position.x += textRun->IsRightToLeft() ? -advance : advance;
  4564         lastPosition = position;
  4566       it.AdvanceOriginal(1);
  4570   // Finally any characters at the end that are not in a frame.
  4571   for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
  4572     aPositions.AppendElement(position);
  4576 /**
  4577  * Physical text-anchor values.
  4578  */
  4579 enum TextAnchorSide {
  4580   eAnchorLeft,
  4581   eAnchorMiddle,
  4582   eAnchorRight
  4583 };
  4585 /**
  4586  * Converts a logical text-anchor value to its physical value, based on whether
  4587  * it is for an RTL frame.
  4588  */
  4589 static TextAnchorSide
  4590 ConvertLogicalTextAnchorToPhysical(uint8_t aTextAnchor, bool aIsRightToLeft)
  4592   NS_ASSERTION(aTextAnchor <= 3, "unexpected value for aTextAnchor");
  4593   if (!aIsRightToLeft)
  4594     return TextAnchorSide(aTextAnchor);
  4595   return TextAnchorSide(2 - aTextAnchor);
  4598 /**
  4599  * Shifts the recorded character positions for an anchored chunk.
  4601  * @param aCharPositions The recorded character positions.
  4602  * @param aChunkStart The character index the starts the anchored chunk.  This
  4603  *   character's initial position is the anchor point.
  4604  * @param aChunkEnd The character index just after the end of the anchored
  4605  *   chunk.
  4606  * @param aLeftEdge The left-most edge of any of the glyphs within the
  4607  *   anchored chunk.
  4608  * @param aRightEdge The right-most edge of any of the glyphs within the
  4609  *   anchored chunk.
  4610  * @param aAnchorSide The direction to anchor.
  4611  */
  4612 static void
  4613 ShiftAnchoredChunk(nsTArray<mozilla::CharPosition>& aCharPositions,
  4614                    uint32_t aChunkStart,
  4615                    uint32_t aChunkEnd,
  4616                    gfxFloat aLeftEdge,
  4617                    gfxFloat aRightEdge,
  4618                    TextAnchorSide aAnchorSide)
  4620   NS_ASSERTION(aLeftEdge <= aRightEdge, "unexpected anchored chunk edges");
  4621   NS_ASSERTION(aChunkStart < aChunkEnd, "unexpected values for aChunkStart and "
  4622                                         "aChunkEnd");
  4624   gfxFloat shift = aCharPositions[aChunkStart].mPosition.x;
  4625   switch (aAnchorSide) {
  4626     case eAnchorLeft:
  4627       shift -= aLeftEdge;
  4628       break;
  4629     case eAnchorMiddle:
  4630       shift -= (aLeftEdge + aRightEdge) / 2;
  4631       break;
  4632     case eAnchorRight:
  4633       shift -= aRightEdge;
  4634       break;
  4635     default:
  4636       NS_NOTREACHED("unexpected value for aAnchorSide");
  4639   if (shift != 0.0) {
  4640     for (uint32_t i = aChunkStart; i < aChunkEnd; i++) {
  4641       aCharPositions[i].mPosition.x += shift;
  4646 void
  4647 SVGTextFrame::AdjustChunksForLineBreaks()
  4649   nsBlockFrame* block = nsLayoutUtils::GetAsBlock(GetFirstPrincipalChild());
  4650   NS_ASSERTION(block, "expected block frame");
  4652   nsBlockFrame::line_iterator line = block->begin_lines();
  4654   CharIterator it(this, CharIterator::eOriginal);
  4655   while (!it.AtEnd() && line != block->end_lines()) {
  4656     if (it.TextFrame() == line->mFirstChild) {
  4657       mPositions[it.TextElementCharIndex()].mStartOfChunk = true;
  4658       line++;
  4660     it.AdvancePastCurrentFrame();
  4664 void
  4665 SVGTextFrame::AdjustPositionsForClusters()
  4667   nsPresContext* presContext = PresContext();
  4669   CharIterator it(this, CharIterator::eClusterOrLigatureGroupMiddle);
  4670   while (!it.AtEnd()) {
  4671     // Find the start of the cluster/ligature group.
  4672     uint32_t charIndex = it.TextElementCharIndex();
  4673     uint32_t startIndex = it.GlyphStartTextElementCharIndex();
  4675     mPositions[charIndex].mClusterOrLigatureGroupMiddle = true;
  4677     // Don't allow different rotations on ligature parts.
  4678     bool rotationAdjusted = false;
  4679     double angle = mPositions[startIndex].mAngle;
  4680     if (mPositions[charIndex].mAngle != angle) {
  4681       mPositions[charIndex].mAngle = angle;
  4682       rotationAdjusted = true;
  4685     // Find out the partial glyph advance for this character and update
  4686     // the character position.
  4687     uint32_t partLength =
  4688       charIndex - startIndex - it.GlyphUndisplayedCharacters();
  4689     gfxFloat advance =
  4690       it.GetGlyphPartialAdvance(partLength, presContext) / mFontSizeScaleFactor;
  4691     gfxPoint direction = gfxPoint(cos(angle), sin(angle)) *
  4692                          (it.TextRun()->IsRightToLeft() ? -1.0 : 1.0);
  4693     mPositions[charIndex].mPosition = mPositions[startIndex].mPosition +
  4694                                       direction * advance;
  4696     // Ensure any runs that would end in the middle of a ligature now end just
  4697     // after the ligature.
  4698     if (mPositions[charIndex].mRunBoundary) {
  4699       mPositions[charIndex].mRunBoundary = false;
  4700       if (charIndex + 1 < mPositions.Length()) {
  4701         mPositions[charIndex + 1].mRunBoundary = true;
  4703     } else if (rotationAdjusted) {
  4704       if (charIndex + 1 < mPositions.Length()) {
  4705         mPositions[charIndex + 1].mRunBoundary = true;
  4709     // Ensure any anchored chunks that would begin in the middle of a ligature
  4710     // now begin just after the ligature.
  4711     if (mPositions[charIndex].mStartOfChunk) {
  4712       mPositions[charIndex].mStartOfChunk = false;
  4713       if (charIndex + 1 < mPositions.Length()) {
  4714         mPositions[charIndex + 1].mStartOfChunk = true;
  4718     it.Next();
  4722 nsIFrame*
  4723 SVGTextFrame::GetTextPathPathFrame(nsIFrame* aTextPathFrame)
  4725   nsSVGTextPathProperty *property = static_cast<nsSVGTextPathProperty*>
  4726     (aTextPathFrame->Properties().Get(nsSVGEffects::HrefProperty()));
  4728   if (!property) {
  4729     nsIContent* content = aTextPathFrame->GetContent();
  4730     dom::SVGTextPathElement* tp = static_cast<dom::SVGTextPathElement*>(content);
  4731     nsAutoString href;
  4732     tp->mStringAttributes[dom::SVGTextPathElement::HREF].GetAnimValue(href, tp);
  4733     if (href.IsEmpty()) {
  4734       return nullptr; // no URL
  4737     nsCOMPtr<nsIURI> targetURI;
  4738     nsCOMPtr<nsIURI> base = content->GetBaseURI();
  4739     nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
  4740                                               content->GetCurrentDoc(), base);
  4742     property = nsSVGEffects::GetTextPathProperty(targetURI, aTextPathFrame,
  4743                                                  nsSVGEffects::HrefProperty());
  4744     if (!property)
  4745       return nullptr;
  4748   return property->GetReferencedFrame(nsGkAtoms::svgPathGeometryFrame, nullptr);
  4751 TemporaryRef<Path>
  4752 SVGTextFrame::GetTextPath(nsIFrame* aTextPathFrame)
  4754   nsIFrame *pathFrame = GetTextPathPathFrame(aTextPathFrame);
  4756   if (!pathFrame) {
  4757     return nullptr;
  4760   nsSVGPathGeometryElement *element =
  4761     static_cast<nsSVGPathGeometryElement*>(pathFrame->GetContent());
  4763   RefPtr<Path> path = element->GetPathForLengthOrPositionMeasuring();
  4764   if (!path) {
  4765     return nullptr;
  4768   gfxMatrix matrix = element->PrependLocalTransformsTo(gfxMatrix());
  4769   if (!matrix.IsIdentity()) {
  4770     RefPtr<PathBuilder> builder =
  4771       path->TransformedCopyToBuilder(ToMatrix(matrix));
  4772     path = builder->Finish();
  4775   return path.forget();
  4778 gfxFloat
  4779 SVGTextFrame::GetOffsetScale(nsIFrame* aTextPathFrame)
  4781   nsIFrame *pathFrame = GetTextPathPathFrame(aTextPathFrame);
  4782   if (!pathFrame)
  4783     return 1.0;
  4785   return static_cast<dom::SVGPathElement*>(pathFrame->GetContent())->
  4786     GetPathLengthScale(dom::SVGPathElement::eForTextPath);
  4789 gfxFloat
  4790 SVGTextFrame::GetStartOffset(nsIFrame* aTextPathFrame)
  4792   dom::SVGTextPathElement *tp =
  4793     static_cast<dom::SVGTextPathElement*>(aTextPathFrame->GetContent());
  4794   nsSVGLength2 *length =
  4795     &tp->mLengthAttributes[dom::SVGTextPathElement::STARTOFFSET];
  4797   if (length->IsPercentage()) {
  4798     RefPtr<Path> data = GetTextPath(aTextPathFrame);
  4799     return data ?
  4800       length->GetAnimValInSpecifiedUnits() * data->ComputeLength() / 100.0 :
  4801       0.0;
  4803   return length->GetAnimValue(tp) * GetOffsetScale(aTextPathFrame);
  4806 void
  4807 SVGTextFrame::DoTextPathLayout()
  4809   nsPresContext* context = PresContext();
  4811   CharIterator it(this, CharIterator::eClusterAndLigatureGroupStart);
  4812   while (!it.AtEnd()) {
  4813     nsIFrame* textPathFrame = it.TextPathFrame();
  4814     if (!textPathFrame) {
  4815       // Skip past this frame if we're not in a text path.
  4816       it.AdvancePastCurrentFrame();
  4817       continue;
  4820     // Get the path itself.
  4821     RefPtr<Path> path = GetTextPath(textPathFrame);
  4822     if (!path) {
  4823       it.AdvancePastCurrentTextPathFrame();
  4824       continue;
  4827     nsIContent* textPath = textPathFrame->GetContent();
  4829     gfxFloat offset = GetStartOffset(textPathFrame);
  4830     Float pathLength = path->ComputeLength();
  4832     // Loop for each text frame in the text path.
  4833     do {
  4834       uint32_t i = it.TextElementCharIndex();
  4835       gfxFloat halfAdvance =
  4836         it.GetGlyphAdvance(context) / mFontSizeScaleFactor / 2.0;
  4837       gfxFloat sign = it.TextRun()->IsRightToLeft() ? -1.0 : 1.0;
  4838       gfxFloat midx = mPositions[i].mPosition.x + sign * halfAdvance + offset;
  4840       // Hide the character if it falls off the end of the path.
  4841       mPositions[i].mHidden = midx < 0 || midx > pathLength;
  4843       // Position the character on the path at the right angle.
  4844       Point tangent; // Unit vector tangent to the point we find.
  4845       Point pt = path->ComputePointAtLength(Float(midx), &tangent);
  4846       Float rotation = atan2f(tangent.y, tangent.x);
  4847       Point normal(-tangent.y, tangent.x); // Unit vector normal to the point.
  4848       Point offsetFromPath = normal * mPositions[i].mPosition.y;
  4849       pt += offsetFromPath;
  4850       Point direction = tangent * sign;
  4851       mPositions[i].mPosition = ThebesPoint(pt) - ThebesPoint(direction) * halfAdvance;
  4852       mPositions[i].mAngle += rotation;
  4854       // Position any characters for a partial ligature.
  4855       for (uint32_t j = i + 1;
  4856            j < mPositions.Length() && mPositions[j].mClusterOrLigatureGroupMiddle;
  4857            j++) {
  4858         gfxPoint partialAdvance =
  4859           ThebesPoint(direction) * it.GetGlyphPartialAdvance(j - i, context) /
  4860                                                          mFontSizeScaleFactor;
  4861         mPositions[j].mPosition = mPositions[i].mPosition + partialAdvance;
  4862         mPositions[j].mAngle = mPositions[i].mAngle;
  4863         mPositions[j].mHidden = mPositions[i].mHidden;
  4865       it.Next();
  4866     } while (it.TextPathFrame() &&
  4867              it.TextPathFrame()->GetContent() == textPath);
  4871 void
  4872 SVGTextFrame::DoAnchoring()
  4874   nsPresContext* presContext = PresContext();
  4876   CharIterator it(this, CharIterator::eOriginal);
  4878   // Don't need to worry about skipped or trimmed characters.
  4879   while (!it.AtEnd() &&
  4880          (it.IsOriginalCharSkipped() || it.IsOriginalCharTrimmed())) {
  4881     it.Next();
  4884   uint32_t start = it.TextElementCharIndex();
  4885   while (start < mPositions.Length()) {
  4886     it.AdvanceToCharacter(start);
  4887     nsTextFrame* chunkFrame = it.TextFrame();
  4889     // Measure characters in this chunk to find the left-most and right-most
  4890     // edges of all glyphs within the chunk.
  4891     uint32_t index = it.TextElementCharIndex();
  4892     uint32_t end = start;
  4893     gfxFloat left = std::numeric_limits<gfxFloat>::infinity();
  4894     gfxFloat right = -std::numeric_limits<gfxFloat>::infinity();
  4895     do {
  4896       if (!it.IsOriginalCharSkipped() && !it.IsOriginalCharTrimmed()) {
  4897         gfxFloat advance = it.GetAdvance(presContext) / mFontSizeScaleFactor;
  4898         if (it.TextRun()->IsRightToLeft()) {
  4899           left  = std::min(left,  mPositions[index].mPosition.x - advance);
  4900           right = std::max(right, mPositions[index].mPosition.x);
  4901         } else {
  4902           left  = std::min(left,  mPositions[index].mPosition.x);
  4903           right = std::max(right, mPositions[index].mPosition.x + advance);
  4906       it.Next();
  4907       index = end = it.TextElementCharIndex();
  4908     } while (!it.AtEnd() && !mPositions[end].mStartOfChunk);
  4910     if (left != std::numeric_limits<gfxFloat>::infinity()) {
  4911       bool isRTL =
  4912         chunkFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  4913       TextAnchorSide anchor =
  4914         ConvertLogicalTextAnchorToPhysical(chunkFrame->StyleSVG()->mTextAnchor,
  4915                                            isRTL);
  4917       ShiftAnchoredChunk(mPositions, start, end, left, right, anchor);
  4920     start = it.TextElementCharIndex();
  4924 void
  4925 SVGTextFrame::DoGlyphPositioning()
  4927   mPositions.Clear();
  4928   RemoveStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
  4930   nsIFrame* kid = GetFirstPrincipalChild();
  4931   if (kid && NS_SUBTREE_DIRTY(kid)) {
  4932     MOZ_ASSERT(false, "should have already reflowed the kid");
  4933     return;
  4936   // Determine the positions of each character in app units.
  4937   nsTArray<nsPoint> charPositions;
  4938   DetermineCharPositions(charPositions);
  4940   if (charPositions.IsEmpty()) {
  4941     // No characters, so nothing to do.
  4942     return;
  4945   // If the textLength="" attribute was specified, then we need ResolvePositions
  4946   // to record that a new run starts with each glyph.
  4947   SVGTextContentElement* element = static_cast<SVGTextContentElement*>(mContent);
  4948   nsSVGLength2* textLengthAttr =
  4949     element->GetAnimatedLength(nsGkAtoms::textLength);
  4950   bool adjustingTextLength = textLengthAttr->IsExplicitlySet();
  4951   float expectedTextLength = textLengthAttr->GetAnimValue(element);
  4953   if (adjustingTextLength && expectedTextLength < 0.0f) {
  4954     // If textLength="" is less than zero, ignore it.
  4955     adjustingTextLength = false;
  4958   // Get the x, y, dx, dy, rotate values for the subtree.
  4959   nsTArray<gfxPoint> deltas;
  4960   if (!ResolvePositions(deltas, adjustingTextLength)) {
  4961     // If ResolvePositions returned false, it means that there were some
  4962     // characters in the DOM but none of them are displayed.  Clear out
  4963     // mPositions so that we don't attempt to do any painting later.
  4964     mPositions.Clear();
  4965     return;
  4968   // XXX We might be able to do less work when there is at most a single
  4969   // x/y/dx/dy position.
  4971   // Truncate the positioning arrays to the actual number of characters present.
  4972   TruncateTo(deltas, charPositions);
  4973   TruncateTo(mPositions, charPositions);
  4975   // Fill in an unspecified character position at index 0.
  4976   if (!mPositions[0].IsXSpecified()) {
  4977     mPositions[0].mPosition.x = 0.0;
  4979   if (!mPositions[0].IsYSpecified()) {
  4980     mPositions[0].mPosition.y = 0.0;
  4982   if (!mPositions[0].IsAngleSpecified()) {
  4983     mPositions[0].mAngle = 0.0;
  4986   nsPresContext* presContext = PresContext();
  4988   float cssPxPerDevPx = presContext->
  4989     AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  4990   double factor = cssPxPerDevPx / mFontSizeScaleFactor;
  4992   // Determine how much to compress or expand glyph positions due to
  4993   // textLength="" and lengthAdjust="".
  4994   double adjustment = 0.0;
  4995   mLengthAdjustScaleFactor = 1.0f;
  4996   if (adjustingTextLength) {
  4997     nscoord frameWidth = GetFirstPrincipalChild()->GetRect().width;
  4998     float actualTextLength =
  4999       static_cast<float>(presContext->AppUnitsToGfxUnits(frameWidth) * factor);
  5001     nsRefPtr<SVGAnimatedEnumeration> lengthAdjustEnum = element->LengthAdjust();
  5002     uint16_t lengthAdjust = lengthAdjustEnum->AnimVal();
  5003     switch (lengthAdjust) {
  5004       case SVG_LENGTHADJUST_SPACINGANDGLYPHS:
  5005         // Scale the glyphs and their positions.
  5006         if (actualTextLength > 0) {
  5007           mLengthAdjustScaleFactor = expectedTextLength / actualTextLength;
  5009         break;
  5011       default:
  5012         MOZ_ASSERT(lengthAdjust == SVG_LENGTHADJUST_SPACING);
  5013         // Just add space between each glyph.
  5014         int32_t adjustableSpaces = 0;
  5015         for (uint32_t i = 1; i < mPositions.Length(); i++) {
  5016           if (!mPositions[i].mUnaddressable) {
  5017             adjustableSpaces++;
  5020         if (adjustableSpaces) {
  5021           adjustment = (expectedTextLength - actualTextLength) / adjustableSpaces;
  5023         break;
  5027   // Fill in any unspecified character positions based on the positions recorded
  5028   // in charPositions, and also add in the dx/dy values.
  5029   if (!deltas.IsEmpty()) {
  5030     mPositions[0].mPosition += deltas[0];
  5033   for (uint32_t i = 1; i < mPositions.Length(); i++) {
  5034     // Fill in unspecified x position.
  5035     if (!mPositions[i].IsXSpecified()) {
  5036       nscoord d = charPositions[i].x - charPositions[i - 1].x;
  5037       mPositions[i].mPosition.x =
  5038         mPositions[i - 1].mPosition.x +
  5039         presContext->AppUnitsToGfxUnits(d) * factor * mLengthAdjustScaleFactor;
  5040       if (!mPositions[i].mUnaddressable) {
  5041         mPositions[i].mPosition.x += adjustment;
  5044     // Fill in unspecified y position.
  5045     if (!mPositions[i].IsYSpecified()) {
  5046       nscoord d = charPositions[i].y - charPositions[i - 1].y;
  5047       mPositions[i].mPosition.y =
  5048         mPositions[i - 1].mPosition.y +
  5049         presContext->AppUnitsToGfxUnits(d) * factor;
  5051     // Add in dx/dy.
  5052     if (i < deltas.Length()) {
  5053       mPositions[i].mPosition += deltas[i];
  5055     // Fill in unspecified rotation values.
  5056     if (!mPositions[i].IsAngleSpecified()) {
  5057       mPositions[i].mAngle = 0.0f;
  5061   MOZ_ASSERT(mPositions.Length() == charPositions.Length());
  5063   AdjustChunksForLineBreaks();
  5064   AdjustPositionsForClusters();
  5065   DoAnchoring();
  5066   DoTextPathLayout();
  5069 bool
  5070 SVGTextFrame::ShouldRenderAsPath(nsRenderingContext* aContext,
  5071                                  nsTextFrame* aFrame,
  5072                                  bool& aShouldPaintSVGGlyphs)
  5074   // Rendering to a clip path.
  5075   if (SVGAutoRenderState::GetRenderMode(aContext) != SVGAutoRenderState::NORMAL) {
  5076     aShouldPaintSVGGlyphs = false;
  5077     return true;
  5080   aShouldPaintSVGGlyphs = true;
  5082   const nsStyleSVG* style = aFrame->StyleSVG();
  5084   // Fill is a non-solid paint, has a non-default fill-rule or has
  5085   // non-1 opacity.
  5086   if (!(style->mFill.mType == eStyleSVGPaintType_None ||
  5087         (style->mFill.mType == eStyleSVGPaintType_Color &&
  5088          style->mFillOpacity == 1))) {
  5089     return true;
  5092   // Text has a stroke.
  5093   if (style->HasStroke() &&
  5094       SVGContentUtils::CoordToFloat(PresContext(),
  5095                                     static_cast<nsSVGElement*>(mContent),
  5096                                     style->mStrokeWidth) > 0) {
  5097     return true;
  5100   return false;
  5103 void
  5104 SVGTextFrame::ScheduleReflowSVG()
  5106   if (mState & NS_FRAME_IS_NONDISPLAY) {
  5107     ScheduleReflowSVGNonDisplayText();
  5108   } else {
  5109     nsSVGUtils::ScheduleReflowSVG(this);
  5113 void
  5114 SVGTextFrame::NotifyGlyphMetricsChange()
  5116   AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
  5117   nsSVGEffects::InvalidateRenderingObservers(this);
  5118   ScheduleReflowSVG();
  5121 void
  5122 SVGTextFrame::UpdateGlyphPositioning()
  5124   nsIFrame* kid = GetFirstPrincipalChild();
  5125   if (!kid) {
  5126     return;
  5129   if (mState & NS_STATE_SVG_POSITIONING_DIRTY) {
  5130     DoGlyphPositioning();
  5134 void
  5135 SVGTextFrame::MaybeReflowAnonymousBlockChild()
  5137   nsIFrame* kid = GetFirstPrincipalChild();
  5138   if (!kid)
  5139     return;
  5141   NS_ASSERTION(!(kid->GetStateBits() & NS_FRAME_IN_REFLOW),
  5142                "should not be in reflow when about to reflow again");
  5144   if (NS_SUBTREE_DIRTY(this)) {
  5145     if (mState & NS_FRAME_IS_DIRTY) {
  5146       // If we require a full reflow, ensure our kid is marked fully dirty.
  5147       // (Note that our anonymous nsBlockFrame is not an nsISVGChildFrame, so
  5148       // even when we are called via our ReflowSVG this will not be done for us
  5149       // by nsSVGDisplayContainerFrame::ReflowSVG.)
  5150       kid->AddStateBits(NS_FRAME_IS_DIRTY);
  5152     MOZ_ASSERT(nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(this),
  5153                "should be under ReflowSVG");
  5154     nsPresContext::InterruptPreventer noInterrupts(PresContext());
  5155     DoReflow();
  5159 void
  5160 SVGTextFrame::DoReflow()
  5162   // Since we are going to reflow the anonymous block frame, we will
  5163   // need to update mPositions.
  5164   AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
  5166   if (mState & NS_FRAME_IS_NONDISPLAY) {
  5167     // Normally, these dirty flags would be cleared in ReflowSVG(), but that
  5168     // doesn't get called for non-display frames. We don't want to reflow our
  5169     // descendants every time SVGTextFrame::PaintSVG makes sure that we have
  5170     // valid positions by calling UpdateGlyphPositioning(), so we need to clear
  5171     // these dirty bits. Note that this also breaks an invalidation loop where
  5172     // our descendants invalidate as they reflow, which invalidates rendering
  5173     // observers, which reschedules the frame that is currently painting by
  5174     // referencing us to paint again. See bug 839958 comment 7. Hopefully we
  5175     // will break that loop more convincingly at some point.
  5176     mState &= ~(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
  5179   nsPresContext *presContext = PresContext();
  5180   nsIFrame* kid = GetFirstPrincipalChild();
  5181   if (!kid)
  5182     return;
  5184   nsRefPtr<nsRenderingContext> renderingContext =
  5185     presContext->PresShell()->CreateReferenceRenderingContext();
  5187   if (UpdateFontSizeScaleFactor()) {
  5188     // If the font size scale factor changed, we need the block to report
  5189     // an updated preferred width.
  5190     kid->MarkIntrinsicWidthsDirty();
  5193   mState |= NS_STATE_SVG_TEXT_IN_REFLOW;
  5195   nscoord width = kid->GetPrefWidth(renderingContext);
  5196   nsHTMLReflowState reflowState(presContext, kid,
  5197                                 renderingContext,
  5198                                 nsSize(width, NS_UNCONSTRAINEDSIZE));
  5199   nsHTMLReflowMetrics desiredSize(reflowState);
  5200   nsReflowStatus status;
  5202   NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) &&
  5203                reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
  5204                "style system should ensure that :-moz-svg-text "
  5205                "does not get styled");
  5207   kid->WillReflow(presContext);
  5208   kid->Reflow(presContext, desiredSize, reflowState, status);
  5209   kid->DidReflow(presContext, &reflowState, nsDidReflowStatus::FINISHED);
  5210   kid->SetSize(nsSize(desiredSize.Width(), desiredSize.Height()));
  5212   mState &= ~NS_STATE_SVG_TEXT_IN_REFLOW;
  5214   TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
  5217 // Usable font size range in devpixels / user-units
  5218 #define CLAMP_MIN_SIZE 8.0
  5219 #define CLAMP_MAX_SIZE 200.0
  5220 #define PRECISE_SIZE   200.0
  5222 bool
  5223 SVGTextFrame::UpdateFontSizeScaleFactor()
  5225   double oldFontSizeScaleFactor = mFontSizeScaleFactor;
  5227   nsPresContext* presContext = PresContext();
  5229   bool geometricPrecision = false;
  5230   nscoord min = nscoord_MAX,
  5231           max = nscoord_MIN;
  5233   // Find the minimum and maximum font sizes used over all the
  5234   // nsTextFrames.
  5235   TextFrameIterator it(this);
  5236   nsTextFrame* f = it.Current();
  5237   while (f) {
  5238     if (!geometricPrecision) {
  5239       // Unfortunately we can't treat text-rendering:geometricPrecision
  5240       // separately for each text frame.
  5241       geometricPrecision = f->StyleSVG()->mTextRendering ==
  5242                              NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION;
  5244     nscoord size = f->StyleFont()->mFont.size;
  5245     if (size) {
  5246       min = std::min(min, size);
  5247       max = std::max(max, size);
  5249     f = it.Next();
  5252   if (min == nscoord_MAX) {
  5253     // No text, so no need for scaling.
  5254     mFontSizeScaleFactor = 1.0;
  5255     return mFontSizeScaleFactor != oldFontSizeScaleFactor;
  5258   double minSize = presContext->AppUnitsToFloatCSSPixels(min);
  5260   if (geometricPrecision) {
  5261     // We want to ensure minSize is scaled to PRECISE_SIZE.
  5262     mFontSizeScaleFactor = PRECISE_SIZE / minSize;
  5263     return mFontSizeScaleFactor != oldFontSizeScaleFactor;
  5266   // When we are non-display, we could be painted in different coordinate
  5267   // spaces, and we don't want to have to reflow for each of these.  We
  5268   // just assume that the context scale is 1.0 for them all, so we don't
  5269   // get stuck with a font size scale factor based on whichever referencing
  5270   // frame happens to reflow first.
  5271   double contextScale = 1.0;
  5272   if (!(mState & NS_FRAME_IS_NONDISPLAY)) {
  5273     gfxMatrix m(GetCanvasTM(FOR_OUTERSVG_TM));
  5274     if (!m.IsSingular()) {
  5275       contextScale = GetContextScale(m);
  5278   mLastContextScale = contextScale;
  5280   double maxSize = presContext->AppUnitsToFloatCSSPixels(max);
  5282   // But we want to ignore any scaling required due to HiDPI displays, since
  5283   // regular CSS text frames will still create text runs using the font size
  5284   // in CSS pixels, and we want SVG text to have the same rendering as HTML
  5285   // text for regular font sizes.
  5286   float cssPxPerDevPx =
  5287     presContext->AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  5288   contextScale *= cssPxPerDevPx;
  5290   double minTextRunSize = minSize * contextScale;
  5291   double maxTextRunSize = maxSize * contextScale;
  5293   if (minTextRunSize >= CLAMP_MIN_SIZE &&
  5294       maxTextRunSize <= CLAMP_MAX_SIZE) {
  5295     // We are already in the ideal font size range for all text frames,
  5296     // so we only have to take into account the contextScale.
  5297     mFontSizeScaleFactor = contextScale;
  5298   } else if (maxSize / minSize > CLAMP_MAX_SIZE / CLAMP_MIN_SIZE) {
  5299     // We can't scale the font sizes so that all of the text frames lie
  5300     // within our ideal font size range, so we treat the minimum as more
  5301     // important and just scale so that minSize = CLAMP_MIN_SIZE.
  5302     mFontSizeScaleFactor = CLAMP_MIN_SIZE / minTextRunSize;
  5303   } else if (minTextRunSize < CLAMP_MIN_SIZE) {
  5304     mFontSizeScaleFactor = CLAMP_MIN_SIZE / minTextRunSize;
  5305   } else {
  5306     mFontSizeScaleFactor = CLAMP_MAX_SIZE / maxTextRunSize;
  5309   return mFontSizeScaleFactor != oldFontSizeScaleFactor;
  5312 double
  5313 SVGTextFrame::GetFontSizeScaleFactor() const
  5315   return mFontSizeScaleFactor;
  5318 /**
  5319  * Take aPoint, which is in the <text> element's user space, and convert
  5320  * it to the appropriate frame user space of aChildFrame according to
  5321  * which rendered run the point hits.
  5322  */
  5323 gfxPoint
  5324 SVGTextFrame::TransformFramePointToTextChild(const gfxPoint& aPoint,
  5325                                              nsIFrame* aChildFrame)
  5327   NS_ASSERTION(aChildFrame &&
  5328                nsLayoutUtils::GetClosestFrameOfType
  5329                  (aChildFrame->GetParent(), nsGkAtoms::svgTextFrame) == this,
  5330                "aChildFrame must be a descendant of this frame");
  5332   UpdateGlyphPositioning();
  5334   nsPresContext* presContext = PresContext();
  5336   // Add in the mRect offset to aPoint, as that will have been taken into
  5337   // account when transforming the point from the ancestor frame down
  5338   // to this one.
  5339   float cssPxPerDevPx = presContext->
  5340     AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  5341   float factor = presContext->AppUnitsPerCSSPixel();
  5342   gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
  5343                          NSAppUnitsToFloatPixels(mRect.y, factor));
  5344   gfxPoint pointInUserSpace = aPoint * cssPxPerDevPx + framePosition;
  5346   // Find the closest rendered run for the text frames beneath aChildFrame.
  5347   TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
  5348                              aChildFrame);
  5349   TextRenderedRun hit;
  5350   gfxPoint pointInRun;
  5351   nscoord dx = nscoord_MAX;
  5352   nscoord dy = nscoord_MAX;
  5353   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  5354     uint32_t flags = TextRenderedRun::eIncludeFill |
  5355                      TextRenderedRun::eIncludeStroke |
  5356                      TextRenderedRun::eNoHorizontalOverflow;
  5357     gfxRect runRect = run.GetRunUserSpaceRect(presContext, flags).ToThebesRect();
  5359     gfxPoint pointInRunUserSpace =
  5360       run.GetTransformFromRunUserSpaceToUserSpace(presContext).Invert().
  5361           Transform(pointInUserSpace);
  5363     if (Inside(runRect, pointInRunUserSpace)) {
  5364       // The point was inside the rendered run's rect, so we choose it.
  5365       dx = 0;
  5366       dy = 0;
  5367       pointInRun = pointInRunUserSpace;
  5368       hit = run;
  5369     } else if (nsLayoutUtils::PointIsCloserToRect(pointInRunUserSpace,
  5370                                                   runRect, dx, dy)) {
  5371       // The point was closer to this rendered run's rect than any others
  5372       // we've seen so far.
  5373       pointInRun.x = clamped(pointInRunUserSpace.x,
  5374                              runRect.X(), runRect.XMost());
  5375       pointInRun.y = clamped(pointInRunUserSpace.y,
  5376                              runRect.Y(), runRect.YMost());
  5377       hit = run;
  5381   if (!hit.mFrame) {
  5382     // We didn't find any rendered runs for the frame.
  5383     return aPoint;
  5386   // Return the point in user units relative to the nsTextFrame,
  5387   // but taking into account mFontSizeScaleFactor.
  5388   gfxMatrix m = hit.GetTransformFromRunUserSpaceToFrameUserSpace(presContext);
  5389   m.Scale(mFontSizeScaleFactor, mFontSizeScaleFactor);
  5390   return m.Transform(pointInRun) / cssPxPerDevPx;
  5393 /**
  5394  * For each rendered run for frames beneath aChildFrame, convert aRect
  5395  * into the run's frame user space and intersect it with the run's
  5396  * frame user space rectangle.  For each of these intersections,
  5397  * then translate them up into aChildFrame's coordinate space
  5398  * and union them all together.
  5399  */
  5400 gfxRect
  5401 SVGTextFrame::TransformFrameRectToTextChild(const gfxRect& aRect,
  5402                                             nsIFrame* aChildFrame)
  5404   NS_ASSERTION(aChildFrame &&
  5405                nsLayoutUtils::GetClosestFrameOfType
  5406                  (aChildFrame->GetParent(), nsGkAtoms::svgTextFrame) == this,
  5407                "aChildFrame must be a descendant of this frame");
  5409   UpdateGlyphPositioning();
  5411   nsPresContext* presContext = PresContext();
  5413   // Add in the mRect offset to aRect, as that will have been taken into
  5414   // account when transforming the rect from the ancestor frame down
  5415   // to this one.
  5416   float cssPxPerDevPx = presContext->
  5417     AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  5418   float factor = presContext->AppUnitsPerCSSPixel();
  5419   gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
  5420                          NSAppUnitsToFloatPixels(mRect.y, factor));
  5421   gfxRect incomingRectInUserSpace(aRect.x * cssPxPerDevPx + framePosition.x,
  5422                                   aRect.y * cssPxPerDevPx + framePosition.y,
  5423                                   aRect.width * cssPxPerDevPx,
  5424                                   aRect.height * cssPxPerDevPx);
  5426   // Find each rendered run for text frames beneath aChildFrame.
  5427   gfxRect result;
  5428   TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
  5429                              aChildFrame);
  5430   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  5431     // Convert the incoming rect into frame user space.
  5432     gfxMatrix m;
  5433     m.PreMultiply(run.GetTransformFromRunUserSpaceToUserSpace(presContext).Invert());
  5434     m.PreMultiply(run.GetTransformFromRunUserSpaceToFrameUserSpace(presContext));
  5435     gfxRect incomingRectInFrameUserSpace =
  5436       m.TransformBounds(incomingRectInUserSpace);
  5438     // Intersect it with this run's rectangle.
  5439     uint32_t flags = TextRenderedRun::eIncludeFill |
  5440                      TextRenderedRun::eIncludeStroke;
  5441     SVGBBox runRectInFrameUserSpace = run.GetFrameUserSpaceRect(presContext, flags);
  5442     if (runRectInFrameUserSpace.IsEmpty()) {
  5443       continue;
  5445     gfxRect runIntersectionInFrameUserSpace =
  5446       incomingRectInFrameUserSpace.Intersect(runRectInFrameUserSpace.ToThebesRect());
  5448     if (!runIntersectionInFrameUserSpace.IsEmpty()) {
  5449       // Take the font size scale into account.
  5450       runIntersectionInFrameUserSpace.x *= mFontSizeScaleFactor;
  5451       runIntersectionInFrameUserSpace.y *= mFontSizeScaleFactor;
  5452       runIntersectionInFrameUserSpace.width *= mFontSizeScaleFactor;
  5453       runIntersectionInFrameUserSpace.height *= mFontSizeScaleFactor;
  5455       // Convert it into the coordinate space of aChildFrame.
  5456       nsPoint offset = run.mFrame->GetOffsetTo(aChildFrame);
  5457       gfxRect runIntersection =
  5458         runIntersectionInFrameUserSpace +
  5459           gfxPoint(NSAppUnitsToFloatPixels(offset.x, factor),
  5460                    NSAppUnitsToFloatPixels(offset.y, factor));
  5462       // Union it into the result.
  5463       result.UnionRect(result, runIntersection);
  5467   return result;
  5470 /**
  5471  * For each rendered run beneath aChildFrame, translate aRect from
  5472  * aChildFrame to the run's text frame, transform it then into
  5473  * the run's frame user space, intersect it with the run's
  5474  * frame user space rect, then transform it up to user space.
  5475  * The result is the union of all of these.
  5476  */
  5477 gfxRect
  5478 SVGTextFrame::TransformFrameRectFromTextChild(const nsRect& aRect,
  5479                                               nsIFrame* aChildFrame)
  5481   NS_ASSERTION(aChildFrame &&
  5482                nsLayoutUtils::GetClosestFrameOfType
  5483                  (aChildFrame->GetParent(), nsGkAtoms::svgTextFrame) == this,
  5484                "aChildFrame must be a descendant of this frame");
  5486   UpdateGlyphPositioning();
  5488   nsPresContext* presContext = PresContext();
  5490   gfxRect result;
  5491   TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
  5492                              aChildFrame);
  5493   for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  5494     // First, translate aRect from aChildFrame to this run's frame.
  5495     nsRect rectInTextFrame = aRect + aChildFrame->GetOffsetTo(run.mFrame);
  5497     // Scale it into frame user space.
  5498     gfxRect rectInFrameUserSpace =
  5499       AppUnitsToFloatCSSPixels(gfxRect(rectInTextFrame.x,
  5500                                        rectInTextFrame.y,
  5501                                        rectInTextFrame.width,
  5502                                        rectInTextFrame.height), presContext);
  5504     // Intersect it with the run.
  5505     uint32_t flags = TextRenderedRun::eIncludeFill |
  5506                      TextRenderedRun::eIncludeStroke;
  5507     rectInFrameUserSpace.IntersectRect
  5508       (rectInFrameUserSpace, run.GetFrameUserSpaceRect(presContext, flags).ToThebesRect());
  5510     if (!rectInFrameUserSpace.IsEmpty()) {
  5511       // Transform it up to user space of the <text>, also taking into
  5512       // account the font size scale.
  5513       gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
  5514       m.Scale(mFontSizeScaleFactor, mFontSizeScaleFactor);
  5515       gfxRect rectInUserSpace = m.Transform(rectInFrameUserSpace);
  5517       // Union it into the result.
  5518       result.UnionRect(result, rectInUserSpace);
  5522   // Subtract the mRect offset from the result, as our user space for
  5523   // this frame is relative to the top-left of mRect.
  5524   float factor = presContext->AppUnitsPerCSSPixel();
  5525   gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
  5526                          NSAppUnitsToFloatPixels(mRect.y, factor));
  5528   return result - framePosition;
  5531 DrawMode
  5532 SVGTextFrame::SetupCairoState(gfxContext* aContext,
  5533                               nsIFrame* aFrame,
  5534                               gfxTextContextPaint* aOuterContextPaint,
  5535                               gfxTextContextPaint** aThisContextPaint)
  5537   DrawMode toDraw = DrawMode(0);
  5538   SVGTextContextPaint *thisContextPaint = new SVGTextContextPaint();
  5540   if (SetupCairoStroke(aContext, aFrame, aOuterContextPaint, thisContextPaint)) {
  5541     toDraw = DrawMode(int(toDraw) | int(DrawMode::GLYPH_STROKE));
  5544   if (SetupCairoFill(aContext, aFrame, aOuterContextPaint, thisContextPaint)) {
  5545     toDraw = DrawMode(int(toDraw) | int(DrawMode::GLYPH_FILL));
  5548   *aThisContextPaint = thisContextPaint;
  5550   return toDraw;
  5553 bool
  5554 SVGTextFrame::SetupCairoStroke(gfxContext* aContext,
  5555                                nsIFrame* aFrame,
  5556                                gfxTextContextPaint* aOuterContextPaint,
  5557                                SVGTextContextPaint* aThisContextPaint)
  5559   const nsStyleSVG *style = aFrame->StyleSVG();
  5560   if (style->mStroke.mType == eStyleSVGPaintType_None) {
  5561     aThisContextPaint->SetStrokeOpacity(0.0f);
  5562     return false;
  5565   nsSVGUtils::SetupCairoStrokeGeometry(aFrame, aContext, aOuterContextPaint);
  5566   float opacity = nsSVGUtils::GetOpacity(style->mStrokeOpacitySource,
  5567                                          style->mStrokeOpacity,
  5568                                          aOuterContextPaint);
  5570   SetupInheritablePaint(aContext, aFrame, opacity, aOuterContextPaint,
  5571                         aThisContextPaint->mStrokePaint, &nsStyleSVG::mStroke,
  5572                         nsSVGEffects::StrokeProperty());
  5574   aThisContextPaint->SetStrokeOpacity(opacity);
  5576   return opacity != 0.0f;
  5579 bool
  5580 SVGTextFrame::SetupCairoFill(gfxContext* aContext,
  5581                              nsIFrame* aFrame,
  5582                              gfxTextContextPaint* aOuterContextPaint,
  5583                              SVGTextContextPaint* aThisContextPaint)
  5585   const nsStyleSVG *style = aFrame->StyleSVG();
  5586   if (style->mFill.mType == eStyleSVGPaintType_None) {
  5587     aThisContextPaint->SetFillOpacity(0.0f);
  5588     return false;
  5591   float opacity = nsSVGUtils::GetOpacity(style->mFillOpacitySource,
  5592                                          style->mFillOpacity,
  5593                                          aOuterContextPaint);
  5595   SetupInheritablePaint(aContext, aFrame, opacity, aOuterContextPaint,
  5596                         aThisContextPaint->mFillPaint, &nsStyleSVG::mFill,
  5597                         nsSVGEffects::FillProperty());
  5599   aThisContextPaint->SetFillOpacity(opacity);
  5601   return true;
  5604 void
  5605 SVGTextFrame::SetupInheritablePaint(gfxContext* aContext,
  5606                                     nsIFrame* aFrame,
  5607                                     float& aOpacity,
  5608                                     gfxTextContextPaint* aOuterContextPaint,
  5609                                     SVGTextContextPaint::Paint& aTargetPaint,
  5610                                     nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
  5611                                     const FramePropertyDescriptor* aProperty)
  5613   const nsStyleSVG *style = aFrame->StyleSVG();
  5614   nsSVGPaintServerFrame *ps =
  5615     nsSVGEffects::GetPaintServer(aFrame, &(style->*aFillOrStroke), aProperty);
  5617   if (ps && ps->SetupPaintServer(aContext, aFrame, aFillOrStroke, aOpacity)) {
  5618     aTargetPaint.SetPaintServer(aFrame, aContext->CurrentMatrix(), ps);
  5619   } else if (nsSVGUtils::SetupContextPaint(aContext, aOuterContextPaint,
  5620                                            style->*aFillOrStroke,
  5621                                            aOpacity)) {
  5622     aTargetPaint.SetContextPaint(aOuterContextPaint, (style->*aFillOrStroke).mType);
  5623   } else {
  5624     nscolor color = nsSVGUtils::GetFallbackOrPaintColor(aContext,
  5625                                                         aFrame->StyleContext(),
  5626                                                         aFillOrStroke);
  5627     aTargetPaint.SetColor(color);
  5629     nsRefPtr<gfxPattern> pattern =
  5630       new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0,
  5631                              NS_GET_G(color) / 255.0,
  5632                              NS_GET_B(color) / 255.0,
  5633                              NS_GET_A(color) / 255.0 * aOpacity));
  5634     aContext->SetPattern(pattern);

mercurial