|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
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 GFX_USER_FONT_SET_H |
|
7 #define GFX_USER_FONT_SET_H |
|
8 |
|
9 #include "gfxFont.h" |
|
10 #include "nsPresContext.h" |
|
11 #include "nsRefPtrHashtable.h" |
|
12 #include "nsAutoPtr.h" |
|
13 #include "nsCOMPtr.h" |
|
14 #include "nsIURI.h" |
|
15 #include "nsIPrincipal.h" |
|
16 #include "nsIScriptError.h" |
|
17 #include "nsURIHashKey.h" |
|
18 |
|
19 class nsFontFaceLoader; |
|
20 |
|
21 //#define DEBUG_USERFONT_CACHE |
|
22 |
|
23 // parsed CSS @font-face rule information |
|
24 // lifetime: from when @font-face rule processed until font is loaded |
|
25 struct gfxFontFaceSrc { |
|
26 bool mIsLocal; // url or local |
|
27 |
|
28 // if url, whether to use the origin principal or not |
|
29 bool mUseOriginPrincipal; |
|
30 |
|
31 // format hint flags, union of all possible formats |
|
32 // (e.g. TrueType, EOT, SVG, etc.) |
|
33 // see FLAG_FORMAT_* enum values below |
|
34 uint32_t mFormatFlags; |
|
35 |
|
36 nsString mLocalName; // full font name if local |
|
37 nsCOMPtr<nsIURI> mURI; // uri if url |
|
38 nsCOMPtr<nsIURI> mReferrer; // referrer url if url |
|
39 nsCOMPtr<nsIPrincipal> mOriginPrincipal; // principal if url |
|
40 }; |
|
41 |
|
42 inline bool |
|
43 operator==(const gfxFontFaceSrc& a, const gfxFontFaceSrc& b) |
|
44 { |
|
45 bool equals; |
|
46 return (a.mIsLocal && b.mIsLocal && |
|
47 a.mLocalName == b.mLocalName) || |
|
48 (!a.mIsLocal && !b.mIsLocal && |
|
49 a.mUseOriginPrincipal == b.mUseOriginPrincipal && |
|
50 a.mFormatFlags == b.mFormatFlags && |
|
51 NS_SUCCEEDED(a.mURI->Equals(b.mURI, &equals)) && equals && |
|
52 NS_SUCCEEDED(a.mReferrer->Equals(b.mReferrer, &equals)) && equals && |
|
53 a.mOriginPrincipal->Equals(b.mOriginPrincipal)); |
|
54 } |
|
55 |
|
56 // Subclassed to store platform-specific code cleaned out when font entry is |
|
57 // deleted. |
|
58 // Lifetime: from when platform font is created until it is deactivated. |
|
59 // If the platform does not need to add any platform-specific code/data here, |
|
60 // then the gfxUserFontSet will allocate a base gfxUserFontData and attach |
|
61 // to the entry to track the basic user font info fields here. |
|
62 class gfxUserFontData { |
|
63 public: |
|
64 gfxUserFontData() |
|
65 : mSrcIndex(0), mFormat(0), mMetaOrigLen(0) |
|
66 { } |
|
67 virtual ~gfxUserFontData() { } |
|
68 |
|
69 nsTArray<uint8_t> mMetadata; // woff metadata block (compressed), if any |
|
70 nsCOMPtr<nsIURI> mURI; // URI of the source, if it was url() |
|
71 nsCOMPtr<nsIPrincipal> mPrincipal; // principal for the download, if url() |
|
72 nsString mLocalName; // font name used for the source, if local() |
|
73 nsString mRealName; // original fullname from the font resource |
|
74 uint32_t mSrcIndex; // index in the rule's source list |
|
75 uint32_t mFormat; // format hint for the source used, if any |
|
76 uint32_t mMetaOrigLen; // length needed to decompress metadata |
|
77 bool mPrivate; // whether font belongs to a private window |
|
78 }; |
|
79 |
|
80 // initially contains a set of proxy font entry objects, replaced with |
|
81 // platform/user fonts as downloaded |
|
82 |
|
83 class gfxMixedFontFamily : public gfxFontFamily { |
|
84 public: |
|
85 friend class gfxUserFontSet; |
|
86 |
|
87 gfxMixedFontFamily(const nsAString& aName) |
|
88 : gfxFontFamily(aName) { } |
|
89 |
|
90 virtual ~gfxMixedFontFamily() { } |
|
91 |
|
92 // Add the given font entry to the end of the family's list. |
|
93 // Any earlier occurrence is removed, so this has the effect of "advancing" |
|
94 // the entry to the end of the list. |
|
95 void AddFontEntry(gfxFontEntry *aFontEntry) { |
|
96 // We append to mAvailableFonts -before- searching for and removing |
|
97 // any existing reference to avoid the risk that we'll remove the last |
|
98 // reference to the font entry, and thus delete it. |
|
99 mAvailableFonts.AppendElement(aFontEntry); |
|
100 uint32_t i = mAvailableFonts.Length() - 1; |
|
101 while (i > 0) { |
|
102 if (mAvailableFonts[--i] == aFontEntry) { |
|
103 mAvailableFonts.RemoveElementAt(i); |
|
104 break; |
|
105 } |
|
106 } |
|
107 aFontEntry->mFamilyName = Name(); |
|
108 ResetCharacterMap(); |
|
109 } |
|
110 |
|
111 // Replace aProxyFontEntry in the family's list with aRealFontEntry. |
|
112 void ReplaceFontEntry(gfxFontEntry *aProxyFontEntry, |
|
113 gfxFontEntry *aRealFontEntry) { |
|
114 uint32_t numFonts = mAvailableFonts.Length(); |
|
115 uint32_t i; |
|
116 for (i = 0; i < numFonts; i++) { |
|
117 gfxFontEntry *fe = mAvailableFonts[i]; |
|
118 if (fe == aProxyFontEntry) { |
|
119 // Note that this may delete aProxyFontEntry, if there's no |
|
120 // other reference to it except from its family. |
|
121 mAvailableFonts[i] = aRealFontEntry; |
|
122 aRealFontEntry->mFamilyName = Name(); |
|
123 break; |
|
124 } |
|
125 } |
|
126 NS_ASSERTION(i < numFonts, "font entry not found in family!"); |
|
127 ResetCharacterMap(); |
|
128 } |
|
129 |
|
130 // Remove all font entries from the family |
|
131 void DetachFontEntries() { |
|
132 mAvailableFonts.Clear(); |
|
133 } |
|
134 }; |
|
135 |
|
136 class gfxProxyFontEntry; |
|
137 |
|
138 class gfxUserFontSet { |
|
139 |
|
140 public: |
|
141 |
|
142 NS_INLINE_DECL_REFCOUNTING(gfxUserFontSet) |
|
143 |
|
144 gfxUserFontSet(); |
|
145 |
|
146 enum { |
|
147 // no flags ==> no hint set |
|
148 // unknown ==> unknown format hint set |
|
149 FLAG_FORMAT_UNKNOWN = 1, |
|
150 FLAG_FORMAT_OPENTYPE = 1 << 1, |
|
151 FLAG_FORMAT_TRUETYPE = 1 << 2, |
|
152 FLAG_FORMAT_TRUETYPE_AAT = 1 << 3, |
|
153 FLAG_FORMAT_EOT = 1 << 4, |
|
154 FLAG_FORMAT_SVG = 1 << 5, |
|
155 FLAG_FORMAT_WOFF = 1 << 6, |
|
156 |
|
157 // mask of all unused bits, update when adding new formats |
|
158 FLAG_FORMAT_NOT_USED = ~((1 << 7)-1) |
|
159 }; |
|
160 |
|
161 enum LoadStatus { |
|
162 STATUS_LOADING = 0, |
|
163 STATUS_LOADED, |
|
164 STATUS_FORMAT_NOT_SUPPORTED, |
|
165 STATUS_ERROR, |
|
166 STATUS_END_OF_LIST |
|
167 }; |
|
168 |
|
169 |
|
170 // add in a font face |
|
171 // weight - 0 == unknown, [100, 900] otherwise (multiples of 100) |
|
172 // stretch = [NS_FONT_STRETCH_ULTRA_CONDENSED, NS_FONT_STRETCH_ULTRA_EXPANDED] |
|
173 // italic style = constants in gfxFontConstants.h, e.g. NS_FONT_STYLE_NORMAL |
|
174 // TODO: support for unicode ranges not yet implemented |
|
175 gfxFontEntry *AddFontFace(const nsAString& aFamilyName, |
|
176 const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, |
|
177 uint32_t aWeight, |
|
178 int32_t aStretch, |
|
179 uint32_t aItalicStyle, |
|
180 const nsTArray<gfxFontFeature>& aFeatureSettings, |
|
181 const nsString& aLanguageOverride, |
|
182 gfxSparseBitSet *aUnicodeRanges = nullptr); |
|
183 |
|
184 // add in a font face for which we have the gfxFontEntry already |
|
185 void AddFontFace(const nsAString& aFamilyName, gfxFontEntry* aFontEntry); |
|
186 |
|
187 // Whether there is a face with this family name |
|
188 bool HasFamily(const nsAString& aFamilyName) const |
|
189 { |
|
190 return GetFamily(aFamilyName) != nullptr; |
|
191 } |
|
192 |
|
193 gfxFontFamily *GetFamily(const nsAString& aName) const; |
|
194 |
|
195 // Lookup a font entry for a given style, returns null if not loaded. |
|
196 // aFamily must be a family returned by our GetFamily method. |
|
197 gfxFontEntry *FindFontEntry(gfxFontFamily *aFamily, |
|
198 const gfxFontStyle& aFontStyle, |
|
199 bool& aNeedsBold, |
|
200 bool& aWaitForUserFont); |
|
201 |
|
202 // Find a family (possibly one of several!) that owns the given entry. |
|
203 // This may be somewhat expensive, as it enumerates all the fonts in |
|
204 // the set. Currently used only by the Linux (gfxPangoFontGroup) backend, |
|
205 // which does not directly track families in the font group's list. |
|
206 gfxFontFamily *FindFamilyFor(gfxFontEntry *aFontEntry) const; |
|
207 |
|
208 // check whether the given source is allowed to be loaded; |
|
209 // returns the Principal (for use in the key when caching the loaded font), |
|
210 // and whether the load should bypass the cache (force-reload). |
|
211 virtual nsresult CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc, |
|
212 nsIPrincipal **aPrincipal, |
|
213 bool *aBypassCache) = 0; |
|
214 |
|
215 // initialize the process that loads external font data, which upon |
|
216 // completion will call OnLoadComplete method |
|
217 virtual nsresult StartLoad(gfxMixedFontFamily *aFamily, |
|
218 gfxProxyFontEntry *aProxy, |
|
219 const gfxFontFaceSrc *aFontFaceSrc) = 0; |
|
220 |
|
221 // when download has been completed, pass back data here |
|
222 // aDownloadStatus == NS_OK ==> download succeeded, error otherwise |
|
223 // returns true if platform font creation sucessful (or local() |
|
224 // reference was next in line) |
|
225 // Ownership of aFontData is passed in here; the font set must |
|
226 // ensure that it is eventually deleted with NS_Free(). |
|
227 bool OnLoadComplete(gfxMixedFontFamily *aFamily, |
|
228 gfxProxyFontEntry *aProxy, |
|
229 const uint8_t *aFontData, uint32_t aLength, |
|
230 nsresult aDownloadStatus); |
|
231 |
|
232 // Replace a proxy with a real fontEntry; this is implemented in |
|
233 // nsUserFontSet in order to keep track of the entry corresponding |
|
234 // to each @font-face rule. |
|
235 virtual void ReplaceFontEntry(gfxMixedFontFamily *aFamily, |
|
236 gfxProxyFontEntry *aProxy, |
|
237 gfxFontEntry *aFontEntry) = 0; |
|
238 |
|
239 // generation - each time a face is loaded, generation is |
|
240 // incremented so that the change can be recognized |
|
241 uint64_t GetGeneration() { return mGeneration; } |
|
242 |
|
243 // increment the generation on font load |
|
244 void IncrementGeneration(); |
|
245 |
|
246 // rebuild if local rules have been used |
|
247 void RebuildLocalRules(); |
|
248 |
|
249 virtual nsPresContext *GetPresContext() { return NULL; } |
|
250 |
|
251 class UserFontCache { |
|
252 public: |
|
253 // Record a loaded user-font in the cache. This requires that the |
|
254 // font-entry's userFontData has been set up already, as it relies |
|
255 // on the URI and Principal recorded there. |
|
256 static void CacheFont(gfxFontEntry *aFontEntry); |
|
257 |
|
258 // The given gfxFontEntry is being destroyed, so remove any record that |
|
259 // refers to it. |
|
260 static void ForgetFont(gfxFontEntry *aFontEntry); |
|
261 |
|
262 // Return the gfxFontEntry corresponding to a given URI and principal, |
|
263 // and the features of the given proxy, or nullptr if none is available. |
|
264 // The aPrivate flag is set for requests coming from private windows, |
|
265 // so we can avoid leaking fonts cached in private windows mode out to |
|
266 // normal windows. |
|
267 static gfxFontEntry* GetFont(nsIURI *aSrcURI, |
|
268 nsIPrincipal *aPrincipal, |
|
269 gfxProxyFontEntry *aProxy, |
|
270 bool aPrivate); |
|
271 |
|
272 // Clear everything so that we don't leak URIs and Principals. |
|
273 static void Shutdown(); |
|
274 |
|
275 #ifdef DEBUG_USERFONT_CACHE |
|
276 // dump contents |
|
277 static void Dump(); |
|
278 #endif |
|
279 |
|
280 private: |
|
281 // Helper that we use to observe the empty-cache notification |
|
282 // from nsICacheService. |
|
283 class Flusher : public nsIObserver |
|
284 { |
|
285 public: |
|
286 NS_DECL_ISUPPORTS |
|
287 NS_DECL_NSIOBSERVER |
|
288 Flusher() {} |
|
289 virtual ~Flusher() {} |
|
290 }; |
|
291 |
|
292 // Key used to look up entries in the user-font cache. |
|
293 // Note that key comparison does *not* use the mFontEntry field |
|
294 // as a whole; it only compares specific fields within the entry |
|
295 // (weight/width/style/features) that could affect font selection |
|
296 // or rendering, and that must match between a font-set's proxy |
|
297 // entry and the corresponding "real" font entry. |
|
298 struct Key { |
|
299 nsCOMPtr<nsIURI> mURI; |
|
300 nsCOMPtr<nsIPrincipal> mPrincipal; |
|
301 gfxFontEntry *mFontEntry; |
|
302 bool mPrivate; |
|
303 |
|
304 Key(nsIURI* aURI, nsIPrincipal* aPrincipal, |
|
305 gfxFontEntry* aFontEntry, bool aPrivate) |
|
306 : mURI(aURI), |
|
307 mPrincipal(aPrincipal), |
|
308 mFontEntry(aFontEntry), |
|
309 mPrivate(aPrivate) |
|
310 { } |
|
311 }; |
|
312 |
|
313 class Entry : public PLDHashEntryHdr { |
|
314 public: |
|
315 typedef const Key& KeyType; |
|
316 typedef const Key* KeyTypePointer; |
|
317 |
|
318 Entry(KeyTypePointer aKey) |
|
319 : mURI(aKey->mURI), |
|
320 mPrincipal(aKey->mPrincipal), |
|
321 mFontEntry(aKey->mFontEntry), |
|
322 mPrivate(aKey->mPrivate) |
|
323 { } |
|
324 |
|
325 Entry(const Entry& aOther) |
|
326 : mURI(aOther.mURI), |
|
327 mPrincipal(aOther.mPrincipal), |
|
328 mFontEntry(aOther.mFontEntry), |
|
329 mPrivate(aOther.mPrivate) |
|
330 { } |
|
331 |
|
332 ~Entry() { } |
|
333 |
|
334 bool KeyEquals(const KeyTypePointer aKey) const; |
|
335 |
|
336 static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } |
|
337 |
|
338 static PLDHashNumber HashKey(const KeyTypePointer aKey) { |
|
339 uint32_t principalHash; |
|
340 aKey->mPrincipal->GetHashValue(&principalHash); |
|
341 return mozilla::HashGeneric(principalHash + int(aKey->mPrivate), |
|
342 nsURIHashKey::HashKey(aKey->mURI), |
|
343 HashFeatures(aKey->mFontEntry->mFeatureSettings), |
|
344 mozilla::HashString(aKey->mFontEntry->mFamilyName), |
|
345 ((uint32_t)aKey->mFontEntry->mItalic | |
|
346 (aKey->mFontEntry->mWeight << 1) | |
|
347 (aKey->mFontEntry->mStretch << 10) ) ^ |
|
348 aKey->mFontEntry->mLanguageOverride); |
|
349 } |
|
350 |
|
351 enum { ALLOW_MEMMOVE = false }; |
|
352 |
|
353 gfxFontEntry* GetFontEntry() const { return mFontEntry; } |
|
354 |
|
355 static PLDHashOperator RemoveIfPrivate(Entry* aEntry, void* aUserData); |
|
356 static PLDHashOperator RemoveIfMatches(Entry* aEntry, void* aUserData); |
|
357 static PLDHashOperator DisconnectSVG(Entry* aEntry, void* aUserData); |
|
358 |
|
359 #ifdef DEBUG_USERFONT_CACHE |
|
360 static PLDHashOperator DumpEntry(Entry* aEntry, void* aUserData); |
|
361 #endif |
|
362 |
|
363 private: |
|
364 static uint32_t |
|
365 HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) { |
|
366 return mozilla::HashBytes(aFeatures.Elements(), |
|
367 aFeatures.Length() * sizeof(gfxFontFeature)); |
|
368 } |
|
369 |
|
370 nsCOMPtr<nsIURI> mURI; |
|
371 nsCOMPtr<nsIPrincipal> mPrincipal; |
|
372 |
|
373 // The "real" font entry corresponding to this downloaded font. |
|
374 // The font entry MUST notify the cache when it is destroyed |
|
375 // (by calling Forget()). |
|
376 gfxFontEntry *mFontEntry; |
|
377 |
|
378 // Whether this font was loaded from a private window. |
|
379 bool mPrivate; |
|
380 }; |
|
381 |
|
382 static nsTHashtable<Entry> *sUserFonts; |
|
383 }; |
|
384 |
|
385 protected: |
|
386 // Protected destructor, to discourage deletion outside of Release(): |
|
387 virtual ~gfxUserFontSet(); |
|
388 |
|
389 // Return whether the font set is associated with a private-browsing tab. |
|
390 virtual bool GetPrivateBrowsing() = 0; |
|
391 |
|
392 // for a given proxy font entry, attempt to load the next resource |
|
393 // in the src list |
|
394 LoadStatus LoadNext(gfxMixedFontFamily *aFamily, |
|
395 gfxProxyFontEntry *aProxyEntry); |
|
396 |
|
397 // helper method for creating a platform font |
|
398 // returns font entry if platform font creation successful |
|
399 // Ownership of aFontData is passed in here; the font set must |
|
400 // ensure that it is eventually deleted with NS_Free(). |
|
401 gfxFontEntry* LoadFont(gfxMixedFontFamily *aFamily, |
|
402 gfxProxyFontEntry *aProxy, |
|
403 const uint8_t *aFontData, uint32_t &aLength); |
|
404 |
|
405 // parse data for a data URL |
|
406 virtual nsresult SyncLoadFontData(gfxProxyFontEntry *aFontToLoad, |
|
407 const gfxFontFaceSrc *aFontFaceSrc, |
|
408 uint8_t* &aBuffer, |
|
409 uint32_t &aBufferLength) = 0; |
|
410 |
|
411 // report a problem of some kind (implemented in nsUserFontSet) |
|
412 virtual nsresult LogMessage(gfxMixedFontFamily *aFamily, |
|
413 gfxProxyFontEntry *aProxy, |
|
414 const char *aMessage, |
|
415 uint32_t aFlags = nsIScriptError::errorFlag, |
|
416 nsresult aStatus = NS_OK) = 0; |
|
417 |
|
418 const uint8_t* SanitizeOpenTypeData(gfxMixedFontFamily *aFamily, |
|
419 gfxProxyFontEntry *aProxy, |
|
420 const uint8_t* aData, |
|
421 uint32_t aLength, |
|
422 uint32_t& aSaneLength, |
|
423 bool aIsCompressed); |
|
424 |
|
425 static bool OTSMessage(void *aUserData, const char *format, ...); |
|
426 |
|
427 // helper method for performing the actual userfont set rebuild |
|
428 virtual void DoRebuildUserFontSet() = 0; |
|
429 |
|
430 // font families defined by @font-face rules |
|
431 nsRefPtrHashtable<nsStringHashKey, gfxMixedFontFamily> mFontFamilies; |
|
432 |
|
433 uint64_t mGeneration; |
|
434 |
|
435 // true when local names have been looked up, false otherwise |
|
436 bool mLocalRulesUsed; |
|
437 |
|
438 static PRLogModuleInfo* GetUserFontsLog(); |
|
439 |
|
440 private: |
|
441 static void CopyWOFFMetadata(const uint8_t* aFontData, |
|
442 uint32_t aLength, |
|
443 FallibleTArray<uint8_t>* aMetadata, |
|
444 uint32_t* aMetaOrigLen); |
|
445 }; |
|
446 |
|
447 // acts a placeholder until the real font is downloaded |
|
448 |
|
449 class gfxProxyFontEntry : public gfxFontEntry { |
|
450 friend class gfxUserFontSet; |
|
451 |
|
452 public: |
|
453 gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, |
|
454 uint32_t aWeight, |
|
455 int32_t aStretch, |
|
456 uint32_t aItalicStyle, |
|
457 const nsTArray<gfxFontFeature>& aFeatureSettings, |
|
458 uint32_t aLanguageOverride, |
|
459 gfxSparseBitSet *aUnicodeRanges); |
|
460 |
|
461 virtual ~gfxProxyFontEntry(); |
|
462 |
|
463 // Return whether the entry matches the given list of attributes |
|
464 bool Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, |
|
465 uint32_t aWeight, |
|
466 int32_t aStretch, |
|
467 uint32_t aItalicStyle, |
|
468 const nsTArray<gfxFontFeature>& aFeatureSettings, |
|
469 uint32_t aLanguageOverride, |
|
470 gfxSparseBitSet *aUnicodeRanges); |
|
471 |
|
472 virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold); |
|
473 |
|
474 // note that code depends on the ordering of these values! |
|
475 enum LoadingState { |
|
476 NOT_LOADING = 0, // not started to load any font resources yet |
|
477 LOADING_STARTED, // loading has started; hide fallback font |
|
478 LOADING_ALMOST_DONE, // timeout happened but we're nearly done, |
|
479 // so keep hiding fallback font |
|
480 LOADING_SLOWLY, // timeout happened and we're not nearly done, |
|
481 // so use the fallback font |
|
482 LOADING_FAILED // failed to load any source: use fallback |
|
483 }; |
|
484 LoadingState mLoadingState; |
|
485 bool mUnsupportedFormat; |
|
486 |
|
487 nsTArray<gfxFontFaceSrc> mSrcList; |
|
488 uint32_t mSrcIndex; // index of loading src item |
|
489 nsFontFaceLoader *mLoader; // current loader for this entry, if any |
|
490 nsCOMPtr<nsIPrincipal> mPrincipal; |
|
491 }; |
|
492 |
|
493 |
|
494 #endif /* GFX_USER_FONT_SET_H */ |