summaryrefslogtreecommitdiffstats
path: root/layout/style/FontFace.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/FontFace.cpp')
-rw-r--r--layout/style/FontFace.cpp808
1 files changed, 808 insertions, 0 deletions
diff --git a/layout/style/FontFace.cpp b/layout/style/FontFace.cpp
new file mode 100644
index 000000000..4558ab1a2
--- /dev/null
+++ b/layout/style/FontFace.cpp
@@ -0,0 +1,808 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/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 "nsCSSParser.h"
+#include "nsCSSRules.h"
+#include "nsIDocument.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);
+
+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.ComputeLengthAndData();
+ // 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(mRule)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOtherFontFaceSets)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+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(mRule)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
+ 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)
+ , mInFontFaceSet(false)
+{
+ MOZ_COUNT_CTOR(FontFace);
+}
+
+FontFace::~FontFace()
+{
+ MOZ_COUNT_DTOR(FontFace);
+
+ SetUserFontEntry(nullptr);
+
+ if (mSourceBuffer) {
+ free(mSourceBuffer);
+ }
+}
+
+JSObject*
+FontFace::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return FontFaceBinding::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_LOADING:
+ return FontFaceLoadStatus::Loading;
+ case gfxUserFontEntry::UserFontLoadState::STATUS_LOADED:
+ return FontFaceLoadStatus::Loaded;
+ case gfxUserFontEntry::UserFontLoadState::STATUS_FAILED:
+ return FontFaceLoadStatus::Error;
+ }
+ NS_NOTREACHED("invalid aLoadState value");
+ return FontFaceLoadStatus::Error;
+}
+
+already_AddRefed<FontFace>
+FontFace::CreateForRule(nsISupports* aGlobal,
+ FontFaceSet* aFontFaceSet,
+ nsCSSFontFaceRule* 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 nsAString& aFamily,
+ const StringOrArrayBufferOrArrayBufferView& aSource,
+ const FontFaceDescriptors& aDescriptors,
+ ErrorResult& aRv)
+{
+ nsISupports* global = aGlobal.GetAsSupports();
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
+ nsIDocument* 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 StringOrArrayBufferOrArrayBufferView& aSource)
+{
+ if (aSource.IsString()) {
+ if (!ParseDescriptor(eCSSFontDesc_Src,
+ aSource.GetAsString(),
+ mDescriptors->mSrc)) {
+ 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(nsString& aResult)
+{
+ mFontFaceSet->FlushUserFontSet();
+
+ // Serialize the same way as in nsCSSFontFaceStyleDecl::GetPropertyValue.
+ nsCSSValue value;
+ GetDesc(eCSSFontDesc_Family, value);
+
+ aResult.Truncate();
+
+ if (value.GetUnit() == eCSSUnit_Null) {
+ return;
+ }
+
+ nsDependentString family(value.GetStringBufferValue());
+ if (!family.IsEmpty()) {
+ // The string length can be zero when the author passed an invalid
+ // family name or an invalid descriptor to the JS FontFace constructor.
+ nsStyleUtil::AppendEscapedCSSString(family, aResult);
+ }
+}
+
+void
+FontFace::SetFamily(const nsAString& aValue, ErrorResult& aRv)
+{
+ mFontFaceSet->FlushUserFontSet();
+ SetDescriptor(eCSSFontDesc_Family, aValue, aRv);
+}
+
+void
+FontFace::GetStyle(nsString& aResult)
+{
+ mFontFaceSet->FlushUserFontSet();
+ GetDesc(eCSSFontDesc_Style, eCSSProperty_font_style, aResult);
+}
+
+void
+FontFace::SetStyle(const nsAString& aValue, ErrorResult& aRv)
+{
+ mFontFaceSet->FlushUserFontSet();
+ SetDescriptor(eCSSFontDesc_Style, aValue, aRv);
+}
+
+void
+FontFace::GetWeight(nsString& aResult)
+{
+ mFontFaceSet->FlushUserFontSet();
+ GetDesc(eCSSFontDesc_Weight, eCSSProperty_font_weight, aResult);
+}
+
+void
+FontFace::SetWeight(const nsAString& aValue, ErrorResult& aRv)
+{
+ mFontFaceSet->FlushUserFontSet();
+ SetDescriptor(eCSSFontDesc_Weight, aValue, aRv);
+}
+
+void
+FontFace::GetStretch(nsString& aResult)
+{
+ mFontFaceSet->FlushUserFontSet();
+ GetDesc(eCSSFontDesc_Stretch, eCSSProperty_font_stretch, aResult);
+}
+
+void
+FontFace::SetStretch(const nsAString& aValue, ErrorResult& aRv)
+{
+ mFontFaceSet->FlushUserFontSet();
+ SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv);
+}
+
+void
+FontFace::GetUnicodeRange(nsString& aResult)
+{
+ mFontFaceSet->FlushUserFontSet();
+
+ // There is no eCSSProperty_unicode_range for us to pass in to GetDesc
+ // to get a serialized (possibly defaulted) value, but that function
+ // doesn't use the property ID for this descriptor anyway.
+ GetDesc(eCSSFontDesc_UnicodeRange, eCSSProperty_UNKNOWN, aResult);
+}
+
+void
+FontFace::SetUnicodeRange(const nsAString& aValue, ErrorResult& aRv)
+{
+ mFontFaceSet->FlushUserFontSet();
+ SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv);
+}
+
+void
+FontFace::GetVariant(nsString& aResult)
+{
+ mFontFaceSet->FlushUserFontSet();
+
+ // XXX Just expose the font-variant descriptor as "normal" until we
+ // support it properly (bug 1055385).
+ aResult.AssignLiteral("normal");
+}
+
+void
+FontFace::SetVariant(const nsAString& aValue, ErrorResult& aRv)
+{
+ mFontFaceSet->FlushUserFontSet();
+
+ // XXX Ignore assignments to variant until we support font-variant
+ // descriptors (bug 1055385).
+}
+
+void
+FontFace::GetFeatureSettings(nsString& aResult)
+{
+ mFontFaceSet->FlushUserFontSet();
+ GetDesc(eCSSFontDesc_FontFeatureSettings, eCSSProperty_font_feature_settings,
+ aResult);
+}
+
+void
+FontFace::SetFeatureSettings(const nsAString& aValue, ErrorResult& aRv)
+{
+ mFontFaceSet->FlushUserFontSet();
+ SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv);
+}
+
+void
+FontFace::GetDisplay(nsString& aResult)
+{
+ mFontFaceSet->FlushUserFontSet();
+ GetDesc(eCSSFontDesc_Display, eCSSProperty_UNKNOWN, aResult);
+}
+
+void
+FontFace::SetDisplay(const nsAString& aValue, ErrorResult& aRv)
+{
+ mFontFaceSet->FlushUserFontSet();
+ SetDescriptor(eCSSFontDesc_Display, aValue, aRv);
+}
+
+FontFaceLoadStatus
+FontFace::Status()
+{
+ return mStatus;
+}
+
+Promise*
+FontFace::Load(ErrorResult& aRv)
+{
+ 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)
+{
+ mFontFaceSet->FlushUserFontSet();
+
+ EnsurePromise();
+
+ if (!mLoaded) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ return mLoaded;
+}
+
+void
+FontFace::SetStatus(FontFaceLoadStatus aStatus)
+{
+ 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) {
+ mLoaded->MaybeResolve(this);
+ }
+ } else if (mStatus == FontFaceLoadStatus::Error) {
+ if (mSourceType == eSourceType_Buffer) {
+ Reject(NS_ERROR_DOM_SYNTAX_ERR);
+ } else {
+ Reject(NS_ERROR_DOM_NETWORK_ERR);
+ }
+ }
+}
+
+bool
+FontFace::ParseDescriptor(nsCSSFontDesc aDescID,
+ const nsAString& aString,
+ nsCSSValue& aResult)
+{
+ nsCSSParser parser;
+
+ 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();
+
+ if (!parser.ParseFontFaceDescriptor(aDescID, aString,
+ docURI, // aSheetURL
+ base,
+ principal,
+ aResult)) {
+ aResult.Reset();
+ return false;
+ }
+
+ return true;
+}
+
+void
+FontFace::SetDescriptor(nsCSSFontDesc aFontDesc,
+ const nsAString& aValue,
+ ErrorResult& aRv)
+{
+ NS_ASSERTION(!HasRule(),
+ "we don't handle rule backed FontFace objects yet");
+ if (HasRule()) {
+ return;
+ }
+
+ nsCSSValue parsedValue;
+ if (!ParseDescriptor(aFontDesc, aValue, parsedValue)) {
+ aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ return;
+ }
+
+ mDescriptors->Get(aFontDesc) = parsedValue;
+
+ // XXX Setting descriptors doesn't actually have any effect on FontFace
+ // objects that have started loading or have already been loaded.
+}
+
+bool
+FontFace::SetDescriptors(const nsAString& aFamily,
+ const FontFaceDescriptors& aDescriptors)
+{
+ MOZ_ASSERT(!HasRule());
+ MOZ_ASSERT(!mDescriptors);
+
+ mDescriptors = new CSSFontFaceDescriptors;
+
+ // Parse all of the mDescriptors in aInitializer, which are the values
+ // we got from the JS constructor.
+ if (!ParseDescriptor(eCSSFontDesc_Family,
+ aFamily,
+ mDescriptors->mFamily) ||
+ *mDescriptors->mFamily.GetStringBufferValue() == 0 ||
+ !ParseDescriptor(eCSSFontDesc_Style,
+ aDescriptors.mStyle,
+ mDescriptors->mStyle) ||
+ !ParseDescriptor(eCSSFontDesc_Weight,
+ aDescriptors.mWeight,
+ mDescriptors->mWeight) ||
+ !ParseDescriptor(eCSSFontDesc_Stretch,
+ aDescriptors.mStretch,
+ mDescriptors->mStretch) ||
+ !ParseDescriptor(eCSSFontDesc_UnicodeRange,
+ aDescriptors.mUnicodeRange,
+ mDescriptors->mUnicodeRange) ||
+ !ParseDescriptor(eCSSFontDesc_FontFeatureSettings,
+ aDescriptors.mFeatureSettings,
+ mDescriptors->mFontFeatureSettings) ||
+ !ParseDescriptor(eCSSFontDesc_Display,
+ aDescriptors.mDisplay,
+ mDescriptors->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 = new CSSFontFaceDescriptors;
+
+ Reject(NS_ERROR_DOM_SYNTAX_ERR);
+
+ SetStatus(FontFaceLoadStatus::Error);
+ return false;
+ }
+
+ return true;
+}
+
+void
+FontFace::GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const
+{
+ if (HasRule()) {
+ MOZ_ASSERT(mRule);
+ MOZ_ASSERT(!mDescriptors);
+ mRule->GetDesc(aDescID, aResult);
+ } else {
+ aResult = mDescriptors->Get(aDescID);
+ }
+}
+
+void
+FontFace::GetDesc(nsCSSFontDesc aDescID,
+ nsCSSPropertyID aPropID,
+ nsString& aResult) const
+{
+ MOZ_ASSERT(aDescID == eCSSFontDesc_UnicodeRange ||
+ aDescID == eCSSFontDesc_Display ||
+ aPropID != eCSSProperty_UNKNOWN,
+ "only pass eCSSProperty_UNKNOWN for eCSSFontDesc_UnicodeRange");
+
+ nsCSSValue value;
+ GetDesc(aDescID, value);
+
+ aResult.Truncate();
+
+ // Fill in a default value for missing descriptors.
+ if (value.GetUnit() == eCSSUnit_Null) {
+ 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");
+ }
+ return;
+ }
+
+ if (aDescID == eCSSFontDesc_UnicodeRange) {
+ // Since there's no unicode-range property, we can't use
+ // nsCSSValue::AppendToString to serialize this descriptor.
+ nsStyleUtil::AppendUnicodeRange(value, aResult);
+ } else if (aDescID == eCSSFontDesc_Display) {
+ AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(value.GetIntValue(),
+ nsCSSProps::kFontDisplayKTable),
+ aResult);
+ } else {
+ value.AppendToString(aPropID, aResult, nsCSSValue::eNormalized);
+ }
+}
+
+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);
+ }
+ }
+}
+
+bool
+FontFace::GetFamilyName(nsString& aResult)
+{
+ nsCSSValue value;
+ GetDesc(eCSSFontDesc_Family, value);
+
+ if (value.GetUnit() == eCSSUnit_String) {
+ nsString familyname;
+ value.GetStringValue(familyname);
+ aResult.Append(familyname);
+ }
+
+ return !aResult.IsEmpty();
+}
+
+void
+FontFace::DisconnectFromRule()
+{
+ MOZ_ASSERT(HasRule());
+
+ // Make a copy of the descriptors.
+ mDescriptors = new CSSFontFaceDescriptors;
+ mRule->GetDescriptors(*mDescriptors);
+ 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)
+{
+ if (mLoaded) {
+ mLoaded->MaybeReject(aResult);
+ } else if (mLoadedRejection == NS_OK) {
+ mLoadedRejection = aResult;
+ }
+}
+
+void
+FontFace::EnsurePromise()
+{
+ 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);
+ }
+ }
+}
+
+// -- 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