Clean up some includes to make the build a bit faster: DOMPromise
[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 "CSSComputedStyleDeclaration.h"
30 #include "CSSFontFaceSource.h"
31 #include "CSSFontFeatureValue.h"
32 #include "CSSFontStyleValue.h"
33 #include "CSSParser.h"
34 #include "CSSPrimitiveValueMappings.h"
35 #include "CSSUnicodeRangeValue.h"
36 #include "CSSValueList.h"
37 #include "CSSValuePool.h"
38 #include "DOMPromiseProxy.h"
39 #include "Document.h"
40 #include "FontVariantBuilder.h"
41 #include "JSFontFace.h"
42 #include "Quirks.h"
43 #include "StyleProperties.h"
44 #include <JavaScriptCore/ArrayBuffer.h>
45 #include <JavaScriptCore/ArrayBufferView.h>
46 #include <JavaScriptCore/JSCInlines.h>
47 #include <wtf/text/StringBuilder.h>
48
49 namespace WebCore {
50
51 static bool populateFontFaceWithArrayBuffer(CSSFontFace& fontFace, Ref<JSC::ArrayBufferView>&& arrayBufferView)
52 {
53     auto source = makeUnique<CSSFontFaceSource>(fontFace, String(), nullptr, nullptr, WTFMove(arrayBufferView));
54     fontFace.adoptSource(WTFMove(source));
55     return false;
56 }
57
58 ExceptionOr<Ref<FontFace>> FontFace::create(Document& document, const String& family, Source&& source, const Descriptors& descriptors)
59 {
60     auto result = adoptRef(*new FontFace(document.fontSelector()));
61
62     bool dataRequiresAsynchronousLoading = true;
63
64     auto setFamilyResult = result->setFamily(document, family);
65     if (setFamilyResult.hasException())
66         return setFamilyResult.releaseException();
67
68     auto sourceConversionResult = WTF::switchOn(source,
69         [&] (String& string) -> ExceptionOr<void> {
70             auto value = FontFace::parseString(string, CSSPropertySrc);
71             if (!is<CSSValueList>(value))
72                 return Exception { SyntaxError };
73             CSSFontFace::appendSources(result->backing(), downcast<CSSValueList>(*value), &document, false);
74             return { };
75         },
76         [&] (RefPtr<ArrayBufferView>& arrayBufferView) -> ExceptionOr<void> {
77             dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView.releaseNonNull());
78             return { };
79         },
80         [&] (RefPtr<ArrayBuffer>& arrayBuffer) -> ExceptionOr<void> {
81             unsigned byteLength = arrayBuffer->byteLength();
82             auto arrayBufferView = JSC::Uint8Array::create(WTFMove(arrayBuffer), 0, byteLength);
83             dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), WTFMove(arrayBufferView));
84             return { };
85         }
86     );
87
88     if (sourceConversionResult.hasException())
89         return sourceConversionResult.releaseException();
90
91     // These ternaries match the default strings inside the FontFaceDescriptors dictionary inside FontFace.idl.
92     auto setStyleResult = result->setStyle(descriptors.style.isEmpty() ? "normal"_s : descriptors.style);
93     if (setStyleResult.hasException())
94         return setStyleResult.releaseException();
95     auto setWeightResult = result->setWeight(descriptors.weight.isEmpty() ? "normal"_s : descriptors.weight);
96     if (setWeightResult.hasException())
97         return setWeightResult.releaseException();
98     auto setStretchResult = result->setStretch(descriptors.stretch.isEmpty() ? "normal"_s : descriptors.stretch);
99     if (setStretchResult.hasException())
100         return setStretchResult.releaseException();
101     auto setUnicodeRangeResult = result->setUnicodeRange(descriptors.unicodeRange.isEmpty() ? "U+0-10FFFF"_s : descriptors.unicodeRange);
102     if (setUnicodeRangeResult.hasException())
103         return setUnicodeRangeResult.releaseException();
104     auto setVariantResult = result->setVariant(descriptors.variant.isEmpty() ? "normal"_s : descriptors.variant);
105     if (setVariantResult.hasException())
106         return setVariantResult.releaseException();
107     auto setFeatureSettingsResult = result->setFeatureSettings(descriptors.featureSettings.isEmpty() ? "normal"_s : descriptors.featureSettings);
108     if (setFeatureSettingsResult.hasException())
109         return setFeatureSettingsResult.releaseException();
110     auto setDisplayResult = result->setDisplay(descriptors.display.isEmpty() ? "auto"_s : descriptors.display);
111     if (setDisplayResult.hasException())
112         return setDisplayResult.releaseException();
113
114     if (!dataRequiresAsynchronousLoading) {
115         result->backing().load();
116         auto status = result->backing().status();
117         ASSERT_UNUSED(status, status == CSSFontFace::Status::Success || status == CSSFontFace::Status::Failure);
118     }
119
120     return result;
121 }
122
123 Ref<FontFace> FontFace::create(CSSFontFace& face)
124 {
125     return adoptRef(*new FontFace(face));
126 }
127
128 FontFace::FontFace(CSSFontSelector& fontSelector)
129     : m_backing(CSSFontFace::create(&fontSelector, nullptr, this))
130     , m_loadedPromise(makeUniqueRef<LoadedPromise>(*this, &FontFace::loadedPromiseResolve))
131 {
132     m_backing->addClient(*this);
133 }
134
135 FontFace::FontFace(CSSFontFace& face)
136     : m_backing(face)
137     , m_loadedPromise(makeUniqueRef<LoadedPromise>(*this, &FontFace::loadedPromiseResolve))
138 {
139     m_backing->addClient(*this);
140 }
141
142 FontFace::~FontFace()
143 {
144     m_backing->removeClient(*this);
145 }
146
147 RefPtr<CSSValue> FontFace::parseString(const String& string, CSSPropertyID propertyID)
148 {
149     // FIXME: Should use the Document to get the right parsing mode.
150     return CSSParser::parseFontFaceDescriptor(propertyID, string, HTMLStandardMode);
151 }
152
153 ExceptionOr<void> FontFace::setFamily(Document& document, const String& family)
154 {
155     if (family.isEmpty())
156         return Exception { SyntaxError };
157
158     String familyNameToUse = family;
159     if (familyNameToUse.contains('\'') && document.quirks().shouldStripQuotationMarkInFontFaceSetFamily())
160         familyNameToUse = family.removeCharacters([](auto character) { return character == '\''; });
161
162     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=196381 Don't use a list here.
163     // See consumeFontFamilyDescriptor() in CSSPropertyParser.cpp for why we're using it.
164     auto list = CSSValueList::createCommaSeparated();
165     list->append(CSSValuePool::singleton().createFontFamilyValue(familyNameToUse));
166     bool success = m_backing->setFamilies(list);
167     if (!success)
168         return Exception { SyntaxError };
169     return { };
170 }
171
172 ExceptionOr<void> FontFace::setStyle(const String& style)
173 {
174     if (style.isEmpty())
175         return Exception { SyntaxError };
176
177     if (auto value = parseString(style, CSSPropertyFontStyle)) {
178         m_backing->setStyle(*value);
179         return { };
180     }
181     return Exception { SyntaxError };
182 }
183
184 ExceptionOr<void> FontFace::setWeight(const String& weight)
185 {
186     if (weight.isEmpty())
187         return Exception { SyntaxError };
188
189     if (auto value = parseString(weight, CSSPropertyFontWeight)) {
190         m_backing->setWeight(*value);
191         return { };
192     }
193     return Exception { SyntaxError };
194 }
195
196 ExceptionOr<void> FontFace::setStretch(const String& stretch)
197 {
198     if (stretch.isEmpty())
199         return Exception { SyntaxError };
200
201     if (auto value = parseString(stretch, CSSPropertyFontStretch)) {
202         m_backing->setStretch(*value);
203         return { };
204     }
205     return Exception { SyntaxError };
206 }
207
208 ExceptionOr<void> FontFace::setUnicodeRange(const String& unicodeRange)
209 {
210     if (unicodeRange.isEmpty())
211         return Exception { SyntaxError };
212
213     bool success = false;
214     if (auto value = parseString(unicodeRange, CSSPropertyUnicodeRange))
215         success = m_backing->setUnicodeRange(*value);
216     if (!success)
217         return Exception { SyntaxError };
218     return { };
219 }
220
221 ExceptionOr<void> FontFace::setVariant(const String& variant)
222 {
223     if (variant.isEmpty())
224         return Exception { SyntaxError };
225
226     auto style = MutableStyleProperties::create();
227     auto result = CSSParser::parseValue(style, CSSPropertyFontVariant, variant, true, HTMLStandardMode);
228     if (result == CSSParser::ParseResult::Error)
229         return Exception { SyntaxError };
230
231     // FIXME: Would be much better to stage the new settings and set them all at once
232     // instead of this dance where we make a backup and revert to it if something fails.
233     FontVariantSettings backup = m_backing->variantSettings();
234
235     auto normal = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
236     bool success = true;
237
238     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantLigatures))
239         success &= m_backing->setVariantLigatures(*value);
240     else
241         m_backing->setVariantLigatures(normal);
242
243     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantPosition))
244         success &= m_backing->setVariantPosition(*value);
245     else
246         m_backing->setVariantPosition(normal);
247
248     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantCaps))
249         success &= m_backing->setVariantCaps(*value);
250     else
251         m_backing->setVariantCaps(normal);
252
253     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantNumeric))
254         success &= m_backing->setVariantNumeric(*value);
255     else
256         m_backing->setVariantNumeric(normal);
257
258     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantAlternates))
259         success &= m_backing->setVariantAlternates(*value);
260     else
261         m_backing->setVariantAlternates(normal);
262
263     if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantEastAsian))
264         success &= m_backing->setVariantEastAsian(*value);
265     else
266         m_backing->setVariantEastAsian(normal);
267
268     if (!success) {
269         m_backing->setVariantSettings(backup);
270         return Exception { SyntaxError };
271     }
272
273     return { };
274 }
275
276 ExceptionOr<void> FontFace::setFeatureSettings(const String& featureSettings)
277 {
278     if (featureSettings.isEmpty())
279         return Exception { SyntaxError };
280
281     auto value = parseString(featureSettings, CSSPropertyFontFeatureSettings);
282     if (!value)
283         return Exception { SyntaxError };
284     m_backing->setFeatureSettings(*value);
285     return { };
286 }
287
288 ExceptionOr<void> FontFace::setDisplay(const String& display)
289 {
290     if (display.isEmpty())
291         return Exception { SyntaxError };
292
293     if (auto value = parseString(display, CSSPropertyFontDisplay)) {
294         m_backing->setLoadingBehavior(*value);
295         return { };
296     }
297
298     return Exception { SyntaxError };
299 }
300
301 String FontFace::family() const
302 {
303     m_backing->updateStyleIfNeeded();
304
305     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=196381 This is only here because CSSFontFace erroneously uses a list of values instead of a single value.
306     // See consumeFontFamilyDescriptor() in CSSPropertyParser.cpp.
307     if (m_backing->families()->length() == 1) {
308         if (m_backing->families()->item(0)) {
309             auto& item = *m_backing->families()->item(0);
310             if (item.isPrimitiveValue()) {
311                 auto& primitiveValue = downcast<CSSPrimitiveValue>(item);
312                 if (primitiveValue.isFontFamily()) {
313                     auto& fontFamily = primitiveValue.fontFamily();
314                     return fontFamily.familyName;
315                 }
316             }
317         }
318     }
319     return m_backing->families()->cssText();
320 }
321
322 String FontFace::style() const
323 {
324     m_backing->updateStyleIfNeeded();
325     auto style = m_backing->italic();
326
327     auto minimum = ComputedStyleExtractor::fontStyleFromStyleValue(style.minimum, FontStyleAxis::ital);
328     auto maximum = ComputedStyleExtractor::fontStyleFromStyleValue(style.maximum, FontStyleAxis::ital);
329
330     if (minimum.get().equals(maximum.get()))
331         return minimum->cssText();
332
333     auto minimumNonKeyword = ComputedStyleExtractor::fontNonKeywordStyleFromStyleValue(style.minimum);
334     auto maximumNonKeyword = ComputedStyleExtractor::fontNonKeywordStyleFromStyleValue(style.maximum);
335
336     ASSERT(minimumNonKeyword->fontStyleValue->valueID() == CSSValueOblique);
337     ASSERT(maximumNonKeyword->fontStyleValue->valueID() == CSSValueOblique);
338
339     StringBuilder builder;
340     builder.append(minimumNonKeyword->fontStyleValue->cssText());
341     builder.append(' ');
342     if (minimum->obliqueValue.get() == maximum->obliqueValue.get())
343         builder.append(minimumNonKeyword->obliqueValue->cssText());
344     else {
345         builder.append(minimumNonKeyword->obliqueValue->cssText());
346         builder.append(' ');
347         builder.append(maximumNonKeyword->obliqueValue->cssText());
348     }
349     return builder.toString();
350 }
351
352 String FontFace::weight() const
353 {
354     m_backing->updateStyleIfNeeded();
355     auto weight = m_backing->weight();
356
357     auto minimum = ComputedStyleExtractor::fontWeightFromStyleValue(weight.minimum);
358     auto maximum = ComputedStyleExtractor::fontWeightFromStyleValue(weight.maximum);
359
360     if (minimum.get().equals(maximum.get()))
361         return minimum->cssText();
362
363     auto minimumNonKeyword = ComputedStyleExtractor::fontNonKeywordWeightFromStyleValue(weight.minimum);
364     auto maximumNonKeyword = ComputedStyleExtractor::fontNonKeywordWeightFromStyleValue(weight.maximum);
365
366     StringBuilder builder;
367     builder.append(minimumNonKeyword->cssText());
368     builder.append(' ');
369     builder.append(maximumNonKeyword->cssText());
370     return builder.toString();
371 }
372
373 String FontFace::stretch() const
374 {
375     m_backing->updateStyleIfNeeded();
376     auto stretch = m_backing->stretch();
377
378     auto minimum = ComputedStyleExtractor::fontStretchFromStyleValue(stretch.minimum);
379     auto maximum = ComputedStyleExtractor::fontStretchFromStyleValue(stretch.maximum);
380
381     if (minimum.get().equals(maximum.get()))
382         return minimum->cssText();
383
384     auto minimumNonKeyword = ComputedStyleExtractor::fontNonKeywordStretchFromStyleValue(stretch.minimum);
385     auto maximumNonKeyword = ComputedStyleExtractor::fontNonKeywordStretchFromStyleValue(stretch.maximum);
386
387     StringBuilder builder;
388     builder.append(minimumNonKeyword->cssText());
389     builder.append(' ');
390     builder.append(maximumNonKeyword->cssText());
391     return builder.toString();
392 }
393
394 String FontFace::unicodeRange() const
395 {
396     m_backing->updateStyleIfNeeded();
397     if (!m_backing->ranges().size())
398         return "U+0-10FFFF"_s;
399     auto values = CSSValueList::createCommaSeparated();
400     for (auto& range : m_backing->ranges())
401         values->append(CSSUnicodeRangeValue::create(range.from, range.to));
402     return values->cssText();
403 }
404
405 String FontFace::variant() const
406 {
407     m_backing->updateStyleIfNeeded();
408     return computeFontVariant(m_backing->variantSettings())->cssText();
409 }
410
411 String FontFace::featureSettings() const
412 {
413     m_backing->updateStyleIfNeeded();
414     if (!m_backing->featureSettings().size())
415         return "normal"_s;
416     auto list = CSSValueList::createCommaSeparated();
417     for (auto& feature : m_backing->featureSettings())
418         list->append(CSSFontFeatureValue::create(FontTag(feature.tag()), feature.value()));
419     return list->cssText();
420 }
421
422 String FontFace::display() const
423 {
424     m_backing->updateStyleIfNeeded();
425     return CSSValuePool::singleton().createValue(m_backing->loadingBehavior())->cssText();
426 }
427
428 auto FontFace::status() const -> LoadStatus
429 {
430     switch (m_backing->status()) {
431     case CSSFontFace::Status::Pending:
432         return LoadStatus::Unloaded;
433     case CSSFontFace::Status::Loading:
434         return LoadStatus::Loading;
435     case CSSFontFace::Status::TimedOut:
436         return LoadStatus::Error;
437     case CSSFontFace::Status::Success:
438         return LoadStatus::Loaded;
439     case CSSFontFace::Status::Failure:
440         return LoadStatus::Error;
441     }
442     ASSERT_NOT_REACHED();
443     return LoadStatus::Error;
444 }
445
446 void FontFace::adopt(CSSFontFace& newFace)
447 {
448     m_backing->removeClient(*this);
449     m_backing = newFace;
450     m_backing->addClient(*this);
451     newFace.setWrapper(*this);
452 }
453
454 void FontFace::fontStateChanged(CSSFontFace& face, CSSFontFace::Status, CSSFontFace::Status newState)
455 {
456     ASSERT_UNUSED(face, &face == m_backing.ptr());
457     switch (newState) {
458     case CSSFontFace::Status::Loading:
459         // We still need to resolve promises when loading completes, even if all references to use have fallen out of scope.
460         ref();
461         break;
462     case CSSFontFace::Status::TimedOut:
463         break;
464     case CSSFontFace::Status::Success:
465         // FIXME: This check should not be needed, but because FontFace's are sometimes adopted after they have already
466         // gone through a load cycle, we can sometimes come back through here and try to resolve the promise again.  
467         if (!m_loadedPromise->isFulfilled())
468             m_loadedPromise->resolve(*this);
469         deref();
470         return;
471     case CSSFontFace::Status::Failure:
472         // FIXME: This check should not be needed, but because FontFace's are sometimes adopted after they have already
473         // gone through a load cycle, we can sometimes come back through here and try to resolve the promise again.  
474         if (!m_loadedPromise->isFulfilled())
475             m_loadedPromise->reject(Exception { NetworkError });
476         deref();
477         return;
478     case CSSFontFace::Status::Pending:
479         ASSERT_NOT_REACHED();
480         return;
481     }
482 }
483
484 auto FontFace::load() -> LoadedPromise&
485 {
486     m_backing->load();
487     return m_loadedPromise.get();
488 }
489
490 FontFace& FontFace::loadedPromiseResolve()
491 {
492     return *this;
493 }
494
495 }