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