Source code

Revision control

Other Tools

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
/* rendering object for textual content of elements */
8
9
#include "nsTextFrame.h"
10
11
#include "gfx2DGlue.h"
12
13
#include "gfxUtils.h"
14
#include "mozilla/Attributes.h"
15
#include "mozilla/ComputedStyle.h"
16
#include "mozilla/DebugOnly.h"
17
#include "mozilla/gfx/2D.h"
18
#include "mozilla/Likely.h"
19
#include "mozilla/MathAlgorithms.h"
20
#include "mozilla/PresShell.h"
21
#include "mozilla/StaticPrefs_layout.h"
22
#include "mozilla/TextEditor.h"
23
#include "mozilla/TextEvents.h"
24
#include "mozilla/BinarySearch.h"
25
#include "mozilla/IntegerRange.h"
26
#include "mozilla/Unused.h"
27
#include "mozilla/PodOperations.h"
28
29
#include "nsCOMPtr.h"
30
#include "nsBlockFrame.h"
31
#include "nsFontMetrics.h"
32
#include "nsSplittableFrame.h"
33
#include "nsLineLayout.h"
34
#include "nsString.h"
35
#include "nsUnicharUtils.h"
36
#include "nsPresContext.h"
37
#include "nsIContent.h"
38
#include "nsStyleConsts.h"
39
#include "nsStyleStruct.h"
40
#include "nsStyleStructInlines.h"
41
#include "SVGTextFrame.h"
42
#include "nsCoord.h"
43
#include "gfxContext.h"
44
#include "nsTArray.h"
45
#include "nsCSSPseudoElements.h"
46
#include "nsCSSFrameConstructor.h"
47
#include "nsCompatibility.h"
48
#include "nsCSSColorUtils.h"
49
#include "nsLayoutUtils.h"
50
#include "nsDisplayList.h"
51
#include "nsFrame.h"
52
#include "nsIMathMLFrame.h"
53
#include "nsPlaceholderFrame.h"
54
#include "nsTextFrameUtils.h"
55
#include "nsTextRunTransformations.h"
56
#include "MathMLTextRunFactory.h"
57
#include "nsUnicodeProperties.h"
58
#include "nsStyleUtil.h"
59
#include "nsRubyFrame.h"
60
#include "TextDrawTarget.h"
61
62
#include "nsTextFragment.h"
63
#include "nsGkAtoms.h"
64
#include "nsFrameSelection.h"
65
#include "nsRange.h"
66
#include "nsCSSRendering.h"
67
#include "nsContentUtils.h"
68
#include "nsLineBreaker.h"
69
#include "nsIFrameInlines.h"
70
#include "mozilla/intl/WordBreaker.h"
71
#include "mozilla/ServoStyleSet.h"
72
73
#include <algorithm>
74
#include <limits>
75
#ifdef ACCESSIBILITY
76
# include "nsAccessibilityService.h"
77
#endif
78
79
#include "nsPrintfCString.h"
80
81
#include "mozilla/gfx/DrawTargetRecording.h"
82
83
#include "mozilla/UniquePtr.h"
84
#include "mozilla/dom/Element.h"
85
#include "mozilla/LookAndFeel.h"
86
87
#include "GeckoProfiler.h"
88
89
#ifdef DEBUG
90
# undef NOISY_REFLOW
91
# undef NOISY_TRIM
92
#else
93
# undef NOISY_REFLOW
94
# undef NOISY_TRIM
95
#endif
96
97
#ifdef DrawText
98
# undef DrawText
99
#endif
100
101
using namespace mozilla;
102
using namespace mozilla::dom;
103
using namespace mozilla::gfx;
104
105
typedef mozilla::layout::TextDrawTarget TextDrawTarget;
106
107
static bool NeedsToMaskPassword(nsTextFrame* aFrame) {
108
MOZ_ASSERT(aFrame);
109
MOZ_ASSERT(aFrame->GetContent());
110
return aFrame->GetContent()->HasFlag(NS_MAYBE_MASKED);
111
}
112
113
struct TabWidth {
114
TabWidth(uint32_t aOffset, uint32_t aWidth)
115
: mOffset(aOffset), mWidth(float(aWidth)) {}
116
117
uint32_t mOffset; // DOM offset relative to the current frame's offset.
118
float mWidth; // extra space to be added at this position (in app units)
119
};
120
121
struct TabWidthStore {
122
explicit TabWidthStore(int32_t aValidForContentOffset)
123
: mLimit(0), mValidForContentOffset(aValidForContentOffset) {}
124
125
// Apply tab widths to the aSpacing array, which corresponds to characters
126
// beginning at aOffset and has length aLength. (Width records outside this
127
// range will be ignored.)
128
void ApplySpacing(gfxTextRun::PropertyProvider::Spacing* aSpacing,
129
uint32_t aOffset, uint32_t aLength);
130
131
// Offset up to which tabs have been measured; positions beyond this have not
132
// been calculated yet but may be appended if needed later. It's a DOM
133
// offset relative to the current frame's offset.
134
uint32_t mLimit;
135
136
// Need to recalc tab offsets if frame content offset differs from this.
137
int32_t mValidForContentOffset;
138
139
// A TabWidth record for each tab character measured so far.
140
nsTArray<TabWidth> mWidths;
141
};
142
143
namespace {
144
145
struct TabwidthAdaptor {
146
const nsTArray<TabWidth>& mWidths;
147
explicit TabwidthAdaptor(const nsTArray<TabWidth>& aWidths)
148
: mWidths(aWidths) {}
149
uint32_t operator[](size_t aIdx) const { return mWidths[aIdx].mOffset; }
150
};
151
152
} // namespace
153
154
void TabWidthStore::ApplySpacing(
155
gfxTextRun::PropertyProvider::Spacing* aSpacing, uint32_t aOffset,
156
uint32_t aLength) {
157
size_t i = 0;
158
const size_t len = mWidths.Length();
159
160
// If aOffset is non-zero, do a binary search to find where to start
161
// processing the tab widths, in case the list is really long. (See bug
162
// 953247.)
163
// We need to start from the first entry where mOffset >= aOffset.
164
if (aOffset > 0) {
165
mozilla::BinarySearch(TabwidthAdaptor(mWidths), 0, len, aOffset, &i);
166
}
167
168
uint32_t limit = aOffset + aLength;
169
while (i < len) {
170
const TabWidth& tw = mWidths[i];
171
if (tw.mOffset >= limit) {
172
break;
173
}
174
aSpacing[tw.mOffset - aOffset].mAfter += tw.mWidth;
175
i++;
176
}
177
}
178
179
NS_DECLARE_FRAME_PROPERTY_DELETABLE(TabWidthProperty, TabWidthStore)
180
181
NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(OffsetToFrameProperty, nsTextFrame)
182
183
NS_DECLARE_FRAME_PROPERTY_RELEASABLE(UninflatedTextRunProperty, gfxTextRun)
184
185
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
186
187
/**
188
* A glyph observer for the change of a font glyph in a text run.
189
*
190
* This is stored in {Simple, Complex}TextRunUserData.
191
*/
192
class GlyphObserver final : public gfxFont::GlyphChangeObserver {
193
public:
194
GlyphObserver(gfxFont* aFont, gfxTextRun* aTextRun)
195
: gfxFont::GlyphChangeObserver(aFont), mTextRun(aTextRun) {
196
MOZ_ASSERT(aTextRun->GetUserData());
197
}
198
void NotifyGlyphsChanged() override;
199
200
private:
201
gfxTextRun* mTextRun;
202
};
203
204
static const nsFrameState TEXT_REFLOW_FLAGS =
205
TEXT_FIRST_LETTER | TEXT_START_OF_LINE | TEXT_END_OF_LINE |
206
TEXT_HYPHEN_BREAK | TEXT_TRIMMED_TRAILING_WHITESPACE |
207
TEXT_JUSTIFICATION_ENABLED | TEXT_HAS_NONCOLLAPSED_CHARACTERS |
208
TEXT_SELECTION_UNDERLINE_OVERFLOWED | TEXT_NO_RENDERED_GLYPHS;
209
210
static const nsFrameState TEXT_WHITESPACE_FLAGS =
211
TEXT_IS_ONLY_WHITESPACE | TEXT_ISNOT_ONLY_WHITESPACE;
212
213
/*
214
* Some general notes
215
*
216
* Text frames delegate work to gfxTextRun objects. The gfxTextRun object
217
* transforms text to positioned glyphs. It can report the geometry of the
218
* glyphs and paint them. Text frames configure gfxTextRuns by providing text,
219
* spacing, language, and other information.
220
*
221
* A gfxTextRun can cover more than one DOM text node. This is necessary to
222
* get kerning, ligatures and shaping for text that spans multiple text nodes
223
* but is all the same font.
224
*
225
* The userdata for a gfxTextRun object can be:
226
*
227
* - A nsTextFrame* in the case a text run maps to only one flow. In this
228
* case, the textrun's user data pointer is a pointer to mStartFrame for that
229
* flow, mDOMOffsetToBeforeTransformOffset is zero, and mContentLength is the
230
* length of the text node.
231
*
232
* - A SimpleTextRunUserData in the case a text run maps to one flow, but we
233
* still have to keep a list of glyph observers.
234
*
235
* - A ComplexTextRunUserData in the case a text run maps to multiple flows,
236
* but we need to keep a list of glyph observers.
237
*
238
* - A TextRunUserData in the case a text run maps multiple flows, but it
239
* doesn't have any glyph observer for changes in SVG fonts.
240
*
241
* You can differentiate between the four different cases with the
242
* IsSimpleFlow and MightHaveGlyphChanges flags.
243
*
244
* We go to considerable effort to make sure things work even if in-flow
245
* siblings have different ComputedStyles (i.e., first-letter and first-line).
246
*
247
* Our convention is that unsigned integer character offsets are offsets into
248
* the transformed string. Signed integer character offsets are offsets into
249
* the DOM string.
250
*
251
* XXX currently we don't handle hyphenated breaks between text frames where the
252
* hyphen occurs at the end of the first text frame, e.g.
253
* <b>Kit&shy;</b>ty
254
*/
255
256
/**
257
* This is our user data for the textrun, when textRun->GetFlags2() has
258
* IsSimpleFlow set, and also MightHaveGlyphChanges.
259
*
260
* This allows having an array of observers if there are fonts whose glyphs
261
* might change, but also avoid allocation in the simple case that there aren't.
262
*/
263
struct SimpleTextRunUserData {
264
nsTArray<UniquePtr<GlyphObserver>> mGlyphObservers;
265
nsTextFrame* mFrame;
266
explicit SimpleTextRunUserData(nsTextFrame* aFrame) : mFrame(aFrame) {}
267
};
268
269
/**
270
* We use an array of these objects to record which text frames
271
* are associated with the textrun. mStartFrame is the start of a list of
272
* text frames. Some sequence of its continuations are covered by the textrun.
273
* A content textnode can have at most one TextRunMappedFlow associated with it
274
* for a given textrun.
275
*
276
* mDOMOffsetToBeforeTransformOffset is added to DOM offsets for those frames to
277
* obtain the offset into the before-transformation text of the textrun. It can
278
* be positive (when a text node starts in the middle of a text run) or negative
279
* (when a text run starts in the middle of a text node). Of course it can also
280
* be zero.
281
*/
282
struct TextRunMappedFlow {
283
nsTextFrame* mStartFrame;
284
int32_t mDOMOffsetToBeforeTransformOffset;
285
// The text mapped starts at mStartFrame->GetContentOffset() and is this long
286
uint32_t mContentLength;
287
};
288
289
/**
290
* This is the type in the gfxTextRun's userdata field in the common case that
291
* the text run maps to multiple flows, but no fonts have been found with
292
* animatable glyphs.
293
*
294
* This way, we avoid allocating and constructing the extra nsTArray.
295
*/
296
struct TextRunUserData {
297
#ifdef DEBUG
298
TextRunMappedFlow* mMappedFlows;
299
#endif
300
uint32_t mMappedFlowCount;
301
uint32_t mLastFlowIndex;
302
};
303
304
/**
305
* This is our user data for the textrun, when textRun->GetFlags2() does not
306
* have IsSimpleFlow set and has the MightHaveGlyphChanges flag.
307
*/
308
struct ComplexTextRunUserData : public TextRunUserData {
309
nsTArray<UniquePtr<GlyphObserver>> mGlyphObservers;
310
};
311
312
/**
313
* This helper object computes colors used for painting, and also IME
314
* underline information. The data is computed lazily and cached as necessary.
315
* These live for just the duration of one paint operation.
316
*/
317
class nsTextPaintStyle {
318
public:
319
explicit nsTextPaintStyle(nsTextFrame* aFrame);
320
321
void SetResolveColors(bool aResolveColors) {
322
mResolveColors = aResolveColors;
323
}
324
325
nscolor GetTextColor();
326
327
// SVG text has its own painting process, so we should never get its stroke
328
// property from here.
329
nscolor GetWebkitTextStrokeColor() {
330
if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
331
return 0;
332
}
333
return mFrame->StyleText()->mWebkitTextStrokeColor.CalcColor(mFrame);
334
}
335
float GetWebkitTextStrokeWidth() {
336
if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
337
return 0.0f;
338
}
339
nscoord coord = mFrame->StyleText()->mWebkitTextStrokeWidth;
340
return mFrame->PresContext()->AppUnitsToFloatDevPixels(coord);
341
}
342
343
/**
344
* Compute the colors for normally-selected text. Returns false if
345
* the normal selection is not being displayed.
346
*/
347
bool GetSelectionColors(nscolor* aForeColor, nscolor* aBackColor);
348
void GetHighlightColors(nscolor* aForeColor, nscolor* aBackColor);
349
void GetURLSecondaryColor(nscolor* aForeColor);
350
void GetIMESelectionColors(int32_t aIndex, nscolor* aForeColor,
351
nscolor* aBackColor);
352
// if this returns false, we don't need to draw underline.
353
bool GetSelectionUnderlineForPaint(int32_t aIndex, nscolor* aLineColor,
354
float* aRelativeSize, uint8_t* aStyle);
355
356
// if this returns false, we don't need to draw underline.
357
static bool GetSelectionUnderline(nsPresContext* aPresContext, int32_t aIndex,
358
nscolor* aLineColor, float* aRelativeSize,
359
uint8_t* aStyle);
360
361
// if this returns false, no text-shadow was specified for the selection
362
// and the *aShadow parameter was not modified.
363
bool GetSelectionShadow(Span<const StyleSimpleShadow>* aShadows);
364
365
nsPresContext* PresContext() const { return mPresContext; }
366
367
enum {
368
eIndexRawInput = 0,
369
eIndexSelRawText,
370
eIndexConvText,
371
eIndexSelConvText,
372
eIndexSpellChecker
373
};
374
375
static int32_t GetUnderlineStyleIndexForSelectionType(
376
SelectionType aSelectionType) {
377
switch (aSelectionType) {
378
case SelectionType::eIMERawClause:
379
return eIndexRawInput;
380
case SelectionType::eIMESelectedRawClause:
381
return eIndexSelRawText;
382
case SelectionType::eIMEConvertedClause:
383
return eIndexConvText;
384
case SelectionType::eIMESelectedClause:
385
return eIndexSelConvText;
386
case SelectionType::eSpellCheck:
387
return eIndexSpellChecker;
388
default:
389
NS_WARNING("non-IME selection type");
390
return eIndexRawInput;
391
}
392
}
393
394
nscolor GetSystemFieldForegroundColor();
395
nscolor GetSystemFieldBackgroundColor();
396
397
protected:
398
nsTextFrame* mFrame;
399
nsPresContext* mPresContext;
400
bool mInitCommonColors;
401
bool mInitSelectionColorsAndShadow;
402
bool mResolveColors;
403
404
// Selection data
405
406
nscolor mSelectionTextColor;
407
nscolor mSelectionBGColor;
408
RefPtr<ComputedStyle> mSelectionPseudoStyle;
409
410
// Common data
411
412
int32_t mSufficientContrast;
413
nscolor mFrameBackgroundColor;
414
nscolor mSystemFieldForegroundColor;
415
nscolor mSystemFieldBackgroundColor;
416
417
// selection colors and underline info, the colors are resolved colors if
418
// mResolveColors is true (which is the default), i.e., the foreground color
419
// and background color are swapped if it's needed. And also line color will
420
// be resolved from them.
421
struct nsSelectionStyle {
422
bool mInit;
423
nscolor mTextColor;
424
nscolor mBGColor;
425
nscolor mUnderlineColor;
426
uint8_t mUnderlineStyle;
427
float mUnderlineRelativeSize;
428
};
429
nsSelectionStyle mSelectionStyle[5];
430
431
// Color initializations
432
void InitCommonColors();
433
bool InitSelectionColorsAndShadow();
434
435
nsSelectionStyle* GetSelectionStyle(int32_t aIndex);
436
void InitSelectionStyle(int32_t aIndex);
437
438
// Ensures sufficient contrast between the frame background color and the
439
// selection background color, and swaps the selection text and background
440
// colors accordingly.
441
// Only used on platforms where mSelectionTextColor != NS_DONT_CHANGE_COLOR
442
bool EnsureSufficientContrast(nscolor* aForeColor, nscolor* aBackColor);
443
444
nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor,
445
nscolor aBackColor);
446
};
447
448
static TextRunUserData* CreateUserData(uint32_t aMappedFlowCount) {
449
TextRunUserData* data = static_cast<TextRunUserData*>(moz_xmalloc(
450
sizeof(TextRunUserData) + aMappedFlowCount * sizeof(TextRunMappedFlow)));
451
#ifdef DEBUG
452
data->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(data + 1);
453
#endif
454
data->mMappedFlowCount = aMappedFlowCount;
455
data->mLastFlowIndex = 0;
456
return data;
457
}
458
459
static void DestroyUserData(TextRunUserData* aUserData) {
460
if (aUserData) {
461
free(aUserData);
462
}
463
}
464
465
static ComplexTextRunUserData* CreateComplexUserData(
466
uint32_t aMappedFlowCount) {
467
ComplexTextRunUserData* data = static_cast<ComplexTextRunUserData*>(
468
moz_xmalloc(sizeof(ComplexTextRunUserData) +
469
aMappedFlowCount * sizeof(TextRunMappedFlow)));
470
new (data) ComplexTextRunUserData();
471
#ifdef DEBUG
472
data->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(data + 1);
473
#endif
474
data->mMappedFlowCount = aMappedFlowCount;
475
data->mLastFlowIndex = 0;
476
return data;
477
}
478
479
static void DestroyComplexUserData(ComplexTextRunUserData* aUserData) {
480
if (aUserData) {
481
aUserData->~ComplexTextRunUserData();
482
free(aUserData);
483
}
484
}
485
486
static void DestroyTextRunUserData(gfxTextRun* aTextRun) {
487
MOZ_ASSERT(aTextRun->GetUserData());
488
if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSimpleFlow) {
489
if (aTextRun->GetFlags2() &
490
nsTextFrameUtils::Flags::MightHaveGlyphChanges) {
491
delete static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData());
492
}
493
} else {
494
if (aTextRun->GetFlags2() &
495
nsTextFrameUtils::Flags::MightHaveGlyphChanges) {
496
DestroyComplexUserData(
497
static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData()));
498
} else {
499
DestroyUserData(static_cast<TextRunUserData*>(aTextRun->GetUserData()));
500
}
501
}
502
aTextRun->ClearFlagBits(nsTextFrameUtils::Flags::MightHaveGlyphChanges);
503
aTextRun->SetUserData(nullptr);
504
}
505
506
static TextRunMappedFlow* GetMappedFlows(const gfxTextRun* aTextRun) {
507
MOZ_ASSERT(aTextRun->GetUserData(), "UserData must exist.");
508
MOZ_ASSERT(!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSimpleFlow),
509
"The method should not be called for simple flows.");
510
TextRunMappedFlow* flows;
511
if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::MightHaveGlyphChanges) {
512
flows = reinterpret_cast<TextRunMappedFlow*>(
513
static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData()) + 1);
514
} else {
515
flows = reinterpret_cast<TextRunMappedFlow*>(
516
static_cast<TextRunUserData*>(aTextRun->GetUserData()) + 1);
517
}
518
MOZ_ASSERT(
519
static_cast<TextRunUserData*>(aTextRun->GetUserData())->mMappedFlows ==
520
flows,
521
"GetMappedFlows should return the same pointer as mMappedFlows.");
522
return flows;
523
}
524
525
/**
526
* These are utility functions just for helping with the complexity related with
527
* the text runs user data.
528
*/
529
static nsTextFrame* GetFrameForSimpleFlow(const gfxTextRun* aTextRun) {
530
MOZ_ASSERT(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSimpleFlow,
531
"Not so simple flow?");
532
if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::MightHaveGlyphChanges) {
533
return static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())->mFrame;
534
}
535
536
return static_cast<nsTextFrame*>(aTextRun->GetUserData());
537
}
538
539
/**
540
* Remove |aTextRun| from the frame continuation chain starting at
541
* |aStartContinuation| if non-null, otherwise starting at |aFrame|.
542
* Unmark |aFrame| as a text run owner if it's the frame we start at.
543
* Return true if |aStartContinuation| is non-null and was found
544
* in the next-continuation chain of |aFrame|.
545
*/
546
static bool ClearAllTextRunReferences(nsTextFrame* aFrame, gfxTextRun* aTextRun,
547
nsTextFrame* aStartContinuation,
548
nsFrameState aWhichTextRunState) {
549
MOZ_ASSERT(aFrame, "null frame");
550
MOZ_ASSERT(!aStartContinuation ||
551
(!aStartContinuation->GetTextRun(nsTextFrame::eInflated) ||
552
aStartContinuation->GetTextRun(nsTextFrame::eInflated) ==
553
aTextRun) ||
554
(!aStartContinuation->GetTextRun(nsTextFrame::eNotInflated) ||
555
aStartContinuation->GetTextRun(nsTextFrame::eNotInflated) ==
556
aTextRun),
557
"wrong aStartContinuation for this text run");
558
559
if (!aStartContinuation || aStartContinuation == aFrame) {
560
aFrame->RemoveStateBits(aWhichTextRunState);
561
} else {
562
do {
563
NS_ASSERTION(aFrame->IsTextFrame(), "Bad frame");
564
aFrame = aFrame->GetNextContinuation();
565
} while (aFrame && aFrame != aStartContinuation);
566
}
567
bool found = aStartContinuation == aFrame;
568
while (aFrame) {
569
NS_ASSERTION(aFrame->IsTextFrame(), "Bad frame");
570
if (!aFrame->RemoveTextRun(aTextRun)) {
571
break;
572
}
573
aFrame = aFrame->GetNextContinuation();
574
}
575
576
MOZ_ASSERT(!found || aStartContinuation, "how did we find null?");
577
return found;
578
}
579
580
/**
581
* Kill all references to |aTextRun| starting at |aStartContinuation|.
582
* It could be referenced by any of its owners, and all their in-flows.
583
* If |aStartContinuation| is null then process all userdata frames
584
* and their continuations.
585
* @note the caller is expected to take care of possibly destroying the
586
* text run if all userdata frames were reset (userdata is deallocated
587
* by this function though). The caller can detect this has occured by
588
* checking |aTextRun->GetUserData() == nullptr|.
589
*/
590
static void UnhookTextRunFromFrames(gfxTextRun* aTextRun,
591
nsTextFrame* aStartContinuation) {
592
if (!aTextRun->GetUserData()) {
593
return;
594
}
595
596
if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSimpleFlow) {
597
nsTextFrame* userDataFrame = GetFrameForSimpleFlow(aTextRun);
598
nsFrameState whichTextRunState =
599
userDataFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
600
? TEXT_IN_TEXTRUN_USER_DATA
601
: TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
602
DebugOnly<bool> found = ClearAllTextRunReferences(
603
userDataFrame, aTextRun, aStartContinuation, whichTextRunState);
604
NS_ASSERTION(!aStartContinuation || found,
605
"aStartContinuation wasn't found in simple flow text run");
606
if (!(userDataFrame->GetStateBits() & whichTextRunState)) {
607
DestroyTextRunUserData(aTextRun);
608
}
609
} else {
610
auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
611
TextRunMappedFlow* userMappedFlows = GetMappedFlows(aTextRun);
612
int32_t destroyFromIndex = aStartContinuation ? -1 : 0;
613
for (uint32_t i = 0; i < userData->mMappedFlowCount; ++i) {
614
nsTextFrame* userDataFrame = userMappedFlows[i].mStartFrame;
615
nsFrameState whichTextRunState =
616
userDataFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
617
? TEXT_IN_TEXTRUN_USER_DATA
618
: TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
619
bool found = ClearAllTextRunReferences(
620
userDataFrame, aTextRun, aStartContinuation, whichTextRunState);
621
if (found) {
622
if (userDataFrame->GetStateBits() & whichTextRunState) {
623
destroyFromIndex = i + 1;
624
} else {
625
destroyFromIndex = i;
626
}
627
aStartContinuation = nullptr;
628
}
629
}
630
NS_ASSERTION(destroyFromIndex >= 0,
631
"aStartContinuation wasn't found in multi flow text run");
632
if (destroyFromIndex == 0) {
633
DestroyTextRunUserData(aTextRun);
634
} else {
635
userData->mMappedFlowCount = uint32_t(destroyFromIndex);
636
if (userData->mLastFlowIndex >= uint32_t(destroyFromIndex)) {
637
userData->mLastFlowIndex = uint32_t(destroyFromIndex) - 1;
638
}
639
}
640
}
641
}
642
643
static void InvalidateFrameDueToGlyphsChanged(nsIFrame* aFrame) {
644
MOZ_ASSERT(aFrame);
645
646
PresShell* presShell = aFrame->PresShell();
647
for (nsIFrame* f = aFrame; f;
648
f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
649
f->InvalidateFrame();
650
651
// If this is a non-display text frame within SVG <text>, we need
652
// to reflow the SVGTextFrame. (This is similar to reflowing the
653
// SVGTextFrame in response to style changes, in
654
// SVGTextFrame::DidSetComputedStyle.)
655
if (nsSVGUtils::IsInSVGTextSubtree(f) &&
656
f->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
657
auto svgTextFrame = static_cast<SVGTextFrame*>(
658
nsLayoutUtils::GetClosestFrameOfType(f, LayoutFrameType::SVGText));
659
svgTextFrame->ScheduleReflowSVGNonDisplayText(IntrinsicDirty::Resize);
660
} else {
661
// Theoretically we could just update overflow areas, perhaps using
662
// OverflowChangedTracker, but that would do a bunch of work eagerly that
663
// we should probably do lazily here since there could be a lot
664
// of text frames affected and we'd like to coalesce the work. So that's
665
// not easy to do well.
666
presShell->FrameNeedsReflow(f, IntrinsicDirty::Resize, NS_FRAME_IS_DIRTY);
667
}
668
}
669
}
670
671
void GlyphObserver::NotifyGlyphsChanged() {
672
if (mTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSimpleFlow) {
673
InvalidateFrameDueToGlyphsChanged(GetFrameForSimpleFlow(mTextRun));
674
return;
675
}
676
677
auto data = static_cast<TextRunUserData*>(mTextRun->GetUserData());
678
TextRunMappedFlow* userMappedFlows = GetMappedFlows(mTextRun);
679
for (uint32_t i = 0; i < data->mMappedFlowCount; ++i) {
680
InvalidateFrameDueToGlyphsChanged(userMappedFlows[i].mStartFrame);
681
}
682
}
683
684
int32_t nsTextFrame::GetContentEnd() const {
685
nsTextFrame* next = GetNextContinuation();
686
return next ? next->GetContentOffset() : TextFragment()->GetLength();
687
}
688
689
struct FlowLengthProperty {
690
int32_t mStartOffset;
691
// The offset of the next fixed continuation after mStartOffset, or
692
// of the end of the text if there is none
693
int32_t mEndFlowOffset;
694
};
695
696
int32_t nsTextFrame::GetInFlowContentLength() {
697
if (!(mState & NS_FRAME_IS_BIDI)) {
698
return mContent->TextLength() - mContentOffset;
699
}
700
701
FlowLengthProperty* flowLength =
702
mContent->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)
703
? static_cast<FlowLengthProperty*>(
704
mContent->GetProperty(nsGkAtoms::flowlength))
705
: nullptr;
706
707
/**
708
* This frame must start inside the cached flow. If the flow starts at
709
* mContentOffset but this frame is empty, logically it might be before the
710
* start of the cached flow.
711
*/
712
if (flowLength &&
713
(flowLength->mStartOffset < mContentOffset ||
714
(flowLength->mStartOffset == mContentOffset &&
715
GetContentEnd() > mContentOffset)) &&
716
flowLength->mEndFlowOffset > mContentOffset) {
717
#ifdef DEBUG
718
NS_ASSERTION(flowLength->mEndFlowOffset >= GetContentEnd(),
719
"frame crosses fixed continuation boundary");
720
#endif
721
return flowLength->mEndFlowOffset - mContentOffset;
722
}
723
724
nsTextFrame* nextBidi = LastInFlow()->GetNextContinuation();
725
int32_t endFlow =
726
nextBidi ? nextBidi->GetContentOffset() : GetContent()->TextLength();
727
728
if (!flowLength) {
729
flowLength = new FlowLengthProperty;
730
if (NS_FAILED(mContent->SetProperty(
731
nsGkAtoms::flowlength, flowLength,
732
nsINode::DeleteProperty<FlowLengthProperty>))) {
733
delete flowLength;
734
flowLength = nullptr;
735
}
736
mContent->SetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
737
}
738
if (flowLength) {
739
flowLength->mStartOffset = mContentOffset;
740
flowLength->mEndFlowOffset = endFlow;
741
}
742
743
return endFlow - mContentOffset;
744
}
745
746
// Smarter versions of dom::IsSpaceCharacter.
747
// Unicode is really annoying; sometimes a space character isn't whitespace ---
748
// when it combines with another character
749
// So we have several versions of IsSpace for use in different contexts.
750
751
static bool IsSpaceCombiningSequenceTail(const nsTextFragment* aFrag,
752
uint32_t aPos) {
753
NS_ASSERTION(aPos <= aFrag->GetLength(), "Bad offset");
754
if (!aFrag->Is2b()) return false;
755
return nsTextFrameUtils::IsSpaceCombiningSequenceTail(
756
aFrag->Get2b() + aPos, aFrag->GetLength() - aPos);
757
}
758
759
// Check whether aPos is a space for CSS 'word-spacing' purposes
760
static bool IsCSSWordSpacingSpace(const nsTextFragment* aFrag, uint32_t aPos,
761
const nsTextFrame* aFrame,
762
const nsStyleText* aStyleText) {
763
NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
764
765
char16_t ch = aFrag->CharAt(aPos);
766
switch (ch) {
767
case ' ':
768
case CH_NBSP:
769
return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
770
case '\r':
771
case '\t':
772
return !aStyleText->WhiteSpaceIsSignificant();
773
case '\n':
774
return !aStyleText->NewlineIsSignificant(aFrame);
775
default:
776
return false;
777
}
778
}
779
780
// Check whether the string aChars/aLength starts with space that's
781
// trimmable according to CSS 'white-space:normal/nowrap'.
782
static bool IsTrimmableSpace(const char16_t* aChars, uint32_t aLength) {
783
NS_ASSERTION(aLength > 0, "No text for IsSpace!");
784
785
char16_t ch = *aChars;
786
if (ch == ' ')
787
return !nsTextFrameUtils::IsSpaceCombiningSequenceTail(aChars + 1,
788
aLength - 1);
789
return ch == '\t' || ch == '\f' || ch == '\n' || ch == '\r';
790
}
791
792
// Check whether the character aCh is trimmable according to CSS
793
// 'white-space:normal/nowrap'
794
static bool IsTrimmableSpace(char aCh) {
795
return aCh == ' ' || aCh == '\t' || aCh == '\f' || aCh == '\n' || aCh == '\r';
796
}
797
798
static bool IsTrimmableSpace(const nsTextFragment* aFrag, uint32_t aPos,
799
const nsStyleText* aStyleText) {
800
NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
801
802
switch (aFrag->CharAt(aPos)) {
803
case ' ':
804
return !aStyleText->WhiteSpaceIsSignificant() &&
805
!IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
806
case '\n':
807
return !aStyleText->NewlineIsSignificantStyle() &&
808
aStyleText->mWhiteSpace != mozilla::StyleWhiteSpace::PreSpace;
809
case '\t':
810
case '\r':
811
case '\f':
812
return !aStyleText->WhiteSpaceIsSignificant();
813
default:
814
return false;
815
}
816
}
817
818
static bool IsSelectionSpace(const nsTextFragment* aFrag, uint32_t aPos) {
819
NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
820
char16_t ch = aFrag->CharAt(aPos);
821
if (ch == ' ' || ch == CH_NBSP)
822
return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
823
return ch == '\t' || ch == '\n' || ch == '\f' || ch == '\r';
824
}
825
826
// Count the amount of trimmable whitespace (as per CSS
827
// 'white-space:normal/nowrap') in a text fragment. The first
828
// character is at offset aStartOffset; the maximum number of characters
829
// to check is aLength. aDirection is -1 or 1 depending on whether we should
830
// progress backwards or forwards.
831
static uint32_t GetTrimmableWhitespaceCount(const nsTextFragment* aFrag,
832
int32_t aStartOffset,
833
int32_t aLength,
834
int32_t aDirection) {
835
if (!aLength) {
836
return 0;
837
}
838
839
int32_t count = 0;
840
if (aFrag->Is2b()) {
841
const char16_t* str = aFrag->Get2b() + aStartOffset;
842
int32_t fragLen = aFrag->GetLength() - aStartOffset;
843
for (; count < aLength; ++count) {
844
if (!IsTrimmableSpace(str, fragLen)) break;
845
str += aDirection;
846
fragLen -= aDirection;
847
}
848
} else {
849
const char* str = aFrag->Get1b() + aStartOffset;
850
for (; count < aLength; ++count) {
851
if (!IsTrimmableSpace(*str)) break;
852
str += aDirection;
853
}
854
}
855
return count;
856
}
857
858
static bool IsAllWhitespace(const nsTextFragment* aFrag, bool aAllowNewline) {
859
if (aFrag->Is2b()) return false;
860
int32_t len = aFrag->GetLength();
861
const char* str = aFrag->Get1b();
862
for (int32_t i = 0; i < len; ++i) {
863
char ch = str[i];
864
if (ch == ' ' || ch == '\t' || ch == '\r' || (ch == '\n' && aAllowNewline))
865
continue;
866
return false;
867
}
868
return true;
869
}
870
871
static void ClearObserversFromTextRun(gfxTextRun* aTextRun) {
872
if (!(aTextRun->GetFlags2() &
873
nsTextFrameUtils::Flags::MightHaveGlyphChanges)) {
874
return;
875
}
876
877
if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSimpleFlow) {
878
static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())
879
->mGlyphObservers.Clear();
880
} else {
881
static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData())
882
->mGlyphObservers.Clear();
883
}
884
}
885
886
static void CreateObserversForAnimatedGlyphs(gfxTextRun* aTextRun) {
887
if (!aTextRun->GetUserData()) {
888
return;
889
}
890
891
ClearObserversFromTextRun(aTextRun);
892
893
nsTArray<gfxFont*> fontsWithAnimatedGlyphs;
894
uint32_t numGlyphRuns;
895
const gfxTextRun::GlyphRun* glyphRuns = aTextRun->GetGlyphRuns(&numGlyphRuns);
896
for (uint32_t i = 0; i < numGlyphRuns; ++i) {
897
gfxFont* font = glyphRuns[i].mFont;
898
if (font->GlyphsMayChange() && !fontsWithAnimatedGlyphs.Contains(font)) {
899
fontsWithAnimatedGlyphs.AppendElement(font);
900
}
901
}
902
if (fontsWithAnimatedGlyphs.IsEmpty()) {
903
// NB: Theoretically, we should clear the MightHaveGlyphChanges
904
// here. That would involve de-allocating the simple user data struct if
905
// present too, and resetting the pointer to the frame. In practice, I
906
// don't think worth doing that work here, given the flag's only purpose is
907
// to distinguish what kind of user data is there.
908
return;
909
}
910
911
nsTArray<UniquePtr<GlyphObserver>>* observers;
912
913
if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSimpleFlow) {
914
// Swap the frame pointer for a just-allocated SimpleTextRunUserData if
915
// appropriate.
916
if (!(aTextRun->GetFlags2() &
917
nsTextFrameUtils::Flags::MightHaveGlyphChanges)) {
918
auto frame = static_cast<nsTextFrame*>(aTextRun->GetUserData());
919
aTextRun->SetUserData(new SimpleTextRunUserData(frame));
920
}
921
922
auto data = static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData());
923
observers = &data->mGlyphObservers;
924
} else {
925
if (!(aTextRun->GetFlags2() &
926
nsTextFrameUtils::Flags::MightHaveGlyphChanges)) {
927
auto oldData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
928
TextRunMappedFlow* oldMappedFlows = GetMappedFlows(aTextRun);
929
ComplexTextRunUserData* data =
930
CreateComplexUserData(oldData->mMappedFlowCount);
931
TextRunMappedFlow* dataMappedFlows =
932
reinterpret_cast<TextRunMappedFlow*>(data + 1);
933
data->mLastFlowIndex = oldData->mLastFlowIndex;
934
for (uint32_t i = 0; i < oldData->mMappedFlowCount; ++i) {
935
dataMappedFlows[i] = oldMappedFlows[i];
936
}
937
DestroyUserData(oldData);
938
aTextRun->SetUserData(data);
939
}
940
auto data = static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData());
941
observers = &data->mGlyphObservers;
942
}
943
944
aTextRun->SetFlagBits(nsTextFrameUtils::Flags::MightHaveGlyphChanges);
945
946
for (auto font : fontsWithAnimatedGlyphs) {
947
observers->AppendElement(new GlyphObserver(font, aTextRun));
948
}
949
}
950
951
/**
952
* This class accumulates state as we scan a paragraph of text. It detects
953
* textrun boundaries (changes from text to non-text, hard
954
* line breaks, and font changes) and builds a gfxTextRun at each boundary.
955
* It also detects linebreaker run boundaries (changes from text to non-text,
956
* and hard line breaks) and at each boundary runs the linebreaker to compute
957
* potential line breaks. It also records actual line breaks to store them in
958
* the textruns.
959
*/
960
class BuildTextRunsScanner {
961
public:
962
BuildTextRunsScanner(nsPresContext* aPresContext, DrawTarget* aDrawTarget,
963
nsIFrame* aLineContainer,
964
nsTextFrame::TextRunType aWhichTextRun)
965
: mDrawTarget(aDrawTarget),
966
mLineContainer(aLineContainer),
967
mCommonAncestorWithLastFrame(nullptr),
968
mMissingFonts(aPresContext->MissingFontRecorder()),
969
mBidiEnabled(aPresContext->BidiEnabled()),
970
mStartOfLine(true),
971
mSkipIncompleteTextRuns(false),
972
mCanStopOnThisLine(false),
973
mWhichTextRun(aWhichTextRun),
974
mNextRunContextInfo(nsTextFrameUtils::INCOMING_NONE),
975
mCurrentRunContextInfo(nsTextFrameUtils::INCOMING_NONE) {
976
ResetRunInfo();
977
}
978
~BuildTextRunsScanner() {
979
NS_ASSERTION(mBreakSinks.IsEmpty(), "Should have been cleared");
980
NS_ASSERTION(mLineBreakBeforeFrames.IsEmpty(), "Should have been cleared");
981
NS_ASSERTION(mMappedFlows.IsEmpty(), "Should have been cleared");
982
}
983
984
void SetAtStartOfLine() {
985
mStartOfLine = true;
986
mCanStopOnThisLine = false;
987
}
988
void SetSkipIncompleteTextRuns(bool aSkip) {
989
mSkipIncompleteTextRuns = aSkip;
990
}
991
void SetCommonAncestorWithLastFrame(nsIFrame* aFrame) {
992
mCommonAncestorWithLastFrame = aFrame;
993
}
994
bool CanStopOnThisLine() { return mCanStopOnThisLine; }
995
nsIFrame* GetCommonAncestorWithLastFrame() {
996
return mCommonAncestorWithLastFrame;
997
}
998
void LiftCommonAncestorWithLastFrameToParent(nsIFrame* aFrame) {
999
if (mCommonAncestorWithLastFrame &&
1000
mCommonAncestorWithLastFrame->GetParent() == aFrame) {
1001
mCommonAncestorWithLastFrame = aFrame;
1002
}
1003
}
1004
void ScanFrame(nsIFrame* aFrame);
1005
bool IsTextRunValidForMappedFlows(const gfxTextRun* aTextRun);
1006
void FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak);
1007
void FlushLineBreaks(gfxTextRun* aTrailingTextRun);
1008
void ResetRunInfo() {
1009
mLastFrame = nullptr;
1010
mMappedFlows.Clear();
1011
mLineBreakBeforeFrames.Clear();
1012
mMaxTextLength = 0;
1013
mDoubleByteText = false;
1014
}
1015
void AccumulateRunInfo(nsTextFrame* aFrame);
1016
/**
1017
* @return null to indicate either textrun construction failed or
1018
* we constructed just a partial textrun to set up linebreaker and other
1019
* state for following textruns.
1020
*/
1021
already_AddRefed<gfxTextRun> BuildTextRunForFrames(void* aTextBuffer);
1022
bool SetupLineBreakerContext(gfxTextRun* aTextRun);
1023
void AssignTextRun(gfxTextRun* aTextRun, float aInflation);
1024
nsTextFrame* GetNextBreakBeforeFrame(uint32_t* aIndex);
1025
void SetupBreakSinksForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
1026
void SetupTextEmphasisForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
1027
struct FindBoundaryState {
1028
nsIFrame* mStopAtFrame;
1029
nsTextFrame* mFirstTextFrame;
1030
nsTextFrame* mLastTextFrame;
1031
bool mSeenTextRunBoundaryOnLaterLine;
1032
bool mSeenTextRunBoundaryOnThisLine;
1033
bool mSeenSpaceForLineBreakingOnThisLine;
1034
nsTArray<char16_t>& mBuffer;
1035
};
1036
enum FindBoundaryResult {
1037
FB_CONTINUE,
1038
FB_STOPPED_AT_STOP_FRAME,
1039
FB_FOUND_VALID_TEXTRUN_BOUNDARY
1040
};
1041
FindBoundaryResult FindBoundaries(nsIFrame* aFrame,
1042
FindBoundaryState* aState);
1043
1044
bool ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2);
1045
1046
// Like TextRunMappedFlow but with some differences. mStartFrame to mEndFrame
1047
// (exclusive) are a sequence of in-flow frames (if mEndFrame is null, then
1048
// continuations starting from mStartFrame are a sequence of in-flow frames).
1049
struct MappedFlow {
1050
nsTextFrame* mStartFrame;
1051
nsTextFrame* mEndFrame;
1052
// When we consider breaking between elements, the nearest common
1053
// ancestor of the elements containing the characters is the one whose
1054
// CSS 'white-space' property governs. So this records the nearest common
1055
// ancestor of mStartFrame and the previous text frame, or null if there
1056
// was no previous text frame on this line.
1057
nsIFrame* mAncestorControllingInitialBreak;
1058
1059
int32_t GetContentEnd() {
1060
return mEndFrame ? mEndFrame->GetContentOffset()
1061
: mStartFrame->TextFragment()->GetLength();
1062
}
1063
};
1064
1065
class BreakSink final : public nsILineBreakSink {
1066
public:
1067
BreakSink(gfxTextRun* aTextRun, DrawTarget* aDrawTarget,
1068
uint32_t aOffsetIntoTextRun)
1069
: mTextRun(aTextRun),
1070
mDrawTarget(aDrawTarget),
1071
mOffsetIntoTextRun(aOffsetIntoTextRun) {}
1072
1073
void SetBreaks(uint32_t aOffset, uint32_t aLength,
1074
uint8_t* aBreakBefore) final {
1075
gfxTextRun::Range range(aOffset + mOffsetIntoTextRun,
1076
aOffset + mOffsetIntoTextRun + aLength);
1077
if (mTextRun->SetPotentialLineBreaks(range, aBreakBefore)) {
1078
// Be conservative and assume that some breaks have been set
1079
mTextRun->ClearFlagBits(nsTextFrameUtils::Flags::NoBreaks);
1080
}
1081
}
1082
1083
void SetCapitalization(uint32_t aOffset, uint32_t aLength,
1084
bool* aCapitalize) final {
1085
MOZ_ASSERT(mTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsTransformed,
1086
"Text run should be transformed!");
1087
if (mTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsTransformed) {
1088
nsTransformedTextRun* transformedTextRun =
1089
static_cast<nsTransformedTextRun*>(mTextRun.get());
1090
transformedTextRun->SetCapitalization(aOffset + mOffsetIntoTextRun,
1091
aLength, aCapitalize);
1092
}
1093
}
1094
1095
void Finish(gfxMissingFontRecorder* aMFR) {
1096
MOZ_ASSERT(
1097
!(mTextRun->GetFlags2() & nsTextFrameUtils::Flags::UnusedFlags),
1098
"Flag set that should never be set! (memory safety error?)");
1099
if (mTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsTransformed) {
1100
nsTransformedTextRun* transformedTextRun =
1101
static_cast<nsTransformedTextRun*>(mTextRun.get());
1102
transformedTextRun->FinishSettingProperties(mDrawTarget, aMFR);
1103
}
1104
// The way nsTransformedTextRun is implemented, its glyph runs aren't
1105
// available until after nsTransformedTextRun::FinishSettingProperties()
1106
// is called. So that's why we defer checking for animated glyphs to here.
1107
CreateObserversForAnimatedGlyphs(mTextRun);
1108
}
1109
1110
RefPtr<gfxTextRun> mTextRun;
1111
DrawTarget* mDrawTarget;
1112
uint32_t mOffsetIntoTextRun;
1113
};
1114
1115
private:
1116
AutoTArray<MappedFlow, 10> mMappedFlows;
1117
AutoTArray<nsTextFrame*, 50> mLineBreakBeforeFrames;
1118
AutoTArray<UniquePtr<BreakSink>, 10> mBreakSinks;
1119
nsLineBreaker mLineBreaker;
1120
RefPtr<gfxTextRun> mCurrentFramesAllSameTextRun;
1121
DrawTarget* mDrawTarget;
1122
nsIFrame* mLineContainer;
1123
nsTextFrame* mLastFrame;
1124
// The common ancestor of the current frame and the previous leaf frame
1125
// on the line, or null if there was no previous leaf frame.
1126
nsIFrame* mCommonAncestorWithLastFrame;
1127
gfxMissingFontRecorder* mMissingFonts;
1128
// mMaxTextLength is an upper bound on the size of the text in all mapped
1129
// frames The value UINT32_MAX represents overflow; text will be discarded
1130
uint32_t mMaxTextLength;
1131
bool mDoubleByteText;
1132
bool mBidiEnabled;
1133
bool mStartOfLine;
1134
bool mSkipIncompleteTextRuns;
1135
bool mCanStopOnThisLine;
1136
nsTextFrame::TextRunType mWhichTextRun;
1137
uint8_t mNextRunContextInfo;
1138
uint8_t mCurrentRunContextInfo;
1139
};
1140
1141
static nsIFrame* FindLineContainer(nsIFrame* aFrame) {
1142
while (aFrame && (aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
1143
aFrame->CanContinueTextRun())) {
1144
aFrame = aFrame->GetParent();
1145
}
1146
return aFrame;
1147
}
1148
1149
static bool IsLineBreakingWhiteSpace(char16_t aChar) {
1150
// 0x0A (\n) is not handled as white-space by the line breaker, since
1151
// we break before it, if it isn't transformed to a normal space.
1152
// (If we treat it as normal white-space then we'd only break after it.)
1153
// However, it does induce a line break or is converted to a regular
1154
// space, and either way it can be used to bound the region of text
1155
// that needs to be analyzed for line breaking.
1156
return nsLineBreaker::IsSpace(aChar) || aChar == 0x0A;
1157
}
1158
1159
static bool TextContainsLineBreakerWhiteSpace(const void* aText,
1160
uint32_t aLength,
1161
bool aIsDoubleByte) {
1162
if (aIsDoubleByte) {
1163
const char16_t* chars = static_cast<const char16_t*>(aText);
1164
for (uint32_t i = 0; i < aLength; ++i) {
1165
if (IsLineBreakingWhiteSpace(chars[i])) return true;
1166
}
1167
return false;
1168
} else {
1169
const uint8_t* chars = static_cast<const uint8_t*>(aText);
1170
for (uint32_t i = 0; i < aLength; ++i) {
1171
if (IsLineBreakingWhiteSpace(chars[i])) return true;
1172
}
1173
return false;
1174
}
1175
}
1176
1177
static nsTextFrameUtils::CompressionMode GetCSSWhitespaceToCompressionMode(
1178
nsTextFrame* aFrame, const nsStyleText* aStyleText) {
1179
switch (aStyleText->mWhiteSpace) {
1180
case StyleWhiteSpace::Normal:
1181
case StyleWhiteSpace::Nowrap:
1182
return nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE;
1183
case StyleWhiteSpace::Pre:
1184
case StyleWhiteSpace::PreWrap:
1185
case StyleWhiteSpace::BreakSpaces:
1186
if (!aStyleText->NewlineIsSignificant(aFrame)) {
1187
// If newline is set to be preserved, but then suppressed,
1188
// transform newline to space.
1189
return nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE;
1190
}
1191
return nsTextFrameUtils::COMPRESS_NONE;
1192
case StyleWhiteSpace::PreSpace:
1193
return nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE;
1194
case StyleWhiteSpace::PreLine:
1195
return nsTextFrameUtils::COMPRESS_WHITESPACE;
1196
default:
1197
MOZ_ASSERT_UNREACHABLE("Unknown white-space value");
1198
return nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE;
1199
}
1200
}
1201
1202
struct FrameTextTraversal {
1203
FrameTextTraversal()
1204
: mFrameToScan(nullptr),
1205
mOverflowFrameToScan(nullptr),
1206
mScanSiblings(false),
1207
mLineBreakerCanCrossFrameBoundary(false),
1208
mTextRunCanCrossFrameBoundary(false) {}
1209
1210
// These fields identify which frames should be recursively scanned
1211
// The first normal frame to scan (or null, if no such frame should be
1212
// scanned)
1213
nsIFrame* mFrameToScan;
1214
// The first overflow frame to scan (or null, if no such frame should be
1215
// scanned)
1216
nsIFrame* mOverflowFrameToScan;
1217
// Whether to scan the siblings of
1218
// mFrameToDescendInto/mOverflowFrameToDescendInto
1219
bool mScanSiblings;
1220
1221
// These identify the boundaries of the context required for
1222
// line breaking or textrun construction
1223
bool mLineBreakerCanCrossFrameBoundary;
1224
bool mTextRunCanCrossFrameBoundary;
1225
1226
nsIFrame* NextFrameToScan() {
1227
nsIFrame* f;
1228
if (mFrameToScan) {
1229
f = mFrameToScan;
1230
mFrameToScan = mScanSiblings ? f->GetNextSibling() : nullptr;
1231
} else if (mOverflowFrameToScan) {
1232
f = mOverflowFrameToScan;
1233
mOverflowFrameToScan = mScanSiblings ? f->GetNextSibling() : nullptr;
1234
} else {
1235
f = nullptr;
1236
}
1237
return f;
1238
}
1239
};
1240
1241
static FrameTextTraversal CanTextCrossFrameBoundary(nsIFrame* aFrame) {
1242
FrameTextTraversal result;
1243
1244
bool continuesTextRun = aFrame->CanContinueTextRun();
1245
if (aFrame->IsPlaceholderFrame()) {
1246
// placeholders are "invisible", so a text run should be able to span
1247
// across one. But don't descend into the out-of-flow.
1248
result.mLineBreakerCanCrossFrameBoundary = true;
1249
if (continuesTextRun) {
1250
// ... Except for first-letter floats, which are really in-flow
1251
// from the point of view of capitalization etc, so we'd better
1252
// descend into them. But we actually need to break the textrun for
1253
// first-letter floats since things look bad if, say, we try to make a
1254
// ligature across the float boundary.
1255
result.mFrameToScan =
1256
(static_cast<nsPlaceholderFrame*>(aFrame))->GetOutOfFlowFrame();
1257
} else {
1258
result.mTextRunCanCrossFrameBoundary = true;
1259
}
1260
} else {
1261
if (continuesTextRun) {
1262
result.mFrameToScan = aFrame->PrincipalChildList().FirstChild();
1263
result.mOverflowFrameToScan =
1264
aFrame->GetChildList(nsIFrame::kOverflowList).FirstChild();
1265
NS_WARNING_ASSERTION(
1266
!result.mOverflowFrameToScan,
1267
"Scanning overflow inline frames is something we should avoid");
1268
result.mScanSiblings = true;
1269
result.mTextRunCanCrossFrameBoundary = true;
1270
result.mLineBreakerCanCrossFrameBoundary = true;
1271
} else {
1272
MOZ_ASSERT(!aFrame->IsRubyTextContainerFrame(),
1273
"Shouldn't call this method for ruby text container");
1274
}
1275
}
1276
return result;
1277
}
1278
1279
BuildTextRunsScanner::FindBoundaryResult BuildTextRunsScanner::FindBoundaries(
1280
nsIFrame* aFrame, FindBoundaryState* aState) {
1281
LayoutFrameType frameType = aFrame->Type();
1282
if (frameType == LayoutFrameType::RubyTextContainer) {
1283
// Don't stop a text run for ruby text container. We want ruby text
1284
// containers to be skipped, but continue the text run across them.
1285
return FB_CONTINUE;
1286
}
1287
1288
nsTextFrame* textFrame = frameType == LayoutFrameType::Text
1289
? static_cast<nsTextFrame*>(aFrame)
1290
: nullptr;
1291
if (textFrame) {
1292
if (aState->mLastTextFrame &&
1293
textFrame != aState->mLastTextFrame->GetNextInFlow() &&
1294
!ContinueTextRunAcrossFrames(aState->mLastTextFrame, textFrame)) {
1295
aState->mSeenTextRunBoundaryOnThisLine = true;
1296
if (aState->mSeenSpaceForLineBreakingOnThisLine)
1297
return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
1298
}
1299
if (!aState->mFirstTextFrame) {
1300
aState->mFirstTextFrame = textFrame;
1301
}
1302
aState->mLastTextFrame = textFrame;
1303
}
1304
1305
if (aFrame == aState->mStopAtFrame) return FB_STOPPED_AT_STOP_FRAME;
1306
1307
if (textFrame) {
1308
if (aState->mSeenSpaceForLineBreakingOnThisLine) {
1309
return FB_CONTINUE;
1310
}
1311
const nsTextFragment* frag = textFrame->TextFragment();
1312
uint32_t start = textFrame->GetContentOffset();
1313
uint32_t length = textFrame->GetContentLength();
1314
const void* text;
1315
if (frag->Is2b()) {
1316
// It is possible that we may end up removing all whitespace in
1317
// a piece of text because of The White Space Processing Rules,
1318
// so we need to transform it before we can check existence of
1319
// such whitespaces.
1320
aState->mBuffer.EnsureLengthAtLeast(length);
1321
nsTextFrameUtils::CompressionMode compression =
1322
GetCSSWhitespaceToCompressionMode(textFrame, textFrame->StyleText());
1323
uint8_t incomingFlags = 0;
1324
gfxSkipChars skipChars;
1325
nsTextFrameUtils::Flags analysisFlags;
1326
char16_t* bufStart = aState->mBuffer.Elements();
1327
char16_t* bufEnd = nsTextFrameUtils::TransformText(
1328
frag->Get2b() + start, length, bufStart, compression, &incomingFlags,
1329
&skipChars, &analysisFlags);
1330
text = bufStart;
1331
length = bufEnd - bufStart;
1332
} else {
1333
// If the text only contains ASCII characters, it is currently
1334
// impossible that TransformText would remove all whitespaces,
1335
// and thus the check below should return the same result for
1336
// transformed text and original text. So we don't need to try
1337
// transforming it here.
1338
text = static_cast<const void*>(frag->Get1b() + start);
1339
}
1340
if (TextContainsLineBreakerWhiteSpace(text, length, frag->Is2b())) {
1341
aState->mSeenSpaceForLineBreakingOnThisLine = true;
1342
if (aState->mSeenTextRunBoundaryOnLaterLine) {
1343
return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
1344
}
1345
}
1346
return FB_CONTINUE;
1347
}
1348
1349
FrameTextTraversal traversal = CanTextCrossFrameBoundary(aFrame);
1350
if (!traversal.mTextRunCanCrossFrameBoundary) {
1351
aState->mSeenTextRunBoundaryOnThisLine = true;
1352
if (aState->mSeenSpaceForLineBreakingOnThisLine)
1353
return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
1354
}
1355
1356
for (nsIFrame* f = traversal.NextFrameToScan(); f;
1357
f = traversal.NextFrameToScan()) {
1358
FindBoundaryResult result = FindBoundaries(f, aState);
1359
if (result != FB_CONTINUE) return result;
1360
}
1361
1362
if (!traversal.mTextRunCanCrossFrameBoundary) {
1363
aState->mSeenTextRunBoundaryOnThisLine = true;
1364
if (aState->mSeenSpaceForLineBreakingOnThisLine)
1365
return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
1366
}
1367
1368
return FB_CONTINUE;
1369
}
1370
1371
// build text runs for the 200 lines following aForFrame, and stop after that
1372
// when we get a chance.
1373
#define NUM_LINES_TO_BUILD_TEXT_RUNS 200
1374
1375
/**
1376
* General routine for building text runs. This is hairy because of the need
1377
* to build text runs that span content nodes.
1378
*
1379
* @param aContext The gfxContext we're using to construct this text run.
1380
* @param aForFrame The nsTextFrame for which we're building this text run.
1381
* @param aLineContainer the line container containing aForFrame; if null,
1382
* we'll walk the ancestors to find it. It's required to be non-null
1383
* when aForFrameLine is non-null.
1384
* @param aForFrameLine the line containing aForFrame; if null, we'll figure
1385
* out the line (slowly)
1386
* @param aWhichTextRun The type of text run we want to build. If font inflation
1387
* is enabled, this will be eInflated, otherwise it's eNotInflated.
1388
*/
1389
static void BuildTextRuns(DrawTarget* aDrawTarget, nsTextFrame* aForFrame,
1390
nsIFrame* aLineContainer,
1391
const nsLineList::iterator* aForFrameLine,
1392
nsTextFrame::TextRunType aWhichTextRun) {
1393
MOZ_ASSERT(aForFrame, "for no frame?");
1394
NS_ASSERTION(!aForFrameLine || aLineContainer, "line but no line container");
1395
1396
nsIFrame* lineContainerChild = aForFrame;
1397
if (!aLineContainer) {
1398
if (aForFrame->IsFloatingFirstLetterChild()) {
1399
lineContainerChild = aForFrame->GetParent()->GetPlaceholderFrame();
1400
}
1401
aLineContainer = FindLineContainer(lineContainerChild);
1402
} else {
1403
NS_ASSERTION(
1404
(aLineContainer == FindLineContainer(aForFrame) ||
1405
(aLineContainer->IsLetterFrame() && aLineContainer->IsFloating())),
1406
"Wrong line container hint");
1407
}
1408
1409
if (aForFrame->HasAnyStateBits(TEXT_IS_IN_TOKEN_MATHML)) {
1410
aLineContainer->AddStateBits(TEXT_IS_IN_TOKEN_MATHML);
1411
if (aForFrame->HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI)) {
1412
aLineContainer->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
1413
}
1414
}
1415
if (aForFrame->HasAnyStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT)) {
1416
aLineContainer->AddStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT);
1417
}
1418
1419
nsPresContext* presContext = aLineContainer->PresContext();
1420
BuildTextRunsScanner scanner(presContext, aDrawTarget, aLineContainer,
1421
aWhichTextRun);
1422
1423
nsBlockFrame* block = do_QueryFrame(aLineContainer);
1424
1425
if (!block) {
1426
nsIFrame* textRunContainer = aLineContainer;
1427
if (aLineContainer->IsRubyTextContainerFrame()) {
1428
textRunContainer = aForFrame;
1429
while (textRunContainer && !textRunContainer->IsRubyTextFrame()) {
1430
textRunContainer = textRunContainer->GetParent();
1431
}
1432
MOZ_ASSERT(textRunContainer &&
1433
textRunContainer->GetParent() == aLineContainer);
1434
} else {
1435
NS_ASSERTION(
1436
!aLineContainer->GetPrevInFlow() && !aLineContainer->GetNextInFlow(),
1437
"Breakable non-block line containers other than "
1438
"ruby text container is not supported");
1439
}
1440
// Just loop through all the children of the linecontainer ... it's really
1441
// just one line
1442
scanner.SetAtStartOfLine();
1443
scanner.SetCommonAncestorWithLastFrame(nullptr);
1444
for (nsIFrame* child : textRunContainer->PrincipalChildList()) {
1445
scanner.ScanFrame(child);
1446
}
1447
// Set mStartOfLine so FlushFrames knows its textrun ends a line
1448
scanner.SetAtStartOfLine();
1449
scanner.FlushFrames(true, false);
1450
return;
1451
}
1452
1453
// Find the line containing 'lineContainerChild'.
1454
1455
bool isValid = true;
1456
nsBlockInFlowLineIterator backIterator(block, &isValid);
1457
if (aForFrameLine) {
1458
backIterator = nsBlockInFlowLineIterator(block, *aForFrameLine);
1459
} else {
1460
backIterator =
1461
nsBlockInFlowLineIterator(block, lineContainerChild, &isValid);
1462
NS_ASSERTION(isValid, "aForFrame not found in block, someone lied to us");
1463
NS_ASSERTION(backIterator.GetContainer() == block,
1464
"Someone lied to us about the block");
1465
}
1466
nsBlockFrame::LineIterator startLine = backIterator.GetLine();
1467
1468
// Find a line where we can start building text runs. We choose the last line
1469
// where:
1470
// -- there is a textrun boundary between the start of the line and the
1471
// start of aForFrame
1472
// -- there is a space between the start of the line and the textrun boundary
1473
// (this is so we can be sure the line breaks will be set properly
1474
// on the textruns we construct).
1475
// The possibly-partial text runs up to and including the first space
1476
// are not reconstructed. We construct partial text runs for that text ---
1477
// for the sake of simplifying the code and feeding the linebreaker ---
1478
// but we discard them instead of assigning them to frames.
1479
// This is a little awkward because we traverse lines in the reverse direction
1480
// but we traverse the frames in each line in the forward direction.
1481
nsBlockInFlowLineIterator forwardIterator = backIterator;
1482
nsIFrame* stopAtFrame = lineContainerChild;
1483
nsTextFrame* nextLineFirstTextFrame = nullptr;
1484
AutoTArray<char16_t, BIG_TEXT_NODE_SIZE> buffer;
1485
bool seenTextRunBoundaryOnLaterLine = false;
1486
bool mayBeginInTextRun = true;
1487
while (true) {
1488
forwardIterator = backIterator;
1489
nsBlockFrame::LineIterator line = backIterator.GetLine();
1490
if (!backIterator.Prev() || backIterator.GetLine()->IsBlock()) {
1491
mayBeginInTextRun = false;
1492
break;
1493
}
1494
1495
BuildTextRunsScanner::FindBoundaryState state = {
1496
stopAtFrame, nullptr, nullptr, bool(seenTextRunBoundaryOnLaterLine),
1497
false, false, buffer};
1498
nsIFrame* child = line->mFirstChild;
1499
bool foundBoundary = false;
1500
for (int32_t i = line->GetChildCount() - 1; i >= 0; --i) {
1501
BuildTextRunsScanner::FindBoundaryResult result =
1502
scanner.FindBoundaries(child, &state);
1503
if (result == BuildTextRunsScanner::FB_FOUND_VALID_TEXTRUN_BOUNDARY) {
1504
foundBoundary = true;
1505
break;
1506
} else if (result == BuildTextRunsScanner::FB_STOPPED_AT_STOP_FRAME) {
1507
break;
1508
}
1509
child = child->GetNextSibling();
1510
}
1511
if (foundBoundary) break;
1512
if (!stopAtFrame && state.mLastTextFrame && nextLineFirstTextFrame &&
1513
!scanner.ContinueTextRunAcrossFrames(state.mLastTextFrame,
1514
nextLineFirstTextFrame)) {
1515
// Found a usable textrun boundary at the end of the line
1516
if (state.mSeenSpaceForLineBreakingOnThisLine) break;
1517
seenTextRunBoundaryOnLaterLine = true;
1518
} else if (state.mSeenTextRunBoundaryOnThisLine) {
1519
seenTextRunBoundaryOnLaterLine = true;
1520
}
1521
stopAtFrame = nullptr;
1522
if (state.mFirstTextFrame) {
1523
nextLineFirstTextFrame = state.mFirstTextFrame;
1524
}
1525
}
1526
scanner.SetSkipIncompleteTextRuns(mayBeginInTextRun);
1527
1528
// Now iterate over all text frames starting from the current line.
1529
// First-in-flow text frames will be accumulated into textRunFrames as we go.
1530
// When a text run boundary is required we flush textRunFrames ((re)building
1531
// their gfxTextRuns as necessary).
1532
bool seenStartLine = false;
1533
uint32_t linesAfterStartLine = 0;
1534
do {
1535
nsBlockFrame::LineIterator line = forwardIterator.GetLine();
1536
if (line->IsBlock()) break;
1537
line->SetInvalidateTextRuns(false);
1538
scanner.SetAtStartOfLine();
1539
scanner.SetCommonAncestorWithLastFrame(nullptr);
1540
nsIFrame* child = line->mFirstChild;
1541
for (int32_t i = line->GetChildCount() - 1; i >= 0; --i) {
1542
scanner.ScanFrame(child);
1543
child = child->GetNextSibling();
1544
}
1545
if (line.get() == startLine.get()) {
1546
seenStartLine = true;
1547
}
1548
if (seenStartLine) {
1549
++linesAfterStartLine;
1550
if (linesAfterStartLine >= NUM_LINES_TO_BUILD_TEXT_RUNS &&
1551
scanner.CanStopOnThisLine()) {
1552
// Don't flush frames; we may be in the middle of a textrun
1553
// that we can't end here. That's OK, we just won't build it.
1554
// Note that we must already have finished the textrun for aForFrame,
1555
// because we've seen the end of a textrun in a line after the line
1556
// containing aForFrame.
1557
scanner.FlushLineBreaks(nullptr);
1558
// This flushes out mMappedFlows and mLineBreakBeforeFrames, which
1559
// silences assertions in the scanner destructor.
1560
scanner.ResetRunInfo();
1561
return;
1562
}
1563
}
1564
} while (forwardIterator.Next());
1565
1566
// Set mStartOfLine so FlushFrames knows its textrun ends a line
1567
scanner.SetAtStartOfLine();
1568
scanner.FlushFrames(true, false);
1569
}
1570
1571
static char16_t* ExpandBuffer(char16_t* aDest, uint8_t* aSrc, uint32_t aCount) {
1572
while (aCount) {
1573
*aDest = *aSrc;
1574
++aDest;
1575
++aSrc;
1576
--aCount;
1577
}
1578
return aDest;
1579
}
1580
1581
bool BuildTextRunsScanner::IsTextRunValidForMappedFlows(
1582
const gfxTextRun* aTextRun) {
1583
if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSimpleFlow) {
1584
return mMappedFlows.Length() == 1 &&
1585
mMappedFlows[0].mStartFrame == GetFrameForSimpleFlow(aTextRun) &&
1586
mMappedFlows[0].mEndFrame == nullptr;
1587
}
1588
1589
auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
1590
TextRunMappedFlow* userMappedFlows = GetMappedFlows(aTextRun);
1591
if (userData->mMappedFlowCount != mMappedFlows.Length()) return false;
1592
for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
1593
if (userMappedFlows[i].mStartFrame != mMappedFlows[i].mStartFrame ||
1594
int32_t(userMappedFlows[i].mContentLength) !=
1595
mMappedFlows[i].GetContentEnd() -
1596
mMappedFlows[i].mStartFrame->GetContentOffset())
1597
return false;
1598
}
1599
return true;
1600
}
1601
1602
/**
1603
* This gets called when we need to make a text run for the current list of
1604
* frames.
1605
*/
1606
void BuildTextRunsScanner::FlushFrames(bool aFlushLineBreaks,
1607
bool aSuppressTrailingBreak) {
1608
RefPtr<gfxTextRun> textRun;
1609
if (!mMappedFlows.IsEmpty()) {
1610
if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun &&
1611
!!(mCurrentFramesAllSameTextRun->GetFlags2() &
1612
nsTextFrameUtils::Flags::IncomingWhitespace) ==
1613
!!(mCurrentRunContextInfo &
1614
nsTextFrameUtils::INCOMING_WHITESPACE) &&
1615
!!(mCurrentFramesAllSameTextRun->GetFlags() &
1616
gfx::ShapedTextFlags::TEXT_INCOMING_ARABICCHAR) ==
1617
!!(mCurrentRunContextInfo &
1618
nsTextFrameUtils::INCOMING_ARABICCHAR) &&
1619
IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) {
1620
// Optimization: We do not need to (re)build the textrun.
1621
textRun = mCurrentFramesAllSameTextRun;
1622
1623
// Feed this run's text into the linebreaker to provide context.
1624
if (!SetupLineBreakerContext(textRun)) {
1625
return;
1626
}
1627
1628
// Update mNextRunContextInfo appropriately
1629
mNextRunContextInfo = nsTextFrameUtils::INCOMING_NONE;
1630
if (textRun->GetFlags2() & nsTextFrameUtils::Flags::TrailingWhitespace) {
1631
mNextRunContextInfo |= nsTextFrameUtils::INCOMING_WHITESPACE;
1632
}
1633
if (textRun->GetFlags() &
1634
gfx::ShapedTextFlags::TEXT_TRAILING_ARABICCHAR) {
1635
mNextRunContextInfo |= nsTextFrameUtils::INCOMING_ARABICCHAR;
1636
}
1637
} else {
1638
AutoTArray<uint8_t, BIG_TEXT_NODE_SIZE> buffer;
1639
uint32_t bufferSize = mMaxTextLength * (mDoubleByteText ? 2 : 1);
1640
if (bufferSize < mMaxTextLength || bufferSize == UINT32_MAX ||
1641
!buffer.AppendElements(bufferSize, fallible)) {
1642
return;
1643
}
1644
textRun = BuildTextRunForFrames(buffer.Elements());
1645
}
1646
}
1647
1648
if (aFlushLineBreaks) {
1649
FlushLineBreaks(aSuppressTrailingBreak ? nullptr : textRun.get());
1650
}
1651
1652
mCanStopOnThisLine = true;
1653
ResetRunInfo();
1654
}
1655
1656
void BuildTextRunsScanner::FlushLineBreaks(gfxTextRun* aTrailingTextRun) {
1657
// If the line-breaker is buffering a potentially-unfinished word,
1658
// preserve the state of being in-word so that we don't spuriously
1659
// capitalize the next letter.
1660
bool inWord = mLineBreaker.InWord();
1661
bool trailingLineBreak;
1662
nsresult rv = mLineBreaker.Reset(&trailingLineBreak);
1663
mLineBreaker.SetWordContinuation(inWord);
1664
// textRun may be null for various reasons, including because we constructed
1665
// a partial textrun just to get the linebreaker and other state set up
1666
// to build the next textrun.
1667
if (NS_SUCCEEDED(rv) && trailingLineBreak && aTrailingTextRun) {
1668
aTrailingTextRun->SetFlagBits(nsTextFrameUtils::Flags::HasTrailingBreak);
1669
}
1670
1671
for (uint32_t i = 0; i < mBreakSinks.Length(); ++i) {
1672
// TODO cause frames associated with the textrun to be reflowed, if they
1673
// aren't being reflowed already!
1674
mBreakSinks[i]->Finish(mMissingFonts);
1675
}
1676
mBreakSinks.Clear();
1677
}
1678
1679
void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame) {
1680
if (mMaxTextLength != UINT32_MAX) {
1681
NS_ASSERTION(mMaxTextLength < UINT32_MAX - aFrame->GetContentLength(),
1682
"integer overflow");
1683
if (mMaxTextLength >= UINT32_MAX - aFrame->GetContentLength()) {
1684
mMaxTextLength = UINT32_MAX;
1685
} else {
1686
mMaxTextLength += aFrame->GetContentLength();
1687
}
1688
}
1689
mDoubleByteText |= aFrame->TextFragment()->Is2b();
1690
mLastFrame = aFrame;
1691
mCommonAncestorWithLastFrame = aFrame->GetParent();
1692
1693
MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
1694
NS_ASSERTION(mappedFlow->mStartFrame == aFrame ||
1695
mappedFlow->GetContentEnd() == aFrame->GetContentOffset(),
1696
"Overlapping or discontiguous frames => BAD");
1697
mappedFlow->mEndFrame = aFrame->GetNextContinuation();
1698
if (mCurrentFramesAllSameTextRun != aFrame->GetTextRun(mWhichTextRun)) {
1699
mCurrentFramesAllSameTextRun = nullptr;
1700
}
1701
1702
if (mStartOfLine) {
1703
mLineBreakBeforeFrames.AppendElement(aFrame);
1704
mStartOfLine = false;
1705
}
1706
}
1707
1708
static bool HasTerminalNewline(const nsTextFrame* aFrame) {
1709
if (aFrame->GetContentLength() == 0) return false;
1710
const nsTextFragment* frag = aFrame->TextFragment();
1711
return frag->CharAt(aFrame->GetContentEnd() - 1) == '\n';
1712
}
1713
1714
static gfxFont::Metrics GetFirstFontMetrics(gfxFontGroup* aFontGroup,
1715
bool aVerticalMetrics) {
1716
if (!aFontGroup) return gfxFont::Metrics();
1717
gfxFont* font = aFontGroup->GetFirstValidFont();
1718
return font->GetMetrics(aVerticalMetrics ? nsFontMetrics::eVertical
1719
: nsFontMetrics::eHorizontal);
1720
}
1721
1722
static nscoord GetSpaceWidthAppUnits(const gfxTextRun* aTextRun) {
1723
// Round the space width when converting to appunits the same way textruns
1724
// do.
1725
gfxFloat spaceWidthAppUnits =
1726
NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(),
1727
aTextRun->UseCenterBaseline())
1728
.spaceWidth *
1729
aTextRun->GetAppUnitsPerDevUnit());
1730
1731
return spaceWidthAppUnits;
1732
}
1733
1734
static gfxFloat GetMinTabAdvanceAppUnits(const gfxTextRun* aTextRun) {
1735
gfxFloat chWidthAppUnits = NS_round(
1736
GetFirstFontMetrics(aTextRun->GetFontGroup(), aTextRun->IsVertical())
1737
.ZeroOrAveCharWidth() *
1738
aTextRun->GetAppUnitsPerDevUnit());
1739
return 0.5 * chWidthAppUnits;
1740
}
1741
1742
static nscoord LetterSpacing(nsIFrame* aFrame,
1743
const nsStyleText* aStyleText = nullptr) {
1744
if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
1745
return 0;
1746
}
1747
if (!aStyleText) {
1748
aStyleText = aFrame->StyleText();
1749
}
1750
return aStyleText->mLetterSpacing.ToAppUnits();
1751
}
1752
1753
// This function converts non-coord values (e.g. percentages) to nscoord.
1754
static nscoord WordSpacing(nsIFrame* aFrame, const gfxTextRun* aTextRun,
1755
const nsStyleText* aStyleText = nullptr) {
1756
if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
1757
return 0;
1758
}
1759
if (!aStyleText) {
1760
aStyleText = aFrame->StyleText();
1761
}
1762
1763
return aStyleText->mWordSpacing.Resolve(
1764
[&] { return GetSpaceWidthAppUnits(aTextRun); });
1765
}
1766
1767
// Returns gfxTextRunFactory::TEXT_ENABLE_SPACING if non-standard
1768
// letter-spacing or word-spacing is present.
1769
static gfx::ShapedTextFlags GetSpacingFlags(
1770
nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr) {
1771
if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
1772
return gfx::ShapedTextFlags();
1773
}
1774
1775
const nsStyleText* styleText = aFrame->StyleText();
1776
const auto& ls = styleText->mLetterSpacing;
1777
const auto& ws = styleText->mWordSpacing;
1778
1779
// It's possible to have a calc() value that computes to zero but for which
1780
// IsDefinitelyZero() is false, in which case we'll return