gfx/thebes/gfxFont.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/thebes/gfxFont.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,7446 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/DebugOnly.h"
    1.10 +#include "mozilla/MathAlgorithms.h"
    1.11 +
    1.12 +#ifdef MOZ_LOGGING
    1.13 +#define FORCE_PR_LOG /* Allow logging in the release build */
    1.14 +#endif
    1.15 +#include "prlog.h"
    1.16 +
    1.17 +#include "nsServiceManagerUtils.h"
    1.18 +#include "nsExpirationTracker.h"
    1.19 +#include "nsILanguageAtomService.h"
    1.20 +#include "nsITimer.h"
    1.21 +
    1.22 +#include "gfxFont.h"
    1.23 +#include "gfxPlatform.h"
    1.24 +#include "nsGkAtoms.h"
    1.25 +
    1.26 +#include "gfxTypes.h"
    1.27 +#include "gfxContext.h"
    1.28 +#include "gfxFontMissingGlyphs.h"
    1.29 +#include "gfxHarfBuzzShaper.h"
    1.30 +#include "gfxUserFontSet.h"
    1.31 +#include "gfxPlatformFontList.h"
    1.32 +#include "gfxScriptItemizer.h"
    1.33 +#include "nsUnicodeProperties.h"
    1.34 +#include "nsMathUtils.h"
    1.35 +#include "nsBidiUtils.h"
    1.36 +#include "nsUnicodeRange.h"
    1.37 +#include "nsStyleConsts.h"
    1.38 +#include "mozilla/FloatingPoint.h"
    1.39 +#include "mozilla/Likely.h"
    1.40 +#include "mozilla/MemoryReporting.h"
    1.41 +#include "mozilla/Preferences.h"
    1.42 +#include "mozilla/Services.h"
    1.43 +#include "mozilla/Telemetry.h"
    1.44 +#include "gfxSVGGlyphs.h"
    1.45 +#include "gfxMathTable.h"
    1.46 +#include "gfx2DGlue.h"
    1.47 +
    1.48 +#if defined(XP_MACOSX)
    1.49 +#include "nsCocoaFeatures.h"
    1.50 +#endif
    1.51 +
    1.52 +#include "cairo.h"
    1.53 +#include "gfxFontTest.h"
    1.54 +
    1.55 +#include "harfbuzz/hb.h"
    1.56 +#include "harfbuzz/hb-ot.h"
    1.57 +#include "graphite2/Font.h"
    1.58 +
    1.59 +#include "nsCRT.h"
    1.60 +#include "GeckoProfiler.h"
    1.61 +#include "gfxFontConstants.h"
    1.62 +
    1.63 +#include <algorithm>
    1.64 +
    1.65 +using namespace mozilla;
    1.66 +using namespace mozilla::gfx;
    1.67 +using namespace mozilla::unicode;
    1.68 +using mozilla::services::GetObserverService;
    1.69 +
    1.70 +gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
    1.71 +
    1.72 +static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
    1.73 +static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
    1.74 +
    1.75 +#ifdef DEBUG_roc
    1.76 +#define DEBUG_TEXT_RUN_STORAGE_METRICS
    1.77 +#endif
    1.78 +
    1.79 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
    1.80 +static uint32_t gTextRunStorageHighWaterMark = 0;
    1.81 +static uint32_t gTextRunStorage = 0;
    1.82 +static uint32_t gFontCount = 0;
    1.83 +static uint32_t gGlyphExtentsCount = 0;
    1.84 +static uint32_t gGlyphExtentsWidthsTotalSize = 0;
    1.85 +static uint32_t gGlyphExtentsSetupEagerSimple = 0;
    1.86 +static uint32_t gGlyphExtentsSetupEagerTight = 0;
    1.87 +static uint32_t gGlyphExtentsSetupLazyTight = 0;
    1.88 +static uint32_t gGlyphExtentsSetupFallBackToTight = 0;
    1.89 +#endif
    1.90 +
    1.91 +#ifdef PR_LOGGING
    1.92 +#define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
    1.93 +                                  PR_LOG_DEBUG, args)
    1.94 +#define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \
    1.95 +                                        gfxPlatform::GetLog(eGfxLog_fontinit), \
    1.96 +                                        PR_LOG_DEBUG)
    1.97 +#endif // PR_LOGGING
    1.98 +
    1.99 +void
   1.100 +gfxCharacterMap::NotifyReleased()
   1.101 +{
   1.102 +    gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
   1.103 +    if (mShared) {
   1.104 +        fontlist->RemoveCmap(this);
   1.105 +    }
   1.106 +    delete this;
   1.107 +}
   1.108 +
   1.109 +gfxFontEntry::gfxFontEntry() :
   1.110 +    mItalic(false), mFixedPitch(false),
   1.111 +    mIsProxy(false), mIsValid(true),
   1.112 +    mIsBadUnderlineFont(false),
   1.113 +    mIsUserFont(false),
   1.114 +    mIsLocalUserFont(false),
   1.115 +    mStandardFace(false),
   1.116 +    mSymbolFont(false),
   1.117 +    mIgnoreGDEF(false),
   1.118 +    mIgnoreGSUB(false),
   1.119 +    mSVGInitialized(false),
   1.120 +    mMathInitialized(false),
   1.121 +    mHasSpaceFeaturesInitialized(false),
   1.122 +    mHasSpaceFeatures(false),
   1.123 +    mHasSpaceFeaturesKerning(false),
   1.124 +    mHasSpaceFeaturesNonKerning(false),
   1.125 +    mSkipDefaultFeatureSpaceCheck(false),
   1.126 +    mCheckedForGraphiteTables(false),
   1.127 +    mHasCmapTable(false),
   1.128 +    mGrFaceInitialized(false),
   1.129 +    mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
   1.130 +    mUVSOffset(0), mUVSData(nullptr),
   1.131 +    mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
   1.132 +    mUnitsPerEm(0),
   1.133 +    mHBFace(nullptr),
   1.134 +    mGrFace(nullptr),
   1.135 +    mGrFaceRefCnt(0)
   1.136 +{
   1.137 +    memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
   1.138 +    memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
   1.139 +}
   1.140 +
   1.141 +gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
   1.142 +    mName(aName), mItalic(false), mFixedPitch(false),
   1.143 +    mIsProxy(false), mIsValid(true),
   1.144 +    mIsBadUnderlineFont(false), mIsUserFont(false),
   1.145 +    mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
   1.146 +    mSymbolFont(false),
   1.147 +    mIgnoreGDEF(false),
   1.148 +    mIgnoreGSUB(false),
   1.149 +    mSVGInitialized(false),
   1.150 +    mMathInitialized(false),
   1.151 +    mHasSpaceFeaturesInitialized(false),
   1.152 +    mHasSpaceFeatures(false),
   1.153 +    mHasSpaceFeaturesKerning(false),
   1.154 +    mHasSpaceFeaturesNonKerning(false),
   1.155 +    mSkipDefaultFeatureSpaceCheck(false),
   1.156 +    mCheckedForGraphiteTables(false),
   1.157 +    mHasCmapTable(false),
   1.158 +    mGrFaceInitialized(false),
   1.159 +    mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
   1.160 +    mUVSOffset(0), mUVSData(nullptr),
   1.161 +    mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
   1.162 +    mUnitsPerEm(0),
   1.163 +    mHBFace(nullptr),
   1.164 +    mGrFace(nullptr),
   1.165 +    mGrFaceRefCnt(0)
   1.166 +{
   1.167 +    memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
   1.168 +    memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
   1.169 +}
   1.170 +
   1.171 +gfxFontEntry::~gfxFontEntry()
   1.172 +{
   1.173 +    // For downloaded fonts, we need to tell the user font cache that this
   1.174 +    // entry is being deleted.
   1.175 +    if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) {
   1.176 +        gfxUserFontSet::UserFontCache::ForgetFont(this);
   1.177 +    }
   1.178 +
   1.179 +    // By the time the entry is destroyed, all font instances that were
   1.180 +    // using it should already have been deleted, and so the HB and/or Gr
   1.181 +    // face objects should have been released.
   1.182 +    MOZ_ASSERT(!mHBFace);
   1.183 +    MOZ_ASSERT(!mGrFaceInitialized);
   1.184 +}
   1.185 +
   1.186 +bool gfxFontEntry::IsSymbolFont() 
   1.187 +{
   1.188 +    return mSymbolFont;
   1.189 +}
   1.190 +
   1.191 +bool gfxFontEntry::TestCharacterMap(uint32_t aCh)
   1.192 +{
   1.193 +    if (!mCharacterMap) {
   1.194 +        ReadCMAP();
   1.195 +        NS_ASSERTION(mCharacterMap, "failed to initialize character map");
   1.196 +    }
   1.197 +    return mCharacterMap->test(aCh);
   1.198 +}
   1.199 +
   1.200 +nsresult gfxFontEntry::InitializeUVSMap()
   1.201 +{
   1.202 +    // mUVSOffset will not be initialized
   1.203 +    // until cmap is initialized.
   1.204 +    if (!mCharacterMap) {
   1.205 +        ReadCMAP();
   1.206 +        NS_ASSERTION(mCharacterMap, "failed to initialize character map");
   1.207 +    }
   1.208 +
   1.209 +    if (!mUVSOffset) {
   1.210 +        return NS_ERROR_FAILURE;
   1.211 +    }
   1.212 +
   1.213 +    if (!mUVSData) {
   1.214 +        const uint32_t kCmapTag = TRUETYPE_TAG('c','m','a','p');
   1.215 +        AutoTable cmapTable(this, kCmapTag);
   1.216 +        if (!cmapTable) {
   1.217 +            mUVSOffset = 0; // don't bother to read the table again
   1.218 +            return NS_ERROR_FAILURE;
   1.219 +        }
   1.220 +
   1.221 +        uint8_t* uvsData;
   1.222 +        unsigned int cmapLen;
   1.223 +        const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
   1.224 +        nsresult rv = gfxFontUtils::ReadCMAPTableFormat14(
   1.225 +                          (const uint8_t*)cmapData + mUVSOffset,
   1.226 +                          cmapLen - mUVSOffset, uvsData);
   1.227 +
   1.228 +        if (NS_FAILED(rv)) {
   1.229 +            mUVSOffset = 0; // don't bother to read the table again
   1.230 +            return rv;
   1.231 +        }
   1.232 +
   1.233 +        mUVSData = uvsData;
   1.234 +    }
   1.235 +
   1.236 +    return NS_OK;
   1.237 +}
   1.238 +
   1.239 +uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS)
   1.240 +{
   1.241 +    InitializeUVSMap();
   1.242 +
   1.243 +    if (mUVSData) {
   1.244 +        return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS);
   1.245 +    }
   1.246 +
   1.247 +    return 0;
   1.248 +}
   1.249 +
   1.250 +bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags)
   1.251 +{
   1.252 +    hb_face_t *face = GetHBFace();
   1.253 +    if (!face) {
   1.254 +        return false;
   1.255 +    }
   1.256 +
   1.257 +    unsigned int index;
   1.258 +    hb_tag_t     chosenScript;
   1.259 +    bool found =
   1.260 +        hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'),
   1.261 +                                         aScriptTags, &index, &chosenScript);
   1.262 +    hb_face_destroy(face);
   1.263 +
   1.264 +    return found && chosenScript != TRUETYPE_TAG('D','F','L','T');
   1.265 +}
   1.266 +
   1.267 +nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
   1.268 +{
   1.269 +    NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
   1.270 +    mCharacterMap = new gfxCharacterMap();
   1.271 +    return NS_OK;
   1.272 +}
   1.273 +
   1.274 +nsString
   1.275 +gfxFontEntry::RealFaceName()
   1.276 +{
   1.277 +    AutoTable nameTable(this, TRUETYPE_TAG('n','a','m','e'));
   1.278 +    if (nameTable) {
   1.279 +        nsAutoString name;
   1.280 +        nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
   1.281 +        if (NS_SUCCEEDED(rv)) {
   1.282 +            return name;
   1.283 +        }
   1.284 +    }
   1.285 +    return Name();
   1.286 +}
   1.287 +
   1.288 +already_AddRefed<gfxFont>
   1.289 +gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, bool aNeedsBold)
   1.290 +{
   1.291 +    // the font entry name is the psname, not the family name
   1.292 +    nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(this, aStyle);
   1.293 +
   1.294 +    if (!font) {
   1.295 +        gfxFont *newFont = CreateFontInstance(aStyle, aNeedsBold);
   1.296 +        if (!newFont)
   1.297 +            return nullptr;
   1.298 +        if (!newFont->Valid()) {
   1.299 +            delete newFont;
   1.300 +            return nullptr;
   1.301 +        }
   1.302 +        font = newFont;
   1.303 +        gfxFontCache::GetCache()->AddNew(font);
   1.304 +    }
   1.305 +    return font.forget();
   1.306 +}
   1.307 +
   1.308 +uint16_t
   1.309 +gfxFontEntry::UnitsPerEm()
   1.310 +{
   1.311 +    if (!mUnitsPerEm) {
   1.312 +        AutoTable headTable(this, TRUETYPE_TAG('h','e','a','d'));
   1.313 +        if (headTable) {
   1.314 +            uint32_t len;
   1.315 +            const HeadTable* head =
   1.316 +                reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
   1.317 +                                                                    &len));
   1.318 +            if (len >= sizeof(HeadTable)) {
   1.319 +                mUnitsPerEm = head->unitsPerEm;
   1.320 +            }
   1.321 +        }
   1.322 +
   1.323 +        // if we didn't find a usable 'head' table, or if the value was
   1.324 +        // outside the valid range, record it as invalid
   1.325 +        if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
   1.326 +            mUnitsPerEm = kInvalidUPEM;
   1.327 +        }
   1.328 +    }
   1.329 +    return mUnitsPerEm;
   1.330 +}
   1.331 +
   1.332 +bool
   1.333 +gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId)
   1.334 +{
   1.335 +    NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
   1.336 +    return mSVGGlyphs->HasSVGGlyph(aGlyphId);
   1.337 +}
   1.338 +
   1.339 +bool
   1.340 +gfxFontEntry::GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
   1.341 +                                 gfxRect *aResult)
   1.342 +{
   1.343 +    NS_ABORT_IF_FALSE(mSVGInitialized,
   1.344 +                      "SVG data has not yet been loaded. TryGetSVGData() first.");
   1.345 +    NS_ABORT_IF_FALSE(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM,
   1.346 +                      "font has invalid unitsPerEm");
   1.347 +
   1.348 +    gfxContextAutoSaveRestore matrixRestore(aContext);
   1.349 +    cairo_matrix_t fontMatrix;
   1.350 +    cairo_get_font_matrix(aContext->GetCairo(), &fontMatrix);
   1.351 +
   1.352 +    gfxMatrix svgToAppSpace = *reinterpret_cast<gfxMatrix*>(&fontMatrix);
   1.353 +    svgToAppSpace.Scale(1.0f / mUnitsPerEm, 1.0f / mUnitsPerEm);
   1.354 +
   1.355 +    return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult);
   1.356 +}
   1.357 +
   1.358 +bool
   1.359 +gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
   1.360 +                             int aDrawMode, gfxTextContextPaint *aContextPaint)
   1.361 +{
   1.362 +    NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
   1.363 +    return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, DrawMode(aDrawMode),
   1.364 +                                   aContextPaint);
   1.365 +}
   1.366 +
   1.367 +bool
   1.368 +gfxFontEntry::TryGetSVGData(gfxFont* aFont)
   1.369 +{
   1.370 +    if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
   1.371 +        return false;
   1.372 +    }
   1.373 +
   1.374 +    if (!mSVGInitialized) {
   1.375 +        mSVGInitialized = true;
   1.376 +
   1.377 +        // If UnitsPerEm is not known/valid, we can't use SVG glyphs
   1.378 +        if (UnitsPerEm() == kInvalidUPEM) {
   1.379 +            return false;
   1.380 +        }
   1.381 +
   1.382 +        // We don't use AutoTable here because we'll pass ownership of this
   1.383 +        // blob to the gfxSVGGlyphs, once we've confirmed the table exists
   1.384 +        hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' '));
   1.385 +        if (!svgTable) {
   1.386 +            return false;
   1.387 +        }
   1.388 +
   1.389 +        // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
   1.390 +        // with it.
   1.391 +        mSVGGlyphs = new gfxSVGGlyphs(svgTable, this);
   1.392 +    }
   1.393 +
   1.394 +    if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
   1.395 +        mFontsUsingSVGGlyphs.AppendElement(aFont);
   1.396 +    }
   1.397 +
   1.398 +    return !!mSVGGlyphs;
   1.399 +}
   1.400 +
   1.401 +void
   1.402 +gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont)
   1.403 +{
   1.404 +    mFontsUsingSVGGlyphs.RemoveElement(aFont);
   1.405 +}
   1.406 +
   1.407 +void
   1.408 +gfxFontEntry::NotifyGlyphsChanged()
   1.409 +{
   1.410 +    for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
   1.411 +        gfxFont* font = mFontsUsingSVGGlyphs[i];
   1.412 +        font->NotifyGlyphsChanged();
   1.413 +    }
   1.414 +}
   1.415 +
   1.416 +bool
   1.417 +gfxFontEntry::TryGetMathTable(gfxFont* aFont)
   1.418 +{
   1.419 +    if (!mMathInitialized) {
   1.420 +        mMathInitialized = true;
   1.421 +
   1.422 +        // If UnitsPerEm is not known/valid, we can't use MATH table
   1.423 +        if (UnitsPerEm() == kInvalidUPEM) {
   1.424 +            return false;
   1.425 +        }
   1.426 +
   1.427 +        // We don't use AutoTable here because we'll pass ownership of this
   1.428 +        // blob to the gfxMathTable, once we've confirmed the table exists
   1.429 +        hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H'));
   1.430 +        if (!mathTable) {
   1.431 +            return false;
   1.432 +        }
   1.433 +
   1.434 +        // gfxMathTable will hb_blob_destroy() the table when it is finished
   1.435 +        // with it.
   1.436 +        mMathTable = new gfxMathTable(mathTable);
   1.437 +        if (!mMathTable->HasValidHeaders()) {
   1.438 +            mMathTable = nullptr;
   1.439 +            return false;
   1.440 +        }
   1.441 +    }
   1.442 +
   1.443 +    return !!mMathTable;
   1.444 +}
   1.445 +
   1.446 +gfxFloat
   1.447 +gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant)
   1.448 +{
   1.449 +    NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
   1.450 +    gfxFloat value = mMathTable->GetMathConstant(aConstant);
   1.451 +    if (aConstant == gfxFontEntry::ScriptPercentScaleDown ||
   1.452 +        aConstant == gfxFontEntry::ScriptScriptPercentScaleDown ||
   1.453 +        aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) {
   1.454 +        return value / 100.0;
   1.455 +    }
   1.456 +    return value / mUnitsPerEm;
   1.457 +}
   1.458 +
   1.459 +bool
   1.460 +gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID,
   1.461 +                                       gfxFloat* aItalicCorrection)
   1.462 +{
   1.463 +    NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
   1.464 +    int16_t italicCorrection;
   1.465 +    if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) {
   1.466 +        return false;
   1.467 +    }
   1.468 +    *aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm;
   1.469 +    return true;
   1.470 +}
   1.471 +
   1.472 +uint32_t
   1.473 +gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
   1.474 +                                  uint16_t aSize)
   1.475 +{
   1.476 +    NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
   1.477 +    return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize);
   1.478 +}
   1.479 +
   1.480 +bool
   1.481 +gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
   1.482 +                                   uint32_t aGlyphs[4])
   1.483 +{
   1.484 +    NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
   1.485 +    return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs);
   1.486 +}
   1.487 +
   1.488 +/**
   1.489 + * FontTableBlobData
   1.490 + *
   1.491 + * See FontTableHashEntry for the general strategy.
   1.492 + */
   1.493 +
   1.494 +class gfxFontEntry::FontTableBlobData {
   1.495 +public:
   1.496 +    // Adopts the content of aBuffer.
   1.497 +    FontTableBlobData(FallibleTArray<uint8_t>& aBuffer)
   1.498 +        : mHashtable(nullptr), mHashKey(0)
   1.499 +    {
   1.500 +        MOZ_COUNT_CTOR(FontTableBlobData);
   1.501 +        mTableData.SwapElements(aBuffer);
   1.502 +    }
   1.503 +
   1.504 +    ~FontTableBlobData() {
   1.505 +        MOZ_COUNT_DTOR(FontTableBlobData);
   1.506 +        if (mHashtable && mHashKey) {
   1.507 +            mHashtable->RemoveEntry(mHashKey);
   1.508 +        }
   1.509 +    }
   1.510 +
   1.511 +    // Useful for creating blobs
   1.512 +    const char *GetTable() const
   1.513 +    {
   1.514 +        return reinterpret_cast<const char*>(mTableData.Elements());
   1.515 +    }
   1.516 +    uint32_t GetTableLength() const { return mTableData.Length(); }
   1.517 +
   1.518 +    // Tell this FontTableBlobData to remove the HashEntry when this is
   1.519 +    // destroyed.
   1.520 +    void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable,
   1.521 +                         uint32_t aHashKey)
   1.522 +    {
   1.523 +        mHashtable = aHashtable;
   1.524 +        mHashKey = aHashKey;
   1.525 +    }
   1.526 +
   1.527 +    // Disconnect from the HashEntry (because the blob has already been
   1.528 +    // removed from the hashtable).
   1.529 +    void ForgetHashEntry()
   1.530 +    {
   1.531 +        mHashtable = nullptr;
   1.532 +        mHashKey = 0;
   1.533 +    }
   1.534 +
   1.535 +    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
   1.536 +        return mTableData.SizeOfExcludingThis(aMallocSizeOf);
   1.537 +    }
   1.538 +    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
   1.539 +        return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   1.540 +    }
   1.541 +
   1.542 +private:
   1.543 +    // The font table data block, owned (via adoption)
   1.544 +    FallibleTArray<uint8_t> mTableData;
   1.545 +
   1.546 +    // The blob destroy function needs to know the owning hashtable
   1.547 +    // and the hashtable key, so that it can remove the entry.
   1.548 +    nsTHashtable<FontTableHashEntry> *mHashtable;
   1.549 +    uint32_t                          mHashKey;
   1.550 +
   1.551 +    // not implemented
   1.552 +    FontTableBlobData(const FontTableBlobData&);
   1.553 +};
   1.554 +
   1.555 +hb_blob_t *
   1.556 +gfxFontEntry::FontTableHashEntry::
   1.557 +ShareTableAndGetBlob(FallibleTArray<uint8_t>& aTable,
   1.558 +                     nsTHashtable<FontTableHashEntry> *aHashtable)
   1.559 +{
   1.560 +    Clear();
   1.561 +    // adopts elements of aTable
   1.562 +    mSharedBlobData = new FontTableBlobData(aTable);
   1.563 +    mBlob = hb_blob_create(mSharedBlobData->GetTable(),
   1.564 +                           mSharedBlobData->GetTableLength(),
   1.565 +                           HB_MEMORY_MODE_READONLY,
   1.566 +                           mSharedBlobData, DeleteFontTableBlobData);
   1.567 +    if (!mSharedBlobData) {
   1.568 +        // The FontTableBlobData was destroyed during hb_blob_create().
   1.569 +        // The (empty) blob is still be held in the hashtable with a strong
   1.570 +        // reference.
   1.571 +        return hb_blob_reference(mBlob);
   1.572 +    }
   1.573 +
   1.574 +    // Tell the FontTableBlobData to remove this hash entry when destroyed.
   1.575 +    // The hashtable does not keep a strong reference.
   1.576 +    mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
   1.577 +    return mBlob;
   1.578 +}
   1.579 +
   1.580 +void
   1.581 +gfxFontEntry::FontTableHashEntry::Clear()
   1.582 +{
   1.583 +    // If the FontTableBlobData is managing the hash entry, then the blob is
   1.584 +    // not owned by this HashEntry; otherwise there is strong reference to the
   1.585 +    // blob that must be removed.
   1.586 +    if (mSharedBlobData) {
   1.587 +        mSharedBlobData->ForgetHashEntry();
   1.588 +        mSharedBlobData = nullptr;
   1.589 +    } else if (mBlob) {
   1.590 +        hb_blob_destroy(mBlob);
   1.591 +    }
   1.592 +    mBlob = nullptr;
   1.593 +}
   1.594 +
   1.595 +// a hb_destroy_func for hb_blob_create
   1.596 +
   1.597 +/* static */ void
   1.598 +gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData)
   1.599 +{
   1.600 +    delete static_cast<FontTableBlobData*>(aBlobData);
   1.601 +}
   1.602 +
   1.603 +hb_blob_t *
   1.604 +gfxFontEntry::FontTableHashEntry::GetBlob() const
   1.605 +{
   1.606 +    return hb_blob_reference(mBlob);
   1.607 +}
   1.608 +
   1.609 +bool
   1.610 +gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob)
   1.611 +{
   1.612 +    if (!mFontTableCache) {
   1.613 +        // we do this here rather than on fontEntry construction
   1.614 +        // because not all shapers will access the table cache at all
   1.615 +        mFontTableCache = new nsTHashtable<FontTableHashEntry>(10);
   1.616 +    }
   1.617 +
   1.618 +    FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag);
   1.619 +    if (!entry) {
   1.620 +        return false;
   1.621 +    }
   1.622 +
   1.623 +    *aBlob = entry->GetBlob();
   1.624 +    return true;
   1.625 +}
   1.626 +
   1.627 +hb_blob_t *
   1.628 +gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
   1.629 +                                       FallibleTArray<uint8_t>* aBuffer)
   1.630 +{
   1.631 +    if (MOZ_UNLIKELY(!mFontTableCache)) {
   1.632 +        // we do this here rather than on fontEntry construction
   1.633 +        // because not all shapers will access the table cache at all
   1.634 +      mFontTableCache = new nsTHashtable<FontTableHashEntry>(10);
   1.635 +    }
   1.636 +
   1.637 +    FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag);
   1.638 +    if (MOZ_UNLIKELY(!entry)) { // OOM
   1.639 +        return nullptr;
   1.640 +    }
   1.641 +
   1.642 +    if (!aBuffer) {
   1.643 +        // ensure the entry is null
   1.644 +        entry->Clear();
   1.645 +        return nullptr;
   1.646 +    }
   1.647 +
   1.648 +    return entry->ShareTableAndGetBlob(*aBuffer, mFontTableCache);
   1.649 +}
   1.650 +
   1.651 +static int
   1.652 +DirEntryCmp(const void* aKey, const void* aItem)
   1.653 +{
   1.654 +    int32_t tag = *static_cast<const int32_t*>(aKey);
   1.655 +    const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
   1.656 +    return tag - int32_t(entry->tag);
   1.657 +}
   1.658 +
   1.659 +hb_blob_t*
   1.660 +gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
   1.661 +{
   1.662 +    const SFNTHeader* header =
   1.663 +        reinterpret_cast<const SFNTHeader*>(aFontData);
   1.664 +    const TableDirEntry* dir =
   1.665 +        reinterpret_cast<const TableDirEntry*>(header + 1);
   1.666 +    dir = static_cast<const TableDirEntry*>
   1.667 +        (bsearch(&aTableTag, dir, uint16_t(header->numTables),
   1.668 +                 sizeof(TableDirEntry), DirEntryCmp));
   1.669 +    if (dir) {
   1.670 +        return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
   1.671 +                                  dir->offset, dir->length,
   1.672 +                              HB_MEMORY_MODE_READONLY, nullptr, nullptr);
   1.673 +
   1.674 +    }
   1.675 +    return nullptr;
   1.676 +}
   1.677 +
   1.678 +already_AddRefed<gfxCharacterMap>
   1.679 +gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
   1.680 +                                  uint32_t& aUVSOffset,
   1.681 +                                  bool& aSymbolFont)
   1.682 +{
   1.683 +    if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
   1.684 +        return nullptr;
   1.685 +    }
   1.686 +
   1.687 +    return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont);
   1.688 +}
   1.689 +
   1.690 +hb_blob_t *
   1.691 +gfxFontEntry::GetFontTable(uint32_t aTag)
   1.692 +{
   1.693 +    hb_blob_t *blob;
   1.694 +    if (GetExistingFontTable(aTag, &blob)) {
   1.695 +        return blob;
   1.696 +    }
   1.697 +
   1.698 +    FallibleTArray<uint8_t> buffer;
   1.699 +    bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
   1.700 +
   1.701 +    return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
   1.702 +}
   1.703 +
   1.704 +// callback for HarfBuzz to get a font table (in hb_blob_t form)
   1.705 +// from the font entry (passed as aUserData)
   1.706 +/*static*/ hb_blob_t *
   1.707 +gfxFontEntry::HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData)
   1.708 +{
   1.709 +    gfxFontEntry *fontEntry = static_cast<gfxFontEntry*>(aUserData);
   1.710 +
   1.711 +    // bug 589682 - ignore the GDEF table in buggy fonts (applies to
   1.712 +    // Italic and BoldItalic faces of Times New Roman)
   1.713 +    if (aTag == TRUETYPE_TAG('G','D','E','F') &&
   1.714 +        fontEntry->IgnoreGDEF()) {
   1.715 +        return nullptr;
   1.716 +    }
   1.717 +
   1.718 +    // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
   1.719 +    // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
   1.720 +    if (aTag == TRUETYPE_TAG('G','S','U','B') &&
   1.721 +        fontEntry->IgnoreGSUB()) {
   1.722 +        return nullptr;
   1.723 +    }
   1.724 +
   1.725 +    return fontEntry->GetFontTable(aTag);
   1.726 +}
   1.727 +
   1.728 +/*static*/ void
   1.729 +gfxFontEntry::HBFaceDeletedCallback(void *aUserData)
   1.730 +{
   1.731 +    gfxFontEntry *fe = static_cast<gfxFontEntry*>(aUserData);
   1.732 +    fe->ForgetHBFace();
   1.733 +}
   1.734 +
   1.735 +void
   1.736 +gfxFontEntry::ForgetHBFace()
   1.737 +{
   1.738 +    mHBFace = nullptr;
   1.739 +}
   1.740 +
   1.741 +hb_face_t*
   1.742 +gfxFontEntry::GetHBFace()
   1.743 +{
   1.744 +    if (!mHBFace) {
   1.745 +        mHBFace = hb_face_create_for_tables(HBGetTable, this,
   1.746 +                                            HBFaceDeletedCallback);
   1.747 +        return mHBFace;
   1.748 +    }
   1.749 +    return hb_face_reference(mHBFace);
   1.750 +}
   1.751 +
   1.752 +/*static*/ const void*
   1.753 +gfxFontEntry::GrGetTable(const void *aAppFaceHandle, unsigned int aName,
   1.754 +                         size_t *aLen)
   1.755 +{
   1.756 +    gfxFontEntry *fontEntry =
   1.757 +        static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
   1.758 +    hb_blob_t *blob = fontEntry->GetFontTable(aName);
   1.759 +    if (blob) {
   1.760 +        unsigned int blobLength;
   1.761 +        const void *tableData = hb_blob_get_data(blob, &blobLength);
   1.762 +        fontEntry->mGrTableMap->Put(tableData, blob);
   1.763 +        *aLen = blobLength;
   1.764 +        return tableData;
   1.765 +    }
   1.766 +    *aLen = 0;
   1.767 +    return nullptr;
   1.768 +}
   1.769 +
   1.770 +/*static*/ void
   1.771 +gfxFontEntry::GrReleaseTable(const void *aAppFaceHandle,
   1.772 +                             const void *aTableBuffer)
   1.773 +{
   1.774 +    gfxFontEntry *fontEntry =
   1.775 +        static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
   1.776 +    void *data;
   1.777 +    if (fontEntry->mGrTableMap->Get(aTableBuffer, &data)) {
   1.778 +        fontEntry->mGrTableMap->Remove(aTableBuffer);
   1.779 +        hb_blob_destroy(static_cast<hb_blob_t*>(data));
   1.780 +    }
   1.781 +}
   1.782 +
   1.783 +gr_face*
   1.784 +gfxFontEntry::GetGrFace()
   1.785 +{
   1.786 +    if (!mGrFaceInitialized) {
   1.787 +        gr_face_ops faceOps = {
   1.788 +            sizeof(gr_face_ops),
   1.789 +            GrGetTable,
   1.790 +            GrReleaseTable
   1.791 +        };
   1.792 +        mGrTableMap = new nsDataHashtable<nsPtrHashKey<const void>,void*>;
   1.793 +        mGrFace = gr_make_face_with_ops(this, &faceOps, gr_face_default);
   1.794 +        mGrFaceInitialized = true;
   1.795 +    }
   1.796 +    ++mGrFaceRefCnt;
   1.797 +    return mGrFace;
   1.798 +}
   1.799 +
   1.800 +void
   1.801 +gfxFontEntry::ReleaseGrFace(gr_face *aFace)
   1.802 +{
   1.803 +    MOZ_ASSERT(aFace == mGrFace); // sanity-check
   1.804 +    MOZ_ASSERT(mGrFaceRefCnt > 0);
   1.805 +    if (--mGrFaceRefCnt == 0) {
   1.806 +        gr_face_destroy(mGrFace);
   1.807 +        mGrFace = nullptr;
   1.808 +        mGrFaceInitialized = false;
   1.809 +        delete mGrTableMap;
   1.810 +        mGrTableMap = nullptr;
   1.811 +    }
   1.812 +}
   1.813 +
   1.814 +void
   1.815 +gfxFontEntry::DisconnectSVG()
   1.816 +{
   1.817 +    if (mSVGInitialized && mSVGGlyphs) {
   1.818 +        mSVGGlyphs = nullptr;
   1.819 +        mSVGInitialized = false;
   1.820 +    }
   1.821 +}
   1.822 +
   1.823 +bool
   1.824 +gfxFontEntry::HasFontTable(uint32_t aTableTag)
   1.825 +{
   1.826 +    AutoTable table(this, aTableTag);
   1.827 +    return table && hb_blob_get_length(table) > 0;
   1.828 +}
   1.829 +
   1.830 +void
   1.831 +gfxFontEntry::CheckForGraphiteTables()
   1.832 +{
   1.833 +    mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f'));
   1.834 +}
   1.835 +
   1.836 +/* static */ size_t
   1.837 +gfxFontEntry::FontTableHashEntry::SizeOfEntryExcludingThis
   1.838 +    (FontTableHashEntry *aEntry,
   1.839 +     MallocSizeOf aMallocSizeOf,
   1.840 +     void* aUserArg)
   1.841 +{
   1.842 +    size_t n = 0;
   1.843 +    if (aEntry->mBlob) {
   1.844 +        n += aMallocSizeOf(aEntry->mBlob);
   1.845 +    }
   1.846 +    if (aEntry->mSharedBlobData) {
   1.847 +        n += aEntry->mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
   1.848 +    }
   1.849 +    return n;
   1.850 +}
   1.851 +
   1.852 +void
   1.853 +gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
   1.854 +                                     FontListSizes* aSizes) const
   1.855 +{
   1.856 +    aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   1.857 +
   1.858 +    // cmaps are shared so only non-shared cmaps are included here
   1.859 +    if (mCharacterMap && mCharacterMap->mBuildOnTheFly) {
   1.860 +        aSizes->mCharMapsSize +=
   1.861 +            mCharacterMap->SizeOfIncludingThis(aMallocSizeOf);
   1.862 +    }
   1.863 +    if (mFontTableCache) {
   1.864 +        aSizes->mFontTableCacheSize +=
   1.865 +            mFontTableCache->SizeOfExcludingThis(
   1.866 +                FontTableHashEntry::SizeOfEntryExcludingThis,
   1.867 +                aMallocSizeOf);
   1.868 +    }
   1.869 +}
   1.870 +
   1.871 +void
   1.872 +gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   1.873 +                                     FontListSizes* aSizes) const
   1.874 +{
   1.875 +    aSizes->mFontListSize += aMallocSizeOf(this);
   1.876 +    AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   1.877 +}
   1.878 +
   1.879 +//////////////////////////////////////////////////////////////////////////////
   1.880 +//
   1.881 +// class gfxFontFamily
   1.882 +//
   1.883 +//////////////////////////////////////////////////////////////////////////////
   1.884 +
   1.885 +// we consider faces with mStandardFace == true to be "greater than" those with false,
   1.886 +// because during style matching, later entries will replace earlier ones
   1.887 +class FontEntryStandardFaceComparator {
   1.888 +  public:
   1.889 +    bool Equals(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
   1.890 +        return a->mStandardFace == b->mStandardFace;
   1.891 +    }
   1.892 +    bool LessThan(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
   1.893 +        return (a->mStandardFace == false && b->mStandardFace == true);
   1.894 +    }
   1.895 +};
   1.896 +
   1.897 +void
   1.898 +gfxFontFamily::SortAvailableFonts()
   1.899 +{
   1.900 +    mAvailableFonts.Sort(FontEntryStandardFaceComparator());
   1.901 +}
   1.902 +
   1.903 +bool
   1.904 +gfxFontFamily::HasOtherFamilyNames()
   1.905 +{
   1.906 +    // need to read in other family names to determine this
   1.907 +    if (!mOtherFamilyNamesInitialized) {
   1.908 +        ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList());  // sets mHasOtherFamilyNames
   1.909 +    }
   1.910 +    return mHasOtherFamilyNames;
   1.911 +}
   1.912 +
   1.913 +gfxFontEntry*
   1.914 +gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle, 
   1.915 +                                bool& aNeedsSyntheticBold)
   1.916 +{
   1.917 +    if (!mHasStyles)
   1.918 +        FindStyleVariations(); // collect faces for the family, if not already done
   1.919 +
   1.920 +    NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
   1.921 +
   1.922 +    aNeedsSyntheticBold = false;
   1.923 +
   1.924 +    int8_t baseWeight = aFontStyle.ComputeWeight();
   1.925 +    bool wantBold = baseWeight >= 6;
   1.926 +
   1.927 +    // If the family has only one face, we simply return it; no further checking needed
   1.928 +    if (mAvailableFonts.Length() == 1) {
   1.929 +        gfxFontEntry *fe = mAvailableFonts[0];
   1.930 +        aNeedsSyntheticBold = wantBold && !fe->IsBold();
   1.931 +        return fe;
   1.932 +    }
   1.933 +
   1.934 +    bool wantItalic = (aFontStyle.style &
   1.935 +                       (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
   1.936 +
   1.937 +    // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
   1.938 +    // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts,
   1.939 +    // stored in the above order; note that some of the entries may be nullptr.
   1.940 +    // We can then pick the required entry based on whether the request is for
   1.941 +    // bold or non-bold, italic or non-italic, without running the more complex
   1.942 +    // matching algorithm used for larger families with many weights and/or widths.
   1.943 +
   1.944 +    if (mIsSimpleFamily) {
   1.945 +        // Family has no more than the "standard" 4 faces, at fixed indexes;
   1.946 +        // calculate which one we want.
   1.947 +        // Note that we cannot simply return it as not all 4 faces are necessarily present.
   1.948 +        uint8_t faceIndex = (wantItalic ? kItalicMask : 0) |
   1.949 +                            (wantBold ? kBoldMask : 0);
   1.950 +
   1.951 +        // if the desired style is available, return it directly
   1.952 +        gfxFontEntry *fe = mAvailableFonts[faceIndex];
   1.953 +        if (fe) {
   1.954 +            // no need to set aNeedsSyntheticBold here as we matched the boldness request
   1.955 +            return fe;
   1.956 +        }
   1.957 +
   1.958 +        // order to check fallback faces in a simple family, depending on requested style
   1.959 +        static const uint8_t simpleFallbacks[4][3] = {
   1.960 +            { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex },   // fallbacks for Regular
   1.961 +            { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold
   1.962 +            { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex },  // Italic
   1.963 +            { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex }       // BoldItalic
   1.964 +        };
   1.965 +        const uint8_t *order = simpleFallbacks[faceIndex];
   1.966 +
   1.967 +        for (uint8_t trial = 0; trial < 3; ++trial) {
   1.968 +            // check remaining faces in order of preference to find the first that actually exists
   1.969 +            fe = mAvailableFonts[order[trial]];
   1.970 +            if (fe) {
   1.971 +                aNeedsSyntheticBold = wantBold && !fe->IsBold();
   1.972 +                return fe;
   1.973 +            }
   1.974 +        }
   1.975 +
   1.976 +        // this can't happen unless we have totally broken the font-list manager!
   1.977 +        NS_NOTREACHED("no face found in simple font family!");
   1.978 +        return nullptr;
   1.979 +    }
   1.980 +
   1.981 +    // This is a large/rich font family, so we do full style- and weight-matching:
   1.982 +    // first collect a list of weights that are the best match for the requested
   1.983 +    // font-stretch and font-style, then pick the best weight match among those
   1.984 +    // available.
   1.985 +
   1.986 +    gfxFontEntry *weightList[10] = { 0 };
   1.987 +    bool foundWeights = FindWeightsForStyle(weightList, wantItalic, aFontStyle.stretch);
   1.988 +    if (!foundWeights) {
   1.989 +        return nullptr;
   1.990 +    }
   1.991 +
   1.992 +    // First find a match for the best weight
   1.993 +    int8_t matchBaseWeight = 0;
   1.994 +    int8_t i = baseWeight;
   1.995 +
   1.996 +    // Need to special case when normal face doesn't exist but medium does.
   1.997 +    // In that case, use medium otherwise weights < 400
   1.998 +    if (baseWeight == 4 && !weightList[4]) {
   1.999 +        i = 5; // medium
  1.1000 +    }
  1.1001 +
  1.1002 +    // Loop through weights, since one exists loop will terminate
  1.1003 +    int8_t direction = (baseWeight > 5) ? 1 : -1;
  1.1004 +    for (; ; i += direction) {
  1.1005 +        if (weightList[i]) {
  1.1006 +            matchBaseWeight = i;
  1.1007 +            break;
  1.1008 +        }
  1.1009 +
  1.1010 +        // If we've reached one side without finding a font,
  1.1011 +        // start over and go the other direction until we find a match
  1.1012 +        if (i == 1 || i == 9) {
  1.1013 +            i = baseWeight;
  1.1014 +            direction = -direction;
  1.1015 +        }
  1.1016 +    }
  1.1017 +
  1.1018 +    NS_ASSERTION(matchBaseWeight != 0, 
  1.1019 +                 "weight mapping should always find at least one font in a family");
  1.1020 +
  1.1021 +    gfxFontEntry *matchFE = weightList[matchBaseWeight];
  1.1022 +
  1.1023 +    NS_ASSERTION(matchFE,
  1.1024 +                 "weight mapping should always find at least one font in a family");
  1.1025 +
  1.1026 +    if (!matchFE->IsBold() && baseWeight >= 6)
  1.1027 +    {
  1.1028 +        aNeedsSyntheticBold = true;
  1.1029 +    }
  1.1030 +
  1.1031 +    return matchFE;
  1.1032 +}
  1.1033 +
  1.1034 +void
  1.1035 +gfxFontFamily::CheckForSimpleFamily()
  1.1036 +{
  1.1037 +    // already checked this family
  1.1038 +    if (mIsSimpleFamily) {
  1.1039 +        return;
  1.1040 +    };
  1.1041 +
  1.1042 +    uint32_t count = mAvailableFonts.Length();
  1.1043 +    if (count > 4 || count == 0) {
  1.1044 +        return; // can't be "simple" if there are >4 faces;
  1.1045 +                // if none then the family is unusable anyway
  1.1046 +    }
  1.1047 +
  1.1048 +    if (count == 1) {
  1.1049 +        mIsSimpleFamily = true;
  1.1050 +        return;
  1.1051 +    }
  1.1052 +
  1.1053 +    int16_t firstStretch = mAvailableFonts[0]->Stretch();
  1.1054 +
  1.1055 +    gfxFontEntry *faces[4] = { 0 };
  1.1056 +    for (uint8_t i = 0; i < count; ++i) {
  1.1057 +        gfxFontEntry *fe = mAvailableFonts[i];
  1.1058 +        if (fe->Stretch() != firstStretch) {
  1.1059 +            return; // font-stretch doesn't match, don't treat as simple family
  1.1060 +        }
  1.1061 +        uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
  1.1062 +                            (fe->Weight() >= 600 ? kBoldMask : 0);
  1.1063 +        if (faces[faceIndex]) {
  1.1064 +            return; // two faces resolve to the same slot; family isn't "simple"
  1.1065 +        }
  1.1066 +        faces[faceIndex] = fe;
  1.1067 +    }
  1.1068 +
  1.1069 +    // we have successfully slotted the available faces into the standard
  1.1070 +    // 4-face framework
  1.1071 +    mAvailableFonts.SetLength(4);
  1.1072 +    for (uint8_t i = 0; i < 4; ++i) {
  1.1073 +        if (mAvailableFonts[i].get() != faces[i]) {
  1.1074 +            mAvailableFonts[i].swap(faces[i]);
  1.1075 +        }
  1.1076 +    }
  1.1077 +
  1.1078 +    mIsSimpleFamily = true;
  1.1079 +}
  1.1080 +
  1.1081 +static inline uint32_t
  1.1082 +StyleDistance(gfxFontEntry *aFontEntry,
  1.1083 +              bool anItalic, int16_t aStretch)
  1.1084 +{
  1.1085 +    // Compute a measure of the "distance" between the requested style
  1.1086 +    // and the given fontEntry,
  1.1087 +    // considering italicness and font-stretch but not weight.
  1.1088 +
  1.1089 +    int32_t distance = 0;
  1.1090 +    if (aStretch != aFontEntry->mStretch) {
  1.1091 +        // stretch values are in the range -4 .. +4
  1.1092 +        // if aStretch is positive, we prefer more-positive values;
  1.1093 +        // if zero or negative, prefer more-negative
  1.1094 +        if (aStretch > 0) {
  1.1095 +            distance = (aFontEntry->mStretch - aStretch) * 2;
  1.1096 +        } else {
  1.1097 +            distance = (aStretch - aFontEntry->mStretch) * 2;
  1.1098 +        }
  1.1099 +        // if the computed "distance" here is negative, it means that
  1.1100 +        // aFontEntry lies in the "non-preferred" direction from aStretch,
  1.1101 +        // so we treat that as larger than any preferred-direction distance
  1.1102 +        // (max possible is 8) by adding an extra 10 to the absolute value
  1.1103 +        if (distance < 0) {
  1.1104 +            distance = -distance + 10;
  1.1105 +        }
  1.1106 +    }
  1.1107 +    if (aFontEntry->IsItalic() != anItalic) {
  1.1108 +        distance += 1;
  1.1109 +    }
  1.1110 +    return uint32_t(distance);
  1.1111 +}
  1.1112 +
  1.1113 +bool
  1.1114 +gfxFontFamily::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[],
  1.1115 +                                   bool anItalic, int16_t aStretch)
  1.1116 +{
  1.1117 +    uint32_t foundWeights = 0;
  1.1118 +    uint32_t bestMatchDistance = 0xffffffff;
  1.1119 +
  1.1120 +    uint32_t count = mAvailableFonts.Length();
  1.1121 +    for (uint32_t i = 0; i < count; i++) {
  1.1122 +        // this is not called for "simple" families, and therefore it does not
  1.1123 +        // need to check the mAvailableFonts entries for nullptr.
  1.1124 +        gfxFontEntry *fe = mAvailableFonts[i];
  1.1125 +        uint32_t distance = StyleDistance(fe, anItalic, aStretch);
  1.1126 +        if (distance <= bestMatchDistance) {
  1.1127 +            int8_t wt = fe->mWeight / 100;
  1.1128 +            NS_ASSERTION(wt >= 1 && wt < 10, "invalid weight in fontEntry");
  1.1129 +            if (!aFontsForWeights[wt]) {
  1.1130 +                // record this as a possible candidate for weight matching
  1.1131 +                aFontsForWeights[wt] = fe;
  1.1132 +                ++foundWeights;
  1.1133 +            } else {
  1.1134 +                uint32_t prevDistance =
  1.1135 +                    StyleDistance(aFontsForWeights[wt], anItalic, aStretch);
  1.1136 +                if (prevDistance >= distance) {
  1.1137 +                    // replacing a weight we already found,
  1.1138 +                    // so don't increment foundWeights
  1.1139 +                    aFontsForWeights[wt] = fe;
  1.1140 +                }
  1.1141 +            }
  1.1142 +            bestMatchDistance = distance;
  1.1143 +        }
  1.1144 +    }
  1.1145 +
  1.1146 +    NS_ASSERTION(foundWeights > 0, "Font family containing no faces?");
  1.1147 +
  1.1148 +    if (foundWeights == 1) {
  1.1149 +        // no need to cull entries if we only found one weight
  1.1150 +        return true;
  1.1151 +    }
  1.1152 +
  1.1153 +    // we might have recorded some faces that were a partial style match, but later found
  1.1154 +    // others that were closer; in this case, we need to cull the poorer matches from the
  1.1155 +    // weight list we'll return
  1.1156 +    for (uint32_t i = 0; i < 10; ++i) {
  1.1157 +        if (aFontsForWeights[i] &&
  1.1158 +            StyleDistance(aFontsForWeights[i], anItalic, aStretch) > bestMatchDistance)
  1.1159 +        {
  1.1160 +            aFontsForWeights[i] = 0;
  1.1161 +        }
  1.1162 +    }
  1.1163 +
  1.1164 +    return (foundWeights > 0);
  1.1165 +}
  1.1166 +
  1.1167 +
  1.1168 +void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
  1.1169 +{
  1.1170 +    // just return the primary name; subclasses should override
  1.1171 +    aLocalizedName = mName;
  1.1172 +}
  1.1173 +
  1.1174 +// metric for how close a given font matches a style
  1.1175 +static int32_t
  1.1176 +CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
  1.1177 +{
  1.1178 +    int32_t rank = 0;
  1.1179 +    if (aStyle) {
  1.1180 +         // italics
  1.1181 +         bool wantItalic =
  1.1182 +             (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
  1.1183 +         if (aFontEntry->IsItalic() == wantItalic) {
  1.1184 +             rank += 10;
  1.1185 +         }
  1.1186 +
  1.1187 +        // measure of closeness of weight to the desired value
  1.1188 +        rank += 9 - DeprecatedAbs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight());
  1.1189 +    } else {
  1.1190 +        // if no font to match, prefer non-bold, non-italic fonts
  1.1191 +        if (!aFontEntry->IsItalic()) {
  1.1192 +            rank += 3;
  1.1193 +        }
  1.1194 +        if (!aFontEntry->IsBold()) {
  1.1195 +            rank += 2;
  1.1196 +        }
  1.1197 +    }
  1.1198 +
  1.1199 +    return rank;
  1.1200 +}
  1.1201 +
  1.1202 +#define RANK_MATCHED_CMAP   20
  1.1203 +
  1.1204 +void
  1.1205 +gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
  1.1206 +{
  1.1207 +    if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
  1.1208 +        // none of the faces in the family support the required char,
  1.1209 +        // so bail out immediately
  1.1210 +        return;
  1.1211 +    }
  1.1212 +
  1.1213 +    bool needsBold;
  1.1214 +    gfxFontStyle normal;
  1.1215 +    gfxFontEntry *fe = FindFontForStyle(
  1.1216 +                  (aMatchData->mStyle == nullptr) ? *aMatchData->mStyle : normal,
  1.1217 +                  needsBold);
  1.1218 +
  1.1219 +    if (fe && !fe->SkipDuringSystemFallback()) {
  1.1220 +        int32_t rank = 0;
  1.1221 +
  1.1222 +        if (fe->TestCharacterMap(aMatchData->mCh)) {
  1.1223 +            rank += RANK_MATCHED_CMAP;
  1.1224 +            aMatchData->mCount++;
  1.1225 +#ifdef PR_LOGGING
  1.1226 +            PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
  1.1227 +
  1.1228 +            if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_DEBUG))) {
  1.1229 +                uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
  1.1230 +                uint32_t script = GetScriptCode(aMatchData->mCh);
  1.1231 +                PR_LOG(log, PR_LOG_DEBUG,\
  1.1232 +                       ("(textrun-systemfallback-fonts) char: u+%6.6x "
  1.1233 +                        "unicode-range: %d script: %d match: [%s]\n",
  1.1234 +                        aMatchData->mCh,
  1.1235 +                        unicodeRange, script,
  1.1236 +                        NS_ConvertUTF16toUTF8(fe->Name()).get()));
  1.1237 +            }
  1.1238 +#endif
  1.1239 +        }
  1.1240 +
  1.1241 +        aMatchData->mCmapsTested++;
  1.1242 +        if (rank == 0) {
  1.1243 +            return;
  1.1244 +        }
  1.1245 +
  1.1246 +         // omitting from original windows code -- family name, lang group, pitch
  1.1247 +         // not available in current FontEntry implementation
  1.1248 +        rank += CalcStyleMatch(fe, aMatchData->mStyle);
  1.1249 +
  1.1250 +        // xxx - add whether AAT font with morphing info for specific lang groups
  1.1251 +
  1.1252 +        if (rank > aMatchData->mMatchRank
  1.1253 +            || (rank == aMatchData->mMatchRank &&
  1.1254 +                Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
  1.1255 +        {
  1.1256 +            aMatchData->mBestMatch = fe;
  1.1257 +            aMatchData->mMatchedFamily = this;
  1.1258 +            aMatchData->mMatchRank = rank;
  1.1259 +        }
  1.1260 +    }
  1.1261 +}
  1.1262 +
  1.1263 +void
  1.1264 +gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
  1.1265 +{
  1.1266 +    uint32_t i, numFonts = mAvailableFonts.Length();
  1.1267 +    for (i = 0; i < numFonts; i++) {
  1.1268 +        gfxFontEntry *fe = mAvailableFonts[i];
  1.1269 +        if (fe && fe->TestCharacterMap(aMatchData->mCh)) {
  1.1270 +            int32_t rank = RANK_MATCHED_CMAP;
  1.1271 +            rank += CalcStyleMatch(fe, aMatchData->mStyle);
  1.1272 +            if (rank > aMatchData->mMatchRank
  1.1273 +                || (rank == aMatchData->mMatchRank &&
  1.1274 +                    Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
  1.1275 +            {
  1.1276 +                aMatchData->mBestMatch = fe;
  1.1277 +                aMatchData->mMatchedFamily = this;
  1.1278 +                aMatchData->mMatchRank = rank;
  1.1279 +            }
  1.1280 +        }
  1.1281 +    }
  1.1282 +}
  1.1283 +
  1.1284 +/*static*/ void
  1.1285 +gfxFontFamily::ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
  1.1286 +                                           const char *aNameData,
  1.1287 +                                           uint32_t aDataLength,
  1.1288 +                                           nsTArray<nsString>& aOtherFamilyNames,
  1.1289 +                                           bool useFullName)
  1.1290 +{
  1.1291 +    const gfxFontUtils::NameHeader *nameHeader =
  1.1292 +        reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
  1.1293 +
  1.1294 +    uint32_t nameCount = nameHeader->count;
  1.1295 +    if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
  1.1296 +        NS_WARNING("invalid font (name records)");
  1.1297 +        return;
  1.1298 +    }
  1.1299 +    
  1.1300 +    const gfxFontUtils::NameRecord *nameRecord =
  1.1301 +        reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader));
  1.1302 +    uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
  1.1303 +
  1.1304 +    for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
  1.1305 +        uint32_t nameLen = nameRecord->length;
  1.1306 +        uint32_t nameOff = nameRecord->offset;  // offset from base of string storage
  1.1307 +
  1.1308 +        if (stringsBase + nameOff + nameLen > aDataLength) {
  1.1309 +            NS_WARNING("invalid font (name table strings)");
  1.1310 +            return;
  1.1311 +        }
  1.1312 +
  1.1313 +        uint16_t nameID = nameRecord->nameID;
  1.1314 +        if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
  1.1315 +            (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
  1.1316 +                              nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
  1.1317 +            nsAutoString otherFamilyName;
  1.1318 +            bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff,
  1.1319 +                                                     nameLen,
  1.1320 +                                                     uint32_t(nameRecord->platformID),
  1.1321 +                                                     uint32_t(nameRecord->encodingID),
  1.1322 +                                                     uint32_t(nameRecord->languageID),
  1.1323 +                                                     otherFamilyName);
  1.1324 +            // add if not same as canonical family name
  1.1325 +            if (ok && otherFamilyName != aFamilyName) {
  1.1326 +                aOtherFamilyNames.AppendElement(otherFamilyName);
  1.1327 +            }
  1.1328 +        }
  1.1329 +    }
  1.1330 +}
  1.1331 +
  1.1332 +// returns true if other names were found, false otherwise
  1.1333 +bool
  1.1334 +gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
  1.1335 +                                           hb_blob_t           *aNameTable,
  1.1336 +                                           bool                 useFullName)
  1.1337 +{
  1.1338 +    uint32_t dataLength;
  1.1339 +    const char *nameData = hb_blob_get_data(aNameTable, &dataLength);
  1.1340 +    nsAutoTArray<nsString,4> otherFamilyNames;
  1.1341 +
  1.1342 +    ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
  1.1343 +                                otherFamilyNames, useFullName);
  1.1344 +
  1.1345 +    uint32_t n = otherFamilyNames.Length();
  1.1346 +    for (uint32_t i = 0; i < n; i++) {
  1.1347 +        aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
  1.1348 +    }
  1.1349 +
  1.1350 +    return n != 0;
  1.1351 +}
  1.1352 +
  1.1353 +void
  1.1354 +gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
  1.1355 +{
  1.1356 +    if (mOtherFamilyNamesInitialized) 
  1.1357 +        return;
  1.1358 +    mOtherFamilyNamesInitialized = true;
  1.1359 +
  1.1360 +    FindStyleVariations();
  1.1361 +
  1.1362 +    // read in other family names for the first face in the list
  1.1363 +    uint32_t i, numFonts = mAvailableFonts.Length();
  1.1364 +    const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
  1.1365 +
  1.1366 +    for (i = 0; i < numFonts; ++i) {
  1.1367 +        gfxFontEntry *fe = mAvailableFonts[i];
  1.1368 +        if (!fe) {
  1.1369 +            continue;
  1.1370 +        }
  1.1371 +        gfxFontEntry::AutoTable nameTable(fe, kNAME);
  1.1372 +        if (!nameTable) {
  1.1373 +            continue;
  1.1374 +        }
  1.1375 +        mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
  1.1376 +                                                           nameTable);
  1.1377 +        break;
  1.1378 +    }
  1.1379 +
  1.1380 +    // read in other names for the first face in the list with the assumption
  1.1381 +    // that if extra names don't exist in that face then they don't exist in
  1.1382 +    // other faces for the same font
  1.1383 +    if (!mHasOtherFamilyNames) 
  1.1384 +        return;
  1.1385 +
  1.1386 +    // read in names for all faces, needed to catch cases where fonts have
  1.1387 +    // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
  1.1388 +    for ( ; i < numFonts; i++) {
  1.1389 +        gfxFontEntry *fe = mAvailableFonts[i];
  1.1390 +        if (!fe) {
  1.1391 +            continue;
  1.1392 +        }
  1.1393 +        gfxFontEntry::AutoTable nameTable(fe, kNAME);
  1.1394 +        if (!nameTable) {
  1.1395 +            continue;
  1.1396 +        }
  1.1397 +        ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
  1.1398 +    }
  1.1399 +}
  1.1400 +
  1.1401 +void
  1.1402 +gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, 
  1.1403 +                             bool aNeedFullnamePostscriptNames,
  1.1404 +                             FontInfoData *aFontInfoData)
  1.1405 +{
  1.1406 +    // if all needed names have already been read, skip
  1.1407 +    if (mOtherFamilyNamesInitialized &&
  1.1408 +        (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
  1.1409 +        return;
  1.1410 +
  1.1411 +    bool asyncFontLoaderDisabled = false;
  1.1412 +
  1.1413 +#if defined(XP_MACOSX)
  1.1414 +    // bug 975460 - async font loader crashes sometimes under 10.6, disable
  1.1415 +    if (!nsCocoaFeatures::OnLionOrLater()) {
  1.1416 +        asyncFontLoaderDisabled = true;
  1.1417 +    }
  1.1418 +#endif
  1.1419 +
  1.1420 +    if (!mOtherFamilyNamesInitialized &&
  1.1421 +        aFontInfoData &&
  1.1422 +        aFontInfoData->mLoadOtherNames &&
  1.1423 +        !asyncFontLoaderDisabled)
  1.1424 +    {
  1.1425 +        nsAutoTArray<nsString,4> otherFamilyNames;
  1.1426 +        bool foundOtherNames =
  1.1427 +            aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames);
  1.1428 +        if (foundOtherNames) {
  1.1429 +            uint32_t i, n = otherFamilyNames.Length();
  1.1430 +            for (i = 0; i < n; i++) {
  1.1431 +                aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
  1.1432 +            }
  1.1433 +        }
  1.1434 +        mOtherFamilyNamesInitialized = true;
  1.1435 +    }
  1.1436 +
  1.1437 +    // if all needed data has been initialized, return
  1.1438 +    if (mOtherFamilyNamesInitialized &&
  1.1439 +        (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
  1.1440 +        return;
  1.1441 +    }
  1.1442 +
  1.1443 +    FindStyleVariations(aFontInfoData);
  1.1444 +
  1.1445 +    // check again, as style enumeration code may have loaded names
  1.1446 +    if (mOtherFamilyNamesInitialized &&
  1.1447 +        (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
  1.1448 +        return;
  1.1449 +    }
  1.1450 +
  1.1451 +    uint32_t i, numFonts = mAvailableFonts.Length();
  1.1452 +    const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
  1.1453 +
  1.1454 +    bool firstTime = true, readAllFaces = false;
  1.1455 +    for (i = 0; i < numFonts; ++i) {
  1.1456 +        gfxFontEntry *fe = mAvailableFonts[i];
  1.1457 +        if (!fe) {
  1.1458 +            continue;
  1.1459 +        }
  1.1460 +
  1.1461 +        nsAutoString fullname, psname;
  1.1462 +        bool foundFaceNames = false;
  1.1463 +        if (!mFaceNamesInitialized &&
  1.1464 +            aNeedFullnamePostscriptNames &&
  1.1465 +            aFontInfoData &&
  1.1466 +            aFontInfoData->mLoadFaceNames) {
  1.1467 +            aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
  1.1468 +            if (!fullname.IsEmpty()) {
  1.1469 +                aPlatformFontList->AddFullname(fe, fullname);
  1.1470 +            }
  1.1471 +            if (!psname.IsEmpty()) {
  1.1472 +                aPlatformFontList->AddPostscriptName(fe, psname);
  1.1473 +            }
  1.1474 +            foundFaceNames = true;
  1.1475 +
  1.1476 +            // found everything needed? skip to next font
  1.1477 +            if (mOtherFamilyNamesInitialized) {
  1.1478 +                continue;
  1.1479 +            }
  1.1480 +        }
  1.1481 +
  1.1482 +        // load directly from the name table
  1.1483 +        gfxFontEntry::AutoTable nameTable(fe, kNAME);
  1.1484 +        if (!nameTable) {
  1.1485 +            continue;
  1.1486 +        }
  1.1487 +
  1.1488 +        if (aNeedFullnamePostscriptNames && !foundFaceNames) {
  1.1489 +            if (gfxFontUtils::ReadCanonicalName(
  1.1490 +                    nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
  1.1491 +            {
  1.1492 +                aPlatformFontList->AddFullname(fe, fullname);
  1.1493 +            }
  1.1494 +
  1.1495 +            if (gfxFontUtils::ReadCanonicalName(
  1.1496 +                    nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
  1.1497 +            {
  1.1498 +                aPlatformFontList->AddPostscriptName(fe, psname);
  1.1499 +            }
  1.1500 +        }
  1.1501 +
  1.1502 +        if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
  1.1503 +            bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList,
  1.1504 +                                                              nameTable);
  1.1505 +
  1.1506 +            // if the first face has a different name, scan all faces, otherwise
  1.1507 +            // assume the family doesn't have other names
  1.1508 +            if (firstTime && foundOtherName) {
  1.1509 +                mHasOtherFamilyNames = true;
  1.1510 +                readAllFaces = true;
  1.1511 +            }
  1.1512 +            firstTime = false;
  1.1513 +        }
  1.1514 +
  1.1515 +        // if not reading in any more names, skip other faces
  1.1516 +        if (!readAllFaces && !aNeedFullnamePostscriptNames) {
  1.1517 +            break;
  1.1518 +        }
  1.1519 +    }
  1.1520 +
  1.1521 +    mFaceNamesInitialized = true;
  1.1522 +    mOtherFamilyNamesInitialized = true;
  1.1523 +}
  1.1524 +
  1.1525 +
  1.1526 +gfxFontEntry*
  1.1527 +gfxFontFamily::FindFont(const nsAString& aPostscriptName)
  1.1528 +{
  1.1529 +    // find the font using a simple linear search
  1.1530 +    uint32_t numFonts = mAvailableFonts.Length();
  1.1531 +    for (uint32_t i = 0; i < numFonts; i++) {
  1.1532 +        gfxFontEntry *fe = mAvailableFonts[i].get();
  1.1533 +        if (fe && fe->Name() == aPostscriptName)
  1.1534 +            return fe;
  1.1535 +    }
  1.1536 +    return nullptr;
  1.1537 +}
  1.1538 +
  1.1539 +void
  1.1540 +gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData)
  1.1541 +{
  1.1542 +    FindStyleVariations(aFontInfoData);
  1.1543 +
  1.1544 +    uint32_t i, numFonts = mAvailableFonts.Length();
  1.1545 +    for (i = 0; i < numFonts; i++) {
  1.1546 +        gfxFontEntry *fe = mAvailableFonts[i];
  1.1547 +        // don't try to load cmaps for downloadable fonts not yet loaded
  1.1548 +        if (!fe || fe->mIsProxy) {
  1.1549 +            continue;
  1.1550 +        }
  1.1551 +        fe->ReadCMAP(aFontInfoData);
  1.1552 +        mFamilyCharacterMap.Union(*(fe->mCharacterMap));
  1.1553 +    }
  1.1554 +    mFamilyCharacterMap.Compact();
  1.1555 +    mFamilyCharacterMapInitialized = true;
  1.1556 +}
  1.1557 +
  1.1558 +void
  1.1559 +gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
  1.1560 +                                      FontListSizes* aSizes) const
  1.1561 +{
  1.1562 +    aSizes->mFontListSize +=
  1.1563 +        mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
  1.1564 +    aSizes->mCharMapsSize +=
  1.1565 +        mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
  1.1566 +
  1.1567 +    aSizes->mFontListSize +=
  1.1568 +        mAvailableFonts.SizeOfExcludingThis(aMallocSizeOf);
  1.1569 +    for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
  1.1570 +        gfxFontEntry *fe = mAvailableFonts[i];
  1.1571 +        if (fe) {
  1.1572 +            fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
  1.1573 +        }
  1.1574 +    }
  1.1575 +}
  1.1576 +
  1.1577 +void
  1.1578 +gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
  1.1579 +                                      FontListSizes* aSizes) const
  1.1580 +{
  1.1581 +    aSizes->mFontListSize += aMallocSizeOf(this);
  1.1582 +    AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
  1.1583 +}
  1.1584 +
  1.1585 +/*
  1.1586 + * gfxFontCache - global cache of gfxFont instances.
  1.1587 + * Expires unused fonts after a short interval;
  1.1588 + * notifies fonts to age their cached shaped-word records;
  1.1589 + * observes memory-pressure notification and tells fonts to clear their
  1.1590 + * shaped-word caches to free up memory.
  1.1591 + */
  1.1592 +
  1.1593 +MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
  1.1594 +
  1.1595 +NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
  1.1596 +
  1.1597 +NS_IMETHODIMP
  1.1598 +gfxFontCache::MemoryReporter::CollectReports
  1.1599 +    (nsIMemoryReporterCallback* aCb,
  1.1600 +     nsISupports* aClosure)
  1.1601 +{
  1.1602 +    FontCacheSizes sizes;
  1.1603 +
  1.1604 +    gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
  1.1605 +                                                     &sizes);
  1.1606 +
  1.1607 +    aCb->Callback(EmptyCString(),
  1.1608 +                  NS_LITERAL_CSTRING("explicit/gfx/font-cache"),
  1.1609 +                  KIND_HEAP, UNITS_BYTES, sizes.mFontInstances,
  1.1610 +                  NS_LITERAL_CSTRING("Memory used for active font instances."),
  1.1611 +                  aClosure);
  1.1612 +
  1.1613 +    aCb->Callback(EmptyCString(),
  1.1614 +                  NS_LITERAL_CSTRING("explicit/gfx/font-shaped-words"),
  1.1615 +                  KIND_HEAP, UNITS_BYTES, sizes.mShapedWords,
  1.1616 +                  NS_LITERAL_CSTRING("Memory used to cache shaped glyph data."),
  1.1617 +                  aClosure);
  1.1618 +
  1.1619 +    return NS_OK;
  1.1620 +}
  1.1621 +
  1.1622 +NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
  1.1623 +
  1.1624 +NS_IMETHODIMP
  1.1625 +gfxFontCache::Observer::Observe(nsISupports *aSubject,
  1.1626 +                                const char *aTopic,
  1.1627 +                                const char16_t *someData)
  1.1628 +{
  1.1629 +    if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
  1.1630 +        gfxFontCache *fontCache = gfxFontCache::GetCache();
  1.1631 +        if (fontCache) {
  1.1632 +            fontCache->FlushShapedWordCaches();
  1.1633 +        }
  1.1634 +    } else {
  1.1635 +        NS_NOTREACHED("unexpected notification topic");
  1.1636 +    }
  1.1637 +    return NS_OK;
  1.1638 +}
  1.1639 +
  1.1640 +nsresult
  1.1641 +gfxFontCache::Init()
  1.1642 +{
  1.1643 +    NS_ASSERTION(!gGlobalCache, "Where did this come from?");
  1.1644 +    gGlobalCache = new gfxFontCache();
  1.1645 +    if (!gGlobalCache) {
  1.1646 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1647 +    }
  1.1648 +    RegisterStrongMemoryReporter(new MemoryReporter());
  1.1649 +    return NS_OK;
  1.1650 +}
  1.1651 +
  1.1652 +void
  1.1653 +gfxFontCache::Shutdown()
  1.1654 +{
  1.1655 +    delete gGlobalCache;
  1.1656 +    gGlobalCache = nullptr;
  1.1657 +
  1.1658 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.1659 +    printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
  1.1660 +    printf("Total number of fonts=%d\n", gFontCount);
  1.1661 +    printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
  1.1662 +            int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
  1.1663 +    printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
  1.1664 +    printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
  1.1665 +    printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
  1.1666 +    printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
  1.1667 +    printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
  1.1668 +#endif
  1.1669 +}
  1.1670 +
  1.1671 +gfxFontCache::gfxFontCache()
  1.1672 +    : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000)
  1.1673 +{
  1.1674 +    nsCOMPtr<nsIObserverService> obs = GetObserverService();
  1.1675 +    if (obs) {
  1.1676 +        obs->AddObserver(new Observer, "memory-pressure", false);
  1.1677 +    }
  1.1678 +
  1.1679 +#ifndef RELEASE_BUILD
  1.1680 +    // Currently disabled for release builds, due to unexplained crashes
  1.1681 +    // during expiration; see bug 717175 & 894798.
  1.1682 +    mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
  1.1683 +    if (mWordCacheExpirationTimer) {
  1.1684 +        mWordCacheExpirationTimer->
  1.1685 +            InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
  1.1686 +                                 SHAPED_WORD_TIMEOUT_SECONDS * 1000,
  1.1687 +                                 nsITimer::TYPE_REPEATING_SLACK);
  1.1688 +    }
  1.1689 +#endif
  1.1690 +}
  1.1691 +
  1.1692 +gfxFontCache::~gfxFontCache()
  1.1693 +{
  1.1694 +    // Ensure the user font cache releases its references to font entries,
  1.1695 +    // so they aren't kept alive after the font instances and font-list
  1.1696 +    // have been shut down.
  1.1697 +    gfxUserFontSet::UserFontCache::Shutdown();
  1.1698 +
  1.1699 +    if (mWordCacheExpirationTimer) {
  1.1700 +        mWordCacheExpirationTimer->Cancel();
  1.1701 +        mWordCacheExpirationTimer = nullptr;
  1.1702 +    }
  1.1703 +
  1.1704 +    // Expire everything that has a zero refcount, so we don't leak them.
  1.1705 +    AgeAllGenerations();
  1.1706 +    // All fonts should be gone.
  1.1707 +    NS_WARN_IF_FALSE(mFonts.Count() == 0,
  1.1708 +                     "Fonts still alive while shutting down gfxFontCache");
  1.1709 +    // Note that we have to delete everything through the expiration
  1.1710 +    // tracker, since there might be fonts not in the hashtable but in
  1.1711 +    // the tracker.
  1.1712 +}
  1.1713 +
  1.1714 +bool
  1.1715 +gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
  1.1716 +{
  1.1717 +    return aKey->mFontEntry == mFont->GetFontEntry() &&
  1.1718 +           aKey->mStyle->Equals(*mFont->GetStyle());
  1.1719 +}
  1.1720 +
  1.1721 +already_AddRefed<gfxFont>
  1.1722 +gfxFontCache::Lookup(const gfxFontEntry *aFontEntry,
  1.1723 +                     const gfxFontStyle *aStyle)
  1.1724 +{
  1.1725 +    Key key(aFontEntry, aStyle);
  1.1726 +    HashEntry *entry = mFonts.GetEntry(key);
  1.1727 +
  1.1728 +    Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nullptr);
  1.1729 +    if (!entry)
  1.1730 +        return nullptr;
  1.1731 +
  1.1732 +    nsRefPtr<gfxFont> font = entry->mFont;
  1.1733 +    return font.forget();
  1.1734 +}
  1.1735 +
  1.1736 +void
  1.1737 +gfxFontCache::AddNew(gfxFont *aFont)
  1.1738 +{
  1.1739 +    Key key(aFont->GetFontEntry(), aFont->GetStyle());
  1.1740 +    HashEntry *entry = mFonts.PutEntry(key);
  1.1741 +    if (!entry)
  1.1742 +        return;
  1.1743 +    gfxFont *oldFont = entry->mFont;
  1.1744 +    entry->mFont = aFont;
  1.1745 +    // Assert that we can find the entry we just put in (this fails if the key
  1.1746 +    // has a NaN float value in it, e.g. 'sizeAdjust').
  1.1747 +    MOZ_ASSERT(entry == mFonts.GetEntry(key));
  1.1748 +    // If someone's asked us to replace an existing font entry, then that's a
  1.1749 +    // bit weird, but let it happen, and expire the old font if it's not used.
  1.1750 +    if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
  1.1751 +        // if oldFont == aFont, recount should be > 0,
  1.1752 +        // so we shouldn't be here.
  1.1753 +        NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
  1.1754 +        NotifyExpired(oldFont);
  1.1755 +    }
  1.1756 +}
  1.1757 +
  1.1758 +void
  1.1759 +gfxFontCache::NotifyReleased(gfxFont *aFont)
  1.1760 +{
  1.1761 +    nsresult rv = AddObject(aFont);
  1.1762 +    if (NS_FAILED(rv)) {
  1.1763 +        // We couldn't track it for some reason. Kill it now.
  1.1764 +        DestroyFont(aFont);
  1.1765 +    }
  1.1766 +    // Note that we might have fonts that aren't in the hashtable, perhaps because
  1.1767 +    // of OOM adding to the hashtable or because someone did an AddNew where
  1.1768 +    // we already had a font. These fonts are added to the expiration tracker
  1.1769 +    // anyway, even though Lookup can't resurrect them. Eventually they will
  1.1770 +    // expire and be deleted.
  1.1771 +}
  1.1772 +
  1.1773 +void
  1.1774 +gfxFontCache::NotifyExpired(gfxFont *aFont)
  1.1775 +{
  1.1776 +    aFont->ClearCachedWords();
  1.1777 +    RemoveObject(aFont);
  1.1778 +    DestroyFont(aFont);
  1.1779 +}
  1.1780 +
  1.1781 +void
  1.1782 +gfxFontCache::DestroyFont(gfxFont *aFont)
  1.1783 +{
  1.1784 +    Key key(aFont->GetFontEntry(), aFont->GetStyle());
  1.1785 +    HashEntry *entry = mFonts.GetEntry(key);
  1.1786 +    if (entry && entry->mFont == aFont) {
  1.1787 +        mFonts.RemoveEntry(key);
  1.1788 +    }
  1.1789 +    NS_ASSERTION(aFont->GetRefCount() == 0,
  1.1790 +                 "Destroying with non-zero ref count!");
  1.1791 +    delete aFont;
  1.1792 +}
  1.1793 +
  1.1794 +/*static*/
  1.1795 +PLDHashOperator
  1.1796 +gfxFontCache::AgeCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
  1.1797 +{
  1.1798 +    aHashEntry->mFont->AgeCachedWords();
  1.1799 +    return PL_DHASH_NEXT;
  1.1800 +}
  1.1801 +
  1.1802 +/*static*/
  1.1803 +void
  1.1804 +gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
  1.1805 +{
  1.1806 +    gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
  1.1807 +    cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nullptr);
  1.1808 +}
  1.1809 +
  1.1810 +/*static*/
  1.1811 +PLDHashOperator
  1.1812 +gfxFontCache::ClearCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
  1.1813 +{
  1.1814 +    aHashEntry->mFont->ClearCachedWords();
  1.1815 +    return PL_DHASH_NEXT;
  1.1816 +}
  1.1817 +
  1.1818 +/*static*/
  1.1819 +size_t
  1.1820 +gfxFontCache::AddSizeOfFontEntryExcludingThis(HashEntry* aHashEntry,
  1.1821 +                                              MallocSizeOf aMallocSizeOf,
  1.1822 +                                              void* aUserArg)
  1.1823 +{
  1.1824 +    HashEntry *entry = static_cast<HashEntry*>(aHashEntry);
  1.1825 +    FontCacheSizes *sizes = static_cast<FontCacheSizes*>(aUserArg);
  1.1826 +    entry->mFont->AddSizeOfExcludingThis(aMallocSizeOf, sizes);
  1.1827 +
  1.1828 +    // The entry's size is recorded in the |sizes| parameter, so we return zero
  1.1829 +    // here to the hashtable enumerator.
  1.1830 +    return 0;
  1.1831 +}
  1.1832 +
  1.1833 +void
  1.1834 +gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
  1.1835 +                                     FontCacheSizes* aSizes) const
  1.1836 +{
  1.1837 +    // TODO: add the overhead of the expiration tracker (generation arrays)
  1.1838 +
  1.1839 +    aSizes->mFontInstances +=
  1.1840 +        mFonts.SizeOfExcludingThis(AddSizeOfFontEntryExcludingThis,
  1.1841 +                                   aMallocSizeOf, aSizes);
  1.1842 +}
  1.1843 +
  1.1844 +void
  1.1845 +gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
  1.1846 +                                     FontCacheSizes* aSizes) const
  1.1847 +{
  1.1848 +    aSizes->mFontInstances += aMallocSizeOf(this);
  1.1849 +    AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
  1.1850 +}
  1.1851 +
  1.1852 +#define MAX_SSXX_VALUE 99
  1.1853 +#define MAX_CVXX_VALUE 99
  1.1854 +
  1.1855 +static void
  1.1856 +LookupAlternateValues(gfxFontFeatureValueSet *featureLookup,
  1.1857 +                      const nsAString& aFamily,
  1.1858 +                      const nsTArray<gfxAlternateValue>& altValue,
  1.1859 +                      nsTArray<gfxFontFeature>& aFontFeatures)
  1.1860 +{
  1.1861 +    uint32_t numAlternates = altValue.Length();
  1.1862 +    for (uint32_t i = 0; i < numAlternates; i++) {
  1.1863 +        const gfxAlternateValue& av = altValue.ElementAt(i);
  1.1864 +        nsAutoTArray<uint32_t,4> values;
  1.1865 +
  1.1866 +        // map <family, name, feature> ==> <values>
  1.1867 +        bool found =
  1.1868 +            featureLookup->GetFontFeatureValuesFor(aFamily, av.alternate,
  1.1869 +                                                   av.value, values);
  1.1870 +        uint32_t numValues = values.Length();
  1.1871 +
  1.1872 +        // nothing defined, skip
  1.1873 +        if (!found || numValues == 0) {
  1.1874 +            continue;
  1.1875 +        }
  1.1876 +
  1.1877 +        gfxFontFeature feature;
  1.1878 +        if (av.alternate == NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT) {
  1.1879 +            NS_ASSERTION(numValues <= 2,
  1.1880 +                         "too many values allowed for character-variant");
  1.1881 +            // character-variant(12 3) ==> 'cv12' = 3
  1.1882 +            uint32_t nn = values.ElementAt(0);
  1.1883 +            // ignore values greater than 99
  1.1884 +            if (nn == 0 || nn > MAX_CVXX_VALUE) {
  1.1885 +                continue;
  1.1886 +            }
  1.1887 +            feature.mValue = 1;
  1.1888 +            if (numValues > 1) {
  1.1889 +                feature.mValue = values.ElementAt(1);
  1.1890 +            }
  1.1891 +            feature.mTag = HB_TAG('c','v',('0' + nn / 10), ('0' + nn % 10));
  1.1892 +            aFontFeatures.AppendElement(feature);
  1.1893 +
  1.1894 +        } else if (av.alternate == NS_FONT_VARIANT_ALTERNATES_STYLESET) {
  1.1895 +            // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
  1.1896 +            feature.mValue = 1;
  1.1897 +            for (uint32_t v = 0; v < numValues; v++) {
  1.1898 +                uint32_t nn = values.ElementAt(v);
  1.1899 +                if (nn == 0 || nn > MAX_SSXX_VALUE) {
  1.1900 +                    continue;
  1.1901 +                }
  1.1902 +                feature.mTag = HB_TAG('s','s',('0' + nn / 10), ('0' + nn % 10));
  1.1903 +                aFontFeatures.AppendElement(feature);
  1.1904 +            }
  1.1905 +
  1.1906 +        } else {
  1.1907 +            NS_ASSERTION(numValues == 1,
  1.1908 +                   "too many values for font-specific font-variant-alternates");
  1.1909 +            feature.mValue = values.ElementAt(0);
  1.1910 +
  1.1911 +            switch (av.alternate) {
  1.1912 +                case NS_FONT_VARIANT_ALTERNATES_STYLISTIC:  // salt
  1.1913 +                    feature.mTag = HB_TAG('s','a','l','t');
  1.1914 +                    break;
  1.1915 +                case NS_FONT_VARIANT_ALTERNATES_SWASH:  // swsh, cswh
  1.1916 +                    feature.mTag = HB_TAG('s','w','s','h');
  1.1917 +                    aFontFeatures.AppendElement(feature);
  1.1918 +                    feature.mTag = HB_TAG('c','s','w','h');
  1.1919 +                    break;
  1.1920 +                case NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: // ornm
  1.1921 +                    feature.mTag = HB_TAG('o','r','n','m');
  1.1922 +                    break;
  1.1923 +                case NS_FONT_VARIANT_ALTERNATES_ANNOTATION: // nalt
  1.1924 +                    feature.mTag = HB_TAG('n','a','l','t');
  1.1925 +                    break;
  1.1926 +                default:
  1.1927 +                    feature.mTag = 0;
  1.1928 +                    break;
  1.1929 +            }
  1.1930 +
  1.1931 +            NS_ASSERTION(feature.mTag, "unsupported alternate type");
  1.1932 +            if (!feature.mTag) {
  1.1933 +                continue;
  1.1934 +            }
  1.1935 +            aFontFeatures.AppendElement(feature);
  1.1936 +        }
  1.1937 +    }
  1.1938 +}
  1.1939 +
  1.1940 +/* static */ bool
  1.1941 +gfxFontShaper::MergeFontFeatures(
  1.1942 +    const gfxFontStyle *aStyle,
  1.1943 +    const nsTArray<gfxFontFeature>& aFontFeatures,
  1.1944 +    bool aDisableLigatures,
  1.1945 +    const nsAString& aFamilyName,
  1.1946 +    nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures)
  1.1947 +{
  1.1948 +    uint32_t numAlts = aStyle->alternateValues.Length();
  1.1949 +    const nsTArray<gfxFontFeature>& styleRuleFeatures =
  1.1950 +        aStyle->featureSettings;
  1.1951 +
  1.1952 +    // bail immediately if nothing to do
  1.1953 +    if (styleRuleFeatures.IsEmpty() &&
  1.1954 +        aFontFeatures.IsEmpty() &&
  1.1955 +        !aDisableLigatures &&
  1.1956 +        numAlts == 0) {
  1.1957 +        return false;
  1.1958 +    }
  1.1959 +
  1.1960 +    // Ligature features are enabled by default in the generic shaper,
  1.1961 +    // so we explicitly turn them off if necessary (for letter-spacing)
  1.1962 +    if (aDisableLigatures) {
  1.1963 +        aMergedFeatures.Put(HB_TAG('l','i','g','a'), 0);
  1.1964 +        aMergedFeatures.Put(HB_TAG('c','l','i','g'), 0);
  1.1965 +    }
  1.1966 +
  1.1967 +    // add feature values from font
  1.1968 +    uint32_t i, count;
  1.1969 +
  1.1970 +    count = aFontFeatures.Length();
  1.1971 +    for (i = 0; i < count; i++) {
  1.1972 +        const gfxFontFeature& feature = aFontFeatures.ElementAt(i);
  1.1973 +        aMergedFeatures.Put(feature.mTag, feature.mValue);
  1.1974 +    }
  1.1975 +
  1.1976 +    // add font-specific feature values from style rules
  1.1977 +    if (aStyle->featureValueLookup && numAlts > 0) {
  1.1978 +        nsAutoTArray<gfxFontFeature,4> featureList;
  1.1979 +
  1.1980 +        // insert list of alternate feature settings
  1.1981 +        LookupAlternateValues(aStyle->featureValueLookup, aFamilyName,
  1.1982 +                              aStyle->alternateValues, featureList);
  1.1983 +
  1.1984 +        count = featureList.Length();
  1.1985 +        for (i = 0; i < count; i++) {
  1.1986 +            const gfxFontFeature& feature = featureList.ElementAt(i);
  1.1987 +            aMergedFeatures.Put(feature.mTag, feature.mValue);
  1.1988 +        }
  1.1989 +    }
  1.1990 +
  1.1991 +    // add feature values from style rules
  1.1992 +    count = styleRuleFeatures.Length();
  1.1993 +    for (i = 0; i < count; i++) {
  1.1994 +        const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i);
  1.1995 +        aMergedFeatures.Put(feature.mTag, feature.mValue);
  1.1996 +    }
  1.1997 +
  1.1998 +    return aMergedFeatures.Count() != 0;
  1.1999 +}
  1.2000 +
  1.2001 +void
  1.2002 +gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
  1.2003 +{
  1.2004 +    mAscent = std::max(mAscent, aOther.mAscent);
  1.2005 +    mDescent = std::max(mDescent, aOther.mDescent);
  1.2006 +    if (aOtherIsOnLeft) {
  1.2007 +        mBoundingBox =
  1.2008 +            (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
  1.2009 +    } else {
  1.2010 +        mBoundingBox =
  1.2011 +            mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
  1.2012 +    }
  1.2013 +    mAdvanceWidth += aOther.mAdvanceWidth;
  1.2014 +}
  1.2015 +
  1.2016 +gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
  1.2017 +                 AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
  1.2018 +    mScaledFont(aScaledFont),
  1.2019 +    mFontEntry(aFontEntry), mIsValid(true),
  1.2020 +    mApplySyntheticBold(false),
  1.2021 +    mStyle(*aFontStyle),
  1.2022 +    mAdjustedSize(0.0),
  1.2023 +    mFUnitsConvFactor(0.0f),
  1.2024 +    mAntialiasOption(anAAOption)
  1.2025 +{
  1.2026 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.2027 +    ++gFontCount;
  1.2028 +#endif
  1.2029 +    mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
  1.2030 +}
  1.2031 +
  1.2032 +static PLDHashOperator
  1.2033 +NotifyFontDestroyed(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
  1.2034 +                    void* aClosure)
  1.2035 +{
  1.2036 +    aKey->GetKey()->ForgetFont();
  1.2037 +    return PL_DHASH_NEXT;
  1.2038 +}
  1.2039 +
  1.2040 +gfxFont::~gfxFont()
  1.2041 +{
  1.2042 +    uint32_t i, count = mGlyphExtentsArray.Length();
  1.2043 +    // We destroy the contents of mGlyphExtentsArray explicitly instead of
  1.2044 +    // using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
  1.2045 +    // of classes that lack a proper copy constructor
  1.2046 +    for (i = 0; i < count; ++i) {
  1.2047 +        delete mGlyphExtentsArray[i];
  1.2048 +    }
  1.2049 +
  1.2050 +    mFontEntry->NotifyFontDestroyed(this);
  1.2051 +
  1.2052 +    if (mGlyphChangeObservers) {
  1.2053 +        mGlyphChangeObservers->EnumerateEntries(NotifyFontDestroyed, nullptr);
  1.2054 +    }
  1.2055 +}
  1.2056 +
  1.2057 +gfxFloat
  1.2058 +gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
  1.2059 +{
  1.2060 +    if (!SetupCairoFont(aCtx)) {
  1.2061 +        return 0;
  1.2062 +    }
  1.2063 +    if (ProvidesGlyphWidths()) {
  1.2064 +        return GetGlyphWidth(aCtx, aGID) / 65536.0;
  1.2065 +    }
  1.2066 +    if (mFUnitsConvFactor == 0.0f) {
  1.2067 +        GetMetrics();
  1.2068 +    }
  1.2069 +    NS_ASSERTION(mFUnitsConvFactor > 0.0f,
  1.2070 +                 "missing font unit conversion factor");
  1.2071 +    if (!mHarfBuzzShaper) {
  1.2072 +        mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
  1.2073 +    }
  1.2074 +    gfxHarfBuzzShaper* shaper =
  1.2075 +        static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
  1.2076 +    if (!shaper->Initialize()) {
  1.2077 +        return 0;
  1.2078 +    }
  1.2079 +    return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0;
  1.2080 +}
  1.2081 +
  1.2082 +/*static*/
  1.2083 +PLDHashOperator
  1.2084 +gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
  1.2085 +{
  1.2086 +    if (!aEntry->mShapedWord) {
  1.2087 +        NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
  1.2088 +        return PL_DHASH_REMOVE;
  1.2089 +    }
  1.2090 +    if (aEntry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) {
  1.2091 +        return PL_DHASH_REMOVE;
  1.2092 +    }
  1.2093 +    return PL_DHASH_NEXT;
  1.2094 +}
  1.2095 +
  1.2096 +static void
  1.2097 +CollectLookupsByFeature(hb_face_t *aFace, hb_tag_t aTableTag,
  1.2098 +                        uint32_t aFeatureIndex, hb_set_t *aLookups)
  1.2099 +{
  1.2100 +    uint32_t lookups[32];
  1.2101 +    uint32_t i, len, offset;
  1.2102 +
  1.2103 +    offset = 0;
  1.2104 +    do {
  1.2105 +        len = ArrayLength(lookups);
  1.2106 +        hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex,
  1.2107 +                                         offset, &len, lookups);
  1.2108 +        for (i = 0; i < len; i++) {
  1.2109 +            hb_set_add(aLookups, lookups[i]);
  1.2110 +        }
  1.2111 +        offset += len;
  1.2112 +    } while (len == ArrayLength(lookups));
  1.2113 +}
  1.2114 +
  1.2115 +static void
  1.2116 +CollectLookupsByLanguage(hb_face_t *aFace, hb_tag_t aTableTag,
  1.2117 +                         const nsTHashtable<nsUint32HashKey>&
  1.2118 +                             aSpecificFeatures,
  1.2119 +                         hb_set_t *aOtherLookups,
  1.2120 +                         hb_set_t *aSpecificFeatureLookups,
  1.2121 +                         uint32_t aScriptIndex, uint32_t aLangIndex)
  1.2122 +{
  1.2123 +    uint32_t reqFeatureIndex;
  1.2124 +    if (hb_ot_layout_language_get_required_feature_index(aFace, aTableTag,
  1.2125 +                                                         aScriptIndex,
  1.2126 +                                                         aLangIndex,
  1.2127 +                                                         &reqFeatureIndex)) {
  1.2128 +        CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex,
  1.2129 +                                aOtherLookups);
  1.2130 +    }
  1.2131 +
  1.2132 +    uint32_t featureIndexes[32];
  1.2133 +    uint32_t i, len, offset;
  1.2134 +
  1.2135 +    offset = 0;
  1.2136 +    do {
  1.2137 +        len = ArrayLength(featureIndexes);
  1.2138 +        hb_ot_layout_language_get_feature_indexes(aFace, aTableTag,
  1.2139 +                                                  aScriptIndex, aLangIndex,
  1.2140 +                                                  offset, &len, featureIndexes);
  1.2141 +
  1.2142 +        for (i = 0; i < len; i++) {
  1.2143 +            uint32_t featureIndex = featureIndexes[i];
  1.2144 +
  1.2145 +            // get the feature tag
  1.2146 +            hb_tag_t featureTag;
  1.2147 +            uint32_t tagLen = 1;
  1.2148 +            hb_ot_layout_language_get_feature_tags(aFace, aTableTag,
  1.2149 +                                                   aScriptIndex, aLangIndex,
  1.2150 +                                                   offset + i, &tagLen,
  1.2151 +                                                   &featureTag);
  1.2152 +
  1.2153 +            // collect lookups
  1.2154 +            hb_set_t *lookups = aSpecificFeatures.GetEntry(featureTag) ?
  1.2155 +                                    aSpecificFeatureLookups : aOtherLookups;
  1.2156 +            CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
  1.2157 +        }
  1.2158 +        offset += len;
  1.2159 +    } while (len == ArrayLength(featureIndexes));
  1.2160 +}
  1.2161 +
  1.2162 +static bool
  1.2163 +HasLookupRuleWithGlyphByScript(hb_face_t *aFace, hb_tag_t aTableTag,
  1.2164 +                               hb_tag_t aScriptTag, uint32_t aScriptIndex,
  1.2165 +                               uint16_t aGlyph,
  1.2166 +                               const nsTHashtable<nsUint32HashKey>&
  1.2167 +                                   aDefaultFeatures,
  1.2168 +                               bool& aHasDefaultFeatureWithGlyph)
  1.2169 +{
  1.2170 +    uint32_t numLangs, lang;
  1.2171 +    hb_set_t *defaultFeatureLookups = hb_set_create();
  1.2172 +    hb_set_t *nonDefaultFeatureLookups = hb_set_create();
  1.2173 +
  1.2174 +    // default lang
  1.2175 +    CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
  1.2176 +                             nonDefaultFeatureLookups, defaultFeatureLookups,
  1.2177 +                             aScriptIndex,
  1.2178 +                             HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
  1.2179 +
  1.2180 +    // iterate over langs
  1.2181 +    numLangs = hb_ot_layout_script_get_language_tags(aFace, aTableTag,
  1.2182 +                                                     aScriptIndex, 0,
  1.2183 +                                                     nullptr, nullptr);
  1.2184 +    for (lang = 0; lang < numLangs; lang++) {
  1.2185 +        CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
  1.2186 +                                 nonDefaultFeatureLookups,
  1.2187 +                                 defaultFeatureLookups,
  1.2188 +                                 aScriptIndex, lang);
  1.2189 +    }
  1.2190 +
  1.2191 +    // look for the glyph among default feature lookups
  1.2192 +    aHasDefaultFeatureWithGlyph = false;
  1.2193 +    hb_set_t *glyphs = hb_set_create();
  1.2194 +    hb_codepoint_t index = -1;
  1.2195 +    while (hb_set_next(defaultFeatureLookups, &index)) {
  1.2196 +        hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
  1.2197 +                                           glyphs, glyphs, glyphs,
  1.2198 +                                           glyphs);
  1.2199 +        if (hb_set_has(glyphs, aGlyph)) {
  1.2200 +            aHasDefaultFeatureWithGlyph = true;
  1.2201 +            break;
  1.2202 +        }
  1.2203 +    }
  1.2204 +
  1.2205 +    // look for the glyph among non-default feature lookups
  1.2206 +    // if no default feature lookups contained spaces
  1.2207 +    bool hasNonDefaultFeatureWithGlyph = false;
  1.2208 +    if (!aHasDefaultFeatureWithGlyph) {
  1.2209 +        hb_set_clear(glyphs);
  1.2210 +        index = -1;
  1.2211 +        while (hb_set_next(nonDefaultFeatureLookups, &index)) {
  1.2212 +            hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
  1.2213 +                                               glyphs, glyphs, glyphs,
  1.2214 +                                               glyphs);
  1.2215 +            if (hb_set_has(glyphs, aGlyph)) {
  1.2216 +                hasNonDefaultFeatureWithGlyph = true;
  1.2217 +                break;
  1.2218 +            }
  1.2219 +        }
  1.2220 +    }
  1.2221 +
  1.2222 +    hb_set_destroy(glyphs);
  1.2223 +    hb_set_destroy(defaultFeatureLookups);
  1.2224 +    hb_set_destroy(nonDefaultFeatureLookups);
  1.2225 +
  1.2226 +    return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
  1.2227 +}
  1.2228 +
  1.2229 +static void
  1.2230 +HasLookupRuleWithGlyph(hb_face_t *aFace, hb_tag_t aTableTag, bool& aHasGlyph,
  1.2231 +                       hb_tag_t aSpecificFeature, bool& aHasGlyphSpecific,
  1.2232 +                       uint16_t aGlyph)
  1.2233 +{
  1.2234 +    // iterate over the scripts in the font
  1.2235 +    uint32_t numScripts, numLangs, script, lang;
  1.2236 +    hb_set_t *otherLookups = hb_set_create();
  1.2237 +    hb_set_t *specificFeatureLookups = hb_set_create();
  1.2238 +    nsTHashtable<nsUint32HashKey> specificFeature;
  1.2239 +
  1.2240 +    specificFeature.PutEntry(aSpecificFeature);
  1.2241 +
  1.2242 +    numScripts = hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0,
  1.2243 +                                                    nullptr, nullptr);
  1.2244 +
  1.2245 +    for (script = 0; script < numScripts; script++) {
  1.2246 +        // default lang
  1.2247 +        CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
  1.2248 +                                 otherLookups, specificFeatureLookups,
  1.2249 +                                 script, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
  1.2250 +
  1.2251 +        // iterate over langs
  1.2252 +        numLangs = hb_ot_layout_script_get_language_tags(aFace, HB_OT_TAG_GPOS,
  1.2253 +                                                         script, 0,
  1.2254 +                                                         nullptr, nullptr);
  1.2255 +        for (lang = 0; lang < numLangs; lang++) {
  1.2256 +            CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
  1.2257 +                                     otherLookups, specificFeatureLookups,
  1.2258 +                                     script, lang);
  1.2259 +        }
  1.2260 +    }
  1.2261 +
  1.2262 +    // look for the glyph among non-specific feature lookups
  1.2263 +    hb_set_t *glyphs = hb_set_create();
  1.2264 +    hb_codepoint_t index = -1;
  1.2265 +    while (hb_set_next(otherLookups, &index)) {
  1.2266 +        hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
  1.2267 +                                           glyphs, glyphs, glyphs,
  1.2268 +                                           glyphs);
  1.2269 +        if (hb_set_has(glyphs, aGlyph)) {
  1.2270 +            aHasGlyph = true;
  1.2271 +            break;
  1.2272 +        }
  1.2273 +    }
  1.2274 +
  1.2275 +    // look for the glyph among specific feature lookups
  1.2276 +    hb_set_clear(glyphs);
  1.2277 +    index = -1;
  1.2278 +    while (hb_set_next(specificFeatureLookups, &index)) {
  1.2279 +        hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
  1.2280 +                                           glyphs, glyphs, glyphs,
  1.2281 +                                           glyphs);
  1.2282 +        if (hb_set_has(glyphs, aGlyph)) {
  1.2283 +            aHasGlyphSpecific = true;
  1.2284 +            break;
  1.2285 +        }
  1.2286 +    }
  1.2287 +
  1.2288 +    hb_set_destroy(glyphs);
  1.2289 +    hb_set_destroy(specificFeatureLookups);
  1.2290 +    hb_set_destroy(otherLookups);
  1.2291 +}
  1.2292 +
  1.2293 +nsDataHashtable<nsUint32HashKey, int32_t> *gfxFont::sScriptTagToCode = nullptr;
  1.2294 +nsTHashtable<nsUint32HashKey>             *gfxFont::sDefaultFeatures = nullptr;
  1.2295 +
  1.2296 +static inline bool
  1.2297 +HasSubstitution(uint32_t *aBitVector, uint32_t aBit) {
  1.2298 +    return (aBitVector[aBit >> 5] & (1 << (aBit & 0x1f))) != 0;
  1.2299 +}
  1.2300 +
  1.2301 +// union of all default substitution features across scripts
  1.2302 +static const hb_tag_t defaultFeatures[] = {
  1.2303 +    HB_TAG('a','b','v','f'),
  1.2304 +    HB_TAG('a','b','v','s'),
  1.2305 +    HB_TAG('a','k','h','n'),
  1.2306 +    HB_TAG('b','l','w','f'),
  1.2307 +    HB_TAG('b','l','w','s'),
  1.2308 +    HB_TAG('c','a','l','t'),
  1.2309 +    HB_TAG('c','c','m','p'),
  1.2310 +    HB_TAG('c','f','a','r'),
  1.2311 +    HB_TAG('c','j','c','t'),
  1.2312 +    HB_TAG('c','l','i','g'),
  1.2313 +    HB_TAG('f','i','n','2'),
  1.2314 +    HB_TAG('f','i','n','3'),
  1.2315 +    HB_TAG('f','i','n','a'),
  1.2316 +    HB_TAG('h','a','l','f'),
  1.2317 +    HB_TAG('h','a','l','n'),
  1.2318 +    HB_TAG('i','n','i','t'),
  1.2319 +    HB_TAG('i','s','o','l'),
  1.2320 +    HB_TAG('l','i','g','a'),
  1.2321 +    HB_TAG('l','j','m','o'),
  1.2322 +    HB_TAG('l','o','c','l'),
  1.2323 +    HB_TAG('l','t','r','a'),
  1.2324 +    HB_TAG('l','t','r','m'),
  1.2325 +    HB_TAG('m','e','d','2'),
  1.2326 +    HB_TAG('m','e','d','i'),
  1.2327 +    HB_TAG('m','s','e','t'),
  1.2328 +    HB_TAG('n','u','k','t'),
  1.2329 +    HB_TAG('p','r','e','f'),
  1.2330 +    HB_TAG('p','r','e','s'),
  1.2331 +    HB_TAG('p','s','t','f'),
  1.2332 +    HB_TAG('p','s','t','s'),
  1.2333 +    HB_TAG('r','c','l','t'),
  1.2334 +    HB_TAG('r','l','i','g'),
  1.2335 +    HB_TAG('r','k','r','f'),
  1.2336 +    HB_TAG('r','p','h','f'),
  1.2337 +    HB_TAG('r','t','l','a'),
  1.2338 +    HB_TAG('r','t','l','m'),
  1.2339 +    HB_TAG('t','j','m','o'),
  1.2340 +    HB_TAG('v','a','t','u'),
  1.2341 +    HB_TAG('v','e','r','t'),
  1.2342 +    HB_TAG('v','j','m','o')
  1.2343 +};
  1.2344 +
  1.2345 +void
  1.2346 +gfxFont::CheckForFeaturesInvolvingSpace()
  1.2347 +{
  1.2348 +    mFontEntry->mHasSpaceFeaturesInitialized = true;
  1.2349 +
  1.2350 +#ifdef PR_LOGGING
  1.2351 +    bool log = LOG_FONTINIT_ENABLED();
  1.2352 +    TimeStamp start;
  1.2353 +    if (MOZ_UNLIKELY(log)) {
  1.2354 +        start = TimeStamp::Now();
  1.2355 +    }
  1.2356 +#endif
  1.2357 +
  1.2358 +    bool result = false;
  1.2359 +
  1.2360 +    uint32_t spaceGlyph = GetSpaceGlyph();
  1.2361 +    if (!spaceGlyph) {
  1.2362 +        return;
  1.2363 +    }
  1.2364 +
  1.2365 +    hb_face_t *face = GetFontEntry()->GetHBFace();
  1.2366 +
  1.2367 +    // GSUB lookups - examine per script
  1.2368 +    if (hb_ot_layout_has_substitution(face)) {
  1.2369 +
  1.2370 +        // set up the script ==> code hashtable if needed
  1.2371 +        if (!sScriptTagToCode) {
  1.2372 +            sScriptTagToCode =
  1.2373 +                new nsDataHashtable<nsUint32HashKey,
  1.2374 +                                    int32_t>(MOZ_NUM_SCRIPT_CODES);
  1.2375 +            sScriptTagToCode->Put(HB_TAG('D','F','L','T'), MOZ_SCRIPT_COMMON);
  1.2376 +            for (int32_t s = MOZ_SCRIPT_ARABIC; s < MOZ_NUM_SCRIPT_CODES; s++) {
  1.2377 +                hb_script_t scriptTag = hb_script_t(GetScriptTagForCode(s));
  1.2378 +                hb_tag_t s1, s2;
  1.2379 +                hb_ot_tags_from_script(scriptTag, &s1, &s2);
  1.2380 +                sScriptTagToCode->Put(s1, s);
  1.2381 +                if (s2 != HB_OT_TAG_DEFAULT_SCRIPT) {
  1.2382 +                    sScriptTagToCode->Put(s2, s);
  1.2383 +                }
  1.2384 +            }
  1.2385 +
  1.2386 +            uint32_t numDefaultFeatures = ArrayLength(defaultFeatures);
  1.2387 +            sDefaultFeatures =
  1.2388 +                new nsTHashtable<nsUint32HashKey>(numDefaultFeatures);
  1.2389 +            for (uint32_t i = 0; i < numDefaultFeatures; i++) {
  1.2390 +                sDefaultFeatures->PutEntry(defaultFeatures[i]);
  1.2391 +            }
  1.2392 +        }
  1.2393 +
  1.2394 +        // iterate over the scripts in the font
  1.2395 +        hb_tag_t scriptTags[8];
  1.2396 +
  1.2397 +        uint32_t len, offset = 0;
  1.2398 +        do {
  1.2399 +            len = ArrayLength(scriptTags);
  1.2400 +            hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset,
  1.2401 +                                               &len, scriptTags);
  1.2402 +            for (uint32_t i = 0; i < len; i++) {
  1.2403 +                bool isDefaultFeature = false;
  1.2404 +                int32_t s;
  1.2405 +                if (!HasLookupRuleWithGlyphByScript(face, HB_OT_TAG_GSUB,
  1.2406 +                                                    scriptTags[i], offset + i,
  1.2407 +                                                    spaceGlyph,
  1.2408 +                                                    *sDefaultFeatures,
  1.2409 +                                                    isDefaultFeature) ||
  1.2410 +                    !sScriptTagToCode->Get(scriptTags[i], &s))
  1.2411 +                {
  1.2412 +                    continue;
  1.2413 +                }
  1.2414 +                result = true;
  1.2415 +                uint32_t index = s >> 5;
  1.2416 +                uint32_t bit = s & 0x1f;
  1.2417 +                if (isDefaultFeature) {
  1.2418 +                    mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
  1.2419 +                } else {
  1.2420 +                    mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
  1.2421 +                }
  1.2422 +            }
  1.2423 +            offset += len;
  1.2424 +        } while (len == ArrayLength(scriptTags));
  1.2425 +    }
  1.2426 +
  1.2427 +    // spaces in default features of default script?
  1.2428 +    // ==> can't use word cache, skip GPOS analysis
  1.2429 +    bool canUseWordCache = true;
  1.2430 +    if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
  1.2431 +                        MOZ_SCRIPT_COMMON)) {
  1.2432 +        canUseWordCache = false;
  1.2433 +    }
  1.2434 +
  1.2435 +    // GPOS lookups - distinguish kerning from non-kerning features
  1.2436 +    mFontEntry->mHasSpaceFeaturesKerning = false;
  1.2437 +    mFontEntry->mHasSpaceFeaturesNonKerning = false;
  1.2438 +
  1.2439 +    if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
  1.2440 +        bool hasKerning = false, hasNonKerning = false;
  1.2441 +        HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
  1.2442 +                               HB_TAG('k','e','r','n'), hasKerning, spaceGlyph);
  1.2443 +        if (hasKerning || hasNonKerning) {
  1.2444 +            result = true;
  1.2445 +        }
  1.2446 +        mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
  1.2447 +        mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
  1.2448 +    }
  1.2449 +
  1.2450 +    hb_face_destroy(face);
  1.2451 +    mFontEntry->mHasSpaceFeatures = result;
  1.2452 +
  1.2453 +#ifdef PR_LOGGING
  1.2454 +    if (MOZ_UNLIKELY(log)) {
  1.2455 +        TimeDuration elapsed = TimeStamp::Now() - start;
  1.2456 +        LOG_FONTINIT((
  1.2457 +            "(fontinit-spacelookups) font: %s - "
  1.2458 +            "subst default: %8.8x %8.8x %8.8x %8.8x "
  1.2459 +            "subst non-default: %8.8x %8.8x %8.8x %8.8x "
  1.2460 +            "kerning: %s non-kerning: %s time: %6.3f\n",
  1.2461 +            NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
  1.2462 +            mFontEntry->mDefaultSubSpaceFeatures[3],
  1.2463 +            mFontEntry->mDefaultSubSpaceFeatures[2],
  1.2464 +            mFontEntry->mDefaultSubSpaceFeatures[1],
  1.2465 +            mFontEntry->mDefaultSubSpaceFeatures[0],
  1.2466 +            mFontEntry->mNonDefaultSubSpaceFeatures[3],
  1.2467 +            mFontEntry->mNonDefaultSubSpaceFeatures[2],
  1.2468 +            mFontEntry->mNonDefaultSubSpaceFeatures[1],
  1.2469 +            mFontEntry->mNonDefaultSubSpaceFeatures[0],
  1.2470 +            (mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
  1.2471 +            (mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"),
  1.2472 +            elapsed.ToMilliseconds()
  1.2473 +        ));
  1.2474 +    }
  1.2475 +#endif
  1.2476 +}
  1.2477 +
  1.2478 +bool
  1.2479 +gfxFont::HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript)
  1.2480 +{
  1.2481 +    NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
  1.2482 +                 "need to initialize space lookup flags");
  1.2483 +    NS_ASSERTION(aRunScript < MOZ_NUM_SCRIPT_CODES, "weird script code");
  1.2484 +    if (aRunScript == MOZ_SCRIPT_INVALID ||
  1.2485 +        aRunScript >= MOZ_NUM_SCRIPT_CODES) {
  1.2486 +        return false;
  1.2487 +    }
  1.2488 +
  1.2489 +    // default features have space lookups ==> true
  1.2490 +    if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
  1.2491 +                        MOZ_SCRIPT_COMMON) ||
  1.2492 +        HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
  1.2493 +                        aRunScript))
  1.2494 +    {
  1.2495 +        return true;
  1.2496 +    }
  1.2497 +
  1.2498 +    // non-default features have space lookups and some type of
  1.2499 +    // font feature, in font or style is specified ==> true
  1.2500 +    if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
  1.2501 +                         MOZ_SCRIPT_COMMON) ||
  1.2502 +         HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
  1.2503 +                         aRunScript)) &&
  1.2504 +        (!mStyle.featureSettings.IsEmpty() ||
  1.2505 +         !mFontEntry->mFeatureSettings.IsEmpty()))
  1.2506 +    {
  1.2507 +        return true;
  1.2508 +    }
  1.2509 +
  1.2510 +    return false;
  1.2511 +}
  1.2512 +
  1.2513 +bool
  1.2514 +gfxFont::SpaceMayParticipateInShaping(int32_t aRunScript)
  1.2515 +{
  1.2516 +    // avoid checking fonts known not to include default space-dependent features
  1.2517 +    if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
  1.2518 +        if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
  1.2519 +            mFontEntry->mFeatureSettings.IsEmpty()) {
  1.2520 +            return false;
  1.2521 +        }
  1.2522 +    }
  1.2523 +
  1.2524 +    // We record the presence of space-dependent features in the font entry
  1.2525 +    // so that subsequent instantiations for the same font face won't
  1.2526 +    // require us to re-check the tables; however, the actual check is done
  1.2527 +    // by gfxFont because not all font entry subclasses know how to create
  1.2528 +    // a harfbuzz face for introspection.
  1.2529 +    if (!mFontEntry->mHasSpaceFeaturesInitialized) {
  1.2530 +        CheckForFeaturesInvolvingSpace();
  1.2531 +    }
  1.2532 +
  1.2533 +    if (!mFontEntry->mHasSpaceFeatures) {
  1.2534 +        return false;
  1.2535 +    }
  1.2536 +
  1.2537 +    // if font has substitution rules or non-kerning positioning rules
  1.2538 +    // that involve spaces, bypass
  1.2539 +    if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
  1.2540 +        mFontEntry->mHasSpaceFeaturesNonKerning) {
  1.2541 +        return true;
  1.2542 +    }
  1.2543 +
  1.2544 +    // if kerning explicitly enabled/disabled via font-feature-settings or
  1.2545 +    // font-kerning and kerning rules use spaces, only bypass when enabled
  1.2546 +    if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
  1.2547 +        return mKerningEnabled;
  1.2548 +    }
  1.2549 +
  1.2550 +    return false;
  1.2551 +}
  1.2552 +
  1.2553 +bool
  1.2554 +gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
  1.2555 +{
  1.2556 +    aFeatureOn = false;
  1.2557 +
  1.2558 +    if (mStyle.featureSettings.IsEmpty() &&
  1.2559 +        GetFontEntry()->mFeatureSettings.IsEmpty()) {
  1.2560 +        return false;
  1.2561 +    }
  1.2562 +
  1.2563 +    // add feature values from font
  1.2564 +    bool featureSet = false;
  1.2565 +    uint32_t i, count;
  1.2566 +
  1.2567 +    nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
  1.2568 +    count = fontFeatures.Length();
  1.2569 +    for (i = 0; i < count; i++) {
  1.2570 +        const gfxFontFeature& feature = fontFeatures.ElementAt(i);
  1.2571 +        if (feature.mTag == aFeature) {
  1.2572 +            featureSet = true;
  1.2573 +            aFeatureOn = (feature.mValue != 0);
  1.2574 +        }
  1.2575 +    }
  1.2576 +
  1.2577 +    // add feature values from style rules
  1.2578 +    nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
  1.2579 +    count = styleFeatures.Length();
  1.2580 +    for (i = 0; i < count; i++) {
  1.2581 +        const gfxFontFeature& feature = styleFeatures.ElementAt(i);
  1.2582 +        if (feature.mTag == aFeature) {
  1.2583 +            featureSet = true;
  1.2584 +            aFeatureOn = (feature.mValue != 0);
  1.2585 +        }
  1.2586 +    }
  1.2587 +
  1.2588 +    return featureSet;
  1.2589 +}
  1.2590 +
  1.2591 +/**
  1.2592 + * A helper function in case we need to do any rounding or other
  1.2593 + * processing here.
  1.2594 + */
  1.2595 +#define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
  1.2596 +    (double(aAppUnits)*double(aDevUnitsPerAppUnit))
  1.2597 +
  1.2598 +struct GlyphBuffer {
  1.2599 +#define GLYPH_BUFFER_SIZE (2048/sizeof(cairo_glyph_t))
  1.2600 +    cairo_glyph_t mGlyphBuffer[GLYPH_BUFFER_SIZE];
  1.2601 +    unsigned int mNumGlyphs;
  1.2602 +
  1.2603 +    GlyphBuffer()
  1.2604 +        : mNumGlyphs(0) { }
  1.2605 +
  1.2606 +    cairo_glyph_t *AppendGlyph() {
  1.2607 +        return &mGlyphBuffer[mNumGlyphs++];
  1.2608 +    }
  1.2609 +
  1.2610 +    void Flush(cairo_t *aCR, DrawMode aDrawMode, bool aReverse,
  1.2611 +               gfxTextContextPaint *aContextPaint,
  1.2612 +               const gfxMatrix& aGlobalMatrix, bool aFinish = false) {
  1.2613 +        // Ensure there's enough room for a glyph to be added to the buffer
  1.2614 +        // and we actually have glyphs to draw
  1.2615 +        if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
  1.2616 +            return;
  1.2617 +        }
  1.2618 +
  1.2619 +        if (aReverse) {
  1.2620 +            for (uint32_t i = 0; i < mNumGlyphs/2; ++i) {
  1.2621 +                cairo_glyph_t tmp = mGlyphBuffer[i];
  1.2622 +                mGlyphBuffer[i] = mGlyphBuffer[mNumGlyphs - 1 - i];
  1.2623 +                mGlyphBuffer[mNumGlyphs - 1 - i] = tmp;
  1.2624 +            }
  1.2625 +        }
  1.2626 +
  1.2627 +        if (aDrawMode == DrawMode::GLYPH_PATH) {
  1.2628 +            cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
  1.2629 +        } else {
  1.2630 +            if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
  1.2631 +                                  (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
  1.2632 +                FlushStroke(aCR, aContextPaint, aGlobalMatrix);
  1.2633 +            }
  1.2634 +            if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) {
  1.2635 +                PROFILER_LABEL("GlyphBuffer", "cairo_show_glyphs");
  1.2636 +                nsRefPtr<gfxPattern> pattern;
  1.2637 +                if (aContextPaint &&
  1.2638 +                    !!(pattern = aContextPaint->GetFillPattern(aGlobalMatrix))) {
  1.2639 +                    cairo_save(aCR);
  1.2640 +                    cairo_set_source(aCR, pattern->CairoPattern());
  1.2641 +                }
  1.2642 +
  1.2643 +                cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
  1.2644 +
  1.2645 +                if (pattern) {
  1.2646 +                    cairo_restore(aCR);
  1.2647 +                }
  1.2648 +            }
  1.2649 +            if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
  1.2650 +                                  int(DrawMode::GLYPH_STROKE)) {
  1.2651 +                FlushStroke(aCR, aContextPaint, aGlobalMatrix);
  1.2652 +            }
  1.2653 +        }
  1.2654 +
  1.2655 +        mNumGlyphs = 0;
  1.2656 +    }
  1.2657 +
  1.2658 +private:
  1.2659 +    void FlushStroke(cairo_t *aCR, gfxTextContextPaint *aContextPaint,
  1.2660 +                     const gfxMatrix& aGlobalMatrix) {
  1.2661 +        nsRefPtr<gfxPattern> pattern;
  1.2662 +        if (aContextPaint &&
  1.2663 +            !!(pattern = aContextPaint->GetStrokePattern(aGlobalMatrix))) {
  1.2664 +            cairo_save(aCR);
  1.2665 +            cairo_set_source(aCR, pattern->CairoPattern());
  1.2666 +        }
  1.2667 +
  1.2668 +        cairo_new_path(aCR);
  1.2669 +        cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
  1.2670 +        cairo_stroke(aCR);
  1.2671 +
  1.2672 +        if (pattern) {
  1.2673 +            cairo_restore(aCR);
  1.2674 +        }
  1.2675 +    }
  1.2676 +
  1.2677 +#undef GLYPH_BUFFER_SIZE
  1.2678 +};
  1.2679 +
  1.2680 +static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
  1.2681 +  switch (aAAOption) {
  1.2682 +  case gfxFont::kAntialiasSubpixel:
  1.2683 +    return AntialiasMode::SUBPIXEL;
  1.2684 +  case gfxFont::kAntialiasGrayscale:
  1.2685 +    return AntialiasMode::GRAY;
  1.2686 +  case gfxFont::kAntialiasNone:
  1.2687 +    return AntialiasMode::NONE;
  1.2688 +  default:
  1.2689 +    return AntialiasMode::DEFAULT;
  1.2690 +  }
  1.2691 +}
  1.2692 +
  1.2693 +struct GlyphBufferAzure {
  1.2694 +#define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph))
  1.2695 +    Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE];
  1.2696 +    unsigned int mNumGlyphs;
  1.2697 +
  1.2698 +    GlyphBufferAzure()
  1.2699 +        : mNumGlyphs(0) { }
  1.2700 +
  1.2701 +    Glyph *AppendGlyph() {
  1.2702 +        return &mGlyphBuffer[mNumGlyphs++];
  1.2703 +    }
  1.2704 +
  1.2705 +    void Flush(DrawTarget *aDT, gfxTextContextPaint *aContextPaint, ScaledFont *aFont,
  1.2706 +               DrawMode aDrawMode, bool aReverse, const GlyphRenderingOptions *aOptions,
  1.2707 +               gfxContext *aThebesContext, const Matrix *aInvFontMatrix, const DrawOptions &aDrawOptions,
  1.2708 +               bool aFinish = false)
  1.2709 +    {
  1.2710 +        // Ensure there's enough room for a glyph to be added to the buffer
  1.2711 +        if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
  1.2712 +            return;
  1.2713 +        }
  1.2714 +
  1.2715 +        if (aReverse) {
  1.2716 +            Glyph *begin = &mGlyphBuffer[0];
  1.2717 +            Glyph *end = &mGlyphBuffer[mNumGlyphs];
  1.2718 +            std::reverse(begin, end);
  1.2719 +        }
  1.2720 +        
  1.2721 +        gfx::GlyphBuffer buf;
  1.2722 +        buf.mGlyphs = mGlyphBuffer;
  1.2723 +        buf.mNumGlyphs = mNumGlyphs;
  1.2724 +
  1.2725 +        gfxContext::AzureState state = aThebesContext->CurrentState();
  1.2726 +        if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
  1.2727 +                              (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
  1.2728 +            FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
  1.2729 +        }
  1.2730 +        if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) {
  1.2731 +            if (state.pattern || aContextPaint) {
  1.2732 +                Pattern *pat;
  1.2733 +
  1.2734 +                nsRefPtr<gfxPattern> fillPattern;
  1.2735 +                if (!aContextPaint ||
  1.2736 +                    !(fillPattern = aContextPaint->GetFillPattern(aThebesContext->CurrentMatrix()))) {
  1.2737 +                    if (state.pattern) {
  1.2738 +                        pat = state.pattern->GetPattern(aDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
  1.2739 +                    } else {
  1.2740 +                        pat = nullptr;
  1.2741 +                    }
  1.2742 +                } else {
  1.2743 +                    pat = fillPattern->GetPattern(aDT);
  1.2744 +                }
  1.2745 +
  1.2746 +                if (pat) {
  1.2747 +                    Matrix saved;
  1.2748 +                    Matrix *mat = nullptr;
  1.2749 +                    if (aInvFontMatrix) {
  1.2750 +                        // The brush matrix needs to be multiplied with the inverted matrix
  1.2751 +                        // as well, to move the brush into the space of the glyphs. Before
  1.2752 +                        // the render target transformation
  1.2753 +
  1.2754 +                        // This relies on the returned Pattern not to be reused by
  1.2755 +                        // others, but regenerated on GetPattern calls. This is true!
  1.2756 +                        if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
  1.2757 +                            mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
  1.2758 +                        } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
  1.2759 +                            mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
  1.2760 +                        } else if (pat->GetType() == PatternType::SURFACE) {
  1.2761 +                            mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
  1.2762 +                        }
  1.2763 +
  1.2764 +                        if (mat) {
  1.2765 +                            saved = *mat;
  1.2766 +                            *mat = (*mat) * (*aInvFontMatrix);
  1.2767 +                        }
  1.2768 +                    }
  1.2769 +
  1.2770 +                    aDT->FillGlyphs(aFont, buf, *pat,
  1.2771 +                                    aDrawOptions, aOptions);
  1.2772 +
  1.2773 +                    if (mat) {
  1.2774 +                        *mat = saved;
  1.2775 +                    }
  1.2776 +                }
  1.2777 +            } else if (state.sourceSurface) {
  1.2778 +                aDT->FillGlyphs(aFont, buf, SurfacePattern(state.sourceSurface,
  1.2779 +                                                           ExtendMode::CLAMP,
  1.2780 +                                                           state.surfTransform),
  1.2781 +                                aDrawOptions, aOptions);
  1.2782 +            } else {
  1.2783 +                aDT->FillGlyphs(aFont, buf, ColorPattern(state.color),
  1.2784 +                                aDrawOptions, aOptions);
  1.2785 +            }
  1.2786 +        }
  1.2787 +        if (int(aDrawMode) & int(DrawMode::GLYPH_PATH)) {
  1.2788 +            aThebesContext->EnsurePathBuilder();
  1.2789 +			Matrix mat = aDT->GetTransform();
  1.2790 +            aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, aDT->GetType(), &mat);
  1.2791 +        }
  1.2792 +        if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
  1.2793 +                              int(DrawMode::GLYPH_STROKE)) {
  1.2794 +            FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
  1.2795 +        }
  1.2796 +
  1.2797 +        mNumGlyphs = 0;
  1.2798 +    }
  1.2799 +
  1.2800 +private:
  1.2801 +    void FlushStroke(DrawTarget *aDT, gfxTextContextPaint *aContextPaint,
  1.2802 +                     ScaledFont *aFont, gfxContext *aThebesContext,
  1.2803 +                     gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState)
  1.2804 +    {
  1.2805 +        RefPtr<Path> path = aFont->GetPathForGlyphs(aBuf, aDT);
  1.2806 +        if (aContextPaint) {
  1.2807 +            nsRefPtr<gfxPattern> strokePattern =
  1.2808 +              aContextPaint->GetStrokePattern(aThebesContext->CurrentMatrix());
  1.2809 +            if (strokePattern) {
  1.2810 +                aDT->Stroke(path, *strokePattern->GetPattern(aDT), aState.strokeOptions);
  1.2811 +            }
  1.2812 +        }
  1.2813 +    }
  1.2814 +
  1.2815 +#undef GLYPH_BUFFER_SIZE
  1.2816 +};
  1.2817 +
  1.2818 +// Bug 674909. When synthetic bolding text by drawing twice, need to
  1.2819 +// render using a pixel offset in device pixels, otherwise text
  1.2820 +// doesn't appear bolded, it appears as if a bad text shadow exists
  1.2821 +// when a non-identity transform exists.  Use an offset factor so that
  1.2822 +// the second draw occurs at a constant offset in device pixels.
  1.2823 +
  1.2824 +double
  1.2825 +gfxFont::CalcXScale(gfxContext *aContext)
  1.2826 +{
  1.2827 +    // determine magnitude of a 1px x offset in device space
  1.2828 +    gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0));
  1.2829 +    if (t.width == 1.0 && t.height == 0.0) {
  1.2830 +        // short-circuit the most common case to avoid sqrt() and division
  1.2831 +        return 1.0;
  1.2832 +    }
  1.2833 +
  1.2834 +    double m = sqrt(t.width * t.width + t.height * t.height);
  1.2835 +
  1.2836 +    NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
  1.2837 +    if (m == 0.0) {
  1.2838 +        return 0.0; // effectively disables offset
  1.2839 +    }
  1.2840 +
  1.2841 +    // scale factor so that offsets are 1px in device pixels
  1.2842 +    return 1.0 / m;
  1.2843 +}
  1.2844 +
  1.2845 +static DrawMode
  1.2846 +ForcePaintingDrawMode(DrawMode aDrawMode)
  1.2847 +{
  1.2848 +    return aDrawMode == DrawMode::GLYPH_PATH ?
  1.2849 +        DrawMode(int(DrawMode::GLYPH_FILL) | int(DrawMode::GLYPH_STROKE)) :
  1.2850 +        aDrawMode;
  1.2851 +}
  1.2852 +
  1.2853 +void
  1.2854 +gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
  1.2855 +              gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt,
  1.2856 +              Spacing *aSpacing, gfxTextContextPaint *aContextPaint,
  1.2857 +              gfxTextRunDrawCallbacks *aCallbacks)
  1.2858 +{
  1.2859 +    NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
  1.2860 +                 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
  1.2861 +
  1.2862 +    if (aStart >= aEnd)
  1.2863 +        return;
  1.2864 +
  1.2865 +    const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
  1.2866 +    const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
  1.2867 +    const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
  1.2868 +    bool isRTL = aTextRun->IsRightToLeft();
  1.2869 +    double direction = aTextRun->GetDirection();
  1.2870 +    gfxMatrix globalMatrix = aContext->CurrentMatrix();
  1.2871 +
  1.2872 +    bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
  1.2873 +    nsAutoPtr<gfxTextContextPaint> contextPaint;
  1.2874 +    if (haveSVGGlyphs && !aContextPaint) {
  1.2875 +        // If no pattern is specified for fill, use the current pattern
  1.2876 +        NS_ASSERTION((int(aDrawMode) & int(DrawMode::GLYPH_STROKE)) == 0, "no pattern supplied for stroking text");
  1.2877 +        nsRefPtr<gfxPattern> fillPattern = aContext->GetPattern();
  1.2878 +        contextPaint = new SimpleTextContextPaint(fillPattern, nullptr,
  1.2879 +                                                 aContext->CurrentMatrix());
  1.2880 +        aContextPaint = contextPaint;
  1.2881 +    }
  1.2882 +
  1.2883 +    // synthetic-bold strikes are each offset one device pixel in run direction
  1.2884 +    // (these values are only needed if IsSyntheticBold() is true)
  1.2885 +    double synBoldOnePixelOffset = 0;
  1.2886 +    int32_t strikes = 1;
  1.2887 +    if (IsSyntheticBold()) {
  1.2888 +        double xscale = CalcXScale(aContext);
  1.2889 +        synBoldOnePixelOffset = direction * xscale;
  1.2890 +        if (xscale != 0.0) {
  1.2891 +            // use as many strikes as needed for the the increased advance
  1.2892 +            strikes = NS_lroundf(GetSyntheticBoldOffset() / xscale);
  1.2893 +        }
  1.2894 +    }
  1.2895 +
  1.2896 +    uint32_t i;
  1.2897 +    // Current position in appunits
  1.2898 +    double x = aPt->x;
  1.2899 +    double y = aPt->y;
  1.2900 +
  1.2901 +    cairo_t *cr = aContext->GetCairo();
  1.2902 +    RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
  1.2903 +
  1.2904 +    bool paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
  1.2905 +    bool emittedGlyphs = false;
  1.2906 +
  1.2907 +    if (aContext->IsCairo()) {
  1.2908 +      bool success = SetupCairoFont(aContext);
  1.2909 +      if (MOZ_UNLIKELY(!success))
  1.2910 +          return;
  1.2911 +
  1.2912 +      ::GlyphBuffer glyphs;
  1.2913 +      cairo_glyph_t *glyph;
  1.2914 +
  1.2915 +      if (aSpacing) {
  1.2916 +          x += direction*aSpacing[0].mBefore;
  1.2917 +      }
  1.2918 +      for (i = aStart; i < aEnd; ++i) {
  1.2919 +          const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
  1.2920 +          if (glyphData->IsSimpleGlyph()) {
  1.2921 +              double advance = glyphData->GetSimpleAdvance();
  1.2922 +              double glyphX;
  1.2923 +              if (isRTL) {
  1.2924 +                  x -= advance;
  1.2925 +                  glyphX = x;
  1.2926 +              } else {
  1.2927 +                  glyphX = x;
  1.2928 +                  x += advance;
  1.2929 +              }
  1.2930 +
  1.2931 +              if (haveSVGGlyphs) {
  1.2932 +                  if (!paintSVGGlyphs) {
  1.2933 +                      continue;
  1.2934 +                  }
  1.2935 +                  gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  1.2936 +                                 ToDeviceUnits(y, devUnitsPerAppUnit));
  1.2937 +                  DrawMode mode = ForcePaintingDrawMode(aDrawMode);
  1.2938 +                  if (RenderSVGGlyph(aContext, point, mode,
  1.2939 +                                     glyphData->GetSimpleGlyph(), aContextPaint,
  1.2940 +                                     aCallbacks, emittedGlyphs)) {
  1.2941 +                      continue;
  1.2942 +                  }
  1.2943 +              }
  1.2944 +
  1.2945 +              // Perhaps we should put a scale in the cairo context instead of
  1.2946 +              // doing this scaling here...
  1.2947 +              // Multiplying by the reciprocal may introduce tiny error here,
  1.2948 +              // but we assume cairo is going to round coordinates at some stage
  1.2949 +              // and this is faster
  1.2950 +              glyph = glyphs.AppendGlyph();
  1.2951 +              glyph->index = glyphData->GetSimpleGlyph();
  1.2952 +              glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
  1.2953 +              glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit);
  1.2954 +              glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
  1.2955 +            
  1.2956 +              // synthetic bolding by multi-striking with 1-pixel offsets
  1.2957 +              // at least once, more if there's room (large font sizes)
  1.2958 +              if (IsSyntheticBold()) {
  1.2959 +                  double strikeOffset = synBoldOnePixelOffset;
  1.2960 +                  int32_t strikeCount = strikes;
  1.2961 +                  do {
  1.2962 +                      cairo_glyph_t *doubleglyph;
  1.2963 +                      doubleglyph = glyphs.AppendGlyph();
  1.2964 +                      doubleglyph->index = glyph->index;
  1.2965 +                      doubleglyph->x =
  1.2966 +                          ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
  1.2967 +                                        devUnitsPerAppUnit);
  1.2968 +                      doubleglyph->y = glyph->y;
  1.2969 +                      strikeOffset += synBoldOnePixelOffset;
  1.2970 +                      glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
  1.2971 +                  } while (--strikeCount > 0);
  1.2972 +              }
  1.2973 +              emittedGlyphs = true;
  1.2974 +          } else {
  1.2975 +              uint32_t glyphCount = glyphData->GetGlyphCount();
  1.2976 +              if (glyphCount > 0) {
  1.2977 +                  const gfxTextRun::DetailedGlyph *details =
  1.2978 +                      aTextRun->GetDetailedGlyphs(i);
  1.2979 +                  NS_ASSERTION(details, "detailedGlyph should not be missing!");
  1.2980 +                  double advance;
  1.2981 +                  for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) {
  1.2982 +                      advance = details->mAdvance;
  1.2983 +                      if (glyphData->IsMissing()) {
  1.2984 +                          // default ignorable characters will have zero advance width.
  1.2985 +                          // we don't have to draw the hexbox for them
  1.2986 +                          if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) {
  1.2987 +                              double glyphX = x;
  1.2988 +                              if (isRTL) {
  1.2989 +                                  glyphX -= advance;
  1.2990 +                              }
  1.2991 +                              gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  1.2992 +                                          ToDeviceUnits(y, devUnitsPerAppUnit));
  1.2993 +                              gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
  1.2994 +                              gfxFloat height = GetMetrics().maxAscent;
  1.2995 +                              gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
  1.2996 +                              gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
  1.2997 +                                                                     glyphRect,
  1.2998 +                                                                     details->mGlyphID,
  1.2999 +                                                                     appUnitsPerDevUnit);
  1.3000 +                          }
  1.3001 +                      } else {
  1.3002 +                          double glyphX = x + details->mXOffset;
  1.3003 +                          if (isRTL) {
  1.3004 +                              glyphX -= advance;
  1.3005 +                          }
  1.3006 +
  1.3007 +                          gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  1.3008 +                                         ToDeviceUnits(y, devUnitsPerAppUnit));
  1.3009 +
  1.3010 +                          if (haveSVGGlyphs) {
  1.3011 +                              if (!paintSVGGlyphs) {
  1.3012 +                                  continue;
  1.3013 +                              }
  1.3014 +                              DrawMode mode = ForcePaintingDrawMode(aDrawMode);
  1.3015 +                              if (RenderSVGGlyph(aContext, point, mode,
  1.3016 +                                                  details->mGlyphID,
  1.3017 +                                                  aContextPaint, aCallbacks,
  1.3018 +                                                  emittedGlyphs)) {
  1.3019 +                                  continue;
  1.3020 +                              }
  1.3021 +                          }
  1.3022 +
  1.3023 +                          glyph = glyphs.AppendGlyph();
  1.3024 +                          glyph->index = details->mGlyphID;
  1.3025 +                          glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
  1.3026 +                          glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
  1.3027 +                          glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
  1.3028 +
  1.3029 +                          if (IsSyntheticBold()) {
  1.3030 +                              double strikeOffset = synBoldOnePixelOffset;
  1.3031 +                              int32_t strikeCount = strikes;
  1.3032 +                              do {
  1.3033 +                                  cairo_glyph_t *doubleglyph;
  1.3034 +                                  doubleglyph = glyphs.AppendGlyph();
  1.3035 +                                  doubleglyph->index = glyph->index;
  1.3036 +                                  doubleglyph->x =
  1.3037 +                                      ToDeviceUnits(glyphX + strikeOffset *
  1.3038 +                                              appUnitsPerDevUnit,
  1.3039 +                                              devUnitsPerAppUnit);
  1.3040 +                                  doubleglyph->y = glyph->y;
  1.3041 +                                  strikeOffset += synBoldOnePixelOffset;
  1.3042 +                                  glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
  1.3043 +                              } while (--strikeCount > 0);
  1.3044 +                          }
  1.3045 +                          emittedGlyphs = true;
  1.3046 +                      }
  1.3047 +                  }
  1.3048 +              }
  1.3049 +          }
  1.3050 +
  1.3051 +          if (aSpacing) {
  1.3052 +              double space = aSpacing[i - aStart].mAfter;
  1.3053 +              if (i + 1 < aEnd) {
  1.3054 +                  space += aSpacing[i + 1 - aStart].mBefore;
  1.3055 +              }
  1.3056 +              x += direction*space;
  1.3057 +          }
  1.3058 +      }
  1.3059 +
  1.3060 +      if (gfxFontTestStore::CurrentStore()) {
  1.3061 +          /* This assumes that the tests won't have anything that results
  1.3062 +           * in more than GLYPH_BUFFER_SIZE glyphs.  Do this before we
  1.3063 +           * flush, since that'll blow away the num_glyphs.
  1.3064 +           */
  1.3065 +          gfxFontTestStore::CurrentStore()->AddItem(GetName(),
  1.3066 +                                                    glyphs.mGlyphBuffer,
  1.3067 +                                                    glyphs.mNumGlyphs);
  1.3068 +      }
  1.3069 +
  1.3070 +      // draw any remaining glyphs
  1.3071 +      glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix, true);
  1.3072 +      if (aCallbacks && emittedGlyphs) {
  1.3073 +          aCallbacks->NotifyGlyphPathEmitted();
  1.3074 +      }
  1.3075 +
  1.3076 +    } else {
  1.3077 +      RefPtr<ScaledFont> scaledFont = GetScaledFont(dt);
  1.3078 +
  1.3079 +      if (!scaledFont) {
  1.3080 +        return;
  1.3081 +      }
  1.3082 +
  1.3083 +      bool oldSubpixelAA = dt->GetPermitSubpixelAA();
  1.3084 +
  1.3085 +      if (!AllowSubpixelAA()) {
  1.3086 +          dt->SetPermitSubpixelAA(false);
  1.3087 +      }
  1.3088 +
  1.3089 +      GlyphBufferAzure glyphs;
  1.3090 +      Glyph *glyph;
  1.3091 +
  1.3092 +      Matrix mat, matInv;
  1.3093 +      Matrix oldMat = dt->GetTransform();
  1.3094 +
  1.3095 +      // This is nullptr when we have inverse-transformed glyphs and we need
  1.3096 +      // to transform the Brush inside flush.
  1.3097 +      Matrix *passedInvMatrix = nullptr;
  1.3098 +
  1.3099 +      RefPtr<GlyphRenderingOptions> renderingOptions =
  1.3100 +        GetGlyphRenderingOptions();
  1.3101 +
  1.3102 +      DrawOptions drawOptions;
  1.3103 +      drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
  1.3104 +
  1.3105 +      // The cairo DrawTarget backend uses the cairo_scaled_font directly
  1.3106 +      // and so has the font skew matrix applied already.
  1.3107 +      if (mScaledFont &&
  1.3108 +          dt->GetType() != BackendType::CAIRO) {
  1.3109 +        cairo_matrix_t matrix;
  1.3110 +        cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
  1.3111 +        if (matrix.xy != 0) {
  1.3112 +          // If this matrix applies a skew, which can happen when drawing
  1.3113 +          // oblique fonts, we will set the DrawTarget matrix to apply the
  1.3114 +          // skew. We'll need to move the glyphs by the inverse of the skew to
  1.3115 +          // get the glyphs positioned correctly in the new device space
  1.3116 +          // though, since the font matrix should only be applied to drawing
  1.3117 +          // the glyphs, and not to their position.
  1.3118 +          mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix));
  1.3119 +
  1.3120 +          mat._11 = mat._22 = 1.0;
  1.3121 +          float adjustedSize = mAdjustedSize > 0 ? mAdjustedSize : GetStyle()->size;
  1.3122 +          mat._21 /= adjustedSize;
  1.3123 +
  1.3124 +          dt->SetTransform(mat * oldMat);
  1.3125 +
  1.3126 +          matInv = mat;
  1.3127 +          matInv.Invert();
  1.3128 +
  1.3129 +          passedInvMatrix = &matInv;
  1.3130 +        }
  1.3131 +      }
  1.3132 +
  1.3133 +      if (aSpacing) {
  1.3134 +          x += direction*aSpacing[0].mBefore;
  1.3135 +      }
  1.3136 +      for (i = aStart; i < aEnd; ++i) {
  1.3137 +          const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
  1.3138 +          if (glyphData->IsSimpleGlyph()) {
  1.3139 +              double advance = glyphData->GetSimpleAdvance();
  1.3140 +              double glyphX;
  1.3141 +              if (isRTL) {
  1.3142 +                  x -= advance;
  1.3143 +                  glyphX = x;
  1.3144 +              } else {
  1.3145 +                  glyphX = x;
  1.3146 +                  x += advance;
  1.3147 +              }
  1.3148 +
  1.3149 +              if (haveSVGGlyphs) {
  1.3150 +                  if (!paintSVGGlyphs) {
  1.3151 +                      continue;
  1.3152 +                  }
  1.3153 +                  gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  1.3154 +                                 ToDeviceUnits(y, devUnitsPerAppUnit));
  1.3155 +                  DrawMode mode = ForcePaintingDrawMode(aDrawMode);
  1.3156 +                  if (RenderSVGGlyph(aContext, point, mode,
  1.3157 +                                     glyphData->GetSimpleGlyph(), aContextPaint,
  1.3158 +                                     aCallbacks, emittedGlyphs)) {
  1.3159 +                      continue;
  1.3160 +                  }
  1.3161 +              }
  1.3162 +
  1.3163 +              // Perhaps we should put a scale in the cairo context instead of
  1.3164 +              // doing this scaling here...
  1.3165 +              // Multiplying by the reciprocal may introduce tiny error here,
  1.3166 +              // but we assume cairo is going to round coordinates at some stage
  1.3167 +              // and this is faster
  1.3168 +              glyph = glyphs.AppendGlyph();
  1.3169 +              glyph->mIndex = glyphData->GetSimpleGlyph();
  1.3170 +              glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
  1.3171 +              glyph->mPosition.y = ToDeviceUnits(y, devUnitsPerAppUnit);
  1.3172 +              glyph->mPosition = matInv * glyph->mPosition;
  1.3173 +              glyphs.Flush(dt, aContextPaint, scaledFont,
  1.3174 +                           aDrawMode, isRTL, renderingOptions,
  1.3175 +                           aContext, passedInvMatrix,
  1.3176 +                           drawOptions);
  1.3177 +
  1.3178 +              // synthetic bolding by multi-striking with 1-pixel offsets
  1.3179 +              // at least once, more if there's room (large font sizes)
  1.3180 +              if (IsSyntheticBold()) {
  1.3181 +                  double strikeOffset = synBoldOnePixelOffset;
  1.3182 +                  int32_t strikeCount = strikes;
  1.3183 +                  do {
  1.3184 +                      Glyph *doubleglyph;
  1.3185 +                      doubleglyph = glyphs.AppendGlyph();
  1.3186 +                      doubleglyph->mIndex = glyph->mIndex;
  1.3187 +                      doubleglyph->mPosition.x =
  1.3188 +                          ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
  1.3189 +                                        devUnitsPerAppUnit);
  1.3190 +                      doubleglyph->mPosition.y = glyph->mPosition.y;
  1.3191 +                      doubleglyph->mPosition = matInv * doubleglyph->mPosition;
  1.3192 +                      strikeOffset += synBoldOnePixelOffset;
  1.3193 +                      glyphs.Flush(dt, aContextPaint, scaledFont,
  1.3194 +                                   aDrawMode, isRTL, renderingOptions,
  1.3195 +                                   aContext, passedInvMatrix,
  1.3196 +                                   drawOptions);
  1.3197 +                  } while (--strikeCount > 0);
  1.3198 +              }
  1.3199 +              emittedGlyphs = true;
  1.3200 +          } else {
  1.3201 +              uint32_t glyphCount = glyphData->GetGlyphCount();
  1.3202 +              if (glyphCount > 0) {
  1.3203 +                  const gfxTextRun::DetailedGlyph *details =
  1.3204 +                      aTextRun->GetDetailedGlyphs(i);
  1.3205 +                  NS_ASSERTION(details, "detailedGlyph should not be missing!");
  1.3206 +                  double advance;
  1.3207 +                  for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) {
  1.3208 +                      advance = details->mAdvance;
  1.3209 +                      if (glyphData->IsMissing()) {
  1.3210 +                          // default ignorable characters will have zero advance width.
  1.3211 +                          // we don't have to draw the hexbox for them
  1.3212 +                          if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) {
  1.3213 +                              double glyphX = x;
  1.3214 +                              if (isRTL) {
  1.3215 +                                  glyphX -= advance;
  1.3216 +                              }
  1.3217 +                              gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  1.3218 +                                          ToDeviceUnits(y, devUnitsPerAppUnit));
  1.3219 +                              gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
  1.3220 +                              gfxFloat height = GetMetrics().maxAscent;
  1.3221 +                              gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
  1.3222 +                              gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
  1.3223 +                                                                     glyphRect,
  1.3224 +                                                                     details->mGlyphID,
  1.3225 +                                                                     appUnitsPerDevUnit);
  1.3226 +                          }
  1.3227 +                      } else {
  1.3228 +                          double glyphX = x + details->mXOffset;
  1.3229 +                          if (isRTL) {
  1.3230 +                              glyphX -= advance;
  1.3231 +                          }
  1.3232 +
  1.3233 +                          gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  1.3234 +                                         ToDeviceUnits(y, devUnitsPerAppUnit));
  1.3235 +
  1.3236 +                          if (haveSVGGlyphs) {
  1.3237 +                              if (!paintSVGGlyphs) {
  1.3238 +                                  continue;
  1.3239 +                              }
  1.3240 +                              DrawMode mode = ForcePaintingDrawMode(aDrawMode);
  1.3241 +                              if (RenderSVGGlyph(aContext, point, mode,
  1.3242 +                                                 details->mGlyphID,
  1.3243 +                                                 aContextPaint, aCallbacks,
  1.3244 +                                                 emittedGlyphs)) {
  1.3245 +                                  continue;
  1.3246 +                              }
  1.3247 +                          }
  1.3248 +
  1.3249 +                          glyph = glyphs.AppendGlyph();
  1.3250 +                          glyph->mIndex = details->mGlyphID;
  1.3251 +                          glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
  1.3252 +                          glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
  1.3253 +                          glyph->mPosition = matInv * glyph->mPosition;
  1.3254 +                          glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode,
  1.3255 +                                       isRTL, renderingOptions, aContext, passedInvMatrix,
  1.3256 +                                       drawOptions);
  1.3257 +
  1.3258 +                          if (IsSyntheticBold()) {
  1.3259 +                              double strikeOffset = synBoldOnePixelOffset;
  1.3260 +                              int32_t strikeCount = strikes;
  1.3261 +                              do {
  1.3262 +                                  Glyph *doubleglyph;
  1.3263 +                                  doubleglyph = glyphs.AppendGlyph();
  1.3264 +                                  doubleglyph->mIndex = glyph->mIndex;
  1.3265 +                                  doubleglyph->mPosition.x =
  1.3266 +                                      ToDeviceUnits(glyphX + strikeOffset *
  1.3267 +                                                    appUnitsPerDevUnit,
  1.3268 +                                                    devUnitsPerAppUnit);
  1.3269 +                                  doubleglyph->mPosition.y = glyph->mPosition.y;
  1.3270 +                                  strikeOffset += synBoldOnePixelOffset;
  1.3271 +                                  doubleglyph->mPosition = matInv * doubleglyph->mPosition;
  1.3272 +                                  glyphs.Flush(dt, aContextPaint, scaledFont,
  1.3273 +                                               aDrawMode, isRTL, renderingOptions,
  1.3274 +                                               aContext, passedInvMatrix, drawOptions);
  1.3275 +                              } while (--strikeCount > 0);
  1.3276 +                          }
  1.3277 +                          emittedGlyphs = true;
  1.3278 +                      }
  1.3279 +                  }
  1.3280 +              }
  1.3281 +          }
  1.3282 +
  1.3283 +          if (aSpacing) {
  1.3284 +              double space = aSpacing[i - aStart].mAfter;
  1.3285 +              if (i + 1 < aEnd) {
  1.3286 +                  space += aSpacing[i + 1 - aStart].mBefore;
  1.3287 +              }
  1.3288 +              x += direction*space;
  1.3289 +          }
  1.3290 +      }
  1.3291 +
  1.3292 +      glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode, isRTL,
  1.3293 +                   renderingOptions, aContext, passedInvMatrix,
  1.3294 +                   drawOptions, true);
  1.3295 +      if (aCallbacks && emittedGlyphs) {
  1.3296 +          aCallbacks->NotifyGlyphPathEmitted();
  1.3297 +      }
  1.3298 +
  1.3299 +      dt->SetTransform(oldMat);
  1.3300 +
  1.3301 +      dt->SetPermitSubpixelAA(oldSubpixelAA);
  1.3302 +    }
  1.3303 +
  1.3304 +    *aPt = gfxPoint(x, y);
  1.3305 +}
  1.3306 +
  1.3307 +bool
  1.3308 +gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
  1.3309 +                        uint32_t aGlyphId, gfxTextContextPaint *aContextPaint)
  1.3310 +{
  1.3311 +    if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
  1.3312 +        return false;
  1.3313 +    }
  1.3314 +
  1.3315 +    const gfxFloat devUnitsPerSVGUnit =
  1.3316 +        GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
  1.3317 +    gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
  1.3318 +
  1.3319 +    aContext->Translate(gfxPoint(aPoint.x, aPoint.y));
  1.3320 +    aContext->Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit);
  1.3321 +
  1.3322 +    aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
  1.3323 +
  1.3324 +    return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, int(aDrawMode),
  1.3325 +                                          aContextPaint);
  1.3326 +}
  1.3327 +
  1.3328 +bool
  1.3329 +gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
  1.3330 +                        uint32_t aGlyphId, gfxTextContextPaint *aContextPaint,
  1.3331 +                        gfxTextRunDrawCallbacks *aCallbacks,
  1.3332 +                        bool& aEmittedGlyphs)
  1.3333 +{
  1.3334 +    if (aCallbacks) {
  1.3335 +        if (aEmittedGlyphs) {
  1.3336 +            aCallbacks->NotifyGlyphPathEmitted();
  1.3337 +            aEmittedGlyphs = false;
  1.3338 +        }
  1.3339 +        aCallbacks->NotifyBeforeSVGGlyphPainted();
  1.3340 +    }
  1.3341 +    bool rendered = RenderSVGGlyph(aContext, aPoint, aDrawMode, aGlyphId,
  1.3342 +                                   aContextPaint);
  1.3343 +    if (aCallbacks) {
  1.3344 +        aCallbacks->NotifyAfterSVGGlyphPainted();
  1.3345 +    }
  1.3346 +    return rendered;
  1.3347 +}
  1.3348 +
  1.3349 +static void
  1.3350 +UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
  1.3351 +{
  1.3352 +    *aDestMin = std::min(*aDestMin, aX);
  1.3353 +    *aDestMax = std::max(*aDestMax, aX);
  1.3354 +}
  1.3355 +
  1.3356 +// We get precise glyph extents if the textrun creator requested them, or
  1.3357 +// if the font is a user font --- in which case the author may be relying
  1.3358 +// on overflowing glyphs.
  1.3359 +static bool
  1.3360 +NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun)
  1.3361 +{
  1.3362 +    return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
  1.3363 +        aFont->GetFontEntry()->IsUserFont();
  1.3364 +}
  1.3365 +
  1.3366 +static bool
  1.3367 +NeedsGlyphExtents(gfxTextRun *aTextRun)
  1.3368 +{
  1.3369 +    if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
  1.3370 +        return true;
  1.3371 +    uint32_t numRuns;
  1.3372 +    const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
  1.3373 +    for (uint32_t i = 0; i < numRuns; ++i) {
  1.3374 +        if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
  1.3375 +            return true;
  1.3376 +    }
  1.3377 +    return false;
  1.3378 +}
  1.3379 +
  1.3380 +gfxFont::RunMetrics
  1.3381 +gfxFont::Measure(gfxTextRun *aTextRun,
  1.3382 +                 uint32_t aStart, uint32_t aEnd,
  1.3383 +                 BoundingBoxType aBoundingBoxType,
  1.3384 +                 gfxContext *aRefContext,
  1.3385 +                 Spacing *aSpacing)
  1.3386 +{
  1.3387 +    // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
  1.3388 +    // and the underlying cairo font may be antialiased,
  1.3389 +    // we need to create a copy in order to avoid getting cached extents.
  1.3390 +    // This is only used by MathML layout at present.
  1.3391 +    if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
  1.3392 +        mAntialiasOption != kAntialiasNone) {
  1.3393 +        if (!mNonAAFont) {
  1.3394 +            mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
  1.3395 +        }
  1.3396 +        // if font subclass doesn't implement CopyWithAntialiasOption(),
  1.3397 +        // it will return null and we'll proceed to use the existing font
  1.3398 +        if (mNonAAFont) {
  1.3399 +            return mNonAAFont->Measure(aTextRun, aStart, aEnd,
  1.3400 +                                       TIGHT_HINTED_OUTLINE_EXTENTS,
  1.3401 +                                       aRefContext, aSpacing);
  1.3402 +        }
  1.3403 +    }
  1.3404 +
  1.3405 +    const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
  1.3406 +    // Current position in appunits
  1.3407 +    const gfxFont::Metrics& fontMetrics = GetMetrics();
  1.3408 +
  1.3409 +    RunMetrics metrics;
  1.3410 +    metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
  1.3411 +    metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
  1.3412 +    if (aStart == aEnd) {
  1.3413 +        // exit now before we look at aSpacing[0], which is undefined
  1.3414 +        metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
  1.3415 +        return metrics;
  1.3416 +    }
  1.3417 +
  1.3418 +    gfxFloat advanceMin = 0, advanceMax = 0;
  1.3419 +    const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
  1.3420 +    bool isRTL = aTextRun->IsRightToLeft();
  1.3421 +    double direction = aTextRun->GetDirection();
  1.3422 +    bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
  1.3423 +    gfxGlyphExtents *extents =
  1.3424 +        (aBoundingBoxType == LOOSE_INK_EXTENTS &&
  1.3425 +            !needsGlyphExtents &&
  1.3426 +            !aTextRun->HasDetailedGlyphs()) ? nullptr
  1.3427 +        : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
  1.3428 +    double x = 0;
  1.3429 +    if (aSpacing) {
  1.3430 +        x += direction*aSpacing[0].mBefore;
  1.3431 +    }
  1.3432 +    uint32_t i;
  1.3433 +    for (i = aStart; i < aEnd; ++i) {
  1.3434 +        const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
  1.3435 +        if (glyphData->IsSimpleGlyph()) {
  1.3436 +            double advance = glyphData->GetSimpleAdvance();
  1.3437 +            // Only get the real glyph horizontal extent if we were asked
  1.3438 +            // for the tight bounding box or we're in quality mode
  1.3439 +            if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
  1.3440 +                extents) {
  1.3441 +                uint32_t glyphIndex = glyphData->GetSimpleGlyph();
  1.3442 +                uint16_t extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
  1.3443 +                if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
  1.3444 +                    aBoundingBoxType == LOOSE_INK_EXTENTS) {
  1.3445 +                    UnionRange(x, &advanceMin, &advanceMax);
  1.3446 +                    UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax);
  1.3447 +                } else {
  1.3448 +                    gfxRect glyphRect;
  1.3449 +                    if (!extents->GetTightGlyphExtentsAppUnits(this,
  1.3450 +                            aRefContext, glyphIndex, &glyphRect)) {
  1.3451 +                        glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
  1.3452 +                            advance, metrics.mBoundingBox.Height());
  1.3453 +                    }
  1.3454 +                    if (isRTL) {
  1.3455 +                        glyphRect -= gfxPoint(advance, 0);
  1.3456 +                    }
  1.3457 +                    glyphRect += gfxPoint(x, 0);
  1.3458 +                    metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
  1.3459 +                }
  1.3460 +            }
  1.3461 +            x += direction*advance;
  1.3462 +        } else {
  1.3463 +            uint32_t glyphCount = glyphData->GetGlyphCount();
  1.3464 +            if (glyphCount > 0) {
  1.3465 +                const gfxTextRun::DetailedGlyph *details =
  1.3466 +                    aTextRun->GetDetailedGlyphs(i);
  1.3467 +                NS_ASSERTION(details != nullptr,
  1.3468 +                             "detaiedGlyph record should not be missing!");
  1.3469 +                uint32_t j;
  1.3470 +                for (j = 0; j < glyphCount; ++j, ++details) {
  1.3471 +                    uint32_t glyphIndex = details->mGlyphID;
  1.3472 +                    gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
  1.3473 +                    double advance = details->mAdvance;
  1.3474 +                    gfxRect glyphRect;
  1.3475 +                    if (glyphData->IsMissing() || !extents ||
  1.3476 +                        !extents->GetTightGlyphExtentsAppUnits(this,
  1.3477 +                                aRefContext, glyphIndex, &glyphRect)) {
  1.3478 +                        // We might have failed to get glyph extents due to
  1.3479 +                        // OOM or something
  1.3480 +                        glyphRect = gfxRect(0, -metrics.mAscent,
  1.3481 +                            advance, metrics.mAscent + metrics.mDescent);
  1.3482 +                    }
  1.3483 +                    if (isRTL) {
  1.3484 +                        glyphRect -= gfxPoint(advance, 0);
  1.3485 +                    }
  1.3486 +                    glyphRect += gfxPoint(x, 0);
  1.3487 +                    metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
  1.3488 +                    x += direction*advance;
  1.3489 +                }
  1.3490 +            }
  1.3491 +        }
  1.3492 +        // Every other glyph type is ignored
  1.3493 +        if (aSpacing) {
  1.3494 +            double space = aSpacing[i - aStart].mAfter;
  1.3495 +            if (i + 1 < aEnd) {
  1.3496 +                space += aSpacing[i + 1 - aStart].mBefore;
  1.3497 +            }
  1.3498 +            x += direction*space;
  1.3499 +        }
  1.3500 +    }
  1.3501 +
  1.3502 +    if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
  1.3503 +        UnionRange(x, &advanceMin, &advanceMax);
  1.3504 +        gfxRect fontBox(advanceMin, -metrics.mAscent,
  1.3505 +                        advanceMax - advanceMin, metrics.mAscent + metrics.mDescent);
  1.3506 +        metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
  1.3507 +    }
  1.3508 +    if (isRTL) {
  1.3509 +        metrics.mBoundingBox -= gfxPoint(x, 0);
  1.3510 +    }
  1.3511 +
  1.3512 +    metrics.mAdvanceWidth = x*direction;
  1.3513 +    return metrics;
  1.3514 +}
  1.3515 +
  1.3516 +static PLDHashOperator
  1.3517 +NotifyGlyphChangeObservers(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
  1.3518 +                           void* aClosure)
  1.3519 +{
  1.3520 +    aKey->GetKey()->NotifyGlyphsChanged();
  1.3521 +    return PL_DHASH_NEXT;
  1.3522 +}
  1.3523 +
  1.3524 +void
  1.3525 +gfxFont::NotifyGlyphsChanged()
  1.3526 +{
  1.3527 +    uint32_t i, count = mGlyphExtentsArray.Length();
  1.3528 +    for (i = 0; i < count; ++i) {
  1.3529 +        // Flush cached extents array
  1.3530 +        mGlyphExtentsArray[i]->NotifyGlyphsChanged();
  1.3531 +    }
  1.3532 +
  1.3533 +    if (mGlyphChangeObservers) {
  1.3534 +        mGlyphChangeObservers->EnumerateEntries(NotifyGlyphChangeObservers, nullptr);
  1.3535 +    }
  1.3536 +}
  1.3537 +
  1.3538 +static bool
  1.3539 +IsBoundarySpace(char16_t aChar, char16_t aNextChar)
  1.3540 +{
  1.3541 +    return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
  1.3542 +}
  1.3543 +
  1.3544 +static inline uint32_t
  1.3545 +HashMix(uint32_t aHash, char16_t aCh)
  1.3546 +{
  1.3547 +    return (aHash >> 28) ^ (aHash << 4) ^ aCh;
  1.3548 +}
  1.3549 +
  1.3550 +#ifdef __GNUC__
  1.3551 +#define GFX_MAYBE_UNUSED __attribute__((unused))
  1.3552 +#else
  1.3553 +#define GFX_MAYBE_UNUSED
  1.3554 +#endif
  1.3555 +
  1.3556 +template<typename T>
  1.3557 +gfxShapedWord*
  1.3558 +gfxFont::GetShapedWord(gfxContext *aContext,
  1.3559 +                       const T    *aText,
  1.3560 +                       uint32_t    aLength,
  1.3561 +                       uint32_t    aHash,
  1.3562 +                       int32_t     aRunScript,
  1.3563 +                       int32_t     aAppUnitsPerDevUnit,
  1.3564 +                       uint32_t    aFlags,
  1.3565 +                       gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
  1.3566 +{
  1.3567 +    // if the cache is getting too big, flush it and start over
  1.3568 +    uint32_t wordCacheMaxEntries =
  1.3569 +        gfxPlatform::GetPlatform()->WordCacheMaxEntries();
  1.3570 +    if (mWordCache->Count() > wordCacheMaxEntries) {
  1.3571 +        NS_WARNING("flushing shaped-word cache");
  1.3572 +        ClearCachedWords();
  1.3573 +    }
  1.3574 +
  1.3575 +    // if there's a cached entry for this word, just return it
  1.3576 +    CacheHashKey key(aText, aLength, aHash,
  1.3577 +                     aRunScript,
  1.3578 +                     aAppUnitsPerDevUnit,
  1.3579 +                     aFlags);
  1.3580 +
  1.3581 +    CacheHashEntry *entry = mWordCache->PutEntry(key);
  1.3582 +    if (!entry) {
  1.3583 +        NS_WARNING("failed to create word cache entry - expect missing text");
  1.3584 +        return nullptr;
  1.3585 +    }
  1.3586 +    gfxShapedWord *sw = entry->mShapedWord;
  1.3587 +
  1.3588 +    bool isContent = !mStyle.systemFont;
  1.3589 +
  1.3590 +    if (sw) {
  1.3591 +        sw->ResetAge();
  1.3592 +        Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT :
  1.3593 +                                   Telemetry::WORD_CACHE_HITS_CHROME),
  1.3594 +                              aLength);
  1.3595 +#ifndef RELEASE_BUILD
  1.3596 +        if (aTextPerf) {
  1.3597 +            aTextPerf->current.wordCacheHit++;
  1.3598 +        }
  1.3599 +#endif
  1.3600 +        return sw;
  1.3601 +    }
  1.3602 +
  1.3603 +    Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT :
  1.3604 +                               Telemetry::WORD_CACHE_MISSES_CHROME),
  1.3605 +                          aLength);
  1.3606 +#ifndef RELEASE_BUILD
  1.3607 +    if (aTextPerf) {
  1.3608 +        aTextPerf->current.wordCacheMiss++;
  1.3609 +    }
  1.3610 +#endif
  1.3611 +
  1.3612 +    sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
  1.3613 +                                                    aRunScript,
  1.3614 +                                                    aAppUnitsPerDevUnit,
  1.3615 +                                                    aFlags);
  1.3616 +    if (!sw) {
  1.3617 +        NS_WARNING("failed to create gfxShapedWord - expect missing text");
  1.3618 +        return nullptr;
  1.3619 +    }
  1.3620 +
  1.3621 +    DebugOnly<bool> ok =
  1.3622 +        ShapeText(aContext, aText, 0, aLength, aRunScript, sw);
  1.3623 +
  1.3624 +    NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
  1.3625 +
  1.3626 +    return sw;
  1.3627 +}
  1.3628 +
  1.3629 +bool
  1.3630 +gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
  1.3631 +{
  1.3632 +    const gfxShapedWord *sw = mShapedWord;
  1.3633 +    if (!sw) {
  1.3634 +        return false;
  1.3635 +    }
  1.3636 +    if (sw->GetLength() != aKey->mLength ||
  1.3637 +        sw->Flags() != aKey->mFlags ||
  1.3638 +        sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
  1.3639 +        sw->Script() != aKey->mScript) {
  1.3640 +        return false;
  1.3641 +    }
  1.3642 +    if (sw->TextIs8Bit()) {
  1.3643 +        if (aKey->mTextIs8Bit) {
  1.3644 +            return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
  1.3645 +                                aKey->mLength * sizeof(uint8_t)));
  1.3646 +        }
  1.3647 +        // The key has 16-bit text, even though all the characters are < 256,
  1.3648 +        // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
  1.3649 +        // comparing with will have 8-bit text.
  1.3650 +        const uint8_t   *s1 = sw->Text8Bit();
  1.3651 +        const char16_t *s2 = aKey->mText.mDouble;
  1.3652 +        const char16_t *s2end = s2 + aKey->mLength;
  1.3653 +        while (s2 < s2end) {
  1.3654 +            if (*s1++ != *s2++) {
  1.3655 +                return false;
  1.3656 +            }
  1.3657 +        }
  1.3658 +        return true;
  1.3659 +    }
  1.3660 +    NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
  1.3661 +                 !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
  1.3662 +    return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
  1.3663 +                        aKey->mLength * sizeof(char16_t)));
  1.3664 +}
  1.3665 +
  1.3666 +bool
  1.3667 +gfxFont::ShapeText(gfxContext    *aContext,
  1.3668 +                   const uint8_t *aText,
  1.3669 +                   uint32_t       aOffset,
  1.3670 +                   uint32_t       aLength,
  1.3671 +                   int32_t        aScript,
  1.3672 +                   gfxShapedText *aShapedText,
  1.3673 +                   bool           aPreferPlatformShaping)
  1.3674 +{
  1.3675 +    nsDependentCSubstring ascii((const char*)aText, aLength);
  1.3676 +    nsAutoString utf16;
  1.3677 +    AppendASCIItoUTF16(ascii, utf16);
  1.3678 +    if (utf16.Length() != aLength) {
  1.3679 +        return false;
  1.3680 +    }
  1.3681 +    return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
  1.3682 +                     aScript, aShapedText, aPreferPlatformShaping);
  1.3683 +}
  1.3684 +
  1.3685 +bool
  1.3686 +gfxFont::ShapeText(gfxContext      *aContext,
  1.3687 +                   const char16_t *aText,
  1.3688 +                   uint32_t         aOffset,
  1.3689 +                   uint32_t         aLength,
  1.3690 +                   int32_t          aScript,
  1.3691 +                   gfxShapedText   *aShapedText,
  1.3692 +                   bool             aPreferPlatformShaping)
  1.3693 +{
  1.3694 +    bool ok = false;
  1.3695 +
  1.3696 +    if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
  1.3697 +        ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
  1.3698 +                                        aScript, aShapedText);
  1.3699 +    }
  1.3700 +
  1.3701 +    if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
  1.3702 +        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
  1.3703 +            ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
  1.3704 +                                            aScript, aShapedText);
  1.3705 +        }
  1.3706 +    }
  1.3707 +
  1.3708 +    if (!ok) {
  1.3709 +        if (!mPlatformShaper) {
  1.3710 +            CreatePlatformShaper();
  1.3711 +            NS_ASSERTION(mPlatformShaper, "no platform shaper available!");
  1.3712 +        }
  1.3713 +        if (mPlatformShaper) {
  1.3714 +            ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
  1.3715 +                                            aScript, aShapedText);
  1.3716 +        }
  1.3717 +    }
  1.3718 +
  1.3719 +    PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
  1.3720 +
  1.3721 +    return ok;
  1.3722 +}
  1.3723 +
  1.3724 +void
  1.3725 +gfxFont::PostShapingFixup(gfxContext      *aContext,
  1.3726 +                          const char16_t *aText,
  1.3727 +                          uint32_t         aOffset,
  1.3728 +                          uint32_t         aLength,
  1.3729 +                          gfxShapedText   *aShapedText)
  1.3730 +{
  1.3731 +    if (IsSyntheticBold()) {
  1.3732 +        float synBoldOffset =
  1.3733 +                GetSyntheticBoldOffset() * CalcXScale(aContext);
  1.3734 +        aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
  1.3735 +                                                    aOffset, aLength);
  1.3736 +    }
  1.3737 +}
  1.3738 +
  1.3739 +#define MAX_SHAPING_LENGTH  32760 // slightly less than 32K, trying to avoid
  1.3740 +                                  // over-stressing platform shapers
  1.3741 +#define BACKTRACK_LIMIT     16 // backtrack this far looking for a good place
  1.3742 +                               // to split into fragments for separate shaping
  1.3743 +
  1.3744 +template<typename T>
  1.3745 +bool
  1.3746 +gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
  1.3747 +                                       const T    *aText,
  1.3748 +                                       uint32_t    aOffset,
  1.3749 +                                       uint32_t    aLength,
  1.3750 +                                       int32_t     aScript,
  1.3751 +                                       gfxTextRun *aTextRun)
  1.3752 +{
  1.3753 +    aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
  1.3754 +
  1.3755 +    bool ok = true;
  1.3756 +
  1.3757 +    while (ok && aLength > 0) {
  1.3758 +        uint32_t fragLen = aLength;
  1.3759 +
  1.3760 +        // limit the length of text we pass to shapers in a single call
  1.3761 +        if (fragLen > MAX_SHAPING_LENGTH) {
  1.3762 +            fragLen = MAX_SHAPING_LENGTH;
  1.3763 +
  1.3764 +            // in the 8-bit case, there are no multi-char clusters,
  1.3765 +            // so we don't need to do this check
  1.3766 +            if (sizeof(T) == sizeof(char16_t)) {
  1.3767 +                uint32_t i;
  1.3768 +                for (i = 0; i < BACKTRACK_LIMIT; ++i) {
  1.3769 +                    if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
  1.3770 +                        fragLen -= i;
  1.3771 +                        break;
  1.3772 +                    }
  1.3773 +                }
  1.3774 +                if (i == BACKTRACK_LIMIT) {
  1.3775 +                    // if we didn't find any cluster start while backtracking,
  1.3776 +                    // just check that we're not in the middle of a surrogate
  1.3777 +                    // pair; back up by one code unit if we are.
  1.3778 +                    if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
  1.3779 +                        NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
  1.3780 +                        --fragLen;
  1.3781 +                    }
  1.3782 +                }
  1.3783 +            }
  1.3784 +        }
  1.3785 +
  1.3786 +        ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun);
  1.3787 +
  1.3788 +        aText += fragLen;
  1.3789 +        aOffset += fragLen;
  1.3790 +        aLength -= fragLen;
  1.3791 +    }
  1.3792 +
  1.3793 +    return ok;
  1.3794 +}
  1.3795 +
  1.3796 +// Check if aCh is an unhandled control character that should be displayed
  1.3797 +// as a hexbox rather than rendered by some random font on the system.
  1.3798 +// We exclude \r as stray &#13;s are rather common (bug 941940).
  1.3799 +// Note that \n and \t don't come through here, as they have specific
  1.3800 +// meanings that have already been handled.
  1.3801 +static bool
  1.3802 +IsInvalidControlChar(uint32_t aCh)
  1.3803 +{
  1.3804 +    return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
  1.3805 +}
  1.3806 +
  1.3807 +template<typename T>
  1.3808 +bool
  1.3809 +gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
  1.3810 +                                   const T    *aText,
  1.3811 +                                   uint32_t    aOffset,
  1.3812 +                                   uint32_t    aLength,
  1.3813 +                                   int32_t     aScript,
  1.3814 +                                   gfxTextRun *aTextRun)
  1.3815 +{
  1.3816 +    uint32_t fragStart = 0;
  1.3817 +    bool ok = true;
  1.3818 +
  1.3819 +    for (uint32_t i = 0; i <= aLength && ok; ++i) {
  1.3820 +        T ch = (i < aLength) ? aText[i] : '\n';
  1.3821 +        bool invalid = gfxFontGroup::IsInvalidChar(ch);
  1.3822 +        uint32_t length = i - fragStart;
  1.3823 +
  1.3824 +        // break into separate fragments when we hit an invalid char
  1.3825 +        if (!invalid) {
  1.3826 +            continue;
  1.3827 +        }
  1.3828 +
  1.3829 +        if (length > 0) {
  1.3830 +            ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
  1.3831 +                                               aOffset + fragStart, length,
  1.3832 +                                               aScript, aTextRun);
  1.3833 +        }
  1.3834 +
  1.3835 +        if (i == aLength) {
  1.3836 +            break;
  1.3837 +        }
  1.3838 +
  1.3839 +        // fragment was terminated by an invalid char: skip it,
  1.3840 +        // unless it's a control char that we want to show as a hexbox,
  1.3841 +        // but record where TAB or NEWLINE occur
  1.3842 +        if (ch == '\t') {
  1.3843 +            aTextRun->SetIsTab(aOffset + i);
  1.3844 +        } else if (ch == '\n') {
  1.3845 +            aTextRun->SetIsNewline(aOffset + i);
  1.3846 +        } else if (IsInvalidControlChar(ch) &&
  1.3847 +            !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
  1.3848 +            aTextRun->SetMissingGlyph(aOffset + i, ch, this);
  1.3849 +        }
  1.3850 +        fragStart = i + 1;
  1.3851 +    }
  1.3852 +
  1.3853 +    NS_WARN_IF_FALSE(ok, "failed to shape text - expect garbled text");
  1.3854 +    return ok;
  1.3855 +}
  1.3856 +
  1.3857 +#ifndef RELEASE_BUILD
  1.3858 +#define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
  1.3859 +#else
  1.3860 +#define TEXT_PERF_INCR(tp, m)
  1.3861 +#endif
  1.3862 +
  1.3863 +inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
  1.3864 +inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; }
  1.3865 +
  1.3866 +inline static bool HasSpaces(const uint8_t *aString, uint32_t aLen)
  1.3867 +{
  1.3868 +    return memchr(aString, 0x20, aLen) != nullptr;
  1.3869 +}
  1.3870 +
  1.3871 +inline static bool HasSpaces(const char16_t *aString, uint32_t aLen)
  1.3872 +{
  1.3873 +    for (const char16_t *ch = aString; ch < aString + aLen; ch++) {
  1.3874 +        if (*ch == 0x20) {
  1.3875 +            return true;
  1.3876 +        }
  1.3877 +    }
  1.3878 +    return false;
  1.3879 +}
  1.3880 +
  1.3881 +template<typename T>
  1.3882 +bool
  1.3883 +gfxFont::SplitAndInitTextRun(gfxContext *aContext,
  1.3884 +                             gfxTextRun *aTextRun,
  1.3885 +                             const T *aString,
  1.3886 +                             uint32_t aRunStart,
  1.3887 +                             uint32_t aRunLength,
  1.3888 +                             int32_t aRunScript)
  1.3889 +{
  1.3890 +    if (aRunLength == 0) {
  1.3891 +        return true;
  1.3892 +    }
  1.3893 +
  1.3894 +    gfxTextPerfMetrics *tp = nullptr;
  1.3895 +
  1.3896 +#ifndef RELEASE_BUILD
  1.3897 +    tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
  1.3898 +    if (tp) {
  1.3899 +        if (mStyle.systemFont) {
  1.3900 +            tp->current.numChromeTextRuns++;
  1.3901 +        } else {
  1.3902 +            tp->current.numContentTextRuns++;
  1.3903 +        }
  1.3904 +        tp->current.numChars += aRunLength;
  1.3905 +        if (aRunLength > tp->current.maxTextRunLen) {
  1.3906 +            tp->current.maxTextRunLen = aRunLength;
  1.3907 +        }
  1.3908 +    }
  1.3909 +#endif
  1.3910 +
  1.3911 +    uint32_t wordCacheCharLimit =
  1.3912 +        gfxPlatform::GetPlatform()->WordCacheCharLimit();
  1.3913 +
  1.3914 +    // If spaces can participate in shaping (e.g. within lookups for automatic
  1.3915 +    // fractions), need to shape without using the word cache which segments
  1.3916 +    // textruns on space boundaries. Word cache can be used if the textrun
  1.3917 +    // is short enough to fit in the word cache and it lacks spaces.
  1.3918 +    if (SpaceMayParticipateInShaping(aRunScript)) {
  1.3919 +        if (aRunLength > wordCacheCharLimit ||
  1.3920 +            HasSpaces(aString + aRunStart, aRunLength)) {
  1.3921 +            TEXT_PERF_INCR(tp, wordCacheSpaceRules);
  1.3922 +            return ShapeTextWithoutWordCache(aContext, aString + aRunStart,
  1.3923 +                                             aRunStart, aRunLength, aRunScript,
  1.3924 +                                             aTextRun);
  1.3925 +        }
  1.3926 +    }
  1.3927 +
  1.3928 +    InitWordCache();
  1.3929 +
  1.3930 +    // the only flags we care about for ShapedWord construction/caching
  1.3931 +    uint32_t flags = aTextRun->GetFlags();
  1.3932 +    flags &= (gfxTextRunFactory::TEXT_IS_RTL |
  1.3933 +              gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES |
  1.3934 +              gfxTextRunFactory::TEXT_USE_MATH_SCRIPT);
  1.3935 +    if (sizeof(T) == sizeof(uint8_t)) {
  1.3936 +        flags |= gfxTextRunFactory::TEXT_IS_8BIT;
  1.3937 +    }
  1.3938 +
  1.3939 +    const T *text = aString + aRunStart;
  1.3940 +    uint32_t wordStart = 0;
  1.3941 +    uint32_t hash = 0;
  1.3942 +    bool wordIs8Bit = true;
  1.3943 +    int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
  1.3944 +
  1.3945 +    T nextCh = text[0];
  1.3946 +    for (uint32_t i = 0; i <= aRunLength; ++i) {
  1.3947 +        T ch = nextCh;
  1.3948 +        nextCh = (i < aRunLength - 1) ? text[i + 1] : '\n';
  1.3949 +        bool boundary = IsBoundarySpace(ch, nextCh);
  1.3950 +        bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
  1.3951 +        uint32_t length = i - wordStart;
  1.3952 +
  1.3953 +        // break into separate ShapedWords when we hit an invalid char,
  1.3954 +        // or a boundary space (always handled individually),
  1.3955 +        // or the first non-space after a space
  1.3956 +        if (!boundary && !invalid) {
  1.3957 +            if (!IsChar8Bit(ch)) {
  1.3958 +                wordIs8Bit = false;
  1.3959 +            }
  1.3960 +            // include this character in the hash, and move on to next
  1.3961 +            hash = HashMix(hash, ch);
  1.3962 +            continue;
  1.3963 +        }
  1.3964 +
  1.3965 +        // We've decided to break here (i.e. we're at the end of a "word");
  1.3966 +        // shape the word and add it to the textrun.
  1.3967 +        // For words longer than the limit, we don't use the
  1.3968 +        // font's word cache but just shape directly into the textrun.
  1.3969 +        if (length > wordCacheCharLimit) {
  1.3970 +            TEXT_PERF_INCR(tp, wordCacheLong);
  1.3971 +            bool ok = ShapeFragmentWithoutWordCache(aContext,
  1.3972 +                                                    text + wordStart,
  1.3973 +                                                    aRunStart + wordStart,
  1.3974 +                                                    length,
  1.3975 +                                                    aRunScript,
  1.3976 +                                                    aTextRun);
  1.3977 +            if (!ok) {
  1.3978 +                return false;
  1.3979 +            }
  1.3980 +        } else if (length > 0) {
  1.3981 +            uint32_t wordFlags = flags;
  1.3982 +            // in the 8-bit version of this method, TEXT_IS_8BIT was
  1.3983 +            // already set as part of |flags|, so no need for a per-word
  1.3984 +            // adjustment here
  1.3985 +            if (sizeof(T) == sizeof(char16_t)) {
  1.3986 +                if (wordIs8Bit) {
  1.3987 +                    wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
  1.3988 +                }
  1.3989 +            }
  1.3990 +            gfxShapedWord *sw = GetShapedWord(aContext,
  1.3991 +                                              text + wordStart, length,
  1.3992 +                                              hash, aRunScript,
  1.3993 +                                              appUnitsPerDevUnit,
  1.3994 +                                              wordFlags, tp);
  1.3995 +            if (sw) {
  1.3996 +                aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
  1.3997 +            } else {
  1.3998 +                return false; // failed, presumably out of memory?
  1.3999 +            }
  1.4000 +        }
  1.4001 +
  1.4002 +        if (boundary) {
  1.4003 +            // word was terminated by a space: add that to the textrun
  1.4004 +            if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
  1.4005 +                                                 aRunStart + i, ch))
  1.4006 +            {
  1.4007 +                static const uint8_t space = ' ';
  1.4008 +                gfxShapedWord *sw =
  1.4009 +                    GetShapedWord(aContext,
  1.4010 +                                  &space, 1,
  1.4011 +                                  HashMix(0, ' '), aRunScript,
  1.4012 +                                  appUnitsPerDevUnit,
  1.4013 +                                  flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
  1.4014 +                if (sw) {
  1.4015 +                    aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
  1.4016 +                } else {
  1.4017 +                    return false;
  1.4018 +                }
  1.4019 +            }
  1.4020 +            hash = 0;
  1.4021 +            wordStart = i + 1;
  1.4022 +            wordIs8Bit = true;
  1.4023 +            continue;
  1.4024 +        }
  1.4025 +
  1.4026 +        if (i == aRunLength) {
  1.4027 +            break;
  1.4028 +        }
  1.4029 +
  1.4030 +        NS_ASSERTION(invalid,
  1.4031 +                     "how did we get here except via an invalid char?");
  1.4032 +
  1.4033 +        // word was terminated by an invalid char: skip it,
  1.4034 +        // unless it's a control char that we want to show as a hexbox,
  1.4035 +        // but record where TAB or NEWLINE occur
  1.4036 +        if (ch == '\t') {
  1.4037 +            aTextRun->SetIsTab(aRunStart + i);
  1.4038 +        } else if (ch == '\n') {
  1.4039 +            aTextRun->SetIsNewline(aRunStart + i);
  1.4040 +        } else if (IsInvalidControlChar(ch) &&
  1.4041 +            !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
  1.4042 +            aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
  1.4043 +        }
  1.4044 +
  1.4045 +        hash = 0;
  1.4046 +        wordStart = i + 1;
  1.4047 +        wordIs8Bit = true;
  1.4048 +    }
  1.4049 +
  1.4050 +    return true;
  1.4051 +}
  1.4052 +
  1.4053 +gfxGlyphExtents *
  1.4054 +gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
  1.4055 +    uint32_t i, count = mGlyphExtentsArray.Length();
  1.4056 +    for (i = 0; i < count; ++i) {
  1.4057 +        if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
  1.4058 +            return mGlyphExtentsArray[i];
  1.4059 +    }
  1.4060 +    gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
  1.4061 +    if (glyphExtents) {
  1.4062 +        mGlyphExtentsArray.AppendElement(glyphExtents);
  1.4063 +        // Initialize the extents of a space glyph, assuming that spaces don't
  1.4064 +        // render anything!
  1.4065 +        glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
  1.4066 +    }
  1.4067 +    return glyphExtents;
  1.4068 +}
  1.4069 +
  1.4070 +void
  1.4071 +gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTight,
  1.4072 +                           gfxGlyphExtents *aExtents)
  1.4073 +{
  1.4074 +    gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
  1.4075 +    aContext->IdentityMatrix();
  1.4076 +
  1.4077 +    gfxRect svgBounds;
  1.4078 +    if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
  1.4079 +        mFontEntry->GetSVGGlyphExtents(aContext, aGlyphID, &svgBounds)) {
  1.4080 +        gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
  1.4081 +        aExtents->SetTightGlyphExtents(aGlyphID,
  1.4082 +                                       gfxRect(svgBounds.x * d2a,
  1.4083 +                                               svgBounds.y * d2a,
  1.4084 +                                               svgBounds.width * d2a,
  1.4085 +                                               svgBounds.height * d2a));
  1.4086 +        return;
  1.4087 +    }
  1.4088 +
  1.4089 +    cairo_glyph_t glyph;
  1.4090 +    glyph.index = aGlyphID;
  1.4091 +    glyph.x = 0;
  1.4092 +    glyph.y = 0;
  1.4093 +    cairo_text_extents_t extents;
  1.4094 +    cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents);
  1.4095 +
  1.4096 +    const Metrics& fontMetrics = GetMetrics();
  1.4097 +    int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
  1.4098 +    if (!aNeedTight && extents.x_bearing >= 0 &&
  1.4099 +        extents.y_bearing >= -fontMetrics.maxAscent &&
  1.4100 +        extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
  1.4101 +        uint32_t appUnitsWidth =
  1.4102 +            uint32_t(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
  1.4103 +        if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
  1.4104 +            aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, uint16_t(appUnitsWidth));
  1.4105 +            return;
  1.4106 +        }
  1.4107 +    }
  1.4108 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.4109 +    if (!aNeedTight) {
  1.4110 +        ++gGlyphExtentsSetupFallBackToTight;
  1.4111 +    }
  1.4112 +#endif
  1.4113 +
  1.4114 +    gfxFloat d2a = appUnitsPerDevUnit;
  1.4115 +    gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
  1.4116 +                   extents.width*d2a, extents.height*d2a);
  1.4117 +    aExtents->SetTightGlyphExtents(aGlyphID, bounds);
  1.4118 +}
  1.4119 +
  1.4120 +// Try to initialize font metrics by reading sfnt tables directly;
  1.4121 +// set mIsValid=TRUE and return TRUE on success.
  1.4122 +// Return FALSE if the gfxFontEntry subclass does not
  1.4123 +// implement GetFontTable(), or for non-sfnt fonts where tables are
  1.4124 +// not available.
  1.4125 +// If this returns TRUE without setting the mIsValid flag, then we -did-
  1.4126 +// apparently find an sfnt, but it was too broken to be used.
  1.4127 +bool
  1.4128 +gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
  1.4129 +{
  1.4130 +    mIsValid = false; // font is NOT valid in case of early return
  1.4131 +
  1.4132 +    const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
  1.4133 +    const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
  1.4134 +    const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
  1.4135 +
  1.4136 +    uint32_t len;
  1.4137 +
  1.4138 +    if (mFUnitsConvFactor == 0.0) {
  1.4139 +        // If the conversion factor from FUnits is not yet set,
  1.4140 +        // get the unitsPerEm from the 'head' table via the font entry
  1.4141 +        uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
  1.4142 +        if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
  1.4143 +            return false;
  1.4144 +        }
  1.4145 +        mFUnitsConvFactor = mAdjustedSize / unitsPerEm;
  1.4146 +    }
  1.4147 +
  1.4148 +    // 'hhea' table is required to get vertical extents
  1.4149 +    gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
  1.4150 +    if (!hheaTable) {
  1.4151 +        return false; // no 'hhea' table -> not an sfnt
  1.4152 +    }
  1.4153 +    const HheaTable* hhea =
  1.4154 +        reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len));
  1.4155 +    if (len < sizeof(HheaTable)) {
  1.4156 +        return false;
  1.4157 +    }
  1.4158 +
  1.4159 +#define SET_UNSIGNED(field,src) aMetrics.field = uint16_t(src) * mFUnitsConvFactor
  1.4160 +#define SET_SIGNED(field,src)   aMetrics.field = int16_t(src) * mFUnitsConvFactor
  1.4161 +
  1.4162 +    SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
  1.4163 +    SET_SIGNED(maxAscent, hhea->ascender);
  1.4164 +    SET_SIGNED(maxDescent, -int16_t(hhea->descender));
  1.4165 +    SET_SIGNED(externalLeading, hhea->lineGap);
  1.4166 +
  1.4167 +    // 'post' table is required for underline metrics
  1.4168 +    gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
  1.4169 +    if (!postTable) {
  1.4170 +        return true; // no 'post' table -> sfnt is not valid
  1.4171 +    }
  1.4172 +    const PostTable *post =
  1.4173 +        reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len));
  1.4174 +    if (len < offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) {
  1.4175 +        return true; // bad post table -> sfnt is not valid
  1.4176 +    }
  1.4177 +
  1.4178 +    SET_SIGNED(underlineOffset, post->underlinePosition);
  1.4179 +    SET_UNSIGNED(underlineSize, post->underlineThickness);
  1.4180 +
  1.4181 +    // 'OS/2' table is optional, if not found we'll estimate xHeight
  1.4182 +    // and aveCharWidth by measuring glyphs
  1.4183 +    gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
  1.4184 +    if (os2Table) {
  1.4185 +        const OS2Table *os2 =
  1.4186 +            reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
  1.4187 +        if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
  1.4188 +            uint16_t(os2->version) >= 2) {
  1.4189 +            // version 2 and later includes the x-height field
  1.4190 +            SET_SIGNED(xHeight, os2->sxHeight);
  1.4191 +            // Abs because of negative xHeight seen in Kokonor (Tibetan) font
  1.4192 +            aMetrics.xHeight = Abs(aMetrics.xHeight);
  1.4193 +        }
  1.4194 +        // this should always be present in any valid OS/2 of any version
  1.4195 +        if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
  1.4196 +            SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
  1.4197 +            SET_SIGNED(subscriptOffset, os2->ySubscriptYOffset);
  1.4198 +            SET_SIGNED(superscriptOffset, os2->ySuperscriptYOffset);
  1.4199 +            SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
  1.4200 +            SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
  1.4201 +
  1.4202 +            // for fonts with USE_TYPO_METRICS set in the fsSelection field,
  1.4203 +            // and for all OpenType math fonts (having a 'MATH' table),
  1.4204 +            // let the OS/2 sTypo* metrics override those from the hhea table
  1.4205 +            // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
  1.4206 +            const uint16_t kUseTypoMetricsMask = 1 << 7;
  1.4207 +            if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask) ||
  1.4208 +                mFontEntry->HasFontTable(TRUETYPE_TAG('M','A','T','H'))) {
  1.4209 +                SET_SIGNED(maxAscent, os2->sTypoAscender);
  1.4210 +                SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
  1.4211 +                SET_SIGNED(externalLeading, os2->sTypoLineGap);
  1.4212 +            }
  1.4213 +        }
  1.4214 +    }
  1.4215 +
  1.4216 +    mIsValid = true;
  1.4217 +
  1.4218 +    return true;
  1.4219 +}
  1.4220 +
  1.4221 +static double
  1.4222 +RoundToNearestMultiple(double aValue, double aFraction)
  1.4223 +{
  1.4224 +    return floor(aValue/aFraction + 0.5) * aFraction;
  1.4225 +}
  1.4226 +
  1.4227 +void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics)
  1.4228 +{
  1.4229 +    aMetrics.maxAscent =
  1.4230 +        ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
  1.4231 +    aMetrics.maxDescent =
  1.4232 +        ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
  1.4233 +
  1.4234 +    if (aMetrics.xHeight <= 0) {
  1.4235 +        // only happens if we couldn't find either font metrics
  1.4236 +        // or a char to measure;
  1.4237 +        // pick an arbitrary value that's better than zero
  1.4238 +        aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
  1.4239 +    }
  1.4240 +
  1.4241 +    aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
  1.4242 +
  1.4243 +    if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) {
  1.4244 +        aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight;
  1.4245 +    } else {
  1.4246 +        aMetrics.internalLeading = 0.0;
  1.4247 +    }
  1.4248 +
  1.4249 +    aMetrics.emAscent = aMetrics.maxAscent * aMetrics.emHeight
  1.4250 +                            / aMetrics.maxHeight;
  1.4251 +    aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
  1.4252 +
  1.4253 +    if (GetFontEntry()->IsFixedPitch()) {
  1.4254 +        // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
  1.4255 +        // advance than the average character width... this forces
  1.4256 +        // those fonts to be recognized like fixed pitch fonts by layout.
  1.4257 +        aMetrics.maxAdvance = aMetrics.aveCharWidth;
  1.4258 +    }
  1.4259 +
  1.4260 +    if (!aMetrics.subscriptOffset) {
  1.4261 +        aMetrics.subscriptOffset = aMetrics.xHeight;
  1.4262 +    }
  1.4263 +    if (!aMetrics.superscriptOffset) {
  1.4264 +        aMetrics.superscriptOffset = aMetrics.xHeight;
  1.4265 +    }
  1.4266 +
  1.4267 +    if (!aMetrics.strikeoutOffset) {
  1.4268 +        aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
  1.4269 +    }
  1.4270 +    if (!aMetrics.strikeoutSize) {
  1.4271 +        aMetrics.strikeoutSize = aMetrics.underlineSize;
  1.4272 +    }
  1.4273 +}
  1.4274 +
  1.4275 +void
  1.4276 +gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont)
  1.4277 +{
  1.4278 +    // Even if this font size is zero, this font is created with non-zero size.
  1.4279 +    // However, for layout and others, we should return the metrics of zero size font.
  1.4280 +    if (mStyle.size == 0.0) {
  1.4281 +        memset(aMetrics, 0, sizeof(gfxFont::Metrics));
  1.4282 +        return;
  1.4283 +    }
  1.4284 +
  1.4285 +    // MS (P)Gothic and MS (P)Mincho are not having suitable values in their super script offset.
  1.4286 +    // If the values are not suitable, we should use x-height instead of them.
  1.4287 +    // See https://bugzilla.mozilla.org/show_bug.cgi?id=353632
  1.4288 +    if (aMetrics->superscriptOffset <= 0 ||
  1.4289 +        aMetrics->superscriptOffset >= aMetrics->maxAscent) {
  1.4290 +        aMetrics->superscriptOffset = aMetrics->xHeight;
  1.4291 +    }
  1.4292 +    // And also checking the case of sub script offset. The old gfx for win has checked this too.
  1.4293 +    if (aMetrics->subscriptOffset <= 0 ||
  1.4294 +        aMetrics->subscriptOffset >= aMetrics->maxAscent) {
  1.4295 +        aMetrics->subscriptOffset = aMetrics->xHeight;
  1.4296 +    }
  1.4297 +
  1.4298 +    aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize);
  1.4299 +    aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize);
  1.4300 +
  1.4301 +    aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0);
  1.4302 +
  1.4303 +    if (aMetrics->maxAscent < 1.0) {
  1.4304 +        // We cannot draw strikeout line and overline in the ascent...
  1.4305 +        aMetrics->underlineSize = 0;
  1.4306 +        aMetrics->underlineOffset = 0;
  1.4307 +        aMetrics->strikeoutSize = 0;
  1.4308 +        aMetrics->strikeoutOffset = 0;
  1.4309 +        return;
  1.4310 +    }
  1.4311 +
  1.4312 +    /**
  1.4313 +     * Some CJK fonts have bad underline offset. Therefore, if this is such font,
  1.4314 +     * we need to lower the underline offset to bottom of *em* descent.
  1.4315 +     * However, if this is system font, we should not do this for the rendering compatibility with
  1.4316 +     * another application's UI on the platform.
  1.4317 +     * XXX Should not use this hack if the font size is too small?
  1.4318 +     *     Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2)
  1.4319 +     */
  1.4320 +    if (!mStyle.systemFont && aIsBadUnderlineFont) {
  1.4321 +        // First, we need 2 pixels between baseline and underline at least. Because many CJK characters
  1.4322 +        // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
  1.4323 +        aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0);
  1.4324 +
  1.4325 +        // Next, we put the underline to bottom of below of the descent space.
  1.4326 +        if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
  1.4327 +            aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -aMetrics->emDescent);
  1.4328 +        } else {
  1.4329 +            aMetrics->underlineOffset = std::min(aMetrics->underlineOffset,
  1.4330 +                                               aMetrics->underlineSize - aMetrics->emDescent);
  1.4331 +        }
  1.4332 +    }
  1.4333 +    // If underline positioned is too far from the text, descent position is preferred so that underline
  1.4334 +    // will stay within the boundary.
  1.4335 +    else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
  1.4336 +        if (aMetrics->underlineSize > aMetrics->maxDescent)
  1.4337 +            aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0);
  1.4338 +        // The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.)
  1.4339 +        aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
  1.4340 +    }
  1.4341 +
  1.4342 +    // If strikeout line is overflowed from the ascent, the line should be resized and moved for
  1.4343 +    // that being in the ascent space.
  1.4344 +    // Note that the strikeoutOffset is *middle* of the strikeout line position.
  1.4345 +    gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
  1.4346 +    if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
  1.4347 +        if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
  1.4348 +            aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0);
  1.4349 +            halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
  1.4350 +        }
  1.4351 +        gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
  1.4352 +        aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0);
  1.4353 +    }
  1.4354 +
  1.4355 +    // If overline is larger than the ascent, the line should be resized.
  1.4356 +    if (aMetrics->underlineSize > aMetrics->maxAscent) {
  1.4357 +        aMetrics->underlineSize = aMetrics->maxAscent;
  1.4358 +    }
  1.4359 +}
  1.4360 +
  1.4361 +gfxFloat
  1.4362 +gfxFont::SynthesizeSpaceWidth(uint32_t aCh)
  1.4363 +{
  1.4364 +    // return an appropriate width for various Unicode space characters
  1.4365 +    // that we "fake" if they're not actually present in the font;
  1.4366 +    // returns negative value if the char is not a known space.
  1.4367 +    switch (aCh) {
  1.4368 +    case 0x2000:                                 // en quad
  1.4369 +    case 0x2002: return GetAdjustedSize() / 2;   // en space
  1.4370 +    case 0x2001:                                 // em quad
  1.4371 +    case 0x2003: return GetAdjustedSize();       // em space
  1.4372 +    case 0x2004: return GetAdjustedSize() / 3;   // three-per-em space
  1.4373 +    case 0x2005: return GetAdjustedSize() / 4;   // four-per-em space
  1.4374 +    case 0x2006: return GetAdjustedSize() / 6;   // six-per-em space
  1.4375 +    case 0x2007: return GetMetrics().zeroOrAveCharWidth; // figure space
  1.4376 +    case 0x2008: return GetMetrics().spaceWidth; // punctuation space 
  1.4377 +    case 0x2009: return GetAdjustedSize() / 5;   // thin space
  1.4378 +    case 0x200a: return GetAdjustedSize() / 10;  // hair space
  1.4379 +    case 0x202f: return GetAdjustedSize() / 5;   // narrow no-break space
  1.4380 +    default: return -1.0;
  1.4381 +    }
  1.4382 +}
  1.4383 +
  1.4384 +/*static*/ size_t
  1.4385 +gfxFont::WordCacheEntrySizeOfExcludingThis(CacheHashEntry*   aHashEntry,
  1.4386 +                                           MallocSizeOf aMallocSizeOf,
  1.4387 +                                           void*             aUserArg)
  1.4388 +{
  1.4389 +    return aMallocSizeOf(aHashEntry->mShapedWord.get());
  1.4390 +}
  1.4391 +
  1.4392 +void
  1.4393 +gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
  1.4394 +                                FontCacheSizes* aSizes) const
  1.4395 +{
  1.4396 +    for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
  1.4397 +        aSizes->mFontInstances +=
  1.4398 +            mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
  1.4399 +    }
  1.4400 +    if (mWordCache) {
  1.4401 +        aSizes->mShapedWords +=
  1.4402 +            mWordCache->SizeOfExcludingThis(WordCacheEntrySizeOfExcludingThis,
  1.4403 +                                            aMallocSizeOf);
  1.4404 +    }
  1.4405 +}
  1.4406 +
  1.4407 +void
  1.4408 +gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
  1.4409 +                                FontCacheSizes* aSizes) const
  1.4410 +{
  1.4411 +    aSizes->mFontInstances += aMallocSizeOf(this);
  1.4412 +    AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
  1.4413 +}
  1.4414 +
  1.4415 +void
  1.4416 +gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver)
  1.4417 +{
  1.4418 +    if (!mGlyphChangeObservers) {
  1.4419 +        mGlyphChangeObservers = new nsTHashtable<nsPtrHashKey<GlyphChangeObserver> >;
  1.4420 +    }
  1.4421 +    mGlyphChangeObservers->PutEntry(aObserver);
  1.4422 +}
  1.4423 +
  1.4424 +void
  1.4425 +gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver)
  1.4426 +{
  1.4427 +    NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
  1.4428 +    NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered");
  1.4429 +    mGlyphChangeObservers->RemoveEntry(aObserver);
  1.4430 +}
  1.4431 +
  1.4432 +gfxGlyphExtents::~gfxGlyphExtents()
  1.4433 +{
  1.4434 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.4435 +    gGlyphExtentsWidthsTotalSize +=
  1.4436 +        mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf);
  1.4437 +    gGlyphExtentsCount++;
  1.4438 +#endif
  1.4439 +    MOZ_COUNT_DTOR(gfxGlyphExtents);
  1.4440 +}
  1.4441 +
  1.4442 +bool
  1.4443 +gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
  1.4444 +    gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents)
  1.4445 +{
  1.4446 +    HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
  1.4447 +    if (!entry) {
  1.4448 +        if (!aContext) {
  1.4449 +            NS_WARNING("Could not get glyph extents (no aContext)");
  1.4450 +            return false;
  1.4451 +        }
  1.4452 +
  1.4453 +        if (aFont->SetupCairoFont(aContext)) {
  1.4454 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.4455 +            ++gGlyphExtentsSetupLazyTight;
  1.4456 +#endif
  1.4457 +            aFont->SetupGlyphExtents(aContext, aGlyphID, true, this);
  1.4458 +            entry = mTightGlyphExtents.GetEntry(aGlyphID);
  1.4459 +        }
  1.4460 +        if (!entry) {
  1.4461 +            NS_WARNING("Could not get glyph extents");
  1.4462 +            return false;
  1.4463 +        }
  1.4464 +    }
  1.4465 +
  1.4466 +    *aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
  1.4467 +    return true;
  1.4468 +}
  1.4469 +
  1.4470 +gfxGlyphExtents::GlyphWidths::~GlyphWidths()
  1.4471 +{
  1.4472 +    uint32_t i, count = mBlocks.Length();
  1.4473 +    for (i = 0; i < count; ++i) {
  1.4474 +        uintptr_t bits = mBlocks[i];
  1.4475 +        if (bits && !(bits & 0x1)) {
  1.4476 +            delete[] reinterpret_cast<uint16_t *>(bits);
  1.4477 +        }
  1.4478 +    }
  1.4479 +}
  1.4480 +
  1.4481 +uint32_t
  1.4482 +gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  1.4483 +{
  1.4484 +    uint32_t i;
  1.4485 +    uint32_t size = mBlocks.SizeOfExcludingThis(aMallocSizeOf);
  1.4486 +    for (i = 0; i < mBlocks.Length(); ++i) {
  1.4487 +        uintptr_t bits = mBlocks[i];
  1.4488 +        if (bits && !(bits & 0x1)) {
  1.4489 +            size += aMallocSizeOf(reinterpret_cast<void*>(bits));
  1.4490 +        }
  1.4491 +    }
  1.4492 +    return size;
  1.4493 +}
  1.4494 +
  1.4495 +void
  1.4496 +gfxGlyphExtents::GlyphWidths::Set(uint32_t aGlyphID, uint16_t aWidth)
  1.4497 +{
  1.4498 +    uint32_t block = aGlyphID >> BLOCK_SIZE_BITS;
  1.4499 +    uint32_t len = mBlocks.Length();
  1.4500 +    if (block >= len) {
  1.4501 +        uintptr_t *elems = mBlocks.AppendElements(block + 1 - len);
  1.4502 +        if (!elems)
  1.4503 +            return;
  1.4504 +        memset(elems, 0, sizeof(uintptr_t)*(block + 1 - len));
  1.4505 +    }
  1.4506 +
  1.4507 +    uintptr_t bits = mBlocks[block];
  1.4508 +    uint32_t glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
  1.4509 +    if (!bits) {
  1.4510 +        mBlocks[block] = MakeSingle(glyphOffset, aWidth);
  1.4511 +        return;
  1.4512 +    }
  1.4513 +
  1.4514 +    uint16_t *newBlock;
  1.4515 +    if (bits & 0x1) {
  1.4516 +        // Expand the block to a real block. We could avoid this by checking
  1.4517 +        // glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
  1.4518 +        newBlock = new uint16_t[BLOCK_SIZE];
  1.4519 +        if (!newBlock)
  1.4520 +            return;
  1.4521 +        uint32_t i;
  1.4522 +        for (i = 0; i < BLOCK_SIZE; ++i) {
  1.4523 +            newBlock[i] = INVALID_WIDTH;
  1.4524 +        }
  1.4525 +        newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
  1.4526 +        mBlocks[block] = reinterpret_cast<uintptr_t>(newBlock);
  1.4527 +    } else {
  1.4528 +        newBlock = reinterpret_cast<uint16_t *>(bits);
  1.4529 +    }
  1.4530 +    newBlock[glyphOffset] = aWidth;
  1.4531 +}
  1.4532 +
  1.4533 +void
  1.4534 +gfxGlyphExtents::SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits)
  1.4535 +{
  1.4536 +    HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
  1.4537 +    if (!entry)
  1.4538 +        return;
  1.4539 +    entry->x = aExtentsAppUnits.X();
  1.4540 +    entry->y = aExtentsAppUnits.Y();
  1.4541 +    entry->width = aExtentsAppUnits.Width();
  1.4542 +    entry->height = aExtentsAppUnits.Height();
  1.4543 +}
  1.4544 +
  1.4545 +size_t
  1.4546 +gfxGlyphExtents::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  1.4547 +{
  1.4548 +    return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) +
  1.4549 +        mTightGlyphExtents.SizeOfExcludingThis(nullptr, aMallocSizeOf);
  1.4550 +}
  1.4551 +
  1.4552 +size_t
  1.4553 +gfxGlyphExtents::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  1.4554 +{
  1.4555 +    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  1.4556 +}
  1.4557 +
  1.4558 +gfxFontGroup::gfxFontGroup(const nsAString& aFamilies,
  1.4559 +                           const gfxFontStyle *aStyle,
  1.4560 +                           gfxUserFontSet *aUserFontSet)
  1.4561 +    : mFamilies(aFamilies)
  1.4562 +    , mStyle(*aStyle)
  1.4563 +    , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
  1.4564 +    , mHyphenWidth(-1)
  1.4565 +    , mUserFontSet(aUserFontSet)
  1.4566 +    , mTextPerf(nullptr)
  1.4567 +    , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language))
  1.4568 +    , mSkipDrawing(false)
  1.4569 +{
  1.4570 +    // We don't use SetUserFontSet() here, as we want to unconditionally call
  1.4571 +    // BuildFontList() rather than only do UpdateFontList() if it changed.
  1.4572 +    mCurrGeneration = GetGeneration();
  1.4573 +    BuildFontList();
  1.4574 +}
  1.4575 +
  1.4576 +void
  1.4577 +gfxFontGroup::BuildFontList()
  1.4578 +{
  1.4579 +// "#if" to be removed once all platforms are moved to gfxPlatformFontList interface
  1.4580 +// and subclasses of gfxFontGroup eliminated
  1.4581 +#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
  1.4582 +    ForEachFont(FindPlatformFont, this);
  1.4583 +
  1.4584 +    if (mFonts.Length() == 0) {
  1.4585 +        bool needsBold;
  1.4586 +        gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
  1.4587 +        gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
  1.4588 +        NS_ASSERTION(defaultFamily,
  1.4589 +                     "invalid default font returned by GetDefaultFont");
  1.4590 +
  1.4591 +        if (defaultFamily) {
  1.4592 +            gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
  1.4593 +                                                               needsBold);
  1.4594 +            if (fe) {
  1.4595 +                nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
  1.4596 +                                                            needsBold);
  1.4597 +                if (font) {
  1.4598 +                    mFonts.AppendElement(FamilyFace(defaultFamily, font));
  1.4599 +                }
  1.4600 +            }
  1.4601 +        }
  1.4602 +
  1.4603 +        if (mFonts.Length() == 0) {
  1.4604 +            // Try for a "font of last resort...."
  1.4605 +            // Because an empty font list would be Really Bad for later code
  1.4606 +            // that assumes it will be able to get valid metrics for layout,
  1.4607 +            // just look for the first usable font and put in the list.
  1.4608 +            // (see bug 554544)
  1.4609 +            nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families;
  1.4610 +            pfl->GetFontFamilyList(families);
  1.4611 +            uint32_t count = families.Length();
  1.4612 +            for (uint32_t i = 0; i < count; ++i) {
  1.4613 +                gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle,
  1.4614 +                                                                 needsBold);
  1.4615 +                if (fe) {
  1.4616 +                    nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
  1.4617 +                                                                needsBold);
  1.4618 +                    if (font) {
  1.4619 +                        mFonts.AppendElement(FamilyFace(families[i], font));
  1.4620 +                        break;
  1.4621 +                    }
  1.4622 +                }
  1.4623 +            }
  1.4624 +        }
  1.4625 +
  1.4626 +        if (mFonts.Length() == 0) {
  1.4627 +            // an empty font list at this point is fatal; we're not going to
  1.4628 +            // be able to do even the most basic layout operations
  1.4629 +            char msg[256]; // CHECK buffer length if revising message below
  1.4630 +            sprintf(msg, "unable to find a usable font (%.220s)",
  1.4631 +                    NS_ConvertUTF16toUTF8(mFamilies).get());
  1.4632 +            NS_RUNTIMEABORT(msg);
  1.4633 +        }
  1.4634 +    }
  1.4635 +
  1.4636 +    if (!mStyle.systemFont) {
  1.4637 +        uint32_t count = mFonts.Length();
  1.4638 +        for (uint32_t i = 0; i < count; ++i) {
  1.4639 +            gfxFont* font = mFonts[i].Font();
  1.4640 +            if (font->GetFontEntry()->mIsBadUnderlineFont) {
  1.4641 +                gfxFloat first = mFonts[0].Font()->GetMetrics().underlineOffset;
  1.4642 +                gfxFloat bad = font->GetMetrics().underlineOffset;
  1.4643 +                mUnderlineOffset = std::min(first, bad);
  1.4644 +                break;
  1.4645 +            }
  1.4646 +        }
  1.4647 +    }
  1.4648 +#endif
  1.4649 +}
  1.4650 +
  1.4651 +bool
  1.4652 +gfxFontGroup::FindPlatformFont(const nsAString& aName,
  1.4653 +                               const nsACString& aGenericName,
  1.4654 +                               bool aUseFontSet,
  1.4655 +                               void *aClosure)
  1.4656 +{
  1.4657 +    gfxFontGroup *fontGroup = static_cast<gfxFontGroup*>(aClosure);
  1.4658 +    const gfxFontStyle *fontStyle = fontGroup->GetStyle();
  1.4659 +
  1.4660 +    bool needsBold;
  1.4661 +    gfxFontFamily *family = nullptr;
  1.4662 +    gfxFontEntry *fe = nullptr;
  1.4663 +
  1.4664 +    if (aUseFontSet) {
  1.4665 +        // First, look up in the user font set...
  1.4666 +        // If the fontSet matches the family, we must not look for a platform
  1.4667 +        // font of the same name, even if we fail to actually get a fontEntry
  1.4668 +        // here; we'll fall back to the next name in the CSS font-family list.
  1.4669 +        gfxUserFontSet *fs = fontGroup->GetUserFontSet();
  1.4670 +        if (fs) {
  1.4671 +            // If the fontSet matches the family, but the font has not yet finished
  1.4672 +            // loading (nor has its load timeout fired), the fontGroup should wait
  1.4673 +            // for the download, and not actually draw its text yet.
  1.4674 +            family = fs->GetFamily(aName);
  1.4675 +            if (family) {
  1.4676 +                bool waitForUserFont = false;
  1.4677 +                fe = fs->FindFontEntry(family, *fontStyle,
  1.4678 +                                       needsBold, waitForUserFont);
  1.4679 +                if (!fe && waitForUserFont) {
  1.4680 +                    fontGroup->mSkipDrawing = true;
  1.4681 +                }
  1.4682 +            }
  1.4683 +        }
  1.4684 +    }
  1.4685 +
  1.4686 +    // Not known in the user font set ==> check system fonts
  1.4687 +    // XXX: Fallback is bad..
  1.4688 +    if (!family) {
  1.4689 +        gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
  1.4690 +        family = fontList->FindFamily(aName);
  1.4691 +        if (family) {
  1.4692 +            fe = family->FindFontForStyle(*fontStyle, needsBold);
  1.4693 +        }
  1.4694 +    }
  1.4695 +
  1.4696 +    // add to the font group, unless it's already there
  1.4697 +    if (fe && !fontGroup->HasFont(fe)) {
  1.4698 +        nsRefPtr<gfxFont> font = fe->FindOrMakeFont(fontStyle, needsBold);
  1.4699 +        if (font) {
  1.4700 +            fontGroup->mFonts.AppendElement(FamilyFace(family, font));
  1.4701 +        }
  1.4702 +    }
  1.4703 +
  1.4704 +    return true;
  1.4705 +}
  1.4706 +
  1.4707 +bool
  1.4708 +gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
  1.4709 +{
  1.4710 +    uint32_t count = mFonts.Length();
  1.4711 +    for (uint32_t i = 0; i < count; ++i) {
  1.4712 +        if (mFonts[i].Font()->GetFontEntry() == aFontEntry)
  1.4713 +            return true;
  1.4714 +    }
  1.4715 +    return false;
  1.4716 +}
  1.4717 +
  1.4718 +gfxFontGroup::~gfxFontGroup()
  1.4719 +{
  1.4720 +    mFonts.Clear();
  1.4721 +}
  1.4722 +
  1.4723 +gfxFontGroup *
  1.4724 +gfxFontGroup::Copy(const gfxFontStyle *aStyle)
  1.4725 +{
  1.4726 +    gfxFontGroup *fg = new gfxFontGroup(mFamilies, aStyle, mUserFontSet);
  1.4727 +    fg->SetTextPerfMetrics(mTextPerf);
  1.4728 +    return fg;
  1.4729 +}
  1.4730 +
  1.4731 +bool 
  1.4732 +gfxFontGroup::IsInvalidChar(uint8_t ch)
  1.4733 +{
  1.4734 +    return ((ch & 0x7f) < 0x20 || ch == 0x7f);
  1.4735 +}
  1.4736 +
  1.4737 +bool 
  1.4738 +gfxFontGroup::IsInvalidChar(char16_t ch)
  1.4739 +{
  1.4740 +    // All printable 7-bit ASCII values are OK
  1.4741 +    if (ch >= ' ' && ch < 0x7f) {
  1.4742 +        return false;
  1.4743 +    }
  1.4744 +    // No point in sending non-printing control chars through font shaping
  1.4745 +    if (ch <= 0x9f) {
  1.4746 +        return true;
  1.4747 +    }
  1.4748 +    return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
  1.4749 +             (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
  1.4750 +            IsBidiControl(ch));
  1.4751 +}
  1.4752 +
  1.4753 +bool
  1.4754 +gfxFontGroup::ForEachFont(FontCreationCallback fc,
  1.4755 +                          void *closure)
  1.4756 +{
  1.4757 +    return ForEachFontInternal(mFamilies, mStyle.language,
  1.4758 +                               true, true, true, fc, closure);
  1.4759 +}
  1.4760 +
  1.4761 +bool
  1.4762 +gfxFontGroup::ForEachFont(const nsAString& aFamilies,
  1.4763 +                          nsIAtom *aLanguage,
  1.4764 +                          FontCreationCallback fc,
  1.4765 +                          void *closure)
  1.4766 +{
  1.4767 +    return ForEachFontInternal(aFamilies, aLanguage,
  1.4768 +                               false, true, true, fc, closure);
  1.4769 +}
  1.4770 +
  1.4771 +struct ResolveData {
  1.4772 +    ResolveData(gfxFontGroup::FontCreationCallback aCallback,
  1.4773 +                nsACString& aGenericFamily,
  1.4774 +                bool aUseFontSet,
  1.4775 +                void *aClosure) :
  1.4776 +        mCallback(aCallback),
  1.4777 +        mGenericFamily(aGenericFamily),
  1.4778 +        mUseFontSet(aUseFontSet),
  1.4779 +        mClosure(aClosure) {
  1.4780 +    }
  1.4781 +    gfxFontGroup::FontCreationCallback mCallback;
  1.4782 +    nsCString mGenericFamily;
  1.4783 +    bool mUseFontSet;
  1.4784 +    void *mClosure;
  1.4785 +};
  1.4786 +
  1.4787 +bool
  1.4788 +gfxFontGroup::ForEachFontInternal(const nsAString& aFamilies,
  1.4789 +                                  nsIAtom *aLanguage,
  1.4790 +                                  bool aResolveGeneric,
  1.4791 +                                  bool aResolveFontName,
  1.4792 +                                  bool aUseFontSet,
  1.4793 +                                  FontCreationCallback fc,
  1.4794 +                                  void *closure)
  1.4795 +{
  1.4796 +    const char16_t kSingleQuote  = char16_t('\'');
  1.4797 +    const char16_t kDoubleQuote  = char16_t('\"');
  1.4798 +    const char16_t kComma        = char16_t(',');
  1.4799 +
  1.4800 +    nsIAtom *groupAtom = nullptr;
  1.4801 +    nsAutoCString groupString;
  1.4802 +    if (aLanguage) {
  1.4803 +        if (!gLangService) {
  1.4804 +            CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
  1.4805 +        }
  1.4806 +        if (gLangService) {
  1.4807 +            nsresult rv;
  1.4808 +            groupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
  1.4809 +        }
  1.4810 +    }
  1.4811 +    if (!groupAtom) {
  1.4812 +        groupAtom = nsGkAtoms::Unicode;
  1.4813 +    }
  1.4814 +    groupAtom->ToUTF8String(groupString);
  1.4815 +
  1.4816 +    nsPromiseFlatString families(aFamilies);
  1.4817 +    const char16_t *p, *p_end;
  1.4818 +    families.BeginReading(p);
  1.4819 +    families.EndReading(p_end);
  1.4820 +    nsAutoString family;
  1.4821 +    nsAutoCString lcFamily;
  1.4822 +    nsAutoString genericFamily;
  1.4823 +
  1.4824 +    while (p < p_end) {
  1.4825 +        while (nsCRT::IsAsciiSpace(*p) || *p == kComma)
  1.4826 +            if (++p == p_end)
  1.4827 +                return true;
  1.4828 +
  1.4829 +        bool generic;
  1.4830 +        if (*p == kSingleQuote || *p == kDoubleQuote) {
  1.4831 +            // quoted font family
  1.4832 +            char16_t quoteMark = *p;
  1.4833 +            if (++p == p_end)
  1.4834 +                return true;
  1.4835 +            const char16_t *nameStart = p;
  1.4836 +
  1.4837 +            // XXX What about CSS character escapes?
  1.4838 +            while (*p != quoteMark)
  1.4839 +                if (++p == p_end)
  1.4840 +                    return true;
  1.4841 +
  1.4842 +            family = Substring(nameStart, p);
  1.4843 +            generic = false;
  1.4844 +            genericFamily.SetIsVoid(true);
  1.4845 +
  1.4846 +            while (++p != p_end && *p != kComma)
  1.4847 +                /* nothing */ ;
  1.4848 +
  1.4849 +        } else {
  1.4850 +            // unquoted font family
  1.4851 +            const char16_t *nameStart = p;
  1.4852 +            while (++p != p_end && *p != kComma)
  1.4853 +                /* nothing */ ;
  1.4854 +
  1.4855 +            family = Substring(nameStart, p);
  1.4856 +            family.CompressWhitespace(false, true);
  1.4857 +
  1.4858 +            if (aResolveGeneric &&
  1.4859 +                (family.LowerCaseEqualsLiteral("serif") ||
  1.4860 +                 family.LowerCaseEqualsLiteral("sans-serif") ||
  1.4861 +                 family.LowerCaseEqualsLiteral("monospace") ||
  1.4862 +                 family.LowerCaseEqualsLiteral("cursive") ||
  1.4863 +                 family.LowerCaseEqualsLiteral("fantasy")))
  1.4864 +            {
  1.4865 +                generic = true;
  1.4866 +
  1.4867 +                ToLowerCase(NS_LossyConvertUTF16toASCII(family), lcFamily);
  1.4868 +
  1.4869 +                nsAutoCString prefName("font.name.");
  1.4870 +                prefName.Append(lcFamily);
  1.4871 +                prefName.AppendLiteral(".");
  1.4872 +                prefName.Append(groupString);
  1.4873 +
  1.4874 +                nsAdoptingString value = Preferences::GetString(prefName.get());
  1.4875 +                if (value) {
  1.4876 +                    CopyASCIItoUTF16(lcFamily, genericFamily);
  1.4877 +                    family = value;
  1.4878 +                }
  1.4879 +            } else {
  1.4880 +                generic = false;
  1.4881 +                genericFamily.SetIsVoid(true);
  1.4882 +            }
  1.4883 +        }
  1.4884 +
  1.4885 +        NS_LossyConvertUTF16toASCII gf(genericFamily);
  1.4886 +        if (generic) {
  1.4887 +            ForEachFontInternal(family, groupAtom, false,
  1.4888 +                                aResolveFontName, false,
  1.4889 +                                fc, closure);
  1.4890 +        } else if (!family.IsEmpty()) {
  1.4891 +            if (aResolveFontName) {
  1.4892 +                ResolveData data(fc, gf, aUseFontSet, closure);
  1.4893 +                bool aborted = false, needsBold;
  1.4894 +                nsresult rv = NS_OK;
  1.4895 +                bool foundFamily = false;
  1.4896 +                bool waitForUserFont = false;
  1.4897 +                gfxFontEntry *fe = nullptr;
  1.4898 +                if (aUseFontSet && mUserFontSet) {
  1.4899 +                    gfxFontFamily *fam = mUserFontSet->GetFamily(family);
  1.4900 +                    if (fam) {
  1.4901 +                        fe = mUserFontSet->FindFontEntry(fam, mStyle,
  1.4902 +                                                         needsBold,
  1.4903 +                                                         waitForUserFont);
  1.4904 +                    }
  1.4905 +                }
  1.4906 +                if (fe) {
  1.4907 +                    gfxFontGroup::FontResolverProc(family, &data);
  1.4908 +                } else {
  1.4909 +                    if (waitForUserFont) {
  1.4910 +                        mSkipDrawing = true;
  1.4911 +                    }
  1.4912 +                    if (!foundFamily) {
  1.4913 +                        gfxPlatform *pf = gfxPlatform::GetPlatform();
  1.4914 +                        // XXX: Fallback is bad
  1.4915 +                        rv = pf->ResolveFontName(family,
  1.4916 +                                                 gfxFontGroup::FontResolverProc,
  1.4917 +                                                 &data, aborted);
  1.4918 +                    }
  1.4919 +                }
  1.4920 +                if (NS_FAILED(rv) || aborted)
  1.4921 +                    return false;
  1.4922 +            }
  1.4923 +            else {
  1.4924 +                if (!fc(family, gf, aUseFontSet, closure))
  1.4925 +                    return false;
  1.4926 +            }
  1.4927 +        }
  1.4928 +
  1.4929 +        if (generic && aResolveGeneric) {
  1.4930 +            nsAutoCString prefName("font.name-list.");
  1.4931 +            prefName.Append(lcFamily);
  1.4932 +            prefName.AppendLiteral(".");
  1.4933 +            prefName.Append(groupString);
  1.4934 +            nsAdoptingString value = Preferences::GetString(prefName.get());
  1.4935 +            if (value) {
  1.4936 +                ForEachFontInternal(value, groupAtom, false,
  1.4937 +                                    aResolveFontName, false,
  1.4938 +                                    fc, closure);
  1.4939 +            }
  1.4940 +        }
  1.4941 +
  1.4942 +        ++p; // may advance past p_end
  1.4943 +    }
  1.4944 +
  1.4945 +    return true;
  1.4946 +}
  1.4947 +
  1.4948 +bool
  1.4949 +gfxFontGroup::FontResolverProc(const nsAString& aName, void *aClosure)
  1.4950 +{
  1.4951 +    ResolveData *data = reinterpret_cast<ResolveData*>(aClosure);
  1.4952 +    return (data->mCallback)(aName, data->mGenericFamily, data->mUseFontSet,
  1.4953 +                             data->mClosure);
  1.4954 +}
  1.4955 +
  1.4956 +gfxTextRun *
  1.4957 +gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
  1.4958 +{
  1.4959 +    aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
  1.4960 +    return gfxTextRun::Create(aParams, 0, this, aFlags);
  1.4961 +}
  1.4962 +
  1.4963 +gfxTextRun *
  1.4964 +gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
  1.4965 +{
  1.4966 +    aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
  1.4967 +
  1.4968 +    gfxTextRun *textRun = gfxTextRun::Create(aParams, 1, this, aFlags);
  1.4969 +    if (!textRun) {
  1.4970 +        return nullptr;
  1.4971 +    }
  1.4972 +
  1.4973 +    gfxFont *font = GetFontAt(0);
  1.4974 +    if (MOZ_UNLIKELY(GetStyle()->size == 0)) {
  1.4975 +        // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
  1.4976 +        // them, and always create at least size 1 fonts, i.e. they still
  1.4977 +        // render something for size 0 fonts.
  1.4978 +        textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false);
  1.4979 +    }
  1.4980 +    else {
  1.4981 +        if (font->GetSpaceGlyph()) {
  1.4982 +            // Normally, the font has a cached space glyph, so we can avoid
  1.4983 +            // the cost of calling FindFontForChar.
  1.4984 +            textRun->SetSpaceGlyph(font, aParams->mContext, 0);
  1.4985 +        } else {
  1.4986 +            // In case the primary font doesn't have <space> (bug 970891),
  1.4987 +            // find one that does.
  1.4988 +            uint8_t matchType;
  1.4989 +            nsRefPtr<gfxFont> spaceFont =
  1.4990 +                FindFontForChar(' ', 0, MOZ_SCRIPT_LATIN, nullptr, &matchType);
  1.4991 +            if (spaceFont) {
  1.4992 +                textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0);
  1.4993 +            }
  1.4994 +        }
  1.4995 +    }
  1.4996 +
  1.4997 +    // Note that the gfxGlyphExtents glyph bounds storage for the font will
  1.4998 +    // always contain an entry for the font's space glyph, so we don't have
  1.4999 +    // to call FetchGlyphExtents here.
  1.5000 +    return textRun;
  1.5001 +}
  1.5002 +
  1.5003 +gfxTextRun *
  1.5004 +gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
  1.5005 +                               const Parameters *aParams, uint32_t aFlags)
  1.5006 +{
  1.5007 +    gfxTextRun *textRun =
  1.5008 +        gfxTextRun::Create(aParams, aLength, this, aFlags);
  1.5009 +    if (!textRun) {
  1.5010 +        return nullptr;
  1.5011 +    }
  1.5012 +
  1.5013 +    textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false);
  1.5014 +    return textRun;
  1.5015 +}
  1.5016 +
  1.5017 +gfxTextRun *
  1.5018 +gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
  1.5019 +{
  1.5020 +    // only use U+2010 if it is supported by the first font in the group;
  1.5021 +    // it's better to use ASCII '-' from the primary font than to fall back to
  1.5022 +    // U+2010 from some other, possibly poorly-matching face
  1.5023 +    static const char16_t hyphen = 0x2010;
  1.5024 +    gfxFont *font = GetFontAt(0);
  1.5025 +    if (font && font->HasCharacter(hyphen)) {
  1.5026 +        return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit,
  1.5027 +                           gfxFontGroup::TEXT_IS_PERSISTENT);
  1.5028 +    }
  1.5029 +
  1.5030 +    static const uint8_t dash = '-';
  1.5031 +    return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit,
  1.5032 +                       gfxFontGroup::TEXT_IS_PERSISTENT);
  1.5033 +}
  1.5034 +
  1.5035 +gfxFloat
  1.5036 +gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
  1.5037 +{
  1.5038 +    if (mHyphenWidth < 0) {
  1.5039 +        nsRefPtr<gfxContext> ctx(aProvider->GetContext());
  1.5040 +        if (ctx) {
  1.5041 +            nsAutoPtr<gfxTextRun>
  1.5042 +                hyphRun(MakeHyphenTextRun(ctx,
  1.5043 +                                          aProvider->GetAppUnitsPerDevUnit()));
  1.5044 +            mHyphenWidth = hyphRun.get() ?
  1.5045 +                hyphRun->GetAdvanceWidth(0, hyphRun->GetLength(), nullptr) : 0;
  1.5046 +        }
  1.5047 +    }
  1.5048 +    return mHyphenWidth;
  1.5049 +}
  1.5050 +
  1.5051 +gfxTextRun *
  1.5052 +gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
  1.5053 +                          const Parameters *aParams, uint32_t aFlags)
  1.5054 +{
  1.5055 +    if (aLength == 0) {
  1.5056 +        return MakeEmptyTextRun(aParams, aFlags);
  1.5057 +    }
  1.5058 +    if (aLength == 1 && aString[0] == ' ') {
  1.5059 +        return MakeSpaceTextRun(aParams, aFlags);
  1.5060 +    }
  1.5061 +
  1.5062 +    aFlags |= TEXT_IS_8BIT;
  1.5063 +
  1.5064 +    if (GetStyle()->size == 0) {
  1.5065 +        // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
  1.5066 +        // them, and always create at least size 1 fonts, i.e. they still
  1.5067 +        // render something for size 0 fonts.
  1.5068 +        return MakeBlankTextRun(aLength, aParams, aFlags);
  1.5069 +    }
  1.5070 +
  1.5071 +    gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
  1.5072 +                                             this, aFlags);
  1.5073 +    if (!textRun) {
  1.5074 +        return nullptr;
  1.5075 +    }
  1.5076 +
  1.5077 +    InitTextRun(aParams->mContext, textRun, aString, aLength);
  1.5078 +
  1.5079 +    textRun->FetchGlyphExtents(aParams->mContext);
  1.5080 +
  1.5081 +    return textRun;
  1.5082 +}
  1.5083 +
  1.5084 +gfxTextRun *
  1.5085 +gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
  1.5086 +                          const Parameters *aParams, uint32_t aFlags)
  1.5087 +{
  1.5088 +    if (aLength == 0) {
  1.5089 +        return MakeEmptyTextRun(aParams, aFlags);
  1.5090 +    }
  1.5091 +    if (aLength == 1 && aString[0] == ' ') {
  1.5092 +        return MakeSpaceTextRun(aParams, aFlags);
  1.5093 +    }
  1.5094 +    if (GetStyle()->size == 0) {
  1.5095 +        return MakeBlankTextRun(aLength, aParams, aFlags);
  1.5096 +    }
  1.5097 +
  1.5098 +    gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
  1.5099 +                                             this, aFlags);
  1.5100 +    if (!textRun) {
  1.5101 +        return nullptr;
  1.5102 +    }
  1.5103 +
  1.5104 +    InitTextRun(aParams->mContext, textRun, aString, aLength);
  1.5105 +
  1.5106 +    textRun->FetchGlyphExtents(aParams->mContext);
  1.5107 +
  1.5108 +    return textRun;
  1.5109 +}
  1.5110 +
  1.5111 +template<typename T>
  1.5112 +void
  1.5113 +gfxFontGroup::InitTextRun(gfxContext *aContext,
  1.5114 +                          gfxTextRun *aTextRun,
  1.5115 +                          const T *aString,
  1.5116 +                          uint32_t aLength)
  1.5117 +{
  1.5118 +    NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
  1.5119 +
  1.5120 +    // we need to do numeral processing even on 8-bit text,
  1.5121 +    // in case we're converting Western to Hindi/Arabic digits
  1.5122 +    int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
  1.5123 +    nsAutoArrayPtr<char16_t> transformedString;
  1.5124 +    if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
  1.5125 +        // scan the string for numerals that may need to be transformed;
  1.5126 +        // if we find any, we'll make a local copy here and use that for
  1.5127 +        // font matching and glyph generation/shaping
  1.5128 +        bool prevIsArabic =
  1.5129 +            (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
  1.5130 +        for (uint32_t i = 0; i < aLength; ++i) {
  1.5131 +            char16_t origCh = aString[i];
  1.5132 +            char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
  1.5133 +            if (newCh != origCh) {
  1.5134 +                if (!transformedString) {
  1.5135 +                    transformedString = new char16_t[aLength];
  1.5136 +                    if (sizeof(T) == sizeof(char16_t)) {
  1.5137 +                        memcpy(transformedString.get(), aString, i * sizeof(char16_t));
  1.5138 +                    } else {
  1.5139 +                        for (uint32_t j = 0; j < i; ++j) {
  1.5140 +                            transformedString[j] = aString[j];
  1.5141 +                        }
  1.5142 +                    }
  1.5143 +                }
  1.5144 +            }
  1.5145 +            if (transformedString) {
  1.5146 +                transformedString[i] = newCh;
  1.5147 +            }
  1.5148 +            prevIsArabic = IS_ARABIC_CHAR(newCh);
  1.5149 +        }
  1.5150 +    }
  1.5151 +
  1.5152 +#ifdef PR_LOGGING
  1.5153 +    PRLogModuleInfo *log = (mStyle.systemFont ?
  1.5154 +                            gfxPlatform::GetLog(eGfxLog_textrunui) :
  1.5155 +                            gfxPlatform::GetLog(eGfxLog_textrun));
  1.5156 +#endif
  1.5157 +
  1.5158 +    if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
  1.5159 +
  1.5160 +#ifdef PR_LOGGING
  1.5161 +        if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
  1.5162 +            nsAutoCString lang;
  1.5163 +            mStyle.language->ToUTF8String(lang);
  1.5164 +            nsAutoCString str((const char*)aString, aLength);
  1.5165 +            PR_LOG(log, PR_LOG_WARNING,\
  1.5166 +                   ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
  1.5167 +                    "weight: %d width: %d style: %s size: %6.2f %d-byte "
  1.5168 +                    "TEXTRUN [%s] ENDTEXTRUN\n",
  1.5169 +                    (mStyle.systemFont ? "textrunui" : "textrun"),
  1.5170 +                    NS_ConvertUTF16toUTF8(mFamilies).get(),
  1.5171 +                    lang.get(), MOZ_SCRIPT_LATIN, aLength,
  1.5172 +                    uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
  1.5173 +                    (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
  1.5174 +                    (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
  1.5175 +                                                            "normal")),
  1.5176 +                    mStyle.size,
  1.5177 +                    sizeof(T),
  1.5178 +                    str.get()));
  1.5179 +        }
  1.5180 +#endif
  1.5181 +
  1.5182 +        // the text is still purely 8-bit; bypass the script-run itemizer
  1.5183 +        // and treat it as a single Latin run
  1.5184 +        InitScriptRun(aContext, aTextRun, aString,
  1.5185 +                      0, aLength, MOZ_SCRIPT_LATIN);
  1.5186 +    } else {
  1.5187 +        const char16_t *textPtr;
  1.5188 +        if (transformedString) {
  1.5189 +            textPtr = transformedString.get();
  1.5190 +        } else {
  1.5191 +            // typecast to avoid compilation error for the 8-bit version,
  1.5192 +            // even though this is dead code in that case
  1.5193 +            textPtr = reinterpret_cast<const char16_t*>(aString);
  1.5194 +        }
  1.5195 +
  1.5196 +        // split into script runs so that script can potentially influence
  1.5197 +        // the font matching process below
  1.5198 +        gfxScriptItemizer scriptRuns(textPtr, aLength);
  1.5199 +
  1.5200 +        uint32_t runStart = 0, runLimit = aLength;
  1.5201 +        int32_t runScript = MOZ_SCRIPT_LATIN;
  1.5202 +        while (scriptRuns.Next(runStart, runLimit, runScript)) {
  1.5203 +
  1.5204 +#ifdef PR_LOGGING
  1.5205 +            if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
  1.5206 +                nsAutoCString lang;
  1.5207 +                mStyle.language->ToUTF8String(lang);
  1.5208 +                uint32_t runLen = runLimit - runStart;
  1.5209 +                PR_LOG(log, PR_LOG_WARNING,\
  1.5210 +                       ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
  1.5211 +                        "weight: %d width: %d style: %s size: %6.2f %d-byte "
  1.5212 +                        "TEXTRUN [%s] ENDTEXTRUN\n",
  1.5213 +                        (mStyle.systemFont ? "textrunui" : "textrun"),
  1.5214 +                        NS_ConvertUTF16toUTF8(mFamilies).get(),
  1.5215 +                        lang.get(), runScript, runLen,
  1.5216 +                        uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
  1.5217 +                        (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
  1.5218 +                        (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
  1.5219 +                                                                "normal")),
  1.5220 +                        mStyle.size,
  1.5221 +                        sizeof(T),
  1.5222 +                        NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
  1.5223 +            }
  1.5224 +#endif
  1.5225 +
  1.5226 +            InitScriptRun(aContext, aTextRun, textPtr,
  1.5227 +                          runStart, runLimit, runScript);
  1.5228 +        }
  1.5229 +    }
  1.5230 +
  1.5231 +    if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
  1.5232 +        gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
  1.5233 +        if (!glyph->IsSimpleGlyph()) {
  1.5234 +            glyph->SetClusterStart(true);
  1.5235 +        }
  1.5236 +    }
  1.5237 +
  1.5238 +    // It's possible for CoreText to omit glyph runs if it decides they contain
  1.5239 +    // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
  1.5240 +    // need to eliminate them from the glyph run array to avoid drawing "partial
  1.5241 +    // ligatures" with the wrong font.
  1.5242 +    // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
  1.5243 +    // it will iterate back over all glyphruns in the textrun, which leads to
  1.5244 +    // pathologically-bad perf in the case where a textrun contains many script
  1.5245 +    // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
  1.5246 +    // every time a new script subrun is processed.
  1.5247 +    aTextRun->SanitizeGlyphRuns();
  1.5248 +
  1.5249 +    aTextRun->SortGlyphRuns();
  1.5250 +}
  1.5251 +
  1.5252 +template<typename T>
  1.5253 +void
  1.5254 +gfxFontGroup::InitScriptRun(gfxContext *aContext,
  1.5255 +                            gfxTextRun *aTextRun,
  1.5256 +                            const T *aString,
  1.5257 +                            uint32_t aScriptRunStart,
  1.5258 +                            uint32_t aScriptRunEnd,
  1.5259 +                            int32_t aRunScript)
  1.5260 +{
  1.5261 +    NS_ASSERTION(aScriptRunEnd > aScriptRunStart,
  1.5262 +                 "don't call InitScriptRun for a zero-length run");
  1.5263 +
  1.5264 +    gfxFont *mainFont = GetFontAt(0);
  1.5265 +
  1.5266 +    uint32_t runStart = aScriptRunStart;
  1.5267 +    nsAutoTArray<gfxTextRange,3> fontRanges;
  1.5268 +    ComputeRanges(fontRanges, aString + aScriptRunStart,
  1.5269 +                  aScriptRunEnd - aScriptRunStart, aRunScript);
  1.5270 +    uint32_t numRanges = fontRanges.Length();
  1.5271 +
  1.5272 +    for (uint32_t r = 0; r < numRanges; r++) {
  1.5273 +        const gfxTextRange& range = fontRanges[r];
  1.5274 +        uint32_t matchedLength = range.Length();
  1.5275 +        gfxFont *matchedFont = range.font;
  1.5276 +
  1.5277 +        // create the glyph run for this range
  1.5278 +        if (matchedFont) {
  1.5279 +            aTextRun->AddGlyphRun(matchedFont, range.matchType,
  1.5280 +                                  runStart, (matchedLength > 0));
  1.5281 +            // do glyph layout and record the resulting positioned glyphs
  1.5282 +            if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, aString,
  1.5283 +                                                  runStart, matchedLength,
  1.5284 +                                                  aRunScript)) {
  1.5285 +                // glyph layout failed! treat as missing glyphs
  1.5286 +                matchedFont = nullptr;
  1.5287 +            }
  1.5288 +        } else {
  1.5289 +            aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
  1.5290 +                                  runStart, (matchedLength > 0));
  1.5291 +        }
  1.5292 +
  1.5293 +        if (!matchedFont) {
  1.5294 +            // We need to set cluster boundaries (and mark spaces) so that
  1.5295 +            // surrogate pairs, combining characters, etc behave properly,
  1.5296 +            // even if we don't have glyphs for them
  1.5297 +            aTextRun->SetupClusterBoundaries(runStart, aString + runStart,
  1.5298 +                                             matchedLength);
  1.5299 +
  1.5300 +            // various "missing" characters may need special handling,
  1.5301 +            // so we check for them here
  1.5302 +            uint32_t runLimit = runStart + matchedLength;
  1.5303 +            for (uint32_t index = runStart; index < runLimit; index++) {
  1.5304 +                T ch = aString[index];
  1.5305 +
  1.5306 +                // tab and newline are not to be displayed as hexboxes,
  1.5307 +                // but do need to be recorded in the textrun
  1.5308 +                if (ch == '\n') {
  1.5309 +                    aTextRun->SetIsNewline(index);
  1.5310 +                    continue;
  1.5311 +                }
  1.5312 +                if (ch == '\t') {
  1.5313 +                    aTextRun->SetIsTab(index);
  1.5314 +                    continue;
  1.5315 +                }
  1.5316 +
  1.5317 +                // for 16-bit textruns only, check for surrogate pairs and
  1.5318 +                // special Unicode spaces; omit these checks in 8-bit runs
  1.5319 +                if (sizeof(T) == sizeof(char16_t)) {
  1.5320 +                    if (NS_IS_HIGH_SURROGATE(ch) &&
  1.5321 +                        index + 1 < aScriptRunEnd &&
  1.5322 +                        NS_IS_LOW_SURROGATE(aString[index + 1]))
  1.5323 +                    {
  1.5324 +                        aTextRun->SetMissingGlyph(index,
  1.5325 +                                                  SURROGATE_TO_UCS4(ch,
  1.5326 +                                                                    aString[index + 1]),
  1.5327 +                                                  mainFont);
  1.5328 +                        index++;
  1.5329 +                        continue;
  1.5330 +                    }
  1.5331 +
  1.5332 +                    // check if this is a known Unicode whitespace character that
  1.5333 +                    // we can render using the space glyph with a custom width
  1.5334 +                    gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
  1.5335 +                    if (wid >= 0.0) {
  1.5336 +                        nscoord advance =
  1.5337 +                            aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
  1.5338 +                        if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
  1.5339 +                            aTextRun->GetCharacterGlyphs()[index].
  1.5340 +                                SetSimpleGlyph(advance,
  1.5341 +                                               mainFont->GetSpaceGlyph());
  1.5342 +                        } else {
  1.5343 +                            gfxTextRun::DetailedGlyph detailedGlyph;
  1.5344 +                            detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
  1.5345 +                            detailedGlyph.mAdvance = advance;
  1.5346 +                            detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
  1.5347 +                            gfxShapedText::CompressedGlyph g;
  1.5348 +                            g.SetComplex(true, true, 1);
  1.5349 +                            aTextRun->SetGlyphs(index,
  1.5350 +                                                g, &detailedGlyph);
  1.5351 +                        }
  1.5352 +                        continue;
  1.5353 +                    }
  1.5354 +                }
  1.5355 +
  1.5356 +                if (IsInvalidChar(ch)) {
  1.5357 +                    // invalid chars are left as zero-width/invisible
  1.5358 +                    continue;
  1.5359 +                }
  1.5360 +
  1.5361 +                // record char code so we can draw a box with the Unicode value
  1.5362 +                aTextRun->SetMissingGlyph(index, ch, mainFont);
  1.5363 +            }
  1.5364 +        }
  1.5365 +
  1.5366 +        runStart += matchedLength;
  1.5367 +    }
  1.5368 +}
  1.5369 +
  1.5370 +gfxTextRun *
  1.5371 +gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
  1.5372 +                                 LazyReferenceContextGetter& aRefContextGetter)
  1.5373 +{
  1.5374 +    if (mCachedEllipsisTextRun &&
  1.5375 +        mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
  1.5376 +        return mCachedEllipsisTextRun;
  1.5377 +    }
  1.5378 +
  1.5379 +    // Use a Unicode ellipsis if the font supports it,
  1.5380 +    // otherwise use three ASCII periods as fallback.
  1.5381 +    gfxFont* firstFont = GetFontAt(0);
  1.5382 +    nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
  1.5383 +        ? nsDependentString(kEllipsisChar,
  1.5384 +                            ArrayLength(kEllipsisChar) - 1)
  1.5385 +        : nsDependentString(kASCIIPeriodsChar,
  1.5386 +                            ArrayLength(kASCIIPeriodsChar) - 1);
  1.5387 +
  1.5388 +    nsRefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext();
  1.5389 +    Parameters params = {
  1.5390 +        refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
  1.5391 +    };
  1.5392 +    gfxTextRun* textRun =
  1.5393 +        MakeTextRun(ellipsis.get(), ellipsis.Length(), &params, TEXT_IS_PERSISTENT);
  1.5394 +    if (!textRun) {
  1.5395 +        return nullptr;
  1.5396 +    }
  1.5397 +    mCachedEllipsisTextRun = textRun;
  1.5398 +    textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis
  1.5399 +                                 // textrun prolong the fontgroup's life
  1.5400 +    return textRun;
  1.5401 +}
  1.5402 +
  1.5403 +already_AddRefed<gfxFont>
  1.5404 +gfxFontGroup::TryAllFamilyMembers(gfxFontFamily* aFamily, uint32_t aCh)
  1.5405 +{
  1.5406 +    if (!aFamily->TestCharacterMap(aCh)) {
  1.5407 +        return nullptr;
  1.5408 +    }
  1.5409 +
  1.5410 +    // Note that we don't need the actual runScript in matchData for
  1.5411 +    // gfxFontFamily::SearchAllFontsForChar, it's only used for the
  1.5412 +    // system-fallback case. So we can just set it to 0 here.
  1.5413 +    GlobalFontMatch matchData(aCh, 0, &mStyle);
  1.5414 +    aFamily->SearchAllFontsForChar(&matchData);
  1.5415 +    gfxFontEntry *fe = matchData.mBestMatch;
  1.5416 +    if (!fe) {
  1.5417 +        return nullptr;
  1.5418 +    }
  1.5419 +
  1.5420 +    bool needsBold = mStyle.weight >= 600 && !fe->IsBold();
  1.5421 +    nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
  1.5422 +    return font.forget();
  1.5423 +}
  1.5424 +
  1.5425 +already_AddRefed<gfxFont>
  1.5426 +gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
  1.5427 +                              int32_t aRunScript, gfxFont *aPrevMatchedFont,
  1.5428 +                              uint8_t *aMatchType)
  1.5429 +{
  1.5430 +    // To optimize common cases, try the first font in the font-group
  1.5431 +    // before going into the more detailed checks below
  1.5432 +    uint32_t nextIndex = 0;
  1.5433 +    bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
  1.5434 +    bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
  1.5435 +    bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
  1.5436 +
  1.5437 +    if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
  1.5438 +        nsRefPtr<gfxFont> firstFont = mFonts[0].Font();
  1.5439 +        if (firstFont->HasCharacter(aCh)) {
  1.5440 +            *aMatchType = gfxTextRange::kFontGroup;
  1.5441 +            return firstFont.forget();
  1.5442 +        }
  1.5443 +        // It's possible that another font in the family (e.g. regular face,
  1.5444 +        // where the requested style was italic) will support the character
  1.5445 +        nsRefPtr<gfxFont> font = TryAllFamilyMembers(mFonts[0].Family(), aCh);
  1.5446 +        if (font) {
  1.5447 +            *aMatchType = gfxTextRange::kFontGroup;
  1.5448 +            return font.forget();
  1.5449 +        }
  1.5450 +        // we don't need to check the first font again below
  1.5451 +        ++nextIndex;
  1.5452 +    }
  1.5453 +
  1.5454 +    if (aPrevMatchedFont) {
  1.5455 +        // Don't switch fonts for control characters, regardless of
  1.5456 +        // whether they are present in the current font, as they won't
  1.5457 +        // actually be rendered (see bug 716229)
  1.5458 +        if (isJoinControl ||
  1.5459 +            GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
  1.5460 +            nsRefPtr<gfxFont> ret = aPrevMatchedFont;
  1.5461 +            return ret.forget();
  1.5462 +        }
  1.5463 +
  1.5464 +        // if previous character was a join-causer (ZWJ),
  1.5465 +        // use the same font as the previous range if we can
  1.5466 +        if (wasJoinCauser) {
  1.5467 +            if (aPrevMatchedFont->HasCharacter(aCh)) {
  1.5468 +                nsRefPtr<gfxFont> ret = aPrevMatchedFont;
  1.5469 +                return ret.forget();
  1.5470 +            }
  1.5471 +        }
  1.5472 +    }
  1.5473 +
  1.5474 +    // if this character is a variation selector,
  1.5475 +    // use the previous font regardless of whether it supports VS or not.
  1.5476 +    // otherwise the text run will be divided.
  1.5477 +    if (isVarSelector) {
  1.5478 +        if (aPrevMatchedFont) {
  1.5479 +            nsRefPtr<gfxFont> ret = aPrevMatchedFont;
  1.5480 +            return ret.forget();
  1.5481 +        }
  1.5482 +        // VS alone. it's meaningless to search different fonts
  1.5483 +        return nullptr;
  1.5484 +    }
  1.5485 +
  1.5486 +    // 1. check remaining fonts in the font group
  1.5487 +    uint32_t fontListLength = FontListLength();
  1.5488 +    for (uint32_t i = nextIndex; i < fontListLength; i++) {
  1.5489 +        nsRefPtr<gfxFont> font = mFonts[i].Font();
  1.5490 +        if (font->HasCharacter(aCh)) {
  1.5491 +            *aMatchType = gfxTextRange::kFontGroup;
  1.5492 +            return font.forget();
  1.5493 +        }
  1.5494 +
  1.5495 +        font = TryAllFamilyMembers(mFonts[i].Family(), aCh);
  1.5496 +        if (font) {
  1.5497 +            *aMatchType = gfxTextRange::kFontGroup;
  1.5498 +            return font.forget();
  1.5499 +        }
  1.5500 +    }
  1.5501 +
  1.5502 +    // if character is in Private Use Area, don't do matching against pref or system fonts
  1.5503 +    if ((aCh >= 0xE000  && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
  1.5504 +        return nullptr;
  1.5505 +
  1.5506 +    // 2. search pref fonts
  1.5507 +    nsRefPtr<gfxFont> font = WhichPrefFontSupportsChar(aCh);
  1.5508 +    if (font) {
  1.5509 +        *aMatchType = gfxTextRange::kPrefsFallback;
  1.5510 +        return font.forget();
  1.5511 +    }
  1.5512 +
  1.5513 +    // 3. use fallback fonts
  1.5514 +    // -- before searching for something else check the font used for the previous character
  1.5515 +    if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
  1.5516 +        *aMatchType = gfxTextRange::kSystemFallback;
  1.5517 +        nsRefPtr<gfxFont> ret = aPrevMatchedFont;
  1.5518 +        return ret.forget();
  1.5519 +    }
  1.5520 +
  1.5521 +    // never fall back for characters from unknown scripts
  1.5522 +    if (aRunScript == HB_SCRIPT_UNKNOWN) {
  1.5523 +        return nullptr;
  1.5524 +    }
  1.5525 +
  1.5526 +    // for known "space" characters, don't do a full system-fallback search;
  1.5527 +    // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
  1.5528 +    if (GetGeneralCategory(aCh) ==
  1.5529 +            HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
  1.5530 +        GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0)
  1.5531 +    {
  1.5532 +        return nullptr;
  1.5533 +    }
  1.5534 +
  1.5535 +    // -- otherwise look for other stuff
  1.5536 +    *aMatchType = gfxTextRange::kSystemFallback;
  1.5537 +    font = WhichSystemFontSupportsChar(aCh, aRunScript);
  1.5538 +    return font.forget();
  1.5539 +}
  1.5540 +
  1.5541 +template<typename T>
  1.5542 +void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
  1.5543 +                                 const T *aString, uint32_t aLength,
  1.5544 +                                 int32_t aRunScript)
  1.5545 +{
  1.5546 +    NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
  1.5547 +    NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
  1.5548 +
  1.5549 +    uint32_t prevCh = 0;
  1.5550 +    int32_t lastRangeIndex = -1;
  1.5551 +
  1.5552 +    // initialize prevFont to the group's primary font, so that this will be
  1.5553 +    // used for string-initial control chars, etc rather than risk hitting font
  1.5554 +    // fallback for these (bug 716229)
  1.5555 +    gfxFont *prevFont = GetFontAt(0);
  1.5556 +
  1.5557 +    // if we use the initial value of prevFont, we treat this as a match from
  1.5558 +    // the font group; fixes bug 978313
  1.5559 +    uint8_t matchType = gfxTextRange::kFontGroup;
  1.5560 +
  1.5561 +    for (uint32_t i = 0; i < aLength; i++) {
  1.5562 +
  1.5563 +        const uint32_t origI = i; // save off in case we increase for surrogate
  1.5564 +
  1.5565 +        // set up current ch
  1.5566 +        uint32_t ch = aString[i];
  1.5567 +
  1.5568 +        // in 16-bit case only, check for surrogate pair
  1.5569 +        if (sizeof(T) == sizeof(char16_t)) {
  1.5570 +            if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
  1.5571 +                                 NS_IS_LOW_SURROGATE(aString[i + 1])) {
  1.5572 +                i++;
  1.5573 +                ch = SURROGATE_TO_UCS4(ch, aString[i]);
  1.5574 +            }
  1.5575 +        }
  1.5576 +
  1.5577 +        if (ch == 0xa0) {
  1.5578 +            ch = ' ';
  1.5579 +        }
  1.5580 +
  1.5581 +        // find the font for this char
  1.5582 +        nsRefPtr<gfxFont> font =
  1.5583 +            FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
  1.5584 +
  1.5585 +#ifndef RELEASE_BUILD
  1.5586 +        if (MOZ_UNLIKELY(mTextPerf)) {
  1.5587 +            if (matchType == gfxTextRange::kPrefsFallback) {
  1.5588 +                mTextPerf->current.fallbackPrefs++;
  1.5589 +            } else if (matchType == gfxTextRange::kSystemFallback) {
  1.5590 +                mTextPerf->current.fallbackSystem++;
  1.5591 +            }
  1.5592 +        }
  1.5593 +#endif
  1.5594 +
  1.5595 +        prevCh = ch;
  1.5596 +
  1.5597 +        if (lastRangeIndex == -1) {
  1.5598 +            // first char ==> make a new range
  1.5599 +            aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
  1.5600 +            lastRangeIndex++;
  1.5601 +            prevFont = font;
  1.5602 +        } else {
  1.5603 +            // if font has changed, make a new range
  1.5604 +            gfxTextRange& prevRange = aRanges[lastRangeIndex];
  1.5605 +            if (prevRange.font != font || prevRange.matchType != matchType) {
  1.5606 +                // close out the previous range
  1.5607 +                prevRange.end = origI;
  1.5608 +                aRanges.AppendElement(gfxTextRange(origI, i + 1,
  1.5609 +                                                   font, matchType));
  1.5610 +                lastRangeIndex++;
  1.5611 +
  1.5612 +                // update prevFont for the next match, *unless* we switched
  1.5613 +                // fonts on a ZWJ, in which case propagating the changed font
  1.5614 +                // is probably not a good idea (see bug 619511)
  1.5615 +                if (sizeof(T) == sizeof(uint8_t) ||
  1.5616 +                    !gfxFontUtils::IsJoinCauser(ch))
  1.5617 +                {
  1.5618 +                    prevFont = font;
  1.5619 +                }
  1.5620 +            }
  1.5621 +        }
  1.5622 +    }
  1.5623 +
  1.5624 +    aRanges[lastRangeIndex].end = aLength;
  1.5625 +}
  1.5626 +
  1.5627 +gfxUserFontSet* 
  1.5628 +gfxFontGroup::GetUserFontSet()
  1.5629 +{
  1.5630 +    return mUserFontSet;
  1.5631 +}
  1.5632 +
  1.5633 +void 
  1.5634 +gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
  1.5635 +{
  1.5636 +    if (aUserFontSet == mUserFontSet) {
  1.5637 +        return;
  1.5638 +    }
  1.5639 +    mUserFontSet = aUserFontSet;
  1.5640 +    mCurrGeneration = GetGeneration() - 1;
  1.5641 +    UpdateFontList();
  1.5642 +}
  1.5643 +
  1.5644 +uint64_t
  1.5645 +gfxFontGroup::GetGeneration()
  1.5646 +{
  1.5647 +    if (!mUserFontSet)
  1.5648 +        return 0;
  1.5649 +    return mUserFontSet->GetGeneration();
  1.5650 +}
  1.5651 +
  1.5652 +void
  1.5653 +gfxFontGroup::UpdateFontList()
  1.5654 +{
  1.5655 +    if (mCurrGeneration != GetGeneration()) {
  1.5656 +        // xxx - can probably improve this to detect when all fonts were found, so no need to update list
  1.5657 +        mFonts.Clear();
  1.5658 +        mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
  1.5659 +        mSkipDrawing = false;
  1.5660 +
  1.5661 +        // bug 548184 - need to clean up FT2, OS/2 platform code to use BuildFontList
  1.5662 +#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
  1.5663 +        BuildFontList();
  1.5664 +#else
  1.5665 +        ForEachFont(FindPlatformFont, this);
  1.5666 +#endif
  1.5667 +        mCurrGeneration = GetGeneration();
  1.5668 +        mCachedEllipsisTextRun = nullptr;
  1.5669 +    }
  1.5670 +}
  1.5671 +
  1.5672 +struct PrefFontCallbackData {
  1.5673 +    PrefFontCallbackData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamiliesArray)
  1.5674 +        : mPrefFamilies(aFamiliesArray)
  1.5675 +    {}
  1.5676 +
  1.5677 +    nsTArray<nsRefPtr<gfxFontFamily> >& mPrefFamilies;
  1.5678 +
  1.5679 +    static bool AddFontFamilyEntry(eFontPrefLang aLang, const nsAString& aName, void *aClosure)
  1.5680 +    {
  1.5681 +        PrefFontCallbackData *prefFontData = static_cast<PrefFontCallbackData*>(aClosure);
  1.5682 +
  1.5683 +        gfxFontFamily *family = gfxPlatformFontList::PlatformFontList()->FindFamily(aName);
  1.5684 +        if (family) {
  1.5685 +            prefFontData->mPrefFamilies.AppendElement(family);
  1.5686 +        }
  1.5687 +        return true;
  1.5688 +    }
  1.5689 +};
  1.5690 +
  1.5691 +already_AddRefed<gfxFont>
  1.5692 +gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh)
  1.5693 +{
  1.5694 +    nsRefPtr<gfxFont> font;
  1.5695 +
  1.5696 +    // get the pref font list if it hasn't been set up already
  1.5697 +    uint32_t unicodeRange = FindCharUnicodeRange(aCh);
  1.5698 +    eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange);
  1.5699 +
  1.5700 +    // if the last pref font was the first family in the pref list, no need to recheck through a list of families
  1.5701 +    if (mLastPrefFont && charLang == mLastPrefLang &&
  1.5702 +        mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
  1.5703 +        font = mLastPrefFont;
  1.5704 +        return font.forget();
  1.5705 +    }
  1.5706 +
  1.5707 +    // based on char lang and page lang, set up list of pref lang fonts to check
  1.5708 +    eFontPrefLang prefLangs[kMaxLenPrefLangList];
  1.5709 +    uint32_t i, numLangs = 0;
  1.5710 +
  1.5711 +    gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
  1.5712 +
  1.5713 +    for (i = 0; i < numLangs; i++) {
  1.5714 +        nsAutoTArray<nsRefPtr<gfxFontFamily>, 5> families;
  1.5715 +        eFontPrefLang currentLang = prefLangs[i];
  1.5716 +
  1.5717 +        gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
  1.5718 +
  1.5719 +        // get the pref families for a single pref lang
  1.5720 +        if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) {
  1.5721 +            eFontPrefLang prefLangsToSearch[1] = { currentLang };
  1.5722 +            PrefFontCallbackData prefFontData(families);
  1.5723 +            gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry,
  1.5724 +                                           &prefFontData);
  1.5725 +            fontList->SetPrefFontFamilyEntries(currentLang, families);
  1.5726 +        }
  1.5727 +
  1.5728 +        // find the first pref font that includes the character
  1.5729 +        uint32_t  j, numPrefs;
  1.5730 +        numPrefs = families.Length();
  1.5731 +        for (j = 0; j < numPrefs; j++) {
  1.5732 +            // look up the appropriate face
  1.5733 +            gfxFontFamily *family = families[j];
  1.5734 +            if (!family) continue;
  1.5735 +
  1.5736 +            // if a pref font is used, it's likely to be used again in the same text run.
  1.5737 +            // the style doesn't change so the face lookup can be cached rather than calling
  1.5738 +            // FindOrMakeFont repeatedly.  speeds up FindFontForChar lookup times for subsequent
  1.5739 +            // pref font lookups
  1.5740 +            if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
  1.5741 +                font = mLastPrefFont;
  1.5742 +                return font.forget();
  1.5743 +            }
  1.5744 +
  1.5745 +            bool needsBold;
  1.5746 +            gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold);
  1.5747 +            // if ch in cmap, create and return a gfxFont
  1.5748 +            if (fe && fe->TestCharacterMap(aCh)) {
  1.5749 +                nsRefPtr<gfxFont> prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
  1.5750 +                if (!prefFont) continue;
  1.5751 +                mLastPrefFamily = family;
  1.5752 +                mLastPrefFont = prefFont;
  1.5753 +                mLastPrefLang = charLang;
  1.5754 +                mLastPrefFirstFont = (i == 0 && j == 0);
  1.5755 +                return prefFont.forget();
  1.5756 +            }
  1.5757 +
  1.5758 +        }
  1.5759 +    }
  1.5760 +
  1.5761 +    return nullptr;
  1.5762 +}
  1.5763 +
  1.5764 +already_AddRefed<gfxFont>
  1.5765 +gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript)
  1.5766 +{
  1.5767 +    gfxFontEntry *fe = 
  1.5768 +        gfxPlatformFontList::PlatformFontList()->
  1.5769 +            SystemFindFontForChar(aCh, aRunScript, &mStyle);
  1.5770 +    if (fe) {
  1.5771 +        bool wantBold = mStyle.ComputeWeight() >= 6;
  1.5772 +        nsRefPtr<gfxFont> font =
  1.5773 +            fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
  1.5774 +        return font.forget();
  1.5775 +    }
  1.5776 +
  1.5777 +    return nullptr;
  1.5778 +}
  1.5779 +
  1.5780 +/*static*/ void
  1.5781 +gfxFontGroup::Shutdown()
  1.5782 +{
  1.5783 +    NS_IF_RELEASE(gLangService);
  1.5784 +}
  1.5785 +
  1.5786 +nsILanguageAtomService* gfxFontGroup::gLangService = nullptr;
  1.5787 +
  1.5788 +
  1.5789 +#define DEFAULT_PIXEL_FONT_SIZE 16.0f
  1.5790 +
  1.5791 +/*static*/ uint32_t
  1.5792 +gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag)
  1.5793 +{
  1.5794 +  if (!aLangTag.Length() || aLangTag.Length() > 4) {
  1.5795 +    return NO_FONT_LANGUAGE_OVERRIDE;
  1.5796 +  }
  1.5797 +  uint32_t index, result = 0;
  1.5798 +  for (index = 0; index < aLangTag.Length(); ++index) {
  1.5799 +    char16_t ch = aLangTag[index];
  1.5800 +    if (!nsCRT::IsAscii(ch)) { // valid tags are pure ASCII
  1.5801 +      return NO_FONT_LANGUAGE_OVERRIDE;
  1.5802 +    }
  1.5803 +    result = (result << 8) + ch;
  1.5804 +  }
  1.5805 +  while (index++ < 4) {
  1.5806 +    result = (result << 8) + 0x20;
  1.5807 +  }
  1.5808 +  return result;
  1.5809 +}
  1.5810 +
  1.5811 +gfxFontStyle::gfxFontStyle() :
  1.5812 +    language(nsGkAtoms::x_western),
  1.5813 +    size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(0.0f),
  1.5814 +    languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
  1.5815 +    weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL),
  1.5816 +    systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
  1.5817 +    style(NS_FONT_STYLE_NORMAL)
  1.5818 +{
  1.5819 +}
  1.5820 +
  1.5821 +gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
  1.5822 +                           gfxFloat aSize, nsIAtom *aLanguage,
  1.5823 +                           float aSizeAdjust, bool aSystemFont,
  1.5824 +                           bool aPrinterFont,
  1.5825 +                           const nsString& aLanguageOverride):
  1.5826 +    language(aLanguage),
  1.5827 +    size(aSize), sizeAdjust(aSizeAdjust),
  1.5828 +    languageOverride(ParseFontLanguageOverride(aLanguageOverride)),
  1.5829 +    weight(aWeight), stretch(aStretch),
  1.5830 +    systemFont(aSystemFont), printerFont(aPrinterFont),
  1.5831 +    useGrayscaleAntialiasing(false), style(aStyle)
  1.5832 +{
  1.5833 +    MOZ_ASSERT(!mozilla::IsNaN(size));
  1.5834 +    MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
  1.5835 +
  1.5836 +    if (weight > 900)
  1.5837 +        weight = 900;
  1.5838 +    if (weight < 100)
  1.5839 +        weight = 100;
  1.5840 +
  1.5841 +    if (size >= FONT_MAX_SIZE) {
  1.5842 +        size = FONT_MAX_SIZE;
  1.5843 +        sizeAdjust = 0.0;
  1.5844 +    } else if (size < 0.0) {
  1.5845 +        NS_WARNING("negative font size");
  1.5846 +        size = 0.0;
  1.5847 +    }
  1.5848 +
  1.5849 +    if (!language) {
  1.5850 +        NS_WARNING("null language");
  1.5851 +        language = nsGkAtoms::x_western;
  1.5852 +    }
  1.5853 +}
  1.5854 +
  1.5855 +gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
  1.5856 +    language(aStyle.language),
  1.5857 +    featureValueLookup(aStyle.featureValueLookup),
  1.5858 +    size(aStyle.size), sizeAdjust(aStyle.sizeAdjust),
  1.5859 +    languageOverride(aStyle.languageOverride),
  1.5860 +    weight(aStyle.weight), stretch(aStyle.stretch),
  1.5861 +    systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
  1.5862 +    useGrayscaleAntialiasing(aStyle.useGrayscaleAntialiasing),
  1.5863 +    style(aStyle.style)
  1.5864 +{
  1.5865 +    featureSettings.AppendElements(aStyle.featureSettings);
  1.5866 +    alternateValues.AppendElements(aStyle.alternateValues);
  1.5867 +}
  1.5868 +
  1.5869 +int8_t
  1.5870 +gfxFontStyle::ComputeWeight() const
  1.5871 +{
  1.5872 +    int8_t baseWeight = (weight + 50) / 100;
  1.5873 +
  1.5874 +    if (baseWeight < 0)
  1.5875 +        baseWeight = 0;
  1.5876 +    if (baseWeight > 9)
  1.5877 +        baseWeight = 9;
  1.5878 +
  1.5879 +    return baseWeight;
  1.5880 +}
  1.5881 +
  1.5882 +void
  1.5883 +gfxShapedText::SetupClusterBoundaries(uint32_t         aOffset,
  1.5884 +                                      const char16_t *aString,
  1.5885 +                                      uint32_t         aLength)
  1.5886 +{
  1.5887 +    CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
  1.5888 +
  1.5889 +    gfxTextRun::CompressedGlyph extendCluster;
  1.5890 +    extendCluster.SetComplex(false, true, 0);
  1.5891 +
  1.5892 +    ClusterIterator iter(aString, aLength);
  1.5893 +
  1.5894 +    // the ClusterIterator won't be able to tell us if the string
  1.5895 +    // _begins_ with a cluster-extender, so we handle that here
  1.5896 +    if (aLength && IsClusterExtender(*aString)) {
  1.5897 +        *glyphs = extendCluster;
  1.5898 +    }
  1.5899 +
  1.5900 +    while (!iter.AtEnd()) {
  1.5901 +        if (*iter == char16_t(' ')) {
  1.5902 +            glyphs->SetIsSpace();
  1.5903 +        }
  1.5904 +        // advance iter to the next cluster-start (or end of text)
  1.5905 +        iter.Next();
  1.5906 +        // step past the first char of the cluster
  1.5907 +        aString++;
  1.5908 +        glyphs++;
  1.5909 +        // mark all the rest as cluster-continuations
  1.5910 +        while (aString < iter) {
  1.5911 +            *glyphs = extendCluster;
  1.5912 +            if (NS_IS_LOW_SURROGATE(*aString)) {
  1.5913 +                glyphs->SetIsLowSurrogate();
  1.5914 +            }
  1.5915 +            glyphs++;
  1.5916 +            aString++;
  1.5917 +        }
  1.5918 +    }
  1.5919 +}
  1.5920 +
  1.5921 +void
  1.5922 +gfxShapedText::SetupClusterBoundaries(uint32_t       aOffset,
  1.5923 +                                      const uint8_t *aString,
  1.5924 +                                      uint32_t       aLength)
  1.5925 +{
  1.5926 +    CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
  1.5927 +    const uint8_t *limit = aString + aLength;
  1.5928 +
  1.5929 +    while (aString < limit) {
  1.5930 +        if (*aString == uint8_t(' ')) {
  1.5931 +            glyphs->SetIsSpace();
  1.5932 +        }
  1.5933 +        aString++;
  1.5934 +        glyphs++;
  1.5935 +    }
  1.5936 +}
  1.5937 +
  1.5938 +gfxShapedText::DetailedGlyph *
  1.5939 +gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
  1.5940 +{
  1.5941 +    NS_ASSERTION(aIndex < GetLength(), "Index out of range");
  1.5942 +
  1.5943 +    if (!mDetailedGlyphs) {
  1.5944 +        mDetailedGlyphs = new DetailedGlyphStore();
  1.5945 +    }
  1.5946 +
  1.5947 +    DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
  1.5948 +    if (!details) {
  1.5949 +        GetCharacterGlyphs()[aIndex].SetMissing(0);
  1.5950 +        return nullptr;
  1.5951 +    }
  1.5952 +
  1.5953 +    return details;
  1.5954 +}
  1.5955 +
  1.5956 +void
  1.5957 +gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
  1.5958 +                         const DetailedGlyph *aGlyphs)
  1.5959 +{
  1.5960 +    NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
  1.5961 +    NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
  1.5962 +                 "First character can't be a ligature continuation!");
  1.5963 +
  1.5964 +    uint32_t glyphCount = aGlyph.GetGlyphCount();
  1.5965 +    if (glyphCount > 0) {
  1.5966 +        DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
  1.5967 +        if (!details) {
  1.5968 +            return;
  1.5969 +        }
  1.5970 +        memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
  1.5971 +    }
  1.5972 +    GetCharacterGlyphs()[aIndex] = aGlyph;
  1.5973 +}
  1.5974 +
  1.5975 +#define ZWNJ 0x200C
  1.5976 +#define ZWJ  0x200D
  1.5977 +// U+061C ARABIC LETTER MARK is expected to be added to XIDMOD_DEFAULT_IGNORABLE
  1.5978 +// in a future Unicode update. Add it manually for now
  1.5979 +#define ALM  0x061C
  1.5980 +static inline bool
  1.5981 +IsDefaultIgnorable(uint32_t aChar)
  1.5982 +{
  1.5983 +    return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE ||
  1.5984 +           aChar == ZWNJ || aChar == ZWJ || aChar == ALM;
  1.5985 +}
  1.5986 +
  1.5987 +void
  1.5988 +gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
  1.5989 +{
  1.5990 +    uint8_t category = GetGeneralCategory(aChar);
  1.5991 +    if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
  1.5992 +        category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
  1.5993 +    {
  1.5994 +        GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
  1.5995 +    }
  1.5996 +
  1.5997 +    DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
  1.5998 +    if (!details) {
  1.5999 +        return;
  1.6000 +    }
  1.6001 +
  1.6002 +    details->mGlyphID = aChar;
  1.6003 +    if (IsDefaultIgnorable(aChar)) {
  1.6004 +        // Setting advance width to zero will prevent drawing the hexbox
  1.6005 +        details->mAdvance = 0;
  1.6006 +    } else {
  1.6007 +        gfxFloat width =
  1.6008 +            std::max(aFont->GetMetrics().aveCharWidth,
  1.6009 +                     gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
  1.6010 +                         mAppUnitsPerDevUnit));
  1.6011 +        details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
  1.6012 +    }
  1.6013 +    details->mXOffset = 0;
  1.6014 +    details->mYOffset = 0;
  1.6015 +    GetCharacterGlyphs()[aIndex].SetMissing(1);
  1.6016 +}
  1.6017 +
  1.6018 +bool
  1.6019 +gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
  1.6020 +{
  1.6021 +    if (IsDefaultIgnorable(aCh)) {
  1.6022 +        DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
  1.6023 +        if (details) {
  1.6024 +            details->mGlyphID = aCh;
  1.6025 +            details->mAdvance = 0;
  1.6026 +            details->mXOffset = 0;
  1.6027 +            details->mYOffset = 0;
  1.6028 +            GetCharacterGlyphs()[aIndex].SetMissing(1);
  1.6029 +            return true;
  1.6030 +        }
  1.6031 +    }
  1.6032 +    return false;
  1.6033 +}
  1.6034 +
  1.6035 +void
  1.6036 +gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
  1.6037 +                                              uint32_t aOffset,
  1.6038 +                                              uint32_t aLength)
  1.6039 +{
  1.6040 +    uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
  1.6041 +    CompressedGlyph *charGlyphs = GetCharacterGlyphs();
  1.6042 +    for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
  1.6043 +         CompressedGlyph *glyphData = charGlyphs + i;
  1.6044 +         if (glyphData->IsSimpleGlyph()) {
  1.6045 +             // simple glyphs ==> just add the advance
  1.6046 +             int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
  1.6047 +             if (CompressedGlyph::IsSimpleAdvance(advance)) {
  1.6048 +                 glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
  1.6049 +             } else {
  1.6050 +                 // rare case, tested by making this the default
  1.6051 +                 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
  1.6052 +                 glyphData->SetComplex(true, true, 1);
  1.6053 +                 DetailedGlyph detail = {glyphIndex, advance, 0, 0};
  1.6054 +                 SetGlyphs(i, *glyphData, &detail);
  1.6055 +             }
  1.6056 +         } else {
  1.6057 +             // complex glyphs ==> add offset at cluster/ligature boundaries
  1.6058 +             uint32_t detailedLength = glyphData->GetGlyphCount();
  1.6059 +             if (detailedLength) {
  1.6060 +                 DetailedGlyph *details = GetDetailedGlyphs(i);
  1.6061 +                 if (!details) {
  1.6062 +                     continue;
  1.6063 +                 }
  1.6064 +                 if (IsRightToLeft()) {
  1.6065 +                     details[0].mAdvance += synAppUnitOffset;
  1.6066 +                 } else {
  1.6067 +                     details[detailedLength - 1].mAdvance += synAppUnitOffset;
  1.6068 +                 }
  1.6069 +             }
  1.6070 +         }
  1.6071 +    }
  1.6072 +}
  1.6073 +
  1.6074 +bool
  1.6075 +gfxTextRun::GlyphRunIterator::NextRun()  {
  1.6076 +    if (mNextIndex >= mTextRun->mGlyphRuns.Length())
  1.6077 +        return false;
  1.6078 +    mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
  1.6079 +    if (mGlyphRun->mCharacterOffset >= mEndOffset)
  1.6080 +        return false;
  1.6081 +
  1.6082 +    mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
  1.6083 +    uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
  1.6084 +        ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
  1.6085 +    mStringEnd = std::min(mEndOffset, last);
  1.6086 +
  1.6087 +    ++mNextIndex;
  1.6088 +    return true;
  1.6089 +}
  1.6090 +
  1.6091 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.6092 +static void
  1.6093 +AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign)
  1.6094 +{
  1.6095 +    // Ignores detailed glyphs... we don't know when those have been constructed
  1.6096 +    // Also ignores gfxSkipChars dynamic storage (which won't be anything
  1.6097 +    // for preformatted text)
  1.6098 +    // Also ignores GlyphRun array, again because it hasn't been constructed
  1.6099 +    // by the time this gets called. If there's only one glyphrun that's stored
  1.6100 +    // directly in the textrun anyway so no additional overhead.
  1.6101 +    uint32_t length = aTextRun->GetLength();
  1.6102 +    int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
  1.6103 +    bytes += sizeof(gfxTextRun);
  1.6104 +    gTextRunStorage += bytes*aSign;
  1.6105 +    gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
  1.6106 +}
  1.6107 +#endif
  1.6108 +
  1.6109 +// Helper for textRun creation to preallocate storage for glyph records;
  1.6110 +// this function returns a pointer to the newly-allocated glyph storage.
  1.6111 +// Returns nullptr if allocation fails.
  1.6112 +void *
  1.6113 +gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
  1.6114 +{
  1.6115 +    // Allocate the storage we need, returning nullptr on failure rather than
  1.6116 +    // throwing an exception (because web content can create huge runs).
  1.6117 +    void *storage = moz_malloc(aSize + aLength * sizeof(CompressedGlyph));
  1.6118 +    if (!storage) {
  1.6119 +        NS_WARNING("failed to allocate storage for text run!");
  1.6120 +        return nullptr;
  1.6121 +    }
  1.6122 +
  1.6123 +    // Initialize the glyph storage (beyond aSize) to zero
  1.6124 +    memset(reinterpret_cast<char*>(storage) + aSize, 0,
  1.6125 +           aLength * sizeof(CompressedGlyph));
  1.6126 +
  1.6127 +    return storage;
  1.6128 +}
  1.6129 +
  1.6130 +gfxTextRun *
  1.6131 +gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
  1.6132 +                   uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
  1.6133 +{
  1.6134 +    void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
  1.6135 +    if (!storage) {
  1.6136 +        return nullptr;
  1.6137 +    }
  1.6138 +
  1.6139 +    return new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags);
  1.6140 +}
  1.6141 +
  1.6142 +gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
  1.6143 +                       uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
  1.6144 +    : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
  1.6145 +    , mUserData(aParams->mUserData)
  1.6146 +    , mFontGroup(aFontGroup)
  1.6147 +    , mReleasedFontGroup(false)
  1.6148 +{
  1.6149 +    NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
  1.6150 +    MOZ_COUNT_CTOR(gfxTextRun);
  1.6151 +    NS_ADDREF(mFontGroup);
  1.6152 +
  1.6153 +#ifndef RELEASE_BUILD
  1.6154 +    gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
  1.6155 +    if (tp) {
  1.6156 +        tp->current.textrunConst++;
  1.6157 +    }
  1.6158 +#endif
  1.6159 +
  1.6160 +    mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
  1.6161 +
  1.6162 +    if (aParams->mSkipChars) {
  1.6163 +        mSkipChars.TakeFrom(aParams->mSkipChars);
  1.6164 +    }
  1.6165 +
  1.6166 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.6167 +    AccountStorageForTextRun(this, 1);
  1.6168 +#endif
  1.6169 +
  1.6170 +    mSkipDrawing = mFontGroup->ShouldSkipDrawing();
  1.6171 +}
  1.6172 +
  1.6173 +gfxTextRun::~gfxTextRun()
  1.6174 +{
  1.6175 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.6176 +    AccountStorageForTextRun(this, -1);
  1.6177 +#endif
  1.6178 +#ifdef DEBUG
  1.6179 +    // Make it easy to detect a dead text run
  1.6180 +    mFlags = 0xFFFFFFFF;
  1.6181 +#endif
  1.6182 +
  1.6183 +    // The cached ellipsis textrun (if any) in a fontgroup will have already
  1.6184 +    // been told to release its reference to the group, so we mustn't do that
  1.6185 +    // again here.
  1.6186 +    if (!mReleasedFontGroup) {
  1.6187 +#ifndef RELEASE_BUILD
  1.6188 +        gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
  1.6189 +        if (tp) {
  1.6190 +            tp->current.textrunDestr++;
  1.6191 +        }
  1.6192 +#endif
  1.6193 +        NS_RELEASE(mFontGroup);
  1.6194 +    }
  1.6195 +
  1.6196 +    MOZ_COUNT_DTOR(gfxTextRun);
  1.6197 +}
  1.6198 +
  1.6199 +void
  1.6200 +gfxTextRun::ReleaseFontGroup()
  1.6201 +{
  1.6202 +    NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
  1.6203 +    NS_RELEASE(mFontGroup);
  1.6204 +    mReleasedFontGroup = true;
  1.6205 +}
  1.6206 +
  1.6207 +bool
  1.6208 +gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
  1.6209 +                                   uint8_t *aBreakBefore,
  1.6210 +                                   gfxContext *aRefContext)
  1.6211 +{
  1.6212 +    NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow");
  1.6213 +
  1.6214 +    uint32_t changed = 0;
  1.6215 +    uint32_t i;
  1.6216 +    CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart;
  1.6217 +    for (i = 0; i < aLength; ++i) {
  1.6218 +        uint8_t canBreak = aBreakBefore[i];
  1.6219 +        if (canBreak && !charGlyphs[i].IsClusterStart()) {
  1.6220 +            // This can happen ... there is no guarantee that our linebreaking rules
  1.6221 +            // align with the platform's idea of what constitutes a cluster.
  1.6222 +            NS_WARNING("Break suggested inside cluster!");
  1.6223 +            canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
  1.6224 +        }
  1.6225 +        changed |= charGlyphs[i].SetCanBreakBefore(canBreak);
  1.6226 +    }
  1.6227 +    return changed != 0;
  1.6228 +}
  1.6229 +
  1.6230 +gfxTextRun::LigatureData
  1.6231 +gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
  1.6232 +                                PropertyProvider *aProvider)
  1.6233 +{
  1.6234 +    NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
  1.6235 +    NS_ASSERTION(aPartEnd <= GetLength(), "Character length overflow");
  1.6236 +  
  1.6237 +    LigatureData result;
  1.6238 +    CompressedGlyph *charGlyphs = mCharacterGlyphs;
  1.6239 +
  1.6240 +    uint32_t i;
  1.6241 +    for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
  1.6242 +        NS_ASSERTION(i > 0, "Ligature at the start of the run??");
  1.6243 +    }
  1.6244 +    result.mLigatureStart = i;
  1.6245 +    for (i = aPartStart + 1; i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
  1.6246 +    }
  1.6247 +    result.mLigatureEnd = i;
  1.6248 +
  1.6249 +    int32_t ligatureWidth =
  1.6250 +        GetAdvanceForGlyphs(result.mLigatureStart, result.mLigatureEnd);
  1.6251 +    // Count the number of started clusters we have seen
  1.6252 +    uint32_t totalClusterCount = 0;
  1.6253 +    uint32_t partClusterIndex = 0;
  1.6254 +    uint32_t partClusterCount = 0;
  1.6255 +    for (i = result.mLigatureStart; i < result.mLigatureEnd; ++i) {
  1.6256 +        // Treat the first character of the ligature as the start of a
  1.6257 +        // cluster for our purposes of allocating ligature width to its
  1.6258 +        // characters.
  1.6259 +        if (i == result.mLigatureStart || charGlyphs[i].IsClusterStart()) {
  1.6260 +            ++totalClusterCount;
  1.6261 +            if (i < aPartStart) {
  1.6262 +                ++partClusterIndex;
  1.6263 +            } else if (i < aPartEnd) {
  1.6264 +                ++partClusterCount;
  1.6265 +            }
  1.6266 +        }
  1.6267 +    }
  1.6268 +    NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
  1.6269 +    result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
  1.6270 +    result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
  1.6271 +
  1.6272 +    // Any rounding errors are apportioned to the final part of the ligature,
  1.6273 +    // so that measuring all parts of a ligature and summing them is equal to
  1.6274 +    // the ligature width.
  1.6275 +    if (aPartEnd == result.mLigatureEnd) {
  1.6276 +        gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
  1.6277 +        result.mPartWidth += ligatureWidth - allParts;
  1.6278 +    }
  1.6279 +
  1.6280 +    if (partClusterCount == 0) {
  1.6281 +        // nothing to draw
  1.6282 +        result.mClipBeforePart = result.mClipAfterPart = true;
  1.6283 +    } else {
  1.6284 +        // Determine whether we should clip before or after this part when
  1.6285 +        // drawing its slice of the ligature.
  1.6286 +        // We need to clip before the part if any cluster is drawn before
  1.6287 +        // this part.
  1.6288 +        result.mClipBeforePart = partClusterIndex > 0;
  1.6289 +        // We need to clip after the part if any cluster is drawn after
  1.6290 +        // this part.
  1.6291 +        result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
  1.6292 +    }
  1.6293 +
  1.6294 +    if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
  1.6295 +        gfxFont::Spacing spacing;
  1.6296 +        if (aPartStart == result.mLigatureStart) {
  1.6297 +            aProvider->GetSpacing(aPartStart, 1, &spacing);
  1.6298 +            result.mPartWidth += spacing.mBefore;
  1.6299 +        }
  1.6300 +        if (aPartEnd == result.mLigatureEnd) {
  1.6301 +            aProvider->GetSpacing(aPartEnd - 1, 1, &spacing);
  1.6302 +            result.mPartWidth += spacing.mAfter;
  1.6303 +        }
  1.6304 +    }
  1.6305 +
  1.6306 +    return result;
  1.6307 +}
  1.6308 +
  1.6309 +gfxFloat
  1.6310 +gfxTextRun::ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
  1.6311 +                                        PropertyProvider *aProvider)
  1.6312 +{
  1.6313 +    if (aPartStart >= aPartEnd)
  1.6314 +        return 0;
  1.6315 +    LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider);
  1.6316 +    return data.mPartWidth;
  1.6317 +}
  1.6318 +
  1.6319 +int32_t
  1.6320 +gfxTextRun::GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd)
  1.6321 +{
  1.6322 +    const CompressedGlyph *glyphData = mCharacterGlyphs + aStart;
  1.6323 +    int32_t advance = 0;
  1.6324 +    uint32_t i;
  1.6325 +    for (i = aStart; i < aEnd; ++i, ++glyphData) {
  1.6326 +        if (glyphData->IsSimpleGlyph()) {
  1.6327 +            advance += glyphData->GetSimpleAdvance();   
  1.6328 +        } else {
  1.6329 +            uint32_t glyphCount = glyphData->GetGlyphCount();
  1.6330 +            if (glyphCount == 0) {
  1.6331 +                continue;
  1.6332 +            }
  1.6333 +            const DetailedGlyph *details = GetDetailedGlyphs(i);
  1.6334 +            if (details) {
  1.6335 +                uint32_t j;
  1.6336 +                for (j = 0; j < glyphCount; ++j, ++details) {
  1.6337 +                    advance += details->mAdvance;
  1.6338 +                }
  1.6339 +            }
  1.6340 +        }
  1.6341 +    }
  1.6342 +    return advance;
  1.6343 +}
  1.6344 +
  1.6345 +static void
  1.6346 +GetAdjustedSpacing(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
  1.6347 +                   gfxTextRun::PropertyProvider *aProvider,
  1.6348 +                   gfxTextRun::PropertyProvider::Spacing *aSpacing)
  1.6349 +{
  1.6350 +    if (aStart >= aEnd)
  1.6351 +        return;
  1.6352 +
  1.6353 +    aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
  1.6354 +
  1.6355 +#ifdef DEBUG
  1.6356 +    // Check to see if we have spacing inside ligatures
  1.6357 +
  1.6358 +    const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
  1.6359 +    uint32_t i;
  1.6360 +
  1.6361 +    for (i = aStart; i < aEnd; ++i) {
  1.6362 +        if (!charGlyphs[i].IsLigatureGroupStart()) {
  1.6363 +            NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0,
  1.6364 +                         "Before-spacing inside a ligature!");
  1.6365 +            NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0,
  1.6366 +                         "After-spacing inside a ligature!");
  1.6367 +        }
  1.6368 +    }
  1.6369 +#endif
  1.6370 +}
  1.6371 +
  1.6372 +bool
  1.6373 +gfxTextRun::GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd,
  1.6374 +                                    PropertyProvider *aProvider,
  1.6375 +                                    uint32_t aSpacingStart, uint32_t aSpacingEnd,
  1.6376 +                                    nsTArray<PropertyProvider::Spacing> *aSpacing)
  1.6377 +{
  1.6378 +    if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
  1.6379 +        return false;
  1.6380 +    if (!aSpacing->AppendElements(aEnd - aStart))
  1.6381 +        return false;
  1.6382 +    memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart));
  1.6383 +    GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
  1.6384 +                       aSpacing->Elements() + aSpacingStart - aStart);
  1.6385 +    memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
  1.6386 +    return true;
  1.6387 +}
  1.6388 +
  1.6389 +void
  1.6390 +gfxTextRun::ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd)
  1.6391 +{
  1.6392 +    if (*aStart >= *aEnd)
  1.6393 +        return;
  1.6394 +  
  1.6395 +    CompressedGlyph *charGlyphs = mCharacterGlyphs;
  1.6396 +
  1.6397 +    while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
  1.6398 +        ++(*aStart);
  1.6399 +    }
  1.6400 +    if (*aEnd < GetLength()) {
  1.6401 +        while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
  1.6402 +            --(*aEnd);
  1.6403 +        }
  1.6404 +    }
  1.6405 +}
  1.6406 +
  1.6407 +void
  1.6408 +gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
  1.6409 +                       DrawMode aDrawMode, gfxPoint *aPt,
  1.6410 +                       gfxTextContextPaint *aContextPaint,
  1.6411 +                       uint32_t aStart, uint32_t aEnd,
  1.6412 +                       PropertyProvider *aProvider,
  1.6413 +                       uint32_t aSpacingStart, uint32_t aSpacingEnd,
  1.6414 +                       gfxTextRunDrawCallbacks *aCallbacks)
  1.6415 +{
  1.6416 +    nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
  1.6417 +    bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
  1.6418 +        aSpacingStart, aSpacingEnd, &spacingBuffer);
  1.6419 +    aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt,
  1.6420 +                haveSpacing ? spacingBuffer.Elements() : nullptr, aContextPaint,
  1.6421 +                aCallbacks);
  1.6422 +}
  1.6423 +
  1.6424 +static void
  1.6425 +ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
  1.6426 +                    gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
  1.6427 +{
  1.6428 +    if (aLigature->mClipBeforePart) {
  1.6429 +        if (aTextRun->IsRightToLeft()) {
  1.6430 +            *aRight = std::min(*aRight, aXOrigin);
  1.6431 +        } else {
  1.6432 +            *aLeft = std::max(*aLeft, aXOrigin);
  1.6433 +        }
  1.6434 +    }
  1.6435 +    if (aLigature->mClipAfterPart) {
  1.6436 +        gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth;
  1.6437 +        if (aTextRun->IsRightToLeft()) {
  1.6438 +            *aLeft = std::max(*aLeft, endEdge);
  1.6439 +        } else {
  1.6440 +            *aRight = std::min(*aRight, endEdge);
  1.6441 +        }
  1.6442 +    }    
  1.6443 +}
  1.6444 +
  1.6445 +void
  1.6446 +gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx,
  1.6447 +                                uint32_t aStart, uint32_t aEnd,
  1.6448 +                                gfxPoint *aPt,
  1.6449 +                                PropertyProvider *aProvider,
  1.6450 +                                gfxTextRunDrawCallbacks *aCallbacks)
  1.6451 +{
  1.6452 +    if (aStart >= aEnd)
  1.6453 +        return;
  1.6454 +
  1.6455 +    // Draw partial ligature. We hack this by clipping the ligature.
  1.6456 +    LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
  1.6457 +    gfxRect clipExtents = aCtx->GetClipExtents();
  1.6458 +    gfxFloat left = clipExtents.X()*mAppUnitsPerDevUnit;
  1.6459 +    gfxFloat right = clipExtents.XMost()*mAppUnitsPerDevUnit;
  1.6460 +    ClipPartialLigature(this, &left, &right, aPt->x, &data);
  1.6461 +
  1.6462 +    {
  1.6463 +      // Need to preserve the path, otherwise this can break canvas text-on-path;
  1.6464 +      // in general it seems like a good thing, as naive callers probably won't
  1.6465 +      // expect gfxTextRun::Draw to implicitly destroy the current path.
  1.6466 +      gfxContextPathAutoSaveRestore savePath(aCtx);
  1.6467 +
  1.6468 +      // use division here to ensure that when the rect is aligned on multiples
  1.6469 +      // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
  1.6470 +      // Also, make sure we snap the rectangle to device pixels.
  1.6471 +      aCtx->Save();
  1.6472 +      aCtx->NewPath();
  1.6473 +      aCtx->Rectangle(gfxRect(left / mAppUnitsPerDevUnit,
  1.6474 +                              clipExtents.Y(),
  1.6475 +                              (right - left) / mAppUnitsPerDevUnit,
  1.6476 +                              clipExtents.Height()), true);
  1.6477 +      aCtx->Clip();
  1.6478 +    }
  1.6479 +
  1.6480 +    gfxFloat direction = GetDirection();
  1.6481 +    gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y);
  1.6482 +    DrawGlyphs(aFont, aCtx,
  1.6483 +               aCallbacks ? DrawMode::GLYPH_PATH : DrawMode::GLYPH_FILL, &pt,
  1.6484 +               nullptr, data.mLigatureStart, data.mLigatureEnd, aProvider,
  1.6485 +               aStart, aEnd, aCallbacks);
  1.6486 +    aCtx->Restore();
  1.6487 +
  1.6488 +    aPt->x += direction*data.mPartWidth;
  1.6489 +}
  1.6490 +
  1.6491 +// returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
  1.6492 +static bool
  1.6493 +HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength)
  1.6494 +{
  1.6495 +    gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
  1.6496 +    while (iter.NextRun()) {
  1.6497 +        gfxFont *font = iter.GetGlyphRun()->mFont;
  1.6498 +        if (font && font->IsSyntheticBold()) {
  1.6499 +            return true;
  1.6500 +        }
  1.6501 +    }
  1.6502 +
  1.6503 +    return false;
  1.6504 +}
  1.6505 +
  1.6506 +// returns true if color is non-opaque (i.e. alpha != 1.0) or completely transparent, false otherwise
  1.6507 +// if true, color is set on output
  1.6508 +static bool
  1.6509 +HasNonOpaqueColor(gfxContext *aContext, gfxRGBA& aCurrentColor)
  1.6510 +{
  1.6511 +    if (aContext->GetDeviceColor(aCurrentColor)) {
  1.6512 +        if (aCurrentColor.a < 1.0 && aCurrentColor.a > 0.0) {
  1.6513 +            return true;
  1.6514 +        }
  1.6515 +    }
  1.6516 +        
  1.6517 +    return false;
  1.6518 +}
  1.6519 +
  1.6520 +// helper class for double-buffering drawing with non-opaque color
  1.6521 +struct BufferAlphaColor {
  1.6522 +    BufferAlphaColor(gfxContext *aContext)
  1.6523 +        : mContext(aContext)
  1.6524 +    {
  1.6525 +
  1.6526 +    }
  1.6527 +
  1.6528 +    ~BufferAlphaColor() {}
  1.6529 +
  1.6530 +    void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, uint32_t appsPerDevUnit)
  1.6531 +    {
  1.6532 +        mContext->Save();
  1.6533 +        mContext->NewPath();
  1.6534 +        mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
  1.6535 +                    aBounds.Y() / appsPerDevUnit,
  1.6536 +                    aBounds.Width() / appsPerDevUnit,
  1.6537 +                    aBounds.Height() / appsPerDevUnit), true);
  1.6538 +        mContext->Clip();
  1.6539 +        mContext->SetColor(gfxRGBA(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
  1.6540 +        mContext->PushGroup(gfxContentType::COLOR_ALPHA);
  1.6541 +        mAlpha = aAlphaColor.a;
  1.6542 +    }
  1.6543 +
  1.6544 +    void PopAlpha()
  1.6545 +    {
  1.6546 +        // pop the text, using the color alpha as the opacity
  1.6547 +        mContext->PopGroupToSource();
  1.6548 +        mContext->SetOperator(gfxContext::OPERATOR_OVER);
  1.6549 +        mContext->Paint(mAlpha);
  1.6550 +        mContext->Restore();
  1.6551 +    }
  1.6552 +
  1.6553 +    gfxContext *mContext;
  1.6554 +    gfxFloat mAlpha;
  1.6555 +};
  1.6556 +
  1.6557 +void
  1.6558 +gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
  1.6559 +                 uint32_t aStart, uint32_t aLength,
  1.6560 +                 PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
  1.6561 +                 gfxTextContextPaint *aContextPaint,
  1.6562 +                 gfxTextRunDrawCallbacks *aCallbacks)
  1.6563 +{
  1.6564 +    NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
  1.6565 +    NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
  1.6566 +                 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
  1.6567 +    NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH");
  1.6568 +
  1.6569 +    bool skipDrawing = mSkipDrawing;
  1.6570 +    if (aDrawMode == DrawMode::GLYPH_FILL) {
  1.6571 +        gfxRGBA currentColor;
  1.6572 +        if (aContext->GetDeviceColor(currentColor) && currentColor.a == 0) {
  1.6573 +            skipDrawing = true;
  1.6574 +        }
  1.6575 +    }
  1.6576 +
  1.6577 +    gfxFloat direction = GetDirection();
  1.6578 +
  1.6579 +    if (skipDrawing) {
  1.6580 +        // We don't need to draw anything;
  1.6581 +        // but if the caller wants advance width, we need to compute it here
  1.6582 +        if (aAdvanceWidth) {
  1.6583 +            gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
  1.6584 +                                                      gfxFont::LOOSE_INK_EXTENTS,
  1.6585 +                                                      aContext, aProvider);
  1.6586 +            *aAdvanceWidth = metrics.mAdvanceWidth * direction;
  1.6587 +        }
  1.6588 +
  1.6589 +        // return without drawing
  1.6590 +        return;
  1.6591 +    }
  1.6592 +
  1.6593 +    gfxPoint pt = aPt;
  1.6594 +
  1.6595 +    // synthetic bolding draws glyphs twice ==> colors with opacity won't draw correctly unless first drawn without alpha
  1.6596 +    BufferAlphaColor syntheticBoldBuffer(aContext);
  1.6597 +    gfxRGBA currentColor;
  1.6598 +    bool needToRestore = false;
  1.6599 +
  1.6600 +    if (aDrawMode == DrawMode::GLYPH_FILL && HasNonOpaqueColor(aContext, currentColor)
  1.6601 +                                          && HasSyntheticBold(this, aStart, aLength)) {
  1.6602 +        needToRestore = true;
  1.6603 +        // measure text, use the bounding box
  1.6604 +        gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS,
  1.6605 +                                                  aContext, aProvider);
  1.6606 +        metrics.mBoundingBox.MoveBy(aPt);
  1.6607 +        syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit());
  1.6608 +    }
  1.6609 +
  1.6610 +    GlyphRunIterator iter(this, aStart, aLength);
  1.6611 +    while (iter.NextRun()) {
  1.6612 +        gfxFont *font = iter.GetGlyphRun()->mFont;
  1.6613 +        uint32_t start = iter.GetStringStart();
  1.6614 +        uint32_t end = iter.GetStringEnd();
  1.6615 +        uint32_t ligatureRunStart = start;
  1.6616 +        uint32_t ligatureRunEnd = end;
  1.6617 +        ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
  1.6618 +        
  1.6619 +        bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL ||
  1.6620 +                           (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks);
  1.6621 +
  1.6622 +        if (drawPartial) {
  1.6623 +            DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt,
  1.6624 +                                aProvider, aCallbacks);
  1.6625 +        }
  1.6626 +
  1.6627 +        DrawGlyphs(font, aContext, aDrawMode, &pt, aContextPaint, ligatureRunStart,
  1.6628 +                   ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd,
  1.6629 +                   aCallbacks);
  1.6630 +
  1.6631 +        if (drawPartial) {
  1.6632 +            DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt,
  1.6633 +                                aProvider, aCallbacks);
  1.6634 +        }
  1.6635 +    }
  1.6636 +
  1.6637 +    // composite result when synthetic bolding used
  1.6638 +    if (needToRestore) {
  1.6639 +        syntheticBoldBuffer.PopAlpha();
  1.6640 +    }
  1.6641 +
  1.6642 +    if (aAdvanceWidth) {
  1.6643 +        *aAdvanceWidth = (pt.x - aPt.x)*direction;
  1.6644 +    }
  1.6645 +}
  1.6646 +
  1.6647 +void
  1.6648 +gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
  1.6649 +                                    uint32_t aStart, uint32_t aEnd,
  1.6650 +                                    gfxFont::BoundingBoxType aBoundingBoxType,
  1.6651 +                                    gfxContext *aRefContext,
  1.6652 +                                    PropertyProvider *aProvider,
  1.6653 +                                    uint32_t aSpacingStart, uint32_t aSpacingEnd,
  1.6654 +                                    Metrics *aMetrics)
  1.6655 +{
  1.6656 +    nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
  1.6657 +    bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
  1.6658 +        aSpacingStart, aSpacingEnd, &spacingBuffer);
  1.6659 +    Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext,
  1.6660 +                                     haveSpacing ? spacingBuffer.Elements() : nullptr);
  1.6661 +    aMetrics->CombineWith(metrics, IsRightToLeft());
  1.6662 +}
  1.6663 +
  1.6664 +void
  1.6665 +gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
  1.6666 +    uint32_t aStart, uint32_t aEnd,
  1.6667 +    gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
  1.6668 +    PropertyProvider *aProvider, Metrics *aMetrics)
  1.6669 +{
  1.6670 +    if (aStart >= aEnd)
  1.6671 +        return;
  1.6672 +
  1.6673 +    // Measure partial ligature. We hack this by clipping the metrics in the
  1.6674 +    // same way we clip the drawing.
  1.6675 +    LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
  1.6676 +
  1.6677 +    // First measure the complete ligature
  1.6678 +    Metrics metrics;
  1.6679 +    AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
  1.6680 +                            aBoundingBoxType, aRefContext,
  1.6681 +                            aProvider, aStart, aEnd, &metrics);
  1.6682 +
  1.6683 +    // Clip the bounding box to the ligature part
  1.6684 +    gfxFloat bboxLeft = metrics.mBoundingBox.X();
  1.6685 +    gfxFloat bboxRight = metrics.mBoundingBox.XMost();
  1.6686 +    // Where we are going to start "drawing" relative to our left baseline origin
  1.6687 +    gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
  1.6688 +    ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
  1.6689 +    metrics.mBoundingBox.x = bboxLeft;
  1.6690 +    metrics.mBoundingBox.width = bboxRight - bboxLeft;
  1.6691 +
  1.6692 +    // mBoundingBox is now relative to the left baseline origin for the entire
  1.6693 +    // ligature. Shift it left.
  1.6694 +    metrics.mBoundingBox.x -=
  1.6695 +        IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
  1.6696 +            : data.mPartAdvance;    
  1.6697 +    metrics.mAdvanceWidth = data.mPartWidth;
  1.6698 +
  1.6699 +    aMetrics->CombineWith(metrics, IsRightToLeft());
  1.6700 +}
  1.6701 +
  1.6702 +gfxTextRun::Metrics
  1.6703 +gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
  1.6704 +                        gfxFont::BoundingBoxType aBoundingBoxType,
  1.6705 +                        gfxContext *aRefContext,
  1.6706 +                        PropertyProvider *aProvider)
  1.6707 +{
  1.6708 +    NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
  1.6709 +
  1.6710 +    Metrics accumulatedMetrics;
  1.6711 +    GlyphRunIterator iter(this, aStart, aLength);
  1.6712 +    while (iter.NextRun()) {
  1.6713 +        gfxFont *font = iter.GetGlyphRun()->mFont;
  1.6714 +        uint32_t start = iter.GetStringStart();
  1.6715 +        uint32_t end = iter.GetStringEnd();
  1.6716 +        uint32_t ligatureRunStart = start;
  1.6717 +        uint32_t ligatureRunEnd = end;
  1.6718 +        ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
  1.6719 +
  1.6720 +        AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
  1.6721 +            aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
  1.6722 +
  1.6723 +        // XXX This sucks. We have to get glyph extents just so we can detect
  1.6724 +        // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
  1.6725 +        // even though in almost all cases we could get correct results just
  1.6726 +        // by getting some ascent/descent from the font and using our stored
  1.6727 +        // advance widths.
  1.6728 +        AccumulateMetricsForRun(font,
  1.6729 +            ligatureRunStart, ligatureRunEnd, aBoundingBoxType,
  1.6730 +            aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
  1.6731 +            &accumulatedMetrics);
  1.6732 +
  1.6733 +        AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
  1.6734 +            aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
  1.6735 +    }
  1.6736 +
  1.6737 +    return accumulatedMetrics;
  1.6738 +}
  1.6739 +
  1.6740 +#define MEASUREMENT_BUFFER_SIZE 100
  1.6741 +
  1.6742 +uint32_t
  1.6743 +gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
  1.6744 +                                bool aLineBreakBefore, gfxFloat aWidth,
  1.6745 +                                PropertyProvider *aProvider,
  1.6746 +                                bool aSuppressInitialBreak,
  1.6747 +                                gfxFloat *aTrimWhitespace,
  1.6748 +                                Metrics *aMetrics,
  1.6749 +                                gfxFont::BoundingBoxType aBoundingBoxType,
  1.6750 +                                gfxContext *aRefContext,
  1.6751 +                                bool *aUsedHyphenation,
  1.6752 +                                uint32_t *aLastBreak,
  1.6753 +                                bool aCanWordWrap,
  1.6754 +                                gfxBreakPriority *aBreakPriority)
  1.6755 +{
  1.6756 +    aMaxLength = std::min(aMaxLength, GetLength() - aStart);
  1.6757 +
  1.6758 +    NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
  1.6759 +
  1.6760 +    uint32_t bufferStart = aStart;
  1.6761 +    uint32_t bufferLength = std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE);
  1.6762 +    PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
  1.6763 +    bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
  1.6764 +    if (haveSpacing) {
  1.6765 +        GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
  1.6766 +                           spacingBuffer);
  1.6767 +    }
  1.6768 +    bool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
  1.6769 +    bool haveHyphenation = aProvider &&
  1.6770 +        (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO ||
  1.6771 +         (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL &&
  1.6772 +          (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
  1.6773 +    if (haveHyphenation) {
  1.6774 +        aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
  1.6775 +                                        hyphenBuffer);
  1.6776 +    }
  1.6777 +
  1.6778 +    gfxFloat width = 0;
  1.6779 +    gfxFloat advance = 0;
  1.6780 +    // The number of space characters that can be trimmed
  1.6781 +    uint32_t trimmableChars = 0;
  1.6782 +    // The amount of space removed by ignoring trimmableChars
  1.6783 +    gfxFloat trimmableAdvance = 0;
  1.6784 +    int32_t lastBreak = -1;
  1.6785 +    int32_t lastBreakTrimmableChars = -1;
  1.6786 +    gfxFloat lastBreakTrimmableAdvance = -1;
  1.6787 +    bool aborted = false;
  1.6788 +    uint32_t end = aStart + aMaxLength;
  1.6789 +    bool lastBreakUsedHyphenation = false;
  1.6790 +
  1.6791 +    uint32_t ligatureRunStart = aStart;
  1.6792 +    uint32_t ligatureRunEnd = end;
  1.6793 +    ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
  1.6794 +
  1.6795 +    uint32_t i;
  1.6796 +    for (i = aStart; i < end; ++i) {
  1.6797 +        if (i >= bufferStart + bufferLength) {
  1.6798 +            // Fetch more spacing and hyphenation data
  1.6799 +            bufferStart = i;
  1.6800 +            bufferLength = std::min(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
  1.6801 +            if (haveSpacing) {
  1.6802 +                GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
  1.6803 +                                   spacingBuffer);
  1.6804 +            }
  1.6805 +            if (haveHyphenation) {
  1.6806 +                aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
  1.6807 +                                                hyphenBuffer);
  1.6808 +            }
  1.6809 +        }
  1.6810 +
  1.6811 +        // There can't be a word-wrap break opportunity at the beginning of the
  1.6812 +        // line: if the width is too small for even one character to fit, it 
  1.6813 +        // could be the first and last break opportunity on the line, and that
  1.6814 +        // would trigger an infinite loop.
  1.6815 +        if (!aSuppressInitialBreak || i > aStart) {
  1.6816 +            bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
  1.6817 +            bool atHyphenationBreak =
  1.6818 +                !atNaturalBreak && haveHyphenation && hyphenBuffer[i - bufferStart];
  1.6819 +            bool atBreak = atNaturalBreak || atHyphenationBreak;
  1.6820 +            bool wordWrapping =
  1.6821 +                aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() &&
  1.6822 +                *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
  1.6823 +
  1.6824 +            if (atBreak || wordWrapping) {
  1.6825 +                gfxFloat hyphenatedAdvance = advance;
  1.6826 +                if (atHyphenationBreak) {
  1.6827 +                    hyphenatedAdvance += aProvider->GetHyphenWidth();
  1.6828 +                }
  1.6829 +            
  1.6830 +                if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
  1.6831 +                    // We can break here.
  1.6832 +                    lastBreak = i;
  1.6833 +                    lastBreakTrimmableChars = trimmableChars;
  1.6834 +                    lastBreakTrimmableAdvance = trimmableAdvance;
  1.6835 +                    lastBreakUsedHyphenation = atHyphenationBreak;
  1.6836 +                    *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
  1.6837 +                                              : gfxBreakPriority::eWordWrapBreak;
  1.6838 +                }
  1.6839 +
  1.6840 +                width += advance;
  1.6841 +                advance = 0;
  1.6842 +                if (width - trimmableAdvance > aWidth) {
  1.6843 +                    // No more text fits. Abort
  1.6844 +                    aborted = true;
  1.6845 +                    break;
  1.6846 +                }
  1.6847 +            }
  1.6848 +        }
  1.6849 +        
  1.6850 +        gfxFloat charAdvance;
  1.6851 +        if (i >= ligatureRunStart && i < ligatureRunEnd) {
  1.6852 +            charAdvance = GetAdvanceForGlyphs(i, i + 1);
  1.6853 +            if (haveSpacing) {
  1.6854 +                PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
  1.6855 +                charAdvance += space->mBefore + space->mAfter;
  1.6856 +            }
  1.6857 +        } else {
  1.6858 +            charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
  1.6859 +        }
  1.6860 +        
  1.6861 +        advance += charAdvance;
  1.6862 +        if (aTrimWhitespace) {
  1.6863 +            if (mCharacterGlyphs[i].CharIsSpace()) {
  1.6864 +                ++trimmableChars;
  1.6865 +                trimmableAdvance += charAdvance;
  1.6866 +            } else {
  1.6867 +                trimmableAdvance = 0;
  1.6868 +                trimmableChars = 0;
  1.6869 +            }
  1.6870 +        }
  1.6871 +    }
  1.6872 +
  1.6873 +    if (!aborted) {
  1.6874 +        width += advance;
  1.6875 +    }
  1.6876 +
  1.6877 +    // There are three possibilities:
  1.6878 +    // 1) all the text fit (width <= aWidth)
  1.6879 +    // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
  1.6880 +    // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
  1.6881 +    uint32_t charsFit;
  1.6882 +    bool usedHyphenation = false;
  1.6883 +    if (width - trimmableAdvance <= aWidth) {
  1.6884 +        charsFit = aMaxLength;
  1.6885 +    } else if (lastBreak >= 0) {
  1.6886 +        charsFit = lastBreak - aStart;
  1.6887 +        trimmableChars = lastBreakTrimmableChars;
  1.6888 +        trimmableAdvance = lastBreakTrimmableAdvance;
  1.6889 +        usedHyphenation = lastBreakUsedHyphenation;
  1.6890 +    } else {
  1.6891 +        charsFit = aMaxLength;
  1.6892 +    }
  1.6893 +
  1.6894 +    if (aMetrics) {
  1.6895 +        *aMetrics = MeasureText(aStart, charsFit - trimmableChars,
  1.6896 +            aBoundingBoxType, aRefContext, aProvider);
  1.6897 +    }
  1.6898 +    if (aTrimWhitespace) {
  1.6899 +        *aTrimWhitespace = trimmableAdvance;
  1.6900 +    }
  1.6901 +    if (aUsedHyphenation) {
  1.6902 +        *aUsedHyphenation = usedHyphenation;
  1.6903 +    }
  1.6904 +    if (aLastBreak && charsFit == aMaxLength) {
  1.6905 +        if (lastBreak < 0) {
  1.6906 +            *aLastBreak = UINT32_MAX;
  1.6907 +        } else {
  1.6908 +            *aLastBreak = lastBreak - aStart;
  1.6909 +        }
  1.6910 +    }
  1.6911 +
  1.6912 +    return charsFit;
  1.6913 +}
  1.6914 +
  1.6915 +gfxFloat
  1.6916 +gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
  1.6917 +                            PropertyProvider *aProvider)
  1.6918 +{
  1.6919 +    NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
  1.6920 +
  1.6921 +    uint32_t ligatureRunStart = aStart;
  1.6922 +    uint32_t ligatureRunEnd = aStart + aLength;
  1.6923 +    ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
  1.6924 +
  1.6925 +    gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) +
  1.6926 +                      ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider);
  1.6927 +
  1.6928 +    // Account for all remaining spacing here. This is more efficient than
  1.6929 +    // processing it along with the glyphs.
  1.6930 +    if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
  1.6931 +        uint32_t i;
  1.6932 +        nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
  1.6933 +        if (spacingBuffer.AppendElements(aLength)) {
  1.6934 +            GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
  1.6935 +                               spacingBuffer.Elements());
  1.6936 +            for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
  1.6937 +                PropertyProvider::Spacing *space = &spacingBuffer[i];
  1.6938 +                result += space->mBefore + space->mAfter;
  1.6939 +            }
  1.6940 +        }
  1.6941 +    }
  1.6942 +
  1.6943 +    return result + GetAdvanceForGlyphs(ligatureRunStart, ligatureRunEnd);
  1.6944 +}
  1.6945 +
  1.6946 +bool
  1.6947 +gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength,
  1.6948 +                          bool aLineBreakBefore, bool aLineBreakAfter,
  1.6949 +                          gfxFloat *aAdvanceWidthDelta,
  1.6950 +                          gfxContext *aRefContext)
  1.6951 +{
  1.6952 +    // Do nothing because our shaping does not currently take linebreaks into
  1.6953 +    // account. There is no change in advance width.
  1.6954 +    if (aAdvanceWidthDelta) {
  1.6955 +        *aAdvanceWidthDelta = 0;
  1.6956 +    }
  1.6957 +    return false;
  1.6958 +}
  1.6959 +
  1.6960 +uint32_t
  1.6961 +gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset)
  1.6962 +{
  1.6963 +    NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
  1.6964 +    NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
  1.6965 +                 "non-empty text but no glyph runs present!");
  1.6966 +    if (aOffset == GetLength())
  1.6967 +        return mGlyphRuns.Length();
  1.6968 +    uint32_t start = 0;
  1.6969 +    uint32_t end = mGlyphRuns.Length();
  1.6970 +    while (end - start > 1) {
  1.6971 +        uint32_t mid = (start + end)/2;
  1.6972 +        if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
  1.6973 +            start = mid;
  1.6974 +        } else {
  1.6975 +            end = mid;
  1.6976 +        }
  1.6977 +    }
  1.6978 +    NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
  1.6979 +                 "Hmm, something went wrong, aOffset should have been found");
  1.6980 +    return start;
  1.6981 +}
  1.6982 +
  1.6983 +nsresult
  1.6984 +gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
  1.6985 +                        uint32_t aUTF16Offset, bool aForceNewRun)
  1.6986 +{
  1.6987 +    NS_ASSERTION(aFont, "adding glyph run for null font!");
  1.6988 +    if (!aFont) {
  1.6989 +        return NS_OK;
  1.6990 +    }    
  1.6991 +    uint32_t numGlyphRuns = mGlyphRuns.Length();
  1.6992 +    if (!aForceNewRun && numGlyphRuns > 0) {
  1.6993 +        GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
  1.6994 +
  1.6995 +        NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
  1.6996 +                     "Glyph runs out of order (and run not forced)");
  1.6997 +
  1.6998 +        // Don't append a run if the font is already the one we want
  1.6999 +        if (lastGlyphRun->mFont == aFont &&
  1.7000 +            lastGlyphRun->mMatchType == aMatchType)
  1.7001 +        {
  1.7002 +            return NS_OK;
  1.7003 +        }
  1.7004 +
  1.7005 +        // If the offset has not changed, avoid leaving a zero-length run
  1.7006 +        // by overwriting the last entry instead of appending...
  1.7007 +        if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
  1.7008 +
  1.7009 +            // ...except that if the run before the last entry had the same
  1.7010 +            // font as the new one wants, merge with it instead of creating
  1.7011 +            // adjacent runs with the same font
  1.7012 +            if (numGlyphRuns > 1 &&
  1.7013 +                mGlyphRuns[numGlyphRuns - 2].mFont == aFont &&
  1.7014 +                mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType)
  1.7015 +            {
  1.7016 +                mGlyphRuns.TruncateLength(numGlyphRuns - 1);
  1.7017 +                return NS_OK;
  1.7018 +            }
  1.7019 +
  1.7020 +            lastGlyphRun->mFont = aFont;
  1.7021 +            lastGlyphRun->mMatchType = aMatchType;
  1.7022 +            return NS_OK;
  1.7023 +        }
  1.7024 +    }
  1.7025 +
  1.7026 +    NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
  1.7027 +                 "First run doesn't cover the first character (and run not forced)?");
  1.7028 +
  1.7029 +    GlyphRun *glyphRun = mGlyphRuns.AppendElement();
  1.7030 +    if (!glyphRun)
  1.7031 +        return NS_ERROR_OUT_OF_MEMORY;
  1.7032 +    glyphRun->mFont = aFont;
  1.7033 +    glyphRun->mCharacterOffset = aUTF16Offset;
  1.7034 +    glyphRun->mMatchType = aMatchType;
  1.7035 +    return NS_OK;
  1.7036 +}
  1.7037 +
  1.7038 +void
  1.7039 +gfxTextRun::SortGlyphRuns()
  1.7040 +{
  1.7041 +    if (mGlyphRuns.Length() <= 1)
  1.7042 +        return;
  1.7043 +
  1.7044 +    nsTArray<GlyphRun> runs(mGlyphRuns);
  1.7045 +    GlyphRunOffsetComparator comp;
  1.7046 +    runs.Sort(comp);
  1.7047 +
  1.7048 +    // Now copy back, coalescing adjacent glyph runs that have the same font
  1.7049 +    mGlyphRuns.Clear();
  1.7050 +    uint32_t i, count = runs.Length();
  1.7051 +    for (i = 0; i < count; ++i) {
  1.7052 +        // a GlyphRun with the same font as the previous GlyphRun can just
  1.7053 +        // be skipped; the last GlyphRun will cover its character range.
  1.7054 +        if (i == 0 || runs[i].mFont != runs[i - 1].mFont) {
  1.7055 +            mGlyphRuns.AppendElement(runs[i]);
  1.7056 +            // If two fonts have the same character offset, Sort() will have
  1.7057 +            // randomized the order.
  1.7058 +            NS_ASSERTION(i == 0 ||
  1.7059 +                         runs[i].mCharacterOffset !=
  1.7060 +                         runs[i - 1].mCharacterOffset,
  1.7061 +                         "Two fonts for the same run, glyph indices may not match the font");
  1.7062 +        }
  1.7063 +    }
  1.7064 +}
  1.7065 +
  1.7066 +// Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
  1.7067 +// therefore we only call it once, at the end of textrun construction,
  1.7068 +// NOT incrementally as each glyph run is added (bug 680402).
  1.7069 +void
  1.7070 +gfxTextRun::SanitizeGlyphRuns()
  1.7071 +{
  1.7072 +    if (mGlyphRuns.Length() <= 1)
  1.7073 +        return;
  1.7074 +
  1.7075 +    // If any glyph run starts with ligature-continuation characters, we need to advance it
  1.7076 +    // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
  1.7077 +    // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
  1.7078 +    // it appear as if a ligature has been formed)
  1.7079 +    int32_t i, lastRunIndex = mGlyphRuns.Length() - 1;
  1.7080 +    const CompressedGlyph *charGlyphs = mCharacterGlyphs;
  1.7081 +    for (i = lastRunIndex; i >= 0; --i) {
  1.7082 +        GlyphRun& run = mGlyphRuns[i];
  1.7083 +        while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
  1.7084 +               run.mCharacterOffset < GetLength()) {
  1.7085 +            run.mCharacterOffset++;
  1.7086 +        }
  1.7087 +        // if the run has become empty, eliminate it
  1.7088 +        if ((i < lastRunIndex &&
  1.7089 +             run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
  1.7090 +            (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
  1.7091 +            mGlyphRuns.RemoveElementAt(i);
  1.7092 +            --lastRunIndex;
  1.7093 +        }
  1.7094 +    }
  1.7095 +}
  1.7096 +
  1.7097 +uint32_t
  1.7098 +gfxTextRun::CountMissingGlyphs()
  1.7099 +{
  1.7100 +    uint32_t i;
  1.7101 +    uint32_t count = 0;
  1.7102 +    for (i = 0; i < GetLength(); ++i) {
  1.7103 +        if (mCharacterGlyphs[i].IsMissing()) {
  1.7104 +            ++count;
  1.7105 +        }
  1.7106 +    }
  1.7107 +    return count;
  1.7108 +}
  1.7109 +
  1.7110 +gfxTextRun::DetailedGlyph *
  1.7111 +gfxTextRun::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
  1.7112 +{
  1.7113 +    NS_ASSERTION(aIndex < GetLength(), "Index out of range");
  1.7114 +
  1.7115 +    if (!mDetailedGlyphs) {
  1.7116 +        mDetailedGlyphs = new DetailedGlyphStore();
  1.7117 +    }
  1.7118 +
  1.7119 +    DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
  1.7120 +    if (!details) {
  1.7121 +        mCharacterGlyphs[aIndex].SetMissing(0);
  1.7122 +        return nullptr;
  1.7123 +    }
  1.7124 +
  1.7125 +    return details;
  1.7126 +}
  1.7127 +
  1.7128 +void
  1.7129 +gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
  1.7130 +{
  1.7131 +    uint32_t wordLen = aShapedWord->GetLength();
  1.7132 +    NS_ASSERTION(aOffset + wordLen <= GetLength(),
  1.7133 +                 "word overruns end of textrun!");
  1.7134 +
  1.7135 +    CompressedGlyph *charGlyphs = GetCharacterGlyphs();
  1.7136 +    const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
  1.7137 +    if (aShapedWord->HasDetailedGlyphs()) {
  1.7138 +        for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
  1.7139 +            const CompressedGlyph& g = wordGlyphs[i];
  1.7140 +            if (g.IsSimpleGlyph()) {
  1.7141 +                charGlyphs[aOffset] = g;
  1.7142 +            } else {
  1.7143 +                const DetailedGlyph *details =
  1.7144 +                    g.GetGlyphCount() > 0 ?
  1.7145 +                        aShapedWord->GetDetailedGlyphs(i) : nullptr;
  1.7146 +                SetGlyphs(aOffset, g, details);
  1.7147 +            }
  1.7148 +        }
  1.7149 +    } else {
  1.7150 +        memcpy(charGlyphs + aOffset, wordGlyphs,
  1.7151 +               wordLen * sizeof(CompressedGlyph));
  1.7152 +    }
  1.7153 +}
  1.7154 +
  1.7155 +void
  1.7156 +gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart,
  1.7157 +                              uint32_t aLength, uint32_t aDest)
  1.7158 +{
  1.7159 +    NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
  1.7160 +                 "Source substring out of range");
  1.7161 +    NS_ASSERTION(aDest + aLength <= GetLength(),
  1.7162 +                 "Destination substring out of range");
  1.7163 +
  1.7164 +    if (aSource->mSkipDrawing) {
  1.7165 +        mSkipDrawing = true;
  1.7166 +    }
  1.7167 +
  1.7168 +    // Copy base glyph data, and DetailedGlyph data where present
  1.7169 +    const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aStart;
  1.7170 +    CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
  1.7171 +    for (uint32_t i = 0; i < aLength; ++i) {
  1.7172 +        CompressedGlyph g = srcGlyphs[i];
  1.7173 +        g.SetCanBreakBefore(!g.IsClusterStart() ?
  1.7174 +            CompressedGlyph::FLAG_BREAK_TYPE_NONE :
  1.7175 +            dstGlyphs[i].CanBreakBefore());
  1.7176 +        if (!g.IsSimpleGlyph()) {
  1.7177 +            uint32_t count = g.GetGlyphCount();
  1.7178 +            if (count > 0) {
  1.7179 +                DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
  1.7180 +                if (dst) {
  1.7181 +                    DetailedGlyph *src = aSource->GetDetailedGlyphs(i + aStart);
  1.7182 +                    if (src) {
  1.7183 +                        ::memcpy(dst, src, count * sizeof(DetailedGlyph));
  1.7184 +                    } else {
  1.7185 +                        g.SetMissing(0);
  1.7186 +                    }
  1.7187 +                } else {
  1.7188 +                    g.SetMissing(0);
  1.7189 +                }
  1.7190 +            }
  1.7191 +        }
  1.7192 +        dstGlyphs[i] = g;
  1.7193 +    }
  1.7194 +
  1.7195 +    // Copy glyph runs
  1.7196 +    GlyphRunIterator iter(aSource, aStart, aLength);
  1.7197 +#ifdef DEBUG
  1.7198 +    gfxFont *lastFont = nullptr;
  1.7199 +#endif
  1.7200 +    while (iter.NextRun()) {
  1.7201 +        gfxFont *font = iter.GetGlyphRun()->mFont;
  1.7202 +        NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
  1.7203 +#ifdef DEBUG
  1.7204 +        lastFont = font;
  1.7205 +        uint32_t end = iter.GetStringEnd();
  1.7206 +#endif
  1.7207 +        uint32_t start = iter.GetStringStart();
  1.7208 +
  1.7209 +        // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
  1.7210 +        // Although it's unusual (and not desirable), it's possible for us to assign
  1.7211 +        // different fonts to a base character and a following diacritic.
  1.7212 +        // Example on OSX 10.5/10.6 with default fonts installed:
  1.7213 +        //     data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
  1.7214 +        //                    &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
  1.7215 +        // This means the rendering of the cluster will probably not be very good,
  1.7216 +        // but it's the best we can do for now if the specified font only covered the
  1.7217 +        // initial base character and not its applied marks.
  1.7218 +        NS_WARN_IF_FALSE(aSource->IsClusterStart(start),
  1.7219 +                         "Started font run in the middle of a cluster");
  1.7220 +        NS_WARN_IF_FALSE(end == aSource->GetLength() || aSource->IsClusterStart(end),
  1.7221 +                         "Ended font run in the middle of a cluster");
  1.7222 +
  1.7223 +        nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
  1.7224 +                                  start - aStart + aDest, false);
  1.7225 +        if (NS_FAILED(rv))
  1.7226 +            return;
  1.7227 +    }
  1.7228 +}
  1.7229 +
  1.7230 +void
  1.7231 +gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
  1.7232 +                          uint32_t aCharIndex)
  1.7233 +{
  1.7234 +    if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ')) {
  1.7235 +        return;
  1.7236 +    }
  1.7237 +
  1.7238 +    aFont->InitWordCache();
  1.7239 +    static const uint8_t space = ' ';
  1.7240 +    gfxShapedWord *sw = aFont->GetShapedWord(aContext,
  1.7241 +                                             &space, 1,
  1.7242 +                                             HashMix(0, ' '), 
  1.7243 +                                             MOZ_SCRIPT_LATIN,
  1.7244 +                                             mAppUnitsPerDevUnit,
  1.7245 +                                             gfxTextRunFactory::TEXT_IS_8BIT |
  1.7246 +                                             gfxTextRunFactory::TEXT_IS_ASCII |
  1.7247 +                                             gfxTextRunFactory::TEXT_IS_PERSISTENT,
  1.7248 +                                             nullptr);
  1.7249 +    if (sw) {
  1.7250 +        AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
  1.7251 +        CopyGlyphDataFrom(sw, aCharIndex);
  1.7252 +    }
  1.7253 +}
  1.7254 +
  1.7255 +bool
  1.7256 +gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
  1.7257 +                                  uint32_t aCharIndex, char16_t aSpaceChar)
  1.7258 +{
  1.7259 +    uint32_t spaceGlyph = aFont->GetSpaceGlyph();
  1.7260 +    if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
  1.7261 +        return false;
  1.7262 +    }
  1.7263 +
  1.7264 +    uint32_t spaceWidthAppUnits =
  1.7265 +        NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit);
  1.7266 +    if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
  1.7267 +        return false;
  1.7268 +    }
  1.7269 +
  1.7270 +    AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
  1.7271 +    CompressedGlyph g;
  1.7272 +    g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
  1.7273 +    if (aSpaceChar == ' ') {
  1.7274 +        g.SetIsSpace();
  1.7275 +    }
  1.7276 +    GetCharacterGlyphs()[aCharIndex] = g;
  1.7277 +    return true;
  1.7278 +}
  1.7279 +
  1.7280 +void
  1.7281 +gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
  1.7282 +{
  1.7283 +    bool needsGlyphExtents = NeedsGlyphExtents(this);
  1.7284 +    if (!needsGlyphExtents && !mDetailedGlyphs)
  1.7285 +        return;
  1.7286 +
  1.7287 +    uint32_t i, runCount = mGlyphRuns.Length();
  1.7288 +    CompressedGlyph *charGlyphs = mCharacterGlyphs;
  1.7289 +    for (i = 0; i < runCount; ++i) {
  1.7290 +        const GlyphRun& run = mGlyphRuns[i];
  1.7291 +        gfxFont *font = run.mFont;
  1.7292 +        uint32_t start = run.mCharacterOffset;
  1.7293 +        uint32_t end = i + 1 < runCount ?
  1.7294 +            mGlyphRuns[i + 1].mCharacterOffset : GetLength();
  1.7295 +        bool fontIsSetup = false;
  1.7296 +        uint32_t j;
  1.7297 +        gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
  1.7298 +  
  1.7299 +        for (j = start; j < end; ++j) {
  1.7300 +            const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
  1.7301 +            if (glyphData->IsSimpleGlyph()) {
  1.7302 +                // If we're in speed mode, don't set up glyph extents here; we'll
  1.7303 +                // just return "optimistic" glyph bounds later
  1.7304 +                if (needsGlyphExtents) {
  1.7305 +                    uint32_t glyphIndex = glyphData->GetSimpleGlyph();
  1.7306 +                    if (!extents->IsGlyphKnown(glyphIndex)) {
  1.7307 +                        if (!fontIsSetup) {
  1.7308 +                            if (!font->SetupCairoFont(aRefContext)) {
  1.7309 +                                NS_WARNING("failed to set up font for glyph extents");
  1.7310 +                                break;
  1.7311 +                            }
  1.7312 +                            fontIsSetup = true;
  1.7313 +                        }
  1.7314 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.7315 +                        ++gGlyphExtentsSetupEagerSimple;
  1.7316 +#endif
  1.7317 +                        font->SetupGlyphExtents(aRefContext, glyphIndex, false, extents);
  1.7318 +                    }
  1.7319 +                }
  1.7320 +            } else if (!glyphData->IsMissing()) {
  1.7321 +                uint32_t glyphCount = glyphData->GetGlyphCount();
  1.7322 +                if (glyphCount == 0) {
  1.7323 +                    continue;
  1.7324 +                }
  1.7325 +                const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
  1.7326 +                if (!details) {
  1.7327 +                    continue;
  1.7328 +                }
  1.7329 +                for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
  1.7330 +                    uint32_t glyphIndex = details->mGlyphID;
  1.7331 +                    if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
  1.7332 +                        if (!fontIsSetup) {
  1.7333 +                            if (!font->SetupCairoFont(aRefContext)) {
  1.7334 +                                NS_WARNING("failed to set up font for glyph extents");
  1.7335 +                                break;
  1.7336 +                            }
  1.7337 +                            fontIsSetup = true;
  1.7338 +                        }
  1.7339 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1.7340 +                        ++gGlyphExtentsSetupEagerTight;
  1.7341 +#endif
  1.7342 +                        font->SetupGlyphExtents(aRefContext, glyphIndex, true, extents);
  1.7343 +                    }
  1.7344 +                }
  1.7345 +            }
  1.7346 +        }
  1.7347 +    }
  1.7348 +}
  1.7349 +
  1.7350 +
  1.7351 +gfxTextRun::ClusterIterator::ClusterIterator(gfxTextRun *aTextRun)
  1.7352 +    : mTextRun(aTextRun), mCurrentChar(uint32_t(-1))
  1.7353 +{
  1.7354 +}
  1.7355 +
  1.7356 +void
  1.7357 +gfxTextRun::ClusterIterator::Reset()
  1.7358 +{
  1.7359 +    mCurrentChar = uint32_t(-1);
  1.7360 +}
  1.7361 +
  1.7362 +bool
  1.7363 +gfxTextRun::ClusterIterator::NextCluster()
  1.7364 +{
  1.7365 +    uint32_t len = mTextRun->GetLength();
  1.7366 +    while (++mCurrentChar < len) {
  1.7367 +        if (mTextRun->IsClusterStart(mCurrentChar)) {
  1.7368 +            return true;
  1.7369 +        }
  1.7370 +    }
  1.7371 +
  1.7372 +    mCurrentChar = uint32_t(-1);
  1.7373 +    return false;
  1.7374 +}
  1.7375 +
  1.7376 +uint32_t
  1.7377 +gfxTextRun::ClusterIterator::ClusterLength() const
  1.7378 +{
  1.7379 +    if (mCurrentChar == uint32_t(-1)) {
  1.7380 +        return 0;
  1.7381 +    }
  1.7382 +
  1.7383 +    uint32_t i = mCurrentChar,
  1.7384 +             len = mTextRun->GetLength();
  1.7385 +    while (++i < len) {
  1.7386 +        if (mTextRun->IsClusterStart(i)) {
  1.7387 +            break;
  1.7388 +        }
  1.7389 +    }
  1.7390 +
  1.7391 +    return i - mCurrentChar;
  1.7392 +}
  1.7393 +
  1.7394 +gfxFloat
  1.7395 +gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const
  1.7396 +{
  1.7397 +    if (mCurrentChar == uint32_t(-1)) {
  1.7398 +        return 0;
  1.7399 +    }
  1.7400 +
  1.7401 +    return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
  1.7402 +}
  1.7403 +
  1.7404 +size_t
  1.7405 +gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
  1.7406 +{
  1.7407 +    // The second arg is how much gfxTextRun::AllocateStorage would have
  1.7408 +    // allocated.
  1.7409 +    size_t total = mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf);
  1.7410 +
  1.7411 +    if (mDetailedGlyphs) {
  1.7412 +        total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
  1.7413 +    }
  1.7414 +
  1.7415 +    return total;
  1.7416 +}
  1.7417 +
  1.7418 +size_t
  1.7419 +gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
  1.7420 +{
  1.7421 +    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  1.7422 +}
  1.7423 +
  1.7424 +
  1.7425 +#ifdef DEBUG
  1.7426 +void
  1.7427 +gfxTextRun::Dump(FILE* aOutput) {
  1.7428 +    if (!aOutput) {
  1.7429 +        aOutput = stdout;
  1.7430 +    }
  1.7431 +
  1.7432 +    uint32_t i;
  1.7433 +    fputc('[', aOutput);
  1.7434 +    for (i = 0; i < mGlyphRuns.Length(); ++i) {
  1.7435 +        if (i > 0) {
  1.7436 +            fputc(',', aOutput);
  1.7437 +        }
  1.7438 +        gfxFont* font = mGlyphRuns[i].mFont;
  1.7439 +        const gfxFontStyle* style = font->GetStyle();
  1.7440 +        NS_ConvertUTF16toUTF8 fontName(font->GetName());
  1.7441 +        nsAutoCString lang;
  1.7442 +        style->language->ToUTF8String(lang);
  1.7443 +        fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
  1.7444 +                fontName.get(), style->size,
  1.7445 +                style->weight, style->style, lang.get());
  1.7446 +    }
  1.7447 +    fputc(']', aOutput);
  1.7448 +}
  1.7449 +#endif

mercurial