tor-browser

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

gfxFont.cpp (186474B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "gfxFont.h"
      7 
      8 #include "mozilla/DebugOnly.h"
      9 #include "mozilla/FontPropertyTypes.h"
     10 #include "mozilla/gfx/2D.h"
     11 #include "mozilla/intl/Segmenter.h"
     12 #include "mozilla/StaticPrefs_gfx.h"
     13 #include "mozilla/ScopeExit.h"
     14 #include "mozilla/SVGContextPaint.h"
     15 
     16 #include "mozilla/Logging.h"
     17 
     18 #include "nsITimer.h"
     19 
     20 #include "gfxGlyphExtents.h"
     21 #include "gfxPlatform.h"
     22 #include "gfxTextRun.h"
     23 #include "nsGkAtoms.h"
     24 
     25 #include "gfxTypes.h"
     26 #include "gfxContext.h"
     27 #include "gfxFontMissingGlyphs.h"
     28 #include "gfxGraphiteShaper.h"
     29 #include "gfxHarfBuzzShaper.h"
     30 #include "gfxUserFontSet.h"
     31 #include "nsCRT.h"
     32 #include "nsContentUtils.h"
     33 #include "nsSpecialCasingData.h"
     34 #include "nsTextRunTransformations.h"
     35 #include "nsUGenCategory.h"
     36 #include "nsUnicodeProperties.h"
     37 #include "nsStyleConsts.h"
     38 #include "mozilla/AppUnits.h"
     39 #include "mozilla/HashTable.h"
     40 #include "mozilla/Likely.h"
     41 #include "mozilla/MemoryReporting.h"
     42 #include "mozilla/Preferences.h"
     43 #include "mozilla/Services.h"
     44 #include "mozilla/glean/GfxMetrics.h"
     45 #include "gfxMathTable.h"
     46 #include "gfxSVGGlyphs.h"
     47 #include "gfx2DGlue.h"
     48 #include "TextDrawTarget.h"
     49 
     50 #include "ThebesRLBox.h"
     51 
     52 #include "GreekCasing.h"
     53 
     54 #include "cairo.h"
     55 #ifdef XP_WIN
     56 #  include "cairo-win32.h"
     57 #  include "gfxWindowsPlatform.h"
     58 #endif
     59 
     60 #include "harfbuzz/hb.h"
     61 #include "harfbuzz/hb-ot.h"
     62 
     63 #include <algorithm>
     64 #include <limits>
     65 #include <cmath>
     66 
     67 using namespace mozilla;
     68 using namespace mozilla::gfx;
     69 using namespace mozilla::unicode;
     70 using mozilla::services::GetObserverService;
     71 
     72 gfxFontCache* gfxFontCache::gGlobalCache = nullptr;
     73 
     74 #ifdef DEBUG_roc
     75 #  define DEBUG_TEXT_RUN_STORAGE_METRICS
     76 #endif
     77 
     78 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     79 uint32_t gTextRunStorageHighWaterMark = 0;
     80 uint32_t gTextRunStorage = 0;
     81 uint32_t gFontCount = 0;
     82 uint32_t gGlyphExtentsCount = 0;
     83 uint32_t gGlyphExtentsWidthsTotalSize = 0;
     84 uint32_t gGlyphExtentsSetupEagerSimple = 0;
     85 uint32_t gGlyphExtentsSetupEagerTight = 0;
     86 uint32_t gGlyphExtentsSetupLazyTight = 0;
     87 uint32_t gGlyphExtentsSetupFallBackToTight = 0;
     88 #endif
     89 
     90 #define LOG_FONTINIT(args) \
     91  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
     92 #define LOG_FONTINIT_ENABLED() \
     93  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
     94 
     95 /*
     96 * gfxFontCache - global cache of gfxFont instances.
     97 * Expires unused fonts after a short interval;
     98 * notifies fonts to age their cached shaped-word records;
     99 * observes memory-pressure notification and tells fonts to clear their
    100 * shaped-word caches to free up memory.
    101 */
    102 
    103 MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
    104 
    105 NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
    106 
    107 /*virtual*/
    108 gfxTextRunFactory::~gfxTextRunFactory() {
    109  // Should not be dropped by stylo
    110  MOZ_ASSERT(!Servo_IsWorkerThread());
    111 }
    112 
    113 NS_IMETHODIMP
    114 gfxFontCache::MemoryReporter::CollectReports(
    115    nsIHandleReportCallback* aHandleReport, nsISupports* aData,
    116    bool aAnonymize) {
    117  FontCacheSizes sizes;
    118 
    119  gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
    120                                                   &sizes);
    121 
    122  MOZ_COLLECT_REPORT("explicit/gfx/font-cache", KIND_HEAP, UNITS_BYTES,
    123                     sizes.mFontInstances,
    124                     "Memory used for active font instances.");
    125 
    126  MOZ_COLLECT_REPORT("explicit/gfx/font-shaped-words", KIND_HEAP, UNITS_BYTES,
    127                     sizes.mShapedWords,
    128                     "Memory used to cache shaped glyph data.");
    129 
    130  return NS_OK;
    131 }
    132 
    133 NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
    134 
    135 NS_IMETHODIMP
    136 gfxFontCache::Observer::Observe(nsISupports* aSubject, const char* aTopic,
    137                                const char16_t* someData) {
    138  if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
    139    gfxFontCache* fontCache = gfxFontCache::GetCache();
    140    if (fontCache) {
    141      fontCache->FlushShapedWordCaches();
    142    }
    143  } else {
    144    MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
    145  }
    146  return NS_OK;
    147 }
    148 
    149 nsresult gfxFontCache::Init() {
    150  NS_ASSERTION(!gGlobalCache, "Where did this come from?");
    151  gGlobalCache = new gfxFontCache(GetMainThreadSerialEventTarget());
    152  if (!gGlobalCache) {
    153    return NS_ERROR_OUT_OF_MEMORY;
    154  }
    155  RegisterStrongMemoryReporter(new MemoryReporter());
    156  return NS_OK;
    157 }
    158 
    159 void gfxFontCache::Shutdown() {
    160  delete gGlobalCache;
    161  gGlobalCache = nullptr;
    162 
    163 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
    164  printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
    165  printf("Total number of fonts=%d\n", gFontCount);
    166  printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
    167         int(gGlyphExtentsCount * sizeof(gfxGlyphExtents)));
    168  printf("Total glyph extents width-storage size allocated=%d\n",
    169         gGlyphExtentsWidthsTotalSize);
    170  printf("Number of simple glyph extents eagerly requested=%d\n",
    171         gGlyphExtentsSetupEagerSimple);
    172  printf("Number of tight glyph extents eagerly requested=%d\n",
    173         gGlyphExtentsSetupEagerTight);
    174  printf("Number of tight glyph extents lazily requested=%d\n",
    175         gGlyphExtentsSetupLazyTight);
    176  printf("Number of simple glyph extent setups that fell back to tight=%d\n",
    177         gGlyphExtentsSetupFallBackToTight);
    178 #endif
    179 }
    180 
    181 gfxFontCache::gfxFontCache(nsIEventTarget* aEventTarget)
    182    : ExpirationTrackerImpl<gfxFont, 3, Lock, AutoLock>(
    183          FONT_TIMEOUT_SECONDS * 1000, "gfxFontCache"_ns, aEventTarget) {
    184  nsCOMPtr<nsIObserverService> obs = GetObserverService();
    185  if (obs) {
    186    obs->AddObserver(new Observer, "memory-pressure", false);
    187  }
    188 
    189  nsIEventTarget* target = nullptr;
    190  if (XRE_IsContentProcess() && NS_IsMainThread()) {
    191    target = aEventTarget;
    192  }
    193 
    194  // Create the timer used to expire shaped-word records from each font's
    195  // cache after a short period of non-use. We have a single timer in
    196  // gfxFontCache that loops over all fonts known to the cache, to avoid
    197  // the overhead of individual timers in each font instance.
    198  // The timer will be started any time shaped word records are cached
    199  // (and pauses itself when all caches become empty).
    200  mWordCacheExpirationTimer = NS_NewTimer(target);
    201 }
    202 
    203 gfxFontCache::~gfxFontCache() {
    204  // Ensure the user font cache releases its references to font entries,
    205  // so they aren't kept alive after the font instances and font-list
    206  // have been shut down.
    207  gfxUserFontSet::UserFontCache::Shutdown();
    208 
    209  if (mWordCacheExpirationTimer) {
    210    mWordCacheExpirationTimer->Cancel();
    211    mWordCacheExpirationTimer = nullptr;
    212  }
    213 
    214  // Expire everything manually so we don't leak them.
    215  Flush();
    216 }
    217 
    218 bool gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const {
    219  const gfxCharacterMap* fontUnicodeRangeMap = mFont->GetUnicodeRangeMap();
    220  return aKey->mFontEntry == mFont->GetFontEntry() &&
    221         aKey->mStyle->Equals(*mFont->GetStyle()) &&
    222         ((!aKey->mUnicodeRangeMap && !fontUnicodeRangeMap) ||
    223          (aKey->mUnicodeRangeMap && fontUnicodeRangeMap &&
    224           aKey->mUnicodeRangeMap->Equals(fontUnicodeRangeMap)));
    225 }
    226 
    227 already_AddRefed<gfxFont> gfxFontCache::Lookup(
    228    const gfxFontEntry* aFontEntry, const gfxFontStyle* aStyle,
    229    const gfxCharacterMap* aUnicodeRangeMap) {
    230  MutexAutoLock lock(mMutex);
    231 
    232  Key key(aFontEntry, aStyle, aUnicodeRangeMap);
    233  HashEntry* entry = mFonts.GetEntry(key);
    234 
    235  glean::fontlist::font_cache_hit
    236      .EnumGet(
    237          static_cast<glean::fontlist::FontCacheHitLabel>(entry != nullptr))
    238      .Add();
    239 
    240  if (!entry) {
    241    return nullptr;
    242  }
    243 
    244  RefPtr<gfxFont> font = entry->mFont;
    245  if (font->GetExpirationState()->IsTracked()) {
    246    RemoveObjectLocked(font, lock);
    247  }
    248  return font.forget();
    249 }
    250 
    251 already_AddRefed<gfxFont> gfxFontCache::MaybeInsert(gfxFont* aFont) {
    252  MOZ_ASSERT(aFont);
    253  MutexAutoLock lock(mMutex);
    254 
    255  Key key(aFont->GetFontEntry(), aFont->GetStyle(),
    256          aFont->GetUnicodeRangeMap());
    257  HashEntry* entry = mFonts.PutEntry(key);
    258  if (!entry) {
    259    return do_AddRef(aFont);
    260  }
    261 
    262  // If it is null, then we are inserting a new entry. Otherwise we are
    263  // attempting to replace an existing font, probably due to a thread race, in
    264  // which case stick with the original font.
    265  if (!entry->mFont) {
    266    entry->mFont = aFont;
    267    // Assert that we can find the entry we just put in (this fails if the key
    268    // has a NaN float value in it, e.g. 'sizeAdjust').
    269    MOZ_ASSERT(entry == mFonts.GetEntry(key));
    270  } else {
    271    MOZ_ASSERT(entry->mFont != aFont);
    272    aFont->Destroy();
    273    if (entry->mFont->GetExpirationState()->IsTracked()) {
    274      RemoveObjectLocked(entry->mFont, lock);
    275    }
    276  }
    277 
    278  return do_AddRef(entry->mFont);
    279 }
    280 
    281 bool gfxFontCache::MaybeDestroy(gfxFont* aFont) {
    282  MOZ_ASSERT(aFont);
    283  MutexAutoLock lock(mMutex);
    284 
    285  // If the font has a non-zero refcount, then we must have lost the race with
    286  // gfxFontCache::Lookup and the same font was reacquired.
    287  if (aFont->GetRefCount() > 0) {
    288    return false;
    289  }
    290 
    291  Key key(aFont->GetFontEntry(), aFont->GetStyle(),
    292          aFont->GetUnicodeRangeMap());
    293  HashEntry* entry = mFonts.GetEntry(key);
    294  if (!entry || entry->mFont != aFont) {
    295    MOZ_ASSERT(!aFont->GetExpirationState()->IsTracked());
    296    return true;
    297  }
    298 
    299  // If the font is being tracked, we must have then also lost another race with
    300  // gfxFontCache::MaybeDestroy which re-added it to the tracker.
    301  if (aFont->GetExpirationState()->IsTracked()) {
    302    return false;
    303  }
    304 
    305  // Typically this won't fail, but it may during startup/shutdown if the timer
    306  // service is not available.
    307  nsresult rv = AddObjectLocked(aFont, lock);
    308  if (NS_SUCCEEDED(rv)) {
    309    return false;
    310  }
    311 
    312  mFonts.RemoveEntry(entry);
    313  return true;
    314 }
    315 
    316 void gfxFontCache::NotifyExpiredLocked(gfxFont* aFont, const AutoLock& aLock) {
    317  MOZ_ASSERT(aFont->GetRefCount() == 0);
    318 
    319  RemoveObjectLocked(aFont, aLock);
    320  mTrackerDiscard.AppendElement(aFont);
    321 
    322  Key key(aFont->GetFontEntry(), aFont->GetStyle(),
    323          aFont->GetUnicodeRangeMap());
    324  HashEntry* entry = mFonts.GetEntry(key);
    325  if (!entry || entry->mFont != aFont) {
    326    MOZ_ASSERT_UNREACHABLE("Invalid font?");
    327    return;
    328  }
    329 
    330  mFonts.RemoveEntry(entry);
    331 }
    332 
    333 void gfxFontCache::NotifyHandlerEnd() {
    334  nsTArray<gfxFont*> discard;
    335  {
    336    MutexAutoLock lock(mMutex);
    337    discard = std::move(mTrackerDiscard);
    338  }
    339  DestroyDiscard(discard);
    340 }
    341 
    342 void gfxFontCache::DestroyDiscard(nsTArray<gfxFont*>& aDiscard) {
    343  for (auto& font : aDiscard) {
    344    NS_ASSERTION(font->GetRefCount() == 0,
    345                 "Destroying with refs outside cache!");
    346    font->ClearCachedWords();
    347    font->Destroy();
    348  }
    349  aDiscard.Clear();
    350 }
    351 
    352 void gfxFontCache::Flush() {
    353  nsTArray<gfxFont*> discard;
    354  {
    355    MutexAutoLock lock(mMutex);
    356    discard.SetCapacity(mFonts.Count());
    357    for (auto iter = mFonts.Iter(); !iter.Done(); iter.Next()) {
    358      HashEntry* entry = static_cast<HashEntry*>(iter.Get());
    359      if (!entry || !entry->mFont) {
    360        MOZ_ASSERT_UNREACHABLE("Invalid font?");
    361        continue;
    362      }
    363 
    364      if (entry->mFont->GetRefCount() == 0) {
    365        // If we are not tracked, then we must have won the race with
    366        // gfxFont::MaybeDestroy and it is waiting on the mutex. To avoid a
    367        // double free, we let gfxFont::MaybeDestroy handle the freeing when it
    368        // acquires the mutex and discovers there is no matching entry in the
    369        // hashtable.
    370        if (entry->mFont->GetExpirationState()->IsTracked()) {
    371          RemoveObjectLocked(entry->mFont, lock);
    372          discard.AppendElement(entry->mFont);
    373        }
    374      } else {
    375        MOZ_ASSERT(!entry->mFont->GetExpirationState()->IsTracked());
    376      }
    377    }
    378    MOZ_ASSERT(IsEmptyLocked(lock),
    379               "Cache tracker still has fonts after flush!");
    380    mFonts.Clear();
    381  }
    382  DestroyDiscard(discard);
    383 }
    384 
    385 /*static*/
    386 void gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer,
    387                                                    void* aCache) {
    388  gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
    389  cache->AgeCachedWords();
    390 }
    391 
    392 void gfxFontCache::AgeCachedWords() {
    393  bool allEmpty = true;
    394  {
    395    MutexAutoLock lock(mMutex);
    396    for (const auto& entry : mFonts) {
    397      allEmpty = entry.mFont->AgeCachedWords() && allEmpty;
    398    }
    399  }
    400  if (allEmpty) {
    401    PauseWordCacheExpirationTimer();
    402  }
    403 }
    404 
    405 void gfxFontCache::FlushShapedWordCaches() {
    406  {
    407    MutexAutoLock lock(mMutex);
    408    for (const auto& entry : mFonts) {
    409      entry.mFont->ClearCachedWords();
    410    }
    411  }
    412  PauseWordCacheExpirationTimer();
    413 }
    414 
    415 void gfxFontCache::NotifyGlyphsChanged() {
    416  MutexAutoLock lock(mMutex);
    417  for (const auto& entry : mFonts) {
    418    entry.mFont->NotifyGlyphsChanged();
    419  }
    420 }
    421 
    422 void gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
    423                                          FontCacheSizes* aSizes) const {
    424  // TODO: add the overhead of the expiration tracker (generation arrays)
    425 
    426  MutexAutoLock lock(*const_cast<Mutex*>(&mMutex));
    427  aSizes->mFontInstances += mFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
    428  for (const auto& entry : mFonts) {
    429    entry.mFont->AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    430  }
    431 }
    432 
    433 void gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
    434                                          FontCacheSizes* aSizes) const {
    435  aSizes->mFontInstances += aMallocSizeOf(this);
    436  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    437 }
    438 
    439 #define MAX_SSXX_VALUE 99
    440 #define MAX_CVXX_VALUE 99
    441 
    442 static void LookupAlternateValues(const gfxFontFeatureValueSet& aFeatureLookup,
    443                                  const nsACString& aFamily,
    444                                  const StyleVariantAlternates& aAlternates,
    445                                  nsTArray<gfxFontFeature>& aFontFeatures) {
    446  using Tag = StyleVariantAlternates::Tag;
    447 
    448  // historical-forms gets handled in nsFont::AddFontFeaturesToStyle.
    449  if (aAlternates.IsHistoricalForms()) {
    450    return;
    451  }
    452 
    453  gfxFontFeature feature;
    454  if (aAlternates.IsCharacterVariant()) {
    455    for (auto& ident : aAlternates.AsCharacterVariant().AsSpan()) {
    456      Span<const uint32_t> values = aFeatureLookup.GetFontFeatureValuesFor(
    457          aFamily, NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT,
    458          ident.AsAtom());
    459      // nothing defined, skip
    460      if (values.IsEmpty()) {
    461        continue;
    462      }
    463      NS_ASSERTION(values.Length() <= 2,
    464                   "too many values allowed for character-variant");
    465      // character-variant(12 3) ==> 'cv12' = 3
    466      uint32_t nn = values[0];
    467      // ignore values greater than 99
    468      if (nn == 0 || nn > MAX_CVXX_VALUE) {
    469        continue;
    470      }
    471      feature.mValue = values.Length() > 1 ? values[1] : 1;
    472      feature.mTag = HB_TAG('c', 'v', ('0' + nn / 10), ('0' + nn % 10));
    473      aFontFeatures.AppendElement(feature);
    474    }
    475    return;
    476  }
    477 
    478  if (aAlternates.IsStyleset()) {
    479    for (auto& ident : aAlternates.AsStyleset().AsSpan()) {
    480      Span<const uint32_t> values = aFeatureLookup.GetFontFeatureValuesFor(
    481          aFamily, NS_FONT_VARIANT_ALTERNATES_STYLESET, ident.AsAtom());
    482 
    483      // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
    484      feature.mValue = 1;
    485      for (uint32_t nn : values) {
    486        if (nn == 0 || nn > MAX_SSXX_VALUE) {
    487          continue;
    488        }
    489        feature.mTag = HB_TAG('s', 's', ('0' + nn / 10), ('0' + nn % 10));
    490        aFontFeatures.AppendElement(feature);
    491      }
    492    }
    493    return;
    494  }
    495 
    496  uint32_t constant = 0;
    497  nsAtom* name = nullptr;
    498  switch (aAlternates.tag) {
    499    case Tag::Swash:
    500      constant = NS_FONT_VARIANT_ALTERNATES_SWASH;
    501      name = aAlternates.AsSwash().AsAtom();
    502      break;
    503    case Tag::Stylistic:
    504      constant = NS_FONT_VARIANT_ALTERNATES_STYLISTIC;
    505      name = aAlternates.AsStylistic().AsAtom();
    506      break;
    507    case Tag::Ornaments:
    508      constant = NS_FONT_VARIANT_ALTERNATES_ORNAMENTS;
    509      name = aAlternates.AsOrnaments().AsAtom();
    510      break;
    511    case Tag::Annotation:
    512      constant = NS_FONT_VARIANT_ALTERNATES_ANNOTATION;
    513      name = aAlternates.AsAnnotation().AsAtom();
    514      break;
    515    default:
    516      MOZ_ASSERT_UNREACHABLE("Unknown font-variant-alternates value!");
    517      return;
    518  }
    519 
    520  Span<const uint32_t> values =
    521      aFeatureLookup.GetFontFeatureValuesFor(aFamily, constant, name);
    522  if (values.IsEmpty()) {
    523    return;
    524  }
    525  MOZ_ASSERT(values.Length() == 1,
    526             "too many values for font-specific font-variant-alternates");
    527 
    528  feature.mValue = values[0];
    529  switch (aAlternates.tag) {
    530    case Tag::Swash:  // swsh, cswh
    531      feature.mTag = HB_TAG('s', 'w', 's', 'h');
    532      aFontFeatures.AppendElement(feature);
    533      feature.mTag = HB_TAG('c', 's', 'w', 'h');
    534      break;
    535    case Tag::Stylistic:  // salt
    536      feature.mTag = HB_TAG('s', 'a', 'l', 't');
    537      break;
    538    case Tag::Ornaments:  // ornm
    539      feature.mTag = HB_TAG('o', 'r', 'n', 'm');
    540      break;
    541    case Tag::Annotation:  // nalt
    542      feature.mTag = HB_TAG('n', 'a', 'l', 't');
    543      break;
    544    default:
    545      MOZ_ASSERT_UNREACHABLE("how?");
    546      return;
    547  }
    548  aFontFeatures.AppendElement(feature);
    549 }
    550 
    551 /* static */
    552 void gfxFontShaper::MergeFontFeatures(
    553    const gfxFontStyle* aStyle, const nsTArray<gfxFontFeature>& aFontFeatures,
    554    bool aDisableLigatures, const nsACString& aFamilyName, bool aAddSmallCaps,
    555    void (*aHandleFeature)(uint32_t, uint32_t, void*),
    556    void* aHandleFeatureData) {
    557  const nsTArray<gfxFontFeature>& styleRuleFeatures = aStyle->featureSettings;
    558 
    559  // Bail immediately if nothing to do, which is the common case.
    560  if (styleRuleFeatures.IsEmpty() && aFontFeatures.IsEmpty() &&
    561      !aDisableLigatures &&
    562      aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL &&
    563      aStyle->variantSubSuper == NS_FONT_VARIANT_POSITION_NORMAL &&
    564      aStyle->variantAlternates.IsEmpty()) {
    565    return;
    566  }
    567 
    568  AutoTArray<gfxFontFeature, 32> mergedFeatures;
    569 
    570  struct FeatureTagCmp {
    571    bool Equals(const gfxFontFeature& a, const gfxFontFeature& b) const {
    572      return a.mTag == b.mTag;
    573    }
    574    bool LessThan(const gfxFontFeature& a, const gfxFontFeature& b) const {
    575      return a.mTag < b.mTag;
    576    }
    577  } cmp;
    578 
    579  auto addOrReplace = [&](const gfxFontFeature& aFeature) {
    580    auto index = mergedFeatures.BinaryIndexOf(aFeature, cmp);
    581    if (index == nsTArray<gfxFontFeature>::NoIndex) {
    582      mergedFeatures.InsertElementSorted(aFeature, cmp);
    583    } else {
    584      mergedFeatures[index].mValue = aFeature.mValue;
    585    }
    586  };
    587 
    588  // add feature values from font
    589  for (const gfxFontFeature& feature : aFontFeatures) {
    590    addOrReplace(feature);
    591  }
    592 
    593  // font-variant-caps - handled here due to the need for fallback handling
    594  // petite caps cases can fallback to appropriate smallcaps
    595  uint32_t variantCaps = aStyle->variantCaps;
    596  switch (variantCaps) {
    597    case NS_FONT_VARIANT_CAPS_NORMAL:
    598      break;
    599 
    600    case NS_FONT_VARIANT_CAPS_ALLSMALL:
    601      addOrReplace(gfxFontFeature{HB_TAG('c', '2', 's', 'c'), 1});
    602      // fall through to the small-caps case
    603      [[fallthrough]];
    604 
    605    case NS_FONT_VARIANT_CAPS_SMALLCAPS:
    606      addOrReplace(gfxFontFeature{HB_TAG('s', 'm', 'c', 'p'), 1});
    607      break;
    608 
    609    case NS_FONT_VARIANT_CAPS_ALLPETITE:
    610      addOrReplace(gfxFontFeature{aAddSmallCaps ? HB_TAG('c', '2', 's', 'c')
    611                                                : HB_TAG('c', '2', 'p', 'c'),
    612                                  1});
    613      // fall through to the petite-caps case
    614      [[fallthrough]];
    615 
    616    case NS_FONT_VARIANT_CAPS_PETITECAPS:
    617      addOrReplace(gfxFontFeature{aAddSmallCaps ? HB_TAG('s', 'm', 'c', 'p')
    618                                                : HB_TAG('p', 'c', 'a', 'p'),
    619                                  1});
    620      break;
    621 
    622    case NS_FONT_VARIANT_CAPS_TITLING:
    623      addOrReplace(gfxFontFeature{HB_TAG('t', 'i', 't', 'l'), 1});
    624      break;
    625 
    626    case NS_FONT_VARIANT_CAPS_UNICASE:
    627      addOrReplace(gfxFontFeature{HB_TAG('u', 'n', 'i', 'c'), 1});
    628      break;
    629 
    630    default:
    631      MOZ_ASSERT_UNREACHABLE("Unexpected variantCaps");
    632      break;
    633  }
    634 
    635  // font-variant-position - handled here due to the need for fallback
    636  switch (aStyle->variantSubSuper) {
    637    case NS_FONT_VARIANT_POSITION_NORMAL:
    638      break;
    639    case NS_FONT_VARIANT_POSITION_SUPER:
    640      addOrReplace(gfxFontFeature{HB_TAG('s', 'u', 'p', 's'), 1});
    641      break;
    642    case NS_FONT_VARIANT_POSITION_SUB:
    643      addOrReplace(gfxFontFeature{HB_TAG('s', 'u', 'b', 's'), 1});
    644      break;
    645    default:
    646      MOZ_ASSERT_UNREACHABLE("Unexpected variantSubSuper");
    647      break;
    648  }
    649 
    650  // add font-specific feature values from style rules
    651  if (aStyle->featureValueLookup && !aStyle->variantAlternates.IsEmpty()) {
    652    AutoTArray<gfxFontFeature, 4> featureList;
    653 
    654    // insert list of alternate feature settings
    655    for (auto& alternate : aStyle->variantAlternates.AsSpan()) {
    656      LookupAlternateValues(*aStyle->featureValueLookup, aFamilyName, alternate,
    657                            featureList);
    658    }
    659 
    660    for (const gfxFontFeature& feature : featureList) {
    661      addOrReplace(gfxFontFeature{feature.mTag, feature.mValue});
    662    }
    663  }
    664 
    665  auto disableOptionalLigatures = [&]() -> void {
    666    addOrReplace(gfxFontFeature{HB_TAG('l', 'i', 'g', 'a'), 0});
    667    addOrReplace(gfxFontFeature{HB_TAG('c', 'l', 'i', 'g'), 0});
    668    addOrReplace(gfxFontFeature{HB_TAG('d', 'l', 'i', 'g'), 0});
    669    addOrReplace(gfxFontFeature{HB_TAG('h', 'l', 'i', 'g'), 0});
    670  };
    671 
    672  // Add features that are already resolved to tags & values in the style.
    673  if (styleRuleFeatures.IsEmpty()) {
    674    // Disable optional ligatures if non-zero letter-spacing is in effect.
    675    if (aDisableLigatures) {
    676      disableOptionalLigatures();
    677    }
    678  } else {
    679    for (const gfxFontFeature& feature : styleRuleFeatures) {
    680      // A dummy feature (0,0) is used as a sentinel to separate features
    681      // originating from font-variant-* or other high-level properties from
    682      // those directly specified as font-feature-settings. The high-level
    683      // features may be overridden by aDisableLigatures, while low-level
    684      // features specified directly as tags will come last and therefore
    685      // take precedence over everything else.
    686      if (feature.mTag) {
    687        addOrReplace(gfxFontFeature{feature.mTag, feature.mValue});
    688      } else if (aDisableLigatures) {
    689        // Handle ligature-disabling setting at the boundary between high-
    690        // and low-level features.
    691        disableOptionalLigatures();
    692      }
    693    }
    694  }
    695 
    696  for (const auto& f : mergedFeatures) {
    697    aHandleFeature(f.mTag, f.mValue, aHandleFeatureData);
    698  }
    699 }
    700 
    701 void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
    702                                           const char16_t* aString,
    703                                           uint32_t aLength) {
    704  if (aLength == 0) {
    705    return;
    706  }
    707 
    708  CompressedGlyph* const glyphs = GetCharacterGlyphs() + aOffset;
    709  CompressedGlyph extendCluster = CompressedGlyph::MakeComplex(false, true);
    710 
    711  // GraphemeClusterBreakIteratorUtf16 won't be able to tell us if the string
    712  // _begins_ with a cluster-extender, so we handle that here
    713  uint32_t ch = aString[0];
    714  if (aLength > 1 && NS_IS_SURROGATE_PAIR(ch, aString[1])) {
    715    ch = SURROGATE_TO_UCS4(ch, aString[1]);
    716  }
    717  if (IsClusterExtender(ch)) {
    718    glyphs[0] = extendCluster;
    719  }
    720 
    721  intl::GraphemeClusterBreakIteratorUtf16 iter(
    722      Span<const char16_t>(aString, aLength));
    723  uint32_t pos = 0;
    724 
    725  const char16_t kIdeographicSpace = 0x3000;
    726  // Special case for Bengali: although Virama normally clusters with the
    727  // preceding letter, we *also* want to cluster it with a following Ya
    728  // so that when the Virama+Ya form ya-phala, this is not separated from the
    729  // preceding letter by any letter-spacing or justification.
    730  const char16_t kBengaliVirama = 0x09CD;
    731  const char16_t kBengaliYa = 0x09AF;
    732  bool prevWasHyphen = false;
    733  while (pos < aLength) {
    734    const char16_t ch = aString[pos];
    735    if (prevWasHyphen) {
    736      if (nsContentUtils::IsAlphanumeric(ch)) {
    737        glyphs[pos].SetCanBreakBefore(
    738            CompressedGlyph::FLAG_BREAK_TYPE_EMERGENCY_WRAP);
    739      }
    740      prevWasHyphen = false;
    741    }
    742    if (ch == char16_t(' ') || ch == kIdeographicSpace) {
    743      glyphs[pos].SetIsSpace();
    744    } else if (nsContentUtils::IsHyphen(ch) && pos &&
    745               nsContentUtils::IsAlphanumeric(aString[pos - 1])) {
    746      prevWasHyphen = true;
    747    } else if (ch == kBengaliYa) {
    748      // Unless we're at the start, check for a preceding virama.
    749      if (pos > 0 && aString[pos - 1] == kBengaliVirama) {
    750        glyphs[pos] = extendCluster;
    751      }
    752    }
    753    // advance iter to the next cluster-start (or end of text)
    754    const uint32_t nextPos = *iter.Next();
    755    // step past the first char of the cluster
    756    ++pos;
    757    // mark all the rest as cluster-continuations
    758    for (; pos < nextPos; ++pos) {
    759      glyphs[pos] = extendCluster;
    760    }
    761  }
    762 }
    763 
    764 void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
    765                                           const uint8_t* aString,
    766                                           uint32_t aLength) {
    767  CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
    768  uint32_t pos = 0;
    769  bool prevWasHyphen = false;
    770  while (pos < aLength) {
    771    uint8_t ch = aString[pos];
    772    if (prevWasHyphen) {
    773      if (nsContentUtils::IsAlphanumeric(ch)) {
    774        glyphs->SetCanBreakBefore(
    775            CompressedGlyph::FLAG_BREAK_TYPE_EMERGENCY_WRAP);
    776      }
    777      prevWasHyphen = false;
    778    }
    779    if (ch == uint8_t(' ')) {
    780      glyphs->SetIsSpace();
    781    } else if (ch == uint8_t('-') && pos &&
    782               nsContentUtils::IsAlphanumeric(aString[pos - 1])) {
    783      prevWasHyphen = true;
    784    }
    785    ++pos;
    786    ++glyphs;
    787  }
    788 }
    789 
    790 gfxShapedText::DetailedGlyph* gfxShapedText::AllocateDetailedGlyphs(
    791    uint32_t aIndex, uint32_t aCount) {
    792  NS_ASSERTION(aIndex < GetLength(), "Index out of range");
    793 
    794  if (!mDetailedGlyphs) {
    795    mDetailedGlyphs = MakeUnique<DetailedGlyphStore>();
    796  }
    797 
    798  return mDetailedGlyphs->Allocate(aIndex, aCount);
    799 }
    800 
    801 void gfxShapedText::SetDetailedGlyphs(uint32_t aIndex, uint32_t aGlyphCount,
    802                                      const DetailedGlyph* aGlyphs) {
    803  CompressedGlyph& g = GetCharacterGlyphs()[aIndex];
    804 
    805  MOZ_ASSERT(aIndex > 0 || g.IsLigatureGroupStart(),
    806             "First character can't be a ligature continuation!");
    807 
    808  if (aGlyphCount > 0) {
    809    DetailedGlyph* details = AllocateDetailedGlyphs(aIndex, aGlyphCount);
    810    memcpy(details, aGlyphs, sizeof(DetailedGlyph) * aGlyphCount);
    811  }
    812 
    813  g.SetGlyphCount(aGlyphCount);
    814 }
    815 
    816 #define ZWNJ 0x200C
    817 #define ZWJ 0x200D
    818 static inline bool IsIgnorable(uint32_t aChar) {
    819  return (IsDefaultIgnorable(aChar)) || aChar == ZWNJ || aChar == ZWJ;
    820 }
    821 
    822 void gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar,
    823                                    gfxFont* aFont) {
    824  CompressedGlyph& g = GetCharacterGlyphs()[aIndex];
    825  uint8_t category = GetGeneralCategory(aChar);
    826  if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
    827      category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
    828    g.SetComplex(false, true);
    829  }
    830 
    831  // Leaving advance as zero will prevent drawing the hexbox for ignorables.
    832  int32_t advance = 0;
    833  if (!IsIgnorable(aChar)) {
    834    gfxFloat width =
    835        std::max(aFont->GetMetrics(nsFontMetrics::eHorizontal).aveCharWidth,
    836                 gfxFloat(gfxFontMissingGlyphs::GetDesiredMinWidth(
    837                     aChar, mAppUnitsPerDevUnit)));
    838    advance = int32_t(width * mAppUnitsPerDevUnit);
    839  }
    840  DetailedGlyph detail = {aChar, advance, gfx::Point()};
    841  SetDetailedGlyphs(aIndex, 1, &detail);
    842  g.SetMissing();
    843 }
    844 
    845 bool gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh) {
    846  if (IsIgnorable(aCh)) {
    847    // There are a few default-ignorables of Letter category (currently,
    848    // just the Hangul filler characters) that we'd better not discard
    849    // if they're followed by additional characters in the same cluster.
    850    // Some fonts use them to carry the width of a whole cluster of
    851    // combining jamos; see bug 1238243.
    852    auto* charGlyphs = GetCharacterGlyphs();
    853    if (GetGenCategory(aCh) == nsUGenCategory::kLetter &&
    854        aIndex + 1 < GetLength() && !charGlyphs[aIndex + 1].IsClusterStart()) {
    855      return false;
    856    }
    857    // A compressedGlyph that is set to MISSING but has no DetailedGlyphs list
    858    // will be zero-width/invisible, which is what we want here.
    859    CompressedGlyph& g = charGlyphs[aIndex];
    860    g.SetComplex(g.IsClusterStart(), g.IsLigatureGroupStart()).SetMissing();
    861    return true;
    862  }
    863  return false;
    864 }
    865 
    866 void gfxShapedText::ApplyTrackingToClusters(gfxFloat aTrackingAdjustment,
    867                                            uint32_t aOffset,
    868                                            uint32_t aLength) {
    869  int32_t appUnitAdjustment =
    870      NS_round(aTrackingAdjustment * gfxFloat(mAppUnitsPerDevUnit));
    871  CompressedGlyph* charGlyphs = GetCharacterGlyphs();
    872  for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
    873    CompressedGlyph* glyphData = charGlyphs + i;
    874    if (glyphData->IsSimpleGlyph()) {
    875      // simple glyphs ==> just add the advance
    876      int32_t advance = glyphData->GetSimpleAdvance();
    877      if (advance > 0) {
    878        advance = std::max(0, advance + appUnitAdjustment);
    879        if (CompressedGlyph::IsSimpleAdvance(advance)) {
    880          glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
    881        } else {
    882          // rare case, tested by making this the default
    883          uint32_t glyphIndex = glyphData->GetSimpleGlyph();
    884          // convert the simple CompressedGlyph to an empty complex record
    885          glyphData->SetComplex(true, true);
    886          // then set its details (glyph ID with its new advance)
    887          DetailedGlyph detail = {glyphIndex, advance, gfx::Point()};
    888          SetDetailedGlyphs(i, 1, &detail);
    889        }
    890      }
    891    } else {
    892      // complex glyphs ==> add offset at cluster/ligature boundaries
    893      uint32_t detailedLength = glyphData->GetGlyphCount();
    894      if (detailedLength) {
    895        DetailedGlyph* details = GetDetailedGlyphs(i);
    896        if (!details) {
    897          continue;
    898        }
    899        auto& advance = IsRightToLeft() ? details[0].mAdvance
    900                                        : details[detailedLength - 1].mAdvance;
    901        if (advance > 0) {
    902          advance = std::max(0, advance + appUnitAdjustment);
    903        }
    904      }
    905    }
    906  }
    907 }
    908 
    909 size_t gfxShapedWord::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    910  size_t total = aMallocSizeOf(this);
    911  if (mDetailedGlyphs) {
    912    total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
    913  }
    914  return total;
    915 }
    916 
    917 float gfxFont::AngleForSyntheticOblique() const {
    918  // First check conditions that mean no synthetic slant should be used:
    919  if (mStyle.style == FontSlantStyle::NORMAL) {
    920    return 0.0f;  // Requested style is 'normal'.
    921  }
    922  if (mStyle.synthesisStyle == StyleFontSynthesisStyle::None) {
    923    return 0.0f;  // Synthetic obliquing is disabled.
    924  }
    925  if (!mFontEntry->MayUseSyntheticSlant()) {
    926    return 0.0f;  // The resource supports "real" slant, so don't synthesize.
    927  }
    928 
    929  // If style calls for italic, and face doesn't support it, use default
    930  // oblique angle as a simulation, but only if synthesis setting allows it.
    931  if (mStyle.style.IsItalic()) {
    932    return mFontEntry->SupportsItalic() ? 0.0f
    933           : mStyle.synthesisStyle == StyleFontSynthesisStyle::Auto
    934               ? FontSlantStyle::DEFAULT_OBLIQUE_DEGREES
    935               : 0.0f;
    936  }
    937 
    938  // OK, we're going to use synthetic oblique: return the requested angle.
    939  return mStyle.style.ObliqueAngle();
    940 }
    941 
    942 float gfxFont::SkewForSyntheticOblique() const {
    943  // Precomputed value of tan(kDefaultAngle), the default italic/oblique slant;
    944  // avoids calling tan() at runtime except for custom oblique values.
    945  static const float kTanDefaultAngle =
    946      tan(FontSlantStyle::DEFAULT_OBLIQUE_DEGREES * (M_PI / 180.0));
    947 
    948  float angle = AngleForSyntheticOblique();
    949  if (angle == 0.0f) {
    950    return 0.0f;
    951  } else if (angle == FontSlantStyle::DEFAULT_OBLIQUE_DEGREES) {
    952    return kTanDefaultAngle;
    953  } else {
    954    return tan(angle * (M_PI / 180.0));
    955  }
    956 }
    957 
    958 void gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther,
    959                                      bool aOtherIsOnLeft) {
    960  mAscent = std::max(mAscent, aOther.mAscent);
    961  mDescent = std::max(mDescent, aOther.mDescent);
    962  if (aOtherIsOnLeft) {
    963    mBoundingBox = (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0))
    964                       .Union(aOther.mBoundingBox);
    965  } else {
    966    mBoundingBox =
    967        mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
    968  }
    969  mAdvanceWidth += aOther.mAdvanceWidth;
    970 }
    971 
    972 gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
    973                 gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle,
    974                 AntialiasOption anAAOption)
    975    : mFontEntry(aFontEntry),
    976      mLock("gfxFont lock"),
    977      mUnscaledFont(aUnscaledFont),
    978      mStyle(*aFontStyle),
    979      mAdjustedSize(-1.0),       // negative to indicate "not yet initialized"
    980      mFUnitsConvFactor(-1.0f),  // negative to indicate "not yet initialized"
    981      mAntialiasOption(anAAOption),
    982      mIsValid(true),
    983      mApplySyntheticBold(false),
    984      mKerningEnabled(false),
    985      mMathInitialized(false) {
    986 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
    987  ++gFontCount;
    988 #endif
    989 
    990  if (MOZ_UNLIKELY(StaticPrefs::gfx_text_disable_aa_AtStartup())) {
    991    mAntialiasOption = kAntialiasNone;
    992  }
    993 
    994  // Turn off AA for Ahem for testing purposes when requested.
    995  if (MOZ_UNLIKELY(StaticPrefs::gfx_font_rendering_ahem_antialias_none() &&
    996                   mFontEntry->FamilyName().EqualsLiteral("Ahem"))) {
    997    mAntialiasOption = kAntialiasNone;
    998  }
    999 
   1000  mKerningSet = HasFeatureSet(HB_TAG('k', 'e', 'r', 'n'), mKerningEnabled);
   1001 
   1002  // Ensure the gfxFontEntry's unitsPerEm and extents fields are initialized,
   1003  // so that GetFontExtents can use them without risk of races.
   1004  (void)mFontEntry->UnitsPerEm();
   1005 }
   1006 
   1007 gfxFont::~gfxFont() {
   1008  mFontEntry->NotifyFontDestroyed(this);
   1009 
   1010  // Delete objects owned through atomic pointers. (Some of these may be null,
   1011  // but that's OK.)
   1012  delete mVerticalMetrics.exchange(nullptr);
   1013  delete mHarfBuzzShaper.exchange(nullptr);
   1014  delete mGraphiteShaper.exchange(nullptr);
   1015  delete mMathTable.exchange(nullptr);
   1016  delete mNonAAFont.exchange(nullptr);
   1017 
   1018  if (auto* scaledFont = mAzureScaledFont.exchange(nullptr)) {
   1019    scaledFont->Release();
   1020  }
   1021 
   1022  if (mGlyphChangeObservers) {
   1023    for (const auto& key : *mGlyphChangeObservers) {
   1024      key->ForgetFont();
   1025    }
   1026  }
   1027 }
   1028 
   1029 // Work out whether cairo will snap inter-glyph spacing to pixels.
   1030 //
   1031 // Layout does not align text to pixel boundaries, so, with font drawing
   1032 // backends that snap glyph positions to pixels, it is important that
   1033 // inter-glyph spacing within words is always an integer number of pixels.
   1034 // This ensures that the drawing backend snaps all of the word's glyphs in the
   1035 // same direction and so inter-glyph spacing remains the same.
   1036 //
   1037 gfxFont::RoundingFlags gfxFont::GetRoundOffsetsToPixels(
   1038    DrawTarget* aDrawTarget) {
   1039  // Could do something fancy here for ScaleFactors of
   1040  // AxisAlignedTransforms, but we leave things simple.
   1041  // Not much point rounding if a matrix will mess things up anyway.
   1042  // Also check if the font already knows hint metrics is off...
   1043  if (aDrawTarget->GetTransform().HasNonTranslation() || !ShouldHintMetrics()) {
   1044    return RoundingFlags(0);
   1045  }
   1046 
   1047  cairo_t* cr = static_cast<cairo_t*>(
   1048      aDrawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
   1049  if (cr) {
   1050    cairo_surface_t* target = cairo_get_target(cr);
   1051 
   1052    // Check whether the cairo surface's font options hint metrics.
   1053    cairo_font_options_t* fontOptions = cairo_font_options_create();
   1054    cairo_surface_get_font_options(target, fontOptions);
   1055    cairo_hint_metrics_t hintMetrics =
   1056        cairo_font_options_get_hint_metrics(fontOptions);
   1057    cairo_font_options_destroy(fontOptions);
   1058 
   1059    switch (hintMetrics) {
   1060      case CAIRO_HINT_METRICS_OFF:
   1061        return RoundingFlags(0);
   1062      case CAIRO_HINT_METRICS_ON:
   1063        return RoundingFlags::kRoundX | RoundingFlags::kRoundY;
   1064      default:
   1065        break;
   1066    }
   1067  }
   1068 
   1069  if (ShouldRoundXOffset(cr)) {
   1070    return RoundingFlags::kRoundX | RoundingFlags::kRoundY;
   1071  } else {
   1072    return RoundingFlags::kRoundY;
   1073  }
   1074 }
   1075 
   1076 gfxHarfBuzzShaper* gfxFont::GetHarfBuzzShaper() {
   1077  if (!mHarfBuzzShaper) {
   1078    auto* shaper = new gfxHarfBuzzShaper(this);
   1079    shaper->Initialize();
   1080    if (!mHarfBuzzShaper.compareExchange(nullptr, shaper)) {
   1081      delete shaper;
   1082    }
   1083  }
   1084  gfxHarfBuzzShaper* shaper = mHarfBuzzShaper;
   1085  return shaper->IsInitialized() ? shaper : nullptr;
   1086 }
   1087 
   1088 gfxFloat gfxFont::GetGlyphAdvance(uint16_t aGID, bool aVertical) {
   1089  if (!aVertical && ProvidesGlyphWidths()) {
   1090    return GetGlyphWidth(aGID) / 65536.0;
   1091  }
   1092  if (mFUnitsConvFactor < 0.0f) {
   1093    // Metrics haven't been initialized; lock while we do that.
   1094    AutoWriteLock lock(mLock);
   1095    if (mFUnitsConvFactor < 0.0f) {
   1096      GetMetrics(nsFontMetrics::eHorizontal);
   1097    }
   1098  }
   1099  NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
   1100               "missing font unit conversion factor");
   1101  if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
   1102    if (aVertical) {
   1103      // Note that GetGlyphVAdvance may return -1 to indicate it was unable
   1104      // to retrieve vertical metrics; in that case we fall back to the
   1105      // aveCharWidth value as a default advance.
   1106      int32_t advance = shaper->GetGlyphVAdvance(aGID);
   1107      if (advance < 0) {
   1108        return GetMetrics(nsFontMetrics::eVertical).aveCharWidth;
   1109      }
   1110      return advance / 65536.0;
   1111    }
   1112    return shaper->GetGlyphHAdvance(aGID) / 65536.0;
   1113  }
   1114  return 0.0;
   1115 }
   1116 
   1117 gfxFloat gfxFont::GetCharAdvance(uint32_t aUnicode, bool aVertical) {
   1118  uint32_t gid = 0;
   1119  if (ProvidesGetGlyph()) {
   1120    gid = GetGlyph(aUnicode, 0);
   1121  } else {
   1122    if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
   1123      gid = shaper->GetNominalGlyph(aUnicode);
   1124    }
   1125  }
   1126  if (!gid) {
   1127    return -1.0;
   1128  }
   1129  return GetGlyphAdvance(gid, aVertical);
   1130 }
   1131 
   1132 static void CollectLookupsByFeature(hb_face_t* aFace, hb_tag_t aTableTag,
   1133                                    uint32_t aFeatureIndex,
   1134                                    hb_set_t* aLookups) {
   1135  uint32_t lookups[32];
   1136  uint32_t i, len, offset;
   1137 
   1138  offset = 0;
   1139  do {
   1140    len = std::size(lookups);
   1141    hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex, offset,
   1142                                     &len, lookups);
   1143    for (i = 0; i < len; i++) {
   1144      hb_set_add(aLookups, lookups[i]);
   1145    }
   1146    offset += len;
   1147  } while (len == std::size(lookups));
   1148 }
   1149 
   1150 static void CollectLookupsByLanguage(
   1151    hb_face_t* aFace, hb_tag_t aTableTag,
   1152    const nsTHashSet<uint32_t>& aSpecificFeatures, hb_set_t* aOtherLookups,
   1153    hb_set_t* aSpecificFeatureLookups, uint32_t aScriptIndex,
   1154    uint32_t aLangIndex) {
   1155  uint32_t reqFeatureIndex;
   1156  if (hb_ot_layout_language_get_required_feature_index(
   1157          aFace, aTableTag, aScriptIndex, aLangIndex, &reqFeatureIndex)) {
   1158    CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex, aOtherLookups);
   1159  }
   1160 
   1161  uint32_t featureIndexes[32];
   1162  uint32_t i, len, offset;
   1163 
   1164  offset = 0;
   1165  do {
   1166    len = std::size(featureIndexes);
   1167    hb_ot_layout_language_get_feature_indexes(aFace, aTableTag, aScriptIndex,
   1168                                              aLangIndex, offset, &len,
   1169                                              featureIndexes);
   1170 
   1171    for (i = 0; i < len; i++) {
   1172      uint32_t featureIndex = featureIndexes[i];
   1173 
   1174      // get the feature tag
   1175      hb_tag_t featureTag;
   1176      uint32_t tagLen = 1;
   1177      hb_ot_layout_language_get_feature_tags(aFace, aTableTag, aScriptIndex,
   1178                                             aLangIndex, offset + i, &tagLen,
   1179                                             &featureTag);
   1180 
   1181      // collect lookups
   1182      hb_set_t* lookups = aSpecificFeatures.Contains(featureTag)
   1183                              ? aSpecificFeatureLookups
   1184                              : aOtherLookups;
   1185      CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
   1186    }
   1187    offset += len;
   1188  } while (len == std::size(featureIndexes));
   1189 }
   1190 
   1191 static bool HasLookupRuleWithGlyphByScript(
   1192    hb_face_t* aFace, hb_tag_t aTableTag, hb_tag_t aScriptTag,
   1193    uint32_t aScriptIndex, uint16_t aGlyph,
   1194    const nsTHashSet<uint32_t>& aDefaultFeatures,
   1195    bool& aHasDefaultFeatureWithGlyph) {
   1196  uint32_t numLangs, lang;
   1197  hb_set_t* defaultFeatureLookups = hb_set_create();
   1198  hb_set_t* nonDefaultFeatureLookups = hb_set_create();
   1199 
   1200  // default lang
   1201  CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
   1202                           nonDefaultFeatureLookups, defaultFeatureLookups,
   1203                           aScriptIndex, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
   1204 
   1205  // iterate over langs
   1206  numLangs = hb_ot_layout_script_get_language_tags(
   1207      aFace, aTableTag, aScriptIndex, 0, nullptr, nullptr);
   1208  for (lang = 0; lang < numLangs; lang++) {
   1209    CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
   1210                             nonDefaultFeatureLookups, defaultFeatureLookups,
   1211                             aScriptIndex, lang);
   1212  }
   1213 
   1214  // look for the glyph among default feature lookups
   1215  aHasDefaultFeatureWithGlyph = false;
   1216  hb_set_t* glyphs = hb_set_create();
   1217  hb_codepoint_t index = -1;
   1218  while (hb_set_next(defaultFeatureLookups, &index)) {
   1219    hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
   1220                                       glyphs, nullptr);
   1221    if (hb_set_has(glyphs, aGlyph)) {
   1222      aHasDefaultFeatureWithGlyph = true;
   1223      break;
   1224    }
   1225  }
   1226 
   1227  // look for the glyph among non-default feature lookups
   1228  // if no default feature lookups contained spaces
   1229  bool hasNonDefaultFeatureWithGlyph = false;
   1230  if (!aHasDefaultFeatureWithGlyph) {
   1231    hb_set_clear(glyphs);
   1232    index = -1;
   1233    while (hb_set_next(nonDefaultFeatureLookups, &index)) {
   1234      hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs,
   1235                                         glyphs, glyphs, nullptr);
   1236      if (hb_set_has(glyphs, aGlyph)) {
   1237        hasNonDefaultFeatureWithGlyph = true;
   1238        break;
   1239      }
   1240    }
   1241  }
   1242 
   1243  hb_set_destroy(glyphs);
   1244  hb_set_destroy(defaultFeatureLookups);
   1245  hb_set_destroy(nonDefaultFeatureLookups);
   1246 
   1247  return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
   1248 }
   1249 
   1250 static void HasLookupRuleWithGlyph(hb_face_t* aFace, hb_tag_t aTableTag,
   1251                                   bool& aHasGlyph, hb_tag_t aSpecificFeature,
   1252                                   bool& aHasGlyphSpecific, uint16_t aGlyph) {
   1253  // iterate over the scripts in the font
   1254  uint32_t numScripts, numLangs, script, lang;
   1255  hb_set_t* otherLookups = hb_set_create();
   1256  hb_set_t* specificFeatureLookups = hb_set_create();
   1257  nsTHashSet<uint32_t> specificFeature(1);
   1258 
   1259  specificFeature.Insert(aSpecificFeature);
   1260 
   1261  numScripts =
   1262      hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0, nullptr, nullptr);
   1263 
   1264  for (script = 0; script < numScripts; script++) {
   1265    // default lang
   1266    CollectLookupsByLanguage(aFace, aTableTag, specificFeature, otherLookups,
   1267                             specificFeatureLookups, script,
   1268                             HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
   1269 
   1270    // iterate over langs
   1271    numLangs = hb_ot_layout_script_get_language_tags(
   1272        aFace, HB_OT_TAG_GPOS, script, 0, nullptr, nullptr);
   1273    for (lang = 0; lang < numLangs; lang++) {
   1274      CollectLookupsByLanguage(aFace, aTableTag, specificFeature, otherLookups,
   1275                               specificFeatureLookups, script, lang);
   1276    }
   1277  }
   1278 
   1279  // look for the glyph among non-specific feature lookups
   1280  hb_set_t* glyphs = hb_set_create();
   1281  hb_codepoint_t index = -1;
   1282  while (hb_set_next(otherLookups, &index)) {
   1283    hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
   1284                                       glyphs, nullptr);
   1285    if (hb_set_has(glyphs, aGlyph)) {
   1286      aHasGlyph = true;
   1287      break;
   1288    }
   1289  }
   1290 
   1291  // look for the glyph among specific feature lookups
   1292  hb_set_clear(glyphs);
   1293  index = -1;
   1294  while (hb_set_next(specificFeatureLookups, &index)) {
   1295    hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
   1296                                       glyphs, nullptr);
   1297    if (hb_set_has(glyphs, aGlyph)) {
   1298      aHasGlyphSpecific = true;
   1299      break;
   1300    }
   1301  }
   1302 
   1303  hb_set_destroy(glyphs);
   1304  hb_set_destroy(specificFeatureLookups);
   1305  hb_set_destroy(otherLookups);
   1306 }
   1307 
   1308 Atomic<nsTHashMap<nsUint32HashKey, intl::Script>*> gfxFont::sScriptTagToCode;
   1309 Atomic<nsTHashSet<uint32_t>*> gfxFont::sDefaultFeatures;
   1310 
   1311 static inline bool HasSubstitution(uint32_t* aBitVector, intl::Script aScript) {
   1312  return (aBitVector[static_cast<uint32_t>(aScript) >> 5] &
   1313          (1 << (static_cast<uint32_t>(aScript) & 0x1f))) != 0;
   1314 }
   1315 
   1316 // union of all default substitution features across scripts
   1317 static const hb_tag_t defaultFeatures[] = {
   1318    HB_TAG('a', 'b', 'v', 'f'), HB_TAG('a', 'b', 'v', 's'),
   1319    HB_TAG('a', 'k', 'h', 'n'), HB_TAG('b', 'l', 'w', 'f'),
   1320    HB_TAG('b', 'l', 'w', 's'), HB_TAG('c', 'a', 'l', 't'),
   1321    HB_TAG('c', 'c', 'm', 'p'), HB_TAG('c', 'f', 'a', 'r'),
   1322    HB_TAG('c', 'j', 'c', 't'), HB_TAG('c', 'l', 'i', 'g'),
   1323    HB_TAG('f', 'i', 'n', '2'), HB_TAG('f', 'i', 'n', '3'),
   1324    HB_TAG('f', 'i', 'n', 'a'), HB_TAG('h', 'a', 'l', 'f'),
   1325    HB_TAG('h', 'a', 'l', 'n'), HB_TAG('i', 'n', 'i', 't'),
   1326    HB_TAG('i', 's', 'o', 'l'), HB_TAG('l', 'i', 'g', 'a'),
   1327    HB_TAG('l', 'j', 'm', 'o'), HB_TAG('l', 'o', 'c', 'l'),
   1328    HB_TAG('l', 't', 'r', 'a'), HB_TAG('l', 't', 'r', 'm'),
   1329    HB_TAG('m', 'e', 'd', '2'), HB_TAG('m', 'e', 'd', 'i'),
   1330    HB_TAG('m', 's', 'e', 't'), HB_TAG('n', 'u', 'k', 't'),
   1331    HB_TAG('p', 'r', 'e', 'f'), HB_TAG('p', 'r', 'e', 's'),
   1332    HB_TAG('p', 's', 't', 'f'), HB_TAG('p', 's', 't', 's'),
   1333    HB_TAG('r', 'c', 'l', 't'), HB_TAG('r', 'l', 'i', 'g'),
   1334    HB_TAG('r', 'k', 'r', 'f'), HB_TAG('r', 'p', 'h', 'f'),
   1335    HB_TAG('r', 't', 'l', 'a'), HB_TAG('r', 't', 'l', 'm'),
   1336    HB_TAG('t', 'j', 'm', 'o'), HB_TAG('v', 'a', 't', 'u'),
   1337    HB_TAG('v', 'e', 'r', 't'), HB_TAG('v', 'j', 'm', 'o')};
   1338 
   1339 void gfxFont::CheckForFeaturesInvolvingSpace() const {
   1340  gfxFontEntry::SpaceFeatures flags = gfxFontEntry::SpaceFeatures::None;
   1341 
   1342  // mFontEntry->mHasSpaceFeatures is a std::atomic<>, so we set it with
   1343  // `exchange` to avoid a potential data race. It's ok if two threads both
   1344  // try to set it; they'll end up with the same value, so it doesn't matter
   1345  // that one will overwrite the other.
   1346  auto setFlags =
   1347      MakeScopeExit([&]() { mFontEntry->mHasSpaceFeatures.exchange(flags); });
   1348 
   1349  bool log = LOG_FONTINIT_ENABLED();
   1350  TimeStamp start;
   1351  if (MOZ_UNLIKELY(log)) {
   1352    start = TimeStamp::Now();
   1353  }
   1354 
   1355  uint32_t spaceGlyph = GetSpaceGlyph();
   1356  if (!spaceGlyph) {
   1357    return;
   1358  }
   1359 
   1360  auto face(GetFontEntry()->GetHBFace());
   1361 
   1362  // GSUB lookups - examine per script
   1363  if (hb_ot_layout_has_substitution(face)) {
   1364    // Get the script ==> code hashtable, creating it on first use.
   1365    nsTHashMap<nsUint32HashKey, Script>* tagToCode = sScriptTagToCode;
   1366    if (!tagToCode) {
   1367      tagToCode = new nsTHashMap<nsUint32HashKey, Script>(
   1368          size_t(Script::NUM_SCRIPT_CODES));
   1369      tagToCode->InsertOrUpdate(HB_TAG('D', 'F', 'L', 'T'), Script::COMMON);
   1370      // Ensure that we don't try to look at script codes beyond what the
   1371      // current version of ICU (at runtime -- in case of system ICU)
   1372      // knows about.
   1373      Script scriptCount = Script(
   1374          std::min<int>(intl::UnicodeProperties::GetMaxNumberOfScripts() + 1,
   1375                        int(Script::NUM_SCRIPT_CODES)));
   1376      for (Script s = Script::ARABIC; s < scriptCount;
   1377           s = Script(static_cast<int>(s) + 1)) {
   1378        hb_script_t script = hb_script_t(GetScriptTagForCode(s));
   1379        unsigned int scriptCount = 4;
   1380        hb_tag_t scriptTags[4];
   1381        hb_ot_tags_from_script_and_language(script, HB_LANGUAGE_INVALID,
   1382                                            &scriptCount, scriptTags, nullptr,
   1383                                            nullptr);
   1384        for (unsigned int i = 0; i < scriptCount; i++) {
   1385          tagToCode->InsertOrUpdate(scriptTags[i], s);
   1386        }
   1387      }
   1388      if (!sScriptTagToCode.compareExchange(nullptr, tagToCode)) {
   1389        // We lost a race! Discard our new table and use the winner.
   1390        delete tagToCode;
   1391        tagToCode = sScriptTagToCode;
   1392      }
   1393    }
   1394 
   1395    // Set up the default-features hashset on first use.
   1396    if (!sDefaultFeatures) {
   1397      uint32_t numDefaultFeatures = std::size(defaultFeatures);
   1398      auto* set = new nsTHashSet<uint32_t>(numDefaultFeatures);
   1399      for (uint32_t i = 0; i < numDefaultFeatures; i++) {
   1400        set->Insert(defaultFeatures[i]);
   1401      }
   1402      if (!sDefaultFeatures.compareExchange(nullptr, set)) {
   1403        delete set;
   1404      }
   1405    }
   1406 
   1407    // iterate over the scripts in the font
   1408    hb_tag_t scriptTags[8];
   1409 
   1410    uint32_t len, offset = 0;
   1411    do {
   1412      len = std::size(scriptTags);
   1413      hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset, &len,
   1414                                         scriptTags);
   1415      for (uint32_t i = 0; i < len; i++) {
   1416        bool isDefaultFeature = false;
   1417        Script s;
   1418        if (!HasLookupRuleWithGlyphByScript(
   1419                face, HB_OT_TAG_GSUB, scriptTags[i], offset + i, spaceGlyph,
   1420                *sDefaultFeatures, isDefaultFeature) ||
   1421            !tagToCode->Get(scriptTags[i], &s)) {
   1422          continue;
   1423        }
   1424        flags = flags | gfxFontEntry::SpaceFeatures::HasFeatures;
   1425        uint32_t index = static_cast<uint32_t>(s) >> 5;
   1426        uint32_t bit = static_cast<uint32_t>(s) & 0x1f;
   1427        MutexAutoLock lock(mFontEntry->mFeatureInfoLock);
   1428        if (isDefaultFeature) {
   1429          mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
   1430        } else {
   1431          mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
   1432        }
   1433      }
   1434      offset += len;
   1435    } while (len == std::size(scriptTags));
   1436  }
   1437 
   1438  // spaces in default features of default script?
   1439  // ==> can't use word cache, skip GPOS analysis
   1440  bool canUseWordCache = true;
   1441  {
   1442    MutexAutoLock lock(mFontEntry->mFeatureInfoLock);
   1443    if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, Script::COMMON)) {
   1444      canUseWordCache = false;
   1445    }
   1446  }
   1447 
   1448  // GPOS lookups - distinguish kerning from non-kerning features
   1449  if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
   1450    bool hasKerning = false, hasNonKerning = false;
   1451    HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
   1452                           HB_TAG('k', 'e', 'r', 'n'), hasKerning, spaceGlyph);
   1453    if (hasKerning) {
   1454      flags |= gfxFontEntry::SpaceFeatures::HasFeatures |
   1455               gfxFontEntry::SpaceFeatures::Kerning;
   1456    }
   1457    if (hasNonKerning) {
   1458      flags |= gfxFontEntry::SpaceFeatures::HasFeatures |
   1459               gfxFontEntry::SpaceFeatures::NonKerning;
   1460    }
   1461  }
   1462 
   1463  if (MOZ_UNLIKELY(log)) {
   1464    MutexAutoLock lock(mFontEntry->mFeatureInfoLock);
   1465    TimeDuration elapsed = TimeStamp::Now() - start;
   1466    LOG_FONTINIT((
   1467        "(fontinit-spacelookups) font: %s - "
   1468        "subst default: %8.8x %8.8x %8.8x %8.8x "
   1469        "subst non-default: %8.8x %8.8x %8.8x %8.8x "
   1470        "kerning: %s non-kerning: %s time: %6.3f\n",
   1471        mFontEntry->Name().get(), mFontEntry->mDefaultSubSpaceFeatures[3],
   1472        mFontEntry->mDefaultSubSpaceFeatures[2],
   1473        mFontEntry->mDefaultSubSpaceFeatures[1],
   1474        mFontEntry->mDefaultSubSpaceFeatures[0],
   1475        mFontEntry->mNonDefaultSubSpaceFeatures[3],
   1476        mFontEntry->mNonDefaultSubSpaceFeatures[2],
   1477        mFontEntry->mNonDefaultSubSpaceFeatures[1],
   1478        mFontEntry->mNonDefaultSubSpaceFeatures[0],
   1479        (mFontEntry->mHasSpaceFeatures & gfxFontEntry::SpaceFeatures::Kerning
   1480             ? "true"
   1481             : "false"),
   1482        (mFontEntry->mHasSpaceFeatures & gfxFontEntry::SpaceFeatures::NonKerning
   1483             ? "true"
   1484             : "false"),
   1485        elapsed.ToMilliseconds()));
   1486  }
   1487 }
   1488 
   1489 bool gfxFont::HasSubstitutionRulesWithSpaceLookups(Script aRunScript) const {
   1490  NS_ASSERTION(GetFontEntry()->mHasSpaceFeatures !=
   1491                   gfxFontEntry::SpaceFeatures::Uninitialized,
   1492               "need to initialize space lookup flags");
   1493  NS_ASSERTION(aRunScript < Script::NUM_SCRIPT_CODES, "weird script code");
   1494  if (aRunScript == Script::INVALID || aRunScript >= Script::NUM_SCRIPT_CODES) {
   1495    return false;
   1496  }
   1497 
   1498  // default features have space lookups ==> true
   1499  MutexAutoLock lock(mFontEntry->mFeatureInfoLock);
   1500  if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, Script::COMMON) ||
   1501      HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, aRunScript)) {
   1502    return true;
   1503  }
   1504 
   1505  // non-default features have space lookups and some type of
   1506  // font feature, in font or style is specified ==> true
   1507  if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
   1508                       Script::COMMON) ||
   1509       HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures, aRunScript)) &&
   1510      (!mStyle.featureSettings.IsEmpty() ||
   1511       !mFontEntry->mFeatureSettings.IsEmpty())) {
   1512    return true;
   1513  }
   1514 
   1515  return false;
   1516 }
   1517 
   1518 tainted_boolean_hint gfxFont::SpaceMayParticipateInShaping(
   1519    Script aRunScript) const {
   1520  // avoid checking fonts known not to include default space-dependent features
   1521  if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
   1522    if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
   1523        mFontEntry->mFeatureSettings.IsEmpty()) {
   1524      return false;
   1525    }
   1526  }
   1527 
   1528  if (FontCanSupportGraphite()) {
   1529    if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
   1530      return mFontEntry->HasGraphiteSpaceContextuals();
   1531    }
   1532  }
   1533 
   1534  // We record the presence of space-dependent features in the font entry
   1535  // so that subsequent instantiations for the same font face won't
   1536  // require us to re-check the tables; however, the actual check is done
   1537  // by gfxFont because not all font entry subclasses know how to create
   1538  // a harfbuzz face for introspection.
   1539  gfxFontEntry::SpaceFeatures flags = mFontEntry->mHasSpaceFeatures;
   1540  if (flags == gfxFontEntry::SpaceFeatures::Uninitialized) {
   1541    CheckForFeaturesInvolvingSpace();
   1542    flags = mFontEntry->mHasSpaceFeatures;
   1543  }
   1544 
   1545  if (!(flags & gfxFontEntry::SpaceFeatures::HasFeatures)) {
   1546    return false;
   1547  }
   1548 
   1549  // if font has substitution rules or non-kerning positioning rules
   1550  // that involve spaces, bypass
   1551  if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
   1552      (flags & gfxFontEntry::SpaceFeatures::NonKerning)) {
   1553    return true;
   1554  }
   1555 
   1556  // if kerning explicitly enabled/disabled via font-feature-settings or
   1557  // font-kerning and kerning rules use spaces, only bypass when enabled
   1558  if (mKerningSet && (flags & gfxFontEntry::SpaceFeatures::Kerning)) {
   1559    return mKerningEnabled;
   1560  }
   1561 
   1562  return false;
   1563 }
   1564 
   1565 bool gfxFont::SupportsFeature(Script aScript, uint32_t aFeatureTag) {
   1566  if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
   1567    return GetFontEntry()->SupportsGraphiteFeature(aFeatureTag);
   1568  }
   1569  return GetFontEntry()->SupportsOpenTypeFeature(aScript, aFeatureTag);
   1570 }
   1571 
   1572 bool gfxFont::SupportsVariantCaps(Script aScript, uint32_t aVariantCaps,
   1573                                  bool& aFallbackToSmallCaps,
   1574                                  bool& aSyntheticLowerToSmallCaps,
   1575                                  bool& aSyntheticUpperToSmallCaps) {
   1576  bool ok = true;  // cases without fallback are fine
   1577  aFallbackToSmallCaps = false;
   1578  aSyntheticLowerToSmallCaps = false;
   1579  aSyntheticUpperToSmallCaps = false;
   1580  switch (aVariantCaps) {
   1581    case NS_FONT_VARIANT_CAPS_SMALLCAPS:
   1582      ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p'));
   1583      if (!ok) {
   1584        aSyntheticLowerToSmallCaps = true;
   1585      }
   1586      break;
   1587    case NS_FONT_VARIANT_CAPS_ALLSMALL:
   1588      ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p')) &&
   1589           SupportsFeature(aScript, HB_TAG('c', '2', 's', 'c'));
   1590      if (!ok) {
   1591        aSyntheticLowerToSmallCaps = true;
   1592        aSyntheticUpperToSmallCaps = true;
   1593      }
   1594      break;
   1595    case NS_FONT_VARIANT_CAPS_PETITECAPS:
   1596      ok = SupportsFeature(aScript, HB_TAG('p', 'c', 'a', 'p'));
   1597      if (!ok) {
   1598        ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p'));
   1599        aFallbackToSmallCaps = ok;
   1600      }
   1601      if (!ok) {
   1602        aSyntheticLowerToSmallCaps = true;
   1603      }
   1604      break;
   1605    case NS_FONT_VARIANT_CAPS_ALLPETITE:
   1606      ok = SupportsFeature(aScript, HB_TAG('p', 'c', 'a', 'p')) &&
   1607           SupportsFeature(aScript, HB_TAG('c', '2', 'p', 'c'));
   1608      if (!ok) {
   1609        ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p')) &&
   1610             SupportsFeature(aScript, HB_TAG('c', '2', 's', 'c'));
   1611        aFallbackToSmallCaps = ok;
   1612      }
   1613      if (!ok) {
   1614        aSyntheticLowerToSmallCaps = true;
   1615        aSyntheticUpperToSmallCaps = true;
   1616      }
   1617      break;
   1618    default:
   1619      break;
   1620  }
   1621 
   1622  NS_ASSERTION(
   1623      !(ok && (aSyntheticLowerToSmallCaps || aSyntheticUpperToSmallCaps)),
   1624      "shouldn't use synthetic features if we found real ones");
   1625 
   1626  NS_ASSERTION(!(!ok && aFallbackToSmallCaps),
   1627               "if we found a usable fallback, that counts as ok");
   1628 
   1629  return ok;
   1630 }
   1631 
   1632 bool gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
   1633                                     const uint8_t* aString, uint32_t aLength,
   1634                                     Script aRunScript) {
   1635  NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString),
   1636                                       aLength);
   1637  return SupportsSubSuperscript(aSubSuperscript, unicodeString.get(), aLength,
   1638                                aRunScript);
   1639 }
   1640 
   1641 bool gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
   1642                                     const char16_t* aString, uint32_t aLength,
   1643                                     Script aRunScript) {
   1644  NS_ASSERTION(aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ||
   1645                   aSubSuperscript == NS_FONT_VARIANT_POSITION_SUB,
   1646               "unknown value of font-variant-position");
   1647 
   1648  uint32_t feature = aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER
   1649                         ? HB_TAG('s', 'u', 'p', 's')
   1650                         : HB_TAG('s', 'u', 'b', 's');
   1651 
   1652  if (!SupportsFeature(aRunScript, feature)) {
   1653    return false;
   1654  }
   1655 
   1656  // xxx - for graphite, don't really know how to sniff lookups so bail
   1657  if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
   1658    return true;
   1659  }
   1660 
   1661  gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper();
   1662  if (!shaper) {
   1663    return false;
   1664  }
   1665 
   1666  // get the hbset containing input glyphs for the feature
   1667  const hb_set_t* inputGlyphs =
   1668      mFontEntry->InputsForOpenTypeFeature(aRunScript, feature);
   1669 
   1670  // create an hbset containing default glyphs for the script run
   1671  hb_set_t* defaultGlyphsInRun = hb_set_create();
   1672 
   1673  // for each character, get the glyph id
   1674  for (uint32_t i = 0; i < aLength; i++) {
   1675    uint32_t ch = aString[i];
   1676 
   1677    if (i + 1 < aLength && NS_IS_SURROGATE_PAIR(ch, aString[i + 1])) {
   1678      i++;
   1679      ch = SURROGATE_TO_UCS4(ch, aString[i]);
   1680    }
   1681 
   1682    hb_codepoint_t gid = shaper->GetNominalGlyph(ch);
   1683    hb_set_add(defaultGlyphsInRun, gid);
   1684  }
   1685 
   1686  // intersect with input glyphs, if size is not the same ==> fallback
   1687  uint32_t origSize = hb_set_get_population(defaultGlyphsInRun);
   1688  hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
   1689  uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
   1690  hb_set_destroy(defaultGlyphsInRun);
   1691 
   1692  return origSize == intersectionSize;
   1693 }
   1694 
   1695 bool gfxFont::FeatureWillHandleChar(Script aRunScript, uint32_t aFeature,
   1696                                    uint32_t aUnicode) {
   1697  if (!SupportsFeature(aRunScript, aFeature)) {
   1698    return false;
   1699  }
   1700 
   1701  // xxx - for graphite, don't really know how to sniff lookups so bail
   1702  if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
   1703    return true;
   1704  }
   1705 
   1706  if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
   1707    // get the hbset containing input glyphs for the feature
   1708    const hb_set_t* inputGlyphs =
   1709        mFontEntry->InputsForOpenTypeFeature(aRunScript, aFeature);
   1710 
   1711    hb_codepoint_t gid = shaper->GetNominalGlyph(aUnicode);
   1712    return hb_set_has(inputGlyphs, gid);
   1713  }
   1714 
   1715  return false;
   1716 }
   1717 
   1718 bool gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn) {
   1719  aFeatureOn = false;
   1720 
   1721  if (mStyle.featureSettings.IsEmpty() &&
   1722      GetFontEntry()->mFeatureSettings.IsEmpty()) {
   1723    return false;
   1724  }
   1725 
   1726  // add feature values from font
   1727  bool featureSet = false;
   1728  uint32_t i, count;
   1729 
   1730  nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
   1731  count = fontFeatures.Length();
   1732  for (i = 0; i < count; i++) {
   1733    const gfxFontFeature& feature = fontFeatures.ElementAt(i);
   1734    if (feature.mTag == aFeature) {
   1735      featureSet = true;
   1736      aFeatureOn = (feature.mValue != 0);
   1737    }
   1738  }
   1739 
   1740  // add feature values from style rules
   1741  nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
   1742  count = styleFeatures.Length();
   1743  for (i = 0; i < count; i++) {
   1744    const gfxFontFeature& feature = styleFeatures.ElementAt(i);
   1745    if (feature.mTag == aFeature) {
   1746      featureSet = true;
   1747      aFeatureOn = (feature.mValue != 0);
   1748    }
   1749  }
   1750 
   1751  return featureSet;
   1752 }
   1753 
   1754 already_AddRefed<mozilla::gfx::ScaledFont> gfxFont::GetScaledFont(
   1755    mozilla::gfx::DrawTarget* aDrawTarget) {
   1756  mozilla::gfx::PaletteCache dummy;
   1757  TextRunDrawParams params(dummy);
   1758  return GetScaledFont(params);
   1759 }
   1760 
   1761 void gfxFont::InitializeScaledFont(
   1762    const RefPtr<mozilla::gfx::ScaledFont>& aScaledFont) {
   1763  if (!aScaledFont) {
   1764    return;
   1765  }
   1766 
   1767  float angle = AngleForSyntheticOblique();
   1768  if (angle != 0.0f) {
   1769    aScaledFont->SetSyntheticObliqueAngle(angle);
   1770  }
   1771 }
   1772 
   1773 /**
   1774 * A helper function in case we need to do any rounding or other
   1775 * processing here.
   1776 */
   1777 #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
   1778  (double(aAppUnits) * double(aDevUnitsPerAppUnit))
   1779 
   1780 static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
   1781  switch (aAAOption) {
   1782    case gfxFont::kAntialiasSubpixel:
   1783      return AntialiasMode::SUBPIXEL;
   1784    case gfxFont::kAntialiasGrayscale:
   1785      return AntialiasMode::GRAY;
   1786    case gfxFont::kAntialiasNone:
   1787      return AntialiasMode::NONE;
   1788    default:
   1789      return AntialiasMode::DEFAULT;
   1790  }
   1791 }
   1792 
   1793 class GlyphBufferAzure {
   1794 #define AUTO_BUFFER_SIZE (2048 / sizeof(Glyph))
   1795 
   1796  typedef mozilla::image::imgDrawingParams imgDrawingParams;
   1797 
   1798 public:
   1799  GlyphBufferAzure(const TextRunDrawParams& aRunParams,
   1800                   const FontDrawParams& aFontParams)
   1801      : mRunParams(aRunParams),
   1802        mFontParams(aFontParams),
   1803        mBuffer(*mAutoBuffer.addr()),
   1804        mBufSize(AUTO_BUFFER_SIZE),
   1805        mCapacity(0),
   1806        mNumGlyphs(0) {}
   1807 
   1808  ~GlyphBufferAzure() {
   1809    if (mNumGlyphs > 0) {
   1810      FlushGlyphs();
   1811    }
   1812 
   1813    if (mBuffer != *mAutoBuffer.addr()) {
   1814      free(mBuffer);
   1815    }
   1816  }
   1817 
   1818  // Ensure the buffer has enough space for aGlyphCount glyphs to be added,
   1819  // considering the supplied strike multipler aStrikeCount.
   1820  // This MUST be called before OutputGlyph is used to actually store glyph
   1821  // records in the buffer. It may be called repeated to add further capacity
   1822  // in case we don't know up-front exactly what will be needed.
   1823  void AddCapacity(uint32_t aGlyphCount, uint32_t aStrikeCount) {
   1824    // Calculate the new capacity and ensure it will fit within the maximum
   1825    // allowed capacity.
   1826    static const uint64_t kMaxCapacity = 64 * 1024;
   1827    mCapacity = uint32_t(std::min(
   1828        kMaxCapacity,
   1829        uint64_t(mCapacity) + uint64_t(aGlyphCount) * uint64_t(aStrikeCount)));
   1830    // See if the required capacity fits within the already-allocated space
   1831    if (mCapacity <= mBufSize) {
   1832      return;
   1833    }
   1834    // We need to grow the buffer: determine a new size, allocate, and
   1835    // copy the existing data over if we didn't use realloc (which would
   1836    // do it automatically).
   1837    mBufSize = std::max(mCapacity, mBufSize * 2);
   1838    if (mBuffer == *mAutoBuffer.addr()) {
   1839      // switching from autobuffer to malloc, so we need to copy
   1840      mBuffer = reinterpret_cast<Glyph*>(moz_xmalloc(mBufSize * sizeof(Glyph)));
   1841      std::memcpy(mBuffer, *mAutoBuffer.addr(), mNumGlyphs * sizeof(Glyph));
   1842    } else {
   1843      mBuffer = reinterpret_cast<Glyph*>(
   1844          moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
   1845    }
   1846  }
   1847 
   1848  void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) {
   1849    // If the buffer is full, flush to make room for the new glyph.
   1850    if (mNumGlyphs >= mCapacity) {
   1851      // Check that AddCapacity has been used appropriately!
   1852      MOZ_ASSERT(mCapacity > 0 && mNumGlyphs == mCapacity);
   1853      Flush();
   1854    }
   1855    Glyph* glyph = mBuffer + mNumGlyphs++;
   1856    glyph->mIndex = aGlyphID;
   1857    glyph->mPosition = aPt;
   1858  }
   1859 
   1860  void Flush() {
   1861    if (mNumGlyphs > 0) {
   1862      FlushGlyphs();
   1863      mNumGlyphs = 0;
   1864    }
   1865  }
   1866 
   1867  const TextRunDrawParams& mRunParams;
   1868  const FontDrawParams& mFontParams;
   1869 
   1870 private:
   1871  static DrawMode GetStrokeMode(DrawMode aMode) {
   1872    return aMode & (DrawMode::GLYPH_STROKE | DrawMode::GLYPH_STROKE_UNDERNEATH);
   1873  }
   1874 
   1875  // Render the buffered glyphs to the draw target.
   1876  void FlushGlyphs() {
   1877    gfx::GlyphBuffer buf;
   1878    buf.mGlyphs = mBuffer;
   1879    buf.mNumGlyphs = mNumGlyphs;
   1880 
   1881    const gfxContext::AzureState& state = mRunParams.context->CurrentState();
   1882 
   1883    // Draw stroke first if the UNDERNEATH flag is set in drawMode.
   1884    if (mRunParams.strokeOpts &&
   1885        GetStrokeMode(mRunParams.drawMode) ==
   1886            (DrawMode::GLYPH_STROKE | DrawMode::GLYPH_STROKE_UNDERNEATH)) {
   1887      DrawStroke(state, buf);
   1888    }
   1889 
   1890    if (mRunParams.drawMode & DrawMode::GLYPH_FILL) {
   1891      if (state.pattern || mFontParams.contextPaint) {
   1892        Pattern* pat;
   1893 
   1894        RefPtr<gfxPattern> fillPattern;
   1895        if (mFontParams.contextPaint) {
   1896          imgDrawingParams imgParams;
   1897          fillPattern = mFontParams.contextPaint->GetFillPattern(
   1898              mRunParams.context->GetDrawTarget(),
   1899              mRunParams.context->CurrentMatrixDouble(), imgParams);
   1900        }
   1901        if (!fillPattern) {
   1902          if (state.pattern) {
   1903            RefPtr<gfxPattern> statePattern =
   1904                mRunParams.context->CurrentState().pattern;
   1905            pat = statePattern->GetPattern(mRunParams.dt,
   1906                                           state.patternTransformChanged
   1907                                               ? &state.patternTransform
   1908                                               : nullptr);
   1909          } else {
   1910            pat = nullptr;
   1911          }
   1912        } else {
   1913          pat = fillPattern->GetPattern(mRunParams.dt);
   1914        }
   1915 
   1916        if (pat) {
   1917          mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf, *pat,
   1918                                    mFontParams.drawOptions);
   1919        }
   1920      } else {
   1921        mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
   1922                                  ColorPattern(state.color),
   1923                                  mFontParams.drawOptions);
   1924      }
   1925    }
   1926 
   1927    // Draw stroke if the UNDERNEATH flag is not set.
   1928    if (mRunParams.strokeOpts &&
   1929        GetStrokeMode(mRunParams.drawMode) == DrawMode::GLYPH_STROKE) {
   1930      DrawStroke(state, buf);
   1931    }
   1932 
   1933    if (mRunParams.drawMode & DrawMode::GLYPH_PATH) {
   1934      mRunParams.context->EnsurePathBuilder();
   1935      Matrix mat = mRunParams.dt->GetTransform();
   1936      mFontParams.scaledFont->CopyGlyphsToBuilder(
   1937          buf, mRunParams.context->mPathBuilder, &mat);
   1938    }
   1939  }
   1940 
   1941  void DrawStroke(const gfxContext::AzureState& aState,
   1942                  gfx::GlyphBuffer& aBuffer) {
   1943    if (mRunParams.textStrokePattern) {
   1944      Pattern* pat = mRunParams.textStrokePattern->GetPattern(
   1945          mRunParams.dt,
   1946          aState.patternTransformChanged ? &aState.patternTransform : nullptr);
   1947 
   1948      if (pat) {
   1949        FlushStroke(aBuffer, *pat);
   1950      }
   1951    } else {
   1952      FlushStroke(aBuffer,
   1953                  ColorPattern(ToDeviceColor(mRunParams.textStrokeColor)));
   1954    }
   1955  }
   1956 
   1957  void FlushStroke(gfx::GlyphBuffer& aBuf, const Pattern& aPattern) {
   1958    mRunParams.dt->StrokeGlyphs(mFontParams.scaledFont, aBuf, aPattern,
   1959                                *mRunParams.strokeOpts,
   1960                                mFontParams.drawOptions);
   1961  }
   1962 
   1963  // We use an "inline" buffer automatically allocated (on the stack) as part
   1964  // of the GlyphBufferAzure object to hold the glyphs in most cases, falling
   1965  // back to a separately-allocated heap buffer if the count of buffered
   1966  // glyphs gets too big.
   1967  //
   1968  // This is basically a rudimentary AutoTArray; so why not use AutoTArray
   1969  // itself?
   1970  //
   1971  // If we used an AutoTArray, we'd want to avoid using SetLength or
   1972  // AppendElements to allocate the space we actually need, because those
   1973  // methods would default-construct the new elements.
   1974  //
   1975  // Could we use SetCapacity to reserve the necessary buffer space without
   1976  // default-constructing all the Glyph records? No, because of a failure
   1977  // that could occur when we need to grow the buffer, which happens when we
   1978  // encounter a DetailedGlyph in the textrun that refers to a sequence of
   1979  // several real glyphs. At that point, we need to add some extra capacity
   1980  // to the buffer we initially allocated based on the length of the textrun
   1981  // range we're rendering.
   1982  //
   1983  // This buffer growth would work fine as long as it still fits within the
   1984  // array's inline buffer (we just use a bit more of it), or if the buffer
   1985  // was already heap-allocated (in which case AutoTArray will use realloc(),
   1986  // preserving its contents). But a problem will arise when the initial
   1987  // capacity we allocated (based on the length of the run) fits within the
   1988  // array's inline buffer, but subsequently we need to extend the buffer
   1989  // beyond the inline buffer size, so we reallocate to the heap. Because we
   1990  // haven't "officially" filled the array with SetLength or AppendElements,
   1991  // its mLength is still zero; as far as it's concerned the buffer is just
   1992  // uninitialized space, and when it switches to use a malloc'd buffer it
   1993  // won't copy the existing contents.
   1994 
   1995  // Allocate space for a buffer of Glyph records, without initializing them.
   1996  AlignedStorage2<Glyph[AUTO_BUFFER_SIZE]> mAutoBuffer;
   1997 
   1998  // Pointer to the buffer we're currently using -- initially mAutoBuffer,
   1999  // but may be changed to a malloc'd buffer, in which case that buffer must
   2000  // be free'd on destruction.
   2001  Glyph* mBuffer;
   2002 
   2003  uint32_t mBufSize;    // size of allocated buffer; capacity can grow to
   2004                        // this before reallocation is needed
   2005  uint32_t mCapacity;   // amount of buffer size reserved
   2006  uint32_t mNumGlyphs;  // number of glyphs actually present in the buffer
   2007 
   2008 #undef AUTO_BUFFER_SIZE
   2009 };
   2010 
   2011 // Bug 674909. When synthetic bolding text by drawing twice, need to
   2012 // render using a pixel offset in device pixels, otherwise text
   2013 // doesn't appear bolded, it appears as if a bad text shadow exists
   2014 // when a non-identity transform exists.  Use an offset factor so that
   2015 // the second draw occurs at a constant offset in device pixels.
   2016 
   2017 gfx::Float gfxFont::CalcXScale(DrawTarget* aDrawTarget) {
   2018  // determine magnitude of a 1px x offset in device space
   2019  Size t = aDrawTarget->GetTransform().TransformSize(Size(1.0, 0.0));
   2020  if (t.width == 1.0 && t.height == 0.0) {
   2021    // short-circuit the most common case to avoid sqrt() and division
   2022    return 1.0;
   2023  }
   2024 
   2025  gfx::Float m = sqrtf(t.width * t.width + t.height * t.height);
   2026 
   2027  NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
   2028  if (m == 0.0) {
   2029    return 0.0;  // effectively disables offset
   2030  }
   2031 
   2032  // scale factor so that offsets are 1px in device pixels
   2033  return 1.0 / m;
   2034 }
   2035 
   2036 // Draw a run of CharacterGlyph records from the given offset in aShapedText.
   2037 // Returns true if glyph paths were actually emitted.
   2038 template <gfxFont::FontComplexityT FC, gfxFont::SpacingT S>
   2039 bool gfxFont::DrawGlyphs(const gfxShapedText* aShapedText,
   2040                         uint32_t aOffset,  // offset in the textrun
   2041                         uint32_t aCount,   // length of run to draw
   2042                         gfx::Point* aPt,
   2043                         const gfx::Matrix* aOffsetMatrix,  // may be null
   2044                         GlyphBufferAzure& aBuffer) {
   2045  float& inlineCoord =
   2046      aBuffer.mFontParams.isVerticalFont ? aPt->y.value : aPt->x.value;
   2047 
   2048  const gfxShapedText::CompressedGlyph* glyphData =
   2049      &aShapedText->GetCharacterGlyphs()[aOffset];
   2050 
   2051  if (S == SpacingT::HasSpacing) {
   2052    float space = aBuffer.mRunParams.spacing[0].mBefore *
   2053                  aBuffer.mFontParams.advanceDirection;
   2054    inlineCoord += space;
   2055  }
   2056 
   2057  // Allocate buffer space for the run, assuming all simple glyphs.
   2058  uint32_t capacityMult = 1 + aBuffer.mFontParams.extraStrikes;
   2059  aBuffer.AddCapacity(aCount, capacityMult);
   2060 
   2061  bool emittedGlyphs = false;
   2062 
   2063  for (uint32_t i = 0; i < aCount; ++i, ++glyphData) {
   2064    if (glyphData->IsSimpleGlyph()) {
   2065      float advance =
   2066          glyphData->GetSimpleAdvance() * aBuffer.mFontParams.advanceDirection;
   2067      if (aBuffer.mRunParams.isRTL) {
   2068        inlineCoord += advance;
   2069      }
   2070      DrawOneGlyph<FC>(glyphData->GetSimpleGlyph(), *aPt, aBuffer,
   2071                       &emittedGlyphs);
   2072      if (!aBuffer.mRunParams.isRTL) {
   2073        inlineCoord += advance;
   2074      }
   2075    } else {
   2076      uint32_t glyphCount = glyphData->GetGlyphCount();
   2077      if (glyphCount > 0) {
   2078        // Add extra buffer capacity to allow for multiple-glyph entry.
   2079        aBuffer.AddCapacity(glyphCount - 1, capacityMult);
   2080        const gfxShapedText::DetailedGlyph* details =
   2081            aShapedText->GetDetailedGlyphs(aOffset + i);
   2082        MOZ_ASSERT(details, "missing DetailedGlyph!");
   2083        for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
   2084          float advance =
   2085              details->mAdvance * aBuffer.mFontParams.advanceDirection;
   2086          if (aBuffer.mRunParams.isRTL) {
   2087            inlineCoord += advance;
   2088          }
   2089          if (glyphData->IsMissing()) {
   2090            if (!DrawMissingGlyph(aBuffer.mRunParams, aBuffer.mFontParams,
   2091                                  details, *aPt)) {
   2092              return false;
   2093            }
   2094          } else {
   2095            gfx::Point glyphPt(
   2096                *aPt + (aOffsetMatrix
   2097                            ? aOffsetMatrix->TransformPoint(details->mOffset)
   2098                            : details->mOffset));
   2099            DrawOneGlyph<FC>(details->mGlyphID, glyphPt, aBuffer,
   2100                             &emittedGlyphs);
   2101          }
   2102          if (!aBuffer.mRunParams.isRTL) {
   2103            inlineCoord += advance;
   2104          }
   2105        }
   2106      }
   2107    }
   2108 
   2109    if (S == SpacingT::HasSpacing) {
   2110      float space = aBuffer.mRunParams.spacing[i].mAfter;
   2111      if (i + 1 < aCount) {
   2112        space += aBuffer.mRunParams.spacing[i + 1].mBefore;
   2113      }
   2114      space *= aBuffer.mFontParams.advanceDirection;
   2115      inlineCoord += space;
   2116    }
   2117  }
   2118 
   2119  return emittedGlyphs;
   2120 }
   2121 
   2122 // Draw an individual glyph at a specific location.
   2123 // *aPt is the glyph position in appUnits; it is converted to device
   2124 // coordinates (devPt) here.
   2125 template <gfxFont::FontComplexityT FC>
   2126 void gfxFont::DrawOneGlyph(uint32_t aGlyphID, const gfx::Point& aPt,
   2127                           GlyphBufferAzure& aBuffer, bool* aEmittedGlyphs) {
   2128  const TextRunDrawParams& runParams(aBuffer.mRunParams);
   2129 
   2130  gfx::Point devPt(ToDeviceUnits(aPt.x, runParams.devPerApp),
   2131                   ToDeviceUnits(aPt.y, runParams.devPerApp));
   2132 
   2133  auto* textDrawer = runParams.textDrawer;
   2134  if (textDrawer) {
   2135    // If the glyph is entirely outside the clip rect, we don't need to draw it
   2136    // at all. (We check the font extents here rather than the individual glyph
   2137    // bounds because that's cheaper to look up, and provides a conservative
   2138    // "worst case" for where this glyph might want to draw.)
   2139    LayoutDeviceRect extents =
   2140        LayoutDeviceRect::FromUnknownRect(aBuffer.mFontParams.fontExtents);
   2141    extents.MoveBy(LayoutDevicePoint::FromUnknownPoint(devPt));
   2142    if (!extents.Intersects(runParams.clipRect)) {
   2143      return;
   2144    }
   2145  }
   2146 
   2147  if (FC == FontComplexityT::ComplexFont) {
   2148    const FontDrawParams& fontParams(aBuffer.mFontParams);
   2149 
   2150    gfxContextMatrixAutoSaveRestore matrixRestore;
   2151 
   2152    if (fontParams.obliqueSkew != 0.0f && fontParams.isVerticalFont &&
   2153        !textDrawer) {
   2154      // We have to flush each glyph individually when doing
   2155      // synthetic-oblique for vertical-upright text, because
   2156      // the skew transform needs to be applied to a separate
   2157      // origin for each glyph, not once for the whole run.
   2158      aBuffer.Flush();
   2159      matrixRestore.SetContext(runParams.context);
   2160      gfx::Point skewPt(
   2161          devPt.x + GetMetrics(nsFontMetrics::eVertical).emHeight / 2, devPt.y);
   2162      gfx::Matrix mat =
   2163          runParams.context->CurrentMatrix()
   2164              .PreTranslate(skewPt)
   2165              .PreMultiply(gfx::Matrix(1, fontParams.obliqueSkew, 0, 1, 0, 0))
   2166              .PreTranslate(-skewPt);
   2167      runParams.context->SetMatrix(mat);
   2168    }
   2169 
   2170    if (fontParams.haveSVGGlyphs) {
   2171      if (!runParams.paintSVGGlyphs) {
   2172        return;
   2173      }
   2174      NS_WARNING_ASSERTION(
   2175          runParams.drawMode != DrawMode::GLYPH_PATH,
   2176          "Rendering SVG glyph despite request for glyph path");
   2177      if (RenderSVGGlyph(runParams.context, textDrawer, devPt, aGlyphID,
   2178                         fontParams.contextPaint, runParams.callbacks,
   2179                         *aEmittedGlyphs)) {
   2180        return;
   2181      }
   2182    }
   2183 
   2184    if (fontParams.haveColorGlyphs && !UseNativeColrFontSupport() &&
   2185        RenderColorGlyph(runParams.dt, runParams.context, textDrawer,
   2186                         fontParams, devPt, aGlyphID)) {
   2187      return;
   2188    }
   2189 
   2190    aBuffer.OutputGlyph(aGlyphID, devPt);
   2191 
   2192    // Synthetic bolding (if required) by multi-striking.
   2193    for (int32_t i = 0; i < fontParams.extraStrikes; ++i) {
   2194      if (fontParams.isVerticalFont) {
   2195        devPt.y += fontParams.synBoldOnePixelOffset;
   2196      } else {
   2197        devPt.x += fontParams.synBoldOnePixelOffset;
   2198      }
   2199      aBuffer.OutputGlyph(aGlyphID, devPt);
   2200    }
   2201 
   2202    if (fontParams.obliqueSkew != 0.0f && fontParams.isVerticalFont &&
   2203        !textDrawer) {
   2204      aBuffer.Flush();
   2205    }
   2206  } else {
   2207    aBuffer.OutputGlyph(aGlyphID, devPt);
   2208  }
   2209 
   2210  *aEmittedGlyphs = true;
   2211 }
   2212 
   2213 bool gfxFont::DrawMissingGlyph(const TextRunDrawParams& aRunParams,
   2214                               const FontDrawParams& aFontParams,
   2215                               const gfxShapedText::DetailedGlyph* aDetails,
   2216                               const gfx::Point& aPt) {
   2217  // Default-ignorable chars will have zero advance width;
   2218  // we don't have to draw the hexbox for them.
   2219  float advance = aDetails->mAdvance;
   2220  if (aRunParams.drawMode != DrawMode::GLYPH_PATH && advance > 0) {
   2221    auto* textDrawer = aRunParams.textDrawer;
   2222    const Matrix* matPtr = nullptr;
   2223    Matrix mat;
   2224    if (textDrawer) {
   2225      // Generate an orientation matrix for the current writing mode
   2226      wr::FontInstanceFlags flags = textDrawer->GetWRGlyphFlags();
   2227      if (flags & wr::FontInstanceFlags::TRANSPOSE) {
   2228        std::swap(mat._11, mat._12);
   2229        std::swap(mat._21, mat._22);
   2230      }
   2231      mat.PostScale(flags & wr::FontInstanceFlags::FLIP_X ? -1.0f : 1.0f,
   2232                    flags & wr::FontInstanceFlags::FLIP_Y ? -1.0f : 1.0f);
   2233      matPtr = &mat;
   2234    }
   2235 
   2236    Point pt(Float(ToDeviceUnits(aPt.x, aRunParams.devPerApp)),
   2237             Float(ToDeviceUnits(aPt.y, aRunParams.devPerApp)));
   2238    Float advanceDevUnits = Float(ToDeviceUnits(advance, aRunParams.devPerApp));
   2239    Float height = GetMetrics(nsFontMetrics::eHorizontal).maxAscent;
   2240    // Horizontally center if drawing vertically upright with no sideways
   2241    // transform.
   2242    Rect glyphRect =
   2243        aFontParams.isVerticalFont && !mat.HasNonAxisAlignedTransform()
   2244            ? Rect(pt.x - height / 2, pt.y, height, advanceDevUnits)
   2245            : Rect(pt.x, pt.y - height, advanceDevUnits, height);
   2246 
   2247    // If there's a fake-italic skew in effect as part
   2248    // of the drawTarget's transform, we need to undo
   2249    // this before drawing the hexbox. (Bug 983985)
   2250    gfxContextMatrixAutoSaveRestore matrixRestore;
   2251    if (aFontParams.obliqueSkew != 0.0f && !aFontParams.isVerticalFont &&
   2252        !textDrawer) {
   2253      matrixRestore.SetContext(aRunParams.context);
   2254      gfx::Matrix mat =
   2255          aRunParams.context->CurrentMatrix()
   2256              .PreTranslate(pt)
   2257              .PreMultiply(gfx::Matrix(1, 0, aFontParams.obliqueSkew, 1, 0, 0))
   2258              .PreTranslate(-pt);
   2259      aRunParams.context->SetMatrix(mat);
   2260    }
   2261 
   2262    gfxFontMissingGlyphs::DrawMissingGlyph(
   2263        aDetails->mGlyphID, glyphRect, *aRunParams.dt,
   2264        PatternFromState(aRunParams.context), matPtr);
   2265  }
   2266  return true;
   2267 }
   2268 
   2269 // This method is mostly parallel to DrawGlyphs.
   2270 void gfxFont::DrawEmphasisMarks(const gfxTextRun* aShapedText, gfx::Point* aPt,
   2271                                uint32_t aOffset, uint32_t aCount,
   2272                                const EmphasisMarkDrawParams& aParams) {
   2273  float& inlineCoord = aParams.isVertical ? aPt->y.value : aPt->x.value;
   2274  gfxTextRun::Range markRange(aParams.mark);
   2275  gfxTextRun::DrawParams params(aParams.context, aParams.paletteCache);
   2276 
   2277  float clusterStart = -std::numeric_limits<float>::infinity();
   2278  bool shouldDrawEmphasisMark = false;
   2279  for (uint32_t i = 0, idx = aOffset; i < aCount; ++i, ++idx) {
   2280    if (aParams.spacing) {
   2281      inlineCoord += aParams.direction * aParams.spacing[i].mBefore;
   2282    }
   2283    if (aShapedText->IsClusterStart(idx) ||
   2284        clusterStart == -std::numeric_limits<float>::infinity()) {
   2285      clusterStart = inlineCoord;
   2286    }
   2287    if (aShapedText->CharMayHaveEmphasisMark(idx)) {
   2288      shouldDrawEmphasisMark = true;
   2289    }
   2290    inlineCoord += aParams.direction * aShapedText->GetAdvanceForGlyph(idx);
   2291    if (shouldDrawEmphasisMark &&
   2292        (i + 1 == aCount || aShapedText->IsClusterStart(idx + 1))) {
   2293      float clusterAdvance = inlineCoord - clusterStart;
   2294      // Move the coord backward to get the needed start point.
   2295      float delta = (clusterAdvance + aParams.advance) / 2;
   2296      inlineCoord -= delta;
   2297      aParams.mark->Draw(markRange, *aPt, params);
   2298      inlineCoord += delta;
   2299      shouldDrawEmphasisMark = false;
   2300    }
   2301    if (aParams.spacing) {
   2302      inlineCoord += aParams.direction * aParams.spacing[i].mAfter;
   2303    }
   2304  }
   2305 }
   2306 
   2307 void gfxFont::Draw(const gfxTextRun* aTextRun, uint32_t aStart, uint32_t aEnd,
   2308                   gfx::Point* aPt, TextRunDrawParams& aRunParams,
   2309                   gfx::ShapedTextFlags aOrientation) {
   2310  NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
   2311                   !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
   2312               "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or "
   2313               "GLYPH_STROKE_UNDERNEATH");
   2314 
   2315  if (aStart >= aEnd) {
   2316    return;
   2317  }
   2318 
   2319  FontDrawParams fontParams;
   2320 
   2321  if (aRunParams.drawOpts) {
   2322    fontParams.drawOptions = *aRunParams.drawOpts;
   2323  }
   2324 
   2325  fontParams.scaledFont = GetScaledFont(aRunParams);
   2326  if (!fontParams.scaledFont) {
   2327    return;
   2328  }
   2329  auto* textDrawer = aRunParams.textDrawer;
   2330 
   2331  fontParams.obliqueSkew = SkewForSyntheticOblique();
   2332  fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
   2333  fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
   2334  fontParams.hasTextShadow = aRunParams.hasTextShadow;
   2335  fontParams.contextPaint = aRunParams.runContextPaint;
   2336 
   2337  if (fontParams.haveColorGlyphs && !UseNativeColrFontSupport()) {
   2338    DeviceColor ctxColor;
   2339    fontParams.currentColor = aRunParams.context->GetDeviceColor(ctxColor)
   2340                                  ? sRGBColor::FromABGR(ctxColor.ToABGR())
   2341                                  : sRGBColor::OpaqueBlack();
   2342    fontParams.palette = aRunParams.paletteCache.GetPaletteFor(
   2343        GetFontEntry(), aRunParams.fontPalette);
   2344  }
   2345 
   2346  if (textDrawer) {
   2347    fontParams.isVerticalFont = aRunParams.isVerticalRun;
   2348  } else {
   2349    fontParams.isVerticalFont =
   2350        aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
   2351  }
   2352 
   2353  gfxContextMatrixAutoSaveRestore matrixRestore;
   2354  layout::TextDrawTarget::AutoRestoreWRGlyphFlags glyphFlagsRestore;
   2355 
   2356  // Save the current baseline offset for restoring later, in case it is
   2357  // modified.
   2358  float& baseline = fontParams.isVerticalFont ? aPt->x.value : aPt->y.value;
   2359  float origBaseline = baseline;
   2360 
   2361  // The point may be advanced in local-space, while the resulting point on
   2362  // return must be advanced in transformed space. So save the original point so
   2363  // we can properly transform the advance later.
   2364  gfx::Point origPt = *aPt;
   2365  const gfx::Matrix* offsetMatrix = nullptr;
   2366 
   2367  // Default to advancing along the +X direction (-X if RTL).
   2368  fontParams.advanceDirection = aRunParams.isRTL ? -1.0f : 1.0f;
   2369  // Default to offsetting baseline downward along the +Y direction.
   2370  float baselineDir = 1.0f;
   2371  // The direction of sideways rotation, if applicable.
   2372  // -1 for rotating left/counter-clockwise
   2373  // 1 for rotating right/clockwise
   2374  // 0 for no rotation
   2375  float sidewaysDir =
   2376      (aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
   2377           ? -1.0f
   2378           : (aOrientation ==
   2379                      gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
   2380                  ? 1.0f
   2381                  : 0.0f));
   2382  // If we're rendering a sideways run, we need to push a rotation transform to
   2383  // the context.
   2384  if (sidewaysDir != 0.0f) {
   2385    if (textDrawer) {
   2386      // For WebRender, we can't use a DrawTarget transform and must instead use
   2387      // flags that locally transform the glyph, without affecting the glyph
   2388      // origin. The glyph origins must thus be offset in the transformed
   2389      // directions (instead of local-space directions). Modify the advance and
   2390      // baseline directions to account for the indicated transform.
   2391 
   2392      // The default text orientation is down being +Y and right being +X.
   2393      // Rotating 90 degrees left/CCW makes down be +X and right be -Y.
   2394      // Rotating 90 degrees right/CW makes down be -X and right be +Y.
   2395      // Thus the advance direction (moving right) is just sidewaysDir,
   2396      // i.e. negative along Y axis if rotated left and positive if
   2397      // rotated right.
   2398      fontParams.advanceDirection *= sidewaysDir;
   2399      // The baseline direction (moving down) is negated relative to the
   2400      // advance direction for sideways transforms.
   2401      baselineDir *= -sidewaysDir;
   2402 
   2403      glyphFlagsRestore.Save(textDrawer);
   2404      // Set the transform flags accordingly. Both sideways rotations transpose
   2405      // X and Y, while left rotation flips the resulting Y axis, and right
   2406      // rotation flips the resulting X axis.
   2407      textDrawer->SetWRGlyphFlags(
   2408          textDrawer->GetWRGlyphFlags() | wr::FontInstanceFlags::TRANSPOSE |
   2409          (aOrientation ==
   2410                   gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
   2411               ? wr::FontInstanceFlags::FLIP_Y
   2412               : wr::FontInstanceFlags::FLIP_X));
   2413      // We also need to set up a transform for the glyph offset vector that
   2414      // may be present in DetailedGlyph records.
   2415      static const gfx::Matrix kSidewaysLeft = {0, -1, 1, 0, 0, 0};
   2416      static const gfx::Matrix kSidewaysRight = {0, 1, -1, 0, 0, 0};
   2417      offsetMatrix =
   2418          (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT)
   2419              ? &kSidewaysLeft
   2420              : &kSidewaysRight;
   2421    } else {
   2422      // For non-WebRender targets, just push a rotation transform.
   2423      matrixRestore.SetContext(aRunParams.context);
   2424      gfxPoint p(aPt->x * aRunParams.devPerApp, aPt->y * aRunParams.devPerApp);
   2425      // Get a matrix we can use to draw the (horizontally-shaped) textrun
   2426      // with 90-degree CW rotation.
   2427      const gfxFloat rotation = sidewaysDir * M_PI / 2.0f;
   2428      gfxMatrix mat = aRunParams.context->CurrentMatrixDouble()
   2429                          .PreTranslate(p)
   2430                          .  // translate origin for rotation
   2431                      PreRotate(rotation)
   2432                          .  // turn 90deg CCW (sideways-left) or CW (*-right)
   2433                      PreTranslate(-p);  // undo the translation
   2434 
   2435      aRunParams.context->SetMatrixDouble(mat);
   2436    }
   2437 
   2438    // If we're drawing rotated horizontal text for an element styled
   2439    // text-orientation:mixed, the dominant baseline will be vertical-
   2440    // centered. So in this case, we need to adjust the position so that
   2441    // the rotated horizontal text (which uses an alphabetic baseline) will
   2442    // look OK when juxtaposed with upright glyphs (rendered on a centered
   2443    // vertical baseline). The adjustment here is somewhat ad hoc; we
   2444    // should eventually look for baseline tables[1] in the fonts and use
   2445    // those if available.
   2446    // [1] See http://www.microsoft.com/typography/otspec/base.htm
   2447    if (aTextRun->UseCenterBaseline()) {
   2448      const Metrics& metrics = GetMetrics(nsFontMetrics::eHorizontal);
   2449      float baseAdj = (metrics.emAscent - metrics.emDescent) / 2;
   2450      baseline += baseAdj * aTextRun->GetAppUnitsPerDevUnit() * baselineDir;
   2451    }
   2452  } else if (textDrawer &&
   2453             aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) {
   2454    glyphFlagsRestore.Save(textDrawer);
   2455    textDrawer->SetWRGlyphFlags(textDrawer->GetWRGlyphFlags() |
   2456                                wr::FontInstanceFlags::VERTICAL);
   2457  }
   2458 
   2459  if (fontParams.obliqueSkew != 0.0f && !fontParams.isVerticalFont &&
   2460      !textDrawer) {
   2461    // Adjust matrix for synthetic-oblique, except if we're doing vertical-
   2462    // upright text, in which case this will be handled for each glyph
   2463    // individually in DrawOneGlyph.
   2464    if (!matrixRestore.HasMatrix()) {
   2465      matrixRestore.SetContext(aRunParams.context);
   2466    }
   2467    gfx::Point p(aPt->x * aRunParams.devPerApp, aPt->y * aRunParams.devPerApp);
   2468    gfx::Matrix mat =
   2469        aRunParams.context->CurrentMatrix()
   2470            .PreTranslate(p)
   2471            .PreMultiply(gfx::Matrix(1, 0, -fontParams.obliqueSkew, 1, 0, 0))
   2472            .PreTranslate(-p);
   2473    aRunParams.context->SetMatrix(mat);
   2474  }
   2475 
   2476  RefPtr<SVGContextPaint> contextPaint;
   2477  if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
   2478    // If no pattern is specified for fill, use the current pattern
   2479    NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
   2480                 "no pattern supplied for stroking text");
   2481    RefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
   2482    contextPaint = new SimpleTextContextPaint(
   2483        fillPattern, nullptr, aRunParams.context->CurrentMatrixDouble());
   2484    fontParams.contextPaint = contextPaint.get();
   2485  }
   2486 
   2487  // Synthetic-bold strikes are each offset one device pixel in run direction
   2488  // (these values are only needed if ApplySyntheticBold() is true).
   2489  // If drawing via webrender, it will do multistrike internally so we don't
   2490  // need to handle it here.
   2491  bool doMultistrikeBold = ApplySyntheticBold() && !textDrawer;
   2492  if (doMultistrikeBold) {
   2493    // For screen display, we want to try and repeat strikes with an offset of
   2494    // one device pixel, accounting for zoom or other transforms that may be
   2495    // in effect, so compute x-axis scale factor from the drawtarget.
   2496    // However, when generating PDF output the drawtarget's transform does not
   2497    // really bear any relation to "device pixels", and may result in an
   2498    // excessively large offset relative to the font size (bug 1823888), so
   2499    // we limit it based on the used font size to avoid this.
   2500    // The constant 48.0 reflects the threshold where the calculation in
   2501    // gfxFont::GetSyntheticBoldOffset() switches to a simple origin-based
   2502    // slope, though the exact value is somewhat arbitrary; it's selected to
   2503    // allow a visible amount of boldness while preventing the offset from
   2504    // becoming "large" in relation to the glyphs.
   2505    Float xscale =
   2506        std::min<Float>(GetAdjustedSize() / 48.0,
   2507                        CalcXScale(aRunParams.context->GetDrawTarget()));
   2508    fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
   2509    if (xscale != 0.0) {
   2510      static const int32_t kMaxExtraStrikes = 128;
   2511      gfxFloat extraStrikes = GetSyntheticBoldOffset() / xscale;
   2512      if (extraStrikes > kMaxExtraStrikes) {
   2513        // if too many strikes are required, limit them and increase the step
   2514        // size to compensate
   2515        fontParams.extraStrikes = kMaxExtraStrikes;
   2516        fontParams.synBoldOnePixelOffset = aRunParams.direction *
   2517                                           GetSyntheticBoldOffset() /
   2518                                           fontParams.extraStrikes;
   2519      } else {
   2520        // use as many strikes as needed for the increased advance
   2521        fontParams.extraStrikes = NS_lroundf(std::max(1.0, extraStrikes));
   2522      }
   2523    } else {
   2524      // Degenerate transform?!
   2525      fontParams.extraStrikes = 0;
   2526    }
   2527  } else {
   2528    fontParams.synBoldOnePixelOffset = 0;
   2529    fontParams.extraStrikes = 0;
   2530  }
   2531 
   2532  // Figure out the maximum extents for the font, accounting for synthetic
   2533  // oblique and bold.
   2534  if (mFUnitsConvFactor > 0.0) {
   2535    fontParams.fontExtents = GetFontEntry()->GetFontExtents(mFUnitsConvFactor);
   2536  } else {
   2537    // Was it not an sfnt? Maybe on Linux... use arbitrary huge extents, so we
   2538    // don't inadvertently clip stuff. A bit less efficient than true extents,
   2539    // but this should be extremely rare.
   2540    auto size = GetAdjustedSize();
   2541    fontParams.fontExtents = Rect(-2 * size, -2 * size, 5 * size, 5 * size);
   2542  }
   2543  if (fontParams.obliqueSkew != 0.0f) {
   2544    gfx::Point p(fontParams.fontExtents.x, fontParams.fontExtents.y);
   2545    gfx::Matrix skew(1, 0, fontParams.obliqueSkew, 1, 0, 0);
   2546    fontParams.fontExtents = skew.TransformBounds(fontParams.fontExtents);
   2547  }
   2548  if (fontParams.extraStrikes) {
   2549    if (fontParams.isVerticalFont) {
   2550      fontParams.fontExtents.height +=
   2551          float(fontParams.extraStrikes) * fontParams.synBoldOnePixelOffset;
   2552    } else {
   2553      fontParams.fontExtents.width +=
   2554          float(fontParams.extraStrikes) * fontParams.synBoldOnePixelOffset;
   2555    }
   2556  }
   2557 
   2558  bool oldSubpixelAA = aRunParams.dt->GetPermitSubpixelAA();
   2559  if (!AllowSubpixelAA()) {
   2560    aRunParams.dt->SetPermitSubpixelAA(false);
   2561  }
   2562 
   2563  Matrix mat;
   2564  Matrix oldMat = aRunParams.dt->GetTransform();
   2565 
   2566  fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
   2567 
   2568  if (mStyle.baselineOffset != 0.0) {
   2569    baseline +=
   2570        mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit() * baselineDir;
   2571  }
   2572 
   2573  bool emittedGlyphs;
   2574  {
   2575    // Select appropriate version of the templated DrawGlyphs method
   2576    // to output glyphs to the buffer, depending on complexity needed
   2577    // for the type of font, and whether added inter-glyph spacing
   2578    // is specified.
   2579    GlyphBufferAzure buffer(aRunParams, fontParams);
   2580    if (fontParams.haveSVGGlyphs || fontParams.haveColorGlyphs ||
   2581        fontParams.extraStrikes ||
   2582        (fontParams.obliqueSkew != 0.0f && fontParams.isVerticalFont &&
   2583         !textDrawer)) {
   2584      if (aRunParams.spacing) {
   2585        emittedGlyphs =
   2586            DrawGlyphs<FontComplexityT::ComplexFont, SpacingT::HasSpacing>(
   2587                aTextRun, aStart, aEnd - aStart, aPt, offsetMatrix, buffer);
   2588      } else {
   2589        emittedGlyphs =
   2590            DrawGlyphs<FontComplexityT::ComplexFont, SpacingT::NoSpacing>(
   2591                aTextRun, aStart, aEnd - aStart, aPt, offsetMatrix, buffer);
   2592      }
   2593    } else {
   2594      if (aRunParams.spacing) {
   2595        emittedGlyphs =
   2596            DrawGlyphs<FontComplexityT::SimpleFont, SpacingT::HasSpacing>(
   2597                aTextRun, aStart, aEnd - aStart, aPt, offsetMatrix, buffer);
   2598      } else {
   2599        emittedGlyphs =
   2600            DrawGlyphs<FontComplexityT::SimpleFont, SpacingT::NoSpacing>(
   2601                aTextRun, aStart, aEnd - aStart, aPt, offsetMatrix, buffer);
   2602      }
   2603    }
   2604  }
   2605 
   2606  baseline = origBaseline;
   2607 
   2608  if (aRunParams.callbacks && emittedGlyphs) {
   2609    aRunParams.callbacks->NotifyGlyphPathEmitted();
   2610  }
   2611 
   2612  aRunParams.dt->SetTransform(oldMat);
   2613  aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
   2614 
   2615  if (sidewaysDir != 0.0f && !textDrawer) {
   2616    // Adjust updated aPt to account for the transform we were using.
   2617    // The advance happened horizontally in local-space, but the transformed
   2618    // sideways advance is actually vertical, with sign depending on the
   2619    // direction of rotation.
   2620    float advance = aPt->x - origPt.x;
   2621    *aPt = gfx::Point(origPt.x, origPt.y + advance * sidewaysDir);
   2622  }
   2623 }
   2624 
   2625 bool gfxFont::RenderSVGGlyph(gfxContext* aContext,
   2626                             layout::TextDrawTarget* aTextDrawer,
   2627                             gfx::Point aPoint, uint32_t aGlyphId,
   2628                             SVGContextPaint* aContextPaint) const {
   2629  if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
   2630    return false;
   2631  }
   2632 
   2633  if (aTextDrawer) {
   2634    // WebRender doesn't support SVG Glyphs.
   2635    // (pretend to succeed, output doesn't matter, we will emit a blob)
   2636    aTextDrawer->FoundUnsupportedFeature();
   2637    return true;
   2638  }
   2639 
   2640  const gfxFloat devUnitsPerSVGUnit =
   2641      GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
   2642  gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
   2643 
   2644  aContext->SetMatrix(aContext->CurrentMatrix()
   2645                          .PreTranslate(aPoint.x, aPoint.y)
   2646                          .PreScale(devUnitsPerSVGUnit, devUnitsPerSVGUnit));
   2647 
   2648  aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
   2649 
   2650  GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, aContextPaint);
   2651  aContext->NewPath();
   2652  return true;
   2653 }
   2654 
   2655 bool gfxFont::RenderSVGGlyph(gfxContext* aContext,
   2656                             layout::TextDrawTarget* aTextDrawer,
   2657                             gfx::Point aPoint, uint32_t aGlyphId,
   2658                             SVGContextPaint* aContextPaint,
   2659                             gfxTextRunDrawCallbacks* aCallbacks,
   2660                             bool& aEmittedGlyphs) const {
   2661  if (aCallbacks && aEmittedGlyphs) {
   2662    aCallbacks->NotifyGlyphPathEmitted();
   2663    aEmittedGlyphs = false;
   2664  }
   2665  return RenderSVGGlyph(aContext, aTextDrawer, aPoint, aGlyphId, aContextPaint);
   2666 }
   2667 
   2668 bool gfxFont::RenderColorGlyph(DrawTarget* aDrawTarget, gfxContext* aContext,
   2669                               layout::TextDrawTarget* aTextDrawer,
   2670                               const FontDrawParams& aFontParams,
   2671                               const Point& aPoint, uint32_t aGlyphId) {
   2672  if (aTextDrawer && aFontParams.hasTextShadow) {
   2673    aTextDrawer->FoundUnsupportedFeature();
   2674    return true;
   2675  }
   2676 
   2677  auto* colr = GetFontEntry()->GetCOLR();
   2678  const auto* paintGraph = COLRFonts::GetGlyphPaintGraph(colr, aGlyphId);
   2679  const gfxHarfBuzzShaper* hbShaper = nullptr;
   2680  if (paintGraph) {
   2681    // We need the hbShaper to get color glyph bounds, so check that it's
   2682    // usable.
   2683    hbShaper = GetHarfBuzzShaper();
   2684    if (!hbShaper && !hbShaper->IsInitialized()) {
   2685      return false;
   2686    }
   2687    if (aTextDrawer) {
   2688      aTextDrawer->FoundUnsupportedFeature();
   2689      return true;
   2690    }
   2691  }
   2692  const auto* layers =
   2693      paintGraph ? nullptr : COLRFonts::GetGlyphLayers(colr, aGlyphId);
   2694 
   2695  if (!paintGraph && !layers) {
   2696    return false;
   2697  }
   2698 
   2699  // For reasonable font sizes, use a cache of rasterized glyphs.
   2700  bool useCache = GetAdjustedSize() <= 256.0;
   2701 
   2702  // If the composition op is not OVER, rasterize to a temporary surface
   2703  // and then composite to the destination, even if we're not caching.
   2704  // But we can't do this if the target is a TextDrawTarget, as it doesn't
   2705  // support DrawSurface.
   2706  RefPtr<SourceSurface> snapshot;
   2707  if ((useCache ||
   2708       aFontParams.drawOptions.mCompositionOp != CompositionOp::OP_OVER) &&
   2709      aDrawTarget->GetBackendType() != BackendType::WEBRENDER_TEXT) {
   2710    AutoWriteLock lock(mLock);
   2711    if (!mColorGlyphCache && useCache) {
   2712      mColorGlyphCache = MakeUnique<ColorGlyphCache>();
   2713    }
   2714 
   2715    Rect bounds;
   2716    if (paintGraph) {
   2717      bounds = COLRFonts::GetColorGlyphBounds(
   2718          colr, hbShaper->GetHBFont(), aGlyphId, aDrawTarget,
   2719          aFontParams.scaledFont, mFUnitsConvFactor);
   2720    } else {
   2721      bounds = GetFontEntry()->GetFontExtents(mFUnitsConvFactor);
   2722    }
   2723    bounds.RoundOut();
   2724 
   2725    // Tell the cache what colors we're using; if they have changed, it
   2726    // will discard any currently-cached entries.
   2727    HashMap<uint32_t, RefPtr<SourceSurface>>::AddPtr cached;
   2728    if (useCache) {
   2729      mColorGlyphCache->SetColors(aFontParams.currentColor,
   2730                                  aFontParams.palette);
   2731      cached = mColorGlyphCache->mCache.lookupForAdd(aGlyphId);
   2732      if (cached) {
   2733        snapshot = cached->value();
   2734      }
   2735    }
   2736 
   2737    const int kScale = 2;
   2738    if (!snapshot) {
   2739      // Create a temporary DrawTarget and render the glyph to it.
   2740      IntSize size(int(bounds.width), int(bounds.height));
   2741      SurfaceFormat format = SurfaceFormat::B8G8R8A8;
   2742      RefPtr target =
   2743          Factory::CreateDrawTarget(BackendType::SKIA, size * kScale, format);
   2744      if (target) {
   2745        // Use OP_OVER and opaque alpha to create the glyph snapshot.
   2746        Matrix m;
   2747        m.PreScale(kScale, kScale);
   2748        target->SetTransform(m);
   2749        DrawOptions drawOptions(aFontParams.drawOptions);
   2750        drawOptions.mCompositionOp = CompositionOp::OP_OVER;
   2751        drawOptions.mAlpha = 1.0f;
   2752        bool ok = false;
   2753        if (paintGraph) {
   2754          ok = COLRFonts::PaintGlyphGraph(
   2755              colr, hbShaper->GetHBFont(), paintGraph, target, nullptr,
   2756              aFontParams.scaledFont, drawOptions, -bounds.TopLeft(),
   2757              aFontParams.currentColor, aFontParams.palette->Colors(), aGlyphId,
   2758              mFUnitsConvFactor);
   2759        } else {
   2760          auto face(GetFontEntry()->GetHBFace());
   2761          ok = COLRFonts::PaintGlyphLayers(
   2762              colr, face, layers, target, nullptr, aFontParams.scaledFont,
   2763              drawOptions, -bounds.TopLeft(), aFontParams.currentColor,
   2764              aFontParams.palette->Colors());
   2765        }
   2766        if (ok) {
   2767          snapshot = target->Snapshot();
   2768          if (useCache) {
   2769            // Save a snapshot of the rendering in the cache.
   2770            // (We ignore potential failure here, and just paint the snapshot
   2771            // without caching it.)
   2772            (void)mColorGlyphCache->mCache.add(cached, aGlyphId, snapshot);
   2773          }
   2774        }
   2775      }
   2776    }
   2777    if (snapshot) {
   2778      // Paint the snapshot using the appropriate composition op.
   2779      Point snappedPoint = Point(roundf(aPoint.x), roundf(aPoint.y));
   2780      aDrawTarget->DrawSurface(
   2781          snapshot, Rect(snappedPoint + bounds.TopLeft(), bounds.Size()),
   2782          Rect(Point(), bounds.Size() * kScale), DrawSurfaceOptions(),
   2783          aFontParams.drawOptions);
   2784      return true;
   2785    }
   2786  }
   2787 
   2788  // If we didn't paint from a cached or temporary snapshot, just render
   2789  // directly to the destination drawTarget.
   2790  if (paintGraph) {
   2791    return COLRFonts::PaintGlyphGraph(
   2792        colr, hbShaper->GetHBFont(), paintGraph, aDrawTarget, aTextDrawer,
   2793        aFontParams.scaledFont, aFontParams.drawOptions, aPoint,
   2794        aFontParams.currentColor, aFontParams.palette->Colors(), aGlyphId,
   2795        mFUnitsConvFactor);
   2796  }
   2797 
   2798  if (layers) {
   2799    auto face(GetFontEntry()->GetHBFace());
   2800    return COLRFonts::PaintGlyphLayers(
   2801        colr, face, layers, aDrawTarget, aTextDrawer, aFontParams.scaledFont,
   2802        aFontParams.drawOptions, aPoint, aFontParams.currentColor,
   2803        aFontParams.palette->Colors());
   2804  }
   2805 
   2806  return false;
   2807 }
   2808 
   2809 void gfxFont::ColorGlyphCache::SetColors(sRGBColor aCurrentColor,
   2810                                         FontPalette* aPalette) {
   2811  if (aCurrentColor != mCurrentColor || aPalette != mPalette) {
   2812    mCache.clear();
   2813    mCurrentColor = aCurrentColor;
   2814    mPalette = aPalette;
   2815  }
   2816 }
   2817 
   2818 bool gfxFont::HasColorGlyphFor(uint32_t aCh, uint32_t aNextCh) {
   2819  // Bitmap fonts are assumed to provide "color" glyphs for all supported chars.
   2820  gfxFontEntry* fe = GetFontEntry();
   2821  if (fe->HasColorBitmapTable()) {
   2822    return true;
   2823  }
   2824  // Use harfbuzz shaper to look up the default glyph ID for the character.
   2825  auto* shaper = GetHarfBuzzShaper();
   2826  if (!shaper) {
   2827    return false;
   2828  }
   2829  uint32_t gid = 0;
   2830  if (gfxFontUtils::IsVarSelector(aNextCh)) {
   2831    gid = shaper->GetVariationGlyph(aCh, aNextCh);
   2832    if (gid) {
   2833      if (aNextCh == kVariationSelector16) {
   2834        // If the font explicitly supports the character + VS16, we accept it
   2835        // as implying it will provide an emoji-style glyph.
   2836        return true;
   2837      }
   2838      if (aNextCh == kVariationSelector15) {
   2839        // Explicit support for VS15 implies a text-style glyph.
   2840        return false;
   2841      }
   2842    }
   2843  }
   2844  gid = shaper->GetNominalGlyph(aCh);
   2845  if (!gid) {
   2846    return false;
   2847  }
   2848 
   2849  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1801521:
   2850  // Emoji special-case: flag sequences NOT based on Regional Indicator pairs
   2851  // use the BLACK FLAG character plus a series of plane-14 TAG LETTERs, e.g.
   2852  //   England = <black-flag, tag-G, tag-B, tag-E, tag-N, tag-G, tag-cancel>
   2853  // Here, we don't check for support of the entire sequence (too much
   2854  // expensive lookahead), but we check that the font at least supports the
   2855  // first of the tag letter codes, because if it doesn't, we're at risk of
   2856  // just getting an undifferentiated black flag glyph.
   2857  if (gfxFontUtils::IsEmojiFlagAndTag(aCh, aNextCh)) {
   2858    if (!shaper->GetNominalGlyph(aNextCh)) {
   2859      return false;
   2860    }
   2861  }
   2862 
   2863  // Check if there is a COLR/CPAL or SVG glyph for this ID.
   2864  if (fe->TryGetColorGlyphs() &&
   2865      (COLRFonts::GetGlyphPaintGraph(fe->GetCOLR(), gid) ||
   2866       COLRFonts::GetGlyphLayers(fe->GetCOLR(), gid))) {
   2867    return true;
   2868  }
   2869  if (fe->TryGetSVGData(this) && fe->HasSVGGlyph(gid)) {
   2870    return true;
   2871  }
   2872  return false;
   2873 }
   2874 
   2875 static void UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax) {
   2876  *aDestMin = std::min(*aDestMin, aX);
   2877  *aDestMax = std::max(*aDestMax, aX);
   2878 }
   2879 
   2880 // We get precise glyph extents if the textrun creator requested them, or
   2881 // if the font is a user font --- in which case the author may be relying
   2882 // on overflowing glyphs.
   2883 static bool NeedsGlyphExtents(gfxFont* aFont, const gfxTextRun* aTextRun) {
   2884  return (aTextRun->GetFlags() &
   2885          gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX) ||
   2886         aFont->GetFontEntry()->IsUserFont();
   2887 }
   2888 
   2889 bool gfxFont::IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget,
   2890                                    const gfxTextRun* aTextRun) {
   2891  gfxFontEntry::LazyFlag flag = mFontEntry->mSpaceGlyphIsInvisible;
   2892  if (flag == gfxFontEntry::LazyFlag::Uninitialized &&
   2893      GetAdjustedSize() >= 1.0) {
   2894    gfxGlyphExtents* extents =
   2895        GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
   2896    gfxRect glyphExtents;
   2897    flag = extents->GetTightGlyphExtentsAppUnits(
   2898               this, aRefDrawTarget, GetSpaceGlyph(), &glyphExtents) &&
   2899                   glyphExtents.IsEmpty()
   2900               ? gfxFontEntry::LazyFlag::Yes
   2901               : gfxFontEntry::LazyFlag::No;
   2902    mFontEntry->mSpaceGlyphIsInvisible = flag;
   2903  }
   2904  return flag == gfxFontEntry::LazyFlag::Yes;
   2905 }
   2906 
   2907 bool gfxFont::MeasureGlyphs(const gfxTextRun* aTextRun, uint32_t aStart,
   2908                            uint32_t aEnd, BoundingBoxType aBoundingBoxType,
   2909                            DrawTarget* aRefDrawTarget, Spacing* aSpacing,
   2910                            gfxGlyphExtents* aExtents, bool aIsRTL,
   2911                            bool aNeedsGlyphExtents, RunMetrics& aMetrics,
   2912                            gfxFloat* aAdvanceMin, gfxFloat* aAdvanceMax) {
   2913  const gfxTextRun::CompressedGlyph* charGlyphs =
   2914      aTextRun->GetCharacterGlyphs();
   2915  uint32_t spaceGlyph = GetSpaceGlyph();
   2916  bool allGlyphsInvisible = true;
   2917 
   2918  AutoReadLock lock(aExtents->mLock);
   2919 
   2920  double x = 0;
   2921  for (uint32_t i = aStart; i < aEnd; ++i) {
   2922    if (aSpacing) {
   2923      x += aSpacing->mBefore;
   2924    }
   2925    const gfxTextRun::CompressedGlyph* glyphData = &charGlyphs[i];
   2926    if (glyphData->IsSimpleGlyph()) {
   2927      double advance = glyphData->GetSimpleAdvance();
   2928      uint32_t glyphIndex = glyphData->GetSimpleGlyph();
   2929      if (allGlyphsInvisible) {
   2930        if (glyphIndex != spaceGlyph) {
   2931          allGlyphsInvisible = false;
   2932        } else {
   2933          gfxFontEntry::LazyFlag flag = mFontEntry->mSpaceGlyphIsInvisible;
   2934          if (flag == gfxFontEntry::LazyFlag::Uninitialized &&
   2935              GetAdjustedSize() >= 1.0) {
   2936            gfxRect glyphExtents;
   2937            flag = aExtents->GetTightGlyphExtentsAppUnitsLocked(
   2938                       this, aRefDrawTarget, spaceGlyph, &glyphExtents) &&
   2939                           glyphExtents.IsEmpty()
   2940                       ? gfxFontEntry::LazyFlag::Yes
   2941                       : gfxFontEntry::LazyFlag::No;
   2942            mFontEntry->mSpaceGlyphIsInvisible = flag;
   2943          }
   2944          if (flag == gfxFontEntry::LazyFlag::No) {
   2945            allGlyphsInvisible = false;
   2946          }
   2947        }
   2948      }
   2949      // Only get the real glyph horizontal extent if we were asked
   2950      // for the tight bounding box or we're in quality mode
   2951      if (aBoundingBoxType != LOOSE_INK_EXTENTS || aNeedsGlyphExtents) {
   2952        uint16_t extentsWidth =
   2953            aExtents->GetContainedGlyphWidthAppUnitsLocked(glyphIndex);
   2954        if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
   2955            aBoundingBoxType == LOOSE_INK_EXTENTS) {
   2956          UnionRange(x, aAdvanceMin, aAdvanceMax);
   2957          UnionRange(x + extentsWidth, aAdvanceMin, aAdvanceMax);
   2958        } else {
   2959          gfxRect glyphRect;
   2960          if (!aExtents->GetTightGlyphExtentsAppUnitsLocked(
   2961                  this, aRefDrawTarget, glyphIndex, &glyphRect)) {
   2962            glyphRect = gfxRect(0, aMetrics.mBoundingBox.Y(), advance,
   2963                                aMetrics.mBoundingBox.Height());
   2964          }
   2965          if (aIsRTL) {
   2966            // In effect, swap left and right sidebearings of the glyph, for
   2967            // proper accumulation of potentially-overlapping glyph rects.
   2968            glyphRect.MoveToX(advance - glyphRect.XMost());
   2969          }
   2970          glyphRect.MoveByX(x);
   2971          aMetrics.mBoundingBox = aMetrics.mBoundingBox.Union(glyphRect);
   2972        }
   2973      }
   2974      x += advance;
   2975    } else {
   2976      allGlyphsInvisible = false;
   2977      uint32_t glyphCount = glyphData->GetGlyphCount();
   2978      if (glyphCount > 0) {
   2979        const gfxTextRun::DetailedGlyph* details =
   2980            aTextRun->GetDetailedGlyphs(i);
   2981        NS_ASSERTION(details != nullptr,
   2982                     "detailedGlyph record should not be missing!");
   2983        uint32_t j;
   2984        for (j = 0; j < glyphCount; ++j, ++details) {
   2985          uint32_t glyphIndex = details->mGlyphID;
   2986          double advance = details->mAdvance;
   2987          gfxRect glyphRect;
   2988          if (glyphData->IsMissing() ||
   2989              !aExtents->GetTightGlyphExtentsAppUnitsLocked(
   2990                  this, aRefDrawTarget, glyphIndex, &glyphRect)) {
   2991            // We might have failed to get glyph extents due to
   2992            // OOM or something
   2993            glyphRect = gfxRect(0, -aMetrics.mAscent, advance,
   2994                                aMetrics.mAscent + aMetrics.mDescent);
   2995          }
   2996          if (aIsRTL) {
   2997            // Swap left/right sidebearings of the glyph, because we're doing
   2998            // mirrored measurement.
   2999            glyphRect.MoveToX(advance - glyphRect.XMost());
   3000            // Move to current x position, mirroring any x-offset amount.
   3001            glyphRect.MoveByX(x - details->mOffset.x);
   3002          } else {
   3003            glyphRect.MoveByX(x + details->mOffset.x);
   3004          }
   3005          glyphRect.MoveByY(details->mOffset.y);
   3006          aMetrics.mBoundingBox = aMetrics.mBoundingBox.Union(glyphRect);
   3007          x += advance;
   3008        }
   3009      }
   3010    }
   3011    if (aSpacing) {
   3012      x += aSpacing->mAfter;
   3013      ++aSpacing;
   3014    }
   3015  }
   3016 
   3017  aMetrics.mAdvanceWidth = x;
   3018  return allGlyphsInvisible;
   3019 }
   3020 
   3021 bool gfxFont::MeasureGlyphs(const gfxTextRun* aTextRun, uint32_t aStart,
   3022                            uint32_t aEnd, BoundingBoxType aBoundingBoxType,
   3023                            DrawTarget* aRefDrawTarget, Spacing* aSpacing,
   3024                            bool aIsRTL, RunMetrics& aMetrics) {
   3025  const gfxTextRun::CompressedGlyph* charGlyphs =
   3026      aTextRun->GetCharacterGlyphs();
   3027  double x = 0;
   3028  if (aSpacing) {
   3029    x += aSpacing[0].mBefore;
   3030  }
   3031  uint32_t spaceGlyph = GetSpaceGlyph();
   3032  bool allGlyphsInvisible = true;
   3033 
   3034  for (uint32_t i = aStart; i < aEnd; ++i) {
   3035    const gfxTextRun::CompressedGlyph* glyphData = &charGlyphs[i];
   3036    if (glyphData->IsSimpleGlyph()) {
   3037      double advance = glyphData->GetSimpleAdvance();
   3038      uint32_t glyphIndex = glyphData->GetSimpleGlyph();
   3039      if (allGlyphsInvisible &&
   3040          (glyphIndex != spaceGlyph ||
   3041           !IsSpaceGlyphInvisible(aRefDrawTarget, aTextRun))) {
   3042        allGlyphsInvisible = false;
   3043      }
   3044      x += advance;
   3045    } else {
   3046      allGlyphsInvisible = false;
   3047      uint32_t glyphCount = glyphData->GetGlyphCount();
   3048      if (glyphCount > 0) {
   3049        const gfxTextRun::DetailedGlyph* details =
   3050            aTextRun->GetDetailedGlyphs(i);
   3051        NS_ASSERTION(details != nullptr,
   3052                     "detailedGlyph record should not be missing!");
   3053        uint32_t j;
   3054        for (j = 0; j < glyphCount; ++j, ++details) {
   3055          double advance = details->mAdvance;
   3056          gfxRect glyphRect(0, -aMetrics.mAscent, advance,
   3057                            aMetrics.mAscent + aMetrics.mDescent);
   3058          if (aIsRTL) {
   3059            // Swap left/right sidebearings of the glyph, because we're doing
   3060            // mirrored measurement.
   3061            glyphRect.MoveToX(advance - glyphRect.XMost());
   3062            // Move to current x position, mirroring any x-offset amount.
   3063            glyphRect.MoveByX(x - details->mOffset.x);
   3064          } else {
   3065            glyphRect.MoveByX(x + details->mOffset.x);
   3066          }
   3067          glyphRect.MoveByY(details->mOffset.y);
   3068          aMetrics.mBoundingBox = aMetrics.mBoundingBox.Union(glyphRect);
   3069          x += advance;
   3070        }
   3071      }
   3072    }
   3073    if (aSpacing) {
   3074      double space = aSpacing[i - aStart].mAfter;
   3075      if (i + 1 < aEnd) {
   3076        space += aSpacing[i + 1 - aStart].mBefore;
   3077      }
   3078      x += space;
   3079    }
   3080  }
   3081 
   3082  aMetrics.mAdvanceWidth = x;
   3083  return allGlyphsInvisible;
   3084 }
   3085 
   3086 gfxFont::RunMetrics gfxFont::Measure(const gfxTextRun* aTextRun,
   3087                                     uint32_t aStart, uint32_t aEnd,
   3088                                     BoundingBoxType aBoundingBoxType,
   3089                                     DrawTarget* aRefDrawTarget,
   3090                                     Spacing* aSpacing,
   3091                                     gfx::ShapedTextFlags aOrientation) {
   3092  // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
   3093  // and the underlying cairo font may be antialiased,
   3094  // we need to create a copy in order to avoid getting cached extents.
   3095  // This is only used by MathML layout at present.
   3096  if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
   3097      mAntialiasOption != kAntialiasNone) {
   3098    gfxFont* nonAA = mNonAAFont;
   3099    if (!nonAA) {
   3100      nonAA = CopyWithAntialiasOption(kAntialiasNone);
   3101      if (nonAA) {
   3102        if (!mNonAAFont.compareExchange(nullptr, nonAA)) {
   3103          delete nonAA;
   3104          nonAA = mNonAAFont;
   3105        }
   3106      }
   3107    }
   3108    // if font subclass doesn't implement CopyWithAntialiasOption(),
   3109    // it will return null and we'll proceed to use the existing font
   3110    if (nonAA) {
   3111      return nonAA->Measure(aTextRun, aStart, aEnd,
   3112                            TIGHT_HINTED_OUTLINE_EXTENTS, aRefDrawTarget,
   3113                            aSpacing, aOrientation);
   3114    }
   3115  }
   3116 
   3117  const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
   3118  // Current position in appunits
   3119  Orientation orientation =
   3120      aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
   3121          ? nsFontMetrics::eVertical
   3122          : nsFontMetrics::eHorizontal;
   3123  const gfxFont::Metrics& fontMetrics = GetMetrics(orientation);
   3124 
   3125  gfxFloat baselineOffset = 0;
   3126  if (aTextRun->UseCenterBaseline() &&
   3127      orientation == nsFontMetrics::eHorizontal) {
   3128    // For a horizontal font being used in vertical writing mode with
   3129    // text-orientation:mixed, the overall metrics we're accumulating
   3130    // will be aimed at a center baseline. But this font's metrics were
   3131    // based on the alphabetic baseline. So we compute a baseline offset
   3132    // that will be applied to ascent/descent values and glyph rects
   3133    // to effectively shift them relative to the baseline.
   3134    // XXX Eventually we should probably use the BASE table, if present.
   3135    // But it usually isn't, so we need an ad hoc adjustment for now.
   3136    baselineOffset =
   3137        appUnitsPerDevUnit * (fontMetrics.emAscent - fontMetrics.emDescent) / 2;
   3138  }
   3139 
   3140  RunMetrics metrics;
   3141  metrics.mAscent = fontMetrics.maxAscent * appUnitsPerDevUnit;
   3142  metrics.mDescent = fontMetrics.maxDescent * appUnitsPerDevUnit;
   3143 
   3144  if (aStart == aEnd) {
   3145    // exit now before we look at aSpacing[0], which is undefined
   3146    metrics.mAscent -= baselineOffset;
   3147    metrics.mDescent += baselineOffset;
   3148    metrics.mBoundingBox =
   3149        gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
   3150    return metrics;
   3151  }
   3152 
   3153  gfxFloat advanceMin = 0, advanceMax = 0;
   3154  bool isRTL = aTextRun->IsRightToLeft();
   3155  bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
   3156  gfxGlyphExtents* extents =
   3157      ((aBoundingBoxType == LOOSE_INK_EXTENTS && !needsGlyphExtents &&
   3158        !aTextRun->HasDetailedGlyphs()) ||
   3159       MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero()))
   3160          ? nullptr
   3161          : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
   3162 
   3163  bool allGlyphsInvisible;
   3164  if (extents) {
   3165    allGlyphsInvisible = MeasureGlyphs(
   3166        aTextRun, aStart, aEnd, aBoundingBoxType, aRefDrawTarget, aSpacing,
   3167        extents, isRTL, needsGlyphExtents, metrics, &advanceMin, &advanceMax);
   3168  } else {
   3169    allGlyphsInvisible =
   3170        MeasureGlyphs(aTextRun, aStart, aEnd, aBoundingBoxType, aRefDrawTarget,
   3171                      aSpacing, isRTL, metrics);
   3172  }
   3173 
   3174  if (allGlyphsInvisible) {
   3175    metrics.mBoundingBox.SetEmpty();
   3176  } else if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
   3177    UnionRange(metrics.mAdvanceWidth, &advanceMin, &advanceMax);
   3178    gfxRect fontBox(advanceMin, -metrics.mAscent, advanceMax - advanceMin,
   3179                    metrics.mAscent + metrics.mDescent);
   3180    metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
   3181  }
   3182 
   3183  if (isRTL) {
   3184    // Reverse the effect of having swapped each glyph's sidebearings, to get
   3185    // the correct sidebearings of the merged bounding box.
   3186    metrics.mBoundingBox.MoveToX(metrics.mAdvanceWidth -
   3187                                 metrics.mBoundingBox.XMost());
   3188  }
   3189 
   3190  // If the font may be rendered with a fake-italic effect, we need to allow
   3191  // for the top-right of the glyphs being skewed to the right, and the
   3192  // bottom-left being skewed further left.
   3193  gfxFloat skew = SkewForSyntheticOblique();
   3194  if (skew != 0.0) {
   3195    gfxFloat extendLeftEdge, extendRightEdge;
   3196    if (orientation == nsFontMetrics::eVertical) {
   3197      // The glyph will actually be skewed vertically, but "left" and "right"
   3198      // here refer to line-left (physical top) and -right (bottom), so these
   3199      // are still the directions in which we need to extend the box.
   3200      extendLeftEdge = skew < 0.0 ? ceil(-skew * metrics.mBoundingBox.XMost())
   3201                                  : ceil(skew * -metrics.mBoundingBox.X());
   3202      extendRightEdge = skew < 0.0 ? ceil(-skew * -metrics.mBoundingBox.X())
   3203                                   : ceil(skew * metrics.mBoundingBox.XMost());
   3204    } else {
   3205      extendLeftEdge = skew < 0.0 ? ceil(-skew * -metrics.mBoundingBox.Y())
   3206                                  : ceil(skew * metrics.mBoundingBox.YMost());
   3207      extendRightEdge = skew < 0.0 ? ceil(-skew * metrics.mBoundingBox.YMost())
   3208                                   : ceil(skew * -metrics.mBoundingBox.Y());
   3209    }
   3210    metrics.mBoundingBox.SetWidth(metrics.mBoundingBox.Width() +
   3211                                  extendLeftEdge + extendRightEdge);
   3212    metrics.mBoundingBox.MoveByX(-extendLeftEdge);
   3213  }
   3214 
   3215  if (baselineOffset != 0) {
   3216    metrics.mAscent -= baselineOffset;
   3217    metrics.mDescent += baselineOffset;
   3218    metrics.mBoundingBox.MoveByY(baselineOffset);
   3219  }
   3220 
   3221  return metrics;
   3222 }
   3223 
   3224 bool gfxFont::AgeCachedWords() {
   3225  mozilla::AutoWriteLock lock(mLock);
   3226  if (mWordCache) {
   3227    for (auto it = mWordCache->modIter(); !it.done(); it.next()) {
   3228      auto& entry = it.get().value();
   3229      if (!entry) {
   3230        NS_ASSERTION(entry, "cache entry has no gfxShapedWord!");
   3231        it.remove();
   3232      } else if (entry->IncrementAge() == kShapedWordCacheMaxAge) {
   3233        it.remove();
   3234      }
   3235    }
   3236    mWordCache->compact();
   3237    return mWordCache->empty();
   3238  }
   3239  return true;
   3240 }
   3241 
   3242 void gfxFont::NotifyGlyphsChanged() const {
   3243  AutoReadLock lock(mLock);
   3244  uint32_t i, count = mGlyphExtentsArray.Length();
   3245  for (i = 0; i < count; ++i) {
   3246    // Flush cached extents array
   3247    mGlyphExtentsArray[i]->NotifyGlyphsChanged();
   3248  }
   3249 
   3250  if (mGlyphChangeObservers) {
   3251    for (const auto& key : *mGlyphChangeObservers) {
   3252      key->NotifyGlyphsChanged();
   3253    }
   3254  }
   3255 }
   3256 
   3257 // If aChar is a "word boundary" for shaped-word caching purposes, return it;
   3258 // else return 0.
   3259 static char16_t IsBoundarySpace(char16_t aChar, char16_t aNextChar) {
   3260  if ((aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar)) {
   3261    return aChar;
   3262  }
   3263  return 0;
   3264 }
   3265 
   3266 // In 8-bit text, there cannot be any cluster-extenders.
   3267 static uint8_t IsBoundarySpace(uint8_t aChar, uint8_t aNextChar) {
   3268  if (aChar == ' ' || aChar == 0x00A0) {
   3269    return aChar;
   3270  }
   3271  return 0;
   3272 }
   3273 
   3274 #ifdef __GNUC__
   3275 #  define GFX_MAYBE_UNUSED __attribute__((unused))
   3276 #else
   3277 #  define GFX_MAYBE_UNUSED
   3278 #endif
   3279 
   3280 template <typename T, typename Func>
   3281 bool gfxFont::ProcessShapedWordInternal(
   3282    DrawTarget* aDrawTarget, const T* aText, uint32_t aLength, uint32_t aHash,
   3283    Script aRunScript, nsAtom* aLanguage, bool aVertical,
   3284    int32_t aAppUnitsPerDevUnit, gfx::ShapedTextFlags aFlags,
   3285    RoundingFlags aRounding, gfxTextPerfMetrics* aTextPerf GFX_MAYBE_UNUSED,
   3286    Func aCallback) {
   3287  WordCacheKey key(aText, aLength, aHash, aRunScript, aLanguage,
   3288                   aAppUnitsPerDevUnit, aFlags, aRounding);
   3289  {
   3290    // If we have a word cache, attempt to look up the word in it.
   3291    AutoReadLock lock(mLock);
   3292    if (mWordCache) {
   3293      // if there's a cached entry for this word, just return it
   3294      if (auto entry = mWordCache->lookup(key)) {
   3295        entry->value()->ResetAge();
   3296 #ifndef RELEASE_OR_BETA
   3297        if (aTextPerf) {
   3298          // XXX we should make sure this is atomic
   3299          aTextPerf->current.wordCacheHit++;
   3300        }
   3301 #endif
   3302        aCallback(entry->value().get());
   3303        return true;
   3304      }
   3305    }
   3306  }
   3307 
   3308  // We didn't find a cached word (or don't even have a cache yet), so create
   3309  // a new gfxShapedWord and cache it. We don't have to lock during shaping,
   3310  // only when it comes time to cache the new entry.
   3311 
   3312  UniquePtr<gfxShapedWord> newShapedWord(
   3313      gfxShapedWord::Create(aText, aLength, aRunScript, aLanguage,
   3314                            aAppUnitsPerDevUnit, aFlags, aRounding));
   3315  if (!newShapedWord) {
   3316    NS_WARNING("failed to create gfxShapedWord - expect missing text");
   3317    return false;
   3318  }
   3319  DebugOnly<bool> ok =
   3320      ShapeText(aDrawTarget, aText, 0, aLength, aRunScript, aLanguage,
   3321                aVertical, aRounding, newShapedWord.get());
   3322  NS_WARNING_ASSERTION(ok, "failed to shape word - expect garbled text");
   3323 
   3324  {
   3325    // We're going to cache the new shaped word, so lock for writing now.
   3326    AutoWriteLock lock(mLock);
   3327    if (!mWordCache) {
   3328      mWordCache = MakeUnique<HashMap<WordCacheKey, UniquePtr<gfxShapedWord>,
   3329                                      WordCacheKey::HashPolicy>>();
   3330    } else {
   3331      // If the cache is getting too big, flush it and start over.
   3332      uint32_t wordCacheMaxEntries =
   3333          gfxPlatform::GetPlatform()->WordCacheMaxEntries();
   3334      if (mWordCache->count() > wordCacheMaxEntries) {
   3335        // Flush the cache if it is getting overly big.
   3336        NS_WARNING("flushing shaped-word cache");
   3337        ClearCachedWordsLocked();
   3338      }
   3339    }
   3340 
   3341    // Update key so that it references the text stored in the newShapedWord,
   3342    // which is guaranteed to live as long as the hashtable entry.
   3343    if ((key.mTextIs8Bit = newShapedWord->TextIs8Bit())) {
   3344      key.mText.mSingle = newShapedWord->Text8Bit();
   3345    } else {
   3346      key.mText.mDouble = newShapedWord->TextUnicode();
   3347    }
   3348    auto entry = mWordCache->lookupForAdd(key);
   3349 
   3350    // It's unlikely, but maybe another thread got there before us...
   3351    if (entry) {
   3352      // Use the existing entry; the newShapedWord will be discarded.
   3353      entry->value()->ResetAge();
   3354 #ifndef RELEASE_OR_BETA
   3355      if (aTextPerf) {
   3356        aTextPerf->current.wordCacheHit++;
   3357      }
   3358 #endif
   3359      aCallback(entry->value().get());
   3360      return true;
   3361    }
   3362 
   3363    if (!mWordCache->add(entry, key, std::move(newShapedWord))) {
   3364      NS_WARNING("failed to cache gfxShapedWord - expect missing text");
   3365      return false;
   3366    }
   3367 
   3368 #ifndef RELEASE_OR_BETA
   3369    if (aTextPerf) {
   3370      aTextPerf->current.wordCacheMiss++;
   3371    }
   3372 #endif
   3373    aCallback(entry->value().get());
   3374  }
   3375 
   3376  gfxFontCache::GetCache()->RunWordCacheExpirationTimer();
   3377  return true;
   3378 }
   3379 
   3380 bool gfxFont::WordCacheKey::HashPolicy::match(const Key& aKey,
   3381                                              const Lookup& aLookup) {
   3382  if (aKey.mLength != aLookup.mLength || aKey.mFlags != aLookup.mFlags ||
   3383      aKey.mRounding != aLookup.mRounding ||
   3384      aKey.mAppUnitsPerDevUnit != aLookup.mAppUnitsPerDevUnit ||
   3385      aKey.mScript != aLookup.mScript || aKey.mLanguage != aLookup.mLanguage) {
   3386    return false;
   3387  }
   3388 
   3389  if (aKey.mTextIs8Bit) {
   3390    if (aLookup.mTextIs8Bit) {
   3391      return (0 == memcmp(aKey.mText.mSingle, aLookup.mText.mSingle,
   3392                          aKey.mLength * sizeof(uint8_t)));
   3393    }
   3394    // The lookup key has 16-bit text, even though all the characters are < 256,
   3395    // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
   3396    // comparing with will have 8-bit text.
   3397    const uint8_t* s1 = aKey.mText.mSingle;
   3398    const char16_t* s2 = aLookup.mText.mDouble;
   3399    const char16_t* s2end = s2 + aKey.mLength;
   3400    while (s2 < s2end) {
   3401      if (*s1++ != *s2++) {
   3402        return false;
   3403      }
   3404    }
   3405    return true;
   3406  }
   3407  NS_ASSERTION(!(aLookup.mFlags & gfx::ShapedTextFlags::TEXT_IS_8BIT) &&
   3408                   !aLookup.mTextIs8Bit,
   3409               "didn't expect 8-bit text here");
   3410  return (0 == memcmp(aKey.mText.mDouble, aLookup.mText.mDouble,
   3411                      aKey.mLength * sizeof(char16_t)));
   3412 }
   3413 
   3414 bool gfxFont::ProcessSingleSpaceShapedWord(
   3415    DrawTarget* aDrawTarget, bool aVertical, int32_t aAppUnitsPerDevUnit,
   3416    gfx::ShapedTextFlags aFlags, RoundingFlags aRounding,
   3417    const std::function<void(gfxShapedWord*)>& aCallback) {
   3418  static const uint8_t space = ' ';
   3419  return ProcessShapedWordInternal(
   3420      aDrawTarget, &space, 1, gfxShapedWord::HashMix(0, ' '), Script::LATIN,
   3421      /* aLanguage = */ nullptr, aVertical, aAppUnitsPerDevUnit, aFlags,
   3422      aRounding, nullptr, aCallback);
   3423 }
   3424 
   3425 bool gfxFont::ShapeText(DrawTarget* aDrawTarget, const uint8_t* aText,
   3426                        uint32_t aOffset, uint32_t aLength, Script aScript,
   3427                        nsAtom* aLanguage, bool aVertical,
   3428                        RoundingFlags aRounding, gfxShapedText* aShapedText) {
   3429  nsDependentCSubstring ascii((const char*)aText, aLength);
   3430  nsAutoString utf16;
   3431  AppendASCIItoUTF16(ascii, utf16);
   3432  if (utf16.Length() != aLength) {
   3433    return false;
   3434  }
   3435  return ShapeText(aDrawTarget, utf16.BeginReading(), aOffset, aLength, aScript,
   3436                   aLanguage, aVertical, aRounding, aShapedText);
   3437 }
   3438 
   3439 bool gfxFont::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
   3440                        uint32_t aOffset, uint32_t aLength, Script aScript,
   3441                        nsAtom* aLanguage, bool aVertical,
   3442                        RoundingFlags aRounding, gfxShapedText* aShapedText) {
   3443  // XXX Currently, we do all vertical shaping through harfbuzz.
   3444  // Vertical graphite support may be wanted as a future enhancement.
   3445  // XXX Graphite shaping currently only supported on the main thread!
   3446  // Worker-thread shaping (offscreen canvas) will always go via harfbuzz.
   3447  if (FontCanSupportGraphite() && !aVertical && NS_IsMainThread()) {
   3448    if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
   3449      gfxGraphiteShaper* shaper = mGraphiteShaper;
   3450      if (!shaper) {
   3451        shaper = new gfxGraphiteShaper(this);
   3452        if (!mGraphiteShaper.compareExchange(nullptr, shaper)) {
   3453          delete shaper;
   3454          shaper = mGraphiteShaper;
   3455        }
   3456      }
   3457      if (shaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
   3458                            aLanguage, aVertical, aRounding, aShapedText)) {
   3459        PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical,
   3460                         aShapedText);
   3461        return true;
   3462      }
   3463    }
   3464  }
   3465 
   3466  gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper();
   3467  if (shaper &&
   3468      shaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
   3469                        aLanguage, aVertical, aRounding, aShapedText)) {
   3470    PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical,
   3471                     aShapedText);
   3472    if (GetFontEntry()->HasTrackingTable()) {
   3473      // Convert font size from device pixels back to CSS px
   3474      // to use in selecting tracking value
   3475      gfxFloat trackSize = GetAdjustedSize() *
   3476                           aShapedText->GetAppUnitsPerDevUnit() /
   3477                           AppUnitsPerCSSPixel();
   3478      // Usually, a given font will be used with the same appunit scale, so we
   3479      // can cache the tracking value rather than recompute it every time.
   3480      {
   3481        AutoReadLock lock(mLock);
   3482        if (trackSize == mCachedTrackingSize) {
   3483          // Applying tracking is a lot like the adjustment we do for
   3484          // synthetic bold: we want to apply between clusters, not to
   3485          // non-spacing glyphs within a cluster. So we can reuse that
   3486          // helper here.
   3487          aShapedText->ApplyTrackingToClusters(mTracking, aOffset, aLength);
   3488          return true;
   3489        }
   3490      }
   3491      // We didn't have the appropriate tracking value cached yet.
   3492      AutoWriteLock lock(mLock);
   3493      if (trackSize != mCachedTrackingSize) {
   3494        mCachedTrackingSize = trackSize;
   3495        mTracking =
   3496            GetFontEntry()->TrackingForCSSPx(trackSize) * mFUnitsConvFactor;
   3497      }
   3498      aShapedText->ApplyTrackingToClusters(mTracking, aOffset, aLength);
   3499    }
   3500    return true;
   3501  }
   3502 
   3503  NS_WARNING_ASSERTION(false, "shaper failed, expect scrambled/missing text");
   3504  return false;
   3505 }
   3506 
   3507 void gfxFont::PostShapingFixup(DrawTarget* aDrawTarget, const char16_t* aText,
   3508                               uint32_t aOffset, uint32_t aLength,
   3509                               bool aVertical, gfxShapedText* aShapedText) {
   3510  if (ApplySyntheticBold()) {
   3511    const Metrics& metrics = GetMetrics(aVertical ? nsFontMetrics::eVertical
   3512                                                  : nsFontMetrics::eHorizontal);
   3513    if (metrics.maxAdvance > metrics.aveCharWidth) {
   3514      aShapedText->ApplyTrackingToClusters(GetSyntheticBoldOffset(), aOffset,
   3515                                           aLength);
   3516    }
   3517  }
   3518 }
   3519 
   3520 #define MAX_SHAPING_LENGTH \
   3521  32760  // slightly less than 32K, trying to avoid
   3522         // over-stressing platform shapers
   3523 #define BACKTRACK_LIMIT \
   3524  16  // backtrack this far looking for a good place
   3525      // to split into fragments for separate shaping
   3526 
   3527 template <typename T>
   3528 bool gfxFont::ShapeFragmentWithoutWordCache(DrawTarget* aDrawTarget,
   3529                                            const T* aText, uint32_t aOffset,
   3530                                            uint32_t aLength, Script aScript,
   3531                                            nsAtom* aLanguage, bool aVertical,
   3532                                            RoundingFlags aRounding,
   3533                                            gfxTextRun* aTextRun) {
   3534  aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
   3535 
   3536  bool ok = true;
   3537 
   3538  while (ok && aLength > 0) {
   3539    uint32_t fragLen = aLength;
   3540 
   3541    // limit the length of text we pass to shapers in a single call
   3542    if (fragLen > MAX_SHAPING_LENGTH) {
   3543      fragLen = MAX_SHAPING_LENGTH;
   3544 
   3545      // in the 8-bit case, there are no multi-char clusters,
   3546      // so we don't need to do this check
   3547      if constexpr (sizeof(T) == sizeof(char16_t)) {
   3548        uint32_t i;
   3549        for (i = 0; i < BACKTRACK_LIMIT; ++i) {
   3550          if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
   3551            fragLen -= i;
   3552            break;
   3553          }
   3554        }
   3555        if (i == BACKTRACK_LIMIT) {
   3556          // if we didn't find any cluster start while backtracking,
   3557          // just check that we're not in the middle of a surrogate
   3558          // pair; back up by one code unit if we are.
   3559          if (NS_IS_SURROGATE_PAIR(aText[fragLen - 1], aText[fragLen])) {
   3560            --fragLen;
   3561          }
   3562        }
   3563      }
   3564    }
   3565 
   3566    ok = ShapeText(aDrawTarget, aText, aOffset, fragLen, aScript, aLanguage,
   3567                   aVertical, aRounding, aTextRun);
   3568 
   3569    aText += fragLen;
   3570    aOffset += fragLen;
   3571    aLength -= fragLen;
   3572  }
   3573 
   3574  return ok;
   3575 }
   3576 
   3577 // Check if aCh is an unhandled control character that should be displayed
   3578 // as a hexbox rather than rendered by some random font on the system.
   3579 // We exclude \r as stray &#13;s are rather common (bug 941940).
   3580 // Note that \n and \t don't come through here, as they have specific
   3581 // meanings that have already been handled.
   3582 static bool IsInvalidControlChar(uint32_t aCh) {
   3583  return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
   3584 }
   3585 
   3586 template <typename T>
   3587 bool gfxFont::ShapeTextWithoutWordCache(DrawTarget* aDrawTarget, const T* aText,
   3588                                        uint32_t aOffset, uint32_t aLength,
   3589                                        Script aScript, nsAtom* aLanguage,
   3590                                        bool aVertical, RoundingFlags aRounding,
   3591                                        gfxTextRun* aTextRun) {
   3592  uint32_t fragStart = 0;
   3593  bool ok = true;
   3594 
   3595  for (uint32_t i = 0; i <= aLength && ok; ++i) {
   3596    T ch = (i < aLength) ? aText[i] : '\n';
   3597    bool invalid = gfxFontGroup::IsInvalidChar(ch);
   3598    uint32_t length = i - fragStart;
   3599 
   3600    // break into separate fragments when we hit an invalid char
   3601    if (!invalid) {
   3602      continue;
   3603    }
   3604 
   3605    if (length > 0) {
   3606      ok = ShapeFragmentWithoutWordCache(
   3607          aDrawTarget, aText + fragStart, aOffset + fragStart, length, aScript,
   3608          aLanguage, aVertical, aRounding, aTextRun);
   3609    }
   3610 
   3611    if (i == aLength) {
   3612      break;
   3613    }
   3614 
   3615    // fragment was terminated by an invalid char: skip it,
   3616    // unless it's a control char that we want to show as a hexbox,
   3617    // but record where TAB or NEWLINE occur
   3618    if (ch == '\t') {
   3619      aTextRun->SetIsTab(aOffset + i);
   3620    } else if (ch == '\n') {
   3621      aTextRun->SetIsNewline(aOffset + i);
   3622    } else if (GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_FORMAT) {
   3623      aTextRun->SetIsFormattingControl(aOffset + i);
   3624    } else if (IsInvalidControlChar(ch) &&
   3625               !(aTextRun->GetFlags() &
   3626                 gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS)) {
   3627      if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
   3628        ShapeFragmentWithoutWordCache(aDrawTarget, aText + i, aOffset + i, 1,
   3629                                      aScript, aLanguage, aVertical, aRounding,
   3630                                      aTextRun);
   3631      } else {
   3632        aTextRun->SetMissingGlyph(aOffset + i, ch, this);
   3633      }
   3634    }
   3635    fragStart = i + 1;
   3636  }
   3637 
   3638  NS_WARNING_ASSERTION(ok, "failed to shape text - expect garbled text");
   3639  return ok;
   3640 }
   3641 
   3642 #ifndef RELEASE_OR_BETA
   3643 #  define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
   3644 #else
   3645 #  define TEXT_PERF_INCR(tp, m)
   3646 #endif
   3647 
   3648 inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
   3649 inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; }
   3650 
   3651 inline static bool HasSpaces(const uint8_t* aString, uint32_t aLen) {
   3652  return memchr(aString, 0x20, aLen) != nullptr;
   3653 }
   3654 
   3655 inline static bool HasSpaces(const char16_t* aString, uint32_t aLen) {
   3656  for (const char16_t* ch = aString; ch < aString + aLen; ch++) {
   3657    if (*ch == 0x20) {
   3658      return true;
   3659    }
   3660  }
   3661  return false;
   3662 }
   3663 
   3664 template <typename T>
   3665 bool gfxFont::SplitAndInitTextRun(
   3666    DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
   3667    const T* aString,    // text for this font run
   3668    uint32_t aRunStart,  // position in the textrun
   3669    uint32_t aRunLength, Script aRunScript, nsAtom* aLanguage,
   3670    ShapedTextFlags aOrientation) {
   3671  if (aRunLength == 0) {
   3672    return true;
   3673  }
   3674 
   3675  gfxTextPerfMetrics* tp = nullptr;
   3676  RoundingFlags rounding = GetRoundOffsetsToPixels(aDrawTarget);
   3677 
   3678 #ifndef RELEASE_OR_BETA
   3679  tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
   3680  if (tp) {
   3681    if (mStyle.systemFont) {
   3682      tp->current.numChromeTextRuns++;
   3683    } else {
   3684      tp->current.numContentTextRuns++;
   3685    }
   3686    tp->current.numChars += aRunLength;
   3687    if (aRunLength > tp->current.maxTextRunLen) {
   3688      tp->current.maxTextRunLen = aRunLength;
   3689    }
   3690  }
   3691 #endif
   3692 
   3693  uint32_t wordCacheCharLimit =
   3694      gfxPlatform::GetPlatform()->WordCacheCharLimit();
   3695 
   3696  bool vertical = aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
   3697 
   3698  // If spaces can participate in shaping (e.g. within lookups for automatic
   3699  // fractions), need to shape without using the word cache which segments
   3700  // textruns on space boundaries. Word cache can be used if the textrun
   3701  // is short enough to fit in the word cache and it lacks spaces.
   3702  tainted_boolean_hint t_canParticipate =
   3703      SpaceMayParticipateInShaping(aRunScript);
   3704  bool canParticipate = t_canParticipate.unverified_safe_because(
   3705      "We need to ensure that this function operates safely independent of "
   3706      "t_canParticipate. The worst that can happen here is that the decision "
   3707      "to use the cache is incorrectly made, resulting in a bad "
   3708      "rendering/slowness. However, this  would not compromise the memory "
   3709      "safety of Firefox in any way, and can thus be permitted");
   3710 
   3711  if (canParticipate) {
   3712    if (aRunLength > wordCacheCharLimit || HasSpaces(aString, aRunLength)) {
   3713      TEXT_PERF_INCR(tp, wordCacheSpaceRules);
   3714      return ShapeTextWithoutWordCache(aDrawTarget, aString, aRunStart,
   3715                                       aRunLength, aRunScript, aLanguage,
   3716                                       vertical, rounding, aTextRun);
   3717    }
   3718  }
   3719 
   3720  // the only flags we care about for ShapedWord construction/caching
   3721  gfx::ShapedTextFlags flags = aTextRun->GetFlags();
   3722  flags &= (gfx::ShapedTextFlags::TEXT_IS_RTL |
   3723            gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES |
   3724            gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT |
   3725            gfx::ShapedTextFlags::TEXT_ORIENT_MASK);
   3726  if constexpr (sizeof(T) == sizeof(uint8_t)) {
   3727    flags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
   3728  }
   3729 
   3730  uint32_t wordStart = 0;
   3731  uint32_t hash = 0;
   3732  bool wordIs8Bit = true;
   3733  int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
   3734 
   3735  T nextCh = aString[0];
   3736  for (uint32_t i = 0; i <= aRunLength; ++i) {
   3737    T ch = nextCh;
   3738    nextCh = (i < aRunLength - 1) ? aString[i + 1] : '\n';
   3739    T boundary = IsBoundarySpace(ch, nextCh);
   3740    bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
   3741    uint32_t length = i - wordStart;
   3742 
   3743    // break into separate ShapedWords when we hit an invalid char,
   3744    // or a boundary space (always handled individually),
   3745    // or the first non-space after a space
   3746    if (!boundary && !invalid) {
   3747      if (!IsChar8Bit(ch)) {
   3748        wordIs8Bit = false;
   3749      }
   3750      // include this character in the hash, and move on to next
   3751      hash = gfxShapedWord::HashMix(hash, ch);
   3752      continue;
   3753    }
   3754 
   3755    // We've decided to break here (i.e. we're at the end of a "word");
   3756    // shape the word and add it to the textrun.
   3757    // For words longer than the limit, we don't use the
   3758    // font's word cache but just shape directly into the textrun.
   3759    if (length > wordCacheCharLimit) {
   3760      TEXT_PERF_INCR(tp, wordCacheLong);
   3761      bool ok = ShapeFragmentWithoutWordCache(
   3762          aDrawTarget, aString + wordStart, aRunStart + wordStart, length,
   3763          aRunScript, aLanguage, vertical, rounding, aTextRun);
   3764      if (!ok) {
   3765        return false;
   3766      }
   3767    } else if (length > 0) {
   3768      gfx::ShapedTextFlags wordFlags = flags;
   3769      // in the 8-bit version of this method, TEXT_IS_8BIT was
   3770      // already set as part of |flags|, so no need for a per-word
   3771      // adjustment here
   3772      if (sizeof(T) == sizeof(char16_t)) {
   3773        if (wordIs8Bit) {
   3774          wordFlags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
   3775        }
   3776      }
   3777      bool processed = ProcessShapedWordInternal(
   3778          aDrawTarget, aString + wordStart, length, hash, aRunScript, aLanguage,
   3779          vertical, appUnitsPerDevUnit, wordFlags, rounding, tp,
   3780          [&](gfxShapedWord* aShapedWord) {
   3781            aTextRun->CopyGlyphDataFrom(aShapedWord, aRunStart + wordStart);
   3782          });
   3783      if (!processed) {
   3784        return false;  // failed, presumably out of memory?
   3785      }
   3786    }
   3787 
   3788    if (boundary) {
   3789      // word was terminated by a space: add that to the textrun
   3790      MOZ_ASSERT(aOrientation != ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED,
   3791                 "text-orientation:mixed should be resolved earlier");
   3792      if (boundary != ' ' || !aTextRun->SetSpaceGlyphIfSimple(
   3793                                 this, aRunStart + i, ch, aOrientation)) {
   3794        // Currently, the only "boundary" characters we recognize are
   3795        // space and no-break space, which are both 8-bit, so we force
   3796        // that flag (below). If we ever change IsBoundarySpace, we
   3797        // may need to revise this.
   3798        // Avoid tautological-constant-out-of-range-compare in 8-bit:
   3799        DebugOnly<char16_t> boundary16 = boundary;
   3800        NS_ASSERTION(boundary16 < 256, "unexpected boundary!");
   3801        bool processed = ProcessShapedWordInternal(
   3802            aDrawTarget, &boundary, 1, gfxShapedWord::HashMix(0, boundary),
   3803            aRunScript, aLanguage, vertical, appUnitsPerDevUnit,
   3804            flags | gfx::ShapedTextFlags::TEXT_IS_8BIT, rounding, tp,
   3805            [&](gfxShapedWord* aShapedWord) {
   3806              aTextRun->CopyGlyphDataFrom(aShapedWord, aRunStart + i);
   3807              if (boundary == ' ') {
   3808                aTextRun->GetCharacterGlyphs()[aRunStart + i].SetIsSpace();
   3809              }
   3810            });
   3811        if (!processed) {
   3812          return false;
   3813        }
   3814      }
   3815      hash = 0;
   3816      wordStart = i + 1;
   3817      wordIs8Bit = true;
   3818      continue;
   3819    }
   3820 
   3821    if (i == aRunLength) {
   3822      break;
   3823    }
   3824 
   3825    NS_ASSERTION(invalid, "how did we get here except via an invalid char?");
   3826 
   3827    // word was terminated by an invalid char: skip it,
   3828    // unless it's a control char that we want to show as a hexbox,
   3829    // but record where TAB or NEWLINE occur
   3830    if (ch == '\t') {
   3831      aTextRun->SetIsTab(aRunStart + i);
   3832    } else if (ch == '\n') {
   3833      aTextRun->SetIsNewline(aRunStart + i);
   3834    } else if (GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_FORMAT) {
   3835      aTextRun->SetIsFormattingControl(aRunStart + i);
   3836    } else if (IsInvalidControlChar(ch) &&
   3837               !(aTextRun->GetFlags() &
   3838                 gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS)) {
   3839      if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
   3840        ShapeFragmentWithoutWordCache(aDrawTarget, aString + i, aRunStart + i,
   3841                                      1, aRunScript, aLanguage, vertical,
   3842                                      rounding, aTextRun);
   3843      } else {
   3844        aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
   3845      }
   3846    }
   3847 
   3848    hash = 0;
   3849    wordStart = i + 1;
   3850    wordIs8Bit = true;
   3851  }
   3852 
   3853  return true;
   3854 }
   3855 
   3856 // Explicit instantiations of SplitAndInitTextRun, to avoid libxul link failure
   3857 template bool gfxFont::SplitAndInitTextRun(
   3858    DrawTarget* aDrawTarget, gfxTextRun* aTextRun, const uint8_t* aString,
   3859    uint32_t aRunStart, uint32_t aRunLength, Script aRunScript,
   3860    nsAtom* aLanguage, ShapedTextFlags aOrientation);
   3861 template bool gfxFont::SplitAndInitTextRun(
   3862    DrawTarget* aDrawTarget, gfxTextRun* aTextRun, const char16_t* aString,
   3863    uint32_t aRunStart, uint32_t aRunLength, Script aRunScript,
   3864    nsAtom* aLanguage, ShapedTextFlags aOrientation);
   3865 
   3866 template <>
   3867 bool gfxFont::InitFakeSmallCapsRun(
   3868    FontVisibilityProvider* aFontVisibilityProvider, DrawTarget* aDrawTarget,
   3869    gfxTextRun* aTextRun, const char16_t* aText, uint32_t aOffset,
   3870    uint32_t aLength, FontMatchType aMatchType,
   3871    gfx::ShapedTextFlags aOrientation, Script aScript, nsAtom* aLanguage,
   3872    bool aSyntheticLower, bool aSyntheticUpper) {
   3873  bool ok = true;
   3874 
   3875  RefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
   3876  if (!smallCapsFont) {
   3877    NS_WARNING("failed to get reduced-size font for smallcaps!");
   3878    smallCapsFont = this;
   3879  }
   3880 
   3881  bool isCJK = gfxTextRun::IsCJKScript(aScript);
   3882 
   3883  enum RunCaseAction { kNoChange, kUppercaseReduce, kUppercase };
   3884 
   3885  RunCaseAction runAction = kNoChange;
   3886  uint32_t runStart = 0;
   3887 
   3888  for (uint32_t i = 0; i <= aLength; ++i) {
   3889    uint32_t extraCodeUnits = 0;  // Will be set to 1 if we need to consume
   3890                                  // a trailing surrogate as well as the
   3891                                  // current code unit.
   3892    RunCaseAction chAction = kNoChange;
   3893    // Unless we're at the end, figure out what treatment the current
   3894    // character will need.
   3895    if (i < aLength) {
   3896      uint32_t ch = aText[i];
   3897      if (i < aLength - 1 && NS_IS_SURROGATE_PAIR(ch, aText[i + 1])) {
   3898        ch = SURROGATE_TO_UCS4(ch, aText[i + 1]);
   3899        extraCodeUnits = 1;
   3900      }
   3901      // Characters that aren't the start of a cluster are ignored here.
   3902      // They get added to whatever lowercase/non-lowercase run we're in.
   3903      if (IsClusterExtender(ch)) {
   3904        chAction = runAction;
   3905      } else {
   3906        if (ch != ToUpperCase(ch) || SpecialUpper(ch)) {
   3907          // ch is lower case
   3908          chAction = (aSyntheticLower ? kUppercaseReduce : kNoChange);
   3909        } else if (ch != ToLowerCase(ch)) {
   3910          // ch is upper case
   3911          chAction = (aSyntheticUpper ? kUppercaseReduce : kNoChange);
   3912          if (aLanguage == nsGkAtoms::el) {
   3913            // In Greek, check for characters that will be modified by
   3914            // the GreekUpperCase mapping - this catches accented
   3915            // capitals where the accent is to be removed (bug 307039).
   3916            // These are handled by using the full-size font with the
   3917            // uppercasing transform.
   3918            mozilla::GreekCasing::State state;
   3919            bool markEta, updateEta;
   3920            uint32_t ch2 =
   3921                mozilla::GreekCasing::UpperCase(ch, state, markEta, updateEta);
   3922            if ((ch != ch2 || markEta) && !aSyntheticUpper) {
   3923              chAction = kUppercase;
   3924            }
   3925          }
   3926        }
   3927      }
   3928    }
   3929 
   3930    // At the end of the text or when the current character needs different
   3931    // casing treatment from the current run, finish the run-in-progress
   3932    // and prepare to accumulate a new run.
   3933    // Note that we do not look at any source data for offset [i] here,
   3934    // as that would be invalid in the case where i==length.
   3935    if ((i == aLength || runAction != chAction) && runStart < i) {
   3936      uint32_t runLength = i - runStart;
   3937      gfxFont* f = this;
   3938      switch (runAction) {
   3939        case kNoChange:
   3940          // just use the current font and the existing string
   3941          aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true,
   3942                                aOrientation, isCJK);
   3943          if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun, aText + runStart,
   3944                                      aOffset + runStart, runLength, aScript,
   3945                                      aLanguage, aOrientation)) {
   3946            ok = false;
   3947          }
   3948          break;
   3949 
   3950        case kUppercaseReduce:
   3951          // use reduced-size font, then fall through to uppercase the text
   3952          f = smallCapsFont;
   3953          [[fallthrough]];
   3954 
   3955        case kUppercase:
   3956          // apply uppercase transform to the string
   3957          nsDependentSubstring origString(aText + runStart, runLength);
   3958          nsAutoString convertedString;
   3959          AutoTArray<bool, 50> charsToMergeArray;
   3960          AutoTArray<bool, 50> deletedCharsArray;
   3961 
   3962          const auto globalTransform = StyleTextTransform::UPPERCASE;
   3963          // No mask needed; we're doing case conversion, not password-hiding.
   3964          const char16_t maskChar = 0;
   3965          bool mergeNeeded = nsCaseTransformTextRunFactory::TransformString(
   3966              origString, convertedString, Some(globalTransform), maskChar,
   3967              /* aCaseTransformsOnly = */ false, aLanguage, charsToMergeArray,
   3968              deletedCharsArray);
   3969 
   3970          // Check whether the font supports the uppercased characters needed;
   3971          // if not, we're not going to be able to simulate small-caps.
   3972          bool failed = false;
   3973          char16_t highSurrogate = 0;
   3974          for (const char16_t* cp = convertedString.BeginReading();
   3975               cp != convertedString.EndReading(); ++cp) {
   3976            if (NS_IS_HIGH_SURROGATE(*cp)) {
   3977              highSurrogate = *cp;
   3978              continue;
   3979            }
   3980            uint32_t ch = *cp;
   3981            if (NS_IS_LOW_SURROGATE(*cp) && highSurrogate) {
   3982              ch = SURROGATE_TO_UCS4(highSurrogate, *cp);
   3983            }
   3984            highSurrogate = 0;
   3985            if (!f->HasCharacter(ch)) {
   3986              if (IsDefaultIgnorable(ch)) {
   3987                continue;
   3988              }
   3989              failed = true;
   3990              break;
   3991            }
   3992          }
   3993          // Required uppercase letter(s) missing from the font. Just use the
   3994          // original text with the original font, no fake small caps!
   3995          if (failed) {
   3996            convertedString = origString;
   3997            mergeNeeded = false;
   3998            f = this;
   3999          }
   4000 
   4001          if (mergeNeeded) {
   4002            // This is the hard case: the transformation caused chars
   4003            // to be inserted or deleted, so we can't shape directly
   4004            // into the destination textrun but have to handle the
   4005            // mismatch of character positions.
   4006            gfxTextRunFactory::Parameters params = {
   4007                aDrawTarget, nullptr, nullptr,
   4008                nullptr,     0,       aTextRun->GetAppUnitsPerDevUnit()};
   4009            RefPtr<gfxTextRun> tempRun(gfxTextRun::Create(
   4010                &params, convertedString.Length(), aTextRun->GetFontGroup(),
   4011                gfx::ShapedTextFlags(), nsTextFrameUtils::Flags()));
   4012            tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation, isCJK);
   4013            if (!f->SplitAndInitTextRun(aDrawTarget, tempRun.get(),
   4014                                        convertedString.BeginReading(), 0,
   4015                                        convertedString.Length(), aScript,
   4016                                        aLanguage, aOrientation)) {
   4017              ok = false;
   4018            } else {
   4019              RefPtr<gfxTextRun> mergedRun(gfxTextRun::Create(
   4020                  &params, runLength, aTextRun->GetFontGroup(),
   4021                  gfx::ShapedTextFlags(), nsTextFrameUtils::Flags()));
   4022              MergeCharactersInTextRun(mergedRun.get(), tempRun.get(),
   4023                                       charsToMergeArray.Elements(),
   4024                                       deletedCharsArray.Elements());
   4025              gfxTextRun::Range runRange(0, runLength);
   4026              aTextRun->CopyGlyphDataFrom(mergedRun.get(), runRange,
   4027                                          aOffset + runStart);
   4028            }
   4029          } else {
   4030            aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true,
   4031                                  aOrientation, isCJK);
   4032            if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun,
   4033                                        convertedString.BeginReading(),
   4034                                        aOffset + runStart, runLength, aScript,
   4035                                        aLanguage, aOrientation)) {
   4036              ok = false;
   4037            }
   4038          }
   4039          break;
   4040      }
   4041 
   4042      runStart = i;
   4043    }
   4044 
   4045    i += extraCodeUnits;
   4046    if (i < aLength) {
   4047      runAction = chAction;
   4048    }
   4049  }
   4050 
   4051  return ok;
   4052 }
   4053 
   4054 template <>
   4055 bool gfxFont::InitFakeSmallCapsRun(
   4056    FontVisibilityProvider* aFontVisibilityProvider, DrawTarget* aDrawTarget,
   4057    gfxTextRun* aTextRun, const uint8_t* aText, uint32_t aOffset,
   4058    uint32_t aLength, FontMatchType aMatchType,
   4059    gfx::ShapedTextFlags aOrientation, Script aScript, nsAtom* aLanguage,
   4060    bool aSyntheticLower, bool aSyntheticUpper) {
   4061  NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
   4062                                       aLength);
   4063  return InitFakeSmallCapsRun(aFontVisibilityProvider, aDrawTarget, aTextRun,
   4064                              static_cast<const char16_t*>(unicodeString.get()),
   4065                              aOffset, aLength, aMatchType, aOrientation,
   4066                              aScript, aLanguage, aSyntheticLower,
   4067                              aSyntheticUpper);
   4068 }
   4069 
   4070 already_AddRefed<gfxFont> gfxFont::GetSmallCapsFont() const {
   4071  gfxFontStyle style(*GetStyle());
   4072  style.size *= SMALL_CAPS_SCALE_FACTOR;
   4073  style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
   4074  gfxFontEntry* fe = GetFontEntry();
   4075  return fe->FindOrMakeFont(&style, mUnicodeRangeMap);
   4076 }
   4077 
   4078 already_AddRefed<gfxFont> gfxFont::GetSubSuperscriptFont(
   4079    int32_t aAppUnitsPerDevPixel) const {
   4080  gfxFontStyle style(*GetStyle());
   4081  style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
   4082  gfxFontEntry* fe = GetFontEntry();
   4083  return fe->FindOrMakeFont(&style, mUnicodeRangeMap);
   4084 }
   4085 
   4086 gfxGlyphExtents* gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
   4087  uint32_t readCount;
   4088  {
   4089    AutoReadLock lock(mLock);
   4090    readCount = mGlyphExtentsArray.Length();
   4091    for (uint32_t i = 0; i < readCount; ++i) {
   4092      if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
   4093        return mGlyphExtentsArray[i].get();
   4094    }
   4095  }
   4096  AutoWriteLock lock(mLock);
   4097  // Re-check in case of race.
   4098  uint32_t count = mGlyphExtentsArray.Length();
   4099  for (uint32_t i = readCount; i < count; ++i) {
   4100    if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
   4101      return mGlyphExtentsArray[i].get();
   4102  }
   4103  gfxGlyphExtents* glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
   4104  if (glyphExtents) {
   4105    mGlyphExtentsArray.AppendElement(glyphExtents);
   4106    // Initialize the extents of a space glyph, assuming that spaces don't
   4107    // render anything!
   4108    glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
   4109  }
   4110  return glyphExtents;
   4111 }
   4112 
   4113 void gfxFont::SetupGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphID,
   4114                                bool aNeedTight, gfxGlyphExtents* aExtents) {
   4115  gfxRect svgBounds;
   4116  if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
   4117      mFontEntry->GetSVGGlyphExtents(aDrawTarget, aGlyphID, GetAdjustedSize(),
   4118                                     &svgBounds)) {
   4119    gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
   4120    aExtents->SetTightGlyphExtents(
   4121        aGlyphID, gfxRect(svgBounds.X() * d2a, svgBounds.Y() * d2a,
   4122                          svgBounds.Width() * d2a, svgBounds.Height() * d2a));
   4123    return;
   4124  }
   4125 
   4126  if (mFontEntry->TryGetColorGlyphs() && mFontEntry->mCOLR &&
   4127      COLRFonts::GetColrTableVersion(mFontEntry->mCOLR) == 1) {
   4128    auto* shaper = GetHarfBuzzShaper();
   4129    if (shaper && shaper->IsInitialized()) {
   4130      RefPtr scaledFont = GetScaledFont(aDrawTarget);
   4131      Rect r = COLRFonts::GetColorGlyphBounds(
   4132          mFontEntry->mCOLR, shaper->GetHBFont(), aGlyphID, aDrawTarget,
   4133          scaledFont, mFUnitsConvFactor);
   4134      if (!r.IsEmpty()) {
   4135        gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
   4136        aExtents->SetTightGlyphExtents(
   4137            aGlyphID, gfxRect(r.X() * d2a, r.Y() * d2a, r.Width() * d2a,
   4138                              r.Height() * d2a));
   4139        return;
   4140      }
   4141    }
   4142  }
   4143 
   4144  gfxRect bounds;
   4145  GetGlyphBounds(aGlyphID, &bounds, mAntialiasOption == kAntialiasNone);
   4146 
   4147  const Metrics& fontMetrics = GetMetrics(nsFontMetrics::eHorizontal);
   4148  int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
   4149  if (!aNeedTight && bounds.x >= 0.0 && bounds.y >= -fontMetrics.maxAscent &&
   4150      bounds.height + bounds.y <= fontMetrics.maxDescent) {
   4151    uint32_t appUnitsWidth =
   4152        uint32_t(ceil((bounds.x + bounds.width) * appUnitsPerDevUnit));
   4153    if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
   4154      aExtents->SetContainedGlyphWidthAppUnits(aGlyphID,
   4155                                               uint16_t(appUnitsWidth));
   4156      return;
   4157    }
   4158  }
   4159 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
   4160  if (!aNeedTight) {
   4161    ++gGlyphExtentsSetupFallBackToTight;
   4162  }
   4163 #endif
   4164 
   4165  gfxFloat d2a = appUnitsPerDevUnit;
   4166  aExtents->SetTightGlyphExtents(
   4167      aGlyphID, gfxRect(bounds.x * d2a, bounds.y * d2a, bounds.width * d2a,
   4168                        bounds.height * d2a));
   4169 }
   4170 
   4171 // Try to initialize font metrics by reading sfnt tables directly;
   4172 // set mIsValid=TRUE and return TRUE on success.
   4173 // Return FALSE if the gfxFontEntry subclass does not
   4174 // implement GetFontTable(), or for non-sfnt fonts where tables are
   4175 // not available.
   4176 // If this returns TRUE without setting the mIsValid flag, then we -did-
   4177 // apparently find an sfnt, but it was too broken to be used.
   4178 bool gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics) {
   4179  mIsValid = false;  // font is NOT valid in case of early return
   4180 
   4181  const uint32_t kHheaTableTag = TRUETYPE_TAG('h', 'h', 'e', 'a');
   4182  const uint32_t kOS_2TableTag = TRUETYPE_TAG('O', 'S', '/', '2');
   4183 
   4184  uint32_t len;
   4185 
   4186  if (mFUnitsConvFactor < 0.0) {
   4187    // If the conversion factor from FUnits is not yet set,
   4188    // get the unitsPerEm from the 'head' table via the font entry
   4189    uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
   4190    if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
   4191      return false;
   4192    }
   4193    mFUnitsConvFactor = GetAdjustedSize() / unitsPerEm;
   4194  }
   4195 
   4196  // 'hhea' table is required for the advanceWidthMax field
   4197  gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
   4198  if (!hheaTable) {
   4199    return false;  // no 'hhea' table -> not an sfnt
   4200  }
   4201  const MetricsHeader* hhea =
   4202      reinterpret_cast<const MetricsHeader*>(hb_blob_get_data(hheaTable, &len));
   4203  if (len < sizeof(MetricsHeader)) {
   4204    return false;
   4205  }
   4206 
   4207 #define SET_UNSIGNED(field, src) \
   4208  aMetrics.field = uint16_t(src) * mFUnitsConvFactor
   4209 #define SET_SIGNED(field, src) aMetrics.field = int16_t(src) * mFUnitsConvFactor
   4210 
   4211  SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
   4212 
   4213  // 'OS/2' table is optional, if not found we'll estimate xHeight
   4214  // and aveCharWidth by measuring glyphs
   4215  gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
   4216  if (os2Table) {
   4217    const OS2Table* os2 =
   4218        reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
   4219    // this should always be present in any valid OS/2 of any version
   4220    if (len >= offsetof(OS2Table, xAvgCharWidth) + sizeof(int16_t)) {
   4221      SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
   4222    }
   4223  }
   4224 
   4225 #undef SET_SIGNED
   4226 #undef SET_UNSIGNED
   4227 
   4228  hb_font_t* hbFont = gfxHarfBuzzShaper::CreateHBFont(this);
   4229  hb_position_t position;
   4230 
   4231  auto FixedToFloat = [](hb_position_t f) -> gfxFloat { return f / 65536.0; };
   4232 
   4233  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER,
   4234                                 &position)) {
   4235    aMetrics.maxAscent = FixedToFloat(position);
   4236  }
   4237  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER,
   4238                                 &position)) {
   4239    aMetrics.maxDescent = -FixedToFloat(position);
   4240  }
   4241  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP,
   4242                                 &position)) {
   4243    aMetrics.externalLeading = FixedToFloat(position);
   4244  }
   4245 
   4246  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_UNDERLINE_OFFSET,
   4247                                 &position)) {
   4248    aMetrics.underlineOffset = FixedToFloat(position);
   4249  }
   4250  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_UNDERLINE_SIZE,
   4251                                 &position)) {
   4252    aMetrics.underlineSize = FixedToFloat(position);
   4253  }
   4254  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_STRIKEOUT_OFFSET,
   4255                                 &position)) {
   4256    aMetrics.strikeoutOffset = FixedToFloat(position);
   4257  }
   4258  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_STRIKEOUT_SIZE,
   4259                                 &position)) {
   4260    aMetrics.strikeoutSize = FixedToFloat(position);
   4261  }
   4262 
   4263  // Although sxHeight and sCapHeight are signed fields, we consider
   4264  // zero/negative values to be erroneous and just ignore them.
   4265  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_X_HEIGHT,
   4266                                 &position) &&
   4267      position > 0) {
   4268    aMetrics.xHeight = FixedToFloat(position);
   4269  }
   4270  if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_CAP_HEIGHT,
   4271                                 &position) &&
   4272      position > 0) {
   4273    aMetrics.capHeight = FixedToFloat(position);
   4274  }
   4275  hb_font_destroy(hbFont);
   4276 
   4277  mIsValid = true;
   4278 
   4279  return true;
   4280 }
   4281 
   4282 static double RoundToNearestMultiple(double aValue, double aFraction) {
   4283  return floor(aValue / aFraction + 0.5) * aFraction;
   4284 }
   4285 
   4286 void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics) {
   4287  aMetrics.maxAscent =
   4288      ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1 / 1024.0));
   4289  aMetrics.maxDescent =
   4290      ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1 / 1024.0));
   4291 
   4292  if (aMetrics.xHeight <= 0) {
   4293    // only happens if we couldn't find either font metrics
   4294    // or a char to measure;
   4295    // pick an arbitrary value that's better than zero
   4296    aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
   4297  }
   4298 
   4299  // If we have a font that doesn't provide a capHeight value, use maxAscent
   4300  // as a reasonable fallback.
   4301  if (aMetrics.capHeight <= 0) {
   4302    aMetrics.capHeight = aMetrics.maxAscent;
   4303  }
   4304 
   4305  aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
   4306  aMetrics.internalLeading =
   4307      std::max(0.0, aMetrics.maxHeight - aMetrics.emHeight);
   4308 
   4309  aMetrics.emAscent =
   4310      aMetrics.maxAscent * aMetrics.emHeight / aMetrics.maxHeight;
   4311  aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
   4312 
   4313  if (GetFontEntry()->IsFixedPitch()) {
   4314    // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
   4315    // advance than the average character width... this forces
   4316    // those fonts to be recognized like fixed pitch fonts by layout.
   4317    aMetrics.maxAdvance = aMetrics.aveCharWidth;
   4318  }
   4319 
   4320  if (!aMetrics.strikeoutOffset) {
   4321    aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
   4322  }
   4323  if (!aMetrics.strikeoutSize) {
   4324    aMetrics.strikeoutSize = aMetrics.underlineSize;
   4325  }
   4326 }
   4327 
   4328 void gfxFont::SanitizeMetrics(gfxFont::Metrics* aMetrics,
   4329                              bool aIsBadUnderlineFont) {
   4330  // Even if this font size is zero, this font is created with non-zero size.
   4331  // However, for layout and others, we should return the metrics of zero size
   4332  // font.
   4333  if (mStyle.AdjustedSizeMustBeZero()) {
   4334    memset(aMetrics, 0, sizeof(gfxFont::Metrics));
   4335    return;
   4336  }
   4337 
   4338  // If the font entry has ascent/descent/lineGap-override values,
   4339  // replace the metrics from the font with the overrides.
   4340  gfxFloat adjustedSize = GetAdjustedSize();
   4341  if (mFontEntry->mAscentOverride >= 0.0) {
   4342    aMetrics->maxAscent = mFontEntry->mAscentOverride * adjustedSize;
   4343    aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
   4344    aMetrics->internalLeading =
   4345        std::max(0.0, aMetrics->maxHeight - aMetrics->emHeight);
   4346  }
   4347  if (mFontEntry->mDescentOverride >= 0.0) {
   4348    aMetrics->maxDescent = mFontEntry->mDescentOverride * adjustedSize;
   4349    aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
   4350    aMetrics->internalLeading =
   4351        std::max(0.0, aMetrics->maxHeight - aMetrics->emHeight);
   4352  }
   4353  if (mFontEntry->mLineGapOverride >= 0.0) {
   4354    aMetrics->externalLeading = mFontEntry->mLineGapOverride * adjustedSize;
   4355  }
   4356 
   4357  aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize);
   4358  aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize);
   4359 
   4360  aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0);
   4361 
   4362  if (aMetrics->maxAscent < 1.0) {
   4363    // We cannot draw strikeout line and overline in the ascent...
   4364    aMetrics->underlineSize = 0;
   4365    aMetrics->underlineOffset = 0;
   4366    aMetrics->strikeoutSize = 0;
   4367    aMetrics->strikeoutOffset = 0;
   4368    return;
   4369  }
   4370 
   4371  /**
   4372   * Some CJK fonts have bad underline offset. Therefore, if this is such font,
   4373   * we need to lower the underline offset to bottom of *em* descent.
   4374   * However, if this is system font, we should not do this for the rendering
   4375   * compatibility with another application's UI on the platform.
   4376   * XXX Should not use this hack if the font size is too small?
   4377   *     Such text cannot be read, this might be used for tight CSS
   4378   *     rendering? (E.g., Acid2)
   4379   */
   4380  if (!mStyle.systemFont && aIsBadUnderlineFont) {
   4381    // First, we need 2 pixels between baseline and underline at least. Because
   4382    // many CJK characters put their glyphs on the baseline, so, 1 pixel is too
   4383    // close for CJK characters.
   4384    aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0);
   4385 
   4386    // Next, we put the underline to bottom of below of the descent space.
   4387    if (aMetrics->internalLeading + aMetrics->externalLeading >
   4388        aMetrics->underlineSize) {
   4389      aMetrics->underlineOffset =
   4390          std::min(aMetrics->underlineOffset, -aMetrics->emDescent);
   4391    } else {
   4392      aMetrics->underlineOffset =
   4393          std::min(aMetrics->underlineOffset,
   4394                   aMetrics->underlineSize - aMetrics->emDescent);
   4395    }
   4396  }
   4397  // If underline positioned is too far from the text, descent position is
   4398  // preferred so that underline will stay within the boundary.
   4399  else if (aMetrics->underlineSize - aMetrics->underlineOffset >
   4400           aMetrics->maxDescent) {
   4401    if (aMetrics->underlineSize > aMetrics->maxDescent)
   4402      aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0);
   4403    // The max underlineOffset is 1px (the min underlineSize is 1px, and min
   4404    // maxDescent is 0px.)
   4405    aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
   4406  }
   4407 
   4408  // If strikeout line is overflowed from the ascent, the line should be resized
   4409  // and moved for that being in the ascent space. Note that the strikeoutOffset
   4410  // is *middle* of the strikeout line position.
   4411  gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
   4412  if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
   4413    if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
   4414      aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0);
   4415      halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
   4416    }
   4417    gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
   4418    aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0);
   4419  }
   4420 
   4421  // If overline is larger than the ascent, the line should be resized.
   4422  if (aMetrics->underlineSize > aMetrics->maxAscent) {
   4423    aMetrics->underlineSize = aMetrics->maxAscent;
   4424  }
   4425 }
   4426 
   4427 gfxFont::Baselines gfxFont::GetBaselines(Orientation aOrientation) {
   4428  // Approximated baselines for fonts lacking actual baseline data. These are
   4429  // fractions of the em ascent/descent from the alphabetic baseline.
   4430  const double kHangingBaselineDefault = 0.8;       // fraction of ascent
   4431  const double kIdeographicBaselineDefault = -0.5;  // fraction of descent
   4432 
   4433  // If no BASE table is present, just return synthetic values immediately.
   4434  if (!mFontEntry->HasFontTable(TRUETYPE_TAG('B', 'A', 'S', 'E'))) {
   4435    // No baseline table; just synthesize them immediately.
   4436    const Metrics& metrics = GetMetrics(aOrientation);
   4437    return Baselines{
   4438        0.0,                                             // alphabetic
   4439        kHangingBaselineDefault * metrics.emAscent,      // hanging
   4440        kIdeographicBaselineDefault * metrics.emDescent  // ideographic
   4441    };
   4442  }
   4443 
   4444  // Use harfbuzz to try to read the font's baseline metrics.
   4445  Baselines result{NAN, NAN, NAN};
   4446  hb_font_t* hbFont = gfxHarfBuzzShaper::CreateHBFont(this);
   4447  hb_direction_t hbDir = aOrientation == nsFontMetrics::eHorizontal
   4448                             ? HB_DIRECTION_LTR
   4449                             : HB_DIRECTION_TTB;
   4450  hb_position_t position;
   4451  unsigned count = 0;
   4452  auto Fix2Float = [](hb_position_t f) -> gfxFloat { return f / 65536.0; };
   4453  if (hb_ot_layout_get_baseline(hbFont, HB_OT_LAYOUT_BASELINE_TAG_ROMAN, hbDir,
   4454                                HB_OT_TAG_DEFAULT_SCRIPT,
   4455                                HB_OT_TAG_DEFAULT_LANGUAGE, &position)) {
   4456    result.mAlphabetic = Fix2Float(position);
   4457    count++;
   4458  }
   4459  if (hb_ot_layout_get_baseline(hbFont, HB_OT_LAYOUT_BASELINE_TAG_HANGING,
   4460                                hbDir, HB_OT_TAG_DEFAULT_SCRIPT,
   4461                                HB_OT_TAG_DEFAULT_LANGUAGE, &position)) {
   4462    result.mHanging = Fix2Float(position);
   4463    count++;
   4464  }
   4465  if (hb_ot_layout_get_baseline(
   4466          hbFont, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, hbDir,
   4467          HB_OT_TAG_DEFAULT_SCRIPT, HB_OT_TAG_DEFAULT_LANGUAGE, &position)) {
   4468    result.mIdeographic = Fix2Float(position);
   4469    count++;
   4470  }
   4471  hb_font_destroy(hbFont);
   4472  // If we successfully read all three, we can return now.
   4473  if (count == 3) {
   4474    return result;
   4475  }
   4476 
   4477  // Synthesize the baselines that we didn't find in the font.
   4478  const Metrics& metrics = GetMetrics(aOrientation);
   4479  if (std::isnan(result.mAlphabetic)) {
   4480    result.mAlphabetic = 0.0;
   4481  }
   4482  if (std::isnan(result.mHanging)) {
   4483    result.mHanging = kHangingBaselineDefault * metrics.emAscent;
   4484  }
   4485  if (std::isnan(result.mIdeographic)) {
   4486    result.mIdeographic = kIdeographicBaselineDefault * metrics.emDescent;
   4487  }
   4488 
   4489  return result;
   4490 }
   4491 
   4492 // Create a Metrics record to be used for vertical layout. This should never
   4493 // fail, as we've already decided this is a valid font. We do not have the
   4494 // option of marking it invalid (as can happen if we're unable to read
   4495 // horizontal metrics), because that could break a font that we're already
   4496 // using for horizontal text.
   4497 // So we will synthesize *something* usable here even if there aren't any of the
   4498 // usual font tables (which can happen in the case of a legacy bitmap or Type1
   4499 // font for which the platform-specific backend used platform APIs instead of
   4500 // sfnt tables to create the horizontal metrics).
   4501 void gfxFont::CreateVerticalMetrics() {
   4502  const uint32_t kHheaTableTag = TRUETYPE_TAG('h', 'h', 'e', 'a');
   4503  const uint32_t kVheaTableTag = TRUETYPE_TAG('v', 'h', 'e', 'a');
   4504  const uint32_t kPostTableTag = TRUETYPE_TAG('p', 'o', 's', 't');
   4505  const uint32_t kOS_2TableTag = TRUETYPE_TAG('O', 'S', '/', '2');
   4506  uint32_t len;
   4507 
   4508  auto* metrics = new Metrics();
   4509  ::memset(metrics, 0, sizeof(Metrics));
   4510 
   4511  // Some basic defaults, in case the font lacks any real metrics tables.
   4512  // TODO: consider what rounding (if any) we should apply to these.
   4513  metrics->emHeight = GetAdjustedSize();
   4514  metrics->emAscent = metrics->emHeight / 2;
   4515  metrics->emDescent = metrics->emHeight - metrics->emAscent;
   4516 
   4517  metrics->maxAscent = metrics->emAscent;
   4518  metrics->maxDescent = metrics->emDescent;
   4519 
   4520  const float UNINITIALIZED_LEADING = -10000.0f;
   4521  metrics->externalLeading = UNINITIALIZED_LEADING;
   4522 
   4523  if (mFUnitsConvFactor < 0.0) {
   4524    uint16_t upem = GetFontEntry()->UnitsPerEm();
   4525    if (upem != gfxFontEntry::kInvalidUPEM) {
   4526      AutoWriteLock lock(mLock);
   4527      mFUnitsConvFactor = GetAdjustedSize() / upem;
   4528    }
   4529  }
   4530 
   4531 #define SET_UNSIGNED(field, src) \
   4532  metrics->field = uint16_t(src) * mFUnitsConvFactor
   4533 #define SET_SIGNED(field, src) metrics->field = int16_t(src) * mFUnitsConvFactor
   4534 
   4535  gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
   4536  if (os2Table && mFUnitsConvFactor >= 0.0) {
   4537    const OS2Table* os2 =
   4538        reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
   4539    // These fields should always be present in any valid OS/2 table
   4540    if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
   4541      SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
   4542      // Use ascent+descent from the horizontal metrics as the default
   4543      // advance (aveCharWidth) in vertical mode
   4544      gfxFloat ascentDescent =
   4545          gfxFloat(mFUnitsConvFactor) *
   4546          (int16_t(os2->sTypoAscender) - int16_t(os2->sTypoDescender));
   4547      metrics->aveCharWidth = std::max(metrics->emHeight, ascentDescent);
   4548      // Use xAvgCharWidth from horizontal metrics as minimum font extent
   4549      // for vertical layout, applying half of it to ascent and half to
   4550      // descent (to work with a default centered baseline).
   4551      gfxFloat halfCharWidth =
   4552          int16_t(os2->xAvgCharWidth) * gfxFloat(mFUnitsConvFactor) / 2;
   4553      metrics->maxAscent = std::max(metrics->maxAscent, halfCharWidth);
   4554      metrics->maxDescent = std::max(metrics->maxDescent, halfCharWidth);
   4555    }
   4556  }
   4557 
   4558  // If we didn't set aveCharWidth from OS/2, try to read 'hhea' metrics
   4559  // and use the line height from its ascent/descent.
   4560  if (!metrics->aveCharWidth) {
   4561    gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
   4562    if (hheaTable && mFUnitsConvFactor >= 0.0) {
   4563      const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
   4564          hb_blob_get_data(hheaTable, &len));
   4565      if (len >= sizeof(MetricsHeader)) {
   4566        SET_SIGNED(aveCharWidth,
   4567                   int16_t(hhea->ascender) - int16_t(hhea->descender));
   4568        metrics->maxAscent = metrics->aveCharWidth / 2;
   4569        metrics->maxDescent = metrics->aveCharWidth - metrics->maxAscent;
   4570      }
   4571    }
   4572  }
   4573 
   4574  // Read real vertical metrics if available.
   4575  metrics->ideographicWidth = -1.0;
   4576  metrics->zeroWidth = -1.0;
   4577  gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag);
   4578  if (vheaTable && mFUnitsConvFactor >= 0.0) {
   4579    const MetricsHeader* vhea = reinterpret_cast<const MetricsHeader*>(
   4580        hb_blob_get_data(vheaTable, &len));
   4581    if (len >= sizeof(MetricsHeader)) {
   4582      SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
   4583      // Redistribute space between ascent/descent because we want a
   4584      // centered vertical baseline by default.
   4585      gfxFloat halfExtent =
   4586          0.5 * gfxFloat(mFUnitsConvFactor) *
   4587          (int16_t(vhea->ascender) + std::abs(int16_t(vhea->descender)));
   4588      // Some bogus fonts have ascent and descent set to zero in 'vhea'.
   4589      // In that case we just ignore them and keep our synthetic values
   4590      // from above.
   4591      if (halfExtent > 0) {
   4592        metrics->maxAscent = halfExtent;
   4593        metrics->maxDescent = halfExtent;
   4594        SET_SIGNED(externalLeading, vhea->lineGap);
   4595      }
   4596      // Call gfxHarfBuzzShaper::GetGlyphVAdvance directly, as GetCharAdvance
   4597      // would potentially recurse if no v-advance is available and it attempts
   4598      // to fall back to a value from mVerticalMetrics.
   4599      if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
   4600        uint32_t gid = ProvidesGetGlyph()
   4601                           ? GetGlyph(kWaterIdeograph, 0)
   4602                           : shaper->GetNominalGlyph(kWaterIdeograph);
   4603        if (gid) {
   4604          int32_t advance = shaper->GetGlyphVAdvance(gid);
   4605          // Convert 16.16 fixed-point advance from the shaper to a float.
   4606          metrics->ideographicWidth =
   4607              advance < 0 ? metrics->aveCharWidth : advance / 65536.0;
   4608        }
   4609        gid = ProvidesGetGlyph() ? GetGlyph('0', 0)
   4610                                 : shaper->GetNominalGlyph('0');
   4611        if (gid) {
   4612          int32_t advance = shaper->GetGlyphVAdvance(gid);
   4613          metrics->zeroWidth =
   4614              advance < 0 ? metrics->aveCharWidth : advance / 65536.0;
   4615        }
   4616      }
   4617    }
   4618  }
   4619 
   4620  // If we didn't set aveCharWidth above, we must be dealing with a non-sfnt
   4621  // font of some kind (Type1, bitmap, vector, ...), so fall back to using
   4622  // whatever the platform backend figured out for horizontal layout.
   4623  // And if we haven't set externalLeading yet, then copy that from the
   4624  // horizontal metrics as well, to help consistency of CSS line-height.
   4625  if (!metrics->aveCharWidth ||
   4626      metrics->externalLeading == UNINITIALIZED_LEADING) {
   4627    const Metrics& horizMetrics = GetHorizontalMetrics();
   4628    if (!metrics->aveCharWidth) {
   4629      metrics->aveCharWidth = horizMetrics.maxAscent + horizMetrics.maxDescent;
   4630    }
   4631    if (metrics->externalLeading == UNINITIALIZED_LEADING) {
   4632      metrics->externalLeading = horizMetrics.externalLeading;
   4633    }
   4634  }
   4635 
   4636  // Get underline thickness from the 'post' table if available.
   4637  // We also read the underline position, although in vertical-upright mode
   4638  // this will not be appropriate to use directly (see nsTextFrame.cpp).
   4639  gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
   4640  if (postTable) {
   4641    const PostTable* post =
   4642        reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len));
   4643    if (len >= offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) {
   4644      static_assert(offsetof(PostTable, underlinePosition) <
   4645                        offsetof(PostTable, underlineThickness),
   4646                    "broken PostTable struct?");
   4647      SET_SIGNED(underlineOffset, post->underlinePosition);
   4648      SET_UNSIGNED(underlineSize, post->underlineThickness);
   4649      // Also use for strikeout if we didn't find that in OS/2 above.
   4650      if (!metrics->strikeoutSize) {
   4651        metrics->strikeoutSize = metrics->underlineSize;
   4652      }
   4653    }
   4654  }
   4655 
   4656 #undef SET_UNSIGNED
   4657 #undef SET_SIGNED
   4658 
   4659  // If we didn't read this from a vhea table, it will still be zero.
   4660  // In any case, let's make sure it is not less than the value we've
   4661  // come up with for aveCharWidth.
   4662  metrics->maxAdvance = std::max(metrics->maxAdvance, metrics->aveCharWidth);
   4663 
   4664  // Thickness of underline and strikeout may have been read from tables,
   4665  // but in case they were not present, ensure a minimum of 1 pixel.
   4666  metrics->underlineSize = std::max(1.0, metrics->underlineSize);
   4667 
   4668  metrics->strikeoutSize = std::max(1.0, metrics->strikeoutSize);
   4669  metrics->strikeoutOffset = -0.5 * metrics->strikeoutSize;
   4670 
   4671  // Somewhat arbitrary values for now, subject to future refinement...
   4672  metrics->spaceWidth = metrics->aveCharWidth;
   4673  metrics->xHeight = metrics->emHeight / 2;
   4674  metrics->capHeight = metrics->maxAscent;
   4675 
   4676  metrics->maxHeight = metrics->maxAscent + metrics->maxDescent;
   4677  metrics->internalLeading =
   4678      std::max(0.0, metrics->maxHeight - metrics->emHeight);
   4679 
   4680  if (metrics->zeroWidth < 0.0) {
   4681    metrics->zeroWidth = metrics->aveCharWidth;
   4682  }
   4683 
   4684  if (!mVerticalMetrics.compareExchange(nullptr, metrics)) {
   4685    delete metrics;
   4686  }
   4687 }
   4688 
   4689 gfxFloat gfxFont::SynthesizeSpaceWidth(uint32_t aCh) {
   4690  // return an appropriate width for various Unicode space characters
   4691  // that we "fake" if they're not actually present in the font;
   4692  // returns negative value if the char is not a known space.
   4693  switch (aCh) {
   4694    case 0x2000:  // en quad
   4695    case 0x2002:
   4696      return GetAdjustedSize() / 2;  // en space
   4697    case 0x2001:                     // em quad
   4698    case 0x2003:
   4699      return GetAdjustedSize();  // em space
   4700    case 0x2004:
   4701      return GetAdjustedSize() / 3;  // three-per-em space
   4702    case 0x2005:
   4703      return GetAdjustedSize() / 4;  // four-per-em space
   4704    case 0x2006:
   4705      return GetAdjustedSize() / 6;  // six-per-em space
   4706    case 0x2007:
   4707      return GetMetrics(nsFontMetrics::eHorizontal)
   4708          .ZeroOrAveCharWidth();  // figure space
   4709    case 0x2008:
   4710      return GetMetrics(nsFontMetrics::eHorizontal)
   4711          .spaceWidth;  // punctuation space
   4712    case 0x2009:
   4713      return GetAdjustedSize() / 5;  // thin space
   4714    case 0x200a:
   4715      return GetAdjustedSize() / 10;  // hair space
   4716    case 0x202f:
   4717      return GetAdjustedSize() / 5;  // narrow no-break space
   4718    case 0x3000:
   4719      return GetAdjustedSize();  // ideographic space
   4720    default:
   4721      return -1.0;
   4722  }
   4723 }
   4724 
   4725 void gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
   4726                                     FontCacheSizes* aSizes) const {
   4727  AutoReadLock lock(mLock);
   4728  for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
   4729    aSizes->mFontInstances +=
   4730        mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
   4731  }
   4732  if (mWordCache) {
   4733    aSizes->mShapedWords +=
   4734        mWordCache->shallowSizeOfIncludingThis(aMallocSizeOf);
   4735    for (auto it = mWordCache->iter(); !it.done(); it.next()) {
   4736      aSizes->mShapedWords +=
   4737          it.get().value()->SizeOfIncludingThis(aMallocSizeOf);
   4738    }
   4739  }
   4740 }
   4741 
   4742 void gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   4743                                     FontCacheSizes* aSizes) const {
   4744  aSizes->mFontInstances += aMallocSizeOf(this);
   4745  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   4746 }
   4747 
   4748 void gfxFont::AddGlyphChangeObserver(GlyphChangeObserver* aObserver) {
   4749  AutoWriteLock lock(mLock);
   4750  if (!mGlyphChangeObservers) {
   4751    mGlyphChangeObservers = MakeUnique<nsTHashSet<GlyphChangeObserver*>>();
   4752  }
   4753  mGlyphChangeObservers->Insert(aObserver);
   4754 }
   4755 
   4756 void gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver* aObserver) {
   4757  AutoWriteLock lock(mLock);
   4758  NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
   4759  NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver),
   4760               "Observer not registered");
   4761  mGlyphChangeObservers->Remove(aObserver);
   4762 }
   4763 
   4764 #define DEFAULT_PIXEL_FONT_SIZE 16.0f
   4765 
   4766 gfxFontStyle::gfxFontStyle()
   4767    : size(DEFAULT_PIXEL_FONT_SIZE),
   4768      sizeAdjust(0.0f),
   4769      baselineOffset(0.0f),
   4770      languageOverride{0},
   4771      weight(FontWeight::NORMAL),
   4772      stretch(FontStretch::NORMAL),
   4773      style(FontSlantStyle::NORMAL),
   4774      variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
   4775      variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
   4776      sizeAdjustBasis(uint8_t(FontSizeAdjust::Tag::None)),
   4777      systemFont(true),
   4778      printerFont(false),
   4779 #ifdef XP_WIN
   4780      allowForceGDIClassic(true),
   4781 #endif
   4782      useGrayscaleAntialiasing(false),
   4783      allowSyntheticWeight(true),
   4784      synthesisStyle(StyleFontSynthesisStyle::Auto),
   4785      allowSyntheticSmallCaps(true),
   4786      useSyntheticPosition(true),
   4787      noFallbackVariantFeatures(true) {
   4788 }
   4789 
   4790 gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
   4791                           FontStretch aStretch, gfxFloat aSize,
   4792                           const FontSizeAdjust& aSizeAdjust, bool aSystemFont,
   4793                           bool aPrinterFont,
   4794 #ifdef XP_WIN
   4795                           bool aAllowForceGDIClassic,
   4796 #endif
   4797                           bool aAllowWeightSynthesis,
   4798                           StyleFontSynthesisStyle aStyleSynthesis,
   4799                           bool aAllowSmallCapsSynthesis,
   4800                           bool aUsePositionSynthesis,
   4801                           StyleFontLanguageOverride aLanguageOverride)
   4802    : size(aSize),
   4803      baselineOffset(0.0f),
   4804      languageOverride(aLanguageOverride),
   4805      weight(aWeight),
   4806      stretch(aStretch),
   4807      style(aStyle),
   4808      variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
   4809      variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
   4810      systemFont(aSystemFont),
   4811      printerFont(aPrinterFont),
   4812 #ifdef XP_WIN
   4813      allowForceGDIClassic(aAllowForceGDIClassic),
   4814 #endif
   4815      useGrayscaleAntialiasing(false),
   4816      allowSyntheticWeight(aAllowWeightSynthesis),
   4817      synthesisStyle(aStyleSynthesis),
   4818      allowSyntheticSmallCaps(aAllowSmallCapsSynthesis),
   4819      useSyntheticPosition(aUsePositionSynthesis),
   4820      noFallbackVariantFeatures(true) {
   4821  MOZ_ASSERT(!std::isnan(size));
   4822 
   4823  sizeAdjustBasis = uint8_t(aSizeAdjust.tag);
   4824  // sizeAdjustBasis is currently a small bitfield, so let's assert that the
   4825  // tag value was not truncated.
   4826  MOZ_ASSERT(FontSizeAdjust::Tag(sizeAdjustBasis) == aSizeAdjust.tag,
   4827             "gfxFontStyle.sizeAdjustBasis too small?");
   4828 
   4829 #define HANDLE_TAG(TAG)                 \
   4830  case FontSizeAdjust::Tag::TAG:        \
   4831    sizeAdjust = aSizeAdjust.As##TAG(); \
   4832    break;
   4833 
   4834  switch (aSizeAdjust.tag) {
   4835    case FontSizeAdjust::Tag::None:
   4836      sizeAdjust = 0.0f;
   4837      break;
   4838      HANDLE_TAG(ExHeight)
   4839      HANDLE_TAG(CapHeight)
   4840      HANDLE_TAG(ChWidth)
   4841      HANDLE_TAG(IcWidth)
   4842      HANDLE_TAG(IcHeight)
   4843  }
   4844 
   4845 #undef HANDLE_TAG
   4846 
   4847  MOZ_ASSERT(!std::isnan(sizeAdjust));
   4848 
   4849  if (weight > FontWeight::FromInt(1000)) {
   4850    weight = FontWeight::FromInt(1000);
   4851  }
   4852  if (weight < FontWeight::FromInt(1)) {
   4853    weight = FontWeight::FromInt(1);
   4854  }
   4855 
   4856  if (size >= FONT_MAX_SIZE) {
   4857    size = FONT_MAX_SIZE;
   4858    sizeAdjust = 0.0f;
   4859    sizeAdjustBasis = uint8_t(FontSizeAdjust::Tag::None);
   4860  } else if (size < 0.0) {
   4861    NS_WARNING("negative font size");
   4862    size = 0.0;
   4863  }
   4864 }
   4865 
   4866 PLDHashNumber gfxFontStyle::Hash() const {
   4867  uint32_t hash = variationSettings.IsEmpty()
   4868                      ? 0
   4869                      : mozilla::HashBytes(variationSettings.Elements(),
   4870                                           variationSettings.Length() *
   4871                                               sizeof(gfxFontVariation));
   4872  return mozilla::AddToHash(hash, systemFont, style.Raw(), stretch.Raw(),
   4873                            weight.Raw(), size, int32_t(sizeAdjust * 1000.0f));
   4874 }
   4875 
   4876 void gfxFontStyle::AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel) {
   4877  MOZ_ASSERT(
   4878      variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL && baselineOffset == 0,
   4879      "can't adjust this style for sub/superscript");
   4880 
   4881  // calculate the baseline offset (before changing the size)
   4882  if (variantSubSuper == NS_FONT_VARIANT_POSITION_SUPER) {
   4883    baselineOffset = size * -NS_FONT_SUPERSCRIPT_OFFSET_RATIO;
   4884  } else {
   4885    baselineOffset = size * NS_FONT_SUBSCRIPT_OFFSET_RATIO;
   4886  }
   4887 
   4888  // calculate reduced size, roughly mimicing behavior of font-size: smaller
   4889  float cssSize = size * aAppUnitsPerDevPixel / AppUnitsPerCSSPixel();
   4890  if (cssSize < NS_FONT_SUB_SUPER_SMALL_SIZE) {
   4891    size *= NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL;
   4892  } else if (cssSize >= NS_FONT_SUB_SUPER_LARGE_SIZE) {
   4893    size *= NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
   4894  } else {
   4895    gfxFloat t = (cssSize - NS_FONT_SUB_SUPER_SMALL_SIZE) /
   4896                 (NS_FONT_SUB_SUPER_LARGE_SIZE - NS_FONT_SUB_SUPER_SMALL_SIZE);
   4897    size *= (1.0 - t) * NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL +
   4898            t * NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
   4899  }
   4900 
   4901  // clear the variant field
   4902  variantSubSuper = NS_FONT_VARIANT_POSITION_NORMAL;
   4903 }
   4904 
   4905 bool gfxFont::TryGetMathTable() {
   4906  if (mMathInitialized) {
   4907    return !!mMathTable;
   4908  }
   4909 
   4910  auto face(GetFontEntry()->GetHBFace());
   4911  if (hb_ot_math_has_data(face)) {
   4912    auto* mathTable = new gfxMathTable(face, GetAdjustedSize());
   4913    if (!mMathTable.compareExchange(nullptr, mathTable)) {
   4914      delete mathTable;
   4915    }
   4916  }
   4917  mMathInitialized = true;
   4918 
   4919  return !!mMathTable;
   4920 }