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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "FontFace.h"
28
29#include "CachedFont.h"
30#include "CSSFontFaceSrcValue.h"
31#include "CSSFontFeatureValue.h"
32#include "CSSFontSelector.h"
33#include "CSSPrimitiveValueMappings.h"
34#include "CSSUnicodeRangeValue.h"
35#include "CSSValuePool.h"
36#include "Document.h"
37#include "FontDescription.h"
38#include "FontFaceSource.h"
39#include "FontVariantBuilder.h"
40#include "ImmediateFontFaceSource.h"
41#include "JSFontFace.h"
42#include "SharedBuffer.h"
43#include "StyleProperties.h"
44#include "RemoteFontFaceSource.h"
45
46namespace WebCore {
47
48enum class FontFace::Status {
49 Unloaded,
50 Loading,
51 Loaded,
52 Error
53};
54
55static inline String valueFromDictionary(const Dictionary& dictionary, const char* key)
56{
57 String result;
58 dictionary.get(key, result);
59 return result;
60}
61
62static inline String stringOr(String value, ASCIILiteral defaultValue)
63{
64 if (value.isNull())
65 return String(defaultValue);
66 return value;
67}
68
69static FontFace::Promise createPromise(Document& document)
70{
71 DOMWrapperWorld& normalWorld = mainThreadNormalWorld();
72 JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(&document, normalWorld);
73 JSC::ExecState* exec = globalObject->globalExec();
74 return FontFace::Promise(DeferredWrapper(exec, globalObject, JSC::JSPromiseDeferred::create(exec, globalObject)));
75}
76
77FontFace::FontFace(Document& document, const String& family, const Dictionary& descriptors)
78 : ActiveDOMObject(&document)
79 , m_status(Status::Unloaded)
80 , m_promise(createPromise(document))
81{
82 setFamily(family);
83 setStyle(stringOr(valueFromDictionary(descriptors, "style"), ASCIILiteral("normal")));
84 setWeight(stringOr(valueFromDictionary(descriptors, "weight"), ASCIILiteral("normal")));
85 setStretch(stringOr(valueFromDictionary(descriptors, "stretch"), ASCIILiteral("normal")));
86 setUnicodeRange(stringOr(valueFromDictionary(descriptors, "unicodeRange"), ASCIILiteral("U+0-10FFFF")));
87 setVariant(stringOr(valueFromDictionary(descriptors, "variant"), ASCIILiteral("normal")));
88 setFeatureSettings(stringOr(valueFromDictionary(descriptors, "featureSettings"), ASCIILiteral("normal")));
89}
90
91FontFace::FontFace(Document& document, FontTraitsMask traitsMask, bool isLocalFallback)
92 : ActiveDOMObject(&document)
93 , m_traitsMask(traitsMask)
94 , m_isLocalFallback(isLocalFallback)
95 , m_promise(createPromise(document))
96{
97}
98
99FontFace::FontFace(Document& document, const String& family, const String& srcString, const Dictionary& dictionary)
100 : FontFace(document, family, dictionary)
101{
102 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
103 if (CSSParser::parseValue(style.ptr(), CSSPropertySrc, srcString, true, CSSStrictMode, nullptr) == CSSParser::ParseResult::Error)
104 return;
105
106 RefPtr<CSSValue> src = style->getPropertyCSSValue(CSSPropertySrc);
107 if (!is<CSSValueList>(src.get()))
108 return;
109
110 m_remoteResources = downcast<CSSValueList>(src.get());
111 if (!m_remoteResources->length())
112 return;
113
114 document.fontSelector().addSourcesToFontFace(*this, *m_remoteResources);
115}
116
117FontFace::FontFace(Document& document, const String& family, const void* data, size_t size, const Dictionary& dictionary)
118 : FontFace(document, family, dictionary)
119{
120 // Because ArrayBuffers and ArrayBufferViews are mutable, we need to take a snapshot of the data at the time of construction.
121 // Otherwise, because the API for this object is asynchronous, script may change the data out from under us.
122 m_immediateData = SharedBuffer::create(static_cast<const char*>(data), size);
123 addSource(std::make_unique<ImmediateFontFaceSource>(*this, *m_immediateData));
124}
125
126FontFace::~FontFace()
127{
128}
129
130bool FontFace::canSuspendForDocumentSuspension() const
131{
132 return m_status == Status::Loading;
133}
134
135void FontFace::addSource(std::unique_ptr<FontFaceSource>&& source)
136{
137 m_sources.append(WTFMove(source));
138}
139
140bool FontFace::allSourcesHaveFailed() const
141{
142 for (auto& source : m_sources) {
143 if (source->state() != FontFaceSource::State::Failed)
144 return false;
145 }
146 return true;
147}
148
149String FontFace::status() const
150{
151 switch (m_status) {
152 case Status::Unloaded:
153 return String("unloaded", String::ConstructFromLiteral);
154 case Status::Loading:
155 return String("loading", String::ConstructFromLiteral);
156 case Status::Loaded:
157 return String("loaded", String::ConstructFromLiteral);
158 case Status::Error:
159 return String("error", String::ConstructFromLiteral);
160 }
161 ASSERT_NOT_REACHED();
162 return String("error", String::ConstructFromLiteral);
163}
164
165String FontFace::family() const
166{
167 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
168 for (auto& family : families())
169 list->append(valueForFamily(family));
170 return list->cssText();
171}
172
173void FontFace::setFamily(const String& family)
174{
175 if (m_status != Status::Unloaded)
176 return;
177
178 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
179 if (CSSParser::parseValue(style.ptr(), CSSPropertyFontFamily, family, true, CSSStrictMode, nullptr) == CSSParser::ParseResult::Error)
180 return;
181
182 RefPtr<CSSValue> value = style->getPropertyCSSValue(CSSPropertyFontFamily);
183 if (!value || !is<CSSValueList>(value.get()))
184 return;
185
186 CSSValueList& familyList = downcast<CSSValueList>(*value);
187 if (!familyList.length())
188 return;
189
190 if (m_fontSelector)
191 m_fontSelector->fontFaceWillChange(*this);
192
193 Vector<String> result;
194 for (auto& item : familyList) {
195 String familyName = CSSFontSelector::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
196 if (familyName.isEmpty())
197 return;
198 result.append(WTFMove(familyName));
199 }
200 m_families.swap(result);
201}
202
203String FontFace::style() const
204{
205 switch (m_traitsMask & FontStyleMask) {
206 case FontStyleNormalMask:
207 return String("normal", String::ConstructFromLiteral);
208 case FontStyleItalicMask:
209 return String("italic", String::ConstructFromLiteral);
210 }
211 ASSERT_NOT_REACHED();
212 return String("normal", String::ConstructFromLiteral);
213}
214
215void FontFace::setStyle(const String& newStyle)
216{
217 if (m_status != Status::Unloaded)
218 return;
219
220 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
221 if (CSSParser::parseValue(style.ptr(), CSSPropertyFontStyle, newStyle, true, CSSStrictMode, nullptr) == CSSParser::ParseResult::Error)
222 return;
223
224 RefPtr<CSSValue> fontStyle = style->getPropertyCSSValue(CSSPropertyFontStyle);
225 if (!fontStyle || !is<CSSPrimitiveValue>(*fontStyle))
226 return;
227
228 if (m_fontSelector)
229 m_fontSelector->fontFaceWillChange(*this);
230
231 unsigned newTraitsMask = m_traitsMask & ~FontStyleMask;
232 switch (downcast<CSSPrimitiveValue>(*fontStyle).getValueID()) {
233 case CSSValueNormal:
234 newTraitsMask |= FontStyleNormalMask;
235 break;
236 case CSSValueItalic:
237 case CSSValueOblique:
238 newTraitsMask |= FontStyleItalicMask;
239 break;
240 default:
241 return;
242 }
243 m_traitsMask = static_cast<FontTraitsMask>(newTraitsMask);
244}
245
246String FontFace::weight() const
247{
248 switch (m_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
272void FontFace::setWeight(const String& weight)
273{
274 if (m_status != Status::Unloaded)
275 return;
276
277 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
278 if (CSSParser::parseValue(style.ptr(), CSSPropertyFontWeight, weight, true, CSSStrictMode, nullptr) == CSSParser::ParseResult::Error)
279 return;
280
281 RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight);
282 if (!fontWeight)
283 return;
284
285 if (!is<CSSPrimitiveValue>(*fontWeight))
286 return;
287
288 if (m_fontSelector)
289 m_fontSelector->fontFaceWillChange(*this);
290
291 unsigned newTraitsMask = m_traitsMask & ~FontWeightMask;
292 switch (downcast<CSSPrimitiveValue>(*fontWeight).getValueID()) {
293 case CSSValueBold:
294 case CSSValue700:
295 newTraitsMask |= FontWeight700Mask;
296 break;
297 case CSSValueNormal:
298 case CSSValue400:
299 newTraitsMask |= FontWeight400Mask;
300 break;
301 case CSSValue900:
302 newTraitsMask |= FontWeight900Mask;
303 break;
304 case CSSValue800:
305 newTraitsMask |= FontWeight800Mask;
306 break;
307 case CSSValue600:
308 newTraitsMask |= FontWeight600Mask;
309 break;
310 case CSSValue500:
311 newTraitsMask |= FontWeight500Mask;
312 break;
313 case CSSValue300:
314 newTraitsMask |= FontWeight300Mask;
315 break;
316 case CSSValue200:
317 newTraitsMask |= FontWeight200Mask;
318 break;
319 case CSSValue100:
320 newTraitsMask |= FontWeight100Mask;
321 break;
322 default:
323 return;
324 }
325 m_traitsMask = static_cast<FontTraitsMask>(newTraitsMask);
326}
327
328String FontFace::stretch() const
329{
330 return m_stretch;
331}
332
333void FontFace::setStretch(const String& stretch)
334{
335 m_stretch = stretch;
336}
337
338String FontFace::unicodeRange() const
339{
340 StringBuilder builder;
341 for (size_t i = 0; i < m_ranges.size(); ++i) {
342 if (i)
343 builder.append(", ");
344 builder.append(String::format("U+%x-%x", m_ranges[i].from(), m_ranges[i].to()));
345 }
346 return builder.toString();
347}
348
349void FontFace::setUnicodeRange(const String& newUnicodeRange)
350{
351 if (m_status != Status::Unloaded)
352 return;
353
354 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
355 if (CSSParser::parseValue(style.ptr(), CSSPropertyUnicodeRange, newUnicodeRange, true, CSSStrictMode, nullptr) == CSSParser::ParseResult::Error)
356 return;
357
358 RefPtr<CSSValue> unicodeRange = style->getPropertyCSSValue(CSSPropertyUnicodeRange);
359 if (!unicodeRange || !is<CSSValueList>(*unicodeRange))
360 return;
361
362 CSSValueList* rangeList = downcast<CSSValueList>(unicodeRange.get());
363 if (!rangeList->length())
364 return;
365
366 if (m_fontSelector)
367 m_fontSelector->fontFaceWillChange(*this);
368
369 m_ranges.clear();
370 for (auto& rangeItem : *rangeList) {
371 CSSUnicodeRangeValue& range = downcast<CSSUnicodeRangeValue>(rangeItem.get());
372 addRange(range.from(), range.to());
373 }
374}
375
376String FontFace::variant() const
377{
378 return computeFontVariant(m_variantSettings)->cssText();
379}
380
381void FontFace::setVariant(const String& variant)
382{
383 if (m_status != Status::Unloaded)
384 return;
385
386 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
387 if (CSSParser::parseValue(style.ptr(), CSSPropertyFontVariant, variant, true, CSSStrictMode, nullptr) == CSSParser::ParseResult::Error)
388 return;
389
390 RefPtr<CSSValue> variantLigatures = style->getPropertyCSSValue(CSSPropertyFontVariantLigatures);
391 RefPtr<CSSValue> variantPosition = style->getPropertyCSSValue(CSSPropertyFontVariantPosition);
392 RefPtr<CSSValue> variantCaps = style->getPropertyCSSValue(CSSPropertyFontVariantCaps);
393 RefPtr<CSSValue> variantNumeric = style->getPropertyCSSValue(CSSPropertyFontVariantNumeric);
394 RefPtr<CSSValue> variantAlternates = style->getPropertyCSSValue(CSSPropertyFontVariantAlternates);
395 RefPtr<CSSValue> variantEastAsian = style->getPropertyCSSValue(CSSPropertyFontVariantEastAsian);
396
397 if (!variantLigatures && !variantPosition && !variantCaps && !variantNumeric && !variantAlternates && !variantEastAsian)
398 return;
399
400 m_variantSettings = FontVariantSettings();
401 if (variantLigatures) {
402 applyValueFontVariantLigatures(*variantLigatures, [&](FontVariantLigatures common, FontVariantLigatures discretionary, FontVariantLigatures historical, FontVariantLigatures contextualAlternates) {
403 setVariantCommonLigatures(common);
404 setVariantDiscretionaryLigatures(discretionary);
405 setVariantHistoricalLigatures(historical);
406 setVariantContextualAlternates(contextualAlternates);
407 });
408 }
409
410 if (variantPosition && is<CSSPrimitiveValue>(*variantPosition))
411 setVariantPosition(downcast<CSSPrimitiveValue>(*variantPosition));
412
413 if (variantCaps && is<CSSPrimitiveValue>(*variantCaps))
414 setVariantCaps(downcast<CSSPrimitiveValue>(*variantCaps));
415
416 if (variantNumeric) {
417 applyValueFontVariantNumeric(*variantNumeric, [&](FontVariantNumericFigure figure, FontVariantNumericSpacing spacing, FontVariantNumericFraction fraction, FontVariantNumericOrdinal ordinal, FontVariantNumericSlashedZero slashedZero) {
418 setVariantNumericFigure(figure);
419 setVariantNumericSpacing(spacing);
420 setVariantNumericFraction(fraction);
421 setVariantNumericOrdinal(ordinal);
422 setVariantNumericSlashedZero(slashedZero);
423 });
424 }
425
426 if (variantAlternates && is<CSSPrimitiveValue>(*variantAlternates))
427 setVariantAlternates(downcast<CSSPrimitiveValue>(*variantAlternates));
428
429 if (variantEastAsian) {
430 applyValueFontVariantEastAsian(*variantEastAsian, [&](FontVariantEastAsianVariant variant, FontVariantEastAsianWidth width, FontVariantEastAsianRuby ruby) {
431 setVariantEastAsianVariant(variant);
432 setVariantEastAsianWidth(width);
433 setVariantEastAsianRuby(ruby);
434 });
435 }
436}
437
438String FontFace::featureSettings()
439{
440 if (!m_featureSettings.size())
441 return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal)->cssText();
442 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
443 for (auto& feature : m_featureSettings)
444 list->append(CSSFontFeatureValue::create(FontFeatureTag(feature.tag()), feature.value()));
445 return list->cssText();
446}
447
448void FontFace::setFeatureSettings(const String& newFeatureSettings)
449{
450 if (m_status != Status::Unloaded)
451 return;
452
453 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
454 if (CSSParser::parseValue(style.ptr(), CSSPropertyFontFeatureSettings, newFeatureSettings, true, CSSStrictMode, nullptr) == CSSParser::ParseResult::Error)
455 return;
456
457 RefPtr<CSSValue> featureSettings = style->getPropertyCSSValue(CSSPropertyFontFeatureSettings);
458 if (!featureSettings)
459 return;
460
461 m_featureSettings = FontFeatureSettings();
462 if (is<CSSPrimitiveValue>(featureSettings.get())) {
463 ASSERT(downcast<CSSPrimitiveValue>(featureSettings.get())->getValueID() == CSSValueNormal);
464 return;
465 }
466
467 for (auto& item : downcast<CSSValueList>(*featureSettings)) {
468 auto& feature = downcast<CSSFontFeatureValue>(item.get());
469 insertFeature(FontFeature(feature.tag(), feature.value()));
470 }
471}
472
473void FontFace::pump()
474{
475 ASSERT(m_status == Status::Loading);
476
477 for (size_t index = 0; index < m_sources.size(); ) {
478 switch (m_sources[index]->state()) {
479 case FontFaceSource::State::Pending:
480 m_sources[index]->load();
481 break;
482 case FontFaceSource::State::Loading:
483 return;
484 case FontFaceSource::State::Failed:
485 ++index;
486 break;
487 case FontFaceSource::State::Succeeded:
488 resolvePromise();
489 return;
490 }
491 }
492
493 rejectPromise();
494}
495
496void FontFace::load()
497{
498 if (m_status != Status::Unloaded)
499 return;
500
501 m_status = Status::Loading;
502
503 pump();
504}
505
506void FontFace::kick(FontFaceSource&)
507{
508 if (m_fontSelector)
509 m_fontSelector->fontLoaded(*this);
510
511 if (m_status == Status::Loading)
512 pump();
513}
514
515RefPtr<Font> FontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic)
516{
517 if (allSourcesHaveFailed())
518 return nullptr;
519
520 load();
521
522 for (size_t index = 0; index < m_sources.size(); ) {
523 switch (m_sources[index]->state()) {
524 case FontFaceSource::State::Pending:
525 ASSERT_NOT_REACHED();
526 ++index;
527 break;
528 case FontFaceSource::State::Loading:
529 return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), true, true);
530 case FontFaceSource::State::Failed:
531 ++index;
532 break;
533 case FontFaceSource::State::Succeeded:
534 if (auto result = m_sources[index]->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings))
535 return result;
536 ++index;
537 break;
538 }
539 }
540
541 return nullptr;
542}
543
544void FontFace::resolvePromise()
545{
546 ASSERT(m_status == Status::Loading);
547 m_status = Status::Loaded;
548
549 // Normally, DeferredWrapper::callFunction resets the reference to the promise.
550 // However, API semantics require our promise to live for the entire lifetime of the FontFace.
551 // Let's make sure it stays alive.
552
553 Promise guard(m_promise);
554 m_promise.resolve(*this);
555 m_promise = guard;
556}
557
558void FontFace::rejectPromise()
559{
560 ASSERT(m_status == Status::Loading);
561 m_status = Status::Error;
562
563 // Normally, DeferredWrapper::callFunction resets the reference to the promise.
564 // However, API semantics require our promise to live for the entire lifetime of the FontFace.
565 // Let's make sure it stays alive.
566
567 Promise guard(m_promise);
568 m_promise.reject(-1); // FIXME: Reject with the correct thing
569 m_promise = guard;
570}
571
572}