Line data Source code
1 : /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 : /* vim: set ts=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 file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "FontFaceSet.h"
8 :
9 : #include "gfxFontConstants.h"
10 : #include "gfxFontSrcPrincipal.h"
11 : #include "gfxFontSrcURI.h"
12 : #include "mozilla/css/Declaration.h"
13 : #include "mozilla/css/Loader.h"
14 : #include "mozilla/dom/FontFaceSetBinding.h"
15 : #include "mozilla/dom/FontFaceSetIterator.h"
16 : #include "mozilla/dom/FontFaceSetLoadEvent.h"
17 : #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
18 : #include "mozilla/dom/Promise.h"
19 : #include "mozilla/AsyncEventDispatcher.h"
20 : #include "mozilla/Logging.h"
21 : #include "mozilla/Preferences.h"
22 : #include "mozilla/ServoStyleSet.h"
23 : #include "mozilla/ServoUtils.h"
24 : #include "mozilla/SizePrintfMacros.h"
25 : #include "mozilla/Sprintf.h"
26 : #include "mozilla/Telemetry.h"
27 : #include "nsAutoPtr.h"
28 : #include "nsContentPolicyUtils.h"
29 : #include "nsCSSParser.h"
30 : #include "nsDeviceContext.h"
31 : #include "nsFontFaceLoader.h"
32 : #include "nsIConsoleService.h"
33 : #include "nsIContentPolicy.h"
34 : #include "nsIContentSecurityPolicy.h"
35 : #include "nsIDocShell.h"
36 : #include "nsIDocument.h"
37 : #include "nsILoadContext.h"
38 : #include "nsINetworkPredictor.h"
39 : #include "nsIPresShell.h"
40 : #include "nsIPrincipal.h"
41 : #include "nsISupportsPriority.h"
42 : #include "nsIWebNavigation.h"
43 : #include "nsNetUtil.h"
44 : #include "nsIProtocolHandler.h"
45 : #include "nsIInputStream.h"
46 : #include "nsPresContext.h"
47 : #include "nsPrintfCString.h"
48 : #include "nsStyleSet.h"
49 : #include "nsUTF8Utils.h"
50 : #include "nsDOMNavigationTiming.h"
51 :
52 : using namespace mozilla;
53 : using namespace mozilla::css;
54 : using namespace mozilla::dom;
55 :
56 : #define LOG(args) MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
57 : #define LOG_ENABLED() MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), \
58 : LogLevel::Debug)
59 :
60 : #define FONT_LOADING_API_ENABLED_PREF "layout.css.font-loading-api.enabled"
61 :
62 : NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet)
63 :
64 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelper)
65 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
66 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady);
67 0 : for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
68 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace);
69 : }
70 0 : for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
71 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces[i].mFontFace);
72 : }
73 0 : if (tmp->mUserFontSet) {
74 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserFontSet->mFontFaceSet);
75 : }
76 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
77 :
78 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelper)
79 0 : tmp->Disconnect();
80 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
81 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady);
82 0 : for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
83 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces[i].mFontFace);
84 : }
85 0 : for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
86 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces[i].mFontFace);
87 : }
88 0 : if (tmp->mUserFontSet) {
89 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mUserFontSet->mFontFaceSet);
90 : }
91 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mUserFontSet);
92 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
93 :
94 0 : NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper)
95 0 : NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper)
96 :
97 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FontFaceSet)
98 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
99 0 : NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
100 0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
101 :
102 0 : FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
103 : : DOMEventTargetHelper(aWindow)
104 : , mDocument(aDocument)
105 : , mResolveLazilyCreatedReadyPromise(false)
106 : , mStatus(FontFaceSetLoadStatus::Loaded)
107 : , mNonRuleFacesDirty(false)
108 : , mHasLoadingFontFaces(false)
109 : , mHasLoadingFontFacesIsDirty(false)
110 : , mDelayedLoadCheck(false)
111 : , mBypassCache(false)
112 : , mPrivateBrowsing(false)
113 0 : , mHasStandardFontLoadPrincipalChanged(false)
114 : {
115 0 : MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
116 :
117 0 : nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aWindow);
118 :
119 : // If the pref is not set, don't create the Promise (which the page wouldn't
120 : // be able to get to anyway) as it causes the window.FontFaceSet constructor
121 : // to be created.
122 0 : if (global && PrefEnabled()) {
123 0 : mResolveLazilyCreatedReadyPromise = true;
124 : }
125 :
126 : // Record the state of the "bypass cache" flags from the docshell now,
127 : // since we want to look at them from style worker threads, and we can
128 : // only get to the docshell through a weak pointer (which is only
129 : // possible on the main thread).
130 : //
131 : // In theory the load type of a docshell could change after the document
132 : // is loaded, but handling that doesn't seem too important.
133 0 : if (nsCOMPtr<nsIDocShell> docShell = mDocument->GetDocShell()) {
134 : uint32_t loadType;
135 : uint32_t flags;
136 0 : if ((NS_SUCCEEDED(docShell->GetLoadType(&loadType)) &&
137 0 : ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)) ||
138 0 : (NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags)) &&
139 0 : (flags & nsIRequest::LOAD_BYPASS_CACHE))) {
140 0 : mBypassCache = true;
141 : }
142 : }
143 :
144 : // Same for the "private browsing" flag.
145 0 : if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) {
146 0 : mPrivateBrowsing = loadContext->UsePrivateBrowsing();
147 : }
148 :
149 0 : if (!mDocument->DidFireDOMContentLoaded()) {
150 0 : mDocument->AddSystemEventListener(NS_LITERAL_STRING("DOMContentLoaded"),
151 0 : this, false, false);
152 : }
153 :
154 0 : mDocument->CSSLoader()->AddObserver(this);
155 :
156 0 : mUserFontSet = new UserFontSet(this);
157 0 : }
158 :
159 0 : FontFaceSet::~FontFaceSet()
160 : {
161 : // Assert that we don't drop any FontFaceSet objects during a Servo traversal,
162 : // since PostTraversalTask objects can hold raw pointers to FontFaceSets.
163 0 : MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
164 :
165 0 : Disconnect();
166 0 : for (auto it = mLoaders.Iter(); !it.Done(); it.Next()) {
167 0 : it.Get()->GetKey()->Cancel();
168 : }
169 0 : }
170 :
171 : JSObject*
172 0 : FontFaceSet::WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto)
173 : {
174 0 : return FontFaceSetBinding::Wrap(aContext, this, aGivenProto);
175 : }
176 :
177 : void
178 0 : FontFaceSet::Disconnect()
179 : {
180 0 : RemoveDOMContentLoadedListener();
181 :
182 0 : if (mDocument && mDocument->CSSLoader()) {
183 : // We're null checking CSSLoader() since FontFaceSet::Disconnect() might be
184 : // being called during unlink, at which time the loader amy already have
185 : // been unlinked from the document.
186 0 : mDocument->CSSLoader()->RemoveObserver(this);
187 : }
188 0 : }
189 :
190 : void
191 0 : FontFaceSet::RemoveDOMContentLoadedListener()
192 : {
193 0 : if (mDocument) {
194 0 : mDocument->RemoveSystemEventListener(NS_LITERAL_STRING("DOMContentLoaded"),
195 0 : this, false);
196 : }
197 0 : }
198 :
199 : void
200 0 : FontFaceSet::ParseFontShorthandForMatching(
201 : const nsAString& aFont,
202 : RefPtr<FontFamilyListRefCnt>& aFamilyList,
203 : uint32_t& aWeight,
204 : int32_t& aStretch,
205 : uint8_t& aStyle,
206 : ErrorResult& aRv)
207 : {
208 : // Parse aFont as a 'font' property value.
209 0 : RefPtr<Declaration> declaration = new Declaration;
210 0 : declaration->InitializeEmpty();
211 :
212 0 : bool changed = false;
213 0 : nsCSSParser parser;
214 0 : parser.ParseProperty(eCSSProperty_font,
215 : aFont,
216 : mDocument->GetDocumentURI(),
217 : mDocument->GetDocumentURI(),
218 0 : mDocument->NodePrincipal(),
219 : declaration,
220 : &changed,
221 0 : /* aIsImportant */ false);
222 :
223 : // All of the properties we are interested in should have been set at once.
224 0 : MOZ_ASSERT(changed == (declaration->HasProperty(eCSSProperty_font_family) &&
225 : declaration->HasProperty(eCSSProperty_font_style) &&
226 : declaration->HasProperty(eCSSProperty_font_weight) &&
227 : declaration->HasProperty(eCSSProperty_font_stretch)));
228 :
229 0 : if (!changed) {
230 0 : aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
231 0 : return;
232 : }
233 :
234 0 : nsCSSCompressedDataBlock* data = declaration->GetNormalBlock();
235 0 : MOZ_ASSERT(!declaration->GetImportantBlock());
236 :
237 0 : const nsCSSValue* family = data->ValueFor(eCSSProperty_font_family);
238 0 : if (family->GetUnit() != eCSSUnit_FontFamilyList) {
239 : // We got inherit, initial, unset, a system font, or a token stream.
240 0 : aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
241 0 : return;
242 : }
243 :
244 : aFamilyList =
245 0 : static_cast<FontFamilyListRefCnt*>(family->GetFontFamilyListValue());
246 :
247 0 : int32_t weight = data->ValueFor(eCSSProperty_font_weight)->GetIntValue();
248 :
249 : // Resolve relative font weights against the initial of font-weight
250 : // (normal, which is equivalent to 400).
251 0 : if (weight == NS_STYLE_FONT_WEIGHT_BOLDER) {
252 0 : weight = NS_FONT_WEIGHT_BOLD;
253 0 : } else if (weight == NS_STYLE_FONT_WEIGHT_LIGHTER) {
254 0 : weight = NS_FONT_WEIGHT_THIN;
255 : }
256 :
257 0 : aWeight = weight;
258 :
259 0 : aStretch = data->ValueFor(eCSSProperty_font_stretch)->GetIntValue();
260 0 : aStyle = data->ValueFor(eCSSProperty_font_style)->GetIntValue();
261 : }
262 :
263 : static bool
264 0 : HasAnyCharacterInUnicodeRange(gfxUserFontEntry* aEntry,
265 : const nsAString& aInput)
266 : {
267 0 : const char16_t* p = aInput.Data();
268 0 : const char16_t* end = p + aInput.Length();
269 :
270 0 : while (p < end) {
271 0 : uint32_t c = UTF16CharEnumerator::NextChar(&p, end);
272 0 : if (aEntry->CharacterInUnicodeRange(c)) {
273 0 : return true;
274 : }
275 : }
276 0 : return false;
277 : }
278 :
279 : void
280 0 : FontFaceSet::FindMatchingFontFaces(const nsAString& aFont,
281 : const nsAString& aText,
282 : nsTArray<FontFace*>& aFontFaces,
283 : ErrorResult& aRv)
284 : {
285 0 : RefPtr<FontFamilyListRefCnt> familyList;
286 : uint32_t weight;
287 : int32_t stretch;
288 : uint8_t italicStyle;
289 : ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle,
290 0 : aRv);
291 0 : if (aRv.Failed()) {
292 0 : return;
293 : }
294 :
295 0 : gfxFontStyle style;
296 0 : style.style = italicStyle;
297 0 : style.weight = weight;
298 0 : style.stretch = stretch;
299 :
300 : nsTArray<FontFaceRecord>* arrays[2];
301 0 : arrays[0] = &mNonRuleFaces;
302 0 : arrays[1] = &mRuleFaces;
303 :
304 : // Set of FontFaces that we want to return.
305 0 : nsTHashtable<nsPtrHashKey<FontFace>> matchingFaces;
306 :
307 0 : for (const FontFamilyName& fontFamilyName : familyList->GetFontlist()) {
308 : RefPtr<gfxFontFamily> family =
309 0 : mUserFontSet->LookupFamily(fontFamilyName.mName);
310 :
311 0 : if (!family) {
312 0 : continue;
313 : }
314 :
315 0 : AutoTArray<gfxFontEntry*,4> entries;
316 : bool needsBold;
317 0 : family->FindAllFontsForStyle(style, entries, needsBold);
318 :
319 0 : for (gfxFontEntry* e : entries) {
320 0 : FontFace::Entry* entry = static_cast<FontFace::Entry*>(e);
321 0 : if (HasAnyCharacterInUnicodeRange(entry, aText)) {
322 0 : for (FontFace* f : entry->GetFontFaces()) {
323 0 : matchingFaces.PutEntry(f);
324 : }
325 : }
326 : }
327 : }
328 :
329 : // Add all FontFaces in matchingFaces to aFontFaces, in the order
330 : // they appear in the FontFaceSet.
331 0 : for (nsTArray<FontFaceRecord>* array : arrays) {
332 0 : for (FontFaceRecord& record : *array) {
333 0 : FontFace* f = record.mFontFace;
334 0 : if (matchingFaces.Contains(f)) {
335 0 : aFontFaces.AppendElement(f);
336 : }
337 : }
338 : }
339 : }
340 :
341 : TimeStamp
342 0 : FontFaceSet::GetNavigationStartTimeStamp()
343 : {
344 0 : TimeStamp navStart;
345 0 : RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
346 0 : if (timing) {
347 0 : navStart = timing->GetNavigationStartTimeStamp();
348 : }
349 0 : return navStart;
350 : }
351 :
352 : already_AddRefed<Promise>
353 0 : FontFaceSet::Load(JSContext* aCx,
354 : const nsAString& aFont,
355 : const nsAString& aText,
356 : ErrorResult& aRv)
357 : {
358 0 : FlushUserFontSet();
359 :
360 0 : nsTArray<RefPtr<Promise>> promises;
361 :
362 0 : nsTArray<FontFace*> faces;
363 0 : FindMatchingFontFaces(aFont, aText, faces, aRv);
364 0 : if (aRv.Failed()) {
365 0 : return nullptr;
366 : }
367 :
368 0 : for (FontFace* f : faces) {
369 0 : RefPtr<Promise> promise = f->Load(aRv);
370 0 : if (aRv.Failed()) {
371 0 : return nullptr;
372 : }
373 0 : if (!promises.AppendElement(promise, fallible)) {
374 0 : aRv.Throw(NS_ERROR_FAILURE);
375 0 : return nullptr;
376 : }
377 : }
378 :
379 0 : nsIGlobalObject* globalObject = GetParentObject();
380 0 : if (!globalObject) {
381 0 : aRv.Throw(NS_ERROR_FAILURE);
382 0 : return nullptr;
383 : }
384 :
385 0 : JS::Rooted<JSObject*> jsGlobal(aCx, globalObject->GetGlobalJSObject());
386 0 : GlobalObject global(aCx, jsGlobal);
387 :
388 0 : RefPtr<Promise> result = Promise::All(global, promises, aRv);
389 0 : return result.forget();
390 : }
391 :
392 : bool
393 0 : FontFaceSet::Check(const nsAString& aFont,
394 : const nsAString& aText,
395 : ErrorResult& aRv)
396 : {
397 0 : FlushUserFontSet();
398 :
399 0 : nsTArray<FontFace*> faces;
400 0 : FindMatchingFontFaces(aFont, aText, faces, aRv);
401 0 : if (aRv.Failed()) {
402 0 : return false;
403 : }
404 :
405 0 : for (FontFace* f : faces) {
406 0 : if (f->Status() != FontFaceLoadStatus::Loaded) {
407 0 : return false;
408 : }
409 : }
410 :
411 0 : return true;
412 : }
413 :
414 : Promise*
415 0 : FontFaceSet::GetReady(ErrorResult& aRv)
416 : {
417 0 : MOZ_ASSERT(NS_IsMainThread());
418 :
419 0 : if (!mReady) {
420 0 : nsCOMPtr<nsIGlobalObject> global = GetParentObject();
421 0 : mReady = Promise::Create(global, aRv);
422 0 : if (!mReady) {
423 0 : aRv.Throw(NS_ERROR_FAILURE);
424 0 : return nullptr;
425 : }
426 0 : if (mResolveLazilyCreatedReadyPromise) {
427 0 : mReady->MaybeResolve(this);
428 0 : mResolveLazilyCreatedReadyPromise = false;
429 : }
430 : }
431 :
432 0 : FlushUserFontSet();
433 0 : return mReady;
434 : }
435 :
436 : FontFaceSetLoadStatus
437 0 : FontFaceSet::Status()
438 : {
439 0 : FlushUserFontSet();
440 0 : return mStatus;
441 : }
442 :
443 : #ifdef DEBUG
444 : bool
445 0 : FontFaceSet::HasRuleFontFace(FontFace* aFontFace)
446 : {
447 0 : for (size_t i = 0; i < mRuleFaces.Length(); i++) {
448 0 : if (mRuleFaces[i].mFontFace == aFontFace) {
449 0 : return true;
450 : }
451 : }
452 0 : return false;
453 : }
454 : #endif
455 :
456 : FontFaceSet*
457 0 : FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv)
458 : {
459 0 : FlushUserFontSet();
460 :
461 0 : if (aFontFace.IsInFontFaceSet(this)) {
462 0 : return this;
463 : }
464 :
465 0 : if (aFontFace.HasRule()) {
466 0 : aRv.Throw(NS_ERROR_DOM_INVALID_MODIFICATION_ERR);
467 0 : return nullptr;
468 : }
469 :
470 0 : aFontFace.AddFontFaceSet(this);
471 :
472 : #ifdef DEBUG
473 0 : for (const FontFaceRecord& rec : mNonRuleFaces) {
474 0 : MOZ_ASSERT(rec.mFontFace != &aFontFace,
475 : "FontFace should not occur in mNonRuleFaces twice");
476 : }
477 : #endif
478 :
479 0 : FontFaceRecord* rec = mNonRuleFaces.AppendElement();
480 0 : rec->mFontFace = &aFontFace;
481 0 : rec->mSheetType = SheetType::Unknown; // unused for mNonRuleFaces
482 0 : rec->mLoadEventShouldFire =
483 0 : aFontFace.Status() == FontFaceLoadStatus::Unloaded ||
484 0 : aFontFace.Status() == FontFaceLoadStatus::Loading;
485 :
486 0 : mNonRuleFacesDirty = true;
487 0 : RebuildUserFontSet();
488 0 : mHasLoadingFontFacesIsDirty = true;
489 0 : CheckLoadingStarted();
490 0 : return this;
491 : }
492 :
493 : void
494 0 : FontFaceSet::Clear()
495 : {
496 0 : FlushUserFontSet();
497 :
498 0 : if (mNonRuleFaces.IsEmpty()) {
499 0 : return;
500 : }
501 :
502 0 : for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
503 0 : FontFace* f = mNonRuleFaces[i].mFontFace;
504 0 : f->RemoveFontFaceSet(this);
505 : }
506 :
507 0 : mNonRuleFaces.Clear();
508 0 : mNonRuleFacesDirty = true;
509 0 : RebuildUserFontSet();
510 0 : mHasLoadingFontFacesIsDirty = true;
511 0 : CheckLoadingFinished();
512 : }
513 :
514 : bool
515 0 : FontFaceSet::Delete(FontFace& aFontFace)
516 : {
517 0 : FlushUserFontSet();
518 :
519 0 : if (aFontFace.HasRule()) {
520 0 : return false;
521 : }
522 :
523 0 : bool removed = false;
524 0 : for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
525 0 : if (mNonRuleFaces[i].mFontFace == &aFontFace) {
526 0 : mNonRuleFaces.RemoveElementAt(i);
527 0 : removed = true;
528 0 : break;
529 : }
530 : }
531 0 : if (!removed) {
532 0 : return false;
533 : }
534 :
535 0 : aFontFace.RemoveFontFaceSet(this);
536 :
537 0 : mNonRuleFacesDirty = true;
538 0 : RebuildUserFontSet();
539 0 : mHasLoadingFontFacesIsDirty = true;
540 0 : CheckLoadingFinished();
541 0 : return true;
542 : }
543 :
544 : bool
545 0 : FontFaceSet::HasAvailableFontFace(FontFace* aFontFace)
546 : {
547 0 : return aFontFace->IsInFontFaceSet(this);
548 : }
549 :
550 : bool
551 0 : FontFaceSet::Has(FontFace& aFontFace)
552 : {
553 0 : FlushUserFontSet();
554 :
555 0 : return HasAvailableFontFace(&aFontFace);
556 : }
557 :
558 : FontFace*
559 0 : FontFaceSet::GetFontFaceAt(uint32_t aIndex)
560 : {
561 0 : FlushUserFontSet();
562 :
563 0 : if (aIndex < mRuleFaces.Length()) {
564 0 : return mRuleFaces[aIndex].mFontFace;
565 : }
566 :
567 0 : aIndex -= mRuleFaces.Length();
568 0 : if (aIndex < mNonRuleFaces.Length()) {
569 0 : return mNonRuleFaces[aIndex].mFontFace;
570 : }
571 :
572 0 : return nullptr;
573 : }
574 :
575 : uint32_t
576 0 : FontFaceSet::Size()
577 : {
578 0 : FlushUserFontSet();
579 :
580 : // Web IDL objects can only expose array index properties up to INT32_MAX.
581 :
582 0 : size_t total = mRuleFaces.Length() + mNonRuleFaces.Length();
583 0 : return std::min<size_t>(total, INT32_MAX);
584 : }
585 :
586 : already_AddRefed<FontFaceSetIterator>
587 0 : FontFaceSet::Entries()
588 : {
589 0 : RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, true);
590 0 : return it.forget();
591 : }
592 :
593 : already_AddRefed<FontFaceSetIterator>
594 0 : FontFaceSet::Values()
595 : {
596 0 : RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, false);
597 0 : return it.forget();
598 : }
599 :
600 : void
601 0 : FontFaceSet::ForEach(JSContext* aCx,
602 : FontFaceSetForEachCallback& aCallback,
603 : JS::Handle<JS::Value> aThisArg,
604 : ErrorResult& aRv)
605 : {
606 0 : JS::Rooted<JS::Value> thisArg(aCx, aThisArg);
607 0 : for (size_t i = 0; i < Size(); i++) {
608 0 : FontFace* face = GetFontFaceAt(i);
609 0 : aCallback.Call(thisArg, *face, *face, *this, aRv);
610 0 : if (aRv.Failed()) {
611 0 : return;
612 : }
613 : }
614 : }
615 :
616 : void
617 0 : FontFaceSet::RemoveLoader(nsFontFaceLoader* aLoader)
618 : {
619 0 : mLoaders.RemoveEntry(aLoader);
620 0 : }
621 :
622 : nsresult
623 0 : FontFaceSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
624 : const gfxFontFaceSrc* aFontFaceSrc)
625 : {
626 : nsresult rv;
627 :
628 0 : nsCOMPtr<nsIStreamLoader> streamLoader;
629 0 : nsCOMPtr<nsILoadGroup> loadGroup(mDocument->GetDocumentLoadGroup());
630 0 : gfxFontSrcPrincipal* principal = aUserFontEntry->GetPrincipal();
631 :
632 0 : nsCOMPtr<nsIChannel> channel;
633 : // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a
634 : // node and a principal. This is because the document where the font is
635 : // being loaded might have a different origin from the principal of the
636 : // stylesheet that initiated the font load.
637 0 : rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
638 : aFontFaceSrc->mURI->get(),
639 : mDocument,
640 : principal ? principal->get() : nullptr,
641 : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
642 : nsIContentPolicy::TYPE_FONT,
643 0 : loadGroup);
644 0 : NS_ENSURE_SUCCESS(rv, rv);
645 :
646 : RefPtr<nsFontFaceLoader> fontLoader =
647 0 : new nsFontFaceLoader(aUserFontEntry, aFontFaceSrc->mURI->get(), this,
648 0 : channel);
649 :
650 0 : if (LOG_ENABLED()) {
651 0 : LOG(("userfonts (%p) download start - font uri: (%s) "
652 : "referrer uri: (%s)\n",
653 : fontLoader.get(), aFontFaceSrc->mURI->GetSpecOrDefault().get(),
654 : aFontFaceSrc->mReferrer
655 : ? aFontFaceSrc->mReferrer->GetSpecOrDefault().get()
656 : : ""));
657 : }
658 :
659 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
660 0 : if (httpChannel) {
661 0 : rv = httpChannel->SetReferrerWithPolicy(aFontFaceSrc->mReferrer,
662 0 : mDocument->GetReferrerPolicy());
663 0 : Unused << NS_WARN_IF(NS_FAILED(rv));
664 :
665 0 : nsAutoCString accept("application/font-woff;q=0.9,*/*;q=0.8");
666 0 : if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED)) {
667 0 : accept.Insert(NS_LITERAL_CSTRING("application/font-woff2;q=1.0,"), 0);
668 : }
669 0 : rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
670 0 : accept, false);
671 0 : NS_ENSURE_SUCCESS(rv, rv);
672 :
673 : // For WOFF and WOFF2, we should tell servers/proxies/etc NOT to try
674 : // and apply additional compression at the content-encoding layer
675 0 : if (aFontFaceSrc->mFormatFlags & (gfxUserFontSet::FLAG_FORMAT_WOFF |
676 : gfxUserFontSet::FLAG_FORMAT_WOFF2)) {
677 0 : rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept-Encoding"),
678 0 : NS_LITERAL_CSTRING("identity"), false);
679 0 : NS_ENSURE_SUCCESS(rv, rv);
680 : }
681 : }
682 0 : nsCOMPtr<nsISupportsPriority> priorityChannel(do_QueryInterface(channel));
683 0 : if (priorityChannel) {
684 0 : priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGH);
685 : }
686 :
687 0 : rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader);
688 0 : NS_ENSURE_SUCCESS(rv, rv);
689 :
690 0 : mozilla::net::PredictorLearn(aFontFaceSrc->mURI->get(),
691 : mDocument->GetDocumentURI(),
692 : nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
693 0 : loadGroup);
694 :
695 0 : rv = channel->AsyncOpen2(streamLoader);
696 0 : if (NS_FAILED(rv)) {
697 0 : fontLoader->DropChannel(); // explicitly need to break ref cycle
698 : }
699 :
700 0 : if (NS_SUCCEEDED(rv)) {
701 0 : mLoaders.PutEntry(fontLoader);
702 0 : fontLoader->StartedLoading(streamLoader);
703 0 : aUserFontEntry->SetLoader(fontLoader); // let the font entry remember the
704 : // loader, in case we need to cancel it
705 : }
706 :
707 0 : return rv;
708 : }
709 :
710 : bool
711 0 : FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
712 : {
713 0 : MOZ_ASSERT(mUserFontSet);
714 :
715 : // If there was a change to the mNonRuleFaces array, then there could
716 : // have been a modification to the user font set.
717 0 : bool modified = mNonRuleFacesDirty;
718 0 : mNonRuleFacesDirty = false;
719 :
720 : // reuse existing FontFace objects mapped to rules already
721 0 : nsDataHashtable<nsPtrHashKey<nsCSSFontFaceRule>, FontFace*> ruleFaceMap;
722 0 : for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) {
723 0 : FontFace* f = mRuleFaces[i].mFontFace;
724 0 : if (!f) {
725 0 : continue;
726 : }
727 0 : ruleFaceMap.Put(f->GetRule(), f);
728 : }
729 :
730 : // The @font-face rules that make up the user font set have changed,
731 : // so we need to update the set. However, we want to preserve existing
732 : // font entries wherever possible, so that we don't discard and then
733 : // re-download resources in the (common) case where at least some of the
734 : // same rules are still present.
735 :
736 0 : nsTArray<FontFaceRecord> oldRecords;
737 0 : mRuleFaces.SwapElements(oldRecords);
738 :
739 : // Remove faces from the font family records; we need to re-insert them
740 : // because we might end up with faces in a different order even if they're
741 : // the same font entries as before. (The order can affect font selection
742 : // where multiple faces match the requested style, perhaps with overlapping
743 : // unicode-range coverage.)
744 0 : for (auto it = mUserFontSet->mFontFamilies.Iter(); !it.Done(); it.Next()) {
745 0 : it.Data()->DetachFontEntries();
746 : }
747 :
748 : // Sometimes aRules has duplicate @font-face rules in it; we should make
749 : // that not happen, but in the meantime, don't try to insert the same
750 : // FontFace object more than once into mRuleFaces. We track which
751 : // ones we've handled in this table.
752 0 : nsTHashtable<nsPtrHashKey<nsCSSFontFaceRule>> handledRules;
753 :
754 0 : for (size_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
755 : // Insert each FontFace objects for each rule into our list, migrating old
756 : // font entries if possible rather than creating new ones; set modified to
757 : // true if we detect that rule ordering has changed, or if a new entry is
758 : // created.
759 0 : nsCSSFontFaceRule* rule = aRules[i].mRule;
760 0 : if (!handledRules.EnsureInserted(rule)) {
761 : // rule was already present in the hashtable
762 0 : continue;
763 : }
764 0 : RefPtr<FontFace> f = ruleFaceMap.Get(rule);
765 0 : if (!f.get()) {
766 0 : f = FontFace::CreateForRule(GetParentObject(), this, rule);
767 : }
768 0 : InsertRuleFontFace(f, aRules[i].mSheetType, oldRecords, modified);
769 : }
770 :
771 0 : for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
772 : // Do the same for the non rule backed FontFace objects.
773 0 : InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace, modified);
774 : }
775 :
776 : // Remove any residual families that have no font entries (i.e., they were
777 : // not defined at all by the updated set of @font-face rules).
778 0 : for (auto it = mUserFontSet->mFontFamilies.Iter(); !it.Done(); it.Next()) {
779 0 : if (it.Data()->GetFontList().IsEmpty()) {
780 0 : it.Remove();
781 : }
782 : }
783 :
784 : // If any FontFace objects for rules are left in the old list, note that the
785 : // set has changed (even if the new set was built entirely by migrating old
786 : // font entries).
787 0 : if (oldRecords.Length() > 0) {
788 0 : modified = true;
789 : // Any in-progress loaders for obsolete rules should be cancelled,
790 : // as the resource being downloaded will no longer be required.
791 : // We need to explicitly remove any loaders here, otherwise the loaders
792 : // will keep their "orphaned" font entries alive until they complete,
793 : // even after the oldRules array is deleted.
794 : //
795 : // XXX Now that it is possible for the author to hold on to a rule backed
796 : // FontFace object, we shouldn't cancel loading here; instead we should do
797 : // it when the FontFace is GCed, if we can detect that.
798 0 : size_t count = oldRecords.Length();
799 0 : for (size_t i = 0; i < count; ++i) {
800 0 : RefPtr<FontFace> f = oldRecords[i].mFontFace;
801 0 : gfxUserFontEntry* userFontEntry = f->GetUserFontEntry();
802 0 : if (userFontEntry) {
803 0 : nsFontFaceLoader* loader = userFontEntry->GetLoader();
804 0 : if (loader) {
805 0 : loader->Cancel();
806 0 : RemoveLoader(loader);
807 : }
808 : }
809 :
810 : // Any left over FontFace objects should also cease being rule backed.
811 0 : f->DisconnectFromRule();
812 : }
813 : }
814 :
815 0 : if (modified) {
816 0 : IncrementGeneration(true);
817 0 : mHasLoadingFontFacesIsDirty = true;
818 0 : CheckLoadingStarted();
819 0 : CheckLoadingFinished();
820 : }
821 :
822 : // if local rules needed to be rebuilt, they have been rebuilt at this point
823 0 : if (mUserFontSet->mRebuildLocalRules) {
824 0 : mUserFontSet->mLocalRulesUsed = false;
825 0 : mUserFontSet->mRebuildLocalRules = false;
826 : }
827 :
828 0 : if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
829 0 : LOG(("userfonts (%p) userfont rules update (%s) rule count: %d",
830 : mUserFontSet.get(),
831 : (modified ? "modified" : "not modified"),
832 : (int)(mRuleFaces.Length())));
833 : }
834 :
835 0 : return modified;
836 : }
837 :
838 : static bool
839 0 : HasLocalSrc(const nsCSSValue::Array *aSrcArr)
840 : {
841 0 : size_t numSrc = aSrcArr->Count();
842 0 : for (size_t i = 0; i < numSrc; i++) {
843 0 : if (aSrcArr->Item(i).GetUnit() == eCSSUnit_Local_Font) {
844 0 : return true;
845 : }
846 : }
847 0 : return false;
848 : }
849 :
850 : void
851 0 : FontFaceSet::IncrementGeneration(bool aIsRebuild)
852 : {
853 0 : MOZ_ASSERT(mUserFontSet);
854 0 : mUserFontSet->IncrementGeneration(aIsRebuild);
855 0 : }
856 :
857 : void
858 0 : FontFaceSet::InsertNonRuleFontFace(FontFace* aFontFace,
859 : bool& aFontSetModified)
860 : {
861 0 : nsAutoString fontfamily;
862 0 : if (!aFontFace->GetFamilyName(fontfamily)) {
863 : // If there is no family name, this rule cannot contribute a
864 : // usable font, so there is no point in processing it further.
865 0 : return;
866 : }
867 :
868 : // Just create a new font entry if we haven't got one already.
869 0 : if (!aFontFace->GetUserFontEntry()) {
870 : // XXX Should we be checking mUserFontSet->mLocalRulesUsed like
871 : // InsertRuleFontFace does?
872 : RefPtr<gfxUserFontEntry> entry =
873 0 : FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace,
874 0 : SheetType::Doc);
875 0 : if (!entry) {
876 0 : return;
877 : }
878 0 : aFontFace->SetUserFontEntry(entry);
879 : }
880 :
881 0 : aFontSetModified = true;
882 0 : mUserFontSet->AddUserFontEntry(fontfamily, aFontFace->GetUserFontEntry());
883 : }
884 :
885 : void
886 0 : FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType,
887 : nsTArray<FontFaceRecord>& aOldRecords,
888 : bool& aFontSetModified)
889 : {
890 0 : nsAutoString fontfamily;
891 0 : if (!aFontFace->GetFamilyName(fontfamily)) {
892 : // If there is no family name, this rule cannot contribute a
893 : // usable font, so there is no point in processing it further.
894 0 : return;
895 : }
896 :
897 0 : bool remove = false;
898 : size_t removeIndex;
899 :
900 : // This is a rule backed FontFace. First, we check in aOldRecords; if
901 : // the FontFace for the rule exists there, just move it to the new record
902 : // list, and put the entry into the appropriate family.
903 0 : for (size_t i = 0; i < aOldRecords.Length(); ++i) {
904 0 : FontFaceRecord& rec = aOldRecords[i];
905 :
906 0 : if (rec.mFontFace == aFontFace &&
907 0 : rec.mSheetType == aSheetType) {
908 :
909 : // if local rules were used, don't use the old font entry
910 : // for rules containing src local usage
911 0 : if (mUserFontSet->mLocalRulesUsed &&
912 0 : mUserFontSet->mRebuildLocalRules) {
913 0 : nsCSSValue val;
914 0 : aFontFace->GetDesc(eCSSFontDesc_Src, val);
915 0 : nsCSSUnit unit = val.GetUnit();
916 0 : if (unit == eCSSUnit_Array && HasLocalSrc(val.GetArrayValue())) {
917 : // Remove the old record, but wait to see if we successfully create a
918 : // new user font entry below.
919 0 : remove = true;
920 0 : removeIndex = i;
921 0 : break;
922 : }
923 : }
924 :
925 0 : gfxUserFontEntry* entry = rec.mFontFace->GetUserFontEntry();
926 0 : MOZ_ASSERT(entry, "FontFace should have a gfxUserFontEntry by now");
927 :
928 0 : mUserFontSet->AddUserFontEntry(fontfamily, entry);
929 :
930 0 : MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace),
931 : "FontFace should not occur in mRuleFaces twice");
932 :
933 0 : mRuleFaces.AppendElement(rec);
934 0 : aOldRecords.RemoveElementAt(i);
935 : // note the set has been modified if an old rule was skipped to find
936 : // this one - something has been dropped, or ordering changed
937 0 : if (i > 0) {
938 0 : aFontSetModified = true;
939 : }
940 0 : return;
941 : }
942 : }
943 :
944 : // this is a new rule:
945 : RefPtr<gfxUserFontEntry> entry =
946 0 : FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace, aSheetType);
947 :
948 0 : if (!entry) {
949 0 : return;
950 : }
951 :
952 0 : if (remove) {
953 : // Although we broke out of the aOldRecords loop above, since we found
954 : // src local usage, and we're not using the old user font entry, we still
955 : // are adding a record to mRuleFaces with the same FontFace object.
956 : // Remove the old record so that we don't have the same FontFace listed
957 : // in both mRuleFaces and oldRecords, which would cause us to call
958 : // DisconnectFromRule on a FontFace that should still be rule backed.
959 0 : aOldRecords.RemoveElementAt(removeIndex);
960 : }
961 :
962 0 : FontFaceRecord rec;
963 0 : rec.mFontFace = aFontFace;
964 0 : rec.mSheetType = aSheetType;
965 0 : rec.mLoadEventShouldFire =
966 0 : aFontFace->Status() == FontFaceLoadStatus::Unloaded ||
967 0 : aFontFace->Status() == FontFaceLoadStatus::Loading;
968 :
969 0 : aFontFace->SetUserFontEntry(entry);
970 :
971 0 : MOZ_ASSERT(!HasRuleFontFace(aFontFace),
972 : "FontFace should not occur in mRuleFaces twice");
973 :
974 0 : mRuleFaces.AppendElement(rec);
975 :
976 : // this was a new rule and font entry, so note that the set was modified
977 0 : aFontSetModified = true;
978 :
979 : // Add the entry to the end of the list. If an existing userfont entry was
980 : // returned by FindOrCreateUserFontEntryFromFontFace that was already stored
981 : // on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry
982 : // calls, will automatically remove the earlier occurrence of the same
983 : // userfont entry.
984 0 : mUserFontSet->AddUserFontEntry(fontfamily, entry);
985 : }
986 :
987 : already_AddRefed<gfxUserFontEntry>
988 0 : FontFaceSet::FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace)
989 : {
990 0 : nsAutoString fontfamily;
991 0 : if (!aFontFace->GetFamilyName(fontfamily)) {
992 : // If there is no family name, this rule cannot contribute a
993 : // usable font, so there is no point in processing it further.
994 0 : return nullptr;
995 : }
996 :
997 : return FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace,
998 0 : SheetType::Doc);
999 : }
1000 :
1001 : already_AddRefed<gfxUserFontEntry>
1002 0 : FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName,
1003 : FontFace* aFontFace,
1004 : SheetType aSheetType)
1005 : {
1006 0 : nsCSSValue val;
1007 : nsCSSUnit unit;
1008 :
1009 0 : uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL;
1010 0 : int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
1011 0 : uint8_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
1012 0 : uint32_t languageOverride = NO_FONT_LANGUAGE_OVERRIDE;
1013 0 : uint8_t fontDisplay = NS_FONT_DISPLAY_AUTO;
1014 :
1015 : // set up weight
1016 0 : aFontFace->GetDesc(eCSSFontDesc_Weight, val);
1017 0 : unit = val.GetUnit();
1018 0 : if (unit == eCSSUnit_Integer || unit == eCSSUnit_Enumerated) {
1019 0 : weight = val.GetIntValue();
1020 0 : if (weight == 0) {
1021 0 : weight = NS_STYLE_FONT_WEIGHT_NORMAL;
1022 : }
1023 0 : } else if (unit == eCSSUnit_Normal) {
1024 0 : weight = NS_STYLE_FONT_WEIGHT_NORMAL;
1025 : } else {
1026 0 : NS_ASSERTION(unit == eCSSUnit_Null,
1027 : "@font-face weight has unexpected unit");
1028 : }
1029 :
1030 : // set up stretch
1031 0 : aFontFace->GetDesc(eCSSFontDesc_Stretch, val);
1032 0 : unit = val.GetUnit();
1033 0 : if (unit == eCSSUnit_Enumerated) {
1034 0 : stretch = val.GetIntValue();
1035 0 : } else if (unit == eCSSUnit_Normal) {
1036 0 : stretch = NS_STYLE_FONT_STRETCH_NORMAL;
1037 : } else {
1038 0 : NS_ASSERTION(unit == eCSSUnit_Null,
1039 : "@font-face stretch has unexpected unit");
1040 : }
1041 :
1042 : // set up font style
1043 0 : aFontFace->GetDesc(eCSSFontDesc_Style, val);
1044 0 : unit = val.GetUnit();
1045 0 : if (unit == eCSSUnit_Enumerated) {
1046 0 : italicStyle = val.GetIntValue();
1047 0 : } else if (unit == eCSSUnit_Normal) {
1048 0 : italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
1049 : } else {
1050 0 : NS_ASSERTION(unit == eCSSUnit_Null,
1051 : "@font-face style has unexpected unit");
1052 : }
1053 :
1054 : // set up font display
1055 0 : aFontFace->GetDesc(eCSSFontDesc_Display, val);
1056 0 : unit = val.GetUnit();
1057 0 : if (unit == eCSSUnit_Enumerated) {
1058 0 : fontDisplay = val.GetIntValue();
1059 : } else {
1060 0 : NS_ASSERTION(unit == eCSSUnit_Null,
1061 : "@font-face style has unexpected unit");
1062 : }
1063 :
1064 : // set up font features
1065 0 : nsTArray<gfxFontFeature> featureSettings;
1066 0 : aFontFace->GetDesc(eCSSFontDesc_FontFeatureSettings, val);
1067 0 : unit = val.GetUnit();
1068 0 : if (unit == eCSSUnit_Normal) {
1069 : // empty list of features
1070 0 : } else if (unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep) {
1071 0 : nsRuleNode::ComputeFontFeatures(val.GetPairListValue(), featureSettings);
1072 : } else {
1073 0 : NS_ASSERTION(unit == eCSSUnit_Null,
1074 : "@font-face font-feature-settings has unexpected unit");
1075 : }
1076 :
1077 : // set up font language override
1078 0 : aFontFace->GetDesc(eCSSFontDesc_FontLanguageOverride, val);
1079 0 : unit = val.GetUnit();
1080 0 : if (unit == eCSSUnit_Normal) {
1081 : // empty feature string
1082 0 : } else if (unit == eCSSUnit_String) {
1083 0 : nsString stringValue;
1084 0 : val.GetStringValue(stringValue);
1085 0 : languageOverride = nsRuleNode::ParseFontLanguageOverride(stringValue);
1086 : } else {
1087 0 : NS_ASSERTION(unit == eCSSUnit_Null,
1088 : "@font-face font-language-override has unexpected unit");
1089 : }
1090 :
1091 : // set up unicode-range
1092 0 : gfxCharacterMap* unicodeRanges = aFontFace->GetUnicodeRangeAsCharacterMap();
1093 :
1094 : // set up src array
1095 0 : nsTArray<gfxFontFaceSrc> srcArray;
1096 :
1097 0 : if (aFontFace->HasFontData()) {
1098 0 : gfxFontFaceSrc* face = srcArray.AppendElement();
1099 0 : if (!face)
1100 0 : return nullptr;
1101 :
1102 0 : face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
1103 0 : face->mBuffer = aFontFace->CreateBufferSource();
1104 : } else {
1105 0 : aFontFace->GetDesc(eCSSFontDesc_Src, val);
1106 0 : unit = val.GetUnit();
1107 0 : if (unit == eCSSUnit_Array) {
1108 0 : nsCSSValue::Array* srcArr = val.GetArrayValue();
1109 0 : size_t numSrc = srcArr->Count();
1110 :
1111 0 : for (size_t i = 0; i < numSrc; i++) {
1112 0 : val = srcArr->Item(i);
1113 0 : unit = val.GetUnit();
1114 0 : gfxFontFaceSrc* face = srcArray.AppendElements(1);
1115 0 : if (!face)
1116 0 : return nullptr;
1117 :
1118 0 : switch (unit) {
1119 :
1120 : case eCSSUnit_Local_Font:
1121 0 : val.GetStringValue(face->mLocalName);
1122 0 : face->mSourceType = gfxFontFaceSrc::eSourceType_Local;
1123 0 : face->mURI = nullptr;
1124 0 : face->mFormatFlags = 0;
1125 0 : break;
1126 : case eCSSUnit_URL: {
1127 0 : face->mSourceType = gfxFontFaceSrc::eSourceType_URL;
1128 0 : nsIURI* uri = val.GetURLValue();
1129 0 : face->mURI = uri ? new gfxFontSrcURI(uri) : nullptr;
1130 0 : URLValue* url = val.GetURLStructValue();
1131 0 : face->mReferrer = url->mExtraData->GetReferrer();
1132 0 : face->mReferrerPolicy = mDocument->GetReferrerPolicy();
1133 : face->mOriginPrincipal =
1134 0 : new gfxFontSrcPrincipal(url->mExtraData->GetPrincipal());
1135 0 : NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
1136 :
1137 : // agent and user stylesheets are treated slightly differently,
1138 : // the same-site origin check and access control headers are
1139 : // enforced against the sheet principal rather than the document
1140 : // principal to allow user stylesheets to include @font-face rules
1141 0 : face->mUseOriginPrincipal = (aSheetType == SheetType::User ||
1142 : aSheetType == SheetType::Agent);
1143 :
1144 0 : face->mLocalName.Truncate();
1145 0 : face->mFormatFlags = 0;
1146 :
1147 0 : while (i + 1 < numSrc) {
1148 0 : val = srcArr->Item(i + 1);
1149 0 : if (val.GetUnit() != eCSSUnit_Font_Format)
1150 0 : break;
1151 :
1152 0 : nsDependentString valueString(val.GetStringBufferValue());
1153 0 : if (valueString.LowerCaseEqualsASCII("woff")) {
1154 0 : face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF;
1155 0 : } else if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED) &&
1156 0 : valueString.LowerCaseEqualsASCII("woff2")) {
1157 0 : face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF2;
1158 0 : } else if (valueString.LowerCaseEqualsASCII("opentype")) {
1159 0 : face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_OPENTYPE;
1160 0 : } else if (valueString.LowerCaseEqualsASCII("truetype")) {
1161 0 : face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE;
1162 0 : } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
1163 0 : face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT;
1164 0 : } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
1165 0 : face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_EOT;
1166 0 : } else if (valueString.LowerCaseEqualsASCII("svg")) {
1167 0 : face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_SVG;
1168 : } else {
1169 : // unknown format specified, mark to distinguish from the
1170 : // case where no format hints are specified
1171 0 : face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_UNKNOWN;
1172 : }
1173 0 : i++;
1174 : }
1175 0 : if (!face->mURI) {
1176 : // if URI not valid, omit from src array
1177 0 : srcArray.RemoveElementAt(srcArray.Length() - 1);
1178 0 : NS_WARNING("null url in @font-face rule");
1179 0 : continue;
1180 : }
1181 0 : break;
1182 : }
1183 : default:
1184 0 : NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
1185 : "strange unit type in font-face src array");
1186 0 : break;
1187 : }
1188 : }
1189 : } else {
1190 0 : NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
1191 : }
1192 : }
1193 :
1194 0 : if (srcArray.IsEmpty()) {
1195 0 : return nullptr;
1196 : }
1197 :
1198 : RefPtr<gfxUserFontEntry> entry =
1199 0 : mUserFontSet->FindOrCreateUserFontEntry(aFamilyName, srcArray, weight,
1200 : stretch, italicStyle,
1201 : featureSettings,
1202 : languageOverride,
1203 0 : unicodeRanges, fontDisplay);
1204 0 : return entry.forget();
1205 : }
1206 :
1207 : nsCSSFontFaceRule*
1208 0 : FontFaceSet::FindRuleForEntry(gfxFontEntry* aFontEntry)
1209 : {
1210 0 : NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
1211 0 : for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
1212 0 : FontFace* f = mRuleFaces[i].mFontFace;
1213 0 : gfxUserFontEntry* entry = f->GetUserFontEntry();
1214 0 : if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
1215 0 : return f->GetRule();
1216 : }
1217 : }
1218 0 : return nullptr;
1219 : }
1220 :
1221 : nsCSSFontFaceRule*
1222 0 : FontFaceSet::FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry)
1223 : {
1224 0 : for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
1225 0 : FontFace* f = mRuleFaces[i].mFontFace;
1226 0 : if (f->GetUserFontEntry() == aUserFontEntry) {
1227 0 : return f->GetRule();
1228 : }
1229 : }
1230 0 : return nullptr;
1231 : }
1232 :
1233 : nsresult
1234 0 : FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
1235 : const char* aMessage,
1236 : uint32_t aFlags,
1237 : nsresult aStatus)
1238 : {
1239 : nsCOMPtr<nsIConsoleService>
1240 0 : console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
1241 0 : if (!console) {
1242 0 : return NS_ERROR_NOT_AVAILABLE;
1243 : }
1244 :
1245 0 : nsAutoCString familyName;
1246 0 : nsAutoCString fontURI;
1247 0 : aUserFontEntry->GetFamilyNameAndURIForLogging(familyName, fontURI);
1248 :
1249 : char weightKeywordBuf[8]; // plenty to sprintf() a uint16_t
1250 : const char* weightKeyword;
1251 : const nsCString& weightKeywordString =
1252 0 : nsCSSProps::ValueToKeyword(aUserFontEntry->Weight(),
1253 0 : nsCSSProps::kFontWeightKTable);
1254 0 : if (weightKeywordString.Length() > 0) {
1255 0 : weightKeyword = weightKeywordString.get();
1256 : } else {
1257 0 : SprintfLiteral(weightKeywordBuf, "%u", aUserFontEntry->Weight());
1258 0 : weightKeyword = weightKeywordBuf;
1259 : }
1260 :
1261 : nsPrintfCString message
1262 : ("downloadable font: %s "
1263 : "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
1264 : aMessage,
1265 : familyName.get(),
1266 0 : aUserFontEntry->IsItalic() ? "italic" : "normal",
1267 : weightKeyword,
1268 0 : nsCSSProps::ValueToKeyword(aUserFontEntry->Stretch(),
1269 0 : nsCSSProps::kFontStretchKTable).get(),
1270 0 : aUserFontEntry->GetSrcIndex());
1271 :
1272 0 : if (NS_FAILED(aStatus)) {
1273 0 : message.AppendLiteral(": ");
1274 0 : switch (aStatus) {
1275 : case NS_ERROR_DOM_BAD_URI:
1276 0 : message.AppendLiteral("bad URI or cross-site access not allowed");
1277 0 : break;
1278 : case NS_ERROR_CONTENT_BLOCKED:
1279 0 : message.AppendLiteral("content blocked");
1280 0 : break;
1281 : default:
1282 0 : message.AppendLiteral("status=");
1283 0 : message.AppendInt(static_cast<uint32_t>(aStatus));
1284 0 : break;
1285 : }
1286 : }
1287 0 : message.AppendLiteral(" source: ");
1288 0 : message.Append(fontURI);
1289 :
1290 0 : if (LOG_ENABLED()) {
1291 0 : LOG(("userfonts (%p) %s", mUserFontSet.get(), message.get()));
1292 : }
1293 :
1294 : // try to give the user an indication of where the rule came from
1295 0 : nsCSSFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
1296 0 : nsString href;
1297 0 : nsString text;
1298 : nsresult rv;
1299 0 : uint32_t line = 0;
1300 0 : uint32_t column = 0;
1301 0 : if (rule) {
1302 0 : rv = rule->GetCssText(text);
1303 0 : NS_ENSURE_SUCCESS(rv, rv);
1304 0 : StyleSheet* sheet = rule->GetStyleSheet();
1305 : // if the style sheet is removed while the font is loading can be null
1306 0 : if (sheet) {
1307 0 : nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault();
1308 0 : CopyUTF8toUTF16(spec, href);
1309 : } else {
1310 0 : NS_WARNING("null parent stylesheet for @font-face rule");
1311 0 : href.AssignLiteral("unknown");
1312 : }
1313 0 : line = rule->GetLineNumber();
1314 0 : column = rule->GetColumnNumber();
1315 : }
1316 :
1317 : nsCOMPtr<nsIScriptError> scriptError =
1318 0 : do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
1319 0 : NS_ENSURE_SUCCESS(rv, rv);
1320 :
1321 0 : uint64_t innerWindowID = mDocument->InnerWindowID();
1322 0 : rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
1323 : href, // file
1324 : text, // src line
1325 : line,
1326 : column,
1327 : aFlags, // flags
1328 : "CSS Loader", // category (make separate?)
1329 : innerWindowID);
1330 0 : if (NS_SUCCEEDED(rv)) {
1331 0 : console->LogMessage(scriptError);
1332 : }
1333 :
1334 0 : return NS_OK;
1335 : }
1336 :
1337 : gfxFontSrcPrincipal*
1338 0 : FontFaceSet::GetStandardFontLoadPrincipal()
1339 : {
1340 0 : if (!ServoStyleSet::IsInServoTraversal()) {
1341 0 : UpdateStandardFontLoadPrincipal();
1342 : }
1343 :
1344 0 : return mStandardFontLoadPrincipal;
1345 : }
1346 :
1347 : nsresult
1348 0 : FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
1349 : gfxFontSrcPrincipal** aPrincipal,
1350 : bool* aBypassCache)
1351 : {
1352 0 : NS_ASSERTION(aFontFaceSrc &&
1353 : aFontFaceSrc->mSourceType == gfxFontFaceSrc::eSourceType_URL,
1354 : "bad font face url passed to fontloader");
1355 :
1356 : // check same-site origin
1357 :
1358 0 : NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
1359 0 : if (!aFontFaceSrc->mURI)
1360 0 : return NS_ERROR_FAILURE;
1361 :
1362 : // use document principal, original principal if flag set
1363 : // this enables user stylesheets to load font files via
1364 : // @font-face rules
1365 0 : *aPrincipal = GetStandardFontLoadPrincipal();
1366 :
1367 0 : NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
1368 : "null origin principal in @font-face rule");
1369 0 : if (aFontFaceSrc->mUseOriginPrincipal) {
1370 0 : *aPrincipal = aFontFaceSrc->mOriginPrincipal;
1371 : }
1372 :
1373 0 : *aBypassCache = mBypassCache;
1374 :
1375 0 : return NS_OK;
1376 : }
1377 :
1378 : // @arg aPrincipal: generally this is mDocument->NodePrincipal() but
1379 : // might also be the original principal which enables user stylesheets
1380 : // to load font files via @font-face rules.
1381 : bool
1382 0 : FontFaceSet::IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal)
1383 : {
1384 0 : int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1385 0 : nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_FONT,
1386 : aFontLocation,
1387 : aPrincipal,
1388 : mDocument,
1389 0 : EmptyCString(), // mime type
1390 : nullptr, // aExtra
1391 : &shouldLoad,
1392 : nsContentUtils::GetContentPolicy(),
1393 0 : nsContentUtils::GetSecurityManager());
1394 :
1395 0 : return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
1396 : }
1397 :
1398 : nsresult
1399 0 : FontFaceSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
1400 : const gfxFontFaceSrc* aFontFaceSrc,
1401 : uint8_t*& aBuffer,
1402 : uint32_t& aBufferLength)
1403 : {
1404 : nsresult rv;
1405 :
1406 0 : gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal();
1407 :
1408 0 : nsCOMPtr<nsIChannel> channel;
1409 : // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a
1410 : // node and a principal. This is because the document where the font is
1411 : // being loaded might have a different origin from the principal of the
1412 : // stylesheet that initiated the font load.
1413 : // Further, we only get here for data: loads, so it doesn't really matter
1414 : // whether we use SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS or not, to be more
1415 : // restrictive we use SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS.
1416 0 : rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
1417 : aFontFaceSrc->mURI->get(),
1418 : mDocument,
1419 : principal ? principal->get() : nullptr,
1420 : nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
1421 0 : nsIContentPolicy::TYPE_FONT);
1422 :
1423 0 : NS_ENSURE_SUCCESS(rv, rv);
1424 :
1425 : // blocking stream is OK for data URIs
1426 0 : nsCOMPtr<nsIInputStream> stream;
1427 0 : rv = channel->Open2(getter_AddRefs(stream));
1428 0 : NS_ENSURE_SUCCESS(rv, rv);
1429 :
1430 : uint64_t bufferLength64;
1431 0 : rv = stream->Available(&bufferLength64);
1432 0 : NS_ENSURE_SUCCESS(rv, rv);
1433 0 : if (bufferLength64 == 0) {
1434 0 : return NS_ERROR_FAILURE;
1435 : }
1436 0 : if (bufferLength64 > UINT32_MAX) {
1437 0 : return NS_ERROR_FILE_TOO_BIG;
1438 : }
1439 0 : aBufferLength = static_cast<uint32_t>(bufferLength64);
1440 :
1441 : // read all the decoded data
1442 0 : aBuffer = static_cast<uint8_t*> (moz_xmalloc(sizeof(uint8_t) * aBufferLength));
1443 0 : if (!aBuffer) {
1444 0 : aBufferLength = 0;
1445 0 : return NS_ERROR_OUT_OF_MEMORY;
1446 : }
1447 :
1448 0 : uint32_t numRead, totalRead = 0;
1449 0 : while (NS_SUCCEEDED(rv =
1450 : stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
1451 0 : aBufferLength - totalRead, &numRead)) &&
1452 0 : numRead != 0)
1453 : {
1454 0 : totalRead += numRead;
1455 0 : if (totalRead > aBufferLength) {
1456 0 : rv = NS_ERROR_FAILURE;
1457 0 : break;
1458 : }
1459 : }
1460 :
1461 : // make sure there's a mime type
1462 0 : if (NS_SUCCEEDED(rv)) {
1463 0 : nsAutoCString mimeType;
1464 0 : rv = channel->GetContentType(mimeType);
1465 0 : aBufferLength = totalRead;
1466 : }
1467 :
1468 0 : if (NS_FAILED(rv)) {
1469 0 : free(aBuffer);
1470 0 : aBuffer = nullptr;
1471 0 : aBufferLength = 0;
1472 0 : return rv;
1473 : }
1474 :
1475 0 : return NS_OK;
1476 : }
1477 :
1478 : void
1479 0 : FontFaceSet::OnFontFaceStatusChanged(FontFace* aFontFace)
1480 : {
1481 0 : AssertIsMainThreadOrServoFontMetricsLocked();
1482 :
1483 0 : MOZ_ASSERT(HasAvailableFontFace(aFontFace));
1484 :
1485 0 : mHasLoadingFontFacesIsDirty = true;
1486 :
1487 0 : if (aFontFace->Status() == FontFaceLoadStatus::Loading) {
1488 0 : CheckLoadingStarted();
1489 : } else {
1490 0 : MOZ_ASSERT(aFontFace->Status() == FontFaceLoadStatus::Loaded ||
1491 : aFontFace->Status() == FontFaceLoadStatus::Error);
1492 : // When a font finishes downloading, nsPresContext::UserFontSetUpdated
1493 : // will be called immediately afterwards to request a reflow of the
1494 : // relevant elements in the document. We want to wait until the reflow
1495 : // request has been done before the FontFaceSet is marked as Loaded so
1496 : // that we don't briefly set the FontFaceSet to Loaded and then Loading
1497 : // again once the reflow is pending. So we go around the event loop
1498 : // and call CheckLoadingFinished() after the reflow has been queued.
1499 0 : if (!mDelayedLoadCheck) {
1500 0 : mDelayedLoadCheck = true;
1501 0 : DispatchCheckLoadingFinishedAfterDelay();
1502 : }
1503 : }
1504 0 : }
1505 :
1506 : void
1507 0 : FontFaceSet::DispatchCheckLoadingFinishedAfterDelay()
1508 : {
1509 0 : AssertIsMainThreadOrServoFontMetricsLocked();
1510 :
1511 0 : if (ServoStyleSet* set = ServoStyleSet::Current()) {
1512 : // See comments in Gecko_GetFontMetrics.
1513 : //
1514 : // We can't just dispatch the runnable below if we're not on the main
1515 : // thread, since it needs to take a strong reference to the FontFaceSet,
1516 : // and being a DOM object, FontFaceSet doesn't support thread-safe
1517 : // refcounting.
1518 0 : set->AppendTask(PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay(this));
1519 0 : return;
1520 : }
1521 :
1522 : nsCOMPtr<nsIRunnable> checkTask =
1523 0 : NewRunnableMethod("dom::FontFaceSet::CheckLoadingFinishedAfterDelay",
1524 : this,
1525 0 : &FontFaceSet::CheckLoadingFinishedAfterDelay);
1526 0 : mDocument->Dispatch("FontFaceSet::CheckLoadingFinishedAfterDelay",
1527 0 : TaskCategory::Other, checkTask.forget());
1528 : }
1529 :
1530 : void
1531 0 : FontFaceSet::DidRefresh()
1532 : {
1533 0 : CheckLoadingFinished();
1534 0 : }
1535 :
1536 : void
1537 0 : FontFaceSet::CheckLoadingFinishedAfterDelay()
1538 : {
1539 0 : mDelayedLoadCheck = false;
1540 0 : CheckLoadingFinished();
1541 0 : }
1542 :
1543 : void
1544 0 : FontFaceSet::CheckLoadingStarted()
1545 : {
1546 0 : AssertIsMainThreadOrServoFontMetricsLocked();
1547 :
1548 0 : if (!HasLoadingFontFaces()) {
1549 0 : return;
1550 : }
1551 :
1552 0 : if (mStatus == FontFaceSetLoadStatus::Loading) {
1553 : // We have already dispatched a loading event and replaced mReady
1554 : // with a fresh, unresolved promise.
1555 0 : return;
1556 : }
1557 :
1558 0 : mStatus = FontFaceSetLoadStatus::Loading;
1559 0 : DispatchLoadingEventAndReplaceReadyPromise();
1560 : }
1561 :
1562 : void
1563 0 : FontFaceSet::DispatchLoadingEventAndReplaceReadyPromise()
1564 : {
1565 0 : AssertIsMainThreadOrServoFontMetricsLocked();
1566 :
1567 0 : if (ServoStyleSet* set = ServoStyleSet::Current()) {
1568 : // See comments in Gecko_GetFontMetrics.
1569 : //
1570 : // We can't just dispatch the runnable below if we're not on the main
1571 : // thread, since it needs to take a strong reference to the FontFaceSet,
1572 : // and being a DOM object, FontFaceSet doesn't support thread-safe
1573 : // refcounting. (Also, the Promise object creation must be done on
1574 : // the main thread.)
1575 0 : set->AppendTask(
1576 0 : PostTraversalTask::DispatchLoadingEventAndReplaceReadyPromise(this));
1577 0 : return;
1578 : }
1579 :
1580 0 : (new AsyncEventDispatcher(this, NS_LITERAL_STRING("loading"),
1581 0 : false))->PostDOMEvent();
1582 :
1583 0 : if (PrefEnabled()) {
1584 0 : if (mReady) {
1585 0 : if (GetParentObject()) {
1586 0 : ErrorResult rv;
1587 0 : mReady = Promise::Create(GetParentObject(), rv);
1588 : }
1589 : }
1590 0 : if (!mReady) {
1591 0 : mResolveLazilyCreatedReadyPromise = false;
1592 : }
1593 : }
1594 : }
1595 :
1596 : void
1597 0 : FontFaceSet::UpdateHasLoadingFontFaces()
1598 : {
1599 0 : mHasLoadingFontFacesIsDirty = false;
1600 0 : mHasLoadingFontFaces = false;
1601 0 : for (size_t i = 0; i < mRuleFaces.Length(); i++) {
1602 0 : FontFace* f = mRuleFaces[i].mFontFace;
1603 0 : if (f->Status() == FontFaceLoadStatus::Loading) {
1604 0 : mHasLoadingFontFaces = true;
1605 0 : return;
1606 : }
1607 : }
1608 0 : for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
1609 0 : if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) {
1610 0 : mHasLoadingFontFaces = true;
1611 0 : return;
1612 : }
1613 : }
1614 : }
1615 :
1616 : bool
1617 0 : FontFaceSet::HasLoadingFontFaces()
1618 : {
1619 0 : if (mHasLoadingFontFacesIsDirty) {
1620 0 : UpdateHasLoadingFontFaces();
1621 : }
1622 0 : return mHasLoadingFontFaces;
1623 : }
1624 :
1625 : bool
1626 0 : FontFaceSet::MightHavePendingFontLoads()
1627 : {
1628 : // Check for FontFace objects in the FontFaceSet that are still loading.
1629 0 : if (HasLoadingFontFaces()) {
1630 0 : return true;
1631 : }
1632 :
1633 : // Check for pending restyles or reflows, as they might cause fonts to
1634 : // load as new styles apply and text runs are rebuilt.
1635 0 : nsPresContext* presContext = GetPresContext();
1636 0 : if (presContext && presContext->HasPendingRestyleOrReflow()) {
1637 0 : return true;
1638 : }
1639 :
1640 0 : if (mDocument) {
1641 : // We defer resolving mReady until the document as fully loaded.
1642 0 : if (!mDocument->DidFireDOMContentLoaded()) {
1643 0 : return true;
1644 : }
1645 :
1646 : // And we also wait for any CSS style sheets to finish loading, as their
1647 : // styles might cause new fonts to load.
1648 0 : if (mDocument->CSSLoader()->HasPendingLoads()) {
1649 0 : return true;
1650 : }
1651 : }
1652 :
1653 0 : return false;
1654 : }
1655 :
1656 : void
1657 0 : FontFaceSet::CheckLoadingFinished()
1658 : {
1659 0 : MOZ_ASSERT(NS_IsMainThread());
1660 :
1661 0 : if (mDelayedLoadCheck) {
1662 : // Wait until the runnable posted in OnFontFaceStatusChanged calls us.
1663 0 : return;
1664 : }
1665 :
1666 0 : if (mStatus == FontFaceSetLoadStatus::Loaded) {
1667 : // We've already resolved mReady and dispatched the loadingdone/loadingerror
1668 : // events.
1669 0 : return;
1670 : }
1671 :
1672 0 : if (MightHavePendingFontLoads()) {
1673 : // We're not finished loading yet.
1674 0 : return;
1675 : }
1676 :
1677 0 : mStatus = FontFaceSetLoadStatus::Loaded;
1678 0 : if (mReady) {
1679 0 : mReady->MaybeResolve(this);
1680 : } else {
1681 0 : mResolveLazilyCreatedReadyPromise = true;
1682 : }
1683 :
1684 : // Now dispatch the loadingdone/loadingerror events.
1685 0 : nsTArray<OwningNonNull<FontFace>> loaded;
1686 0 : nsTArray<OwningNonNull<FontFace>> failed;
1687 :
1688 0 : for (size_t i = 0; i < mRuleFaces.Length(); i++) {
1689 0 : if (!mRuleFaces[i].mLoadEventShouldFire) {
1690 0 : continue;
1691 : }
1692 0 : FontFace* f = mRuleFaces[i].mFontFace;
1693 0 : if (f->Status() == FontFaceLoadStatus::Loaded) {
1694 0 : loaded.AppendElement(*f);
1695 0 : mRuleFaces[i].mLoadEventShouldFire = false;
1696 0 : } else if (f->Status() == FontFaceLoadStatus::Error) {
1697 0 : failed.AppendElement(*f);
1698 0 : mRuleFaces[i].mLoadEventShouldFire = false;
1699 : }
1700 : }
1701 :
1702 0 : for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
1703 0 : if (!mNonRuleFaces[i].mLoadEventShouldFire) {
1704 0 : continue;
1705 : }
1706 0 : FontFace* f = mNonRuleFaces[i].mFontFace;
1707 0 : if (f->Status() == FontFaceLoadStatus::Loaded) {
1708 0 : loaded.AppendElement(*f);
1709 0 : mNonRuleFaces[i].mLoadEventShouldFire = false;
1710 0 : } else if (f->Status() == FontFaceLoadStatus::Error) {
1711 0 : failed.AppendElement(*f);
1712 0 : mNonRuleFaces[i].mLoadEventShouldFire = false;
1713 : }
1714 : }
1715 :
1716 0 : DispatchLoadingFinishedEvent(NS_LITERAL_STRING("loadingdone"),
1717 0 : Move(loaded));
1718 :
1719 0 : if (!failed.IsEmpty()) {
1720 0 : DispatchLoadingFinishedEvent(NS_LITERAL_STRING("loadingerror"),
1721 0 : Move(failed));
1722 : }
1723 : }
1724 :
1725 : void
1726 0 : FontFaceSet::DispatchLoadingFinishedEvent(
1727 : const nsAString& aType,
1728 : nsTArray<OwningNonNull<FontFace>>&& aFontFaces)
1729 : {
1730 0 : FontFaceSetLoadEventInit init;
1731 0 : init.mBubbles = false;
1732 0 : init.mCancelable = false;
1733 0 : init.mFontfaces.SwapElements(aFontFaces);
1734 : RefPtr<FontFaceSetLoadEvent> event =
1735 0 : FontFaceSetLoadEvent::Constructor(this, aType, init);
1736 0 : (new AsyncEventDispatcher(this, event))->PostDOMEvent();
1737 0 : }
1738 :
1739 : // nsIDOMEventListener
1740 :
1741 : NS_IMETHODIMP
1742 0 : FontFaceSet::HandleEvent(nsIDOMEvent* aEvent)
1743 : {
1744 0 : nsString type;
1745 0 : aEvent->GetType(type);
1746 :
1747 0 : if (!type.EqualsLiteral("DOMContentLoaded")) {
1748 0 : return NS_ERROR_FAILURE;
1749 : }
1750 :
1751 0 : RemoveDOMContentLoadedListener();
1752 0 : CheckLoadingFinished();
1753 :
1754 0 : return NS_OK;
1755 : }
1756 :
1757 : /* static */ bool
1758 0 : FontFaceSet::PrefEnabled()
1759 : {
1760 : static bool initialized = false;
1761 : static bool enabled;
1762 0 : if (!initialized) {
1763 0 : initialized = true;
1764 0 : Preferences::AddBoolVarCache(&enabled, FONT_LOADING_API_ENABLED_PREF);
1765 : }
1766 0 : return enabled;
1767 : }
1768 :
1769 : // nsICSSLoaderObserver
1770 :
1771 : NS_IMETHODIMP
1772 0 : FontFaceSet::StyleSheetLoaded(StyleSheet* aSheet,
1773 : bool aWasAlternate,
1774 : nsresult aStatus)
1775 : {
1776 0 : CheckLoadingFinished();
1777 0 : return NS_OK;
1778 : }
1779 :
1780 : void
1781 0 : FontFaceSet::FlushUserFontSet()
1782 : {
1783 0 : if (mDocument) {
1784 0 : mDocument->FlushUserFontSet();
1785 : }
1786 0 : }
1787 :
1788 : void
1789 0 : FontFaceSet::RebuildUserFontSet()
1790 : {
1791 0 : if (mDocument) {
1792 0 : mDocument->RebuildUserFontSet();
1793 : }
1794 0 : }
1795 :
1796 : nsPresContext*
1797 0 : FontFaceSet::GetPresContext()
1798 : {
1799 0 : if (!mDocument) {
1800 0 : return nullptr;
1801 : }
1802 :
1803 0 : nsIPresShell* shell = mDocument->GetShell();
1804 0 : return shell ? shell->GetPresContext() : nullptr;
1805 : }
1806 :
1807 : void
1808 0 : FontFaceSet::UpdateStandardFontLoadPrincipal()
1809 : {
1810 0 : MOZ_ASSERT(NS_IsMainThread());
1811 :
1812 0 : nsIPrincipal* documentPrincipal = mDocument->NodePrincipal();
1813 :
1814 0 : if (!mStandardFontLoadPrincipal ||
1815 0 : mStandardFontLoadPrincipal->get() != documentPrincipal) {
1816 0 : if (mStandardFontLoadPrincipal) {
1817 0 : mHasStandardFontLoadPrincipalChanged = true;
1818 : }
1819 0 : mStandardFontLoadPrincipal = new gfxFontSrcPrincipal(documentPrincipal);
1820 : }
1821 0 : }
1822 :
1823 : // -- FontFaceSet::UserFontSet ------------------------------------------------
1824 :
1825 : /* virtual */ nsresult
1826 0 : FontFaceSet::UserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
1827 : gfxFontSrcPrincipal** aPrincipal,
1828 : bool* aBypassCache)
1829 : {
1830 0 : if (!mFontFaceSet) {
1831 0 : return NS_ERROR_FAILURE;
1832 : }
1833 0 : return mFontFaceSet->CheckFontLoad(aFontFaceSrc, aPrincipal, aBypassCache);
1834 : }
1835 :
1836 : /* virtual */ gfxFontSrcPrincipal*
1837 0 : FontFaceSet::UserFontSet::GetStandardFontLoadPrincipal()
1838 : {
1839 0 : if (!mFontFaceSet) {
1840 0 : return nullptr;
1841 : }
1842 0 : return mFontFaceSet->GetStandardFontLoadPrincipal();
1843 : }
1844 :
1845 : /* virtual */ bool
1846 0 : FontFaceSet::UserFontSet::IsFontLoadAllowed(nsIURI* aFontLocation,
1847 : nsIPrincipal* aPrincipal)
1848 : {
1849 0 : return mFontFaceSet &&
1850 0 : mFontFaceSet->IsFontLoadAllowed(aFontLocation, aPrincipal);
1851 : }
1852 :
1853 : /* virtual */ nsresult
1854 0 : FontFaceSet::UserFontSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
1855 : const gfxFontFaceSrc* aFontFaceSrc)
1856 : {
1857 0 : if (!mFontFaceSet) {
1858 0 : return NS_ERROR_FAILURE;
1859 : }
1860 0 : return mFontFaceSet->StartLoad(aUserFontEntry, aFontFaceSrc);
1861 : }
1862 :
1863 : void
1864 0 : FontFaceSet::UserFontSet::RecordFontLoadDone(uint32_t aFontSize,
1865 : TimeStamp aDoneTime)
1866 : {
1867 0 : mDownloadCount++;
1868 0 : mDownloadSize += aFontSize;
1869 0 : Telemetry::Accumulate(Telemetry::WEBFONT_SIZE, aFontSize / 1024);
1870 :
1871 0 : if (!mFontFaceSet) {
1872 0 : return;
1873 : }
1874 :
1875 0 : TimeStamp navStart = mFontFaceSet->GetNavigationStartTimeStamp();
1876 0 : TimeStamp zero;
1877 0 : if (navStart != zero) {
1878 : Telemetry::AccumulateTimeDelta(Telemetry::WEBFONT_DOWNLOAD_TIME_AFTER_START,
1879 0 : navStart, aDoneTime);
1880 : }
1881 : }
1882 :
1883 : /* virtual */ nsresult
1884 0 : FontFaceSet::UserFontSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
1885 : const char* aMessage,
1886 : uint32_t aFlags,
1887 : nsresult aStatus)
1888 : {
1889 0 : if (!mFontFaceSet) {
1890 0 : return NS_ERROR_FAILURE;
1891 : }
1892 0 : return mFontFaceSet->LogMessage(aUserFontEntry, aMessage, aFlags, aStatus);
1893 : }
1894 :
1895 : /* virtual */ nsresult
1896 0 : FontFaceSet::UserFontSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
1897 : const gfxFontFaceSrc* aFontFaceSrc,
1898 : uint8_t*& aBuffer,
1899 : uint32_t& aBufferLength)
1900 : {
1901 0 : if (!mFontFaceSet) {
1902 0 : return NS_ERROR_FAILURE;
1903 : }
1904 0 : return mFontFaceSet->SyncLoadFontData(aFontToLoad, aFontFaceSrc,
1905 0 : aBuffer, aBufferLength);
1906 : }
1907 :
1908 : /* virtual */ bool
1909 0 : FontFaceSet::UserFontSet::GetPrivateBrowsing()
1910 : {
1911 0 : return mFontFaceSet && mFontFaceSet->mPrivateBrowsing;
1912 : }
1913 :
1914 : /* virtual */ void
1915 0 : FontFaceSet::UserFontSet::DoRebuildUserFontSet()
1916 : {
1917 0 : if (!mFontFaceSet) {
1918 0 : return;
1919 : }
1920 0 : mFontFaceSet->RebuildUserFontSet();
1921 : }
1922 :
1923 : /* virtual */ already_AddRefed<gfxUserFontEntry>
1924 0 : FontFaceSet::UserFontSet::CreateUserFontEntry(
1925 : const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
1926 : uint32_t aWeight,
1927 : int32_t aStretch,
1928 : uint8_t aStyle,
1929 : const nsTArray<gfxFontFeature>& aFeatureSettings,
1930 : uint32_t aLanguageOverride,
1931 : gfxCharacterMap* aUnicodeRanges,
1932 : uint8_t aFontDisplay)
1933 : {
1934 : RefPtr<gfxUserFontEntry> entry =
1935 : new FontFace::Entry(this, aFontFaceSrcList, aWeight, aStretch, aStyle,
1936 : aFeatureSettings, aLanguageOverride, aUnicodeRanges,
1937 0 : aFontDisplay);
1938 0 : return entry.forget();
1939 : }
1940 :
1941 : #undef LOG_ENABLED
1942 : #undef LOG
|