Coverage Report

Created: 2018-09-25 14:53

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