LCOV - code coverage report
Current view: top level - gfx/thebes - gfxFont.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 655 1833 35.7 %
Date: 2017-07-14 16:53:18 Functions: 60 119 50.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       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/BinarySearch.h"
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/gfx/2D.h"
      11             : #include "mozilla/MathAlgorithms.h"
      12             : #include "mozilla/SVGContextPaint.h"
      13             : 
      14             : #include "mozilla/Logging.h"
      15             : 
      16             : #include "nsITimer.h"
      17             : 
      18             : #include "gfxGlyphExtents.h"
      19             : #include "gfxPlatform.h"
      20             : #include "gfxTextRun.h"
      21             : #include "nsGkAtoms.h"
      22             : 
      23             : #include "gfxTypes.h"
      24             : #include "gfxContext.h"
      25             : #include "gfxFontMissingGlyphs.h"
      26             : #include "gfxGraphiteShaper.h"
      27             : #include "gfxHarfBuzzShaper.h"
      28             : #include "gfxUserFontSet.h"
      29             : #include "nsSpecialCasingData.h"
      30             : #include "nsTextRunTransformations.h"
      31             : #include "nsUGenCategory.h"
      32             : #include "nsUnicodeProperties.h"
      33             : #include "nsStyleConsts.h"
      34             : #include "mozilla/AppUnits.h"
      35             : #include "mozilla/Likely.h"
      36             : #include "mozilla/MemoryReporting.h"
      37             : #include "mozilla/Preferences.h"
      38             : #include "mozilla/Services.h"
      39             : #include "mozilla/Telemetry.h"
      40             : #include "gfxMathTable.h"
      41             : #include "gfxSVGGlyphs.h"
      42             : #include "gfx2DGlue.h"
      43             : 
      44             : #include "GreekCasing.h"
      45             : 
      46             : #include "cairo.h"
      47             : 
      48             : #include "harfbuzz/hb.h"
      49             : #include "harfbuzz/hb-ot.h"
      50             : 
      51             : #include <algorithm>
      52             : #include <limits>
      53             : #include <cmath>
      54             : 
      55             : using namespace mozilla;
      56             : using namespace mozilla::gfx;
      57             : using namespace mozilla::unicode;
      58             : using mozilla::services::GetObserverService;
      59             : 
      60             : gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
      61             : 
      62             : #ifdef DEBUG_roc
      63             : #define DEBUG_TEXT_RUN_STORAGE_METRICS
      64             : #endif
      65             : 
      66             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
      67             : uint32_t gTextRunStorageHighWaterMark = 0;
      68             : uint32_t gTextRunStorage = 0;
      69             : uint32_t gFontCount = 0;
      70             : uint32_t gGlyphExtentsCount = 0;
      71             : uint32_t gGlyphExtentsWidthsTotalSize = 0;
      72             : uint32_t gGlyphExtentsSetupEagerSimple = 0;
      73             : uint32_t gGlyphExtentsSetupEagerTight = 0;
      74             : uint32_t gGlyphExtentsSetupLazyTight = 0;
      75             : uint32_t gGlyphExtentsSetupFallBackToTight = 0;
      76             : #endif
      77             : 
      78             : #define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
      79             :                                   LogLevel::Debug, args)
      80             : #define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
      81             :                                         gfxPlatform::GetLog(eGfxLog_fontinit), \
      82             :                                         LogLevel::Debug)
      83             : 
      84             : 
      85             : /*
      86             :  * gfxFontCache - global cache of gfxFont instances.
      87             :  * Expires unused fonts after a short interval;
      88             :  * notifies fonts to age their cached shaped-word records;
      89             :  * observes memory-pressure notification and tells fonts to clear their
      90             :  * shaped-word caches to free up memory.
      91             :  */
      92             : 
      93           0 : MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
      94             : 
      95          39 : NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
      96             : 
      97             : /*virtual*/
      98           2 : gfxTextRunFactory::~gfxTextRunFactory()
      99             : {
     100             :     // Should not be dropped by stylo
     101           1 :     MOZ_ASSERT(NS_IsMainThread());
     102           1 : }
     103             : 
     104             : NS_IMETHODIMP
     105           0 : gfxFontCache::MemoryReporter::CollectReports(
     106             :     nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
     107             : {
     108           0 :     FontCacheSizes sizes;
     109             : 
     110           0 :     gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
     111           0 :                                                      &sizes);
     112             : 
     113           0 :     MOZ_COLLECT_REPORT(
     114             :         "explicit/gfx/font-cache", KIND_HEAP, UNITS_BYTES,
     115             :         sizes.mFontInstances,
     116           0 :         "Memory used for active font instances.");
     117             : 
     118           0 :     MOZ_COLLECT_REPORT(
     119             :         "explicit/gfx/font-shaped-words", KIND_HEAP, UNITS_BYTES,
     120             :         sizes.mShapedWords,
     121           0 :         "Memory used to cache shaped glyph data.");
     122             : 
     123           0 :     return NS_OK;
     124             : }
     125             : 
     126           3 : NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
     127             : 
     128             : NS_IMETHODIMP
     129           0 : gfxFontCache::Observer::Observe(nsISupports *aSubject,
     130             :                                 const char *aTopic,
     131             :                                 const char16_t *someData)
     132             : {
     133           0 :     if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
     134           0 :         gfxFontCache *fontCache = gfxFontCache::GetCache();
     135           0 :         if (fontCache) {
     136           0 :             fontCache->FlushShapedWordCaches();
     137             :         }
     138             :     } else {
     139           0 :         NS_NOTREACHED("unexpected notification topic");
     140             :     }
     141           0 :     return NS_OK;
     142             : }
     143             : 
     144             : nsresult
     145           3 : gfxFontCache::Init()
     146             : {
     147           3 :     NS_ASSERTION(!gGlobalCache, "Where did this come from?");
     148           6 :     gGlobalCache = new gfxFontCache(SystemGroup::EventTargetFor(TaskCategory::Other));
     149           3 :     if (!gGlobalCache) {
     150           0 :         return NS_ERROR_OUT_OF_MEMORY;
     151             :     }
     152           3 :     RegisterStrongMemoryReporter(new MemoryReporter());
     153           3 :     return NS_OK;
     154             : }
     155             : 
     156             : void
     157           0 : gfxFontCache::Shutdown()
     158             : {
     159           0 :     delete gGlobalCache;
     160           0 :     gGlobalCache = nullptr;
     161             : 
     162             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     163             :     printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
     164             :     printf("Total number of fonts=%d\n", gFontCount);
     165             :     printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
     166             :             int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
     167             :     printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
     168             :     printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
     169             :     printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
     170             :     printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
     171             :     printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
     172             : #endif
     173           0 : }
     174             : 
     175           3 : gfxFontCache::gfxFontCache(nsIEventTarget* aEventTarget)
     176             :     : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000,
     177           3 :                                      "gfxFontCache", aEventTarget)
     178             : {
     179           6 :     nsCOMPtr<nsIObserverService> obs = GetObserverService();
     180           3 :     if (obs) {
     181           6 :         obs->AddObserver(new Observer, "memory-pressure", false);
     182             :     }
     183             : 
     184             : #ifndef RELEASE_OR_BETA
     185             :     // Currently disabled for release builds, due to unexplained crashes
     186             :     // during expiration; see bug 717175 & 894798.
     187           3 :     mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
     188           3 :     if (mWordCacheExpirationTimer) {
     189           3 :         if (XRE_IsContentProcess() && NS_IsMainThread()) {
     190           2 :             mWordCacheExpirationTimer->SetTarget(aEventTarget);
     191             :         }
     192           3 :         mWordCacheExpirationTimer->InitWithNamedFuncCallback(
     193             :           WordCacheExpirationTimerCallback,
     194             :           this,
     195             :           SHAPED_WORD_TIMEOUT_SECONDS * 1000,
     196             :           nsITimer::TYPE_REPEATING_SLACK,
     197           3 :           "gfxFontCache::gfxFontCache");
     198             :     }
     199             : #endif
     200           3 : }
     201             : 
     202           0 : gfxFontCache::~gfxFontCache()
     203             : {
     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           0 :     gfxUserFontSet::UserFontCache::Shutdown();
     208             : 
     209           0 :     if (mWordCacheExpirationTimer) {
     210           0 :         mWordCacheExpirationTimer->Cancel();
     211           0 :         mWordCacheExpirationTimer = nullptr;
     212             :     }
     213             : 
     214             :     // Expire everything that has a zero refcount, so we don't leak them.
     215           0 :     AgeAllGenerations();
     216             :     // All fonts should be gone.
     217           0 :     NS_WARNING_ASSERTION(mFonts.Count() == 0,
     218             :                          "Fonts still alive while shutting down gfxFontCache");
     219             :     // Note that we have to delete everything through the expiration
     220             :     // tracker, since there might be fonts not in the hashtable but in
     221             :     // the tracker.
     222           0 : }
     223             : 
     224             : bool
     225           9 : gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
     226             : {
     227           9 :     const gfxCharacterMap* fontUnicodeRangeMap = mFont->GetUnicodeRangeMap();
     228          18 :     return aKey->mFontEntry == mFont->GetFontEntry() &&
     229          27 :            aKey->mStyle->Equals(*mFont->GetStyle()) &&
     230          18 :            ((!aKey->mUnicodeRangeMap && !fontUnicodeRangeMap) ||
     231           0 :             (aKey->mUnicodeRangeMap && fontUnicodeRangeMap &&
     232           9 :              aKey->mUnicodeRangeMap->Equals(fontUnicodeRangeMap)));
     233             : }
     234             : 
     235             : gfxFont*
     236           9 : gfxFontCache::Lookup(const gfxFontEntry* aFontEntry,
     237             :                      const gfxFontStyle* aStyle,
     238             :                      const gfxCharacterMap* aUnicodeRangeMap)
     239             : {
     240           9 :     Key key(aFontEntry, aStyle, aUnicodeRangeMap);
     241           9 :     HashEntry *entry = mFonts.GetEntry(key);
     242             : 
     243           9 :     Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nullptr);
     244           9 :     if (!entry)
     245           8 :         return nullptr;
     246             : 
     247           1 :     return entry->mFont;
     248             : }
     249             : 
     250             : void
     251           8 : gfxFontCache::AddNew(gfxFont *aFont)
     252             : {
     253           8 :     Key key(aFont->GetFontEntry(), aFont->GetStyle(),
     254          16 :             aFont->GetUnicodeRangeMap());
     255           8 :     HashEntry *entry = mFonts.PutEntry(key);
     256           8 :     if (!entry)
     257           0 :         return;
     258           8 :     gfxFont *oldFont = entry->mFont;
     259           8 :     entry->mFont = aFont;
     260             :     // Assert that we can find the entry we just put in (this fails if the key
     261             :     // has a NaN float value in it, e.g. 'sizeAdjust').
     262           8 :     MOZ_ASSERT(entry == mFonts.GetEntry(key));
     263             :     // If someone's asked us to replace an existing font entry, then that's a
     264             :     // bit weird, but let it happen, and expire the old font if it's not used.
     265           8 :     if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
     266             :         // if oldFont == aFont, recount should be > 0,
     267             :         // so we shouldn't be here.
     268           0 :         NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
     269           0 :         NotifyExpired(oldFont);
     270             :     }
     271             : }
     272             : 
     273             : void
     274          12 : gfxFontCache::NotifyReleased(gfxFont *aFont)
     275             : {
     276          12 :     nsresult rv = AddObject(aFont);
     277          12 :     if (NS_FAILED(rv)) {
     278             :         // We couldn't track it for some reason. Kill it now.
     279           0 :         DestroyFont(aFont);
     280             :     }
     281             :     // Note that we might have fonts that aren't in the hashtable, perhaps because
     282             :     // of OOM adding to the hashtable or because someone did an AddNew where
     283             :     // we already had a font. These fonts are added to the expiration tracker
     284             :     // anyway, even though Lookup can't resurrect them. Eventually they will
     285             :     // expire and be deleted.
     286          12 : }
     287             : 
     288             : void
     289           0 : gfxFontCache::NotifyExpired(gfxFont *aFont)
     290             : {
     291           0 :     aFont->ClearCachedWords();
     292           0 :     RemoveObject(aFont);
     293           0 :     DestroyFont(aFont);
     294           0 : }
     295             : 
     296             : void
     297           0 : gfxFontCache::DestroyFont(gfxFont *aFont)
     298             : {
     299           0 :     Key key(aFont->GetFontEntry(), aFont->GetStyle(),
     300           0 :             aFont->GetUnicodeRangeMap());
     301           0 :     HashEntry *entry = mFonts.GetEntry(key);
     302           0 :     if (entry && entry->mFont == aFont) {
     303           0 :         mFonts.RemoveEntry(entry);
     304             :     }
     305           0 :     NS_ASSERTION(aFont->GetRefCount() == 0,
     306             :                  "Destroying with non-zero ref count!");
     307           0 :     delete aFont;
     308           0 : }
     309             : 
     310             : /*static*/
     311             : void
     312           0 : gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
     313             : {
     314           0 :     gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
     315           0 :     for (auto it = cache->mFonts.Iter(); !it.Done(); it.Next()) {
     316           0 :         it.Get()->mFont->AgeCachedWords();
     317             :     }
     318           0 : }
     319             : 
     320             : void
     321           0 : gfxFontCache::FlushShapedWordCaches()
     322             : {
     323           0 :     for (auto it = mFonts.Iter(); !it.Done(); it.Next()) {
     324           0 :         it.Get()->mFont->ClearCachedWords();
     325             :     }
     326           0 : }
     327             : 
     328             : void
     329           0 : gfxFontCache::NotifyGlyphsChanged()
     330             : {
     331           0 :     for (auto it = mFonts.Iter(); !it.Done(); it.Next()) {
     332           0 :         it.Get()->mFont->NotifyGlyphsChanged();
     333             :     }
     334           0 : }
     335             : 
     336             : void
     337           0 : gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
     338             :                                      FontCacheSizes* aSizes) const
     339             : {
     340             :     // TODO: add the overhead of the expiration tracker (generation arrays)
     341             : 
     342           0 :     aSizes->mFontInstances += mFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
     343           0 :     for (auto iter = mFonts.ConstIter(); !iter.Done(); iter.Next()) {
     344           0 :         iter.Get()->mFont->AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
     345             :     }
     346           0 : }
     347             : 
     348             : void
     349           0 : gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
     350             :                                      FontCacheSizes* aSizes) const
     351             : {
     352           0 :     aSizes->mFontInstances += aMallocSizeOf(this);
     353           0 :     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
     354           0 : }
     355             : 
     356             : #define MAX_SSXX_VALUE 99
     357             : #define MAX_CVXX_VALUE 99
     358             : 
     359             : static void
     360           0 : LookupAlternateValues(gfxFontFeatureValueSet *featureLookup,
     361             :                       const nsAString& aFamily,
     362             :                       const nsTArray<gfxAlternateValue>& altValue,
     363             :                       nsTArray<gfxFontFeature>& aFontFeatures)
     364             : {
     365           0 :     uint32_t numAlternates = altValue.Length();
     366           0 :     for (uint32_t i = 0; i < numAlternates; i++) {
     367           0 :         const gfxAlternateValue& av = altValue.ElementAt(i);
     368           0 :         AutoTArray<uint32_t,4> values;
     369             : 
     370             :         // map <family, name, feature> ==> <values>
     371             :         bool found =
     372           0 :             featureLookup->GetFontFeatureValuesFor(aFamily, av.alternate,
     373           0 :                                                    av.value, values);
     374           0 :         uint32_t numValues = values.Length();
     375             : 
     376             :         // nothing defined, skip
     377           0 :         if (!found || numValues == 0) {
     378           0 :             continue;
     379             :         }
     380             : 
     381             :         gfxFontFeature feature;
     382           0 :         if (av.alternate == NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT) {
     383           0 :             NS_ASSERTION(numValues <= 2,
     384             :                          "too many values allowed for character-variant");
     385             :             // character-variant(12 3) ==> 'cv12' = 3
     386           0 :             uint32_t nn = values.ElementAt(0);
     387             :             // ignore values greater than 99
     388           0 :             if (nn == 0 || nn > MAX_CVXX_VALUE) {
     389           0 :                 continue;
     390             :             }
     391           0 :             feature.mValue = 1;
     392           0 :             if (numValues > 1) {
     393           0 :                 feature.mValue = values.ElementAt(1);
     394             :             }
     395           0 :             feature.mTag = HB_TAG('c','v',('0' + nn / 10), ('0' + nn % 10));
     396           0 :             aFontFeatures.AppendElement(feature);
     397             : 
     398           0 :         } else if (av.alternate == NS_FONT_VARIANT_ALTERNATES_STYLESET) {
     399             :             // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
     400           0 :             feature.mValue = 1;
     401           0 :             for (uint32_t v = 0; v < numValues; v++) {
     402           0 :                 uint32_t nn = values.ElementAt(v);
     403           0 :                 if (nn == 0 || nn > MAX_SSXX_VALUE) {
     404           0 :                     continue;
     405             :                 }
     406           0 :                 feature.mTag = HB_TAG('s','s',('0' + nn / 10), ('0' + nn % 10));
     407           0 :                 aFontFeatures.AppendElement(feature);
     408             :             }
     409             : 
     410             :         } else {
     411           0 :             NS_ASSERTION(numValues == 1,
     412             :                    "too many values for font-specific font-variant-alternates");
     413           0 :             feature.mValue = values.ElementAt(0);
     414             : 
     415           0 :             switch (av.alternate) {
     416             :                 case NS_FONT_VARIANT_ALTERNATES_STYLISTIC:  // salt
     417           0 :                     feature.mTag = HB_TAG('s','a','l','t');
     418           0 :                     break;
     419             :                 case NS_FONT_VARIANT_ALTERNATES_SWASH:  // swsh, cswh
     420           0 :                     feature.mTag = HB_TAG('s','w','s','h');
     421           0 :                     aFontFeatures.AppendElement(feature);
     422           0 :                     feature.mTag = HB_TAG('c','s','w','h');
     423           0 :                     break;
     424             :                 case NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: // ornm
     425           0 :                     feature.mTag = HB_TAG('o','r','n','m');
     426           0 :                     break;
     427             :                 case NS_FONT_VARIANT_ALTERNATES_ANNOTATION: // nalt
     428           0 :                     feature.mTag = HB_TAG('n','a','l','t');
     429           0 :                     break;
     430             :                 default:
     431           0 :                     feature.mTag = 0;
     432           0 :                     break;
     433             :             }
     434             : 
     435           0 :             NS_ASSERTION(feature.mTag, "unsupported alternate type");
     436           0 :             if (!feature.mTag) {
     437           0 :                 continue;
     438             :             }
     439           0 :             aFontFeatures.AppendElement(feature);
     440             :         }
     441             :     }
     442           0 : }
     443             : 
     444             : /* static */ void
     445          34 : gfxFontShaper::MergeFontFeatures(
     446             :     const gfxFontStyle *aStyle,
     447             :     const nsTArray<gfxFontFeature>& aFontFeatures,
     448             :     bool aDisableLigatures,
     449             :     const nsAString& aFamilyName,
     450             :     bool aAddSmallCaps,
     451             :     void (*aHandleFeature)(const uint32_t&, uint32_t&, void*),
     452             :     void* aHandleFeatureData)
     453             : {
     454          34 :     uint32_t numAlts = aStyle->alternateValues.Length();
     455             :     const nsTArray<gfxFontFeature>& styleRuleFeatures =
     456          34 :         aStyle->featureSettings;
     457             : 
     458             :     // Bail immediately if nothing to do, which is the common case.
     459         102 :     if (styleRuleFeatures.IsEmpty() &&
     460          68 :         aFontFeatures.IsEmpty() &&
     461          68 :         !aDisableLigatures &&
     462          68 :         aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL &&
     463         102 :         aStyle->variantSubSuper == NS_FONT_VARIANT_POSITION_NORMAL &&
     464             :         numAlts == 0) {
     465          34 :         return;
     466             :     }
     467             : 
     468           0 :     nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
     469             : 
     470             :     // Ligature features are enabled by default in the generic shaper,
     471             :     // so we explicitly turn them off if necessary (for letter-spacing)
     472           0 :     if (aDisableLigatures) {
     473           0 :         mergedFeatures.Put(HB_TAG('l','i','g','a'), 0);
     474           0 :         mergedFeatures.Put(HB_TAG('c','l','i','g'), 0);
     475             :     }
     476             : 
     477             :     // add feature values from font
     478             :     uint32_t i, count;
     479             : 
     480           0 :     count = aFontFeatures.Length();
     481           0 :     for (i = 0; i < count; i++) {
     482           0 :         const gfxFontFeature& feature = aFontFeatures.ElementAt(i);
     483           0 :         mergedFeatures.Put(feature.mTag, feature.mValue);
     484             :     }
     485             : 
     486             :     // font-variant-caps - handled here due to the need for fallback handling
     487             :     // petite caps cases can fallback to appropriate smallcaps
     488           0 :     uint32_t variantCaps = aStyle->variantCaps;
     489           0 :     switch (variantCaps) {
     490             :         case NS_FONT_VARIANT_CAPS_NORMAL:
     491           0 :             break;
     492             : 
     493             :         case NS_FONT_VARIANT_CAPS_ALLSMALL:
     494           0 :             mergedFeatures.Put(HB_TAG('c','2','s','c'), 1);
     495             :             // fall through to the small-caps case
     496             :             MOZ_FALLTHROUGH;
     497             : 
     498             :         case NS_FONT_VARIANT_CAPS_SMALLCAPS:
     499           0 :             mergedFeatures.Put(HB_TAG('s','m','c','p'), 1);
     500           0 :             break;
     501             : 
     502             :         case NS_FONT_VARIANT_CAPS_ALLPETITE:
     503           0 :             mergedFeatures.Put(aAddSmallCaps ? HB_TAG('c','2','s','c') :
     504           0 :                                                HB_TAG('c','2','p','c'), 1);
     505             :             // fall through to the petite-caps case
     506             :             MOZ_FALLTHROUGH;
     507             : 
     508             :         case NS_FONT_VARIANT_CAPS_PETITECAPS:
     509           0 :             mergedFeatures.Put(aAddSmallCaps ? HB_TAG('s','m','c','p') :
     510           0 :                                                HB_TAG('p','c','a','p'), 1);
     511           0 :             break;
     512             : 
     513             :         case NS_FONT_VARIANT_CAPS_TITLING:
     514           0 :             mergedFeatures.Put(HB_TAG('t','i','t','l'), 1);
     515           0 :             break;
     516             : 
     517             :         case NS_FONT_VARIANT_CAPS_UNICASE:
     518           0 :             mergedFeatures.Put(HB_TAG('u','n','i','c'), 1);
     519           0 :             break;
     520             : 
     521             :         default:
     522           0 :             MOZ_ASSERT_UNREACHABLE("Unexpected variantCaps");
     523             :             break;
     524             :     }
     525             : 
     526             :     // font-variant-position - handled here due to the need for fallback
     527           0 :     switch (aStyle->variantSubSuper) {
     528             :         case NS_FONT_VARIANT_POSITION_NORMAL:
     529           0 :             break;
     530             :         case NS_FONT_VARIANT_POSITION_SUPER:
     531           0 :             mergedFeatures.Put(HB_TAG('s','u','p','s'), 1);
     532           0 :             break;
     533             :         case NS_FONT_VARIANT_POSITION_SUB:
     534           0 :             mergedFeatures.Put(HB_TAG('s','u','b','s'), 1);
     535           0 :             break;
     536             :         default:
     537           0 :             MOZ_ASSERT_UNREACHABLE("Unexpected variantSubSuper");
     538             :             break;
     539             :     }
     540             : 
     541             :     // add font-specific feature values from style rules
     542           0 :     if (aStyle->featureValueLookup && numAlts > 0) {
     543           0 :         AutoTArray<gfxFontFeature,4> featureList;
     544             : 
     545             :         // insert list of alternate feature settings
     546           0 :         LookupAlternateValues(aStyle->featureValueLookup, aFamilyName,
     547           0 :                               aStyle->alternateValues, featureList);
     548             : 
     549           0 :         count = featureList.Length();
     550           0 :         for (i = 0; i < count; i++) {
     551           0 :             const gfxFontFeature& feature = featureList.ElementAt(i);
     552           0 :             mergedFeatures.Put(feature.mTag, feature.mValue);
     553             :         }
     554             :     }
     555             : 
     556             :     // add feature values from style rules
     557           0 :     count = styleRuleFeatures.Length();
     558           0 :     for (i = 0; i < count; i++) {
     559           0 :         const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i);
     560           0 :         mergedFeatures.Put(feature.mTag, feature.mValue);
     561             :     }
     562             : 
     563           0 :     if (mergedFeatures.Count() != 0) {
     564           0 :         for (auto iter = mergedFeatures.Iter(); !iter.Done(); iter.Next()) {
     565           0 :             aHandleFeature(iter.Key(), iter.Data(), aHandleFeatureData);
     566             :         }
     567             :     }
     568             : }
     569             : 
     570             : void
     571           2 : gfxShapedText::SetupClusterBoundaries(uint32_t        aOffset,
     572             :                                       const char16_t *aString,
     573             :                                       uint32_t        aLength)
     574             : {
     575           2 :     CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
     576             : 
     577           2 :     gfxTextRun::CompressedGlyph extendCluster;
     578           2 :     extendCluster.SetComplex(false, true, 0);
     579             : 
     580           2 :     ClusterIterator iter(aString, aLength);
     581             : 
     582             :     // the ClusterIterator won't be able to tell us if the string
     583             :     // _begins_ with a cluster-extender, so we handle that here
     584           2 :     if (aLength) {
     585           2 :         uint32_t ch = *aString;
     586           2 :         if (aLength > 1 && NS_IS_HIGH_SURROGATE(ch) &&
     587           0 :             NS_IS_LOW_SURROGATE(aString[1])) {
     588           0 :             ch = SURROGATE_TO_UCS4(ch, aString[1]);
     589             :         }
     590           2 :         if (IsClusterExtender(ch)) {
     591           0 :             *glyphs = extendCluster;
     592             :         }
     593             :     }
     594             : 
     595          24 :     while (!iter.AtEnd()) {
     596          11 :         if (*iter == char16_t(' ')) {
     597           0 :             glyphs->SetIsSpace();
     598             :         }
     599             :         // advance iter to the next cluster-start (or end of text)
     600          11 :         iter.Next();
     601             :         // step past the first char of the cluster
     602          11 :         aString++;
     603          11 :         glyphs++;
     604             :         // mark all the rest as cluster-continuations
     605          11 :         while (aString < iter) {
     606           0 :             *glyphs = extendCluster;
     607           0 :             glyphs++;
     608           0 :             aString++;
     609             :         }
     610             :     }
     611           2 : }
     612             : 
     613             : void
     614           2 : gfxShapedText::SetupClusterBoundaries(uint32_t       aOffset,
     615             :                                       const uint8_t *aString,
     616             :                                       uint32_t       aLength)
     617             : {
     618           2 :     CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
     619           2 :     const uint8_t *limit = aString + aLength;
     620             : 
     621         200 :     while (aString < limit) {
     622          99 :         if (*aString == uint8_t(' ')) {
     623           0 :             glyphs->SetIsSpace();
     624             :         }
     625          99 :         aString++;
     626          99 :         glyphs++;
     627             :     }
     628           2 : }
     629             : 
     630             : gfxShapedText::DetailedGlyph *
     631           0 : gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
     632             : {
     633           0 :     NS_ASSERTION(aIndex < GetLength(), "Index out of range");
     634             : 
     635           0 :     if (!mDetailedGlyphs) {
     636           0 :         mDetailedGlyphs = MakeUnique<DetailedGlyphStore>();
     637             :     }
     638             : 
     639           0 :     return mDetailedGlyphs->Allocate(aIndex, aCount);
     640             : }
     641             : 
     642             : void
     643           0 : gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
     644             :                          const DetailedGlyph *aGlyphs)
     645             : {
     646           0 :     NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
     647           0 :     NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
     648             :                  "First character can't be a ligature continuation!");
     649             : 
     650           0 :     uint32_t glyphCount = aGlyph.GetGlyphCount();
     651           0 :     if (glyphCount > 0) {
     652           0 :         DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
     653           0 :         memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
     654             :     }
     655           0 :     GetCharacterGlyphs()[aIndex] = aGlyph;
     656           0 : }
     657             : 
     658             : #define ZWNJ 0x200C
     659             : #define ZWJ  0x200D
     660             : static inline bool
     661         265 : IsIgnorable(uint32_t aChar)
     662             : {
     663         265 :     return (IsDefaultIgnorable(aChar)) || aChar == ZWNJ || aChar == ZWJ;
     664             : }
     665             : 
     666             : void
     667           0 : gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
     668             : {
     669           0 :     uint8_t category = GetGeneralCategory(aChar);
     670           0 :     if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
     671           0 :         category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
     672             :     {
     673           0 :         GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
     674             :     }
     675             : 
     676           0 :     DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
     677             : 
     678           0 :     details->mGlyphID = aChar;
     679           0 :     if (IsIgnorable(aChar)) {
     680             :         // Setting advance width to zero will prevent drawing the hexbox
     681           0 :         details->mAdvance = 0;
     682             :     } else {
     683             :         gfxFloat width =
     684           0 :             std::max(aFont->GetMetrics(gfxFont::eHorizontal).aveCharWidth,
     685           0 :                      gfxFloat(gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
     686           0 :                                 mAppUnitsPerDevUnit)));
     687           0 :         details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
     688             :     }
     689           0 :     details->mXOffset = 0;
     690           0 :     details->mYOffset = 0;
     691           0 :     GetCharacterGlyphs()[aIndex].SetMissing(1);
     692           0 : }
     693             : 
     694             : bool
     695         265 : gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
     696             : {
     697         265 :     if (IsIgnorable(aCh)) {
     698             :         // There are a few default-ignorables of Letter category (currently,
     699             :         // just the Hangul filler characters) that we'd better not discard
     700             :         // if they're followed by additional characters in the same cluster.
     701             :         // Some fonts use them to carry the width of a whole cluster of
     702             :         // combining jamos; see bug 1238243.
     703           0 :         if (GetGenCategory(aCh) == nsUGenCategory::kLetter &&
     704           0 :             aIndex + 1 < GetLength() &&
     705           0 :             !GetCharacterGlyphs()[aIndex + 1].IsClusterStart()) {
     706           0 :             return false;
     707             :         }
     708           0 :         DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
     709           0 :         details->mGlyphID = aCh;
     710           0 :         details->mAdvance = 0;
     711           0 :         details->mXOffset = 0;
     712           0 :         details->mYOffset = 0;
     713           0 :         GetCharacterGlyphs()[aIndex].SetMissing(1);
     714           0 :         return true;
     715             :     }
     716         265 :     return false;
     717             : }
     718             : 
     719             : void
     720           0 : gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
     721             :                                               uint32_t aOffset,
     722             :                                               uint32_t aLength)
     723             : {
     724           0 :     uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
     725           0 :     CompressedGlyph *charGlyphs = GetCharacterGlyphs();
     726           0 :     for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
     727           0 :          CompressedGlyph *glyphData = charGlyphs + i;
     728           0 :          if (glyphData->IsSimpleGlyph()) {
     729             :              // simple glyphs ==> just add the advance
     730           0 :              int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
     731           0 :              if (CompressedGlyph::IsSimpleAdvance(advance)) {
     732           0 :                  glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
     733             :              } else {
     734             :                  // rare case, tested by making this the default
     735           0 :                  uint32_t glyphIndex = glyphData->GetSimpleGlyph();
     736           0 :                  glyphData->SetComplex(true, true, 1);
     737           0 :                  DetailedGlyph detail = {glyphIndex, advance, 0, 0};
     738           0 :                  SetGlyphs(i, *glyphData, &detail);
     739             :              }
     740             :          } else {
     741             :              // complex glyphs ==> add offset at cluster/ligature boundaries
     742           0 :              uint32_t detailedLength = glyphData->GetGlyphCount();
     743           0 :              if (detailedLength) {
     744           0 :                  DetailedGlyph *details = GetDetailedGlyphs(i);
     745           0 :                  if (!details) {
     746           0 :                      continue;
     747             :                  }
     748           0 :                  if (IsRightToLeft()) {
     749           0 :                      details[0].mAdvance += synAppUnitOffset;
     750             :                  } else {
     751           0 :                      details[detailedLength - 1].mAdvance += synAppUnitOffset;
     752             :                  }
     753             :              }
     754             :          }
     755             :     }
     756           0 : }
     757             : 
     758             : void
     759          48 : gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
     760             : {
     761          48 :     mAscent = std::max(mAscent, aOther.mAscent);
     762          48 :     mDescent = std::max(mDescent, aOther.mDescent);
     763          48 :     if (aOtherIsOnLeft) {
     764             :         mBoundingBox =
     765           0 :             (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
     766             :     } else {
     767             :         mBoundingBox =
     768          48 :             mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
     769             :     }
     770          48 :     mAdvanceWidth += aOther.mAdvanceWidth;
     771          48 : }
     772             : 
     773           8 : gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
     774             :                  gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
     775           8 :                  AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
     776             :     mScaledFont(aScaledFont),
     777             :     mFontEntry(aFontEntry), mIsValid(true),
     778             :     mApplySyntheticBold(false),
     779             :     mMathInitialized(false),
     780             :     mStyle(*aFontStyle),
     781             :     mAdjustedSize(0.0),
     782             :     mFUnitsConvFactor(-1.0f), // negative to indicate "not yet initialized"
     783             :     mAntialiasOption(anAAOption),
     784           8 :     mUnscaledFont(aUnscaledFont)
     785             : {
     786             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     787             :     ++gFontCount;
     788             : #endif
     789           8 :     mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
     790           8 : }
     791             : 
     792           0 : gfxFont::~gfxFont()
     793             : {
     794           0 :     mFontEntry->NotifyFontDestroyed(this);
     795             : 
     796           0 :     if (mGlyphChangeObservers) {
     797           0 :         for (auto it = mGlyphChangeObservers->Iter(); !it.Done(); it.Next()) {
     798           0 :             it.Get()->GetKey()->ForgetFont();
     799             :         }
     800             :     }
     801           0 : }
     802             : 
     803             : // Work out whether cairo will snap inter-glyph spacing to pixels.
     804             : //
     805             : // Layout does not align text to pixel boundaries, so, with font drawing
     806             : // backends that snap glyph positions to pixels, it is important that
     807             : // inter-glyph spacing within words is always an integer number of pixels.
     808             : // This ensures that the drawing backend snaps all of the word's glyphs in the
     809             : // same direction and so inter-glyph spacing remains the same.
     810             : //
     811             : gfxFont::RoundingFlags
     812          71 : gfxFont::GetRoundOffsetsToPixels(DrawTarget* aDrawTarget)
     813             : {
     814          71 :   RoundingFlags result = RoundingFlags(0);
     815             : 
     816             :   // Could do something fancy here for ScaleFactors of
     817             :   // AxisAlignedTransforms, but we leave things simple.
     818             :   // Not much point rounding if a matrix will mess things up anyway.
     819             :   // Also return false for non-cairo contexts.
     820          71 :   if (aDrawTarget->GetTransform().HasNonTranslation()) {
     821           0 :     return result;
     822             :   }
     823             : 
     824             :   // All raster backends snap glyphs to pixels vertically.
     825             :   // Print backends set CAIRO_HINT_METRICS_OFF.
     826          71 :   result |= RoundingFlags::kRoundY;
     827             : 
     828             :   // If we can't set up the cairo font, bail out.
     829          71 :   if (!SetupCairoFont(aDrawTarget)) {
     830           0 :     return result;
     831             :   }
     832             : 
     833          71 :   cairo_t* cr = gfxFont::RefCairo(aDrawTarget);
     834          71 :   cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
     835             : 
     836             :   // bug 1198921 - this sometimes fails under Windows for whatver reason
     837          71 :   NS_ASSERTION(scaled_font, "null cairo scaled font should never be returned "
     838             :     "by cairo_get_scaled_font");
     839          71 :   if (!scaled_font) {
     840           0 :     result |= RoundingFlags::kRoundX; // default to the same as the fallback path below
     841           0 :     return result;
     842             :   }
     843             : 
     844             :   // Sometimes hint metrics gets set for us, most notably for printing.
     845          71 :   cairo_font_options_t *font_options = cairo_font_options_create();
     846          71 :   cairo_scaled_font_get_font_options(scaled_font, font_options);
     847             :   cairo_hint_metrics_t hint_metrics =
     848          71 :     cairo_font_options_get_hint_metrics(font_options);
     849          71 :   cairo_font_options_destroy(font_options);
     850             : 
     851          71 :   switch (hint_metrics) {
     852             :   case CAIRO_HINT_METRICS_OFF:
     853           0 :     result &= ~RoundingFlags::kRoundY;
     854           0 :     return result;
     855             :   case CAIRO_HINT_METRICS_DEFAULT:
     856             :     // Here we mimic what cairo surface/font backends do.  Printing
     857             :     // surfaces have already been handled by hint_metrics.  The
     858             :     // fallback show_glyphs implementation composites pixel-aligned
     859             :     // glyph surfaces, so we just pick surface/font combinations that
     860             :     // override this.
     861           0 :     switch (cairo_scaled_font_get_type(scaled_font)) {
     862             : #if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
     863             :     case CAIRO_FONT_TYPE_DWRITE:
     864             :       // show_glyphs is implemented on the font and so is used for
     865             :       // all surface types; however, it may pixel-snap depending on
     866             :       // the dwrite rendering mode
     867             :       if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
     868             :         gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
     869             :         DWRITE_MEASURING_MODE_NATURAL) {
     870             :         return result;
     871             :       }
     872             :       MOZ_FALLTHROUGH;
     873             : #endif
     874             :     case CAIRO_FONT_TYPE_QUARTZ:
     875             :       // Quartz surfaces implement show_glyphs for Quartz fonts
     876           0 :       if (cairo_surface_get_type(cairo_get_target(cr)) ==
     877             :         CAIRO_SURFACE_TYPE_QUARTZ) {
     878           0 :         return result;
     879             :       }
     880           0 :       break;
     881             :     default:
     882           0 :       break;
     883             :     }
     884           0 :     break;
     885             :   case CAIRO_HINT_METRICS_ON:
     886          71 :     break;
     887             :   }
     888          71 :   result |= RoundingFlags::kRoundX;
     889          71 :   return result;
     890             : }
     891             : 
     892             : gfxFloat
     893           0 : gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID)
     894             : {
     895           0 :     if (!SetupCairoFont(aDrawTarget)) {
     896           0 :         return 0;
     897             :     }
     898           0 :     if (ProvidesGlyphWidths()) {
     899           0 :         return GetGlyphWidth(*aDrawTarget, aGID) / 65536.0;
     900             :     }
     901           0 :     if (mFUnitsConvFactor < 0.0f) {
     902           0 :         GetMetrics(eHorizontal);
     903             :     }
     904           0 :     NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
     905             :                  "missing font unit conversion factor");
     906           0 :     if (!mHarfBuzzShaper) {
     907           0 :         mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
     908             :     }
     909             :     gfxHarfBuzzShaper* shaper =
     910           0 :         static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
     911           0 :     if (!shaper->Initialize()) {
     912           0 :         return 0;
     913             :     }
     914           0 :     return shaper->GetGlyphHAdvance(aGID) / 65536.0;
     915             : }
     916             : 
     917             : static void
     918         301 : CollectLookupsByFeature(hb_face_t *aFace, hb_tag_t aTableTag,
     919             :                         uint32_t aFeatureIndex, hb_set_t *aLookups)
     920             : {
     921             :     uint32_t lookups[32];
     922             :     uint32_t i, len, offset;
     923             : 
     924         301 :     offset = 0;
     925         301 :     do {
     926         301 :         len = ArrayLength(lookups);
     927             :         hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex,
     928         301 :                                          offset, &len, lookups);
     929         685 :         for (i = 0; i < len; i++) {
     930         384 :             hb_set_add(aLookups, lookups[i]);
     931             :         }
     932         301 :         offset += len;
     933         301 :     } while (len == ArrayLength(lookups));
     934         301 : }
     935             : 
     936             : static void
     937          61 : CollectLookupsByLanguage(hb_face_t *aFace, hb_tag_t aTableTag,
     938             :                          const nsTHashtable<nsUint32HashKey>&
     939             :                              aSpecificFeatures,
     940             :                          hb_set_t *aOtherLookups,
     941             :                          hb_set_t *aSpecificFeatureLookups,
     942             :                          uint32_t aScriptIndex, uint32_t aLangIndex)
     943             : {
     944             :     uint32_t reqFeatureIndex;
     945          61 :     if (hb_ot_layout_language_get_required_feature_index(aFace, aTableTag,
     946             :                                                          aScriptIndex,
     947             :                                                          aLangIndex,
     948             :                                                          &reqFeatureIndex)) {
     949             :         CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex,
     950           0 :                                 aOtherLookups);
     951             :     }
     952             : 
     953             :     uint32_t featureIndexes[32];
     954             :     uint32_t i, len, offset;
     955             : 
     956          61 :     offset = 0;
     957          61 :     do {
     958          61 :         len = ArrayLength(featureIndexes);
     959             :         hb_ot_layout_language_get_feature_indexes(aFace, aTableTag,
     960             :                                                   aScriptIndex, aLangIndex,
     961          61 :                                                   offset, &len, featureIndexes);
     962             : 
     963         362 :         for (i = 0; i < len; i++) {
     964         301 :             uint32_t featureIndex = featureIndexes[i];
     965             : 
     966             :             // get the feature tag
     967             :             hb_tag_t featureTag;
     968         301 :             uint32_t tagLen = 1;
     969         301 :             hb_ot_layout_language_get_feature_tags(aFace, aTableTag,
     970             :                                                    aScriptIndex, aLangIndex,
     971             :                                                    offset + i, &tagLen,
     972         301 :                                                    &featureTag);
     973             : 
     974             :             // collect lookups
     975         301 :             hb_set_t *lookups = aSpecificFeatures.GetEntry(featureTag) ?
     976         301 :                                     aSpecificFeatureLookups : aOtherLookups;
     977         301 :             CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
     978             :         }
     979          61 :         offset += len;
     980          61 :     } while (len == ArrayLength(featureIndexes));
     981          61 : }
     982             : 
     983             : static bool
     984           8 : HasLookupRuleWithGlyphByScript(hb_face_t *aFace, hb_tag_t aTableTag,
     985             :                                hb_tag_t aScriptTag, uint32_t aScriptIndex,
     986             :                                uint16_t aGlyph,
     987             :                                const nsTHashtable<nsUint32HashKey>&
     988             :                                    aDefaultFeatures,
     989             :                                bool& aHasDefaultFeatureWithGlyph)
     990             : {
     991             :     uint32_t numLangs, lang;
     992           8 :     hb_set_t *defaultFeatureLookups = hb_set_create();
     993           8 :     hb_set_t *nonDefaultFeatureLookups = hb_set_create();
     994             : 
     995             :     // default lang
     996             :     CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
     997             :                              nonDefaultFeatureLookups, defaultFeatureLookups,
     998             :                              aScriptIndex,
     999           8 :                              HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
    1000             : 
    1001             :     // iterate over langs
    1002             :     numLangs = hb_ot_layout_script_get_language_tags(aFace, aTableTag,
    1003             :                                                      aScriptIndex, 0,
    1004           8 :                                                      nullptr, nullptr);
    1005          36 :     for (lang = 0; lang < numLangs; lang++) {
    1006             :         CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
    1007             :                                  nonDefaultFeatureLookups,
    1008             :                                  defaultFeatureLookups,
    1009          28 :                                  aScriptIndex, lang);
    1010             :     }
    1011             : 
    1012             :     // look for the glyph among default feature lookups
    1013           8 :     aHasDefaultFeatureWithGlyph = false;
    1014           8 :     hb_set_t *glyphs = hb_set_create();
    1015           8 :     hb_codepoint_t index = -1;
    1016          54 :     while (hb_set_next(defaultFeatureLookups, &index)) {
    1017             :         hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
    1018             :                                            glyphs, glyphs, glyphs,
    1019          23 :                                            nullptr);
    1020          23 :         if (hb_set_has(glyphs, aGlyph)) {
    1021           0 :             aHasDefaultFeatureWithGlyph = true;
    1022           0 :             break;
    1023             :         }
    1024             :     }
    1025             : 
    1026             :     // look for the glyph among non-default feature lookups
    1027             :     // if no default feature lookups contained spaces
    1028           8 :     bool hasNonDefaultFeatureWithGlyph = false;
    1029           8 :     if (!aHasDefaultFeatureWithGlyph) {
    1030           8 :         hb_set_clear(glyphs);
    1031           8 :         index = -1;
    1032         130 :         while (hb_set_next(nonDefaultFeatureLookups, &index)) {
    1033             :             hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
    1034             :                                                glyphs, glyphs, glyphs,
    1035          61 :                                                nullptr);
    1036          61 :             if (hb_set_has(glyphs, aGlyph)) {
    1037           0 :                 hasNonDefaultFeatureWithGlyph = true;
    1038           0 :                 break;
    1039             :             }
    1040             :         }
    1041             :     }
    1042             : 
    1043           8 :     hb_set_destroy(glyphs);
    1044           8 :     hb_set_destroy(defaultFeatureLookups);
    1045           8 :     hb_set_destroy(nonDefaultFeatureLookups);
    1046             : 
    1047           8 :     return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
    1048             : }
    1049             : 
    1050             : static void
    1051           2 : HasLookupRuleWithGlyph(hb_face_t *aFace, hb_tag_t aTableTag, bool& aHasGlyph,
    1052             :                        hb_tag_t aSpecificFeature, bool& aHasGlyphSpecific,
    1053             :                        uint16_t aGlyph)
    1054             : {
    1055             :     // iterate over the scripts in the font
    1056             :     uint32_t numScripts, numLangs, script, lang;
    1057           2 :     hb_set_t *otherLookups = hb_set_create();
    1058           2 :     hb_set_t *specificFeatureLookups = hb_set_create();
    1059           4 :     nsTHashtable<nsUint32HashKey> specificFeature;
    1060             : 
    1061           2 :     specificFeature.PutEntry(aSpecificFeature);
    1062             : 
    1063             :     numScripts = hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0,
    1064           2 :                                                     nullptr, nullptr);
    1065             : 
    1066          10 :     for (script = 0; script < numScripts; script++) {
    1067             :         // default lang
    1068             :         CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
    1069             :                                  otherLookups, specificFeatureLookups,
    1070           8 :                                  script, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
    1071             : 
    1072             :         // iterate over langs
    1073             :         numLangs = hb_ot_layout_script_get_language_tags(aFace, HB_OT_TAG_GPOS,
    1074             :                                                          script, 0,
    1075           8 :                                                          nullptr, nullptr);
    1076          25 :         for (lang = 0; lang < numLangs; lang++) {
    1077             :             CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
    1078             :                                      otherLookups, specificFeatureLookups,
    1079          17 :                                      script, lang);
    1080             :         }
    1081             :     }
    1082             : 
    1083             :     // look for the glyph among non-specific feature lookups
    1084           2 :     hb_set_t *glyphs = hb_set_create();
    1085           2 :     hb_codepoint_t index = -1;
    1086           6 :     while (hb_set_next(otherLookups, &index)) {
    1087             :         hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
    1088             :                                            glyphs, glyphs, glyphs,
    1089           2 :                                            nullptr);
    1090           2 :         if (hb_set_has(glyphs, aGlyph)) {
    1091           0 :             aHasGlyph = true;
    1092           0 :             break;
    1093             :         }
    1094             :     }
    1095             : 
    1096             :     // look for the glyph among specific feature lookups
    1097           2 :     hb_set_clear(glyphs);
    1098           2 :     index = -1;
    1099          22 :     while (hb_set_next(specificFeatureLookups, &index)) {
    1100             :         hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
    1101             :                                            glyphs, glyphs, glyphs,
    1102          10 :                                            nullptr);
    1103          10 :         if (hb_set_has(glyphs, aGlyph)) {
    1104           0 :             aHasGlyphSpecific = true;
    1105           0 :             break;
    1106             :         }
    1107             :     }
    1108             : 
    1109           2 :     hb_set_destroy(glyphs);
    1110           2 :     hb_set_destroy(specificFeatureLookups);
    1111           2 :     hb_set_destroy(otherLookups);
    1112           2 : }
    1113             : 
    1114             : nsDataHashtable<nsUint32HashKey,Script> *gfxFont::sScriptTagToCode = nullptr;
    1115             : nsTHashtable<nsUint32HashKey>           *gfxFont::sDefaultFeatures = nullptr;
    1116             : 
    1117             : static inline bool
    1118           2 : HasSubstitution(uint32_t *aBitVector, Script aScript) {
    1119           2 :     return (aBitVector[static_cast<uint32_t>(aScript) >> 5]
    1120           2 :            & (1 << (static_cast<uint32_t>(aScript) & 0x1f))) != 0;
    1121             : }
    1122             : 
    1123             : // union of all default substitution features across scripts
    1124             : static const hb_tag_t defaultFeatures[] = {
    1125             :     HB_TAG('a','b','v','f'),
    1126             :     HB_TAG('a','b','v','s'),
    1127             :     HB_TAG('a','k','h','n'),
    1128             :     HB_TAG('b','l','w','f'),
    1129             :     HB_TAG('b','l','w','s'),
    1130             :     HB_TAG('c','a','l','t'),
    1131             :     HB_TAG('c','c','m','p'),
    1132             :     HB_TAG('c','f','a','r'),
    1133             :     HB_TAG('c','j','c','t'),
    1134             :     HB_TAG('c','l','i','g'),
    1135             :     HB_TAG('f','i','n','2'),
    1136             :     HB_TAG('f','i','n','3'),
    1137             :     HB_TAG('f','i','n','a'),
    1138             :     HB_TAG('h','a','l','f'),
    1139             :     HB_TAG('h','a','l','n'),
    1140             :     HB_TAG('i','n','i','t'),
    1141             :     HB_TAG('i','s','o','l'),
    1142             :     HB_TAG('l','i','g','a'),
    1143             :     HB_TAG('l','j','m','o'),
    1144             :     HB_TAG('l','o','c','l'),
    1145             :     HB_TAG('l','t','r','a'),
    1146             :     HB_TAG('l','t','r','m'),
    1147             :     HB_TAG('m','e','d','2'),
    1148             :     HB_TAG('m','e','d','i'),
    1149             :     HB_TAG('m','s','e','t'),
    1150             :     HB_TAG('n','u','k','t'),
    1151             :     HB_TAG('p','r','e','f'),
    1152             :     HB_TAG('p','r','e','s'),
    1153             :     HB_TAG('p','s','t','f'),
    1154             :     HB_TAG('p','s','t','s'),
    1155             :     HB_TAG('r','c','l','t'),
    1156             :     HB_TAG('r','l','i','g'),
    1157             :     HB_TAG('r','k','r','f'),
    1158             :     HB_TAG('r','p','h','f'),
    1159             :     HB_TAG('r','t','l','a'),
    1160             :     HB_TAG('r','t','l','m'),
    1161             :     HB_TAG('t','j','m','o'),
    1162             :     HB_TAG('v','a','t','u'),
    1163             :     HB_TAG('v','e','r','t'),
    1164             :     HB_TAG('v','j','m','o')
    1165             : };
    1166             : 
    1167             : void
    1168           2 : gfxFont::CheckForFeaturesInvolvingSpace()
    1169             : {
    1170           2 :     mFontEntry->mHasSpaceFeaturesInitialized = true;
    1171             : 
    1172           2 :     bool log = LOG_FONTINIT_ENABLED();
    1173           2 :     TimeStamp start;
    1174           2 :     if (MOZ_UNLIKELY(log)) {
    1175           0 :         start = TimeStamp::Now();
    1176             :     }
    1177             : 
    1178           2 :     bool result = false;
    1179             : 
    1180           2 :     uint32_t spaceGlyph = GetSpaceGlyph();
    1181           2 :     if (!spaceGlyph) {
    1182           0 :         return;
    1183             :     }
    1184             : 
    1185           2 :     hb_face_t *face = GetFontEntry()->GetHBFace();
    1186             : 
    1187             :     // GSUB lookups - examine per script
    1188           2 :     if (hb_ot_layout_has_substitution(face)) {
    1189             : 
    1190             :         // set up the script ==> code hashtable if needed
    1191           2 :         if (!sScriptTagToCode) {
    1192           2 :             sScriptTagToCode =
    1193             :                 new nsDataHashtable<nsUint32HashKey,
    1194           2 :                                     Script>(size_t(Script::NUM_SCRIPT_CODES));
    1195           2 :             sScriptTagToCode->Put(HB_TAG('D','F','L','T'), Script::COMMON);
    1196         348 :             for (Script s = Script::ARABIC; s < Script::NUM_SCRIPT_CODES;
    1197         346 :                  s = Script(static_cast<int>(s) + 1)) {
    1198         346 :                 hb_script_t scriptTag = hb_script_t(GetScriptTagForCode(s));
    1199             :                 hb_tag_t s1, s2;
    1200         346 :                 hb_ot_tags_from_script(scriptTag, &s1, &s2);
    1201         346 :                 sScriptTagToCode->Put(s1, s);
    1202         346 :                 if (s2 != HB_OT_TAG_DEFAULT_SCRIPT) {
    1203          20 :                     sScriptTagToCode->Put(s2, s);
    1204             :                 }
    1205             :             }
    1206             : 
    1207           2 :             uint32_t numDefaultFeatures = ArrayLength(defaultFeatures);
    1208           2 :             sDefaultFeatures =
    1209           2 :                 new nsTHashtable<nsUint32HashKey>(numDefaultFeatures);
    1210          82 :             for (uint32_t i = 0; i < numDefaultFeatures; i++) {
    1211          80 :                 sDefaultFeatures->PutEntry(defaultFeatures[i]);
    1212             :             }
    1213             :         }
    1214             : 
    1215             :         // iterate over the scripts in the font
    1216             :         hb_tag_t scriptTags[8];
    1217             : 
    1218           2 :         uint32_t len, offset = 0;
    1219           2 :         do {
    1220           2 :             len = ArrayLength(scriptTags);
    1221             :             hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset,
    1222           2 :                                                &len, scriptTags);
    1223          10 :             for (uint32_t i = 0; i < len; i++) {
    1224           8 :                 bool isDefaultFeature = false;
    1225             :                 Script s;
    1226          16 :                 if (!HasLookupRuleWithGlyphByScript(face, HB_OT_TAG_GSUB,
    1227             :                                                     scriptTags[i], offset + i,
    1228             :                                                     spaceGlyph,
    1229             :                                                     *sDefaultFeatures,
    1230           8 :                                                     isDefaultFeature) ||
    1231           0 :                     !sScriptTagToCode->Get(scriptTags[i], &s))
    1232             :                 {
    1233           8 :                     continue;
    1234             :                 }
    1235           0 :                 result = true;
    1236           0 :                 uint32_t index = static_cast<uint32_t>(s) >> 5;
    1237           0 :                 uint32_t bit = static_cast<uint32_t>(s) & 0x1f;
    1238           0 :                 if (isDefaultFeature) {
    1239           0 :                     mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
    1240             :                 } else {
    1241           0 :                     mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
    1242             :                 }
    1243             :             }
    1244           2 :             offset += len;
    1245           2 :         } while (len == ArrayLength(scriptTags));
    1246             :     }
    1247             : 
    1248             :     // spaces in default features of default script?
    1249             :     // ==> can't use word cache, skip GPOS analysis
    1250           2 :     bool canUseWordCache = true;
    1251           2 :     if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
    1252             :                         Script::COMMON)) {
    1253           0 :         canUseWordCache = false;
    1254             :     }
    1255             : 
    1256             :     // GPOS lookups - distinguish kerning from non-kerning features
    1257           2 :     mFontEntry->mHasSpaceFeaturesKerning = false;
    1258           2 :     mFontEntry->mHasSpaceFeaturesNonKerning = false;
    1259             : 
    1260           2 :     if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
    1261           2 :         bool hasKerning = false, hasNonKerning = false;
    1262           2 :         HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
    1263           2 :                                HB_TAG('k','e','r','n'), hasKerning, spaceGlyph);
    1264           2 :         if (hasKerning || hasNonKerning) {
    1265           0 :             result = true;
    1266             :         }
    1267           2 :         mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
    1268           2 :         mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
    1269             :     }
    1270             : 
    1271           2 :     hb_face_destroy(face);
    1272           2 :     mFontEntry->mHasSpaceFeatures = result;
    1273             : 
    1274           2 :     if (MOZ_UNLIKELY(log)) {
    1275           0 :         TimeDuration elapsed = TimeStamp::Now() - start;
    1276           0 :         LOG_FONTINIT((
    1277             :             "(fontinit-spacelookups) font: %s - "
    1278             :             "subst default: %8.8x %8.8x %8.8x %8.8x "
    1279             :             "subst non-default: %8.8x %8.8x %8.8x %8.8x "
    1280             :             "kerning: %s non-kerning: %s time: %6.3f\n",
    1281             :             NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
    1282             :             mFontEntry->mDefaultSubSpaceFeatures[3],
    1283             :             mFontEntry->mDefaultSubSpaceFeatures[2],
    1284             :             mFontEntry->mDefaultSubSpaceFeatures[1],
    1285             :             mFontEntry->mDefaultSubSpaceFeatures[0],
    1286             :             mFontEntry->mNonDefaultSubSpaceFeatures[3],
    1287             :             mFontEntry->mNonDefaultSubSpaceFeatures[2],
    1288             :             mFontEntry->mNonDefaultSubSpaceFeatures[1],
    1289             :             mFontEntry->mNonDefaultSubSpaceFeatures[0],
    1290             :             (mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
    1291             :             (mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"),
    1292             :             elapsed.ToMilliseconds()
    1293             :         ));
    1294             :     }
    1295             : }
    1296             : 
    1297             : bool
    1298           0 : gfxFont::HasSubstitutionRulesWithSpaceLookups(Script aRunScript)
    1299             : {
    1300           0 :     NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
    1301             :                  "need to initialize space lookup flags");
    1302           0 :     NS_ASSERTION(aRunScript < Script::NUM_SCRIPT_CODES, "weird script code");
    1303           0 :     if (aRunScript == Script::INVALID ||
    1304             :         aRunScript >= Script::NUM_SCRIPT_CODES) {
    1305           0 :         return false;
    1306             :     }
    1307             : 
    1308             :     // default features have space lookups ==> true
    1309           0 :     if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
    1310           0 :                         Script::COMMON) ||
    1311           0 :         HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
    1312             :                         aRunScript))
    1313             :     {
    1314           0 :         return true;
    1315             :     }
    1316             : 
    1317             :     // non-default features have space lookups and some type of
    1318             :     // font feature, in font or style is specified ==> true
    1319           0 :     if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
    1320           0 :                          Script::COMMON) ||
    1321           0 :          HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
    1322           0 :                          aRunScript)) &&
    1323           0 :         (!mStyle.featureSettings.IsEmpty() ||
    1324           0 :          !mFontEntry->mFeatureSettings.IsEmpty()))
    1325             :     {
    1326           0 :         return true;
    1327             :     }
    1328             : 
    1329           0 :     return false;
    1330             : }
    1331             : 
    1332             : bool
    1333          71 : gfxFont::SpaceMayParticipateInShaping(Script aRunScript)
    1334             : {
    1335             :     // avoid checking fonts known not to include default space-dependent features
    1336          71 :     if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
    1337           0 :         if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
    1338           0 :             mFontEntry->mFeatureSettings.IsEmpty()) {
    1339           0 :             return false;
    1340             :         }
    1341             :     }
    1342             : 
    1343          71 :     if (FontCanSupportGraphite()) {
    1344           0 :         if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
    1345           0 :             return mFontEntry->HasGraphiteSpaceContextuals();
    1346             :         }
    1347             :     }
    1348             : 
    1349             :     // We record the presence of space-dependent features in the font entry
    1350             :     // so that subsequent instantiations for the same font face won't
    1351             :     // require us to re-check the tables; however, the actual check is done
    1352             :     // by gfxFont because not all font entry subclasses know how to create
    1353             :     // a harfbuzz face for introspection.
    1354          71 :     if (!mFontEntry->mHasSpaceFeaturesInitialized) {
    1355           2 :         CheckForFeaturesInvolvingSpace();
    1356             :     }
    1357             : 
    1358          71 :     if (!mFontEntry->mHasSpaceFeatures) {
    1359          71 :         return false;
    1360             :     }
    1361             : 
    1362             :     // if font has substitution rules or non-kerning positioning rules
    1363             :     // that involve spaces, bypass
    1364           0 :     if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
    1365           0 :         mFontEntry->mHasSpaceFeaturesNonKerning) {
    1366           0 :         return true;
    1367             :     }
    1368             : 
    1369             :     // if kerning explicitly enabled/disabled via font-feature-settings or
    1370             :     // font-kerning and kerning rules use spaces, only bypass when enabled
    1371           0 :     if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
    1372           0 :         return mKerningEnabled;
    1373             :     }
    1374             : 
    1375           0 :     return false;
    1376             : }
    1377             : 
    1378             : bool
    1379           0 : gfxFont::SupportsFeature(Script aScript, uint32_t aFeatureTag)
    1380             : {
    1381           0 :     if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
    1382           0 :         return GetFontEntry()->SupportsGraphiteFeature(aFeatureTag);
    1383             :     }
    1384           0 :     return GetFontEntry()->SupportsOpenTypeFeature(aScript, aFeatureTag);
    1385             : }
    1386             : 
    1387             : bool
    1388           0 : gfxFont::SupportsVariantCaps(Script aScript,
    1389             :                              uint32_t aVariantCaps,
    1390             :                              bool& aFallbackToSmallCaps,
    1391             :                              bool& aSyntheticLowerToSmallCaps,
    1392             :                              bool& aSyntheticUpperToSmallCaps)
    1393             : {
    1394           0 :     bool ok = true;  // cases without fallback are fine
    1395           0 :     aFallbackToSmallCaps = false;
    1396           0 :     aSyntheticLowerToSmallCaps = false;
    1397           0 :     aSyntheticUpperToSmallCaps = false;
    1398           0 :     switch (aVariantCaps) {
    1399             :         case NS_FONT_VARIANT_CAPS_SMALLCAPS:
    1400           0 :             ok = SupportsFeature(aScript, HB_TAG('s','m','c','p'));
    1401           0 :             if (!ok) {
    1402           0 :                 aSyntheticLowerToSmallCaps = true;
    1403             :             }
    1404           0 :             break;
    1405             :         case NS_FONT_VARIANT_CAPS_ALLSMALL:
    1406           0 :             ok = SupportsFeature(aScript, HB_TAG('s','m','c','p')) &&
    1407           0 :                  SupportsFeature(aScript, HB_TAG('c','2','s','c'));
    1408           0 :             if (!ok) {
    1409           0 :                 aSyntheticLowerToSmallCaps = true;
    1410           0 :                 aSyntheticUpperToSmallCaps = true;
    1411             :             }
    1412           0 :             break;
    1413             :         case NS_FONT_VARIANT_CAPS_PETITECAPS:
    1414           0 :             ok = SupportsFeature(aScript, HB_TAG('p','c','a','p'));
    1415           0 :             if (!ok) {
    1416           0 :                 ok = SupportsFeature(aScript, HB_TAG('s','m','c','p'));
    1417           0 :                 aFallbackToSmallCaps = ok;
    1418             :             }
    1419           0 :             if (!ok) {
    1420           0 :                 aSyntheticLowerToSmallCaps = true;
    1421             :             }
    1422           0 :             break;
    1423             :         case NS_FONT_VARIANT_CAPS_ALLPETITE:
    1424           0 :             ok = SupportsFeature(aScript, HB_TAG('p','c','a','p')) &&
    1425           0 :                  SupportsFeature(aScript, HB_TAG('c','2','p','c'));
    1426           0 :             if (!ok) {
    1427           0 :                 ok = SupportsFeature(aScript, HB_TAG('s','m','c','p')) &&
    1428           0 :                      SupportsFeature(aScript, HB_TAG('c','2','s','c'));
    1429           0 :                 aFallbackToSmallCaps = ok;
    1430             :             }
    1431           0 :             if (!ok) {
    1432           0 :                 aSyntheticLowerToSmallCaps = true;
    1433           0 :                 aSyntheticUpperToSmallCaps = true;
    1434             :             }
    1435           0 :             break;
    1436             :         default:
    1437           0 :             break;
    1438             :     }
    1439             : 
    1440           0 :     NS_ASSERTION(!(ok && (aSyntheticLowerToSmallCaps ||
    1441             :                           aSyntheticUpperToSmallCaps)),
    1442             :                  "shouldn't use synthetic features if we found real ones");
    1443             : 
    1444           0 :     NS_ASSERTION(!(!ok && aFallbackToSmallCaps),
    1445             :                  "if we found a usable fallback, that counts as ok");
    1446             : 
    1447           0 :     return ok;
    1448             : }
    1449             : 
    1450             : bool
    1451           0 : gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
    1452             :                                 const uint8_t *aString,
    1453             :                                 uint32_t aLength, Script aRunScript)
    1454             : {
    1455             :     NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString),
    1456           0 :                                          aLength);
    1457           0 :     return SupportsSubSuperscript(aSubSuperscript, unicodeString.get(),
    1458           0 :                                   aLength, aRunScript);
    1459             : }
    1460             : 
    1461             : bool
    1462           0 : gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
    1463             :                                 const char16_t *aString,
    1464             :                                 uint32_t aLength, Script aRunScript)
    1465             : {
    1466           0 :     NS_ASSERTION(aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ||
    1467             :                  aSubSuperscript == NS_FONT_VARIANT_POSITION_SUB,
    1468             :                  "unknown value of font-variant-position");
    1469             : 
    1470           0 :     uint32_t feature = aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ?
    1471           0 :                        HB_TAG('s','u','p','s') : HB_TAG('s','u','b','s');
    1472             : 
    1473           0 :     if (!SupportsFeature(aRunScript, feature)) {
    1474           0 :         return false;
    1475             :     }
    1476             : 
    1477             :     // xxx - for graphite, don't really know how to sniff lookups so bail
    1478           0 :     if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
    1479           0 :         return true;
    1480             :     }
    1481             : 
    1482           0 :     if (!mHarfBuzzShaper) {
    1483           0 :         mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
    1484             :     }
    1485             :     gfxHarfBuzzShaper* shaper =
    1486           0 :         static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
    1487           0 :     if (!shaper->Initialize()) {
    1488           0 :         return false;
    1489             :     }
    1490             : 
    1491             :     // get the hbset containing input glyphs for the feature
    1492           0 :     const hb_set_t *inputGlyphs = mFontEntry->InputsForOpenTypeFeature(aRunScript, feature);
    1493             : 
    1494             :     // create an hbset containing default glyphs for the script run
    1495           0 :     hb_set_t *defaultGlyphsInRun = hb_set_create();
    1496             : 
    1497             :     // for each character, get the glyph id
    1498           0 :     for (uint32_t i = 0; i < aLength; i++) {
    1499           0 :         uint32_t ch = aString[i];
    1500             : 
    1501           0 :         if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
    1502           0 :                              NS_IS_LOW_SURROGATE(aString[i + 1])) {
    1503           0 :             i++;
    1504           0 :             ch = SURROGATE_TO_UCS4(ch, aString[i]);
    1505             :         }
    1506             : 
    1507           0 :         if (ch == 0xa0) {
    1508           0 :             ch = ' ';
    1509             :         }
    1510             : 
    1511           0 :         hb_codepoint_t gid = shaper->GetNominalGlyph(ch);
    1512           0 :         hb_set_add(defaultGlyphsInRun, gid);
    1513             :     }
    1514             : 
    1515             :     // intersect with input glyphs, if size is not the same ==> fallback
    1516           0 :     uint32_t origSize = hb_set_get_population(defaultGlyphsInRun);
    1517           0 :     hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
    1518           0 :     uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
    1519           0 :     hb_set_destroy(defaultGlyphsInRun);
    1520             : 
    1521           0 :     return origSize == intersectionSize;
    1522             : }
    1523             : 
    1524             : bool
    1525           8 : gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
    1526             : {
    1527           8 :     aFeatureOn = false;
    1528             : 
    1529          16 :     if (mStyle.featureSettings.IsEmpty() &&
    1530           8 :         GetFontEntry()->mFeatureSettings.IsEmpty()) {
    1531           8 :         return false;
    1532             :     }
    1533             : 
    1534             :     // add feature values from font
    1535           0 :     bool featureSet = false;
    1536             :     uint32_t i, count;
    1537             : 
    1538           0 :     nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
    1539           0 :     count = fontFeatures.Length();
    1540           0 :     for (i = 0; i < count; i++) {
    1541           0 :         const gfxFontFeature& feature = fontFeatures.ElementAt(i);
    1542           0 :         if (feature.mTag == aFeature) {
    1543           0 :             featureSet = true;
    1544           0 :             aFeatureOn = (feature.mValue != 0);
    1545             :         }
    1546             :     }
    1547             : 
    1548             :     // add feature values from style rules
    1549           0 :     nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
    1550           0 :     count = styleFeatures.Length();
    1551           0 :     for (i = 0; i < count; i++) {
    1552           0 :         const gfxFontFeature& feature = styleFeatures.ElementAt(i);
    1553           0 :         if (feature.mTag == aFeature) {
    1554           0 :             featureSet = true;
    1555           0 :             aFeatureOn = (feature.mValue != 0);
    1556             :         }
    1557             :     }
    1558             : 
    1559           0 :     return featureSet;
    1560             : }
    1561             : 
    1562             : /**
    1563             :  * A helper function in case we need to do any rounding or other
    1564             :  * processing here.
    1565             :  */
    1566             : #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
    1567             :     (double(aAppUnits)*double(aDevUnitsPerAppUnit))
    1568             : 
    1569          21 : static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
    1570          21 :   switch (aAAOption) {
    1571             :   case gfxFont::kAntialiasSubpixel:
    1572           0 :     return AntialiasMode::SUBPIXEL;
    1573             :   case gfxFont::kAntialiasGrayscale:
    1574           0 :     return AntialiasMode::GRAY;
    1575             :   case gfxFont::kAntialiasNone:
    1576           0 :     return AntialiasMode::NONE;
    1577             :   default:
    1578          21 :     return AntialiasMode::DEFAULT;
    1579             :   }
    1580             : }
    1581             : 
    1582             : class GlyphBufferAzure
    1583             : {
    1584             :     typedef mozilla::image::imgDrawingParams imgDrawingParams;
    1585             : 
    1586             : public:
    1587          21 :     GlyphBufferAzure(const TextRunDrawParams& aRunParams,
    1588             :                      const FontDrawParams&    aFontParams)
    1589          21 :         : mRunParams(aRunParams)
    1590             :         , mFontParams(aFontParams)
    1591          21 :         , mNumGlyphs(0)
    1592             :     {
    1593          21 :     }
    1594             : 
    1595          21 :     ~GlyphBufferAzure()
    1596          21 :     {
    1597          21 :         Flush(true); // flush any remaining buffered glyphs
    1598          21 :     }
    1599             : 
    1600         448 :     void OutputGlyph(uint32_t aGlyphID, const gfxPoint& aPt)
    1601             :     {
    1602         448 :         Glyph *glyph = AppendGlyph();
    1603         448 :         glyph->mIndex = aGlyphID;
    1604         448 :         glyph->mPosition.x = aPt.x;
    1605         448 :         glyph->mPosition.y = aPt.y;
    1606         448 :         glyph->mPosition = mFontParams.matInv.TransformPoint(glyph->mPosition);
    1607         448 :         Flush(false); // this will flush only if the buffer is full
    1608         448 :     }
    1609             : 
    1610             :     const TextRunDrawParams& mRunParams;
    1611             :     const FontDrawParams& mFontParams;
    1612             : 
    1613             : private:
    1614             : #define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph))
    1615             : 
    1616         448 :     Glyph *AppendGlyph()
    1617             :     {
    1618         448 :         return &mGlyphBuffer[mNumGlyphs++];
    1619             :     }
    1620             : 
    1621             :     static DrawMode
    1622          21 :     GetStrokeMode(DrawMode aMode)
    1623             :     {
    1624          42 :         return aMode & (DrawMode::GLYPH_STROKE |
    1625          63 :                         DrawMode::GLYPH_STROKE_UNDERNEATH);
    1626             :     }
    1627             : 
    1628             :     // Render the buffered glyphs to the draw target and clear the buffer.
    1629             :     // This actually flushes the glyphs only if the buffer is full, or if the
    1630             :     // aFinish parameter is true; otherwise it simply returns.
    1631         469 :     void Flush(bool aFinish)
    1632             :     {
    1633             :         // Ensure there's enough room for a glyph to be added to the buffer
    1634         469 :         if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
    1635         448 :             return;
    1636             :         }
    1637             : 
    1638          21 :         if (mRunParams.isRTL) {
    1639           0 :             Glyph *begin = &mGlyphBuffer[0];
    1640           0 :             Glyph *end = &mGlyphBuffer[mNumGlyphs];
    1641           0 :             std::reverse(begin, end);
    1642             :         }
    1643             : 
    1644             :         gfx::GlyphBuffer buf;
    1645          21 :         buf.mGlyphs = mGlyphBuffer;
    1646          21 :         buf.mNumGlyphs = mNumGlyphs;
    1647             : 
    1648          21 :         const gfxContext::AzureState &state = mRunParams.context->CurrentState();
    1649          21 :         if (mRunParams.drawMode & DrawMode::GLYPH_FILL) {
    1650          21 :             if (state.pattern || mFontParams.contextPaint) {
    1651             :                 Pattern *pat;
    1652             : 
    1653           0 :                 RefPtr<gfxPattern> fillPattern;
    1654           0 :                 if (mFontParams.contextPaint) {
    1655           0 :                   imgDrawingParams imgParams;
    1656             :                   fillPattern =
    1657           0 :                     mFontParams.contextPaint->GetFillPattern(
    1658           0 :                                           mRunParams.context->GetDrawTarget(),
    1659           0 :                                           mRunParams.context->CurrentMatrix(),
    1660           0 :                                           imgParams);
    1661             :                 }
    1662           0 :                 if (!fillPattern) {
    1663           0 :                     if (state.pattern) {
    1664             :                         RefPtr<gfxPattern> statePattern =
    1665           0 :                           mRunParams.context->CurrentState().pattern;
    1666           0 :                         pat = statePattern->GetPattern(mRunParams.dt,
    1667           0 :                                       state.patternTransformChanged ?
    1668           0 :                                           &state.patternTransform : nullptr);
    1669             :                     } else {
    1670           0 :                         pat = nullptr;
    1671             :                     }
    1672             :                 } else {
    1673           0 :                     pat = fillPattern->GetPattern(mRunParams.dt);
    1674             :                 }
    1675             : 
    1676           0 :                 if (pat) {
    1677           0 :                     Matrix saved;
    1678           0 :                     Matrix *mat = nullptr;
    1679           0 :                     if (mFontParams.passedInvMatrix) {
    1680             :                         // The brush matrix needs to be multiplied with the
    1681             :                         // inverted matrix as well, to move the brush into the
    1682             :                         // space of the glyphs.
    1683             : 
    1684             :                         // This relies on the returned Pattern not to be reused
    1685             :                         // by others, but regenerated on GetPattern calls. This
    1686             :                         // is true!
    1687           0 :                         if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
    1688           0 :                             mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
    1689           0 :                         } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
    1690           0 :                             mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
    1691           0 :                         } else if (pat->GetType() == PatternType::SURFACE) {
    1692           0 :                             mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
    1693             :                         }
    1694             : 
    1695           0 :                         if (mat) {
    1696           0 :                             saved = *mat;
    1697           0 :                             *mat = (*mat) * (*mFontParams.passedInvMatrix);
    1698             :                         }
    1699             :                     }
    1700             : 
    1701           0 :                     mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
    1702           0 :                                               *pat, mFontParams.drawOptions,
    1703           0 :                                               mFontParams.renderingOptions);
    1704             : 
    1705           0 :                     if (mat) {
    1706           0 :                         *mat = saved;
    1707             :                     }
    1708             :                 }
    1709          21 :             } else if (state.sourceSurface) {
    1710           0 :                 mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
    1711           0 :                                           SurfacePattern(state.sourceSurface,
    1712             :                                                          ExtendMode::CLAMP,
    1713             :                                                          state.surfTransform),
    1714           0 :                                           mFontParams.drawOptions,
    1715           0 :                                           mFontParams.renderingOptions);
    1716             :             } else {
    1717          63 :                 mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
    1718          42 :                                           ColorPattern(state.color),
    1719          21 :                                           mFontParams.drawOptions,
    1720          42 :                                           mFontParams.renderingOptions);
    1721             :             }
    1722             :         }
    1723          21 :         if (GetStrokeMode(mRunParams.drawMode) == DrawMode::GLYPH_STROKE &&
    1724           0 :             mRunParams.strokeOpts) {
    1725             :             Pattern *pat;
    1726           0 :             if (mRunParams.textStrokePattern) {
    1727           0 :                 pat = mRunParams.textStrokePattern->GetPattern(
    1728           0 :                   mRunParams.dt, state.patternTransformChanged
    1729             :                                    ? &state.patternTransform
    1730           0 :                                    : nullptr);
    1731             : 
    1732           0 :                 if (pat) {
    1733           0 :                     Matrix saved;
    1734           0 :                     Matrix *mat = nullptr;
    1735           0 :                     if (mFontParams.passedInvMatrix) {
    1736             :                         // The brush matrix needs to be multiplied with the
    1737             :                         // inverted matrix as well, to move the brush into the
    1738             :                         // space of the glyphs.
    1739             : 
    1740             :                         // This relies on the returned Pattern not to be reused
    1741             :                         // by others, but regenerated on GetPattern calls. This
    1742             :                         // is true!
    1743           0 :                         if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
    1744           0 :                             mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
    1745           0 :                         } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
    1746           0 :                             mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
    1747           0 :                         } else if (pat->GetType() == PatternType::SURFACE) {
    1748           0 :                             mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
    1749             :                         }
    1750             : 
    1751           0 :                         if (mat) {
    1752           0 :                             saved = *mat;
    1753           0 :                             *mat = (*mat) * (*mFontParams.passedInvMatrix);
    1754             :                         }
    1755             :                     }
    1756           0 :                     FlushStroke(buf, *pat);
    1757             : 
    1758           0 :                     if (mat) {
    1759           0 :                         *mat = saved;
    1760             :                     }
    1761             :                 }
    1762             :             } else {
    1763             :                 FlushStroke(buf,
    1764           0 :                             ColorPattern(
    1765           0 :                               Color::FromABGR(mRunParams.textStrokeColor)));
    1766             :             }
    1767             :         }
    1768          21 :         if (mRunParams.drawMode & DrawMode::GLYPH_PATH) {
    1769           0 :             mRunParams.context->EnsurePathBuilder();
    1770           0 :             Matrix mat = mRunParams.dt->GetTransform();
    1771           0 :             mFontParams.scaledFont->CopyGlyphsToBuilder(
    1772           0 :                 buf, mRunParams.context->mPathBuilder, &mat);
    1773             :         }
    1774             : 
    1775          21 :         mNumGlyphs = 0;
    1776             :     }
    1777             : 
    1778           0 :     void FlushStroke(gfx::GlyphBuffer& aBuf, const Pattern& aPattern)
    1779             :     {
    1780           0 :         mRunParams.dt->StrokeGlyphs(mFontParams.scaledFont, aBuf,
    1781             :                                     aPattern,
    1782           0 :                                     *mRunParams.strokeOpts,
    1783           0 :                                     mFontParams.drawOptions,
    1784           0 :                                     mFontParams.renderingOptions);
    1785           0 :     }
    1786             : 
    1787             :     Glyph        mGlyphBuffer[GLYPH_BUFFER_SIZE];
    1788             :     unsigned int mNumGlyphs;
    1789             : 
    1790             : #undef GLYPH_BUFFER_SIZE
    1791             : };
    1792             : 
    1793             : // Bug 674909. When synthetic bolding text by drawing twice, need to
    1794             : // render using a pixel offset in device pixels, otherwise text
    1795             : // doesn't appear bolded, it appears as if a bad text shadow exists
    1796             : // when a non-identity transform exists.  Use an offset factor so that
    1797             : // the second draw occurs at a constant offset in device pixels.
    1798             : 
    1799             : double
    1800           0 : gfxFont::CalcXScale(DrawTarget* aDrawTarget)
    1801             : {
    1802             :     // determine magnitude of a 1px x offset in device space
    1803           0 :     Size t = aDrawTarget->GetTransform().TransformSize(Size(1.0, 0.0));
    1804           0 :     if (t.width == 1.0 && t.height == 0.0) {
    1805             :         // short-circuit the most common case to avoid sqrt() and division
    1806           0 :         return 1.0;
    1807             :     }
    1808             : 
    1809           0 :     double m = sqrt(t.width * t.width + t.height * t.height);
    1810             : 
    1811           0 :     NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
    1812           0 :     if (m == 0.0) {
    1813           0 :         return 0.0; // effectively disables offset
    1814             :     }
    1815             : 
    1816             :     // scale factor so that offsets are 1px in device pixels
    1817           0 :     return 1.0 / m;
    1818             : }
    1819             : 
    1820             : // Draw an individual glyph at a specific location.
    1821             : // *aPt is the glyph position in appUnits; it is converted to device
    1822             : // coordinates (devPt) here.
    1823             : void
    1824         448 : gfxFont::DrawOneGlyph(uint32_t aGlyphID, double aAdvance, gfxPoint *aPt,
    1825             :                       GlyphBufferAzure& aBuffer, bool *aEmittedGlyphs) const
    1826             : {
    1827         448 :     const TextRunDrawParams& runParams(aBuffer.mRunParams);
    1828         448 :     const FontDrawParams& fontParams(aBuffer.mFontParams);
    1829             : 
    1830             :     double glyphX, glyphY;
    1831         448 :     if (fontParams.isVerticalFont) {
    1832           0 :         glyphX = aPt->x;
    1833           0 :         if (runParams.isRTL) {
    1834           0 :             aPt->y -= aAdvance;
    1835           0 :             glyphY = aPt->y;
    1836             :         } else {
    1837           0 :             glyphY = aPt->y;
    1838           0 :             aPt->y += aAdvance;
    1839             :         }
    1840             :     } else {
    1841         448 :         glyphY = aPt->y;
    1842         448 :         if (runParams.isRTL) {
    1843           0 :             aPt->x -= aAdvance;
    1844           0 :             glyphX = aPt->x;
    1845             :         } else {
    1846         448 :             glyphX = aPt->x;
    1847         448 :             aPt->x += aAdvance;
    1848             :         }
    1849             :     }
    1850         448 :     gfxPoint devPt(ToDeviceUnits(glyphX, runParams.devPerApp),
    1851         896 :                    ToDeviceUnits(glyphY, runParams.devPerApp));
    1852             : 
    1853         448 :     if (fontParams.haveSVGGlyphs) {
    1854           0 :         if (!runParams.paintSVGGlyphs) {
    1855           0 :             return;
    1856             :         }
    1857           0 :         NS_WARNING_ASSERTION(
    1858             :           runParams.drawMode != DrawMode::GLYPH_PATH,
    1859             :           "Rendering SVG glyph despite request for glyph path");
    1860           0 :         if (RenderSVGGlyph(runParams.context, devPt,
    1861           0 :                            aGlyphID, fontParams.contextPaint,
    1862           0 :                            runParams.callbacks, *aEmittedGlyphs)) {
    1863           0 :             return;
    1864             :         }
    1865             :     }
    1866             : 
    1867        1344 :     if (fontParams.haveColorGlyphs &&
    1868           0 :         RenderColorGlyph(runParams.dt, runParams.context,
    1869             :                          fontParams.scaledFont, fontParams.renderingOptions,
    1870             :                          fontParams.drawOptions,
    1871         448 :                          fontParams.matInv.TransformPoint(gfx::Point(devPt.x, devPt.y)),
    1872             :                          aGlyphID)) {
    1873           0 :         return;
    1874             :     }
    1875             : 
    1876         448 :     aBuffer.OutputGlyph(aGlyphID, devPt);
    1877             : 
    1878             :     // Synthetic bolding (if required) by multi-striking.
    1879         448 :     for (int32_t i = 0; i < fontParams.extraStrikes; ++i) {
    1880           0 :         if (fontParams.isVerticalFont) {
    1881           0 :             devPt.y += fontParams.synBoldOnePixelOffset;
    1882             :         } else {
    1883           0 :             devPt.x += fontParams.synBoldOnePixelOffset;
    1884             :         }
    1885           0 :         aBuffer.OutputGlyph(aGlyphID, devPt);
    1886             :     }
    1887             : 
    1888         448 :     *aEmittedGlyphs = true;
    1889             : }
    1890             : 
    1891             : // Draw a run of CharacterGlyph records from the given offset in aShapedText.
    1892             : // Returns true if glyph paths were actually emitted.
    1893             : bool
    1894          21 : gfxFont::DrawGlyphs(const gfxShapedText      *aShapedText,
    1895             :                     uint32_t                  aOffset, // offset in the textrun
    1896             :                     uint32_t                  aCount, // length of run to draw
    1897             :                     gfxPoint                 *aPt,
    1898             :                     const TextRunDrawParams&  aRunParams,
    1899             :                     const FontDrawParams&     aFontParams)
    1900             : {
    1901          21 :     bool emittedGlyphs = false;
    1902          42 :     GlyphBufferAzure buffer(aRunParams, aFontParams);
    1903             : 
    1904          21 :     gfxFloat& inlineCoord = aFontParams.isVerticalFont ? aPt->y : aPt->x;
    1905             : 
    1906          21 :     if (aRunParams.spacing) {
    1907           0 :         inlineCoord += aRunParams.isRTL ? -aRunParams.spacing[0].mBefore
    1908           0 :                                         : aRunParams.spacing[0].mBefore;
    1909             :     }
    1910             : 
    1911             :     const gfxShapedText::CompressedGlyph *glyphData =
    1912          21 :         &aShapedText->GetCharacterGlyphs()[aOffset];
    1913             : 
    1914         469 :     for (uint32_t i = 0; i < aCount; ++i, ++glyphData) {
    1915         448 :         if (glyphData->IsSimpleGlyph()) {
    1916         448 :             DrawOneGlyph(glyphData->GetSimpleGlyph(),
    1917         448 :                          glyphData->GetSimpleAdvance(),
    1918         448 :                          aPt, buffer, &emittedGlyphs);
    1919             :         } else {
    1920           0 :             uint32_t glyphCount = glyphData->GetGlyphCount();
    1921           0 :             if (glyphCount > 0) {
    1922             :                 const gfxShapedText::DetailedGlyph *details =
    1923           0 :                     aShapedText->GetDetailedGlyphs(aOffset + i);
    1924           0 :                 NS_ASSERTION(details, "detailedGlyph should not be missing!");
    1925           0 :                 for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
    1926           0 :                     double advance = details->mAdvance;
    1927             : 
    1928           0 :                     if (glyphData->IsMissing()) {
    1929             :                         // Default-ignorable chars will have zero advance width;
    1930             :                         // we don't have to draw the hexbox for them.
    1931           0 :                         if (aRunParams.drawMode != DrawMode::GLYPH_PATH &&
    1932             :                             advance > 0) {
    1933           0 :                             double glyphX = aPt->x;
    1934           0 :                             double glyphY = aPt->y;
    1935           0 :                             if (aRunParams.isRTL) {
    1936           0 :                                 if (aFontParams.isVerticalFont) {
    1937           0 :                                     glyphY -= advance;
    1938             :                                 } else {
    1939           0 :                                     glyphX -= advance;
    1940             :                                 }
    1941             :                             }
    1942           0 :                             Point pt(Float(ToDeviceUnits(glyphX, aRunParams.devPerApp)),
    1943           0 :                                      Float(ToDeviceUnits(glyphY, aRunParams.devPerApp)));
    1944             :                             Float advanceDevUnits =
    1945           0 :                                 Float(ToDeviceUnits(advance, aRunParams.devPerApp));
    1946           0 :                             Float height = GetMetrics(eHorizontal).maxAscent;
    1947           0 :                             Rect glyphRect = aFontParams.isVerticalFont ?
    1948           0 :                                 Rect(pt.x - height / 2, pt.y,
    1949             :                                      height, advanceDevUnits) :
    1950           0 :                                 Rect(pt.x, pt.y - height,
    1951           0 :                                      advanceDevUnits, height);
    1952             : 
    1953             :                             // If there's a fake-italic skew in effect as part
    1954             :                             // of the drawTarget's transform, we need to remove
    1955             :                             // this before drawing the hexbox. (Bug 983985)
    1956           0 :                             Matrix oldMat;
    1957           0 :                             if (aFontParams.passedInvMatrix) {
    1958           0 :                                 oldMat = aRunParams.dt->GetTransform();
    1959           0 :                                 aRunParams.dt->SetTransform(
    1960           0 :                                     *aFontParams.passedInvMatrix * oldMat);
    1961             :                             }
    1962             : 
    1963           0 :                             gfxFontMissingGlyphs::DrawMissingGlyph(
    1964           0 :                                 details->mGlyphID, glyphRect, *aRunParams.dt,
    1965           0 :                                 PatternFromState(aRunParams.context),
    1966           0 :                                 aShapedText->GetAppUnitsPerDevUnit());
    1967             : 
    1968             :                             // Restore the matrix, if we modified it before
    1969             :                             // drawing the hexbox.
    1970           0 :                             if (aFontParams.passedInvMatrix) {
    1971           0 :                                 aRunParams.dt->SetTransform(oldMat);
    1972             :                             }
    1973             :                         }
    1974             :                     } else {
    1975           0 :                         gfxPoint glyphXY(*aPt);
    1976           0 :                         if (aFontParams.isVerticalFont) {
    1977           0 :                             glyphXY.x += details->mYOffset;
    1978           0 :                             glyphXY.y += details->mXOffset;
    1979             :                         } else {
    1980           0 :                             glyphXY.x += details->mXOffset;
    1981           0 :                             glyphXY.y += details->mYOffset;
    1982             :                         }
    1983           0 :                         DrawOneGlyph(details->mGlyphID, advance, &glyphXY,
    1984           0 :                                      buffer, &emittedGlyphs);
    1985             :                     }
    1986             : 
    1987           0 :                     inlineCoord += aRunParams.isRTL ? -advance : advance;
    1988             :                 }
    1989             :             }
    1990             :         }
    1991             : 
    1992         448 :         if (aRunParams.spacing) {
    1993           0 :             double space = aRunParams.spacing[i].mAfter;
    1994           0 :             if (i + 1 < aCount) {
    1995           0 :                 space += aRunParams.spacing[i + 1].mBefore;
    1996             :             }
    1997           0 :             inlineCoord += aRunParams.isRTL ? -space : space;
    1998             :         }
    1999             :     }
    2000             : 
    2001          42 :     return emittedGlyphs;
    2002             : }
    2003             : 
    2004             : // This method is mostly parallel to DrawGlyphs.
    2005             : void
    2006           0 : gfxFont::DrawEmphasisMarks(const gfxTextRun* aShapedText, gfxPoint* aPt,
    2007             :                            uint32_t aOffset, uint32_t aCount,
    2008             :                            const EmphasisMarkDrawParams& aParams)
    2009             : {
    2010           0 :     gfxFloat& inlineCoord = aParams.isVertical ? aPt->y : aPt->x;
    2011           0 :     gfxTextRun::Range markRange(aParams.mark);
    2012           0 :     gfxTextRun::DrawParams params(aParams.context);
    2013             : 
    2014           0 :     gfxFloat clusterStart = -std::numeric_limits<gfxFloat>::infinity();
    2015           0 :     bool shouldDrawEmphasisMark = false;
    2016           0 :     for (uint32_t i = 0, idx = aOffset; i < aCount; ++i, ++idx) {
    2017           0 :         if (aParams.spacing) {
    2018           0 :             inlineCoord += aParams.direction * aParams.spacing[i].mBefore;
    2019             :         }
    2020           0 :         if (aShapedText->IsClusterStart(idx) ||
    2021           0 :             clusterStart == -std::numeric_limits<gfxFloat>::infinity()) {
    2022           0 :             clusterStart = inlineCoord;
    2023             :         }
    2024           0 :         if (aShapedText->CharMayHaveEmphasisMark(idx)) {
    2025           0 :             shouldDrawEmphasisMark = true;
    2026             :         }
    2027           0 :         inlineCoord += aParams.direction * aShapedText->GetAdvanceForGlyph(idx);
    2028           0 :         if (shouldDrawEmphasisMark &&
    2029           0 :             (i + 1 == aCount || aShapedText->IsClusterStart(idx + 1))) {
    2030           0 :             gfxFloat clusterAdvance = inlineCoord - clusterStart;
    2031             :             // Move the coord backward to get the needed start point.
    2032           0 :             gfxFloat delta = (clusterAdvance + aParams.advance) / 2;
    2033           0 :             inlineCoord -= delta;
    2034           0 :             aParams.mark->Draw(markRange, *aPt, params);
    2035           0 :             inlineCoord += delta;
    2036           0 :             shouldDrawEmphasisMark = false;
    2037             :         }
    2038           0 :         if (aParams.spacing) {
    2039           0 :             inlineCoord += aParams.direction * aParams.spacing[i].mAfter;
    2040             :         }
    2041             :     }
    2042           0 : }
    2043             : 
    2044             : void
    2045          21 : gfxFont::Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
    2046             :               gfxPoint *aPt, const TextRunDrawParams& aRunParams,
    2047             :               gfx::ShapedTextFlags aOrientation)
    2048             : {
    2049          21 :     NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
    2050             :                  !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
    2051             :                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
    2052             : 
    2053          21 :     if (aStart >= aEnd) {
    2054           0 :         return;
    2055             :     }
    2056             : 
    2057          42 :     FontDrawParams fontParams;
    2058             : 
    2059          21 :     if (aRunParams.drawOpts) {
    2060           0 :         fontParams.drawOptions = *aRunParams.drawOpts;
    2061             :     }
    2062             : 
    2063          21 :     fontParams.scaledFont = GetScaledFont(aRunParams.dt);
    2064          21 :     if (!fontParams.scaledFont) {
    2065           0 :         return;
    2066             :     }
    2067             : 
    2068          21 :     fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
    2069          21 :     fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
    2070          21 :     fontParams.contextPaint = aRunParams.runContextPaint;
    2071          21 :     fontParams.isVerticalFont =
    2072          21 :         aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
    2073             : 
    2074          21 :     bool sideways = false;
    2075          42 :     gfxContextMatrixAutoSaveRestore matrixRestore;
    2076             : 
    2077          21 :     gfxPoint origPt = *aPt;
    2078          21 :     if (aRunParams.isVerticalRun && !fontParams.isVerticalFont) {
    2079           0 :         sideways = true;
    2080           0 :         matrixRestore.SetContext(aRunParams.context);
    2081           0 :         gfxPoint p(aPt->x * aRunParams.devPerApp,
    2082           0 :                    aPt->y * aRunParams.devPerApp);
    2083           0 :         const Metrics& metrics = GetMetrics(eHorizontal);
    2084             :         // Get a matrix we can use to draw the (horizontally-shaped) textrun
    2085             :         // with 90-degree CW rotation.
    2086             :         const gfxFloat
    2087             :             rotation = (aOrientation ==
    2088             :                         gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT)
    2089           0 :                        ? -M_PI / 2.0 : M_PI / 2.0;
    2090             :         gfxMatrix mat =
    2091           0 :             aRunParams.context->CurrentMatrix().
    2092           0 :             PreTranslate(p).     // translate origin for rotation
    2093           0 :             PreRotate(rotation). // turn 90deg CCW (sideways-left) or CW (*-right)
    2094           0 :             PreTranslate(-p);    // undo the translation
    2095             : 
    2096             :         // If we're drawing rotated horizontal text for an element styled
    2097             :         // text-orientation:mixed, the dominant baseline will be vertical-
    2098             :         // centered. So in this case, we need to adjust the position so that
    2099             :         // the rotated horizontal text (which uses an alphabetic baseline) will
    2100             :         // look OK when juxtaposed with upright glyphs (rendered on a centered
    2101             :         // vertical baseline). The adjustment here is somewhat ad hoc; we
    2102             :         // should eventually look for baseline tables[1] in the fonts and use
    2103             :         // those if available.
    2104             :         // [1] See http://www.microsoft.com/typography/otspec/base.htm
    2105           0 :         if (aTextRun->UseCenterBaseline()) {
    2106           0 :             gfxPoint baseAdj(0, (metrics.emAscent - metrics.emDescent) / 2);
    2107           0 :             mat.PreTranslate(baseAdj);
    2108             :         }
    2109             : 
    2110           0 :         aRunParams.context->SetMatrix(mat);
    2111             :     }
    2112             : 
    2113          42 :     RefPtr<SVGContextPaint> contextPaint;
    2114          21 :     if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
    2115             :         // If no pattern is specified for fill, use the current pattern
    2116           0 :         NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
    2117             :                      "no pattern supplied for stroking text");
    2118           0 :         RefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
    2119             :         contextPaint =
    2120             :             new SimpleTextContextPaint(fillPattern, nullptr,
    2121           0 :                                        aRunParams.context->CurrentMatrix());
    2122           0 :         fontParams.contextPaint = contextPaint.get();
    2123             :     }
    2124             : 
    2125             :     // Synthetic-bold strikes are each offset one device pixel in run direction.
    2126             :     // (these values are only needed if IsSyntheticBold() is true)
    2127          21 :     if (IsSyntheticBold()) {
    2128           0 :         double xscale = CalcXScale(aRunParams.context->GetDrawTarget());
    2129           0 :         fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
    2130           0 :         if (xscale != 0.0) {
    2131             :             // use as many strikes as needed for the the increased advance
    2132           0 :             fontParams.extraStrikes =
    2133           0 :                 std::max(1, NS_lroundf(GetSyntheticBoldOffset() / xscale));
    2134             :         }
    2135             :     } else {
    2136          21 :         fontParams.synBoldOnePixelOffset = 0;
    2137          21 :         fontParams.extraStrikes = 0;
    2138             :     }
    2139             : 
    2140          21 :     bool oldSubpixelAA = aRunParams.dt->GetPermitSubpixelAA();
    2141          21 :     if (!AllowSubpixelAA()) {
    2142           0 :         aRunParams.dt->SetPermitSubpixelAA(false);
    2143             :     }
    2144             : 
    2145          21 :     Matrix mat;
    2146          21 :     Matrix oldMat = aRunParams.dt->GetTransform();
    2147             : 
    2148             :     // This is nullptr when we have inverse-transformed glyphs and we need
    2149             :     // to transform the Brush inside flush.
    2150          21 :     fontParams.passedInvMatrix = nullptr;
    2151             : 
    2152          21 :     fontParams.renderingOptions = GetGlyphRenderingOptions(&aRunParams);
    2153          21 :     fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
    2154             : 
    2155             :     // The cairo DrawTarget backend uses the cairo_scaled_font directly
    2156             :     // and so has the font skew matrix applied already.
    2157          42 :     if (mScaledFont &&
    2158          21 :         aRunParams.dt->GetBackendType() != BackendType::CAIRO) {
    2159             :         cairo_matrix_t matrix;
    2160          21 :         cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
    2161          21 :         if (matrix.xy != 0) {
    2162             :             // If this matrix applies a skew, which can happen when drawing
    2163             :             // oblique fonts, we will set the DrawTarget matrix to apply the
    2164             :             // skew. We'll need to move the glyphs by the inverse of the skew to
    2165             :             // get the glyphs positioned correctly in the new device space
    2166             :             // though, since the font matrix should only be applied to drawing
    2167             :             // the glyphs, and not to their position.
    2168           0 :             mat = Matrix(matrix.xx, matrix.yx,
    2169           0 :                          matrix.xy, matrix.yy,
    2170           0 :                          matrix.x0, matrix.y0);
    2171             : 
    2172           0 :             mat._11 = mat._22 = 1.0;
    2173           0 :             mat._21 /= GetAdjustedSize();
    2174             : 
    2175           0 :             aRunParams.dt->SetTransform(mat * oldMat);
    2176             : 
    2177           0 :             fontParams.matInv = mat;
    2178           0 :             fontParams.matInv.Invert();
    2179             : 
    2180           0 :             fontParams.passedInvMatrix = &fontParams.matInv;
    2181             :         }
    2182             :     }
    2183             : 
    2184          21 :     gfxFloat& baseline = fontParams.isVerticalFont ? aPt->x : aPt->y;
    2185          21 :     gfxFloat origBaseline = baseline;
    2186          21 :     if (mStyle.baselineOffset != 0.0) {
    2187           0 :         baseline +=
    2188           0 :             mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit();
    2189             :     }
    2190             : 
    2191             :     bool emittedGlyphs =
    2192          21 :         DrawGlyphs(aTextRun, aStart, aEnd - aStart, aPt,
    2193          21 :                    aRunParams, fontParams);
    2194             : 
    2195          21 :     baseline = origBaseline;
    2196             : 
    2197          21 :     if (aRunParams.callbacks && emittedGlyphs) {
    2198           0 :         aRunParams.callbacks->NotifyGlyphPathEmitted();
    2199             :     }
    2200             : 
    2201          21 :     aRunParams.dt->SetTransform(oldMat);
    2202          21 :     aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
    2203             : 
    2204          21 :     if (sideways) {
    2205             :         // adjust updated aPt to account for the transform we were using
    2206           0 :         gfxFloat advance = aPt->x - origPt.x;
    2207           0 :         if (aOrientation ==
    2208             :             gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT) {
    2209           0 :             *aPt = gfxPoint(origPt.x, origPt.y - advance);
    2210             :         } else {
    2211           0 :             *aPt = gfxPoint(origPt.x, origPt.y + advance);
    2212             :         }
    2213             :     }
    2214             : }
    2215             : 
    2216             : bool
    2217           0 : gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
    2218             :                         uint32_t aGlyphId, SVGContextPaint* aContextPaint) const
    2219             : {
    2220           0 :     if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
    2221           0 :         return false;
    2222             :     }
    2223             : 
    2224             :     const gfxFloat devUnitsPerSVGUnit =
    2225           0 :         GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
    2226           0 :     gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
    2227             : 
    2228             :     aContext->SetMatrix(
    2229           0 :       aContext->CurrentMatrix().PreTranslate(aPoint.x, aPoint.y).
    2230           0 :                                 PreScale(devUnitsPerSVGUnit, devUnitsPerSVGUnit));
    2231             : 
    2232           0 :     aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
    2233             : 
    2234           0 :     GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, aContextPaint);
    2235           0 :     aContext->NewPath();
    2236           0 :     return true;
    2237             : }
    2238             : 
    2239             : bool
    2240           0 : gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
    2241             :                         uint32_t aGlyphId, SVGContextPaint* aContextPaint,
    2242             :                         gfxTextRunDrawCallbacks *aCallbacks,
    2243             :                         bool& aEmittedGlyphs) const
    2244             : {
    2245           0 :     if (aCallbacks && aEmittedGlyphs) {
    2246           0 :         aCallbacks->NotifyGlyphPathEmitted();
    2247           0 :         aEmittedGlyphs = false;
    2248             :     }
    2249           0 :     return RenderSVGGlyph(aContext, aPoint, aGlyphId, aContextPaint);
    2250             : }
    2251             : 
    2252             : bool
    2253           0 : gfxFont::RenderColorGlyph(DrawTarget* aDrawTarget,
    2254             :                           gfxContext* aContext,
    2255             :                           mozilla::gfx::ScaledFont* scaledFont,
    2256             :                           GlyphRenderingOptions* aRenderingOptions,
    2257             :                           mozilla::gfx::DrawOptions aDrawOptions,
    2258             :                           const mozilla::gfx::Point& aPoint,
    2259             :                           uint32_t aGlyphId) const
    2260             : {
    2261           0 :     AutoTArray<uint16_t, 8> layerGlyphs;
    2262           0 :     AutoTArray<mozilla::gfx::Color, 8> layerColors;
    2263             : 
    2264           0 :     mozilla::gfx::Color defaultColor;
    2265           0 :     if (!aContext->GetDeviceColor(defaultColor)) {
    2266           0 :         defaultColor = mozilla::gfx::Color(0, 0, 0);
    2267             :     }
    2268           0 :     if (!GetFontEntry()->GetColorLayersInfo(aGlyphId, defaultColor,
    2269             :                                             layerGlyphs, layerColors)) {
    2270           0 :         return false;
    2271             :     }
    2272             : 
    2273           0 :     for (uint32_t layerIndex = 0; layerIndex < layerGlyphs.Length();
    2274             :          layerIndex++) {
    2275           0 :         Glyph glyph;
    2276           0 :         glyph.mIndex = layerGlyphs[layerIndex];
    2277           0 :         glyph.mPosition = aPoint;
    2278             : 
    2279             :         mozilla::gfx::GlyphBuffer buffer;
    2280           0 :         buffer.mGlyphs = &glyph;
    2281           0 :         buffer.mNumGlyphs = 1;
    2282             : 
    2283             :         aDrawTarget->FillGlyphs(scaledFont, buffer,
    2284           0 :                                 ColorPattern(layerColors[layerIndex]),
    2285           0 :                                 aDrawOptions, aRenderingOptions);
    2286             :     }
    2287           0 :     return true;
    2288             : }
    2289             : 
    2290             : static void
    2291         879 : UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
    2292             : {
    2293         879 :     *aDestMin = std::min(*aDestMin, aX);
    2294         879 :     *aDestMax = std::max(*aDestMax, aX);
    2295         879 : }
    2296             : 
    2297             : // We get precise glyph extents if the textrun creator requested them, or
    2298             : // if the font is a user font --- in which case the author may be relying
    2299             : // on overflowing glyphs.
    2300             : static bool
    2301          43 : NeedsGlyphExtents(gfxFont *aFont, const gfxTextRun *aTextRun)
    2302             : {
    2303         191 :     return (aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX) ||
    2304         148 :         aFont->GetFontEntry()->IsUserFont();
    2305             : }
    2306             : 
    2307             : bool
    2308          48 : gfxFont::IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget,
    2309             :                                const gfxTextRun* aTextRun)
    2310             : {
    2311          50 :     if (!mFontEntry->mSpaceGlyphIsInvisibleInitialized &&
    2312           2 :         GetAdjustedSize() >= 1.0) {
    2313             :         gfxGlyphExtents *extents =
    2314           2 :             GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
    2315           2 :         gfxRect glyphExtents;
    2316           4 :         mFontEntry->mSpaceGlyphIsInvisible =
    2317           2 :             extents->GetTightGlyphExtentsAppUnits(this, aRefDrawTarget,
    2318           6 :                 GetSpaceGlyph(), &glyphExtents) &&
    2319           2 :             glyphExtents.IsEmpty();
    2320           2 :         mFontEntry->mSpaceGlyphIsInvisibleInitialized = true;
    2321             :     }
    2322          48 :     return mFontEntry->mSpaceGlyphIsInvisible;
    2323             : }
    2324             : 
    2325             : gfxFont::RunMetrics
    2326          48 : gfxFont::Measure(const gfxTextRun *aTextRun,
    2327             :                  uint32_t aStart, uint32_t aEnd,
    2328             :                  BoundingBoxType aBoundingBoxType,
    2329             :                  DrawTarget* aRefDrawTarget,
    2330             :                  Spacing *aSpacing,
    2331             :                  gfx::ShapedTextFlags aOrientation)
    2332             : {
    2333             :     // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
    2334             :     // and the underlying cairo font may be antialiased,
    2335             :     // we need to create a copy in order to avoid getting cached extents.
    2336             :     // This is only used by MathML layout at present.
    2337          48 :     if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
    2338           0 :         mAntialiasOption != kAntialiasNone) {
    2339           0 :         if (!mNonAAFont) {
    2340           0 :             mNonAAFont = Move(CopyWithAntialiasOption(kAntialiasNone));
    2341             :         }
    2342             :         // if font subclass doesn't implement CopyWithAntialiasOption(),
    2343             :         // it will return null and we'll proceed to use the existing font
    2344           0 :         if (mNonAAFont) {
    2345           0 :             return mNonAAFont->Measure(aTextRun, aStart, aEnd,
    2346             :                                        TIGHT_HINTED_OUTLINE_EXTENTS,
    2347           0 :                                        aRefDrawTarget, aSpacing, aOrientation);
    2348             :         }
    2349             :     }
    2350             : 
    2351          48 :     const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
    2352             :     // Current position in appunits
    2353             :     gfxFont::Orientation orientation =
    2354             :         aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
    2355          48 :         ? eVertical : eHorizontal;
    2356          48 :     const gfxFont::Metrics& fontMetrics = GetMetrics(orientation);
    2357             : 
    2358          48 :     gfxFloat baselineOffset = 0;
    2359          48 :     if (aTextRun->UseCenterBaseline() && orientation == eHorizontal) {
    2360             :         // For a horizontal font being used in vertical writing mode with
    2361             :         // text-orientation:mixed, the overall metrics we're accumulating
    2362             :         // will be aimed at a center baseline. But this font's metrics were
    2363             :         // based on the alphabetic baseline. So we compute a baseline offset
    2364             :         // that will be applied to ascent/descent values and glyph rects
    2365             :         // to effectively shift them relative to the baseline.
    2366             :         // XXX Eventually we should probably use the BASE table, if present.
    2367             :         // But it usually isn't, so we need an ad hoc adjustment for now.
    2368           0 :         baselineOffset = appUnitsPerDevUnit *
    2369           0 :             (fontMetrics.emAscent - fontMetrics.emDescent) / 2;
    2370             :     }
    2371             : 
    2372          48 :     RunMetrics metrics;
    2373          48 :     metrics.mAscent = fontMetrics.maxAscent * appUnitsPerDevUnit;
    2374          48 :     metrics.mDescent = fontMetrics.maxDescent * appUnitsPerDevUnit;
    2375             : 
    2376          48 :     if (aStart == aEnd) {
    2377             :         // exit now before we look at aSpacing[0], which is undefined
    2378           5 :         metrics.mAscent -= baselineOffset;
    2379           5 :         metrics.mDescent += baselineOffset;
    2380           5 :         metrics.mBoundingBox = gfxRect(0, -metrics.mAscent,
    2381           5 :                                        0, metrics.mAscent + metrics.mDescent);
    2382           5 :         return metrics;
    2383             :     }
    2384             : 
    2385          43 :     gfxFloat advanceMin = 0, advanceMax = 0;
    2386          43 :     const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
    2387          43 :     bool isRTL = aTextRun->IsRightToLeft();
    2388          43 :     double direction = aTextRun->GetDirection();
    2389          43 :     bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
    2390             :     gfxGlyphExtents *extents =
    2391          43 :         ((aBoundingBoxType == LOOSE_INK_EXTENTS &&
    2392          19 :             !needsGlyphExtents &&
    2393          43 :             !aTextRun->HasDetailedGlyphs()) ||
    2394          48 :          (MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0)) ||
    2395          91 :          (MOZ_UNLIKELY(GetStyle()->size == 0))) ? nullptr
    2396          67 :         : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
    2397          43 :     double x = 0;
    2398          43 :     if (aSpacing) {
    2399           0 :         x += direction*aSpacing[0].mBefore;
    2400             :     }
    2401          43 :     uint32_t spaceGlyph = GetSpaceGlyph();
    2402          43 :     bool allGlyphsInvisible = true;
    2403             :     uint32_t i;
    2404         655 :     for (i = aStart; i < aEnd; ++i) {
    2405         612 :         const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
    2406         612 :         if (glyphData->IsSimpleGlyph()) {
    2407         612 :             double advance = glyphData->GetSimpleAdvance();
    2408         612 :             uint32_t glyphIndex = glyphData->GetSimpleGlyph();
    2409         660 :             if (glyphIndex != spaceGlyph ||
    2410          48 :                 !IsSpaceGlyphInvisible(aRefDrawTarget, aTextRun)) {
    2411         564 :                 allGlyphsInvisible = false;
    2412             :             }
    2413             :             // Only get the real glyph horizontal extent if we were asked
    2414             :             // for the tight bounding box or we're in quality mode
    2415         612 :             if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
    2416             :                 extents){
    2417         429 :                 uint16_t extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
    2418         429 :                 if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
    2419             :                     aBoundingBoxType == LOOSE_INK_EXTENTS) {
    2420         418 :                     UnionRange(x, &advanceMin, &advanceMax);
    2421         418 :                     UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax);
    2422             :                 } else {
    2423          11 :                     gfxRect glyphRect;
    2424          11 :                     if (!extents->GetTightGlyphExtentsAppUnits(this,
    2425             :                             aRefDrawTarget, glyphIndex, &glyphRect)) {
    2426           0 :                         glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
    2427             :                             advance, metrics.mBoundingBox.Height());
    2428             :                     }
    2429          11 :                     if (orientation == eVertical) {
    2430           0 :                         Swap(glyphRect.x, glyphRect.y);
    2431           0 :                         Swap(glyphRect.width, glyphRect.height);
    2432             :                     }
    2433          11 :                     if (isRTL) {
    2434           0 :                         glyphRect -= gfxPoint(advance, 0);
    2435             :                     }
    2436          11 :                     glyphRect += gfxPoint(x, 0);
    2437          11 :                     metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
    2438             :                 }
    2439             :             }
    2440         612 :             x += direction*advance;
    2441             :         } else {
    2442           0 :             allGlyphsInvisible = false;
    2443           0 :             uint32_t glyphCount = glyphData->GetGlyphCount();
    2444           0 :             if (glyphCount > 0) {
    2445             :                 const gfxTextRun::DetailedGlyph *details =
    2446           0 :                     aTextRun->GetDetailedGlyphs(i);
    2447           0 :                 NS_ASSERTION(details != nullptr,
    2448             :                              "detailedGlyph record should not be missing!");
    2449             :                 uint32_t j;
    2450           0 :                 for (j = 0; j < glyphCount; ++j, ++details) {
    2451           0 :                     uint32_t glyphIndex = details->mGlyphID;
    2452           0 :                     gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
    2453           0 :                     double advance = details->mAdvance;
    2454           0 :                     gfxRect glyphRect;
    2455           0 :                     if (glyphData->IsMissing() || !extents ||
    2456           0 :                         !extents->GetTightGlyphExtentsAppUnits(this,
    2457             :                                 aRefDrawTarget, glyphIndex, &glyphRect)) {
    2458             :                         // We might have failed to get glyph extents due to
    2459             :                         // OOM or something
    2460           0 :                         glyphRect = gfxRect(0, -metrics.mAscent,
    2461           0 :                             advance, metrics.mAscent + metrics.mDescent);
    2462             :                     }
    2463           0 :                     if (orientation == eVertical) {
    2464           0 :                         Swap(glyphRect.x, glyphRect.y);
    2465           0 :                         Swap(glyphRect.width, glyphRect.height);
    2466             :                     }
    2467           0 :                     if (isRTL) {
    2468           0 :                         glyphRect -= gfxPoint(advance, 0);
    2469             :                     }
    2470           0 :                     glyphRect += glyphPt;
    2471           0 :                     metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
    2472           0 :                     x += direction*advance;
    2473             :                 }
    2474             :             }
    2475             :         }
    2476             :         // Every other glyph type is ignored
    2477         612 :         if (aSpacing) {
    2478           0 :             double space = aSpacing[i - aStart].mAfter;
    2479           0 :             if (i + 1 < aEnd) {
    2480           0 :                 space += aSpacing[i + 1 - aStart].mBefore;
    2481             :             }
    2482           0 :             x += direction*space;
    2483             :         }
    2484             :     }
    2485             : 
    2486          43 :     if (allGlyphsInvisible) {
    2487           0 :         metrics.mBoundingBox.SetEmpty();
    2488             :     } else {
    2489          43 :         if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
    2490          43 :             UnionRange(x, &advanceMin, &advanceMax);
    2491          43 :             gfxRect fontBox(advanceMin, -metrics.mAscent,
    2492          86 :                             advanceMax - advanceMin, metrics.mAscent + metrics.mDescent);
    2493          43 :             metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
    2494             :         }
    2495          43 :         if (isRTL) {
    2496           0 :             metrics.mBoundingBox -= gfxPoint(x, 0);
    2497             :         }
    2498             :     }
    2499             : 
    2500             :     // If the font may be rendered with a fake-italic effect, we need to allow
    2501             :     // for the top-right of the glyphs being skewed to the right, and the
    2502             :     // bottom-left being skewed further left.
    2503          86 :     if (mStyle.style != NS_FONT_STYLE_NORMAL &&
    2504          43 :         mFontEntry->IsUpright() &&
    2505           0 :         mStyle.allowSyntheticStyle) {
    2506             :         gfxFloat extendLeftEdge =
    2507           0 :             ceil(OBLIQUE_SKEW_FACTOR * metrics.mBoundingBox.YMost());
    2508             :         gfxFloat extendRightEdge =
    2509           0 :             ceil(OBLIQUE_SKEW_FACTOR * -metrics.mBoundingBox.y);
    2510           0 :         metrics.mBoundingBox.width += extendLeftEdge + extendRightEdge;
    2511           0 :         metrics.mBoundingBox.x -= extendLeftEdge;
    2512             :     }
    2513             : 
    2514          43 :     if (baselineOffset != 0) {
    2515           0 :         metrics.mAscent -= baselineOffset;
    2516           0 :         metrics.mDescent += baselineOffset;
    2517           0 :         metrics.mBoundingBox.y += baselineOffset;
    2518             :     }
    2519             : 
    2520          43 :     metrics.mAdvanceWidth = x*direction;
    2521          43 :     return metrics;
    2522             : }
    2523             : 
    2524             : void
    2525           0 : gfxFont::AgeCachedWords()
    2526             : {
    2527           0 :     if (mWordCache) {
    2528           0 :         for (auto it = mWordCache->Iter(); !it.Done(); it.Next()) {
    2529           0 :             CacheHashEntry *entry = it.Get();
    2530           0 :             if (!entry->mShapedWord) {
    2531           0 :                 NS_ASSERTION(entry->mShapedWord,
    2532             :                              "cache entry has no gfxShapedWord!");
    2533           0 :                 it.Remove();
    2534           0 :             } else if (entry->mShapedWord->IncrementAge() ==
    2535             :                        kShapedWordCacheMaxAge) {
    2536           0 :                 it.Remove();
    2537             :             }
    2538             :         }
    2539             :     }
    2540           0 : }
    2541             : 
    2542             : void
    2543           0 : gfxFont::NotifyGlyphsChanged()
    2544             : {
    2545           0 :     uint32_t i, count = mGlyphExtentsArray.Length();
    2546           0 :     for (i = 0; i < count; ++i) {
    2547             :         // Flush cached extents array
    2548           0 :         mGlyphExtentsArray[i]->NotifyGlyphsChanged();
    2549             :     }
    2550             : 
    2551           0 :     if (mGlyphChangeObservers) {
    2552           0 :         for (auto it = mGlyphChangeObservers->Iter(); !it.Done(); it.Next()) {
    2553           0 :             it.Get()->GetKey()->NotifyGlyphsChanged();
    2554             :         }
    2555             :     }
    2556           0 : }
    2557             : 
    2558             : // If aChar is a "word boundary" for shaped-word caching purposes, return it;
    2559             : // else return 0.
    2560             : static char16_t
    2561         931 : IsBoundarySpace(char16_t aChar, char16_t aNextChar)
    2562             : {
    2563         931 :     if ((aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar)) {
    2564          58 :         return aChar;
    2565             :     }
    2566         873 :     return 0;
    2567             : }
    2568             : 
    2569             : #ifdef __GNUC__
    2570             : #define GFX_MAYBE_UNUSED __attribute__((unused))
    2571             : #else
    2572             : #define GFX_MAYBE_UNUSED
    2573             : #endif
    2574             : 
    2575             : template<typename T>
    2576             : gfxShapedWord*
    2577         127 : gfxFont::GetShapedWord(DrawTarget *aDrawTarget,
    2578             :                        const T    *aText,
    2579             :                        uint32_t    aLength,
    2580             :                        uint32_t    aHash,
    2581             :                        Script      aRunScript,
    2582             :                        bool        aVertical,
    2583             :                        int32_t     aAppUnitsPerDevUnit,
    2584             :                        gfx::ShapedTextFlags aFlags,
    2585             :                        RoundingFlags aRounding,
    2586             :                        gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
    2587             : {
    2588             :     // if the cache is getting too big, flush it and start over
    2589             :     uint32_t wordCacheMaxEntries =
    2590         127 :         gfxPlatform::GetPlatform()->WordCacheMaxEntries();
    2591         127 :     if (mWordCache->Count() > wordCacheMaxEntries) {
    2592           0 :         NS_WARNING("flushing shaped-word cache");
    2593           0 :         ClearCachedWords();
    2594             :     }
    2595             : 
    2596             :     // if there's a cached entry for this word, just return it
    2597             :     CacheHashKey key(aText, aLength, aHash,
    2598             :                      aRunScript,
    2599             :                      aAppUnitsPerDevUnit,
    2600         127 :                      aFlags, aRounding);
    2601             : 
    2602         127 :     CacheHashEntry* entry = mWordCache->PutEntry(key, fallible);
    2603         127 :     if (!entry) {
    2604           0 :         NS_WARNING("failed to create word cache entry - expect missing text");
    2605           0 :         return nullptr;
    2606             :     }
    2607         127 :     gfxShapedWord* sw = entry->mShapedWord.get();
    2608             : 
    2609         127 :     if (sw) {
    2610          95 :         sw->ResetAge();
    2611             : #ifndef RELEASE_OR_BETA
    2612          95 :         if (aTextPerf) {
    2613           0 :             aTextPerf->current.wordCacheHit++;
    2614             :         }
    2615             : #endif
    2616          95 :         return sw;
    2617             :     }
    2618             : 
    2619             : #ifndef RELEASE_OR_BETA
    2620          32 :     if (aTextPerf) {
    2621           0 :         aTextPerf->current.wordCacheMiss++;
    2622             :     }
    2623             : #endif
    2624             : 
    2625          32 :     sw = gfxShapedWord::Create(aText, aLength, aRunScript, aAppUnitsPerDevUnit,
    2626             :                                aFlags, aRounding);
    2627          32 :     entry->mShapedWord.reset(sw);
    2628          32 :     if (!sw) {
    2629           0 :         NS_WARNING("failed to create gfxShapedWord - expect missing text");
    2630           0 :         return nullptr;
    2631             :     }
    2632             : 
    2633             :     DebugOnly<bool> ok =
    2634             :         ShapeText(aDrawTarget, aText, 0, aLength, aRunScript, aVertical,
    2635          64 :                   aRounding, sw);
    2636             : 
    2637          32 :     NS_WARNING_ASSERTION(ok, "failed to shape word - expect garbled text");
    2638             : 
    2639          32 :     return sw;
    2640             : }
    2641             : 
    2642             : bool
    2643          95 : gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
    2644             : {
    2645          95 :     const gfxShapedWord* sw = mShapedWord.get();
    2646          95 :     if (!sw) {
    2647           0 :         return false;
    2648             :     }
    2649         285 :     if (sw->GetLength() != aKey->mLength ||
    2650         190 :         sw->GetFlags() != aKey->mFlags ||
    2651         190 :         sw->GetRounding() != aKey->mRounding ||
    2652         285 :         sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
    2653          95 :         sw->GetScript() != aKey->mScript) {
    2654           0 :         return false;
    2655             :     }
    2656          95 :     if (sw->TextIs8Bit()) {
    2657          88 :         if (aKey->mTextIs8Bit) {
    2658           8 :             return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
    2659          16 :                                 aKey->mLength * sizeof(uint8_t)));
    2660             :         }
    2661             :         // The key has 16-bit text, even though all the characters are < 256,
    2662             :         // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
    2663             :         // comparing with will have 8-bit text.
    2664          80 :         const uint8_t   *s1 = sw->Text8Bit();
    2665          80 :         const char16_t *s2 = aKey->mText.mDouble;
    2666          80 :         const char16_t *s2end = s2 + aKey->mLength;
    2667         938 :         while (s2 < s2end) {
    2668         429 :             if (*s1++ != *s2++) {
    2669           0 :                 return false;
    2670             :             }
    2671             :         }
    2672          80 :         return true;
    2673             :     }
    2674           7 :     NS_ASSERTION(!(aKey->mFlags & gfx::ShapedTextFlags::TEXT_IS_8BIT) &&
    2675             :                  !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
    2676           7 :     return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
    2677          14 :                         aKey->mLength * sizeof(char16_t)));
    2678             : }
    2679             : 
    2680             : bool
    2681          13 : gfxFont::ShapeText(DrawTarget    *aDrawTarget,
    2682             :                    const uint8_t *aText,
    2683             :                    uint32_t       aOffset,
    2684             :                    uint32_t       aLength,
    2685             :                    Script         aScript,
    2686             :                    bool           aVertical,
    2687             :                    RoundingFlags  aRounding,
    2688             :                    gfxShapedText *aShapedText)
    2689             : {
    2690          26 :     nsDependentCSubstring ascii((const char*)aText, aLength);
    2691          26 :     nsAutoString utf16;
    2692          13 :     AppendASCIItoUTF16(ascii, utf16);
    2693          13 :     if (utf16.Length() != aLength) {
    2694           0 :         return false;
    2695             :     }
    2696          13 :     return ShapeText(aDrawTarget, utf16.BeginReading(), aOffset, aLength,
    2697          26 :                      aScript, aVertical, aRounding, aShapedText);
    2698             : }
    2699             : 
    2700             : bool
    2701          34 : gfxFont::ShapeText(DrawTarget      *aDrawTarget,
    2702             :                    const char16_t *aText,
    2703             :                    uint32_t         aOffset,
    2704             :                    uint32_t         aLength,
    2705             :                    Script           aScript,
    2706             :                    bool             aVertical,
    2707             :                    RoundingFlags    aRounding,
    2708             :                    gfxShapedText   *aShapedText)
    2709             : {
    2710          34 :     bool ok = false;
    2711             : 
    2712             :     // XXX Currently, we do all vertical shaping through harfbuzz.
    2713             :     // Vertical graphite support may be wanted as a future enhancement.
    2714          34 :     if (FontCanSupportGraphite() && !aVertical) {
    2715           0 :         if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
    2716           0 :             if (!mGraphiteShaper) {
    2717           0 :                 mGraphiteShaper = MakeUnique<gfxGraphiteShaper>(this);
    2718           0 :                 Telemetry::ScalarAdd(Telemetry::ScalarID::BROWSER_USAGE_GRAPHITE, 1);
    2719             :             }
    2720           0 :             ok = mGraphiteShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
    2721             :                                             aScript, aVertical, aRounding,
    2722           0 :                                             aShapedText);
    2723             :         }
    2724             :     }
    2725             : 
    2726          34 :     if (!ok) {
    2727          34 :         if (!mHarfBuzzShaper) {
    2728           2 :             mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
    2729             :         }
    2730          68 :         ok = mHarfBuzzShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
    2731             :                                         aScript, aVertical, aRounding,
    2732          68 :                                         aShapedText);
    2733             :     }
    2734             : 
    2735          34 :     NS_WARNING_ASSERTION(ok, "shaper failed, expect scrambled or missing text");
    2736             : 
    2737          34 :     PostShapingFixup(aDrawTarget, aText, aOffset, aLength,
    2738          34 :                      aVertical, aShapedText);
    2739             : 
    2740          34 :     return ok;
    2741             : }
    2742             : 
    2743             : void
    2744          34 : gfxFont::PostShapingFixup(DrawTarget*     aDrawTarget,
    2745             :                           const char16_t* aText,
    2746             :                           uint32_t        aOffset,
    2747             :                           uint32_t        aLength,
    2748             :                           bool            aVertical,
    2749             :                           gfxShapedText*  aShapedText)
    2750             : {
    2751          34 :     if (IsSyntheticBold()) {
    2752             :         const Metrics& metrics =
    2753           0 :             GetMetrics(aVertical ? eVertical : eHorizontal);
    2754           0 :         if (metrics.maxAdvance > metrics.aveCharWidth) {
    2755             :             float synBoldOffset =
    2756           0 :                     GetSyntheticBoldOffset() * CalcXScale(aDrawTarget);
    2757             :             aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
    2758           0 :                                                         aOffset, aLength);
    2759             :         }
    2760             :     }
    2761          34 : }
    2762             : 
    2763             : #define MAX_SHAPING_LENGTH  32760 // slightly less than 32K, trying to avoid
    2764             :                                   // over-stressing platform shapers
    2765             : #define BACKTRACK_LIMIT     16 // backtrack this far looking for a good place
    2766             :                                // to split into fragments for separate shaping
    2767             : 
    2768             : template<typename T>
    2769             : bool
    2770           2 : gfxFont::ShapeFragmentWithoutWordCache(DrawTarget *aDrawTarget,
    2771             :                                        const T    *aText,
    2772             :                                        uint32_t    aOffset,
    2773             :                                        uint32_t    aLength,
    2774             :                                        Script      aScript,
    2775             :                                        bool        aVertical,
    2776             :                                        RoundingFlags aRounding,
    2777             :                                        gfxTextRun *aTextRun)
    2778             : {
    2779           2 :     aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
    2780             : 
    2781           2 :     bool ok = true;
    2782             : 
    2783           6 :     while (ok && aLength > 0) {
    2784           2 :         uint32_t fragLen = aLength;
    2785             : 
    2786             :         // limit the length of text we pass to shapers in a single call
    2787           2 :         if (fragLen > MAX_SHAPING_LENGTH) {
    2788           0 :             fragLen = MAX_SHAPING_LENGTH;
    2789             : 
    2790             :             // in the 8-bit case, there are no multi-char clusters,
    2791             :             // so we don't need to do this check
    2792             :             if (sizeof(T) == sizeof(char16_t)) {
    2793             :                 uint32_t i;
    2794           0 :                 for (i = 0; i < BACKTRACK_LIMIT; ++i) {
    2795           0 :                     if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
    2796           0 :                         fragLen -= i;
    2797           0 :                         break;
    2798             :                     }
    2799             :                 }
    2800           0 :                 if (i == BACKTRACK_LIMIT) {
    2801             :                     // if we didn't find any cluster start while backtracking,
    2802             :                     // just check that we're not in the middle of a surrogate
    2803             :                     // pair; back up by one code unit if we are.
    2804           0 :                     if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
    2805           0 :                         NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
    2806           0 :                         --fragLen;
    2807             :                     }
    2808             :                 }
    2809             :             }
    2810             :         }
    2811             : 
    2812           2 :         ok = ShapeText(aDrawTarget, aText, aOffset, fragLen, aScript,
    2813             :                        aVertical, aRounding, aTextRun);
    2814             : 
    2815           2 :         aText += fragLen;
    2816           2 :         aOffset += fragLen;
    2817           2 :         aLength -= fragLen;
    2818             :     }
    2819             : 
    2820           2 :     return ok;
    2821             : }
    2822             : 
    2823             : // Check if aCh is an unhandled control character that should be displayed
    2824             : // as a hexbox rather than rendered by some random font on the system.
    2825             : // We exclude \r as stray &#13;s are rather common (bug 941940).
    2826             : // Note that \n and \t don't come through here, as they have specific
    2827             : // meanings that have already been handled.
    2828             : static bool
    2829           0 : IsInvalidControlChar(uint32_t aCh)
    2830             : {
    2831           0 :     return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
    2832             : }
    2833             : 
    2834             : template<typename T>
    2835             : bool
    2836           0 : gfxFont::ShapeTextWithoutWordCache(DrawTarget *aDrawTarget,
    2837             :                                    const T    *aText,
    2838             :                                    uint32_t    aOffset,
    2839             :                                    uint32_t    aLength,
    2840             :                                    Script      aScript,
    2841             :                                    bool        aVertical,
    2842             :                                    RoundingFlags aRounding,
    2843             :                                    gfxTextRun *aTextRun)
    2844             : {
    2845           0 :     uint32_t fragStart = 0;
    2846           0 :     bool ok = true;
    2847             : 
    2848           0 :     for (uint32_t i = 0; i <= aLength && ok; ++i) {
    2849           0 :         T ch = (i < aLength) ? aText[i] : '\n';
    2850           0 :         bool invalid = gfxFontGroup::IsInvalidChar(ch);
    2851           0 :         uint32_t length = i - fragStart;
    2852             : 
    2853             :         // break into separate fragments when we hit an invalid char
    2854           0 :         if (!invalid) {
    2855           0 :             continue;
    2856             :         }
    2857             : 
    2858           0 :         if (length > 0) {
    2859           0 :             ok = ShapeFragmentWithoutWordCache(aDrawTarget, aText + fragStart,
    2860             :                                                aOffset + fragStart, length,
    2861             :                                                aScript, aVertical, aRounding,
    2862             :                                                aTextRun);
    2863             :         }
    2864             : 
    2865           0 :         if (i == aLength) {
    2866           0 :             break;
    2867             :         }
    2868             : 
    2869             :         // fragment was terminated by an invalid char: skip it,
    2870             :         // unless it's a control char that we want to show as a hexbox,
    2871             :         // but record where TAB or NEWLINE occur
    2872           0 :         if (ch == '\t') {
    2873           0 :             aTextRun->SetIsTab(aOffset + i);
    2874           0 :         } else if (ch == '\n') {
    2875           0 :             aTextRun->SetIsNewline(aOffset + i);
    2876           0 :         } else if (IsInvalidControlChar(ch) &&
    2877           0 :             !(aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS)) {
    2878           0 :             if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
    2879           0 :                 ShapeFragmentWithoutWordCache(aDrawTarget, aText + i,
    2880             :                                               aOffset + i, 1,
    2881             :                                               aScript, aVertical, aRounding,
    2882             :                                               aTextRun);
    2883             :             } else {
    2884           0 :                 aTextRun->SetMissingGlyph(aOffset + i, ch, this);
    2885             :             }
    2886             :         }
    2887           0 :         fragStart = i + 1;
    2888             :     }
    2889             : 
    2890           0 :     NS_WARNING_ASSERTION(ok, "failed to shape text - expect garbled text");
    2891           0 :     return ok;
    2892             : }
    2893             : 
    2894             : #ifndef RELEASE_OR_BETA
    2895             : #define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
    2896             : #else
    2897             : #define TEXT_PERF_INCR(tp, m)
    2898             : #endif
    2899             : 
    2900         190 : inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
    2901         612 : inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; }
    2902             : 
    2903           0 : inline static bool HasSpaces(const uint8_t *aString, uint32_t aLen)
    2904             : {
    2905           0 :     return memchr(aString, 0x20, aLen) != nullptr;
    2906             : }
    2907             : 
    2908           0 : inline static bool HasSpaces(const char16_t *aString, uint32_t aLen)
    2909             : {
    2910           0 :     for (const char16_t *ch = aString; ch < aString + aLen; ch++) {
    2911           0 :         if (*ch == 0x20) {
    2912           0 :             return true;
    2913             :         }
    2914             :     }
    2915           0 :     return false;
    2916             : }
    2917             : 
    2918             : template<typename T>
    2919             : bool
    2920          71 : gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
    2921             :                              gfxTextRun *aTextRun,
    2922             :                              const T *aString, // text for this font run
    2923             :                              uint32_t aRunStart, // position in the textrun
    2924             :                              uint32_t aRunLength,
    2925             :                              Script aRunScript,
    2926             :                              bool aVertical)
    2927             : {
    2928          71 :     if (aRunLength == 0) {
    2929           0 :         return true;
    2930             :     }
    2931             : 
    2932          71 :     gfxTextPerfMetrics *tp = nullptr;
    2933          71 :     RoundingFlags rounding = GetRoundOffsetsToPixels(aDrawTarget);
    2934             : 
    2935             : #ifndef RELEASE_OR_BETA
    2936          71 :     tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
    2937          71 :     if (tp) {
    2938           0 :         if (mStyle.systemFont) {
    2939           0 :             tp->current.numChromeTextRuns++;
    2940             :         } else {
    2941           0 :             tp->current.numContentTextRuns++;
    2942             :         }
    2943           0 :         tp->current.numChars += aRunLength;
    2944           0 :         if (aRunLength > tp->current.maxTextRunLen) {
    2945           0 :             tp->current.maxTextRunLen = aRunLength;
    2946             :         }
    2947             :     }
    2948             : #endif
    2949             : 
    2950             :     uint32_t wordCacheCharLimit =
    2951          71 :         gfxPlatform::GetPlatform()->WordCacheCharLimit();
    2952             : 
    2953             :     // If spaces can participate in shaping (e.g. within lookups for automatic
    2954             :     // fractions), need to shape without using the word cache which segments
    2955             :     // textruns on space boundaries. Word cache can be used if the textrun
    2956             :     // is short enough to fit in the word cache and it lacks spaces.
    2957          71 :     if (SpaceMayParticipateInShaping(aRunScript)) {
    2958           0 :         if (aRunLength > wordCacheCharLimit ||
    2959           0 :             HasSpaces(aString, aRunLength)) {
    2960           0 :             TEXT_PERF_INCR(tp, wordCacheSpaceRules);
    2961             :             return ShapeTextWithoutWordCache(aDrawTarget, aString,
    2962             :                                              aRunStart, aRunLength,
    2963             :                                              aRunScript, aVertical,
    2964           0 :                                              rounding, aTextRun);
    2965             :         }
    2966             :     }
    2967             : 
    2968          71 :     InitWordCache();
    2969             : 
    2970             :     // the only flags we care about for ShapedWord construction/caching
    2971          71 :     gfx::ShapedTextFlags flags = aTextRun->GetFlags();
    2972         213 :     flags &= (gfx::ShapedTextFlags::TEXT_IS_RTL |
    2973         284 :               gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES |
    2974         284 :               gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT |
    2975             :               gfx::ShapedTextFlags::TEXT_ORIENT_MASK);
    2976             :     if (sizeof(T) == sizeof(uint8_t)) {
    2977           9 :         flags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
    2978             :     }
    2979             : 
    2980          71 :     uint32_t wordStart = 0;
    2981          71 :     uint32_t hash = 0;
    2982          71 :     bool wordIs8Bit = true;
    2983          71 :     int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
    2984             : 
    2985          71 :     T nextCh = aString[0];
    2986         931 :     for (uint32_t i = 0; i <= aRunLength; ++i) {
    2987         931 :         T ch = nextCh;
    2988         931 :         nextCh = (i < aRunLength - 1) ? aString[i + 1] : '\n';
    2989         931 :         T boundary = IsBoundarySpace(ch, nextCh);
    2990         931 :         bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
    2991         931 :         uint32_t length = i - wordStart;
    2992             : 
    2993             :         // break into separate ShapedWords when we hit an invalid char,
    2994             :         // or a boundary space (always handled individually),
    2995             :         // or the first non-space after a space
    2996         931 :         if (!boundary && !invalid) {
    2997         802 :             if (!IsChar8Bit(ch)) {
    2998           9 :                 wordIs8Bit = false;
    2999             :             }
    3000             :             // include this character in the hash, and move on to next
    3001         802 :             hash = gfxShapedWord::HashMix(hash, ch);
    3002         802 :             continue;
    3003             :         }
    3004             : 
    3005             :         // We've decided to break here (i.e. we're at the end of a "word");
    3006             :         // shape the word and add it to the textrun.
    3007             :         // For words longer than the limit, we don't use the
    3008             :         // font's word cache but just shape directly into the textrun.
    3009         129 :         if (length > wordCacheCharLimit) {
    3010           2 :             TEXT_PERF_INCR(tp, wordCacheLong);
    3011             :             bool ok = ShapeFragmentWithoutWordCache(aDrawTarget,
    3012           0 :                                                     aString + wordStart,
    3013             :                                                     aRunStart + wordStart,
    3014             :                                                     length,
    3015             :                                                     aRunScript,
    3016             :                                                     aVertical,
    3017             :                                                     rounding,
    3018           2 :                                                     aTextRun);
    3019           2 :             if (!ok) {
    3020           0 :                 return false;
    3021             :             }
    3022         127 :         } else if (length > 0) {
    3023         127 :             gfx::ShapedTextFlags wordFlags = flags;
    3024             :             // in the 8-bit version of this method, TEXT_IS_8BIT was
    3025             :             // already set as part of |flags|, so no need for a per-word
    3026             :             // adjustment here
    3027             :             if (sizeof(T) == sizeof(char16_t)) {
    3028         108 :                 if (wordIs8Bit) {
    3029          99 :                     wordFlags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
    3030             :                 }
    3031             :             }
    3032             :             gfxShapedWord* sw = GetShapedWord(aDrawTarget,
    3033         108 :                                               aString + wordStart, length,
    3034             :                                               hash, aRunScript, aVertical,
    3035             :                                               appUnitsPerDevUnit,
    3036         235 :                                               wordFlags, rounding, tp);
    3037         127 :             if (sw) {
    3038         127 :                 aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
    3039             :             } else {
    3040           0 :                 return false; // failed, presumably out of memory?
    3041             :             }
    3042             :         }
    3043             : 
    3044         129 :         if (boundary) {
    3045             :             // word was terminated by a space: add that to the textrun
    3046             :             gfx::ShapedTextFlags orientation =
    3047          58 :                 flags & gfx::ShapedTextFlags::TEXT_ORIENT_MASK;
    3048          58 :             if (orientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
    3049           0 :                 orientation = aVertical ?
    3050             :                     gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT :
    3051             :                     gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
    3052             :             }
    3053         116 :             if (boundary != ' ' ||
    3054          58 :                 !aTextRun->SetSpaceGlyphIfSimple(this, aRunStart + i, ch,
    3055             :                                                  orientation)) {
    3056             :                 // Currently, the only "boundary" characters we recognize are
    3057             :                 // space and no-break space, which are both 8-bit, so we force
    3058             :                 // that flag (below). If we ever change IsBoundarySpace, we
    3059             :                 // may need to revise this.
    3060             :                 // Avoid tautological-constant-out-of-range-compare in 8-bit:
    3061           0 :                 DebugOnly<char16_t> boundary16 = boundary;
    3062           0 :                 NS_ASSERTION(boundary16 < 256, "unexpected boundary!");
    3063             :                 gfxShapedWord *sw =
    3064             :                     GetShapedWord(aDrawTarget, &boundary, 1,
    3065             :                                   gfxShapedWord::HashMix(0, boundary),
    3066             :                                   aRunScript, aVertical, appUnitsPerDevUnit,
    3067           0 :                                   flags | gfx::ShapedTextFlags::TEXT_IS_8BIT,
    3068           0 :                                   rounding, tp);
    3069           0 :                 if (sw) {
    3070           0 :                     aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
    3071             :                 } else {
    3072           0 :                     return false;
    3073             :                 }
    3074             :             }
    3075          58 :             hash = 0;
    3076          58 :             wordStart = i + 1;
    3077          58 :             wordIs8Bit = true;
    3078          58 :             continue;
    3079             :         }
    3080             : 
    3081          71 :         if (i == aRunLength) {
    3082          71 :             break;
    3083             :         }
    3084             : 
    3085           0 :         NS_ASSERTION(invalid,
    3086             :                      "how did we get here except via an invalid char?");
    3087             : 
    3088             :         // word was terminated by an invalid char: skip it,
    3089             :         // unless it's a control char that we want to show as a hexbox,
    3090             :         // but record where TAB or NEWLINE occur
    3091           0 :         if (ch == '\t') {
    3092           0 :             aTextRun->SetIsTab(aRunStart + i);
    3093           0 :         } else if (ch == '\n') {
    3094           0 :             aTextRun->SetIsNewline(aRunStart + i);
    3095           0 :         } else if (IsInvalidControlChar(ch) &&
    3096           0 :             !(aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS)) {
    3097           0 :             if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
    3098           0 :                 ShapeFragmentWithoutWordCache(aDrawTarget, aString + i,
    3099             :                                               aRunStart + i, 1,
    3100             :                                               aRunScript, aVertical,
    3101             :                                               rounding, aTextRun);
    3102             :             } else {
    3103           0 :                 aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
    3104             :             }
    3105             :         }
    3106             : 
    3107           0 :         hash = 0;
    3108           0 :         wordStart = i + 1;
    3109           0 :         wordIs8Bit = true;
    3110             :     }
    3111             : 
    3112          71 :     return true;
    3113             : }
    3114             : 
    3115             : // Explicit instantiations of SplitAndInitTextRun, to avoid libxul link failure
    3116             : template bool
    3117             : gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
    3118             :                              gfxTextRun *aTextRun,
    3119             :                              const uint8_t *aString,
    3120             :                              uint32_t aRunStart,
    3121             :                              uint32_t aRunLength,
    3122             :                              Script aRunScript,
    3123             :                              bool aVertical);
    3124             : template bool
    3125             : gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
    3126             :                              gfxTextRun *aTextRun,
    3127             :                              const char16_t *aString,
    3128             :                              uint32_t aRunStart,
    3129             :                              uint32_t aRunLength,
    3130             :                              Script aRunScript,
    3131             :                              bool aVertical);
    3132             : 
    3133             : template<>
    3134             : bool
    3135           0 : gfxFont::InitFakeSmallCapsRun(DrawTarget     *aDrawTarget,
    3136             :                               gfxTextRun     *aTextRun,
    3137             :                               const char16_t *aText,
    3138             :                               uint32_t        aOffset,
    3139             :                               uint32_t        aLength,
    3140             :                               uint8_t         aMatchType,
    3141             :                               gfx::ShapedTextFlags aOrientation,
    3142             :                               Script          aScript,
    3143             :                               bool            aSyntheticLower,
    3144             :                               bool            aSyntheticUpper)
    3145             : {
    3146           0 :     bool ok = true;
    3147             : 
    3148           0 :     RefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
    3149           0 :     if (!smallCapsFont) {
    3150           0 :         NS_WARNING("failed to get reduced-size font for smallcaps!");
    3151           0 :         smallCapsFont = this;
    3152             :     }
    3153             : 
    3154             :     enum RunCaseAction {
    3155             :         kNoChange,
    3156             :         kUppercaseReduce,
    3157             :         kUppercase
    3158             :     };
    3159             : 
    3160           0 :     RunCaseAction runAction = kNoChange;
    3161           0 :     uint32_t runStart = 0;
    3162             :     bool vertical =
    3163           0 :         aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
    3164             : 
    3165           0 :     for (uint32_t i = 0; i <= aLength; ++i) {
    3166           0 :         uint32_t extraCodeUnits = 0; // Will be set to 1 if we need to consume
    3167             :                                      // a trailing surrogate as well as the
    3168             :                                      // current code unit.
    3169           0 :         RunCaseAction chAction = kNoChange;
    3170             :         // Unless we're at the end, figure out what treatment the current
    3171             :         // character will need.
    3172           0 :         if (i < aLength) {
    3173           0 :             uint32_t ch = aText[i];
    3174           0 :             if (NS_IS_HIGH_SURROGATE(ch) && i < aLength - 1 &&
    3175           0 :                 NS_IS_LOW_SURROGATE(aText[i + 1])) {
    3176           0 :                 ch = SURROGATE_TO_UCS4(ch, aText[i + 1]);
    3177           0 :                 extraCodeUnits = 1;
    3178             :             }
    3179             :             // Characters that aren't the start of a cluster are ignored here.
    3180             :             // They get added to whatever lowercase/non-lowercase run we're in.
    3181           0 :             if (IsClusterExtender(ch)) {
    3182           0 :                 chAction = runAction;
    3183             :             } else {
    3184           0 :                 if (ch != ToUpperCase(ch) || SpecialUpper(ch)) {
    3185             :                     // ch is lower case
    3186           0 :                     chAction = (aSyntheticLower ? kUppercaseReduce : kNoChange);
    3187           0 :                 } else if (ch != ToLowerCase(ch)) {
    3188             :                     // ch is upper case
    3189           0 :                     chAction = (aSyntheticUpper ? kUppercaseReduce : kNoChange);
    3190           0 :                     if (mStyle.explicitLanguage &&
    3191           0 :                         mStyle.language == nsGkAtoms::el) {
    3192             :                         // In Greek, check for characters that will be modified by
    3193             :                         // the GreekUpperCase mapping - this catches accented
    3194             :                         // capitals where the accent is to be removed (bug 307039).
    3195             :                         // These are handled by using the full-size font with the
    3196             :                         // uppercasing transform.
    3197           0 :                         mozilla::GreekCasing::State state;
    3198             :                         bool markEta, updateEta;
    3199             :                         uint32_t ch2 =
    3200             :                             mozilla::GreekCasing::UpperCase(ch, state, markEta,
    3201           0 :                                                             updateEta);
    3202           0 :                         if ((ch != ch2 || markEta) && !aSyntheticUpper) {
    3203           0 :                             chAction = kUppercase;
    3204             :                         }
    3205             :                     }
    3206             :                 }
    3207             :             }
    3208             :         }
    3209             : 
    3210             :         // At the end of the text or when the current character needs different
    3211             :         // casing treatment from the current run, finish the run-in-progress
    3212             :         // and prepare to accumulate a new run.
    3213             :         // Note that we do not look at any source data for offset [i] here,
    3214             :         // as that would be invalid in the case where i==length.
    3215           0 :         if ((i == aLength || runAction != chAction) && runStart < i) {
    3216           0 :             uint32_t runLength = i - runStart;
    3217           0 :             gfxFont* f = this;
    3218           0 :             switch (runAction) {
    3219             :             case kNoChange:
    3220             :                 // just use the current font and the existing string
    3221           0 :                 aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true,
    3222           0 :                                       aOrientation);
    3223           0 :                 if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun,
    3224           0 :                                             aText + runStart,
    3225             :                                             aOffset + runStart, runLength,
    3226             :                                             aScript, vertical)) {
    3227           0 :                     ok = false;
    3228             :                 }
    3229           0 :                 break;
    3230             : 
    3231             :             case kUppercaseReduce:
    3232             :                 // use reduced-size font, then fall through to uppercase the text
    3233           0 :                 f = smallCapsFont;
    3234             :                 MOZ_FALLTHROUGH;
    3235             : 
    3236             :             case kUppercase:
    3237             :                 // apply uppercase transform to the string
    3238           0 :                 nsDependentSubstring origString(aText + runStart, runLength);
    3239           0 :                 nsAutoString convertedString;
    3240           0 :                 AutoTArray<bool,50> charsToMergeArray;
    3241           0 :                 AutoTArray<bool,50> deletedCharsArray;
    3242             : 
    3243             :                 bool mergeNeeded = nsCaseTransformTextRunFactory::
    3244           0 :                     TransformString(origString,
    3245             :                                     convertedString,
    3246             :                                     true,
    3247           0 :                                     mStyle.explicitLanguage
    3248           0 :                                       ? mStyle.language.get() : nullptr,
    3249             :                                     charsToMergeArray,
    3250           0 :                                     deletedCharsArray);
    3251             : 
    3252           0 :                 if (mergeNeeded) {
    3253             :                     // This is the hard case: the transformation caused chars
    3254             :                     // to be inserted or deleted, so we can't shape directly
    3255             :                     // into the destination textrun but have to handle the
    3256             :                     // mismatch of character positions.
    3257             :                     gfxTextRunFactory::Parameters params = {
    3258             :                         aDrawTarget, nullptr, nullptr, nullptr, 0,
    3259           0 :                         aTextRun->GetAppUnitsPerDevUnit()
    3260           0 :                     };
    3261             :                     RefPtr<gfxTextRun> tempRun(
    3262           0 :                         gfxTextRun::Create(&params, convertedString.Length(),
    3263             :                                            aTextRun->GetFontGroup(),
    3264             :                                            gfx::ShapedTextFlags(), 
    3265           0 :                                            nsTextFrameUtils::Flags()));
    3266           0 :                     tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation);
    3267           0 :                     if (!f->SplitAndInitTextRun(aDrawTarget, tempRun.get(),
    3268             :                                                 convertedString.BeginReading(),
    3269             :                                                 0, convertedString.Length(),
    3270             :                                                 aScript, vertical)) {
    3271           0 :                         ok = false;
    3272             :                     } else {
    3273             :                         RefPtr<gfxTextRun> mergedRun(
    3274           0 :                             gfxTextRun::Create(&params, runLength,
    3275             :                                                aTextRun->GetFontGroup(),
    3276             :                                                gfx::ShapedTextFlags(), 
    3277           0 :                                                nsTextFrameUtils::Flags()));
    3278           0 :                         MergeCharactersInTextRun(mergedRun.get(), tempRun.get(),
    3279           0 :                                                  charsToMergeArray.Elements(),
    3280           0 :                                                  deletedCharsArray.Elements());
    3281           0 :                         gfxTextRun::Range runRange(0, runLength);
    3282           0 :                         aTextRun->CopyGlyphDataFrom(mergedRun.get(), runRange,
    3283           0 :                                                     aOffset + runStart);
    3284             :                     }
    3285             :                 } else {
    3286           0 :                     aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart,
    3287           0 :                                           true, aOrientation);
    3288           0 :                     if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun,
    3289             :                                                 convertedString.BeginReading(),
    3290             :                                                 aOffset + runStart, runLength,
    3291             :                                                 aScript, vertical)) {
    3292           0 :                         ok = false;
    3293             :                     }
    3294             :                 }
    3295           0 :                 break;
    3296             :             }
    3297             : 
    3298           0 :             runStart = i;
    3299             :         }
    3300             : 
    3301           0 :         i += extraCodeUnits;
    3302           0 :         if (i < aLength) {
    3303           0 :             runAction = chAction;
    3304             :         }
    3305             :     }
    3306             : 
    3307           0 :     return ok;
    3308             : }
    3309             : 
    3310             : template<>
    3311             : bool
    3312           0 : gfxFont::InitFakeSmallCapsRun(DrawTarget     *aDrawTarget,
    3313             :                               gfxTextRun     *aTextRun,
    3314             :                               const uint8_t  *aText,
    3315             :                               uint32_t        aOffset,
    3316             :                               uint32_t        aLength,
    3317             :                               uint8_t         aMatchType,
    3318             :                               gfx::ShapedTextFlags aOrientation,
    3319             :                               Script          aScript,
    3320             :                               bool            aSyntheticLower,
    3321             :                               bool            aSyntheticUpper)
    3322             : {
    3323             :     NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
    3324           0 :                                          aLength);
    3325           0 :     return InitFakeSmallCapsRun(aDrawTarget, aTextRun, static_cast<const char16_t*>(unicodeString.get()),
    3326             :                                 aOffset, aLength, aMatchType, aOrientation,
    3327           0 :                                 aScript, aSyntheticLower, aSyntheticUpper);
    3328             : }
    3329             : 
    3330             : gfxFont*
    3331           0 : gfxFont::GetSmallCapsFont()
    3332             : {
    3333           0 :     gfxFontStyle style(*GetStyle());
    3334           0 :     style.size *= SMALL_CAPS_SCALE_FACTOR;
    3335           0 :     style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
    3336           0 :     gfxFontEntry* fe = GetFontEntry();
    3337           0 :     bool needsBold = style.weight >= 600 && !fe->IsBold();
    3338           0 :     return fe->FindOrMakeFont(&style, needsBold, mUnicodeRangeMap);
    3339             : }
    3340             : 
    3341             : gfxFont*
    3342           0 : gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
    3343             : {
    3344           0 :     gfxFontStyle style(*GetStyle());
    3345           0 :     style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
    3346           0 :     gfxFontEntry* fe = GetFontEntry();
    3347           0 :     bool needsBold = style.weight >= 600 && !fe->IsBold();
    3348           0 :     return fe->FindOrMakeFont(&style, needsBold, mUnicodeRangeMap);
    3349             : }
    3350             : 
    3351             : static void
    3352           0 : DestroyRefCairo(void* aData)
    3353             : {
    3354           0 :   cairo_t* refCairo = static_cast<cairo_t*>(aData);
    3355           0 :   MOZ_ASSERT(refCairo);
    3356           0 :   cairo_destroy(refCairo);
    3357           0 : }
    3358             : 
    3359             : /* static */ cairo_t *
    3360         236 : gfxFont::RefCairo(DrawTarget* aDT)
    3361             : {
    3362             :   // DrawTargets that don't use a Cairo backend can be given a 1x1 "reference"
    3363             :   // |cairo_t*|, stored in the DrawTarget's user data, for doing font-related
    3364             :   // operations.
    3365             :   static UserDataKey sRefCairo;
    3366             : 
    3367         236 :   cairo_t* refCairo = nullptr;
    3368         236 :   if (aDT->GetBackendType() == BackendType::CAIRO) {
    3369             :     refCairo = static_cast<cairo_t*>
    3370           0 :       (aDT->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
    3371           0 :     if (refCairo) {
    3372           0 :       return refCairo;
    3373             :     }
    3374             :   }
    3375             : 
    3376         236 :   refCairo = static_cast<cairo_t*>(aDT->GetUserData(&sRefCairo));
    3377         236 :   if (!refCairo) {
    3378           2 :     refCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface());
    3379           2 :     aDT->AddUserData(&sRefCairo, refCairo, DestroyRefCairo);
    3380             :   }
    3381             : 
    3382         236 :   return refCairo;
    3383             : }
    3384             : 
    3385             : gfxGlyphExtents *
    3386          35 : gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
    3387          35 :     uint32_t i, count = mGlyphExtentsArray.Length();
    3388          35 :     for (i = 0; i < count; ++i) {
    3389          33 :         if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
    3390          33 :             return mGlyphExtentsArray[i].get();
    3391             :     }
    3392           2 :     gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
    3393           2 :     if (glyphExtents) {
    3394           2 :         mGlyphExtentsArray.AppendElement(glyphExtents);
    3395             :         // Initialize the extents of a space glyph, assuming that spaces don't
    3396             :         // render anything!
    3397           2 :         glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
    3398             :     }
    3399           2 :     return glyphExtents;
    3400             : }
    3401             : 
    3402             : void
    3403          54 : gfxFont::SetupGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphID,
    3404             :                            bool aNeedTight, gfxGlyphExtents *aExtents)
    3405             : {
    3406          54 :     gfxRect svgBounds;
    3407          54 :     if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
    3408           0 :         mFontEntry->GetSVGGlyphExtents(aDrawTarget, aGlyphID, &svgBounds)) {
    3409           0 :         gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
    3410             :         aExtents->SetTightGlyphExtents(aGlyphID,
    3411           0 :                                        gfxRect(svgBounds.x * d2a,
    3412           0 :                                                svgBounds.y * d2a,
    3413           0 :                                                svgBounds.width * d2a,
    3414           0 :                                                svgBounds.height * d2a));
    3415          49 :         return;
    3416             :     }
    3417             : 
    3418             :     cairo_glyph_t glyph;
    3419          54 :     glyph.index = aGlyphID;
    3420          54 :     glyph.x = 0;
    3421          54 :     glyph.y = 0;
    3422             :     cairo_text_extents_t extents;
    3423          54 :     cairo_glyph_extents(gfxFont::RefCairo(aDrawTarget), &glyph, 1, &extents);
    3424             : 
    3425          54 :     const Metrics& fontMetrics = GetMetrics(eHorizontal);
    3426          54 :     int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
    3427         103 :     if (!aNeedTight && extents.x_bearing >= 0 &&
    3428          98 :         extents.y_bearing >= -fontMetrics.maxAscent &&
    3429          49 :         extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
    3430             :         uint32_t appUnitsWidth =
    3431          49 :             uint32_t(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
    3432          49 :         if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
    3433          49 :             aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, uint16_t(appUnitsWidth));
    3434          49 :             return;
    3435             :         }
    3436             :     }
    3437             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
    3438             :     if (!aNeedTight) {
    3439             :         ++gGlyphExtentsSetupFallBackToTight;
    3440             :     }
    3441             : #endif
    3442             : 
    3443           5 :     gfxFloat d2a = appUnitsPerDevUnit;
    3444          10 :     gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
    3445          15 :                    extents.width*d2a, extents.height*d2a);
    3446           5 :     aExtents->SetTightGlyphExtents(aGlyphID, bounds);
    3447             : }
    3448             : 
    3449             : // Try to initialize font metrics by reading sfnt tables directly;
    3450             : // set mIsValid=TRUE and return TRUE on success.
    3451             : // Return FALSE if the gfxFontEntry subclass does not
    3452             : // implement GetFontTable(), or for non-sfnt fonts where tables are
    3453             : // not available.
    3454             : // If this returns TRUE without setting the mIsValid flag, then we -did-
    3455             : // apparently find an sfnt, but it was too broken to be used.
    3456             : bool
    3457           0 : gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
    3458             : {
    3459           0 :     mIsValid = false; // font is NOT valid in case of early return
    3460             : 
    3461           0 :     const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
    3462           0 :     const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
    3463           0 :     const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
    3464             : 
    3465             :     uint32_t len;
    3466             : 
    3467           0 :     if (mFUnitsConvFactor < 0.0) {
    3468             :         // If the conversion factor from FUnits is not yet set,
    3469             :         // get the unitsPerEm from the 'head' table via the font entry
    3470           0 :         uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
    3471           0 :         if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
    3472           0 :             return false;
    3473             :         }
    3474           0 :         mFUnitsConvFactor = GetAdjustedSize() / unitsPerEm;
    3475             :     }
    3476             : 
    3477             :     // 'hhea' table is required to get vertical extents
    3478           0 :     gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
    3479           0 :     if (!hheaTable) {
    3480           0 :         return false; // no 'hhea' table -> not an sfnt
    3481             :     }
    3482             :     const MetricsHeader* hhea =
    3483             :         reinterpret_cast<const MetricsHeader*>
    3484           0 :             (hb_blob_get_data(hheaTable, &len));
    3485           0 :     if (len < sizeof(MetricsHeader)) {
    3486           0 :         return false;
    3487             :     }
    3488             : 
    3489             : #define SET_UNSIGNED(field,src) aMetrics.field = uint16_t(src) * mFUnitsConvFactor
    3490             : #define SET_SIGNED(field,src)   aMetrics.field = int16_t(src) * mFUnitsConvFactor
    3491             : 
    3492           0 :     SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
    3493           0 :     SET_SIGNED(maxAscent, hhea->ascender);
    3494           0 :     SET_SIGNED(maxDescent, -int16_t(hhea->descender));
    3495           0 :     SET_SIGNED(externalLeading, hhea->lineGap);
    3496             : 
    3497             :     // 'post' table is required for underline metrics
    3498           0 :     gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
    3499           0 :     if (!postTable) {
    3500           0 :         return true; // no 'post' table -> sfnt is not valid
    3501             :     }
    3502             :     const PostTable *post =
    3503           0 :         reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len));
    3504           0 :     if (len < offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) {
    3505           0 :         return true; // bad post table -> sfnt is not valid
    3506             :     }
    3507             : 
    3508           0 :     SET_SIGNED(underlineOffset, post->underlinePosition);
    3509           0 :     SET_UNSIGNED(underlineSize, post->underlineThickness);
    3510             : 
    3511             :     // 'OS/2' table is optional, if not found we'll estimate xHeight
    3512             :     // and aveCharWidth by measuring glyphs
    3513           0 :     gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
    3514           0 :     if (os2Table) {
    3515             :         const OS2Table *os2 =
    3516           0 :             reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
    3517             :         // although sxHeight and sCapHeight are signed fields, we consider
    3518             :         // negative values to be erroneous and just ignore them
    3519           0 :         if (uint16_t(os2->version) >= 2) {
    3520             :             // version 2 and later includes the x-height and cap-height fields
    3521           0 :             if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
    3522           0 :                 int16_t(os2->sxHeight) > 0) {
    3523           0 :                 SET_SIGNED(xHeight, os2->sxHeight);
    3524             :             }
    3525           0 :             if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
    3526           0 :                 int16_t(os2->sCapHeight) > 0) {
    3527           0 :                 SET_SIGNED(capHeight, os2->sCapHeight);
    3528             :             }
    3529             :         }
    3530             :         // this should always be present in any valid OS/2 of any version
    3531           0 :         if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
    3532           0 :             SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
    3533           0 :             SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
    3534           0 :             SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
    3535             : 
    3536             :             // for fonts with USE_TYPO_METRICS set in the fsSelection field,
    3537             :             // let the OS/2 sTypo* metrics override those from the hhea table
    3538             :             // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
    3539           0 :             const uint16_t kUseTypoMetricsMask = 1 << 7;
    3540           0 :             if (uint16_t(os2->fsSelection) & kUseTypoMetricsMask) {
    3541           0 :                 SET_SIGNED(maxAscent, os2->sTypoAscender);
    3542           0 :                 SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
    3543           0 :                 SET_SIGNED(externalLeading, os2->sTypoLineGap);
    3544             :             }
    3545             :         }
    3546             :     }
    3547             : 
    3548             : #undef SET_SIGNED
    3549             : #undef SET_UNSIGNED
    3550             : 
    3551           0 :     mIsValid = true;
    3552             : 
    3553           0 :     return true;
    3554             : }
    3555             : 
    3556             : static double
    3557           0 : RoundToNearestMultiple(double aValue, double aFraction)
    3558             : {
    3559           0 :     return floor(aValue/aFraction + 0.5) * aFraction;
    3560             : }
    3561             : 
    3562           0 : void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics)
    3563             : {
    3564           0 :     aMetrics.maxAscent =
    3565           0 :         ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
    3566           0 :     aMetrics.maxDescent =
    3567           0 :         ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
    3568             : 
    3569           0 :     if (aMetrics.xHeight <= 0) {
    3570             :         // only happens if we couldn't find either font metrics
    3571             :         // or a char to measure;
    3572             :         // pick an arbitrary value that's better than zero
    3573           0 :         aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
    3574             :     }
    3575             : 
    3576             :     // If we have a font that doesn't provide a capHeight value, use maxAscent
    3577             :     // as a reasonable fallback.
    3578           0 :     if (aMetrics.capHeight <= 0) {
    3579           0 :         aMetrics.capHeight = aMetrics.maxAscent;
    3580             :     }
    3581             : 
    3582           0 :     aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
    3583             : 
    3584           0 :     if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) {
    3585           0 :         aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight;
    3586             :     } else {
    3587           0 :         aMetrics.internalLeading = 0.0;
    3588             :     }
    3589             : 
    3590           0 :     aMetrics.emAscent = aMetrics.maxAscent * aMetrics.emHeight
    3591           0 :                             / aMetrics.maxHeight;
    3592           0 :     aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
    3593             : 
    3594           0 :     if (GetFontEntry()->IsFixedPitch()) {
    3595             :         // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
    3596             :         // advance than the average character width... this forces
    3597             :         // those fonts to be recognized like fixed pitch fonts by layout.
    3598           0 :         aMetrics.maxAdvance = aMetrics.aveCharWidth;
    3599             :     }
    3600             : 
    3601           0 :     if (!aMetrics.strikeoutOffset) {
    3602           0 :         aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
    3603             :     }
    3604           0 :     if (!aMetrics.strikeoutSize) {
    3605           0 :         aMetrics.strikeoutSize = aMetrics.underlineSize;
    3606             :     }
    3607           0 : }
    3608             : 
    3609             : void
    3610           5 : gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont)
    3611             : {
    3612             :     // Even if this font size is zero, this font is created with non-zero size.
    3613             :     // However, for layout and others, we should return the metrics of zero size font.
    3614           5 :     if (mStyle.size == 0.0 || mStyle.sizeAdjust == 0.0) {
    3615           0 :         memset(aMetrics, 0, sizeof(gfxFont::Metrics));
    3616           0 :         return;
    3617             :     }
    3618             : 
    3619           5 :     aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize);
    3620           5 :     aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize);
    3621             : 
    3622           5 :     aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0);
    3623             : 
    3624           5 :     if (aMetrics->maxAscent < 1.0) {
    3625             :         // We cannot draw strikeout line and overline in the ascent...
    3626           0 :         aMetrics->underlineSize = 0;
    3627           0 :         aMetrics->underlineOffset = 0;
    3628           0 :         aMetrics->strikeoutSize = 0;
    3629           0 :         aMetrics->strikeoutOffset = 0;
    3630           0 :         return;
    3631             :     }
    3632             : 
    3633             :     /**
    3634             :      * Some CJK fonts have bad underline offset. Therefore, if this is such font,
    3635             :      * we need to lower the underline offset to bottom of *em* descent.
    3636             :      * However, if this is system font, we should not do this for the rendering compatibility with
    3637             :      * another application's UI on the platform.
    3638             :      * XXX Should not use this hack if the font size is too small?
    3639             :      *     Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2)
    3640             :      */
    3641           5 :     if (!mStyle.systemFont && aIsBadUnderlineFont) {
    3642             :         // First, we need 2 pixels between baseline and underline at least. Because many CJK characters
    3643             :         // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
    3644           0 :         aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0);
    3645             : 
    3646             :         // Next, we put the underline to bottom of below of the descent space.
    3647           0 :         if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
    3648           0 :             aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -aMetrics->emDescent);
    3649             :         } else {
    3650           0 :             aMetrics->underlineOffset = std::min(aMetrics->underlineOffset,
    3651           0 :                                                aMetrics->underlineSize - aMetrics->emDescent);
    3652             :         }
    3653             :     }
    3654             :     // If underline positioned is too far from the text, descent position is preferred so that underline
    3655             :     // will stay within the boundary.
    3656           5 :     else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
    3657           1 :         if (aMetrics->underlineSize > aMetrics->maxDescent)
    3658           0 :             aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0);
    3659             :         // The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.)
    3660           1 :         aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
    3661             :     }
    3662             : 
    3663             :     // If strikeout line is overflowed from the ascent, the line should be resized and moved for
    3664             :     // that being in the ascent space.
    3665             :     // Note that the strikeoutOffset is *middle* of the strikeout line position.
    3666           5 :     gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
    3667           5 :     if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
    3668           0 :         if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
    3669           0 :             aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0);
    3670           0 :             halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
    3671             :         }
    3672           0 :         gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
    3673           0 :         aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0);
    3674             :     }
    3675             : 
    3676             :     // If overline is larger than the ascent, the line should be resized.
    3677           5 :     if (aMetrics->underlineSize > aMetrics->maxAscent) {
    3678           0 :         aMetrics->underlineSize = aMetrics->maxAscent;
    3679             :     }
    3680             : }
    3681             : 
    3682             : // Create a Metrics record to be used for vertical layout. This should never
    3683             : // fail, as we've already decided this is a valid font. We do not have the
    3684             : // option of marking it invalid (as can happen if we're unable to read
    3685             : // horizontal metrics), because that could break a font that we're already
    3686             : // using for horizontal text.
    3687             : // So we will synthesize *something* usable here even if there aren't any of the
    3688             : // usual font tables (which can happen in the case of a legacy bitmap or Type1
    3689             : // font for which the platform-specific backend used platform APIs instead of
    3690             : // sfnt tables to create the horizontal metrics).
    3691             : UniquePtr<const gfxFont::Metrics>
    3692           0 : gfxFont::CreateVerticalMetrics()
    3693             : {
    3694           0 :     const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
    3695           0 :     const uint32_t kVheaTableTag = TRUETYPE_TAG('v','h','e','a');
    3696           0 :     const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
    3697           0 :     const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
    3698             :     uint32_t len;
    3699             : 
    3700           0 :     UniquePtr<Metrics> metrics = MakeUnique<Metrics>();
    3701           0 :     ::memset(metrics.get(), 0, sizeof(Metrics));
    3702             : 
    3703             :     // Some basic defaults, in case the font lacks any real metrics tables.
    3704             :     // TODO: consider what rounding (if any) we should apply to these.
    3705           0 :     metrics->emHeight = GetAdjustedSize();
    3706           0 :     metrics->emAscent = metrics->emHeight / 2;
    3707           0 :     metrics->emDescent = metrics->emHeight - metrics->emAscent;
    3708             : 
    3709           0 :     metrics->maxAscent = metrics->emAscent;
    3710           0 :     metrics->maxDescent = metrics->emDescent;
    3711             : 
    3712           0 :     const float UNINITIALIZED_LEADING = -10000.0f;
    3713           0 :     metrics->externalLeading = UNINITIALIZED_LEADING;
    3714             : 
    3715           0 :     if (mFUnitsConvFactor < 0.0) {
    3716           0 :         uint16_t upem = GetFontEntry()->UnitsPerEm();
    3717           0 :         if (upem != gfxFontEntry::kInvalidUPEM) {
    3718           0 :             mFUnitsConvFactor = GetAdjustedSize() / upem;
    3719             :         }
    3720             :     }
    3721             : 
    3722             : #define SET_UNSIGNED(field,src) metrics->field = uint16_t(src) * mFUnitsConvFactor
    3723             : #define SET_SIGNED(field,src)   metrics->field = int16_t(src) * mFUnitsConvFactor
    3724             : 
    3725           0 :     gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
    3726           0 :     if (os2Table && mFUnitsConvFactor >= 0.0) {
    3727             :         const OS2Table *os2 =
    3728           0 :             reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
    3729             :         // These fields should always be present in any valid OS/2 table
    3730           0 :         if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
    3731           0 :             SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
    3732             :             // Use ascent+descent from the horizontal metrics as the default
    3733             :             // advance (aveCharWidth) in vertical mode
    3734           0 :             gfxFloat ascentDescent = gfxFloat(mFUnitsConvFactor) *
    3735           0 :                 (int16_t(os2->sTypoAscender) - int16_t(os2->sTypoDescender));
    3736           0 :             metrics->aveCharWidth =
    3737           0 :                 std::max(metrics->emHeight, ascentDescent);
    3738             :             // Use xAvgCharWidth from horizontal metrics as minimum font extent
    3739             :             // for vertical layout, applying half of it to ascent and half to
    3740             :             // descent (to work with a default centered baseline).
    3741             :             gfxFloat halfCharWidth =
    3742           0 :                 int16_t(os2->xAvgCharWidth) * gfxFloat(mFUnitsConvFactor) / 2;
    3743           0 :             metrics->maxAscent = std::max(metrics->maxAscent, halfCharWidth);
    3744           0 :             metrics->maxDescent = std::max(metrics->maxDescent, halfCharWidth);
    3745             :         }
    3746             :     }
    3747             : 
    3748             :     // If we didn't set aveCharWidth from OS/2, try to read 'hhea' metrics
    3749             :     // and use the line height from its ascent/descent.
    3750           0 :     if (!metrics->aveCharWidth) {
    3751           0 :         gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
    3752           0 :         if (hheaTable && mFUnitsConvFactor >= 0.0) {
    3753             :             const MetricsHeader* hhea =
    3754             :                 reinterpret_cast<const MetricsHeader*>
    3755           0 :                     (hb_blob_get_data(hheaTable, &len));
    3756           0 :             if (len >= sizeof(MetricsHeader)) {
    3757           0 :                 SET_SIGNED(aveCharWidth, int16_t(hhea->ascender) -
    3758             :                                          int16_t(hhea->descender));
    3759           0 :                 metrics->maxAscent = metrics->aveCharWidth / 2;
    3760           0 :                 metrics->maxDescent =
    3761           0 :                     metrics->aveCharWidth - metrics->maxAscent;
    3762             :             }
    3763             :         }
    3764             :     }
    3765             : 
    3766             :     // Read real vertical metrics if available.
    3767           0 :     gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag);
    3768           0 :     if (vheaTable && mFUnitsConvFactor >= 0.0) {
    3769             :         const MetricsHeader* vhea =
    3770             :             reinterpret_cast<const MetricsHeader*>
    3771           0 :                 (hb_blob_get_data(vheaTable, &len));
    3772           0 :         if (len >= sizeof(MetricsHeader)) {
    3773           0 :             SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
    3774             :             // Redistribute space between ascent/descent because we want a
    3775             :             // centered vertical baseline by default.
    3776           0 :             gfxFloat halfExtent = 0.5 * gfxFloat(mFUnitsConvFactor) *
    3777           0 :                 (int16_t(vhea->ascender) + std::abs(int16_t(vhea->descender)));
    3778             :             // Some bogus fonts have ascent and descent set to zero in 'vhea'.
    3779             :             // In that case we just ignore them and keep our synthetic values
    3780             :             // from above.
    3781           0 :             if (halfExtent > 0) {
    3782           0 :                 metrics->maxAscent = halfExtent;
    3783           0 :                 metrics->maxDescent = halfExtent;
    3784           0 :                 SET_SIGNED(externalLeading, vhea->lineGap);
    3785             :             }
    3786             :         }
    3787             :     }
    3788             : 
    3789             :     // If we didn't set aveCharWidth above, we must be dealing with a non-sfnt
    3790             :     // font of some kind (Type1, bitmap, vector, ...), so fall back to using
    3791             :     // whatever the platform backend figured out for horizontal layout.
    3792             :     // And if we haven't set externalLeading yet, then copy that from the
    3793             :     // horizontal metrics as well, to help consistency of CSS line-height.
    3794           0 :     if (!metrics->aveCharWidth ||
    3795           0 :         metrics->externalLeading == UNINITIALIZED_LEADING) {
    3796           0 :         const Metrics& horizMetrics = GetHorizontalMetrics();
    3797           0 :         if (!metrics->aveCharWidth) {
    3798           0 :             metrics->aveCharWidth = horizMetrics.maxAscent + horizMetrics.maxDescent;
    3799             :         }
    3800           0 :         if (metrics->externalLeading == UNINITIALIZED_LEADING) {
    3801           0 :             metrics->externalLeading = horizMetrics.externalLeading;
    3802             :         }
    3803             :     }
    3804             : 
    3805             :     // Get underline thickness from the 'post' table if available.
    3806           0 :     gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
    3807           0 :     if (postTable) {
    3808             :         const PostTable *post =
    3809           0 :             reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable,
    3810           0 :                                                                 &len));
    3811           0 :         if (len >= offsetof(PostTable, underlineThickness) +
    3812             :                        sizeof(uint16_t)) {
    3813           0 :             SET_UNSIGNED(underlineSize, post->underlineThickness);
    3814             :             // Also use for strikeout if we didn't find that in OS/2 above.
    3815           0 :             if (!metrics->strikeoutSize) {
    3816           0 :                 metrics->strikeoutSize = metrics->underlineSize;
    3817             :             }
    3818             :         }
    3819             :     }
    3820             : 
    3821             : #undef SET_UNSIGNED
    3822             : #undef SET_SIGNED
    3823             : 
    3824             :     // If we didn't read this from a vhea table, it will still be zero.
    3825             :     // In any case, let's make sure it is not less than the value we've
    3826             :     // come up with for aveCharWidth.
    3827           0 :     metrics->maxAdvance = std::max(metrics->maxAdvance, metrics->aveCharWidth);
    3828             : 
    3829             :     // Thickness of underline and strikeout may have been read from tables,
    3830             :     // but in case they were not present, ensure a minimum of 1 pixel.
    3831             :     // We synthesize our own positions, as font metrics don't provide these
    3832             :     // for vertical layout.
    3833           0 :     metrics->underlineSize = std::max(1.0, metrics->underlineSize);
    3834           0 :     metrics->underlineOffset = - metrics->maxDescent - metrics->underlineSize;
    3835             : 
    3836           0 :     metrics->strikeoutSize = std::max(1.0, metrics->strikeoutSize);
    3837           0 :     metrics->strikeoutOffset = - 0.5 * metrics->strikeoutSize;
    3838             : 
    3839             :     // Somewhat arbitrary values for now, subject to future refinement...
    3840           0 :     metrics->spaceWidth = metrics->aveCharWidth;
    3841           0 :     metrics->zeroOrAveCharWidth = metrics->aveCharWidth;
    3842           0 :     metrics->maxHeight = metrics->maxAscent + metrics->maxDescent;
    3843           0 :     metrics->xHeight = metrics->emHeight / 2;
    3844           0 :     metrics->capHeight = metrics->maxAscent;
    3845             : 
    3846           0 :     return Move(metrics);
    3847             : }
    3848             : 
    3849             : gfxFloat
    3850           0 : gfxFont::SynthesizeSpaceWidth(uint32_t aCh)
    3851             : {
    3852             :     // return an appropriate width for various Unicode space characters
    3853             :     // that we "fake" if they're not actually present in the font;
    3854             :     // returns negative value if the char is not a known space.
    3855           0 :     switch (aCh) {
    3856             :     case 0x2000:                                 // en quad
    3857           0 :     case 0x2002: return GetAdjustedSize() / 2;   // en space
    3858             :     case 0x2001:                                 // em quad
    3859           0 :     case 0x2003: return GetAdjustedSize();       // em space
    3860           0 :     case 0x2004: return GetAdjustedSize() / 3;   // three-per-em space
    3861           0 :     case 0x2005: return GetAdjustedSize() / 4;   // four-per-em space
    3862           0 :     case 0x2006: return GetAdjustedSize() / 6;   // six-per-em space
    3863           0 :     case 0x2007: return GetMetrics(eHorizontal).zeroOrAveCharWidth; // figure space
    3864           0 :     case 0x2008: return GetMetrics(eHorizontal).spaceWidth; // punctuation space
    3865           0 :     case 0x2009: return GetAdjustedSize() / 5;   // thin space
    3866           0 :     case 0x200a: return GetAdjustedSize() / 10;  // hair space
    3867           0 :     case 0x202f: return GetAdjustedSize() / 5;   // narrow no-break space
    3868           0 :     default: return -1.0;
    3869             :     }
    3870             : }
    3871             : 
    3872             : void
    3873           0 : gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
    3874             :                                 FontCacheSizes* aSizes) const
    3875             : {
    3876           0 :     for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
    3877           0 :         aSizes->mFontInstances +=
    3878           0 :             mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
    3879             :     }
    3880           0 :     if (mWordCache) {
    3881           0 :         aSizes->mShapedWords += mWordCache->SizeOfIncludingThis(aMallocSizeOf);
    3882             :     }
    3883           0 : }
    3884             : 
    3885             : void
    3886           0 : gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
    3887             :                                 FontCacheSizes* aSizes) const
    3888             : {
    3889           0 :     aSizes->mFontInstances += aMallocSizeOf(this);
    3890           0 :     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    3891           0 : }
    3892             : 
    3893             : void
    3894           0 : gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver)
    3895             : {
    3896           0 :     if (!mGlyphChangeObservers) {
    3897             :         mGlyphChangeObservers =
    3898           0 :             MakeUnique<nsTHashtable<nsPtrHashKey<GlyphChangeObserver>>>();
    3899             :     }
    3900           0 :     mGlyphChangeObservers->PutEntry(aObserver);
    3901           0 : }
    3902             : 
    3903             : void
    3904           0 : gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver)
    3905             : {
    3906           0 :     NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
    3907           0 :     NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered");
    3908           0 :     mGlyphChangeObservers->RemoveEntry(aObserver);
    3909           0 : }
    3910             : 
    3911             : #define DEFAULT_PIXEL_FONT_SIZE 16.0f
    3912             : 
    3913          17 : gfxFontStyle::gfxFontStyle() :
    3914             :     language(nsGkAtoms::x_western),
    3915             :     size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(-1.0f), baselineOffset(0.0f),
    3916             :     languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
    3917             :     weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL),
    3918             :     style(NS_FONT_STYLE_NORMAL),
    3919             :     variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
    3920             :     variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
    3921             :     systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
    3922             :     allowSyntheticWeight(true), allowSyntheticStyle(true),
    3923             :     noFallbackVariantFeatures(true),
    3924          17 :     explicitLanguage(false)
    3925             : {
    3926          17 : }
    3927             : 
    3928           6 : gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
    3929             :                            gfxFloat aSize,
    3930             :                            nsIAtom *aLanguage, bool aExplicitLanguage,
    3931             :                            float aSizeAdjust, bool aSystemFont,
    3932             :                            bool aPrinterFont,
    3933             :                            bool aAllowWeightSynthesis,
    3934             :                            bool aAllowStyleSynthesis,
    3935           6 :                            uint32_t aLanguageOverride):
    3936             :     language(aLanguage),
    3937             :     size(aSize), sizeAdjust(aSizeAdjust), baselineOffset(0.0f),
    3938             :     languageOverride(aLanguageOverride),
    3939             :     weight(aWeight), stretch(aStretch),
    3940             :     style(aStyle),
    3941             :     variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
    3942             :     variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
    3943             :     systemFont(aSystemFont), printerFont(aPrinterFont),
    3944             :     useGrayscaleAntialiasing(false),
    3945             :     allowSyntheticWeight(aAllowWeightSynthesis),
    3946             :     allowSyntheticStyle(aAllowStyleSynthesis),
    3947             :     noFallbackVariantFeatures(true),
    3948           6 :     explicitLanguage(aExplicitLanguage)
    3949             : {
    3950           6 :     MOZ_ASSERT(!mozilla::IsNaN(size));
    3951           6 :     MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
    3952             : 
    3953           6 :     if (weight > 900)
    3954           0 :         weight = 900;
    3955           6 :     if (weight < 100)
    3956           0 :         weight = 100;
    3957             : 
    3958           6 :     if (size >= FONT_MAX_SIZE) {
    3959           0 :         size = FONT_MAX_SIZE;
    3960           0 :         sizeAdjust = -1.0f;
    3961           6 :     } else if (size < 0.0) {
    3962           0 :         NS_WARNING("negative font size");
    3963           0 :         size = 0.0;
    3964             :     }
    3965             : 
    3966           6 :     if (!language) {
    3967           0 :         NS_WARNING("null language");
    3968           0 :         language = nsGkAtoms::x_western;
    3969             :     }
    3970           6 : }
    3971             : 
    3972             : int8_t
    3973          19 : gfxFontStyle::ComputeWeight() const
    3974             : {
    3975          19 :     int8_t baseWeight = (weight + 50) / 100;
    3976             : 
    3977          19 :     if (baseWeight < 0)
    3978           0 :         baseWeight = 0;
    3979          19 :     if (baseWeight > 9)
    3980           0 :         baseWeight = 9;
    3981             : 
    3982          19 :     return baseWeight;
    3983             : }
    3984             : 
    3985             : void
    3986           0 : gfxFontStyle::AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel)
    3987             : {
    3988           0 :     NS_PRECONDITION(variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
    3989             :                     baselineOffset == 0,
    3990             :                     "can't adjust this style for sub/superscript");
    3991             : 
    3992             :     // calculate the baseline offset (before changing the size)
    3993           0 :     if (variantSubSuper == NS_FONT_VARIANT_POSITION_SUPER) {
    3994           0 :         baselineOffset = size * -NS_FONT_SUPERSCRIPT_OFFSET_RATIO;
    3995             :     } else {
    3996           0 :         baselineOffset = size * NS_FONT_SUBSCRIPT_OFFSET_RATIO;
    3997             :     }
    3998             : 
    3999             :     // calculate reduced size, roughly mimicing behavior of font-size: smaller
    4000           0 :     float cssSize = size * aAppUnitsPerDevPixel / AppUnitsPerCSSPixel();
    4001           0 :     if (cssSize < NS_FONT_SUB_SUPER_SMALL_SIZE) {
    4002           0 :         size *= NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL;
    4003           0 :     } else if (cssSize >= NS_FONT_SUB_SUPER_LARGE_SIZE) {
    4004           0 :         size *= NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
    4005             :     } else {
    4006           0 :         gfxFloat t = (cssSize - NS_FONT_SUB_SUPER_SMALL_SIZE) /
    4007             :                          (NS_FONT_SUB_SUPER_LARGE_SIZE -
    4008           0 :                           NS_FONT_SUB_SUPER_SMALL_SIZE);
    4009           0 :         size *= (1.0 - t) * NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL +
    4010           0 :                     t * NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
    4011             :     }
    4012             : 
    4013             :     // clear the variant field
    4014           0 :     variantSubSuper = NS_FONT_VARIANT_POSITION_NORMAL;
    4015           0 : }
    4016             : 
    4017             : bool
    4018           0 : gfxFont::TryGetMathTable()
    4019             : {
    4020           0 :     if (!mMathInitialized) {
    4021           0 :         mMathInitialized = true;
    4022             : 
    4023           0 :         hb_face_t *face = GetFontEntry()->GetHBFace();
    4024           0 :         if (face) {
    4025           0 :             if (hb_ot_math_has_data(face)) {
    4026           0 :                 mMathTable = MakeUnique<gfxMathTable>(face, GetAdjustedSize());
    4027             :             }
    4028           0 :             hb_face_destroy(face);
    4029             :         }
    4030             :     }
    4031             : 
    4032           0 :     return !!mMathTable;
    4033             : }

Generated by: LCOV version 1.13