diff options
Diffstat (limited to 'gfx/thebes/gfxFontEntry.h')
-rw-r--r-- | gfx/thebes/gfxFontEntry.h | 1253 |
1 files changed, 1253 insertions, 0 deletions
diff --git a/gfx/thebes/gfxFontEntry.h b/gfx/thebes/gfxFontEntry.h new file mode 100644 index 0000000000..86d2445d0b --- /dev/null +++ b/gfx/thebes/gfxFontEntry.h @@ -0,0 +1,1253 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_FONTENTRY_H +#define GFX_FONTENTRY_H + +#include <limits> +#include <math.h> +#include <new> +#include <utility> +#include "COLRFonts.h" +#include "ThebesRLBoxTypes.h" +#include "gfxFontUtils.h" +#include "gfxFontVariations.h" +#include "gfxRect.h" +#include "gfxTypes.h" +#include "harfbuzz/hb.h" +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Assertions.h" +#include "mozilla/FontPropertyTypes.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/Mutex.h" +#include "mozilla/RefPtr.h" +#include "mozilla/RWLock.h" +#include "mozilla/TypedEnumBits.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/intl/UnicodeScriptCodes.h" +#include "nsTHashMap.h" +#include "nsDebug.h" +#include "nsHashKeys.h" +#include "nsISupports.h" +#include "nsStringFwd.h" +#include "nsTArray.h" +#include "nscore.h" + +class FontInfoData; +class gfxContext; +class gfxFont; +class gfxFontFamily; +class gfxPlatformFontList; +class gfxSVGGlyphs; +class gfxUserFontData; +class nsAtom; +struct FontListSizes; +struct gfxFontFeature; +struct gfxFontStyle; +enum class eFontPresentation : uint8_t; + +namespace IPC { +template <class P> +struct ParamTraits; +} + +namespace mozilla { +class SVGContextPaint; +namespace fontlist { +struct Face; +struct Family; +} // namespace fontlist +} // namespace mozilla + +typedef struct gr_face gr_face; +typedef struct FT_MM_Var_ FT_MM_Var; + +#define NO_FONT_LANGUAGE_OVERRIDE 0 + +class gfxCharacterMap : public gfxSparseBitSet { + public: + // gfxCharacterMap instances may be shared across multiple threads via a + // global table managed by gfxPlatformFontList. Once a gfxCharacterMap is + // inserted in the global table, its mShared flag will be TRUE, and we + // cannot safely delete it except from gfxPlatformFontList (which will + // use a lock to ensure entries are removed from its table and deleted + // safely). + + // AddRef() is pretty much standard. We don't return the refcount as our + // users don't care about it. + void AddRef() { + MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(gfxCharacterMap); + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); + [[maybe_unused]] nsrefcnt count = ++mRefCnt; + NS_LOG_ADDREF(this, count, "gfxCharacterMap", sizeof(*this)); + } + + // Custom Release(): if the object is referenced from the global shared + // table, and we're releasing the last *other* reference to it, then we + // notify the global table to consider also releasing its ref. (That may + // not actually happen, if another thread is racing with us and takes a + // new reference, or completes the release first!) + void Release() { + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); + // We can't safely read this after we've decremented mRefCnt, so save it + // in a local variable here. Note that the value is never reset to false + // once it has been set to true (when recording the cmap in the shared + // table), so there's no risk of this resulting in a "false positive" when + // tested later. A "false negative" is possible but harmless; it would + // just mean we miss an opportunity to release a reference from the shared + // cmap table. + bool isShared = mShared; + + // Ensure we only access mRefCnt once, for consistency if the object is + // being used by multiple threads. + nsrefcnt count = --mRefCnt; + NS_LOG_RELEASE(this, count, "gfxCharacterMap"); + + // If isShared was true, this object has been shared across threads. In + // that case, if the refcount went to 1, we notify the shared table so + // it can drop its reference and delete the object. + if (isShared) { + MOZ_ASSERT(count > 0); + if (count == 1) { + NotifyMaybeReleased(this); + } + return; + } + + // Otherwise, this object hasn't been shared and we can safely delete it + // as we must have been holding the only reference. (Note that if we were + // holding the only reference, there's no other owner who can have set + // mShared to true since we read it above.) + if (count == 0) { + delete this; + } + } + + gfxCharacterMap() = default; + + explicit gfxCharacterMap(const gfxSparseBitSet& aOther) + : gfxSparseBitSet(aOther) {} + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { + return gfxSparseBitSet::SizeOfExcludingThis(aMallocSizeOf); + } + + // hash of the cmap bitvector + uint32_t mHash = 0; + + // if cmap is built on the fly it's never shared + bool mBuildOnTheFly = false; + + // Character map is shared globally. This can only be set by the thread that + // originally created the map, as no other thread can get a reference until + // it has been shared via the global table. + bool mShared = false; + + protected: + friend class gfxPlatformFontList; + + // Destructor should not be called except via Release(). + // (Note that our "friend" gfxPlatformFontList also accesses this from its + // MaybeRemoveCmap method.) + ~gfxCharacterMap() = default; + + nsrefcnt RefCount() const { return mRefCnt; } + + void CalcHash() { mHash = GetChecksum(); } + + static void NotifyMaybeReleased(gfxCharacterMap* aCmap); + + // Only used when clearing the shared-cmap hashtable during shutdown. + void ClearSharedFlag() { + MOZ_ASSERT(NS_IsMainThread()); + mShared = false; + } + + mozilla::ThreadSafeAutoRefCnt mRefCnt; + + private: + gfxCharacterMap(const gfxCharacterMap&) = delete; + gfxCharacterMap& operator=(const gfxCharacterMap&) = delete; +}; + +// Info on an individual font feature, for reporting available features +// to DevTools via the GetFeatureInfo method. +struct gfxFontFeatureInfo { + uint32_t mTag; + uint32_t mScript; + uint32_t mLangSys; +}; + +class gfxFontEntryCallbacks; + +class gfxFontEntry { + public: + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::intl::Script Script; + typedef mozilla::FontWeight FontWeight; + typedef mozilla::FontSlantStyle FontSlantStyle; + typedef mozilla::FontStretch FontStretch; + typedef mozilla::WeightRange WeightRange; + typedef mozilla::SlantStyleRange SlantStyleRange; + typedef mozilla::StretchRange StretchRange; + + // Used by stylo + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxFontEntry) + + explicit gfxFontEntry(const nsACString& aName, bool aIsStandardFace = false); + + gfxFontEntry() = delete; + gfxFontEntry(const gfxFontEntry&) = delete; + gfxFontEntry& operator=(const gfxFontEntry&) = delete; + + // Create a new entry that refers to the same font as this, but without + // additional state that may have been set up (such as family name). + // (This is only to be used for system fonts in the platform font list, + // not user fonts.) + virtual gfxFontEntry* Clone() const = 0; + + // unique name for the face, *not* the family; not necessarily the + // "real" or user-friendly name, may be an internal identifier + const nsCString& Name() const { return mName; } + + // family name + const nsCString& FamilyName() const { return mFamilyName; } + + // The following two methods may be relatively expensive, as they + // will (usually, except on Linux) load and parse the 'name' table; + // they are intended only for the font-inspection API, not for + // perf-critical layout/drawing work. + + // The "real" name of the face, if available from the font resource; + // returns Name() if nothing better is available. + virtual nsCString RealFaceName(); + + WeightRange Weight() const { return mWeightRange; } + StretchRange Stretch() const { return mStretchRange; } + SlantStyleRange SlantStyle() const { return mStyleRange; } + + bool IsUserFont() const { return mIsDataUserFont || mIsLocalUserFont; } + bool IsLocalUserFont() const { return mIsLocalUserFont; } + bool IsFixedPitch() const { return mFixedPitch; } + bool IsItalic() const { return SlantStyle().Min().IsItalic(); } + bool IsOblique() const { return SlantStyle().Min().IsOblique(); } + bool IsUpright() const { return SlantStyle().Min().IsNormal(); } + inline bool SupportsItalic(); // defined below, because of RangeFlags use + inline bool SupportsBold(); + inline bool MayUseSyntheticSlant(); + bool IgnoreGDEF() const { return mIgnoreGDEF; } + bool IgnoreGSUB() const { return mIgnoreGSUB; } + + // Return whether the face corresponds to "normal" CSS style properties: + // font-style: normal; + // font-weight: normal; + // font-stretch: normal; + // If this is false, we might want to fall back to a different face and + // possibly apply synthetic styling. + bool IsNormalStyle() const { + return IsUpright() && Weight().Min() <= FontWeight::NORMAL && + Weight().Max() >= FontWeight::NORMAL && + Stretch().Min() <= FontStretch::NORMAL && + Stretch().Max() >= FontStretch::NORMAL; + } + + // whether a feature is supported by the font (limited to a small set + // of features for which some form of fallback needs to be implemented) + virtual bool SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag); + bool SupportsGraphiteFeature(uint32_t aFeatureTag); + + // returns a set containing all input glyph ids for a given feature + const hb_set_t* InputsForOpenTypeFeature(Script aScript, + uint32_t aFeatureTag); + + virtual bool HasFontTable(uint32_t aTableTag); + + inline bool HasGraphiteTables() { + LazyFlag flag = mHasGraphiteTables; + if (flag == LazyFlag::Uninitialized) { + flag = CheckForGraphiteTables() ? LazyFlag::Yes : LazyFlag::No; + mHasGraphiteTables = flag; + } + return flag == LazyFlag::Yes; + } + + inline bool HasCmapTable() { + if (!mCharacterMap && !mShmemCharacterMap) { + ReadCMAP(); + NS_ASSERTION(mCharacterMap || mShmemCharacterMap, + "failed to initialize character map"); + } + return mHasCmapTable; + } + + inline bool HasCharacter(uint32_t ch) { + if (mShmemCharacterMap) { + return GetShmemCharacterMap()->test(ch); + } + if (mCharacterMap) { + if (mShmemFace && TrySetShmemCharacterMap()) { + // Forget our temporary local copy, now we can use the shared cmap + auto* oldCmap = mCharacterMap.exchange(nullptr); + NS_IF_RELEASE(oldCmap); + return GetShmemCharacterMap()->test(ch); + } + if (GetCharacterMap()->test(ch)) { + return true; + } + } + return TestCharacterMap(ch); + } + + virtual bool SkipDuringSystemFallback() { return false; } + void EnsureUVSMapInitialized(); + uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS); + + // All concrete gfxFontEntry subclasses (except gfxUserFontEntry) need + // to override this, otherwise the font will never be used as it will + // be considered to support no characters. + // ReadCMAP() must *always* set the mCharacterMap pointer to a valid + // gfxCharacterMap, even if empty, as other code assumes this pointer + // can be safely dereferenced. + virtual nsresult ReadCMAP(FontInfoData* aFontInfoData = nullptr); + + bool TryGetSVGData(const gfxFont* aFont); + bool HasSVGGlyph(uint32_t aGlyphId); + bool GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId, + gfxFloat aSize, gfxRect* aResult); + void RenderSVGGlyph(gfxContext* aContext, uint32_t aGlyphId, + mozilla::SVGContextPaint* aContextPaint); + // Call this when glyph geometry or rendering has changed + // (e.g. animated SVG glyphs) + void NotifyGlyphsChanged(); + + bool TryGetColorGlyphs(); + + bool HasColorBitmapTable() { + LazyFlag flag = mHasColorBitmapTable; + if (flag == LazyFlag::Uninitialized) { + flag = HasFontTable(TRUETYPE_TAG('C', 'B', 'D', 'T')) || + HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x')) + ? LazyFlag::Yes + : LazyFlag::No; + mHasColorBitmapTable = flag; + } + return flag == LazyFlag::Yes; + } + + // Access to raw font table data (needed for Harfbuzz): + // returns a pointer to data owned by the fontEntry or the OS, + // which will remain valid until the blob is destroyed. + // The data MUST be treated as read-only; we may be getting a + // reference to a shared system font cache. + // + // The default implementation uses CopyFontTable to get the data + // into a byte array, and maintains a cache of loaded tables. + // + // Subclasses should override this if they can provide more efficient + // access than copying table data into our own buffers. + // + // Get blob that encapsulates a specific font table, or nullptr if + // the table doesn't exist in the font. + // + // Caller is responsible to call hb_blob_destroy() on the returned blob + // (if non-nullptr) when no longer required. For transient access to a + // table, use of AutoTable (below) is generally preferred. + virtual hb_blob_t* GetFontTable(uint32_t aTag); + + // Stack-based utility to return a specified table, automatically releasing + // the blob when the AutoTable goes out of scope. + class AutoTable { + public: + AutoTable(gfxFontEntry* aFontEntry, uint32_t aTag) { + mBlob = aFontEntry->GetFontTable(aTag); + } + ~AutoTable() { hb_blob_destroy(mBlob); } + operator hb_blob_t*() const { return mBlob; } + + private: + hb_blob_t* mBlob; + // not implemented: + AutoTable(const AutoTable&) = delete; + AutoTable& operator=(const AutoTable&) = delete; + }; + + // Return a font instance for a particular style. This may be a newly- + // created instance, or a font already in the global cache. + // We can't return a UniquePtr here, because we may be returning a shared + // cached instance; but we also don't return already_AddRefed, because + // the caller may only need to use the font temporarily and doesn't need + // a strong reference. + already_AddRefed<gfxFont> FindOrMakeFont( + const gfxFontStyle* aStyle, gfxCharacterMap* aUnicodeRangeMap = nullptr); + + // Get an existing font table cache entry in aBlob if it has been + // registered, or return false if not. Callers must call + // hb_blob_destroy on aBlob if true is returned. + // + // Note that some gfxFont implementations may not call this at all, + // if it is more efficient to get the table from the OS at that level. + bool GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob); + + // Elements of aTable are transferred (not copied) to and returned in a + // new hb_blob_t which is registered on the gfxFontEntry, but the initial + // reference is owned by the caller. Removing the last reference + // unregisters the table from the font entry. + // + // Pass nullptr for aBuffer to indicate that the table is not present and + // nullptr will be returned. Also returns nullptr on OOM. + hb_blob_t* ShareFontTableAndGetBlob(uint32_t aTag, nsTArray<uint8_t>* aTable); + + // Get the font's unitsPerEm from the 'head' table, in the case of an + // sfnt resource. Will return kInvalidUPEM for non-sfnt fonts, + // if present on the platform. + uint16_t UnitsPerEm(); + enum { + kMinUPEM = 16, // Limits on valid unitsPerEm range, from the + kMaxUPEM = 16384, // OpenType spec + kInvalidUPEM = uint16_t(-1) + }; + + // Shaper face accessors: + // NOTE that harfbuzz and graphite handle ownership/lifetime of the face + // object in completely different ways. + + // Create a HarfBuzz face corresponding to this font file. + // Our reference to the underlying hb_face_t will be released when the + // returned AutoHBFace goes out of scope, but the hb_face_t itself may + // be kept alive by other references (e.g. if an hb_font_t has been + // instantiated for it). + class MOZ_STACK_CLASS AutoHBFace { + public: + explicit AutoHBFace(hb_face_t* aFace) : mFace(aFace) {} + ~AutoHBFace() { hb_face_destroy(mFace); } + + operator hb_face_t*() const { return mFace; } + + // Not default-constructible, not copyable. + AutoHBFace() = delete; + AutoHBFace(const AutoHBFace&) = delete; + AutoHBFace& operator=(const AutoHBFace&) = delete; + + private: + hb_face_t* mFace; + }; + + AutoHBFace GetHBFace() { + return AutoHBFace(hb_face_create_for_tables(HBGetTable, this, nullptr)); + } + + // Get the sandbox instance that graphite is running in. + rlbox_sandbox_gr* GetGrSandbox(); + + // Register and get the callback handle for the glyph advance firefox callback + // Since the sandbox instance is shared with multiple test shapers, callback + // registration must be handled centrally to ensure multiple instances don't + // register the same callback. + sandbox_callback_gr<float (*)(const void*, uint16_t)>* + GetGrSandboxAdvanceCallbackHandle(); + + // Get Graphite face corresponding to this font file. + // Caller must call gfxFontEntry::ReleaseGrFace when finished with it. + // Graphite is run in a sandbox + tainted_opaque_gr<gr_face*> GetGrFace(); + void ReleaseGrFace(tainted_opaque_gr<gr_face*> aFace); + + // Does the font have graphite contextuals that involve the space glyph + // (and therefore we should bypass the word cache)? + // Since this function inspects data from libGraphite stored in sandbox memory + // it can only return a "hint" to the correct return value. This is because + // a compromised libGraphite could change the sandbox memory maliciously at + // any moment. The caller must ensure the calling code performs safe actions + // independent of the value returned, to unwrap this return. + tainted_boolean_hint HasGraphiteSpaceContextuals(); + + // Release any SVG-glyphs document this font may have loaded. + void DisconnectSVG(); + + // Called to notify that aFont is being destroyed. Needed when we're tracking + // the fonts belonging to this font entry. + void NotifyFontDestroyed(gfxFont* aFont); + + // For memory reporting of the platform font list. + virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const; + virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const; + + // Used for reporting on individual font entries in the user font cache, + // which are not present in the platform font list. + size_t ComputedSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + + // Used when checking for complex script support, to mask off cmap ranges + struct ScriptRange { + uint32_t rangeStart; + uint32_t rangeEnd; + uint32_t numTags; // number of entries in the tags[] array + hb_tag_t tags[3]; // up to three OpenType script tags to check + }; + + bool SupportsScriptInGSUB(const hb_tag_t* aScriptTags, uint32_t aNumTags); + + /** + * Font-variation query methods. + * + * Font backends that don't support variations should provide empty + * implementations. + */ + virtual bool HasVariations() = 0; + + virtual void GetVariationAxes( + nsTArray<gfxFontVariationAxis>& aVariationAxes) = 0; + + virtual void GetVariationInstances( + nsTArray<gfxFontVariationInstance>& aInstances) = 0; + + bool HasBoldVariableWeight(); + bool HasItalicVariation(); + bool HasSlantVariation(); + bool HasOpticalSize(); + + void CheckForVariationAxes(); + + // Set up the entry's weight/stretch/style ranges according to axes found + // by GetVariationAxes (for installed fonts; do NOT call this for user + // fonts, where the ranges are provided by @font-face descriptors). + void SetupVariationRanges(); + + // Get variation axis settings that should be used to implement a particular + // font style using this resource. + void GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult, + const gfxFontStyle& aStyle); + + // Get the font's list of features (if any) for DevTools support. + void GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo); + + // This is only called on platforms where we use FreeType. + virtual FT_MM_Var* GetMMVar() { return nullptr; } + + // Return true if the font has a 'trak' table (and we can successfully + // interpret it), otherwise false. This will load and cache the table + // the first time it is called. + bool HasTrackingTable(); + + // Return the tracking (in font units) to be applied for the given size. + // (This is a floating-point number because of possible interpolation.) + float TrackingForCSSPx(float aSize) const; + + mozilla::gfx::Rect GetFontExtents(float aFUnitScaleFactor) const { + // Flip the y-axis here to match the orientation of Gecko's coordinates. + return mozilla::gfx::Rect(float(mXMin) * aFUnitScaleFactor, + float(-mYMax) * aFUnitScaleFactor, + float(mXMax - mXMin) * aFUnitScaleFactor, + float(mYMax - mYMin) * aFUnitScaleFactor); + } + + nsCString mName; + nsCString mFamilyName; + + // These are mutable so that we can take a read lock within a const method. + mutable mozilla::RWLock mLock; + mutable mozilla::Mutex mFeatureInfoLock; + + mozilla::Atomic<gfxCharacterMap*> mCharacterMap; // strong ref + gfxCharacterMap* GetCharacterMap() const { return mCharacterMap; } + + mozilla::fontlist::Face* mShmemFace = nullptr; + + mozilla::Atomic<const SharedBitSet*> mShmemCharacterMap; + const SharedBitSet* GetShmemCharacterMap() const { + return mShmemCharacterMap; + } + + mozilla::Atomic<const uint8_t*> mUVSData; + const uint8_t* GetUVSData() const { return mUVSData; } + + mozilla::UniquePtr<gfxUserFontData> mUserFontData; + + mozilla::Atomic<gfxSVGGlyphs*> mSVGGlyphs; + gfxSVGGlyphs* GetSVGGlyphs() const { return mSVGGlyphs; } + + // list of gfxFonts that are using SVG glyphs + nsTArray<const gfxFont*> mFontsUsingSVGGlyphs MOZ_GUARDED_BY(mLock); + nsTArray<gfxFontFeature> mFeatureSettings; + nsTArray<gfxFontVariation> mVariationSettings; + + mozilla::UniquePtr<nsTHashMap<nsUint32HashKey, bool>> mSupportedFeatures + MOZ_GUARDED_BY(mFeatureInfoLock); + mozilla::UniquePtr<nsTHashMap<nsUint32HashKey, hb_set_t*>> mFeatureInputs + MOZ_GUARDED_BY(mFeatureInfoLock); + + // Color Layer font support. These tables are inert once loaded, so we don't + // need to hold a lock when reading them. + mozilla::Atomic<hb_blob_t*> mCOLR; + mozilla::Atomic<hb_blob_t*> mCPAL; + hb_blob_t* GetCOLR() const { return mCOLR; } + hb_blob_t* GetCPAL() const { return mCPAL; } + + // bitvector of substitution space features per script, one each + // for default and non-default features + uint32_t mDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32]; + uint32_t + mNonDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32]; + + mozilla::Atomic<uint32_t> mUVSOffset; + + uint32_t mLanguageOverride = NO_FONT_LANGUAGE_OVERRIDE; + + WeightRange mWeightRange = WeightRange(FontWeight::FromInt(500)); + StretchRange mStretchRange = StretchRange(FontStretch::NORMAL); + SlantStyleRange mStyleRange = SlantStyleRange(FontSlantStyle::NORMAL); + + // Font metrics overrides (as multiples of used font size); negative values + // indicate no override to be applied. + float mAscentOverride = -1.0; + float mDescentOverride = -1.0; + float mLineGapOverride = -1.0; + + // Scaling factor to be applied to the font size. + float mSizeAdjust = 1.0; + + // For user fonts (only), we need to record whether or not weight/stretch/ + // slant variations should be clamped to the range specified in the entry + // properties. When the @font-face rule omitted one or more of these + // descriptors, it is treated as the initial value for font-matching (and + // so that is what we record in the font entry), but when rendering the + // range is NOT clamped. + enum class RangeFlags : uint16_t { + eNoFlags = 0, + eAutoWeight = (1 << 0), + eAutoStretch = (1 << 1), + eAutoSlantStyle = (1 << 2), + + // Flag to record whether the face has a variable "wght" axis + // that supports "bold" values, used to disable the application + // of synthetic-bold effects. + eBoldVariableWeight = (1 << 3), + // Whether the face has an 'ital' axis. + eItalicVariation = (1 << 4), + // Whether the face has a 'slnt' axis. + eSlantVariation = (1 << 5), + + // Flags to record if the face uses a non-CSS-compatible scale + // for weight and/or stretch, in which case we won't map the + // properties to the variation axes (though they can still be + // explicitly set using font-variation-settings). + eNonCSSWeight = (1 << 6), + eNonCSSStretch = (1 << 7), + + // Whether the font has an 'opsz' axis. + eOpticalSize = (1 << 8) + }; + RangeFlags mRangeFlags = RangeFlags::eNoFlags; + + bool mFixedPitch : 1; + bool mIsBadUnderlineFont : 1; + bool mIsUserFontContainer : 1; // userfont entry + bool mIsDataUserFont : 1; // platform font entry (data) + bool mIsLocalUserFont : 1; // platform font entry (local) + bool mStandardFace : 1; + bool mIgnoreGDEF : 1; + bool mIgnoreGSUB : 1; + bool mSkipDefaultFeatureSpaceCheck : 1; + + mozilla::Atomic<bool> mSVGInitialized; + mozilla::Atomic<bool> mHasCmapTable; + mozilla::Atomic<bool> mGrFaceInitialized; + mozilla::Atomic<bool> mCheckedForColorGlyph; + mozilla::Atomic<bool> mCheckedForVariationAxes; + + // Atomic flags that are lazily evaluated - initially set to UNINITIALIZED, + // changed to NO or YES once we determine the actual value. + enum class LazyFlag : uint8_t { Uninitialized = 0xff, No = 0, Yes = 1 }; + + std::atomic<LazyFlag> mSpaceGlyphIsInvisible; + std::atomic<LazyFlag> mHasGraphiteTables; + std::atomic<LazyFlag> mHasGraphiteSpaceContextuals; + std::atomic<LazyFlag> mHasColorBitmapTable; + + enum class SpaceFeatures : uint8_t { + Uninitialized = 0xff, + None = 0, + HasFeatures = 1 << 0, + Kerning = 1 << 1, + NonKerning = 1 << 2 + }; + + std::atomic<SpaceFeatures> mHasSpaceFeatures; + + protected: + friend class gfxPlatformFontList; + friend class gfxFontFamily; + friend class gfxUserFontEntry; + + // Protected destructor, to discourage deletion outside of Release(): + virtual ~gfxFontEntry(); + + virtual gfxFont* CreateFontInstance(const gfxFontStyle* aFontStyle) = 0; + + inline bool CheckForGraphiteTables() { + return HasFontTable(TRUETYPE_TAG('S', 'i', 'l', 'f')); + } + + // Copy a font table into aBuffer. + // The caller will be responsible for ownership of the data. + virtual nsresult CopyFontTable(uint32_t aTableTag, + nsTArray<uint8_t>& aBuffer) { + MOZ_ASSERT_UNREACHABLE( + "forgot to override either GetFontTable or " + "CopyFontTable?"); + return NS_ERROR_FAILURE; + } + + // Helper for HasTrackingTable; check/parse the table and cache pointers + // to the subtables we need. Returns false on failure, in which case the + // table is unusable. + bool ParseTrakTable() MOZ_REQUIRES(mLock); + + // lookup the cmap in cached font data + virtual already_AddRefed<gfxCharacterMap> GetCMAPFromFontInfo( + FontInfoData* aFontInfoData, uint32_t& aUVSOffset); + + // helper for HasCharacter(), which is what client code should call + virtual bool TestCharacterMap(uint32_t aCh); + + // Try to set mShmemCharacterMap, based on the char map in mShmemFace; + // return true if successful, false if it remains null (maybe the parent + // hasn't handled our SetCharacterMap message yet). + bool TrySetShmemCharacterMap(); + + // Helper for gfxPlatformFontList::CreateFontEntry methods: set properties + // of the gfxFontEntry based on shared Face and Family records. + void InitializeFrom(mozilla::fontlist::Face* aFace, + const mozilla::fontlist::Family* aFamily); + + // Shaper-specific face objects, shared by all instantiations of the same + // physical font, regardless of size. + // Usually, only one of these will actually be created for any given font + // entry, depending on the font tables that are present. + + // hb_face_t is refcounted internally, so each shaper that's using it will + // bump the ref count when it acquires the face, and "destroy" (release) it + // in its destructor. The font entry has only this non-owning reference to + // the face; when the face is deleted, it will tell the font entry to forget + // it, so that a new face will be created next time it is needed. + mozilla::Atomic<hb_face_t*> mHBFace; + + static hb_blob_t* HBGetTable(hb_face_t* face, uint32_t aTag, void* aUserData); + + // Callback that the hb_face will use to tell us when it is being deleted. + static void HBFaceDeletedCallback(void* aUserData); + + // All libGraphite functionality is sandboxed in an rlbox sandbox. This + // contains data for the sandbox instance. + // Currently graphite shaping is only supported on the main thread. + struct GrSandboxData; + GrSandboxData* mSandboxData = nullptr; + + // gr_face is -not- refcounted, so it will be owned directly by the font + // entry, and we'll keep a count of how many references we've handed out; + // each shaper is responsible to call ReleaseGrFace on its entry when + // finished with it, so that we know when it can be deleted. + tainted_opaque_gr<gr_face*> mGrFace; + + // For AAT font, a strong reference to the 'trak' table (if present). + hb_blob_t* const kTrakTableUninitialized = (hb_blob_t*)(intptr_t(-1)); + mozilla::Atomic<hb_blob_t*> mTrakTable; + hb_blob_t* GetTrakTable() const { return mTrakTable; } + bool TrakTableInitialized() const { + return mTrakTable != kTrakTableUninitialized; + } + + // Cached pointers to tables within 'trak', initialized by ParseTrakTable. + // This data is inert once loaded, so locking is not required to read it. + const mozilla::AutoSwap_PRInt16* mTrakValues = nullptr; + const mozilla::AutoSwap_PRInt32* mTrakSizeTable = nullptr; + + // number of current users of this entry's mGrFace + nsrefcnt mGrFaceRefCnt = 0; + + friend class gfxFontEntryCallbacks; + + // For memory reporting: size of user-font data belonging to this entry. + // We record this in the font entry because the actual data block may be + // handed over to platform APIs, so that it would become difficult (and + // platform-specific) to measure it directly at report-gathering time. + uint32_t mComputedSizeOfUserFont = 0; + + // Font's unitsPerEm from the 'head' table, if available (will be set to + // kInvalidUPEM for non-sfnt font formats) + uint16_t mUnitsPerEm = 0; + + uint16_t mNumTrakSizes = 0; + + // Font extents in FUnits. (To be set from the 'head' table; default to + // "huge" to avoid any clipping if real extents not available.) + int16_t mXMin = std::numeric_limits<int16_t>::min(); + int16_t mYMin = std::numeric_limits<int16_t>::min(); + int16_t mXMax = std::numeric_limits<int16_t>::max(); + int16_t mYMax = std::numeric_limits<int16_t>::max(); + + private: + /** + * Font table hashtable, to support GetFontTable for harfbuzz. + * + * The harfbuzz shaper (and potentially other clients) needs access to raw + * font table data. This needs to be cached so that it can be used + * repeatedly (each time we construct a text run; in some cases, for + * each character/glyph within the run) without re-fetching large tables + * every time. + * + * Because we may instantiate many gfxFonts for the same physical font + * file (at different sizes), we should ensure that they can share a + * single cached copy of the font tables. To do this, we implement table + * access and sharing on the fontEntry rather than the font itself. + * + * The default implementation uses GetFontTable() to read font table + * data into byte arrays, and wraps them in blobs which are registered in + * a hashtable. The hashtable can then return pre-existing blobs to + * harfbuzz. + * + * Harfbuzz will "destroy" the blobs when it is finished with them. When + * the last blob reference is removed, the FontTableBlobData user data + * will remove the blob from the hashtable if still registered. + */ + + class FontTableBlobData; + + /** + * FontTableHashEntry manages the entries of hb_blob_t's containing font + * table data. + * + * This is used to share font tables across fonts with the same + * font entry (but different sizes) for use by HarfBuzz. The hashtable + * does not own a strong reference to the blob, but keeps a weak pointer, + * managed by FontTableBlobData. Similarly FontTableBlobData keeps only a + * weak pointer to the hashtable, managed by FontTableHashEntry. + */ + + class FontTableHashEntry : public nsUint32HashKey { + public: + // Declarations for nsTHashtable + + typedef nsUint32HashKey KeyClass; + typedef KeyClass::KeyType KeyType; + typedef KeyClass::KeyTypePointer KeyTypePointer; + + explicit FontTableHashEntry(KeyTypePointer aTag) + : KeyClass(aTag), mSharedBlobData(nullptr), mBlob(nullptr) {} + + // NOTE: This assumes the new entry belongs to the same hashtable as + // the old, because the mHashtable pointer in mSharedBlobData (if + // present) will not be updated. + FontTableHashEntry(FontTableHashEntry&& toMove) + : KeyClass(std::move(toMove)), + mSharedBlobData(std::move(toMove.mSharedBlobData)), + mBlob(std::move(toMove.mBlob)) { + toMove.mSharedBlobData = nullptr; + toMove.mBlob = nullptr; + } + + ~FontTableHashEntry() { Clear(); } + + // FontTable/Blob API + + // Transfer (not copy) elements of aTable to a new hb_blob_t and + // return ownership to the caller. A weak reference to the blob is + // recorded in the hashtable entry so that others may use the same + // table. + hb_blob_t* ShareTableAndGetBlob( + nsTArray<uint8_t>&& aTable, + nsTHashtable<FontTableHashEntry>* aHashtable); + + // Return a strong reference to the blob. + // Callers must hb_blob_destroy the returned blob. + hb_blob_t* GetBlob() const; + + void Clear(); + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + + private: + static void DeleteFontTableBlobData(void* aBlobData); + // not implemented + FontTableHashEntry& operator=(FontTableHashEntry& toCopy); + + FontTableBlobData* mSharedBlobData; + hb_blob_t* mBlob; + }; + + using FontTableCache = nsTHashtable<FontTableHashEntry>; + mozilla::Atomic<FontTableCache*> mFontTableCache; + FontTableCache* GetFontTableCache() const { return mFontTableCache; } +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gfxFontEntry::RangeFlags) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gfxFontEntry::SpaceFeatures) + +inline bool gfxFontEntry::SupportsItalic() { + return SlantStyle().Max().IsItalic() || + ((mRangeFlags & RangeFlags::eAutoSlantStyle) == + RangeFlags::eAutoSlantStyle && + HasItalicVariation()); +} + +inline bool gfxFontEntry::SupportsBold() { + // bold == weights 600 and above + // We return true if the face has a max weight descriptor >= 600, + // OR if it's a user font with auto-weight (no descriptor) and has + // a weight axis that supports values >= 600 + return Weight().Max().IsBold() || + ((mRangeFlags & RangeFlags::eAutoWeight) == RangeFlags::eAutoWeight && + HasBoldVariableWeight()); +} + +inline bool gfxFontEntry::MayUseSyntheticSlant() { + if (!IsUpright()) { + return false; // The resource is already non-upright. + } + if (HasSlantVariation()) { + if (mRangeFlags & RangeFlags::eAutoSlantStyle) { + return false; + } + if (!SlantStyle().IsSingle()) { + return false; // The resource has a 'slnt' axis, and has not been + // clamped to just its upright setting. + } + } + return true; +} + +// used when iterating over all fonts looking for a match for a given character +struct GlobalFontMatch { + GlobalFontMatch(uint32_t aCharacter, uint32_t aNextCh, + const gfxFontStyle& aStyle, eFontPresentation aPresentation) + : mStyle(aStyle), + mCh(aCharacter), + mNextCh(aNextCh), + mPresentation(aPresentation) {} + + RefPtr<gfxFontEntry> mBestMatch; // current best match + RefPtr<gfxFontFamily> mMatchedFamily; // the family it belongs to + mozilla::fontlist::Family* mMatchedSharedFamily = nullptr; + const gfxFontStyle& mStyle; // style to match + const uint32_t mCh; // codepoint to be matched + const uint32_t mNextCh; // following codepoint (or zero) + eFontPresentation mPresentation; + uint32_t mCount = 0; // number of fonts matched + uint32_t mCmapsTested = 0; // number of cmaps tested + double mMatchDistance = INFINITY; // metric indicating closest match +}; + +class gfxFontFamily { + public: + // Used by stylo + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxFontFamily) + + gfxFontFamily(const nsACString& aName, FontVisibility aVisibility) + : mName(aName), + mLock("gfxFontFamily lock"), + mVisibility(aVisibility), + mIsSimpleFamily(false), + mIsBadUnderlineFamily(false), + mSkipDefaultFeatureSpaceCheck(false), + mCheckForFallbackFaces(false) {} + + const nsCString& Name() const { return mName; } + + virtual void LocalizedName(nsACString& aLocalizedName); + virtual bool HasOtherFamilyNames(); + + // See https://bugzilla.mozilla.org/show_bug.cgi?id=835204: + // check the font's 'name' table to see if it has a legacy family name + // that would have been used by GDI (e.g. to split extra-bold or light + // faces in a large family into separate "styled families" because of + // GDI's 4-faces-per-family limitation). If found, the styled family + // name will be added to the font list's "other family names" table. + // Note that the caller must already hold the gfxPlatformFontList lock. + bool CheckForLegacyFamilyNames(gfxPlatformFontList* aFontList); + + // Callers must hold a read-lock for as long as they're using the list. + const nsTArray<RefPtr<gfxFontEntry>>& GetFontList() + MOZ_REQUIRES_SHARED(mLock) { + return mAvailableFonts; + } + void ReadLock() MOZ_ACQUIRE_SHARED(mLock) { mLock.ReadLock(); } + void ReadUnlock() MOZ_RELEASE_SHARED(mLock) { mLock.ReadUnlock(); } + + uint32_t FontListLength() const { + mozilla::AutoReadLock lock(mLock); + return mAvailableFonts.Length(); + } + + void AddFontEntry(RefPtr<gfxFontEntry> aFontEntry) { + mozilla::AutoWriteLock lock(mLock); + AddFontEntryLocked(aFontEntry); + } + + void AddFontEntryLocked(RefPtr<gfxFontEntry> aFontEntry) MOZ_REQUIRES(mLock) { + // Avoid potentially duplicating entries. + if (mAvailableFonts.Contains(aFontEntry)) { + return; + } + // bug 589682 - set the IgnoreGDEF flag on entries for Italic faces + // of Times New Roman, because of buggy table in those fonts + if (aFontEntry->IsItalic() && !aFontEntry->IsUserFont() && + Name().EqualsLiteral("Times New Roman")) { + aFontEntry->mIgnoreGDEF = true; + } + if (aFontEntry->mFamilyName.IsEmpty()) { + aFontEntry->mFamilyName = Name(); + } else { + MOZ_ASSERT(aFontEntry->mFamilyName.Equals(Name())); + } + aFontEntry->mSkipDefaultFeatureSpaceCheck = mSkipDefaultFeatureSpaceCheck; + mAvailableFonts.AppendElement(aFontEntry); + + // If we're adding a face to a family that has been marked as "simple", + // we need to ensure any null entries are removed, as well as clearing + // the flag (which may be set again later). + if (mIsSimpleFamily) { + mAvailableFonts.RemoveElementsBy([](const auto& font) { return !font; }); + mIsSimpleFamily = false; + } + } + + // note that the styles for this family have been added + bool HasStyles() const { return mHasStyles; } + void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; } + + void SetCheckedForLegacyFamilyNames(bool aChecked) { + mCheckedForLegacyFamilyNames = aChecked; + } + + // choose a specific face to match a style using CSS font matching + // rules (weight matching occurs here). may return a face that doesn't + // precisely match (e.g. normal face when no italic face exists). + gfxFontEntry* FindFontForStyle(const gfxFontStyle& aFontStyle, + bool aIgnoreSizeTolerance = false); + + virtual void FindAllFontsForStyle(const gfxFontStyle& aFontStyle, + nsTArray<gfxFontEntry*>& aFontEntryList, + bool aIgnoreSizeTolerance = false); + + // Checks for a matching font within the family; used as part of the font + // fallback process. + // Note that when this is called, the caller must already be holding the + // gfxPlatformFontList lock. + void FindFontForChar(GlobalFontMatch* aMatchData); + + // checks all fonts for a matching font within the family + void SearchAllFontsForChar(GlobalFontMatch* aMatchData); + + // read in other family names, if any, and use functor to add each into cache + virtual void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList); + + // set when other family names have been read in + void SetOtherFamilyNamesInitialized() { mOtherFamilyNamesInitialized = true; } + + // Read in other localized family names, fullnames and Postscript names + // for all faces and append to lookup tables. + // Note that when this is called, the caller must already be holding the + // gfxPlatformFontList lock. + virtual void ReadFaceNames(gfxPlatformFontList* aPlatformFontList, + bool aNeedFullnamePostscriptNames, + FontInfoData* aFontInfoData = nullptr); + + // Find faces belonging to this family (platform implementations override). + // This is a no-op in cases where the family is explicitly populated by other + // means, rather than being asked to find its faces via system API. + virtual void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr) + MOZ_REQUIRES(mLock){}; + void FindStyleVariations(FontInfoData* aFontInfoData = nullptr) { + if (mHasStyles) { + return; + } + mozilla::AutoWriteLock lock(mLock); + FindStyleVariationsLocked(aFontInfoData); + } + + // search for a specific face using the Postscript name + gfxFontEntry* FindFont(const nsACString& aFontName, + const nsCStringComparator& aCmp) const; + + // Read in cmaps for all the faces. + // Note that when this is called, the caller must already be holding the + // gfxPlatformFontList lock. + void ReadAllCMAPs(FontInfoData* aFontInfoData = nullptr); + + bool TestCharacterMap(uint32_t aCh) { + if (!mFamilyCharacterMapInitialized) { + ReadAllCMAPs(); + } + mozilla::AutoReadLock lock(mLock); + return mFamilyCharacterMap.test(aCh); + } + + void ResetCharacterMap() MOZ_REQUIRES(mLock) { + mFamilyCharacterMap.reset(); + mFamilyCharacterMapInitialized = false; + } + + // mark this family as being in the "bad" underline offset blocklist + void SetBadUnderlineFamily() { + mozilla::AutoWriteLock lock(mLock); + mIsBadUnderlineFamily = true; + if (mHasStyles) { + SetBadUnderlineFonts(); + } + } + + virtual bool IsSingleFaceFamily() const { return false; } + + bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; } + bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; } + + // sort available fonts to put preferred (standard) faces towards the end + void SortAvailableFonts() MOZ_REQUIRES(mLock); + + // check whether the family fits into the simple 4-face model, + // so we can use simplified style-matching; + // if so set the mIsSimpleFamily flag (defaults to False before we've checked) + void CheckForSimpleFamily() MOZ_REQUIRES(mLock); + + // For memory reporter + virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const; + virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const; + +#ifdef DEBUG + // Only used for debugging checks - does a linear search + bool ContainsFace(gfxFontEntry* aFontEntry); +#endif + + void SetSkipSpaceFeatureCheck(bool aSkipCheck) { + mSkipDefaultFeatureSpaceCheck = aSkipCheck; + } + + // Check whether this family is appropriate to include in the Preferences + // font list for the given langGroup and CSS generic, if the platform lets + // us determine this. + // Return true if the family should be included in the list, false to omit. + // Default implementation returns true for everything, so no filtering + // will occur; individual platforms may override. + virtual bool FilterForFontList(nsAtom* aLangGroup, + const nsACString& aGeneric) const { + return true; + } + + FontVisibility Visibility() const { return mVisibility; } + bool IsHidden() const { return Visibility() == FontVisibility::Hidden; } + bool IsWebFontFamily() const { + return Visibility() == FontVisibility::Webfont; + } + + protected: + // Protected destructor, to discourage deletion outside of Release(): + virtual ~gfxFontFamily(); + + bool ReadOtherFamilyNamesForFace(gfxPlatformFontList* aPlatformFontList, + hb_blob_t* aNameTable, + bool useFullName = false); + + // set whether this font family is in "bad" underline offset blocklist. + void SetBadUnderlineFonts() MOZ_REQUIRES(mLock) { + for (auto& f : mAvailableFonts) { + if (f) { + f->mIsBadUnderlineFont = true; + } + } + } + + nsCString mName; + nsTArray<RefPtr<gfxFontEntry>> mAvailableFonts MOZ_GUARDED_BY(mLock); + gfxSparseBitSet mFamilyCharacterMap MOZ_GUARDED_BY(mLock); + + mutable mozilla::RWLock mLock; + + FontVisibility mVisibility; + + mozilla::Atomic<bool> mOtherFamilyNamesInitialized; + mozilla::Atomic<bool> mFaceNamesInitialized; + mozilla::Atomic<bool> mHasStyles; + mozilla::Atomic<bool> mFamilyCharacterMapInitialized; + mozilla::Atomic<bool> mCheckedForLegacyFamilyNames; + mozilla::Atomic<bool> mHasOtherFamilyNames; + + bool mIsSimpleFamily : 1 MOZ_GUARDED_BY(mLock); + bool mIsBadUnderlineFamily : 1; + bool mSkipDefaultFeatureSpaceCheck : 1; + bool mCheckForFallbackFaces : 1; // check other faces for character + + enum { + // for "simple" families, the faces are stored in mAvailableFonts + // with fixed positions: + kRegularFaceIndex = 0, + kBoldFaceIndex = 1, + kItalicFaceIndex = 2, + kBoldItalicFaceIndex = 3, + // mask values for selecting face with bold and/or italic attributes + kBoldMask = 0x01, + kItalicMask = 0x02 + }; +}; + +// Wrapper for either a raw pointer to a mozilla::fontlist::Family in the shared +// font list or a strong pointer to an unshared gfxFontFamily that belongs just +// to the current process. +struct FontFamily { + FontFamily() = default; + FontFamily(const FontFamily& aOther) = default; + + explicit FontFamily(RefPtr<gfxFontFamily>&& aFamily) + : mUnshared(std::move(aFamily)) {} + + explicit FontFamily(gfxFontFamily* aFamily) : mUnshared(aFamily) {} + + explicit FontFamily(mozilla::fontlist::Family* aFamily) : mShared(aFamily) {} + + bool operator==(const FontFamily& aOther) const { + return mShared == aOther.mShared && mUnshared == aOther.mUnshared; + } + + bool IsNull() const { return !mShared && !mUnshared; } + + RefPtr<gfxFontFamily> mUnshared; + mozilla::fontlist::Family* mShared = nullptr; +}; + +// Struct used in the gfxFontGroup font list to keep track of a font family +// together with the CSS generic (if any) that was mapped to it in this +// particular case (so it can be reported to the DevTools font inspector). +struct FamilyAndGeneric final { + FamilyAndGeneric() + : mFamily(), mGeneric(mozilla::StyleGenericFontFamily(0)) {} + FamilyAndGeneric(const FamilyAndGeneric& aOther) = default; + explicit FamilyAndGeneric(gfxFontFamily* aFamily, + mozilla::StyleGenericFontFamily aGeneric = + mozilla::StyleGenericFontFamily(0)) + : mFamily(aFamily), mGeneric(aGeneric) {} + explicit FamilyAndGeneric(RefPtr<gfxFontFamily>&& aFamily, + mozilla::StyleGenericFontFamily aGeneric = + mozilla::StyleGenericFontFamily(0)) + : mFamily(std::move(aFamily)), mGeneric(aGeneric) {} + explicit FamilyAndGeneric(mozilla::fontlist::Family* aFamily, + mozilla::StyleGenericFontFamily aGeneric = + mozilla::StyleGenericFontFamily(0)) + : mFamily(aFamily), mGeneric(aGeneric) {} + explicit FamilyAndGeneric(const FontFamily& aFamily, + mozilla::StyleGenericFontFamily aGeneric = + mozilla::StyleGenericFontFamily(0)) + : mFamily(aFamily), mGeneric(aGeneric) {} + + bool operator==(const FamilyAndGeneric& aOther) const { + return mFamily == aOther.mFamily && mGeneric == aOther.mGeneric; + } + + FontFamily mFamily; + mozilla::StyleGenericFontFamily mGeneric; +}; + +#endif |