/* -*- 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 "mozilla/dom/FontFaceImpl.h" #include #include "gfxFontUtils.h" #include "gfxPlatformFontList.h" #include "mozilla/dom/CSSFontFaceRule.h" #include "mozilla/dom/FontFaceBinding.h" #include "mozilla/dom/FontFaceSetImpl.h" #include "mozilla/dom/TypedArray.h" #include "mozilla/dom/UnionTypes.h" #include "mozilla/ServoBindings.h" #include "mozilla/ServoCSSParser.h" #include "mozilla/ServoUtils.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/dom/Document.h" #include "nsStyleUtil.h" namespace mozilla { namespace dom { // -- FontFaceBufferSource --------------------------------------------------- /** * An object that wraps a FontFace object and exposes its ArrayBuffer * or ArrayBufferView data in a form the user font set can consume. */ class FontFaceBufferSource : public gfxFontFaceBufferSource { public: FontFaceBufferSource(uint8_t* aBuffer, uint32_t aLength) : mBuffer(aBuffer), mLength(aLength) {} void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) override { MOZ_ASSERT(mBuffer, "only call TakeBuffer once on a given " "FontFaceBufferSource object"); aBuffer = mBuffer; aLength = mLength; mBuffer = nullptr; mLength = 0; } private: ~FontFaceBufferSource() override { if (mBuffer) { free(mBuffer); } } uint8_t* mBuffer; uint32_t mLength; }; // -- FontFaceImpl ----------------------------------------------------------- FontFaceImpl::FontFaceImpl(FontFace* aOwner, FontFaceSetImpl* aFontFaceSet) : mOwner(aOwner), mStatus(FontFaceLoadStatus::Unloaded), mSourceType(SourceType(0)), mFontFaceSet(aFontFaceSet), mUnicodeRangeDirty(true), mInFontFaceSet(false) {} FontFaceImpl::~FontFaceImpl() { // Assert that we don't drop any FontFace objects during a Servo traversal, // since PostTraversalTask objects can hold raw pointers to FontFaces. MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal()); SetUserFontEntry(nullptr); } #ifdef DEBUG void FontFaceImpl::AssertIsOnOwningThread() const { mFontFaceSet->AssertIsOnOwningThread(); } #endif void FontFaceImpl::Destroy() { mInFontFaceSet = false; SetUserFontEntry(nullptr); mOwner = nullptr; } static FontFaceLoadStatus LoadStateToStatus( gfxUserFontEntry::UserFontLoadState aLoadState) { switch (aLoadState) { case gfxUserFontEntry::UserFontLoadState::STATUS_NOT_LOADED: return FontFaceLoadStatus::Unloaded; case gfxUserFontEntry::UserFontLoadState::STATUS_LOAD_PENDING: case gfxUserFontEntry::UserFontLoadState::STATUS_LOADING: return FontFaceLoadStatus::Loading; case gfxUserFontEntry::UserFontLoadState::STATUS_LOADED: return FontFaceLoadStatus::Loaded; case gfxUserFontEntry::UserFontLoadState::STATUS_FAILED: return FontFaceLoadStatus::Error; } MOZ_ASSERT_UNREACHABLE("invalid aLoadState value"); return FontFaceLoadStatus::Error; } already_AddRefed FontFaceImpl::CreateForRule( FontFace* aOwner, FontFaceSetImpl* aFontFaceSet, StyleLockedFontFaceRule* aRule) { RefPtr obj = new FontFaceImpl(aOwner, aFontFaceSet); obj->mRule = aRule; obj->mSourceType = eSourceType_FontFaceRule; obj->mInFontFaceSet = true; return obj.forget(); } void FontFaceImpl::InitializeSourceURL(const nsACString& aURL) { MOZ_ASSERT(mOwner); mSourceType = eSourceType_URLs; IgnoredErrorResult rv; SetDescriptor(eCSSFontDesc_Src, aURL, rv); if (rv.Failed()) { mOwner->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); SetStatus(FontFaceLoadStatus::Error); } } void FontFaceImpl::InitializeSourceBuffer(uint8_t* aBuffer, uint32_t aLength) { MOZ_ASSERT(mOwner); MOZ_ASSERT(!mBufferSource); mSourceType = FontFaceImpl::eSourceType_Buffer; if (aBuffer) { mBufferSource = new FontFaceBufferSource(aBuffer, aLength); } SetStatus(FontFaceLoadStatus::Loading); DoLoad(); } void FontFaceImpl::GetFamily(nsACString& aResult) { GetDesc(eCSSFontDesc_Family, aResult); } void FontFaceImpl::SetFamily(const nsACString& aValue, ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); if (SetDescriptor(eCSSFontDesc_Family, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetStyle(nsACString& aResult) { GetDesc(eCSSFontDesc_Style, aResult); } void FontFaceImpl::SetStyle(const nsACString& aValue, ErrorResult& aRv) { if (SetDescriptor(eCSSFontDesc_Style, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetWeight(nsACString& aResult) { GetDesc(eCSSFontDesc_Weight, aResult); } void FontFaceImpl::SetWeight(const nsACString& aValue, ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); if (SetDescriptor(eCSSFontDesc_Weight, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetStretch(nsACString& aResult) { GetDesc(eCSSFontDesc_Stretch, aResult); } void FontFaceImpl::SetStretch(const nsACString& aValue, ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); if (SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetUnicodeRange(nsACString& aResult) { GetDesc(eCSSFontDesc_UnicodeRange, aResult); } void FontFaceImpl::SetUnicodeRange(const nsACString& aValue, ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); if (SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetVariant(nsACString& aResult) { // XXX Just expose the font-variant descriptor as "normal" until we // support it properly (bug 1055385). aResult.AssignLiteral("normal"); } void FontFaceImpl::SetVariant(const nsACString& aValue, ErrorResult& aRv) { // XXX Ignore assignments to variant until we support font-variant // descriptors (bug 1055385). } void FontFaceImpl::GetFeatureSettings(nsACString& aResult) { GetDesc(eCSSFontDesc_FontFeatureSettings, aResult); } void FontFaceImpl::SetFeatureSettings(const nsACString& aValue, ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); if (SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetVariationSettings(nsACString& aResult) { GetDesc(eCSSFontDesc_FontVariationSettings, aResult); } void FontFaceImpl::SetVariationSettings(const nsACString& aValue, ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); if (SetDescriptor(eCSSFontDesc_FontVariationSettings, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetDisplay(nsACString& aResult) { GetDesc(eCSSFontDesc_Display, aResult); } void FontFaceImpl::SetDisplay(const nsACString& aValue, ErrorResult& aRv) { if (SetDescriptor(eCSSFontDesc_Display, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetAscentOverride(nsACString& aResult) { GetDesc(eCSSFontDesc_AscentOverride, aResult); } void FontFaceImpl::SetAscentOverride(const nsACString& aValue, ErrorResult& aRv) { if (SetDescriptor(eCSSFontDesc_AscentOverride, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetDescentOverride(nsACString& aResult) { GetDesc(eCSSFontDesc_DescentOverride, aResult); } void FontFaceImpl::SetDescentOverride(const nsACString& aValue, ErrorResult& aRv) { if (SetDescriptor(eCSSFontDesc_DescentOverride, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetLineGapOverride(nsACString& aResult) { GetDesc(eCSSFontDesc_LineGapOverride, aResult); } void FontFaceImpl::SetLineGapOverride(const nsACString& aValue, ErrorResult& aRv) { if (SetDescriptor(eCSSFontDesc_LineGapOverride, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::GetSizeAdjust(nsACString& aResult) { GetDesc(eCSSFontDesc_SizeAdjust, aResult); } void FontFaceImpl::SetSizeAdjust(const nsACString& aValue, ErrorResult& aRv) { if (SetDescriptor(eCSSFontDesc_SizeAdjust, aValue, aRv)) { DescriptorUpdated(); } } void FontFaceImpl::DescriptorUpdated() { // If we haven't yet initialized mUserFontEntry, no need to do anything here; // we'll respect the updated descriptor when the time comes to create it. if (!mUserFontEntry) { return; } gfxUserFontAttributes attr; RefPtr newEntry; if (GetAttributes(attr)) { newEntry = mFontFaceSet->FindOrCreateUserFontEntryFromFontFace( this, std::move(attr), StyleOrigin::Author); } SetUserFontEntry(newEntry); // Behind the scenes, this will actually update the existing entry and return // it, rather than create a new one. if (mInFontFaceSet) { mFontFaceSet->MarkUserFontSetDirty(); } for (auto& set : mOtherFontFaceSets) { set->MarkUserFontSetDirty(); } } FontFaceLoadStatus FontFaceImpl::Status() { return mStatus; } void FontFaceImpl::Load(ErrorResult& aRv) { mFontFaceSet->FlushUserFontSet(); // Calling Load on a FontFace constructed with an ArrayBuffer data source, // or on one that is already loading (or has finished loading), has no // effect. if (mSourceType == eSourceType_Buffer || mStatus != FontFaceLoadStatus::Unloaded) { return; } // Calling the user font entry's Load method will end up setting our // status to Loading, but the spec requires us to set it to Loading // here. SetStatus(FontFaceLoadStatus::Loading); DoLoad(); } gfxUserFontEntry* FontFaceImpl::CreateUserFontEntry() { if (!mUserFontEntry) { MOZ_ASSERT(!HasRule(), "Rule backed FontFace objects should already have a user font " "entry by the time Load() can be called on them"); gfxUserFontAttributes attr; if (GetAttributes(attr)) { RefPtr newEntry = mFontFaceSet->FindOrCreateUserFontEntryFromFontFace( this, std::move(attr), StyleOrigin::Author); if (newEntry) { SetUserFontEntry(newEntry); } } } return mUserFontEntry; } void FontFaceImpl::DoLoad() { if (!CreateUserFontEntry()) { return; } mUserFontEntry->Load(); } void FontFaceImpl::SetStatus(FontFaceLoadStatus aStatus) { gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); if (mStatus == aStatus) { return; } if (aStatus < mStatus) { // We're being asked to go backwards in status! Normally, this shouldn't // happen. But it can if the FontFace had a user font entry that had // loaded, but then was given a new one by FontFaceSet::InsertRuleFontFace // if we used a local() rule. For now, just ignore the request to // go backwards in status. return; } mStatus = aStatus; if (mInFontFaceSet) { mFontFaceSet->OnFontFaceStatusChanged(this); } for (FontFaceSetImpl* otherSet : mOtherFontFaceSets) { otherSet->OnFontFaceStatusChanged(this); } UpdateOwnerPromise(); } void FontFaceImpl::UpdateOwnerPromise() { if (!mFontFaceSet->IsOnOwningThread()) { mFontFaceSet->DispatchToOwningThread( "FontFaceImpl::UpdateOwnerPromise", [self = RefPtr{this}] { self->UpdateOwnerPromise(); }); return; } if (NS_WARN_IF(!mOwner)) { return; } if (mStatus == FontFaceLoadStatus::Loaded) { mOwner->MaybeResolve(); } else if (mStatus == FontFaceLoadStatus::Error) { if (mSourceType == eSourceType_Buffer) { mOwner->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); } else { mOwner->MaybeReject(NS_ERROR_DOM_NETWORK_ERR); } } } // Boolean result indicates whether the value of the descriptor was actually // changed. bool FontFaceImpl::SetDescriptor(nsCSSFontDesc aFontDesc, const nsACString& aValue, ErrorResult& aRv) { // FIXME We probably don't need to distinguish between this anymore // since we have common backend now. NS_ASSERTION(!HasRule(), "we don't handle rule backed FontFace objects yet"); if (HasRule()) { return false; } RefPtr url = mFontFaceSet->GetURLExtraData(); if (NS_WARN_IF(!url)) { // This should only happen on worker threads, where we failed to initialize // the worker before it was shutdown. aRv.ThrowInvalidStateError("Missing URLExtraData"); return false; } // FIXME(heycam): Should not allow modification of FontFaces that are // CSS-connected and whose rule is read only. bool changed; if (!Servo_FontFaceRule_SetDescriptor(GetData(), aFontDesc, &aValue, url, &changed)) { aRv.ThrowSyntaxError("Invalid font descriptor"); return false; } if (!changed) { return false; } if (aFontDesc == eCSSFontDesc_UnicodeRange) { mUnicodeRangeDirty = true; } return true; } bool FontFaceImpl::SetDescriptors(const nsACString& aFamily, const FontFaceDescriptors& aDescriptors) { MOZ_ASSERT(!HasRule()); MOZ_ASSERT(!mDescriptors); mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume(); // Helper to call SetDescriptor and return true on success, false on failure. auto setDesc = [=](nsCSSFontDesc aDesc, const nsACString& aVal) -> bool { IgnoredErrorResult rv; SetDescriptor(aDesc, aVal, rv); return !rv.Failed(); }; // Parse all of the mDescriptors in aInitializer, which are the values // we got from the JS constructor. if (!setDesc(eCSSFontDesc_Family, aFamily) || !setDesc(eCSSFontDesc_Style, aDescriptors.mStyle) || !setDesc(eCSSFontDesc_Weight, aDescriptors.mWeight) || !setDesc(eCSSFontDesc_Stretch, aDescriptors.mStretch) || !setDesc(eCSSFontDesc_UnicodeRange, aDescriptors.mUnicodeRange) || !setDesc(eCSSFontDesc_FontFeatureSettings, aDescriptors.mFeatureSettings) || (StaticPrefs::layout_css_font_variations_enabled() && !setDesc(eCSSFontDesc_FontVariationSettings, aDescriptors.mVariationSettings)) || !setDesc(eCSSFontDesc_Display, aDescriptors.mDisplay) || ((!setDesc(eCSSFontDesc_AscentOverride, aDescriptors.mAscentOverride) || !setDesc(eCSSFontDesc_DescentOverride, aDescriptors.mDescentOverride) || !setDesc(eCSSFontDesc_LineGapOverride, aDescriptors.mLineGapOverride))) || (StaticPrefs::layout_css_size_adjust_enabled() && !setDesc(eCSSFontDesc_SizeAdjust, aDescriptors.mSizeAdjust))) { // XXX Handle font-variant once we support it (bug 1055385). // If any of the descriptors failed to parse, none of them should be set // on the FontFace. mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume(); if (mOwner) { mOwner->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); } SetStatus(FontFaceLoadStatus::Error); return false; } return true; } void FontFaceImpl::GetDesc(nsCSSFontDesc aDescID, nsACString& aResult) const { aResult.Truncate(); Servo_FontFaceRule_GetDescriptorCssText(GetData(), aDescID, &aResult); // Fill in a default value for missing descriptors. if (aResult.IsEmpty()) { if (aDescID == eCSSFontDesc_UnicodeRange) { aResult.AssignLiteral("U+0-10FFFF"); } else if (aDescID == eCSSFontDesc_Display) { aResult.AssignLiteral("auto"); } else if (aDescID != eCSSFontDesc_Family && aDescID != eCSSFontDesc_Src) { aResult.AssignLiteral("normal"); } } } void FontFaceImpl::SetUserFontEntry(gfxUserFontEntry* aEntry) { AssertIsOnOwningThread(); if (mUserFontEntry == aEntry) { return; } if (mUserFontEntry) { mUserFontEntry->RemoveFontFace(this); } auto* entry = static_cast(aEntry); if (entry) { entry->AddFontFace(this); } mUserFontEntry = entry; if (!mUserFontEntry) { return; } MOZ_ASSERT(mUserFontEntry->HasUserFontSet(mFontFaceSet), "user font entry must be associated with the same user font set " "as the FontFace"); // Our newly assigned user font entry might be in the process of or // finished loading, so set our status accordingly. But only do so // if we're not going "backwards" in status, which could otherwise // happen in this case: // // new FontFace("ABC", "url(x)").load(); // // where the SetUserFontEntry call (from the after-initialization // DoLoad call) comes after the author's call to load(), which set mStatus // to Loading. FontFaceLoadStatus newStatus = LoadStateToStatus(mUserFontEntry->LoadState()); if (newStatus > mStatus) { SetStatus(newStatus); } } bool FontFaceImpl::GetAttributes(gfxUserFontAttributes& aAttr) { StyleLockedFontFaceRule* data = GetData(); if (!data) { return false; } nsAtom* fontFamily = Servo_FontFaceRule_GetFamilyName(data); if (!fontFamily) { return false; } aAttr.mFamilyName = nsAtomCString(fontFamily); StyleComputedFontWeightRange weightRange; if (Servo_FontFaceRule_GetFontWeight(data, &weightRange)) { aAttr.mRangeFlags &= ~gfxFontEntry::RangeFlags::eAutoWeight; aAttr.mWeight = WeightRange(FontWeight::FromFloat(weightRange._0), FontWeight::FromFloat(weightRange._1)); } StyleComputedFontStretchRange stretchRange; if (Servo_FontFaceRule_GetFontStretch(data, &stretchRange)) { aAttr.mRangeFlags &= ~gfxFontEntry::RangeFlags::eAutoStretch; aAttr.mStretch = StretchRange(stretchRange._0, stretchRange._1); } auto styleDesc = StyleComputedFontStyleDescriptor::Normal(); if (Servo_FontFaceRule_GetFontStyle(data, &styleDesc)) { aAttr.mRangeFlags &= ~gfxFontEntry::RangeFlags::eAutoSlantStyle; switch (styleDesc.tag) { case StyleComputedFontStyleDescriptor::Tag::Normal: aAttr.mStyle = SlantStyleRange(FontSlantStyle::NORMAL); break; case StyleComputedFontStyleDescriptor::Tag::Italic: aAttr.mStyle = SlantStyleRange(FontSlantStyle::ITALIC); break; case StyleComputedFontStyleDescriptor::Tag::Oblique: aAttr.mStyle = SlantStyleRange( FontSlantStyle::FromFloat(styleDesc.AsOblique()._0), FontSlantStyle::FromFloat(styleDesc.AsOblique()._1)); break; default: MOZ_ASSERT_UNREACHABLE("Unhandled tag"); } } StylePercentage ascent{0}; if (Servo_FontFaceRule_GetAscentOverride(data, &ascent)) { aAttr.mAscentOverride = ascent._0; } StylePercentage descent{0}; if (Servo_FontFaceRule_GetDescentOverride(data, &descent)) { aAttr.mDescentOverride = descent._0; } StylePercentage lineGap{0}; if (Servo_FontFaceRule_GetLineGapOverride(data, &lineGap)) { aAttr.mLineGapOverride = lineGap._0; } StylePercentage sizeAdjust; if (Servo_FontFaceRule_GetSizeAdjust(data, &sizeAdjust)) { aAttr.mSizeAdjust = sizeAdjust._0; } StyleFontLanguageOverride langOverride; if (Servo_FontFaceRule_GetFontLanguageOverride(data, &langOverride)) { aAttr.mLanguageOverride = langOverride._0; } Servo_FontFaceRule_GetFontDisplay(data, &aAttr.mFontDisplay); Servo_FontFaceRule_GetFeatureSettings(data, &aAttr.mFeatureSettings); Servo_FontFaceRule_GetVariationSettings(data, &aAttr.mVariationSettings); Servo_FontFaceRule_GetSources(data, &aAttr.mSources); aAttr.mUnicodeRanges = GetUnicodeRangeAsCharacterMap(); return true; } bool FontFaceImpl::HasLocalSrc() const { AutoTArray components; Servo_FontFaceRule_GetSources(GetData(), &components); for (auto& component : components) { if (component.tag == StyleFontFaceSourceListComponent::Tag::Local) { return true; } } return false; } nsAtom* FontFaceImpl::GetFamilyName() const { return Servo_FontFaceRule_GetFamilyName(GetData()); } void FontFaceImpl::DisconnectFromRule() { MOZ_ASSERT(HasRule()); // Make a copy of the descriptors. mDescriptors = Servo_FontFaceRule_Clone(mRule).Consume(); mRule = nullptr; mInFontFaceSet = false; } bool FontFaceImpl::HasFontData() const { return mSourceType == eSourceType_Buffer && mBufferSource; } already_AddRefed FontFaceImpl::TakeBufferSource() { MOZ_ASSERT(mBufferSource); return mBufferSource.forget(); } bool FontFaceImpl::IsInFontFaceSet(FontFaceSetImpl* aFontFaceSet) const { if (mFontFaceSet == aFontFaceSet) { return mInFontFaceSet; } return mOtherFontFaceSets.Contains(aFontFaceSet); } void FontFaceImpl::AddFontFaceSet(FontFaceSetImpl* aFontFaceSet) { MOZ_ASSERT(!IsInFontFaceSet(aFontFaceSet)); if (mFontFaceSet == aFontFaceSet) { mInFontFaceSet = true; } else { mOtherFontFaceSets.AppendElement(aFontFaceSet); } } void FontFaceImpl::RemoveFontFaceSet(FontFaceSetImpl* aFontFaceSet) { MOZ_ASSERT(IsInFontFaceSet(aFontFaceSet)); if (mFontFaceSet == aFontFaceSet) { mInFontFaceSet = false; } else { mOtherFontFaceSets.RemoveElement(aFontFaceSet); } // The caller should be holding a strong reference to the FontFaceSetImpl. if (mUserFontEntry) { mUserFontEntry->CheckUserFontSet(); } } gfxCharacterMap* FontFaceImpl::GetUnicodeRangeAsCharacterMap() { if (!mUnicodeRangeDirty) { return mUnicodeRange; } size_t len; const StyleUnicodeRange* rangesPtr = Servo_FontFaceRule_GetUnicodeRanges(GetData(), &len); Span ranges(rangesPtr, len); if (!ranges.IsEmpty()) { RefPtr charMap = new gfxCharacterMap(); for (auto& range : ranges) { charMap->SetRange(range.start, range.end); } charMap->Compact(); // As it's common for multiple font resources to have the same // unicode-range list, look for an existing copy of this map to share, // or add this one to the sharing cache if not already present. mUnicodeRange = gfxPlatformFontList::PlatformFontList()->FindCharMap(charMap); } else { mUnicodeRange = nullptr; } mUnicodeRangeDirty = false; return mUnicodeRange; } // -- FontFaceImpl::Entry // -------------------------------------------------------- /* virtual */ void FontFaceImpl::Entry::SetLoadState(UserFontLoadState aLoadState) { gfxUserFontEntry::SetLoadState(aLoadState); FontFaceLoadStatus status = LoadStateToStatus(aLoadState); nsTArray> fontFaces; { MutexAutoLock lock(mMutex); fontFaces.SetCapacity(mFontFaces.Length()); for (FontFaceImpl* f : mFontFaces) { fontFaces.AppendElement(f); } } for (FontFaceImpl* impl : fontFaces) { auto* setImpl = impl->GetPrimaryFontFaceSet(); if (setImpl->IsOnOwningThread()) { impl->SetStatus(status); } else { setImpl->DispatchToOwningThread( "FontFaceImpl::Entry::SetLoadState", [self = RefPtr{impl}, status] { self->SetStatus(status); }); } } } /* virtual */ void FontFaceImpl::Entry::GetUserFontSets( nsTArray>& aResult) { MutexAutoLock lock(mMutex); aResult.Clear(); if (mFontSet) { aResult.AppendElement(mFontSet); } for (FontFaceImpl* f : mFontFaces) { if (f->mInFontFaceSet) { aResult.AppendElement(f->mFontFaceSet); } for (FontFaceSetImpl* s : f->mOtherFontFaceSets) { aResult.AppendElement(s); } } // Remove duplicates. aResult.Sort(); auto it = std::unique(aResult.begin(), aResult.end()); aResult.TruncateLength(it - aResult.begin()); } /* virtual */ already_AddRefed FontFaceImpl::Entry::GetUserFontSet() const { MutexAutoLock lock(mMutex); if (mFontSet) { return do_AddRef(mFontSet); } if (NS_IsMainThread() && mLoadingFontSet) { return do_AddRef(mLoadingFontSet); } return nullptr; } void FontFaceImpl::Entry::CheckUserFontSetLocked() { // If this is the last font containing a strong reference to the set, we need // to clear the reference as there is no longer anything guaranteeing the set // will be kept alive. if (mFontSet) { auto* set = static_cast(mFontSet); for (FontFaceImpl* f : mFontFaces) { if (f->mFontFaceSet == set || f->mOtherFontFaceSets.Contains(set)) { return; } } } // If possible, promote the most recently added FontFace and its owning // FontFaceSetImpl as the primary set. if (!mFontFaces.IsEmpty()) { mFontSet = mFontFaces.LastElement()->mFontFaceSet; } else { mFontSet = nullptr; } } void FontFaceImpl::Entry::FindFontFaceOwners(nsTHashSet& aOwners) { MutexAutoLock lock(mMutex); for (FontFaceImpl* f : mFontFaces) { if (FontFace* owner = f->GetOwner()) { aOwners.Insert(owner); } } } void FontFaceImpl::Entry::AddFontFace(FontFaceImpl* aFontFace) { MutexAutoLock lock(mMutex); mFontFaces.AppendElement(aFontFace); CheckUserFontSetLocked(); } void FontFaceImpl::Entry::RemoveFontFace(FontFaceImpl* aFontFace) { MutexAutoLock lock(mMutex); mFontFaces.RemoveElement(aFontFace); CheckUserFontSetLocked(); } } // namespace dom } // namespace mozilla