Extract UTI mapping and allow for additions
[WebKit-https.git] / Source / WebCore / css / FontFaceSet.cpp
index ad5aa48..1aaffe5 100644 (file)
 #include "FontFaceSet.h"
 
 #include "Document.h"
-#include "ExceptionCodeDescription.h"
 #include "FontFace.h"
 #include "JSDOMBinding.h"
-#include "JSDOMCoreException.h"
 #include "JSFontFace.h"
 #include "JSFontFaceSet.h"
+#include <wtf/IsoMallocInlines.h>
 
 namespace WebCore {
 
-static FontFaceSet::Promise createPromise(JSC::ExecState& exec)
-{
-    JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(exec.lexicalGlobalObject());
-    return FontFaceSet::Promise(DeferredWrapper(&exec, &globalObject, JSC::JSPromiseDeferred::create(&exec, &globalObject)));
-}
+WTF_MAKE_ISO_ALLOCATED_IMPL(FontFaceSet);
 
 Ref<FontFaceSet> FontFaceSet::create(Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
 {
@@ -57,18 +52,22 @@ Ref<FontFaceSet> FontFaceSet::create(Document& document, CSSFontFaceSet& backing
 }
 
 FontFaceSet::FontFaceSet(Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
-    : ActiveDOMObject(&document)
+    : ActiveDOMObject(document)
     , m_backing(CSSFontFaceSet::create())
+    , m_readyPromise(*this, &FontFaceSet::readyPromiseResolve)
 {
     m_backing->addClient(*this);
     for (auto& face : initialFaces)
-        add(face.get());
+        add(*face);
 }
 
 FontFaceSet::FontFaceSet(Document& document, CSSFontFaceSet& backing)
-    : ActiveDOMObject(&document)
+    : ActiveDOMObject(document)
     , m_backing(backing)
+    , m_readyPromise(*this, &FontFaceSet::readyPromiseResolve)
 {
+    if (!backing.hasActiveFontFaces())
+        m_readyPromise.resolve(*this);
     m_backing->addClient(*this);
 }
 
@@ -82,28 +81,23 @@ FontFaceSet::Iterator::Iterator(FontFaceSet& set)
 {
 }
 
-Optional<WTF::KeyValuePair<RefPtr<FontFace>, RefPtr<FontFace>>> FontFaceSet::Iterator::next(JSC::ExecState& state)
+RefPtr<FontFace> FontFaceSet::Iterator::next()
 {
     if (m_index == m_target->size())
-        return Nullopt;
-    RefPtr<FontFace> item = m_target->backing()[m_index++].wrapper(state);
-    return WTF::KeyValuePair<RefPtr<FontFace>, RefPtr<FontFace>>(item, item);
+        return nullptr;
+    return m_target->backing()[m_index++].wrapper();
 }
 
-FontFaceSet::PendingPromise::PendingPromise(Promise&& promise)
+FontFaceSet::PendingPromise::PendingPromise(LoadPromise&& promise)
     : promise(WTFMove(promise))
 {
 }
 
-FontFaceSet::PendingPromise::~PendingPromise()
-{
-}
+FontFaceSet::PendingPromise::~PendingPromise() = default;
 
-bool FontFaceSet::has(RefPtr<WebCore::FontFace> face) const
+bool FontFaceSet::has(FontFace& face) const
 {
-    if (!face)
-        return false;
-    return m_backing->hasFace(face->backing());
+    return m_backing->hasFace(face.backing());
 }
 
 size_t FontFaceSet::size() const
@@ -111,21 +105,18 @@ size_t FontFaceSet::size() const
     return m_backing->faceCount();
 }
 
-FontFaceSet& FontFaceSet::add(RefPtr<WebCore::FontFace> face)
+FontFaceSet& FontFaceSet::add(FontFace& face)
 {
-    if (face && !m_backing->hasFace(face->backing()))
-        m_backing->add(face->backing());
+    if (!m_backing->hasFace(face.backing()))
+        m_backing->add(face.backing());
     return *this;
 }
 
-bool FontFaceSet::remove(RefPtr<WebCore::FontFace> face)
+bool FontFaceSet::remove(FontFace& face)
 {
-    if (!face)
-        return false;
-
-    bool result = m_backing->hasFace(face->backing());
+    bool result = m_backing->hasFace(face.backing());
     if (result)
-        m_backing->remove(face->backing());
+        m_backing->remove(face.backing());
     return result;
 }
 
@@ -135,68 +126,61 @@ void FontFaceSet::clear()
         m_backing->remove(m_backing.get()[0]);
 }
 
-void FontFaceSet::load(JSC::ExecState& execState, const String& font, const String& text, DeferredWrapper&& promise, ExceptionCode& ec)
+void FontFaceSet::load(const String& font, const String& text, LoadPromise&& promise)
 {
-    auto matchingFaces = m_backing->matchingFaces(font, text, ec);
-    if (ec)
+    auto matchingFacesResult = m_backing->matchingFacesExcludingPreinstalledFonts(font, text);
+    if (matchingFacesResult.hasException()) {
+        promise.reject(matchingFacesResult.releaseException());
         return;
+    }
+    auto matchingFaces = matchingFacesResult.releaseReturnValue();
 
     if (matchingFaces.isEmpty()) {
-        promise.resolve(Vector<RefPtr<FontFace>>());
+        promise.resolve({ });
         return;
     }
 
     for (auto& face : matchingFaces)
         face.get().load();
 
-    auto pendingPromise = PendingPromise::create(WTFMove(promise));
-    bool waiting = false;
-
     for (auto& face : matchingFaces) {
         if (face.get().status() == CSSFontFace::Status::Failure) {
-            pendingPromise->promise.reject(DOMCoreException::create(ExceptionCodeDescription(NETWORK_ERR)));
+            promise.reject(NetworkError);
             return;
         }
     }
 
+    auto pendingPromise = PendingPromise::create(WTFMove(promise));
+    bool waiting = false;
+
     for (auto& face : matchingFaces) {
-        pendingPromise->faces.append(face.get().wrapper(execState));
+        pendingPromise->faces.append(face.get().wrapper());
         if (face.get().status() == CSSFontFace::Status::Success)
             continue;
         waiting = true;
-        auto& vector = m_pendingPromises.add(RefPtr<CSSFontFace>(&face.get()), Vector<Ref<PendingPromise>>()).iterator->value;
-        vector.append(pendingPromise.copyRef());
+        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);
 }
 
-bool FontFaceSet::check(const String& family, const String& text, ExceptionCode& ec)
+ExceptionOr<bool> FontFaceSet::check(const String& family, const String& text)
 {
-    return m_backing->check(family, text, ec);
-}
-
-auto FontFaceSet::promise(JSC::ExecState& execState) -> Promise&
-{
-    if (!m_promise) {
-        m_promise = createPromise(execState);
-        if (m_backing->status() == CSSFontFaceSet::Status::Loaded)
-            fulfillPromise();
-    }
-    return m_promise.value();
+    return m_backing->check(family, text);
 }
     
-String FontFaceSet::status() const
+auto FontFaceSet::status() const -> LoadStatus
 {
     switch (m_backing->status()) {
     case CSSFontFaceSet::Status::Loading:
-        return String("loading", String::ConstructFromLiteral);
+        return LoadStatus::Loading;
     case CSSFontFaceSet::Status::Loaded:
-        return String("loaded", String::ConstructFromLiteral);
+        return LoadStatus::Loaded;
     }
     ASSERT_NOT_REACHED();
-    return String("loaded", String::ConstructFromLiteral);
+    return LoadStatus::Loaded;
 }
 
 bool FontFaceSet::canSuspendForDocumentSuspension() const
@@ -207,45 +191,44 @@ bool FontFaceSet::canSuspendForDocumentSuspension() const
 void FontFaceSet::startedLoading()
 {
     // FIXME: Fire a "loading" event asynchronously.
+    m_readyPromise.clear();
 }
 
 void FontFaceSet::completedLoading()
 {
-    if (m_promise)
-        fulfillPromise();
-    m_promise = Nullopt;
-    // FIXME: Fire a "loadingdone" and possibly a "loadingerror" event asynchronously.
-}
-
-void FontFaceSet::fulfillPromise()
-{
-    // Normally, DeferredWrapper::callFunction resets the reference to the promise.
-    // However, API semantics require our promise to live for the entire lifetime of the FontFace.
-    // Let's make sure it stays alive.
-
-    Promise guard(m_promise.value());
-    m_promise.value().resolve(*this);
-    m_promise = guard;
+    m_readyPromise.resolve(*this);
 }
 
 void FontFaceSet::faceFinished(CSSFontFace& face, CSSFontFace::Status newStatus)
 {
-    auto iterator = m_pendingPromises.find(&face);
+    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())
+            if (pendingPromise->hasOneRef()) {
                 pendingPromise->promise.resolve(pendingPromise->faces);
+                pendingPromise->hasReachedTerminalState = true;
+            }
         } else {
             ASSERT(newStatus == CSSFontFace::Status::Failure);
-            // The first resolution wins, so we can just reject early now.
-            pendingPromise->promise.reject(DOMCoreException::create(ExceptionCodeDescription(NETWORK_ERR)));
+            pendingPromise->promise.reject(NetworkError);
+            pendingPromise->hasReachedTerminalState = true;
         }
     }
 
     m_pendingPromises.remove(iterator);
 }
 
+FontFaceSet& FontFaceSet::readyPromiseResolve()
+{
+    return *this;
+}
+
 }