Coverage Report

Created: 2018-09-25 14:53

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