diff options
Diffstat (limited to 'layout/style/FontFace.cpp')
-rw-r--r-- | layout/style/FontFace.cpp | 817 |
1 files changed, 817 insertions, 0 deletions
diff --git a/layout/style/FontFace.cpp b/layout/style/FontFace.cpp new file mode 100644 index 0000000000..4bf89b96a1 --- /dev/null +++ b/layout/style/FontFace.cpp @@ -0,0 +1,817 @@ +/* -*- 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/FontFace.h" + +#include <algorithm> +#include "mozilla/dom/CSSFontFaceRule.h" +#include "mozilla/dom/FontFaceBinding.h" +#include "mozilla/dom/FontFaceSet.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/TypedArray.h" +#include "mozilla/dom/UnionTypes.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoCSSParser.h" +#include "mozilla/ServoStyleSet.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: + explicit FontFaceBufferSource(FontFace* aFontFace) : mFontFace(aFontFace) {} + virtual void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) override; + + private: + RefPtr<FontFace> mFontFace; +}; + +void FontFaceBufferSource::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) { + MOZ_ASSERT(mFontFace, + "only call TakeBuffer once on a given " + "FontFaceBufferSource object"); + mFontFace->TakeBuffer(aBuffer, aLength); + mFontFace = nullptr; +} + +// -- Utility functions ------------------------------------------------------ + +template <typename T> +static void GetDataFrom(const T& aObject, uint8_t*& aBuffer, + uint32_t& aLength) { + MOZ_ASSERT(!aBuffer); + aObject.ComputeState(); + // We use malloc here rather than a FallibleTArray or fallible + // operator new[] since the gfxUserFontEntry will be calling free + // on it. + aBuffer = (uint8_t*)malloc(aObject.Length()); + if (!aBuffer) { + return; + } + memcpy((void*)aBuffer, aObject.Data(), aObject.Length()); + aLength = aObject.Length(); +} + +// -- FontFace --------------------------------------------------------------- + +NS_IMPL_CYCLE_COLLECTION_CLASS(FontFace) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FontFace) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoaded) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOtherFontFaceSets) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FontFace) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoaded) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) + tmp->mInFontFaceSet = false; + tmp->SetUserFontEntry(nullptr); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOtherFontFaceSets) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FontFace) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFace) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(FontFace) +NS_IMPL_CYCLE_COLLECTING_RELEASE(FontFace) + +FontFace::FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet) + : mParent(aParent), + mLoadedRejection(NS_OK), + mStatus(FontFaceLoadStatus::Unloaded), + mSourceType(SourceType(0)), + mSourceBuffer(nullptr), + mSourceBufferLength(0), + mFontFaceSet(aFontFaceSet), + mUnicodeRangeDirty(true), + mInFontFaceSet(false) {} + +FontFace::~FontFace() { + // Assert that we don't drop any FontFace objects during a Servo traversal, + // since PostTraversalTask objects can hold raw pointers to FontFaces. + MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal()); + + SetUserFontEntry(nullptr); + + if (mSourceBuffer) { + free(mSourceBuffer); + } +} + +JSObject* FontFace::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return FontFace_Binding::Wrap(aCx, this, aGivenProto); +} + +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<FontFace> FontFace::CreateForRule( + nsISupports* aGlobal, FontFaceSet* aFontFaceSet, + RawServoFontFaceRule* aRule) { + RefPtr<FontFace> obj = new FontFace(aGlobal, aFontFaceSet); + obj->mRule = aRule; + obj->mSourceType = eSourceType_FontFaceRule; + obj->mInFontFaceSet = true; + return obj.forget(); +} + +already_AddRefed<FontFace> FontFace::Constructor( + const GlobalObject& aGlobal, const nsACString& aFamily, + const UTF8StringOrArrayBufferOrArrayBufferView& aSource, + const FontFaceDescriptors& aDescriptors, ErrorResult& aRv) { + nsISupports* global = aGlobal.GetAsSupports(); + nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global); + Document* doc = window->GetDoc(); + if (!doc) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr<FontFace> obj = new FontFace(global, doc->Fonts()); + if (!obj->SetDescriptors(aFamily, aDescriptors)) { + return obj.forget(); + } + + obj->InitializeSource(aSource); + return obj.forget(); +} + +void FontFace::InitializeSource( + const UTF8StringOrArrayBufferOrArrayBufferView& aSource) { + if (aSource.IsUTF8String()) { + IgnoredErrorResult rv; + SetDescriptor(eCSSFontDesc_Src, aSource.GetAsUTF8String(), rv); + if (rv.Failed()) { + Reject(NS_ERROR_DOM_SYNTAX_ERR); + + SetStatus(FontFaceLoadStatus::Error); + return; + } + + mSourceType = eSourceType_URLs; + return; + } + + mSourceType = FontFace::eSourceType_Buffer; + + if (aSource.IsArrayBuffer()) { + GetDataFrom(aSource.GetAsArrayBuffer(), mSourceBuffer, mSourceBufferLength); + } else { + MOZ_ASSERT(aSource.IsArrayBufferView()); + GetDataFrom(aSource.GetAsArrayBufferView(), mSourceBuffer, + mSourceBufferLength); + } + + SetStatus(FontFaceLoadStatus::Loading); + DoLoad(); +} + +void FontFace::GetFamily(nsACString& aResult) { + GetDesc(eCSSFontDesc_Family, aResult); +} + +void FontFace::SetFamily(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_Family, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFace::GetStyle(nsACString& aResult) { + GetDesc(eCSSFontDesc_Style, aResult); +} + +void FontFace::SetStyle(const nsACString& aValue, ErrorResult& aRv) { + if (SetDescriptor(eCSSFontDesc_Style, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFace::GetWeight(nsACString& aResult) { + GetDesc(eCSSFontDesc_Weight, aResult); +} + +void FontFace::SetWeight(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_Weight, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFace::GetStretch(nsACString& aResult) { + GetDesc(eCSSFontDesc_Stretch, aResult); +} + +void FontFace::SetStretch(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFace::GetUnicodeRange(nsACString& aResult) { + GetDesc(eCSSFontDesc_UnicodeRange, aResult); +} + +void FontFace::SetUnicodeRange(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFace::GetVariant(nsACString& aResult) { + // XXX Just expose the font-variant descriptor as "normal" until we + // support it properly (bug 1055385). + aResult.AssignLiteral("normal"); +} + +void FontFace::SetVariant(const nsACString& aValue, ErrorResult& aRv) { + // XXX Ignore assignments to variant until we support font-variant + // descriptors (bug 1055385). +} + +void FontFace::GetFeatureSettings(nsACString& aResult) { + GetDesc(eCSSFontDesc_FontFeatureSettings, aResult); +} + +void FontFace::SetFeatureSettings(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFace::GetVariationSettings(nsACString& aResult) { + GetDesc(eCSSFontDesc_FontVariationSettings, aResult); +} + +void FontFace::SetVariationSettings(const nsACString& aValue, + ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_FontVariationSettings, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFace::GetDisplay(nsACString& aResult) { + GetDesc(eCSSFontDesc_Display, aResult); +} + +void FontFace::SetDisplay(const nsACString& aValue, ErrorResult& aRv) { + if (SetDescriptor(eCSSFontDesc_Display, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFace::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; + } + + // Behind the scenes, this will actually update the existing entry and return + // it, rather than create a new one. + RefPtr<gfxUserFontEntry> newEntry = + mFontFaceSet->FindOrCreateUserFontEntryFromFontFace(this); + SetUserFontEntry(newEntry); + + if (mInFontFaceSet) { + mFontFaceSet->MarkUserFontSetDirty(); + } + for (auto& set : mOtherFontFaceSets) { + set->MarkUserFontSetDirty(); + } +} + +FontFaceLoadStatus FontFace::Status() { return mStatus; } + +Promise* FontFace::Load(ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread()); + + mFontFaceSet->FlushUserFontSet(); + + EnsurePromise(); + + if (!mLoaded) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + // 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 mLoaded; + } + + // 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(); + + return mLoaded; +} + +gfxUserFontEntry* FontFace::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"); + + RefPtr<gfxUserFontEntry> newEntry = + mFontFaceSet->FindOrCreateUserFontEntryFromFontFace(this); + if (newEntry) { + SetUserFontEntry(newEntry); + } + } + + return mUserFontEntry; +} + +void FontFace::DoLoad() { + if (!CreateUserFontEntry()) { + return; + } + mUserFontEntry->Load(); +} + +Promise* FontFace::GetLoaded(ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread()); + + EnsurePromise(); + + if (!mLoaded) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return mLoaded; +} + +void FontFace::SetStatus(FontFaceLoadStatus aStatus) { + AssertIsMainThreadOrServoFontMetricsLocked(); + + 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 (FontFaceSet* otherSet : mOtherFontFaceSets) { + otherSet->OnFontFaceStatusChanged(this); + } + + if (mStatus == FontFaceLoadStatus::Loaded) { + if (mLoaded) { + DoResolve(); + } + } else if (mStatus == FontFaceLoadStatus::Error) { + if (mSourceType == eSourceType_Buffer) { + Reject(NS_ERROR_DOM_SYNTAX_ERR); + } else { + Reject(NS_ERROR_DOM_NETWORK_ERR); + } + } +} + +void FontFace::DoResolve() { + AssertIsMainThreadOrServoFontMetricsLocked(); + + if (ServoStyleSet* ss = ServoStyleSet::Current()) { + // See comments in Gecko_GetFontMetrics. + ss->AppendTask(PostTraversalTask::ResolveFontFaceLoadedPromise(this)); + return; + } + + mLoaded->MaybeResolve(this); +} + +void FontFace::DoReject(nsresult aResult) { + AssertIsMainThreadOrServoFontMetricsLocked(); + + if (ServoStyleSet* ss = ServoStyleSet::Current()) { + // See comments in Gecko_GetFontMetrics. + ss->AppendTask( + PostTraversalTask::RejectFontFaceLoadedPromise(this, aResult)); + return; + } + + mLoaded->MaybeReject(aResult); +} + +already_AddRefed<URLExtraData> FontFace::GetURLExtraData() const { + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent); + nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull(); + + nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent); + nsCOMPtr<nsIURI> docURI = window->GetDocumentURI(); + nsCOMPtr<nsIURI> base = window->GetDocBaseURI(); + + // We pass RP_Unset when creating ReferrerInfo object here because it's not + // going to result to change referer policy in a resource request. + nsCOMPtr<nsIReferrerInfo> referrerInfo = + new ReferrerInfo(docURI, ReferrerPolicy::_empty); + + RefPtr<URLExtraData> url = new URLExtraData(base, referrerInfo, principal); + return url.forget(); +} + +// Boolean result indicates whether the value of the descriptor was actually +// changed. +bool FontFace::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; + } + + // FIXME(heycam): Should not allow modification of FontFaces that are + // CSS-connected and whose rule is read only. + RefPtr<URLExtraData> url = GetURLExtraData(); + 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 FontFace::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)) { + // 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(); + + Reject(NS_ERROR_DOM_SYNTAX_ERR); + + SetStatus(FontFaceLoadStatus::Error); + return false; + } + + return true; +} + +void FontFace::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 FontFace::SetUserFontEntry(gfxUserFontEntry* aEntry) { + if (mUserFontEntry) { + mUserFontEntry->mFontFaces.RemoveElement(this); + } + + mUserFontEntry = static_cast<Entry*>(aEntry); + if (mUserFontEntry) { + mUserFontEntry->mFontFaces.AppendElement(this); + + MOZ_ASSERT( + mUserFontEntry->GetUserFontSet() == mFontFaceSet->GetUserFontSet(), + "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); + } + } +} + +Maybe<StyleComputedFontWeightRange> FontFace::GetFontWeight() const { + StyleComputedFontWeightRange range; + if (!Servo_FontFaceRule_GetFontWeight(GetData(), &range)) { + return Nothing(); + } + return Some(range); +} + +Maybe<StyleComputedFontStretchRange> FontFace::GetFontStretch() const { + StyleComputedFontStretchRange range; + if (!Servo_FontFaceRule_GetFontStretch(GetData(), &range)) { + return Nothing(); + } + return Some(range); +} + +Maybe<StyleComputedFontStyleDescriptor> FontFace::GetFontStyle() const { + auto descriptor = StyleComputedFontStyleDescriptor::Normal(); + if (!Servo_FontFaceRule_GetFontStyle(GetData(), &descriptor)) { + return Nothing(); + } + return Some(descriptor); +} + +Maybe<StyleFontDisplay> FontFace::GetFontDisplay() const { + StyleFontDisplay display; + if (!Servo_FontFaceRule_GetFontDisplay(GetData(), &display)) { + return Nothing(); + } + return Some(display); +} + +Maybe<StyleFontLanguageOverride> FontFace::GetFontLanguageOverride() const { + StyleFontLanguageOverride langOverride; + if (!Servo_FontFaceRule_GetFontLanguageOverride(GetData(), &langOverride)) { + return Nothing(); + } + return Some(langOverride); +} + +bool FontFace::HasLocalSrc() const { + AutoTArray<StyleFontFaceSourceListComponent, 8> components; + GetSources(components); + for (auto& component : components) { + if (component.tag == StyleFontFaceSourceListComponent::Tag::Local) { + return true; + } + } + return false; +} + +void FontFace::GetFontFeatureSettings( + nsTArray<gfxFontFeature>& aFeatures) const { + Servo_FontFaceRule_GetFeatureSettings(GetData(), &aFeatures); +} + +void FontFace::GetFontVariationSettings( + nsTArray<gfxFontVariation>& aVariations) const { + Servo_FontFaceRule_GetVariationSettings(GetData(), &aVariations); +} + +void FontFace::GetSources( + nsTArray<StyleFontFaceSourceListComponent>& aSources) const { + Servo_FontFaceRule_GetSources(GetData(), &aSources); +} + +nsAtom* FontFace::GetFamilyName() const { + return Servo_FontFaceRule_GetFamilyName(GetData()); +} + +void FontFace::DisconnectFromRule() { + MOZ_ASSERT(HasRule()); + + // Make a copy of the descriptors. + mDescriptors = Servo_FontFaceRule_Clone(mRule).Consume(); + mRule = nullptr; + mInFontFaceSet = false; +} + +bool FontFace::HasFontData() const { + return mSourceType == eSourceType_Buffer && mSourceBuffer; +} + +void FontFace::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) { + MOZ_ASSERT(HasFontData()); + + aBuffer = mSourceBuffer; + aLength = mSourceBufferLength; + + mSourceBuffer = nullptr; + mSourceBufferLength = 0; +} + +already_AddRefed<gfxFontFaceBufferSource> FontFace::CreateBufferSource() { + RefPtr<FontFaceBufferSource> bufferSource = new FontFaceBufferSource(this); + return bufferSource.forget(); +} + +bool FontFace::IsInFontFaceSet(FontFaceSet* aFontFaceSet) const { + if (mFontFaceSet == aFontFaceSet) { + return mInFontFaceSet; + } + return mOtherFontFaceSets.Contains(aFontFaceSet); +} + +void FontFace::AddFontFaceSet(FontFaceSet* aFontFaceSet) { + MOZ_ASSERT(!IsInFontFaceSet(aFontFaceSet)); + + if (mFontFaceSet == aFontFaceSet) { + mInFontFaceSet = true; + } else { + mOtherFontFaceSets.AppendElement(aFontFaceSet); + } +} + +void FontFace::RemoveFontFaceSet(FontFaceSet* aFontFaceSet) { + MOZ_ASSERT(IsInFontFaceSet(aFontFaceSet)); + + if (mFontFaceSet == aFontFaceSet) { + mInFontFaceSet = false; + } else { + mOtherFontFaceSets.RemoveElement(aFontFaceSet); + } +} + +void FontFace::Reject(nsresult aResult) { + AssertIsMainThreadOrServoFontMetricsLocked(); + + if (mLoaded) { + DoReject(aResult); + } else if (mLoadedRejection == NS_OK) { + mLoadedRejection = aResult; + } +} + +void FontFace::EnsurePromise() { + MOZ_ASSERT(NS_IsMainThread()); + + if (mLoaded) { + return; + } + + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent); + + // 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.FontFace constructor + // to be created. + if (global && FontFaceSet::PrefEnabled()) { + ErrorResult rv; + mLoaded = Promise::Create(global, rv); + + if (mStatus == FontFaceLoadStatus::Loaded) { + mLoaded->MaybeResolve(this); + } else if (mLoadedRejection != NS_OK) { + mLoaded->MaybeReject(mLoadedRejection); + } + } +} + +gfxCharacterMap* FontFace::GetUnicodeRangeAsCharacterMap() { + if (!mUnicodeRangeDirty) { + return mUnicodeRange; + } + + size_t len; + const StyleUnicodeRange* rangesPtr = + Servo_FontFaceRule_GetUnicodeRanges(GetData(), &len); + + Span<const StyleUnicodeRange> ranges(rangesPtr, len); + if (!ranges.IsEmpty()) { + RefPtr<gfxCharacterMap> 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; +} + +// -- FontFace::Entry -------------------------------------------------------- + +/* virtual */ +void FontFace::Entry::SetLoadState(UserFontLoadState aLoadState) { + gfxUserFontEntry::SetLoadState(aLoadState); + + for (size_t i = 0; i < mFontFaces.Length(); i++) { + mFontFaces[i]->SetStatus(LoadStateToStatus(aLoadState)); + } +} + +/* virtual */ +void FontFace::Entry::GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult) { + aResult.Clear(); + + for (FontFace* f : mFontFaces) { + if (f->mInFontFaceSet) { + aResult.AppendElement(f->mFontFaceSet->GetUserFontSet()); + } + for (FontFaceSet* s : f->mOtherFontFaceSets) { + aResult.AppendElement(s->GetUserFontSet()); + } + } + + // Remove duplicates. + aResult.Sort(); + auto it = std::unique(aResult.begin(), aResult.end()); + aResult.TruncateLength(it - aResult.begin()); +} + +} // namespace dom +} // namespace mozilla |