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