We check that we can cc and kill the global object on shutdown. At this point it doesn't really matter that the FontFace is still loading, the promise will never resolve. To fix this, implement the "keep alive" by using a target shutdown task. Differential Revision: https://phabricator.services.mozilla.com/D214741
868 lines
25 KiB
C++
868 lines
25 KiB
C++
/* -*- 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 <algorithm>
|
|
#include "gfxFontUtils.h"
|
|
#include "gfxPlatformFontList.h"
|
|
#include "mozilla/dom/FontFaceBinding.h"
|
|
#include "mozilla/dom/FontFaceSetImpl.h"
|
|
#include "mozilla/ServoCSSParser.h"
|
|
#include "mozilla/StaticPrefs_layout.h"
|
|
#include "mozilla/dom/Document.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) {}
|
|
|
|
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::StopKeepingOwnerAlive() {
|
|
if (mKeepingOwnerAlive) {
|
|
mKeepingOwnerAlive = false;
|
|
MOZ_ASSERT(mOwner);
|
|
mOwner->Release();
|
|
}
|
|
}
|
|
|
|
void FontFaceImpl::Destroy() {
|
|
mInFontFaceSet = false;
|
|
SetUserFontEntry(nullptr);
|
|
StopKeepingOwnerAlive();
|
|
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> FontFaceImpl::CreateForRule(
|
|
FontFace* aOwner, FontFaceSetImpl* aFontFaceSet,
|
|
StyleLockedFontFaceRule* aRule) {
|
|
auto obj = MakeRefPtr<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<gfxUserFontEntry> 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<gfxUserFontEntry> 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)) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!mKeepingOwnerAlive);
|
|
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);
|
|
}
|
|
}
|
|
|
|
const bool shouldKeepOwnerAlive = mStatus == FontFaceLoadStatus::Loading;
|
|
if (shouldKeepOwnerAlive != mKeepingOwnerAlive) {
|
|
mKeepingOwnerAlive = shouldKeepOwnerAlive;
|
|
if (shouldKeepOwnerAlive) {
|
|
mOwner->AddRef();
|
|
} else {
|
|
mOwner->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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<URLExtraData> 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<Entry*>(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<StyleFontFaceSourceListComponent, 8> 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<gfxFontFaceBufferSource> 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));
|
|
|
|
auto doAddFontFaceSet = [&]() {
|
|
if (mFontFaceSet == aFontFaceSet) {
|
|
mInFontFaceSet = true;
|
|
} else {
|
|
mOtherFontFaceSets.AppendElement(aFontFaceSet);
|
|
}
|
|
};
|
|
|
|
if (mUserFontEntry) {
|
|
AutoWriteLock lock(mUserFontEntry->Lock());
|
|
doAddFontFaceSet();
|
|
} else {
|
|
doAddFontFaceSet();
|
|
}
|
|
}
|
|
|
|
void FontFaceImpl::RemoveFontFaceSet(FontFaceSetImpl* aFontFaceSet) {
|
|
MOZ_ASSERT(IsInFontFaceSet(aFontFaceSet));
|
|
|
|
auto doRemoveFontFaceSet = [&]() {
|
|
if (mFontFaceSet == aFontFaceSet) {
|
|
mInFontFaceSet = false;
|
|
} else {
|
|
mOtherFontFaceSets.RemoveElement(aFontFaceSet);
|
|
}
|
|
};
|
|
|
|
if (mUserFontEntry) {
|
|
AutoWriteLock lock(mUserFontEntry->Lock());
|
|
doRemoveFontFaceSet();
|
|
// The caller should be holding a strong reference to the FontFaceSetImpl.
|
|
mUserFontEntry->CheckUserFontSetLocked();
|
|
} else {
|
|
doRemoveFontFaceSet();
|
|
}
|
|
}
|
|
|
|
gfxCharacterMap* FontFaceImpl::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()) {
|
|
auto charMap = MakeRefPtr<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<RefPtr<FontFaceImpl>> fontFaces;
|
|
{
|
|
AutoReadLock lock(mLock);
|
|
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<RefPtr<gfxUserFontSet>>& aResult) {
|
|
AutoReadLock lock(mLock);
|
|
|
|
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<gfxUserFontSet>
|
|
FontFaceImpl::Entry::GetUserFontSet() const {
|
|
AutoReadLock lock(mLock);
|
|
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<FontFaceSetImpl*>(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<FontFace*>& aOwners) {
|
|
AutoReadLock lock(mLock);
|
|
for (FontFaceImpl* f : mFontFaces) {
|
|
if (FontFace* owner = f->GetOwner()) {
|
|
aOwners.Insert(owner);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FontFaceImpl::Entry::AddFontFace(FontFaceImpl* aFontFace) {
|
|
AutoWriteLock lock(mLock);
|
|
mFontFaces.AppendElement(aFontFace);
|
|
CheckUserFontSetLocked();
|
|
}
|
|
|
|
void FontFaceImpl::Entry::RemoveFontFace(FontFaceImpl* aFontFace) {
|
|
AutoWriteLock lock(mLock);
|
|
mFontFaces.RemoveElement(aFontFace);
|
|
CheckUserFontSetLocked();
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|