Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
6 #ifndef GFX_USER_FONT_SET_H
7 #define GFX_USER_FONT_SET_H
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"
19 class nsFontFaceLoader;
21 //#define DEBUG_USERFONT_CACHE
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
28 // if url, whether to use the origin principal or not
29 bool mUseOriginPrincipal;
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;
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 };
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 }
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() { }
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 };
80 // initially contains a set of proxy font entry objects, replaced with
81 // platform/user fonts as downloaded
83 class gfxMixedFontFamily : public gfxFontFamily {
84 public:
85 friend class gfxUserFontSet;
87 gfxMixedFontFamily(const nsAString& aName)
88 : gfxFontFamily(aName) { }
90 virtual ~gfxMixedFontFamily() { }
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 }
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 }
130 // Remove all font entries from the family
131 void DetachFontEntries() {
132 mAvailableFonts.Clear();
133 }
134 };
136 class gfxProxyFontEntry;
138 class gfxUserFontSet {
140 public:
142 NS_INLINE_DECL_REFCOUNTING(gfxUserFontSet)
144 gfxUserFontSet();
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,
157 // mask of all unused bits, update when adding new formats
158 FLAG_FORMAT_NOT_USED = ~((1 << 7)-1)
159 };
161 enum LoadStatus {
162 STATUS_LOADING = 0,
163 STATUS_LOADED,
164 STATUS_FORMAT_NOT_SUPPORTED,
165 STATUS_ERROR,
166 STATUS_END_OF_LIST
167 };
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);
184 // add in a font face for which we have the gfxFontEntry already
185 void AddFontFace(const nsAString& aFamilyName, gfxFontEntry* aFontEntry);
187 // Whether there is a face with this family name
188 bool HasFamily(const nsAString& aFamilyName) const
189 {
190 return GetFamily(aFamilyName) != nullptr;
191 }
193 gfxFontFamily *GetFamily(const nsAString& aName) const;
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);
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;
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;
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;
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);
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;
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; }
243 // increment the generation on font load
244 void IncrementGeneration();
246 // rebuild if local rules have been used
247 void RebuildLocalRules();
249 virtual nsPresContext *GetPresContext() { return NULL; }
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);
258 // The given gfxFontEntry is being destroyed, so remove any record that
259 // refers to it.
260 static void ForgetFont(gfxFontEntry *aFontEntry);
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);
272 // Clear everything so that we don't leak URIs and Principals.
273 static void Shutdown();
275 #ifdef DEBUG_USERFONT_CACHE
276 // dump contents
277 static void Dump();
278 #endif
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 };
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;
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 };
313 class Entry : public PLDHashEntryHdr {
314 public:
315 typedef const Key& KeyType;
316 typedef const Key* KeyTypePointer;
318 Entry(KeyTypePointer aKey)
319 : mURI(aKey->mURI),
320 mPrincipal(aKey->mPrincipal),
321 mFontEntry(aKey->mFontEntry),
322 mPrivate(aKey->mPrivate)
323 { }
325 Entry(const Entry& aOther)
326 : mURI(aOther.mURI),
327 mPrincipal(aOther.mPrincipal),
328 mFontEntry(aOther.mFontEntry),
329 mPrivate(aOther.mPrivate)
330 { }
332 ~Entry() { }
334 bool KeyEquals(const KeyTypePointer aKey) const;
336 static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
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 }
351 enum { ALLOW_MEMMOVE = false };
353 gfxFontEntry* GetFontEntry() const { return mFontEntry; }
355 static PLDHashOperator RemoveIfPrivate(Entry* aEntry, void* aUserData);
356 static PLDHashOperator RemoveIfMatches(Entry* aEntry, void* aUserData);
357 static PLDHashOperator DisconnectSVG(Entry* aEntry, void* aUserData);
359 #ifdef DEBUG_USERFONT_CACHE
360 static PLDHashOperator DumpEntry(Entry* aEntry, void* aUserData);
361 #endif
363 private:
364 static uint32_t
365 HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) {
366 return mozilla::HashBytes(aFeatures.Elements(),
367 aFeatures.Length() * sizeof(gfxFontFeature));
368 }
370 nsCOMPtr<nsIURI> mURI;
371 nsCOMPtr<nsIPrincipal> mPrincipal;
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;
378 // Whether this font was loaded from a private window.
379 bool mPrivate;
380 };
382 static nsTHashtable<Entry> *sUserFonts;
383 };
385 protected:
386 // Protected destructor, to discourage deletion outside of Release():
387 virtual ~gfxUserFontSet();
389 // Return whether the font set is associated with a private-browsing tab.
390 virtual bool GetPrivateBrowsing() = 0;
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);
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);
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;
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;
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);
425 static bool OTSMessage(void *aUserData, const char *format, ...);
427 // helper method for performing the actual userfont set rebuild
428 virtual void DoRebuildUserFontSet() = 0;
430 // font families defined by @font-face rules
431 nsRefPtrHashtable<nsStringHashKey, gfxMixedFontFamily> mFontFamilies;
433 uint64_t mGeneration;
435 // true when local names have been looked up, false otherwise
436 bool mLocalRulesUsed;
438 static PRLogModuleInfo* GetUserFontsLog();
440 private:
441 static void CopyWOFFMetadata(const uint8_t* aFontData,
442 uint32_t aLength,
443 FallibleTArray<uint8_t>* aMetadata,
444 uint32_t* aMetaOrigLen);
445 };
447 // acts a placeholder until the real font is downloaded
449 class gfxProxyFontEntry : public gfxFontEntry {
450 friend class gfxUserFontSet;
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);
461 virtual ~gfxProxyFontEntry();
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);
472 virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold);
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;
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 };
494 #endif /* GFX_USER_FONT_SET_H */