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