1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=4 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 #include "gfxTextRun.h"
8 #include "gfxGlyphExtents.h"
9 #include "gfxHarfBuzzShaper.h"
10 #include "gfxPlatformFontList.h"
11 #include "gfxUserFontSet.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/gfx/PathHelpers.h"
14 #include "mozilla/Sprintf.h"
15 #include "mozilla/StaticPresData.h"
16 
17 #include "gfxContext.h"
18 #include "gfxFontConstants.h"
19 #include "gfxFontMissingGlyphs.h"
20 #include "gfxScriptItemizer.h"
21 #include "nsUnicodeProperties.h"
22 #include "nsStyleConsts.h"
23 #include "nsStyleUtil.h"
24 #include "mozilla/Likely.h"
25 #include "gfx2DGlue.h"
26 #include "mozilla/gfx/Logging.h"  // for gfxCriticalError
27 #include "mozilla/intl/UnicodeProperties.h"
28 #include "mozilla/UniquePtr.h"
29 #include "mozilla/Unused.h"
30 #include "SharedFontList-impl.h"
31 #include "TextDrawTarget.h"
32 
33 #include <unicode/unorm2.h>
34 
35 #ifdef XP_WIN
36 #  include "gfxWindowsPlatform.h"
37 #endif
38 
39 using namespace mozilla;
40 using namespace mozilla::gfx;
41 using namespace mozilla::intl;
42 using namespace mozilla::unicode;
43 using mozilla::services::GetObserverService;
44 
45 static const char16_t kEllipsisChar[] = {0x2026, 0x0};
46 static const char16_t kASCIIPeriodsChar[] = {'.', '.', '.', 0x0};
47 
48 #ifdef DEBUG_roc
49 #  define DEBUG_TEXT_RUN_STORAGE_METRICS
50 #endif
51 
52 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
53 extern uint32_t gTextRunStorageHighWaterMark;
54 extern uint32_t gTextRunStorage;
55 extern uint32_t gFontCount;
56 extern uint32_t gGlyphExtentsCount;
57 extern uint32_t gGlyphExtentsWidthsTotalSize;
58 extern uint32_t gGlyphExtentsSetupEagerSimple;
59 extern uint32_t gGlyphExtentsSetupEagerTight;
60 extern uint32_t gGlyphExtentsSetupLazyTight;
61 extern uint32_t gGlyphExtentsSetupFallBackToTight;
62 #endif
63 
NextRun()64 bool gfxTextRun::GlyphRunIterator::NextRun() {
65   int32_t glyphRunCount;
66   if (mTextRun->mHasGlyphRunArray) {
67     glyphRunCount = mTextRun->mGlyphRunArray.Length();
68     if (mNextIndex >= glyphRunCount || mNextIndex < 0) {
69       return false;
70     }
71     mGlyphRun = &mTextRun->mGlyphRunArray[mNextIndex];
72   } else {
73     if (mNextIndex != 0 || !mTextRun->mSingleGlyphRun.mFont) {
74       return false;
75     }
76     glyphRunCount = 1;
77     mGlyphRun = &mTextRun->mSingleGlyphRun;
78   }
79 
80   if (mGlyphRun->mCharacterOffset >= mEndOffset) {
81     return false;
82   }
83 
84   uint32_t glyphRunEndOffset =
85       mNextIndex + 1 < (int32_t)glyphRunCount
86           ? mTextRun->mGlyphRunArray[mNextIndex + 1].mCharacterOffset
87           : mTextRun->GetLength();
88 
89   if (glyphRunEndOffset <= mStartOffset) {
90     return false;
91   }
92 
93   mStringEnd = std::min(mEndOffset, glyphRunEndOffset);
94   mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
95   mNextIndex += mDirection;
96   return true;
97 }
98 
99 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
AccountStorageForTextRun(gfxTextRun * aTextRun,int32_t aSign)100 static void AccountStorageForTextRun(gfxTextRun* aTextRun, int32_t aSign) {
101   // Ignores detailed glyphs... we don't know when those have been constructed
102   // Also ignores gfxSkipChars dynamic storage (which won't be anything
103   // for preformatted text)
104   // Also ignores GlyphRun array, again because it hasn't been constructed
105   // by the time this gets called. If there's only one glyphrun that's stored
106   // directly in the textrun anyway so no additional overhead.
107   uint32_t length = aTextRun->GetLength();
108   int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
109   bytes += sizeof(gfxTextRun);
110   gTextRunStorage += bytes * aSign;
111   gTextRunStorageHighWaterMark =
112       std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
113 }
114 #endif
115 
NeedsGlyphExtents(gfxTextRun * aTextRun)116 static bool NeedsGlyphExtents(gfxTextRun* aTextRun) {
117   if (aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX)
118     return true;
119   uint32_t numRuns;
120   const gfxTextRun::GlyphRun* glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
121   for (uint32_t i = 0; i < numRuns; ++i) {
122     if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont()) return true;
123   }
124   return false;
125 }
126 
127 // Helper for textRun creation to preallocate storage for glyph records;
128 // this function returns a pointer to the newly-allocated glyph storage.
129 // Returns nullptr if allocation fails.
AllocateStorageForTextRun(size_t aSize,uint32_t aLength)130 void* gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength) {
131   // Allocate the storage we need, returning nullptr on failure rather than
132   // throwing an exception (because web content can create huge runs).
133   void* storage = malloc(aSize + aLength * sizeof(CompressedGlyph));
134   if (!storage) {
135     NS_WARNING("failed to allocate storage for text run!");
136     return nullptr;
137   }
138 
139   // Initialize the glyph storage (beyond aSize) to zero
140   memset(reinterpret_cast<char*>(storage) + aSize, 0,
141          aLength * sizeof(CompressedGlyph));
142 
143   return storage;
144 }
145 
Create(const gfxTextRunFactory::Parameters * aParams,uint32_t aLength,gfxFontGroup * aFontGroup,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)146 already_AddRefed<gfxTextRun> gfxTextRun::Create(
147     const gfxTextRunFactory::Parameters* aParams, uint32_t aLength,
148     gfxFontGroup* aFontGroup, gfx::ShapedTextFlags aFlags,
149     nsTextFrameUtils::Flags aFlags2) {
150   void* storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
151   if (!storage) {
152     return nullptr;
153   }
154 
155   RefPtr<gfxTextRun> result =
156       new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags, aFlags2);
157   return result.forget();
158 }
159 
gfxTextRun(const gfxTextRunFactory::Parameters * aParams,uint32_t aLength,gfxFontGroup * aFontGroup,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)160 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters* aParams,
161                        uint32_t aLength, gfxFontGroup* aFontGroup,
162                        gfx::ShapedTextFlags aFlags,
163                        nsTextFrameUtils::Flags aFlags2)
164     : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit),
165       mSingleGlyphRun(),
166       mUserData(aParams->mUserData),
167       mFontGroup(aFontGroup),
168       mFlags2(aFlags2),
169       mReleasedFontGroup(false),
170       mReleasedFontGroupSkippedDrawing(false),
171       mHasGlyphRunArray(false),
172       mShapingState(eShapingState_Normal) {
173   NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
174   NS_ADDREF(mFontGroup);
175 
176 #ifndef RELEASE_OR_BETA
177   gfxTextPerfMetrics* tp = aFontGroup->GetTextPerfMetrics();
178   if (tp) {
179     tp->current.textrunConst++;
180   }
181 #endif
182 
183   mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
184 
185   if (aParams->mSkipChars) {
186     mSkipChars.TakeFrom(aParams->mSkipChars);
187   }
188 
189 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
190   AccountStorageForTextRun(this, 1);
191 #endif
192 
193   mDontSkipDrawing =
194       !!(aFlags2 & nsTextFrameUtils::Flags::DontSkipDrawingForPendingUserFonts);
195 }
196 
~gfxTextRun()197 gfxTextRun::~gfxTextRun() {
198 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
199   AccountStorageForTextRun(this, -1);
200 #endif
201 #ifdef DEBUG
202   // Make it easy to detect a dead text run
203   mFlags = ~gfx::ShapedTextFlags();
204   mFlags2 = ~nsTextFrameUtils::Flags();
205 #endif
206 
207   if (mHasGlyphRunArray) {
208     mGlyphRunArray.~nsTArray<GlyphRun>();
209   } else {
210     mSingleGlyphRun.mFont = nullptr;
211   }
212 
213   // The cached ellipsis textrun (if any) in a fontgroup will have already
214   // been told to release its reference to the group, so we mustn't do that
215   // again here.
216   if (!mReleasedFontGroup) {
217 #ifndef RELEASE_OR_BETA
218     gfxTextPerfMetrics* tp = mFontGroup->GetTextPerfMetrics();
219     if (tp) {
220       tp->current.textrunDestr++;
221     }
222 #endif
223     NS_RELEASE(mFontGroup);
224   }
225 }
226 
ReleaseFontGroup()227 void gfxTextRun::ReleaseFontGroup() {
228   NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
229 
230   // After dropping our reference to the font group, we'll no longer be able
231   // to get up-to-date results for ShouldSkipDrawing().  Store the current
232   // value in mReleasedFontGroupSkippedDrawing.
233   //
234   // (It doesn't actually matter that we can't get up-to-date results for
235   // ShouldSkipDrawing(), since the only text runs that we call
236   // ReleaseFontGroup() for are ellipsis text runs, and we ask the font
237   // group for a new ellipsis text run each time we want to draw one,
238   // and ensure that the cached one is cleared in ClearCachedData() when
239   // font loading status changes.)
240   mReleasedFontGroupSkippedDrawing = mFontGroup->ShouldSkipDrawing();
241 
242   NS_RELEASE(mFontGroup);
243   mReleasedFontGroup = true;
244 }
245 
SetPotentialLineBreaks(Range aRange,const uint8_t * aBreakBefore)246 bool gfxTextRun::SetPotentialLineBreaks(Range aRange,
247                                         const uint8_t* aBreakBefore) {
248   NS_ASSERTION(aRange.end <= GetLength(), "Overflow");
249 
250   uint32_t changed = 0;
251   CompressedGlyph* cg = mCharacterGlyphs + aRange.start;
252   const CompressedGlyph* const end = cg + aRange.Length();
253   while (cg < end) {
254     uint8_t canBreak = *aBreakBefore++;
255     if (canBreak && !cg->IsClusterStart()) {
256       // XXX If we replace the line-breaker with one based more closely
257       // on UAX#14 (e.g. using ICU), this may not be needed any more.
258       // Avoid possible breaks inside a cluster, EXCEPT when the previous
259       // character was a space (compare UAX#14 rules LB9, LB10).
260       if (cg == mCharacterGlyphs || !(cg - 1)->CharIsSpace()) {
261         canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
262       }
263     }
264     changed |= cg->SetCanBreakBefore(canBreak);
265     ++cg;
266   }
267   return changed != 0;
268 }
269 
ComputeLigatureData(Range aPartRange,PropertyProvider * aProvider) const270 gfxTextRun::LigatureData gfxTextRun::ComputeLigatureData(
271     Range aPartRange, PropertyProvider* aProvider) const {
272   NS_ASSERTION(aPartRange.start < aPartRange.end,
273                "Computing ligature data for empty range");
274   NS_ASSERTION(aPartRange.end <= GetLength(), "Character length overflow");
275 
276   LigatureData result;
277   const CompressedGlyph* charGlyphs = mCharacterGlyphs;
278 
279   uint32_t i;
280   for (i = aPartRange.start; !charGlyphs[i].IsLigatureGroupStart(); --i) {
281     NS_ASSERTION(i > 0, "Ligature at the start of the run??");
282   }
283   result.mRange.start = i;
284   for (i = aPartRange.start + 1;
285        i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
286   }
287   result.mRange.end = i;
288 
289   int32_t ligatureWidth = GetAdvanceForGlyphs(result.mRange);
290   // Count the number of started clusters we have seen
291   uint32_t totalClusterCount = 0;
292   uint32_t partClusterIndex = 0;
293   uint32_t partClusterCount = 0;
294   for (i = result.mRange.start; i < result.mRange.end; ++i) {
295     // Treat the first character of the ligature as the start of a
296     // cluster for our purposes of allocating ligature width to its
297     // characters.
298     if (i == result.mRange.start || charGlyphs[i].IsClusterStart()) {
299       ++totalClusterCount;
300       if (i < aPartRange.start) {
301         ++partClusterIndex;
302       } else if (i < aPartRange.end) {
303         ++partClusterCount;
304       }
305     }
306   }
307   NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
308   result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
309   result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
310 
311   // Any rounding errors are apportioned to the final part of the ligature,
312   // so that measuring all parts of a ligature and summing them is equal to
313   // the ligature width.
314   if (aPartRange.end == result.mRange.end) {
315     gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
316     result.mPartWidth += ligatureWidth - allParts;
317   }
318 
319   if (partClusterCount == 0) {
320     // nothing to draw
321     result.mClipBeforePart = result.mClipAfterPart = true;
322   } else {
323     // Determine whether we should clip before or after this part when
324     // drawing its slice of the ligature.
325     // We need to clip before the part if any cluster is drawn before
326     // this part.
327     result.mClipBeforePart = partClusterIndex > 0;
328     // We need to clip after the part if any cluster is drawn after
329     // this part.
330     result.mClipAfterPart =
331         partClusterIndex + partClusterCount < totalClusterCount;
332   }
333 
334   if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
335     gfxFont::Spacing spacing;
336     if (aPartRange.start == result.mRange.start) {
337       aProvider->GetSpacing(Range(aPartRange.start, aPartRange.start + 1),
338                             &spacing);
339       result.mPartWidth += spacing.mBefore;
340     }
341     if (aPartRange.end == result.mRange.end) {
342       aProvider->GetSpacing(Range(aPartRange.end - 1, aPartRange.end),
343                             &spacing);
344       result.mPartWidth += spacing.mAfter;
345     }
346   }
347 
348   return result;
349 }
350 
ComputePartialLigatureWidth(Range aPartRange,PropertyProvider * aProvider) const351 gfxFloat gfxTextRun::ComputePartialLigatureWidth(
352     Range aPartRange, PropertyProvider* aProvider) const {
353   if (aPartRange.start >= aPartRange.end) return 0;
354   LigatureData data = ComputeLigatureData(aPartRange, aProvider);
355   return data.mPartWidth;
356 }
357 
GetAdvanceForGlyphs(Range aRange) const358 int32_t gfxTextRun::GetAdvanceForGlyphs(Range aRange) const {
359   int32_t advance = 0;
360   for (auto i = aRange.start; i < aRange.end; ++i) {
361     advance += GetAdvanceForGlyph(i);
362   }
363   return advance;
364 }
365 
GetAdjustedSpacing(const gfxTextRun * aTextRun,gfxTextRun::Range aRange,gfxTextRun::PropertyProvider * aProvider,gfxTextRun::PropertyProvider::Spacing * aSpacing)366 static void GetAdjustedSpacing(
367     const gfxTextRun* aTextRun, gfxTextRun::Range aRange,
368     gfxTextRun::PropertyProvider* aProvider,
369     gfxTextRun::PropertyProvider::Spacing* aSpacing) {
370   if (aRange.start >= aRange.end) return;
371 
372   aProvider->GetSpacing(aRange, aSpacing);
373 
374 #ifdef DEBUG
375   // Check to see if we have spacing inside ligatures
376 
377   const gfxTextRun::CompressedGlyph* charGlyphs =
378       aTextRun->GetCharacterGlyphs();
379   uint32_t i;
380 
381   for (i = aRange.start; i < aRange.end; ++i) {
382     if (!charGlyphs[i].IsLigatureGroupStart()) {
383       NS_ASSERTION(i == aRange.start || aSpacing[i - aRange.start].mBefore == 0,
384                    "Before-spacing inside a ligature!");
385       NS_ASSERTION(
386           i - 1 <= aRange.start || aSpacing[i - 1 - aRange.start].mAfter == 0,
387           "After-spacing inside a ligature!");
388     }
389   }
390 #endif
391 }
392 
GetAdjustedSpacingArray(Range aRange,PropertyProvider * aProvider,Range aSpacingRange,nsTArray<PropertyProvider::Spacing> * aSpacing) const393 bool gfxTextRun::GetAdjustedSpacingArray(
394     Range aRange, PropertyProvider* aProvider, Range aSpacingRange,
395     nsTArray<PropertyProvider::Spacing>* aSpacing) const {
396   if (!aProvider || !(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
397     return false;
398   }
399   if (!aSpacing->AppendElements(aRange.Length(), fallible)) {
400     return false;
401   }
402   auto spacingOffset = aSpacingRange.start - aRange.start;
403   memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing) * spacingOffset);
404   GetAdjustedSpacing(this, aSpacingRange, aProvider,
405                      aSpacing->Elements() + spacingOffset);
406   memset(aSpacing->Elements() + aSpacingRange.end - aRange.start, 0,
407          sizeof(gfxFont::Spacing) * (aRange.end - aSpacingRange.end));
408   return true;
409 }
410 
ShrinkToLigatureBoundaries(Range * aRange) const411 void gfxTextRun::ShrinkToLigatureBoundaries(Range* aRange) const {
412   if (aRange->start >= aRange->end) return;
413 
414   const CompressedGlyph* charGlyphs = mCharacterGlyphs;
415 
416   while (aRange->start < aRange->end &&
417          !charGlyphs[aRange->start].IsLigatureGroupStart()) {
418     ++aRange->start;
419   }
420   if (aRange->end < GetLength()) {
421     while (aRange->end > aRange->start &&
422            !charGlyphs[aRange->end].IsLigatureGroupStart()) {
423       --aRange->end;
424     }
425   }
426 }
427 
DrawGlyphs(gfxFont * aFont,Range aRange,gfx::Point * aPt,PropertyProvider * aProvider,Range aSpacingRange,TextRunDrawParams & aParams,gfx::ShapedTextFlags aOrientation) const428 void gfxTextRun::DrawGlyphs(gfxFont* aFont, Range aRange, gfx::Point* aPt,
429                             PropertyProvider* aProvider, Range aSpacingRange,
430                             TextRunDrawParams& aParams,
431                             gfx::ShapedTextFlags aOrientation) const {
432   AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
433   bool haveSpacing =
434       GetAdjustedSpacingArray(aRange, aProvider, aSpacingRange, &spacingBuffer);
435   aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
436   aFont->Draw(this, aRange.start, aRange.end, aPt, aParams, aOrientation);
437 }
438 
ClipPartialLigature(const gfxTextRun * aTextRun,gfxFloat * aStart,gfxFloat * aEnd,gfxFloat aOrigin,gfxTextRun::LigatureData * aLigature)439 static void ClipPartialLigature(const gfxTextRun* aTextRun, gfxFloat* aStart,
440                                 gfxFloat* aEnd, gfxFloat aOrigin,
441                                 gfxTextRun::LigatureData* aLigature) {
442   if (aLigature->mClipBeforePart) {
443     if (aTextRun->IsRightToLeft()) {
444       *aEnd = std::min(*aEnd, aOrigin);
445     } else {
446       *aStart = std::max(*aStart, aOrigin);
447     }
448   }
449   if (aLigature->mClipAfterPart) {
450     gfxFloat endEdge =
451         aOrigin + aTextRun->GetDirection() * aLigature->mPartWidth;
452     if (aTextRun->IsRightToLeft()) {
453       *aStart = std::max(*aStart, endEdge);
454     } else {
455       *aEnd = std::min(*aEnd, endEdge);
456     }
457   }
458 }
459 
DrawPartialLigature(gfxFont * aFont,Range aRange,gfx::Point * aPt,PropertyProvider * aProvider,TextRunDrawParams & aParams,gfx::ShapedTextFlags aOrientation) const460 void gfxTextRun::DrawPartialLigature(gfxFont* aFont, Range aRange,
461                                      gfx::Point* aPt,
462                                      PropertyProvider* aProvider,
463                                      TextRunDrawParams& aParams,
464                                      gfx::ShapedTextFlags aOrientation) const {
465   if (aRange.start >= aRange.end) {
466     return;
467   }
468 
469   // Draw partial ligature. We hack this by clipping the ligature.
470   LigatureData data = ComputeLigatureData(aRange, aProvider);
471   gfxRect clipExtents = aParams.context->GetClipExtents();
472   gfxFloat start, end;
473   if (aParams.isVerticalRun) {
474     start = clipExtents.Y() * mAppUnitsPerDevUnit;
475     end = clipExtents.YMost() * mAppUnitsPerDevUnit;
476     ClipPartialLigature(this, &start, &end, aPt->y, &data);
477   } else {
478     start = clipExtents.X() * mAppUnitsPerDevUnit;
479     end = clipExtents.XMost() * mAppUnitsPerDevUnit;
480     ClipPartialLigature(this, &start, &end, aPt->x, &data);
481   }
482 
483   {
484     // use division here to ensure that when the rect is aligned on multiples
485     // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
486     // Also, make sure we snap the rectangle to device pixels.
487     Rect clipRect =
488         aParams.isVerticalRun
489             ? Rect(clipExtents.X(), start / mAppUnitsPerDevUnit,
490                    clipExtents.Width(), (end - start) / mAppUnitsPerDevUnit)
491             : Rect(start / mAppUnitsPerDevUnit, clipExtents.Y(),
492                    (end - start) / mAppUnitsPerDevUnit, clipExtents.Height());
493     MaybeSnapToDevicePixels(clipRect, *aParams.dt, true);
494 
495     aParams.context->Clip(clipRect);
496   }
497 
498   gfx::Point pt;
499   if (aParams.isVerticalRun) {
500     pt = Point(aPt->x, aPt->y - aParams.direction * data.mPartAdvance);
501   } else {
502     pt = Point(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
503   }
504 
505   DrawGlyphs(aFont, data.mRange, &pt, aProvider, aRange, aParams, aOrientation);
506   aParams.context->PopClip();
507 
508   if (aParams.isVerticalRun) {
509     aPt->y += aParams.direction * data.mPartWidth;
510   } else {
511     aPt->x += aParams.direction * data.mPartWidth;
512   }
513 }
514 
515 // Returns true if the font has synthetic bolding enabled,
516 // or is a color font (COLR/SVG/sbix/CBDT), false otherwise. This is used to
517 // check whether the text run needs to be explicitly composited in order to
518 // support opacity.
HasSyntheticBoldOrColor(gfxFont * aFont)519 static bool HasSyntheticBoldOrColor(gfxFont* aFont) {
520   if (aFont->ApplySyntheticBold()) {
521     return true;
522   }
523   gfxFontEntry* fe = aFont->GetFontEntry();
524   if (fe->TryGetSVGData(aFont) || fe->TryGetColorGlyphs()) {
525     return true;
526   }
527 #if defined(XP_MACOSX)  // sbix fonts only supported via Core Text
528   if (fe->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))) {
529     return true;
530   }
531 #endif
532   return false;
533 }
534 
535 // helper class for double-buffering drawing with non-opaque color
536 struct MOZ_STACK_CLASS BufferAlphaColor {
BufferAlphaColorBufferAlphaColor537   explicit BufferAlphaColor(gfxContext* aContext) : mContext(aContext) {}
538 
539   ~BufferAlphaColor() = default;
540 
PushSolidColorBufferAlphaColor541   void PushSolidColor(const gfxRect& aBounds, const DeviceColor& aAlphaColor,
542                       uint32_t appsPerDevUnit) {
543     mContext->Save();
544     mContext->SnappedClip(gfxRect(
545         aBounds.X() / appsPerDevUnit, aBounds.Y() / appsPerDevUnit,
546         aBounds.Width() / appsPerDevUnit, aBounds.Height() / appsPerDevUnit));
547     mContext->SetDeviceColor(
548         DeviceColor(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
549     mContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aAlphaColor.a);
550   }
551 
PopAlphaBufferAlphaColor552   void PopAlpha() {
553     // pop the text, using the color alpha as the opacity
554     mContext->PopGroupAndBlend();
555     mContext->Restore();
556   }
557 
558   gfxContext* mContext;
559 };
560 
Draw(const Range aRange,const gfx::Point aPt,const DrawParams & aParams) const561 void gfxTextRun::Draw(const Range aRange, const gfx::Point aPt,
562                       const DrawParams& aParams) const {
563   NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
564   NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH ||
565                    !(aParams.drawMode & DrawMode::GLYPH_PATH),
566                "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or "
567                "GLYPH_STROKE_UNDERNEATH");
568   NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH || !aParams.callbacks,
569                "callback must not be specified unless using GLYPH_PATH");
570 
571   bool skipDrawing =
572       !mDontSkipDrawing && (mFontGroup ? mFontGroup->ShouldSkipDrawing()
573                                        : mReleasedFontGroupSkippedDrawing);
574   if (aParams.drawMode & DrawMode::GLYPH_FILL) {
575     DeviceColor currentColor;
576     if (aParams.context->GetDeviceColor(currentColor) && currentColor.a == 0 &&
577         !aParams.context->GetTextDrawer()) {
578       skipDrawing = true;
579     }
580   }
581 
582   gfxFloat direction = GetDirection();
583 
584   if (skipDrawing) {
585     // We don't need to draw anything;
586     // but if the caller wants advance width, we need to compute it here
587     if (aParams.advanceWidth) {
588       gfxTextRun::Metrics metrics =
589           MeasureText(aRange, gfxFont::LOOSE_INK_EXTENTS,
590                       aParams.context->GetDrawTarget(), aParams.provider);
591       *aParams.advanceWidth = metrics.mAdvanceWidth * direction;
592     }
593 
594     // return without drawing
595     return;
596   }
597 
598   // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
599   // correctly unless first drawn without alpha
600   BufferAlphaColor syntheticBoldBuffer(aParams.context);
601   DeviceColor currentColor;
602   bool mayNeedBuffering =
603       aParams.drawMode & DrawMode::GLYPH_FILL &&
604       aParams.context->HasNonOpaqueNonTransparentColor(currentColor) &&
605       !aParams.context->GetTextDrawer();
606 
607   // If we need to double-buffer, we'll need to measure the text first to
608   // get the bounds of the area of interest. Ideally we'd do that just for
609   // the specific glyph run(s) that need buffering, but because of bug
610   // 1612610 we currently use the extent of the entire range even when
611   // just buffering a subrange. So we'll measure the full range once and
612   // keep the metrics on hand for any subsequent subranges.
613   gfxTextRun::Metrics metrics;
614   bool gotMetrics = false;
615 
616   // Set up parameters that will be constant across all glyph runs we need
617   // to draw, regardless of the font used.
618   TextRunDrawParams params;
619   params.context = aParams.context;
620   params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
621   params.isVerticalRun = IsVertical();
622   params.isRTL = IsRightToLeft();
623   params.direction = direction;
624   params.strokeOpts = aParams.strokeOpts;
625   params.textStrokeColor = aParams.textStrokeColor;
626   params.textStrokePattern = aParams.textStrokePattern;
627   params.drawOpts = aParams.drawOpts;
628   params.drawMode = aParams.drawMode;
629   params.callbacks = aParams.callbacks;
630   params.runContextPaint = aParams.contextPaint;
631   params.paintSVGGlyphs =
632       !aParams.callbacks || aParams.callbacks->mShouldPaintSVGGlyphs;
633   params.dt = aParams.context->GetDrawTarget();
634   params.allowGDI = aParams.allowGDI;
635 
636   GlyphRunIterator iter(this, aRange);
637   gfxFloat advance = 0.0;
638   gfx::Point pt = aPt;
639 
640   while (iter.NextRun()) {
641     gfxFont* font = iter.GetGlyphRun()->mFont;
642     Range runRange(iter.GetStringStart(), iter.GetStringEnd());
643 
644     bool needToRestore = false;
645     if (mayNeedBuffering && HasSyntheticBoldOrColor(font)) {
646       needToRestore = true;
647       if (!gotMetrics) {
648         // Measure text; use the bounding box to determine the area we need
649         // to buffer. We measure the entire range, rather than just the glyph
650         // run that we're actually handling, because of bug 1612610: if the
651         // bounding box passed to PushSolidColor does not intersect the
652         // drawTarget's current clip, the skia backend fails to clip properly.
653         // This means we may use a larger buffer than actually needed, but is
654         // otherwise harmless.
655         metrics =
656             MeasureText(aRange, gfxFont::LOOSE_INK_EXTENTS,
657                         aParams.context->GetDrawTarget(), aParams.provider);
658         if (IsRightToLeft()) {
659           metrics.mBoundingBox.MoveBy(
660               gfxPoint(aPt.x - metrics.mAdvanceWidth, aPt.y));
661         } else {
662           metrics.mBoundingBox.MoveBy(gfxPoint(aPt.x, aPt.y));
663         }
664         gotMetrics = true;
665       }
666       syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
667                                          GetAppUnitsPerDevUnit());
668     }
669 
670     Range ligatureRange(runRange);
671     ShrinkToLigatureBoundaries(&ligatureRange);
672 
673     bool drawPartial =
674         (aParams.drawMode & (DrawMode::GLYPH_FILL | DrawMode::GLYPH_STROKE)) ||
675         (aParams.drawMode == DrawMode::GLYPH_PATH && aParams.callbacks);
676     gfx::Point origPt = pt;
677 
678     if (drawPartial) {
679       DrawPartialLigature(font, Range(runRange.start, ligatureRange.start), &pt,
680                           aParams.provider, params,
681                           iter.GetGlyphRun()->mOrientation);
682     }
683 
684     DrawGlyphs(font, ligatureRange, &pt, aParams.provider, ligatureRange,
685                params, iter.GetGlyphRun()->mOrientation);
686 
687     if (drawPartial) {
688       DrawPartialLigature(font, Range(ligatureRange.end, runRange.end), &pt,
689                           aParams.provider, params,
690                           iter.GetGlyphRun()->mOrientation);
691     }
692 
693     if (params.isVerticalRun) {
694       advance += (pt.y - origPt.y) * params.direction;
695     } else {
696       advance += (pt.x - origPt.x) * params.direction;
697     }
698 
699     // composite result when synthetic bolding used
700     if (needToRestore) {
701       syntheticBoldBuffer.PopAlpha();
702     }
703   }
704 
705   if (aParams.advanceWidth) {
706     *aParams.advanceWidth = advance;
707   }
708 }
709 
710 // This method is mostly parallel to Draw().
DrawEmphasisMarks(gfxContext * aContext,gfxTextRun * aMark,gfxFloat aMarkAdvance,gfx::Point aPt,Range aRange,PropertyProvider * aProvider) const711 void gfxTextRun::DrawEmphasisMarks(gfxContext* aContext, gfxTextRun* aMark,
712                                    gfxFloat aMarkAdvance, gfx::Point aPt,
713                                    Range aRange,
714                                    PropertyProvider* aProvider) const {
715   MOZ_ASSERT(aRange.end <= GetLength());
716 
717   EmphasisMarkDrawParams params;
718   params.context = aContext;
719   params.mark = aMark;
720   params.advance = aMarkAdvance;
721   params.direction = GetDirection();
722   params.isVertical = IsVertical();
723 
724   float& inlineCoord = params.isVertical ? aPt.y : aPt.x;
725   float direction = params.direction;
726 
727   GlyphRunIterator iter(this, aRange);
728   while (iter.NextRun()) {
729     gfxFont* font = iter.GetGlyphRun()->mFont;
730     uint32_t start = iter.GetStringStart();
731     uint32_t end = iter.GetStringEnd();
732     Range ligatureRange(start, end);
733     ShrinkToLigatureBoundaries(&ligatureRange);
734 
735     inlineCoord +=
736         direction * ComputePartialLigatureWidth(
737                         Range(start, ligatureRange.start), aProvider);
738 
739     AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
740     bool haveSpacing = GetAdjustedSpacingArray(ligatureRange, aProvider,
741                                                ligatureRange, &spacingBuffer);
742     params.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
743     font->DrawEmphasisMarks(this, &aPt, ligatureRange.start,
744                             ligatureRange.Length(), params);
745 
746     inlineCoord += direction * ComputePartialLigatureWidth(
747                                    Range(ligatureRange.end, end), aProvider);
748   }
749 }
750 
AccumulateMetricsForRun(gfxFont * aFont,Range aRange,gfxFont::BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,PropertyProvider * aProvider,Range aSpacingRange,gfx::ShapedTextFlags aOrientation,Metrics * aMetrics) const751 void gfxTextRun::AccumulateMetricsForRun(
752     gfxFont* aFont, Range aRange, gfxFont::BoundingBoxType aBoundingBoxType,
753     DrawTarget* aRefDrawTarget, PropertyProvider* aProvider,
754     Range aSpacingRange, gfx::ShapedTextFlags aOrientation,
755     Metrics* aMetrics) const {
756   AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
757   bool haveSpacing =
758       GetAdjustedSpacingArray(aRange, aProvider, aSpacingRange, &spacingBuffer);
759   Metrics metrics = aFont->Measure(
760       this, aRange.start, aRange.end, aBoundingBoxType, aRefDrawTarget,
761       haveSpacing ? spacingBuffer.Elements() : nullptr, aOrientation);
762   aMetrics->CombineWith(metrics, IsRightToLeft());
763 }
764 
AccumulatePartialLigatureMetrics(gfxFont * aFont,Range aRange,gfxFont::BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,PropertyProvider * aProvider,gfx::ShapedTextFlags aOrientation,Metrics * aMetrics) const765 void gfxTextRun::AccumulatePartialLigatureMetrics(
766     gfxFont* aFont, Range aRange, gfxFont::BoundingBoxType aBoundingBoxType,
767     DrawTarget* aRefDrawTarget, PropertyProvider* aProvider,
768     gfx::ShapedTextFlags aOrientation, Metrics* aMetrics) const {
769   if (aRange.start >= aRange.end) return;
770 
771   // Measure partial ligature. We hack this by clipping the metrics in the
772   // same way we clip the drawing.
773   LigatureData data = ComputeLigatureData(aRange, aProvider);
774 
775   // First measure the complete ligature
776   Metrics metrics;
777   AccumulateMetricsForRun(aFont, data.mRange, aBoundingBoxType, aRefDrawTarget,
778                           aProvider, aRange, aOrientation, &metrics);
779 
780   // Clip the bounding box to the ligature part
781   gfxFloat bboxLeft = metrics.mBoundingBox.X();
782   gfxFloat bboxRight = metrics.mBoundingBox.XMost();
783   // Where we are going to start "drawing" relative to our left baseline origin
784   gfxFloat origin =
785       IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
786   ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
787   metrics.mBoundingBox.SetBoxX(bboxLeft, bboxRight);
788 
789   // mBoundingBox is now relative to the left baseline origin for the entire
790   // ligature. Shift it left.
791   metrics.mBoundingBox.MoveByX(
792       -(IsRightToLeft()
793             ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
794             : data.mPartAdvance));
795   metrics.mAdvanceWidth = data.mPartWidth;
796 
797   aMetrics->CombineWith(metrics, IsRightToLeft());
798 }
799 
MeasureText(Range aRange,gfxFont::BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,PropertyProvider * aProvider) const800 gfxTextRun::Metrics gfxTextRun::MeasureText(
801     Range aRange, gfxFont::BoundingBoxType aBoundingBoxType,
802     DrawTarget* aRefDrawTarget, PropertyProvider* aProvider) const {
803   NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
804 
805   Metrics accumulatedMetrics;
806   GlyphRunIterator iter(this, aRange);
807   while (iter.NextRun()) {
808     gfxFont* font = iter.GetGlyphRun()->mFont;
809     uint32_t start = iter.GetStringStart();
810     uint32_t end = iter.GetStringEnd();
811     Range ligatureRange(start, end);
812     ShrinkToLigatureBoundaries(&ligatureRange);
813 
814     AccumulatePartialLigatureMetrics(
815         font, Range(start, ligatureRange.start), aBoundingBoxType,
816         aRefDrawTarget, aProvider, iter.GetGlyphRun()->mOrientation,
817         &accumulatedMetrics);
818 
819     // XXX This sucks. We have to get glyph extents just so we can detect
820     // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
821     // even though in almost all cases we could get correct results just
822     // by getting some ascent/descent from the font and using our stored
823     // advance widths.
824     AccumulateMetricsForRun(
825         font, ligatureRange, aBoundingBoxType, aRefDrawTarget, aProvider,
826         ligatureRange, iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
827 
828     AccumulatePartialLigatureMetrics(
829         font, Range(ligatureRange.end, end), aBoundingBoxType, aRefDrawTarget,
830         aProvider, iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
831   }
832 
833   return accumulatedMetrics;
834 }
835 
836 #define MEASUREMENT_BUFFER_SIZE 100
837 
ClassifyAutoHyphenations(uint32_t aStart,Range aRange,nsTArray<HyphenType> & aHyphenBuffer,HyphenationState * aWordState)838 void gfxTextRun::ClassifyAutoHyphenations(uint32_t aStart, Range aRange,
839                                           nsTArray<HyphenType>& aHyphenBuffer,
840                                           HyphenationState* aWordState) {
841   MOZ_ASSERT(
842       aRange.end - aStart <= aHyphenBuffer.Length() && aRange.start >= aStart,
843       "Range out of bounds");
844   MOZ_ASSERT(aWordState->mostRecentBoundary >= aStart,
845              "Unexpected aMostRecentWordBoundary!!");
846 
847   uint32_t start =
848       std::min<uint32_t>(aRange.start, aWordState->mostRecentBoundary);
849 
850   for (uint32_t i = start; i < aRange.end; ++i) {
851     if (aHyphenBuffer[i - aStart] == HyphenType::Explicit &&
852         !aWordState->hasExplicitHyphen) {
853       aWordState->hasExplicitHyphen = true;
854     }
855     if (!aWordState->hasManualHyphen &&
856         (aHyphenBuffer[i - aStart] == HyphenType::Soft ||
857          aHyphenBuffer[i - aStart] == HyphenType::Explicit)) {
858       aWordState->hasManualHyphen = true;
859       // This is the first manual hyphen in the current word. We can only
860       // know if the current word has a manual hyphen until now. So, we need
861       // to run a sub loop to update the auto hyphens between the start of
862       // the current word and this manual hyphen.
863       if (aWordState->hasAutoHyphen) {
864         for (uint32_t j = aWordState->mostRecentBoundary; j < i; j++) {
865           if (aHyphenBuffer[j - aStart] ==
866               HyphenType::AutoWithoutManualInSameWord) {
867             aHyphenBuffer[j - aStart] = HyphenType::AutoWithManualInSameWord;
868           }
869         }
870       }
871     }
872     if (aHyphenBuffer[i - aStart] == HyphenType::AutoWithoutManualInSameWord) {
873       if (!aWordState->hasAutoHyphen) {
874         aWordState->hasAutoHyphen = true;
875       }
876       if (aWordState->hasManualHyphen) {
877         aHyphenBuffer[i - aStart] = HyphenType::AutoWithManualInSameWord;
878       }
879     }
880 
881     // If we're at the word boundary, clear/reset couple states.
882     if (mCharacterGlyphs[i].CharIsSpace() || mCharacterGlyphs[i].CharIsTab() ||
883         mCharacterGlyphs[i].CharIsNewline() ||
884         // Since we will not have a boundary in the end of the string, let's
885         // call the end of the string a special case for word boundary.
886         i == GetLength() - 1) {
887       // We can only get to know whether we should raise/clear an explicit
888       // manual hyphen until we get to the end of a word, because this depends
889       // on whether there exists at least one auto hyphen in the same word.
890       if (!aWordState->hasAutoHyphen && aWordState->hasExplicitHyphen) {
891         for (uint32_t j = aWordState->mostRecentBoundary; j <= i; j++) {
892           if (aHyphenBuffer[j - aStart] == HyphenType::Explicit) {
893             aHyphenBuffer[j - aStart] = HyphenType::None;
894           }
895         }
896       }
897       aWordState->mostRecentBoundary = i;
898       aWordState->hasManualHyphen = false;
899       aWordState->hasAutoHyphen = false;
900       aWordState->hasExplicitHyphen = false;
901     }
902   }
903 }
904 
BreakAndMeasureText(uint32_t aStart,uint32_t aMaxLength,bool aLineBreakBefore,gfxFloat aWidth,PropertyProvider * aProvider,SuppressBreak aSuppressBreak,gfxFloat * aTrimWhitespace,bool aWhitespaceCanHang,Metrics * aMetrics,gfxFont::BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,bool * aUsedHyphenation,uint32_t * aLastBreak,bool aCanWordWrap,bool aCanWhitespaceWrap,gfxBreakPriority * aBreakPriority)905 uint32_t gfxTextRun::BreakAndMeasureText(
906     uint32_t aStart, uint32_t aMaxLength, bool aLineBreakBefore,
907     gfxFloat aWidth, PropertyProvider* aProvider, SuppressBreak aSuppressBreak,
908     gfxFloat* aTrimWhitespace, bool aWhitespaceCanHang, Metrics* aMetrics,
909     gfxFont::BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget,
910     bool* aUsedHyphenation, uint32_t* aLastBreak, bool aCanWordWrap,
911     bool aCanWhitespaceWrap, gfxBreakPriority* aBreakPriority) {
912   aMaxLength = std::min(aMaxLength, GetLength() - aStart);
913 
914   NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
915 
916   Range bufferRange(
917       aStart, aStart + std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE));
918   PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
919   bool haveSpacing =
920       aProvider && !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING);
921   if (haveSpacing) {
922     GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
923   }
924   AutoTArray<HyphenType, 4096> hyphenBuffer;
925   HyphenationState wordState;
926   wordState.mostRecentBoundary = aStart;
927   bool haveHyphenation =
928       aProvider &&
929       (aProvider->GetHyphensOption() == StyleHyphens::Auto ||
930        (aProvider->GetHyphensOption() == StyleHyphens::Manual &&
931         !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS)));
932   if (haveHyphenation) {
933     if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
934       aProvider->GetHyphenationBreaks(bufferRange, hyphenBuffer.Elements());
935       if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
936         ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer, &wordState);
937       }
938     } else {
939       haveHyphenation = false;
940     }
941   }
942 
943   gfxFloat width = 0;
944   gfxFloat advance = 0;
945   // The number of space characters that can be trimmed or hang at a soft-wrap
946   uint32_t trimmableChars = 0;
947   // The amount of space removed by ignoring trimmableChars
948   gfxFloat trimmableAdvance = 0;
949   int32_t lastBreak = -1;
950   int32_t lastBreakTrimmableChars = -1;
951   gfxFloat lastBreakTrimmableAdvance = -1;
952   // Cache the last candidate break
953   int32_t lastCandidateBreak = -1;
954   int32_t lastCandidateBreakTrimmableChars = -1;
955   gfxFloat lastCandidateBreakTrimmableAdvance = -1;
956   bool lastCandidateBreakUsedHyphenation = false;
957   gfxBreakPriority lastCandidateBreakPriority = gfxBreakPriority::eNoBreak;
958   bool aborted = false;
959   uint32_t end = aStart + aMaxLength;
960   bool lastBreakUsedHyphenation = false;
961   Range ligatureRange(aStart, end);
962   ShrinkToLigatureBoundaries(&ligatureRange);
963 
964   // We may need to move `i` backwards in the following loop, and re-scan
965   // part of the textrun; we'll use `rescanLimit` so we can tell when that
966   // is happening: if `i < rescanLimit` then we're rescanning.
967   uint32_t rescanLimit = aStart;
968   for (uint32_t i = aStart; i < end; ++i) {
969     if (i >= bufferRange.end) {
970       // Fetch more spacing and hyphenation data
971       uint32_t oldHyphenBufferLength = hyphenBuffer.Length();
972       bufferRange.start = i;
973       bufferRange.end =
974           std::min(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE);
975       // For spacing, we always overwrite the old data with the newly
976       // fetched one. However, for hyphenation, hyphenation data sometimes
977       // depends on the context in every word (if "hyphens: auto" is set).
978       // To ensure we get enough information between neighboring buffers,
979       // we grow the hyphenBuffer instead of overwrite it.
980       // NOTE that this means bufferRange does not correspond to the
981       // entire hyphenBuffer, but only to the most recently added portion.
982       // Therefore, we need to add the old length to hyphenBuffer.Elements()
983       // when getting more data.
984       if (haveSpacing) {
985         GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
986       }
987       if (haveHyphenation) {
988         if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
989           aProvider->GetHyphenationBreaks(
990               bufferRange, hyphenBuffer.Elements() + oldHyphenBufferLength);
991           if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
992             uint32_t prevMostRecentWordBoundary = wordState.mostRecentBoundary;
993             ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer,
994                                      &wordState);
995             // If the buffer boundary is in the middle of a word,
996             // we need to go back to the start of the current word.
997             // So, we can correct the wrong candidates that we set
998             // in the previous runs of the loop.
999             if (prevMostRecentWordBoundary < oldHyphenBufferLength) {
1000               rescanLimit = i;
1001               i = prevMostRecentWordBoundary - 1;
1002               continue;
1003             }
1004           }
1005         } else {
1006           haveHyphenation = false;
1007         }
1008       }
1009     }
1010 
1011     // There can't be a word-wrap break opportunity at the beginning of the
1012     // line: if the width is too small for even one character to fit, it
1013     // could be the first and last break opportunity on the line, and that
1014     // would trigger an infinite loop.
1015     if (aSuppressBreak != eSuppressAllBreaks &&
1016         (aSuppressBreak != eSuppressInitialBreak || i > aStart)) {
1017       bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
1018       // atHyphenationBreak indicates we're at a "soft" hyphen, where an extra
1019       // hyphen glyph will need to be painted. It is NOT set for breaks at an
1020       // explicit hyphen present in the text.
1021       //
1022       // NOTE(emilio): If you change this condition you also need to change
1023       // nsTextFrame::AddInlineMinISizeForFlow to match.
1024       bool atHyphenationBreak = !atNaturalBreak && haveHyphenation &&
1025                                 IsOptionalHyphenBreak(hyphenBuffer[i - aStart]);
1026       bool atAutoHyphenWithManualHyphenInSameWord =
1027           atHyphenationBreak &&
1028           hyphenBuffer[i - aStart] == HyphenType::AutoWithManualInSameWord;
1029       bool atBreak = atNaturalBreak || atHyphenationBreak;
1030       bool wordWrapping = aCanWordWrap &&
1031                           mCharacterGlyphs[i].IsClusterStart() &&
1032                           *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
1033 
1034       bool whitespaceWrapping = false;
1035       if (i > aStart) {
1036         // The spec says the breaking opportunity is *after* whitespace.
1037         auto const& g = mCharacterGlyphs[i - 1];
1038         whitespaceWrapping =
1039             aCanWhitespaceWrap &&
1040             (g.CharIsSpace() || g.CharIsTab() || g.CharIsNewline());
1041       }
1042 
1043       if (atBreak || wordWrapping || whitespaceWrapping) {
1044         gfxFloat hyphenatedAdvance = advance;
1045         if (atHyphenationBreak) {
1046           hyphenatedAdvance += aProvider->GetHyphenWidth();
1047         }
1048 
1049         if (lastBreak < 0 ||
1050             width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
1051           // We can break here.
1052           lastBreak = i;
1053           lastBreakTrimmableChars = trimmableChars;
1054           lastBreakTrimmableAdvance = trimmableAdvance;
1055           lastBreakUsedHyphenation = atHyphenationBreak;
1056           *aBreakPriority = (atBreak || whitespaceWrapping)
1057                                 ? gfxBreakPriority::eNormalBreak
1058                                 : gfxBreakPriority::eWordWrapBreak;
1059         }
1060 
1061         width += advance;
1062         advance = 0;
1063         if (width - trimmableAdvance > aWidth) {
1064           // No more text fits. Abort
1065           aborted = true;
1066           break;
1067         }
1068         // There are various kinds of break opportunities:
1069         // 1. word wrap break,
1070         // 2. natural break,
1071         // 3. manual hyphenation break,
1072         // 4. auto hyphenation break without any manual hyphenation
1073         //    in the same word,
1074         // 5. auto hyphenation break with another manual hyphenation
1075         //    in the same word.
1076         // Allow all of them except the last one to be a candidate.
1077         // So, we can ensure that we don't use an automatic
1078         // hyphenation opportunity within a word that contains another
1079         // manual hyphenation, unless it is the only choice.
1080         if (wordWrapping || !atAutoHyphenWithManualHyphenInSameWord) {
1081           lastCandidateBreak = lastBreak;
1082           lastCandidateBreakTrimmableChars = lastBreakTrimmableChars;
1083           lastCandidateBreakTrimmableAdvance = lastBreakTrimmableAdvance;
1084           lastCandidateBreakUsedHyphenation = lastBreakUsedHyphenation;
1085           lastCandidateBreakPriority = *aBreakPriority;
1086         }
1087       }
1088     }
1089 
1090     // If we're re-scanning part of a word (to re-process potential
1091     // hyphenation types) then we don't want to accumulate widths again
1092     // for the characters that were already added to `advance`.
1093     if (i < rescanLimit) {
1094       continue;
1095     }
1096 
1097     gfxFloat charAdvance;
1098     if (i >= ligatureRange.start && i < ligatureRange.end) {
1099       charAdvance = GetAdvanceForGlyphs(Range(i, i + 1));
1100       if (haveSpacing) {
1101         PropertyProvider::Spacing* space =
1102             &spacingBuffer[i - bufferRange.start];
1103         charAdvance += space->mBefore + space->mAfter;
1104       }
1105     } else {
1106       charAdvance = ComputePartialLigatureWidth(Range(i, i + 1), aProvider);
1107     }
1108 
1109     advance += charAdvance;
1110     if (aTrimWhitespace || aWhitespaceCanHang) {
1111       if (mCharacterGlyphs[i].CharIsSpace()) {
1112         ++trimmableChars;
1113         trimmableAdvance += charAdvance;
1114       } else {
1115         trimmableAdvance = 0;
1116         trimmableChars = 0;
1117       }
1118     }
1119   }
1120 
1121   if (!aborted) {
1122     width += advance;
1123   }
1124 
1125   // There are three possibilities:
1126   // 1) all the text fit (width <= aWidth)
1127   // 2) some of the text fit up to a break opportunity (width > aWidth &&
1128   //    lastBreak >= 0)
1129   // 3) none of the text fits before a break opportunity (width > aWidth &&
1130   //    lastBreak < 0)
1131   uint32_t charsFit;
1132   bool usedHyphenation = false;
1133   if (width - trimmableAdvance <= aWidth) {
1134     charsFit = aMaxLength;
1135   } else if (lastBreak >= 0) {
1136     if (lastCandidateBreak >= 0 && lastCandidateBreak != lastBreak) {
1137       lastBreak = lastCandidateBreak;
1138       lastBreakTrimmableChars = lastCandidateBreakTrimmableChars;
1139       lastBreakTrimmableAdvance = lastCandidateBreakTrimmableAdvance;
1140       lastBreakUsedHyphenation = lastCandidateBreakUsedHyphenation;
1141       *aBreakPriority = lastCandidateBreakPriority;
1142     }
1143     charsFit = lastBreak - aStart;
1144     trimmableChars = lastBreakTrimmableChars;
1145     trimmableAdvance = lastBreakTrimmableAdvance;
1146     usedHyphenation = lastBreakUsedHyphenation;
1147   } else {
1148     charsFit = aMaxLength;
1149   }
1150 
1151   if (aMetrics) {
1152     auto fitEnd = aStart + charsFit;
1153     // Initially, measure everything, so that our bounding box includes
1154     // any trimmable or hanging whitespace.
1155     *aMetrics = MeasureText(Range(aStart, fitEnd), aBoundingBoxType,
1156                             aRefDrawTarget, aProvider);
1157     if (aTrimWhitespace || aWhitespaceCanHang) {
1158       // Measure trailing whitespace that is to be trimmed/hung.
1159       Metrics trimOrHangMetrics =
1160           MeasureText(Range(fitEnd - trimmableChars, fitEnd), aBoundingBoxType,
1161                       aRefDrawTarget, aProvider);
1162       if (aTrimWhitespace) {
1163         aMetrics->mAdvanceWidth -= trimOrHangMetrics.mAdvanceWidth;
1164       } else if (aMetrics->mAdvanceWidth > aWidth) {
1165         // Restrict width of hanging whitespace so it doesn't overflow.
1166         aMetrics->mAdvanceWidth = std::max(
1167             aWidth, aMetrics->mAdvanceWidth - trimOrHangMetrics.mAdvanceWidth);
1168       }
1169     }
1170   }
1171   if (aTrimWhitespace) {
1172     *aTrimWhitespace = trimmableAdvance;
1173   }
1174   if (aUsedHyphenation) {
1175     *aUsedHyphenation = usedHyphenation;
1176   }
1177   if (aLastBreak && charsFit == aMaxLength) {
1178     if (lastBreak < 0) {
1179       *aLastBreak = UINT32_MAX;
1180     } else {
1181       *aLastBreak = lastBreak - aStart;
1182     }
1183   }
1184 
1185   return charsFit;
1186 }
1187 
GetAdvanceWidth(Range aRange,PropertyProvider * aProvider,PropertyProvider::Spacing * aSpacing) const1188 gfxFloat gfxTextRun::GetAdvanceWidth(
1189     Range aRange, PropertyProvider* aProvider,
1190     PropertyProvider::Spacing* aSpacing) const {
1191   NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
1192 
1193   Range ligatureRange = aRange;
1194   ShrinkToLigatureBoundaries(&ligatureRange);
1195 
1196   gfxFloat result = ComputePartialLigatureWidth(
1197                         Range(aRange.start, ligatureRange.start), aProvider) +
1198                     ComputePartialLigatureWidth(
1199                         Range(ligatureRange.end, aRange.end), aProvider);
1200 
1201   if (aSpacing) {
1202     aSpacing->mBefore = aSpacing->mAfter = 0;
1203   }
1204 
1205   // Account for all remaining spacing here. This is more efficient than
1206   // processing it along with the glyphs.
1207   if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
1208     uint32_t i;
1209     AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
1210     if (spacingBuffer.AppendElements(aRange.Length(), fallible)) {
1211       GetAdjustedSpacing(this, ligatureRange, aProvider,
1212                          spacingBuffer.Elements());
1213       for (i = 0; i < ligatureRange.Length(); ++i) {
1214         PropertyProvider::Spacing* space = &spacingBuffer[i];
1215         result += space->mBefore + space->mAfter;
1216       }
1217       if (aSpacing) {
1218         aSpacing->mBefore = spacingBuffer[0].mBefore;
1219         aSpacing->mAfter = spacingBuffer.LastElement().mAfter;
1220       }
1221     }
1222   }
1223 
1224   return result + GetAdvanceForGlyphs(ligatureRange);
1225 }
1226 
GetMinAdvanceWidth(Range aRange)1227 gfxFloat gfxTextRun::GetMinAdvanceWidth(Range aRange) {
1228   MOZ_ASSERT(aRange.end <= GetLength(), "Substring out of range");
1229 
1230   Range ligatureRange = aRange;
1231   ShrinkToLigatureBoundaries(&ligatureRange);
1232 
1233   gfxFloat result =
1234       std::max(ComputePartialLigatureWidth(
1235                    Range(aRange.start, ligatureRange.start), nullptr),
1236                ComputePartialLigatureWidth(Range(ligatureRange.end, aRange.end),
1237                                            nullptr));
1238 
1239   // Compute min advance width by assuming each grapheme cluster takes its own
1240   // line.
1241   gfxFloat clusterAdvance = 0;
1242   for (uint32_t i = ligatureRange.start; i < ligatureRange.end; ++i) {
1243     if (mCharacterGlyphs[i].CharIsSpace()) {
1244       // Skip space char to prevent its advance width contributing to the
1245       // result. That is, don't consider a space can be in its own line.
1246       continue;
1247     }
1248     clusterAdvance += GetAdvanceForGlyph(i);
1249     if (i + 1 == ligatureRange.end || IsClusterStart(i + 1)) {
1250       result = std::max(result, clusterAdvance);
1251       clusterAdvance = 0;
1252     }
1253   }
1254 
1255   return result;
1256 }
1257 
SetLineBreaks(Range aRange,bool aLineBreakBefore,bool aLineBreakAfter,gfxFloat * aAdvanceWidthDelta)1258 bool gfxTextRun::SetLineBreaks(Range aRange, bool aLineBreakBefore,
1259                                bool aLineBreakAfter,
1260                                gfxFloat* aAdvanceWidthDelta) {
1261   // Do nothing because our shaping does not currently take linebreaks into
1262   // account. There is no change in advance width.
1263   if (aAdvanceWidthDelta) {
1264     *aAdvanceWidthDelta = 0;
1265   }
1266   return false;
1267 }
1268 
FindFirstGlyphRunContaining(uint32_t aOffset) const1269 uint32_t gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset) const {
1270   NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
1271   NS_ASSERTION(GetLength() == 0 ||
1272                    (!mHasGlyphRunArray && mSingleGlyphRun.mFont) ||
1273                    (mHasGlyphRunArray && mGlyphRunArray.Length() > 0),
1274                "non-empty text but no glyph runs present!");
1275   if (!mHasGlyphRunArray) {
1276     return 0;
1277   }
1278   if (aOffset == GetLength()) {
1279     return mGlyphRunArray.Length();
1280   }
1281   uint32_t start = 0;
1282   uint32_t end = mGlyphRunArray.Length();
1283   while (end - start > 1) {
1284     uint32_t mid = (start + end) / 2;
1285     if (mGlyphRunArray[mid].mCharacterOffset <= aOffset) {
1286       start = mid;
1287     } else {
1288       end = mid;
1289     }
1290   }
1291   NS_ASSERTION(mGlyphRunArray[start].mCharacterOffset <= aOffset,
1292                "Hmm, something went wrong, aOffset should have been found");
1293   return start;
1294 }
1295 
AddGlyphRun(gfxFont * aFont,FontMatchType aMatchType,uint32_t aUTF16Offset,bool aForceNewRun,gfx::ShapedTextFlags aOrientation,bool aIsCJK)1296 void gfxTextRun::AddGlyphRun(gfxFont* aFont, FontMatchType aMatchType,
1297                              uint32_t aUTF16Offset, bool aForceNewRun,
1298                              gfx::ShapedTextFlags aOrientation, bool aIsCJK) {
1299   NS_ASSERTION(aFont, "adding glyph run for null font!");
1300   NS_ASSERTION(aOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED,
1301                "mixed orientation should have been resolved");
1302   if (!aFont) {
1303     return;
1304   }
1305   if (!mHasGlyphRunArray) {
1306     // We don't currently have an array.
1307     if (!mSingleGlyphRun.mFont) {
1308       // This is the first glyph run: just store it directly.
1309       mSingleGlyphRun.SetProperties(aFont, aOrientation, aIsCJK, aMatchType);
1310       mSingleGlyphRun.mCharacterOffset = aUTF16Offset;
1311       return;
1312     }
1313   }
1314   uint32_t numGlyphRuns = mHasGlyphRunArray ? mGlyphRunArray.Length() : 1;
1315   if (!aForceNewRun && numGlyphRuns > 0) {
1316     GlyphRun* lastGlyphRun = mHasGlyphRunArray
1317                                  ? &mGlyphRunArray[numGlyphRuns - 1]
1318                                  : &mSingleGlyphRun;
1319 
1320     NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
1321                  "Glyph runs out of order (and run not forced)");
1322 
1323     // Don't append a run if the font is already the one we want
1324     if (lastGlyphRun->Matches(aFont, aOrientation, aIsCJK, aMatchType)) {
1325       return;
1326     }
1327 
1328     // If the offset has not changed, avoid leaving a zero-length run
1329     // by overwriting the last entry instead of appending...
1330     if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
1331       // ...except that if the run before the last entry had the same
1332       // font as the new one wants, merge with it instead of creating
1333       // adjacent runs with the same font
1334       if (numGlyphRuns > 1 && mGlyphRunArray[numGlyphRuns - 2].Matches(
1335                                   aFont, aOrientation, aIsCJK, aMatchType)) {
1336         mGlyphRunArray.TruncateLength(numGlyphRuns - 1);
1337         if (mGlyphRunArray.Length() == 1) {
1338           ConvertFromGlyphRunArray();
1339         }
1340         return;
1341       }
1342 
1343       lastGlyphRun->SetProperties(aFont, aOrientation, aIsCJK, aMatchType);
1344       return;
1345     }
1346   }
1347 
1348   NS_ASSERTION(
1349       aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
1350       "First run doesn't cover the first character (and run not forced)?");
1351 
1352   if (!mHasGlyphRunArray) {
1353     ConvertToGlyphRunArray();
1354   }
1355 
1356   GlyphRun* glyphRun = mGlyphRunArray.AppendElement();
1357   glyphRun->SetProperties(aFont, aOrientation, aIsCJK, aMatchType);
1358   glyphRun->mCharacterOffset = aUTF16Offset;
1359 }
1360 
SortGlyphRuns()1361 void gfxTextRun::SortGlyphRuns() {
1362   if (!mHasGlyphRunArray) {
1363     return;
1364   }
1365 
1366   // We should never have an empty or one-element array here; if there's only
1367   // one glyphrun, it should be stored directly in the textrun without using
1368   // an array at all.
1369   MOZ_ASSERT(mGlyphRunArray.Length() > 1);
1370 
1371   AutoTArray<GlyphRun, 16> runs(std::move(mGlyphRunArray));
1372   GlyphRunOffsetComparator comp;
1373   runs.Sort(comp);
1374 
1375   // Now copy back, coalescing adjacent glyph runs that have the same
1376   // properties.
1377   mGlyphRunArray.Clear();
1378   GlyphRun* prevRun = nullptr;
1379   for (auto& run : runs) {
1380     // A GlyphRun with the same font and orientation as the previous can
1381     // just be skipped; the last GlyphRun will cover its character range.
1382     MOZ_ASSERT(run.mFont != nullptr);
1383     if (!prevRun || !prevRun->Matches(run.mFont, run.mOrientation, run.mIsCJK,
1384                                       run.mMatchType)) {
1385       // If two font runs have the same character offset, Sort() will have
1386       // randomized their order!
1387       MOZ_ASSERT(prevRun == nullptr ||
1388                      prevRun->mCharacterOffset < run.mCharacterOffset,
1389                  "Two fonts for the same run, glyph indices unreliable");
1390       prevRun = mGlyphRunArray.AppendElement(std::move(run));
1391     }
1392   }
1393 
1394   MOZ_ASSERT(mGlyphRunArray.Length() > 0);
1395   if (mGlyphRunArray.Length() == 1) {
1396     ConvertFromGlyphRunArray();
1397   }
1398 }
1399 
1400 // Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
1401 // therefore we only call it once, at the end of textrun construction,
1402 // NOT incrementally as each glyph run is added (bug 680402).
SanitizeGlyphRuns()1403 void gfxTextRun::SanitizeGlyphRuns() {
1404   if (!mHasGlyphRunArray) {
1405     return;
1406   }
1407 
1408   MOZ_ASSERT(mGlyphRunArray.Length() > 1);
1409 
1410   // If any glyph run starts with ligature-continuation characters, we need to
1411   // advance it to the first "real" character to avoid drawing partial ligature
1412   // glyphs from wrong font (seen with U+FEFF in reftest 474417-1, as Core Text
1413   // eliminates the glyph, which makes it appear as if a ligature has been
1414   // formed)
1415   int32_t i, lastRunIndex = mGlyphRunArray.Length() - 1;
1416   const CompressedGlyph* charGlyphs = mCharacterGlyphs;
1417   for (i = lastRunIndex; i >= 0; --i) {
1418     GlyphRun& run = mGlyphRunArray[i];
1419     while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
1420            run.mCharacterOffset < GetLength()) {
1421       run.mCharacterOffset++;
1422     }
1423     // if the run has become empty, eliminate it
1424     if ((i < lastRunIndex &&
1425          run.mCharacterOffset >= mGlyphRunArray[i + 1].mCharacterOffset) ||
1426         (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
1427       mGlyphRunArray.RemoveElementAt(i);
1428       --lastRunIndex;
1429     }
1430   }
1431 
1432   MOZ_ASSERT(mGlyphRunArray.Length() > 0);
1433   if (mGlyphRunArray.Length() == 1) {
1434     ConvertFromGlyphRunArray();
1435   }
1436 }
1437 
CopyGlyphDataFrom(gfxShapedWord * aShapedWord,uint32_t aOffset)1438 void gfxTextRun::CopyGlyphDataFrom(gfxShapedWord* aShapedWord,
1439                                    uint32_t aOffset) {
1440   uint32_t wordLen = aShapedWord->GetLength();
1441   NS_ASSERTION(aOffset + wordLen <= GetLength(),
1442                "word overruns end of textrun!");
1443 
1444   CompressedGlyph* charGlyphs = GetCharacterGlyphs();
1445   const CompressedGlyph* wordGlyphs = aShapedWord->GetCharacterGlyphs();
1446   if (aShapedWord->HasDetailedGlyphs()) {
1447     for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
1448       const CompressedGlyph& g = wordGlyphs[i];
1449       if (!g.IsSimpleGlyph()) {
1450         const DetailedGlyph* details =
1451             g.GetGlyphCount() > 0 ? aShapedWord->GetDetailedGlyphs(i) : nullptr;
1452         SetDetailedGlyphs(aOffset, g.GetGlyphCount(), details);
1453       }
1454       charGlyphs[aOffset] = g;
1455     }
1456   } else {
1457     memcpy(charGlyphs + aOffset, wordGlyphs, wordLen * sizeof(CompressedGlyph));
1458   }
1459 }
1460 
CopyGlyphDataFrom(gfxTextRun * aSource,Range aRange,uint32_t aDest)1461 void gfxTextRun::CopyGlyphDataFrom(gfxTextRun* aSource, Range aRange,
1462                                    uint32_t aDest) {
1463   NS_ASSERTION(aRange.end <= aSource->GetLength(),
1464                "Source substring out of range");
1465   NS_ASSERTION(aDest + aRange.Length() <= GetLength(),
1466                "Destination substring out of range");
1467 
1468   if (aSource->mDontSkipDrawing) {
1469     mDontSkipDrawing = true;
1470   }
1471 
1472   // Copy base glyph data, and DetailedGlyph data where present
1473   const CompressedGlyph* srcGlyphs = aSource->mCharacterGlyphs + aRange.start;
1474   CompressedGlyph* dstGlyphs = mCharacterGlyphs + aDest;
1475   for (uint32_t i = 0; i < aRange.Length(); ++i) {
1476     CompressedGlyph g = srcGlyphs[i];
1477     g.SetCanBreakBefore(!g.IsClusterStart()
1478                             ? CompressedGlyph::FLAG_BREAK_TYPE_NONE
1479                             : dstGlyphs[i].CanBreakBefore());
1480     if (!g.IsSimpleGlyph()) {
1481       uint32_t count = g.GetGlyphCount();
1482       if (count > 0) {
1483         // DetailedGlyphs allocation is infallible, so this should never be
1484         // null unless the source textrun is somehow broken.
1485         DetailedGlyph* src = aSource->GetDetailedGlyphs(i + aRange.start);
1486         MOZ_ASSERT(src, "missing DetailedGlyphs?");
1487         if (src) {
1488           DetailedGlyph* dst = AllocateDetailedGlyphs(i + aDest, count);
1489           ::memcpy(dst, src, count * sizeof(DetailedGlyph));
1490         } else {
1491           g.SetMissing();
1492         }
1493       }
1494     }
1495     dstGlyphs[i] = g;
1496   }
1497 
1498   // Copy glyph runs
1499   GlyphRunIterator iter(aSource, aRange);
1500 #ifdef DEBUG
1501   GlyphRun* prevRun = nullptr;
1502 #endif
1503   while (iter.NextRun()) {
1504     gfxFont* font = iter.GetGlyphRun()->mFont;
1505     MOZ_ASSERT(!prevRun || !prevRun->Matches(iter.GetGlyphRun()->mFont,
1506                                              iter.GetGlyphRun()->mOrientation,
1507                                              iter.GetGlyphRun()->mIsCJK,
1508                                              FontMatchType::Kind::kUnspecified),
1509                "Glyphruns not coalesced?");
1510 #ifdef DEBUG
1511     prevRun = const_cast<GlyphRun*>(iter.GetGlyphRun());
1512     uint32_t end = iter.GetStringEnd();
1513 #endif
1514     uint32_t start = iter.GetStringStart();
1515 
1516     // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
1517     // Although it's unusual (and not desirable), it's possible for us to assign
1518     // different fonts to a base character and a following diacritic.
1519     // Example on OSX 10.5/10.6 with default fonts installed:
1520     //     data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
1521     //                    &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
1522     // This means the rendering of the cluster will probably not be very good,
1523     // but it's the best we can do for now if the specified font only covered
1524     // the initial base character and not its applied marks.
1525     NS_WARNING_ASSERTION(aSource->IsClusterStart(start),
1526                          "Started font run in the middle of a cluster");
1527     NS_WARNING_ASSERTION(
1528         end == aSource->GetLength() || aSource->IsClusterStart(end),
1529         "Ended font run in the middle of a cluster");
1530 
1531     AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
1532                 start - aRange.start + aDest, false,
1533                 iter.GetGlyphRun()->mOrientation, iter.GetGlyphRun()->mIsCJK);
1534   }
1535 }
1536 
ClearGlyphsAndCharacters()1537 void gfxTextRun::ClearGlyphsAndCharacters() {
1538   ResetGlyphRuns();
1539   memset(reinterpret_cast<char*>(mCharacterGlyphs), 0,
1540          mLength * sizeof(CompressedGlyph));
1541   mDetailedGlyphs = nullptr;
1542 }
1543 
SetSpaceGlyph(gfxFont * aFont,DrawTarget * aDrawTarget,uint32_t aCharIndex,gfx::ShapedTextFlags aOrientation)1544 void gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
1545                                uint32_t aCharIndex,
1546                                gfx::ShapedTextFlags aOrientation) {
1547   if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) {
1548     return;
1549   }
1550 
1551   aFont->InitWordCache();
1552   static const uint8_t space = ' ';
1553   gfx::ShapedTextFlags flags =
1554       gfx::ShapedTextFlags::TEXT_IS_8BIT | aOrientation;
1555   bool vertical =
1556       !!(GetFlags() & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT);
1557   gfxFontShaper::RoundingFlags roundingFlags =
1558       aFont->GetRoundOffsetsToPixels(aDrawTarget);
1559   gfxShapedWord* sw = aFont->GetShapedWord(
1560       aDrawTarget, &space, 1, gfxShapedWord::HashMix(0, ' '), Script::LATIN,
1561       /* aLanguage = */ nullptr, vertical, mAppUnitsPerDevUnit, flags,
1562       roundingFlags, nullptr);
1563   if (sw) {
1564     const GlyphRun* prevRun = TrailingGlyphRun();
1565     bool isCJK = prevRun && prevRun->mFont == aFont &&
1566                          prevRun->mOrientation == aOrientation
1567                      ? prevRun->mIsCJK
1568                      : false;
1569     AddGlyphRun(aFont, FontMatchType::Kind::kUnspecified, aCharIndex, false,
1570                 aOrientation, isCJK);
1571     CopyGlyphDataFrom(sw, aCharIndex);
1572     GetCharacterGlyphs()[aCharIndex].SetIsSpace();
1573   }
1574 }
1575 
SetSpaceGlyphIfSimple(gfxFont * aFont,uint32_t aCharIndex,char16_t aSpaceChar,gfx::ShapedTextFlags aOrientation)1576 bool gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
1577                                        char16_t aSpaceChar,
1578                                        gfx::ShapedTextFlags aOrientation) {
1579   uint32_t spaceGlyph = aFont->GetSpaceGlyph();
1580   if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
1581     return false;
1582   }
1583 
1584   gfxFont::Orientation fontOrientation =
1585       (aOrientation & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT)
1586           ? nsFontMetrics::eVertical
1587           : nsFontMetrics::eHorizontal;
1588   uint32_t spaceWidthAppUnits = NS_lroundf(
1589       aFont->GetMetrics(fontOrientation).spaceWidth * mAppUnitsPerDevUnit);
1590   if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
1591     return false;
1592   }
1593 
1594   const GlyphRun* prevRun = TrailingGlyphRun();
1595   bool isCJK = prevRun && prevRun->mFont == aFont &&
1596                        prevRun->mOrientation == aOrientation
1597                    ? prevRun->mIsCJK
1598                    : false;
1599   AddGlyphRun(aFont, FontMatchType::Kind::kUnspecified, aCharIndex, false,
1600               aOrientation, isCJK);
1601   CompressedGlyph g =
1602       CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
1603   if (aSpaceChar == ' ') {
1604     g.SetIsSpace();
1605   }
1606   GetCharacterGlyphs()[aCharIndex] = g;
1607   return true;
1608 }
1609 
FetchGlyphExtents(DrawTarget * aRefDrawTarget)1610 void gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget) {
1611   bool needsGlyphExtents = NeedsGlyphExtents(this);
1612   if (!needsGlyphExtents && !mDetailedGlyphs) return;
1613 
1614   uint32_t runCount;
1615   const GlyphRun* glyphRuns = GetGlyphRuns(&runCount);
1616   CompressedGlyph* charGlyphs = mCharacterGlyphs;
1617   for (uint32_t i = 0; i < runCount; ++i) {
1618     const GlyphRun& run = glyphRuns[i];
1619     gfxFont* font = run.mFont;
1620     if (MOZ_UNLIKELY(font->GetStyle()->AdjustedSizeMustBeZero())) {
1621       continue;
1622     }
1623 
1624     uint32_t start = run.mCharacterOffset;
1625     uint32_t end =
1626         i + 1 < runCount ? glyphRuns[i + 1].mCharacterOffset : GetLength();
1627     uint32_t j;
1628     gfxGlyphExtents* extents =
1629         font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
1630 
1631     for (j = start; j < end; ++j) {
1632       const gfxTextRun::CompressedGlyph* glyphData = &charGlyphs[j];
1633       if (glyphData->IsSimpleGlyph()) {
1634         // If we're in speed mode, don't set up glyph extents here; we'll
1635         // just return "optimistic" glyph bounds later
1636         if (needsGlyphExtents) {
1637           uint32_t glyphIndex = glyphData->GetSimpleGlyph();
1638           if (!extents->IsGlyphKnown(glyphIndex)) {
1639 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1640             ++gGlyphExtentsSetupEagerSimple;
1641 #endif
1642             font->SetupGlyphExtents(aRefDrawTarget, glyphIndex, false, extents);
1643           }
1644         }
1645       } else if (!glyphData->IsMissing()) {
1646         uint32_t glyphCount = glyphData->GetGlyphCount();
1647         if (glyphCount == 0) {
1648           continue;
1649         }
1650         const gfxTextRun::DetailedGlyph* details = GetDetailedGlyphs(j);
1651         if (!details) {
1652           continue;
1653         }
1654         for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
1655           uint32_t glyphIndex = details->mGlyphID;
1656           if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
1657 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1658             ++gGlyphExtentsSetupEagerTight;
1659 #endif
1660             font->SetupGlyphExtents(aRefDrawTarget, glyphIndex, true, extents);
1661           }
1662         }
1663       }
1664     }
1665   }
1666 }
1667 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)1668 size_t gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
1669   // The second arg is how much gfxTextRun::AllocateStorage would have
1670   // allocated.
1671   size_t total = mHasGlyphRunArray
1672                      ? mGlyphRunArray.ShallowSizeOfExcludingThis(aMallocSizeOf)
1673                      : 0;
1674 
1675   if (mDetailedGlyphs) {
1676     total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
1677   }
1678 
1679   return total;
1680 }
1681 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)1682 size_t gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
1683   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
1684 }
1685 
1686 #ifdef DEBUG_FRAME_DUMP
Dump(FILE * out)1687 void gfxTextRun::Dump(FILE* out) {
1688 #  define APPEND_FLAG(string_, enum_, field_, flag_)                    \
1689     if (field_ & enum_::flag_) {                                        \
1690       string_.AppendPrintf(remaining != field_ ? " %s" : "%s", #flag_); \
1691       remaining &= ~enum_::flag_;                                       \
1692     }
1693 #  define APPEND_FLAGS(string_, enum_, field_, flags_)              \
1694     {                                                               \
1695       auto remaining = field_;                                      \
1696       MOZ_FOR_EACH(APPEND_FLAG, (string_, enum_, field_, ), flags_) \
1697       if (int(remaining)) {                                         \
1698         string_.AppendPrintf(" %s(0x%0x)", #enum_, int(remaining)); \
1699       }                                                             \
1700     }
1701 
1702   nsCString flagsString;
1703   ShapedTextFlags orient = mFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
1704   ShapedTextFlags otherFlags = mFlags & ~ShapedTextFlags::TEXT_ORIENT_MASK;
1705   APPEND_FLAGS(flagsString, ShapedTextFlags, otherFlags,
1706                (TEXT_IS_RTL, TEXT_ENABLE_SPACING, TEXT_IS_8BIT,
1707                 TEXT_ENABLE_HYPHEN_BREAKS, TEXT_NEED_BOUNDING_BOX,
1708                 TEXT_DISABLE_OPTIONAL_LIGATURES, TEXT_OPTIMIZE_SPEED,
1709                 TEXT_HIDE_CONTROL_CHARACTERS, TEXT_TRAILING_ARABICCHAR,
1710                 TEXT_INCOMING_ARABICCHAR, TEXT_USE_MATH_SCRIPT))
1711 
1712   if (orient != ShapedTextFlags::TEXT_ORIENT_HORIZONTAL &&
1713       !flagsString.IsEmpty()) {
1714     flagsString += ' ';
1715   }
1716 
1717   switch (orient) {
1718     case ShapedTextFlags::TEXT_ORIENT_HORIZONTAL:
1719       break;
1720     case ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT:
1721       flagsString += "TEXT_ORIENT_VERTICAL_UPRIGHT";
1722       break;
1723     case ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT:
1724       flagsString += "TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT";
1725       break;
1726     case ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED:
1727       flagsString += "TEXT_ORIENT_VERTICAL_MIXED";
1728       break;
1729     case ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT:
1730       flagsString += "TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT";
1731       break;
1732     default:
1733       flagsString.AppendPrintf("UNKNOWN_TEXT_ORIENT_MASK(0x%0x)", int(orient));
1734       break;
1735   }
1736 
1737   nsCString flags2String;
1738   APPEND_FLAGS(
1739       flags2String, nsTextFrameUtils::Flags, mFlags2,
1740       (HasTab, HasShy, DontSkipDrawingForPendingUserFonts, IsSimpleFlow,
1741        IncomingWhitespace, TrailingWhitespace, CompressedLeadingWhitespace,
1742        NoBreaks, IsTransformed, HasTrailingBreak, IsSingleCharMi,
1743        MightHaveGlyphChanges, RunSizeAccounted))
1744 
1745 #  undef APPEND_FLAGS
1746 #  undef APPEND_FLAG
1747 
1748   nsAutoCString lang;
1749   mFontGroup->Language()->ToUTF8String(lang);
1750   fprintf(out, "gfxTextRun@%p (length %u) [%s] [%s] [%s]\n", this, mLength,
1751           flagsString.get(), flags2String.get(), lang.get());
1752 
1753   uint32_t numGlyphRuns;
1754   const GlyphRun* glyphRuns = GetGlyphRuns(&numGlyphRuns);
1755   fprintf(out, "  Glyph runs:\n");
1756   for (uint32_t i = 0; i < numGlyphRuns; ++i) {
1757     gfxFont* font = glyphRuns[i].mFont;
1758     const gfxFontStyle* style = font->GetStyle();
1759     nsAutoString styleString;
1760     nsStyleUtil::AppendFontSlantStyle(style->style, styleString);
1761     fprintf(out, "    [%d] offset=%d %s %f/%g/%s\n", i,
1762             glyphRuns[i].mCharacterOffset, font->GetName().get(), style->size,
1763             style->weight.ToFloat(), NS_ConvertUTF16toUTF8(styleString).get());
1764   }
1765 
1766   fprintf(out, "  Glyphs:\n");
1767   for (uint32_t i = 0; i < mLength; ++i) {
1768     auto glyphData = GetCharacterGlyphs()[i];
1769 
1770     nsCString line;
1771     line.AppendPrintf("    [%d] 0x%p %s", i, GetCharacterGlyphs() + i,
1772                       glyphData.IsSimpleGlyph() ? "simple" : "detailed");
1773 
1774     if (glyphData.IsSimpleGlyph()) {
1775       line.AppendPrintf(" id=%d adv=%d", glyphData.GetSimpleGlyph(),
1776                         glyphData.GetSimpleAdvance());
1777     } else {
1778       uint32_t count = glyphData.GetGlyphCount();
1779       if (count) {
1780         line += " ids=";
1781         for (uint32_t j = 0; j < count; j++) {
1782           line.AppendPrintf(j ? ",%d" : "%d", GetDetailedGlyphs(i)[j].mGlyphID);
1783         }
1784         line += " advs=";
1785         for (uint32_t j = 0; j < count; j++) {
1786           line.AppendPrintf(j ? ",%d" : "%d", GetDetailedGlyphs(i)[j].mAdvance);
1787         }
1788         line += " offsets=";
1789         for (uint32_t j = 0; j < count; j++) {
1790           auto offset = GetDetailedGlyphs(i)[j].mOffset;
1791           line.AppendPrintf(j ? ",(%g,%g)" : "(%g,%g)", offset.x, offset.y);
1792         }
1793       } else {
1794         line += " (no glyphs)";
1795       }
1796     }
1797 
1798     if (glyphData.CharIsSpace()) {
1799       line += " CHAR_IS_SPACE";
1800     }
1801     if (glyphData.CharIsTab()) {
1802       line += " CHAR_IS_TAB";
1803     }
1804     if (glyphData.CharIsNewline()) {
1805       line += " CHAR_IS_NEWLINE";
1806     }
1807     if (glyphData.CharIsFormattingControl()) {
1808       line += " CHAR_IS_FORMATTING_CONTROL";
1809     }
1810     if (glyphData.CharTypeFlags() &
1811         CompressedGlyph::FLAG_CHAR_NO_EMPHASIS_MARK) {
1812       line += " CHAR_NO_EMPHASIS_MARK";
1813     }
1814 
1815     if (!glyphData.IsSimpleGlyph()) {
1816       if (!glyphData.IsMissing()) {
1817         line += " NOT_MISSING";
1818       }
1819       if (!glyphData.IsClusterStart()) {
1820         line += " NOT_IS_CLUSTER_START";
1821       }
1822       if (!glyphData.IsLigatureGroupStart()) {
1823         line += " NOT_LIGATURE_GROUP_START";
1824       }
1825     }
1826 
1827     switch (glyphData.CanBreakBefore()) {
1828       case CompressedGlyph::FLAG_BREAK_TYPE_NORMAL:
1829         line += " BREAK_TYPE_NORMAL";
1830         break;
1831       case CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN:
1832         line += " BREAK_TYPE_HYPHEN";
1833         break;
1834     }
1835 
1836     fprintf(out, "%s\n", line.get());
1837   }
1838 }
1839 #endif
1840 
gfxFontGroup(nsPresContext * aPresContext,const StyleFontFamilyList & aFontFamilyList,const gfxFontStyle * aStyle,nsAtom * aLanguage,bool aExplicitLanguage,gfxTextPerfMetrics * aTextPerf,gfxUserFontSet * aUserFontSet,gfxFloat aDevToCssSize)1841 gfxFontGroup::gfxFontGroup(nsPresContext* aPresContext,
1842                            const StyleFontFamilyList& aFontFamilyList,
1843                            const gfxFontStyle* aStyle, nsAtom* aLanguage,
1844                            bool aExplicitLanguage,
1845                            gfxTextPerfMetrics* aTextPerf,
1846                            gfxUserFontSet* aUserFontSet, gfxFloat aDevToCssSize)
1847     : mPresContext(aPresContext),  // Note that aPresContext may be null!
1848       mFamilyList(aFontFamilyList),
1849       mStyle(*aStyle),
1850       mLanguage(aLanguage),
1851       mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET),
1852       mHyphenWidth(-1),
1853       mDevToCssSize(aDevToCssSize),
1854       mUserFontSet(aUserFontSet),
1855       mTextPerf(aTextPerf),
1856       mLastPrefLang(eFontPrefLang_Western),
1857       mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aLanguage)),
1858       mLastPrefFirstFont(false),
1859       mSkipDrawing(false),
1860       mExplicitLanguage(aExplicitLanguage) {
1861   // We don't use SetUserFontSet() here, as we want to unconditionally call
1862   // BuildFontList() rather than only do UpdateUserFonts() if it changed.
1863   mCurrGeneration = GetGeneration();
1864   BuildFontList();
1865 }
1866 
~gfxFontGroup()1867 gfxFontGroup::~gfxFontGroup() {
1868   // Should not be dropped by stylo
1869   MOZ_ASSERT(NS_IsMainThread());
1870 }
1871 
GetDefaultGeneric(nsAtom * aLanguage)1872 static StyleGenericFontFamily GetDefaultGeneric(nsAtom* aLanguage) {
1873   return StaticPresData::Get()
1874       ->GetFontPrefsForLang(aLanguage)
1875       ->GetDefaultGeneric();
1876 }
1877 
BuildFontList()1878 void gfxFontGroup::BuildFontList() {
1879   // initialize fonts in the font family list
1880   AutoTArray<FamilyAndGeneric, 10> fonts;
1881   gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
1882 
1883   // lookup fonts in the fontlist
1884   for (const StyleSingleFontFamily& name : mFamilyList.list.AsSpan()) {
1885     if (name.IsFamilyName()) {
1886       const auto& familyName = name.AsFamilyName();
1887       AddPlatformFont(nsAtomCString(familyName.name.AsAtom()),
1888                       familyName.syntax == StyleFontFamilyNameSyntax::Quoted,
1889                       fonts);
1890     } else {
1891       MOZ_ASSERT(name.IsGeneric());
1892       const StyleGenericFontFamily generic = name.AsGeneric();
1893       // system-ui is usually a single family, so it doesn't work great as
1894       // fallback. Prefer the following generic or the language default instead.
1895       if (mFallbackGeneric == StyleGenericFontFamily::None &&
1896           generic != StyleGenericFontFamily::SystemUi) {
1897         mFallbackGeneric = generic;
1898       }
1899       pfl->AddGenericFonts(mPresContext, generic, mLanguage, fonts);
1900       if (mTextPerf) {
1901         mTextPerf->current.genericLookups++;
1902       }
1903     }
1904   }
1905 
1906   // If necessary, append default language generic onto the end.
1907   if (mFallbackGeneric == StyleGenericFontFamily::None && !mStyle.systemFont) {
1908     auto defaultLanguageGeneric = GetDefaultGeneric(mLanguage);
1909 
1910     pfl->AddGenericFonts(mPresContext, defaultLanguageGeneric, mLanguage,
1911                          fonts);
1912     if (mTextPerf) {
1913       mTextPerf->current.genericLookups++;
1914     }
1915   }
1916 
1917   // build the fontlist from the specified families
1918   for (const auto& f : fonts) {
1919     if (f.mFamily.mIsShared) {
1920       AddFamilyToFontList(f.mFamily.mShared, f.mGeneric);
1921     } else {
1922       AddFamilyToFontList(f.mFamily.mUnshared, f.mGeneric);
1923     }
1924   }
1925 
1926   mFontListGeneration = pfl->GetGeneration();
1927 }
1928 
AddPlatformFont(const nsACString & aName,bool aQuotedName,nsTArray<FamilyAndGeneric> & aFamilyList)1929 void gfxFontGroup::AddPlatformFont(const nsACString& aName, bool aQuotedName,
1930                                    nsTArray<FamilyAndGeneric>& aFamilyList) {
1931   // First, look up in the user font set...
1932   // If the fontSet matches the family, we must not look for a platform
1933   // font of the same name, even if we fail to actually get a fontEntry
1934   // here; we'll fall back to the next name in the CSS font-family list.
1935   if (mUserFontSet) {
1936     // Add userfonts to the fontlist whether already loaded
1937     // or not. Loading is initiated during font matching.
1938     gfxFontFamily* family = mUserFontSet->LookupFamily(aName);
1939     if (family) {
1940       aFamilyList.AppendElement(family);
1941       return;
1942     }
1943   }
1944 
1945   // Not known in the user font set ==> check system fonts
1946   gfxPlatformFontList::PlatformFontList()->FindAndAddFamilies(
1947       mPresContext, StyleGenericFontFamily::None, aName, &aFamilyList,
1948       aQuotedName ? gfxPlatformFontList::FindFamiliesFlags::eQuotedFamilyName
1949                   : gfxPlatformFontList::FindFamiliesFlags(0),
1950       &mStyle, mLanguage.get(), mDevToCssSize);
1951 }
1952 
AddFamilyToFontList(gfxFontFamily * aFamily,StyleGenericFontFamily aGeneric)1953 void gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily,
1954                                        StyleGenericFontFamily aGeneric) {
1955   if (!aFamily) {
1956     MOZ_ASSERT_UNREACHABLE("don't try to add a null font family!");
1957     return;
1958   }
1959   AutoTArray<gfxFontEntry*, 4> fontEntryList;
1960   aFamily->FindAllFontsForStyle(mStyle, fontEntryList);
1961   // add these to the fontlist
1962   for (gfxFontEntry* fe : fontEntryList) {
1963     if (!HasFont(fe)) {
1964       FamilyFace ff(aFamily, fe, aGeneric);
1965       if (fe->mIsUserFontContainer) {
1966         ff.CheckState(mSkipDrawing);
1967       }
1968       mFonts.AppendElement(ff);
1969     }
1970   }
1971   // for a family marked as "check fallback faces", only mark the last
1972   // entry so that fallbacks for a family are only checked once
1973   if (aFamily->CheckForFallbackFaces() && !fontEntryList.IsEmpty() &&
1974       !mFonts.IsEmpty()) {
1975     mFonts.LastElement().SetCheckForFallbackFaces();
1976   }
1977 }
1978 
AddFamilyToFontList(fontlist::Family * aFamily,StyleGenericFontFamily aGeneric)1979 void gfxFontGroup::AddFamilyToFontList(fontlist::Family* aFamily,
1980                                        StyleGenericFontFamily aGeneric) {
1981   gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
1982   if (!aFamily->IsInitialized()) {
1983     if (!NS_IsMainThread()) {
1984       // If we need to initialize a Family record, but we're on a style
1985       // worker thread, we have to defer it.
1986       ServoStyleSet* set = ServoStyleSet::Current();
1987       MOZ_ASSERT(set);
1988       set->AppendTask(PostTraversalTask::InitializeFamily(aFamily));
1989       set->AppendTask(PostTraversalTask::FontInfoUpdate(set));
1990       return;
1991     }
1992     if (!pfl->InitializeFamily(aFamily)) {
1993       return;
1994     }
1995   }
1996   AutoTArray<fontlist::Face*, 4> faceList;
1997   aFamily->FindAllFacesForStyle(pfl->SharedFontList(), mStyle, faceList);
1998   for (auto face : faceList) {
1999     gfxFontEntry* fe = pfl->GetOrCreateFontEntry(face, aFamily);
2000     if (fe && !HasFont(fe)) {
2001       FamilyFace ff(aFamily, fe, aGeneric);
2002       mFonts.AppendElement(ff);
2003     }
2004   }
2005 }
2006 
HasFont(const gfxFontEntry * aFontEntry)2007 bool gfxFontGroup::HasFont(const gfxFontEntry* aFontEntry) {
2008   for (auto& f : mFonts) {
2009     if (f.FontEntry() == aFontEntry) {
2010       return true;
2011     }
2012   }
2013   return false;
2014 }
2015 
GetFontAt(int32_t i,uint32_t aCh,bool * aLoading)2016 gfxFont* gfxFontGroup::GetFontAt(int32_t i, uint32_t aCh, bool* aLoading) {
2017   if (uint32_t(i) >= mFonts.Length()) {
2018     return nullptr;
2019   }
2020 
2021   FamilyFace& ff = mFonts[i];
2022   if (ff.IsInvalid() || ff.IsLoading()) {
2023     return nullptr;
2024   }
2025 
2026   gfxFont* font = ff.Font();
2027   if (!font) {
2028     gfxFontEntry* fe = ff.FontEntry();
2029     if (!fe) {
2030       return nullptr;
2031     }
2032     gfxCharacterMap* unicodeRangeMap = nullptr;
2033     if (fe->mIsUserFontContainer) {
2034       gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
2035       if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
2036           ufe->CharacterInUnicodeRange(aCh) && !*aLoading) {
2037         ufe->Load();
2038         ff.CheckState(mSkipDrawing);
2039         *aLoading = ff.IsLoading();
2040       }
2041       fe = ufe->GetPlatformFontEntry();
2042       if (!fe) {
2043         return nullptr;
2044       }
2045       unicodeRangeMap = ufe->GetUnicodeRangeMap();
2046     }
2047     font = fe->FindOrMakeFont(&mStyle, unicodeRangeMap);
2048     if (!font || !font->Valid()) {
2049       ff.SetInvalid();
2050       // We can't just |delete font| here, in case there are other
2051       // references to the object FindOrMakeFont returned.
2052       RefPtr<gfxFont> ref(font);
2053       return nullptr;
2054     }
2055     ff.SetFont(font);
2056   }
2057   return font;
2058 }
2059 
CheckState(bool & aSkipDrawing)2060 void gfxFontGroup::FamilyFace::CheckState(bool& aSkipDrawing) {
2061   gfxFontEntry* fe = FontEntry();
2062   if (!fe) {
2063     return;
2064   }
2065   if (fe->mIsUserFontContainer) {
2066     gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
2067     gfxUserFontEntry::UserFontLoadState state = ufe->LoadState();
2068     switch (state) {
2069       case gfxUserFontEntry::STATUS_LOAD_PENDING:
2070       case gfxUserFontEntry::STATUS_LOADING:
2071         SetLoading(true);
2072         break;
2073       case gfxUserFontEntry::STATUS_FAILED:
2074         SetInvalid();
2075         // fall-thru to the default case
2076         [[fallthrough]];
2077       default:
2078         SetLoading(false);
2079     }
2080     if (ufe->WaitForUserFont()) {
2081       aSkipDrawing = true;
2082     }
2083   }
2084 }
2085 
EqualsUserFont(const gfxUserFontEntry * aUserFont) const2086 bool gfxFontGroup::FamilyFace::EqualsUserFont(
2087     const gfxUserFontEntry* aUserFont) const {
2088   gfxFontEntry* fe = FontEntry();
2089   // if there's a font, the entry is the underlying platform font
2090   if (mFontCreated) {
2091     gfxFontEntry* pfe = aUserFont->GetPlatformFontEntry();
2092     if (pfe == fe) {
2093       return true;
2094     }
2095   } else if (fe == aUserFont) {
2096     return true;
2097   }
2098   return false;
2099 }
2100 
FamilyListToString(const StyleFontFamilyList & aFamilyList)2101 static nsAutoCString FamilyListToString(
2102     const StyleFontFamilyList& aFamilyList) {
2103   return StringJoin(","_ns, aFamilyList.list.AsSpan(),
2104                     [](nsACString& dst, const StyleSingleFontFamily& name) {
2105                       name.AppendToString(dst);
2106                     });
2107 }
2108 
GetDefaultFont()2109 gfxFont* gfxFontGroup::GetDefaultFont() {
2110   if (mDefaultFont) {
2111     return mDefaultFont.get();
2112   }
2113 
2114   gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
2115   FontFamily family = pfl->GetDefaultFont(mPresContext, &mStyle);
2116   MOZ_ASSERT(!family.IsNull(),
2117              "invalid default font returned by GetDefaultFont");
2118 
2119   gfxFontEntry* fe = nullptr;
2120   if (family.mIsShared) {
2121     fontlist::Family* fam = family.mShared;
2122     if (!fam->IsInitialized()) {
2123       // If this fails, FindFaceForStyle will just safely return nullptr
2124       Unused << pfl->InitializeFamily(fam);
2125     }
2126     fontlist::Face* face = fam->FindFaceForStyle(pfl->SharedFontList(), mStyle);
2127     if (face) {
2128       fe = pfl->GetOrCreateFontEntry(face, fam);
2129     }
2130   } else {
2131     fe = family.mUnshared->FindFontForStyle(mStyle);
2132   }
2133   if (fe) {
2134     mDefaultFont = fe->FindOrMakeFont(&mStyle);
2135   }
2136 
2137   uint32_t numInits, loaderState;
2138   pfl->GetFontlistInitInfo(numInits, loaderState);
2139 
2140   MOZ_ASSERT(numInits != 0,
2141              "must initialize system fontlist before getting default font!");
2142 
2143   uint32_t numFonts = 0;
2144   if (!mDefaultFont) {
2145     // Try for a "font of last resort...."
2146     // Because an empty font list would be Really Bad for later code
2147     // that assumes it will be able to get valid metrics for layout,
2148     // just look for the first usable font and put in the list.
2149     // (see bug 554544)
2150     if (pfl->SharedFontList()) {
2151       fontlist::FontList* list = pfl->SharedFontList();
2152       numFonts = list->NumFamilies();
2153       fontlist::Family* families = list->Families();
2154       for (uint32_t i = 0; i < numFonts; ++i) {
2155         fontlist::Family* fam = &families[i];
2156         if (!fam->IsInitialized()) {
2157           Unused << pfl->InitializeFamily(fam);
2158         }
2159         fontlist::Face* face =
2160             fam->FindFaceForStyle(pfl->SharedFontList(), mStyle);
2161         if (face) {
2162           fe = pfl->GetOrCreateFontEntry(face, fam);
2163           if (fe) {
2164             mDefaultFont = fe->FindOrMakeFont(&mStyle);
2165             if (mDefaultFont) {
2166               break;
2167             }
2168             NS_WARNING("FindOrMakeFont failed");
2169           }
2170         }
2171       }
2172     } else {
2173       AutoTArray<RefPtr<gfxFontFamily>, 200> familyList;
2174       pfl->GetFontFamilyList(familyList);
2175       numFonts = familyList.Length();
2176       for (uint32_t i = 0; i < numFonts; ++i) {
2177         gfxFontEntry* fe = familyList[i]->FindFontForStyle(mStyle, true);
2178         if (fe) {
2179           mDefaultFont = fe->FindOrMakeFont(&mStyle);
2180           if (mDefaultFont) {
2181             break;
2182           }
2183         }
2184       }
2185     }
2186   }
2187 
2188   if (!mDefaultFont && pfl->SharedFontList() && !XRE_IsParentProcess()) {
2189     // If we're a content process, it's possible this is failing because the
2190     // chrome process has just updated the shared font list and we haven't yet
2191     // refreshed our reference to it. If that's the case, update and retry.
2192     // But if we're not on the main thread, we can't do this, so just use
2193     // the platform default font directly.
2194     if (NS_IsMainThread()) {
2195       uint32_t oldGeneration = pfl->SharedFontList()->GetGeneration();
2196       pfl->UpdateFontList();
2197       if (pfl->SharedFontList()->GetGeneration() != oldGeneration) {
2198         return GetDefaultFont();
2199       }
2200     } else {
2201       gfxFontEntry* fe = pfl->GetDefaultFontEntry();
2202       if (fe) {
2203         gfxFont* f = fe->FindOrMakeFont(&mStyle);
2204         if (f) {
2205           return f;
2206         }
2207       }
2208     }
2209   }
2210 
2211   if (!mDefaultFont) {
2212     // an empty font list at this point is fatal; we're not going to
2213     // be able to do even the most basic layout operations
2214 
2215     // annotate crash report with fontlist info
2216     nsAutoCString fontInitInfo;
2217     fontInitInfo.AppendPrintf("no fonts - init: %d fonts: %d loader: %d",
2218                               numInits, numFonts, loaderState);
2219 #ifdef XP_WIN
2220     bool dwriteEnabled = gfxWindowsPlatform::GetPlatform()->DWriteEnabled();
2221     double upTime = (double)GetTickCount();
2222     fontInitInfo.AppendPrintf(" backend: %s system-uptime: %9.3f sec",
2223                               dwriteEnabled ? "directwrite" : "gdi",
2224                               upTime / 1000);
2225 #endif
2226     gfxCriticalError() << fontInitInfo.get();
2227 
2228     char msg[256];  // CHECK buffer length if revising message below
2229     SprintfLiteral(msg, "unable to find a usable font (%.220s)",
2230                    FamilyListToString(mFamilyList).get());
2231     MOZ_CRASH_UNSAFE(msg);
2232   }
2233 
2234   return mDefaultFont.get();
2235 }
2236 
GetFirstValidFont(uint32_t aCh,StyleGenericFontFamily * aGeneric)2237 gfxFont* gfxFontGroup::GetFirstValidFont(uint32_t aCh,
2238                                          StyleGenericFontFamily* aGeneric) {
2239   // Ensure cached font instances are valid.
2240   CheckForUpdatedPlatformList();
2241 
2242   uint32_t count = mFonts.Length();
2243   bool loading = false;
2244   for (uint32_t i = 0; i < count; ++i) {
2245     FamilyFace& ff = mFonts[i];
2246     if (ff.IsInvalid()) {
2247       continue;
2248     }
2249 
2250     // already have a font?
2251     gfxFont* font = ff.Font();
2252     if (font) {
2253       if (aGeneric) {
2254         *aGeneric = ff.Generic();
2255       }
2256       return font;
2257     }
2258 
2259     // Need to build a font, loading userfont if not loaded. In
2260     // cases where unicode range might apply, use the character
2261     // provided.
2262     gfxFontEntry* fe = ff.FontEntry();
2263     if (fe && fe->mIsUserFontContainer) {
2264       gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
2265       bool inRange = ufe->CharacterInUnicodeRange(aCh);
2266       if (inRange) {
2267         if (!loading &&
2268             ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED) {
2269           ufe->Load();
2270           ff.CheckState(mSkipDrawing);
2271         }
2272         if (ff.IsLoading()) {
2273           loading = true;
2274         }
2275       }
2276       if (ufe->LoadState() != gfxUserFontEntry::STATUS_LOADED || !inRange) {
2277         continue;
2278       }
2279     }
2280 
2281     font = GetFontAt(i, aCh, &loading);
2282     if (font) {
2283       if (aGeneric) {
2284         *aGeneric = ff.Generic();
2285       }
2286       return font;
2287     }
2288   }
2289   if (aGeneric) {
2290     *aGeneric = StyleGenericFontFamily::None;
2291   }
2292   return GetDefaultFont();
2293 }
2294 
GetFirstMathFont()2295 gfxFont* gfxFontGroup::GetFirstMathFont() {
2296   uint32_t count = mFonts.Length();
2297   for (uint32_t i = 0; i < count; ++i) {
2298     gfxFont* font = GetFontAt(i);
2299     if (font && font->TryGetMathTable()) {
2300       return font;
2301     }
2302   }
2303   return nullptr;
2304 }
2305 
IsInvalidChar(uint8_t ch)2306 bool gfxFontGroup::IsInvalidChar(uint8_t ch) {
2307   return ((ch & 0x7f) < 0x20 || ch == 0x7f);
2308 }
2309 
IsInvalidChar(char16_t ch)2310 bool gfxFontGroup::IsInvalidChar(char16_t ch) {
2311   // All printable 7-bit ASCII values are OK
2312   if (ch >= ' ' && ch < 0x7f) {
2313     return false;
2314   }
2315   // No point in sending non-printing control chars through font shaping
2316   if (ch <= 0x9f) {
2317     return true;
2318   }
2319   // Word-separating format/bidi control characters are not shaped as part
2320   // of words.
2321   return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
2322            (ch == 0x200B /*ZWSP*/ || ch == 0x2028 /*LSEP*/ ||
2323             ch == 0x2029 /*PSEP*/ || ch == 0x2060 /*WJ*/)) ||
2324           ch == 0xfeff /*ZWNBSP*/ || IsBidiControl(ch));
2325 }
2326 
MakeEmptyTextRun(const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)2327 already_AddRefed<gfxTextRun> gfxFontGroup::MakeEmptyTextRun(
2328     const Parameters* aParams, gfx::ShapedTextFlags aFlags,
2329     nsTextFrameUtils::Flags aFlags2) {
2330   aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2331   return gfxTextRun::Create(aParams, 0, this, aFlags, aFlags2);
2332 }
2333 
MakeSpaceTextRun(const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)2334 already_AddRefed<gfxTextRun> gfxFontGroup::MakeSpaceTextRun(
2335     const Parameters* aParams, gfx::ShapedTextFlags aFlags,
2336     nsTextFrameUtils::Flags aFlags2) {
2337   aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2338 
2339   RefPtr<gfxTextRun> textRun =
2340       gfxTextRun::Create(aParams, 1, this, aFlags, aFlags2);
2341   if (!textRun) {
2342     return nullptr;
2343   }
2344 
2345   gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
2346   if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
2347     orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
2348   }
2349 
2350   gfxFont* font = GetFirstValidFont();
2351   if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
2352     // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
2353     // them, and always create at least size 1 fonts, i.e. they still
2354     // render something for size 0 fonts.
2355     textRun->AddGlyphRun(font, FontMatchType::Kind::kUnspecified, 0, false,
2356                          orientation, false);
2357   } else {
2358     if (font->GetSpaceGlyph()) {
2359       // Normally, the font has a cached space glyph, so we can avoid
2360       // the cost of calling FindFontForChar.
2361       textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation);
2362     } else {
2363       // In case the primary font doesn't have <space> (bug 970891),
2364       // find one that does.
2365       FontMatchType matchType;
2366       gfxFont* spaceFont =
2367           FindFontForChar(' ', 0, 0, Script::LATIN, nullptr, &matchType);
2368       if (spaceFont) {
2369         textRun->SetSpaceGlyph(spaceFont, aParams->mDrawTarget, 0, orientation);
2370       }
2371     }
2372   }
2373 
2374   // Note that the gfxGlyphExtents glyph bounds storage for the font will
2375   // always contain an entry for the font's space glyph, so we don't have
2376   // to call FetchGlyphExtents here.
2377   return textRun.forget();
2378 }
2379 
2380 template <typename T>
MakeBlankTextRun(const T * aString,uint32_t aLength,const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)2381 already_AddRefed<gfxTextRun> gfxFontGroup::MakeBlankTextRun(
2382     const T* aString, uint32_t aLength, const Parameters* aParams,
2383     gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2) {
2384   RefPtr<gfxTextRun> textRun =
2385       gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
2386   if (!textRun) {
2387     return nullptr;
2388   }
2389 
2390   gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
2391   if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
2392     orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
2393   }
2394   textRun->AddGlyphRun(GetFirstValidFont(), FontMatchType::Kind::kUnspecified,
2395                        0, false, orientation, false);
2396 
2397   for (uint32_t i = 0; i < aLength; i++) {
2398     if (aString[i] == '\n') {
2399       textRun->SetIsNewline(i);
2400     } else if (aString[i] == '\t') {
2401       textRun->SetIsTab(i);
2402     }
2403   }
2404 
2405   return textRun.forget();
2406 }
2407 
MakeHyphenTextRun(DrawTarget * aDrawTarget,gfx::ShapedTextFlags aFlags,uint32_t aAppUnitsPerDevUnit)2408 already_AddRefed<gfxTextRun> gfxFontGroup::MakeHyphenTextRun(
2409     DrawTarget* aDrawTarget, gfx::ShapedTextFlags aFlags,
2410     uint32_t aAppUnitsPerDevUnit) {
2411   // only use U+2010 if it is supported by the first font in the group;
2412   // it's better to use ASCII '-' from the primary font than to fall back to
2413   // U+2010 from some other, possibly poorly-matching face
2414   static const char16_t hyphen = 0x2010;
2415   gfxFont* font = GetFirstValidFont(uint32_t(hyphen));
2416   if (font->HasCharacter(hyphen)) {
2417     return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit, aFlags,
2418                        nsTextFrameUtils::Flags(), nullptr);
2419   }
2420 
2421   static const uint8_t dash = '-';
2422   return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit, aFlags,
2423                      nsTextFrameUtils::Flags(), nullptr);
2424 }
2425 
GetHyphenWidth(const gfxTextRun::PropertyProvider * aProvider)2426 gfxFloat gfxFontGroup::GetHyphenWidth(
2427     const gfxTextRun::PropertyProvider* aProvider) {
2428   if (mHyphenWidth < 0) {
2429     RefPtr<DrawTarget> dt(aProvider->GetDrawTarget());
2430     if (dt) {
2431       RefPtr<gfxTextRun> hyphRun(
2432           MakeHyphenTextRun(dt, aProvider->GetShapedTextFlags(),
2433                             aProvider->GetAppUnitsPerDevUnit()));
2434       mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0;
2435     }
2436   }
2437   return mHyphenWidth;
2438 }
2439 
MakeTextRun(const uint8_t * aString,uint32_t aLength,const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2,gfxMissingFontRecorder * aMFR)2440 already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun(
2441     const uint8_t* aString, uint32_t aLength, const Parameters* aParams,
2442     gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2,
2443     gfxMissingFontRecorder* aMFR) {
2444   if (aLength == 0) {
2445     return MakeEmptyTextRun(aParams, aFlags, aFlags2);
2446   }
2447   if (aLength == 1 && aString[0] == ' ') {
2448     return MakeSpaceTextRun(aParams, aFlags, aFlags2);
2449   }
2450 
2451   aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2452 
2453   if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
2454     // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
2455     // them, and always create at least size 1 fonts, i.e. they still
2456     // render something for size 0 fonts.
2457     return MakeBlankTextRun(aString, aLength, aParams, aFlags, aFlags2);
2458   }
2459 
2460   RefPtr<gfxTextRun> textRun =
2461       gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
2462   if (!textRun) {
2463     return nullptr;
2464   }
2465 
2466   InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
2467 
2468   textRun->FetchGlyphExtents(aParams->mDrawTarget);
2469 
2470   return textRun.forget();
2471 }
2472 
MakeTextRun(const char16_t * aString,uint32_t aLength,const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2,gfxMissingFontRecorder * aMFR)2473 already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun(
2474     const char16_t* aString, uint32_t aLength, const Parameters* aParams,
2475     gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2,
2476     gfxMissingFontRecorder* aMFR) {
2477   if (aLength == 0) {
2478     return MakeEmptyTextRun(aParams, aFlags, aFlags2);
2479   }
2480   if (aLength == 1 && aString[0] == ' ') {
2481     return MakeSpaceTextRun(aParams, aFlags, aFlags2);
2482   }
2483   if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
2484     return MakeBlankTextRun(aString, aLength, aParams, aFlags, aFlags2);
2485   }
2486 
2487   RefPtr<gfxTextRun> textRun =
2488       gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
2489   if (!textRun) {
2490     return nullptr;
2491   }
2492 
2493   InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
2494 
2495   textRun->FetchGlyphExtents(aParams->mDrawTarget);
2496 
2497   return textRun.forget();
2498 }
2499 
2500 template <typename T>
InitTextRun(DrawTarget * aDrawTarget,gfxTextRun * aTextRun,const T * aString,uint32_t aLength,gfxMissingFontRecorder * aMFR)2501 void gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
2502                                const T* aString, uint32_t aLength,
2503                                gfxMissingFontRecorder* aMFR) {
2504   NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
2505 
2506   // we need to do numeral processing even on 8-bit text,
2507   // in case we're converting Western to Hindi/Arabic digits
2508   int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
2509   UniquePtr<char16_t[]> transformedString;
2510   if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
2511     // scan the string for numerals that may need to be transformed;
2512     // if we find any, we'll make a local copy here and use that for
2513     // font matching and glyph generation/shaping
2514     bool prevIsArabic =
2515         !!(aTextRun->GetFlags() & ShapedTextFlags::TEXT_INCOMING_ARABICCHAR);
2516     for (uint32_t i = 0; i < aLength; ++i) {
2517       char16_t origCh = aString[i];
2518       char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
2519       if (newCh != origCh) {
2520         if (!transformedString) {
2521           transformedString = MakeUnique<char16_t[]>(aLength);
2522           if constexpr (sizeof(T) == sizeof(char16_t)) {
2523             memcpy(transformedString.get(), aString, i * sizeof(char16_t));
2524           } else {
2525             for (uint32_t j = 0; j < i; ++j) {
2526               transformedString[j] = aString[j];
2527             }
2528           }
2529         }
2530       }
2531       if (transformedString) {
2532         transformedString[i] = newCh;
2533       }
2534       prevIsArabic = IS_ARABIC_CHAR(newCh);
2535     }
2536   }
2537 
2538   LogModule* log = mStyle.systemFont ? gfxPlatform::GetLog(eGfxLog_textrunui)
2539                                      : gfxPlatform::GetLog(eGfxLog_textrun);
2540 
2541   // variant fallback handling may end up passing through this twice
2542   bool redo;
2543   do {
2544     redo = false;
2545 
2546     if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
2547       if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
2548         nsAutoCString lang;
2549         mLanguage->ToUTF8String(lang);
2550         nsAutoCString str((const char*)aString, aLength);
2551         nsAutoString styleString;
2552         nsStyleUtil::AppendFontSlantStyle(mStyle.style, styleString);
2553         auto defaultLanguageGeneric = GetDefaultGeneric(mLanguage);
2554         MOZ_LOG(
2555             log, LogLevel::Warning,
2556             ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
2557              "len %d weight: %g stretch: %g%% style: %s size: %6.2f %zu-byte "
2558              "TEXTRUN [%s] ENDTEXTRUN\n",
2559              (mStyle.systemFont ? "textrunui" : "textrun"),
2560              FamilyListToString(mFamilyList).get(),
2561              (defaultLanguageGeneric == StyleGenericFontFamily::Serif
2562                   ? "serif"
2563                   : (defaultLanguageGeneric == StyleGenericFontFamily::SansSerif
2564                          ? "sans-serif"
2565                          : "none")),
2566              lang.get(), static_cast<int>(Script::LATIN), aLength,
2567              mStyle.weight.ToFloat(), mStyle.stretch.Percentage(),
2568              NS_ConvertUTF16toUTF8(styleString).get(), mStyle.size, sizeof(T),
2569              str.get()));
2570       }
2571 
2572       // the text is still purely 8-bit; bypass the script-run itemizer
2573       // and treat it as a single Latin run
2574       InitScriptRun(aDrawTarget, aTextRun, aString, 0, aLength, Script::LATIN,
2575                     aMFR);
2576     } else {
2577       const char16_t* textPtr;
2578       if (transformedString) {
2579         textPtr = transformedString.get();
2580       } else {
2581         // typecast to avoid compilation error for the 8-bit version,
2582         // even though this is dead code in that case
2583         textPtr = reinterpret_cast<const char16_t*>(aString);
2584       }
2585 
2586       // split into script runs so that script can potentially influence
2587       // the font matching process below
2588       gfxScriptItemizer scriptRuns(textPtr, aLength);
2589 
2590       uint32_t runStart = 0, runLimit = aLength;
2591       Script runScript = Script::LATIN;
2592       while (scriptRuns.Next(runStart, runLimit, runScript)) {
2593         if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
2594           nsAutoCString lang;
2595           mLanguage->ToUTF8String(lang);
2596           nsAutoString styleString;
2597           nsStyleUtil::AppendFontSlantStyle(mStyle.style, styleString);
2598           auto defaultLanguageGeneric = GetDefaultGeneric(mLanguage);
2599           uint32_t runLen = runLimit - runStart;
2600           MOZ_LOG(
2601               log, LogLevel::Warning,
2602               ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
2603                "len %d weight: %g stretch: %g%% style: %s size: %6.2f "
2604                "%zu-byte TEXTRUN [%s] ENDTEXTRUN\n",
2605                (mStyle.systemFont ? "textrunui" : "textrun"),
2606                FamilyListToString(mFamilyList).get(),
2607                (defaultLanguageGeneric == StyleGenericFontFamily::Serif
2608                     ? "serif"
2609                     : (defaultLanguageGeneric ==
2610                                StyleGenericFontFamily::SansSerif
2611                            ? "sans-serif"
2612                            : "none")),
2613                lang.get(), static_cast<int>(runScript), runLen,
2614                mStyle.weight.ToFloat(), mStyle.stretch.Percentage(),
2615                NS_ConvertUTF16toUTF8(styleString).get(), mStyle.size, sizeof(T),
2616                NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
2617         }
2618 
2619         InitScriptRun(aDrawTarget, aTextRun, textPtr + runStart, runStart,
2620                       runLimit - runStart, runScript, aMFR);
2621       }
2622     }
2623 
2624     // if shaping was aborted due to lack of feature support, clear out
2625     // glyph runs and redo shaping with fallback forced on
2626     if (aTextRun->GetShapingState() == gfxTextRun::eShapingState_Aborted) {
2627       redo = true;
2628       aTextRun->SetShapingState(gfxTextRun::eShapingState_ForceFallbackFeature);
2629       aTextRun->ClearGlyphsAndCharacters();
2630     }
2631 
2632   } while (redo);
2633 
2634   if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
2635     gfxTextRun::CompressedGlyph* glyph = aTextRun->GetCharacterGlyphs();
2636     if (!glyph->IsSimpleGlyph()) {
2637       glyph->SetClusterStart(true);
2638     }
2639   }
2640 
2641   // It's possible for CoreText to omit glyph runs if it decides they contain
2642   // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
2643   // need to eliminate them from the glyph run array to avoid drawing "partial
2644   // ligatures" with the wrong font.
2645   // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
2646   // it will iterate back over all glyphruns in the textrun, which leads to
2647   // pathologically-bad perf in the case where a textrun contains many script
2648   // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
2649   // every time a new script subrun is processed.
2650   aTextRun->SanitizeGlyphRuns();
2651 
2652   aTextRun->SortGlyphRuns();
2653 }
2654 
IsPUA(uint32_t aUSV)2655 static inline bool IsPUA(uint32_t aUSV) {
2656   // We could look up the General Category of the codepoint here,
2657   // but it's simpler to check PUA codepoint ranges.
2658   return (aUSV >= 0xE000 && aUSV <= 0xF8FF) || (aUSV >= 0xF0000);
2659 }
2660 
2661 template <typename T>
InitScriptRun(DrawTarget * aDrawTarget,gfxTextRun * aTextRun,const T * aString,uint32_t aOffset,uint32_t aLength,Script aRunScript,gfxMissingFontRecorder * aMFR)2662 void gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
2663                                  const T* aString,  // text for this script run,
2664                                                     // not the entire textrun
2665                                  uint32_t aOffset,  // position of the script
2666                                                     // run within the textrun
2667                                  uint32_t aLength,  // length of the script run
2668                                  Script aRunScript,
2669                                  gfxMissingFontRecorder* aMFR) {
2670   NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
2671   NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
2672                "don't call InitScriptRun with aborted shaping state");
2673 
2674   // confirm the load state of userfonts in the list
2675   if (mUserFontSet && mCurrGeneration != mUserFontSet->GetGeneration()) {
2676     UpdateUserFonts();
2677   }
2678 
2679   gfxFont* mainFont = GetFirstValidFont();
2680 
2681   ShapedTextFlags orientation =
2682       aTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK;
2683 
2684   if (orientation != ShapedTextFlags::TEXT_ORIENT_HORIZONTAL &&
2685       (aRunScript == Script::MONGOLIAN || aRunScript == Script::PHAGS_PA)) {
2686     // Mongolian and Phags-pa text should ignore text-orientation and
2687     // always render in its "native" vertical mode, implemented by fonts
2688     // as sideways-right (i.e as if shaped horizontally, and then the
2689     // entire line is rotated to render vertically). Therefore, we ignore
2690     // the aOrientation value from the textrun's flags, and make all
2691     // vertical Mongolian/Phags-pa use sideways-right.
2692     orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
2693   }
2694 
2695   uint32_t runStart = 0;
2696   AutoTArray<TextRange, 3> fontRanges;
2697   ComputeRanges(fontRanges, aString, aLength, aRunScript, orientation);
2698   uint32_t numRanges = fontRanges.Length();
2699   bool missingChars = false;
2700   bool isCJK = gfxTextRun::IsCJKScript(aRunScript);
2701 
2702   for (uint32_t r = 0; r < numRanges; r++) {
2703     const TextRange& range = fontRanges[r];
2704     uint32_t matchedLength = range.Length();
2705     gfxFont* matchedFont = range.font;
2706     // create the glyph run for this range
2707     if (matchedFont && mStyle.noFallbackVariantFeatures) {
2708       // common case - just do glyph layout and record the
2709       // resulting positioned glyphs
2710       aTextRun->AddGlyphRun(matchedFont, range.matchType, aOffset + runStart,
2711                             (matchedLength > 0), range.orientation, isCJK);
2712       if (!matchedFont->SplitAndInitTextRun(
2713               aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
2714               matchedLength, aRunScript, mLanguage, range.orientation)) {
2715         // glyph layout failed! treat as missing glyphs
2716         matchedFont = nullptr;
2717       }
2718     } else if (matchedFont) {
2719       // shape with some variant feature that requires fallback handling
2720       bool petiteToSmallCaps = false;
2721       bool syntheticLower = false;
2722       bool syntheticUpper = false;
2723 
2724       if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
2725           (aTextRun->GetShapingState() ==
2726                gfxTextRun::eShapingState_ForceFallbackFeature ||
2727            !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper, aString,
2728                                                 aLength, aRunScript))) {
2729         // fallback for subscript/superscript variant glyphs
2730 
2731         // if the feature was already used, abort and force
2732         // fallback across the entire textrun
2733         gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
2734 
2735         if (ss == gfxTextRun::eShapingState_Normal) {
2736           aTextRun->SetShapingState(
2737               gfxTextRun::eShapingState_ShapingWithFallback);
2738         } else if (ss == gfxTextRun::eShapingState_ShapingWithFeature) {
2739           aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
2740           return;
2741         }
2742 
2743         RefPtr<gfxFont> subSuperFont = matchedFont->GetSubSuperscriptFont(
2744             aTextRun->GetAppUnitsPerDevUnit());
2745         aTextRun->AddGlyphRun(subSuperFont, range.matchType, aOffset + runStart,
2746                               (matchedLength > 0), range.orientation, isCJK);
2747         if (!subSuperFont->SplitAndInitTextRun(
2748                 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
2749                 matchedLength, aRunScript, mLanguage, range.orientation)) {
2750           // glyph layout failed! treat as missing glyphs
2751           matchedFont = nullptr;
2752         }
2753       } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
2754                  mStyle.allowSyntheticSmallCaps &&
2755                  !matchedFont->SupportsVariantCaps(
2756                      aRunScript, mStyle.variantCaps, petiteToSmallCaps,
2757                      syntheticLower, syntheticUpper)) {
2758         // fallback for small-caps variant glyphs
2759         if (!matchedFont->InitFakeSmallCapsRun(
2760                 mPresContext, aDrawTarget, aTextRun, aString + runStart,
2761                 aOffset + runStart, matchedLength, range.matchType,
2762                 range.orientation, aRunScript,
2763                 mExplicitLanguage ? mLanguage.get() : nullptr, syntheticLower,
2764                 syntheticUpper)) {
2765           matchedFont = nullptr;
2766         }
2767       } else {
2768         // shape normally with variant feature enabled
2769         gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
2770 
2771         // adjust the shaping state if necessary
2772         if (ss == gfxTextRun::eShapingState_Normal) {
2773           aTextRun->SetShapingState(
2774               gfxTextRun::eShapingState_ShapingWithFeature);
2775         } else if (ss == gfxTextRun::eShapingState_ShapingWithFallback) {
2776           // already have shaping results using fallback, need to redo
2777           aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
2778           return;
2779         }
2780 
2781         // do glyph layout and record the resulting positioned glyphs
2782         aTextRun->AddGlyphRun(matchedFont, range.matchType, aOffset + runStart,
2783                               (matchedLength > 0), range.orientation, isCJK);
2784         if (!matchedFont->SplitAndInitTextRun(
2785                 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
2786                 matchedLength, aRunScript, mLanguage, range.orientation)) {
2787           // glyph layout failed! treat as missing glyphs
2788           matchedFont = nullptr;
2789         }
2790       }
2791     } else {
2792       aTextRun->AddGlyphRun(mainFont, FontMatchType::Kind::kFontGroup,
2793                             aOffset + runStart, (matchedLength > 0),
2794                             range.orientation, isCJK);
2795     }
2796 
2797     if (!matchedFont) {
2798       // We need to set cluster boundaries (and mark spaces) so that
2799       // surrogate pairs, combining characters, etc behave properly,
2800       // even if we don't have glyphs for them
2801       aTextRun->SetupClusterBoundaries(aOffset + runStart, aString + runStart,
2802                                        matchedLength);
2803 
2804       // various "missing" characters may need special handling,
2805       // so we check for them here
2806       uint32_t runLimit = runStart + matchedLength;
2807       for (uint32_t index = runStart; index < runLimit; index++) {
2808         T ch = aString[index];
2809 
2810         // tab and newline are not to be displayed as hexboxes,
2811         // but do need to be recorded in the textrun
2812         if (ch == '\n') {
2813           aTextRun->SetIsNewline(aOffset + index);
2814           continue;
2815         }
2816         if (ch == '\t') {
2817           aTextRun->SetIsTab(aOffset + index);
2818           continue;
2819         }
2820 
2821         // for 16-bit textruns only, check for surrogate pairs and
2822         // special Unicode spaces; omit these checks in 8-bit runs
2823         if constexpr (sizeof(T) == sizeof(char16_t)) {
2824           if (index + 1 < aLength &&
2825               NS_IS_SURROGATE_PAIR(ch, aString[index + 1])) {
2826             uint32_t usv = SURROGATE_TO_UCS4(ch, aString[index + 1]);
2827             aTextRun->SetMissingGlyph(aOffset + index, usv, mainFont);
2828             index++;
2829             if (!mSkipDrawing && !IsPUA(usv)) {
2830               missingChars = true;
2831             }
2832             continue;
2833           }
2834 
2835           // check if this is a known Unicode whitespace character that
2836           // we can render using the space glyph with a custom width
2837           gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
2838           if (wid >= 0.0) {
2839             nscoord advance =
2840                 aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
2841             if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
2842               aTextRun->GetCharacterGlyphs()[aOffset + index].SetSimpleGlyph(
2843                   advance, mainFont->GetSpaceGlyph());
2844             } else {
2845               gfxTextRun::DetailedGlyph detailedGlyph;
2846               detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
2847               detailedGlyph.mAdvance = advance;
2848               aTextRun->SetDetailedGlyphs(aOffset + index, 1, &detailedGlyph);
2849             }
2850             continue;
2851           }
2852         }
2853 
2854         if (IsInvalidChar(ch)) {
2855           // invalid chars are left as zero-width/invisible
2856           continue;
2857         }
2858 
2859         // record char code so we can draw a box with the Unicode value
2860         aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont);
2861         if (!mSkipDrawing && !IsPUA(ch)) {
2862           missingChars = true;
2863         }
2864       }
2865     }
2866 
2867     runStart += matchedLength;
2868   }
2869 
2870   if (aMFR && missingChars) {
2871     aMFR->RecordScript(aRunScript);
2872   }
2873 }
2874 
GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,gfx::ShapedTextFlags aFlags,LazyReferenceDrawTargetGetter & aRefDrawTargetGetter)2875 gfxTextRun* gfxFontGroup::GetEllipsisTextRun(
2876     int32_t aAppUnitsPerDevPixel, gfx::ShapedTextFlags aFlags,
2877     LazyReferenceDrawTargetGetter& aRefDrawTargetGetter) {
2878   MOZ_ASSERT(!(aFlags & ~ShapedTextFlags::TEXT_ORIENT_MASK),
2879              "flags here should only be used to specify orientation");
2880   if (mCachedEllipsisTextRun &&
2881       (mCachedEllipsisTextRun->GetFlags() &
2882        ShapedTextFlags::TEXT_ORIENT_MASK) == aFlags &&
2883       mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
2884     return mCachedEllipsisTextRun.get();
2885   }
2886 
2887   // Use a Unicode ellipsis if the font supports it,
2888   // otherwise use three ASCII periods as fallback.
2889   gfxFont* firstFont = GetFirstValidFont(uint32_t(kEllipsisChar[0]));
2890   nsString ellipsis =
2891       firstFont->HasCharacter(kEllipsisChar[0])
2892           ? nsDependentString(kEllipsisChar, ArrayLength(kEllipsisChar) - 1)
2893           : nsDependentString(kASCIIPeriodsChar,
2894                               ArrayLength(kASCIIPeriodsChar) - 1);
2895 
2896   RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
2897   Parameters params = {refDT,   nullptr, nullptr,
2898                        nullptr, 0,       aAppUnitsPerDevPixel};
2899   mCachedEllipsisTextRun =
2900       MakeTextRun(ellipsis.get(), ellipsis.Length(), &params, aFlags,
2901                   nsTextFrameUtils::Flags(), nullptr);
2902   if (!mCachedEllipsisTextRun) {
2903     return nullptr;
2904   }
2905   // don't let the presence of a cached ellipsis textrun prolong the
2906   // fontgroup's life
2907   mCachedEllipsisTextRun->ReleaseFontGroup();
2908   return mCachedEllipsisTextRun.get();
2909 }
2910 
FindFallbackFaceForChar(gfxFontFamily * aFamily,uint32_t aCh,uint32_t aNextCh,eFontPresentation aPresentation)2911 gfxFont* gfxFontGroup::FindFallbackFaceForChar(
2912     gfxFontFamily* aFamily, uint32_t aCh, uint32_t aNextCh,
2913     eFontPresentation aPresentation) {
2914   GlobalFontMatch data(aCh, aNextCh, mStyle, aPresentation);
2915   aFamily->SearchAllFontsForChar(&data);
2916   gfxFontEntry* fe = data.mBestMatch;
2917   if (!fe) {
2918     return nullptr;
2919   }
2920   return fe->FindOrMakeFont(&mStyle);
2921 }
2922 
FindFallbackFaceForChar(fontlist::Family * aFamily,uint32_t aCh,uint32_t aNextCh,eFontPresentation aPresentation)2923 gfxFont* gfxFontGroup::FindFallbackFaceForChar(
2924     fontlist::Family* aFamily, uint32_t aCh, uint32_t aNextCh,
2925     eFontPresentation aPresentation) {
2926   auto* pfl = gfxPlatformFontList::PlatformFontList();
2927   auto* list = pfl->SharedFontList();
2928 
2929   // If async fallback is enabled, and the family isn't fully initialized yet,
2930   // just start the async cmap loading and return.
2931   if (!aFamily->IsFullyInitialized() &&
2932       StaticPrefs::gfx_font_rendering_fallback_async() &&
2933       !XRE_IsParentProcess()) {
2934     pfl->StartCmapLoadingFromFamily(aFamily - list->Families());
2935     return nullptr;
2936   }
2937 
2938   GlobalFontMatch data(aCh, aNextCh, mStyle, aPresentation);
2939   aFamily->SearchAllFontsForChar(list, &data);
2940   gfxFontEntry* fe = data.mBestMatch;
2941   if (!fe) {
2942     return nullptr;
2943   }
2944   return fe->FindOrMakeFont(&mStyle);
2945 }
2946 
FindFallbackFaceForChar(const FamilyFace & aFamily,uint32_t aCh,uint32_t aNextCh,eFontPresentation aPresentation)2947 gfxFont* gfxFontGroup::FindFallbackFaceForChar(
2948     const FamilyFace& aFamily, uint32_t aCh, uint32_t aNextCh,
2949     eFontPresentation aPresentation) {
2950   if (aFamily.IsSharedFamily()) {
2951     return FindFallbackFaceForChar(aFamily.SharedFamily(), aCh, aNextCh,
2952                                    aPresentation);
2953   }
2954   return FindFallbackFaceForChar(aFamily.OwnedFamily(), aCh, aNextCh,
2955                                  aPresentation);
2956 }
2957 
GetUnderlineOffset()2958 gfxFloat gfxFontGroup::GetUnderlineOffset() {
2959   if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) {
2960     // if the fontlist contains a bad underline font, make the underline
2961     // offset the min of the first valid font and bad font underline offsets
2962     uint32_t len = mFonts.Length();
2963     for (uint32_t i = 0; i < len; i++) {
2964       FamilyFace& ff = mFonts[i];
2965       gfxFontEntry* fe = ff.FontEntry();
2966       if (!fe) {
2967         continue;
2968       }
2969       if (!fe->mIsUserFontContainer && !fe->IsUserFont() &&
2970           ((ff.IsSharedFamily() && ff.SharedFamily() &&
2971             ff.SharedFamily()->IsBadUnderlineFamily()) ||
2972            (!ff.IsSharedFamily() && ff.OwnedFamily() &&
2973             ff.OwnedFamily()->IsBadUnderlineFamily()))) {
2974         gfxFont* font = GetFontAt(i);
2975         if (!font) {
2976           continue;
2977         }
2978         gfxFloat bad =
2979             font->GetMetrics(nsFontMetrics::eHorizontal).underlineOffset;
2980         gfxFloat first = GetFirstValidFont()
2981                              ->GetMetrics(nsFontMetrics::eHorizontal)
2982                              .underlineOffset;
2983         mUnderlineOffset = std::min(first, bad);
2984         return mUnderlineOffset;
2985       }
2986     }
2987 
2988     // no bad underline fonts, use the first valid font's metric
2989     mUnderlineOffset = GetFirstValidFont()
2990                            ->GetMetrics(nsFontMetrics::eHorizontal)
2991                            .underlineOffset;
2992   }
2993 
2994   return mUnderlineOffset;
2995 }
2996 
2997 #define NARROW_NO_BREAK_SPACE 0x202fu
2998 
FindFontForChar(uint32_t aCh,uint32_t aPrevCh,uint32_t aNextCh,Script aRunScript,gfxFont * aPrevMatchedFont,FontMatchType * aMatchType)2999 gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
3000                                        uint32_t aNextCh, Script aRunScript,
3001                                        gfxFont* aPrevMatchedFont,
3002                                        FontMatchType* aMatchType) {
3003   // If the char is a cluster extender, we want to use the same font as the
3004   // preceding character if possible. This is preferable to using the font
3005   // group because it avoids breaks in shaping within a cluster.
3006   if (aPrevMatchedFont && IsClusterExtender(aCh)) {
3007     if (aPrevMatchedFont->HasCharacter(aCh) || IsDefaultIgnorable(aCh)) {
3008       return aPrevMatchedFont;
3009     }
3010     // Get the singleton NFC normalizer; this does not need to be deleted.
3011     static UErrorCode err = U_ZERO_ERROR;
3012     static const UNormalizer2* nfc = unorm2_getNFCInstance(&err);
3013     // Check if this char and preceding char can compose; if so, is the
3014     // combination supported by the current font.
3015     int32_t composed = unorm2_composePair(nfc, aPrevCh, aCh);
3016     if (composed > 0 && aPrevMatchedFont->HasCharacter(composed)) {
3017       return aPrevMatchedFont;
3018     }
3019   }
3020 
3021   // Special cases for NNBSP (as used in Mongolian):
3022   if (aCh == NARROW_NO_BREAK_SPACE) {
3023     // If there is no preceding character, try the font that we'd use
3024     // for the next char (unless it's just another NNBSP; we don't try
3025     // to look ahead through a whole run of them).
3026     if (!aPrevCh && aNextCh && aNextCh != NARROW_NO_BREAK_SPACE) {
3027       gfxFont* nextFont = FindFontForChar(aNextCh, 0, 0, aRunScript,
3028                                           aPrevMatchedFont, aMatchType);
3029       if (nextFont && nextFont->HasCharacter(aCh)) {
3030         return nextFont;
3031       }
3032     }
3033     // Otherwise, treat NNBSP like a cluster extender (as above) and try
3034     // to continue the preceding font run.
3035     if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
3036       return aPrevMatchedFont;
3037     }
3038   }
3039 
3040   // To optimize common cases, try the first font in the font-group
3041   // before going into the more detailed checks below
3042   uint32_t fontListLength = mFonts.Length();
3043   uint32_t nextIndex = 0;
3044   bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
3045   bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
3046   bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
3047   bool nextIsVarSelector = gfxFontUtils::IsVarSelector(aNextCh);
3048 
3049   // For Unicode hyphens, if not supported in the font then we'll try for
3050   // the ASCII hyphen-minus as a fallback.
3051   // Similarly, for NBSP we try normal <space> as a fallback.
3052   uint32_t fallbackChar = (aCh == 0x2010 || aCh == 0x2011) ? '-'
3053                           : (aCh == 0x00A0)                ? ' '
3054                                                            : 0;
3055 
3056   // Whether we've seen a font that is currently loading a resource that may
3057   // provide this character (so we should not start a new load).
3058   bool loading = false;
3059 
3060   // Do we need to explicitly look for a font that does or does not provide a
3061   // color glyph for the given character?
3062   // For characters with no `EMOJI` property, we'll use whatever the family
3063   // list calls for; but if it's a potential emoji codepoint, we need to check
3064   // if there's a variation selector specifically asking for Text-style or
3065   // Emoji-style rendering and look for a suitable font.
3066   eFontPresentation presentation = eFontPresentation::Any;
3067   EmojiPresentation emojiPresentation = GetEmojiPresentation(aCh);
3068   if (emojiPresentation != TextOnly) {
3069     // If the prefer-emoji selector is present, or if it's a default-emoji char
3070     // and the prefer-text selector is NOT present, or if there's a skin-tone
3071     // modifier, we specifically look for a font with a color glyph.
3072     // If the prefer-text selector is present, we specifically look for a font
3073     // that will provide a monochrome glyph.
3074     // Otherwise, we'll accept either color or monochrome font-family entries,
3075     // so that a color font can be explicitly applied via font-family even to
3076     // characters that are not inherently emoji-style.
3077     if (aNextCh == kVariationSelector16 ||
3078         (aNextCh >= kEmojiSkinToneFirst && aNextCh <= kEmojiSkinToneLast)) {
3079       // Emoji presentation is explicitly requested by a variation selector or
3080       // the presence of a skin-tone codepoint.
3081       presentation = eFontPresentation::EmojiExplicit;
3082     } else if (emojiPresentation == EmojiPresentation::EmojiDefault &&
3083                aNextCh != kVariationSelector15) {
3084       // Emoji presentation is the default for this Unicode character. but we
3085       // will allow an explicitly-specified webfont to apply to it, regardless
3086       // of its glyph type.
3087       presentation = eFontPresentation::EmojiDefault;
3088     } else if (aNextCh == kVariationSelector15) {
3089       // Text presentation is explicitly requested.
3090       presentation = eFontPresentation::Text;
3091     }
3092   }
3093 
3094   if (!isJoinControl && !wasJoinCauser && !isVarSelector &&
3095       !nextIsVarSelector && presentation == eFontPresentation::Any) {
3096     gfxFont* firstFont = GetFontAt(0, aCh, &loading);
3097     if (firstFont) {
3098       if (firstFont->HasCharacter(aCh) ||
3099           (fallbackChar && firstFont->HasCharacter(fallbackChar))) {
3100         *aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()};
3101         return firstFont;
3102       }
3103 
3104       gfxFont* font = nullptr;
3105       if (mFonts[0].CheckForFallbackFaces()) {
3106         font = FindFallbackFaceForChar(mFonts[0], aCh, aNextCh, presentation);
3107       } else if (!firstFont->GetFontEntry()->IsUserFont()) {
3108         // For platform fonts (but not userfonts), we may need to do
3109         // fallback within the family to handle cases where some faces
3110         // such as Italic or Black have reduced character sets compared
3111         // to the family's Regular face.
3112         font = FindFallbackFaceForChar(mFonts[0], aCh, aNextCh, presentation);
3113       }
3114       if (font) {
3115         *aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()};
3116         return font;
3117       }
3118     } else {
3119       if (fontListLength > 0) {
3120         loading = loading || mFonts[0].IsLoadingFor(aCh);
3121       }
3122     }
3123 
3124     // we don't need to check the first font again below
3125     ++nextIndex;
3126   }
3127 
3128   if (aPrevMatchedFont) {
3129     // Don't switch fonts for control characters, regardless of
3130     // whether they are present in the current font, as they won't
3131     // actually be rendered (see bug 716229)
3132     if (isJoinControl ||
3133         GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
3134       return aPrevMatchedFont;
3135     }
3136 
3137     // if previous character was a join-causer (ZWJ),
3138     // use the same font as the previous range if we can
3139     if (wasJoinCauser) {
3140       if (aPrevMatchedFont->HasCharacter(aCh)) {
3141         return aPrevMatchedFont;
3142       }
3143     }
3144   }
3145 
3146   // If this character is a variation selector or default-ignorable, use the
3147   // previous font regardless of whether it supports the codepoint or not.
3148   // (We don't want to unnecessarily split glyph runs, and the character will
3149   // not be visibly rendered.)
3150   if (isVarSelector || IsDefaultIgnorable(aCh)) {
3151     return aPrevMatchedFont;
3152   }
3153 
3154   // Used to remember the first "candidate" font that would provide a fallback
3155   // text-style rendering if no color glyph can be found.
3156   // If we decide NOT to return this font, we must AddRef/Release it to ensure
3157   // that it goes into the global font cache as a candidate for deletion.
3158   // This is done for us by CheckCandidate, but any code path that returns
3159   // WITHOUT calling CheckCandidate needs to handle it explicitly.
3160   gfxFont* candidateFont = nullptr;
3161   FontMatchType candidateMatchType;
3162 
3163   // Handle a candidate font that could support the character, returning true
3164   // if we should go ahead and return |f|, false to continue searching.
3165   // If there is already a saved candidate font, and the new candidate is
3166   // accepted, we AddRef/Release the existing candidate so it won't leak.
3167   auto CheckCandidate = [&](gfxFont* f, FontMatchType t) -> bool {
3168     // If no preference, then just accept the font.
3169     if (presentation == eFontPresentation::Any ||
3170         (presentation == eFontPresentation::EmojiDefault &&
3171          f->GetFontEntry()->IsUserFont())) {
3172       RefPtr<gfxFont> autoRefDeref(candidateFont);
3173       *aMatchType = t;
3174       return true;
3175     }
3176     // Does the candidate font provide a color glyph for the current character?
3177     bool hasColorGlyph = f->HasColorGlyphFor(aCh, aNextCh);
3178     // If the provided glyph matches the preference, accept the font.
3179     if (hasColorGlyph == PrefersColor(presentation)) {
3180       RefPtr<gfxFont> autoRefDeref(candidateFont);
3181       *aMatchType = t;
3182       return true;
3183     }
3184     // Otherwise, remember the first potential fallback, but keep searching.
3185     if (!candidateFont) {
3186       candidateFont = f;
3187       candidateMatchType = t;
3188     }
3189     return false;
3190   };
3191 
3192   // 1. check remaining fonts in the font group
3193   for (uint32_t i = nextIndex; i < fontListLength; i++) {
3194     FamilyFace& ff = mFonts[i];
3195     if (ff.IsInvalid() || ff.IsLoading()) {
3196       if (ff.IsLoadingFor(aCh)) {
3197         loading = true;
3198       }
3199       continue;
3200     }
3201 
3202     gfxFont* font = ff.Font();
3203     if (font) {
3204       // if available, use already-made gfxFont and check for character
3205       if (font->HasCharacter(aCh) ||
3206           (fallbackChar && font->HasCharacter(fallbackChar))) {
3207         if (CheckCandidate(font,
3208                            {FontMatchType::Kind::kFontGroup, ff.Generic()})) {
3209           return font;
3210         }
3211       }
3212     } else {
3213       // don't have a gfxFont yet, test charmap before instantiating
3214       gfxFontEntry* fe = ff.FontEntry();
3215       if (fe && fe->mIsUserFontContainer) {
3216         // for userfonts, need to test both the unicode range map and
3217         // the cmap of the platform font entry
3218         gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
3219 
3220         // never match a character outside the defined unicode range
3221         if (!ufe->CharacterInUnicodeRange(aCh)) {
3222           continue;
3223         }
3224 
3225         // Load if not already loaded, unless we've already seen an in-
3226         // progress load that is expected to satisfy this request.
3227         if (!loading &&
3228             ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED) {
3229           ufe->Load();
3230           ff.CheckState(mSkipDrawing);
3231         }
3232 
3233         if (ff.IsLoading()) {
3234           loading = true;
3235         }
3236 
3237         gfxFontEntry* pfe = ufe->GetPlatformFontEntry();
3238         if (pfe && (pfe->HasCharacter(aCh) ||
3239                     (fallbackChar && pfe->HasCharacter(fallbackChar)))) {
3240           font = GetFontAt(i, aCh, &loading);
3241           if (font) {
3242             if (CheckCandidate(font, {FontMatchType::Kind::kFontGroup,
3243                                       mFonts[i].Generic()})) {
3244               return font;
3245             }
3246           }
3247         }
3248       } else if (fe && (fe->HasCharacter(aCh) ||
3249                         (fallbackChar && fe->HasCharacter(fallbackChar)))) {
3250         // for normal platform fonts, after checking the cmap
3251         // build the font via GetFontAt
3252         font = GetFontAt(i, aCh, &loading);
3253         if (font) {
3254           if (CheckCandidate(font, {FontMatchType::Kind::kFontGroup,
3255                                     mFonts[i].Generic()})) {
3256             return font;
3257           }
3258         }
3259       }
3260     }
3261 
3262     // check other family faces if needed
3263     if (ff.CheckForFallbackFaces()) {
3264 #ifdef DEBUG
3265       if (i > 0) {
3266         fontlist::FontList* list =
3267             gfxPlatformFontList::PlatformFontList()->SharedFontList();
3268         nsCString s1 = mFonts[i - 1].IsSharedFamily()
3269                            ? mFonts[i - 1].SharedFamily()->Key().AsString(list)
3270                            : mFonts[i - 1].OwnedFamily()->Name();
3271         nsCString s2 = ff.IsSharedFamily()
3272                            ? ff.SharedFamily()->Key().AsString(list)
3273                            : ff.OwnedFamily()->Name();
3274         MOZ_ASSERT(!mFonts[i - 1].CheckForFallbackFaces() || !s1.Equals(s2),
3275                    "should only do fallback once per font family");
3276       }
3277 #endif
3278       font = FindFallbackFaceForChar(ff, aCh, aNextCh, presentation);
3279       if (font) {
3280         if (CheckCandidate(font,
3281                            {FontMatchType::Kind::kFontGroup, ff.Generic()})) {
3282           return font;
3283         }
3284       }
3285     } else {
3286       // For platform fonts, but not user fonts, consider intra-family
3287       // fallback to handle styles with reduced character sets (see
3288       // also above).
3289       gfxFontEntry* fe = ff.FontEntry();
3290       if (fe && !fe->mIsUserFontContainer && !fe->IsUserFont()) {
3291         font = FindFallbackFaceForChar(ff, aCh, aNextCh, presentation);
3292         if (font) {
3293           if (CheckCandidate(font,
3294                              {FontMatchType::Kind::kFontGroup, ff.Generic()})) {
3295             return font;
3296           }
3297         }
3298       }
3299     }
3300   }
3301 
3302   if (fontListLength == 0) {
3303     gfxFont* defaultFont = GetDefaultFont();
3304     if (defaultFont->HasCharacter(aCh) ||
3305         (fallbackChar && defaultFont->HasCharacter(fallbackChar))) {
3306       if (CheckCandidate(defaultFont, FontMatchType::Kind::kFontGroup)) {
3307         return defaultFont;
3308       }
3309     }
3310   }
3311 
3312   // If character is in Private Use Area, or is unassigned in Unicode, don't do
3313   // matching against pref or system fonts. We only support such codepoints
3314   // when used with an explicitly-specified font, as they have no standard/
3315   // interoperable meaning.
3316   // Also don't attempt any fallback for control characters or noncharacters,
3317   // where we won't be rendering a glyph anyhow, or for codepoints where global
3318   // fallback has already noted a failure.
3319   FontVisibility level =
3320       mPresContext ? mPresContext->GetFontVisibility() : FontVisibility::User;
3321   if (gfxPlatformFontList::PlatformFontList()->SkipFontFallbackForChar(level,
3322                                                                        aCh) ||
3323       GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) {
3324     if (candidateFont) {
3325       *aMatchType = candidateMatchType;
3326     }
3327     return candidateFont;
3328   }
3329 
3330   // 2. search pref fonts
3331   gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh, presentation);
3332   if (font) {
3333     if (PrefersColor(presentation) &&
3334         Preferences::HasUserValue("font.name-list.emoji")) {
3335       // For emoji, always accept the font from preferences if it's explicitly
3336       // user-set, even if it isn't actually a color-emoji font, as some users
3337       // may want to set their emoji font preference to a monochrome font like
3338       // Symbola.
3339       // So a user-provided font.name-list.emoji preference takes precedence
3340       // over the Unicode presentation style here.
3341       RefPtr<gfxFont> autoRefDeref(candidateFont);
3342       *aMatchType = FontMatchType::Kind::kPrefsFallback;
3343       return font;
3344     }
3345     if (CheckCandidate(font, FontMatchType::Kind::kPrefsFallback)) {
3346       return font;
3347     }
3348     // Don't leak `font` if we decided not to return it.
3349     RefPtr<gfxFont> autoRefDeref(font);
3350   }
3351 
3352   // For fallback searches, we don't want to use a color-emoji font unless
3353   // emoji-style presentation is specifically required, so we map Any to
3354   // Text here.
3355   if (presentation == eFontPresentation::Any) {
3356     presentation = eFontPresentation::Text;
3357   }
3358 
3359   // 3. use fallback fonts
3360   // -- before searching for something else check the font used for the
3361   //    previous character
3362   if (aPrevMatchedFont &&
3363       (aPrevMatchedFont->HasCharacter(aCh) ||
3364        (fallbackChar && aPrevMatchedFont->HasCharacter(fallbackChar)))) {
3365     if (CheckCandidate(aPrevMatchedFont,
3366                        FontMatchType::Kind::kSystemFallback)) {
3367       return aPrevMatchedFont;
3368     }
3369   }
3370 
3371   // for known "space" characters, don't do a full system-fallback search;
3372   // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
3373   if (GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
3374       GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0) {
3375     RefPtr<gfxFont> autoRefDeref(candidateFont);
3376     return nullptr;
3377   }
3378 
3379   // -- otherwise look for other stuff
3380   font = WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript, presentation);
3381   if (font) {
3382     if (CheckCandidate(font, FontMatchType::Kind::kSystemFallback)) {
3383       return font;
3384     }
3385     RefPtr<gfxFont> autoRefDeref(font);
3386   }
3387   if (candidateFont) {
3388     *aMatchType = candidateMatchType;
3389   }
3390   return candidateFont;
3391 }
3392 
3393 template <typename T>
ComputeRanges(nsTArray<TextRange> & aRanges,const T * aString,uint32_t aLength,Script aRunScript,gfx::ShapedTextFlags aOrientation)3394 void gfxFontGroup::ComputeRanges(nsTArray<TextRange>& aRanges, const T* aString,
3395                                  uint32_t aLength, Script aRunScript,
3396                                  gfx::ShapedTextFlags aOrientation) {
3397   NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
3398   NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
3399 
3400   uint32_t prevCh = 0;
3401   uint32_t nextCh = aString[0];
3402   if constexpr (sizeof(T) == sizeof(char16_t)) {
3403     if (aLength > 1 && NS_IS_SURROGATE_PAIR(nextCh, aString[1])) {
3404       nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]);
3405     }
3406   }
3407   int32_t lastRangeIndex = -1;
3408 
3409   // initialize prevFont to the group's primary font, so that this will be
3410   // used for string-initial control chars, etc rather than risk hitting font
3411   // fallback for these (bug 716229)
3412   StyleGenericFontFamily generic = StyleGenericFontFamily::None;
3413   gfxFont* prevFont = GetFirstValidFont(' ', &generic);
3414 
3415   // if we use the initial value of prevFont, we treat this as a match from
3416   // the font group; fixes bug 978313
3417   FontMatchType matchType = {FontMatchType::Kind::kFontGroup, generic};
3418 
3419   for (uint32_t i = 0; i < aLength; i++) {
3420     const uint32_t origI = i;  // save off in case we increase for surrogate
3421 
3422     // set up current ch
3423     uint32_t ch = nextCh;
3424 
3425     // Get next char (if any) so that FindFontForChar can look ahead
3426     // for a possible variation selector.
3427 
3428     if constexpr (sizeof(T) == sizeof(char16_t)) {
3429       // In 16-bit case only, check for surrogate pairs.
3430       if (ch > 0xffffu) {
3431         i++;
3432       }
3433       if (i < aLength - 1) {
3434         nextCh = aString[i + 1];
3435         if (i + 2 < aLength && NS_IS_SURROGATE_PAIR(nextCh, aString[i + 2])) {
3436           nextCh = SURROGATE_TO_UCS4(nextCh, aString[i + 2]);
3437         }
3438       } else {
3439         nextCh = 0;
3440       }
3441     } else {
3442       // 8-bit case is trivial.
3443       nextCh = i < aLength - 1 ? aString[i + 1] : 0;
3444     }
3445 
3446     gfxFont* font;
3447 
3448     // Find the font for this char; but try to avoid calling the expensive
3449     // FindFontForChar method for the most common case, where the first
3450     // font in the list supports the current char, and it is not one of
3451     // the special cases where FindFontForChar will attempt to propagate
3452     // the font selected for an adjacent character.
3453     if ((font = GetFontAt(0, ch)) != nullptr && font->HasCharacter(ch) &&
3454         (sizeof(T) == sizeof(uint8_t) ||
3455          (!IsClusterExtender(ch) && ch != NARROW_NO_BREAK_SPACE &&
3456           !gfxFontUtils::IsJoinControl(ch) &&
3457           !gfxFontUtils::IsJoinCauser(prevCh) &&
3458           !gfxFontUtils::IsVarSelector(ch) &&
3459           GetEmojiPresentation(ch) == TextOnly))) {
3460       matchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()};
3461     } else {
3462       font =
3463           FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont, &matchType);
3464     }
3465 
3466 #ifndef RELEASE_OR_BETA
3467     if (MOZ_UNLIKELY(mTextPerf)) {
3468       if (matchType.kind == FontMatchType::Kind::kPrefsFallback) {
3469         mTextPerf->current.fallbackPrefs++;
3470       } else if (matchType.kind == FontMatchType::Kind::kSystemFallback) {
3471         mTextPerf->current.fallbackSystem++;
3472       }
3473     }
3474 #endif
3475 
3476     prevCh = ch;
3477 
3478     ShapedTextFlags orient = aOrientation;
3479     if (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
3480       // For CSS text-orientation:mixed, we need to resolve orientation
3481       // on a per-character basis using the UTR50 orientation property.
3482       switch (GetVerticalOrientation(ch)) {
3483         case VERTICAL_ORIENTATION_U:
3484         case VERTICAL_ORIENTATION_Tu:
3485           orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
3486           break;
3487         case VERTICAL_ORIENTATION_Tr: {
3488           // We check for a vertical presentation form first as that's
3489           // likely to be cheaper than inspecting lookups to see if the
3490           // 'vert' feature is going to handle this character, and if the
3491           // presentation form is available then it will be used as
3492           // fallback if needed, so it's OK if the feature is missing.
3493           //
3494           // Because "common" CJK punctuation characters in isolation will be
3495           // resolved to Bopomofo script (as the first script listed in their
3496           // ScriptExtensions property), but this is not always well supported
3497           // by fonts' OpenType tables, we also try Han script; harfbuzz will
3498           // apply a 'vert' feature from any available script (see
3499           // https://github.com/harfbuzz/harfbuzz/issues/63) when shaping,
3500           // so this is OK. It's not quite as general as what harfbuzz does
3501           // (it will find the feature in *any* script), but should be enough
3502           // for likely real-world examples.
3503           uint32_t v = gfxHarfBuzzShaper::GetVerticalPresentationForm(ch);
3504           const uint32_t kVert = HB_TAG('v', 'e', 'r', 't');
3505           orient = (!font || (v && font->HasCharacter(v)) ||
3506                     font->FeatureWillHandleChar(aRunScript, kVert, ch) ||
3507                     (aRunScript == Script::BOPOMOFO &&
3508                      font->FeatureWillHandleChar(Script::HAN, kVert, ch)))
3509                        ? ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
3510                        : ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
3511           break;
3512         }
3513         case VERTICAL_ORIENTATION_R:
3514           orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
3515           break;
3516       }
3517     }
3518 
3519     if (lastRangeIndex == -1) {
3520       // first char ==> make a new range
3521       aRanges.AppendElement(TextRange(0, 1, font, matchType, orient));
3522       lastRangeIndex++;
3523       prevFont = font;
3524     } else {
3525       // if font or orientation has changed, make a new range...
3526       // unless ch is a variation selector (bug 1248248)
3527       TextRange& prevRange = aRanges[lastRangeIndex];
3528       if (prevRange.font != font ||
3529           (prevRange.orientation != orient && !IsClusterExtender(ch))) {
3530         // close out the previous range
3531         prevRange.end = origI;
3532         aRanges.AppendElement(TextRange(origI, i + 1, font, matchType, orient));
3533         lastRangeIndex++;
3534 
3535         // update prevFont for the next match, *unless* we switched
3536         // fonts on a ZWJ, in which case propagating the changed font
3537         // is probably not a good idea (see bug 619511)
3538         if (sizeof(T) == sizeof(uint8_t) || !gfxFontUtils::IsJoinCauser(ch)) {
3539           prevFont = font;
3540         }
3541       } else {
3542         prevRange.matchType |= matchType;
3543       }
3544     }
3545   }
3546 
3547   aRanges[lastRangeIndex].end = aLength;
3548 
3549 #ifndef RELEASE_OR_BETA
3550   LogModule* log = mStyle.systemFont ? gfxPlatform::GetLog(eGfxLog_textrunui)
3551                                      : gfxPlatform::GetLog(eGfxLog_textrun);
3552 
3553   if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
3554     nsAutoCString lang;
3555     mLanguage->ToUTF8String(lang);
3556     auto defaultLanguageGeneric = GetDefaultGeneric(mLanguage);
3557 
3558     // collect the font matched for each range
3559     nsAutoCString fontMatches;
3560     for (size_t i = 0, i_end = aRanges.Length(); i < i_end; i++) {
3561       const TextRange& r = aRanges[i];
3562       nsAutoCString matchTypes;
3563       if (r.matchType.kind & FontMatchType::Kind::kFontGroup) {
3564         matchTypes.AppendLiteral("list");
3565       }
3566       if (r.matchType.kind & FontMatchType::Kind::kPrefsFallback) {
3567         if (!matchTypes.IsEmpty()) {
3568           matchTypes.AppendLiteral(",");
3569         }
3570         matchTypes.AppendLiteral("prefs");
3571       }
3572       if (r.matchType.kind & FontMatchType::Kind::kSystemFallback) {
3573         if (!matchTypes.IsEmpty()) {
3574           matchTypes.AppendLiteral(",");
3575         }
3576         matchTypes.AppendLiteral("sys");
3577       }
3578       fontMatches.AppendPrintf(
3579           " [%u:%u] %.200s (%s)", r.start, r.end,
3580           (r.font.get() ? r.font->GetName().get() : "<null>"),
3581           matchTypes.get());
3582     }
3583     MOZ_LOG(log, LogLevel::Debug,
3584             ("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d"
3585              "%s\n",
3586              (mStyle.systemFont ? "textrunui" : "textrun"),
3587              FamilyListToString(mFamilyList).get(),
3588              (defaultLanguageGeneric == StyleGenericFontFamily::Serif
3589                   ? "serif"
3590                   : (defaultLanguageGeneric == StyleGenericFontFamily::SansSerif
3591                          ? "sans-serif"
3592                          : "none")),
3593              lang.get(), static_cast<int>(aRunScript), fontMatches.get()));
3594   }
3595 #endif
3596 }
3597 
GetUserFontSet()3598 gfxUserFontSet* gfxFontGroup::GetUserFontSet() { return mUserFontSet; }
3599 
SetUserFontSet(gfxUserFontSet * aUserFontSet)3600 void gfxFontGroup::SetUserFontSet(gfxUserFontSet* aUserFontSet) {
3601   if (aUserFontSet == mUserFontSet) {
3602     return;
3603   }
3604   mUserFontSet = aUserFontSet;
3605   mCurrGeneration = GetGeneration() - 1;
3606   UpdateUserFonts();
3607 }
3608 
GetGeneration()3609 uint64_t gfxFontGroup::GetGeneration() {
3610   if (!mUserFontSet) return 0;
3611   return mUserFontSet->GetGeneration();
3612 }
3613 
GetRebuildGeneration()3614 uint64_t gfxFontGroup::GetRebuildGeneration() {
3615   if (!mUserFontSet) return 0;
3616   return mUserFontSet->GetRebuildGeneration();
3617 }
3618 
UpdateUserFonts()3619 void gfxFontGroup::UpdateUserFonts() {
3620   if (mCurrGeneration < GetRebuildGeneration()) {
3621     // fonts in userfont set changed, need to redo the fontlist
3622     mFonts.Clear();
3623     ClearCachedData();
3624     BuildFontList();
3625     mCurrGeneration = GetGeneration();
3626   } else if (mCurrGeneration != GetGeneration()) {
3627     // load state change occurred, verify load state and validity of fonts
3628     ClearCachedData();
3629 
3630     uint32_t len = mFonts.Length();
3631     for (uint32_t i = 0; i < len; i++) {
3632       FamilyFace& ff = mFonts[i];
3633       if (ff.Font() || !ff.IsUserFontContainer()) {
3634         continue;
3635       }
3636       ff.CheckState(mSkipDrawing);
3637     }
3638 
3639     mCurrGeneration = GetGeneration();
3640   }
3641 }
3642 
ContainsUserFont(const gfxUserFontEntry * aUserFont)3643 bool gfxFontGroup::ContainsUserFont(const gfxUserFontEntry* aUserFont) {
3644   UpdateUserFonts();
3645   // search through the fonts list for a specific user font
3646   uint32_t len = mFonts.Length();
3647   for (uint32_t i = 0; i < len; i++) {
3648     FamilyFace& ff = mFonts[i];
3649     if (ff.EqualsUserFont(aUserFont)) {
3650       return true;
3651     }
3652   }
3653   return false;
3654 }
3655 
WhichPrefFontSupportsChar(uint32_t aCh,uint32_t aNextCh,eFontPresentation aPresentation)3656 gfxFont* gfxFontGroup::WhichPrefFontSupportsChar(
3657     uint32_t aCh, uint32_t aNextCh, eFontPresentation aPresentation) {
3658   eFontPrefLang charLang;
3659   gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
3660 
3661   if (PrefersColor(aPresentation)) {
3662     charLang = eFontPrefLang_Emoji;
3663   } else {
3664     // get the pref font list if it hasn't been set up already
3665     charLang = pfl->GetFontPrefLangFor(aCh);
3666   }
3667 
3668   // if the last pref font was the first family in the pref list, no need to
3669   // recheck through a list of families
3670   if (mLastPrefFont && charLang == mLastPrefLang && mLastPrefFirstFont &&
3671       mLastPrefFont->HasCharacter(aCh)) {
3672     return mLastPrefFont;
3673   }
3674 
3675   // based on char lang and page lang, set up list of pref lang fonts to check
3676   eFontPrefLang prefLangs[kMaxLenPrefLangList];
3677   uint32_t i, numLangs = 0;
3678 
3679   pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
3680 
3681   for (i = 0; i < numLangs; i++) {
3682     eFontPrefLang currentLang = prefLangs[i];
3683     StyleGenericFontFamily generic =
3684         mFallbackGeneric != StyleGenericFontFamily::None
3685             ? mFallbackGeneric
3686             : pfl->GetDefaultGeneric(currentLang);
3687     gfxPlatformFontList::PrefFontList* families =
3688         pfl->GetPrefFontsLangGroup(mPresContext, generic, currentLang);
3689     NS_ASSERTION(families, "no pref font families found");
3690 
3691     // find the first pref font that includes the character
3692     uint32_t j, numPrefs;
3693     numPrefs = families->Length();
3694     for (j = 0; j < numPrefs; j++) {
3695       // look up the appropriate face
3696       FontFamily family = (*families)[j];
3697       if (family.IsNull()) {
3698         continue;
3699       }
3700 
3701       // if a pref font is used, it's likely to be used again in the same text
3702       // run. the style doesn't change so the face lookup can be cached rather
3703       // than calling FindOrMakeFont repeatedly.  speeds up FindFontForChar
3704       // lookup times for subsequent pref font lookups
3705       if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
3706         return mLastPrefFont;
3707       }
3708 
3709       gfxFontEntry* fe = nullptr;
3710       if (family.mIsShared) {
3711         fontlist::Family* fam = family.mShared;
3712         if (!fam->IsInitialized()) {
3713           Unused << pfl->InitializeFamily(fam);
3714         }
3715         fontlist::Face* face =
3716             fam->FindFaceForStyle(pfl->SharedFontList(), mStyle);
3717         if (face) {
3718           fe = pfl->GetOrCreateFontEntry(face, fam);
3719         }
3720       } else {
3721         fe = family.mUnshared->FindFontForStyle(mStyle);
3722       }
3723       if (!fe) {
3724         continue;
3725       }
3726 
3727       // if ch in cmap, create and return a gfxFont
3728       gfxFont* prefFont = nullptr;
3729       if (fe->HasCharacter(aCh)) {
3730         prefFont = fe->FindOrMakeFont(&mStyle);
3731         if (!prefFont) {
3732           continue;
3733         }
3734       }
3735 
3736       // If the char was not available, see if we can fall back to an
3737       // alternative face in the same family.
3738       if (!prefFont) {
3739         prefFont = family.mIsShared
3740                        ? FindFallbackFaceForChar(family.mShared, aCh, aNextCh,
3741                                                  aPresentation)
3742                        : FindFallbackFaceForChar(family.mUnshared, aCh, aNextCh,
3743                                                  aPresentation);
3744       }
3745       if (prefFont) {
3746         mLastPrefFamily = family;
3747         mLastPrefFont = prefFont;
3748         mLastPrefLang = charLang;
3749         mLastPrefFirstFont = (i == 0 && j == 0);
3750         return prefFont;
3751       }
3752     }
3753   }
3754 
3755   return nullptr;
3756 }
3757 
WhichSystemFontSupportsChar(uint32_t aCh,uint32_t aNextCh,Script aRunScript,eFontPresentation aPresentation)3758 gfxFont* gfxFontGroup::WhichSystemFontSupportsChar(
3759     uint32_t aCh, uint32_t aNextCh, Script aRunScript,
3760     eFontPresentation aPresentation) {
3761   FontVisibility visibility;
3762   gfxFont* font =
3763       gfxPlatformFontList::PlatformFontList()->SystemFindFontForChar(
3764           mPresContext, aCh, aNextCh, aRunScript, aPresentation, &mStyle,
3765           &visibility);
3766   if (font) {
3767     return font;
3768   }
3769 
3770   return nullptr;
3771 }
3772 
Flush()3773 void gfxMissingFontRecorder::Flush() {
3774   static bool mNotifiedFontsInitialized = false;
3775   static uint32_t mNotifiedFonts[gfxMissingFontRecorder::kNumScriptBitsWords];
3776   if (!mNotifiedFontsInitialized) {
3777     memset(&mNotifiedFonts, 0, sizeof(mNotifiedFonts));
3778     mNotifiedFontsInitialized = true;
3779   }
3780 
3781   nsAutoString fontNeeded;
3782   for (uint32_t i = 0; i < kNumScriptBitsWords; ++i) {
3783     mMissingFonts[i] &= ~mNotifiedFonts[i];
3784     if (!mMissingFonts[i]) {
3785       continue;
3786     }
3787     for (uint32_t j = 0; j < 32; ++j) {
3788       if (!(mMissingFonts[i] & (1 << j))) {
3789         continue;
3790       }
3791       mNotifiedFonts[i] |= (1 << j);
3792       if (!fontNeeded.IsEmpty()) {
3793         fontNeeded.Append(char16_t(','));
3794       }
3795       uint32_t sc = i * 32 + j;
3796       MOZ_ASSERT(sc < static_cast<uint32_t>(Script::NUM_SCRIPT_CODES),
3797                  "how did we set the bit for an invalid script code?");
3798       uint32_t tag = GetScriptTagForCode(static_cast<Script>(sc));
3799       fontNeeded.Append(char16_t(tag >> 24));
3800       fontNeeded.Append(char16_t((tag >> 16) & 0xff));
3801       fontNeeded.Append(char16_t((tag >> 8) & 0xff));
3802       fontNeeded.Append(char16_t(tag & 0xff));
3803     }
3804     mMissingFonts[i] = 0;
3805   }
3806   if (!fontNeeded.IsEmpty()) {
3807     nsCOMPtr<nsIObserverService> service = GetObserverService();
3808     service->NotifyObservers(nullptr, "font-needed", fontNeeded.get());
3809   }
3810 }
3811