#include "config.h"
#include "FontFaceSet.h"
#include "Document.h"
#include "FontFace.h"
#include "JSDOMBinding.h"
#include "JSFontFace.h"
#include "JSFontFaceSet.h"
namespace WebCore {
Ref<FontFaceSet> FontFaceSet::create(Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
{
Ref<FontFaceSet> result = adoptRef(*new FontFaceSet(document, initialFaces));
result->suspendIfNeeded();
return result;
}
Ref<FontFaceSet> FontFaceSet::create(Document& document, CSSFontFaceSet& backing)
{
Ref<FontFaceSet> result = adoptRef(*new FontFaceSet(document, backing));
result->suspendIfNeeded();
return result;
}
FontFaceSet::FontFaceSet(Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
: ActiveDOMObject(&document)
, m_backing(CSSFontFaceSet::create())
, m_readyPromise(*this, &FontFaceSet::readyPromiseResolve)
{
m_backing->addClient(*this);
for (auto& face : initialFaces)
add(*face);
}
FontFaceSet::FontFaceSet(Document& document, CSSFontFaceSet& backing)
: ActiveDOMObject(&document)
, m_backing(backing)
, m_readyPromise(*this, &FontFaceSet::readyPromiseResolve)
{
if (!backing.hasActiveFontFaces())
m_readyPromise.resolve(*this);
m_backing->addClient(*this);
}
FontFaceSet::~FontFaceSet()
{
m_backing->removeClient(*this);
}
FontFaceSet::Iterator::Iterator(FontFaceSet& set)
: m_target(set)
{
}
RefPtr<FontFace> FontFaceSet::Iterator::next()
{
if (m_index == m_target->size())
return nullptr;
return m_target->backing()[m_index++].wrapper();
}
FontFaceSet::PendingPromise::PendingPromise(LoadPromise&& promise)
: promise(WTFMove(promise))
{
}
FontFaceSet::PendingPromise::~PendingPromise() = default;
bool FontFaceSet::has(FontFace& face) const
{
return m_backing->hasFace(face.backing());
}
size_t FontFaceSet::size() const
{
return m_backing->faceCount();
}
FontFaceSet& FontFaceSet::add(FontFace& face)
{
if (!m_backing->hasFace(face.backing()))
m_backing->add(face.backing());
return *this;
}
bool FontFaceSet::remove(FontFace& face)
{
bool result = m_backing->hasFace(face.backing());
if (result)
m_backing->remove(face.backing());
return result;
}
void FontFaceSet::clear()
{
while (m_backing->faceCount())
m_backing->remove(m_backing.get()[0]);
}
void FontFaceSet::load(const String& font, const String& text, LoadPromise&& promise)
{
auto matchingFacesResult = m_backing->matchingFacesExcludingPreinstalledFonts(font, text);
if (matchingFacesResult.hasException()) {
promise.reject(matchingFacesResult.releaseException());
return;
}
auto matchingFaces = matchingFacesResult.releaseReturnValue();
if (matchingFaces.isEmpty()) {
promise.resolve({ });
return;
}
for (auto& face : matchingFaces)
face.get().load();
for (auto& face : matchingFaces) {
if (face.get().status() == CSSFontFace::Status::Failure) {
promise.reject(NetworkError);
return;
}
}
auto pendingPromise = PendingPromise::create(WTFMove(promise));
bool waiting = false;
for (auto& face : matchingFaces) {
pendingPromise->faces.append(face.get().wrapper());
if (face.get().status() == CSSFontFace::Status::Success)
continue;
waiting = true;
ASSERT(face.get().existingWrapper());
m_pendingPromises.add(face.get().existingWrapper(), Vector<Ref<PendingPromise>>()).iterator->value.append(pendingPromise.copyRef());
}
if (!waiting)
pendingPromise->promise.resolve(pendingPromise->faces);
}
ExceptionOr<bool> FontFaceSet::check(const String& family, const String& text)
{
return m_backing->check(family, text);
}
auto FontFaceSet::status() const -> LoadStatus
{
switch (m_backing->status()) {
case CSSFontFaceSet::Status::Loading:
return LoadStatus::Loading;
case CSSFontFaceSet::Status::Loaded:
return LoadStatus::Loaded;
}
ASSERT_NOT_REACHED();
return LoadStatus::Loaded;
}
bool FontFaceSet::canSuspendForDocumentSuspension() const
{
return m_backing->status() == CSSFontFaceSet::Status::Loaded;
}
void FontFaceSet::startedLoading()
{
m_readyPromise.clear();
}
void FontFaceSet::completedLoading()
{
m_readyPromise.resolve(*this);
}
void FontFaceSet::faceFinished(CSSFontFace& face, CSSFontFace::Status newStatus)
{
if (!face.existingWrapper())
return;
auto iterator = m_pendingPromises.find(face.existingWrapper());
if (iterator == m_pendingPromises.end())
return;
for (auto& pendingPromise : iterator->value) {
if (pendingPromise->hasReachedTerminalState)
continue;
if (newStatus == CSSFontFace::Status::Success) {
if (pendingPromise->hasOneRef()) {
pendingPromise->promise.resolve(pendingPromise->faces);
pendingPromise->hasReachedTerminalState = true;
}
} else {
ASSERT(newStatus == CSSFontFace::Status::Failure);
pendingPromise->promise.reject(NetworkError);
pendingPromise->hasReachedTerminalState = true;
}
}
m_pendingPromises.remove(iterator);
}
FontFaceSet& FontFaceSet::readyPromiseResolve()
{
return *this;
}
}