diff options
Diffstat (limited to 'layout/style/FontFaceSet.h')
-rw-r--r-- | layout/style/FontFaceSet.h | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h new file mode 100644 index 0000000000..1b4d6ef044 --- /dev/null +++ b/layout/style/FontFaceSet.h @@ -0,0 +1,392 @@ +/* -*- 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; + } + + bool IsFontLoadAllowed(const gfxFontFaceSrc&) final; + + void DispatchFontLoadViolations( + nsTArray<nsCOMPtr<nsIRunnable>>& aViolations) override; + + virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, + const gfxFontFaceSrc* aFontFaceSrc) 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, + const char* aMessage, + uint32_t aFlags = nsIScriptError::errorFlag, + nsresult aStatus = NS_OK) override; + virtual void DoRebuildUserFontSet() override; + already_AddRefed<gfxUserFontEntry> CreateUserFontEntry( + const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, WeightRange aWeight, + StretchRange aStretch, SlantStyleRange aStyle, + const nsTArray<gfxFontFeature>& aFeatureSettings, + const nsTArray<gfxFontVariation>& aVariationSettings, + uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges, + StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags) override; + + private: + RefPtr<FontFaceSet> 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<JSObject*> 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<nsFontFaceRuleContainer>& 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<gfxUserFontEntry> + 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; + + FontFace* GetFontFaceAt(uint32_t aIndex); + + void FlushUserFontSet(); + + static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet) { + FontFaceSet* set = static_cast<UserFontSet*>(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<dom::Promise> 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); + uint32_t Size(); + already_AddRefed<dom::FontFaceSetIterator> Entries(); + already_AddRefed<dom::FontFaceSetIterator> Values(); + MOZ_CAN_RUN_SCRIPT + void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback, + JS::Handle<JS::Value> aThisArg, ErrorResult& aRv); + + // For ServoStyleSet to know ahead of time whether a font is loadable. + void CacheFontLoadability(); + + void MarkUserFontSetDirty(); + + private: + ~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(); + + /** + * Dispatches a FontFaceSetLoadEvent to this object. + */ + void DispatchLoadingFinishedEvent( + const nsAString& aType, nsTArray<OwningNonNull<FontFace>>&& aFontFaces); + + // Note: if you add new cycle collected objects to FontFaceRecord, + // make sure to update FontFaceSet's cycle collection macros + // accordingly. + struct FontFaceRecord { + RefPtr<FontFace> mFontFace; + Maybe<StyleOrigin> 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<gfxUserFontEntry> + 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, + const gfxFontFaceSrc* aFontFaceSrc); + gfxFontSrcPrincipal* GetStandardFontLoadPrincipal(); + nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, + gfxFontSrcPrincipal** aPrincipal, bool* aBypassCache); + bool IsFontLoadAllowed(const gfxFontFaceSrc& aSrc); + + void DispatchFontLoadViolations(nsTArray<nsCOMPtr<nsIRunnable>>& aViolations); + nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc, + uint8_t*& aBuffer, uint32_t& aBufferLength); + nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, const char* aMessage, + uint32_t aFlags, nsresult aStatus); + + void InsertRuleFontFace(FontFace* aFontFace, StyleOrigin aOrigin, + nsTArray<FontFaceRecord>& 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, + RefPtr<SharedFontList>& aFamilyList, + FontWeight& aWeight, FontStretch& aStretch, + FontSlantStyle& aStyle, ErrorResult& aRv); + void FindMatchingFontFaces(const nsACString& aFont, const nsAString& aText, + nsTArray<FontFace*>& aFontFaces, ErrorResult& aRv); + + void DispatchLoadingEventAndReplaceReadyPromise(); + void DispatchCheckLoadingFinishedAfterDelay(); + + TimeStamp GetNavigationStartTimeStamp(); + + RefPtr<UserFontSet> mUserFontSet; + + // The document this is a FontFaceSet for. + RefPtr<dom::Document> 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<gfxFontSrcPrincipal> 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<dom::Promise> 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<nsPtrHashKey<nsFontFaceLoader>> mLoaders; + + // The @font-face rule backed FontFace objects in the FontFaceSet. + nsTArray<FontFaceRecord> mRuleFaces; + + // The non rule backed FontFace objects that have been added to this + // FontFaceSet. + nsTArray<FontFaceRecord> 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. + nsDataHashtable<nsPtrHashKey<const gfxFontFaceSrc>, 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) |