/* * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "FontFaceSet.h" #include "Document.h" #include "ExceptionCodeDescription.h" #include "FontFace.h" #include "JSDOMBinding.h" #include "JSDOMCoreException.h" #include "JSFontFace.h" #include "JSFontFaceSet.h" namespace WebCore { Ref FontFaceSet::create(Document& document, const Vector>& initialFaces) { Ref result = adoptRef(*new FontFaceSet(document, initialFaces)); result->suspendIfNeeded(); return result; } Ref FontFaceSet::create(Document& document, CSSFontFaceSet& backing) { Ref result = adoptRef(*new FontFaceSet(document, backing)); result->suspendIfNeeded(); return result; } FontFaceSet::FontFaceSet(Document& document, const Vector>& initialFaces) : ActiveDOMObject(&document) , m_backing(CSSFontFaceSet::create()) { m_backing->addClient(*this); for (auto& face : initialFaces) add(*face); } FontFaceSet::FontFaceSet(Document& document, CSSFontFaceSet& backing) : ActiveDOMObject(&document) , m_backing(backing) { m_backing->addClient(*this); } FontFaceSet::~FontFaceSet() { m_backing->removeClient(*this); } FontFaceSet::Iterator::Iterator(FontFaceSet& set) : m_target(set) { } RefPtr 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() { } 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->matchingFaces(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(NETWORK_ERR); 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>()).iterator->value.append(pendingPromise.copyRef()); } if (!waiting) pendingPromise->promise.resolve(pendingPromise->faces); } ExceptionOr FontFaceSet::check(const String& family, const String& text) { return m_backing->check(family, text); } void FontFaceSet::registerReady(ReadyPromise&& promise) { ASSERT(!m_promise); if (m_isReady) { promise.resolve(*this); return; } m_promise = WTFMove(promise); } 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() { // FIXME: Fire a "loading" event asynchronously. m_isReady = false; } void FontFaceSet::completedLoading() { if (m_promise) std::exchange(m_promise, std::nullopt)->resolve(*this); m_isReady = true; } 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(NETWORK_ERR); pendingPromise->hasReachedTerminalState = true; } } m_pendingPromises.remove(iterator); } }