layout/svg/SVGTextFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/svg/SVGTextFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,5636 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +// Main header first:
    1.10 +#include "SVGTextFrame.h"
    1.11 +
    1.12 +// Keep others in (case-insensitive) order:
    1.13 +#include "DOMSVGPoint.h"
    1.14 +#include "gfx2DGlue.h"
    1.15 +#include "gfxFont.h"
    1.16 +#include "gfxSkipChars.h"
    1.17 +#include "gfxTypes.h"
    1.18 +#include "LookAndFeel.h"
    1.19 +#include "mozilla/gfx/2D.h"
    1.20 +#include "nsAlgorithm.h"
    1.21 +#include "nsBlockFrame.h"
    1.22 +#include "nsCaret.h"
    1.23 +#include "nsContentUtils.h"
    1.24 +#include "nsGkAtoms.h"
    1.25 +#include "nsIDOMSVGLength.h"
    1.26 +#include "nsISelection.h"
    1.27 +#include "nsQuickSort.h"
    1.28 +#include "nsRenderingContext.h"
    1.29 +#include "nsSVGEffects.h"
    1.30 +#include "nsSVGOuterSVGFrame.h"
    1.31 +#include "nsSVGPaintServerFrame.h"
    1.32 +#include "mozilla/dom/SVGRect.h"
    1.33 +#include "nsSVGIntegrationUtils.h"
    1.34 +#include "nsSVGUtils.h"
    1.35 +#include "nsTArray.h"
    1.36 +#include "nsTextFrame.h"
    1.37 +#include "nsTextNode.h"
    1.38 +#include "SVGAnimatedNumberList.h"
    1.39 +#include "SVGContentUtils.h"
    1.40 +#include "SVGLengthList.h"
    1.41 +#include "SVGNumberList.h"
    1.42 +#include "SVGPathElement.h"
    1.43 +#include "SVGTextPathElement.h"
    1.44 +#include "nsLayoutUtils.h"
    1.45 +#include <algorithm>
    1.46 +#include <cmath>
    1.47 +#include <limits>
    1.48 +
    1.49 +using namespace mozilla;
    1.50 +using namespace mozilla::dom;
    1.51 +using namespace mozilla::gfx;
    1.52 +
    1.53 +// ============================================================================
    1.54 +// Utility functions
    1.55 +
    1.56 +/**
    1.57 + * Using the specified gfxSkipCharsIterator, converts an offset and length
    1.58 + * in original char indexes to skipped char indexes.
    1.59 + *
    1.60 + * @param aIterator The gfxSkipCharsIterator to use for the conversion.
    1.61 + * @param aOriginalOffset The original offset (input).
    1.62 + * @param aOriginalLength The original length (input).
    1.63 + * @param aSkippedOffset The skipped offset (output).
    1.64 + * @param aSkippedLength The skipped length (output).
    1.65 + */
    1.66 +static void
    1.67 +ConvertOriginalToSkipped(gfxSkipCharsIterator& aIterator,
    1.68 +                         uint32_t aOriginalOffset, uint32_t aOriginalLength,
    1.69 +                         uint32_t& aSkippedOffset, uint32_t& aSkippedLength)
    1.70 +{
    1.71 +  aSkippedOffset = aIterator.ConvertOriginalToSkipped(aOriginalOffset);
    1.72 +  aIterator.AdvanceOriginal(aOriginalLength);
    1.73 +  aSkippedLength = aIterator.GetSkippedOffset() - aSkippedOffset;
    1.74 +}
    1.75 +
    1.76 +/**
    1.77 + * Using the specified gfxSkipCharsIterator, converts an offset and length
    1.78 + * in original char indexes to skipped char indexes in place.
    1.79 + *
    1.80 + * @param aIterator The gfxSkipCharsIterator to use for the conversion.
    1.81 + * @param aOriginalOffset The offset to convert from original to skipped.
    1.82 + * @param aOriginalLength The length to convert from original to skipped.
    1.83 + */
    1.84 +static void
    1.85 +ConvertOriginalToSkipped(gfxSkipCharsIterator& aIterator,
    1.86 +                         uint32_t& aOffset, uint32_t& aLength)
    1.87 +{
    1.88 +  ConvertOriginalToSkipped(aIterator, aOffset, aLength, aOffset, aLength);
    1.89 +}
    1.90 +
    1.91 +/**
    1.92 + * Converts an nsPoint from app units to user space units using the specified
    1.93 + * nsPresContext and returns it as a gfxPoint.
    1.94 + */
    1.95 +static gfxPoint
    1.96 +AppUnitsToGfxUnits(const nsPoint& aPoint, const nsPresContext* aContext)
    1.97 +{
    1.98 +  return gfxPoint(aContext->AppUnitsToGfxUnits(aPoint.x),
    1.99 +                  aContext->AppUnitsToGfxUnits(aPoint.y));
   1.100 +}
   1.101 +
   1.102 +/**
   1.103 + * Converts a gfxRect that is in app units to CSS pixels using the specified
   1.104 + * nsPresContext and returns it as a gfxRect.
   1.105 + */
   1.106 +static gfxRect
   1.107 +AppUnitsToFloatCSSPixels(const gfxRect& aRect, const nsPresContext* aContext)
   1.108 +{
   1.109 +  return gfxRect(aContext->AppUnitsToFloatCSSPixels(aRect.x),
   1.110 +                 aContext->AppUnitsToFloatCSSPixels(aRect.y),
   1.111 +                 aContext->AppUnitsToFloatCSSPixels(aRect.width),
   1.112 +                 aContext->AppUnitsToFloatCSSPixels(aRect.height));
   1.113 +}
   1.114 +
   1.115 +/**
   1.116 + * Scales a gfxRect around a given point.
   1.117 + *
   1.118 + * @param aRect The rectangle to scale.
   1.119 + * @param aPoint The point around which to scale.
   1.120 + * @param aScale The scale amount.
   1.121 + */
   1.122 +static void
   1.123 +ScaleAround(gfxRect& aRect, const gfxPoint& aPoint, double aScale)
   1.124 +{
   1.125 +  aRect.x = aPoint.x - aScale * (aPoint.x - aRect.x);
   1.126 +  aRect.y = aPoint.y - aScale * (aPoint.y - aRect.y);
   1.127 +  aRect.width *= aScale;
   1.128 +  aRect.height *= aScale;
   1.129 +}
   1.130 +
   1.131 +/**
   1.132 + * Returns whether a gfxPoint lies within a gfxRect.
   1.133 + */
   1.134 +static bool
   1.135 +Inside(const gfxRect& aRect, const gfxPoint& aPoint)
   1.136 +{
   1.137 +  return aPoint.x >= aRect.x &&
   1.138 +         aPoint.x < aRect.XMost() &&
   1.139 +         aPoint.y >= aRect.y &&
   1.140 +         aPoint.y < aRect.YMost();
   1.141 +}
   1.142 +
   1.143 +/**
   1.144 + * Gets the measured ascent and descent of the text in the given nsTextFrame
   1.145 + * in app units.
   1.146 + *
   1.147 + * @param aFrame The text frame.
   1.148 + * @param aAscent The ascent in app units (output).
   1.149 + * @param aDescent The descent in app units (output).
   1.150 + */
   1.151 +static void
   1.152 +GetAscentAndDescentInAppUnits(nsTextFrame* aFrame,
   1.153 +                              gfxFloat& aAscent, gfxFloat& aDescent)
   1.154 +{
   1.155 +  gfxSkipCharsIterator it = aFrame->EnsureTextRun(nsTextFrame::eInflated);
   1.156 +  gfxTextRun* textRun = aFrame->GetTextRun(nsTextFrame::eInflated);
   1.157 +
   1.158 +  uint32_t offset, length;
   1.159 +  ConvertOriginalToSkipped(it,
   1.160 +                           aFrame->GetContentOffset(),
   1.161 +                           aFrame->GetContentLength(),
   1.162 +                           offset, length);
   1.163 +
   1.164 +  gfxTextRun::Metrics metrics =
   1.165 +    textRun->MeasureText(offset, length, gfxFont::LOOSE_INK_EXTENTS, nullptr,
   1.166 +                         nullptr);
   1.167 +
   1.168 +  aAscent = metrics.mAscent;
   1.169 +  aDescent = metrics.mDescent;
   1.170 +}
   1.171 +
   1.172 +/**
   1.173 + * Updates an interval by intersecting it with another interval.
   1.174 + * The intervals are specified using a start index and a length.
   1.175 + */
   1.176 +static void
   1.177 +IntersectInterval(uint32_t& aStart, uint32_t& aLength,
   1.178 +                  uint32_t aStartOther, uint32_t aLengthOther)
   1.179 +{
   1.180 +  uint32_t aEnd = aStart + aLength;
   1.181 +  uint32_t aEndOther = aStartOther + aLengthOther;
   1.182 +
   1.183 +  if (aStartOther >= aEnd || aStart >= aEndOther) {
   1.184 +    aLength = 0;
   1.185 +  } else {
   1.186 +    if (aStartOther >= aStart)
   1.187 +      aStart = aStartOther;
   1.188 +    aLength = std::min(aEnd, aEndOther) - aStart;
   1.189 +  }
   1.190 +}
   1.191 +
   1.192 +/**
   1.193 + * Intersects an interval as IntersectInterval does but by taking
   1.194 + * the offset and length of the other interval from a
   1.195 + * nsTextFrame::TrimmedOffsets object.
   1.196 + */
   1.197 +static void
   1.198 +TrimOffsets(uint32_t& aStart, uint32_t& aLength,
   1.199 +            const nsTextFrame::TrimmedOffsets& aTrimmedOffsets)
   1.200 +{
   1.201 +  IntersectInterval(aStart, aLength,
   1.202 +                    aTrimmedOffsets.mStart, aTrimmedOffsets.mLength);
   1.203 +}
   1.204 +
   1.205 +/**
   1.206 + * Returns the closest ancestor-or-self node that is not an SVG <a>
   1.207 + * element.
   1.208 + */
   1.209 +static nsIContent*
   1.210 +GetFirstNonAAncestor(nsIContent* aContent)
   1.211 +{
   1.212 +  while (aContent && aContent->IsSVG(nsGkAtoms::a)) {
   1.213 +    aContent = aContent->GetParent();
   1.214 +  }
   1.215 +  return aContent;
   1.216 +}
   1.217 +
   1.218 +/**
   1.219 + * Returns whether the given node is a text content element[1], taking into
   1.220 + * account whether it has a valid parent.
   1.221 + *
   1.222 + * For example, in:
   1.223 + *
   1.224 + *   <svg xmlns="http://www.w3.org/2000/svg">
   1.225 + *     <text><a/><text/></text>
   1.226 + *     <tspan/>
   1.227 + *   </svg>
   1.228 + *
   1.229 + * true would be returned for the outer <text> element and the <a> element,
   1.230 + * and false for the inner <text> element (since a <text> is not allowed
   1.231 + * to be a child of another <text>) and the <tspan> element (because it
   1.232 + * must be inside a <text> subtree).
   1.233 + *
   1.234 + * Note that we don't support the <tref> element yet and this function
   1.235 + * returns false for it.
   1.236 + *
   1.237 + * [1] https://svgwg.org/svg2-draft/intro.html#TermTextContentElement
   1.238 + */
   1.239 +static bool
   1.240 +IsTextContentElement(nsIContent* aContent)
   1.241 +{
   1.242 +  if (!aContent->IsSVG()) {
   1.243 +    return false;
   1.244 +  }
   1.245 +
   1.246 +  if (aContent->Tag() == nsGkAtoms::text) {
   1.247 +    nsIContent* parent = GetFirstNonAAncestor(aContent->GetParent());
   1.248 +    return !parent || !IsTextContentElement(parent);
   1.249 +  }
   1.250 +
   1.251 +  if (aContent->Tag() == nsGkAtoms::textPath) {
   1.252 +    nsIContent* parent = GetFirstNonAAncestor(aContent->GetParent());
   1.253 +    return parent && parent->IsSVG(nsGkAtoms::text);
   1.254 +  }
   1.255 +
   1.256 +  if (aContent->Tag() == nsGkAtoms::a ||
   1.257 +      aContent->Tag() == nsGkAtoms::tspan ||
   1.258 +      aContent->Tag() == nsGkAtoms::altGlyph) {
   1.259 +    return true;
   1.260 +  }
   1.261 +
   1.262 +  return false;
   1.263 +}
   1.264 +
   1.265 +/**
   1.266 + * Returns whether the specified frame is an nsTextFrame that has some text
   1.267 + * content.
   1.268 + */
   1.269 +static bool
   1.270 +IsNonEmptyTextFrame(nsIFrame* aFrame)
   1.271 +{
   1.272 +  nsTextFrame* textFrame = do_QueryFrame(aFrame);
   1.273 +  if (!textFrame) {
   1.274 +    return false;
   1.275 +  }
   1.276 +
   1.277 +  return textFrame->GetContentLength() != 0;
   1.278 +}
   1.279 +
   1.280 +/**
   1.281 + * Takes an nsIFrame and if it is a text frame that has some text content,
   1.282 + * returns it as an nsTextFrame and its corresponding nsTextNode.
   1.283 + *
   1.284 + * @param aFrame The frame to look at.
   1.285 + * @param aTextFrame aFrame as an nsTextFrame (output).
   1.286 + * @param aTextNode The nsTextNode content of aFrame (output).
   1.287 + * @return true if aFrame is a non-empty text frame, false otherwise.
   1.288 + */
   1.289 +static bool
   1.290 +GetNonEmptyTextFrameAndNode(nsIFrame* aFrame,
   1.291 +                            nsTextFrame*& aTextFrame,
   1.292 +                            nsTextNode*& aTextNode)
   1.293 +{
   1.294 +  nsTextFrame* text = do_QueryFrame(aFrame);
   1.295 +  if (!text) {
   1.296 +    return false;
   1.297 +  }
   1.298 +
   1.299 +  nsIContent* content = text->GetContent();
   1.300 +  NS_ASSERTION(content && content->IsNodeOfType(nsINode::eTEXT),
   1.301 +               "unexpected content type for nsTextFrame");
   1.302 +
   1.303 +  nsTextNode* node = static_cast<nsTextNode*>(content);
   1.304 +  if (node->TextLength() == 0) {
   1.305 +    return false;
   1.306 +  }
   1.307 +
   1.308 +  aTextFrame = text;
   1.309 +  aTextNode = node;
   1.310 +  return true;
   1.311 +}
   1.312 +
   1.313 +/**
   1.314 + * Returns whether the specified atom is for one of the five
   1.315 + * glyph positioning attributes that can appear on SVG text
   1.316 + * elements -- x, y, dx, dy or rotate.
   1.317 + */
   1.318 +static bool
   1.319 +IsGlyphPositioningAttribute(nsIAtom* aAttribute)
   1.320 +{
   1.321 +  return aAttribute == nsGkAtoms::x ||
   1.322 +         aAttribute == nsGkAtoms::y ||
   1.323 +         aAttribute == nsGkAtoms::dx ||
   1.324 +         aAttribute == nsGkAtoms::dy ||
   1.325 +         aAttribute == nsGkAtoms::rotate;
   1.326 +}
   1.327 +
   1.328 +/**
   1.329 + * Returns the position in app units of a given baseline (using an
   1.330 + * SVG dominant-baseline property value) for a given nsTextFrame.
   1.331 + *
   1.332 + * @param aFrame The text frame to inspect.
   1.333 + * @param aTextRun The text run of aFrame.
   1.334 + * @param aDominantBaseline The dominant-baseline value to use.
   1.335 + */
   1.336 +static nscoord
   1.337 +GetBaselinePosition(nsTextFrame* aFrame,
   1.338 +                    gfxTextRun* aTextRun,
   1.339 +                    uint8_t aDominantBaseline)
   1.340 +{
   1.341 +  switch (aDominantBaseline) {
   1.342 +    case NS_STYLE_DOMINANT_BASELINE_HANGING:
   1.343 +    case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE:
   1.344 +      return 0;
   1.345 +    case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT:
   1.346 +    case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE:
   1.347 +    case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE:
   1.348 +      // These three should not simply map to 'baseline', but we don't
   1.349 +      // support the complex baseline model that SVG 1.1 has and which
   1.350 +      // css3-linebox now defines.
   1.351 +      // (fall through)
   1.352 +    case NS_STYLE_DOMINANT_BASELINE_AUTO:
   1.353 +    case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC:
   1.354 +      return aFrame->GetBaseline();
   1.355 +  }
   1.356 +
   1.357 +  gfxTextRun::Metrics metrics =
   1.358 +    aTextRun->MeasureText(0, aTextRun->GetLength(), gfxFont::LOOSE_INK_EXTENTS,
   1.359 +                          nullptr, nullptr);
   1.360 +
   1.361 +  switch (aDominantBaseline) {
   1.362 +    case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE:
   1.363 +    case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC:
   1.364 +      return metrics.mAscent + metrics.mDescent;
   1.365 +    case NS_STYLE_DOMINANT_BASELINE_CENTRAL:
   1.366 +    case NS_STYLE_DOMINANT_BASELINE_MIDDLE:
   1.367 +    case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL:
   1.368 +      return (metrics.mAscent + metrics.mDescent) / 2.0;
   1.369 +  }
   1.370 +
   1.371 +  NS_NOTREACHED("unexpected dominant-baseline value");
   1.372 +  return aFrame->GetBaseline();
   1.373 +}
   1.374 +
   1.375 +/**
   1.376 + * For a given text run, returns the number of skipped characters that comprise
   1.377 + * the ligature group and/or cluster that includes the character represented
   1.378 + * by the specified gfxSkipCharsIterator.
   1.379 + *
   1.380 + * @param aTextRun The text run to use for determining whether a given character
   1.381 + *   is part of a ligature or cluster.
   1.382 + * @param aIterator The gfxSkipCharsIterator to use for the current position
   1.383 + *   in the text run.
   1.384 + */
   1.385 +static uint32_t
   1.386 +ClusterLength(gfxTextRun* aTextRun, const gfxSkipCharsIterator& aIterator)
   1.387 +{
   1.388 +  uint32_t start = aIterator.GetSkippedOffset();
   1.389 +  uint32_t end = start + 1;
   1.390 +  while (end < aTextRun->GetLength() &&
   1.391 +         (!aTextRun->IsLigatureGroupStart(end) ||
   1.392 +          !aTextRun->IsClusterStart(end))) {
   1.393 +    end++;
   1.394 +  }
   1.395 +  return end - start;
   1.396 +}
   1.397 +
   1.398 +/**
   1.399 + * Truncates an array to be at most the length of another array.
   1.400 + *
   1.401 + * @param aArrayToTruncate The array to truncate.
   1.402 + * @param aReferenceArray The array whose length will be used to truncate
   1.403 + *   aArrayToTruncate to.
   1.404 + */
   1.405 +template<typename T, typename U>
   1.406 +static void
   1.407 +TruncateTo(nsTArray<T>& aArrayToTruncate, const nsTArray<U>& aReferenceArray)
   1.408 +{
   1.409 +  uint32_t length = aReferenceArray.Length();
   1.410 +  if (aArrayToTruncate.Length() > length) {
   1.411 +    aArrayToTruncate.TruncateLength(length);
   1.412 +  }
   1.413 +}
   1.414 +
   1.415 +/**
   1.416 + * Asserts that the anonymous block child of the SVGTextFrame has been
   1.417 + * reflowed (or does not exist).  Returns null if the child has not been
   1.418 + * reflowed, and the frame otherwise.
   1.419 + *
   1.420 + * We check whether the kid has been reflowed and not the frame itself
   1.421 + * since we sometimes need to call this function during reflow, after the
   1.422 + * kid has been reflowed but before we have cleared the dirty bits on the
   1.423 + * frame itself.
   1.424 + */
   1.425 +static SVGTextFrame*
   1.426 +FrameIfAnonymousChildReflowed(SVGTextFrame* aFrame)
   1.427 +{
   1.428 +  NS_PRECONDITION(aFrame, "aFrame must not be null");
   1.429 +  nsIFrame* kid = aFrame->GetFirstPrincipalChild();
   1.430 +  if (NS_SUBTREE_DIRTY(kid)) {
   1.431 +    MOZ_ASSERT(false, "should have already reflowed the anonymous block child");
   1.432 +    return nullptr;
   1.433 +  }
   1.434 +  return aFrame;
   1.435 +}
   1.436 +
   1.437 +static double
   1.438 +GetContextScale(const gfxMatrix& aMatrix)
   1.439 +{
   1.440 +  // The context scale is the ratio of the length of the transformed
   1.441 +  // diagonal vector (1,1) to the length of the untransformed diagonal
   1.442 +  // (which is sqrt(2)).
   1.443 +  gfxPoint p = aMatrix.Transform(gfxPoint(1, 1)) -
   1.444 +               aMatrix.Transform(gfxPoint(0, 0));
   1.445 +  return SVGContentUtils::ComputeNormalizedHypotenuse(p.x, p.y);
   1.446 +}
   1.447 +
   1.448 +// ============================================================================
   1.449 +// Utility classes
   1.450 +
   1.451 +namespace mozilla {
   1.452 +
   1.453 +// ----------------------------------------------------------------------------
   1.454 +// TextRenderedRun
   1.455 +
   1.456 +/**
   1.457 + * A run of text within a single nsTextFrame whose glyphs can all be painted
   1.458 + * with a single call to nsTextFrame::PaintText.  A text rendered run can
   1.459 + * be created for a sequence of two or more consecutive glyphs as long as:
   1.460 + *
   1.461 + *   - Only the first glyph has (or none of the glyphs have) been positioned
   1.462 + *     with SVG text positioning attributes
   1.463 + *   - All of the glyphs have zero rotation
   1.464 + *   - The glyphs are not on a text path
   1.465 + *   - The glyphs correspond to content within the one nsTextFrame
   1.466 + *
   1.467 + * A TextRenderedRunIterator produces TextRenderedRuns required for painting a
   1.468 + * whole SVGTextFrame.
   1.469 + */
   1.470 +struct TextRenderedRun
   1.471 +{
   1.472 +  /**
   1.473 +   * Constructs a TextRenderedRun that is uninitialized except for mFrame
   1.474 +   * being null.
   1.475 +   */
   1.476 +  TextRenderedRun()
   1.477 +    : mFrame(nullptr)
   1.478 +  {
   1.479 +  }
   1.480 +
   1.481 +  /**
   1.482 +   * Constructs a TextRenderedRun with all of the information required to
   1.483 +   * paint it.  See the comments documenting the member variables below
   1.484 +   * for descriptions of the arguments.
   1.485 +   */
   1.486 +  TextRenderedRun(nsTextFrame* aFrame, const gfxPoint& aPosition,
   1.487 +                  float aLengthAdjustScaleFactor, double aRotate,
   1.488 +                  float aFontSizeScaleFactor, nscoord aBaseline,
   1.489 +                  uint32_t aTextFrameContentOffset,
   1.490 +                  uint32_t aTextFrameContentLength,
   1.491 +                  uint32_t aTextElementCharIndex)
   1.492 +    : mFrame(aFrame),
   1.493 +      mPosition(aPosition),
   1.494 +      mLengthAdjustScaleFactor(aLengthAdjustScaleFactor),
   1.495 +      mRotate(static_cast<float>(aRotate)),
   1.496 +      mFontSizeScaleFactor(aFontSizeScaleFactor),
   1.497 +      mBaseline(aBaseline),
   1.498 +      mTextFrameContentOffset(aTextFrameContentOffset),
   1.499 +      mTextFrameContentLength(aTextFrameContentLength),
   1.500 +      mTextElementCharIndex(aTextElementCharIndex)
   1.501 +  {
   1.502 +  }
   1.503 +
   1.504 +  /**
   1.505 +   * Returns the text run for the text frame that this rendered run is part of.
   1.506 +   */
   1.507 +  gfxTextRun* GetTextRun() const
   1.508 +  {
   1.509 +    mFrame->EnsureTextRun(nsTextFrame::eInflated);
   1.510 +    return mFrame->GetTextRun(nsTextFrame::eInflated);
   1.511 +  }
   1.512 +
   1.513 +  /**
   1.514 +   * Returns whether this rendered run is RTL.
   1.515 +   */
   1.516 +  bool IsRightToLeft() const
   1.517 +  {
   1.518 +    return GetTextRun()->IsRightToLeft();
   1.519 +  }
   1.520 +
   1.521 +  /**
   1.522 +   * Returns the transform that converts from a <text> element's user space into
   1.523 +   * the coordinate space that rendered runs can be painted directly in.
   1.524 +   *
   1.525 +   * The difference between this method and GetTransformFromRunUserSpaceToUserSpace
   1.526 +   * is that when calling in to nsTextFrame::PaintText, it will already take
   1.527 +   * into account any left clip edge (that is, it doesn't just apply a visual
   1.528 +   * clip to the rendered text, it shifts the glyphs over so that they are
   1.529 +   * painted with their left edge at the x coordinate passed in to it).
   1.530 +   * Thus we need to account for this in our transform.
   1.531 +   *
   1.532 +   *
   1.533 +   * Assume that we have <text x="100" y="100" rotate="0 0 1 0 0 1">abcdef</text>.
   1.534 +   * This would result in four text rendered runs:
   1.535 +   *
   1.536 +   *   - one for "ab"
   1.537 +   *   - one for "c"
   1.538 +   *   - one for "de"
   1.539 +   *   - one for "f"
   1.540 +   *
   1.541 +   * Assume now that we are painting the third TextRenderedRun.  It will have
   1.542 +   * a left clip edge that is the sum of the advances of "abc", and it will
   1.543 +   * have a right clip edge that is the advance of "f".  In
   1.544 +   * SVGTextFrame::PaintSVG(), we pass in nsPoint() (i.e., the origin)
   1.545 +   * as the point at which to paint the text frame, and we pass in the
   1.546 +   * clip edge values.  The nsTextFrame will paint the substring of its
   1.547 +   * text such that the top-left corner of the "d"'s glyph cell will be at
   1.548 +   * (0, 0) in the current coordinate system.
   1.549 +   *
   1.550 +   * Thus, GetTransformFromUserSpaceForPainting must return a transform from
   1.551 +   * whatever user space the <text> element is in to a coordinate space in
   1.552 +   * device pixels (as that's what nsTextFrame works in) where the origin is at
   1.553 +   * the same position as our user space mPositions[i].mPosition value for
   1.554 +   * the "d" glyph, which will be (100 + userSpaceAdvance("abc"), 100).
   1.555 +   * The translation required to do this (ignoring the scale to get from
   1.556 +   * user space to device pixels, and ignoring the
   1.557 +   * (100 + userSpaceAdvance("abc"), 100) translation) is:
   1.558 +   *
   1.559 +   *   (-leftEdge, -baseline)
   1.560 +   *
   1.561 +   * where baseline is the distance between the baseline of the text and the top
   1.562 +   * edge of the nsTextFrame.  We translate by -leftEdge horizontally because
   1.563 +   * the nsTextFrame will already shift the glyphs over by that amount and start
   1.564 +   * painting glyphs at x = 0.  We translate by -baseline vertically so that
   1.565 +   * painting the top edges of the glyphs at y = 0 will result in their
   1.566 +   * baselines being at our desired y position.
   1.567 +   *
   1.568 +   *
   1.569 +   * Now for an example with RTL text.  Assume our content is now
   1.570 +   * <text x="100" y="100" rotate="0 0 1 0 0 1">WERBEH</text>.  We'd have
   1.571 +   * the following text rendered runs:
   1.572 +   *
   1.573 +   *   - one for "EH"
   1.574 +   *   - one for "B"
   1.575 +   *   - one for "ER"
   1.576 +   *   - one for "W"
   1.577 +   *
   1.578 +   * Again, we are painting the third TextRenderedRun.  The left clip edge
   1.579 +   * is the advance of the "W" and the right clip edge is the sum of the
   1.580 +   * advances of "BEH".  Our translation to get the rendered "ER" glyphs
   1.581 +   * in the right place this time is:
   1.582 +   *
   1.583 +   *   (-frameWidth + rightEdge, -baseline)
   1.584 +   *
   1.585 +   * which is equivalent to:
   1.586 +   *
   1.587 +   *   (-(leftEdge + advance("ER")), -baseline)
   1.588 +   *
   1.589 +   * The reason we have to shift left additionally by the width of the run
   1.590 +   * of glyphs we are painting is that although the nsTextFrame is RTL,
   1.591 +   * we still supply the top-left corner to paint the frame at when calling
   1.592 +   * nsTextFrame::PaintText, even though our user space positions for each
   1.593 +   * glyph in mPositions specifies the origin of each glyph, which for RTL
   1.594 +   * glyphs is at the right edge of the glyph cell.
   1.595 +   *
   1.596 +   *
   1.597 +   * For any other use of an nsTextFrame in the context of a particular run
   1.598 +   * (such as hit testing, or getting its rectangle),
   1.599 +   * GetTransformFromRunUserSpaceToUserSpace should be used.
   1.600 +   *
   1.601 +   * @param aContext The context to use for unit conversions.
   1.602 +   * @param aItem The nsCharClipDisplayItem that holds the amount of clipping
   1.603 +   *   from the left and right edges of the text frame for this rendered run.
   1.604 +   *   An appropriate nsCharClipDisplayItem can be obtained by constructing an
   1.605 +   *   SVGCharClipDisplayItem for the TextRenderedRun.
   1.606 +   */
   1.607 +  gfxMatrix GetTransformFromUserSpaceForPainting(
   1.608 +                                      nsPresContext* aContext,
   1.609 +                                      const nsCharClipDisplayItem& aItem) const;
   1.610 +
   1.611 +  /**
   1.612 +   * Returns the transform that converts from "run user space" to a <text>
   1.613 +   * element's user space.  Run user space is a coordinate system that has the
   1.614 +   * same size as the <text>'s user space but rotated and translated such that
   1.615 +   * (0,0) is the top-left of the rectangle that bounds the text.
   1.616 +   *
   1.617 +   * @param aContext The context to use for unit conversions.
   1.618 +   */
   1.619 +  gfxMatrix GetTransformFromRunUserSpaceToUserSpace(nsPresContext* aContext) const;
   1.620 +
   1.621 +  /**
   1.622 +   * Returns the transform that converts from "run user space" to float pixels
   1.623 +   * relative to the nsTextFrame that this rendered run is a part of.
   1.624 +   *
   1.625 +   * @param aContext The context to use for unit conversions.
   1.626 +   */
   1.627 +  gfxMatrix GetTransformFromRunUserSpaceToFrameUserSpace(nsPresContext* aContext) const;
   1.628 +
   1.629 +  /**
   1.630 +   * Flag values used for the aFlags arguments of GetRunUserSpaceRect,
   1.631 +   * GetFrameUserSpaceRect and GetUserSpaceRect.
   1.632 +   */
   1.633 +  enum {
   1.634 +    // Includes the fill geometry of the text in the returned rectangle.
   1.635 +    eIncludeFill = 1,
   1.636 +    // Includes the stroke geometry of the text in the returned rectangle.
   1.637 +    eIncludeStroke = 2,
   1.638 +    // Includes any text shadow in the returned rectangle.
   1.639 +    eIncludeTextShadow = 4,
   1.640 +    // Don't include any horizontal glyph overflow in the returned rectangle.
   1.641 +    eNoHorizontalOverflow = 8
   1.642 +  };
   1.643 +
   1.644 +  /**
   1.645 +   * Returns a rectangle that bounds the fill and/or stroke of the rendered run
   1.646 +   * in run user space.
   1.647 +   *
   1.648 +   * @param aContext The context to use for unit conversions.
   1.649 +   * @param aFlags A combination of the flags above (eIncludeFill and
   1.650 +   *   eIncludeStroke) indicating what parts of the text to include in
   1.651 +   *   the rectangle.
   1.652 +   */
   1.653 +  SVGBBox GetRunUserSpaceRect(nsPresContext* aContext, uint32_t aFlags) const;
   1.654 +
   1.655 +  /**
   1.656 +   * Returns a rectangle that covers the fill and/or stroke of the rendered run
   1.657 +   * in "frame user space".
   1.658 +   *
   1.659 +   * Frame user space is a coordinate space of the same scale as the <text>
   1.660 +   * element's user space, but with its rotation set to the rotation of
   1.661 +   * the glyphs within this rendered run and its origin set to the position
   1.662 +   * such that placing the nsTextFrame there would result in the glyphs in
   1.663 +   * this rendered run being at their correct positions.
   1.664 +   *
   1.665 +   * For example, say we have <text x="100 150" y="100">ab</text>.  Assume
   1.666 +   * the advance of both the "a" and the "b" is 12 user units, and the
   1.667 +   * ascent of the text is 8 user units and its descent is 6 user units,
   1.668 +   * and that we are not measuing the stroke of the text, so that we stay
   1.669 +   * entirely within the glyph cells.
   1.670 +   *
   1.671 +   * There will be two text rendered runs, one for "a" and one for "b".
   1.672 +   *
   1.673 +   * The frame user space for the "a" run will have its origin at
   1.674 +   * (100, 100 - 8) in the <text> element's user space and will have its
   1.675 +   * axes aligned with the user space (since there is no rotate="" or
   1.676 +   * text path involve) and with its scale the same as the user space.
   1.677 +   * The rect returned by this method will be (0, 0, 12, 14), since the "a"
   1.678 +   * glyph is right at the left of the nsTextFrame.
   1.679 +   *
   1.680 +   * The frame user space for the "b" run will have its origin at
   1.681 +   * (150 - 12, 100 - 8), and scale/rotation the same as above.  The rect
   1.682 +   * returned by this method will be (12, 0, 12, 14), since we are
   1.683 +   * advance("a") horizontally in to the text frame.
   1.684 +   *
   1.685 +   * @param aContext The context to use for unit conversions.
   1.686 +   * @param aFlags A combination of the flags above (eIncludeFill and
   1.687 +   *   eIncludeStroke) indicating what parts of the text to include in
   1.688 +   *   the rectangle.
   1.689 +   */
   1.690 +  SVGBBox GetFrameUserSpaceRect(nsPresContext* aContext, uint32_t aFlags) const;
   1.691 +
   1.692 +  /**
   1.693 +   * Returns a rectangle that covers the fill and/or stroke of the rendered run
   1.694 +   * in the <text> element's user space.
   1.695 +   *
   1.696 +   * @param aContext The context to use for unit conversions.
   1.697 +   * @param aFlags A combination of the flags above indicating what parts of the
   1.698 +   *   text to include in the rectangle.
   1.699 +   * @param aAdditionalTransform An additional transform to apply to the
   1.700 +   *   frame user space rectangle before its bounds are transformed into
   1.701 +   *   user space.
   1.702 +   */
   1.703 +  SVGBBox GetUserSpaceRect(nsPresContext* aContext, uint32_t aFlags,
   1.704 +                           const gfxMatrix* aAdditionalTransform = nullptr) const;
   1.705 +
   1.706 +  /**
   1.707 +   * Gets the app unit amounts to clip from the left and right edges of
   1.708 +   * the nsTextFrame in order to paint just this rendered run.
   1.709 +   *
   1.710 +   * Note that if clip edge amounts land in the middle of a glyph, the
   1.711 +   * glyph won't be painted at all.  The clip edges are thus more of
   1.712 +   * a selection mechanism for which glyphs will be painted, rather
   1.713 +   * than a geometric clip.
   1.714 +   */
   1.715 +  void GetClipEdges(nscoord& aLeftEdge, nscoord& aRightEdge) const;
   1.716 +
   1.717 +  /**
   1.718 +   * Returns the advance width of the whole rendered run.
   1.719 +   */
   1.720 +  nscoord GetAdvanceWidth() const;
   1.721 +
   1.722 +  /**
   1.723 +   * Returns the index of the character into this rendered run whose
   1.724 +   * glyph cell contains the given point, or -1 if there is no such
   1.725 +   * character.  This does not hit test against any overflow.
   1.726 +   *
   1.727 +   * @param aContext The context to use for unit conversions.
   1.728 +   * @param aPoint The point in the user space of the <text> element.
   1.729 +   */
   1.730 +  int32_t GetCharNumAtPosition(nsPresContext* aContext,
   1.731 +                               const gfxPoint& aPoint) const;
   1.732 +
   1.733 +  /**
   1.734 +   * The text frame that this rendered run lies within.
   1.735 +   */
   1.736 +  nsTextFrame* mFrame;
   1.737 +
   1.738 +  /**
   1.739 +   * The point in user space that the text is positioned at.
   1.740 +   *
   1.741 +   * The x coordinate is the left edge of a LTR run of text or the right edge of
   1.742 +   * an RTL run.  The y coordinate is the baseline of the text.
   1.743 +   */
   1.744 +  gfxPoint mPosition;
   1.745 +
   1.746 +  /**
   1.747 +   * The horizontal scale factor to apply when painting glyphs to take
   1.748 +   * into account textLength="".
   1.749 +   */
   1.750 +  float mLengthAdjustScaleFactor;
   1.751 +
   1.752 +  /**
   1.753 +   * The rotation in radians in the user coordinate system that the text has.
   1.754 +   */
   1.755 +  float mRotate;
   1.756 +
   1.757 +  /**
   1.758 +   * The scale factor that was used to transform the text run's original font
   1.759 +   * size into a sane range for painting and measurement.
   1.760 +   */
   1.761 +  double mFontSizeScaleFactor;
   1.762 +
   1.763 +  /**
   1.764 +   * The baseline in app units of this text run.  The measurement is from the
   1.765 +   * top of the text frame.
   1.766 +   */
   1.767 +  nscoord mBaseline;
   1.768 +
   1.769 +  /**
   1.770 +   * The offset and length in mFrame's content nsTextNode that corresponds to
   1.771 +   * this text rendered run.  These are original char indexes.
   1.772 +   */
   1.773 +  uint32_t mTextFrameContentOffset;
   1.774 +  uint32_t mTextFrameContentLength;
   1.775 +
   1.776 +  /**
   1.777 +   * The character index in the whole SVG <text> element that this text rendered
   1.778 +   * run begins at.
   1.779 +   */
   1.780 +  uint32_t mTextElementCharIndex;
   1.781 +};
   1.782 +
   1.783 +gfxMatrix
   1.784 +TextRenderedRun::GetTransformFromUserSpaceForPainting(
   1.785 +                                       nsPresContext* aContext,
   1.786 +                                       const nsCharClipDisplayItem& aItem) const
   1.787 +{
   1.788 +  // We transform to device pixels positioned such that painting the text frame
   1.789 +  // at (0,0) with aItem will result in the text being in the right place.
   1.790 +
   1.791 +  gfxMatrix m;
   1.792 +  if (!mFrame) {
   1.793 +    return m;
   1.794 +  }
   1.795 +
   1.796 +  float cssPxPerDevPx = aContext->
   1.797 +    AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
   1.798 +
   1.799 +  // Glyph position in user space.
   1.800 +  m.Translate(mPosition / cssPxPerDevPx);
   1.801 +
   1.802 +  // Take into account any font size scaling and scaling due to textLength="".
   1.803 +  m.Scale(1.0 / mFontSizeScaleFactor, 1.0 / mFontSizeScaleFactor);
   1.804 +
   1.805 +  // Rotation due to rotate="" or a <textPath>.
   1.806 +  m.Rotate(mRotate);
   1.807 +
   1.808 +  m.Scale(mLengthAdjustScaleFactor, 1.0);
   1.809 +
   1.810 +  // Translation to get the text frame in the right place.
   1.811 +  nsPoint t(IsRightToLeft() ?
   1.812 +              -mFrame->GetRect().width + aItem.mRightEdge :
   1.813 +              -aItem.mLeftEdge,
   1.814 +            -mBaseline);
   1.815 +  m.Translate(AppUnitsToGfxUnits(t, aContext));
   1.816 +
   1.817 +  return m;
   1.818 +}
   1.819 +
   1.820 +gfxMatrix
   1.821 +TextRenderedRun::GetTransformFromRunUserSpaceToUserSpace(
   1.822 +                                                  nsPresContext* aContext) const
   1.823 +{
   1.824 +  gfxMatrix m;
   1.825 +  if (!mFrame) {
   1.826 +    return m;
   1.827 +  }
   1.828 +
   1.829 +  float cssPxPerDevPx = aContext->
   1.830 +    AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
   1.831 +
   1.832 +  nscoord left, right;
   1.833 +  GetClipEdges(left, right);
   1.834 +
   1.835 +  // Glyph position in user space.
   1.836 +  m.Translate(mPosition);
   1.837 +
   1.838 +  // Rotation due to rotate="" or a <textPath>.
   1.839 +  m.Rotate(mRotate);
   1.840 +
   1.841 +  // Scale due to textLength="".
   1.842 +  m.Scale(mLengthAdjustScaleFactor, 1.0);
   1.843 +
   1.844 +  // Translation to get the text frame in the right place.
   1.845 +  nsPoint t(IsRightToLeft() ?
   1.846 +              -mFrame->GetRect().width + left + right :
   1.847 +              0,
   1.848 +            -mBaseline);
   1.849 +  m.Translate(AppUnitsToGfxUnits(t, aContext) *
   1.850 +                cssPxPerDevPx / mFontSizeScaleFactor);
   1.851 +
   1.852 +  return m;
   1.853 +}
   1.854 +
   1.855 +gfxMatrix
   1.856 +TextRenderedRun::GetTransformFromRunUserSpaceToFrameUserSpace(
   1.857 +                                                  nsPresContext* aContext) const
   1.858 +{
   1.859 +  gfxMatrix m;
   1.860 +  if (!mFrame) {
   1.861 +    return m;
   1.862 +  }
   1.863 +
   1.864 +  nscoord left, right;
   1.865 +  GetClipEdges(left, right);
   1.866 +
   1.867 +  // Translate by the horizontal distance into the text frame this
   1.868 +  // rendered run is.
   1.869 +  return m.Translate(gfxPoint(gfxFloat(left) / aContext->AppUnitsPerCSSPixel(),
   1.870 +                              0));
   1.871 +}
   1.872 +
   1.873 +SVGBBox
   1.874 +TextRenderedRun::GetRunUserSpaceRect(nsPresContext* aContext,
   1.875 +                                     uint32_t aFlags) const
   1.876 +{
   1.877 +  SVGBBox r;
   1.878 +  if (!mFrame) {
   1.879 +    return r;
   1.880 +  }
   1.881 +
   1.882 +  // Determine the amount of overflow above and below the frame's mRect.
   1.883 +  //
   1.884 +  // We need to call GetVisualOverflowRectRelativeToSelf because this includes
   1.885 +  // overflowing decorations, which the MeasureText call below does not.  We
   1.886 +  // assume here the decorations only overflow above and below the frame, never
   1.887 +  // horizontally.
   1.888 +  nsRect self = mFrame->GetVisualOverflowRectRelativeToSelf();
   1.889 +  nsRect rect = mFrame->GetRect();
   1.890 +  nscoord above = -self.y;
   1.891 +  nscoord below = self.YMost() - rect.height;
   1.892 +
   1.893 +  gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
   1.894 +  gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
   1.895 +
   1.896 +  // Get the content range for this rendered run.
   1.897 +  uint32_t offset, length;
   1.898 +  ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength,
   1.899 +                           offset, length);
   1.900 +
   1.901 +  // Measure that range.
   1.902 +  gfxTextRun::Metrics metrics =
   1.903 +    textRun->MeasureText(offset, length, gfxFont::LOOSE_INK_EXTENTS,
   1.904 +                         nullptr, nullptr);
   1.905 +
   1.906 +  // Determine the rectangle that covers the rendered run's fill,
   1.907 +  // taking into account the measured vertical overflow due to
   1.908 +  // decorations.
   1.909 +  nscoord baseline = metrics.mBoundingBox.y + metrics.mAscent;
   1.910 +  gfxFloat x, width;
   1.911 +  if (aFlags & eNoHorizontalOverflow) {
   1.912 +    x = 0.0;
   1.913 +    width = textRun->GetAdvanceWidth(offset, length, nullptr);
   1.914 +  } else {
   1.915 +    x = metrics.mBoundingBox.x;
   1.916 +    width = metrics.mBoundingBox.width;
   1.917 +  }
   1.918 +  nsRect fillInAppUnits(x, baseline - above,
   1.919 +                        width, metrics.mBoundingBox.height + above + below);
   1.920 +
   1.921 +  // Account for text-shadow.
   1.922 +  if (aFlags & eIncludeTextShadow) {
   1.923 +    fillInAppUnits =
   1.924 +      nsLayoutUtils::GetTextShadowRectsUnion(fillInAppUnits, mFrame);
   1.925 +  }
   1.926 +
   1.927 +  // Convert the app units rectangle to user units.
   1.928 +  gfxRect fill = AppUnitsToFloatCSSPixels(gfxRect(fillInAppUnits.x,
   1.929 +                                                  fillInAppUnits.y,
   1.930 +                                                  fillInAppUnits.width,
   1.931 +                                                  fillInAppUnits.height),
   1.932 +                                          aContext);
   1.933 +
   1.934 +  // Scale the rectangle up due to any mFontSizeScaleFactor.  We scale
   1.935 +  // it around the text's origin.
   1.936 +  ScaleAround(fill,
   1.937 +              gfxPoint(0.0, aContext->AppUnitsToFloatCSSPixels(baseline)),
   1.938 +              1.0 / mFontSizeScaleFactor);
   1.939 +
   1.940 +  // Include the fill if requested.
   1.941 +  if (aFlags & eIncludeFill) {
   1.942 +    r = fill;
   1.943 +  }
   1.944 +
   1.945 +  // Include the stroke if requested.
   1.946 +  if ((aFlags & eIncludeStroke) &&
   1.947 +      nsSVGUtils::GetStrokeWidth(mFrame) > 0) {
   1.948 +    r.UnionEdges(nsSVGUtils::PathExtentsToMaxStrokeExtents(fill, mFrame,
   1.949 +                                                           gfxMatrix()));
   1.950 +  }
   1.951 +
   1.952 +  return r;
   1.953 +}
   1.954 +
   1.955 +SVGBBox
   1.956 +TextRenderedRun::GetFrameUserSpaceRect(nsPresContext* aContext,
   1.957 +                                       uint32_t aFlags) const
   1.958 +{
   1.959 +  SVGBBox r = GetRunUserSpaceRect(aContext, aFlags);
   1.960 +  if (r.IsEmpty()) {
   1.961 +    return r;
   1.962 +  }
   1.963 +  gfxMatrix m = GetTransformFromRunUserSpaceToFrameUserSpace(aContext);
   1.964 +  return m.TransformBounds(r.ToThebesRect());
   1.965 +}
   1.966 +
   1.967 +SVGBBox
   1.968 +TextRenderedRun::GetUserSpaceRect(nsPresContext* aContext,
   1.969 +                                  uint32_t aFlags,
   1.970 +                                  const gfxMatrix* aAdditionalTransform) const
   1.971 +{
   1.972 +  SVGBBox r = GetRunUserSpaceRect(aContext, aFlags);
   1.973 +  if (r.IsEmpty()) {
   1.974 +    return r;
   1.975 +  }
   1.976 +  gfxMatrix m = GetTransformFromRunUserSpaceToUserSpace(aContext);
   1.977 +  if (aAdditionalTransform) {
   1.978 +    m.Multiply(*aAdditionalTransform);
   1.979 +  }
   1.980 +  return m.TransformBounds(r.ToThebesRect());
   1.981 +}
   1.982 +
   1.983 +void
   1.984 +TextRenderedRun::GetClipEdges(nscoord& aLeftEdge, nscoord& aRightEdge) const
   1.985 +{
   1.986 +  uint32_t contentLength = mFrame->GetContentLength();
   1.987 +  if (mTextFrameContentOffset == 0 &&
   1.988 +      mTextFrameContentLength == contentLength) {
   1.989 +    // If the rendered run covers the entire content, we know we don't need
   1.990 +    // to clip without having to measure anything.
   1.991 +    aLeftEdge = 0;
   1.992 +    aRightEdge = 0;
   1.993 +    return;
   1.994 +  }
   1.995 +
   1.996 +  gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
   1.997 +  gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
   1.998 +
   1.999 +  // Get the covered content offset/length for this rendered run in skipped
  1.1000 +  // characters, since that is what GetAdvanceWidth expects.
  1.1001 +  uint32_t runOffset, runLength, frameOffset, frameLength;
  1.1002 +  ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength,
  1.1003 +                               runOffset, runLength);
  1.1004 +
  1.1005 +  // Get the offset/length of the whole nsTextFrame.
  1.1006 +  frameOffset = mFrame->GetContentOffset();
  1.1007 +  frameLength = mFrame->GetContentLength();
  1.1008 +
  1.1009 +  // Trim the whole-nsTextFrame offset/length to remove any leading/trailing
  1.1010 +  // white space, as the nsTextFrame when painting does not include them when
  1.1011 +  // interpreting clip edges.
  1.1012 +  nsTextFrame::TrimmedOffsets trimmedOffsets =
  1.1013 +    mFrame->GetTrimmedOffsets(mFrame->GetContent()->GetText(), true);
  1.1014 +  TrimOffsets(frameOffset, frameLength, trimmedOffsets);
  1.1015 +
  1.1016 +  // Convert the trimmed whole-nsTextFrame offset/length into skipped
  1.1017 +  // characters.
  1.1018 +  ConvertOriginalToSkipped(it, frameOffset, frameLength);
  1.1019 +
  1.1020 +  // Measure the advance width in the text run between the start of
  1.1021 +  // frame's content and the start of the rendered run's content,
  1.1022 +  nscoord leftEdge =
  1.1023 +    textRun->GetAdvanceWidth(frameOffset, runOffset - frameOffset, nullptr);
  1.1024 +
  1.1025 +  // and between the end of the rendered run's content and the end
  1.1026 +  // of the frame's content.
  1.1027 +  nscoord rightEdge =
  1.1028 +    textRun->GetAdvanceWidth(runOffset + runLength,
  1.1029 +                             frameOffset + frameLength - (runOffset + runLength),
  1.1030 +                             nullptr);
  1.1031 +
  1.1032 +  if (textRun->IsRightToLeft()) {
  1.1033 +    aLeftEdge  = rightEdge;
  1.1034 +    aRightEdge = leftEdge;
  1.1035 +  } else {
  1.1036 +    aLeftEdge  = leftEdge;
  1.1037 +    aRightEdge = rightEdge;
  1.1038 +  }
  1.1039 +}
  1.1040 +
  1.1041 +nscoord
  1.1042 +TextRenderedRun::GetAdvanceWidth() const
  1.1043 +{
  1.1044 +  gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
  1.1045 +  gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
  1.1046 +
  1.1047 +  uint32_t offset, length;
  1.1048 +  ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength,
  1.1049 +                           offset, length);
  1.1050 +
  1.1051 +  return textRun->GetAdvanceWidth(offset, length, nullptr);
  1.1052 +}
  1.1053 +
  1.1054 +int32_t
  1.1055 +TextRenderedRun::GetCharNumAtPosition(nsPresContext* aContext,
  1.1056 +                                      const gfxPoint& aPoint) const
  1.1057 +{
  1.1058 +  if (mTextFrameContentLength == 0) {
  1.1059 +    return -1;
  1.1060 +  }
  1.1061 +
  1.1062 +  float cssPxPerDevPx = aContext->
  1.1063 +    AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
  1.1064 +
  1.1065 +  // Convert the point from user space into run user space, and take
  1.1066 +  // into account any mFontSizeScaleFactor.
  1.1067 +  gfxMatrix m = GetTransformFromRunUserSpaceToUserSpace(aContext).Invert();
  1.1068 +  gfxPoint p = m.Transform(aPoint) / cssPxPerDevPx * mFontSizeScaleFactor;
  1.1069 +
  1.1070 +  // First check that the point lies vertically between the top and bottom
  1.1071 +  // edges of the text.
  1.1072 +  gfxFloat ascent, descent;
  1.1073 +  GetAscentAndDescentInAppUnits(mFrame, ascent, descent);
  1.1074 +
  1.1075 +  gfxFloat topEdge = mFrame->GetBaseline() - ascent;
  1.1076 +  gfxFloat bottomEdge = topEdge + ascent + descent;
  1.1077 +
  1.1078 +  if (p.y < aContext->AppUnitsToGfxUnits(topEdge) ||
  1.1079 +      p.y >= aContext->AppUnitsToGfxUnits(bottomEdge)) {
  1.1080 +    return -1;
  1.1081 +  }
  1.1082 +
  1.1083 +  gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
  1.1084 +  gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
  1.1085 +
  1.1086 +  // Next check that the point lies horizontally within the left and right
  1.1087 +  // edges of the text.
  1.1088 +  uint32_t offset, length;
  1.1089 +  ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength,
  1.1090 +                           offset, length);
  1.1091 +  gfxFloat runAdvance =
  1.1092 +    aContext->AppUnitsToGfxUnits(textRun->GetAdvanceWidth(offset, length,
  1.1093 +                                                          nullptr));
  1.1094 +
  1.1095 +  if (p.x < 0 || p.x >= runAdvance) {
  1.1096 +    return -1;
  1.1097 +  }
  1.1098 +
  1.1099 +  // Finally, measure progressively smaller portions of the rendered run to
  1.1100 +  // find which glyph it lies within.  This will need to change once we
  1.1101 +  // support letter-spacing and word-spacing.
  1.1102 +  bool rtl = textRun->IsRightToLeft();
  1.1103 +  for (int32_t i = mTextFrameContentLength - 1; i >= 0; i--) {
  1.1104 +    ConvertOriginalToSkipped(it, mTextFrameContentOffset, i, offset, length);
  1.1105 +    gfxFloat advance =
  1.1106 +      aContext->AppUnitsToGfxUnits(textRun->GetAdvanceWidth(offset, length,
  1.1107 +                                                            nullptr));
  1.1108 +    if ((rtl && p.x < runAdvance - advance) ||
  1.1109 +        (!rtl && p.x >= advance)) {
  1.1110 +      return i;
  1.1111 +    }
  1.1112 +  }
  1.1113 +  return -1;
  1.1114 +}
  1.1115 +
  1.1116 +// ----------------------------------------------------------------------------
  1.1117 +// TextNodeIterator
  1.1118 +
  1.1119 +enum SubtreePosition
  1.1120 +{
  1.1121 +  eBeforeSubtree,
  1.1122 +  eWithinSubtree,
  1.1123 +  eAfterSubtree
  1.1124 +};
  1.1125 +
  1.1126 +/**
  1.1127 + * An iterator class for nsTextNodes that are descendants of a given node, the
  1.1128 + * root.  Nodes are iterated in document order.  An optional subtree can be
  1.1129 + * specified, in which case the iterator will track whether the current state of
  1.1130 + * the traversal over the tree is within that subtree or is past that subtree.
  1.1131 + */
  1.1132 +class TextNodeIterator
  1.1133 +{
  1.1134 +public:
  1.1135 +  /**
  1.1136 +   * Constructs a TextNodeIterator with the specified root node and optional
  1.1137 +   * subtree.
  1.1138 +   */
  1.1139 +  TextNodeIterator(nsIContent* aRoot, nsIContent* aSubtree = nullptr)
  1.1140 +    : mRoot(aRoot),
  1.1141 +      mSubtree(aSubtree == aRoot ? nullptr : aSubtree),
  1.1142 +      mCurrent(aRoot),
  1.1143 +      mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree)
  1.1144 +  {
  1.1145 +    NS_ASSERTION(aRoot, "expected non-null root");
  1.1146 +    if (!aRoot->IsNodeOfType(nsINode::eTEXT)) {
  1.1147 +      Next();
  1.1148 +    }
  1.1149 +  }
  1.1150 +
  1.1151 +  /**
  1.1152 +   * Returns the current nsTextNode, or null if the iterator has finished.
  1.1153 +   */
  1.1154 +  nsTextNode* Current() const
  1.1155 +  {
  1.1156 +    return static_cast<nsTextNode*>(mCurrent);
  1.1157 +  }
  1.1158 +
  1.1159 +  /**
  1.1160 +   * Advances to the next nsTextNode and returns it, or null if the end of
  1.1161 +   * iteration has been reached.
  1.1162 +   */
  1.1163 +  nsTextNode* Next();
  1.1164 +
  1.1165 +  /**
  1.1166 +   * Returns whether the iterator is currently within the subtree rooted
  1.1167 +   * at mSubtree.  Returns true if we are not tracking a subtree (we consider
  1.1168 +   * that we're always within the subtree).
  1.1169 +   */
  1.1170 +  bool IsWithinSubtree() const
  1.1171 +  {
  1.1172 +    return mSubtreePosition == eWithinSubtree;
  1.1173 +  }
  1.1174 +
  1.1175 +  /**
  1.1176 +   * Returns whether the iterator is past the subtree rooted at mSubtree.
  1.1177 +   * Returns false if we are not tracking a subtree.
  1.1178 +   */
  1.1179 +  bool IsAfterSubtree() const
  1.1180 +  {
  1.1181 +    return mSubtreePosition == eAfterSubtree;
  1.1182 +  }
  1.1183 +
  1.1184 +private:
  1.1185 +  /**
  1.1186 +   * The root under which all nsTextNodes will be iterated over.
  1.1187 +   */
  1.1188 +  nsIContent* mRoot;
  1.1189 +
  1.1190 +  /**
  1.1191 +   * The node rooting the subtree to track.
  1.1192 +   */
  1.1193 +  nsIContent* mSubtree;
  1.1194 +
  1.1195 +  /**
  1.1196 +   * The current node during iteration.
  1.1197 +   */
  1.1198 +  nsIContent* mCurrent;
  1.1199 +
  1.1200 +  /**
  1.1201 +   * The current iterator position relative to mSubtree.
  1.1202 +   */
  1.1203 +  SubtreePosition mSubtreePosition;
  1.1204 +};
  1.1205 +
  1.1206 +nsTextNode*
  1.1207 +TextNodeIterator::Next()
  1.1208 +{
  1.1209 +  // Starting from mCurrent, we do a non-recursive traversal to the next
  1.1210 +  // nsTextNode beneath mRoot, updating mSubtreePosition appropriately if we
  1.1211 +  // encounter mSubtree.
  1.1212 +  if (mCurrent) {
  1.1213 +    do {
  1.1214 +      nsIContent* next = IsTextContentElement(mCurrent) ?
  1.1215 +                           mCurrent->GetFirstChild() :
  1.1216 +                           nullptr;
  1.1217 +      if (next) {
  1.1218 +        mCurrent = next;
  1.1219 +        if (mCurrent == mSubtree) {
  1.1220 +          mSubtreePosition = eWithinSubtree;
  1.1221 +        }
  1.1222 +      } else {
  1.1223 +        for (;;) {
  1.1224 +          if (mCurrent == mRoot) {
  1.1225 +            mCurrent = nullptr;
  1.1226 +            break;
  1.1227 +          }
  1.1228 +          if (mCurrent == mSubtree) {
  1.1229 +            mSubtreePosition = eAfterSubtree;
  1.1230 +          }
  1.1231 +          next = mCurrent->GetNextSibling();
  1.1232 +          if (next) {
  1.1233 +            mCurrent = next;
  1.1234 +            if (mCurrent == mSubtree) {
  1.1235 +              mSubtreePosition = eWithinSubtree;
  1.1236 +            }
  1.1237 +            break;
  1.1238 +          }
  1.1239 +          if (mCurrent == mSubtree) {
  1.1240 +            mSubtreePosition = eAfterSubtree;
  1.1241 +          }
  1.1242 +          mCurrent = mCurrent->GetParent();
  1.1243 +        }
  1.1244 +      }
  1.1245 +    } while (mCurrent && !mCurrent->IsNodeOfType(nsINode::eTEXT));
  1.1246 +  }
  1.1247 +
  1.1248 +  return static_cast<nsTextNode*>(mCurrent);
  1.1249 +}
  1.1250 +
  1.1251 +// ----------------------------------------------------------------------------
  1.1252 +// TextNodeCorrespondenceRecorder
  1.1253 +
  1.1254 +/**
  1.1255 + * TextNodeCorrespondence is used as the value of a frame property that
  1.1256 + * is stored on all its descendant nsTextFrames.  It stores the number of DOM
  1.1257 + * characters between it and the previous nsTextFrame that did not have an
  1.1258 + * nsTextFrame created for them, due to either not being in a correctly
  1.1259 + * parented text content element, or because they were display:none.
  1.1260 + * These are called "undisplayed characters".
  1.1261 + *
  1.1262 + * See also TextNodeCorrespondenceRecorder below, which is what sets the
  1.1263 + * frame property.
  1.1264 + */
  1.1265 +struct TextNodeCorrespondence
  1.1266 +{
  1.1267 +  TextNodeCorrespondence(uint32_t aUndisplayedCharacters)
  1.1268 +    : mUndisplayedCharacters(aUndisplayedCharacters)
  1.1269 +  {
  1.1270 +  }
  1.1271 +
  1.1272 +  uint32_t mUndisplayedCharacters;
  1.1273 +};
  1.1274 +
  1.1275 +static void DestroyTextNodeCorrespondence(void* aPropertyValue)
  1.1276 +{
  1.1277 +  delete static_cast<TextNodeCorrespondence*>(aPropertyValue);
  1.1278 +}
  1.1279 +
  1.1280 +NS_DECLARE_FRAME_PROPERTY(TextNodeCorrespondenceProperty, DestroyTextNodeCorrespondence)
  1.1281 +
  1.1282 +/**
  1.1283 + * Returns the number of undisplayed characters before the specified
  1.1284 + * nsTextFrame.
  1.1285 + */
  1.1286 +static uint32_t
  1.1287 +GetUndisplayedCharactersBeforeFrame(nsTextFrame* aFrame)
  1.1288 +{
  1.1289 +  void* value = aFrame->Properties().Get(TextNodeCorrespondenceProperty());
  1.1290 +  TextNodeCorrespondence* correspondence =
  1.1291 +    static_cast<TextNodeCorrespondence*>(value);
  1.1292 +  if (!correspondence) {
  1.1293 +    NS_NOTREACHED("expected a TextNodeCorrespondenceProperty on nsTextFrame "
  1.1294 +                  "used for SVG text");
  1.1295 +    return 0;
  1.1296 +  }
  1.1297 +  return correspondence->mUndisplayedCharacters;
  1.1298 +}
  1.1299 +
  1.1300 +/**
  1.1301 + * Traverses the nsTextFrames for an SVGTextFrame and records a
  1.1302 + * TextNodeCorrespondenceProperty on each for the number of undisplayed DOM
  1.1303 + * characters between each frame.  This is done by iterating simultaenously
  1.1304 + * over the nsTextNodes and nsTextFrames and noting when nsTextNodes (or
  1.1305 + * parts of them) are skipped when finding the next nsTextFrame.
  1.1306 + */
  1.1307 +class TextNodeCorrespondenceRecorder
  1.1308 +{
  1.1309 +public:
  1.1310 +  /**
  1.1311 +   * Entry point for the TextNodeCorrespondenceProperty recording.
  1.1312 +   */
  1.1313 +  static void RecordCorrespondence(SVGTextFrame* aRoot);
  1.1314 +
  1.1315 +private:
  1.1316 +  TextNodeCorrespondenceRecorder(SVGTextFrame* aRoot)
  1.1317 +    : mNodeIterator(aRoot->GetContent()),
  1.1318 +      mPreviousNode(nullptr),
  1.1319 +      mNodeCharIndex(0)
  1.1320 +  {
  1.1321 +  }
  1.1322 +
  1.1323 +  void Record(SVGTextFrame* aRoot);
  1.1324 +  void TraverseAndRecord(nsIFrame* aFrame);
  1.1325 +
  1.1326 +  /**
  1.1327 +   * Returns the next non-empty nsTextNode.
  1.1328 +   */
  1.1329 +  nsTextNode* NextNode();
  1.1330 +
  1.1331 +  /**
  1.1332 +   * The iterator over the nsTextNodes that we use as we simultaneously
  1.1333 +   * iterate over the nsTextFrames.
  1.1334 +   */
  1.1335 +  TextNodeIterator mNodeIterator;
  1.1336 +
  1.1337 +  /**
  1.1338 +   * The previous nsTextNode we iterated over.
  1.1339 +   */
  1.1340 +  nsTextNode* mPreviousNode;
  1.1341 +
  1.1342 +  /**
  1.1343 +   * The index into the current nsTextNode's character content.
  1.1344 +   */
  1.1345 +  uint32_t mNodeCharIndex;
  1.1346 +};
  1.1347 +
  1.1348 +/* static */ void
  1.1349 +TextNodeCorrespondenceRecorder::RecordCorrespondence(SVGTextFrame* aRoot)
  1.1350 +{
  1.1351 +  TextNodeCorrespondenceRecorder recorder(aRoot);
  1.1352 +  recorder.Record(aRoot);
  1.1353 +}
  1.1354 +
  1.1355 +void
  1.1356 +TextNodeCorrespondenceRecorder::Record(SVGTextFrame* aRoot)
  1.1357 +{
  1.1358 +  if (!mNodeIterator.Current()) {
  1.1359 +    // If there are no nsTextNodes then there is nothing to do.
  1.1360 +    return;
  1.1361 +  }
  1.1362 +
  1.1363 +  // Traverse over all the nsTextFrames and record the number of undisplayed
  1.1364 +  // characters.
  1.1365 +  TraverseAndRecord(aRoot);
  1.1366 +
  1.1367 +  // Find how many undisplayed characters there are after the final nsTextFrame.
  1.1368 +  uint32_t undisplayed = 0;
  1.1369 +  if (mNodeIterator.Current()) {
  1.1370 +    if (mPreviousNode && mPreviousNode->TextLength() != mNodeCharIndex) {
  1.1371 +      // The last nsTextFrame ended part way through an nsTextNode.  The
  1.1372 +      // remaining characters count as undisplayed.
  1.1373 +      NS_ASSERTION(mNodeCharIndex < mPreviousNode->TextLength(),
  1.1374 +                   "incorrect tracking of undisplayed characters in "
  1.1375 +                   "text nodes");
  1.1376 +      undisplayed += mPreviousNode->TextLength() - mNodeCharIndex;
  1.1377 +    }
  1.1378 +    // All the remaining nsTextNodes that we iterate must also be undisplayed.
  1.1379 +    for (nsTextNode* textNode = mNodeIterator.Current();
  1.1380 +         textNode;
  1.1381 +         textNode = NextNode()) {
  1.1382 +      undisplayed += textNode->TextLength();
  1.1383 +    }
  1.1384 +  }
  1.1385 +
  1.1386 +  // Record the trailing number of undisplayed characters on the
  1.1387 +  // SVGTextFrame.
  1.1388 +  aRoot->mTrailingUndisplayedCharacters = undisplayed;
  1.1389 +}
  1.1390 +
  1.1391 +nsTextNode*
  1.1392 +TextNodeCorrespondenceRecorder::NextNode()
  1.1393 +{
  1.1394 +  mPreviousNode = mNodeIterator.Current();
  1.1395 +  nsTextNode* next;
  1.1396 +  do {
  1.1397 +    next = mNodeIterator.Next();
  1.1398 +  } while (next && next->TextLength() == 0);
  1.1399 +  return next;
  1.1400 +}
  1.1401 +
  1.1402 +void
  1.1403 +TextNodeCorrespondenceRecorder::TraverseAndRecord(nsIFrame* aFrame)
  1.1404 +{
  1.1405 +  // Recursively iterate over the frame tree, for frames that correspond
  1.1406 +  // to text content elements.
  1.1407 +  if (IsTextContentElement(aFrame->GetContent())) {
  1.1408 +    for (nsIFrame* f = aFrame->GetFirstPrincipalChild();
  1.1409 +         f;
  1.1410 +         f = f->GetNextSibling()) {
  1.1411 +      TraverseAndRecord(f);
  1.1412 +    }
  1.1413 +    return;
  1.1414 +  }
  1.1415 +
  1.1416 +  nsTextFrame* frame;  // The current text frame.
  1.1417 +  nsTextNode* node;    // The text node for the current text frame.
  1.1418 +  if (!GetNonEmptyTextFrameAndNode(aFrame, frame, node)) {
  1.1419 +    // If this isn't an nsTextFrame, or is empty, nothing to do.
  1.1420 +    return;
  1.1421 +  }
  1.1422 +
  1.1423 +  NS_ASSERTION(frame->GetContentOffset() >= 0,
  1.1424 +               "don't know how to handle negative content indexes");
  1.1425 +
  1.1426 +  uint32_t undisplayed = 0;
  1.1427 +  if (!mPreviousNode) {
  1.1428 +    // Must be the very first text frame.
  1.1429 +    NS_ASSERTION(mNodeCharIndex == 0, "incorrect tracking of undisplayed "
  1.1430 +                                      "characters in text nodes");
  1.1431 +    if (!mNodeIterator.Current()) {
  1.1432 +      NS_NOTREACHED("incorrect tracking of correspondence between text frames "
  1.1433 +                    "and text nodes");
  1.1434 +    } else {
  1.1435 +      // Each whole nsTextNode we find before we get to the text node for the
  1.1436 +      // first text frame must be undisplayed.
  1.1437 +      while (mNodeIterator.Current() != node) {
  1.1438 +        undisplayed += mNodeIterator.Current()->TextLength();
  1.1439 +        NextNode();
  1.1440 +      }
  1.1441 +      // If the first text frame starts at a non-zero content offset, then those
  1.1442 +      // earlier characters are also undisplayed.
  1.1443 +      undisplayed += frame->GetContentOffset();
  1.1444 +      NextNode();
  1.1445 +    }
  1.1446 +  } else if (mPreviousNode == node) {
  1.1447 +    // Same text node as last time.
  1.1448 +    if (static_cast<uint32_t>(frame->GetContentOffset()) != mNodeCharIndex) {
  1.1449 +      // We have some characters in the middle of the text node
  1.1450 +      // that are undisplayed.
  1.1451 +      NS_ASSERTION(mNodeCharIndex <
  1.1452 +                     static_cast<uint32_t>(frame->GetContentOffset()),
  1.1453 +                   "incorrect tracking of undisplayed characters in "
  1.1454 +                   "text nodes");
  1.1455 +      undisplayed = frame->GetContentOffset() - mNodeCharIndex;
  1.1456 +    }
  1.1457 +  } else {
  1.1458 +    // Different text node from last time.
  1.1459 +    if (mPreviousNode->TextLength() != mNodeCharIndex) {
  1.1460 +      NS_ASSERTION(mNodeCharIndex < mPreviousNode->TextLength(),
  1.1461 +                   "incorrect tracking of undisplayed characters in "
  1.1462 +                   "text nodes");
  1.1463 +      // Any trailing characters at the end of the previous nsTextNode are
  1.1464 +      // undisplayed.
  1.1465 +      undisplayed = mPreviousNode->TextLength() - mNodeCharIndex;
  1.1466 +    }
  1.1467 +    // Each whole nsTextNode we find before we get to the text node for
  1.1468 +    // the current text frame must be undisplayed.
  1.1469 +    while (mNodeIterator.Current() != node) {
  1.1470 +      undisplayed += mNodeIterator.Current()->TextLength();
  1.1471 +      NextNode();
  1.1472 +    }
  1.1473 +    // If the current text frame starts at a non-zero content offset, then those
  1.1474 +    // earlier characters are also undisplayed.
  1.1475 +    undisplayed += frame->GetContentOffset();
  1.1476 +    NextNode();
  1.1477 +  }
  1.1478 +
  1.1479 +  // Set the frame property.
  1.1480 +  frame->Properties().Set(TextNodeCorrespondenceProperty(),
  1.1481 +                          new TextNodeCorrespondence(undisplayed));
  1.1482 +
  1.1483 +  // Remember how far into the current nsTextNode we are.
  1.1484 +  mNodeCharIndex = frame->GetContentEnd();
  1.1485 +}
  1.1486 +
  1.1487 +// ----------------------------------------------------------------------------
  1.1488 +// TextFrameIterator
  1.1489 +
  1.1490 +/**
  1.1491 + * An iterator class for nsTextFrames that are descendants of an
  1.1492 + * SVGTextFrame.  The iterator can optionally track whether the
  1.1493 + * current nsTextFrame is for a descendant of, or past, a given subtree
  1.1494 + * content node or frame.  (This functionality is used for example by the SVG
  1.1495 + * DOM text methods to get only the nsTextFrames for a particular <tspan>.)
  1.1496 + *
  1.1497 + * TextFrameIterator also tracks and exposes other information about the
  1.1498 + * current nsTextFrame:
  1.1499 + *
  1.1500 + *   * how many undisplayed characters came just before it
  1.1501 + *   * its position (in app units) relative to the SVGTextFrame's anonymous
  1.1502 + *     block frame
  1.1503 + *   * what nsInlineFrame corresponding to a <textPath> element it is a
  1.1504 + *     descendant of
  1.1505 + *   * what computed dominant-baseline value applies to it
  1.1506 + *
  1.1507 + * Note that any text frames that are empty -- whose ContentLength() is 0 --
  1.1508 + * will be skipped over.
  1.1509 + */
  1.1510 +class TextFrameIterator
  1.1511 +{
  1.1512 +public:
  1.1513 +  /**
  1.1514 +   * Constructs a TextFrameIterator for the specified SVGTextFrame
  1.1515 +   * with an optional frame subtree to restrict iterated text frames to.
  1.1516 +   */
  1.1517 +  TextFrameIterator(SVGTextFrame* aRoot, nsIFrame* aSubtree = nullptr)
  1.1518 +    : mRootFrame(aRoot),
  1.1519 +      mSubtree(aSubtree),
  1.1520 +      mCurrentFrame(aRoot),
  1.1521 +      mCurrentPosition(),
  1.1522 +      mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree)
  1.1523 +  {
  1.1524 +    Init();
  1.1525 +  }
  1.1526 +
  1.1527 +  /**
  1.1528 +   * Constructs a TextFrameIterator for the specified SVGTextFrame
  1.1529 +   * with an optional frame content subtree to restrict iterated text frames to.
  1.1530 +   */
  1.1531 +  TextFrameIterator(SVGTextFrame* aRoot, nsIContent* aSubtree)
  1.1532 +    : mRootFrame(aRoot),
  1.1533 +      mSubtree(aRoot && aSubtree && aSubtree != aRoot->GetContent() ?
  1.1534 +                 aSubtree->GetPrimaryFrame() :
  1.1535 +                 nullptr),
  1.1536 +      mCurrentFrame(aRoot),
  1.1537 +      mCurrentPosition(),
  1.1538 +      mSubtreePosition(mSubtree ? eBeforeSubtree : eWithinSubtree)
  1.1539 +  {
  1.1540 +    Init();
  1.1541 +  }
  1.1542 +
  1.1543 +  /**
  1.1544 +   * Returns the root SVGTextFrame this TextFrameIterator is iterating over.
  1.1545 +   */
  1.1546 +  SVGTextFrame* Root() const
  1.1547 +  {
  1.1548 +    return mRootFrame;
  1.1549 +  }
  1.1550 +
  1.1551 +  /**
  1.1552 +   * Returns the current nsTextFrame.
  1.1553 +   */
  1.1554 +  nsTextFrame* Current() const
  1.1555 +  {
  1.1556 +    return do_QueryFrame(mCurrentFrame);
  1.1557 +  }
  1.1558 +
  1.1559 +  /**
  1.1560 +   * Returns the number of undisplayed characters in the DOM just before the
  1.1561 +   * current frame.
  1.1562 +   */
  1.1563 +  uint32_t UndisplayedCharacters() const;
  1.1564 +
  1.1565 +  /**
  1.1566 +   * Returns the current frame's position, in app units, relative to the
  1.1567 +   * root SVGTextFrame's anonymous block frame.
  1.1568 +   */
  1.1569 +  nsPoint Position() const
  1.1570 +  {
  1.1571 +    return mCurrentPosition;
  1.1572 +  }
  1.1573 +
  1.1574 +  /**
  1.1575 +   * Advances to the next nsTextFrame and returns it.
  1.1576 +   */
  1.1577 +  nsTextFrame* Next();
  1.1578 +
  1.1579 +  /**
  1.1580 +   * Returns whether the iterator is within the subtree.
  1.1581 +   */
  1.1582 +  bool IsWithinSubtree() const
  1.1583 +  {
  1.1584 +    return mSubtreePosition == eWithinSubtree;
  1.1585 +  }
  1.1586 +
  1.1587 +  /**
  1.1588 +   * Returns whether the iterator is past the subtree.
  1.1589 +   */
  1.1590 +  bool IsAfterSubtree() const
  1.1591 +  {
  1.1592 +    return mSubtreePosition == eAfterSubtree;
  1.1593 +  }
  1.1594 +
  1.1595 +  /**
  1.1596 +   * Returns the frame corresponding to the <textPath> element, if we
  1.1597 +   * are inside one.
  1.1598 +   */
  1.1599 +  nsIFrame* TextPathFrame() const
  1.1600 +  {
  1.1601 +    return mTextPathFrames.IsEmpty() ?
  1.1602 +             nullptr :
  1.1603 +             mTextPathFrames.ElementAt(mTextPathFrames.Length() - 1);
  1.1604 +  }
  1.1605 +
  1.1606 +  /**
  1.1607 +   * Returns the current frame's computed dominant-baseline value.
  1.1608 +   */
  1.1609 +  uint8_t DominantBaseline() const
  1.1610 +  {
  1.1611 +    return mBaselines.ElementAt(mBaselines.Length() - 1);
  1.1612 +  }
  1.1613 +
  1.1614 +  /**
  1.1615 +   * Finishes the iterator.
  1.1616 +   */
  1.1617 +  void Close()
  1.1618 +  {
  1.1619 +    mCurrentFrame = nullptr;
  1.1620 +  }
  1.1621 +
  1.1622 +private:
  1.1623 +  /**
  1.1624 +   * Initializes the iterator and advances to the first item.
  1.1625 +   */
  1.1626 +  void Init()
  1.1627 +  {
  1.1628 +    if (!mRootFrame) {
  1.1629 +      return;
  1.1630 +    }
  1.1631 +
  1.1632 +    mBaselines.AppendElement(mRootFrame->StyleSVGReset()->mDominantBaseline);
  1.1633 +    Next();
  1.1634 +  }
  1.1635 +
  1.1636 +  /**
  1.1637 +   * Pushes the specified frame's computed dominant-baseline value.
  1.1638 +   * If the value of the property is "auto", then the parent frame's
  1.1639 +   * computed value is used.
  1.1640 +   */
  1.1641 +  void PushBaseline(nsIFrame* aNextFrame);
  1.1642 +
  1.1643 +  /**
  1.1644 +   * Pops the current dominant-baseline off the stack.
  1.1645 +   */
  1.1646 +  void PopBaseline();
  1.1647 +
  1.1648 +  /**
  1.1649 +   * The root frame we are iterating through.
  1.1650 +   */
  1.1651 +  SVGTextFrame* mRootFrame;
  1.1652 +
  1.1653 +  /**
  1.1654 +   * The frame for the subtree we are also interested in tracking.
  1.1655 +   */
  1.1656 +  nsIFrame* mSubtree;
  1.1657 +
  1.1658 +  /**
  1.1659 +   * The current value of the iterator.
  1.1660 +   */
  1.1661 +  nsIFrame* mCurrentFrame;
  1.1662 +
  1.1663 +  /**
  1.1664 +   * The position, in app units, of the current frame relative to mRootFrame.
  1.1665 +   */
  1.1666 +  nsPoint mCurrentPosition;
  1.1667 +
  1.1668 +  /**
  1.1669 +   * Stack of frames corresponding to <textPath> elements that are in scope
  1.1670 +   * for the current frame.
  1.1671 +   */
  1.1672 +  nsAutoTArray<nsIFrame*, 1> mTextPathFrames;
  1.1673 +
  1.1674 +  /**
  1.1675 +   * Stack of dominant-baseline values to record as we traverse through the
  1.1676 +   * frame tree.
  1.1677 +   */
  1.1678 +  nsAutoTArray<uint8_t, 8> mBaselines;
  1.1679 +
  1.1680 +  /**
  1.1681 +   * The iterator's current position relative to mSubtree.
  1.1682 +   */
  1.1683 +  SubtreePosition mSubtreePosition;
  1.1684 +};
  1.1685 +
  1.1686 +uint32_t
  1.1687 +TextFrameIterator::UndisplayedCharacters() const
  1.1688 +{
  1.1689 +  MOZ_ASSERT(!(mRootFrame->GetFirstPrincipalChild() &&
  1.1690 +               NS_SUBTREE_DIRTY(mRootFrame->GetFirstPrincipalChild())),
  1.1691 +             "should have already reflowed the anonymous block child");
  1.1692 +
  1.1693 +  if (!mCurrentFrame) {
  1.1694 +    return mRootFrame->mTrailingUndisplayedCharacters;
  1.1695 +  }
  1.1696 +
  1.1697 +  nsTextFrame* frame = do_QueryFrame(mCurrentFrame);
  1.1698 +  return GetUndisplayedCharactersBeforeFrame(frame);
  1.1699 +}
  1.1700 +
  1.1701 +nsTextFrame*
  1.1702 +TextFrameIterator::Next()
  1.1703 +{
  1.1704 +  // Starting from mCurrentFrame, we do a non-recursive traversal to the next
  1.1705 +  // nsTextFrame beneath mRoot, updating mSubtreePosition appropriately if we
  1.1706 +  // encounter mSubtree.
  1.1707 +  if (mCurrentFrame) {
  1.1708 +    do {
  1.1709 +      nsIFrame* next = IsTextContentElement(mCurrentFrame->GetContent()) ?
  1.1710 +                         mCurrentFrame->GetFirstPrincipalChild() :
  1.1711 +                         nullptr;
  1.1712 +      if (next) {
  1.1713 +        // Descend into this frame, and accumulate its position.
  1.1714 +        mCurrentPosition += next->GetPosition();
  1.1715 +        if (next->GetContent()->Tag() == nsGkAtoms::textPath) {
  1.1716 +          // Record this <textPath> frame.
  1.1717 +          mTextPathFrames.AppendElement(next);
  1.1718 +        }
  1.1719 +        // Record the frame's baseline.
  1.1720 +        PushBaseline(next);
  1.1721 +        mCurrentFrame = next;
  1.1722 +        if (mCurrentFrame == mSubtree) {
  1.1723 +          // If the current frame is mSubtree, we have now moved into it.
  1.1724 +          mSubtreePosition = eWithinSubtree;
  1.1725 +        }
  1.1726 +      } else {
  1.1727 +        for (;;) {
  1.1728 +          // We want to move past the current frame.
  1.1729 +          if (mCurrentFrame == mRootFrame) {
  1.1730 +            // If we've reached the root frame, we're finished.
  1.1731 +            mCurrentFrame = nullptr;
  1.1732 +            break;
  1.1733 +          }
  1.1734 +          // Remove the current frame's position.
  1.1735 +          mCurrentPosition -= mCurrentFrame->GetPosition();
  1.1736 +          if (mCurrentFrame->GetContent()->Tag() == nsGkAtoms::textPath) {
  1.1737 +            // Pop off the <textPath> frame if this is a <textPath>.
  1.1738 +            mTextPathFrames.TruncateLength(mTextPathFrames.Length() - 1);
  1.1739 +          }
  1.1740 +          // Pop off the current baseline.
  1.1741 +          PopBaseline();
  1.1742 +          if (mCurrentFrame == mSubtree) {
  1.1743 +            // If this was mSubtree, we have now moved past it.
  1.1744 +            mSubtreePosition = eAfterSubtree;
  1.1745 +          }
  1.1746 +          next = mCurrentFrame->GetNextSibling();
  1.1747 +          if (next) {
  1.1748 +            // Moving to the next sibling.
  1.1749 +            mCurrentPosition += next->GetPosition();
  1.1750 +            if (next->GetContent()->Tag() == nsGkAtoms::textPath) {
  1.1751 +              // Record this <textPath> frame.
  1.1752 +              mTextPathFrames.AppendElement(next);
  1.1753 +            }
  1.1754 +            // Record the frame's baseline.
  1.1755 +            PushBaseline(next);
  1.1756 +            mCurrentFrame = next;
  1.1757 +            if (mCurrentFrame == mSubtree) {
  1.1758 +              // If the current frame is mSubtree, we have now moved into it.
  1.1759 +              mSubtreePosition = eWithinSubtree;
  1.1760 +            }
  1.1761 +            break;
  1.1762 +          }
  1.1763 +          if (mCurrentFrame == mSubtree) {
  1.1764 +            // If there is no next sibling frame, and the current frame is
  1.1765 +            // mSubtree, we have now moved past it.
  1.1766 +            mSubtreePosition = eAfterSubtree;
  1.1767 +          }
  1.1768 +          // Ascend out of this frame.
  1.1769 +          mCurrentFrame = mCurrentFrame->GetParent();
  1.1770 +        }
  1.1771 +      }
  1.1772 +    } while (mCurrentFrame &&
  1.1773 +             !IsNonEmptyTextFrame(mCurrentFrame));
  1.1774 +  }
  1.1775 +
  1.1776 +  return Current();
  1.1777 +}
  1.1778 +
  1.1779 +void
  1.1780 +TextFrameIterator::PushBaseline(nsIFrame* aNextFrame)
  1.1781 +{
  1.1782 +  uint8_t baseline = aNextFrame->StyleSVGReset()->mDominantBaseline;
  1.1783 +  if (baseline == NS_STYLE_DOMINANT_BASELINE_AUTO) {
  1.1784 +    baseline = mBaselines.LastElement();
  1.1785 +  }
  1.1786 +  mBaselines.AppendElement(baseline);
  1.1787 +}
  1.1788 +
  1.1789 +void
  1.1790 +TextFrameIterator::PopBaseline()
  1.1791 +{
  1.1792 +  NS_ASSERTION(!mBaselines.IsEmpty(), "popped too many baselines");
  1.1793 +  mBaselines.TruncateLength(mBaselines.Length() - 1);
  1.1794 +}
  1.1795 +
  1.1796 +// -----------------------------------------------------------------------------
  1.1797 +// TextRenderedRunIterator
  1.1798 +
  1.1799 +/**
  1.1800 + * Iterator for TextRenderedRun objects for the SVGTextFrame.
  1.1801 + */
  1.1802 +class TextRenderedRunIterator
  1.1803 +{
  1.1804 +public:
  1.1805 +  /**
  1.1806 +   * Values for the aFilter argument of the constructor, to indicate which frames
  1.1807 +   * we should be limited to iterating TextRenderedRun objects for.
  1.1808 +   */
  1.1809 +  enum RenderedRunFilter {
  1.1810 +    // Iterate TextRenderedRuns for all nsTextFrames.
  1.1811 +    eAllFrames,
  1.1812 +    // Iterate only TextRenderedRuns for nsTextFrames that are
  1.1813 +    // visibility:visible.
  1.1814 +    eVisibleFrames
  1.1815 +  };
  1.1816 +
  1.1817 +  /**
  1.1818 +   * Constructs a TextRenderedRunIterator with an optional frame subtree to
  1.1819 +   * restrict iterated rendered runs to.
  1.1820 +   *
  1.1821 +   * @param aSVGTextFrame The SVGTextFrame whose rendered runs to iterate
  1.1822 +   *   through.
  1.1823 +   * @param aFilter Indicates whether to iterate rendered runs for non-visible
  1.1824 +   *   nsTextFrames.
  1.1825 +   * @param aSubtree An optional frame subtree to restrict iterated rendered
  1.1826 +   *   runs to.
  1.1827 +   */
  1.1828 +  TextRenderedRunIterator(SVGTextFrame* aSVGTextFrame,
  1.1829 +                          RenderedRunFilter aFilter = eAllFrames,
  1.1830 +                          nsIFrame* aSubtree = nullptr)
  1.1831 +    : mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
  1.1832 +      mFilter(aFilter),
  1.1833 +      mTextElementCharIndex(0),
  1.1834 +      mFrameStartTextElementCharIndex(0),
  1.1835 +      mFontSizeScaleFactor(aSVGTextFrame->mFontSizeScaleFactor),
  1.1836 +      mCurrent(First())
  1.1837 +  {
  1.1838 +  }
  1.1839 +
  1.1840 +  /**
  1.1841 +   * Constructs a TextRenderedRunIterator with a content subtree to restrict
  1.1842 +   * iterated rendered runs to.
  1.1843 +   *
  1.1844 +   * @param aSVGTextFrame The SVGTextFrame whose rendered runs to iterate
  1.1845 +   *   through.
  1.1846 +   * @param aFilter Indicates whether to iterate rendered runs for non-visible
  1.1847 +   *   nsTextFrames.
  1.1848 +   * @param aSubtree A content subtree to restrict iterated rendered runs to.
  1.1849 +   */
  1.1850 +  TextRenderedRunIterator(SVGTextFrame* aSVGTextFrame,
  1.1851 +                          RenderedRunFilter aFilter,
  1.1852 +                          nsIContent* aSubtree)
  1.1853 +    : mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
  1.1854 +      mFilter(aFilter),
  1.1855 +      mTextElementCharIndex(0),
  1.1856 +      mFrameStartTextElementCharIndex(0),
  1.1857 +      mFontSizeScaleFactor(aSVGTextFrame->mFontSizeScaleFactor),
  1.1858 +      mCurrent(First())
  1.1859 +  {
  1.1860 +  }
  1.1861 +
  1.1862 +  /**
  1.1863 +   * Returns the current TextRenderedRun.
  1.1864 +   */
  1.1865 +  TextRenderedRun Current() const
  1.1866 +  {
  1.1867 +    return mCurrent;
  1.1868 +  }
  1.1869 +
  1.1870 +  /**
  1.1871 +   * Advances to the next TextRenderedRun and returns it.
  1.1872 +   */
  1.1873 +  TextRenderedRun Next();
  1.1874 +
  1.1875 +private:
  1.1876 +  /**
  1.1877 +   * Returns the root SVGTextFrame this iterator is for.
  1.1878 +   */
  1.1879 +  SVGTextFrame* Root() const
  1.1880 +  {
  1.1881 +    return mFrameIterator.Root();
  1.1882 +  }
  1.1883 +
  1.1884 +  /**
  1.1885 +   * Advances to the first TextRenderedRun and returns it.
  1.1886 +   */
  1.1887 +  TextRenderedRun First();
  1.1888 +
  1.1889 +  /**
  1.1890 +   * The frame iterator to use.
  1.1891 +   */
  1.1892 +  TextFrameIterator mFrameIterator;
  1.1893 +
  1.1894 +  /**
  1.1895 +   * The filter indicating which TextRenderedRuns to return.
  1.1896 +   */
  1.1897 +  RenderedRunFilter mFilter;
  1.1898 +
  1.1899 +  /**
  1.1900 +   * The character index across the entire <text> element we are currently
  1.1901 +   * up to.
  1.1902 +   */
  1.1903 +  uint32_t mTextElementCharIndex;
  1.1904 +
  1.1905 +  /**
  1.1906 +   * The character index across the entire <text> for the start of the current
  1.1907 +   * frame.
  1.1908 +   */
  1.1909 +  uint32_t mFrameStartTextElementCharIndex;
  1.1910 +
  1.1911 +  /**
  1.1912 +   * The font-size scale factor we used when constructing the nsTextFrames.
  1.1913 +   */
  1.1914 +  double mFontSizeScaleFactor;
  1.1915 +
  1.1916 +  /**
  1.1917 +   * The current TextRenderedRun.
  1.1918 +   */
  1.1919 +  TextRenderedRun mCurrent;
  1.1920 +};
  1.1921 +
  1.1922 +TextRenderedRun
  1.1923 +TextRenderedRunIterator::Next()
  1.1924 +{
  1.1925 +  if (!mFrameIterator.Current()) {
  1.1926 +    // If there are no more frames, then there are no more rendered runs to
  1.1927 +    // return.
  1.1928 +    mCurrent = TextRenderedRun();
  1.1929 +    return mCurrent;
  1.1930 +  }
  1.1931 +
  1.1932 +  // The values we will use to initialize the TextRenderedRun with.
  1.1933 +  nsTextFrame* frame;
  1.1934 +  gfxPoint pt;
  1.1935 +  double rotate;
  1.1936 +  nscoord baseline;
  1.1937 +  uint32_t offset, length;
  1.1938 +  uint32_t charIndex;
  1.1939 +
  1.1940 +  // We loop, because we want to skip over rendered runs that either aren't
  1.1941 +  // within our subtree of interest, because they don't match the filter,
  1.1942 +  // or because they are hidden due to having fallen off the end of a
  1.1943 +  // <textPath>.
  1.1944 +  for (;;) {
  1.1945 +    if (mFrameIterator.IsAfterSubtree()) {
  1.1946 +      mCurrent = TextRenderedRun();
  1.1947 +      return mCurrent;
  1.1948 +    }
  1.1949 +
  1.1950 +    frame = mFrameIterator.Current();
  1.1951 +
  1.1952 +    charIndex = mTextElementCharIndex;
  1.1953 +
  1.1954 +    // Find the end of the rendered run, by looking through the
  1.1955 +    // SVGTextFrame's positions array until we find one that is recorded
  1.1956 +    // as a run boundary.
  1.1957 +    uint32_t runStart, runEnd;  // XXX Replace runStart with mTextElementCharIndex.
  1.1958 +    runStart = mTextElementCharIndex;
  1.1959 +    runEnd = runStart + 1;
  1.1960 +    while (runEnd < Root()->mPositions.Length() &&
  1.1961 +           !Root()->mPositions[runEnd].mRunBoundary) {
  1.1962 +      runEnd++;
  1.1963 +    }
  1.1964 +
  1.1965 +    // Convert the global run start/end indexes into an offset/length into the
  1.1966 +    // current frame's nsTextNode.
  1.1967 +    offset = frame->GetContentOffset() + runStart -
  1.1968 +             mFrameStartTextElementCharIndex;
  1.1969 +    length = runEnd - runStart;
  1.1970 +
  1.1971 +    // If the end of the frame's content comes before the run boundary we found
  1.1972 +    // in SVGTextFrame's position array, we need to shorten the rendered run.
  1.1973 +    uint32_t contentEnd = frame->GetContentEnd();
  1.1974 +    if (offset + length > contentEnd) {
  1.1975 +      length = contentEnd - offset;
  1.1976 +    }
  1.1977 +
  1.1978 +    NS_ASSERTION(offset >= uint32_t(frame->GetContentOffset()), "invalid offset");
  1.1979 +    NS_ASSERTION(offset + length <= contentEnd, "invalid offset or length");
  1.1980 +
  1.1981 +    // Get the frame's baseline position.
  1.1982 +    frame->EnsureTextRun(nsTextFrame::eInflated);
  1.1983 +    baseline = GetBaselinePosition(frame,
  1.1984 +                                   frame->GetTextRun(nsTextFrame::eInflated),
  1.1985 +                                   mFrameIterator.DominantBaseline());
  1.1986 +
  1.1987 +    // Trim the offset/length to remove any leading/trailing white space.
  1.1988 +    uint32_t untrimmedOffset = offset;
  1.1989 +    uint32_t untrimmedLength = length;
  1.1990 +    nsTextFrame::TrimmedOffsets trimmedOffsets =
  1.1991 +      frame->GetTrimmedOffsets(frame->GetContent()->GetText(), true);
  1.1992 +    TrimOffsets(offset, length, trimmedOffsets);
  1.1993 +    charIndex += offset - untrimmedOffset;
  1.1994 +
  1.1995 +    // Get the position and rotation of the character that begins this
  1.1996 +    // rendered run.
  1.1997 +    pt = Root()->mPositions[charIndex].mPosition;
  1.1998 +    rotate = Root()->mPositions[charIndex].mAngle;
  1.1999 +
  1.2000 +    // Determine if we should skip this rendered run.
  1.2001 +    bool skip = !mFrameIterator.IsWithinSubtree() ||
  1.2002 +                Root()->mPositions[mTextElementCharIndex].mHidden;
  1.2003 +    if (mFilter == eVisibleFrames) {
  1.2004 +      skip = skip || !frame->StyleVisibility()->IsVisible();
  1.2005 +    }
  1.2006 +
  1.2007 +    // Update our global character index to move past the characters
  1.2008 +    // corresponding to this rendered run.
  1.2009 +    mTextElementCharIndex += untrimmedLength;
  1.2010 +
  1.2011 +    // If we have moved past the end of the current frame's content, we need to
  1.2012 +    // advance to the next frame.
  1.2013 +    if (offset + untrimmedLength >= contentEnd) {
  1.2014 +      mFrameIterator.Next();
  1.2015 +      mTextElementCharIndex += mFrameIterator.UndisplayedCharacters();
  1.2016 +      mFrameStartTextElementCharIndex = mTextElementCharIndex;
  1.2017 +    }
  1.2018 +
  1.2019 +    if (!mFrameIterator.Current()) {
  1.2020 +      if (skip) {
  1.2021 +        // That was the last frame, and we skipped this rendered run.  So we
  1.2022 +        // have no rendered run to return.
  1.2023 +        mCurrent = TextRenderedRun();
  1.2024 +        return mCurrent;
  1.2025 +      }
  1.2026 +      break;
  1.2027 +    }
  1.2028 +
  1.2029 +    if (length && !skip) {
  1.2030 +      // Only return a rendered run if it didn't get collapsed away entirely
  1.2031 +      // (due to it being all white space) and if we don't want to skip it.
  1.2032 +      break;
  1.2033 +    }
  1.2034 +  }
  1.2035 +
  1.2036 +  mCurrent = TextRenderedRun(frame, pt, Root()->mLengthAdjustScaleFactor,
  1.2037 +                             rotate, mFontSizeScaleFactor, baseline,
  1.2038 +                             offset, length, charIndex);
  1.2039 +  return mCurrent;
  1.2040 +}
  1.2041 +
  1.2042 +TextRenderedRun
  1.2043 +TextRenderedRunIterator::First()
  1.2044 +{
  1.2045 +  if (!mFrameIterator.Current()) {
  1.2046 +    return TextRenderedRun();
  1.2047 +  }
  1.2048 +
  1.2049 +  if (Root()->mPositions.IsEmpty()) {
  1.2050 +    mFrameIterator.Close();
  1.2051 +    return TextRenderedRun();
  1.2052 +  }
  1.2053 +
  1.2054 +  // Get the character index for the start of this rendered run, by skipping
  1.2055 +  // any undisplayed characters.
  1.2056 +  mTextElementCharIndex = mFrameIterator.UndisplayedCharacters();
  1.2057 +  mFrameStartTextElementCharIndex = mTextElementCharIndex;
  1.2058 +
  1.2059 +  return Next();
  1.2060 +}
  1.2061 +
  1.2062 +// -----------------------------------------------------------------------------
  1.2063 +// CharIterator
  1.2064 +
  1.2065 +/**
  1.2066 + * Iterator for characters within an SVGTextFrame.
  1.2067 + */
  1.2068 +class CharIterator
  1.2069 +{
  1.2070 +public:
  1.2071 +  /**
  1.2072 +   * Values for the aFilter argument of the constructor, to indicate which
  1.2073 +   * characters we should be iterating over.
  1.2074 +   */
  1.2075 +  enum CharacterFilter {
  1.2076 +    // Iterate over all original characters from the DOM that are within valid
  1.2077 +    // text content elements.
  1.2078 +    eOriginal,
  1.2079 +    // Iterate only over characters that are addressable by the positioning
  1.2080 +    // attributes x="", y="", etc.  This includes all characters after
  1.2081 +    // collapsing white space as required by the value of 'white-space'.
  1.2082 +    eAddressable,
  1.2083 +    // Iterate only over characters that are the first of clusters or ligature
  1.2084 +    // groups.
  1.2085 +    eClusterAndLigatureGroupStart,
  1.2086 +    // Iterate only over characters that are part of a cluster or ligature
  1.2087 +    // group but not the first character.
  1.2088 +    eClusterOrLigatureGroupMiddle
  1.2089 +  };
  1.2090 +
  1.2091 +  /**
  1.2092 +   * Constructs a CharIterator.
  1.2093 +   *
  1.2094 +   * @param aSVGTextFrame The SVGTextFrame whose characters to iterate
  1.2095 +   *   through.
  1.2096 +   * @param aFilter Indicates which characters to iterate over.
  1.2097 +   * @param aSubtree A content subtree to track whether the current character
  1.2098 +   *   is within.
  1.2099 +   */
  1.2100 +  CharIterator(SVGTextFrame* aSVGTextFrame,
  1.2101 +               CharacterFilter aFilter,
  1.2102 +               nsIContent* aSubtree = nullptr);
  1.2103 +
  1.2104 +  /**
  1.2105 +   * Returns whether the iterator is finished.
  1.2106 +   */
  1.2107 +  bool AtEnd() const
  1.2108 +  {
  1.2109 +    return !mFrameIterator.Current();
  1.2110 +  }
  1.2111 +
  1.2112 +  /**
  1.2113 +   * Advances to the next matching character.  Returns true if there was a
  1.2114 +   * character to advance to, and false otherwise.
  1.2115 +   */
  1.2116 +  bool Next();
  1.2117 +
  1.2118 +  /**
  1.2119 +   * Advances ahead aCount matching characters.  Returns true if there were
  1.2120 +   * enough characters to advance past, and false otherwise.
  1.2121 +   */
  1.2122 +  bool Next(uint32_t aCount);
  1.2123 +
  1.2124 +  /**
  1.2125 +   * Advances ahead up to aCount matching characters.
  1.2126 +   */
  1.2127 +  void NextWithinSubtree(uint32_t aCount);
  1.2128 +
  1.2129 +  /**
  1.2130 +   * Advances to the character with the specified index.  The index is in the
  1.2131 +   * space of original characters (i.e., all DOM characters under the <text>
  1.2132 +   * that are within valid text content elements).
  1.2133 +   */
  1.2134 +  bool AdvanceToCharacter(uint32_t aTextElementCharIndex);
  1.2135 +
  1.2136 +  /**
  1.2137 +   * Advances to the first matching character after the current nsTextFrame.
  1.2138 +   */
  1.2139 +  bool AdvancePastCurrentFrame();
  1.2140 +
  1.2141 +  /**
  1.2142 +   * Advances to the first matching character after the frames within
  1.2143 +   * the current <textPath>.
  1.2144 +   */
  1.2145 +  bool AdvancePastCurrentTextPathFrame();
  1.2146 +
  1.2147 +  /**
  1.2148 +   * Advances to the first matching character of the subtree.  Returns true
  1.2149 +   * if we successfully advance to the subtree, or if we are already within
  1.2150 +   * the subtree.  Returns false if we are past the subtree.
  1.2151 +   */
  1.2152 +  bool AdvanceToSubtree();
  1.2153 +
  1.2154 +  /**
  1.2155 +   * Returns the nsTextFrame for the current character.
  1.2156 +   */
  1.2157 +  nsTextFrame* TextFrame() const
  1.2158 +  {
  1.2159 +    return mFrameIterator.Current();
  1.2160 +  }
  1.2161 +
  1.2162 +  /**
  1.2163 +   * Returns whether the iterator is within the subtree.
  1.2164 +   */
  1.2165 +  bool IsWithinSubtree() const
  1.2166 +  {
  1.2167 +    return mFrameIterator.IsWithinSubtree();
  1.2168 +  }
  1.2169 +
  1.2170 +  /**
  1.2171 +   * Returns whether the iterator is past the subtree.
  1.2172 +   */
  1.2173 +  bool IsAfterSubtree() const
  1.2174 +  {
  1.2175 +    return mFrameIterator.IsAfterSubtree();
  1.2176 +  }
  1.2177 +
  1.2178 +  /**
  1.2179 +   * Returns whether the current character is a skipped character.
  1.2180 +   */
  1.2181 +  bool IsOriginalCharSkipped() const
  1.2182 +  {
  1.2183 +    return mSkipCharsIterator.IsOriginalCharSkipped();
  1.2184 +  }
  1.2185 +
  1.2186 +  /**
  1.2187 +   * Returns whether the current character is the start of a cluster and
  1.2188 +   * ligature group.
  1.2189 +   */
  1.2190 +  bool IsClusterAndLigatureGroupStart() const;
  1.2191 +
  1.2192 +  /**
  1.2193 +   * Returns whether the current character is trimmed away when painting,
  1.2194 +   * due to it being leading/trailing white space.
  1.2195 +   */
  1.2196 +  bool IsOriginalCharTrimmed() const;
  1.2197 +
  1.2198 +  /**
  1.2199 +   * Returns whether the current character is unaddressable from the SVG glyph
  1.2200 +   * positioning attributes.
  1.2201 +   */
  1.2202 +  bool IsOriginalCharUnaddressable() const
  1.2203 +  {
  1.2204 +    return IsOriginalCharSkipped() || IsOriginalCharTrimmed();
  1.2205 +  }
  1.2206 +
  1.2207 +  /**
  1.2208 +   * Returns the text run for the current character.
  1.2209 +   */
  1.2210 +  gfxTextRun* TextRun() const
  1.2211 +  {
  1.2212 +    return mTextRun;
  1.2213 +  }
  1.2214 +
  1.2215 +  /**
  1.2216 +   * Returns the current character index.
  1.2217 +   */
  1.2218 +  uint32_t TextElementCharIndex() const
  1.2219 +  {
  1.2220 +    return mTextElementCharIndex;
  1.2221 +  }
  1.2222 +
  1.2223 +  /**
  1.2224 +   * Returns the character index for the start of the cluster/ligature group it
  1.2225 +   * is part of.
  1.2226 +   */
  1.2227 +  uint32_t GlyphStartTextElementCharIndex() const
  1.2228 +  {
  1.2229 +    return mGlyphStartTextElementCharIndex;
  1.2230 +  }
  1.2231 +
  1.2232 +  /**
  1.2233 +   * Returns the number of undisplayed characters between the beginning of
  1.2234 +   * the glyph and the current character.
  1.2235 +   */
  1.2236 +  uint32_t GlyphUndisplayedCharacters() const
  1.2237 +  {
  1.2238 +    return mGlyphUndisplayedCharacters;
  1.2239 +  }
  1.2240 +
  1.2241 +  /**
  1.2242 +   * Gets the original character offsets within the nsTextNode for the
  1.2243 +   * cluster/ligature group the current character is a part of.
  1.2244 +   *
  1.2245 +   * @param aOriginalOffset The offset of the start of the cluster/ligature
  1.2246 +   *   group (output).
  1.2247 +   * @param aOriginalLength The length of cluster/ligature group (output).
  1.2248 +   */
  1.2249 +  void GetOriginalGlyphOffsets(uint32_t& aOriginalOffset,
  1.2250 +                               uint32_t& aOriginalLength) const;
  1.2251 +
  1.2252 +  /**
  1.2253 +   * Gets the advance, in user units, of the glyph the current character is
  1.2254 +   * part of.
  1.2255 +   *
  1.2256 +   * @param aContext The context to use for unit conversions.
  1.2257 +   */
  1.2258 +  gfxFloat GetGlyphAdvance(nsPresContext* aContext) const;
  1.2259 +
  1.2260 +  /**
  1.2261 +   * Gets the advance, in user units, of the current character.  If the
  1.2262 +   * character is a part of ligature, then the advance returned will be
  1.2263 +   * a fraction of the ligature glyph's advance.
  1.2264 +   *
  1.2265 +   * @param aContext The context to use for unit conversions.
  1.2266 +   */
  1.2267 +  gfxFloat GetAdvance(nsPresContext* aContext) const;
  1.2268 +
  1.2269 +  /**
  1.2270 +   * Gets the specified partial advance of the glyph the current character is
  1.2271 +   * part of.  The partial advance is measured from the first character
  1.2272 +   * corresponding to the glyph until the specified part length.
  1.2273 +   *
  1.2274 +   * The part length value does not include any undisplayed characters in the
  1.2275 +   * middle of the cluster/ligature group.  For example, if you have:
  1.2276 +   *
  1.2277 +   *   <text>f<tspan display="none">x</tspan>i</text>
  1.2278 +   *
  1.2279 +   * and the "f" and "i" are ligaturized, then calling GetGlyphPartialAdvance
  1.2280 +   * with aPartLength values will have the following results:
  1.2281 +   *
  1.2282 +   *   0 => 0
  1.2283 +   *   1 => adv("fi") / 2
  1.2284 +   *   2 => adv("fi")
  1.2285 +   *
  1.2286 +   * @param aPartLength The number of characters in the cluster/ligature group
  1.2287 +   *   to measure.
  1.2288 +   * @param aContext The context to use for unit conversions.
  1.2289 +   */
  1.2290 +  gfxFloat GetGlyphPartialAdvance(uint32_t aPartLength,
  1.2291 +                                  nsPresContext* aContext) const;
  1.2292 +
  1.2293 +  /**
  1.2294 +   * Returns the frame corresponding to the <textPath> that the current
  1.2295 +   * character is within.
  1.2296 +   */
  1.2297 +  nsIFrame* TextPathFrame() const
  1.2298 +  {
  1.2299 +    return mFrameIterator.TextPathFrame();
  1.2300 +  }
  1.2301 +
  1.2302 +private:
  1.2303 +  /**
  1.2304 +   * Advances to the next character without checking it against the filter.
  1.2305 +   * Returns true if there was a next character to advance to, or false
  1.2306 +   * otherwise.
  1.2307 +   */
  1.2308 +  bool NextCharacter();
  1.2309 +
  1.2310 +  /**
  1.2311 +   * Returns whether the current character matches the filter.
  1.2312 +   */
  1.2313 +  bool MatchesFilter() const;
  1.2314 +
  1.2315 +  /**
  1.2316 +   * If this is the start of a glyph, record it.
  1.2317 +   */
  1.2318 +  void UpdateGlyphStartTextElementCharIndex() {
  1.2319 +    if (!IsOriginalCharSkipped() && IsClusterAndLigatureGroupStart()) {
  1.2320 +      mGlyphStartTextElementCharIndex = mTextElementCharIndex;
  1.2321 +      mGlyphUndisplayedCharacters = 0;
  1.2322 +    }
  1.2323 +  }
  1.2324 +
  1.2325 +  /**
  1.2326 +   * The filter to use.
  1.2327 +   */
  1.2328 +  CharacterFilter mFilter;
  1.2329 +
  1.2330 +  /**
  1.2331 +   * The iterator for text frames.
  1.2332 +   */
  1.2333 +  TextFrameIterator mFrameIterator;
  1.2334 +
  1.2335 +  /**
  1.2336 +   * A gfxSkipCharsIterator for the text frame the current character is
  1.2337 +   * a part of.
  1.2338 +   */
  1.2339 +  gfxSkipCharsIterator mSkipCharsIterator;
  1.2340 +
  1.2341 +  // Cache for information computed by IsOriginalCharTrimmed.
  1.2342 +  mutable nsTextFrame* mFrameForTrimCheck;
  1.2343 +  mutable uint32_t mTrimmedOffset;
  1.2344 +  mutable uint32_t mTrimmedLength;
  1.2345 +
  1.2346 +  /**
  1.2347 +   * The text run the current character is a part of.
  1.2348 +   */
  1.2349 +  gfxTextRun* mTextRun;
  1.2350 +
  1.2351 +  /**
  1.2352 +   * The current character's index.
  1.2353 +   */
  1.2354 +  uint32_t mTextElementCharIndex;
  1.2355 +
  1.2356 +  /**
  1.2357 +   * The index of the character that starts the cluster/ligature group the
  1.2358 +   * current character is a part of.
  1.2359 +   */
  1.2360 +  uint32_t mGlyphStartTextElementCharIndex;
  1.2361 +
  1.2362 +  /**
  1.2363 +   * If we are iterating in mode eClusterOrLigatureGroupMiddle, then
  1.2364 +   * this tracks how many undisplayed characters were encountered
  1.2365 +   * between the start of this glyph (at mGlyphStartTextElementCharIndex)
  1.2366 +   * and the current character (at mTextElementCharIndex).
  1.2367 +   */
  1.2368 +  uint32_t mGlyphUndisplayedCharacters;
  1.2369 +
  1.2370 +  /**
  1.2371 +   * The scale factor to apply to glyph advances returned by
  1.2372 +   * GetGlyphAdvance etc. to take into account textLength="".
  1.2373 +   */
  1.2374 +  float mLengthAdjustScaleFactor;
  1.2375 +};
  1.2376 +
  1.2377 +CharIterator::CharIterator(SVGTextFrame* aSVGTextFrame,
  1.2378 +                           CharIterator::CharacterFilter aFilter,
  1.2379 +                           nsIContent* aSubtree)
  1.2380 +  : mFilter(aFilter),
  1.2381 +    mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
  1.2382 +    mFrameForTrimCheck(nullptr),
  1.2383 +    mTrimmedOffset(0),
  1.2384 +    mTrimmedLength(0),
  1.2385 +    mTextElementCharIndex(0),
  1.2386 +    mGlyphStartTextElementCharIndex(0),
  1.2387 +    mLengthAdjustScaleFactor(aSVGTextFrame->mLengthAdjustScaleFactor)
  1.2388 +{
  1.2389 +  if (!AtEnd()) {
  1.2390 +    mSkipCharsIterator = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  1.2391 +    mTextRun = TextFrame()->GetTextRun(nsTextFrame::eInflated);
  1.2392 +    mTextElementCharIndex = mFrameIterator.UndisplayedCharacters();
  1.2393 +    UpdateGlyphStartTextElementCharIndex();
  1.2394 +    if (!MatchesFilter()) {
  1.2395 +      Next();
  1.2396 +    }
  1.2397 +  }
  1.2398 +}
  1.2399 +
  1.2400 +bool
  1.2401 +CharIterator::Next()
  1.2402 +{
  1.2403 +  while (NextCharacter()) {
  1.2404 +    if (MatchesFilter()) {
  1.2405 +      return true;
  1.2406 +    }
  1.2407 +  }
  1.2408 +  return false;
  1.2409 +}
  1.2410 +
  1.2411 +bool
  1.2412 +CharIterator::Next(uint32_t aCount)
  1.2413 +{
  1.2414 +  if (aCount == 0 && AtEnd()) {
  1.2415 +    return false;
  1.2416 +  }
  1.2417 +  while (aCount) {
  1.2418 +    if (!Next()) {
  1.2419 +      return false;
  1.2420 +    }
  1.2421 +    aCount--;
  1.2422 +  }
  1.2423 +  return true;
  1.2424 +}
  1.2425 +
  1.2426 +void
  1.2427 +CharIterator::NextWithinSubtree(uint32_t aCount)
  1.2428 +{
  1.2429 +  while (IsWithinSubtree() && aCount) {
  1.2430 +    --aCount;
  1.2431 +    if (!Next()) {
  1.2432 +      return;
  1.2433 +    }
  1.2434 +  }
  1.2435 +}
  1.2436 +
  1.2437 +bool
  1.2438 +CharIterator::AdvanceToCharacter(uint32_t aTextElementCharIndex)
  1.2439 +{
  1.2440 +  while (mTextElementCharIndex < aTextElementCharIndex) {
  1.2441 +    if (!Next()) {
  1.2442 +      return false;
  1.2443 +    }
  1.2444 +  }
  1.2445 +  return true;
  1.2446 +}
  1.2447 +
  1.2448 +bool
  1.2449 +CharIterator::AdvancePastCurrentFrame()
  1.2450 +{
  1.2451 +  // XXX Can do this better than one character at a time if it matters.
  1.2452 +  nsTextFrame* currentFrame = TextFrame();
  1.2453 +  do {
  1.2454 +    if (!Next()) {
  1.2455 +      return false;
  1.2456 +    }
  1.2457 +  } while (TextFrame() == currentFrame);
  1.2458 +  return true;
  1.2459 +}
  1.2460 +
  1.2461 +bool
  1.2462 +CharIterator::AdvancePastCurrentTextPathFrame()
  1.2463 +{
  1.2464 +  nsIFrame* currentTextPathFrame = TextPathFrame();
  1.2465 +  NS_ASSERTION(currentTextPathFrame,
  1.2466 +               "expected AdvancePastCurrentTextPathFrame to be called only "
  1.2467 +               "within a text path frame");
  1.2468 +  do {
  1.2469 +    if (!AdvancePastCurrentFrame()) {
  1.2470 +      return false;
  1.2471 +    }
  1.2472 +  } while (TextPathFrame() == currentTextPathFrame);
  1.2473 +  return true;
  1.2474 +}
  1.2475 +
  1.2476 +bool
  1.2477 +CharIterator::AdvanceToSubtree()
  1.2478 +{
  1.2479 +  while (!IsWithinSubtree()) {
  1.2480 +    if (IsAfterSubtree()) {
  1.2481 +      return false;
  1.2482 +    }
  1.2483 +    if (!AdvancePastCurrentFrame()) {
  1.2484 +      return false;
  1.2485 +    }
  1.2486 +  }
  1.2487 +  return true;
  1.2488 +}
  1.2489 +
  1.2490 +bool
  1.2491 +CharIterator::IsClusterAndLigatureGroupStart() const
  1.2492 +{
  1.2493 +  return mTextRun->IsLigatureGroupStart(mSkipCharsIterator.GetSkippedOffset()) &&
  1.2494 +         mTextRun->IsClusterStart(mSkipCharsIterator.GetSkippedOffset());
  1.2495 +}
  1.2496 +
  1.2497 +bool
  1.2498 +CharIterator::IsOriginalCharTrimmed() const
  1.2499 +{
  1.2500 +  if (mFrameForTrimCheck != TextFrame()) {
  1.2501 +    // Since we do a lot of trim checking, we cache the trimmed offsets and
  1.2502 +    // lengths while we are in the same frame.
  1.2503 +    mFrameForTrimCheck = TextFrame();
  1.2504 +    uint32_t offset = mFrameForTrimCheck->GetContentOffset();
  1.2505 +    uint32_t length = mFrameForTrimCheck->GetContentLength();
  1.2506 +    nsIContent* content = mFrameForTrimCheck->GetContent();
  1.2507 +    nsTextFrame::TrimmedOffsets trim =
  1.2508 +      mFrameForTrimCheck->GetTrimmedOffsets(content->GetText(), true);
  1.2509 +    TrimOffsets(offset, length, trim);
  1.2510 +    mTrimmedOffset = offset;
  1.2511 +    mTrimmedLength = length;
  1.2512 +  }
  1.2513 +
  1.2514 +  // A character is trimmed if it is outside the mTrimmedOffset/mTrimmedLength
  1.2515 +  // range and it is not a significant newline character.
  1.2516 +  uint32_t index = mSkipCharsIterator.GetOriginalOffset();
  1.2517 +  return !((index >= mTrimmedOffset &&
  1.2518 +            index < mTrimmedOffset + mTrimmedLength) ||
  1.2519 +           (index >= mTrimmedOffset + mTrimmedLength &&
  1.2520 +            mFrameForTrimCheck->StyleText()->NewlineIsSignificant() &&
  1.2521 +            mFrameForTrimCheck->GetContent()->GetText()->CharAt(index) == '\n'));
  1.2522 +}
  1.2523 +
  1.2524 +void
  1.2525 +CharIterator::GetOriginalGlyphOffsets(uint32_t& aOriginalOffset,
  1.2526 +                                      uint32_t& aOriginalLength) const
  1.2527 +{
  1.2528 +  gfxSkipCharsIterator it = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  1.2529 +  it.SetOriginalOffset(mSkipCharsIterator.GetOriginalOffset() -
  1.2530 +                         (mTextElementCharIndex -
  1.2531 +                          mGlyphStartTextElementCharIndex -
  1.2532 +                          mGlyphUndisplayedCharacters));
  1.2533 +
  1.2534 +  while (it.GetSkippedOffset() > 0 &&
  1.2535 +         (!mTextRun->IsClusterStart(it.GetSkippedOffset()) ||
  1.2536 +          !mTextRun->IsLigatureGroupStart(it.GetSkippedOffset()))) {
  1.2537 +    it.AdvanceSkipped(-1);
  1.2538 +  }
  1.2539 +
  1.2540 +  aOriginalOffset = it.GetOriginalOffset();
  1.2541 +
  1.2542 +  // Find the end of the cluster/ligature group.
  1.2543 +  it.SetOriginalOffset(mSkipCharsIterator.GetOriginalOffset());
  1.2544 +  do {
  1.2545 +    it.AdvanceSkipped(1);
  1.2546 +  } while (it.GetSkippedOffset() < mTextRun->GetLength() &&
  1.2547 +           (!mTextRun->IsClusterStart(it.GetSkippedOffset()) ||
  1.2548 +            !mTextRun->IsLigatureGroupStart(it.GetSkippedOffset())));
  1.2549 +
  1.2550 +  aOriginalLength = it.GetOriginalOffset() - aOriginalOffset;
  1.2551 +}
  1.2552 +
  1.2553 +gfxFloat
  1.2554 +CharIterator::GetGlyphAdvance(nsPresContext* aContext) const
  1.2555 +{
  1.2556 +  uint32_t offset, length;
  1.2557 +  GetOriginalGlyphOffsets(offset, length);
  1.2558 +
  1.2559 +  gfxSkipCharsIterator it = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  1.2560 +  ConvertOriginalToSkipped(it, offset, length);
  1.2561 +
  1.2562 +  float cssPxPerDevPx = aContext->
  1.2563 +    AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
  1.2564 +
  1.2565 +  gfxFloat advance = mTextRun->GetAdvanceWidth(offset, length, nullptr);
  1.2566 +  return aContext->AppUnitsToGfxUnits(advance) *
  1.2567 +         mLengthAdjustScaleFactor * cssPxPerDevPx;
  1.2568 +}
  1.2569 +
  1.2570 +gfxFloat
  1.2571 +CharIterator::GetAdvance(nsPresContext* aContext) const
  1.2572 +{
  1.2573 +  float cssPxPerDevPx = aContext->
  1.2574 +    AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
  1.2575 +
  1.2576 +  gfxFloat advance =
  1.2577 +    mTextRun->GetAdvanceWidth(mSkipCharsIterator.GetSkippedOffset(), 1, nullptr);
  1.2578 +  return aContext->AppUnitsToGfxUnits(advance) *
  1.2579 +         mLengthAdjustScaleFactor * cssPxPerDevPx;
  1.2580 +}
  1.2581 +
  1.2582 +gfxFloat
  1.2583 +CharIterator::GetGlyphPartialAdvance(uint32_t aPartLength,
  1.2584 +                                     nsPresContext* aContext) const
  1.2585 +{
  1.2586 +  uint32_t offset, length;
  1.2587 +  GetOriginalGlyphOffsets(offset, length);
  1.2588 +
  1.2589 +  NS_ASSERTION(aPartLength <= length, "invalid aPartLength value");
  1.2590 +  length = aPartLength;
  1.2591 +
  1.2592 +  gfxSkipCharsIterator it = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  1.2593 +  ConvertOriginalToSkipped(it, offset, length);
  1.2594 +
  1.2595 +  float cssPxPerDevPx = aContext->
  1.2596 +    AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
  1.2597 +
  1.2598 +  gfxFloat advance = mTextRun->GetAdvanceWidth(offset, length, nullptr);
  1.2599 +  return aContext->AppUnitsToGfxUnits(advance) *
  1.2600 +         mLengthAdjustScaleFactor * cssPxPerDevPx;
  1.2601 +}
  1.2602 +
  1.2603 +bool
  1.2604 +CharIterator::NextCharacter()
  1.2605 +{
  1.2606 +  if (AtEnd()) {
  1.2607 +    return false;
  1.2608 +  }
  1.2609 +
  1.2610 +  mTextElementCharIndex++;
  1.2611 +
  1.2612 +  // Advance within the current text run.
  1.2613 +  mSkipCharsIterator.AdvanceOriginal(1);
  1.2614 +  if (mSkipCharsIterator.GetOriginalOffset() < TextFrame()->GetContentEnd()) {
  1.2615 +    // We're still within the part of the text run for the current text frame.
  1.2616 +    UpdateGlyphStartTextElementCharIndex();
  1.2617 +    return true;
  1.2618 +  }
  1.2619 +
  1.2620 +  // Advance to the next frame.
  1.2621 +  mFrameIterator.Next();
  1.2622 +
  1.2623 +  // Skip any undisplayed characters.
  1.2624 +  uint32_t undisplayed = mFrameIterator.UndisplayedCharacters();
  1.2625 +  mGlyphUndisplayedCharacters += undisplayed;
  1.2626 +  mTextElementCharIndex += undisplayed;
  1.2627 +  if (!TextFrame()) {
  1.2628 +    // We're at the end.
  1.2629 +    mSkipCharsIterator = gfxSkipCharsIterator();
  1.2630 +    return false;
  1.2631 +  }
  1.2632 +
  1.2633 +  mSkipCharsIterator = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
  1.2634 +  mTextRun = TextFrame()->GetTextRun(nsTextFrame::eInflated);
  1.2635 +  UpdateGlyphStartTextElementCharIndex();
  1.2636 +  return true;
  1.2637 +}
  1.2638 +
  1.2639 +bool
  1.2640 +CharIterator::MatchesFilter() const
  1.2641 +{
  1.2642 +  if (mFilter == eOriginal) {
  1.2643 +    return true;
  1.2644 +  }
  1.2645 +
  1.2646 +  if (IsOriginalCharSkipped()) {
  1.2647 +    return false;
  1.2648 +  }
  1.2649 +
  1.2650 +  if (mFilter == eAddressable) {
  1.2651 +    return !IsOriginalCharUnaddressable();
  1.2652 +  }
  1.2653 +
  1.2654 +  return (mFilter == eClusterAndLigatureGroupStart) ==
  1.2655 +         IsClusterAndLigatureGroupStart();
  1.2656 +}
  1.2657 +
  1.2658 +// -----------------------------------------------------------------------------
  1.2659 +// nsCharClipDisplayItem
  1.2660 +
  1.2661 +/**
  1.2662 + * An nsCharClipDisplayItem that obtains its left and right clip edges from a
  1.2663 + * TextRenderedRun object.
  1.2664 + */
  1.2665 +class SVGCharClipDisplayItem : public nsCharClipDisplayItem {
  1.2666 +public:
  1.2667 +  SVGCharClipDisplayItem(const TextRenderedRun& aRun)
  1.2668 +    : nsCharClipDisplayItem(aRun.mFrame)
  1.2669 +  {
  1.2670 +    aRun.GetClipEdges(mLeftEdge, mRightEdge);
  1.2671 +  }
  1.2672 +
  1.2673 +  NS_DISPLAY_DECL_NAME("SVGText", TYPE_TEXT)
  1.2674 +};
  1.2675 +
  1.2676 +// -----------------------------------------------------------------------------
  1.2677 +// SVGTextDrawPathCallbacks
  1.2678 +
  1.2679 +/**
  1.2680 + * Text frame draw callback class that paints the text and text decoration parts
  1.2681 + * of an nsTextFrame using SVG painting properties, and selection backgrounds
  1.2682 + * and decorations as they would normally.
  1.2683 + *
  1.2684 + * An instance of this class is passed to nsTextFrame::PaintText if painting
  1.2685 + * cannot be done directly (e.g. if we are using an SVG pattern fill, stroking
  1.2686 + * the text, etc.).
  1.2687 + */
  1.2688 +class SVGTextDrawPathCallbacks : public nsTextFrame::DrawPathCallbacks
  1.2689 +{
  1.2690 +public:
  1.2691 +  /**
  1.2692 +   * Constructs an SVGTextDrawPathCallbacks.
  1.2693 +   *
  1.2694 +   * @param aContext The context to use for painting.
  1.2695 +   * @param aFrame The nsTextFrame to paint.
  1.2696 +   * @param aCanvasTM The transformation matrix to set when painting; this
  1.2697 +   *   should be the FOR_OUTERSVG_TM canvas TM of the text, so that
  1.2698 +   *   paint servers are painted correctly.
  1.2699 +   * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
  1.2700 +   */
  1.2701 +  SVGTextDrawPathCallbacks(nsRenderingContext* aContext,
  1.2702 +                           nsTextFrame* aFrame,
  1.2703 +                           const gfxMatrix& aCanvasTM,
  1.2704 +                           bool aShouldPaintSVGGlyphs)
  1.2705 +    : DrawPathCallbacks(aShouldPaintSVGGlyphs),
  1.2706 +      gfx(aContext->ThebesContext()),
  1.2707 +      mRenderMode(SVGAutoRenderState::GetRenderMode(aContext)),
  1.2708 +      mFrame(aFrame),
  1.2709 +      mCanvasTM(aCanvasTM)
  1.2710 +  {
  1.2711 +  }
  1.2712 +
  1.2713 +  void NotifyBeforeText(nscolor aColor) MOZ_OVERRIDE;
  1.2714 +  void NotifyGlyphPathEmitted() MOZ_OVERRIDE;
  1.2715 +  void NotifyBeforeSVGGlyphPainted() MOZ_OVERRIDE;
  1.2716 +  void NotifyAfterSVGGlyphPainted() MOZ_OVERRIDE;
  1.2717 +  void NotifyAfterText() MOZ_OVERRIDE;
  1.2718 +  void NotifyBeforeSelectionBackground(nscolor aColor) MOZ_OVERRIDE;
  1.2719 +  void NotifySelectionBackgroundPathEmitted() MOZ_OVERRIDE;
  1.2720 +  void NotifyBeforeDecorationLine(nscolor aColor) MOZ_OVERRIDE;
  1.2721 +  void NotifyDecorationLinePathEmitted() MOZ_OVERRIDE;
  1.2722 +  void NotifyBeforeSelectionDecorationLine(nscolor aColor) MOZ_OVERRIDE;
  1.2723 +  void NotifySelectionDecorationLinePathEmitted() MOZ_OVERRIDE;
  1.2724 +
  1.2725 +private:
  1.2726 +  void FillWithOpacity();
  1.2727 +
  1.2728 +  void SetupContext();
  1.2729 +
  1.2730 +  /**
  1.2731 +   * Paints a piece of text geometry.  This is called when glyphs
  1.2732 +   * or text decorations have been emitted to the gfxContext.
  1.2733 +   */
  1.2734 +  void HandleTextGeometry();
  1.2735 +
  1.2736 +  /**
  1.2737 +   * Sets the gfxContext paint to the appropriate color or pattern
  1.2738 +   * for filling text geometry.
  1.2739 +   */
  1.2740 +  bool SetFillColor();
  1.2741 +
  1.2742 +  /**
  1.2743 +   * Fills and strokes a piece of text geometry, using group opacity
  1.2744 +   * if the selection style requires it.
  1.2745 +   */
  1.2746 +  void FillAndStrokeGeometry();
  1.2747 +
  1.2748 +  /**
  1.2749 +   * Fills a piece of text geometry.
  1.2750 +   */
  1.2751 +  void FillGeometry();
  1.2752 +
  1.2753 +  /**
  1.2754 +   * Strokes a piece of text geometry.
  1.2755 +   */
  1.2756 +  void StrokeGeometry();
  1.2757 +
  1.2758 +  gfxContext* gfx;
  1.2759 +  uint16_t mRenderMode;
  1.2760 +  nsTextFrame* mFrame;
  1.2761 +  const gfxMatrix& mCanvasTM;
  1.2762 +
  1.2763 +  /**
  1.2764 +   * The color that we were last told from one of the path callback functions.
  1.2765 +   * This color can be the special NS_SAME_AS_FOREGROUND_COLOR,
  1.2766 +   * NS_40PERCENT_FOREGROUND_COLOR and NS_TRANSPARENT colors when we are
  1.2767 +   * painting selections or IME decorations.
  1.2768 +   */
  1.2769 +  nscolor mColor;
  1.2770 +};
  1.2771 +
  1.2772 +void
  1.2773 +SVGTextDrawPathCallbacks::NotifyBeforeText(nscolor aColor)
  1.2774 +{
  1.2775 +  mColor = aColor;
  1.2776 +  SetupContext();
  1.2777 +  gfx->NewPath();
  1.2778 +}
  1.2779 +
  1.2780 +void
  1.2781 +SVGTextDrawPathCallbacks::NotifyGlyphPathEmitted()
  1.2782 +{
  1.2783 +  HandleTextGeometry();
  1.2784 +  gfx->NewPath();
  1.2785 +}
  1.2786 +
  1.2787 +void
  1.2788 +SVGTextDrawPathCallbacks::NotifyBeforeSVGGlyphPainted()
  1.2789 +{
  1.2790 +  gfx->Save();
  1.2791 +}
  1.2792 +
  1.2793 +void
  1.2794 +SVGTextDrawPathCallbacks::NotifyAfterSVGGlyphPainted()
  1.2795 +{
  1.2796 +  gfx->Restore();
  1.2797 +  gfx->NewPath();
  1.2798 +}
  1.2799 +
  1.2800 +void
  1.2801 +SVGTextDrawPathCallbacks::NotifyAfterText()
  1.2802 +{
  1.2803 +  gfx->Restore();
  1.2804 +}
  1.2805 +
  1.2806 +void
  1.2807 +SVGTextDrawPathCallbacks::NotifyBeforeSelectionBackground(nscolor aColor)
  1.2808 +{
  1.2809 +  if (mRenderMode != SVGAutoRenderState::NORMAL) {
  1.2810 +    // Don't paint selection backgrounds when in a clip path.
  1.2811 +    return;
  1.2812 +  }
  1.2813 +
  1.2814 +  mColor = aColor;
  1.2815 +  gfx->Save();
  1.2816 +}
  1.2817 +
  1.2818 +void
  1.2819 +SVGTextDrawPathCallbacks::NotifySelectionBackgroundPathEmitted()
  1.2820 +{
  1.2821 +  if (mRenderMode != SVGAutoRenderState::NORMAL) {
  1.2822 +    // Don't paint selection backgrounds when in a clip path.
  1.2823 +    return;
  1.2824 +  }
  1.2825 +
  1.2826 +  if (SetFillColor()) {
  1.2827 +    FillWithOpacity();
  1.2828 +  }
  1.2829 +  gfx->Restore();
  1.2830 +}
  1.2831 +
  1.2832 +void
  1.2833 +SVGTextDrawPathCallbacks::NotifyBeforeDecorationLine(nscolor aColor)
  1.2834 +{
  1.2835 +  mColor = aColor;
  1.2836 +  SetupContext();
  1.2837 +}
  1.2838 +
  1.2839 +void
  1.2840 +SVGTextDrawPathCallbacks::NotifyDecorationLinePathEmitted()
  1.2841 +{
  1.2842 +  HandleTextGeometry();
  1.2843 +  gfx->NewPath();
  1.2844 +  gfx->Restore();
  1.2845 +}
  1.2846 +
  1.2847 +void
  1.2848 +SVGTextDrawPathCallbacks::NotifyBeforeSelectionDecorationLine(nscolor aColor)
  1.2849 +{
  1.2850 +  if (mRenderMode != SVGAutoRenderState::NORMAL) {
  1.2851 +    // Don't paint selection decorations when in a clip path.
  1.2852 +    return;
  1.2853 +  }
  1.2854 +
  1.2855 +  mColor = aColor;
  1.2856 +  gfx->Save();
  1.2857 +}
  1.2858 +
  1.2859 +void
  1.2860 +SVGTextDrawPathCallbacks::NotifySelectionDecorationLinePathEmitted()
  1.2861 +{
  1.2862 +  if (mRenderMode != SVGAutoRenderState::NORMAL) {
  1.2863 +    // Don't paint selection decorations when in a clip path.
  1.2864 +    return;
  1.2865 +  }
  1.2866 +
  1.2867 +  FillAndStrokeGeometry();
  1.2868 +  gfx->Restore();
  1.2869 +}
  1.2870 +
  1.2871 +void
  1.2872 +SVGTextDrawPathCallbacks::FillWithOpacity()
  1.2873 +{
  1.2874 +  gfx->FillWithOpacity(mColor == NS_40PERCENT_FOREGROUND_COLOR ? 0.4 : 1.0);
  1.2875 +}
  1.2876 +
  1.2877 +void
  1.2878 +SVGTextDrawPathCallbacks::SetupContext()
  1.2879 +{
  1.2880 +  gfx->Save();
  1.2881 +
  1.2882 +  // XXX This is copied from nsSVGGlyphFrame::Render, but cairo doesn't actually
  1.2883 +  // seem to do anything with the antialias mode.  So we can perhaps remove it,
  1.2884 +  // or make SetAntialiasMode set cairo text antialiasing too.
  1.2885 +  switch (mFrame->StyleSVG()->mTextRendering) {
  1.2886 +  case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
  1.2887 +    gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
  1.2888 +    break;
  1.2889 +  default:
  1.2890 +    gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
  1.2891 +    break;
  1.2892 +  }
  1.2893 +}
  1.2894 +
  1.2895 +void
  1.2896 +SVGTextDrawPathCallbacks::HandleTextGeometry()
  1.2897 +{
  1.2898 +  if (mRenderMode != SVGAutoRenderState::NORMAL) {
  1.2899 +    // We're in a clip path.
  1.2900 +    if (mRenderMode == SVGAutoRenderState::CLIP_MASK) {
  1.2901 +      gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
  1.2902 +      gfx->Fill();
  1.2903 +    }
  1.2904 +  } else {
  1.2905 +    // Normal painting.
  1.2906 +    gfxContextMatrixAutoSaveRestore saveMatrix(gfx);
  1.2907 +    gfx->SetMatrix(mCanvasTM);
  1.2908 +
  1.2909 +    FillAndStrokeGeometry();
  1.2910 +  }
  1.2911 +}
  1.2912 +
  1.2913 +bool
  1.2914 +SVGTextDrawPathCallbacks::SetFillColor()
  1.2915 +{
  1.2916 +  if (mColor == NS_SAME_AS_FOREGROUND_COLOR ||
  1.2917 +      mColor == NS_40PERCENT_FOREGROUND_COLOR) {
  1.2918 +    return nsSVGUtils::SetupCairoFillPaint(mFrame, gfx);
  1.2919 +  }
  1.2920 +
  1.2921 +  if (mColor == NS_TRANSPARENT) {
  1.2922 +    return false;
  1.2923 +  }
  1.2924 +
  1.2925 +  gfx->SetColor(gfxRGBA(mColor));
  1.2926 +  return true;
  1.2927 +}
  1.2928 +
  1.2929 +void
  1.2930 +SVGTextDrawPathCallbacks::FillAndStrokeGeometry()
  1.2931 +{
  1.2932 +  bool pushedGroup = false;
  1.2933 +  if (mColor == NS_40PERCENT_FOREGROUND_COLOR) {
  1.2934 +    pushedGroup = true;
  1.2935 +    gfx->PushGroup(gfxContentType::COLOR_ALPHA);
  1.2936 +  }
  1.2937 +
  1.2938 +  uint32_t paintOrder = mFrame->StyleSVG()->mPaintOrder;
  1.2939 +  if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
  1.2940 +    FillGeometry();
  1.2941 +    StrokeGeometry();
  1.2942 +  } else {
  1.2943 +    while (paintOrder) {
  1.2944 +      uint32_t component =
  1.2945 +        paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
  1.2946 +      switch (component) {
  1.2947 +        case NS_STYLE_PAINT_ORDER_FILL:
  1.2948 +          FillGeometry();
  1.2949 +          break;
  1.2950 +        case NS_STYLE_PAINT_ORDER_STROKE:
  1.2951 +          StrokeGeometry();
  1.2952 +          break;
  1.2953 +      }
  1.2954 +      paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
  1.2955 +    }
  1.2956 +  }
  1.2957 +
  1.2958 +  if (pushedGroup) {
  1.2959 +    gfx->PopGroupToSource();
  1.2960 +    gfx->Paint(0.4);
  1.2961 +  }
  1.2962 +}
  1.2963 +
  1.2964 +void
  1.2965 +SVGTextDrawPathCallbacks::FillGeometry()
  1.2966 +{
  1.2967 +  if (SetFillColor()) {
  1.2968 +    gfx->Fill();
  1.2969 +  }
  1.2970 +}
  1.2971 +
  1.2972 +void
  1.2973 +SVGTextDrawPathCallbacks::StrokeGeometry()
  1.2974 +{
  1.2975 +  if (mColor == NS_SAME_AS_FOREGROUND_COLOR ||
  1.2976 +      mColor == NS_40PERCENT_FOREGROUND_COLOR) {
  1.2977 +    // Don't paint the stroke when we are filling with a selection color.
  1.2978 +    if (nsSVGUtils::SetupCairoStroke(mFrame, gfx)) {
  1.2979 +      gfx->Stroke();
  1.2980 +    }
  1.2981 +  }
  1.2982 +}
  1.2983 +
  1.2984 +//----------------------------------------------------------------------
  1.2985 +// SVGTextContextPaint methods:
  1.2986 +
  1.2987 +already_AddRefed<gfxPattern>
  1.2988 +SVGTextContextPaint::GetFillPattern(float aOpacity,
  1.2989 +                                    const gfxMatrix& aCTM)
  1.2990 +{
  1.2991 +  return mFillPaint.GetPattern(aOpacity, &nsStyleSVG::mFill, aCTM);
  1.2992 +}
  1.2993 +
  1.2994 +already_AddRefed<gfxPattern>
  1.2995 +SVGTextContextPaint::GetStrokePattern(float aOpacity,
  1.2996 +                                      const gfxMatrix& aCTM)
  1.2997 +{
  1.2998 +  return mStrokePaint.GetPattern(aOpacity, &nsStyleSVG::mStroke, aCTM);
  1.2999 +}
  1.3000 +
  1.3001 +already_AddRefed<gfxPattern>
  1.3002 +SVGTextContextPaint::Paint::GetPattern(float aOpacity,
  1.3003 +                                       nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
  1.3004 +                                       const gfxMatrix& aCTM)
  1.3005 +{
  1.3006 +  nsRefPtr<gfxPattern> pattern;
  1.3007 +  if (mPatternCache.Get(aOpacity, getter_AddRefs(pattern))) {
  1.3008 +    // Set the pattern matrix just in case it was messed with by a previous
  1.3009 +    // caller. We should get the same matrix each time a pattern is constructed
  1.3010 +    // so this should be fine.
  1.3011 +    pattern->SetMatrix(aCTM * mPatternMatrix);
  1.3012 +    return pattern.forget();
  1.3013 +  }
  1.3014 +
  1.3015 +  switch (mPaintType) {
  1.3016 +  case eStyleSVGPaintType_None:
  1.3017 +    pattern = new gfxPattern(gfxRGBA(0.0f, 0.0f, 0.0f, 0.0f));
  1.3018 +    mPatternMatrix = gfxMatrix();
  1.3019 +    break;
  1.3020 +  case eStyleSVGPaintType_Color:
  1.3021 +    pattern = new gfxPattern(gfxRGBA(NS_GET_R(mPaintDefinition.mColor) / 255.0,
  1.3022 +                                     NS_GET_G(mPaintDefinition.mColor) / 255.0,
  1.3023 +                                     NS_GET_B(mPaintDefinition.mColor) / 255.0,
  1.3024 +                                     NS_GET_A(mPaintDefinition.mColor) / 255.0 * aOpacity));
  1.3025 +    mPatternMatrix = gfxMatrix();
  1.3026 +    break;
  1.3027 +  case eStyleSVGPaintType_Server:
  1.3028 +    pattern = mPaintDefinition.mPaintServerFrame->GetPaintServerPattern(mFrame,
  1.3029 +                                                                        mContextMatrix,
  1.3030 +                                                                        aFillOrStroke,
  1.3031 +                                                                        aOpacity);
  1.3032 +    {
  1.3033 +      // m maps original-user-space to pattern space
  1.3034 +      gfxMatrix m = pattern->GetMatrix();
  1.3035 +      gfxMatrix deviceToOriginalUserSpace = mContextMatrix;
  1.3036 +      deviceToOriginalUserSpace.Invert();
  1.3037 +      // mPatternMatrix maps device space to pattern space via original user space
  1.3038 +      mPatternMatrix = deviceToOriginalUserSpace * m;
  1.3039 +    }
  1.3040 +    pattern->SetMatrix(aCTM * mPatternMatrix);
  1.3041 +    break;
  1.3042 +  case eStyleSVGPaintType_ContextFill:
  1.3043 +    pattern = mPaintDefinition.mContextPaint->GetFillPattern(aOpacity, aCTM);
  1.3044 +    // Don't cache this. mContextPaint will have cached it anyway. If we
  1.3045 +    // cache it, we'll have to compute mPatternMatrix, which is annoying.
  1.3046 +    return pattern.forget();
  1.3047 +  case eStyleSVGPaintType_ContextStroke:
  1.3048 +    pattern = mPaintDefinition.mContextPaint->GetStrokePattern(aOpacity, aCTM);
  1.3049 +    // Don't cache this. mContextPaint will have cached it anyway. If we
  1.3050 +    // cache it, we'll have to compute mPatternMatrix, which is annoying.
  1.3051 +    return pattern.forget();
  1.3052 +  default:
  1.3053 +    MOZ_ASSERT(false, "invalid paint type");
  1.3054 +    return nullptr;
  1.3055 +  }
  1.3056 +
  1.3057 +  mPatternCache.Put(aOpacity, pattern);
  1.3058 +  return pattern.forget();
  1.3059 +}
  1.3060 +
  1.3061 +} // namespace mozilla
  1.3062 +
  1.3063 +
  1.3064 +// ============================================================================
  1.3065 +// SVGTextFrame
  1.3066 +
  1.3067 +// ----------------------------------------------------------------------------
  1.3068 +// Display list item
  1.3069 +
  1.3070 +class nsDisplaySVGText : public nsDisplayItem {
  1.3071 +public:
  1.3072 +  nsDisplaySVGText(nsDisplayListBuilder* aBuilder,
  1.3073 +                   SVGTextFrame* aFrame)
  1.3074 +    : nsDisplayItem(aBuilder, aFrame),
  1.3075 +      mDisableSubpixelAA(false)
  1.3076 +  {
  1.3077 +    MOZ_COUNT_CTOR(nsDisplaySVGText);
  1.3078 +    NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
  1.3079 +  }
  1.3080 +#ifdef NS_BUILD_REFCNT_LOGGING
  1.3081 +  virtual ~nsDisplaySVGText() {
  1.3082 +    MOZ_COUNT_DTOR(nsDisplaySVGText);
  1.3083 +  }
  1.3084 +#endif
  1.3085 +
  1.3086 +  NS_DISPLAY_DECL_NAME("nsDisplaySVGText", TYPE_SVG_TEXT)
  1.3087 +
  1.3088 +  virtual void DisableComponentAlpha() MOZ_OVERRIDE {
  1.3089 +    mDisableSubpixelAA = true;
  1.3090 +  }
  1.3091 +  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  1.3092 +                       HitTestState* aState,
  1.3093 +                       nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE;
  1.3094 +  virtual void Paint(nsDisplayListBuilder* aBuilder,
  1.3095 +                     nsRenderingContext* aCtx) MOZ_OVERRIDE;
  1.3096 +  virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE {
  1.3097 +    bool snap;
  1.3098 +    return GetBounds(aBuilder, &snap);
  1.3099 +  }
  1.3100 +private:
  1.3101 +  bool mDisableSubpixelAA;
  1.3102 +};
  1.3103 +
  1.3104 +void
  1.3105 +nsDisplaySVGText::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  1.3106 +                          HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
  1.3107 +{
  1.3108 +  SVGTextFrame *frame = static_cast<SVGTextFrame*>(mFrame);
  1.3109 +  nsPoint pointRelativeToReferenceFrame = aRect.Center();
  1.3110 +  // ToReferenceFrame() includes frame->GetPosition(), our user space position.
  1.3111 +  nsPoint userSpacePt = pointRelativeToReferenceFrame -
  1.3112 +                          (ToReferenceFrame() - frame->GetPosition());
  1.3113 +
  1.3114 +  nsIFrame* target = frame->GetFrameForPoint(userSpacePt);
  1.3115 +  if (target) {
  1.3116 +    aOutFrames->AppendElement(target);
  1.3117 +  }
  1.3118 +}
  1.3119 +
  1.3120 +void
  1.3121 +nsDisplaySVGText::Paint(nsDisplayListBuilder* aBuilder,
  1.3122 +                        nsRenderingContext* aCtx)
  1.3123 +{
  1.3124 +  gfxContextAutoDisableSubpixelAntialiasing
  1.3125 +    disable(aCtx->ThebesContext(), mDisableSubpixelAA);
  1.3126 +
  1.3127 +  // ToReferenceFrame includes our mRect offset, but painting takes
  1.3128 +  // account of that too. To avoid double counting, we subtract that
  1.3129 +  // here.
  1.3130 +  nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
  1.3131 +
  1.3132 +  aCtx->PushState();
  1.3133 +  aCtx->Translate(offset);
  1.3134 +  static_cast<SVGTextFrame*>(mFrame)->PaintSVG(aCtx, nullptr);
  1.3135 +  aCtx->PopState();
  1.3136 +}
  1.3137 +
  1.3138 +// ---------------------------------------------------------------------
  1.3139 +// nsQueryFrame methods
  1.3140 +
  1.3141 +NS_QUERYFRAME_HEAD(SVGTextFrame)
  1.3142 +  NS_QUERYFRAME_ENTRY(SVGTextFrame)
  1.3143 +NS_QUERYFRAME_TAIL_INHERITING(SVGTextFrameBase)
  1.3144 +
  1.3145 +// ---------------------------------------------------------------------
  1.3146 +// Implementation
  1.3147 +
  1.3148 +nsIFrame*
  1.3149 +NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  1.3150 +{
  1.3151 +  return new (aPresShell) SVGTextFrame(aContext);
  1.3152 +}
  1.3153 +
  1.3154 +NS_IMPL_FRAMEARENA_HELPERS(SVGTextFrame)
  1.3155 +
  1.3156 +// ---------------------------------------------------------------------
  1.3157 +// nsIFrame methods
  1.3158 +
  1.3159 +void
  1.3160 +SVGTextFrame::Init(nsIContent* aContent,
  1.3161 +                   nsIFrame* aParent,
  1.3162 +                   nsIFrame* aPrevInFlow)
  1.3163 +{
  1.3164 +  NS_ASSERTION(aContent->IsSVG(nsGkAtoms::text), "Content is not an SVG text");
  1.3165 +
  1.3166 +  SVGTextFrameBase::Init(aContent, aParent, aPrevInFlow);
  1.3167 +  AddStateBits((aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) |
  1.3168 +               NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_SVG_TEXT);
  1.3169 +
  1.3170 +  mMutationObserver.StartObserving(this);
  1.3171 +}
  1.3172 +
  1.3173 +void
  1.3174 +SVGTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  1.3175 +                               const nsRect& aDirtyRect,
  1.3176 +                               const nsDisplayListSet& aLists)
  1.3177 +{
  1.3178 +  if (NS_SUBTREE_DIRTY(this)) {
  1.3179 +    // We can sometimes be asked to paint before reflow happens and we
  1.3180 +    // have updated mPositions, etc.  In this case, we just avoid
  1.3181 +    // painting.
  1.3182 +    return;
  1.3183 +  }
  1.3184 +  aLists.Content()->AppendNewToTop(
  1.3185 +    new (aBuilder) nsDisplaySVGText(aBuilder, this));
  1.3186 +}
  1.3187 +
  1.3188 +nsresult
  1.3189 +SVGTextFrame::AttributeChanged(int32_t aNameSpaceID,
  1.3190 +                               nsIAtom* aAttribute,
  1.3191 +                               int32_t aModType)
  1.3192 +{
  1.3193 +  if (aNameSpaceID != kNameSpaceID_None)
  1.3194 +    return NS_OK;
  1.3195 +
  1.3196 +  if (aAttribute == nsGkAtoms::transform) {
  1.3197 +    // We don't invalidate for transform changes (the layers code does that).
  1.3198 +    // Also note that SVGTransformableElement::GetAttributeChangeHint will
  1.3199 +    // return nsChangeHint_UpdateOverflow for "transform" attribute changes
  1.3200 +    // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
  1.3201 +
  1.3202 +    if (!(mState & NS_FRAME_FIRST_REFLOW) &&
  1.3203 +        mCanvasTM && mCanvasTM->IsSingular()) {
  1.3204 +      // We won't have calculated the glyph positions correctly.
  1.3205 +      NotifyGlyphMetricsChange();
  1.3206 +    }
  1.3207 +    mCanvasTM = nullptr;
  1.3208 +  } else if (IsGlyphPositioningAttribute(aAttribute) ||
  1.3209 +             aAttribute == nsGkAtoms::textLength ||
  1.3210 +             aAttribute == nsGkAtoms::lengthAdjust) {
  1.3211 +    NotifyGlyphMetricsChange();
  1.3212 +  }
  1.3213 +
  1.3214 +  return NS_OK;
  1.3215 +}
  1.3216 +
  1.3217 +nsIAtom *
  1.3218 +SVGTextFrame::GetType() const
  1.3219 +{
  1.3220 +  return nsGkAtoms::svgTextFrame;
  1.3221 +}
  1.3222 +
  1.3223 +void
  1.3224 +SVGTextFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
  1.3225 +{
  1.3226 +  if (mState & NS_FRAME_IS_NONDISPLAY) {
  1.3227 +    // We need this DidSetStyleContext override to handle cases like this:
  1.3228 +    //
  1.3229 +    //   <defs>
  1.3230 +    //     <g>
  1.3231 +    //       <mask>
  1.3232 +    //         <text>...</text>
  1.3233 +    //       </mask>
  1.3234 +    //     </g>
  1.3235 +    //   </defs>
  1.3236 +    //
  1.3237 +    // where the <text> is non-display, and a style change occurs on the <defs>,
  1.3238 +    // the <g>, the <mask>, or the <text> itself.  If the style change happened
  1.3239 +    // on the parent of the <defs>, then in
  1.3240 +    // nsSVGDisplayContainerFrame::ReflowSVG, we would find the non-display
  1.3241 +    // <defs> container and then call ReflowSVGNonDisplayText on it.  If we do
  1.3242 +    // not actually reflow the parent of the <defs>, then without this
  1.3243 +    // DidSetStyleContext we would (a) not cause the <text>'s anonymous block
  1.3244 +    // child to be reflowed when it is next painted, and (b) not cause the
  1.3245 +    // <text> to be repainted anyway since the user of the <mask> would not
  1.3246 +    // know it needs to be repainted.
  1.3247 +    ScheduleReflowSVGNonDisplayText();
  1.3248 +  }
  1.3249 +}
  1.3250 +
  1.3251 +void
  1.3252 +SVGTextFrame::ReflowSVGNonDisplayText()
  1.3253 +{
  1.3254 +  MOZ_ASSERT(nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(this),
  1.3255 +             "only call ReflowSVGNonDisplayText when an outer SVG frame is "
  1.3256 +             "under ReflowSVG");
  1.3257 +  MOZ_ASSERT(mState & NS_FRAME_IS_NONDISPLAY,
  1.3258 +             "only call ReflowSVGNonDisplayText if the frame is "
  1.3259 +             "NS_FRAME_IS_NONDISPLAY");
  1.3260 +
  1.3261 +  // We had a style change, so we mark this frame as dirty so that the next
  1.3262 +  // time it is painted, we reflow the anonymous block frame.
  1.3263 +  AddStateBits(NS_FRAME_IS_DIRTY);
  1.3264 +
  1.3265 +  // We also need to call InvalidateRenderingObservers, so that if the <text>
  1.3266 +  // element is within a <mask>, say, the element referencing the <mask> will
  1.3267 +  // be updated, which will then cause this SVGTextFrame to be painted and
  1.3268 +  // in doing so cause the anonymous block frame to be reflowed.
  1.3269 +  nsSVGEffects::InvalidateRenderingObservers(this);
  1.3270 +
  1.3271 +  // Finally, we need to actually reflow the anonymous block frame and update
  1.3272 +  // mPositions, in case we are being reflowed immediately after a DOM
  1.3273 +  // mutation that needs frame reconstruction.
  1.3274 +  MaybeReflowAnonymousBlockChild();
  1.3275 +  UpdateGlyphPositioning();
  1.3276 +}
  1.3277 +
  1.3278 +void
  1.3279 +SVGTextFrame::ScheduleReflowSVGNonDisplayText()
  1.3280 +{
  1.3281 +  MOZ_ASSERT(!nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
  1.3282 +             "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
  1.3283 +             "frame is under ReflowSVG");
  1.3284 +  MOZ_ASSERT(!(mState & NS_STATE_SVG_TEXT_IN_REFLOW),
  1.3285 +             "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
  1.3286 +             "anonymous block child");
  1.3287 +
  1.3288 +  // We need to find an ancestor frame that we can call FrameNeedsReflow
  1.3289 +  // on that will cause the document to be marked as needing relayout,
  1.3290 +  // and for that ancestor (or some further ancestor) to be marked as
  1.3291 +  // a root to reflow.  We choose the closest ancestor frame that is not
  1.3292 +  // NS_FRAME_IS_NONDISPLAY and which is either an outer SVG frame or a
  1.3293 +  // non-SVG frame.  (We don't consider displayed SVG frame ancestors toerh
  1.3294 +  // than nsSVGOuterSVGFrame, since calling FrameNeedsReflow on those other
  1.3295 +  // SVG frames would do a bunch of unnecessary work on the SVG frames up to
  1.3296 +  // the nsSVGOuterSVGFrame.)
  1.3297 +
  1.3298 +  nsIFrame* f = this;
  1.3299 +  while (f) {
  1.3300 +    if (!(f->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
  1.3301 +      if (NS_SUBTREE_DIRTY(f)) {
  1.3302 +        // This is a displayed frame, so if it is already dirty, we will be reflowed
  1.3303 +        // soon anyway.  No need to call FrameNeedsReflow again, then.
  1.3304 +        return;
  1.3305 +      }
  1.3306 +      if (!f->IsFrameOfType(eSVG) ||
  1.3307 +          (f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
  1.3308 +        break;
  1.3309 +      }
  1.3310 +      f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
  1.3311 +    }
  1.3312 +    f = f->GetParent();
  1.3313 +  }
  1.3314 +
  1.3315 +  MOZ_ASSERT(f, "should have found an ancestor frame to reflow");
  1.3316 +
  1.3317 +  PresContext()->PresShell()->FrameNeedsReflow(
  1.3318 +    f, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
  1.3319 +}
  1.3320 +
  1.3321 +NS_IMPL_ISUPPORTS(SVGTextFrame::MutationObserver, nsIMutationObserver)
  1.3322 +
  1.3323 +void
  1.3324 +SVGTextFrame::MutationObserver::ContentAppended(nsIDocument* aDocument,
  1.3325 +                                                nsIContent* aContainer,
  1.3326 +                                                nsIContent* aFirstNewContent,
  1.3327 +                                                int32_t aNewIndexInContainer)
  1.3328 +{
  1.3329 +  mFrame->NotifyGlyphMetricsChange();
  1.3330 +}
  1.3331 +
  1.3332 +void
  1.3333 +SVGTextFrame::MutationObserver::ContentInserted(
  1.3334 +                                        nsIDocument* aDocument,
  1.3335 +                                        nsIContent* aContainer,
  1.3336 +                                        nsIContent* aChild,
  1.3337 +                                        int32_t aIndexInContainer)
  1.3338 +{
  1.3339 +  mFrame->NotifyGlyphMetricsChange();
  1.3340 +}
  1.3341 +
  1.3342 +void
  1.3343 +SVGTextFrame::MutationObserver::ContentRemoved(
  1.3344 +                                       nsIDocument *aDocument,
  1.3345 +                                       nsIContent* aContainer,
  1.3346 +                                       nsIContent* aChild,
  1.3347 +                                       int32_t aIndexInContainer,
  1.3348 +                                       nsIContent* aPreviousSibling)
  1.3349 +{
  1.3350 +  mFrame->NotifyGlyphMetricsChange();
  1.3351 +}
  1.3352 +
  1.3353 +void
  1.3354 +SVGTextFrame::MutationObserver::CharacterDataChanged(
  1.3355 +                                                 nsIDocument* aDocument,
  1.3356 +                                                 nsIContent* aContent,
  1.3357 +                                                 CharacterDataChangeInfo* aInfo)
  1.3358 +{
  1.3359 +  mFrame->NotifyGlyphMetricsChange();
  1.3360 +}
  1.3361 +
  1.3362 +void
  1.3363 +SVGTextFrame::MutationObserver::AttributeChanged(
  1.3364 +                                                nsIDocument* aDocument,
  1.3365 +                                                mozilla::dom::Element* aElement,
  1.3366 +                                                int32_t aNameSpaceID,
  1.3367 +                                                nsIAtom* aAttribute,
  1.3368 +                                                int32_t aModType)
  1.3369 +{
  1.3370 +  if (!aElement->IsSVG()) {
  1.3371 +    return;
  1.3372 +  }
  1.3373 +
  1.3374 +  // Attribute changes on this element are handled in
  1.3375 +  // SVGTextFrame::AttributeChanged.
  1.3376 +  if (aElement == mFrame->GetContent()) {
  1.3377 +    return;
  1.3378 +  }
  1.3379 +
  1.3380 +  // Attributes changes on descendent elements.
  1.3381 +  if (aElement->Tag() == nsGkAtoms::textPath) {
  1.3382 +    if (aNameSpaceID == kNameSpaceID_None &&
  1.3383 +        aAttribute == nsGkAtoms::startOffset) {
  1.3384 +      mFrame->NotifyGlyphMetricsChange();
  1.3385 +    } else if (aNameSpaceID == kNameSpaceID_XLink &&
  1.3386 +               aAttribute == nsGkAtoms::href) {
  1.3387 +      // Blow away our reference, if any
  1.3388 +      nsIFrame* childElementFrame = aElement->GetPrimaryFrame();
  1.3389 +      if (childElementFrame) {
  1.3390 +        childElementFrame->Properties().Delete(nsSVGEffects::HrefProperty());
  1.3391 +        mFrame->NotifyGlyphMetricsChange();
  1.3392 +      }
  1.3393 +    }
  1.3394 +  } else {
  1.3395 +    if (aNameSpaceID == kNameSpaceID_None &&
  1.3396 +        IsGlyphPositioningAttribute(aAttribute)) {
  1.3397 +      mFrame->NotifyGlyphMetricsChange();
  1.3398 +    }
  1.3399 +  }
  1.3400 +}
  1.3401 +
  1.3402 +void
  1.3403 +SVGTextFrame::FindCloserFrameForSelection(
  1.3404 +                                 nsPoint aPoint,
  1.3405 +                                 nsIFrame::FrameWithDistance* aCurrentBestFrame)
  1.3406 +{
  1.3407 +  if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
  1.3408 +    return;
  1.3409 +  }
  1.3410 +
  1.3411 +  UpdateGlyphPositioning();
  1.3412 +
  1.3413 +  nsPresContext* presContext = PresContext();
  1.3414 +
  1.3415 +  // Find the frame that has the closest rendered run rect to aPoint.
  1.3416 +  TextRenderedRunIterator it(this);
  1.3417 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.3418 +    uint32_t flags = TextRenderedRun::eIncludeFill |
  1.3419 +                     TextRenderedRun::eIncludeStroke |
  1.3420 +                     TextRenderedRun::eNoHorizontalOverflow;
  1.3421 +    SVGBBox userRect = run.GetUserSpaceRect(presContext, flags);
  1.3422 +    if (!userRect.IsEmpty()) {
  1.3423 +      nsRect rect = nsSVGUtils::ToCanvasBounds(userRect.ToThebesRect(),
  1.3424 +                                               GetCanvasTM(FOR_HIT_TESTING),
  1.3425 +                                               presContext);
  1.3426 +
  1.3427 +      if (nsLayoutUtils::PointIsCloserToRect(aPoint, rect,
  1.3428 +                                             aCurrentBestFrame->mXDistance,
  1.3429 +                                             aCurrentBestFrame->mYDistance)) {
  1.3430 +        aCurrentBestFrame->mFrame = run.mFrame;
  1.3431 +      }
  1.3432 +    }
  1.3433 +  }
  1.3434 +}
  1.3435 +
  1.3436 +//----------------------------------------------------------------------
  1.3437 +// nsISVGChildFrame methods
  1.3438 +
  1.3439 +void
  1.3440 +SVGTextFrame::NotifySVGChanged(uint32_t aFlags)
  1.3441 +{
  1.3442 +  NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
  1.3443 +                    "Invalidation logic may need adjusting");
  1.3444 +
  1.3445 +  bool needNewBounds = false;
  1.3446 +  bool needGlyphMetricsUpdate = false;
  1.3447 +  bool needNewCanvasTM = false;
  1.3448 +
  1.3449 +  if ((aFlags & COORD_CONTEXT_CHANGED) &&
  1.3450 +      (mState & NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES)) {
  1.3451 +    needGlyphMetricsUpdate = true;
  1.3452 +  }
  1.3453 +
  1.3454 +  if (aFlags & TRANSFORM_CHANGED) {
  1.3455 +    needNewCanvasTM = true;
  1.3456 +    if (mCanvasTM && mCanvasTM->IsSingular()) {
  1.3457 +      // We won't have calculated the glyph positions correctly.
  1.3458 +      needNewBounds = true;
  1.3459 +      needGlyphMetricsUpdate = true;
  1.3460 +    }
  1.3461 +    if (StyleSVGReset()->mVectorEffect ==
  1.3462 +        NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
  1.3463 +      // Stroke currently contributes to our mRect, and our stroke depends on
  1.3464 +      // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
  1.3465 +      needNewBounds = true;
  1.3466 +    }
  1.3467 +  }
  1.3468 +
  1.3469 +  // If the scale at which we computed our mFontSizeScaleFactor has changed by
  1.3470 +  // at least a factor of two, reflow the text.  This avoids reflowing text
  1.3471 +  // at every tick of a transform animation, but ensures our glyph metrics
  1.3472 +  // do not get too far out of sync with the final font size on the screen.
  1.3473 +  if (needNewCanvasTM && mLastContextScale != 0.0f) {
  1.3474 +    mCanvasTM = nullptr;
  1.3475 +    // If we are a non-display frame, then we don't want to call
  1.3476 +    // GetCanvasTM(FOR_OUTERSVG_TM), since the context scale does not use it.
  1.3477 +    gfxMatrix newTM =
  1.3478 +      (mState & NS_FRAME_IS_NONDISPLAY) ? gfxMatrix() :
  1.3479 +                                          GetCanvasTM(FOR_OUTERSVG_TM);
  1.3480 +    // Compare the old and new context scales.
  1.3481 +    float scale = GetContextScale(newTM);
  1.3482 +    float change = scale / mLastContextScale;
  1.3483 +    if (change >= 2.0f || change <= 0.5f) {
  1.3484 +      needNewBounds = true;
  1.3485 +      needGlyphMetricsUpdate = true;
  1.3486 +    }
  1.3487 +  }
  1.3488 +
  1.3489 +  if (needNewBounds) {
  1.3490 +    // Ancestor changes can't affect how we render from the perspective of
  1.3491 +    // any rendering observers that we may have, so we don't need to
  1.3492 +    // invalidate them. We also don't need to invalidate ourself, since our
  1.3493 +    // changed ancestor will have invalidated its entire area, which includes
  1.3494 +    // our area.
  1.3495 +    ScheduleReflowSVG();
  1.3496 +  }
  1.3497 +
  1.3498 +  if (needGlyphMetricsUpdate) {
  1.3499 +    // If we are positioned using percentage values we need to update our
  1.3500 +    // position whenever our viewport's dimensions change.  But only do this if
  1.3501 +    // we have been reflowed once, otherwise the glyph positioning will be
  1.3502 +    // wrong.  (We need to wait until bidi reordering has been done.)
  1.3503 +    if (!(mState & NS_FRAME_FIRST_REFLOW)) {
  1.3504 +      NotifyGlyphMetricsChange();
  1.3505 +    }
  1.3506 +  }
  1.3507 +}
  1.3508 +
  1.3509 +/**
  1.3510 + * Gets the offset into a DOM node that the specified caret is positioned at.
  1.3511 + */
  1.3512 +static int32_t
  1.3513 +GetCaretOffset(nsCaret* aCaret)
  1.3514 +{
  1.3515 +  nsCOMPtr<nsISelection> selection = aCaret->GetCaretDOMSelection();
  1.3516 +  if (!selection) {
  1.3517 +    return -1;
  1.3518 +  }
  1.3519 +
  1.3520 +  int32_t offset = -1;
  1.3521 +  selection->GetAnchorOffset(&offset);
  1.3522 +  return offset;
  1.3523 +}
  1.3524 +
  1.3525 +/**
  1.3526 + * Returns whether the caret should be painted for a given TextRenderedRun
  1.3527 + * by checking whether the caret is in the range covered by the rendered run.
  1.3528 + *
  1.3529 + * @param aThisRun The TextRenderedRun to be painted.
  1.3530 + * @param aCaret The caret.
  1.3531 + */
  1.3532 +static bool
  1.3533 +ShouldPaintCaret(const TextRenderedRun& aThisRun, nsCaret* aCaret)
  1.3534 +{
  1.3535 +  int32_t caretOffset = GetCaretOffset(aCaret);
  1.3536 +
  1.3537 +  if (caretOffset < 0) {
  1.3538 +    return false;
  1.3539 +  }
  1.3540 +
  1.3541 +  if (uint32_t(caretOffset) >= aThisRun.mTextFrameContentOffset &&
  1.3542 +      uint32_t(caretOffset) < aThisRun.mTextFrameContentOffset +
  1.3543 +                                aThisRun.mTextFrameContentLength) {
  1.3544 +    return true;
  1.3545 +  }
  1.3546 +
  1.3547 +  return false;
  1.3548 +}
  1.3549 +
  1.3550 +nsresult
  1.3551 +SVGTextFrame::PaintSVG(nsRenderingContext* aContext,
  1.3552 +                       const nsIntRect *aDirtyRect,
  1.3553 +                       nsIFrame* aTransformRoot)
  1.3554 +{
  1.3555 +  nsIFrame* kid = GetFirstPrincipalChild();
  1.3556 +  if (!kid)
  1.3557 +    return NS_OK;
  1.3558 +
  1.3559 +  nsPresContext* presContext = PresContext();
  1.3560 +
  1.3561 +  gfxContext *gfx = aContext->ThebesContext();
  1.3562 +  gfxMatrix initialMatrix = gfx->CurrentMatrix();
  1.3563 +
  1.3564 +  if (mState & NS_FRAME_IS_NONDISPLAY) {
  1.3565 +    // If we are in a canvas DrawWindow call that used the
  1.3566 +    // DRAWWINDOW_DO_NOT_FLUSH flag, then we may still have out
  1.3567 +    // of date frames.  Just don't paint anything if they are
  1.3568 +    // dirty.
  1.3569 +    if (presContext->PresShell()->InDrawWindowNotFlushing() &&
  1.3570 +        NS_SUBTREE_DIRTY(this)) {
  1.3571 +      return NS_OK;
  1.3572 +    }
  1.3573 +    // Text frames inside <clipPath>, <mask>, etc. will never have had
  1.3574 +    // ReflowSVG called on them, so call UpdateGlyphPositioning to do this now.
  1.3575 +    UpdateGlyphPositioning();
  1.3576 +  } else if (NS_SUBTREE_DIRTY(this)) {
  1.3577 +    // If we are asked to paint before reflow has recomputed mPositions etc.
  1.3578 +    // directly via PaintSVG, rather than via a display list, then we need
  1.3579 +    // to bail out here too.
  1.3580 +    return NS_OK;
  1.3581 +  }
  1.3582 +
  1.3583 +  gfxMatrix canvasTM = GetCanvasTM(FOR_PAINTING, aTransformRoot);
  1.3584 +  if (canvasTM.IsSingular()) {
  1.3585 +    NS_WARNING("Can't render text element!");
  1.3586 +    return NS_ERROR_FAILURE;
  1.3587 +  }
  1.3588 +
  1.3589 +  gfxMatrix matrixForPaintServers(canvasTM);
  1.3590 +  matrixForPaintServers.Multiply(initialMatrix);
  1.3591 +
  1.3592 +  // Check if we need to draw anything.
  1.3593 +  if (aDirtyRect) {
  1.3594 +    NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
  1.3595 +                 (mState & NS_FRAME_IS_NONDISPLAY),
  1.3596 +                 "Display lists handle dirty rect intersection test");
  1.3597 +    nsRect dirtyRect(aDirtyRect->x, aDirtyRect->y,
  1.3598 +                     aDirtyRect->width, aDirtyRect->height);
  1.3599 +
  1.3600 +    gfxFloat appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
  1.3601 +    gfxRect frameRect(mRect.x / appUnitsPerDevPixel,
  1.3602 +                      mRect.y / appUnitsPerDevPixel,
  1.3603 +                      mRect.width / appUnitsPerDevPixel,
  1.3604 +                      mRect.height / appUnitsPerDevPixel);
  1.3605 +
  1.3606 +    nsRect canvasRect = nsLayoutUtils::RoundGfxRectToAppRect(
  1.3607 +        GetCanvasTM(FOR_OUTERSVG_TM).TransformBounds(frameRect), 1);
  1.3608 +    if (!canvasRect.Intersects(dirtyRect)) {
  1.3609 +      return NS_OK;
  1.3610 +    }
  1.3611 +  }
  1.3612 +
  1.3613 +  // SVG paints in CSS px, but normally frames paint in dev pixels. Here we
  1.3614 +  // multiply a CSS-px-to-dev-pixel factor onto canvasTM so our children paint
  1.3615 +  // correctly.
  1.3616 +  float cssPxPerDevPx = presContext->
  1.3617 +    AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  1.3618 +  gfxMatrix canvasTMForChildren = canvasTM;
  1.3619 +  canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx);
  1.3620 +  initialMatrix.Scale(1 / cssPxPerDevPx, 1 / cssPxPerDevPx);
  1.3621 +
  1.3622 +  gfxContextAutoSaveRestore save(gfx);
  1.3623 +  gfx->NewPath();
  1.3624 +  gfx->Multiply(canvasTMForChildren);
  1.3625 +  gfxMatrix currentMatrix = gfx->CurrentMatrix();
  1.3626 +
  1.3627 +  nsRefPtr<nsCaret> caret = presContext->PresShell()->GetCaret();
  1.3628 +  nsIFrame* caretFrame = caret->GetCaretFrame();
  1.3629 +
  1.3630 +  TextRenderedRunIterator it(this, TextRenderedRunIterator::eVisibleFrames);
  1.3631 +  TextRenderedRun run = it.Current();
  1.3632 +  while (run.mFrame) {
  1.3633 +    nsTextFrame* frame = run.mFrame;
  1.3634 +
  1.3635 +    // Determine how much of the left and right edges of the text frame we
  1.3636 +    // need to ignore.
  1.3637 +    SVGCharClipDisplayItem item(run);
  1.3638 +
  1.3639 +    // Set up the fill and stroke so that SVG glyphs can get painted correctly
  1.3640 +    // when they use context-fill etc.
  1.3641 +    gfx->SetMatrix(initialMatrix);
  1.3642 +    gfxTextContextPaint *outerContextPaint =
  1.3643 +      (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
  1.3644 +
  1.3645 +    nsAutoPtr<gfxTextContextPaint> contextPaint;
  1.3646 +    DrawMode drawMode =
  1.3647 +      SetupCairoState(gfx, frame, outerContextPaint,
  1.3648 +                      getter_Transfers(contextPaint));
  1.3649 +
  1.3650 +    // Set up the transform for painting the text frame for the substring
  1.3651 +    // indicated by the run.
  1.3652 +    gfxMatrix runTransform =
  1.3653 +      run.GetTransformFromUserSpaceForPainting(presContext, item);
  1.3654 +    runTransform.Multiply(currentMatrix);
  1.3655 +    gfx->SetMatrix(runTransform);
  1.3656 +
  1.3657 +    if (drawMode != DrawMode(0)) {
  1.3658 +      nsRect frameRect = frame->GetVisualOverflowRect();
  1.3659 +      bool paintSVGGlyphs;
  1.3660 +      if (ShouldRenderAsPath(aContext, frame, paintSVGGlyphs)) {
  1.3661 +        SVGTextDrawPathCallbacks callbacks(aContext, frame,
  1.3662 +                                           matrixForPaintServers,
  1.3663 +                                           paintSVGGlyphs);
  1.3664 +        frame->PaintText(aContext, nsPoint(), frameRect, item,
  1.3665 +                         contextPaint, &callbacks);
  1.3666 +      } else {
  1.3667 +        frame->PaintText(aContext, nsPoint(), frameRect, item,
  1.3668 +                         contextPaint, nullptr);
  1.3669 +      }
  1.3670 +    }
  1.3671 +
  1.3672 +    if (frame == caretFrame && ShouldPaintCaret(run, caret)) {
  1.3673 +      // XXX Should we be looking at the fill/stroke colours to paint the
  1.3674 +      // caret with, rather than using the color property?
  1.3675 +      caret->PaintCaret(nullptr, aContext, frame, nsPoint());
  1.3676 +      gfx->NewPath();
  1.3677 +    }
  1.3678 +
  1.3679 +    run = it.Next();
  1.3680 +  }
  1.3681 +
  1.3682 +  return NS_OK;
  1.3683 +}
  1.3684 +
  1.3685 +nsIFrame*
  1.3686 +SVGTextFrame::GetFrameForPoint(const nsPoint& aPoint)
  1.3687 +{
  1.3688 +  NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
  1.3689 +
  1.3690 +  if (mState & NS_FRAME_IS_NONDISPLAY) {
  1.3691 +    // Text frames inside <clipPath> will never have had ReflowSVG called on
  1.3692 +    // them, so call UpdateGlyphPositioning to do this now.  (Text frames
  1.3693 +    // inside <mask> and other non-display containers will never need to
  1.3694 +    // be hit tested.)
  1.3695 +    UpdateGlyphPositioning();
  1.3696 +  } else {
  1.3697 +    NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "reflow should have happened");
  1.3698 +  }
  1.3699 +
  1.3700 +  nsPresContext* presContext = PresContext();
  1.3701 +
  1.3702 +  gfxPoint pointInOuterSVGUserUnits = AppUnitsToGfxUnits(aPoint, presContext);
  1.3703 +
  1.3704 +  TextRenderedRunIterator it(this);
  1.3705 +  nsIFrame* hit = nullptr;
  1.3706 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.3707 +    uint16_t hitTestFlags = nsSVGUtils::GetGeometryHitTestFlags(run.mFrame);
  1.3708 +    if (!(hitTestFlags & (SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE))) {
  1.3709 +      continue;
  1.3710 +    }
  1.3711 +
  1.3712 +    gfxMatrix m = GetCanvasTM(FOR_HIT_TESTING);
  1.3713 +    m.PreMultiply(run.GetTransformFromRunUserSpaceToUserSpace(presContext));
  1.3714 +    m.Invert();
  1.3715 +
  1.3716 +    gfxPoint pointInRunUserSpace = m.Transform(pointInOuterSVGUserUnits);
  1.3717 +    gfxRect frameRect =
  1.3718 +      run.GetRunUserSpaceRect(presContext, TextRenderedRun::eIncludeFill |
  1.3719 +                                           TextRenderedRun::eIncludeStroke).ToThebesRect();
  1.3720 +
  1.3721 +    if (Inside(frameRect, pointInRunUserSpace) &&
  1.3722 +        nsSVGUtils::HitTestClip(this, aPoint)) {
  1.3723 +      hit = run.mFrame;
  1.3724 +    }
  1.3725 +  }
  1.3726 +  return hit;
  1.3727 +}
  1.3728 +
  1.3729 +nsRect
  1.3730 +SVGTextFrame::GetCoveredRegion()
  1.3731 +{
  1.3732 +  return nsSVGUtils::TransformFrameRectToOuterSVG(
  1.3733 +           mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
  1.3734 +}
  1.3735 +
  1.3736 +void
  1.3737 +SVGTextFrame::ReflowSVG()
  1.3738 +{
  1.3739 +  NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
  1.3740 +               "This call is probaby a wasteful mistake");
  1.3741 +
  1.3742 +  NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
  1.3743 +                    "ReflowSVG mechanism not designed for this");
  1.3744 +
  1.3745 +  if (!nsSVGUtils::NeedsReflowSVG(this)) {
  1.3746 +    NS_ASSERTION(!(mState & NS_STATE_SVG_POSITIONING_DIRTY), "How did this happen?");
  1.3747 +    return;
  1.3748 +  }
  1.3749 +
  1.3750 +  MaybeReflowAnonymousBlockChild();
  1.3751 +  UpdateGlyphPositioning();
  1.3752 +
  1.3753 +  nsPresContext* presContext = PresContext();
  1.3754 +
  1.3755 +  SVGBBox r;
  1.3756 +  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames);
  1.3757 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.3758 +    uint32_t runFlags = 0;
  1.3759 +    if (run.mFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None) {
  1.3760 +      runFlags |= TextRenderedRun::eIncludeFill |
  1.3761 +                  TextRenderedRun::eIncludeTextShadow;
  1.3762 +    }
  1.3763 +    if (nsSVGUtils::HasStroke(run.mFrame)) {
  1.3764 +      runFlags |= TextRenderedRun::eIncludeFill |
  1.3765 +                  TextRenderedRun::eIncludeTextShadow;
  1.3766 +    }
  1.3767 +    // Our "visual" overflow rect needs to be valid for building display lists
  1.3768 +    // for hit testing, which means that for certain values of 'pointer-events'
  1.3769 +    // it needs to include the geometry of the fill or stroke even when the fill/
  1.3770 +    // stroke don't actually render (e.g. when stroke="none" or
  1.3771 +    // stroke-opacity="0"). GetGeometryHitTestFlags accounts for 'pointer-events'.
  1.3772 +    // The text-shadow is not part of the hit-test area.
  1.3773 +    uint16_t hitTestFlags = nsSVGUtils::GetGeometryHitTestFlags(run.mFrame);
  1.3774 +    if (hitTestFlags & SVG_HIT_TEST_FILL) {
  1.3775 +      runFlags |= TextRenderedRun::eIncludeFill;
  1.3776 +    }
  1.3777 +    if (hitTestFlags & SVG_HIT_TEST_STROKE) {
  1.3778 +      runFlags |= TextRenderedRun::eIncludeStroke;
  1.3779 +    }
  1.3780 +
  1.3781 +    if (runFlags) {
  1.3782 +      r.UnionEdges(run.GetUserSpaceRect(presContext, runFlags));
  1.3783 +    }
  1.3784 +  }
  1.3785 +
  1.3786 +  if (r.IsEmpty()) {
  1.3787 +    mRect.SetEmpty();
  1.3788 +  } else {
  1.3789 +    mRect =
  1.3790 +      nsLayoutUtils::RoundGfxRectToAppRect(r.ToThebesRect(), presContext->AppUnitsPerCSSPixel());
  1.3791 +
  1.3792 +    // Due to rounding issues when we have a transform applied, we sometimes
  1.3793 +    // don't include an additional row of pixels.  For now, just inflate our
  1.3794 +    // covered region.
  1.3795 +    mRect.Inflate(presContext->AppUnitsPerDevPixel());
  1.3796 +  }
  1.3797 +
  1.3798 +  if (mState & NS_FRAME_FIRST_REFLOW) {
  1.3799 +    // Make sure we have our filter property (if any) before calling
  1.3800 +    // FinishAndStoreOverflow (subsequent filter changes are handled off
  1.3801 +    // nsChangeHint_UpdateEffects):
  1.3802 +    nsSVGEffects::UpdateEffects(this);
  1.3803 +  }
  1.3804 +
  1.3805 +  nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
  1.3806 +  nsOverflowAreas overflowAreas(overflow, overflow);
  1.3807 +  FinishAndStoreOverflow(overflowAreas, mRect.Size());
  1.3808 +
  1.3809 +  // Now unset the various reflow bits:
  1.3810 +  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
  1.3811 +              NS_FRAME_HAS_DIRTY_CHILDREN);
  1.3812 +
  1.3813 +  // XXX nsSVGContainerFrame::ReflowSVG only looks at its nsISVGChildFrame
  1.3814 +  // children, and calls ConsiderChildOverflow on them.  Does it matter
  1.3815 +  // that ConsiderChildOverflow won't be called on our children?
  1.3816 +  SVGTextFrameBase::ReflowSVG();
  1.3817 +}
  1.3818 +
  1.3819 +/**
  1.3820 + * Converts nsSVGUtils::eBBox* flags into TextRenderedRun flags appropriate
  1.3821 + * for the specified rendered run.
  1.3822 + */
  1.3823 +static uint32_t
  1.3824 +TextRenderedRunFlagsForBBoxContribution(const TextRenderedRun& aRun,
  1.3825 +                                        uint32_t aBBoxFlags)
  1.3826 +{
  1.3827 +  uint32_t flags = 0;
  1.3828 +  if ((aBBoxFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
  1.3829 +      ((aBBoxFlags & nsSVGUtils::eBBoxIncludeFill) &&
  1.3830 +       aRun.mFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
  1.3831 +    flags |= TextRenderedRun::eIncludeFill;
  1.3832 +  }
  1.3833 +  if ((aBBoxFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
  1.3834 +      ((aBBoxFlags & nsSVGUtils::eBBoxIncludeStroke) &&
  1.3835 +       nsSVGUtils::HasStroke(aRun.mFrame))) {
  1.3836 +    flags |= TextRenderedRun::eIncludeStroke;
  1.3837 +  }
  1.3838 +  return flags;
  1.3839 +}
  1.3840 +
  1.3841 +SVGBBox
  1.3842 +SVGTextFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace,
  1.3843 +                                  uint32_t aFlags)
  1.3844 +{
  1.3845 +  NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
  1.3846 +
  1.3847 +  UpdateGlyphPositioning();
  1.3848 +
  1.3849 +  SVGBBox bbox;
  1.3850 +  nsPresContext* presContext = PresContext();
  1.3851 +
  1.3852 +  TextRenderedRunIterator it(this);
  1.3853 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.3854 +    uint32_t flags = TextRenderedRunFlagsForBBoxContribution(run, aFlags);
  1.3855 +    gfxMatrix m = ThebesMatrix(aToBBoxUserspace);
  1.3856 +    SVGBBox bboxForRun =
  1.3857 +      run.GetUserSpaceRect(presContext, flags, &m);
  1.3858 +    bbox.UnionEdges(bboxForRun);
  1.3859 +  }
  1.3860 +
  1.3861 +  return bbox;
  1.3862 +}
  1.3863 +
  1.3864 +//----------------------------------------------------------------------
  1.3865 +// nsSVGContainerFrame methods
  1.3866 +
  1.3867 +gfxMatrix
  1.3868 +SVGTextFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
  1.3869 +{
  1.3870 +  if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
  1.3871 +      !aTransformRoot) {
  1.3872 +    if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
  1.3873 +        (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
  1.3874 +      return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
  1.3875 +    }
  1.3876 +  }
  1.3877 +  if (!mCanvasTM) {
  1.3878 +    NS_ASSERTION(mParent, "null parent");
  1.3879 +    NS_ASSERTION(!(aFor == FOR_OUTERSVG_TM &&
  1.3880 +                   (GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
  1.3881 +                 "should not call GetCanvasTM(FOR_OUTERSVG_TM) when we are "
  1.3882 +                 "non-display");
  1.3883 +
  1.3884 +    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
  1.3885 +    dom::SVGTextContentElement *content = static_cast<dom::SVGTextContentElement*>(mContent);
  1.3886 +
  1.3887 +    gfxMatrix tm = content->PrependLocalTransformsTo(
  1.3888 +        this == aTransformRoot ? gfxMatrix() :
  1.3889 +                                 parent->GetCanvasTM(aFor, aTransformRoot));
  1.3890 +
  1.3891 +    mCanvasTM = new gfxMatrix(tm);
  1.3892 +  }
  1.3893 +  return *mCanvasTM;
  1.3894 +}
  1.3895 +
  1.3896 +//----------------------------------------------------------------------
  1.3897 +// SVGTextFrame SVG DOM methods
  1.3898 +
  1.3899 +/**
  1.3900 + * Returns whether the specified node has any non-empty nsTextNodes
  1.3901 + * beneath it.
  1.3902 + */
  1.3903 +static bool
  1.3904 +HasTextContent(nsIContent* aContent)
  1.3905 +{
  1.3906 +  NS_ASSERTION(aContent, "expected non-null aContent");
  1.3907 +
  1.3908 +  TextNodeIterator it(aContent);
  1.3909 +  for (nsTextNode* text = it.Current(); text; text = it.Next()) {
  1.3910 +    if (text->TextLength() != 0) {
  1.3911 +      return true;
  1.3912 +    }
  1.3913 +  }
  1.3914 +  return false;
  1.3915 +}
  1.3916 +
  1.3917 +/**
  1.3918 + * Returns the number of DOM characters beneath the specified node.
  1.3919 + */
  1.3920 +static uint32_t
  1.3921 +GetTextContentLength(nsIContent* aContent)
  1.3922 +{
  1.3923 +  NS_ASSERTION(aContent, "expected non-null aContent");
  1.3924 +
  1.3925 +  uint32_t length = 0;
  1.3926 +  TextNodeIterator it(aContent);
  1.3927 +  for (nsTextNode* text = it.Current(); text; text = it.Next()) {
  1.3928 +    length += text->TextLength();
  1.3929 +  }
  1.3930 +  return length;
  1.3931 +}
  1.3932 +
  1.3933 +int32_t
  1.3934 +SVGTextFrame::ConvertTextElementCharIndexToAddressableIndex(
  1.3935 +                                                           int32_t aIndex,
  1.3936 +                                                           nsIContent* aContent)
  1.3937 +{
  1.3938 +  CharIterator it(this, CharIterator::eOriginal, aContent);
  1.3939 +  if (!it.AdvanceToSubtree()) {
  1.3940 +    return -1;
  1.3941 +  }
  1.3942 +  int32_t result = 0;
  1.3943 +  int32_t textElementCharIndex;
  1.3944 +  while (!it.AtEnd() &&
  1.3945 +         it.IsWithinSubtree()) {
  1.3946 +    bool addressable = !it.IsOriginalCharUnaddressable();
  1.3947 +    textElementCharIndex = it.TextElementCharIndex();
  1.3948 +    it.Next();
  1.3949 +    uint32_t delta = it.TextElementCharIndex() - textElementCharIndex;
  1.3950 +    aIndex -= delta;
  1.3951 +    if (addressable) {
  1.3952 +      if (aIndex < 0) {
  1.3953 +        return result;
  1.3954 +      }
  1.3955 +      result += delta;
  1.3956 +    }
  1.3957 +  }
  1.3958 +  return -1;
  1.3959 +}
  1.3960 +
  1.3961 +/**
  1.3962 + * Implements the SVG DOM GetNumberOfChars method for the specified
  1.3963 + * text content element.
  1.3964 + */
  1.3965 +uint32_t
  1.3966 +SVGTextFrame::GetNumberOfChars(nsIContent* aContent)
  1.3967 +{
  1.3968 +  UpdateGlyphPositioning();
  1.3969 +
  1.3970 +  uint32_t n = 0;
  1.3971 +  CharIterator it(this, CharIterator::eAddressable, aContent);
  1.3972 +  if (it.AdvanceToSubtree()) {
  1.3973 +    while (!it.AtEnd() && it.IsWithinSubtree()) {
  1.3974 +      n++;
  1.3975 +      it.Next();
  1.3976 +    }
  1.3977 +  }
  1.3978 +  return n;
  1.3979 +}
  1.3980 +
  1.3981 +/**
  1.3982 + * Implements the SVG DOM GetComputedTextLength method for the specified
  1.3983 + * text child element.
  1.3984 + */
  1.3985 +float
  1.3986 +SVGTextFrame::GetComputedTextLength(nsIContent* aContent)
  1.3987 +{
  1.3988 +  UpdateGlyphPositioning();
  1.3989 +
  1.3990 +  float cssPxPerDevPx = PresContext()->
  1.3991 +    AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
  1.3992 +
  1.3993 +  nscoord length = 0;
  1.3994 +  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
  1.3995 +                             aContent);
  1.3996 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.3997 +    length += run.GetAdvanceWidth();
  1.3998 +  }
  1.3999 +
  1.4000 +  return PresContext()->AppUnitsToGfxUnits(length) *
  1.4001 +           cssPxPerDevPx * mLengthAdjustScaleFactor / mFontSizeScaleFactor;
  1.4002 +}
  1.4003 +
  1.4004 +/**
  1.4005 + * Implements the SVG DOM SelectSubString method for the specified
  1.4006 + * text content element.
  1.4007 + */
  1.4008 +nsresult
  1.4009 +SVGTextFrame::SelectSubString(nsIContent* aContent,
  1.4010 +                              uint32_t charnum, uint32_t nchars)
  1.4011 +{
  1.4012 +  UpdateGlyphPositioning();
  1.4013 +
  1.4014 +  // Convert charnum/nchars from addressable characters relative to
  1.4015 +  // aContent to global character indices.
  1.4016 +  CharIterator chit(this, CharIterator::eAddressable, aContent);
  1.4017 +  if (!chit.AdvanceToSubtree() ||
  1.4018 +      !chit.Next(charnum) ||
  1.4019 +      chit.IsAfterSubtree()) {
  1.4020 +    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.4021 +  }
  1.4022 +  charnum = chit.TextElementCharIndex();
  1.4023 +  nsIContent* content = chit.TextFrame()->GetContent();
  1.4024 +  chit.NextWithinSubtree(nchars);
  1.4025 +  nchars = chit.TextElementCharIndex() - charnum;
  1.4026 +
  1.4027 +  nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
  1.4028 +
  1.4029 +  frameSelection->HandleClick(content, charnum, charnum + nchars,
  1.4030 +                              false, false, false);
  1.4031 +  return NS_OK;
  1.4032 +}
  1.4033 +
  1.4034 +/**
  1.4035 + * Implements the SVG DOM GetSubStringLength method for the specified
  1.4036 + * text content element.
  1.4037 + */
  1.4038 +nsresult
  1.4039 +SVGTextFrame::GetSubStringLength(nsIContent* aContent,
  1.4040 +                                 uint32_t charnum, uint32_t nchars,
  1.4041 +                                 float* aResult)
  1.4042 +{
  1.4043 +  UpdateGlyphPositioning();
  1.4044 +
  1.4045 +  // Convert charnum/nchars from addressable characters relative to
  1.4046 +  // aContent to global character indices.
  1.4047 +  CharIterator chit(this, CharIterator::eAddressable, aContent);
  1.4048 +  if (!chit.AdvanceToSubtree() ||
  1.4049 +      !chit.Next(charnum) ||
  1.4050 +      chit.IsAfterSubtree()) {
  1.4051 +    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.4052 +  }
  1.4053 +
  1.4054 +  if (nchars == 0) {
  1.4055 +    *aResult = 0.0f;
  1.4056 +    return NS_OK;
  1.4057 +  }
  1.4058 +
  1.4059 +  charnum = chit.TextElementCharIndex();
  1.4060 +  chit.NextWithinSubtree(nchars);
  1.4061 +  nchars = chit.TextElementCharIndex() - charnum;
  1.4062 +
  1.4063 +  // Find each rendered run that intersects with the range defined
  1.4064 +  // by charnum/nchars.
  1.4065 +  nscoord textLength = 0;
  1.4066 +  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames);
  1.4067 +  TextRenderedRun run = it.Current();
  1.4068 +  while (run.mFrame) {
  1.4069 +    // If this rendered run is past the substring we are interested in, we
  1.4070 +    // are done.
  1.4071 +    uint32_t offset = run.mTextElementCharIndex;
  1.4072 +    if (offset >= charnum + nchars) {
  1.4073 +      break;
  1.4074 +    }
  1.4075 +
  1.4076 +    // Intersect the substring we are interested in with the range covered by
  1.4077 +    // the rendered run.
  1.4078 +    uint32_t length = run.mTextFrameContentLength;
  1.4079 +    IntersectInterval(offset, length, charnum, nchars);
  1.4080 +
  1.4081 +    if (length != 0) {
  1.4082 +      // Convert offset into an index into the frame.
  1.4083 +      offset += run.mTextFrameContentOffset - run.mTextElementCharIndex;
  1.4084 +
  1.4085 +      gfxSkipCharsIterator it =
  1.4086 +        run.mFrame->EnsureTextRun(nsTextFrame::eInflated);
  1.4087 +      gfxTextRun* textRun = run.mFrame->GetTextRun(nsTextFrame::eInflated);
  1.4088 +      ConvertOriginalToSkipped(it, offset, length);
  1.4089 +
  1.4090 +      // Accumulate the advance.
  1.4091 +      textLength += textRun->GetAdvanceWidth(offset, length, nullptr);
  1.4092 +    }
  1.4093 +
  1.4094 +    run = it.Next();
  1.4095 +  }
  1.4096 +
  1.4097 +  nsPresContext* presContext = PresContext();
  1.4098 +  float cssPxPerDevPx = presContext->
  1.4099 +    AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  1.4100 +
  1.4101 +  *aResult = presContext->AppUnitsToGfxUnits(textLength) *
  1.4102 +               cssPxPerDevPx / mFontSizeScaleFactor;
  1.4103 +  return NS_OK;
  1.4104 +}
  1.4105 +
  1.4106 +/**
  1.4107 + * Implements the SVG DOM GetCharNumAtPosition method for the specified
  1.4108 + * text content element.
  1.4109 + */
  1.4110 +int32_t
  1.4111 +SVGTextFrame::GetCharNumAtPosition(nsIContent* aContent,
  1.4112 +                                   mozilla::nsISVGPoint* aPoint)
  1.4113 +{
  1.4114 +  UpdateGlyphPositioning();
  1.4115 +
  1.4116 +  nsPresContext* context = PresContext();
  1.4117 +
  1.4118 +  gfxPoint p(aPoint->X(), aPoint->Y());
  1.4119 +
  1.4120 +  int32_t result = -1;
  1.4121 +
  1.4122 +  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames, aContent);
  1.4123 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.4124 +    // Hit test this rendered run.  Later runs will override earlier ones.
  1.4125 +    int32_t index = run.GetCharNumAtPosition(context, p);
  1.4126 +    if (index != -1) {
  1.4127 +      result = index + run.mTextElementCharIndex;
  1.4128 +    }
  1.4129 +  }
  1.4130 +
  1.4131 +  if (result == -1) {
  1.4132 +    return result;
  1.4133 +  }
  1.4134 +
  1.4135 +  return ConvertTextElementCharIndexToAddressableIndex(result, aContent);
  1.4136 +}
  1.4137 +
  1.4138 +/**
  1.4139 + * Implements the SVG DOM GetStartPositionOfChar method for the specified
  1.4140 + * text content element.
  1.4141 + */
  1.4142 +nsresult
  1.4143 +SVGTextFrame::GetStartPositionOfChar(nsIContent* aContent,
  1.4144 +                                     uint32_t aCharNum,
  1.4145 +                                     mozilla::nsISVGPoint** aResult)
  1.4146 +{
  1.4147 +  UpdateGlyphPositioning();
  1.4148 +
  1.4149 +  CharIterator it(this, CharIterator::eAddressable, aContent);
  1.4150 +  if (!it.AdvanceToSubtree() ||
  1.4151 +      !it.Next(aCharNum)) {
  1.4152 +    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.4153 +  }
  1.4154 +
  1.4155 +  // We need to return the start position of the whole glyph.
  1.4156 +  uint32_t startIndex = it.GlyphStartTextElementCharIndex();
  1.4157 +
  1.4158 +  NS_ADDREF(*aResult =
  1.4159 +    new DOMSVGPoint(ToPoint(mPositions[startIndex].mPosition)));
  1.4160 +  return NS_OK;
  1.4161 +}
  1.4162 +
  1.4163 +/**
  1.4164 + * Implements the SVG DOM GetEndPositionOfChar method for the specified
  1.4165 + * text content element.
  1.4166 + */
  1.4167 +nsresult
  1.4168 +SVGTextFrame::GetEndPositionOfChar(nsIContent* aContent,
  1.4169 +                                   uint32_t aCharNum,
  1.4170 +                                   mozilla::nsISVGPoint** aResult)
  1.4171 +{
  1.4172 +  UpdateGlyphPositioning();
  1.4173 +
  1.4174 +  CharIterator it(this, CharIterator::eAddressable, aContent);
  1.4175 +  if (!it.AdvanceToSubtree() ||
  1.4176 +      !it.Next(aCharNum)) {
  1.4177 +    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.4178 +  }
  1.4179 +
  1.4180 +  // We need to return the end position of the whole glyph.
  1.4181 +  uint32_t startIndex = it.GlyphStartTextElementCharIndex();
  1.4182 +
  1.4183 +  // Get the advance of the glyph.
  1.4184 +  gfxFloat advance = it.GetGlyphAdvance(PresContext());
  1.4185 +  if (it.TextRun()->IsRightToLeft()) {
  1.4186 +    advance = -advance;
  1.4187 +  }
  1.4188 +
  1.4189 +  // The end position is the start position plus the advance in the direction
  1.4190 +  // of the glyph's rotation.
  1.4191 +  Matrix m =
  1.4192 +    Matrix::Rotation(mPositions[startIndex].mAngle) *
  1.4193 +    Matrix::Translation(ToPoint(mPositions[startIndex].mPosition));
  1.4194 +  Point p = m * Point(advance / mFontSizeScaleFactor, 0);
  1.4195 +
  1.4196 +  NS_ADDREF(*aResult = new DOMSVGPoint(p));
  1.4197 +  return NS_OK;
  1.4198 +}
  1.4199 +
  1.4200 +/**
  1.4201 + * Implements the SVG DOM GetExtentOfChar method for the specified
  1.4202 + * text content element.
  1.4203 + */
  1.4204 +nsresult
  1.4205 +SVGTextFrame::GetExtentOfChar(nsIContent* aContent,
  1.4206 +                              uint32_t aCharNum,
  1.4207 +                              dom::SVGIRect** aResult)
  1.4208 +{
  1.4209 +  UpdateGlyphPositioning();
  1.4210 +
  1.4211 +  CharIterator it(this, CharIterator::eAddressable, aContent);
  1.4212 +  if (!it.AdvanceToSubtree() ||
  1.4213 +      !it.Next(aCharNum)) {
  1.4214 +    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.4215 +  }
  1.4216 +
  1.4217 +  nsPresContext* presContext = PresContext();
  1.4218 +
  1.4219 +  float cssPxPerDevPx = presContext->
  1.4220 +    AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  1.4221 +
  1.4222 +  // We need to return the extent of the whole glyph.
  1.4223 +  uint32_t startIndex = it.GlyphStartTextElementCharIndex();
  1.4224 +
  1.4225 +  // The ascent and descent gives the height of the glyph.
  1.4226 +  gfxFloat ascent, descent;
  1.4227 +  GetAscentAndDescentInAppUnits(it.TextFrame(), ascent, descent);
  1.4228 +
  1.4229 +  // Get the advance of the glyph.
  1.4230 +  gfxFloat advance = it.GetGlyphAdvance(presContext);
  1.4231 +  gfxFloat x = it.TextRun()->IsRightToLeft() ? -advance : 0.0;
  1.4232 +
  1.4233 +  // The horizontal extent is the origin of the glyph plus the advance
  1.4234 +  // in the direction of the glyph's rotation.
  1.4235 +  gfxMatrix m;
  1.4236 +  m.Translate(mPositions[startIndex].mPosition);
  1.4237 +  m.Rotate(mPositions[startIndex].mAngle);
  1.4238 +  m.Scale(1 / mFontSizeScaleFactor, 1 / mFontSizeScaleFactor);
  1.4239 +
  1.4240 +  gfxRect glyphRect
  1.4241 +    (x, -presContext->AppUnitsToGfxUnits(ascent) * cssPxPerDevPx,
  1.4242 +     advance, presContext->AppUnitsToGfxUnits(ascent + descent) * cssPxPerDevPx);
  1.4243 +
  1.4244 +  // Transform the glyph's rect into user space.
  1.4245 +  gfxRect r = m.TransformBounds(glyphRect);
  1.4246 +
  1.4247 +  NS_ADDREF(*aResult = new dom::SVGRect(aContent, r.x, r.y, r.width, r.height));
  1.4248 +  return NS_OK;
  1.4249 +}
  1.4250 +
  1.4251 +/**
  1.4252 + * Implements the SVG DOM GetRotationOfChar method for the specified
  1.4253 + * text content element.
  1.4254 + */
  1.4255 +nsresult
  1.4256 +SVGTextFrame::GetRotationOfChar(nsIContent* aContent,
  1.4257 +                                uint32_t aCharNum,
  1.4258 +                                float* aResult)
  1.4259 +{
  1.4260 +  UpdateGlyphPositioning();
  1.4261 +
  1.4262 +  CharIterator it(this, CharIterator::eAddressable, aContent);
  1.4263 +  if (!it.AdvanceToSubtree() ||
  1.4264 +      !it.Next(aCharNum)) {
  1.4265 +    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1.4266 +  }
  1.4267 +
  1.4268 +  *aResult = mPositions[it.TextElementCharIndex()].mAngle * 180.0 / M_PI;
  1.4269 +  return NS_OK;
  1.4270 +}
  1.4271 +
  1.4272 +//----------------------------------------------------------------------
  1.4273 +// SVGTextFrame text layout methods
  1.4274 +
  1.4275 +/**
  1.4276 + * Given the character position array before values have been filled in
  1.4277 + * to any unspecified positions, and an array of dx/dy values, returns whether
  1.4278 + * a character at a given index should start a new rendered run.
  1.4279 + *
  1.4280 + * @param aPositions The array of character positions before unspecified
  1.4281 + *   positions have been filled in and dx/dy values have been added to them.
  1.4282 + * @param aDeltas The array of dx/dy values.
  1.4283 + * @param aIndex The character index in question.
  1.4284 + */
  1.4285 +static bool
  1.4286 +ShouldStartRunAtIndex(const nsTArray<CharPosition>& aPositions,
  1.4287 +                      const nsTArray<gfxPoint>& aDeltas,
  1.4288 +                      uint32_t aIndex)
  1.4289 +{
  1.4290 +  if (aIndex == 0) {
  1.4291 +    return true;
  1.4292 +  }
  1.4293 +
  1.4294 +  if (aIndex < aPositions.Length()) {
  1.4295 +    // If an explicit x or y value was given, start a new run.
  1.4296 +    if (aPositions[aIndex].IsXSpecified() ||
  1.4297 +        aPositions[aIndex].IsYSpecified()) {
  1.4298 +      return true;
  1.4299 +    }
  1.4300 +
  1.4301 +    // If a non-zero rotation was given, or the previous character had a non-
  1.4302 +    // zero rotation, start a new run.
  1.4303 +    if ((aPositions[aIndex].IsAngleSpecified() &&
  1.4304 +         aPositions[aIndex].mAngle != 0.0f) ||
  1.4305 +        (aPositions[aIndex - 1].IsAngleSpecified() &&
  1.4306 +         (aPositions[aIndex - 1].mAngle != 0.0f))) {
  1.4307 +      return true;
  1.4308 +    }
  1.4309 +  }
  1.4310 +
  1.4311 +  if (aIndex < aDeltas.Length()) {
  1.4312 +    // If a non-zero dx or dy value was given, start a new run.
  1.4313 +    if (aDeltas[aIndex].x != 0.0 ||
  1.4314 +        aDeltas[aIndex].y != 0.0) {
  1.4315 +      return true;
  1.4316 +    }
  1.4317 +  }
  1.4318 +
  1.4319 +  return false;
  1.4320 +}
  1.4321 +
  1.4322 +uint32_t
  1.4323 +SVGTextFrame::ResolvePositions(nsIContent* aContent,
  1.4324 +                               uint32_t aIndex,
  1.4325 +                               bool aInTextPath,
  1.4326 +                               bool& aForceStartOfChunk,
  1.4327 +                               nsTArray<gfxPoint>& aDeltas)
  1.4328 +{
  1.4329 +  if (aContent->IsNodeOfType(nsINode::eTEXT)) {
  1.4330 +    // We found a text node.
  1.4331 +    uint32_t length = static_cast<nsTextNode*>(aContent)->TextLength();
  1.4332 +    if (length) {
  1.4333 +      if (aForceStartOfChunk) {
  1.4334 +        // Note this character as starting a new anchored chunk.
  1.4335 +        mPositions[aIndex].mStartOfChunk = true;
  1.4336 +        aForceStartOfChunk = false;
  1.4337 +      }
  1.4338 +      uint32_t end = aIndex + length;
  1.4339 +      while (aIndex < end) {
  1.4340 +        // Record whether each of these characters should start a new rendered
  1.4341 +        // run.  That is always the case for characters on a text path.
  1.4342 +        //
  1.4343 +        // Run boundaries due to rotate="" values are handled in
  1.4344 +        // DoGlyphPositioning.
  1.4345 +        if (aInTextPath || ShouldStartRunAtIndex(mPositions, aDeltas, aIndex)) {
  1.4346 +          mPositions[aIndex].mRunBoundary = true;
  1.4347 +        }
  1.4348 +        aIndex++;
  1.4349 +      }
  1.4350 +    }
  1.4351 +    return aIndex;
  1.4352 +  }
  1.4353 +
  1.4354 +  // Skip past elements that aren't text content elements.
  1.4355 +  if (!IsTextContentElement(aContent)) {
  1.4356 +    return aIndex;
  1.4357 +  }
  1.4358 +
  1.4359 +  if (aContent->Tag() == nsGkAtoms::textPath) {
  1.4360 +    // <textPath> elements are as if they are specified with x="0" y="0", but
  1.4361 +    // only if they actually have some text content.
  1.4362 +    if (HasTextContent(aContent)) {
  1.4363 +      mPositions[aIndex].mPosition = gfxPoint();
  1.4364 +      mPositions[aIndex].mStartOfChunk = true;
  1.4365 +    }
  1.4366 +  } else if (aContent->Tag() != nsGkAtoms::a) {
  1.4367 +    // We have a text content element that can have x/y/dx/dy/rotate attributes.
  1.4368 +    nsSVGElement* element = static_cast<nsSVGElement*>(aContent);
  1.4369 +
  1.4370 +    // Get x, y, dx, dy.
  1.4371 +    SVGUserUnitList x, y, dx, dy;
  1.4372 +    element->GetAnimatedLengthListValues(&x, &y, &dx, &dy);
  1.4373 +
  1.4374 +    // Get rotate.
  1.4375 +    const SVGNumberList* rotate = nullptr;
  1.4376 +    SVGAnimatedNumberList* animatedRotate =
  1.4377 +      element->GetAnimatedNumberList(nsGkAtoms::rotate);
  1.4378 +    if (animatedRotate) {
  1.4379 +      rotate = &animatedRotate->GetAnimValue();
  1.4380 +    }
  1.4381 +
  1.4382 +    uint32_t count = GetTextContentLength(aContent);
  1.4383 +    bool percentages = false;
  1.4384 +
  1.4385 +    // New text anchoring chunks start at each character assigned a position
  1.4386 +    // with x="" or y="", or if we forced one with aForceStartOfChunk due to
  1.4387 +    // being just after a <textPath>.
  1.4388 +    uint32_t newChunkCount = std::max(x.Length(), y.Length());
  1.4389 +    if (!newChunkCount && aForceStartOfChunk) {
  1.4390 +      newChunkCount = 1;
  1.4391 +    }
  1.4392 +    for (uint32_t i = 0, j = 0; i < newChunkCount && j < count; j++) {
  1.4393 +      if (!mPositions[aIndex + j].mUnaddressable) {
  1.4394 +        mPositions[aIndex + j].mStartOfChunk = true;
  1.4395 +        i++;
  1.4396 +      }
  1.4397 +    }
  1.4398 +
  1.4399 +    // Copy dx="" and dy="" values into aDeltas.
  1.4400 +    if (!dx.IsEmpty() || !dy.IsEmpty()) {
  1.4401 +      // Any unspecified deltas when we grow the array just get left as 0s.
  1.4402 +      aDeltas.EnsureLengthAtLeast(aIndex + count);
  1.4403 +      for (uint32_t i = 0, j = 0; i < dx.Length() && j < count; j++) {
  1.4404 +        if (!mPositions[aIndex + j].mUnaddressable) {
  1.4405 +          aDeltas[aIndex + j].x = dx[i];
  1.4406 +          percentages = percentages || dx.HasPercentageValueAt(i);
  1.4407 +          i++;
  1.4408 +        }
  1.4409 +      }
  1.4410 +      for (uint32_t i = 0, j = 0; i < dy.Length() && j < count; j++) {
  1.4411 +        if (!mPositions[aIndex + j].mUnaddressable) {
  1.4412 +          aDeltas[aIndex + j].y = dy[i];
  1.4413 +          percentages = percentages || dy.HasPercentageValueAt(i);
  1.4414 +          i++;
  1.4415 +        }
  1.4416 +      }
  1.4417 +    }
  1.4418 +
  1.4419 +    // Copy x="" and y="" values.
  1.4420 +    for (uint32_t i = 0, j = 0; i < x.Length() && j < count; j++) {
  1.4421 +      if (!mPositions[aIndex + j].mUnaddressable) {
  1.4422 +        mPositions[aIndex + j].mPosition.x = x[i];
  1.4423 +        percentages = percentages || x.HasPercentageValueAt(i);
  1.4424 +        i++;
  1.4425 +      }
  1.4426 +    }
  1.4427 +    for (uint32_t i = 0, j = 0; i < y.Length() && j < count; j++) {
  1.4428 +      if (!mPositions[aIndex + j].mUnaddressable) {
  1.4429 +        mPositions[aIndex + j].mPosition.y = y[i];
  1.4430 +        percentages = percentages || y.HasPercentageValueAt(i);
  1.4431 +        i++;
  1.4432 +      }
  1.4433 +    }
  1.4434 +
  1.4435 +    // Copy rotate="" values.
  1.4436 +    if (rotate && !rotate->IsEmpty()) {
  1.4437 +      uint32_t i = 0, j = 0;
  1.4438 +      while (i < rotate->Length() && j < count) {
  1.4439 +        if (!mPositions[aIndex + j].mUnaddressable) {
  1.4440 +          mPositions[aIndex + j].mAngle = M_PI * (*rotate)[i] / 180.0;
  1.4441 +          i++;
  1.4442 +        }
  1.4443 +        j++;
  1.4444 +      }
  1.4445 +      // Propagate final rotate="" value to the end of this element.
  1.4446 +      while (j < count) {
  1.4447 +        mPositions[aIndex + j].mAngle = mPositions[aIndex + j - 1].mAngle;
  1.4448 +        j++;
  1.4449 +      }
  1.4450 +    }
  1.4451 +
  1.4452 +    if (percentages) {
  1.4453 +      AddStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES);
  1.4454 +    }
  1.4455 +  }
  1.4456 +
  1.4457 +  // Recurse to children.
  1.4458 +  bool inTextPath = aInTextPath || aContent->Tag() == nsGkAtoms::textPath;
  1.4459 +  for (nsIContent* child = aContent->GetFirstChild();
  1.4460 +       child;
  1.4461 +       child = child->GetNextSibling()) {
  1.4462 +    aIndex = ResolvePositions(child, aIndex, inTextPath, aForceStartOfChunk,
  1.4463 +                              aDeltas);
  1.4464 +  }
  1.4465 +
  1.4466 +  if (aContent->Tag() == nsGkAtoms::textPath) {
  1.4467 +    // Force a new anchored chunk just after a <textPath>.
  1.4468 +    aForceStartOfChunk = true;
  1.4469 +  }
  1.4470 +
  1.4471 +  return aIndex;
  1.4472 +}
  1.4473 +
  1.4474 +bool
  1.4475 +SVGTextFrame::ResolvePositions(nsTArray<gfxPoint>& aDeltas,
  1.4476 +                               bool aRunPerGlyph)
  1.4477 +{
  1.4478 +  NS_ASSERTION(mPositions.IsEmpty(), "expected mPositions to be empty");
  1.4479 +  RemoveStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES);
  1.4480 +
  1.4481 +  CharIterator it(this, CharIterator::eOriginal);
  1.4482 +  if (it.AtEnd()) {
  1.4483 +    return false;
  1.4484 +  }
  1.4485 +
  1.4486 +  // We assume the first character position is (0,0) unless we later see
  1.4487 +  // otherwise, and note it as unaddressable if it is.
  1.4488 +  bool firstCharUnaddressable = it.IsOriginalCharUnaddressable();
  1.4489 +  mPositions.AppendElement(CharPosition::Unspecified(firstCharUnaddressable));
  1.4490 +
  1.4491 +  // Fill in unspecified positions for all remaining characters, noting
  1.4492 +  // them as unaddressable if they are.
  1.4493 +  uint32_t index = 0;
  1.4494 +  while (it.Next()) {
  1.4495 +    while (++index < it.TextElementCharIndex()) {
  1.4496 +      mPositions.AppendElement(CharPosition::Unspecified(false));
  1.4497 +    }
  1.4498 +    mPositions.AppendElement(CharPosition::Unspecified(
  1.4499 +                                             it.IsOriginalCharUnaddressable()));
  1.4500 +  }
  1.4501 +  while (++index < it.TextElementCharIndex()) {
  1.4502 +    mPositions.AppendElement(CharPosition::Unspecified(false));
  1.4503 +  }
  1.4504 +
  1.4505 +  // Recurse over the content and fill in character positions as we go.
  1.4506 +  bool forceStartOfChunk = false;
  1.4507 +  return ResolvePositions(mContent, 0, aRunPerGlyph,
  1.4508 +                          forceStartOfChunk, aDeltas) != 0;
  1.4509 +}
  1.4510 +
  1.4511 +void
  1.4512 +SVGTextFrame::DetermineCharPositions(nsTArray<nsPoint>& aPositions)
  1.4513 +{
  1.4514 +  NS_ASSERTION(aPositions.IsEmpty(), "expected aPositions to be empty");
  1.4515 +
  1.4516 +  nsPoint position, lastPosition;
  1.4517 +
  1.4518 +  TextFrameIterator frit(this);
  1.4519 +  for (nsTextFrame* frame = frit.Current(); frame; frame = frit.Next()) {
  1.4520 +    gfxSkipCharsIterator it = frame->EnsureTextRun(nsTextFrame::eInflated);
  1.4521 +    gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated);
  1.4522 +
  1.4523 +    // Reset the position to the new frame's position.
  1.4524 +    position = frit.Position();
  1.4525 +    if (textRun->IsRightToLeft()) {
  1.4526 +      position.x += frame->GetRect().width;
  1.4527 +    }
  1.4528 +    position.y += GetBaselinePosition(frame, textRun, frit.DominantBaseline());
  1.4529 +
  1.4530 +    // Any characters not in a frame, e.g. when display:none.
  1.4531 +    for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
  1.4532 +      aPositions.AppendElement(position);
  1.4533 +    }
  1.4534 +
  1.4535 +    // Any white space characters trimmed at the start of the line of text.
  1.4536 +    nsTextFrame::TrimmedOffsets trimmedOffsets =
  1.4537 +      frame->GetTrimmedOffsets(frame->GetContent()->GetText(), true);
  1.4538 +    while (it.GetOriginalOffset() < trimmedOffsets.mStart) {
  1.4539 +      aPositions.AppendElement(position);
  1.4540 +      it.AdvanceOriginal(1);
  1.4541 +    }
  1.4542 +
  1.4543 +    // If a ligature was started in the previous frame, we should record
  1.4544 +    // the ligature's start position, not any partial position.
  1.4545 +    while (it.GetOriginalOffset() < frame->GetContentEnd() &&
  1.4546 +           !it.IsOriginalCharSkipped() &&
  1.4547 +           (!textRun->IsLigatureGroupStart(it.GetSkippedOffset()) ||
  1.4548 +            !textRun->IsClusterStart(it.GetSkippedOffset()))) {
  1.4549 +      nscoord advance = textRun->GetAdvanceWidth(it.GetSkippedOffset(), 1,
  1.4550 +                                                 nullptr);
  1.4551 +      position.x += textRun->IsRightToLeft() ? -advance : advance;
  1.4552 +      aPositions.AppendElement(lastPosition);
  1.4553 +      it.AdvanceOriginal(1);
  1.4554 +    }
  1.4555 +
  1.4556 +    // The meat of the text frame.
  1.4557 +    while (it.GetOriginalOffset() < frame->GetContentEnd()) {
  1.4558 +      aPositions.AppendElement(position);
  1.4559 +      if (!it.IsOriginalCharSkipped() &&
  1.4560 +          textRun->IsLigatureGroupStart(it.GetSkippedOffset()) &&
  1.4561 +          textRun->IsClusterStart(it.GetSkippedOffset())) {
  1.4562 +        // A real visible character.
  1.4563 +        uint32_t length = ClusterLength(textRun, it);
  1.4564 +        nscoord advance = textRun->GetAdvanceWidth(it.GetSkippedOffset(),
  1.4565 +                                                   length, nullptr);
  1.4566 +        position.x += textRun->IsRightToLeft() ? -advance : advance;
  1.4567 +        lastPosition = position;
  1.4568 +      }
  1.4569 +      it.AdvanceOriginal(1);
  1.4570 +    }
  1.4571 +  }
  1.4572 +
  1.4573 +  // Finally any characters at the end that are not in a frame.
  1.4574 +  for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
  1.4575 +    aPositions.AppendElement(position);
  1.4576 +  }
  1.4577 +}
  1.4578 +
  1.4579 +/**
  1.4580 + * Physical text-anchor values.
  1.4581 + */
  1.4582 +enum TextAnchorSide {
  1.4583 +  eAnchorLeft,
  1.4584 +  eAnchorMiddle,
  1.4585 +  eAnchorRight
  1.4586 +};
  1.4587 +
  1.4588 +/**
  1.4589 + * Converts a logical text-anchor value to its physical value, based on whether
  1.4590 + * it is for an RTL frame.
  1.4591 + */
  1.4592 +static TextAnchorSide
  1.4593 +ConvertLogicalTextAnchorToPhysical(uint8_t aTextAnchor, bool aIsRightToLeft)
  1.4594 +{
  1.4595 +  NS_ASSERTION(aTextAnchor <= 3, "unexpected value for aTextAnchor");
  1.4596 +  if (!aIsRightToLeft)
  1.4597 +    return TextAnchorSide(aTextAnchor);
  1.4598 +  return TextAnchorSide(2 - aTextAnchor);
  1.4599 +}
  1.4600 +
  1.4601 +/**
  1.4602 + * Shifts the recorded character positions for an anchored chunk.
  1.4603 + *
  1.4604 + * @param aCharPositions The recorded character positions.
  1.4605 + * @param aChunkStart The character index the starts the anchored chunk.  This
  1.4606 + *   character's initial position is the anchor point.
  1.4607 + * @param aChunkEnd The character index just after the end of the anchored
  1.4608 + *   chunk.
  1.4609 + * @param aLeftEdge The left-most edge of any of the glyphs within the
  1.4610 + *   anchored chunk.
  1.4611 + * @param aRightEdge The right-most edge of any of the glyphs within the
  1.4612 + *   anchored chunk.
  1.4613 + * @param aAnchorSide The direction to anchor.
  1.4614 + */
  1.4615 +static void
  1.4616 +ShiftAnchoredChunk(nsTArray<mozilla::CharPosition>& aCharPositions,
  1.4617 +                   uint32_t aChunkStart,
  1.4618 +                   uint32_t aChunkEnd,
  1.4619 +                   gfxFloat aLeftEdge,
  1.4620 +                   gfxFloat aRightEdge,
  1.4621 +                   TextAnchorSide aAnchorSide)
  1.4622 +{
  1.4623 +  NS_ASSERTION(aLeftEdge <= aRightEdge, "unexpected anchored chunk edges");
  1.4624 +  NS_ASSERTION(aChunkStart < aChunkEnd, "unexpected values for aChunkStart and "
  1.4625 +                                        "aChunkEnd");
  1.4626 +
  1.4627 +  gfxFloat shift = aCharPositions[aChunkStart].mPosition.x;
  1.4628 +  switch (aAnchorSide) {
  1.4629 +    case eAnchorLeft:
  1.4630 +      shift -= aLeftEdge;
  1.4631 +      break;
  1.4632 +    case eAnchorMiddle:
  1.4633 +      shift -= (aLeftEdge + aRightEdge) / 2;
  1.4634 +      break;
  1.4635 +    case eAnchorRight:
  1.4636 +      shift -= aRightEdge;
  1.4637 +      break;
  1.4638 +    default:
  1.4639 +      NS_NOTREACHED("unexpected value for aAnchorSide");
  1.4640 +  }
  1.4641 +
  1.4642 +  if (shift != 0.0) {
  1.4643 +    for (uint32_t i = aChunkStart; i < aChunkEnd; i++) {
  1.4644 +      aCharPositions[i].mPosition.x += shift;
  1.4645 +    }
  1.4646 +  }
  1.4647 +}
  1.4648 +
  1.4649 +void
  1.4650 +SVGTextFrame::AdjustChunksForLineBreaks()
  1.4651 +{
  1.4652 +  nsBlockFrame* block = nsLayoutUtils::GetAsBlock(GetFirstPrincipalChild());
  1.4653 +  NS_ASSERTION(block, "expected block frame");
  1.4654 +
  1.4655 +  nsBlockFrame::line_iterator line = block->begin_lines();
  1.4656 +
  1.4657 +  CharIterator it(this, CharIterator::eOriginal);
  1.4658 +  while (!it.AtEnd() && line != block->end_lines()) {
  1.4659 +    if (it.TextFrame() == line->mFirstChild) {
  1.4660 +      mPositions[it.TextElementCharIndex()].mStartOfChunk = true;
  1.4661 +      line++;
  1.4662 +    }
  1.4663 +    it.AdvancePastCurrentFrame();
  1.4664 +  }
  1.4665 +}
  1.4666 +
  1.4667 +void
  1.4668 +SVGTextFrame::AdjustPositionsForClusters()
  1.4669 +{
  1.4670 +  nsPresContext* presContext = PresContext();
  1.4671 +
  1.4672 +  CharIterator it(this, CharIterator::eClusterOrLigatureGroupMiddle);
  1.4673 +  while (!it.AtEnd()) {
  1.4674 +    // Find the start of the cluster/ligature group.
  1.4675 +    uint32_t charIndex = it.TextElementCharIndex();
  1.4676 +    uint32_t startIndex = it.GlyphStartTextElementCharIndex();
  1.4677 +
  1.4678 +    mPositions[charIndex].mClusterOrLigatureGroupMiddle = true;
  1.4679 +
  1.4680 +    // Don't allow different rotations on ligature parts.
  1.4681 +    bool rotationAdjusted = false;
  1.4682 +    double angle = mPositions[startIndex].mAngle;
  1.4683 +    if (mPositions[charIndex].mAngle != angle) {
  1.4684 +      mPositions[charIndex].mAngle = angle;
  1.4685 +      rotationAdjusted = true;
  1.4686 +    }
  1.4687 +
  1.4688 +    // Find out the partial glyph advance for this character and update
  1.4689 +    // the character position.
  1.4690 +    uint32_t partLength =
  1.4691 +      charIndex - startIndex - it.GlyphUndisplayedCharacters();
  1.4692 +    gfxFloat advance =
  1.4693 +      it.GetGlyphPartialAdvance(partLength, presContext) / mFontSizeScaleFactor;
  1.4694 +    gfxPoint direction = gfxPoint(cos(angle), sin(angle)) *
  1.4695 +                         (it.TextRun()->IsRightToLeft() ? -1.0 : 1.0);
  1.4696 +    mPositions[charIndex].mPosition = mPositions[startIndex].mPosition +
  1.4697 +                                      direction * advance;
  1.4698 +
  1.4699 +    // Ensure any runs that would end in the middle of a ligature now end just
  1.4700 +    // after the ligature.
  1.4701 +    if (mPositions[charIndex].mRunBoundary) {
  1.4702 +      mPositions[charIndex].mRunBoundary = false;
  1.4703 +      if (charIndex + 1 < mPositions.Length()) {
  1.4704 +        mPositions[charIndex + 1].mRunBoundary = true;
  1.4705 +      }
  1.4706 +    } else if (rotationAdjusted) {
  1.4707 +      if (charIndex + 1 < mPositions.Length()) {
  1.4708 +        mPositions[charIndex + 1].mRunBoundary = true;
  1.4709 +      }
  1.4710 +    }
  1.4711 +
  1.4712 +    // Ensure any anchored chunks that would begin in the middle of a ligature
  1.4713 +    // now begin just after the ligature.
  1.4714 +    if (mPositions[charIndex].mStartOfChunk) {
  1.4715 +      mPositions[charIndex].mStartOfChunk = false;
  1.4716 +      if (charIndex + 1 < mPositions.Length()) {
  1.4717 +        mPositions[charIndex + 1].mStartOfChunk = true;
  1.4718 +      }
  1.4719 +    }
  1.4720 +
  1.4721 +    it.Next();
  1.4722 +  }
  1.4723 +}
  1.4724 +
  1.4725 +nsIFrame*
  1.4726 +SVGTextFrame::GetTextPathPathFrame(nsIFrame* aTextPathFrame)
  1.4727 +{
  1.4728 +  nsSVGTextPathProperty *property = static_cast<nsSVGTextPathProperty*>
  1.4729 +    (aTextPathFrame->Properties().Get(nsSVGEffects::HrefProperty()));
  1.4730 +
  1.4731 +  if (!property) {
  1.4732 +    nsIContent* content = aTextPathFrame->GetContent();
  1.4733 +    dom::SVGTextPathElement* tp = static_cast<dom::SVGTextPathElement*>(content);
  1.4734 +    nsAutoString href;
  1.4735 +    tp->mStringAttributes[dom::SVGTextPathElement::HREF].GetAnimValue(href, tp);
  1.4736 +    if (href.IsEmpty()) {
  1.4737 +      return nullptr; // no URL
  1.4738 +    }
  1.4739 +
  1.4740 +    nsCOMPtr<nsIURI> targetURI;
  1.4741 +    nsCOMPtr<nsIURI> base = content->GetBaseURI();
  1.4742 +    nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
  1.4743 +                                              content->GetCurrentDoc(), base);
  1.4744 +
  1.4745 +    property = nsSVGEffects::GetTextPathProperty(targetURI, aTextPathFrame,
  1.4746 +                                                 nsSVGEffects::HrefProperty());
  1.4747 +    if (!property)
  1.4748 +      return nullptr;
  1.4749 +  }
  1.4750 +
  1.4751 +  return property->GetReferencedFrame(nsGkAtoms::svgPathGeometryFrame, nullptr);
  1.4752 +}
  1.4753 +
  1.4754 +TemporaryRef<Path>
  1.4755 +SVGTextFrame::GetTextPath(nsIFrame* aTextPathFrame)
  1.4756 +{
  1.4757 +  nsIFrame *pathFrame = GetTextPathPathFrame(aTextPathFrame);
  1.4758 +
  1.4759 +  if (!pathFrame) {
  1.4760 +    return nullptr;
  1.4761 +  }
  1.4762 +
  1.4763 +  nsSVGPathGeometryElement *element =
  1.4764 +    static_cast<nsSVGPathGeometryElement*>(pathFrame->GetContent());
  1.4765 +
  1.4766 +  RefPtr<Path> path = element->GetPathForLengthOrPositionMeasuring();
  1.4767 +  if (!path) {
  1.4768 +    return nullptr;
  1.4769 +  }
  1.4770 +
  1.4771 +  gfxMatrix matrix = element->PrependLocalTransformsTo(gfxMatrix());
  1.4772 +  if (!matrix.IsIdentity()) {
  1.4773 +    RefPtr<PathBuilder> builder =
  1.4774 +      path->TransformedCopyToBuilder(ToMatrix(matrix));
  1.4775 +    path = builder->Finish();
  1.4776 +  }
  1.4777 +
  1.4778 +  return path.forget();
  1.4779 +}
  1.4780 +
  1.4781 +gfxFloat
  1.4782 +SVGTextFrame::GetOffsetScale(nsIFrame* aTextPathFrame)
  1.4783 +{
  1.4784 +  nsIFrame *pathFrame = GetTextPathPathFrame(aTextPathFrame);
  1.4785 +  if (!pathFrame)
  1.4786 +    return 1.0;
  1.4787 +
  1.4788 +  return static_cast<dom::SVGPathElement*>(pathFrame->GetContent())->
  1.4789 +    GetPathLengthScale(dom::SVGPathElement::eForTextPath);
  1.4790 +}
  1.4791 +
  1.4792 +gfxFloat
  1.4793 +SVGTextFrame::GetStartOffset(nsIFrame* aTextPathFrame)
  1.4794 +{
  1.4795 +  dom::SVGTextPathElement *tp =
  1.4796 +    static_cast<dom::SVGTextPathElement*>(aTextPathFrame->GetContent());
  1.4797 +  nsSVGLength2 *length =
  1.4798 +    &tp->mLengthAttributes[dom::SVGTextPathElement::STARTOFFSET];
  1.4799 +
  1.4800 +  if (length->IsPercentage()) {
  1.4801 +    RefPtr<Path> data = GetTextPath(aTextPathFrame);
  1.4802 +    return data ?
  1.4803 +      length->GetAnimValInSpecifiedUnits() * data->ComputeLength() / 100.0 :
  1.4804 +      0.0;
  1.4805 +  }
  1.4806 +  return length->GetAnimValue(tp) * GetOffsetScale(aTextPathFrame);
  1.4807 +}
  1.4808 +
  1.4809 +void
  1.4810 +SVGTextFrame::DoTextPathLayout()
  1.4811 +{
  1.4812 +  nsPresContext* context = PresContext();
  1.4813 +
  1.4814 +  CharIterator it(this, CharIterator::eClusterAndLigatureGroupStart);
  1.4815 +  while (!it.AtEnd()) {
  1.4816 +    nsIFrame* textPathFrame = it.TextPathFrame();
  1.4817 +    if (!textPathFrame) {
  1.4818 +      // Skip past this frame if we're not in a text path.
  1.4819 +      it.AdvancePastCurrentFrame();
  1.4820 +      continue;
  1.4821 +    }
  1.4822 +
  1.4823 +    // Get the path itself.
  1.4824 +    RefPtr<Path> path = GetTextPath(textPathFrame);
  1.4825 +    if (!path) {
  1.4826 +      it.AdvancePastCurrentTextPathFrame();
  1.4827 +      continue;
  1.4828 +    }
  1.4829 +
  1.4830 +    nsIContent* textPath = textPathFrame->GetContent();
  1.4831 +
  1.4832 +    gfxFloat offset = GetStartOffset(textPathFrame);
  1.4833 +    Float pathLength = path->ComputeLength();
  1.4834 +
  1.4835 +    // Loop for each text frame in the text path.
  1.4836 +    do {
  1.4837 +      uint32_t i = it.TextElementCharIndex();
  1.4838 +      gfxFloat halfAdvance =
  1.4839 +        it.GetGlyphAdvance(context) / mFontSizeScaleFactor / 2.0;
  1.4840 +      gfxFloat sign = it.TextRun()->IsRightToLeft() ? -1.0 : 1.0;
  1.4841 +      gfxFloat midx = mPositions[i].mPosition.x + sign * halfAdvance + offset;
  1.4842 +
  1.4843 +      // Hide the character if it falls off the end of the path.
  1.4844 +      mPositions[i].mHidden = midx < 0 || midx > pathLength;
  1.4845 +
  1.4846 +      // Position the character on the path at the right angle.
  1.4847 +      Point tangent; // Unit vector tangent to the point we find.
  1.4848 +      Point pt = path->ComputePointAtLength(Float(midx), &tangent);
  1.4849 +      Float rotation = atan2f(tangent.y, tangent.x);
  1.4850 +      Point normal(-tangent.y, tangent.x); // Unit vector normal to the point.
  1.4851 +      Point offsetFromPath = normal * mPositions[i].mPosition.y;
  1.4852 +      pt += offsetFromPath;
  1.4853 +      Point direction = tangent * sign;
  1.4854 +      mPositions[i].mPosition = ThebesPoint(pt) - ThebesPoint(direction) * halfAdvance;
  1.4855 +      mPositions[i].mAngle += rotation;
  1.4856 +
  1.4857 +      // Position any characters for a partial ligature.
  1.4858 +      for (uint32_t j = i + 1;
  1.4859 +           j < mPositions.Length() && mPositions[j].mClusterOrLigatureGroupMiddle;
  1.4860 +           j++) {
  1.4861 +        gfxPoint partialAdvance =
  1.4862 +          ThebesPoint(direction) * it.GetGlyphPartialAdvance(j - i, context) /
  1.4863 +                                                         mFontSizeScaleFactor;
  1.4864 +        mPositions[j].mPosition = mPositions[i].mPosition + partialAdvance;
  1.4865 +        mPositions[j].mAngle = mPositions[i].mAngle;
  1.4866 +        mPositions[j].mHidden = mPositions[i].mHidden;
  1.4867 +      }
  1.4868 +      it.Next();
  1.4869 +    } while (it.TextPathFrame() &&
  1.4870 +             it.TextPathFrame()->GetContent() == textPath);
  1.4871 +  }
  1.4872 +}
  1.4873 +
  1.4874 +void
  1.4875 +SVGTextFrame::DoAnchoring()
  1.4876 +{
  1.4877 +  nsPresContext* presContext = PresContext();
  1.4878 +
  1.4879 +  CharIterator it(this, CharIterator::eOriginal);
  1.4880 +
  1.4881 +  // Don't need to worry about skipped or trimmed characters.
  1.4882 +  while (!it.AtEnd() &&
  1.4883 +         (it.IsOriginalCharSkipped() || it.IsOriginalCharTrimmed())) {
  1.4884 +    it.Next();
  1.4885 +  }
  1.4886 +
  1.4887 +  uint32_t start = it.TextElementCharIndex();
  1.4888 +  while (start < mPositions.Length()) {
  1.4889 +    it.AdvanceToCharacter(start);
  1.4890 +    nsTextFrame* chunkFrame = it.TextFrame();
  1.4891 +
  1.4892 +    // Measure characters in this chunk to find the left-most and right-most
  1.4893 +    // edges of all glyphs within the chunk.
  1.4894 +    uint32_t index = it.TextElementCharIndex();
  1.4895 +    uint32_t end = start;
  1.4896 +    gfxFloat left = std::numeric_limits<gfxFloat>::infinity();
  1.4897 +    gfxFloat right = -std::numeric_limits<gfxFloat>::infinity();
  1.4898 +    do {
  1.4899 +      if (!it.IsOriginalCharSkipped() && !it.IsOriginalCharTrimmed()) {
  1.4900 +        gfxFloat advance = it.GetAdvance(presContext) / mFontSizeScaleFactor;
  1.4901 +        if (it.TextRun()->IsRightToLeft()) {
  1.4902 +          left  = std::min(left,  mPositions[index].mPosition.x - advance);
  1.4903 +          right = std::max(right, mPositions[index].mPosition.x);
  1.4904 +        } else {
  1.4905 +          left  = std::min(left,  mPositions[index].mPosition.x);
  1.4906 +          right = std::max(right, mPositions[index].mPosition.x + advance);
  1.4907 +        }
  1.4908 +      }
  1.4909 +      it.Next();
  1.4910 +      index = end = it.TextElementCharIndex();
  1.4911 +    } while (!it.AtEnd() && !mPositions[end].mStartOfChunk);
  1.4912 +
  1.4913 +    if (left != std::numeric_limits<gfxFloat>::infinity()) {
  1.4914 +      bool isRTL =
  1.4915 +        chunkFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  1.4916 +      TextAnchorSide anchor =
  1.4917 +        ConvertLogicalTextAnchorToPhysical(chunkFrame->StyleSVG()->mTextAnchor,
  1.4918 +                                           isRTL);
  1.4919 +
  1.4920 +      ShiftAnchoredChunk(mPositions, start, end, left, right, anchor);
  1.4921 +    }
  1.4922 +
  1.4923 +    start = it.TextElementCharIndex();
  1.4924 +  }
  1.4925 +}
  1.4926 +
  1.4927 +void
  1.4928 +SVGTextFrame::DoGlyphPositioning()
  1.4929 +{
  1.4930 +  mPositions.Clear();
  1.4931 +  RemoveStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
  1.4932 +
  1.4933 +  nsIFrame* kid = GetFirstPrincipalChild();
  1.4934 +  if (kid && NS_SUBTREE_DIRTY(kid)) {
  1.4935 +    MOZ_ASSERT(false, "should have already reflowed the kid");
  1.4936 +    return;
  1.4937 +  }
  1.4938 +
  1.4939 +  // Determine the positions of each character in app units.
  1.4940 +  nsTArray<nsPoint> charPositions;
  1.4941 +  DetermineCharPositions(charPositions);
  1.4942 +
  1.4943 +  if (charPositions.IsEmpty()) {
  1.4944 +    // No characters, so nothing to do.
  1.4945 +    return;
  1.4946 +  }
  1.4947 +
  1.4948 +  // If the textLength="" attribute was specified, then we need ResolvePositions
  1.4949 +  // to record that a new run starts with each glyph.
  1.4950 +  SVGTextContentElement* element = static_cast<SVGTextContentElement*>(mContent);
  1.4951 +  nsSVGLength2* textLengthAttr =
  1.4952 +    element->GetAnimatedLength(nsGkAtoms::textLength);
  1.4953 +  bool adjustingTextLength = textLengthAttr->IsExplicitlySet();
  1.4954 +  float expectedTextLength = textLengthAttr->GetAnimValue(element);
  1.4955 +
  1.4956 +  if (adjustingTextLength && expectedTextLength < 0.0f) {
  1.4957 +    // If textLength="" is less than zero, ignore it.
  1.4958 +    adjustingTextLength = false;
  1.4959 +  }
  1.4960 +
  1.4961 +  // Get the x, y, dx, dy, rotate values for the subtree.
  1.4962 +  nsTArray<gfxPoint> deltas;
  1.4963 +  if (!ResolvePositions(deltas, adjustingTextLength)) {
  1.4964 +    // If ResolvePositions returned false, it means that there were some
  1.4965 +    // characters in the DOM but none of them are displayed.  Clear out
  1.4966 +    // mPositions so that we don't attempt to do any painting later.
  1.4967 +    mPositions.Clear();
  1.4968 +    return;
  1.4969 +  }
  1.4970 +
  1.4971 +  // XXX We might be able to do less work when there is at most a single
  1.4972 +  // x/y/dx/dy position.
  1.4973 +
  1.4974 +  // Truncate the positioning arrays to the actual number of characters present.
  1.4975 +  TruncateTo(deltas, charPositions);
  1.4976 +  TruncateTo(mPositions, charPositions);
  1.4977 +
  1.4978 +  // Fill in an unspecified character position at index 0.
  1.4979 +  if (!mPositions[0].IsXSpecified()) {
  1.4980 +    mPositions[0].mPosition.x = 0.0;
  1.4981 +  }
  1.4982 +  if (!mPositions[0].IsYSpecified()) {
  1.4983 +    mPositions[0].mPosition.y = 0.0;
  1.4984 +  }
  1.4985 +  if (!mPositions[0].IsAngleSpecified()) {
  1.4986 +    mPositions[0].mAngle = 0.0;
  1.4987 +  }
  1.4988 +
  1.4989 +  nsPresContext* presContext = PresContext();
  1.4990 +
  1.4991 +  float cssPxPerDevPx = presContext->
  1.4992 +    AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  1.4993 +  double factor = cssPxPerDevPx / mFontSizeScaleFactor;
  1.4994 +
  1.4995 +  // Determine how much to compress or expand glyph positions due to
  1.4996 +  // textLength="" and lengthAdjust="".
  1.4997 +  double adjustment = 0.0;
  1.4998 +  mLengthAdjustScaleFactor = 1.0f;
  1.4999 +  if (adjustingTextLength) {
  1.5000 +    nscoord frameWidth = GetFirstPrincipalChild()->GetRect().width;
  1.5001 +    float actualTextLength =
  1.5002 +      static_cast<float>(presContext->AppUnitsToGfxUnits(frameWidth) * factor);
  1.5003 +
  1.5004 +    nsRefPtr<SVGAnimatedEnumeration> lengthAdjustEnum = element->LengthAdjust();
  1.5005 +    uint16_t lengthAdjust = lengthAdjustEnum->AnimVal();
  1.5006 +    switch (lengthAdjust) {
  1.5007 +      case SVG_LENGTHADJUST_SPACINGANDGLYPHS:
  1.5008 +        // Scale the glyphs and their positions.
  1.5009 +        if (actualTextLength > 0) {
  1.5010 +          mLengthAdjustScaleFactor = expectedTextLength / actualTextLength;
  1.5011 +        }
  1.5012 +        break;
  1.5013 +
  1.5014 +      default:
  1.5015 +        MOZ_ASSERT(lengthAdjust == SVG_LENGTHADJUST_SPACING);
  1.5016 +        // Just add space between each glyph.
  1.5017 +        int32_t adjustableSpaces = 0;
  1.5018 +        for (uint32_t i = 1; i < mPositions.Length(); i++) {
  1.5019 +          if (!mPositions[i].mUnaddressable) {
  1.5020 +            adjustableSpaces++;
  1.5021 +          }
  1.5022 +        }
  1.5023 +        if (adjustableSpaces) {
  1.5024 +          adjustment = (expectedTextLength - actualTextLength) / adjustableSpaces;
  1.5025 +        }
  1.5026 +        break;
  1.5027 +    }
  1.5028 +  }
  1.5029 +
  1.5030 +  // Fill in any unspecified character positions based on the positions recorded
  1.5031 +  // in charPositions, and also add in the dx/dy values.
  1.5032 +  if (!deltas.IsEmpty()) {
  1.5033 +    mPositions[0].mPosition += deltas[0];
  1.5034 +  }
  1.5035 +
  1.5036 +  for (uint32_t i = 1; i < mPositions.Length(); i++) {
  1.5037 +    // Fill in unspecified x position.
  1.5038 +    if (!mPositions[i].IsXSpecified()) {
  1.5039 +      nscoord d = charPositions[i].x - charPositions[i - 1].x;
  1.5040 +      mPositions[i].mPosition.x =
  1.5041 +        mPositions[i - 1].mPosition.x +
  1.5042 +        presContext->AppUnitsToGfxUnits(d) * factor * mLengthAdjustScaleFactor;
  1.5043 +      if (!mPositions[i].mUnaddressable) {
  1.5044 +        mPositions[i].mPosition.x += adjustment;
  1.5045 +      }
  1.5046 +    }
  1.5047 +    // Fill in unspecified y position.
  1.5048 +    if (!mPositions[i].IsYSpecified()) {
  1.5049 +      nscoord d = charPositions[i].y - charPositions[i - 1].y;
  1.5050 +      mPositions[i].mPosition.y =
  1.5051 +        mPositions[i - 1].mPosition.y +
  1.5052 +        presContext->AppUnitsToGfxUnits(d) * factor;
  1.5053 +    }
  1.5054 +    // Add in dx/dy.
  1.5055 +    if (i < deltas.Length()) {
  1.5056 +      mPositions[i].mPosition += deltas[i];
  1.5057 +    }
  1.5058 +    // Fill in unspecified rotation values.
  1.5059 +    if (!mPositions[i].IsAngleSpecified()) {
  1.5060 +      mPositions[i].mAngle = 0.0f;
  1.5061 +    }
  1.5062 +  }
  1.5063 +
  1.5064 +  MOZ_ASSERT(mPositions.Length() == charPositions.Length());
  1.5065 +
  1.5066 +  AdjustChunksForLineBreaks();
  1.5067 +  AdjustPositionsForClusters();
  1.5068 +  DoAnchoring();
  1.5069 +  DoTextPathLayout();
  1.5070 +}
  1.5071 +
  1.5072 +bool
  1.5073 +SVGTextFrame::ShouldRenderAsPath(nsRenderingContext* aContext,
  1.5074 +                                 nsTextFrame* aFrame,
  1.5075 +                                 bool& aShouldPaintSVGGlyphs)
  1.5076 +{
  1.5077 +  // Rendering to a clip path.
  1.5078 +  if (SVGAutoRenderState::GetRenderMode(aContext) != SVGAutoRenderState::NORMAL) {
  1.5079 +    aShouldPaintSVGGlyphs = false;
  1.5080 +    return true;
  1.5081 +  }
  1.5082 +
  1.5083 +  aShouldPaintSVGGlyphs = true;
  1.5084 +
  1.5085 +  const nsStyleSVG* style = aFrame->StyleSVG();
  1.5086 +
  1.5087 +  // Fill is a non-solid paint, has a non-default fill-rule or has
  1.5088 +  // non-1 opacity.
  1.5089 +  if (!(style->mFill.mType == eStyleSVGPaintType_None ||
  1.5090 +        (style->mFill.mType == eStyleSVGPaintType_Color &&
  1.5091 +         style->mFillOpacity == 1))) {
  1.5092 +    return true;
  1.5093 +  }
  1.5094 +
  1.5095 +  // Text has a stroke.
  1.5096 +  if (style->HasStroke() &&
  1.5097 +      SVGContentUtils::CoordToFloat(PresContext(),
  1.5098 +                                    static_cast<nsSVGElement*>(mContent),
  1.5099 +                                    style->mStrokeWidth) > 0) {
  1.5100 +    return true;
  1.5101 +  }
  1.5102 +
  1.5103 +  return false;
  1.5104 +}
  1.5105 +
  1.5106 +void
  1.5107 +SVGTextFrame::ScheduleReflowSVG()
  1.5108 +{
  1.5109 +  if (mState & NS_FRAME_IS_NONDISPLAY) {
  1.5110 +    ScheduleReflowSVGNonDisplayText();
  1.5111 +  } else {
  1.5112 +    nsSVGUtils::ScheduleReflowSVG(this);
  1.5113 +  }
  1.5114 +}
  1.5115 +
  1.5116 +void
  1.5117 +SVGTextFrame::NotifyGlyphMetricsChange()
  1.5118 +{
  1.5119 +  AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
  1.5120 +  nsSVGEffects::InvalidateRenderingObservers(this);
  1.5121 +  ScheduleReflowSVG();
  1.5122 +}
  1.5123 +
  1.5124 +void
  1.5125 +SVGTextFrame::UpdateGlyphPositioning()
  1.5126 +{
  1.5127 +  nsIFrame* kid = GetFirstPrincipalChild();
  1.5128 +  if (!kid) {
  1.5129 +    return;
  1.5130 +  }
  1.5131 +
  1.5132 +  if (mState & NS_STATE_SVG_POSITIONING_DIRTY) {
  1.5133 +    DoGlyphPositioning();
  1.5134 +  }
  1.5135 +}
  1.5136 +
  1.5137 +void
  1.5138 +SVGTextFrame::MaybeReflowAnonymousBlockChild()
  1.5139 +{
  1.5140 +  nsIFrame* kid = GetFirstPrincipalChild();
  1.5141 +  if (!kid)
  1.5142 +    return;
  1.5143 +
  1.5144 +  NS_ASSERTION(!(kid->GetStateBits() & NS_FRAME_IN_REFLOW),
  1.5145 +               "should not be in reflow when about to reflow again");
  1.5146 +
  1.5147 +  if (NS_SUBTREE_DIRTY(this)) {
  1.5148 +    if (mState & NS_FRAME_IS_DIRTY) {
  1.5149 +      // If we require a full reflow, ensure our kid is marked fully dirty.
  1.5150 +      // (Note that our anonymous nsBlockFrame is not an nsISVGChildFrame, so
  1.5151 +      // even when we are called via our ReflowSVG this will not be done for us
  1.5152 +      // by nsSVGDisplayContainerFrame::ReflowSVG.)
  1.5153 +      kid->AddStateBits(NS_FRAME_IS_DIRTY);
  1.5154 +    }
  1.5155 +    MOZ_ASSERT(nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(this),
  1.5156 +               "should be under ReflowSVG");
  1.5157 +    nsPresContext::InterruptPreventer noInterrupts(PresContext());
  1.5158 +    DoReflow();
  1.5159 +  }
  1.5160 +}
  1.5161 +
  1.5162 +void
  1.5163 +SVGTextFrame::DoReflow()
  1.5164 +{
  1.5165 +  // Since we are going to reflow the anonymous block frame, we will
  1.5166 +  // need to update mPositions.
  1.5167 +  AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
  1.5168 +
  1.5169 +  if (mState & NS_FRAME_IS_NONDISPLAY) {
  1.5170 +    // Normally, these dirty flags would be cleared in ReflowSVG(), but that
  1.5171 +    // doesn't get called for non-display frames. We don't want to reflow our
  1.5172 +    // descendants every time SVGTextFrame::PaintSVG makes sure that we have
  1.5173 +    // valid positions by calling UpdateGlyphPositioning(), so we need to clear
  1.5174 +    // these dirty bits. Note that this also breaks an invalidation loop where
  1.5175 +    // our descendants invalidate as they reflow, which invalidates rendering
  1.5176 +    // observers, which reschedules the frame that is currently painting by
  1.5177 +    // referencing us to paint again. See bug 839958 comment 7. Hopefully we
  1.5178 +    // will break that loop more convincingly at some point.
  1.5179 +    mState &= ~(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
  1.5180 +  }
  1.5181 +
  1.5182 +  nsPresContext *presContext = PresContext();
  1.5183 +  nsIFrame* kid = GetFirstPrincipalChild();
  1.5184 +  if (!kid)
  1.5185 +    return;
  1.5186 +
  1.5187 +  nsRefPtr<nsRenderingContext> renderingContext =
  1.5188 +    presContext->PresShell()->CreateReferenceRenderingContext();
  1.5189 +
  1.5190 +  if (UpdateFontSizeScaleFactor()) {
  1.5191 +    // If the font size scale factor changed, we need the block to report
  1.5192 +    // an updated preferred width.
  1.5193 +    kid->MarkIntrinsicWidthsDirty();
  1.5194 +  }
  1.5195 +
  1.5196 +  mState |= NS_STATE_SVG_TEXT_IN_REFLOW;
  1.5197 +
  1.5198 +  nscoord width = kid->GetPrefWidth(renderingContext);
  1.5199 +  nsHTMLReflowState reflowState(presContext, kid,
  1.5200 +                                renderingContext,
  1.5201 +                                nsSize(width, NS_UNCONSTRAINEDSIZE));
  1.5202 +  nsHTMLReflowMetrics desiredSize(reflowState);
  1.5203 +  nsReflowStatus status;
  1.5204 +
  1.5205 +  NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) &&
  1.5206 +               reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
  1.5207 +               "style system should ensure that :-moz-svg-text "
  1.5208 +               "does not get styled");
  1.5209 +
  1.5210 +  kid->WillReflow(presContext);
  1.5211 +  kid->Reflow(presContext, desiredSize, reflowState, status);
  1.5212 +  kid->DidReflow(presContext, &reflowState, nsDidReflowStatus::FINISHED);
  1.5213 +  kid->SetSize(nsSize(desiredSize.Width(), desiredSize.Height()));
  1.5214 +
  1.5215 +  mState &= ~NS_STATE_SVG_TEXT_IN_REFLOW;
  1.5216 +
  1.5217 +  TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
  1.5218 +}
  1.5219 +
  1.5220 +// Usable font size range in devpixels / user-units
  1.5221 +#define CLAMP_MIN_SIZE 8.0
  1.5222 +#define CLAMP_MAX_SIZE 200.0
  1.5223 +#define PRECISE_SIZE   200.0
  1.5224 +
  1.5225 +bool
  1.5226 +SVGTextFrame::UpdateFontSizeScaleFactor()
  1.5227 +{
  1.5228 +  double oldFontSizeScaleFactor = mFontSizeScaleFactor;
  1.5229 +
  1.5230 +  nsPresContext* presContext = PresContext();
  1.5231 +
  1.5232 +  bool geometricPrecision = false;
  1.5233 +  nscoord min = nscoord_MAX,
  1.5234 +          max = nscoord_MIN;
  1.5235 +
  1.5236 +  // Find the minimum and maximum font sizes used over all the
  1.5237 +  // nsTextFrames.
  1.5238 +  TextFrameIterator it(this);
  1.5239 +  nsTextFrame* f = it.Current();
  1.5240 +  while (f) {
  1.5241 +    if (!geometricPrecision) {
  1.5242 +      // Unfortunately we can't treat text-rendering:geometricPrecision
  1.5243 +      // separately for each text frame.
  1.5244 +      geometricPrecision = f->StyleSVG()->mTextRendering ==
  1.5245 +                             NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION;
  1.5246 +    }
  1.5247 +    nscoord size = f->StyleFont()->mFont.size;
  1.5248 +    if (size) {
  1.5249 +      min = std::min(min, size);
  1.5250 +      max = std::max(max, size);
  1.5251 +    }
  1.5252 +    f = it.Next();
  1.5253 +  }
  1.5254 +
  1.5255 +  if (min == nscoord_MAX) {
  1.5256 +    // No text, so no need for scaling.
  1.5257 +    mFontSizeScaleFactor = 1.0;
  1.5258 +    return mFontSizeScaleFactor != oldFontSizeScaleFactor;
  1.5259 +  }
  1.5260 +
  1.5261 +  double minSize = presContext->AppUnitsToFloatCSSPixels(min);
  1.5262 +
  1.5263 +  if (geometricPrecision) {
  1.5264 +    // We want to ensure minSize is scaled to PRECISE_SIZE.
  1.5265 +    mFontSizeScaleFactor = PRECISE_SIZE / minSize;
  1.5266 +    return mFontSizeScaleFactor != oldFontSizeScaleFactor;
  1.5267 +  }
  1.5268 +
  1.5269 +  // When we are non-display, we could be painted in different coordinate
  1.5270 +  // spaces, and we don't want to have to reflow for each of these.  We
  1.5271 +  // just assume that the context scale is 1.0 for them all, so we don't
  1.5272 +  // get stuck with a font size scale factor based on whichever referencing
  1.5273 +  // frame happens to reflow first.
  1.5274 +  double contextScale = 1.0;
  1.5275 +  if (!(mState & NS_FRAME_IS_NONDISPLAY)) {
  1.5276 +    gfxMatrix m(GetCanvasTM(FOR_OUTERSVG_TM));
  1.5277 +    if (!m.IsSingular()) {
  1.5278 +      contextScale = GetContextScale(m);
  1.5279 +    }
  1.5280 +  }
  1.5281 +  mLastContextScale = contextScale;
  1.5282 +
  1.5283 +  double maxSize = presContext->AppUnitsToFloatCSSPixels(max);
  1.5284 +
  1.5285 +  // But we want to ignore any scaling required due to HiDPI displays, since
  1.5286 +  // regular CSS text frames will still create text runs using the font size
  1.5287 +  // in CSS pixels, and we want SVG text to have the same rendering as HTML
  1.5288 +  // text for regular font sizes.
  1.5289 +  float cssPxPerDevPx =
  1.5290 +    presContext->AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  1.5291 +  contextScale *= cssPxPerDevPx;
  1.5292 +
  1.5293 +  double minTextRunSize = minSize * contextScale;
  1.5294 +  double maxTextRunSize = maxSize * contextScale;
  1.5295 +
  1.5296 +  if (minTextRunSize >= CLAMP_MIN_SIZE &&
  1.5297 +      maxTextRunSize <= CLAMP_MAX_SIZE) {
  1.5298 +    // We are already in the ideal font size range for all text frames,
  1.5299 +    // so we only have to take into account the contextScale.
  1.5300 +    mFontSizeScaleFactor = contextScale;
  1.5301 +  } else if (maxSize / minSize > CLAMP_MAX_SIZE / CLAMP_MIN_SIZE) {
  1.5302 +    // We can't scale the font sizes so that all of the text frames lie
  1.5303 +    // within our ideal font size range, so we treat the minimum as more
  1.5304 +    // important and just scale so that minSize = CLAMP_MIN_SIZE.
  1.5305 +    mFontSizeScaleFactor = CLAMP_MIN_SIZE / minTextRunSize;
  1.5306 +  } else if (minTextRunSize < CLAMP_MIN_SIZE) {
  1.5307 +    mFontSizeScaleFactor = CLAMP_MIN_SIZE / minTextRunSize;
  1.5308 +  } else {
  1.5309 +    mFontSizeScaleFactor = CLAMP_MAX_SIZE / maxTextRunSize;
  1.5310 +  }
  1.5311 +
  1.5312 +  return mFontSizeScaleFactor != oldFontSizeScaleFactor;
  1.5313 +}
  1.5314 +
  1.5315 +double
  1.5316 +SVGTextFrame::GetFontSizeScaleFactor() const
  1.5317 +{
  1.5318 +  return mFontSizeScaleFactor;
  1.5319 +}
  1.5320 +
  1.5321 +/**
  1.5322 + * Take aPoint, which is in the <text> element's user space, and convert
  1.5323 + * it to the appropriate frame user space of aChildFrame according to
  1.5324 + * which rendered run the point hits.
  1.5325 + */
  1.5326 +gfxPoint
  1.5327 +SVGTextFrame::TransformFramePointToTextChild(const gfxPoint& aPoint,
  1.5328 +                                             nsIFrame* aChildFrame)
  1.5329 +{
  1.5330 +  NS_ASSERTION(aChildFrame &&
  1.5331 +               nsLayoutUtils::GetClosestFrameOfType
  1.5332 +                 (aChildFrame->GetParent(), nsGkAtoms::svgTextFrame) == this,
  1.5333 +               "aChildFrame must be a descendant of this frame");
  1.5334 +
  1.5335 +  UpdateGlyphPositioning();
  1.5336 +
  1.5337 +  nsPresContext* presContext = PresContext();
  1.5338 +
  1.5339 +  // Add in the mRect offset to aPoint, as that will have been taken into
  1.5340 +  // account when transforming the point from the ancestor frame down
  1.5341 +  // to this one.
  1.5342 +  float cssPxPerDevPx = presContext->
  1.5343 +    AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  1.5344 +  float factor = presContext->AppUnitsPerCSSPixel();
  1.5345 +  gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
  1.5346 +                         NSAppUnitsToFloatPixels(mRect.y, factor));
  1.5347 +  gfxPoint pointInUserSpace = aPoint * cssPxPerDevPx + framePosition;
  1.5348 +
  1.5349 +  // Find the closest rendered run for the text frames beneath aChildFrame.
  1.5350 +  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
  1.5351 +                             aChildFrame);
  1.5352 +  TextRenderedRun hit;
  1.5353 +  gfxPoint pointInRun;
  1.5354 +  nscoord dx = nscoord_MAX;
  1.5355 +  nscoord dy = nscoord_MAX;
  1.5356 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.5357 +    uint32_t flags = TextRenderedRun::eIncludeFill |
  1.5358 +                     TextRenderedRun::eIncludeStroke |
  1.5359 +                     TextRenderedRun::eNoHorizontalOverflow;
  1.5360 +    gfxRect runRect = run.GetRunUserSpaceRect(presContext, flags).ToThebesRect();
  1.5361 +
  1.5362 +    gfxPoint pointInRunUserSpace =
  1.5363 +      run.GetTransformFromRunUserSpaceToUserSpace(presContext).Invert().
  1.5364 +          Transform(pointInUserSpace);
  1.5365 +
  1.5366 +    if (Inside(runRect, pointInRunUserSpace)) {
  1.5367 +      // The point was inside the rendered run's rect, so we choose it.
  1.5368 +      dx = 0;
  1.5369 +      dy = 0;
  1.5370 +      pointInRun = pointInRunUserSpace;
  1.5371 +      hit = run;
  1.5372 +    } else if (nsLayoutUtils::PointIsCloserToRect(pointInRunUserSpace,
  1.5373 +                                                  runRect, dx, dy)) {
  1.5374 +      // The point was closer to this rendered run's rect than any others
  1.5375 +      // we've seen so far.
  1.5376 +      pointInRun.x = clamped(pointInRunUserSpace.x,
  1.5377 +                             runRect.X(), runRect.XMost());
  1.5378 +      pointInRun.y = clamped(pointInRunUserSpace.y,
  1.5379 +                             runRect.Y(), runRect.YMost());
  1.5380 +      hit = run;
  1.5381 +    }
  1.5382 +  }
  1.5383 +
  1.5384 +  if (!hit.mFrame) {
  1.5385 +    // We didn't find any rendered runs for the frame.
  1.5386 +    return aPoint;
  1.5387 +  }
  1.5388 +
  1.5389 +  // Return the point in user units relative to the nsTextFrame,
  1.5390 +  // but taking into account mFontSizeScaleFactor.
  1.5391 +  gfxMatrix m = hit.GetTransformFromRunUserSpaceToFrameUserSpace(presContext);
  1.5392 +  m.Scale(mFontSizeScaleFactor, mFontSizeScaleFactor);
  1.5393 +  return m.Transform(pointInRun) / cssPxPerDevPx;
  1.5394 +}
  1.5395 +
  1.5396 +/**
  1.5397 + * For each rendered run for frames beneath aChildFrame, convert aRect
  1.5398 + * into the run's frame user space and intersect it with the run's
  1.5399 + * frame user space rectangle.  For each of these intersections,
  1.5400 + * then translate them up into aChildFrame's coordinate space
  1.5401 + * and union them all together.
  1.5402 + */
  1.5403 +gfxRect
  1.5404 +SVGTextFrame::TransformFrameRectToTextChild(const gfxRect& aRect,
  1.5405 +                                            nsIFrame* aChildFrame)
  1.5406 +{
  1.5407 +  NS_ASSERTION(aChildFrame &&
  1.5408 +               nsLayoutUtils::GetClosestFrameOfType
  1.5409 +                 (aChildFrame->GetParent(), nsGkAtoms::svgTextFrame) == this,
  1.5410 +               "aChildFrame must be a descendant of this frame");
  1.5411 +
  1.5412 +  UpdateGlyphPositioning();
  1.5413 +
  1.5414 +  nsPresContext* presContext = PresContext();
  1.5415 +
  1.5416 +  // Add in the mRect offset to aRect, as that will have been taken into
  1.5417 +  // account when transforming the rect from the ancestor frame down
  1.5418 +  // to this one.
  1.5419 +  float cssPxPerDevPx = presContext->
  1.5420 +    AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
  1.5421 +  float factor = presContext->AppUnitsPerCSSPixel();
  1.5422 +  gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
  1.5423 +                         NSAppUnitsToFloatPixels(mRect.y, factor));
  1.5424 +  gfxRect incomingRectInUserSpace(aRect.x * cssPxPerDevPx + framePosition.x,
  1.5425 +                                  aRect.y * cssPxPerDevPx + framePosition.y,
  1.5426 +                                  aRect.width * cssPxPerDevPx,
  1.5427 +                                  aRect.height * cssPxPerDevPx);
  1.5428 +
  1.5429 +  // Find each rendered run for text frames beneath aChildFrame.
  1.5430 +  gfxRect result;
  1.5431 +  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
  1.5432 +                             aChildFrame);
  1.5433 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.5434 +    // Convert the incoming rect into frame user space.
  1.5435 +    gfxMatrix m;
  1.5436 +    m.PreMultiply(run.GetTransformFromRunUserSpaceToUserSpace(presContext).Invert());
  1.5437 +    m.PreMultiply(run.GetTransformFromRunUserSpaceToFrameUserSpace(presContext));
  1.5438 +    gfxRect incomingRectInFrameUserSpace =
  1.5439 +      m.TransformBounds(incomingRectInUserSpace);
  1.5440 +
  1.5441 +    // Intersect it with this run's rectangle.
  1.5442 +    uint32_t flags = TextRenderedRun::eIncludeFill |
  1.5443 +                     TextRenderedRun::eIncludeStroke;
  1.5444 +    SVGBBox runRectInFrameUserSpace = run.GetFrameUserSpaceRect(presContext, flags);
  1.5445 +    if (runRectInFrameUserSpace.IsEmpty()) {
  1.5446 +      continue;
  1.5447 +    }
  1.5448 +    gfxRect runIntersectionInFrameUserSpace =
  1.5449 +      incomingRectInFrameUserSpace.Intersect(runRectInFrameUserSpace.ToThebesRect());
  1.5450 +
  1.5451 +    if (!runIntersectionInFrameUserSpace.IsEmpty()) {
  1.5452 +      // Take the font size scale into account.
  1.5453 +      runIntersectionInFrameUserSpace.x *= mFontSizeScaleFactor;
  1.5454 +      runIntersectionInFrameUserSpace.y *= mFontSizeScaleFactor;
  1.5455 +      runIntersectionInFrameUserSpace.width *= mFontSizeScaleFactor;
  1.5456 +      runIntersectionInFrameUserSpace.height *= mFontSizeScaleFactor;
  1.5457 +
  1.5458 +      // Convert it into the coordinate space of aChildFrame.
  1.5459 +      nsPoint offset = run.mFrame->GetOffsetTo(aChildFrame);
  1.5460 +      gfxRect runIntersection =
  1.5461 +        runIntersectionInFrameUserSpace +
  1.5462 +          gfxPoint(NSAppUnitsToFloatPixels(offset.x, factor),
  1.5463 +                   NSAppUnitsToFloatPixels(offset.y, factor));
  1.5464 +
  1.5465 +      // Union it into the result.
  1.5466 +      result.UnionRect(result, runIntersection);
  1.5467 +    }
  1.5468 +  }
  1.5469 +
  1.5470 +  return result;
  1.5471 +}
  1.5472 +
  1.5473 +/**
  1.5474 + * For each rendered run beneath aChildFrame, translate aRect from
  1.5475 + * aChildFrame to the run's text frame, transform it then into
  1.5476 + * the run's frame user space, intersect it with the run's
  1.5477 + * frame user space rect, then transform it up to user space.
  1.5478 + * The result is the union of all of these.
  1.5479 + */
  1.5480 +gfxRect
  1.5481 +SVGTextFrame::TransformFrameRectFromTextChild(const nsRect& aRect,
  1.5482 +                                              nsIFrame* aChildFrame)
  1.5483 +{
  1.5484 +  NS_ASSERTION(aChildFrame &&
  1.5485 +               nsLayoutUtils::GetClosestFrameOfType
  1.5486 +                 (aChildFrame->GetParent(), nsGkAtoms::svgTextFrame) == this,
  1.5487 +               "aChildFrame must be a descendant of this frame");
  1.5488 +
  1.5489 +  UpdateGlyphPositioning();
  1.5490 +
  1.5491 +  nsPresContext* presContext = PresContext();
  1.5492 +
  1.5493 +  gfxRect result;
  1.5494 +  TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
  1.5495 +                             aChildFrame);
  1.5496 +  for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
  1.5497 +    // First, translate aRect from aChildFrame to this run's frame.
  1.5498 +    nsRect rectInTextFrame = aRect + aChildFrame->GetOffsetTo(run.mFrame);
  1.5499 +
  1.5500 +    // Scale it into frame user space.
  1.5501 +    gfxRect rectInFrameUserSpace =
  1.5502 +      AppUnitsToFloatCSSPixels(gfxRect(rectInTextFrame.x,
  1.5503 +                                       rectInTextFrame.y,
  1.5504 +                                       rectInTextFrame.width,
  1.5505 +                                       rectInTextFrame.height), presContext);
  1.5506 +
  1.5507 +    // Intersect it with the run.
  1.5508 +    uint32_t flags = TextRenderedRun::eIncludeFill |
  1.5509 +                     TextRenderedRun::eIncludeStroke;
  1.5510 +    rectInFrameUserSpace.IntersectRect
  1.5511 +      (rectInFrameUserSpace, run.GetFrameUserSpaceRect(presContext, flags).ToThebesRect());
  1.5512 +
  1.5513 +    if (!rectInFrameUserSpace.IsEmpty()) {
  1.5514 +      // Transform it up to user space of the <text>, also taking into
  1.5515 +      // account the font size scale.
  1.5516 +      gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
  1.5517 +      m.Scale(mFontSizeScaleFactor, mFontSizeScaleFactor);
  1.5518 +      gfxRect rectInUserSpace = m.Transform(rectInFrameUserSpace);
  1.5519 +
  1.5520 +      // Union it into the result.
  1.5521 +      result.UnionRect(result, rectInUserSpace);
  1.5522 +    }
  1.5523 +  }
  1.5524 +
  1.5525 +  // Subtract the mRect offset from the result, as our user space for
  1.5526 +  // this frame is relative to the top-left of mRect.
  1.5527 +  float factor = presContext->AppUnitsPerCSSPixel();
  1.5528 +  gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
  1.5529 +                         NSAppUnitsToFloatPixels(mRect.y, factor));
  1.5530 +
  1.5531 +  return result - framePosition;
  1.5532 +}
  1.5533 +
  1.5534 +DrawMode
  1.5535 +SVGTextFrame::SetupCairoState(gfxContext* aContext,
  1.5536 +                              nsIFrame* aFrame,
  1.5537 +                              gfxTextContextPaint* aOuterContextPaint,
  1.5538 +                              gfxTextContextPaint** aThisContextPaint)
  1.5539 +{
  1.5540 +  DrawMode toDraw = DrawMode(0);
  1.5541 +  SVGTextContextPaint *thisContextPaint = new SVGTextContextPaint();
  1.5542 +
  1.5543 +  if (SetupCairoStroke(aContext, aFrame, aOuterContextPaint, thisContextPaint)) {
  1.5544 +    toDraw = DrawMode(int(toDraw) | int(DrawMode::GLYPH_STROKE));
  1.5545 +  }
  1.5546 +
  1.5547 +  if (SetupCairoFill(aContext, aFrame, aOuterContextPaint, thisContextPaint)) {
  1.5548 +    toDraw = DrawMode(int(toDraw) | int(DrawMode::GLYPH_FILL));
  1.5549 +  }
  1.5550 +
  1.5551 +  *aThisContextPaint = thisContextPaint;
  1.5552 +
  1.5553 +  return toDraw;
  1.5554 +}
  1.5555 +
  1.5556 +bool
  1.5557 +SVGTextFrame::SetupCairoStroke(gfxContext* aContext,
  1.5558 +                               nsIFrame* aFrame,
  1.5559 +                               gfxTextContextPaint* aOuterContextPaint,
  1.5560 +                               SVGTextContextPaint* aThisContextPaint)
  1.5561 +{
  1.5562 +  const nsStyleSVG *style = aFrame->StyleSVG();
  1.5563 +  if (style->mStroke.mType == eStyleSVGPaintType_None) {
  1.5564 +    aThisContextPaint->SetStrokeOpacity(0.0f);
  1.5565 +    return false;
  1.5566 +  }
  1.5567 +
  1.5568 +  nsSVGUtils::SetupCairoStrokeGeometry(aFrame, aContext, aOuterContextPaint);
  1.5569 +  float opacity = nsSVGUtils::GetOpacity(style->mStrokeOpacitySource,
  1.5570 +                                         style->mStrokeOpacity,
  1.5571 +                                         aOuterContextPaint);
  1.5572 +
  1.5573 +  SetupInheritablePaint(aContext, aFrame, opacity, aOuterContextPaint,
  1.5574 +                        aThisContextPaint->mStrokePaint, &nsStyleSVG::mStroke,
  1.5575 +                        nsSVGEffects::StrokeProperty());
  1.5576 +
  1.5577 +  aThisContextPaint->SetStrokeOpacity(opacity);
  1.5578 +
  1.5579 +  return opacity != 0.0f;
  1.5580 +}
  1.5581 +
  1.5582 +bool
  1.5583 +SVGTextFrame::SetupCairoFill(gfxContext* aContext,
  1.5584 +                             nsIFrame* aFrame,
  1.5585 +                             gfxTextContextPaint* aOuterContextPaint,
  1.5586 +                             SVGTextContextPaint* aThisContextPaint)
  1.5587 +{
  1.5588 +  const nsStyleSVG *style = aFrame->StyleSVG();
  1.5589 +  if (style->mFill.mType == eStyleSVGPaintType_None) {
  1.5590 +    aThisContextPaint->SetFillOpacity(0.0f);
  1.5591 +    return false;
  1.5592 +  }
  1.5593 +
  1.5594 +  float opacity = nsSVGUtils::GetOpacity(style->mFillOpacitySource,
  1.5595 +                                         style->mFillOpacity,
  1.5596 +                                         aOuterContextPaint);
  1.5597 +
  1.5598 +  SetupInheritablePaint(aContext, aFrame, opacity, aOuterContextPaint,
  1.5599 +                        aThisContextPaint->mFillPaint, &nsStyleSVG::mFill,
  1.5600 +                        nsSVGEffects::FillProperty());
  1.5601 +
  1.5602 +  aThisContextPaint->SetFillOpacity(opacity);
  1.5603 +
  1.5604 +  return true;
  1.5605 +}
  1.5606 +
  1.5607 +void
  1.5608 +SVGTextFrame::SetupInheritablePaint(gfxContext* aContext,
  1.5609 +                                    nsIFrame* aFrame,
  1.5610 +                                    float& aOpacity,
  1.5611 +                                    gfxTextContextPaint* aOuterContextPaint,
  1.5612 +                                    SVGTextContextPaint::Paint& aTargetPaint,
  1.5613 +                                    nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
  1.5614 +                                    const FramePropertyDescriptor* aProperty)
  1.5615 +{
  1.5616 +  const nsStyleSVG *style = aFrame->StyleSVG();
  1.5617 +  nsSVGPaintServerFrame *ps =
  1.5618 +    nsSVGEffects::GetPaintServer(aFrame, &(style->*aFillOrStroke), aProperty);
  1.5619 +
  1.5620 +  if (ps && ps->SetupPaintServer(aContext, aFrame, aFillOrStroke, aOpacity)) {
  1.5621 +    aTargetPaint.SetPaintServer(aFrame, aContext->CurrentMatrix(), ps);
  1.5622 +  } else if (nsSVGUtils::SetupContextPaint(aContext, aOuterContextPaint,
  1.5623 +                                           style->*aFillOrStroke,
  1.5624 +                                           aOpacity)) {
  1.5625 +    aTargetPaint.SetContextPaint(aOuterContextPaint, (style->*aFillOrStroke).mType);
  1.5626 +  } else {
  1.5627 +    nscolor color = nsSVGUtils::GetFallbackOrPaintColor(aContext,
  1.5628 +                                                        aFrame->StyleContext(),
  1.5629 +                                                        aFillOrStroke);
  1.5630 +    aTargetPaint.SetColor(color);
  1.5631 +
  1.5632 +    nsRefPtr<gfxPattern> pattern =
  1.5633 +      new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0,
  1.5634 +                             NS_GET_G(color) / 255.0,
  1.5635 +                             NS_GET_B(color) / 255.0,
  1.5636 +                             NS_GET_A(color) / 255.0 * aOpacity));
  1.5637 +    aContext->SetPattern(pattern);
  1.5638 +  }
  1.5639 +}

mercurial