tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FontFaceSet.cpp (15085B)


      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 "FontPreloader.h"
     10 #include "ReferrerInfo.h"
     11 #include "gfxFontConstants.h"
     12 #include "gfxFontSrcPrincipal.h"
     13 #include "gfxFontSrcURI.h"
     14 #include "gfxFontUtils.h"
     15 #include "mozilla/AsyncEventDispatcher.h"
     16 #include "mozilla/BasePrincipal.h"
     17 #include "mozilla/FontPropertyTypes.h"
     18 #include "mozilla/LoadInfo.h"
     19 #include "mozilla/Logging.h"
     20 #include "mozilla/Preferences.h"
     21 #include "mozilla/PresShell.h"
     22 #include "mozilla/PresShellInlines.h"
     23 #include "mozilla/ServoBindings.h"
     24 #include "mozilla/ServoCSSParser.h"
     25 #include "mozilla/ServoStyleSet.h"
     26 #include "mozilla/ServoUtils.h"
     27 #include "mozilla/Sprintf.h"
     28 #include "mozilla/css/Loader.h"
     29 #include "mozilla/dom/CSSFontFaceRule.h"
     30 #include "mozilla/dom/Document.h"
     31 #include "mozilla/dom/DocumentInlines.h"
     32 #include "mozilla/dom/Event.h"
     33 #include "mozilla/dom/FontFaceImpl.h"
     34 #include "mozilla/dom/FontFaceSetBinding.h"
     35 #include "mozilla/dom/FontFaceSetDocumentImpl.h"
     36 #include "mozilla/dom/FontFaceSetIterator.h"
     37 #include "mozilla/dom/FontFaceSetLoadEvent.h"
     38 #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
     39 #include "mozilla/dom/FontFaceSetWorkerImpl.h"
     40 #include "mozilla/dom/Promise.h"
     41 #include "nsComponentManagerUtils.h"
     42 #include "nsContentPolicyUtils.h"
     43 #include "nsContentUtils.h"
     44 #include "nsDOMNavigationTiming.h"
     45 #include "nsDeviceContext.h"
     46 #include "nsFontFaceLoader.h"
     47 #include "nsIConsoleService.h"
     48 #include "nsIContentPolicy.h"
     49 #include "nsIDocShell.h"
     50 #include "nsIInputStream.h"
     51 #include "nsILoadContext.h"
     52 #include "nsIPrincipal.h"
     53 #include "nsIWebNavigation.h"
     54 #include "nsLayoutUtils.h"
     55 #include "nsNetUtil.h"
     56 #include "nsPresContext.h"
     57 #include "nsPrintfCString.h"
     58 #include "nsUTF8Utils.h"
     59 
     60 using namespace mozilla;
     61 using namespace mozilla::css;
     62 using namespace mozilla::dom;
     63 
     64 NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet)
     65 
     66 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet,
     67                                                  DOMEventTargetHelper)
     68  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mImpl->GetDocument());
     69  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady);
     70  for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
     71    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace);
     72  }
     73  for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
     74    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces[i].mFontFace);
     75  }
     76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     77 
     78 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet,
     79                                                DOMEventTargetHelper)
     80  tmp->Destroy();
     81  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady);
     82  for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
     83    NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces[i].mFontFace);
     84  }
     85  for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
     86    NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces[i].mFontFace);
     87  }
     88 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     89 
     90 NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper)
     91 NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper)
     92 
     93 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFaceSet)
     94 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     95 
     96 FontFaceSet::FontFaceSet(nsIGlobalObject* aParent)
     97    : DOMEventTargetHelper(aParent) {}
     98 
     99 FontFaceSet::~FontFaceSet() {
    100  // Assert that we don't drop any FontFaceSet objects during a Servo traversal,
    101  // since PostTraversalTask objects can hold raw pointers to FontFaceSets.
    102  MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
    103 
    104  Destroy();
    105 }
    106 
    107 /* static */ already_AddRefed<FontFaceSet> FontFaceSet::CreateForDocument(
    108    dom::Document* aDocument) {
    109  RefPtr<FontFaceSet> set = new FontFaceSet(aDocument->GetScopeObject());
    110  RefPtr<FontFaceSetDocumentImpl> impl =
    111      new FontFaceSetDocumentImpl(set, aDocument);
    112  set->mImpl = impl;
    113  impl->Initialize();
    114  return set.forget();
    115 }
    116 
    117 /* static */ already_AddRefed<FontFaceSet> FontFaceSet::CreateForWorker(
    118    nsIGlobalObject* aParent, WorkerPrivate* aWorkerPrivate) {
    119  RefPtr<FontFaceSet> set = new FontFaceSet(aParent);
    120  RefPtr<FontFaceSetWorkerImpl> impl = new FontFaceSetWorkerImpl(set);
    121  set->mImpl = impl;
    122  if (NS_WARN_IF(!impl->Initialize(aWorkerPrivate))) {
    123    return nullptr;
    124  }
    125  return set.forget();
    126 }
    127 
    128 JSObject* FontFaceSet::WrapObject(JSContext* aContext,
    129                                  JS::Handle<JSObject*> aGivenProto) {
    130  return FontFaceSet_Binding::Wrap(aContext, this, aGivenProto);
    131 }
    132 
    133 void FontFaceSet::Destroy() { mImpl->Destroy(); }
    134 
    135 already_AddRefed<Promise> FontFaceSet::Load(JSContext* aCx,
    136                                            const nsACString& aFont,
    137                                            const nsAString& aText,
    138                                            ErrorResult& aRv) {
    139  FlushUserFontSet();
    140 
    141  nsTArray<RefPtr<Promise>> promises;
    142 
    143  nsTArray<RefPtr<FontFace>> faces;
    144  {
    145    nsTArray<FontFace*> weakFaces;
    146    mImpl->FindMatchingFontFaces(aFont, aText, weakFaces, aRv);
    147    if (aRv.Failed()) {
    148      return nullptr;
    149    }
    150    if (!faces.AppendElements(weakFaces, fallible) ||
    151        !promises.SetCapacity(weakFaces.Length(), fallible)) {
    152      aRv.Throw(NS_ERROR_FAILURE);
    153      return nullptr;
    154    }
    155  }
    156 
    157  for (FontFace* f : faces) {
    158    RefPtr<Promise> promise = f->Load(aRv);
    159    if (aRv.Failed()) {
    160      return nullptr;
    161    }
    162    promises.AppendElement(promise);
    163  }
    164 
    165  return Promise::All(aCx, promises, aRv);
    166 }
    167 
    168 bool FontFaceSet::Check(const nsACString& aFont, const nsAString& aText,
    169                        ErrorResult& aRv) {
    170  FlushUserFontSet();
    171 
    172  nsTArray<FontFace*> faces;
    173  mImpl->FindMatchingFontFaces(aFont, aText, faces, aRv);
    174  if (aRv.Failed()) {
    175    return false;
    176  }
    177 
    178  for (FontFace* f : faces) {
    179    if (f->Status() != FontFaceLoadStatus::Loaded) {
    180      return false;
    181    }
    182  }
    183 
    184  return true;
    185 }
    186 
    187 bool FontFaceSet::ReadyPromiseIsPending() const {
    188  return mReady ? mReady->State() == Promise::PromiseState::Pending
    189                : !mResolveLazilyCreatedReadyPromise;
    190 }
    191 
    192 Promise* FontFaceSet::GetReady(ErrorResult& aRv) {
    193  mImpl->EnsureReady();
    194 
    195  if (!mReady) {
    196    nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    197    mReady = Promise::Create(global, aRv);
    198    if (!mReady) {
    199      aRv.Throw(NS_ERROR_FAILURE);
    200      return nullptr;
    201    }
    202    if (mResolveLazilyCreatedReadyPromise) {
    203      mReady->MaybeResolve(this);
    204      mResolveLazilyCreatedReadyPromise = false;
    205    }
    206  }
    207 
    208  return mReady;
    209 }
    210 
    211 FontFaceSetLoadStatus FontFaceSet::Status() { return mImpl->Status(); }
    212 
    213 #ifdef DEBUG
    214 bool FontFaceSet::HasRuleFontFace(FontFace* aFontFace) {
    215  for (size_t i = 0; i < mRuleFaces.Length(); i++) {
    216    if (mRuleFaces[i].mFontFace == aFontFace) {
    217      return true;
    218    }
    219  }
    220  return false;
    221 }
    222 #endif
    223 
    224 void FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv) {
    225  FontFaceImpl* fontImpl = aFontFace.GetImpl();
    226  MOZ_ASSERT(fontImpl);
    227 
    228  if (!mImpl->Add(fontImpl, aRv)) {
    229    return;
    230  }
    231 
    232  MOZ_ASSERT(!aRv.Failed());
    233 
    234 #ifdef DEBUG
    235  for (const FontFaceRecord& rec : mNonRuleFaces) {
    236    MOZ_ASSERT(rec.mFontFace != &aFontFace,
    237               "FontFace should not occur in mNonRuleFaces twice");
    238  }
    239 #endif
    240 
    241  FontFaceRecord* rec = mNonRuleFaces.AppendElement();
    242  rec->mFontFace = &aFontFace;
    243  rec->mOrigin = Nothing();
    244  rec->mLoadEventShouldFire =
    245      fontImpl->Status() == FontFaceLoadStatus::Unloaded ||
    246      fontImpl->Status() == FontFaceLoadStatus::Loading;
    247 }
    248 
    249 void FontFaceSet::Clear() {
    250  nsTArray<FontFaceRecord> oldRecords = std::move(mNonRuleFaces);
    251  mImpl->Clear();
    252 }
    253 
    254 bool FontFaceSet::Delete(FontFace& aFontFace) {
    255  // Hold onto a strong reference to make sure that when we remove FontFace from
    256  // the list, the FontFaceImpl does not get freed right away. We need to check
    257  // the FontFaceSetImpl first.
    258  RefPtr<FontFaceImpl> fontImpl = aFontFace.GetImpl();
    259  MOZ_ASSERT(fontImpl);
    260 
    261  // Ensure that we remove from mNonRuleFaces first. This is important so that
    262  // when we check to see if all of the fonts have finished loading, the list in
    263  // FontFaceSet and FontFaceSetImpl match.
    264  bool removed = false;
    265  for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
    266    if (mNonRuleFaces[i].mFontFace == &aFontFace) {
    267      mNonRuleFaces.RemoveElementAt(i);
    268      removed = true;
    269      break;
    270    }
    271  }
    272 
    273  if (!mImpl->Delete(fontImpl)) {
    274    MOZ_ASSERT(!removed, "Missing rule present in Impl!");
    275  } else {
    276    MOZ_ASSERT(removed, "Rule present but missing in Impl!");
    277  }
    278 
    279  return removed;
    280 }
    281 
    282 bool FontFaceSet::HasAvailableFontFace(FontFace* aFontFace) {
    283  return aFontFace->GetImpl()->IsInFontFaceSet(mImpl);
    284 }
    285 
    286 bool FontFaceSet::Has(FontFace& aFontFace) {
    287  FlushUserFontSet();
    288 
    289  return HasAvailableFontFace(&aFontFace);
    290 }
    291 
    292 FontFace* FontFaceSet::GetFontFaceAt(uint32_t aIndex) {
    293  FlushUserFontSet();
    294 
    295  if (aIndex < mRuleFaces.Length()) {
    296    auto& entry = mRuleFaces[aIndex];
    297    if (entry.mOrigin.value() != StyleOrigin::Author) {
    298      return nullptr;
    299    }
    300    return entry.mFontFace;
    301  }
    302 
    303  aIndex -= mRuleFaces.Length();
    304  if (aIndex < mNonRuleFaces.Length()) {
    305    return mNonRuleFaces[aIndex].mFontFace;
    306  }
    307 
    308  return nullptr;
    309 }
    310 
    311 uint32_t FontFaceSet::Size() {
    312  FlushUserFontSet();
    313 
    314  // Web IDL objects can only expose array index properties up to INT32_MAX.
    315 
    316  size_t total = mNonRuleFaces.Length();
    317  for (const auto& entry : mRuleFaces) {
    318    if (entry.mOrigin.value() == StyleOrigin::Author) {
    319      ++total;
    320    }
    321  }
    322  return std::min<size_t>(total, INT32_MAX);
    323 }
    324 
    325 uint32_t FontFaceSet::SizeIncludingNonAuthorOrigins() {
    326  FlushUserFontSet();
    327 
    328  // Web IDL objects can only expose array index properties up to INT32_MAX.
    329 
    330  size_t total = mRuleFaces.Length() + mNonRuleFaces.Length();
    331  return std::min<size_t>(total, INT32_MAX);
    332 }
    333 
    334 already_AddRefed<FontFaceSetIterator> FontFaceSet::Entries() {
    335  RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, true);
    336  return it.forget();
    337 }
    338 
    339 already_AddRefed<FontFaceSetIterator> FontFaceSet::Values() {
    340  RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, false);
    341  return it.forget();
    342 }
    343 
    344 void FontFaceSet::ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback,
    345                          JS::Handle<JS::Value> aThisArg, ErrorResult& aRv) {
    346  JS::Rooted<JS::Value> thisArg(aCx, aThisArg);
    347  for (size_t i = 0; i < SizeIncludingNonAuthorOrigins(); i++) {
    348    RefPtr<FontFace> face = GetFontFaceAt(i);
    349    if (!face) {
    350      // The font at index |i| is a non-Author origin font, which we shouldn't
    351      // expose per spec.
    352      continue;
    353    }
    354    aCallback.Call(thisArg, *face, *face, *this, aRv);
    355    if (aRv.Failed()) {
    356      return;
    357    }
    358  }
    359 }
    360 
    361 bool FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules) {
    362  // The impl object handles the callbacks for recreating the mRulesFaces array.
    363  nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces);
    364  return mImpl->UpdateRules(aRules);
    365 }
    366 
    367 void FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, StyleOrigin aOrigin) {
    368  MOZ_ASSERT(!HasRuleFontFace(aFontFace));
    369 
    370  FontFaceRecord* rec = mRuleFaces.AppendElement();
    371  rec->mFontFace = aFontFace;
    372  rec->mOrigin = Some(aOrigin);
    373  rec->mLoadEventShouldFire =
    374      aFontFace->Status() == FontFaceLoadStatus::Unloaded ||
    375      aFontFace->Status() == FontFaceLoadStatus::Loading;
    376 }
    377 
    378 void FontFaceSet::DidRefresh() { mImpl->CheckLoadingFinished(); }
    379 
    380 void FontFaceSet::DispatchLoadingEventAndReplaceReadyPromise() {
    381  gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
    382 
    383  if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) {
    384    // See comments in Gecko_GetFontMetrics.
    385    //
    386    // We can't just dispatch the runnable below if we're not on the main
    387    // thread, since it needs to take a strong reference to the FontFaceSet,
    388    // and being a DOM object, FontFaceSet doesn't support thread-safe
    389    // refcounting.  (Also, the Promise object creation must be done on
    390    // the main thread.)
    391    set->AppendTask(
    392        PostTraversalTask::DispatchLoadingEventAndReplaceReadyPromise(this));
    393    return;
    394  }
    395 
    396  (new AsyncEventDispatcher(this, u"loading"_ns, CanBubble::eNo))
    397      ->PostDOMEvent();
    398 
    399  if (mReady && mReady->State() != Promise::PromiseState::Pending) {
    400    if (GetParentObject()) {
    401      ErrorResult rv;
    402      mReady = Promise::Create(GetParentObject(), rv);
    403    }
    404  }
    405 
    406  // We may previously have been in a state where all fonts had finished
    407  // loading and we'd set mResolveLazilyCreatedReadyPromise to make sure that
    408  // if we lazily create mReady for a consumer that we resolve it before
    409  // returning it.  We're now loading fonts, so we need to clear that flag.
    410  mResolveLazilyCreatedReadyPromise = false;
    411 }
    412 
    413 void FontFaceSet::MaybeResolve() {
    414  if (mReady) {
    415    mReady->MaybeResolve(this);
    416  } else {
    417    mResolveLazilyCreatedReadyPromise = true;
    418  }
    419 
    420  // Now dispatch the loadingdone/loadingerror events.
    421  nsTArray<OwningNonNull<FontFace>> loaded;
    422  nsTArray<OwningNonNull<FontFace>> failed;
    423 
    424  auto checkStatus = [&](nsTArray<FontFaceRecord>& faces) -> void {
    425    for (auto& face : faces) {
    426      if (!face.mLoadEventShouldFire) {
    427        continue;
    428      }
    429      FontFace* f = face.mFontFace;
    430      switch (f->Status()) {
    431        case FontFaceLoadStatus::Unloaded:
    432          break;
    433        case FontFaceLoadStatus::Loaded:
    434          loaded.AppendElement(*f);
    435          face.mLoadEventShouldFire = false;
    436          break;
    437        case FontFaceLoadStatus::Error:
    438          failed.AppendElement(*f);
    439          face.mLoadEventShouldFire = false;
    440          break;
    441        case FontFaceLoadStatus::Loading:
    442          // We should've returned above at MightHavePendingFontLoads()!
    443          MOZ_ASSERT_UNREACHABLE("unexpected FontFaceLoadStatus");
    444          break;
    445      }
    446    }
    447  };
    448 
    449  checkStatus(mRuleFaces);
    450  checkStatus(mNonRuleFaces);
    451 
    452  DispatchLoadingFinishedEvent(u"loadingdone"_ns, std::move(loaded));
    453 
    454  if (!failed.IsEmpty()) {
    455    DispatchLoadingFinishedEvent(u"loadingerror"_ns, std::move(failed));
    456  }
    457 }
    458 
    459 void FontFaceSet::DispatchLoadingFinishedEvent(
    460    const nsAString& aType, nsTArray<OwningNonNull<FontFace>>&& aFontFaces) {
    461  FontFaceSetLoadEventInit init;
    462  init.mBubbles = false;
    463  init.mCancelable = false;
    464  init.mFontfaces = std::move(aFontFaces);
    465  RefPtr<FontFaceSetLoadEvent> event =
    466      FontFaceSetLoadEvent::Constructor(this, aType, init);
    467  (new AsyncEventDispatcher(this, event.forget()))->PostDOMEvent();
    468 }
    469 
    470 void FontFaceSet::FlushUserFontSet() { mImpl->FlushUserFontSet(); }
    471 
    472 void FontFaceSet::RefreshStandardFontLoadPrincipal() {
    473  MOZ_ASSERT(NS_IsMainThread());
    474  mImpl->RefreshStandardFontLoadPrincipal();
    475 }
    476 
    477 void FontFaceSet::CopyNonRuleFacesTo(FontFaceSet* aFontFaceSet) const {
    478  for (const FontFaceRecord& rec : mNonRuleFaces) {
    479    IgnoredErrorResult rv;
    480    RefPtr<FontFace> f = rec.mFontFace;
    481    aFontFaceSet->Add(*f, rv);
    482    MOZ_ASSERT(!rv.Failed());
    483  }
    484 }
    485 
    486 #undef LOG_ENABLED
    487 #undef LOG