1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843 |
- /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
- /* 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 "FontFaceSet.h"
- #include "gfxFontConstants.h"
- #include "mozilla/css/Declaration.h"
- #include "mozilla/css/Loader.h"
- #include "mozilla/dom/FontFaceSetBinding.h"
- #include "mozilla/dom/FontFaceSetIterator.h"
- #include "mozilla/dom/FontFaceSetLoadEvent.h"
- #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
- #include "mozilla/dom/Promise.h"
- #include "mozilla/AsyncEventDispatcher.h"
- #include "mozilla/Logging.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/SizePrintfMacros.h"
- #include "mozilla/Sprintf.h"
- #include "mozilla/Telemetry.h"
- #include "nsAutoPtr.h"
- #include "nsContentPolicyUtils.h"
- #include "nsCSSParser.h"
- #include "nsDeviceContext.h"
- #include "nsFontFaceLoader.h"
- #include "nsIConsoleService.h"
- #include "nsIContentPolicy.h"
- #include "nsIContentSecurityPolicy.h"
- #include "nsIDocShell.h"
- #include "nsIDocument.h"
- #include "nsINetworkPredictor.h"
- #include "nsIPresShell.h"
- #include "nsIPrincipal.h"
- #include "nsISupportsPriority.h"
- #include "nsIWebNavigation.h"
- #include "nsNetUtil.h"
- #include "nsIProtocolHandler.h"
- #include "nsIInputStream.h"
- #include "nsPresContext.h"
- #include "nsPrintfCString.h"
- #include "nsStyleSet.h"
- #include "nsUTF8Utils.h"
- #include "nsDOMNavigationTiming.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)
- #define FONT_LOADING_API_ENABLED_PREF "layout.css.font-loading-api.enabled"
- NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelper)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady);
- for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace);
- }
- for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces[i].mFontFace);
- }
- if (tmp->mUserFontSet) {
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserFontSet->mFontFaceSet);
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelper)
- tmp->Disconnect();
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady);
- for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces[i].mFontFace);
- }
- for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces[i].mFontFace);
- }
- if (tmp->mUserFontSet) {
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mUserFontSet->mFontFaceSet);
- }
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mUserFontSet);
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper)
- NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FontFaceSet)
- NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
- NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
- NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
- FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
- : DOMEventTargetHelper(aWindow)
- , mDocument(aDocument)
- , mResolveLazilyCreatedReadyPromise(false)
- , mStatus(FontFaceSetLoadStatus::Loaded)
- , mNonRuleFacesDirty(false)
- , mHasLoadingFontFaces(false)
- , mHasLoadingFontFacesIsDirty(false)
- , mDelayedLoadCheck(false)
- {
- MOZ_COUNT_CTOR(FontFaceSet);
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aWindow);
- // If the pref is not set, don't create the Promise (which the page wouldn't
- // be able to get to anyway) as it causes the window.FontFaceSet constructor
- // to be created.
- if (global && PrefEnabled()) {
- mResolveLazilyCreatedReadyPromise = true;
- }
- if (!mDocument->DidFireDOMContentLoaded()) {
- mDocument->AddSystemEventListener(NS_LITERAL_STRING("DOMContentLoaded"),
- this, false, false);
- }
- mDocument->CSSLoader()->AddObserver(this);
- mUserFontSet = new UserFontSet(this);
- }
- FontFaceSet::~FontFaceSet()
- {
- MOZ_COUNT_DTOR(FontFaceSet);
- Disconnect();
- for (auto it = mLoaders.Iter(); !it.Done(); it.Next()) {
- it.Get()->GetKey()->Cancel();
- }
- }
- JSObject*
- FontFaceSet::WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto)
- {
- return FontFaceSetBinding::Wrap(aContext, this, aGivenProto);
- }
- void
- FontFaceSet::Disconnect()
- {
- RemoveDOMContentLoadedListener();
- if (mDocument && mDocument->CSSLoader()) {
- // We're null checking CSSLoader() since FontFaceSet::Disconnect() might be
- // being called during unlink, at which time the loader amy already have
- // been unlinked from the document.
- mDocument->CSSLoader()->RemoveObserver(this);
- }
- }
- void
- FontFaceSet::RemoveDOMContentLoadedListener()
- {
- if (mDocument) {
- mDocument->RemoveSystemEventListener(NS_LITERAL_STRING("DOMContentLoaded"),
- this, false);
- }
- }
- void
- FontFaceSet::ParseFontShorthandForMatching(
- const nsAString& aFont,
- RefPtr<FontFamilyListRefCnt>& aFamilyList,
- uint32_t& aWeight,
- int32_t& aStretch,
- uint8_t& aStyle,
- ErrorResult& aRv)
- {
- // Parse aFont as a 'font' property value.
- RefPtr<Declaration> declaration = new Declaration;
- declaration->InitializeEmpty();
- bool changed = false;
- nsCSSParser parser;
- parser.ParseProperty(eCSSProperty_font,
- aFont,
- mDocument->GetDocumentURI(),
- mDocument->GetDocumentURI(),
- mDocument->NodePrincipal(),
- declaration,
- &changed,
- /* aIsImportant */ false);
- // All of the properties we are interested in should have been set at once.
- MOZ_ASSERT(changed == (declaration->HasProperty(eCSSProperty_font_family) &&
- declaration->HasProperty(eCSSProperty_font_style) &&
- declaration->HasProperty(eCSSProperty_font_weight) &&
- declaration->HasProperty(eCSSProperty_font_stretch)));
- if (!changed) {
- aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
- return;
- }
- nsCSSCompressedDataBlock* data = declaration->GetNormalBlock();
- MOZ_ASSERT(!declaration->GetImportantBlock());
- const nsCSSValue* family = data->ValueFor(eCSSProperty_font_family);
- if (family->GetUnit() != eCSSUnit_FontFamilyList) {
- // We got inherit, initial, unset, a system font, or a token stream.
- aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
- return;
- }
- aFamilyList =
- static_cast<FontFamilyListRefCnt*>(family->GetFontFamilyListValue());
- int32_t weight = data->ValueFor(eCSSProperty_font_weight)->GetIntValue();
- // Resolve relative font weights against the initial of font-weight
- // (normal, which is equivalent to 400).
- if (weight == NS_STYLE_FONT_WEIGHT_BOLDER) {
- weight = NS_FONT_WEIGHT_BOLD;
- } else if (weight == NS_STYLE_FONT_WEIGHT_LIGHTER) {
- weight = NS_FONT_WEIGHT_THIN;
- }
- aWeight = weight;
- aStretch = data->ValueFor(eCSSProperty_font_stretch)->GetIntValue();
- aStyle = data->ValueFor(eCSSProperty_font_style)->GetIntValue();
- }
- static bool
- HasAnyCharacterInUnicodeRange(gfxUserFontEntry* aEntry,
- const nsAString& aInput)
- {
- const char16_t* p = aInput.Data();
- const char16_t* end = p + aInput.Length();
- while (p < end) {
- uint32_t c = UTF16CharEnumerator::NextChar(&p, end);
- if (aEntry->CharacterInUnicodeRange(c)) {
- return true;
- }
- }
- return false;
- }
- void
- FontFaceSet::FindMatchingFontFaces(const nsAString& aFont,
- const nsAString& aText,
- nsTArray<FontFace*>& aFontFaces,
- ErrorResult& aRv)
- {
- RefPtr<FontFamilyListRefCnt> familyList;
- uint32_t weight;
- int32_t stretch;
- uint8_t italicStyle;
- ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle,
- aRv);
- if (aRv.Failed()) {
- return;
- }
- gfxFontStyle style;
- style.style = italicStyle;
- style.weight = weight;
- style.stretch = stretch;
- nsTArray<FontFaceRecord>* arrays[2];
- arrays[0] = &mNonRuleFaces;
- arrays[1] = &mRuleFaces;
- // Set of FontFaces that we want to return.
- nsTHashtable<nsPtrHashKey<FontFace>> matchingFaces;
- for (const FontFamilyName& fontFamilyName : familyList->GetFontlist()) {
- RefPtr<gfxFontFamily> family =
- mUserFontSet->LookupFamily(fontFamilyName.mName);
- if (!family) {
- continue;
- }
- AutoTArray<gfxFontEntry*,4> entries;
- bool needsBold;
- family->FindAllFontsForStyle(style, entries, needsBold);
- for (gfxFontEntry* e : entries) {
- FontFace::Entry* entry = static_cast<FontFace::Entry*>(e);
- if (HasAnyCharacterInUnicodeRange(entry, aText)) {
- for (FontFace* f : entry->GetFontFaces()) {
- matchingFaces.PutEntry(f);
- }
- }
- }
- }
- // 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* f = record.mFontFace;
- if (matchingFaces.Contains(f)) {
- aFontFaces.AppendElement(f);
- }
- }
- }
- }
- TimeStamp
- FontFaceSet::GetNavigationStartTimeStamp()
- {
- TimeStamp navStart;
- RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
- if (timing) {
- navStart = timing->GetNavigationStartTimeStamp();
- }
- return navStart;
- }
- already_AddRefed<Promise>
- FontFaceSet::Load(JSContext* aCx,
- const nsAString& aFont,
- const nsAString& aText,
- ErrorResult& aRv)
- {
- FlushUserFontSet();
- nsTArray<RefPtr<Promise>> promises;
- nsTArray<FontFace*> faces;
- FindMatchingFontFaces(aFont, aText, faces, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
- for (FontFace* f : faces) {
- RefPtr<Promise> promise = f->Load(aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
- if (!promises.AppendElement(promise, fallible)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return nullptr;
- }
- }
- return Promise::All(aCx, promises, aRv);
- }
- bool
- FontFaceSet::Check(const nsAString& aFont,
- const nsAString& aText,
- ErrorResult& aRv)
- {
- FlushUserFontSet();
- nsTArray<FontFace*> faces;
- FindMatchingFontFaces(aFont, aText, faces, aRv);
- if (aRv.Failed()) {
- return false;
- }
- for (FontFace* f : faces) {
- if (f->Status() != FontFaceLoadStatus::Loaded) {
- return false;
- }
- }
- return true;
- }
- Promise*
- FontFaceSet::GetReady(ErrorResult& aRv)
- {
- if (!mReady) {
- nsCOMPtr<nsIGlobalObject> global = GetParentObject();
- mReady = Promise::Create(global, aRv);
- if (!mReady) {
- aRv.Throw(NS_ERROR_FAILURE);
- return nullptr;
- }
- if (mResolveLazilyCreatedReadyPromise) {
- mReady->MaybeResolve(this);
- mResolveLazilyCreatedReadyPromise = false;
- }
- }
- FlushUserFontSet();
- return mReady;
- }
- FontFaceSetLoadStatus
- FontFaceSet::Status()
- {
- FlushUserFontSet();
- return mStatus;
- }
- #ifdef DEBUG
- bool
- FontFaceSet::HasRuleFontFace(FontFace* aFontFace)
- {
- for (size_t i = 0; i < mRuleFaces.Length(); i++) {
- if (mRuleFaces[i].mFontFace == aFontFace) {
- return true;
- }
- }
- return false;
- }
- #endif
- FontFaceSet*
- FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv)
- {
- FlushUserFontSet();
- if (aFontFace.IsInFontFaceSet(this)) {
- return this;
- }
- if (aFontFace.HasRule()) {
- aRv.Throw(NS_ERROR_DOM_INVALID_MODIFICATION_ERR);
- return nullptr;
- }
- aFontFace.AddFontFaceSet(this);
- #ifdef DEBUG
- for (const FontFaceRecord& rec : mNonRuleFaces) {
- MOZ_ASSERT(rec.mFontFace != &aFontFace,
- "FontFace should not occur in mNonRuleFaces twice");
- }
- #endif
- FontFaceRecord* rec = mNonRuleFaces.AppendElement();
- rec->mFontFace = &aFontFace;
- rec->mSheetType = SheetType::Unknown; // unused for mNonRuleFaces
- rec->mLoadEventShouldFire =
- aFontFace.Status() == FontFaceLoadStatus::Unloaded ||
- aFontFace.Status() == FontFaceLoadStatus::Loading;
- mNonRuleFacesDirty = true;
- RebuildUserFontSet();
- mHasLoadingFontFacesIsDirty = true;
- CheckLoadingStarted();
- return this;
- }
- void
- FontFaceSet::Clear()
- {
- FlushUserFontSet();
- if (mNonRuleFaces.IsEmpty()) {
- return;
- }
- for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
- FontFace* f = mNonRuleFaces[i].mFontFace;
- f->RemoveFontFaceSet(this);
- }
- mNonRuleFaces.Clear();
- mNonRuleFacesDirty = true;
- RebuildUserFontSet();
- mHasLoadingFontFacesIsDirty = true;
- CheckLoadingFinished();
- }
- bool
- FontFaceSet::Delete(FontFace& aFontFace)
- {
- FlushUserFontSet();
- if (aFontFace.HasRule()) {
- return false;
- }
- bool removed = false;
- for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
- if (mNonRuleFaces[i].mFontFace == &aFontFace) {
- mNonRuleFaces.RemoveElementAt(i);
- removed = true;
- break;
- }
- }
- if (!removed) {
- return false;
- }
- aFontFace.RemoveFontFaceSet(this);
- mNonRuleFacesDirty = true;
- RebuildUserFontSet();
- mHasLoadingFontFacesIsDirty = true;
- CheckLoadingFinished();
- return true;
- }
- bool
- FontFaceSet::HasAvailableFontFace(FontFace* aFontFace)
- {
- return aFontFace->IsInFontFaceSet(this);
- }
- bool
- FontFaceSet::Has(FontFace& aFontFace)
- {
- FlushUserFontSet();
- return HasAvailableFontFace(&aFontFace);
- }
- FontFace*
- FontFaceSet::GetFontFaceAt(uint32_t aIndex)
- {
- FlushUserFontSet();
- if (aIndex < mRuleFaces.Length()) {
- return mRuleFaces[aIndex].mFontFace;
- }
- aIndex -= mRuleFaces.Length();
- if (aIndex < mNonRuleFaces.Length()) {
- return mNonRuleFaces[aIndex].mFontFace;
- }
- return nullptr;
- }
- uint32_t
- FontFaceSet::Size()
- {
- FlushUserFontSet();
- // Web IDL objects can only expose array index properties up to INT32_MAX.
- size_t total = mRuleFaces.Length() + mNonRuleFaces.Length();
- return std::min<size_t>(total, INT32_MAX);
- }
- already_AddRefed<FontFaceSetIterator>
- FontFaceSet::Entries()
- {
- RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, true);
- return it.forget();
- }
- already_AddRefed<FontFaceSetIterator>
- FontFaceSet::Values()
- {
- RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, false);
- return it.forget();
- }
- void
- FontFaceSet::ForEach(JSContext* aCx,
- FontFaceSetForEachCallback& aCallback,
- JS::Handle<JS::Value> aThisArg,
- ErrorResult& aRv)
- {
- JS::Rooted<JS::Value> thisArg(aCx, aThisArg);
- for (size_t i = 0; i < Size(); i++) {
- FontFace* face = GetFontFaceAt(i);
- aCallback.Call(thisArg, *face, *face, *this, aRv);
- if (aRv.Failed()) {
- return;
- }
- }
- }
- void
- FontFaceSet::RemoveLoader(nsFontFaceLoader* aLoader)
- {
- mLoaders.RemoveEntry(aLoader);
- }
- nsresult
- FontFaceSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
- const gfxFontFaceSrc* aFontFaceSrc)
- {
- nsresult rv;
- nsCOMPtr<nsIStreamLoader> streamLoader;
- nsCOMPtr<nsILoadGroup> loadGroup(mDocument->GetDocumentLoadGroup());
- // We're determining the security flags for font loading here based on
- // scheme, because we want to allow fonts to be loaded using file:
- // even if unique origins for file: access is enforced (allow CORS
- // bypass in this case).
- uint32_t securityFlags = 0;
- bool isFile = false;
- if (NS_SUCCEEDED(aFontFaceSrc->mURI->SchemeIs("file", &isFile)) &&
- isFile) {
- securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
- } else {
- securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
- }
- 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.
- rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
- aFontFaceSrc->mURI,
- mDocument,
- aUserFontEntry->GetPrincipal(),
- securityFlags,
- nsIContentPolicy::TYPE_FONT,
- loadGroup);
- NS_ENSURE_SUCCESS(rv, rv);
- RefPtr<nsFontFaceLoader> fontLoader =
- new nsFontFaceLoader(aUserFontEntry, aFontFaceSrc->mURI, this, channel);
- if (LOG_ENABLED()) {
- LOG(("userfonts (%p) download start - font uri: (%s) "
- "referrer uri: (%s)\n",
- fontLoader.get(), aFontFaceSrc->mURI->GetSpecOrDefault().get(),
- aFontFaceSrc->mReferrer
- ? aFontFaceSrc->mReferrer->GetSpecOrDefault().get()
- : ""));
- }
- nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
- if (httpChannel) {
- httpChannel->SetReferrerWithPolicy(aFontFaceSrc->mReferrer,
- mDocument->GetReferrerPolicy());
- nsAutoCString accept("application/font-woff;q=0.9,*/*;q=0.8");
- if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED)) {
- accept.Insert(NS_LITERAL_CSTRING("application/font-woff2;q=1.0,"), 0);
- }
- httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
- accept, false);
- // For WOFF and WOFF2, we should tell servers/proxies/etc NOT to try
- // and apply additional compression at the content-encoding layer
- if (aFontFaceSrc->mFormatFlags & (gfxUserFontSet::FLAG_FORMAT_WOFF |
- gfxUserFontSet::FLAG_FORMAT_WOFF2)) {
- httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept-Encoding"),
- NS_LITERAL_CSTRING("identity"), false);
- }
- }
- nsCOMPtr<nsISupportsPriority> priorityChannel(do_QueryInterface(channel));
- if (priorityChannel) {
- priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGH);
- }
- rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader);
- NS_ENSURE_SUCCESS(rv, rv);
- mozilla::net::PredictorLearn(aFontFaceSrc->mURI, mDocument->GetDocumentURI(),
- nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
- loadGroup);
- rv = channel->AsyncOpen2(streamLoader);
- if (NS_FAILED(rv)) {
- fontLoader->DropChannel(); // explicitly need to break ref cycle
- }
- if (NS_SUCCEEDED(rv)) {
- mLoaders.PutEntry(fontLoader);
- fontLoader->StartedLoading(streamLoader);
- aUserFontEntry->SetLoader(fontLoader); // let the font entry remember the
- // loader, in case we need to cancel it
- }
- return rv;
- }
- bool
- FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
- {
- MOZ_ASSERT(mUserFontSet);
- // 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
- nsDataHashtable<nsPtrHashKey<nsCSSFontFaceRule>, FontFace*> ruleFaceMap;
- for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) {
- FontFace* f = mRuleFaces[i].mFontFace;
- if (!f) {
- continue;
- }
- ruleFaceMap.Put(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;
- mRuleFaces.SwapElements(oldRecords);
- // 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 (auto it = mUserFontSet->mFontFamilies.Iter(); !it.Done(); it.Next()) {
- it.Data()->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.
- nsTHashtable<nsPtrHashKey<nsCSSFontFaceRule>> 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.
- if (handledRules.Contains(aRules[i].mRule)) {
- continue;
- }
- nsCSSFontFaceRule* rule = aRules[i].mRule;
- RefPtr<FontFace> f = ruleFaceMap.Get(rule);
- if (!f.get()) {
- f = FontFace::CreateForRule(GetParentObject(), this, rule);
- }
- InsertRuleFontFace(f, aRules[i].mSheetType, oldRecords, modified);
- handledRules.PutEntry(aRules[i].mRule);
- }
- 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 = mUserFontSet->mFontFamilies.Iter(); !it.Done(); it.Next()) {
- if (it.Data()->GetFontList().IsEmpty()) {
- 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<FontFace> 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 (mUserFontSet->mRebuildLocalRules) {
- mUserFontSet->mLocalRulesUsed = false;
- mUserFontSet->mRebuildLocalRules = false;
- }
- if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
- LOG(("userfonts (%p) userfont rules update (%s) rule count: %d",
- mUserFontSet.get(),
- (modified ? "modified" : "not modified"),
- (int)(mRuleFaces.Length())));
- }
- return modified;
- }
- static bool
- HasLocalSrc(const nsCSSValue::Array *aSrcArr)
- {
- size_t numSrc = aSrcArr->Count();
- for (size_t i = 0; i < numSrc; i++) {
- if (aSrcArr->Item(i).GetUnit() == eCSSUnit_Local_Font) {
- return true;
- }
- }
- return false;
- }
- void
- FontFaceSet::IncrementGeneration(bool aIsRebuild)
- {
- MOZ_ASSERT(mUserFontSet);
- mUserFontSet->IncrementGeneration(aIsRebuild);
- }
- void
- FontFaceSet::InsertNonRuleFontFace(FontFace* aFontFace,
- bool& aFontSetModified)
- {
- nsAutoString fontfamily;
- if (!aFontFace->GetFamilyName(fontfamily)) {
- // If there is no family name, this rule cannot contribute a
- // usable font, so there is no point in processing it further.
- return;
- }
- // Just create a new font entry if we haven't got one already.
- if (!aFontFace->GetUserFontEntry()) {
- // XXX Should we be checking mUserFontSet->mLocalRulesUsed like
- // InsertRuleFontFace does?
- RefPtr<gfxUserFontEntry> entry =
- FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace,
- SheetType::Doc);
- if (!entry) {
- return;
- }
- aFontFace->SetUserFontEntry(entry);
- }
- aFontSetModified = true;
- mUserFontSet->AddUserFontEntry(fontfamily, aFontFace->GetUserFontEntry());
- }
- void
- FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType,
- nsTArray<FontFaceRecord>& aOldRecords,
- bool& aFontSetModified)
- {
- nsAutoString fontfamily;
- if (!aFontFace->GetFamilyName(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;
- // 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.mSheetType == aSheetType) {
- // if local rules were used, don't use the old font entry
- // for rules containing src local usage
- if (mUserFontSet->mLocalRulesUsed &&
- mUserFontSet->mRebuildLocalRules) {
- nsCSSValue val;
- aFontFace->GetDesc(eCSSFontDesc_Src, val);
- nsCSSUnit unit = val.GetUnit();
- if (unit == eCSSUnit_Array && HasLocalSrc(val.GetArrayValue())) {
- // 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");
- mUserFontSet->AddUserFontEntry(fontfamily, entry);
- MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace),
- "FontFace should not occur in mRuleFaces twice");
- mRuleFaces.AppendElement(rec);
- aOldRecords.RemoveElementAt(i);
- // 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(fontfamily, 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.mSheetType = aSheetType;
- rec.mLoadEventShouldFire =
- aFontFace->Status() == FontFaceLoadStatus::Unloaded ||
- aFontFace->Status() == FontFaceLoadStatus::Loading;
- aFontFace->SetUserFontEntry(entry);
- MOZ_ASSERT(!HasRuleFontFace(aFontFace),
- "FontFace should not occur in mRuleFaces twice");
- mRuleFaces.AppendElement(rec);
- // 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.
- mUserFontSet->AddUserFontEntry(fontfamily, entry);
- }
- /* static */ already_AddRefed<gfxUserFontEntry>
- FontFaceSet::FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace)
- {
- nsAutoString fontfamily;
- if (!aFontFace->GetFamilyName(fontfamily)) {
- // If there is no family name, this rule cannot contribute a
- // usable font, so there is no point in processing it further.
- return nullptr;
- }
- return FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace,
- SheetType::Doc);
- }
- /* static */ already_AddRefed<gfxUserFontEntry>
- FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName,
- FontFace* aFontFace,
- SheetType aSheetType)
- {
- FontFaceSet* set = aFontFace->GetPrimaryFontFaceSet();
- nsCSSValue val;
- nsCSSUnit unit;
- uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL;
- int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
- uint8_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
- uint32_t languageOverride = NO_FONT_LANGUAGE_OVERRIDE;
- uint8_t fontDisplay = NS_FONT_DISPLAY_AUTO;
- // set up weight
- aFontFace->GetDesc(eCSSFontDesc_Weight, val);
- unit = val.GetUnit();
- if (unit == eCSSUnit_Integer || unit == eCSSUnit_Enumerated) {
- weight = val.GetIntValue();
- if (weight == 0) {
- weight = NS_STYLE_FONT_WEIGHT_NORMAL;
- }
- } else if (unit == eCSSUnit_Normal) {
- weight = NS_STYLE_FONT_WEIGHT_NORMAL;
- } else {
- NS_ASSERTION(unit == eCSSUnit_Null,
- "@font-face weight has unexpected unit");
- }
- // set up stretch
- aFontFace->GetDesc(eCSSFontDesc_Stretch, val);
- unit = val.GetUnit();
- if (unit == eCSSUnit_Enumerated) {
- stretch = val.GetIntValue();
- } else if (unit == eCSSUnit_Normal) {
- stretch = NS_STYLE_FONT_STRETCH_NORMAL;
- } else {
- NS_ASSERTION(unit == eCSSUnit_Null,
- "@font-face stretch has unexpected unit");
- }
- // set up font style
- aFontFace->GetDesc(eCSSFontDesc_Style, val);
- unit = val.GetUnit();
- if (unit == eCSSUnit_Enumerated) {
- italicStyle = val.GetIntValue();
- } else if (unit == eCSSUnit_Normal) {
- italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
- } else {
- NS_ASSERTION(unit == eCSSUnit_Null,
- "@font-face style has unexpected unit");
- }
- // set up font display
- aFontFace->GetDesc(eCSSFontDesc_Display, val);
- unit = val.GetUnit();
- if (unit == eCSSUnit_Enumerated) {
- fontDisplay = val.GetIntValue();
- } else {
- NS_ASSERTION(unit == eCSSUnit_Null,
- "@font-face style has unexpected unit");
- }
- // set up font features
- nsTArray<gfxFontFeature> featureSettings;
- aFontFace->GetDesc(eCSSFontDesc_FontFeatureSettings, val);
- unit = val.GetUnit();
- if (unit == eCSSUnit_Normal) {
- // empty list of features
- } else if (unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep) {
- nsRuleNode::ComputeFontFeatures(val.GetPairListValue(), featureSettings);
- } else {
- NS_ASSERTION(unit == eCSSUnit_Null,
- "@font-face font-feature-settings has unexpected unit");
- }
- // set up font language override
- aFontFace->GetDesc(eCSSFontDesc_FontLanguageOverride, val);
- unit = val.GetUnit();
- if (unit == eCSSUnit_Normal) {
- // empty feature string
- } else if (unit == eCSSUnit_String) {
- nsString stringValue;
- val.GetStringValue(stringValue);
- languageOverride = gfxFontStyle::ParseFontLanguageOverride(stringValue);
- } else {
- NS_ASSERTION(unit == eCSSUnit_Null,
- "@font-face font-language-override has unexpected unit");
- }
- // set up unicode-range
- nsAutoPtr<gfxCharacterMap> unicodeRanges;
- aFontFace->GetDesc(eCSSFontDesc_UnicodeRange, val);
- unit = val.GetUnit();
- if (unit == eCSSUnit_Array) {
- unicodeRanges = new gfxCharacterMap();
- const nsCSSValue::Array& sources = *val.GetArrayValue();
- MOZ_ASSERT(sources.Count() % 2 == 0,
- "odd number of entries in a unicode-range: array");
- for (uint32_t i = 0; i < sources.Count(); i += 2) {
- uint32_t min = sources[i].GetIntValue();
- uint32_t max = sources[i+1].GetIntValue();
- unicodeRanges->SetRange(min, max);
- }
- }
- // set up src array
- nsTArray<gfxFontFaceSrc> srcArray;
- if (aFontFace->HasFontData()) {
- gfxFontFaceSrc* face = srcArray.AppendElement();
- if (!face)
- return nullptr;
- face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
- face->mBuffer = aFontFace->CreateBufferSource();
- } else {
- aFontFace->GetDesc(eCSSFontDesc_Src, val);
- unit = val.GetUnit();
- if (unit == eCSSUnit_Array) {
- nsCSSValue::Array* srcArr = val.GetArrayValue();
- size_t numSrc = srcArr->Count();
- for (size_t i = 0; i < numSrc; i++) {
- val = srcArr->Item(i);
- unit = val.GetUnit();
- gfxFontFaceSrc* face = srcArray.AppendElements(1);
- if (!face)
- return nullptr;
- switch (unit) {
- case eCSSUnit_Local_Font:
- val.GetStringValue(face->mLocalName);
- face->mSourceType = gfxFontFaceSrc::eSourceType_Local;
- face->mURI = nullptr;
- face->mFormatFlags = 0;
- break;
- case eCSSUnit_URL:
- face->mSourceType = gfxFontFaceSrc::eSourceType_URL;
- face->mURI = val.GetURLValue();
- face->mReferrer = val.GetURLStructValue()->mReferrer;
- face->mReferrerPolicy = set->mDocument->GetReferrerPolicy();
- face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal;
- NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
- // agent and user stylesheets are treated slightly differently,
- // the same-site origin check and access control headers are
- // enforced against the sheet principal rather than the document
- // principal to allow user stylesheets to include @font-face rules
- face->mUseOriginPrincipal = (aSheetType == SheetType::User ||
- aSheetType == SheetType::Agent);
- face->mLocalName.Truncate();
- face->mFormatFlags = 0;
- while (i + 1 < numSrc && (val = srcArr->Item(i+1),
- val.GetUnit() == eCSSUnit_Font_Format)) {
- nsDependentString valueString(val.GetStringBufferValue());
- if (valueString.LowerCaseEqualsASCII("woff")) {
- face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF;
- } else if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED) &&
- valueString.LowerCaseEqualsASCII("woff2")) {
- face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF2;
- } else if (valueString.LowerCaseEqualsASCII("opentype")) {
- face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_OPENTYPE;
- } else if (valueString.LowerCaseEqualsASCII("truetype")) {
- face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE;
- } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
- face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT;
- } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
- face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_EOT;
- } else if (valueString.LowerCaseEqualsASCII("svg")) {
- face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_SVG;
- } else {
- // unknown format specified, mark to distinguish from the
- // case where no format hints are specified
- face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_UNKNOWN;
- }
- i++;
- }
- if (!face->mURI) {
- // if URI not valid, omit from src array
- srcArray.RemoveElementAt(srcArray.Length() - 1);
- NS_WARNING("null url in @font-face rule");
- continue;
- }
- break;
- default:
- NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
- "strange unit type in font-face src array");
- break;
- }
- }
- } else {
- NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
- }
- }
- if (srcArray.IsEmpty()) {
- return nullptr;
- }
- RefPtr<gfxUserFontEntry> entry =
- set->mUserFontSet->FindOrCreateUserFontEntry(aFamilyName, srcArray, weight,
- stretch, italicStyle,
- featureSettings,
- languageOverride,
- unicodeRanges, fontDisplay);
- return entry.forget();
- }
- nsCSSFontFaceRule*
- FontFaceSet::FindRuleForEntry(gfxFontEntry* aFontEntry)
- {
- NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
- for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
- FontFace* f = mRuleFaces[i].mFontFace;
- gfxUserFontEntry* entry = f->GetUserFontEntry();
- if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
- return f->GetRule();
- }
- }
- return nullptr;
- }
- nsCSSFontFaceRule*
- FontFaceSet::FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry)
- {
- for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
- FontFace* f = mRuleFaces[i].mFontFace;
- if (f->GetUserFontEntry() == aUserFontEntry) {
- return f->GetRule();
- }
- }
- return nullptr;
- }
- nsresult
- FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
- const char* aMessage,
- uint32_t aFlags,
- nsresult aStatus)
- {
- nsCOMPtr<nsIConsoleService>
- console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
- if (!console) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsAutoCString familyName;
- nsAutoCString fontURI;
- aUserFontEntry->GetFamilyNameAndURIForLogging(familyName, fontURI);
- char weightKeywordBuf[8]; // plenty to sprintf() a uint16_t
- const char* weightKeyword;
- const nsAFlatCString& weightKeywordString =
- nsCSSProps::ValueToKeyword(aUserFontEntry->Weight(),
- nsCSSProps::kFontWeightKTable);
- if (weightKeywordString.Length() > 0) {
- weightKeyword = weightKeywordString.get();
- } else {
- SprintfLiteral(weightKeywordBuf, "%u", aUserFontEntry->Weight());
- weightKeyword = weightKeywordBuf;
- }
- nsPrintfCString message
- ("downloadable font: %s "
- "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
- aMessage,
- familyName.get(),
- aUserFontEntry->IsItalic() ? "italic" : "normal",
- weightKeyword,
- nsCSSProps::ValueToKeyword(aUserFontEntry->Stretch(),
- nsCSSProps::kFontStretchKTable).get(),
- aUserFontEntry->GetSrcIndex());
- if (NS_FAILED(aStatus)) {
- message.AppendLiteral(": ");
- switch (aStatus) {
- case NS_ERROR_DOM_BAD_URI:
- message.AppendLiteral("bad URI or cross-site access not allowed");
- break;
- case NS_ERROR_CONTENT_BLOCKED:
- message.AppendLiteral("content blocked");
- break;
- default:
- message.AppendLiteral("status=");
- message.AppendInt(static_cast<uint32_t>(aStatus));
- break;
- }
- }
- message.AppendLiteral(" source: ");
- message.Append(fontURI);
- if (LOG_ENABLED()) {
- LOG(("userfonts (%p) %s", mUserFontSet.get(), message.get()));
- }
- // try to give the user an indication of where the rule came from
- nsCSSFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
- nsString href;
- nsString text;
- nsresult rv;
- uint32_t line = 0;
- uint32_t column = 0;
- if (rule) {
- rv = rule->GetCssText(text);
- NS_ENSURE_SUCCESS(rv, rv);
- CSSStyleSheet* sheet = rule->GetStyleSheet();
- // if the style sheet is removed while the font is loading can be null
- if (sheet) {
- nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault();
- CopyUTF8toUTF16(spec, href);
- } else {
- NS_WARNING("null parent stylesheet for @font-face rule");
- href.AssignLiteral("unknown");
- }
- line = rule->GetLineNumber();
- column = rule->GetColumnNumber();
- }
- nsCOMPtr<nsIScriptError> scriptError =
- do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- uint64_t innerWindowID = mDocument->InnerWindowID();
- rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
- href, // file
- text, // src line
- line,
- column,
- aFlags, // flags
- "CSS Loader", // category (make separate?)
- innerWindowID);
- if (NS_SUCCEEDED(rv)) {
- console->LogMessage(scriptError);
- }
- return NS_OK;
- }
- nsresult
- FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
- nsIPrincipal** aPrincipal,
- bool* aBypassCache)
- {
- NS_ASSERTION(aFontFaceSrc &&
- aFontFaceSrc->mSourceType == gfxFontFaceSrc::eSourceType_URL,
- "bad font face url passed to fontloader");
- // check same-site origin
- NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
- if (!aFontFaceSrc->mURI)
- return NS_ERROR_FAILURE;
- // use document principal, original principal if flag set
- // this enables user stylesheets to load font files via
- // @font-face rules
- *aPrincipal = mDocument->NodePrincipal();
- NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
- "null origin principal in @font-face rule");
- if (aFontFaceSrc->mUseOriginPrincipal) {
- *aPrincipal = aFontFaceSrc->mOriginPrincipal;
- }
- *aBypassCache = false;
- nsCOMPtr<nsIDocShell> docShell = mDocument->GetDocShell();
- if (docShell) {
- uint32_t loadType;
- if (NS_SUCCEEDED(docShell->GetLoadType(&loadType))) {
- if ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE) {
- *aBypassCache = true;
- }
- }
- uint32_t flags;
- if (NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags))) {
- if (flags & nsIRequest::LOAD_BYPASS_CACHE) {
- *aBypassCache = true;
- }
- }
- }
- return NS_OK;
- }
- // @arg aPrincipal: generally this is mDocument->NodePrincipal() but
- // might also be the original principal which enables user stylesheets
- // to load font files via @font-face rules.
- bool
- FontFaceSet::IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal)
- {
- int16_t shouldLoad = nsIContentPolicy::ACCEPT;
- nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_FONT,
- aFontLocation,
- aPrincipal,
- mDocument,
- EmptyCString(), // mime type
- nullptr, // aExtra
- &shouldLoad,
- nsContentUtils::GetContentPolicy(),
- nsContentUtils::GetSecurityManager());
- return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
- }
- nsresult
- FontFaceSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
- const gfxFontFaceSrc* aFontFaceSrc,
- uint8_t*& aBuffer,
- uint32_t& aBufferLength)
- {
- nsresult rv;
- 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_DATA_INHERITS or not, to be more
- // restrictive we use SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS.
- rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
- aFontFaceSrc->mURI,
- mDocument,
- aFontToLoad->GetPrincipal(),
- nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
- nsIContentPolicy::TYPE_FONT);
- NS_ENSURE_SUCCESS(rv, rv);
- // blocking stream is OK for data URIs
- nsCOMPtr<nsIInputStream> stream;
- rv = channel->Open2(getter_AddRefs(stream));
- NS_ENSURE_SUCCESS(rv, rv);
- uint64_t bufferLength64;
- rv = stream->Available(&bufferLength64);
- NS_ENSURE_SUCCESS(rv, rv);
- if (bufferLength64 == 0) {
- return NS_ERROR_FAILURE;
- }
- if (bufferLength64 > UINT32_MAX) {
- return NS_ERROR_FILE_TOO_BIG;
- }
- aBufferLength = static_cast<uint32_t>(bufferLength64);
- // read all the decoded data
- aBuffer = static_cast<uint8_t*> (moz_xmalloc(sizeof(uint8_t) * aBufferLength));
- if (!aBuffer) {
- aBufferLength = 0;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- uint32_t numRead, totalRead = 0;
- while (NS_SUCCEEDED(rv =
- stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
- aBufferLength - totalRead, &numRead)) &&
- numRead != 0)
- {
- totalRead += numRead;
- if (totalRead > aBufferLength) {
- rv = NS_ERROR_FAILURE;
- break;
- }
- }
- // make sure there's a mime type
- if (NS_SUCCEEDED(rv)) {
- nsAutoCString mimeType;
- rv = channel->GetContentType(mimeType);
- aBufferLength = totalRead;
- }
- if (NS_FAILED(rv)) {
- free(aBuffer);
- aBuffer = nullptr;
- aBufferLength = 0;
- return rv;
- }
- return NS_OK;
- }
- bool
- FontFaceSet::GetPrivateBrowsing()
- {
- nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext();
- return loadContext && loadContext->UsePrivateBrowsing();
- }
- void
- FontFaceSet::OnFontFaceStatusChanged(FontFace* aFontFace)
- {
- MOZ_ASSERT(HasAvailableFontFace(aFontFace));
- mHasLoadingFontFacesIsDirty = true;
- if (aFontFace->Status() == FontFaceLoadStatus::Loading) {
- CheckLoadingStarted();
- } else {
- MOZ_ASSERT(aFontFace->Status() == FontFaceLoadStatus::Loaded ||
- aFontFace->Status() == FontFaceLoadStatus::Error);
- // When a font finishes downloading, nsPresContext::UserFontSetUpdated
- // will be called immediately afterwards to request a reflow of the
- // relevant elements in the document. We want to wait until the reflow
- // request has been done before the FontFaceSet is marked as Loaded so
- // that we don't briefly set the FontFaceSet to Loaded and then Loading
- // again once the reflow is pending. So we go around the event loop
- // and call CheckLoadingFinished() after the reflow has been queued.
- if (!mDelayedLoadCheck) {
- mDelayedLoadCheck = true;
- nsCOMPtr<nsIRunnable> checkTask =
- NewRunnableMethod(this, &FontFaceSet::CheckLoadingFinishedAfterDelay);
- NS_DispatchToMainThread(checkTask);
- }
- }
- }
- void
- FontFaceSet::DidRefresh()
- {
- CheckLoadingFinished();
- }
- void
- FontFaceSet::CheckLoadingFinishedAfterDelay()
- {
- mDelayedLoadCheck = false;
- CheckLoadingFinished();
- }
- void
- FontFaceSet::CheckLoadingStarted()
- {
- if (!HasLoadingFontFaces()) {
- return;
- }
- if (mStatus == FontFaceSetLoadStatus::Loading) {
- // We have already dispatched a loading event and replaced mReady
- // with a fresh, unresolved promise.
- return;
- }
- mStatus = FontFaceSetLoadStatus::Loading;
- (new AsyncEventDispatcher(this, NS_LITERAL_STRING("loading"),
- false))->PostDOMEvent();
- if (PrefEnabled()) {
- if (mReady) {
- if (GetParentObject()) {
- ErrorResult rv;
- mReady = Promise::Create(GetParentObject(), rv);
- }
- }
- if (!mReady) {
- mResolveLazilyCreatedReadyPromise = false;
- }
- }
- }
- void
- FontFaceSet::UpdateHasLoadingFontFaces()
- {
- mHasLoadingFontFacesIsDirty = false;
- mHasLoadingFontFaces = false;
- for (size_t i = 0; i < mRuleFaces.Length(); i++) {
- FontFace* 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;
- return;
- }
- }
- }
- bool
- FontFaceSet::HasLoadingFontFaces()
- {
- if (mHasLoadingFontFacesIsDirty) {
- UpdateHasLoadingFontFaces();
- }
- return mHasLoadingFontFaces;
- }
- bool
- FontFaceSet::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;
- }
- void
- FontFaceSet::CheckLoadingFinished()
- {
- if (mDelayedLoadCheck) {
- // Wait until the runnable posted in OnFontFaceStatusChanged calls us.
- return;
- }
- if (mStatus == FontFaceSetLoadStatus::Loaded) {
- // We've already resolved mReady and dispatched the loadingdone/loadingerror
- // events.
- return;
- }
- if (MightHavePendingFontLoads()) {
- // We're not finished loading yet.
- return;
- }
- mStatus = FontFaceSetLoadStatus::Loaded;
- if (mReady) {
- mReady->MaybeResolve(this);
- } else {
- mResolveLazilyCreatedReadyPromise = true;
- }
- // Now dispatch the loadingdone/loadingerror events.
- nsTArray<FontFace*> loaded;
- nsTArray<FontFace*> failed;
- for (size_t i = 0; i < mRuleFaces.Length(); i++) {
- if (!mRuleFaces[i].mLoadEventShouldFire) {
- continue;
- }
- FontFace* f = mRuleFaces[i].mFontFace;
- if (f->Status() == FontFaceLoadStatus::Loaded) {
- loaded.AppendElement(f);
- mRuleFaces[i].mLoadEventShouldFire = false;
- } else if (f->Status() == FontFaceLoadStatus::Error) {
- failed.AppendElement(f);
- mRuleFaces[i].mLoadEventShouldFire = false;
- }
- }
- for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
- if (!mNonRuleFaces[i].mLoadEventShouldFire) {
- continue;
- }
- FontFace* f = mNonRuleFaces[i].mFontFace;
- if (f->Status() == FontFaceLoadStatus::Loaded) {
- loaded.AppendElement(f);
- mNonRuleFaces[i].mLoadEventShouldFire = false;
- } else if (f->Status() == FontFaceLoadStatus::Error) {
- failed.AppendElement(f);
- mNonRuleFaces[i].mLoadEventShouldFire = false;
- }
- }
- DispatchLoadingFinishedEvent(NS_LITERAL_STRING("loadingdone"), loaded);
- if (!failed.IsEmpty()) {
- DispatchLoadingFinishedEvent(NS_LITERAL_STRING("loadingerror"), failed);
- }
- }
- void
- FontFaceSet::DispatchLoadingFinishedEvent(
- const nsAString& aType,
- const nsTArray<FontFace*>& aFontFaces)
- {
- FontFaceSetLoadEventInit init;
- init.mBubbles = false;
- init.mCancelable = false;
- OwningNonNull<FontFace>* elements =
- init.mFontfaces.AppendElements(aFontFaces.Length(), fallible);
- MOZ_ASSERT(elements);
- for (size_t i = 0; i < aFontFaces.Length(); i++) {
- elements[i] = aFontFaces[i];
- }
- RefPtr<FontFaceSetLoadEvent> event =
- FontFaceSetLoadEvent::Constructor(this, aType, init);
- (new AsyncEventDispatcher(this, event))->PostDOMEvent();
- }
- // nsIDOMEventListener
- NS_IMETHODIMP
- FontFaceSet::HandleEvent(nsIDOMEvent* aEvent)
- {
- nsString type;
- aEvent->GetType(type);
- if (!type.EqualsLiteral("DOMContentLoaded")) {
- return NS_ERROR_FAILURE;
- }
- RemoveDOMContentLoadedListener();
- CheckLoadingFinished();
- return NS_OK;
- }
- /* static */ bool
- FontFaceSet::PrefEnabled()
- {
- static bool initialized = false;
- static bool enabled;
- if (!initialized) {
- initialized = true;
- Preferences::AddBoolVarCache(&enabled, FONT_LOADING_API_ENABLED_PREF);
- }
- return enabled;
- }
- // nsICSSLoaderObserver
- NS_IMETHODIMP
- FontFaceSet::StyleSheetLoaded(StyleSheet* aSheet,
- bool aWasAlternate,
- nsresult aStatus)
- {
- CheckLoadingFinished();
- return NS_OK;
- }
- void
- FontFaceSet::FlushUserFontSet()
- {
- if (mDocument) {
- mDocument->FlushUserFontSet();
- }
- }
- void
- FontFaceSet::RebuildUserFontSet()
- {
- if (mDocument) {
- mDocument->RebuildUserFontSet();
- }
- }
- nsPresContext*
- FontFaceSet::GetPresContext()
- {
- if (!mDocument) {
- return nullptr;
- }
- nsIPresShell* shell = mDocument->GetShell();
- return shell ? shell->GetPresContext() : nullptr;
- }
- // -- FontFaceSet::UserFontSet ------------------------------------------------
- /* virtual */ nsresult
- FontFaceSet::UserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
- nsIPrincipal** aPrincipal,
- bool* aBypassCache)
- {
- if (!mFontFaceSet) {
- return NS_ERROR_FAILURE;
- }
- return mFontFaceSet->CheckFontLoad(aFontFaceSrc, aPrincipal, aBypassCache);
- }
- /* virtual */ bool
- FontFaceSet::UserFontSet::IsFontLoadAllowed(nsIURI* aFontLocation,
- nsIPrincipal* aPrincipal)
- {
- return mFontFaceSet &&
- mFontFaceSet->IsFontLoadAllowed(aFontLocation, aPrincipal);
- }
- /* virtual */ nsresult
- FontFaceSet::UserFontSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
- const gfxFontFaceSrc* aFontFaceSrc)
- {
- if (!mFontFaceSet) {
- return NS_ERROR_FAILURE;
- }
- return mFontFaceSet->StartLoad(aUserFontEntry, aFontFaceSrc);
- }
- void
- FontFaceSet::UserFontSet::RecordFontLoadDone(uint32_t aFontSize,
- TimeStamp aDoneTime)
- {
- mDownloadCount++;
- mDownloadSize += aFontSize;
- if (!mFontFaceSet) {
- return;
- }
- }
- /* virtual */ nsresult
- FontFaceSet::UserFontSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
- const char* aMessage,
- uint32_t aFlags,
- nsresult aStatus)
- {
- if (!mFontFaceSet) {
- return NS_ERROR_FAILURE;
- }
- return mFontFaceSet->LogMessage(aUserFontEntry, aMessage, aFlags, aStatus);
- }
- /* virtual */ nsresult
- FontFaceSet::UserFontSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
- const gfxFontFaceSrc* aFontFaceSrc,
- uint8_t*& aBuffer,
- uint32_t& aBufferLength)
- {
- if (!mFontFaceSet) {
- return NS_ERROR_FAILURE;
- }
- return mFontFaceSet->SyncLoadFontData(aFontToLoad, aFontFaceSrc,
- aBuffer, aBufferLength);
- }
- /* virtual */ bool
- FontFaceSet::UserFontSet::GetPrivateBrowsing()
- {
- return mFontFaceSet && mFontFaceSet->GetPrivateBrowsing();
- }
- /* virtual */ void
- FontFaceSet::UserFontSet::DoRebuildUserFontSet()
- {
- if (!mFontFaceSet) {
- return;
- }
- mFontFaceSet->RebuildUserFontSet();
- }
- /* virtual */ already_AddRefed<gfxUserFontEntry>
- FontFaceSet::UserFontSet::CreateUserFontEntry(
- const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
- uint32_t aWeight,
- int32_t aStretch,
- uint8_t aStyle,
- const nsTArray<gfxFontFeature>& aFeatureSettings,
- uint32_t aLanguageOverride,
- gfxSparseBitSet* aUnicodeRanges,
- uint8_t aFontDisplay)
- {
- RefPtr<gfxUserFontEntry> entry =
- new FontFace::Entry(this, aFontFaceSrcList, aWeight, aStretch, aStyle,
- aFeatureSettings, aLanguageOverride, aUnicodeRanges,
- aFontDisplay);
- return entry.forget();
- }
- #undef LOG_ENABLED
- #undef LOG
|