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