mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			855 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			855 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/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> 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)) {
 | 
						|
    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<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
 |