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