tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxTextRun.cpp (153130B)


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