Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #ifndef mozilla_dom_FontFaceSet_h
7 : #define mozilla_dom_FontFaceSet_h
8 :
9 : #include "mozilla/dom/FontFace.h"
10 : #include "mozilla/dom/FontFaceSetBinding.h"
11 : #include "mozilla/DOMEventTargetHelper.h"
12 : #include "gfxUserFontSet.h"
13 : #include "nsCSSFontFaceRule.h"
14 : #include "nsICSSLoaderObserver.h"
15 :
16 : struct gfxFontFaceSrc;
17 : class gfxFontSrcPrincipal;
18 : class gfxUserFontEntry;
19 : class nsFontFaceLoader;
20 : class nsIPrincipal;
21 : class nsPIDOMWindowInner;
22 :
23 : namespace mozilla {
24 : class PostTraversalTask;
25 : namespace css {
26 : class FontFamilyListRefCnt;
27 : } // namespace css
28 : namespace dom {
29 : class FontFace;
30 : class Promise;
31 : } // namespace dom
32 : } // namespace mozilla
33 :
34 : namespace mozilla {
35 : namespace dom {
36 :
37 : class FontFaceSet final : public DOMEventTargetHelper
38 : , public nsIDOMEventListener
39 : , public nsICSSLoaderObserver
40 : {
41 : friend class mozilla::PostTraversalTask;
42 : friend class UserFontSet;
43 :
44 : public:
45 : /**
46 : * A gfxUserFontSet that integrates with the layout and style systems to
47 : * manage @font-face rules and handle network requests for font loading.
48 : *
49 : * We would combine this class and FontFaceSet into the one class if it were
50 : * possible; it's not because FontFaceSet is cycle collected and
51 : * gfxUserFontSet isn't (and can't be, as gfx classes don't use the cycle
52 : * collector). So UserFontSet exists just to override the needed virtual
53 : * methods from gfxUserFontSet and to forward them on FontFaceSet.
54 : */
55 0 : class UserFontSet final : public gfxUserFontSet
56 : {
57 : friend class FontFaceSet;
58 :
59 : public:
60 0 : explicit UserFontSet(FontFaceSet* aFontFaceSet)
61 0 : : mFontFaceSet(aFontFaceSet)
62 : {
63 0 : }
64 :
65 0 : FontFaceSet* GetFontFaceSet() { return mFontFaceSet; }
66 :
67 : gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() override;
68 :
69 : virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
70 : gfxFontSrcPrincipal** aPrincipal,
71 : bool* aBypassCache) override;
72 :
73 : virtual bool IsFontLoadAllowed(nsIURI* aFontLocation,
74 : nsIPrincipal* aPrincipal) override;
75 :
76 : virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
77 : const gfxFontFaceSrc* aFontFaceSrc) override;
78 :
79 : void RecordFontLoadDone(uint32_t aFontSize,
80 : mozilla::TimeStamp aDoneTime) override;
81 :
82 : protected:
83 : virtual bool GetPrivateBrowsing() override;
84 : virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
85 : const gfxFontFaceSrc* aFontFaceSrc,
86 : uint8_t*& aBuffer,
87 : uint32_t& aBufferLength) override;
88 : virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
89 : const char* aMessage,
90 : uint32_t aFlags = nsIScriptError::errorFlag,
91 : nsresult aStatus = NS_OK) override;
92 : virtual void DoRebuildUserFontSet() override;
93 : virtual already_AddRefed<gfxUserFontEntry> CreateUserFontEntry(
94 : const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
95 : uint32_t aWeight,
96 : int32_t aStretch,
97 : uint8_t aStyle,
98 : const nsTArray<gfxFontFeature>& aFeatureSettings,
99 : uint32_t aLanguageOverride,
100 : gfxCharacterMap* aUnicodeRanges,
101 : uint8_t aFontDisplay) override;
102 :
103 : private:
104 : RefPtr<FontFaceSet> mFontFaceSet;
105 : };
106 :
107 : NS_DECL_ISUPPORTS_INHERITED
108 0 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FontFaceSet, DOMEventTargetHelper)
109 : NS_DECL_NSIDOMEVENTLISTENER
110 :
111 : FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument);
112 :
113 : virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
114 :
115 0 : UserFontSet* GetUserFontSet() { return mUserFontSet; }
116 :
117 : // Called by nsFontFaceLoader when the loader has completed normally.
118 : // It's removed from the mLoaders set.
119 : void RemoveLoader(nsFontFaceLoader* aLoader);
120 :
121 : bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
122 :
123 : nsPresContext* GetPresContext();
124 :
125 : // search for @font-face rule that matches a platform font entry
126 : nsCSSFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
127 :
128 : void IncrementGeneration(bool aIsRebuild = false);
129 :
130 : /**
131 : * Finds an existing entry in the user font cache or creates a new user
132 : * font entry for the given FontFace object.
133 : */
134 : already_AddRefed<gfxUserFontEntry>
135 : FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace);
136 :
137 : /**
138 : * Notification method called by a FontFace to indicate that its loading
139 : * status has changed.
140 : */
141 : void OnFontFaceStatusChanged(FontFace* aFontFace);
142 :
143 : /**
144 : * Notification method called by the nsPresContext to indicate that the
145 : * refresh driver ticked and flushed style and layout.
146 : * were just flushed.
147 : */
148 : void DidRefresh();
149 :
150 : /**
151 : * Returns whether the "layout.css.font-loading-api.enabled" pref is true.
152 : */
153 : static bool PrefEnabled();
154 :
155 : // nsICSSLoaderObserver
156 : NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet,
157 : bool aWasAlternate,
158 : nsresult aStatus) override;
159 :
160 : FontFace* GetFontFaceAt(uint32_t aIndex);
161 :
162 : void FlushUserFontSet();
163 :
164 0 : static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet)
165 : {
166 0 : FontFaceSet* set = static_cast<UserFontSet*>(aUserFontSet)->mFontFaceSet;
167 0 : return set ? set->GetPresContext() : nullptr;
168 : }
169 :
170 : void UpdateStandardFontLoadPrincipal();
171 :
172 0 : bool HasStandardFontLoadPrincipalChanged()
173 : {
174 0 : bool changed = mHasStandardFontLoadPrincipalChanged;
175 0 : mHasStandardFontLoadPrincipalChanged = false;
176 0 : return changed;
177 : }
178 :
179 0 : nsIDocument* Document() const { return mDocument; }
180 :
181 : // -- Web IDL --------------------------------------------------------------
182 :
183 0 : IMPL_EVENT_HANDLER(loading)
184 0 : IMPL_EVENT_HANDLER(loadingdone)
185 0 : IMPL_EVENT_HANDLER(loadingerror)
186 : already_AddRefed<mozilla::dom::Promise> Load(JSContext* aCx,
187 : const nsAString& aFont,
188 : const nsAString& aText,
189 : mozilla::ErrorResult& aRv);
190 : bool Check(const nsAString& aFont,
191 : const nsAString& aText,
192 : mozilla::ErrorResult& aRv);
193 : mozilla::dom::Promise* GetReady(mozilla::ErrorResult& aRv);
194 : mozilla::dom::FontFaceSetLoadStatus Status();
195 :
196 : FontFaceSet* Add(FontFace& aFontFace, mozilla::ErrorResult& aRv);
197 : void Clear();
198 : bool Delete(FontFace& aFontFace);
199 : bool Has(FontFace& aFontFace);
200 : uint32_t Size();
201 : already_AddRefed<mozilla::dom::FontFaceSetIterator> Entries();
202 : already_AddRefed<mozilla::dom::FontFaceSetIterator> Values();
203 : void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback,
204 : JS::Handle<JS::Value> aThisArg,
205 : mozilla::ErrorResult& aRv);
206 :
207 : private:
208 : ~FontFaceSet();
209 :
210 : /**
211 : * Returns whether the given FontFace is currently "in" the FontFaceSet.
212 : */
213 : bool HasAvailableFontFace(FontFace* aFontFace);
214 :
215 : /**
216 : * Removes any listeners and observers.
217 : */
218 : void Disconnect();
219 :
220 : void RemoveDOMContentLoadedListener();
221 :
222 : /**
223 : * Returns whether there might be any pending font loads, which should cause
224 : * the mReady Promise not to be resolved yet.
225 : */
226 : bool MightHavePendingFontLoads();
227 :
228 : /**
229 : * Checks to see whether it is time to replace mReady and dispatch a
230 : * "loading" event.
231 : */
232 : void CheckLoadingStarted();
233 :
234 : /**
235 : * Checks to see whether it is time to resolve mReady and dispatch any
236 : * "loadingdone" and "loadingerror" events.
237 : */
238 : void CheckLoadingFinished();
239 :
240 : /**
241 : * Callback for invoking CheckLoadingFinished after going through the
242 : * event loop. See OnFontFaceStatusChanged.
243 : */
244 : void CheckLoadingFinishedAfterDelay();
245 :
246 : /**
247 : * Dispatches a FontFaceSetLoadEvent to this object.
248 : */
249 : void DispatchLoadingFinishedEvent(
250 : const nsAString& aType,
251 : nsTArray<OwningNonNull<FontFace>>&& aFontFaces);
252 :
253 : // Note: if you add new cycle collected objects to FontFaceRecord,
254 : // make sure to update FontFaceSet's cycle collection macros
255 : // accordingly.
256 0 : struct FontFaceRecord {
257 : RefPtr<FontFace> mFontFace;
258 : SheetType mSheetType; // only relevant for mRuleFaces entries
259 :
260 : // When true, indicates that when finished loading, the FontFace should be
261 : // included in the subsequent loadingdone/loadingerror event fired at the
262 : // FontFaceSet.
263 : bool mLoadEventShouldFire;
264 : };
265 :
266 : already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntryFromFontFace(
267 : const nsAString& aFamilyName,
268 : FontFace* aFontFace,
269 : SheetType aSheetType);
270 :
271 : // search for @font-face rule that matches a userfont font entry
272 : nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
273 :
274 : nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
275 : const gfxFontFaceSrc* aFontFaceSrc);
276 : gfxFontSrcPrincipal* GetStandardFontLoadPrincipal();
277 : nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
278 : gfxFontSrcPrincipal** aPrincipal,
279 : bool* aBypassCache);
280 : bool IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal);
281 : nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
282 : const gfxFontFaceSrc* aFontFaceSrc,
283 : uint8_t*& aBuffer,
284 : uint32_t& aBufferLength);
285 : nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
286 : const char* aMessage,
287 : uint32_t aFlags,
288 : nsresult aStatus);
289 : void RebuildUserFontSet();
290 :
291 : void InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType,
292 : nsTArray<FontFaceRecord>& aOldRecords,
293 : bool& aFontSetModified);
294 : void InsertNonRuleFontFace(FontFace* aFontFace, bool& aFontSetModified);
295 :
296 : #ifdef DEBUG
297 : bool HasRuleFontFace(FontFace* aFontFace);
298 : #endif
299 :
300 : /**
301 : * Returns whether we have any loading FontFace objects in the FontFaceSet.
302 : */
303 : bool HasLoadingFontFaces();
304 :
305 : // Helper function for HasLoadingFontFaces.
306 : void UpdateHasLoadingFontFaces();
307 :
308 : void ParseFontShorthandForMatching(
309 : const nsAString& aFont,
310 : RefPtr<mozilla::css::FontFamilyListRefCnt>& aFamilyList,
311 : uint32_t& aWeight,
312 : int32_t& aStretch,
313 : uint8_t& aStyle,
314 : ErrorResult& aRv);
315 : void FindMatchingFontFaces(const nsAString& aFont,
316 : const nsAString& aText,
317 : nsTArray<FontFace*>& aFontFaces,
318 : mozilla::ErrorResult& aRv);
319 :
320 : void DispatchLoadingEventAndReplaceReadyPromise();
321 : void DispatchCheckLoadingFinishedAfterDelay();
322 :
323 : TimeStamp GetNavigationStartTimeStamp();
324 :
325 : RefPtr<UserFontSet> mUserFontSet;
326 :
327 : // The document this is a FontFaceSet for.
328 : nsCOMPtr<nsIDocument> mDocument;
329 :
330 : // The document's node principal, which is the principal font loads for
331 : // this FontFaceSet will generally use. (This principal is not used for
332 : // @font-face rules in UA and user sheets, where the principal of the
333 : // sheet is used instead.)
334 : //
335 : // This field is used from GetStandardFontLoadPrincipal. When on a
336 : // style worker thread, we use mStandardFontLoadPrincipal assuming
337 : // it is up to date. Because mDocument's principal can change over time,
338 : // its value must be updated by a call to UpdateStandardFontLoadPrincipal
339 : // before a restyle. (When called while on the main thread,
340 : // GetStandardFontLoadPrincipal will call UpdateStandardFontLoadPrincipal
341 : // to ensure its value is up to date.)
342 : RefPtr<gfxFontSrcPrincipal> mStandardFontLoadPrincipal;
343 :
344 : // A Promise that is fulfilled once all of the FontFace objects
345 : // in mRuleFaces and mNonRuleFaces that started or were loading at the
346 : // time the Promise was created have finished loading. It is rejected if
347 : // any of those fonts failed to load. mReady is replaced with
348 : // a new Promise object whenever mReady is settled and another
349 : // FontFace in mRuleFaces or mNonRuleFaces starts to load.
350 : // Note that mReady is created lazily when GetReady() is called.
351 : RefPtr<mozilla::dom::Promise> mReady;
352 : // Whether the ready promise must be resolved when it's created.
353 : bool mResolveLazilyCreatedReadyPromise;
354 :
355 : // Set of all loaders pointing to us. These are not strong pointers,
356 : // but that's OK because nsFontFaceLoader always calls RemoveLoader on
357 : // us before it dies (unless we die first).
358 : nsTHashtable< nsPtrHashKey<nsFontFaceLoader> > mLoaders;
359 :
360 : // The @font-face rule backed FontFace objects in the FontFaceSet.
361 : nsTArray<FontFaceRecord> mRuleFaces;
362 :
363 : // The non rule backed FontFace objects that have been added to this
364 : // FontFaceSet.
365 : nsTArray<FontFaceRecord> mNonRuleFaces;
366 :
367 : // The overall status of the loading or loaded fonts in the FontFaceSet.
368 : mozilla::dom::FontFaceSetLoadStatus mStatus;
369 :
370 : // Whether mNonRuleFaces has changed since last time UpdateRules ran.
371 : bool mNonRuleFacesDirty;
372 :
373 : // Whether any FontFace objects in mRuleFaces or mNonRuleFaces are
374 : // loading. Only valid when mHasLoadingFontFacesIsDirty is false. Don't use
375 : // this variable directly; call the HasLoadingFontFaces method instead.
376 : bool mHasLoadingFontFaces;
377 :
378 : // This variable is only valid when mLoadingDirty is false.
379 : bool mHasLoadingFontFacesIsDirty;
380 :
381 : // Whether CheckLoadingFinished calls should be ignored. See comment in
382 : // OnFontFaceStatusChanged.
383 : bool mDelayedLoadCheck;
384 :
385 : // Whether the docshell for our document indicated that loads should
386 : // bypass the cache.
387 : bool mBypassCache;
388 :
389 : // Whether the docshell for our document indicates that we are in private
390 : // browsing mode.
391 : bool mPrivateBrowsing;
392 :
393 : // Whether mStandardFontLoadPrincipal has changed since the last call to
394 : // HasStandardFontLoadPrincipalChanged.
395 : bool mHasStandardFontLoadPrincipalChanged;
396 : };
397 :
398 : } // namespace dom
399 : } // namespace mozilla
400 :
401 : #endif // !defined(mozilla_dom_FontFaceSet_h)
|