2 * Copyright (C) 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "FontFaceSet.h"
30 #include "ExceptionCodeDescription.h"
32 #include "JSDOMBinding.h"
33 #include "JSDOMCoreException.h"
34 #include "JSFontFace.h"
35 #include "JSFontFaceSet.h"
39 static FontFaceSet::Promise createPromise(JSC::ExecState& exec)
41 JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(exec.lexicalGlobalObject());
42 return FontFaceSet::Promise(DeferredWrapper(&exec, &globalObject, JSC::JSPromiseDeferred::create(&exec, &globalObject)));
45 Ref<FontFaceSet> FontFaceSet::create(Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
47 Ref<FontFaceSet> result = adoptRef(*new FontFaceSet(document, initialFaces));
48 result->suspendIfNeeded();
52 Ref<FontFaceSet> FontFaceSet::create(Document& document, CSSFontFaceSet& backing)
54 Ref<FontFaceSet> result = adoptRef(*new FontFaceSet(document, backing));
55 result->suspendIfNeeded();
59 FontFaceSet::FontFaceSet(Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
60 : ActiveDOMObject(&document)
61 , m_backing(CSSFontFaceSet::create())
63 m_backing->addClient(*this);
64 for (auto& face : initialFaces)
68 FontFaceSet::FontFaceSet(Document& document, CSSFontFaceSet& backing)
69 : ActiveDOMObject(&document)
72 m_backing->addClient(*this);
75 FontFaceSet::~FontFaceSet()
77 m_backing->removeClient(*this);
80 FontFaceSet::Iterator::Iterator(FontFaceSet& set)
85 Optional<WTF::KeyValuePair<RefPtr<FontFace>, RefPtr<FontFace>>> FontFaceSet::Iterator::next(JSC::ExecState& state)
87 if (m_index == m_target->size())
89 RefPtr<FontFace> item = m_target->backing()[m_index++].wrapper(state);
90 return WTF::KeyValuePair<RefPtr<FontFace>, RefPtr<FontFace>>(item, item);
93 FontFaceSet::PendingPromise::PendingPromise(Promise&& promise)
94 : promise(WTFMove(promise))
98 FontFaceSet::PendingPromise::~PendingPromise()
102 bool FontFaceSet::has(FontFace& face) const
104 return m_backing->hasFace(face.backing());
107 size_t FontFaceSet::size() const
109 return m_backing->faceCount();
112 FontFaceSet& FontFaceSet::add(FontFace& face)
114 if (!m_backing->hasFace(face.backing()))
115 m_backing->add(face.backing());
119 bool FontFaceSet::remove(FontFace& face)
121 bool result = m_backing->hasFace(face.backing());
123 m_backing->remove(face.backing());
127 void FontFaceSet::clear()
129 while (m_backing->faceCount())
130 m_backing->remove(m_backing.get()[0]);
133 void FontFaceSet::load(JSC::ExecState& execState, const String& font, const String& text, DeferredWrapper&& promise, ExceptionCode& ec)
136 auto matchingFaces = m_backing->matchingFaces(font, text, ec);
140 if (matchingFaces.isEmpty()) {
141 promise.resolve(Vector<RefPtr<FontFace>>());
145 for (auto& face : matchingFaces)
148 for (auto& face : matchingFaces) {
149 if (face.get().status() == CSSFontFace::Status::Failure) {
150 promise.reject(DOMCoreException::create(ExceptionCodeDescription(NETWORK_ERR)).ptr());
155 auto pendingPromise = PendingPromise::create(WTFMove(promise));
156 bool waiting = false;
158 for (auto& face : matchingFaces) {
159 pendingPromise->faces.append(face.get().wrapper(execState));
160 if (face.get().status() == CSSFontFace::Status::Success)
163 m_pendingPromises.add(&face.get(), Vector<Ref<PendingPromise>>()).iterator->value.append(pendingPromise.copyRef());
167 pendingPromise->promise.resolve(pendingPromise->faces);
170 bool FontFaceSet::check(const String& family, const String& text, ExceptionCode& ec)
172 return m_backing->check(family, text, ec);
175 auto FontFaceSet::promise(JSC::ExecState& execState) -> Promise&
178 m_promise = createPromise(execState);
179 if (m_backing->status() == CSSFontFaceSet::Status::Loaded)
182 return m_promise.value();
185 String FontFaceSet::status() const
187 switch (m_backing->status()) {
188 case CSSFontFaceSet::Status::Loading:
189 return ASCIILiteral("loading");
190 case CSSFontFaceSet::Status::Loaded:
191 return ASCIILiteral("loaded");
193 ASSERT_NOT_REACHED();
194 return ASCIILiteral("loaded");
197 bool FontFaceSet::canSuspendForDocumentSuspension() const
199 return m_backing->status() == CSSFontFaceSet::Status::Loaded;
202 void FontFaceSet::startedLoading()
204 // FIXME: Fire a "loading" event asynchronously.
207 void FontFaceSet::completedLoading()
212 // FIXME: Fire a "loadingdone" and possibly a "loadingerror" event asynchronously.
215 void FontFaceSet::fulfillPromise()
217 // Normally, DeferredWrapper::callFunction resets the reference to the promise.
218 // However, API semantics require our promise to live for the entire lifetime of the FontFace.
219 // Let's make sure it stays alive.
221 Promise guard(m_promise.value());
222 m_promise.value().resolve(*this);
226 void FontFaceSet::faceFinished(CSSFontFace& face, CSSFontFace::Status newStatus)
228 auto iterator = m_pendingPromises.find(&face);
229 if (iterator == m_pendingPromises.end())
232 for (auto& pendingPromise : iterator->value) {
233 if (pendingPromise->hasReachedTerminalState)
235 if (newStatus == CSSFontFace::Status::Success) {
236 if (pendingPromise->hasOneRef()) {
237 pendingPromise->promise.resolve(pendingPromise->faces);
238 pendingPromise->hasReachedTerminalState = true;
241 ASSERT(newStatus == CSSFontFace::Status::Failure);
242 pendingPromise->promise.reject(DOMCoreException::create(ExceptionCodeDescription(NETWORK_ERR)));
243 pendingPromise->hasReachedTerminalState = true;
247 m_pendingPromises.remove(iterator);
250 void FontFaceSet::load(JSC::ExecState& state, const String& font, DeferredWrapper&& promise, ExceptionCode& ec)
252 load(state, font, ASCIILiteral(" "), WTFMove(promise), ec);
255 bool FontFaceSet::check(const String& font, ExceptionCode& ec)
257 return check(font, ASCIILiteral(" "), ec);