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 +}