/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 mozilla_dom_FontFaceSet_h #define mozilla_dom_FontFaceSet_h #include "mozilla/dom/FontFace.h" #include "mozilla/dom/FontFaceSetBinding.h" #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/FontPropertyTypes.h" #include "gfxUserFontSet.h" #include "nsICSSLoaderObserver.h" #include "nsIDOMEventListener.h" struct gfxFontFaceSrc; class gfxFontSrcPrincipal; class gfxUserFontEntry; class nsFontFaceLoader; class nsIPrincipal; class nsPIDOMWindowInner; struct RawServoFontFaceRule; namespace mozilla { class PostTraversalTask; class SharedFontList; namespace dom { class FontFace; class Promise; } // namespace dom } // namespace mozilla namespace mozilla { namespace dom { class FontFaceSet final : public DOMEventTargetHelper, public nsIDOMEventListener, public nsICSSLoaderObserver { friend class mozilla::PostTraversalTask; friend class UserFontSet; public: /** * A gfxUserFontSet that integrates with the layout and style systems to * manage @font-face rules and handle network requests for font loading. * * We would combine this class and FontFaceSet into the one class if it were * possible; it's not because FontFaceSet is cycle collected and * gfxUserFontSet isn't (and can't be, as gfx classes don't use the cycle * collector). So UserFontSet exists just to override the needed virtual * methods from gfxUserFontSet and to forward them on FontFaceSet. */ class UserFontSet final : public gfxUserFontSet { friend class FontFaceSet; public: explicit UserFontSet(FontFaceSet* aFontFaceSet) : mFontFaceSet(aFontFaceSet) {} FontFaceSet* GetFontFaceSet() { return mFontFaceSet; } gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() const final { return mFontFaceSet ? mFontFaceSet->mStandardFontLoadPrincipal.get() : nullptr; } nsPresContext* GetPresContext() const final { return mFontFaceSet ? mFontFaceSet->GetPresContext() : nullptr; } bool IsFontLoadAllowed(const gfxFontFaceSrc&) final; void DispatchFontLoadViolations( nsTArray>& aViolations) override; virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, uint32_t aSrcIndex) override; void RecordFontLoadDone(uint32_t aFontSize, TimeStamp aDoneTime) override; bool BypassCache() final { return mFontFaceSet && mFontFaceSet->mBypassCache; } protected: virtual bool GetPrivateBrowsing() override; virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, const gfxFontFaceSrc* aFontFaceSrc, uint8_t*& aBuffer, uint32_t& aBufferLength) override; virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, uint32_t aSrcIndex, const char* aMessage, uint32_t aFlags = nsIScriptError::errorFlag, nsresult aStatus = NS_OK) override; virtual void DoRebuildUserFontSet() override; already_AddRefed CreateUserFontEntry( const nsTArray& aFontFaceSrcList, WeightRange aWeight, StretchRange aStretch, SlantStyleRange aStyle, const nsTArray& aFeatureSettings, const nsTArray& aVariationSettings, uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges, StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags, float aAscentOverride, float aDescentOverride, float aLineGapOverride, float aSizeAdjust) override; private: RefPtr mFontFaceSet; }; NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FontFaceSet, DOMEventTargetHelper) NS_DECL_NSIDOMEVENTLISTENER FontFaceSet(nsPIDOMWindowInner* aWindow, dom::Document* aDocument); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; UserFontSet* GetUserFontSet() { return mUserFontSet; } // Called by nsFontFaceLoader when the loader has completed normally. // It's removed from the mLoaders set. void RemoveLoader(nsFontFaceLoader* aLoader); bool UpdateRules(const nsTArray& aRules); nsPresContext* GetPresContext(); // search for @font-face rule that matches a platform font entry RawServoFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry); void IncrementGeneration(bool aIsRebuild = false); /** * Finds an existing entry in the user font cache or creates a new user * font entry for the given FontFace object. */ static already_AddRefed FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace); /** * Notification method called by a FontFace to indicate that its loading * status has changed. */ void OnFontFaceStatusChanged(FontFace* aFontFace); /** * Notification method called by the nsPresContext to indicate that the * refresh driver ticked and flushed style and layout. * were just flushed. */ void DidRefresh(); /** * Returns whether the "layout.css.font-loading-api.enabled" pref is true. */ static bool PrefEnabled(); // nsICSSLoaderObserver NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred, nsresult aStatus) override; void FlushUserFontSet(); static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet) { FontFaceSet* set = static_cast(aUserFontSet)->mFontFaceSet; return set ? set->GetPresContext() : nullptr; } void RefreshStandardFontLoadPrincipal(); void CopyNonRuleFacesTo(FontFaceSet* aFontFaceSet) const; dom::Document* Document() const { return mDocument; } // -- Web IDL -------------------------------------------------------------- IMPL_EVENT_HANDLER(loading) IMPL_EVENT_HANDLER(loadingdone) IMPL_EVENT_HANDLER(loadingerror) already_AddRefed Load(JSContext* aCx, const nsACString& aFont, const nsAString& aText, ErrorResult& aRv); bool Check(const nsACString& aFont, const nsAString& aText, ErrorResult& aRv); dom::Promise* GetReady(ErrorResult& aRv); dom::FontFaceSetLoadStatus Status(); void Add(FontFace& aFontFace, ErrorResult& aRv); void Clear(); bool Delete(FontFace& aFontFace); bool Has(FontFace& aFontFace); /** * This returns the number of Author origin fonts only. * (see also SizeIncludingNonAuthorOrigins() below) */ uint32_t Size(); already_AddRefed Entries(); already_AddRefed Values(); MOZ_CAN_RUN_SCRIPT void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback, JS::Handle aThisArg, ErrorResult& aRv); // For ServoStyleSet to know ahead of time whether a font is loadable. void CacheFontLoadability(); void MarkUserFontSetDirty(); /** * Unlike Size(), this returns the size including non-Author origin fonts. */ uint32_t SizeIncludingNonAuthorOrigins(); private: friend mozilla::dom::FontFaceSetIterator; // needs GetFontFaceAt() ~FontFaceSet(); /** * Returns whether the given FontFace is currently "in" the FontFaceSet. */ bool HasAvailableFontFace(FontFace* aFontFace); /** * Removes any listeners and observers. */ void Disconnect(); void RemoveDOMContentLoadedListener(); /** * Returns whether there might be any pending font loads, which should cause * the mReady Promise not to be resolved yet. */ bool MightHavePendingFontLoads(); /** * Checks to see whether it is time to replace mReady and dispatch a * "loading" event. */ void CheckLoadingStarted(); /** * Checks to see whether it is time to resolve mReady and dispatch any * "loadingdone" and "loadingerror" events. */ void CheckLoadingFinished(); /** * Callback for invoking CheckLoadingFinished after going through the * event loop. See OnFontFaceStatusChanged. */ void CheckLoadingFinishedAfterDelay(); /** * Returns the font at aIndex if it's an Author origin font, or nullptr * otherwise. */ FontFace* GetFontFaceAt(uint32_t aIndex); /** * Dispatches a FontFaceSetLoadEvent to this object. */ void DispatchLoadingFinishedEvent( const nsAString& aType, nsTArray>&& aFontFaces); // Note: if you add new cycle collected objects to FontFaceRecord, // make sure to update FontFaceSet's cycle collection macros // accordingly. struct FontFaceRecord { RefPtr mFontFace; Maybe mOrigin; // only relevant for mRuleFaces entries // When true, indicates that when finished loading, the FontFace should be // included in the subsequent loadingdone/loadingerror event fired at the // FontFaceSet. bool mLoadEventShouldFire; }; static already_AddRefed FindOrCreateUserFontEntryFromFontFace(const nsACString& aFamilyName, FontFace* aFontFace, StyleOrigin); // search for @font-face rule that matches a userfont font entry RawServoFontFaceRule* FindRuleForUserFontEntry( gfxUserFontEntry* aUserFontEntry); nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, uint32_t aSrcIndex); gfxFontSrcPrincipal* GetStandardFontLoadPrincipal(); nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, gfxFontSrcPrincipal** aPrincipal, bool* aBypassCache); bool IsFontLoadAllowed(const gfxFontFaceSrc& aSrc); void DispatchFontLoadViolations(nsTArray>& aViolations); nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, const gfxFontFaceSrc* aFontFaceSrc, uint8_t*& aBuffer, uint32_t& aBufferLength); nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, uint32_t aSrcIndex, const char* aMessage, uint32_t aFlags, nsresult aStatus); void InsertRuleFontFace(FontFace* aFontFace, StyleOrigin aOrigin, nsTArray& aOldRecords, bool& aFontSetModified); void InsertNonRuleFontFace(FontFace* aFontFace, bool& aFontSetModified); #ifdef DEBUG bool HasRuleFontFace(FontFace* aFontFace); #endif /** * Returns whether we have any loading FontFace objects in the FontFaceSet. */ bool HasLoadingFontFaces(); // Whether mReady is pending, or would be when created. bool ReadyPromiseIsPending() const; // Helper function for HasLoadingFontFaces. void UpdateHasLoadingFontFaces(); void ParseFontShorthandForMatching(const nsACString& aFont, StyleFontFamilyList& aFamilyList, FontWeight& aWeight, FontStretch& aStretch, FontSlantStyle& aStyle, ErrorResult& aRv); void FindMatchingFontFaces(const nsACString& aFont, const nsAString& aText, nsTArray& aFontFaces, ErrorResult& aRv); void DispatchLoadingEventAndReplaceReadyPromise(); void DispatchCheckLoadingFinishedAfterDelay(); TimeStamp GetNavigationStartTimeStamp(); RefPtr mUserFontSet; // The document this is a FontFaceSet for. RefPtr mDocument; // The document's node principal, which is the principal font loads for // this FontFaceSet will generally use. (This principal is not used for // @font-face rules in UA and user sheets, where the principal of the // sheet is used instead.) // // This field is used from GetStandardFontLoadPrincipal. When on a // style worker thread, we use mStandardFontLoadPrincipal assuming // it is up to date. // // Because mDocument's principal can change over time, // its value must be updated by a call to ResetStandardFontLoadPrincipal. RefPtr mStandardFontLoadPrincipal; // A Promise that is fulfilled once all of the FontFace objects // in mRuleFaces and mNonRuleFaces that started or were loading at the // time the Promise was created have finished loading. It is rejected if // any of those fonts failed to load. mReady is replaced with // a new Promise object whenever mReady is settled and another // FontFace in mRuleFaces or mNonRuleFaces starts to load. // Note that mReady is created lazily when GetReady() is called. RefPtr mReady; // Whether the ready promise must be resolved when it's created. bool mResolveLazilyCreatedReadyPromise; // Set of all loaders pointing to us. These are not strong pointers, // but that's OK because nsFontFaceLoader always calls RemoveLoader on // us before it dies (unless we die first). nsTHashtable> mLoaders; // The @font-face rule backed FontFace objects in the FontFaceSet. nsTArray mRuleFaces; // The non rule backed FontFace objects that have been added to this // FontFaceSet. nsTArray mNonRuleFaces; // The overall status of the loading or loaded fonts in the FontFaceSet. dom::FontFaceSetLoadStatus mStatus; // A map from gfxFontFaceSrc pointer identity to whether the load is allowed // by CSP or other checks. We store this here because querying CSP off the // main thread is not a great idea. // // We could use just the pointer and use this as a hash set, but then we'd // have no way to verify that we've checked all the loads we should. nsTHashMap, bool> mAllowedFontLoads; // Whether mNonRuleFaces has changed since last time UpdateRules ran. bool mNonRuleFacesDirty; // Whether any FontFace objects in mRuleFaces or mNonRuleFaces are // loading. Only valid when mHasLoadingFontFacesIsDirty is false. Don't use // this variable directly; call the HasLoadingFontFaces method instead. bool mHasLoadingFontFaces; // This variable is only valid when mLoadingDirty is false. bool mHasLoadingFontFacesIsDirty; // Whether CheckLoadingFinished calls should be ignored. See comment in // OnFontFaceStatusChanged. bool mDelayedLoadCheck; // Whether the docshell for our document indicated that loads should // bypass the cache. bool mBypassCache; // Whether the docshell for our document indicates that we are in private // browsing mode. bool mPrivateBrowsing; }; } // namespace dom } // namespace mozilla #endif // !defined(mozilla_dom_FontFaceSet_h)