Unreviewed, rolling out r201887.
[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 "CSSFontFaceSource.h"
30 #include "CSSFontFeatureValue.h"
31 #include "CSSParser.h"
32 #include "CSSUnicodeRangeValue.h"
33 #include "CSSValuePool.h"
34 #include "Document.h"
35 #include "FontVariantBuilder.h"
36 #include "JSFontFace.h"
37 #include "StyleProperties.h"
38
39 namespace WebCore {
40
41 static bool populateFontFaceWithArrayBuffer(CSSFontFace& fontFace, Ref<JSC::ArrayBufferView>&& arrayBufferView)
42 {
43     auto source = std::make_unique<CSSFontFaceSource>(fontFace, String(), nullptr, nullptr, WTFMove(arrayBufferView));
44     fontFace.adoptSource(WTFMove(source));
45     return false;
46 }
47
48 RefPtr<FontFace> FontFace::create(JSC::ExecState& state, Document& document, const String& family, JSC::JSValue source, const Descriptors& descriptors, ExceptionCode& ec)
49 {
50     auto result = adoptRef(*new FontFace(document.fontSelector()));
51
52     bool dataRequiresAsynchronousLoading = true;
53
54     ec = 0;
55     result->setFamily(family, ec);
56     if (ec)
57         return nullptr;
58
59     if (source.isString()) {
60         auto value = FontFace::parseString(source.getString(&state), CSSPropertySrc);
61         if (!is<CSSValueList>(value.get())) {
62             ec = SYNTAX_ERR;
63             return nullptr;
64         }
65         CSSFontFace::appendSources(result->backing(), downcast<CSSValueList>(*value), &document, false);
66     } else if (auto arrayBufferView = toArrayBufferView(source))
67         dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView.releaseNonNull());
68     else if (auto arrayBuffer = toArrayBuffer(source)) {
69         auto arrayBufferView = JSC::Uint8Array::create(arrayBuffer, 0, arrayBuffer->byteLength());
70         dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView.releaseNonNull());
71     }
72
73     // These ternaries match the default strings inside the FontFaceDescriptors dictionary inside FontFace.idl.
74     result->setStyle(descriptors.style.isEmpty() ? ASCIILiteral("normal") : descriptors.style, ec);
75     if (ec)
76         return nullptr;
77     result->setWeight(descriptors.weight.isEmpty() ? ASCIILiteral("normal") : descriptors.weight, ec);
78     if (ec)
79         return nullptr;
80     result->setStretch(descriptors.stretch.isEmpty() ? ASCIILiteral("normal") : descriptors.stretch, ec);
81     if (ec)
82         return nullptr;
83     result->setUnicodeRange(descriptors.unicodeRange.isEmpty() ? ASCIILiteral("U+0-10FFFF") : descriptors.unicodeRange, ec);
84     if (ec)
85         return nullptr;
86     result->setVariant(descriptors.variant.isEmpty() ? ASCIILiteral("normal") : descriptors.variant, ec);
87     if (ec)
88         return nullptr;
89     result->setFeatureSettings(descriptors.featureSettings.isEmpty() ? ASCIILiteral("normal") : descriptors.featureSettings, ec);
90     if (ec)
91         return nullptr;
92
93     if (!dataRequiresAsynchronousLoading) {
94         result->backing().load();
95         ASSERT(result->backing().status() == CSSFontFace::Status::Success);
96     }
97
98     return WTFMove(result);
99 }
100
101 Ref<FontFace> FontFace::create(CSSFontFace& face)
102 {
103     return adoptRef(*new FontFace(face));
104 }
105
106 FontFace::FontFace(CSSFontSelector& fontSelector)
107     : m_weakPtrFactory(this)
108     , m_backing(CSSFontFace::create(&fontSelector, nullptr, this))
109 {
110     m_backing->addClient(*this);
111 }
112
113 FontFace::FontFace(CSSFontFace& face)
114     : m_weakPtrFactory(this)
115     , m_backing(face)
116 {
117     m_backing->addClient(*this);
118 }
119
120 FontFace::~FontFace()
121 {
122     m_backing->removeClient(*this);
123 }
124
125 WeakPtr<FontFace> FontFace::createWeakPtr() const
126 {
127     return m_weakPtrFactory.createWeakPtr();
128 }
129
130 RefPtr<CSSValue> FontFace::parseString(const String& string, CSSPropertyID propertyID)
131 {
132     auto style = MutableStyleProperties::create();
133     if (CSSParser::parseValue(style, propertyID, string, true, CSSStrictMode, nullptr) == CSSParser::ParseResult::Error)
134         return nullptr;
135     return style->getPropertyCSSValue(propertyID);
136 }
137
138 void FontFace::setFamily(const String& family, ExceptionCode& ec)
139 {
140     if (family.isEmpty()) {
141         ec = SYNTAX_ERR;
142         return;
143     }
144
145     bool success = false;
146     if (auto value = parseString(family, CSSPropertyFontFamily))
147         success = m_backing->setFamilies(*value);
148     if (!success)
149         ec = SYNTAX_ERR;
150 }
151
152 void FontFace::setStyle(const String& style, ExceptionCode& ec)
153 {
154     if (style.isEmpty()) {
155         ec = SYNTAX_ERR;
156         return;
157     }
158
159     bool success = false;
160     if (auto value = parseString(style, CSSPropertyFontStyle))
161         success = m_backing->setStyle(*value);
162     if (!success)
163         ec = SYNTAX_ERR;
164 }
165
166 void FontFace::setWeight(const String& weight, ExceptionCode& ec)
167 {
168     if (weight.isEmpty()) {
169         ec = SYNTAX_ERR;
170         return;
171     }
172
173     bool success = false;
174     if (auto value = parseString(weight, CSSPropertyFontWeight))
175         success = m_backing->setWeight(*value);
176     if (!success)
177         ec = SYNTAX_ERR;
178 }
179
180 void FontFace::setStretch(const String&, ExceptionCode&)
181 {
182     // We don't support font-stretch. Swallow the call.
183 }
184
185 void FontFace::setUnicodeRange(const String& unicodeRange, ExceptionCode& ec)
186 {
187     if (unicodeRange.isEmpty()) {
188         ec = SYNTAX_ERR;
189         return;
190     }
191
192     bool success = false;
193     if (auto value = parseString(unicodeRange, CSSPropertyUnicodeRange))
194         success = m_backing->setUnicodeRange(*value);
195     if (!success)
196         ec = SYNTAX_ERR;
197 }
198
199 void FontFace::setVariant(const String& variant, ExceptionCode& ec)
200 {
201     if (variant.isEmpty()) {
202         ec = SYNTAX_ERR;
203         return;
204     }
205
206     auto style = MutableStyleProperties::create();
207     auto result = CSSParser::parseValue(style, CSSPropertyFontVariant, variant, true, CSSStrictMode, nullptr);
208     if (result == CSSParser::ParseResult::Error) {
209         ec = SYNTAX_ERR;
210         return;
211     }
212
213     // FIXME: Would be much better to stage the new settings and set them all at once
214     // instead of this dance where we make a backup and revert to it if something fails.
215     FontVariantSettings backup = m_backing->variantSettings();
216
217     auto normal = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
218     bool success = true;
219
220     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantLigatures))
221         success &= m_backing->setVariantLigatures(*value);
222     else
223         m_backing->setVariantLigatures(normal);
224
225     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantPosition))
226         success &= m_backing->setVariantPosition(*value);
227     else
228         m_backing->setVariantPosition(normal);
229
230     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantCaps))
231         success &= m_backing->setVariantCaps(*value);
232     else
233         m_backing->setVariantCaps(normal);
234
235     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantNumeric))
236         success &= m_backing->setVariantNumeric(*value);
237     else
238         m_backing->setVariantNumeric(normal);
239
240     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantAlternates))
241         success &= m_backing->setVariantAlternates(*value);
242     else
243         m_backing->setVariantAlternates(normal);
244
245     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantEastAsian))
246         success &= m_backing->setVariantEastAsian(*value);
247     else
248         m_backing->setVariantEastAsian(normal);
249
250     if (!success) {
251         m_backing->setVariantSettings(backup);
252         ec = SYNTAX_ERR;
253     }
254 }
255
256 void FontFace::setFeatureSettings(const String& featureSettings, ExceptionCode& ec)
257 {
258     if (featureSettings.isEmpty()) {
259         ec = SYNTAX_ERR;
260         return;
261     }
262
263     auto value = parseString(featureSettings, CSSPropertyFontFeatureSettings);
264     if (!value) {
265         ec = SYNTAX_ERR;
266         return;
267     }
268     m_backing->setFeatureSettings(*value);
269 }
270
271 String FontFace::family() const
272 {
273     return m_backing->families()->cssText();
274 }
275
276 String FontFace::style() const
277 {
278     switch (m_backing->traitsMask() & FontStyleMask) {
279     case FontStyleNormalMask:
280         return String("normal", String::ConstructFromLiteral);
281     case FontStyleItalicMask:
282         return String("italic", String::ConstructFromLiteral);
283     }
284     ASSERT_NOT_REACHED();
285     return String("normal", String::ConstructFromLiteral);
286 }
287
288 String FontFace::weight() const
289 {
290     switch (m_backing->traitsMask() & FontWeightMask) {
291     case FontWeight100Mask:
292         return String("100", String::ConstructFromLiteral);
293     case FontWeight200Mask:
294         return String("200", String::ConstructFromLiteral);
295     case FontWeight300Mask:
296         return String("300", String::ConstructFromLiteral);
297     case FontWeight400Mask:
298         return String("normal", String::ConstructFromLiteral);
299     case FontWeight500Mask:
300         return String("500", String::ConstructFromLiteral);
301     case FontWeight600Mask:
302         return String("600", String::ConstructFromLiteral);
303     case FontWeight700Mask:
304         return String("bold", String::ConstructFromLiteral);
305     case FontWeight800Mask:
306         return String("800", String::ConstructFromLiteral);
307     case FontWeight900Mask:
308         return String("900", String::ConstructFromLiteral);
309     }
310     ASSERT_NOT_REACHED();
311     return String("normal", String::ConstructFromLiteral);
312 }
313
314 String FontFace::stretch() const
315 {
316     return "normal";
317 }
318
319 String FontFace::unicodeRange() const
320 {
321     if (!m_backing->ranges().size())
322         return "U+0-10FFFF";
323     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
324     for (auto& range : m_backing->ranges())
325         values->append(CSSUnicodeRangeValue::create(range.from, range.to));
326     return values->cssText();
327 }
328
329 String FontFace::variant() const
330 {
331     return computeFontVariant(m_backing->variantSettings())->cssText();
332 }
333
334 String FontFace::featureSettings() const
335 {
336     if (!m_backing->featureSettings().size())
337         return "normal";
338     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
339     for (auto& feature : m_backing->featureSettings())
340         list->append(CSSFontFeatureValue::create(FontFeatureTag(feature.tag()), feature.value()));
341     return list->cssText();
342 }
343
344 auto FontFace::status() const -> LoadStatus
345 {
346     switch (m_backing->status()) {
347     case CSSFontFace::Status::Pending:
348         return LoadStatus::Unloaded;
349     case CSSFontFace::Status::Loading:
350         return LoadStatus::Loading;
351     case CSSFontFace::Status::TimedOut:
352         return LoadStatus::Error;
353     case CSSFontFace::Status::Success:
354         return LoadStatus::Loaded;
355     case CSSFontFace::Status::Failure:
356         return LoadStatus::Error;
357     }
358     ASSERT_NOT_REACHED();
359     return LoadStatus::Error;
360 }
361
362 void FontFace::fontStateChanged(CSSFontFace& face, CSSFontFace::Status, CSSFontFace::Status newState)
363 {
364     ASSERT_UNUSED(face, &face == m_backing.ptr());
365     switch (newState) {
366     case CSSFontFace::Status::Loading:
367         // We still need to resolve promises when loading completes, even if all references to use have fallen out of scope.
368         ref();
369         break;
370     case CSSFontFace::Status::TimedOut:
371         break;
372     case CSSFontFace::Status::Success:
373         if (m_promise)
374             std::exchange(m_promise, Nullopt)->resolve(*this);
375         deref();
376         return;
377     case CSSFontFace::Status::Failure:
378         if (m_promise)
379             std::exchange(m_promise, Nullopt)->reject(NETWORK_ERR);
380         deref();
381         return;
382     case CSSFontFace::Status::Pending:
383         ASSERT_NOT_REACHED();
384         return;
385     }
386 }
387
388 void FontFace::registerLoaded(Promise&& promise)
389 {
390     ASSERT(!m_promise);
391     switch (m_backing->status()) {
392     case CSSFontFace::Status::Loading:
393     case CSSFontFace::Status::Pending:
394         m_promise = WTFMove(promise);
395         return;
396     case CSSFontFace::Status::Success:
397         promise.resolve(*this);
398         return;
399     case CSSFontFace::Status::TimedOut:
400     case CSSFontFace::Status::Failure:
401         promise.reject(NETWORK_ERR);
402         return;
403     }
404 }
405
406 void FontFace::load()
407 {
408     m_backing->load();
409 }
410
411 }