зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1771493
- Part 4. Split FontFaceSetDocumentImpl from FontFaceSetImpl. r=emilio
This patch splits document specific functionality from FontFaceSetImpl into a new class FontFaceSetDocumentImpl. This will make it easier to fork FontFaceSetImpl for workers. Differential Revision: https://phabricator.services.mozilla.com/D147819
This commit is contained in:
Родитель
7c2ad16f5c
Коммит
091dc0483c
|
@ -16122,8 +16122,7 @@ void Document::FlushUserFontSet() {
|
|||
}
|
||||
|
||||
if (!mFontFaceSet && !rules.IsEmpty()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
|
||||
mFontFaceSet = new FontFaceSet(window, this);
|
||||
mFontFaceSet = FontFaceSet::CreateForDocument(this);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
@ -16155,8 +16154,7 @@ void Document::MarkUserFontSetDirty() {
|
|||
|
||||
FontFaceSet* Document::Fonts() {
|
||||
if (!mFontFaceSet) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
|
||||
mFontFaceSet = new FontFaceSet(window, this);
|
||||
mFontFaceSet = FontFaceSet::CreateForDocument(this);
|
||||
FlushUserFontSet();
|
||||
}
|
||||
return mFontFaceSet;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/FontFaceImpl.h"
|
||||
#include "mozilla/dom/FontFaceSetBinding.h"
|
||||
#include "mozilla/dom/FontFaceSetDocumentImpl.h"
|
||||
#include "mozilla/dom/FontFaceSetIterator.h"
|
||||
#include "mozilla/dom/FontFaceSetLoadEvent.h"
|
||||
#include "mozilla/dom/FontFaceSetLoadEventBinding.h"
|
||||
|
@ -93,11 +94,8 @@ NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper)
|
|||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFaceSet)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, dom::Document* aDocument)
|
||||
: DOMEventTargetHelper(aWindow),
|
||||
mImpl(new FontFaceSetImpl(this, aDocument)) {
|
||||
mImpl->Initialize();
|
||||
}
|
||||
FontFaceSet::FontFaceSet(nsIGlobalObject* aParent)
|
||||
: DOMEventTargetHelper(aParent) {}
|
||||
|
||||
FontFaceSet::~FontFaceSet() {
|
||||
// Assert that we don't drop any FontFaceSet objects during a Servo traversal,
|
||||
|
@ -107,6 +105,16 @@ FontFaceSet::~FontFaceSet() {
|
|||
Destroy();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<FontFaceSet> FontFaceSet::CreateForDocument(
|
||||
dom::Document* aDocument) {
|
||||
RefPtr<FontFaceSet> set = new FontFaceSet(aDocument->GetScopeObject());
|
||||
RefPtr<FontFaceSetDocumentImpl> impl =
|
||||
new FontFaceSetDocumentImpl(set, aDocument);
|
||||
impl->Initialize();
|
||||
set->mImpl = std::move(impl);
|
||||
return set.forget();
|
||||
}
|
||||
|
||||
JSObject* FontFaceSet::WrapObject(JSContext* aContext,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return FontFaceSet_Binding::Wrap(aContext, this, aGivenProto);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class nsFontFaceLoader;
|
||||
class nsIPrincipal;
|
||||
class nsPIDOMWindowInner;
|
||||
class nsIGlobalObject;
|
||||
struct RawServoFontFaceRule;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -36,7 +36,8 @@ class FontFaceSet final : public DOMEventTargetHelper {
|
|||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FontFaceSet, DOMEventTargetHelper)
|
||||
|
||||
FontFaceSet(nsPIDOMWindowInner* aWindow, dom::Document* aDocument);
|
||||
static already_AddRefed<FontFaceSet> CreateForDocument(
|
||||
dom::Document* aDocument);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
@ -112,6 +113,7 @@ class FontFaceSet final : public DOMEventTargetHelper {
|
|||
private:
|
||||
friend mozilla::dom::FontFaceSetIterator; // needs GetFontFaceAt()
|
||||
|
||||
explicit FontFaceSet(nsIGlobalObject* aParent);
|
||||
~FontFaceSet();
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,694 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "FontFaceSetDocumentImpl.h"
|
||||
#include "FontPreloader.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/PresShellInlines.h"
|
||||
#include "mozilla/css/Loader.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/FontFaceImpl.h"
|
||||
#include "mozilla/dom/FontFaceSet.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsFontFaceLoader.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsINetworkPredictor.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::css;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
#define LOG(args) \
|
||||
MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
|
||||
#define LOG_ENABLED() \
|
||||
MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(FontFaceSetDocumentImpl, FontFaceSetImpl,
|
||||
nsIDOMEventListener, nsICSSLoaderObserver)
|
||||
|
||||
FontFaceSetDocumentImpl::FontFaceSetDocumentImpl(FontFaceSet* aOwner,
|
||||
dom::Document* aDocument)
|
||||
: FontFaceSetImpl(aOwner), mDocument(aDocument) {}
|
||||
|
||||
FontFaceSetDocumentImpl::~FontFaceSetDocumentImpl() = default;
|
||||
|
||||
void FontFaceSetDocumentImpl::Initialize() {
|
||||
MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
|
||||
|
||||
// Record the state of the "bypass cache" flags from the docshell now,
|
||||
// since we want to look at them from style worker threads, and we can
|
||||
// only get to the docshell through a weak pointer (which is only
|
||||
// possible on the main thread).
|
||||
//
|
||||
// In theory the load type of a docshell could change after the document
|
||||
// is loaded, but handling that doesn't seem too important.
|
||||
if (nsCOMPtr<nsIDocShell> docShell = mDocument->GetDocShell()) {
|
||||
uint32_t loadType;
|
||||
uint32_t flags;
|
||||
if ((NS_SUCCEEDED(docShell->GetLoadType(&loadType)) &&
|
||||
((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)) ||
|
||||
(NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags)) &&
|
||||
(flags & nsIRequest::LOAD_BYPASS_CACHE))) {
|
||||
mBypassCache = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Same for the "private browsing" flag.
|
||||
if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) {
|
||||
mPrivateBrowsing = loadContext->UsePrivateBrowsing();
|
||||
}
|
||||
|
||||
if (!mDocument->DidFireDOMContentLoaded()) {
|
||||
mDocument->AddSystemEventListener(u"DOMContentLoaded"_ns, this, false,
|
||||
false);
|
||||
} else {
|
||||
// In some cases we can't rely on CheckLoadingFinished being called from
|
||||
// the refresh driver. For example, documents in display:none iframes.
|
||||
// Or if the document has finished loading and painting at the time that
|
||||
// script requests document.fonts and causes us to get here.
|
||||
CheckLoadingFinished();
|
||||
}
|
||||
|
||||
mDocument->CSSLoader()->AddObserver(this);
|
||||
|
||||
mStandardFontLoadPrincipal = CreateStandardFontLoadPrincipal();
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::Destroy() {
|
||||
RemoveDOMContentLoadedListener();
|
||||
|
||||
if (mDocument && mDocument->CSSLoader()) {
|
||||
// We're null checking CSSLoader() since FontFaceSetImpl::Disconnect() might
|
||||
// be being called during unlink, at which time the loader may already have
|
||||
// been unlinked from the document.
|
||||
mDocument->CSSLoader()->RemoveObserver(this);
|
||||
}
|
||||
|
||||
mRuleFaces.Clear();
|
||||
|
||||
mDocument = nullptr;
|
||||
|
||||
FontFaceSetImpl::Destroy();
|
||||
}
|
||||
|
||||
uint64_t FontFaceSetDocumentImpl::GetInnerWindowID() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mDocument) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mDocument->InnerWindowID();
|
||||
}
|
||||
|
||||
nsPresContext* FontFaceSetDocumentImpl::GetPresContext() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mDocument) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mDocument->GetPresContext();
|
||||
}
|
||||
|
||||
already_AddRefed<gfxFontSrcPrincipal>
|
||||
FontFaceSetDocumentImpl::CreateStandardFontLoadPrincipal() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return MakeAndAddRef<gfxFontSrcPrincipal>(mDocument->NodePrincipal(),
|
||||
mDocument->PartitionedPrincipal());
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::RemoveDOMContentLoadedListener() {
|
||||
if (mDocument) {
|
||||
mDocument->RemoveSystemEventListener(u"DOMContentLoaded"_ns, this, false);
|
||||
}
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::FindMatchingFontFaces(
|
||||
const nsTHashSet<FontFace*>& aMatchingFaces,
|
||||
nsTArray<FontFace*>& aFontFaces) {
|
||||
FontFaceSetImpl::FindMatchingFontFaces(aMatchingFaces, aFontFaces);
|
||||
for (FontFaceRecord& record : mRuleFaces) {
|
||||
FontFace* owner = record.mFontFace->GetOwner();
|
||||
if (owner && aMatchingFaces.Contains(owner)) {
|
||||
aFontFaces.AppendElement(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeStamp FontFaceSetDocumentImpl::GetNavigationStartTimeStamp() {
|
||||
TimeStamp navStart;
|
||||
RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
|
||||
if (timing) {
|
||||
navStart = timing->GetNavigationStartTimeStamp();
|
||||
}
|
||||
return navStart;
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::EnsureReady() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// There may be outstanding style changes that will trigger the loading of
|
||||
// new fonts. We need to flush layout to initiate any such loads so that
|
||||
// if mReady is currently resolved we replace it with a new pending Promise.
|
||||
// (That replacement will happen under this flush call.)
|
||||
if (!ReadyPromiseIsPending() && mDocument) {
|
||||
mDocument->FlushPendingNotifications(FlushType::Layout);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool FontFaceSetDocumentImpl::HasRuleFontFace(FontFaceImpl* aFontFace) {
|
||||
for (size_t i = 0; i < mRuleFaces.Length(); i++) {
|
||||
if (mRuleFaces[i].mFontFace == aFontFace) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool FontFaceSetDocumentImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
|
||||
if (!FontFaceSetImpl::Add(aFontFace, aRv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<dom::Document> clonedDoc = mDocument->GetLatestStaticClone();
|
||||
if (clonedDoc) {
|
||||
// The document is printing, copy the font to the static clone as well.
|
||||
nsCOMPtr<nsIPrincipal> principal = mDocument->GetPrincipal();
|
||||
if (principal->IsSystemPrincipal() || nsContentUtils::IsPDFJS(principal)) {
|
||||
ErrorResult rv;
|
||||
clonedDoc->Fonts()->Add(*aFontFace->GetOwner(), rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult FontFaceSetDocumentImpl::StartLoad(gfxUserFontEntry* aUserFontEntry,
|
||||
uint32_t aSrcIndex) {
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIStreamLoader> streamLoader;
|
||||
RefPtr<nsFontFaceLoader> fontLoader;
|
||||
|
||||
const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex);
|
||||
|
||||
auto preloadKey =
|
||||
PreloadHashKey::CreateAsFont(src.mURI->get(), CORS_ANONYMOUS);
|
||||
RefPtr<PreloaderBase> preload =
|
||||
mDocument->Preloads().LookupPreload(preloadKey);
|
||||
|
||||
if (preload) {
|
||||
fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this,
|
||||
preload->Channel());
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
|
||||
fontLoader);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = preload->AsyncConsume(streamLoader);
|
||||
|
||||
// We don't want this to hang around regardless of the result, there will be
|
||||
// no coalescing of later found <link preload> tags for fonts.
|
||||
preload->RemoveSelf(mDocument);
|
||||
} else {
|
||||
// No preload found, open a channel.
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup(mDocument->GetDocumentLoadGroup());
|
||||
if (NS_FAILED(rv)) {
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = FontPreloader::BuildChannel(
|
||||
getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS,
|
||||
dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src,
|
||||
mDocument, loadGroup, nullptr, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel);
|
||||
|
||||
if (LOG_ENABLED()) {
|
||||
nsCOMPtr<nsIURI> referrer = src.mReferrerInfo
|
||||
? src.mReferrerInfo->GetOriginalReferrer()
|
||||
: nullptr;
|
||||
LOG((
|
||||
"userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n",
|
||||
fontLoader.get(), src.mURI->GetSpecOrDefault().get(),
|
||||
referrer ? referrer->GetSpecOrDefault().get() : ""));
|
||||
}
|
||||
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
|
||||
fontLoader);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channel->AsyncOpen(streamLoader);
|
||||
if (NS_FAILED(rv)) {
|
||||
fontLoader->DropChannel(); // explicitly need to break ref cycle
|
||||
}
|
||||
}
|
||||
|
||||
mLoaders.PutEntry(fontLoader);
|
||||
|
||||
net::PredictorLearn(src.mURI->get(), mDocument->GetDocumentURI(),
|
||||
nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadGroup);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
fontLoader->StartedLoading(streamLoader);
|
||||
// let the font entry remember the loader, in case we need to cancel it
|
||||
aUserFontEntry->SetLoader(fontLoader);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool FontFaceSetDocumentImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) {
|
||||
MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL);
|
||||
|
||||
if (ServoStyleSet::IsInServoTraversal()) {
|
||||
auto entry = mAllowedFontLoads.Lookup(&aSrc);
|
||||
MOZ_DIAGNOSTIC_ASSERT(entry, "Missed an update?");
|
||||
return entry ? *entry : false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (aSrc.mUseOriginPrincipal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<gfxFontSrcPrincipal> gfxPrincipal =
|
||||
aSrc.mURI->InheritsSecurityContext() ? nullptr
|
||||
: aSrc.LoadPrincipal(*this);
|
||||
|
||||
nsIPrincipal* principal =
|
||||
gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr;
|
||||
|
||||
nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
|
||||
mDocument->NodePrincipal(), // loading principal
|
||||
principal, // triggering principal
|
||||
mDocument, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
|
||||
nsIContentPolicy::TYPE_FONT);
|
||||
|
||||
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
|
||||
nsresult rv = NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo,
|
||||
""_ns, // mime type
|
||||
&shouldLoad,
|
||||
nsContentUtils::GetContentPolicy());
|
||||
|
||||
return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
|
||||
}
|
||||
|
||||
nsresult FontFaceSetDocumentImpl::CreateChannelForSyncLoadFontData(
|
||||
nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad,
|
||||
const gfxFontFaceSrc* aFontFaceSrc) {
|
||||
gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal();
|
||||
|
||||
// Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a
|
||||
// node and a principal. This is because the document where the font is
|
||||
// being loaded might have a different origin from the principal of the
|
||||
// stylesheet that initiated the font load.
|
||||
// Further, we only get here for data: loads, so it doesn't really matter
|
||||
// whether we use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be
|
||||
// more restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT.
|
||||
return NS_NewChannelWithTriggeringPrincipal(
|
||||
aOutChannel, aFontFaceSrc->mURI->get(), mDocument,
|
||||
principal ? principal->NodePrincipal() : nullptr,
|
||||
nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
|
||||
aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT
|
||||
: nsIContentPolicy::TYPE_FONT);
|
||||
}
|
||||
|
||||
bool FontFaceSetDocumentImpl::UpdateRules(
|
||||
const nsTArray<nsFontFaceRuleContainer>& aRules) {
|
||||
// If there was a change to the mNonRuleFaces array, then there could
|
||||
// have been a modification to the user font set.
|
||||
bool modified = mNonRuleFacesDirty;
|
||||
mNonRuleFacesDirty = false;
|
||||
|
||||
// reuse existing FontFace objects mapped to rules already
|
||||
nsTHashMap<nsPtrHashKey<RawServoFontFaceRule>, FontFaceImpl*> ruleFaceMap;
|
||||
for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) {
|
||||
FontFaceImpl* f = mRuleFaces[i].mFontFace;
|
||||
if (!f || !f->GetOwner()) {
|
||||
continue;
|
||||
}
|
||||
ruleFaceMap.InsertOrUpdate(f->GetRule(), f);
|
||||
}
|
||||
|
||||
// The @font-face rules that make up the user font set have changed,
|
||||
// so we need to update the set. However, we want to preserve existing
|
||||
// font entries wherever possible, so that we don't discard and then
|
||||
// re-download resources in the (common) case where at least some of the
|
||||
// same rules are still present.
|
||||
|
||||
nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces);
|
||||
|
||||
// Remove faces from the font family records; we need to re-insert them
|
||||
// because we might end up with faces in a different order even if they're
|
||||
// the same font entries as before. (The order can affect font selection
|
||||
// where multiple faces match the requested style, perhaps with overlapping
|
||||
// unicode-range coverage.)
|
||||
for (const auto& fontFamily : mFontFamilies.Values()) {
|
||||
fontFamily->DetachFontEntries();
|
||||
}
|
||||
|
||||
// Sometimes aRules has duplicate @font-face rules in it; we should make
|
||||
// that not happen, but in the meantime, don't try to insert the same
|
||||
// FontFace object more than once into mRuleFaces. We track which
|
||||
// ones we've handled in this table.
|
||||
nsTHashSet<RawServoFontFaceRule*> handledRules;
|
||||
|
||||
for (size_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
|
||||
// Insert each FontFace objects for each rule into our list, migrating old
|
||||
// font entries if possible rather than creating new ones; set modified to
|
||||
// true if we detect that rule ordering has changed, or if a new entry is
|
||||
// created.
|
||||
RawServoFontFaceRule* rule = aRules[i].mRule;
|
||||
if (!handledRules.EnsureInserted(rule)) {
|
||||
// rule was already present in the hashtable
|
||||
continue;
|
||||
}
|
||||
RefPtr<FontFaceImpl> faceImpl = ruleFaceMap.Get(rule);
|
||||
RefPtr<FontFace> face = faceImpl ? faceImpl->GetOwner() : nullptr;
|
||||
if (mOwner && (!faceImpl || !face)) {
|
||||
face = FontFace::CreateForRule(mOwner->GetParentObject(), mOwner, rule);
|
||||
faceImpl = face->GetImpl();
|
||||
}
|
||||
InsertRuleFontFace(faceImpl, face, aRules[i].mOrigin, oldRecords, modified);
|
||||
}
|
||||
|
||||
for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
|
||||
// Do the same for the non rule backed FontFace objects.
|
||||
InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace, modified);
|
||||
}
|
||||
|
||||
// Remove any residual families that have no font entries (i.e., they were
|
||||
// not defined at all by the updated set of @font-face rules).
|
||||
for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) {
|
||||
if (!it.Data()->FontListLength()) {
|
||||
it.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
// If any FontFace objects for rules are left in the old list, note that the
|
||||
// set has changed (even if the new set was built entirely by migrating old
|
||||
// font entries).
|
||||
if (oldRecords.Length() > 0) {
|
||||
modified = true;
|
||||
// Any in-progress loaders for obsolete rules should be cancelled,
|
||||
// as the resource being downloaded will no longer be required.
|
||||
// We need to explicitly remove any loaders here, otherwise the loaders
|
||||
// will keep their "orphaned" font entries alive until they complete,
|
||||
// even after the oldRules array is deleted.
|
||||
//
|
||||
// XXX Now that it is possible for the author to hold on to a rule backed
|
||||
// FontFace object, we shouldn't cancel loading here; instead we should do
|
||||
// it when the FontFace is GCed, if we can detect that.
|
||||
size_t count = oldRecords.Length();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
RefPtr<FontFaceImpl> f = oldRecords[i].mFontFace;
|
||||
gfxUserFontEntry* userFontEntry = f->GetUserFontEntry();
|
||||
if (userFontEntry) {
|
||||
nsFontFaceLoader* loader = userFontEntry->GetLoader();
|
||||
if (loader) {
|
||||
loader->Cancel();
|
||||
RemoveLoader(loader);
|
||||
}
|
||||
}
|
||||
|
||||
// Any left over FontFace objects should also cease being rule backed.
|
||||
f->DisconnectFromRule();
|
||||
}
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
IncrementGeneration(true);
|
||||
mHasLoadingFontFacesIsDirty = true;
|
||||
CheckLoadingStarted();
|
||||
CheckLoadingFinished();
|
||||
}
|
||||
|
||||
// if local rules needed to be rebuilt, they have been rebuilt at this point
|
||||
if (mRebuildLocalRules) {
|
||||
mLocalRulesUsed = false;
|
||||
mRebuildLocalRules = false;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
|
||||
LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", this,
|
||||
(modified ? "modified" : "not modified"), (int)(mRuleFaces.Length())));
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::InsertRuleFontFace(
|
||||
FontFaceImpl* aFontFace, FontFace* aFontFaceOwner, StyleOrigin aSheetType,
|
||||
nsTArray<FontFaceRecord>& aOldRecords, bool& aFontSetModified) {
|
||||
nsAtom* fontFamily = aFontFace->GetFamilyName();
|
||||
if (!fontFamily) {
|
||||
// If there is no family name, this rule cannot contribute a
|
||||
// usable font, so there is no point in processing it further.
|
||||
return;
|
||||
}
|
||||
|
||||
bool remove = false;
|
||||
size_t removeIndex;
|
||||
|
||||
nsAtomCString family(fontFamily);
|
||||
|
||||
// This is a rule backed FontFace. First, we check in aOldRecords; if
|
||||
// the FontFace for the rule exists there, just move it to the new record
|
||||
// list, and put the entry into the appropriate family.
|
||||
for (size_t i = 0; i < aOldRecords.Length(); ++i) {
|
||||
FontFaceRecord& rec = aOldRecords[i];
|
||||
|
||||
if (rec.mFontFace == aFontFace && rec.mOrigin == Some(aSheetType)) {
|
||||
// if local rules were used, don't use the old font entry
|
||||
// for rules containing src local usage
|
||||
if (mLocalRulesUsed && mRebuildLocalRules) {
|
||||
if (aFontFace->HasLocalSrc()) {
|
||||
// Remove the old record, but wait to see if we successfully create a
|
||||
// new user font entry below.
|
||||
remove = true;
|
||||
removeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gfxUserFontEntry* entry = rec.mFontFace->GetUserFontEntry();
|
||||
MOZ_ASSERT(entry, "FontFace should have a gfxUserFontEntry by now");
|
||||
|
||||
AddUserFontEntry(family, entry);
|
||||
|
||||
MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace),
|
||||
"FontFace should not occur in mRuleFaces twice");
|
||||
|
||||
mRuleFaces.AppendElement(rec);
|
||||
aOldRecords.RemoveElementAt(i);
|
||||
|
||||
if (mOwner && aFontFaceOwner) {
|
||||
mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType);
|
||||
}
|
||||
|
||||
// note the set has been modified if an old rule was skipped to find
|
||||
// this one - something has been dropped, or ordering changed
|
||||
if (i > 0) {
|
||||
aFontSetModified = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a new rule:
|
||||
RefPtr<gfxUserFontEntry> entry =
|
||||
FindOrCreateUserFontEntryFromFontFace(family, aFontFace, aSheetType);
|
||||
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
// Although we broke out of the aOldRecords loop above, since we found
|
||||
// src local usage, and we're not using the old user font entry, we still
|
||||
// are adding a record to mRuleFaces with the same FontFace object.
|
||||
// Remove the old record so that we don't have the same FontFace listed
|
||||
// in both mRuleFaces and oldRecords, which would cause us to call
|
||||
// DisconnectFromRule on a FontFace that should still be rule backed.
|
||||
aOldRecords.RemoveElementAt(removeIndex);
|
||||
}
|
||||
|
||||
FontFaceRecord rec;
|
||||
rec.mFontFace = aFontFace;
|
||||
rec.mOrigin = Some(aSheetType);
|
||||
|
||||
aFontFace->SetUserFontEntry(entry);
|
||||
|
||||
MOZ_ASSERT(!HasRuleFontFace(aFontFace),
|
||||
"FontFace should not occur in mRuleFaces twice");
|
||||
|
||||
mRuleFaces.AppendElement(rec);
|
||||
|
||||
if (mOwner && aFontFaceOwner) {
|
||||
mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType);
|
||||
}
|
||||
|
||||
// this was a new rule and font entry, so note that the set was modified
|
||||
aFontSetModified = true;
|
||||
|
||||
// Add the entry to the end of the list. If an existing userfont entry was
|
||||
// returned by FindOrCreateUserFontEntryFromFontFace that was already stored
|
||||
// on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry
|
||||
// calls, will automatically remove the earlier occurrence of the same
|
||||
// userfont entry.
|
||||
AddUserFontEntry(family, entry);
|
||||
}
|
||||
|
||||
RawServoFontFaceRule* FontFaceSetDocumentImpl::FindRuleForEntry(
|
||||
gfxFontEntry* aFontEntry) {
|
||||
NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
|
||||
for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
|
||||
FontFaceImpl* f = mRuleFaces[i].mFontFace;
|
||||
gfxUserFontEntry* entry = f->GetUserFontEntry();
|
||||
if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
|
||||
return f->GetRule();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RawServoFontFaceRule* FontFaceSetDocumentImpl::FindRuleForUserFontEntry(
|
||||
gfxUserFontEntry* aUserFontEntry) {
|
||||
for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
|
||||
FontFaceImpl* f = mRuleFaces[i].mFontFace;
|
||||
if (f->GetUserFontEntry() == aUserFontEntry) {
|
||||
return f->GetRule();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::CacheFontLoadability() {
|
||||
// TODO(emilio): We could do it a bit more incrementally maybe?
|
||||
for (const auto& fontFamily : mFontFamilies.Values()) {
|
||||
fontFamily->ReadLock();
|
||||
for (const gfxFontEntry* entry : fontFamily->GetFontList()) {
|
||||
if (!entry->mIsUserFontContainer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& sourceList =
|
||||
static_cast<const gfxUserFontEntry*>(entry)->SourceList();
|
||||
for (const gfxFontFaceSrc& src : sourceList) {
|
||||
if (src.mSourceType != gfxFontFaceSrc::eSourceType_URL) {
|
||||
continue;
|
||||
}
|
||||
mAllowedFontLoads.LookupOrInsertWith(
|
||||
&src, [&] { return IsFontLoadAllowed(src); });
|
||||
}
|
||||
}
|
||||
fontFamily->ReadUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::DidRefresh() { CheckLoadingFinished(); }
|
||||
|
||||
void FontFaceSetDocumentImpl::UpdateHasLoadingFontFaces() {
|
||||
FontFaceSetImpl::UpdateHasLoadingFontFaces();
|
||||
|
||||
if (mHasLoadingFontFaces) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mRuleFaces.Length(); i++) {
|
||||
FontFaceImpl* f = mRuleFaces[i].mFontFace;
|
||||
if (f->Status() == FontFaceLoadStatus::Loading) {
|
||||
mHasLoadingFontFaces = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FontFaceSetDocumentImpl::MightHavePendingFontLoads() {
|
||||
if (FontFaceSetImpl::MightHavePendingFontLoads()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for pending restyles or reflows, as they might cause fonts to
|
||||
// load as new styles apply and text runs are rebuilt.
|
||||
nsPresContext* presContext = GetPresContext();
|
||||
if (presContext && presContext->HasPendingRestyleOrReflow()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mDocument) {
|
||||
// We defer resolving mReady until the document as fully loaded.
|
||||
if (!mDocument->DidFireDOMContentLoaded()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// And we also wait for any CSS style sheets to finish loading, as their
|
||||
// styles might cause new fonts to load.
|
||||
if (mDocument->CSSLoader()->HasPendingLoads()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// nsIDOMEventListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
FontFaceSetDocumentImpl::HandleEvent(Event* aEvent) {
|
||||
nsString type;
|
||||
aEvent->GetType(type);
|
||||
|
||||
if (!type.EqualsLiteral("DOMContentLoaded")) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RemoveDOMContentLoadedListener();
|
||||
CheckLoadingFinished();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsICSSLoaderObserver
|
||||
|
||||
NS_IMETHODIMP
|
||||
FontFaceSetDocumentImpl::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
|
||||
nsresult aStatus) {
|
||||
CheckLoadingFinished();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::FlushUserFontSet() {
|
||||
if (mDocument) {
|
||||
mDocument->FlushUserFontSet();
|
||||
}
|
||||
}
|
||||
|
||||
void FontFaceSetDocumentImpl::MarkUserFontSetDirty() {
|
||||
if (mDocument) {
|
||||
// Ensure we trigger at least a style flush, that will eventually flush the
|
||||
// user font set. Otherwise the font loads that that flush may cause could
|
||||
// never be triggered.
|
||||
if (PresShell* presShell = mDocument->GetPresShell()) {
|
||||
presShell->EnsureStyleFlush();
|
||||
}
|
||||
mDocument->MarkUserFontSetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOG_ENABLED
|
||||
#undef LOG
|
|
@ -0,0 +1,115 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_FontFaceSetDocumentImpl_h
|
||||
#define mozilla_dom_FontFaceSetDocumentImpl_h
|
||||
|
||||
#include "mozilla/dom/FontFaceSetImpl.h"
|
||||
#include "nsICSSLoaderObserver.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class FontFaceSetDocumentImpl final : public FontFaceSetImpl,
|
||||
public nsIDOMEventListener,
|
||||
public nsICSSLoaderObserver {
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
public:
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
FontFaceSetDocumentImpl(FontFaceSet* aOwner, dom::Document* aDocument);
|
||||
|
||||
void Initialize();
|
||||
void Destroy() override;
|
||||
|
||||
dom::Document* Document() const override { return mDocument; }
|
||||
|
||||
// gfxUserFontSet
|
||||
|
||||
nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
|
||||
uint32_t aSrcIndex) override;
|
||||
|
||||
bool IsFontLoadAllowed(const gfxFontFaceSrc&) override;
|
||||
|
||||
nsPresContext* GetPresContext() const override;
|
||||
|
||||
bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules) override;
|
||||
|
||||
RawServoFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry) override;
|
||||
|
||||
/**
|
||||
* Notification method called by the nsPresContext to indicate that the
|
||||
* refresh driver ticked and flushed style and layout.
|
||||
* were just flushed.
|
||||
*/
|
||||
void DidRefresh() override;
|
||||
|
||||
// nsICSSLoaderObserver
|
||||
NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
|
||||
nsresult aStatus) override;
|
||||
|
||||
// For ServoStyleSet to know ahead of time whether a font is loadable.
|
||||
void CacheFontLoadability() override;
|
||||
|
||||
void EnsureReady() override;
|
||||
|
||||
bool Add(FontFaceImpl* aFontFace, ErrorResult& aRv) override;
|
||||
|
||||
void FlushUserFontSet() override;
|
||||
void MarkUserFontSetDirty() override;
|
||||
|
||||
private:
|
||||
~FontFaceSetDocumentImpl() override;
|
||||
|
||||
uint64_t GetInnerWindowID() override;
|
||||
|
||||
void RemoveDOMContentLoadedListener();
|
||||
|
||||
nsresult CreateChannelForSyncLoadFontData(
|
||||
nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad,
|
||||
const gfxFontFaceSrc* aFontFaceSrc) override;
|
||||
|
||||
// search for @font-face rule that matches a userfont font entry
|
||||
RawServoFontFaceRule* FindRuleForUserFontEntry(
|
||||
gfxUserFontEntry* aUserFontEntry) override;
|
||||
|
||||
void FindMatchingFontFaces(const nsTHashSet<FontFace*>& aMatchingFaces,
|
||||
nsTArray<FontFace*>& aFontFaces) override;
|
||||
|
||||
void InsertRuleFontFace(FontFaceImpl* aFontFace, FontFace* aFontFaceOwner,
|
||||
StyleOrigin aOrigin,
|
||||
nsTArray<FontFaceRecord>& aOldRecords,
|
||||
bool& aFontSetModified);
|
||||
|
||||
// Helper function for HasLoadingFontFaces.
|
||||
void UpdateHasLoadingFontFaces() override;
|
||||
|
||||
/**
|
||||
* Returns whether there might be any pending font loads, which should cause
|
||||
* the mReady Promise not to be resolved yet.
|
||||
*/
|
||||
bool MightHavePendingFontLoads() override;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool HasRuleFontFace(FontFaceImpl* aFontFace);
|
||||
#endif
|
||||
|
||||
already_AddRefed<gfxFontSrcPrincipal> CreateStandardFontLoadPrincipal()
|
||||
const override;
|
||||
|
||||
TimeStamp GetNavigationStartTimeStamp() override;
|
||||
|
||||
// The document this is a FontFaceSet for.
|
||||
RefPtr<dom::Document> mDocument;
|
||||
|
||||
// The @font-face rule backed FontFace objects in the FontFaceSet.
|
||||
nsTArray<FontFaceRecord> mRuleFaces;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // !defined(mozilla_dom_FontFaceSetDocumentImpl_h)
|
|
@ -9,7 +9,6 @@
|
|||
#include "gfxFontConstants.h"
|
||||
#include "gfxFontSrcPrincipal.h"
|
||||
#include "gfxFontSrcURI.h"
|
||||
#include "FontPreloader.h"
|
||||
#include "mozilla/css/Loader.h"
|
||||
#include "mozilla/dom/CSSFontFaceRule.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
|
@ -36,16 +35,13 @@
|
|||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDeviceContext.h"
|
||||
#include "nsFontFaceLoader.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "nsILoadContext.h"
|
||||
#include "nsINetworkPredictor.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
@ -66,13 +62,10 @@ using namespace mozilla::dom;
|
|||
#define LOG_ENABLED() \
|
||||
MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
|
||||
|
||||
NS_IMPL_ISUPPORTS(FontFaceSetImpl, nsIDOMEventListener, nsICSSLoaderObserver)
|
||||
NS_IMPL_ISUPPORTS0(FontFaceSetImpl)
|
||||
|
||||
FontFaceSetImpl::FontFaceSetImpl(FontFaceSet* aOwner, dom::Document* aDocument)
|
||||
FontFaceSetImpl::FontFaceSetImpl(FontFaceSet* aOwner)
|
||||
: mOwner(aOwner),
|
||||
mDocument(aDocument),
|
||||
mStandardFontLoadPrincipal(new gfxFontSrcPrincipal(
|
||||
mDocument->NodePrincipal(), mDocument->PartitionedPrincipal())),
|
||||
mStatus(FontFaceSetLoadStatus::Loaded),
|
||||
mNonRuleFacesDirty(false),
|
||||
mHasLoadingFontFaces(false),
|
||||
|
@ -89,81 +82,29 @@ FontFaceSetImpl::~FontFaceSetImpl() {
|
|||
Destroy();
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::Initialize() {
|
||||
MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
|
||||
|
||||
// Record the state of the "bypass cache" flags from the docshell now,
|
||||
// since we want to look at them from style worker threads, and we can
|
||||
// only get to the docshell through a weak pointer (which is only
|
||||
// possible on the main thread).
|
||||
//
|
||||
// In theory the load type of a docshell could change after the document
|
||||
// is loaded, but handling that doesn't seem too important.
|
||||
if (nsCOMPtr<nsIDocShell> docShell = mDocument->GetDocShell()) {
|
||||
uint32_t loadType;
|
||||
uint32_t flags;
|
||||
if ((NS_SUCCEEDED(docShell->GetLoadType(&loadType)) &&
|
||||
((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)) ||
|
||||
(NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags)) &&
|
||||
(flags & nsIRequest::LOAD_BYPASS_CACHE))) {
|
||||
mBypassCache = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Same for the "private browsing" flag.
|
||||
if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) {
|
||||
mPrivateBrowsing = loadContext->UsePrivateBrowsing();
|
||||
}
|
||||
|
||||
if (!mDocument->DidFireDOMContentLoaded()) {
|
||||
mDocument->AddSystemEventListener(u"DOMContentLoaded"_ns, this, false,
|
||||
false);
|
||||
} else {
|
||||
// In some cases we can't rely on CheckLoadingFinished being called from
|
||||
// the refresh driver. For example, documents in display:none iframes.
|
||||
// Or if the document has finished loading and painting at the time that
|
||||
// script requests document.fonts and causes us to get here.
|
||||
CheckLoadingFinished();
|
||||
}
|
||||
|
||||
mDocument->CSSLoader()->AddObserver(this);
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::Destroy() {
|
||||
RemoveDOMContentLoadedListener();
|
||||
|
||||
if (mDocument && mDocument->CSSLoader()) {
|
||||
// We're null checking CSSLoader() since FontFaceSetImpl::Disconnect() might
|
||||
// be being called during unlink, at which time the loader may already have
|
||||
// been unlinked from the document.
|
||||
mDocument->CSSLoader()->RemoveObserver(this);
|
||||
}
|
||||
|
||||
for (const auto& key : mLoaders.Keys()) {
|
||||
key->Cancel();
|
||||
}
|
||||
|
||||
mLoaders.Clear();
|
||||
|
||||
mDocument = nullptr;
|
||||
|
||||
mRuleFaces.Clear();
|
||||
mNonRuleFaces.Clear();
|
||||
|
||||
gfxUserFontSet::Destroy();
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::RemoveDOMContentLoadedListener() {
|
||||
if (mDocument) {
|
||||
mDocument->RemoveSystemEventListener(u"DOMContentLoaded"_ns, this, false);
|
||||
}
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::ParseFontShorthandForMatching(
|
||||
const nsACString& aFont, StyleFontFamilyList& aFamilyList,
|
||||
FontWeight& aWeight, FontStretch& aStretch, FontSlantStyle& aStyle,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<URLExtraData> url = ServoCSSParser::GetURLExtraData(mDocument);
|
||||
auto* doc = Document();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
aRv.ThrowInvalidStateError("Missing document");
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<URLExtraData> url = ServoCSSParser::GetURLExtraData(doc);
|
||||
if (!ServoCSSParser::ParseFontShorthandForMatching(
|
||||
aFont, url, aFamilyList, aStyle, aStretch, aWeight)) {
|
||||
aRv.ThrowSyntaxError("Invalid font shorthand");
|
||||
|
@ -204,10 +145,6 @@ void FontFaceSetImpl::FindMatchingFontFaces(const nsACString& aFont,
|
|||
style.weight = weight;
|
||||
style.stretch = stretch;
|
||||
|
||||
nsTArray<FontFaceRecord>* arrays[2];
|
||||
arrays[0] = &mNonRuleFaces;
|
||||
arrays[1] = &mRuleFaces;
|
||||
|
||||
// Set of FontFaces that we want to return.
|
||||
nsTHashSet<FontFace*> matchingFaces;
|
||||
|
||||
|
@ -239,59 +176,35 @@ void FontFaceSetImpl::FindMatchingFontFaces(const nsACString& aFont,
|
|||
}
|
||||
}
|
||||
|
||||
if (matchingFaces.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add all FontFaces in matchingFaces to aFontFaces, in the order
|
||||
// they appear in the FontFaceSet.
|
||||
for (nsTArray<FontFaceRecord>* array : arrays) {
|
||||
for (FontFaceRecord& record : *array) {
|
||||
FontFace* owner = record.mFontFace->GetOwner();
|
||||
if (owner && matchingFaces.Contains(owner)) {
|
||||
aFontFaces.AppendElement(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
FindMatchingFontFaces(matchingFaces, aFontFaces);
|
||||
}
|
||||
|
||||
TimeStamp FontFaceSetImpl::GetNavigationStartTimeStamp() {
|
||||
TimeStamp navStart;
|
||||
RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
|
||||
if (timing) {
|
||||
navStart = timing->GetNavigationStartTimeStamp();
|
||||
void FontFaceSetImpl::FindMatchingFontFaces(
|
||||
const nsTHashSet<FontFace*>& aMatchingFaces,
|
||||
nsTArray<FontFace*>& aFontFaces) {
|
||||
for (FontFaceRecord& record : mNonRuleFaces) {
|
||||
FontFace* owner = record.mFontFace->GetOwner();
|
||||
if (owner && aMatchingFaces.Contains(owner)) {
|
||||
aFontFaces.AppendElement(owner);
|
||||
}
|
||||
}
|
||||
return navStart;
|
||||
}
|
||||
|
||||
bool FontFaceSetImpl::ReadyPromiseIsPending() const {
|
||||
return mOwner && mOwner->ReadyPromiseIsPending();
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::EnsureReady() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// There may be outstanding style changes that will trigger the loading of
|
||||
// new fonts. We need to flush layout to initiate any such loads so that
|
||||
// if mReady is currently resolved we replace it with a new pending Promise.
|
||||
// (That replacement will happen under this flush call.)
|
||||
if (!ReadyPromiseIsPending() && mDocument) {
|
||||
mDocument->FlushPendingNotifications(FlushType::Layout);
|
||||
}
|
||||
}
|
||||
|
||||
FontFaceSetLoadStatus FontFaceSetImpl::Status() {
|
||||
FlushUserFontSet();
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool FontFaceSetImpl::HasRuleFontFace(FontFaceImpl* aFontFace) {
|
||||
for (size_t i = 0; i < mRuleFaces.Length(); i++) {
|
||||
if (mRuleFaces[i].mFontFace == aFontFace) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool FontFaceSetImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
|
||||
FlushUserFontSet();
|
||||
|
||||
|
@ -322,17 +235,6 @@ bool FontFaceSetImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
|
|||
MarkUserFontSetDirty();
|
||||
mHasLoadingFontFacesIsDirty = true;
|
||||
CheckLoadingStarted();
|
||||
RefPtr<dom::Document> clonedDoc = mDocument->GetLatestStaticClone();
|
||||
if (clonedDoc) {
|
||||
// The document is printing, copy the font to the static clone as well.
|
||||
nsCOMPtr<nsIPrincipal> principal = mDocument->GetPrincipal();
|
||||
if (principal->IsSystemPrincipal() || nsContentUtils::IsPDFJS(principal)) {
|
||||
ErrorResult rv;
|
||||
clonedDoc->Fonts()->Add(*aFontFace->GetOwner(), rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -391,207 +293,6 @@ void FontFaceSetImpl::RemoveLoader(nsFontFaceLoader* aLoader) {
|
|||
mLoaders.RemoveEntry(aLoader);
|
||||
}
|
||||
|
||||
nsresult FontFaceSetImpl::StartLoad(gfxUserFontEntry* aUserFontEntry,
|
||||
uint32_t aSrcIndex) {
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIStreamLoader> streamLoader;
|
||||
RefPtr<nsFontFaceLoader> fontLoader;
|
||||
|
||||
const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex);
|
||||
|
||||
auto preloadKey =
|
||||
PreloadHashKey::CreateAsFont(src.mURI->get(), CORS_ANONYMOUS);
|
||||
RefPtr<PreloaderBase> preload =
|
||||
mDocument->Preloads().LookupPreload(preloadKey);
|
||||
|
||||
if (preload) {
|
||||
fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this,
|
||||
preload->Channel());
|
||||
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
|
||||
fontLoader);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = preload->AsyncConsume(streamLoader);
|
||||
|
||||
// We don't want this to hang around regardless of the result, there will be
|
||||
// no coalescing of later found <link preload> tags for fonts.
|
||||
preload->RemoveSelf(mDocument);
|
||||
} else {
|
||||
// No preload found, open a channel.
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup(mDocument->GetDocumentLoadGroup());
|
||||
if (NS_FAILED(rv)) {
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = FontPreloader::BuildChannel(
|
||||
getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS,
|
||||
dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src,
|
||||
mDocument, loadGroup, nullptr, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel);
|
||||
|
||||
if (LOG_ENABLED()) {
|
||||
nsCOMPtr<nsIURI> referrer = src.mReferrerInfo
|
||||
? src.mReferrerInfo->GetOriginalReferrer()
|
||||
: nullptr;
|
||||
LOG((
|
||||
"userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n",
|
||||
fontLoader.get(), src.mURI->GetSpecOrDefault().get(),
|
||||
referrer ? referrer->GetSpecOrDefault().get() : ""));
|
||||
}
|
||||
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
|
||||
fontLoader);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channel->AsyncOpen(streamLoader);
|
||||
if (NS_FAILED(rv)) {
|
||||
fontLoader->DropChannel(); // explicitly need to break ref cycle
|
||||
}
|
||||
}
|
||||
|
||||
mLoaders.PutEntry(fontLoader);
|
||||
|
||||
net::PredictorLearn(src.mURI->get(), mDocument->GetDocumentURI(),
|
||||
nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadGroup);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
fontLoader->StartedLoading(streamLoader);
|
||||
// let the font entry remember the loader, in case we need to cancel it
|
||||
aUserFontEntry->SetLoader(fontLoader);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool FontFaceSetImpl::UpdateRules(
|
||||
const nsTArray<nsFontFaceRuleContainer>& aRules) {
|
||||
// If there was a change to the mNonRuleFaces array, then there could
|
||||
// have been a modification to the user font set.
|
||||
bool modified = mNonRuleFacesDirty;
|
||||
mNonRuleFacesDirty = false;
|
||||
|
||||
// reuse existing FontFace objects mapped to rules already
|
||||
nsTHashMap<nsPtrHashKey<RawServoFontFaceRule>, FontFaceImpl*> ruleFaceMap;
|
||||
for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) {
|
||||
FontFaceImpl* f = mRuleFaces[i].mFontFace;
|
||||
if (!f || !f->GetOwner()) {
|
||||
continue;
|
||||
}
|
||||
ruleFaceMap.InsertOrUpdate(f->GetRule(), f);
|
||||
}
|
||||
|
||||
// The @font-face rules that make up the user font set have changed,
|
||||
// so we need to update the set. However, we want to preserve existing
|
||||
// font entries wherever possible, so that we don't discard and then
|
||||
// re-download resources in the (common) case where at least some of the
|
||||
// same rules are still present.
|
||||
|
||||
nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces);
|
||||
|
||||
// Remove faces from the font family records; we need to re-insert them
|
||||
// because we might end up with faces in a different order even if they're
|
||||
// the same font entries as before. (The order can affect font selection
|
||||
// where multiple faces match the requested style, perhaps with overlapping
|
||||
// unicode-range coverage.)
|
||||
for (const auto& fontFamily : mFontFamilies.Values()) {
|
||||
fontFamily->DetachFontEntries();
|
||||
}
|
||||
|
||||
// Sometimes aRules has duplicate @font-face rules in it; we should make
|
||||
// that not happen, but in the meantime, don't try to insert the same
|
||||
// FontFace object more than once into mRuleFaces. We track which
|
||||
// ones we've handled in this table.
|
||||
nsTHashSet<RawServoFontFaceRule*> handledRules;
|
||||
|
||||
for (size_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
|
||||
// Insert each FontFace objects for each rule into our list, migrating old
|
||||
// font entries if possible rather than creating new ones; set modified to
|
||||
// true if we detect that rule ordering has changed, or if a new entry is
|
||||
// created.
|
||||
RawServoFontFaceRule* rule = aRules[i].mRule;
|
||||
if (!handledRules.EnsureInserted(rule)) {
|
||||
// rule was already present in the hashtable
|
||||
continue;
|
||||
}
|
||||
RefPtr<FontFaceImpl> faceImpl = ruleFaceMap.Get(rule);
|
||||
RefPtr<FontFace> face = faceImpl ? faceImpl->GetOwner() : nullptr;
|
||||
if (mOwner && (!faceImpl || !face)) {
|
||||
face = FontFace::CreateForRule(mOwner->GetParentObject(), mOwner, rule);
|
||||
faceImpl = face->GetImpl();
|
||||
}
|
||||
InsertRuleFontFace(faceImpl, face, aRules[i].mOrigin, oldRecords, modified);
|
||||
}
|
||||
|
||||
for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
|
||||
// Do the same for the non rule backed FontFace objects.
|
||||
InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace, modified);
|
||||
}
|
||||
|
||||
// Remove any residual families that have no font entries (i.e., they were
|
||||
// not defined at all by the updated set of @font-face rules).
|
||||
for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) {
|
||||
if (!it.Data()->FontListLength()) {
|
||||
it.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
// If any FontFace objects for rules are left in the old list, note that the
|
||||
// set has changed (even if the new set was built entirely by migrating old
|
||||
// font entries).
|
||||
if (oldRecords.Length() > 0) {
|
||||
modified = true;
|
||||
// Any in-progress loaders for obsolete rules should be cancelled,
|
||||
// as the resource being downloaded will no longer be required.
|
||||
// We need to explicitly remove any loaders here, otherwise the loaders
|
||||
// will keep their "orphaned" font entries alive until they complete,
|
||||
// even after the oldRules array is deleted.
|
||||
//
|
||||
// XXX Now that it is possible for the author to hold on to a rule backed
|
||||
// FontFace object, we shouldn't cancel loading here; instead we should do
|
||||
// it when the FontFace is GCed, if we can detect that.
|
||||
size_t count = oldRecords.Length();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
RefPtr<FontFaceImpl> f = oldRecords[i].mFontFace;
|
||||
gfxUserFontEntry* userFontEntry = f->GetUserFontEntry();
|
||||
if (userFontEntry) {
|
||||
nsFontFaceLoader* loader = userFontEntry->GetLoader();
|
||||
if (loader) {
|
||||
loader->Cancel();
|
||||
RemoveLoader(loader);
|
||||
}
|
||||
}
|
||||
|
||||
// Any left over FontFace objects should also cease being rule backed.
|
||||
f->DisconnectFromRule();
|
||||
}
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
IncrementGeneration(true);
|
||||
mHasLoadingFontFacesIsDirty = true;
|
||||
CheckLoadingStarted();
|
||||
CheckLoadingFinished();
|
||||
}
|
||||
|
||||
// if local rules needed to be rebuilt, they have been rebuilt at this point
|
||||
if (mRebuildLocalRules) {
|
||||
mLocalRulesUsed = false;
|
||||
mRebuildLocalRules = false;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
|
||||
LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", this,
|
||||
(modified ? "modified" : "not modified"), (int)(mRuleFaces.Length())));
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::InsertNonRuleFontFace(FontFaceImpl* aFontFace,
|
||||
bool& aFontSetModified) {
|
||||
nsAtom* fontFamily = aFontFace->GetFamilyName();
|
||||
|
@ -618,110 +319,6 @@ void FontFaceSetImpl::InsertNonRuleFontFace(FontFaceImpl* aFontFace,
|
|||
AddUserFontEntry(family, aFontFace->GetUserFontEntry());
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::InsertRuleFontFace(FontFaceImpl* aFontFace,
|
||||
FontFace* aFontFaceOwner,
|
||||
StyleOrigin aSheetType,
|
||||
nsTArray<FontFaceRecord>& aOldRecords,
|
||||
bool& aFontSetModified) {
|
||||
nsAtom* fontFamily = aFontFace->GetFamilyName();
|
||||
if (!fontFamily) {
|
||||
// If there is no family name, this rule cannot contribute a
|
||||
// usable font, so there is no point in processing it further.
|
||||
return;
|
||||
}
|
||||
|
||||
bool remove = false;
|
||||
size_t removeIndex;
|
||||
|
||||
nsAtomCString family(fontFamily);
|
||||
|
||||
// This is a rule backed FontFace. First, we check in aOldRecords; if
|
||||
// the FontFace for the rule exists there, just move it to the new record
|
||||
// list, and put the entry into the appropriate family.
|
||||
for (size_t i = 0; i < aOldRecords.Length(); ++i) {
|
||||
FontFaceRecord& rec = aOldRecords[i];
|
||||
|
||||
if (rec.mFontFace == aFontFace && rec.mOrigin == Some(aSheetType)) {
|
||||
// if local rules were used, don't use the old font entry
|
||||
// for rules containing src local usage
|
||||
if (mLocalRulesUsed && mRebuildLocalRules) {
|
||||
if (aFontFace->HasLocalSrc()) {
|
||||
// Remove the old record, but wait to see if we successfully create a
|
||||
// new user font entry below.
|
||||
remove = true;
|
||||
removeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gfxUserFontEntry* entry = rec.mFontFace->GetUserFontEntry();
|
||||
MOZ_ASSERT(entry, "FontFace should have a gfxUserFontEntry by now");
|
||||
|
||||
AddUserFontEntry(family, entry);
|
||||
|
||||
MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace),
|
||||
"FontFace should not occur in mRuleFaces twice");
|
||||
|
||||
mRuleFaces.AppendElement(rec);
|
||||
aOldRecords.RemoveElementAt(i);
|
||||
|
||||
if (mOwner && aFontFaceOwner) {
|
||||
mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType);
|
||||
}
|
||||
|
||||
// note the set has been modified if an old rule was skipped to find
|
||||
// this one - something has been dropped, or ordering changed
|
||||
if (i > 0) {
|
||||
aFontSetModified = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a new rule:
|
||||
RefPtr<gfxUserFontEntry> entry =
|
||||
FindOrCreateUserFontEntryFromFontFace(family, aFontFace, aSheetType);
|
||||
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
// Although we broke out of the aOldRecords loop above, since we found
|
||||
// src local usage, and we're not using the old user font entry, we still
|
||||
// are adding a record to mRuleFaces with the same FontFace object.
|
||||
// Remove the old record so that we don't have the same FontFace listed
|
||||
// in both mRuleFaces and oldRecords, which would cause us to call
|
||||
// DisconnectFromRule on a FontFace that should still be rule backed.
|
||||
aOldRecords.RemoveElementAt(removeIndex);
|
||||
}
|
||||
|
||||
FontFaceRecord rec;
|
||||
rec.mFontFace = aFontFace;
|
||||
rec.mOrigin = Some(aSheetType);
|
||||
|
||||
aFontFace->SetUserFontEntry(entry);
|
||||
|
||||
MOZ_ASSERT(!HasRuleFontFace(aFontFace),
|
||||
"FontFace should not occur in mRuleFaces twice");
|
||||
|
||||
mRuleFaces.AppendElement(rec);
|
||||
|
||||
if (mOwner && aFontFaceOwner) {
|
||||
mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType);
|
||||
}
|
||||
|
||||
// this was a new rule and font entry, so note that the set was modified
|
||||
aFontSetModified = true;
|
||||
|
||||
// Add the entry to the end of the list. If an existing userfont entry was
|
||||
// returned by FindOrCreateUserFontEntryFromFontFace that was already stored
|
||||
// on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry
|
||||
// calls, will automatically remove the earlier occurrence of the same
|
||||
// userfont entry.
|
||||
AddUserFontEntry(family, entry);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<gfxUserFontEntry>
|
||||
FontFaceSetImpl::FindOrCreateUserFontEntryFromFontFace(
|
||||
|
@ -995,30 +592,6 @@ FontFaceSetImpl::FindOrCreateUserFontEntryFromFontFace(
|
|||
return entry.forget();
|
||||
}
|
||||
|
||||
RawServoFontFaceRule* FontFaceSetImpl::FindRuleForEntry(
|
||||
gfxFontEntry* aFontEntry) {
|
||||
NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
|
||||
for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
|
||||
FontFaceImpl* f = mRuleFaces[i].mFontFace;
|
||||
gfxUserFontEntry* entry = f->GetUserFontEntry();
|
||||
if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
|
||||
return f->GetRule();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RawServoFontFaceRule* FontFaceSetImpl::FindRuleForUserFontEntry(
|
||||
gfxUserFontEntry* aUserFontEntry) {
|
||||
for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
|
||||
FontFaceImpl* f = mRuleFaces[i].mFontFace;
|
||||
if (f->GetUserFontEntry() == aUserFontEntry) {
|
||||
return f->GetRule();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult FontFaceSetImpl::LogMessage(gfxUserFontEntry* aUserFontEntry,
|
||||
uint32_t aSrcIndex, const char* aMessage,
|
||||
uint32_t aFlags, nsresult aStatus) {
|
||||
|
@ -1096,14 +669,13 @@ nsresult FontFaceSetImpl::LogMessage(gfxUserFontEntry* aUserFontEntry,
|
|||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint64_t innerWindowID = mDocument->InnerWindowID();
|
||||
rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
|
||||
href, // file
|
||||
NS_ConvertUTF8toUTF16(text), // src line
|
||||
line, column,
|
||||
aFlags, // flags
|
||||
"CSS Loader", // category (make separate?)
|
||||
innerWindowID);
|
||||
GetInnerWindowID());
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
console->LogMessage(scriptError);
|
||||
}
|
||||
|
@ -1111,89 +683,13 @@ nsresult FontFaceSetImpl::LogMessage(gfxUserFontEntry* aUserFontEntry,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::CacheFontLoadability() {
|
||||
// TODO(emilio): We could do it a bit more incrementally maybe?
|
||||
for (const auto& fontFamily : mFontFamilies.Values()) {
|
||||
fontFamily->ReadLock();
|
||||
for (const gfxFontEntry* entry : fontFamily->GetFontList()) {
|
||||
if (!entry->mIsUserFontContainer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& sourceList =
|
||||
static_cast<const gfxUserFontEntry*>(entry)->SourceList();
|
||||
for (const gfxFontFaceSrc& src : sourceList) {
|
||||
if (src.mSourceType != gfxFontFaceSrc::eSourceType_URL) {
|
||||
continue;
|
||||
}
|
||||
mAllowedFontLoads.LookupOrInsertWith(
|
||||
&src, [&] { return IsFontLoadAllowed(src); });
|
||||
}
|
||||
}
|
||||
fontFamily->ReadUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool FontFaceSetImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) {
|
||||
MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL);
|
||||
|
||||
if (ServoStyleSet::IsInServoTraversal()) {
|
||||
auto entry = mAllowedFontLoads.Lookup(&aSrc);
|
||||
MOZ_DIAGNOSTIC_ASSERT(entry, "Missed an update?");
|
||||
return entry ? *entry : false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (aSrc.mUseOriginPrincipal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<gfxFontSrcPrincipal> gfxPrincipal =
|
||||
aSrc.mURI->InheritsSecurityContext() ? nullptr
|
||||
: aSrc.LoadPrincipal(*this);
|
||||
|
||||
nsIPrincipal* principal =
|
||||
gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr;
|
||||
|
||||
nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
|
||||
mDocument->NodePrincipal(), // loading principal
|
||||
principal, // triggering principal
|
||||
mDocument, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
|
||||
nsIContentPolicy::TYPE_FONT);
|
||||
|
||||
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
|
||||
nsresult rv = NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo,
|
||||
""_ns, // mime type
|
||||
&shouldLoad,
|
||||
nsContentUtils::GetContentPolicy());
|
||||
|
||||
return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
|
||||
}
|
||||
|
||||
nsresult FontFaceSetImpl::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
|
||||
const gfxFontFaceSrc* aFontFaceSrc,
|
||||
uint8_t*& aBuffer,
|
||||
uint32_t& aBufferLength) {
|
||||
nsresult rv;
|
||||
|
||||
gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal();
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
// Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a
|
||||
// node and a principal. This is because the document where the font is
|
||||
// being loaded might have a different origin from the principal of the
|
||||
// stylesheet that initiated the font load.
|
||||
// Further, we only get here for data: loads, so it doesn't really matter
|
||||
// whether we use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be
|
||||
// more restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT.
|
||||
rv = NS_NewChannelWithTriggeringPrincipal(
|
||||
getter_AddRefs(channel), aFontFaceSrc->mURI->get(), mDocument,
|
||||
principal ? principal->NodePrincipal() : nullptr,
|
||||
nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
|
||||
aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT
|
||||
: nsIContentPolicy::TYPE_FONT);
|
||||
|
||||
nsresult rv = CreateChannelForSyncLoadFontData(getter_AddRefs(channel),
|
||||
aFontToLoad, aFontFaceSrc);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// blocking stream is OK for data URIs
|
||||
|
@ -1290,14 +786,17 @@ void FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay() {
|
|||
return;
|
||||
}
|
||||
|
||||
auto* doc = Document();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> checkTask =
|
||||
NewRunnableMethod("dom::FontFaceSetImpl::CheckLoadingFinishedAfterDelay",
|
||||
this, &FontFaceSetImpl::CheckLoadingFinishedAfterDelay);
|
||||
mDocument->Dispatch(TaskCategory::Other, checkTask.forget());
|
||||
doc->Dispatch(TaskCategory::Other, checkTask.forget());
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::DidRefresh() { CheckLoadingFinished(); }
|
||||
|
||||
void FontFaceSetImpl::CheckLoadingFinishedAfterDelay() {
|
||||
mDelayedLoadCheck = false;
|
||||
CheckLoadingFinished();
|
||||
|
@ -1325,13 +824,6 @@ void FontFaceSetImpl::CheckLoadingStarted() {
|
|||
void FontFaceSetImpl::UpdateHasLoadingFontFaces() {
|
||||
mHasLoadingFontFacesIsDirty = false;
|
||||
mHasLoadingFontFaces = false;
|
||||
for (size_t i = 0; i < mRuleFaces.Length(); i++) {
|
||||
FontFaceImpl* f = mRuleFaces[i].mFontFace;
|
||||
if (f->Status() == FontFaceLoadStatus::Loading) {
|
||||
mHasLoadingFontFaces = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
|
||||
if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) {
|
||||
mHasLoadingFontFaces = true;
|
||||
|
@ -1349,31 +841,7 @@ bool FontFaceSetImpl::HasLoadingFontFaces() {
|
|||
|
||||
bool FontFaceSetImpl::MightHavePendingFontLoads() {
|
||||
// Check for FontFace objects in the FontFaceSet that are still loading.
|
||||
if (HasLoadingFontFaces()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for pending restyles or reflows, as they might cause fonts to
|
||||
// load as new styles apply and text runs are rebuilt.
|
||||
nsPresContext* presContext = GetPresContext();
|
||||
if (presContext && presContext->HasPendingRestyleOrReflow()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mDocument) {
|
||||
// We defer resolving mReady until the document as fully loaded.
|
||||
if (!mDocument->DidFireDOMContentLoaded()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// And we also wait for any CSS style sheets to finish loading, as their
|
||||
// styles might cause new fonts to load.
|
||||
if (mDocument->CSSLoader()->HasPendingLoads()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return HasLoadingFontFaces();
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::CheckLoadingFinished() {
|
||||
|
@ -1401,67 +869,13 @@ void FontFaceSetImpl::CheckLoadingFinished() {
|
|||
}
|
||||
}
|
||||
|
||||
// nsIDOMEventListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
FontFaceSetImpl::HandleEvent(Event* aEvent) {
|
||||
nsString type;
|
||||
aEvent->GetType(type);
|
||||
|
||||
if (!type.EqualsLiteral("DOMContentLoaded")) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RemoveDOMContentLoadedListener();
|
||||
CheckLoadingFinished();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool FontFaceSetImpl::PrefEnabled() {
|
||||
return StaticPrefs::layout_css_font_loading_api_enabled();
|
||||
}
|
||||
|
||||
// nsICSSLoaderObserver
|
||||
|
||||
NS_IMETHODIMP
|
||||
FontFaceSetImpl::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
|
||||
nsresult aStatus) {
|
||||
CheckLoadingFinished();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::FlushUserFontSet() {
|
||||
if (mDocument) {
|
||||
mDocument->FlushUserFontSet();
|
||||
}
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::MarkUserFontSetDirty() {
|
||||
if (mDocument) {
|
||||
// Ensure we trigger at least a style flush, that will eventually flush the
|
||||
// user font set. Otherwise the font loads that that flush may cause could
|
||||
// never be triggered.
|
||||
if (PresShell* presShell = mDocument->GetPresShell()) {
|
||||
presShell->EnsureStyleFlush();
|
||||
}
|
||||
mDocument->MarkUserFontSetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
nsPresContext* FontFaceSetImpl::GetPresContext() const {
|
||||
if (!mDocument) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mDocument->GetPresContext();
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mStandardFontLoadPrincipal = new gfxFontSrcPrincipal(
|
||||
mDocument->NodePrincipal(), mDocument->PartitionedPrincipal());
|
||||
mStandardFontLoadPrincipal = CreateStandardFontLoadPrincipal();
|
||||
mAllowedFontLoads.Clear();
|
||||
IncrementGeneration(false);
|
||||
}
|
||||
|
@ -1469,6 +883,14 @@ void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() {
|
|||
// -- gfxUserFontSet
|
||||
// ------------------------------------------------
|
||||
|
||||
already_AddRefed<gfxFontSrcPrincipal>
|
||||
FontFaceSetImpl::GetStandardFontLoadPrincipal() const {
|
||||
if (!mStandardFontLoadPrincipal) {
|
||||
mStandardFontLoadPrincipal = CreateStandardFontLoadPrincipal();
|
||||
}
|
||||
return RefPtr{mStandardFontLoadPrincipal}.forget();
|
||||
}
|
||||
|
||||
void FontFaceSetImpl::RecordFontLoadDone(uint32_t aFontSize,
|
||||
TimeStamp aDoneTime) {
|
||||
mDownloadCount++;
|
||||
|
|
|
@ -19,6 +19,7 @@ struct gfxFontFaceSrc;
|
|||
class gfxFontSrcPrincipal;
|
||||
class gfxUserFontEntry;
|
||||
class nsFontFaceLoader;
|
||||
class nsIChannel;
|
||||
class nsIPrincipal;
|
||||
class nsPIDOMWindowInner;
|
||||
struct RawServoFontFaceRule;
|
||||
|
@ -33,31 +34,24 @@ class FontFace;
|
|||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class FontFaceSetImpl final : public gfxUserFontSet,
|
||||
public nsIDOMEventListener,
|
||||
public nsICSSLoaderObserver {
|
||||
class FontFaceSetImpl : public nsISupports, public gfxUserFontSet {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
public:
|
||||
// gfxUserFontSet
|
||||
|
||||
already_AddRefed<gfxFontSrcPrincipal> GetStandardFontLoadPrincipal()
|
||||
const override {
|
||||
return RefPtr{mStandardFontLoadPrincipal}.forget();
|
||||
}
|
||||
|
||||
nsPresContext* GetPresContext() const override;
|
||||
|
||||
bool IsFontLoadAllowed(const gfxFontFaceSrc&) override;
|
||||
|
||||
nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
|
||||
uint32_t aSrcIndex) override;
|
||||
const override;
|
||||
|
||||
void RecordFontLoadDone(uint32_t aFontSize, TimeStamp aDoneTime) override;
|
||||
|
||||
bool BypassCache() final { return mBypassCache; }
|
||||
|
||||
protected:
|
||||
virtual nsresult CreateChannelForSyncLoadFontData(
|
||||
nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad,
|
||||
const gfxFontFaceSrc* aFontFaceSrc) = 0;
|
||||
|
||||
// gfxUserFontSet
|
||||
|
||||
bool GetPrivateBrowsing() override { return mPrivateBrowsing; }
|
||||
|
@ -80,22 +74,25 @@ class FontFaceSetImpl final : public gfxUserFontSet,
|
|||
float aAscentOverride, float aDescentOverride, float aLineGapOverride,
|
||||
float aSizeAdjust) override;
|
||||
|
||||
explicit FontFaceSetImpl(FontFaceSet* aOwner);
|
||||
|
||||
public:
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
FontFaceSetImpl(FontFaceSet* aOwner, dom::Document* aDocument);
|
||||
|
||||
void Initialize();
|
||||
void Destroy();
|
||||
virtual void Destroy();
|
||||
|
||||
// Called by nsFontFaceLoader when the loader has completed normally.
|
||||
// It's removed from the mLoaders set.
|
||||
void RemoveLoader(nsFontFaceLoader* aLoader);
|
||||
|
||||
bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
|
||||
virtual bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules) {
|
||||
MOZ_ASSERT_UNREACHABLE("Not implemented!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// search for @font-face rule that matches a platform font entry
|
||||
RawServoFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
|
||||
virtual RawServoFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry) {
|
||||
MOZ_ASSERT_UNREACHABLE("Not implemented!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an existing entry in the user font cache or creates a new user
|
||||
|
@ -115,18 +112,14 @@ class FontFaceSetImpl final : public gfxUserFontSet,
|
|||
* refresh driver ticked and flushed style and layout.
|
||||
* were just flushed.
|
||||
*/
|
||||
void DidRefresh();
|
||||
virtual void DidRefresh() { MOZ_ASSERT_UNREACHABLE("Not implemented!"); }
|
||||
|
||||
/**
|
||||
* Returns whether the "layout.css.font-loading-api.enabled" pref is true.
|
||||
*/
|
||||
static bool PrefEnabled();
|
||||
|
||||
// nsICSSLoaderObserver
|
||||
NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
|
||||
nsresult aStatus) override;
|
||||
|
||||
void FlushUserFontSet();
|
||||
virtual void FlushUserFontSet() {}
|
||||
|
||||
static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet) {
|
||||
const auto* set = static_cast<FontFaceSetImpl*>(aUserFontSet);
|
||||
|
@ -135,21 +128,23 @@ class FontFaceSetImpl final : public gfxUserFontSet,
|
|||
|
||||
void RefreshStandardFontLoadPrincipal();
|
||||
|
||||
dom::Document* Document() const { return mDocument; }
|
||||
virtual dom::Document* Document() const { return nullptr; }
|
||||
|
||||
// -- Web IDL --------------------------------------------------------------
|
||||
|
||||
void EnsureReady();
|
||||
virtual void EnsureReady() {}
|
||||
dom::FontFaceSetLoadStatus Status();
|
||||
|
||||
bool Add(FontFaceImpl* aFontFace, ErrorResult& aRv);
|
||||
virtual bool Add(FontFaceImpl* aFontFace, ErrorResult& aRv);
|
||||
void Clear();
|
||||
bool Delete(FontFaceImpl* aFontFace);
|
||||
|
||||
// For ServoStyleSet to know ahead of time whether a font is loadable.
|
||||
void CacheFontLoadability();
|
||||
virtual void CacheFontLoadability() {
|
||||
MOZ_ASSERT_UNREACHABLE("Not implemented!");
|
||||
}
|
||||
|
||||
void MarkUserFontSetDirty();
|
||||
virtual void MarkUserFontSetDirty() {}
|
||||
|
||||
/**
|
||||
* Checks to see whether it is time to resolve mReady and dispatch any
|
||||
|
@ -162,21 +157,21 @@ class FontFaceSetImpl final : public gfxUserFontSet,
|
|||
|
||||
void DispatchCheckLoadingFinishedAfterDelay();
|
||||
|
||||
private:
|
||||
~FontFaceSetImpl();
|
||||
protected:
|
||||
~FontFaceSetImpl() override;
|
||||
|
||||
virtual uint64_t GetInnerWindowID() = 0;
|
||||
|
||||
/**
|
||||
* Returns whether the given FontFace is currently "in" the FontFaceSet.
|
||||
*/
|
||||
bool HasAvailableFontFace(FontFaceImpl* aFontFace);
|
||||
|
||||
void RemoveDOMContentLoadedListener();
|
||||
|
||||
/**
|
||||
* Returns whether there might be any pending font loads, which should cause
|
||||
* the mReady Promise not to be resolved yet.
|
||||
*/
|
||||
bool MightHavePendingFontLoads();
|
||||
virtual bool MightHavePendingFontLoads();
|
||||
|
||||
/**
|
||||
* Checks to see whether it is time to replace mReady and dispatch a
|
||||
|
@ -203,23 +198,20 @@ class FontFaceSetImpl final : public gfxUserFontSet,
|
|||
FontFaceImpl* aFontFace, StyleOrigin);
|
||||
|
||||
// search for @font-face rule that matches a userfont font entry
|
||||
RawServoFontFaceRule* FindRuleForUserFontEntry(
|
||||
gfxUserFontEntry* aUserFontEntry);
|
||||
virtual RawServoFontFaceRule* FindRuleForUserFontEntry(
|
||||
gfxUserFontEntry* aUserFontEntry) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void FindMatchingFontFaces(
|
||||
const nsTHashSet<FontFace*>& aMatchingFaces,
|
||||
nsTArray<FontFace*>& aFontFaces);
|
||||
|
||||
already_AddRefed<gfxFontSrcPrincipal> GetStandardFontLoadPrincipal();
|
||||
nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
|
||||
gfxFontSrcPrincipal** aPrincipal, bool* aBypassCache);
|
||||
|
||||
void InsertRuleFontFace(FontFaceImpl* aFontFace, FontFace* aFontFaceOwner,
|
||||
StyleOrigin aOrigin,
|
||||
nsTArray<FontFaceRecord>& aOldRecords,
|
||||
bool& aFontSetModified);
|
||||
void InsertNonRuleFontFace(FontFaceImpl* aFontFace, bool& aFontSetModified);
|
||||
|
||||
#ifdef DEBUG
|
||||
bool HasRuleFontFace(FontFaceImpl* aFontFace);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns whether we have any loading FontFace objects in the FontFaceSet.
|
||||
*/
|
||||
|
@ -229,20 +221,20 @@ class FontFaceSetImpl final : public gfxUserFontSet,
|
|||
bool ReadyPromiseIsPending() const;
|
||||
|
||||
// Helper function for HasLoadingFontFaces.
|
||||
void UpdateHasLoadingFontFaces();
|
||||
virtual void UpdateHasLoadingFontFaces();
|
||||
|
||||
void ParseFontShorthandForMatching(const nsACString& aFont,
|
||||
StyleFontFamilyList& aFamilyList,
|
||||
FontWeight& aWeight, FontStretch& aStretch,
|
||||
FontSlantStyle& aStyle, ErrorResult& aRv);
|
||||
|
||||
TimeStamp GetNavigationStartTimeStamp();
|
||||
virtual TimeStamp GetNavigationStartTimeStamp() = 0;
|
||||
|
||||
virtual already_AddRefed<gfxFontSrcPrincipal>
|
||||
CreateStandardFontLoadPrincipal() const = 0;
|
||||
|
||||
FontFaceSet* MOZ_NON_OWNING_REF mOwner;
|
||||
|
||||
// The document this is a FontFaceSet for.
|
||||
RefPtr<dom::Document> mDocument;
|
||||
|
||||
// The document's node principal, which is the principal font loads for
|
||||
// this FontFaceSet will generally use. (This principal is not used for
|
||||
// @font-face rules in UA and user sheets, where the principal of the
|
||||
|
@ -254,16 +246,13 @@ class FontFaceSetImpl final : public gfxUserFontSet,
|
|||
//
|
||||
// Because mDocument's principal can change over time,
|
||||
// its value must be updated by a call to ResetStandardFontLoadPrincipal.
|
||||
RefPtr<gfxFontSrcPrincipal> mStandardFontLoadPrincipal;
|
||||
mutable RefPtr<gfxFontSrcPrincipal> mStandardFontLoadPrincipal;
|
||||
|
||||
// Set of all loaders pointing to us. These are not strong pointers,
|
||||
// but that's OK because nsFontFaceLoader always calls RemoveLoader on
|
||||
// us before it dies (unless we die first).
|
||||
nsTHashtable<nsPtrHashKey<nsFontFaceLoader>> mLoaders;
|
||||
|
||||
// The @font-face rule backed FontFace objects in the FontFaceSet.
|
||||
nsTArray<FontFaceRecord> mRuleFaces;
|
||||
|
||||
// The non rule backed FontFace objects that have been added to this
|
||||
// FontFaceSet.
|
||||
nsTArray<FontFaceRecord> mNonRuleFaces;
|
||||
|
|
|
@ -142,6 +142,7 @@ EXPORTS.mozilla.dom += [
|
|||
"FontFace.h",
|
||||
"FontFaceImpl.h",
|
||||
"FontFaceSet.h",
|
||||
"FontFaceSetDocumentImpl.h",
|
||||
"FontFaceSetImpl.h",
|
||||
"FontFaceSetIterator.h",
|
||||
"MediaList.h",
|
||||
|
@ -192,6 +193,7 @@ UNIFIED_SOURCES += [
|
|||
"FontFace.cpp",
|
||||
"FontFaceImpl.cpp",
|
||||
"FontFaceSet.cpp",
|
||||
"FontFaceSetDocumentImpl.cpp",
|
||||
"FontFaceSetImpl.cpp",
|
||||
"FontFaceSetIterator.cpp",
|
||||
"FontPreloader.cpp",
|
||||
|
|
Загрузка…
Ссылка в новой задаче