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