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