[Font Loading] Implement FontFaceSet
[WebKit-https.git] / Source / WebCore / css / FontFace.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "FontFace.h"
28
29 #include "CSSFontFace.h"
30 #include "CSSFontFeatureValue.h"
31 #include "CSSFontSelector.h"
32 #include "CSSUnicodeRangeValue.h"
33 #include "CSSValue.h"
34 #include "CSSValuePool.h"
35 #include "Dictionary.h"
36 #include "Document.h"
37 #include "ExceptionCodeDescription.h"
38 #include "FontVariantBuilder.h"
39 #include "JSDOMCoreException.h"
40 #include "JSFontFace.h"
41 #include "ScriptExecutionContext.h"
42 #include "StyleProperties.h"
43 #include <wtf/text/StringBuilder.h>
44
45 namespace WebCore {
46
47 static FontFace::Promise createPromise(JSC::ExecState& exec)
48 {
49     JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(exec.lexicalGlobalObject());
50     return FontFace::Promise(DeferredWrapper(&exec, &globalObject, JSC::JSPromiseDeferred::create(&exec, &globalObject)));
51 }
52
53 static inline Optional<String> valueFromDictionary(const Dictionary& dictionary, const char* key)
54 {
55     String result;
56     dictionary.get(key, result);
57     return result.isNull() ? Nullopt : Optional<String>(result);
58 }
59
60 RefPtr<FontFace> FontFace::create(JSC::ExecState& execState, ScriptExecutionContext& context, const String& family, const Deprecated::ScriptValue& source, const Dictionary& descriptors, ExceptionCode& ec)
61 {
62     if (!context.isDocument()) {
63         ec = TypeError;
64         return nullptr;
65     }
66
67     Ref<FontFace> result = adoptRef(*new FontFace(execState, downcast<Document>(context).fontSelector()));
68
69     result->setFamily(family, ec);
70     if (ec)
71         return nullptr;
72
73     if (source.jsValue().isString()) {
74         String sourceString = source.jsValue().toString(&execState)->value(&execState);
75         auto value = FontFace::parseString(sourceString, CSSPropertySrc);
76         if (is<CSSValueList>(value.get())) {
77             CSSValueList& srcList = downcast<CSSValueList>(*value);
78             CSSFontSelector::appendSources(result->backing(), srcList, &downcast<Document>(context), false);
79         } else {
80             ec = SYNTAX_ERR;
81             return nullptr;
82         }
83     }
84
85     if (auto style = valueFromDictionary(descriptors, "style"))
86         result->setStyle(style.value(), ec);
87     if (ec)
88         return nullptr;
89     if (auto style = valueFromDictionary(descriptors, "weight"))
90         result->setWeight(style.value(), ec);
91     if (ec)
92         return nullptr;
93     if (auto style = valueFromDictionary(descriptors, "stretch"))
94         result->setStretch(style.value(), ec);
95     if (ec)
96         return nullptr;
97     if (auto style = valueFromDictionary(descriptors, "unicodeRange"))
98         result->setUnicodeRange(style.value(), ec);
99     if (ec)
100         return nullptr;
101     if (auto style = valueFromDictionary(descriptors, "variant"))
102         result->setVariant(style.value(), ec);
103     if (ec)
104         return nullptr;
105     if (auto style = valueFromDictionary(descriptors, "featureSettings"))
106         result->setFeatureSettings(style.value(), ec);
107     if (ec)
108         return nullptr;
109
110     return result.ptr();
111 }
112
113 FontFace::FontFace(JSC::ExecState& execState, CSSFontSelector& fontSelector)
114     : m_backing(CSSFontFace::create(fontSelector, this))
115     , m_promise(createPromise(execState))
116 {
117     m_backing->addClient(*this);
118 }
119
120 FontFace::~FontFace()
121 {
122     m_backing->removeClient(*this);
123 }
124
125 RefPtr<CSSValue> FontFace::parseString(const String& string, CSSPropertyID propertyID)
126 {
127     Ref<MutableStyleProperties> style = MutableStyleProperties::create();
128     auto result = CSSParser::parseValue(style.ptr(), propertyID, string, true, CSSStrictMode, nullptr);
129     if (result == CSSParser::ParseResult::Error)
130         return nullptr;
131     return style->getPropertyCSSValue(propertyID);
132 }
133
134 void FontFace::setFamily(const String& family, ExceptionCode& ec)
135 {
136     bool success = false;
137     if (auto value = parseString(family, CSSPropertyFontFamily))
138         success = m_backing->setFamilies(*value);
139     if (!success)
140         ec = SYNTAX_ERR;
141 }
142
143 void FontFace::setStyle(const String& style, ExceptionCode& ec)
144 {
145     bool success = false;
146     if (auto value = parseString(style, CSSPropertyFontStyle))
147         success = m_backing->setStyle(*value);
148     if (!success)
149         ec = SYNTAX_ERR;
150 }
151
152 void FontFace::setWeight(const String& weight, ExceptionCode& ec)
153 {
154     bool success = false;
155     if (auto value = parseString(weight, CSSPropertyFontWeight))
156         success = m_backing->setWeight(*value);
157     if (!success)
158         ec = SYNTAX_ERR;
159 }
160
161 void FontFace::setStretch(const String&, ExceptionCode&)
162 {
163     // We don't support font-stretch. Swallow the call.
164 }
165
166 void FontFace::setUnicodeRange(const String& unicodeRange, ExceptionCode& ec)
167 {
168     bool success = false;
169     if (auto value = parseString(unicodeRange, CSSPropertyUnicodeRange))
170         success = m_backing->setUnicodeRange(*value);
171     if (!success)
172         ec = SYNTAX_ERR;
173 }
174
175 void FontFace::setVariant(const String& variant, ExceptionCode& ec)
176 {
177     Ref<MutableStyleProperties> style = MutableStyleProperties::create();
178     auto result = CSSParser::parseValue(style.ptr(), CSSPropertyFontVariant, variant, true, CSSStrictMode, nullptr);
179     if (result != CSSParser::ParseResult::Error) {
180         FontVariantSettings backup = m_backing->variantSettings();
181         auto normal = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
182         bool success = true;
183         if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantLigatures))
184             success &= m_backing->setVariantLigatures(*value);
185         else
186             m_backing->setVariantLigatures(normal);
187
188         if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantPosition))
189             success &= m_backing->setVariantPosition(*value);
190         else
191             m_backing->setVariantPosition(normal);
192
193         if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantCaps))
194             success &= m_backing->setVariantCaps(*value);
195         else
196             m_backing->setVariantCaps(normal);
197
198         if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantNumeric))
199             success &= m_backing->setVariantNumeric(*value);
200         else
201             m_backing->setVariantNumeric(normal);
202
203         if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantAlternates))
204             success &= m_backing->setVariantAlternates(*value);
205         else
206             m_backing->setVariantAlternates(normal);
207
208         if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantEastAsian))
209             success &= m_backing->setVariantEastAsian(*value);
210         else
211             m_backing->setVariantEastAsian(normal);
212
213         if (success)
214             return;
215         m_backing->setVariantSettings(backup);
216     }
217     ec = SYNTAX_ERR;
218 }
219
220 void FontFace::setFeatureSettings(const String& featureSettings, ExceptionCode& ec)
221 {
222     bool success = false;
223     if (auto value = parseString(featureSettings, CSSPropertyFontFeatureSettings))
224         success = m_backing->setFeatureSettings(*value);
225     if (!success)
226         ec = SYNTAX_ERR;
227 }
228
229 String FontFace::family() const
230 {
231     return m_backing->families()->cssText();
232 }
233
234 String FontFace::style() const
235 {
236     switch (m_backing->traitsMask() & FontStyleMask) {
237     case FontStyleNormalMask:
238         return String("normal", String::ConstructFromLiteral);
239     case FontStyleItalicMask:
240         return String("italic", String::ConstructFromLiteral);
241     }
242     ASSERT_NOT_REACHED();
243     return String("normal", String::ConstructFromLiteral);
244 }
245
246 String FontFace::weight() const
247 {
248     switch (m_backing->traitsMask() & FontWeightMask) {
249     case FontWeight100Mask:
250         return String("100", String::ConstructFromLiteral);
251     case FontWeight200Mask:
252         return String("200", String::ConstructFromLiteral);
253     case FontWeight300Mask:
254         return String("300", String::ConstructFromLiteral);
255     case FontWeight400Mask:
256         return String("normal", String::ConstructFromLiteral);
257     case FontWeight500Mask:
258         return String("500", String::ConstructFromLiteral);
259     case FontWeight600Mask:
260         return String("600", String::ConstructFromLiteral);
261     case FontWeight700Mask:
262         return String("bold", String::ConstructFromLiteral);
263     case FontWeight800Mask:
264         return String("800", String::ConstructFromLiteral);
265     case FontWeight900Mask:
266         return String("900", String::ConstructFromLiteral);
267     }
268     ASSERT_NOT_REACHED();
269     return String("normal", String::ConstructFromLiteral);
270 }
271
272 String FontFace::stretch() const
273 {
274     return "normal";
275 }
276
277 String FontFace::unicodeRange() const
278 {
279     if (!m_backing->ranges().size())
280         return "U+0-10FFFF";
281     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
282     for (auto& range : m_backing->ranges())
283         values->append(CSSUnicodeRangeValue::create(range.from(), range.to()));
284     return values->cssText();
285 }
286
287 String FontFace::variant() const
288 {
289     return computeFontVariant(m_backing->variantSettings())->cssText();
290 }
291
292 String FontFace::featureSettings() const
293 {
294     if (!m_backing->featureSettings().size())
295         return "normal";
296     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
297     for (auto& feature : m_backing->featureSettings())
298         list->append(CSSFontFeatureValue::create(FontFeatureTag(feature.tag()), feature.value()));
299     return list->cssText();
300 }
301
302 String FontFace::status() const
303 {
304     switch (m_backing->status()) {
305     case CSSFontFace::Status::Pending:
306         return String("unloaded", String::ConstructFromLiteral);
307     case CSSFontFace::Status::Loading:
308         return String("loading", String::ConstructFromLiteral);
309     case CSSFontFace::Status::TimedOut:
310         return String("error", String::ConstructFromLiteral);
311     case CSSFontFace::Status::Success:
312         return String("loaded", String::ConstructFromLiteral);
313     case CSSFontFace::Status::Failure:
314         return String("error", String::ConstructFromLiteral);
315     }
316     ASSERT_NOT_REACHED();
317     return String("error", String::ConstructFromLiteral);
318 }
319
320 void FontFace::stateChanged(CSSFontFace& face, CSSFontFace::Status, CSSFontFace::Status newState)
321 {
322     ASSERT_UNUSED(face, &face == m_backing.ptr());
323     switch (newState) {
324     case CSSFontFace::Status::TimedOut:
325         rejectPromise(NETWORK_ERR);
326         return;
327     case CSSFontFace::Status::Success:
328         fulfillPromise();
329         return;
330     case CSSFontFace::Status::Failure:
331         rejectPromise(NETWORK_ERR);
332         return;
333     default:
334         return;
335     }
336 }
337
338 void FontFace::load()
339 {
340     m_backing->load();
341 }
342
343 void FontFace::fulfillPromise()
344 {
345     // Normally, DeferredWrapper::callFunction resets the reference to the promise.
346     // However, API semantics require our promise to live for the entire lifetime of the FontFace.
347     // Let's make sure it stays alive.
348
349     Promise guard(m_promise);
350     m_promise.resolve(*this);
351     m_promise = guard;
352 }
353
354 void FontFace::rejectPromise(ExceptionCode code)
355 {
356     // Normally, DeferredWrapper::callFunction resets the reference to the promise.
357     // However, API semantics require our promise to live for the entire lifetime of the FontFace.
358     // Let's make sure it stays alive.
359
360     Promise guard(m_promise);
361     m_promise.reject(DOMCoreException::create(ExceptionCodeDescription(code)).get());
362     m_promise = guard;
363 }
364
365 }