1 | /* |
2 | * Copyright (C) 2007-2019 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 "CSSFontFace.h" |
28 | |
29 | #include "CSSFontFaceSource.h" |
30 | #include "CSSFontFaceSrcValue.h" |
31 | #include "CSSFontFeatureValue.h" |
32 | #include "CSSFontSelector.h" |
33 | #include "CSSFontStyleRangeValue.h" |
34 | #include "CSSPrimitiveValueMappings.h" |
35 | #include "CSSUnicodeRangeValue.h" |
36 | #include "CSSValue.h" |
37 | #include "CSSValueList.h" |
38 | #include "Document.h" |
39 | #include "Font.h" |
40 | #include "FontCache.h" |
41 | #include "FontDescription.h" |
42 | #include "FontFace.h" |
43 | #include "FontVariantBuilder.h" |
44 | #include "Settings.h" |
45 | #include "StyleBuilderConverter.h" |
46 | #include "StyleProperties.h" |
47 | #include "StyleRule.h" |
48 | |
49 | namespace WebCore { |
50 | |
51 | template<typename T> void iterateClients(HashSet<CSSFontFace::Client*>& clients, T callback) |
52 | { |
53 | Vector<Ref<CSSFontFace::Client>> clientsCopy; |
54 | clientsCopy.reserveInitialCapacity(clients.size()); |
55 | for (auto* client : clients) |
56 | clientsCopy.uncheckedAppend(*client); |
57 | |
58 | for (auto* client : clients) |
59 | callback(*client); |
60 | } |
61 | |
62 | void CSSFontFace::appendSources(CSSFontFace& fontFace, CSSValueList& srcList, Document* document, bool isInitiatingElementInUserAgentShadowTree) |
63 | { |
64 | for (auto& src : srcList) { |
65 | // An item in the list either specifies a string (local font name) or a URL (remote font to download). |
66 | CSSFontFaceSrcValue& item = downcast<CSSFontFaceSrcValue>(src.get()); |
67 | std::unique_ptr<CSSFontFaceSource> source; |
68 | SVGFontFaceElement* fontFaceElement = nullptr; |
69 | bool foundSVGFont = false; |
70 | |
71 | #if ENABLE(SVG_FONTS) |
72 | foundSVGFont = item.isSVGFontFaceSrc() || item.svgFontFaceElement(); |
73 | fontFaceElement = item.svgFontFaceElement(); |
74 | #endif |
75 | if (!item.isLocal()) { |
76 | const Settings* settings = document ? &document->settings() : nullptr; |
77 | bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled()); |
78 | if (allowDownloading && item.isSupportedFormat() && document) { |
79 | if (CachedFont* cachedFont = item.cachedFont(document, foundSVGFont, isInitiatingElementInUserAgentShadowTree)) |
80 | source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), cachedFont); |
81 | } |
82 | } else |
83 | source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), nullptr, fontFaceElement); |
84 | |
85 | if (source) |
86 | fontFace.adoptSource(WTFMove(source)); |
87 | } |
88 | fontFace.sourcesPopulated(); |
89 | } |
90 | |
91 | CSSFontFace::CSSFontFace(CSSFontSelector* fontSelector, StyleRuleFontFace* cssConnection, FontFace* wrapper, bool isLocalFallback) |
92 | : m_fontSelector(fontSelector) |
93 | , m_cssConnection(cssConnection) |
94 | , m_wrapper(makeWeakPtr(wrapper)) |
95 | , m_isLocalFallback(isLocalFallback) |
96 | , m_mayBePurged(!wrapper) |
97 | , m_timeoutTimer(*this, &CSSFontFace::timeoutFired) |
98 | { |
99 | } |
100 | |
101 | CSSFontFace::~CSSFontFace() = default; |
102 | |
103 | bool CSSFontFace::setFamilies(CSSValue& family) |
104 | { |
105 | if (!is<CSSValueList>(family)) |
106 | return false; |
107 | |
108 | CSSValueList& familyList = downcast<CSSValueList>(family); |
109 | if (!familyList.length()) |
110 | return false; |
111 | |
112 | RefPtr<CSSValueList> oldFamilies = m_families; |
113 | m_families = &familyList; |
114 | |
115 | if (m_cssConnection) |
116 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFamily, &family); |
117 | |
118 | iterateClients(m_clients, [&](Client& client) { |
119 | client.fontPropertyChanged(*this, oldFamilies.get()); |
120 | }); |
121 | |
122 | return true; |
123 | } |
124 | |
125 | static FontSelectionRange calculateWeightRange(CSSValue& value) |
126 | { |
127 | if (value.isValueList()) { |
128 | auto& valueList = downcast<CSSValueList>(value); |
129 | ASSERT(valueList.length() == 2); |
130 | if (valueList.length() != 2) |
131 | return { normalWeightValue(), normalWeightValue() }; |
132 | ASSERT(valueList.item(0)->isPrimitiveValue()); |
133 | ASSERT(valueList.item(1)->isPrimitiveValue()); |
134 | auto& value0 = downcast<CSSPrimitiveValue>(*valueList.item(0)); |
135 | auto& value1 = downcast<CSSPrimitiveValue>(*valueList.item(1)); |
136 | auto result0 = StyleBuilderConverter::convertFontWeightFromValue(value0); |
137 | auto result1 = StyleBuilderConverter::convertFontWeightFromValue(value1); |
138 | return { result0, result1 }; |
139 | } |
140 | |
141 | ASSERT(is<CSSPrimitiveValue>(value)); |
142 | auto& primitiveValue = downcast<CSSPrimitiveValue>(value); |
143 | FontSelectionValue result = StyleBuilderConverter::convertFontWeightFromValue(primitiveValue); |
144 | return { result, result }; |
145 | } |
146 | |
147 | void CSSFontFace::setWeight(CSSValue& weight) |
148 | { |
149 | auto range = calculateWeightRange(weight); |
150 | if (m_fontSelectionCapabilities.weight == range) |
151 | return; |
152 | |
153 | setWeight(range); |
154 | |
155 | if (m_cssConnection) |
156 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontWeight, &weight); |
157 | |
158 | iterateClients(m_clients, [&](Client& client) { |
159 | client.fontPropertyChanged(*this); |
160 | }); |
161 | } |
162 | |
163 | static FontSelectionRange calculateStretchRange(CSSValue& value) |
164 | { |
165 | if (value.isValueList()) { |
166 | auto& valueList = downcast<CSSValueList>(value); |
167 | ASSERT(valueList.length() == 2); |
168 | if (valueList.length() != 2) |
169 | return { normalStretchValue(), normalStretchValue() }; |
170 | ASSERT(valueList.item(0)->isPrimitiveValue()); |
171 | ASSERT(valueList.item(1)->isPrimitiveValue()); |
172 | auto& value0 = downcast<CSSPrimitiveValue>(*valueList.item(0)); |
173 | auto& value1 = downcast<CSSPrimitiveValue>(*valueList.item(1)); |
174 | auto result0 = StyleBuilderConverter::convertFontStretchFromValue(value0); |
175 | auto result1 = StyleBuilderConverter::convertFontStretchFromValue(value1); |
176 | return { result0, result1 }; |
177 | } |
178 | |
179 | ASSERT(is<CSSPrimitiveValue>(value)); |
180 | const auto& primitiveValue = downcast<CSSPrimitiveValue>(value); |
181 | FontSelectionValue result = StyleBuilderConverter::convertFontStretchFromValue(primitiveValue); |
182 | return { result, result }; |
183 | } |
184 | |
185 | void CSSFontFace::setStretch(CSSValue& style) |
186 | { |
187 | auto range = calculateStretchRange(style); |
188 | if (m_fontSelectionCapabilities.width == range) |
189 | return; |
190 | |
191 | setStretch(range); |
192 | |
193 | if (m_cssConnection) |
194 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStretch, &style); |
195 | |
196 | iterateClients(m_clients, [&](Client& client) { |
197 | client.fontPropertyChanged(*this); |
198 | }); |
199 | } |
200 | |
201 | static FontSelectionRange calculateItalicRange(CSSValue& value) |
202 | { |
203 | if (value.isFontStyleValue()) { |
204 | auto result = StyleBuilderConverter::convertFontStyleFromValue(value); |
205 | return { result.valueOr(normalItalicValue()), result.valueOr(normalItalicValue()) }; |
206 | } |
207 | |
208 | ASSERT(value.isFontStyleRangeValue()); |
209 | auto& rangeValue = downcast<CSSFontStyleRangeValue>(value); |
210 | ASSERT(rangeValue.fontStyleValue->isValueID()); |
211 | auto valueID = rangeValue.fontStyleValue->valueID(); |
212 | if (!rangeValue.obliqueValues) { |
213 | if (valueID == CSSValueNormal) |
214 | return { normalItalicValue(), normalItalicValue() }; |
215 | ASSERT(valueID == CSSValueItalic || valueID == CSSValueOblique); |
216 | return { italicValue(), italicValue() }; |
217 | } |
218 | ASSERT(valueID == CSSValueOblique); |
219 | auto length = rangeValue.obliqueValues->length(); |
220 | if (length == 1) { |
221 | auto& primitiveValue = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(0)); |
222 | FontSelectionValue result(primitiveValue.value<float>(CSSPrimitiveValue::CSS_DEG)); |
223 | return { result, result }; |
224 | } |
225 | ASSERT(length == 2); |
226 | auto& primitiveValue1 = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(0)); |
227 | auto& primitiveValue2 = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(1)); |
228 | FontSelectionValue result1(primitiveValue1.value<float>(CSSPrimitiveValue::CSS_DEG)); |
229 | FontSelectionValue result2(primitiveValue2.value<float>(CSSPrimitiveValue::CSS_DEG)); |
230 | return { result1, result2 }; |
231 | } |
232 | |
233 | void CSSFontFace::setStyle(CSSValue& style) |
234 | { |
235 | auto range = calculateItalicRange(style); |
236 | if (m_fontSelectionCapabilities.slope == range) |
237 | return; |
238 | |
239 | setStyle(range); |
240 | |
241 | if (m_cssConnection) |
242 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStyle, &style); |
243 | |
244 | iterateClients(m_clients, [&](Client& client) { |
245 | client.fontPropertyChanged(*this); |
246 | }); |
247 | } |
248 | |
249 | bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange) |
250 | { |
251 | if (!is<CSSValueList>(unicodeRange)) |
252 | return false; |
253 | |
254 | Vector<UnicodeRange> ranges; |
255 | auto& list = downcast<CSSValueList>(unicodeRange); |
256 | for (auto& rangeValue : list) { |
257 | auto& range = downcast<CSSUnicodeRangeValue>(rangeValue.get()); |
258 | ranges.append({ range.from(), range.to() }); |
259 | } |
260 | |
261 | if (ranges == m_ranges) |
262 | return true; |
263 | |
264 | m_ranges = WTFMove(ranges); |
265 | |
266 | if (m_cssConnection) |
267 | m_cssConnection->mutableProperties().setProperty(CSSPropertyUnicodeRange, &unicodeRange); |
268 | |
269 | iterateClients(m_clients, [&](Client& client) { |
270 | client.fontPropertyChanged(*this); |
271 | }); |
272 | |
273 | return true; |
274 | } |
275 | |
276 | bool CSSFontFace::setVariantLigatures(CSSValue& variantLigatures) |
277 | { |
278 | auto ligatures = extractFontVariantLigatures(variantLigatures); |
279 | |
280 | if (m_variantSettings.commonLigatures == ligatures.commonLigatures |
281 | && m_variantSettings.discretionaryLigatures == ligatures.discretionaryLigatures |
282 | && m_variantSettings.historicalLigatures == ligatures.historicalLigatures |
283 | && m_variantSettings.contextualAlternates == ligatures.contextualAlternates) |
284 | return true; |
285 | |
286 | m_variantSettings.commonLigatures = ligatures.commonLigatures; |
287 | m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures; |
288 | m_variantSettings.historicalLigatures = ligatures.historicalLigatures; |
289 | m_variantSettings.contextualAlternates = ligatures.contextualAlternates; |
290 | |
291 | if (m_cssConnection) |
292 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantLigatures, &variantLigatures); |
293 | |
294 | iterateClients(m_clients, [&](Client& client) { |
295 | client.fontPropertyChanged(*this); |
296 | }); |
297 | |
298 | return true; |
299 | } |
300 | |
301 | bool CSSFontFace::setVariantPosition(CSSValue& variantPosition) |
302 | { |
303 | if (!is<CSSPrimitiveValue>(variantPosition)) |
304 | return false; |
305 | |
306 | FontVariantPosition position = downcast<CSSPrimitiveValue>(variantPosition); |
307 | |
308 | if (m_variantSettings.position == position) |
309 | return true; |
310 | |
311 | m_variantSettings.position = position; |
312 | |
313 | if (m_cssConnection) |
314 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantPosition, &variantPosition); |
315 | |
316 | iterateClients(m_clients, [&](Client& client) { |
317 | client.fontPropertyChanged(*this); |
318 | }); |
319 | |
320 | return true; |
321 | } |
322 | |
323 | bool CSSFontFace::setVariantCaps(CSSValue& variantCaps) |
324 | { |
325 | if (!is<CSSPrimitiveValue>(variantCaps)) |
326 | return false; |
327 | |
328 | FontVariantCaps caps = downcast<CSSPrimitiveValue>(variantCaps); |
329 | |
330 | if (m_variantSettings.caps == caps) |
331 | return true; |
332 | |
333 | m_variantSettings.caps = caps; |
334 | |
335 | if (m_cssConnection) |
336 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantCaps, &variantCaps); |
337 | |
338 | iterateClients(m_clients, [&](Client& client) { |
339 | client.fontPropertyChanged(*this); |
340 | }); |
341 | |
342 | return true; |
343 | } |
344 | |
345 | bool CSSFontFace::setVariantNumeric(CSSValue& variantNumeric) |
346 | { |
347 | auto numeric = extractFontVariantNumeric(variantNumeric); |
348 | |
349 | if (m_variantSettings.numericFigure == numeric.figure |
350 | && m_variantSettings.numericSpacing == numeric.spacing |
351 | && m_variantSettings.numericFraction == numeric.fraction |
352 | && m_variantSettings.numericOrdinal == numeric.ordinal |
353 | && m_variantSettings.numericSlashedZero == numeric.slashedZero) |
354 | return true; |
355 | |
356 | m_variantSettings.numericFigure = numeric.figure; |
357 | m_variantSettings.numericSpacing = numeric.spacing; |
358 | m_variantSettings.numericFraction = numeric.fraction; |
359 | m_variantSettings.numericOrdinal = numeric.ordinal; |
360 | m_variantSettings.numericSlashedZero = numeric.slashedZero; |
361 | |
362 | if (m_cssConnection) |
363 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantNumeric, &variantNumeric); |
364 | |
365 | iterateClients(m_clients, [&](Client& client) { |
366 | client.fontPropertyChanged(*this); |
367 | }); |
368 | |
369 | return true; |
370 | } |
371 | |
372 | bool CSSFontFace::setVariantAlternates(CSSValue& variantAlternates) |
373 | { |
374 | if (!is<CSSPrimitiveValue>(variantAlternates)) |
375 | return false; |
376 | |
377 | FontVariantAlternates alternates = downcast<CSSPrimitiveValue>(variantAlternates); |
378 | |
379 | if (m_variantSettings.alternates == alternates) |
380 | return true; |
381 | |
382 | m_variantSettings.alternates = alternates; |
383 | |
384 | if (m_cssConnection) |
385 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantAlternates, &variantAlternates); |
386 | |
387 | iterateClients(m_clients, [&](Client& client) { |
388 | client.fontPropertyChanged(*this); |
389 | }); |
390 | |
391 | return true; |
392 | } |
393 | |
394 | bool CSSFontFace::setVariantEastAsian(CSSValue& variantEastAsian) |
395 | { |
396 | auto eastAsian = extractFontVariantEastAsian(variantEastAsian); |
397 | |
398 | if (m_variantSettings.eastAsianVariant == eastAsian.variant |
399 | && m_variantSettings.eastAsianWidth == eastAsian.width |
400 | && m_variantSettings.eastAsianRuby == eastAsian.ruby) |
401 | return true; |
402 | |
403 | m_variantSettings.eastAsianVariant = eastAsian.variant; |
404 | m_variantSettings.eastAsianWidth = eastAsian.width; |
405 | m_variantSettings.eastAsianRuby = eastAsian.ruby; |
406 | |
407 | if (m_cssConnection) |
408 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantEastAsian, &variantEastAsian); |
409 | |
410 | iterateClients(m_clients, [&](Client& client) { |
411 | client.fontPropertyChanged(*this); |
412 | }); |
413 | |
414 | return true; |
415 | } |
416 | |
417 | void CSSFontFace::setFeatureSettings(CSSValue& featureSettings) |
418 | { |
419 | // Can only call this with a primitive value of normal, or a value list containing font feature values. |
420 | ASSERT(is<CSSPrimitiveValue>(featureSettings) || is<CSSValueList>(featureSettings)); |
421 | |
422 | FontFeatureSettings settings; |
423 | |
424 | if (is<CSSValueList>(featureSettings)) { |
425 | auto& list = downcast<CSSValueList>(featureSettings); |
426 | for (auto& rangeValue : list) { |
427 | auto& feature = downcast<CSSFontFeatureValue>(rangeValue.get()); |
428 | settings.insert({ feature.tag(), feature.value() }); |
429 | } |
430 | } |
431 | |
432 | if (m_featureSettings == settings) |
433 | return; |
434 | |
435 | m_featureSettings = WTFMove(settings); |
436 | |
437 | if (m_cssConnection) |
438 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFeatureSettings, &featureSettings); |
439 | |
440 | iterateClients(m_clients, [&](Client& client) { |
441 | client.fontPropertyChanged(*this); |
442 | }); |
443 | } |
444 | |
445 | void CSSFontFace::setLoadingBehavior(CSSValue& loadingBehaviorValue) |
446 | { |
447 | auto loadingBehavior = static_cast<FontLoadingBehavior>(downcast<CSSPrimitiveValue>(loadingBehaviorValue)); |
448 | |
449 | if (m_loadingBehavior == loadingBehavior) |
450 | return; |
451 | |
452 | m_loadingBehavior = loadingBehavior; |
453 | |
454 | if (m_cssConnection) |
455 | m_cssConnection->mutableProperties().setProperty(CSSPropertyFontDisplay, &loadingBehaviorValue); |
456 | |
457 | iterateClients(m_clients, [&](Client& client) { |
458 | client.fontPropertyChanged(*this); |
459 | }); |
460 | } |
461 | |
462 | bool CSSFontFace::rangesMatchCodePoint(UChar32 character) const |
463 | { |
464 | if (m_ranges.isEmpty()) |
465 | return true; |
466 | |
467 | for (auto& range : m_ranges) { |
468 | if (range.from <= character && character <= range.to) |
469 | return true; |
470 | } |
471 | return false; |
472 | } |
473 | |
474 | void CSSFontFace::fontLoadEventOccurred() |
475 | { |
476 | // If the font is already in the cache, CSSFontFaceSource may report it's loaded before it is added here as a source. |
477 | // Let's not pump the state machine until we've got all our sources. font() and load() are smart enough to act correctly |
478 | // when a source is failed or succeeded before we have asked it to load. |
479 | if (m_sourcesPopulated) |
480 | pump(ExternalResourceDownloadPolicy::Forbid); |
481 | |
482 | ASSERT(m_fontSelector); |
483 | m_fontSelector->fontLoaded(); |
484 | |
485 | iterateClients(m_clients, [&](Client& client) { |
486 | client.fontLoaded(*this); |
487 | }); |
488 | } |
489 | |
490 | void CSSFontFace::timeoutFired() |
491 | { |
492 | Ref<CSSFontFace> protectedThis(*this); |
493 | |
494 | switch (status()) { |
495 | case Status::Loading: |
496 | setStatus(Status::TimedOut); |
497 | break; |
498 | case Status::TimedOut: |
499 | // Cancelling the network request here could lead to a situation where a site's font never gets |
500 | // shown as the user navigates around to different pages on the site. This would occur if the |
501 | // download always takes longer than the timeout (even though the user may spend substantial time |
502 | // on each page). Therefore, we shouldn't cancel the network request here, but should use the |
503 | // loading infrastructure's timeout policies instead. |
504 | setStatus(Status::Failure); |
505 | break; |
506 | default: |
507 | ASSERT_NOT_REACHED(); |
508 | break; |
509 | } |
510 | |
511 | fontLoadEventOccurred(); |
512 | } |
513 | |
514 | bool CSSFontFace::computeFailureState() const |
515 | { |
516 | if (status() == Status::Failure) |
517 | return true; |
518 | for (auto& source : m_sources) { |
519 | if (source->status() != CSSFontFaceSource::Status::Failure) |
520 | return false; |
521 | } |
522 | return true; |
523 | } |
524 | |
525 | void CSSFontFace::addClient(Client& client) |
526 | { |
527 | m_clients.add(&client); |
528 | } |
529 | |
530 | void CSSFontFace::removeClient(Client& client) |
531 | { |
532 | ASSERT(m_clients.contains(&client)); |
533 | m_clients.remove(&client); |
534 | } |
535 | |
536 | void CSSFontFace::initializeWrapper() |
537 | { |
538 | switch (m_status) { |
539 | case Status::Pending: |
540 | break; |
541 | case Status::Loading: |
542 | m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); |
543 | break; |
544 | case Status::TimedOut: |
545 | m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); |
546 | m_wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut); |
547 | break; |
548 | case Status::Success: |
549 | m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); |
550 | m_wrapper->fontStateChanged(*this, Status::Pending, Status::Success); |
551 | break; |
552 | case Status::Failure: |
553 | m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); |
554 | m_wrapper->fontStateChanged(*this, Status::Pending, Status::Failure); |
555 | break; |
556 | } |
557 | m_mayBePurged = false; |
558 | } |
559 | |
560 | Ref<FontFace> CSSFontFace::wrapper() |
561 | { |
562 | if (m_wrapper) |
563 | return *m_wrapper.get(); |
564 | |
565 | auto wrapper = FontFace::create(*this); |
566 | m_wrapper = makeWeakPtr(wrapper.get()); |
567 | initializeWrapper(); |
568 | return wrapper; |
569 | } |
570 | |
571 | void CSSFontFace::setWrapper(FontFace& newWrapper) |
572 | { |
573 | m_wrapper = makeWeakPtr(newWrapper); |
574 | initializeWrapper(); |
575 | } |
576 | |
577 | void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source) |
578 | { |
579 | m_sources.append(WTFMove(source)); |
580 | |
581 | // We should never add sources in the middle of loading. |
582 | ASSERT(!m_sourcesPopulated); |
583 | } |
584 | |
585 | AllowUserInstalledFonts CSSFontFace::allowUserInstalledFonts() const |
586 | { |
587 | if (m_fontSelector && m_fontSelector->document()) |
588 | return m_fontSelector->document()->settings().shouldAllowUserInstalledFonts() ? AllowUserInstalledFonts::Yes : AllowUserInstalledFonts::No; |
589 | return AllowUserInstalledFonts::Yes; |
590 | } |
591 | |
592 | static Settings::FontLoadTimingOverride fontLoadTimingOverride(CSSFontSelector* fontSelector) |
593 | { |
594 | auto overrideValue = Settings::FontLoadTimingOverride::None; |
595 | if (fontSelector && fontSelector->document()) |
596 | overrideValue = fontSelector->document()->settings().fontLoadTimingOverride(); |
597 | return overrideValue; |
598 | } |
599 | |
600 | auto CSSFontFace::fontLoadTiming() const -> FontLoadTiming |
601 | { |
602 | switch (fontLoadTimingOverride(m_fontSelector.get())) { |
603 | case Settings::FontLoadTimingOverride::None: |
604 | switch (m_loadingBehavior) { |
605 | case FontLoadingBehavior::Auto: |
606 | case FontLoadingBehavior::Block: |
607 | return { 3_s, Seconds::infinity() }; |
608 | case FontLoadingBehavior::Swap: |
609 | return { 0_s, Seconds::infinity() }; |
610 | case FontLoadingBehavior::Fallback: |
611 | return { 0.1_s, 3_s }; |
612 | case FontLoadingBehavior::Optional: |
613 | return { 0.1_s, 0_s }; |
614 | } |
615 | RELEASE_ASSERT_NOT_REACHED(); |
616 | case Settings::FontLoadTimingOverride::Block: |
617 | return { Seconds::infinity(), 0_s }; |
618 | case Settings::FontLoadTimingOverride::Swap: |
619 | return { 0_s, Seconds::infinity() }; |
620 | case Settings::FontLoadTimingOverride::Failure: |
621 | return { 0_s, 0_s }; |
622 | } |
623 | RELEASE_ASSERT_NOT_REACHED(); |
624 | } |
625 | |
626 | void CSSFontFace::setStatus(Status newStatus) |
627 | { |
628 | switch (newStatus) { |
629 | case Status::Pending: |
630 | ASSERT_NOT_REACHED(); |
631 | break; |
632 | case Status::Loading: |
633 | ASSERT(m_status == Status::Pending); |
634 | break; |
635 | case Status::TimedOut: |
636 | ASSERT(m_status == Status::Loading); |
637 | break; |
638 | case Status::Success: |
639 | ASSERT(m_status == Status::Loading || m_status == Status::TimedOut); |
640 | break; |
641 | case Status::Failure: |
642 | ASSERT(m_status == Status::Loading || m_status == Status::TimedOut); |
643 | break; |
644 | } |
645 | |
646 | iterateClients(m_clients, [&](Client& client) { |
647 | client.fontStateChanged(*this, m_status, newStatus); |
648 | }); |
649 | |
650 | m_status = newStatus; |
651 | |
652 | Seconds blockPeriodTimeout; |
653 | Seconds swapPeriodTimeout; |
654 | auto timeouts = fontLoadTiming(); |
655 | blockPeriodTimeout = timeouts.blockPeriod; |
656 | swapPeriodTimeout = timeouts.swapPeriod; |
657 | |
658 | // Transfer across 0-delay timers synchronously. Layouts/script may |
659 | // take arbitrarily long time, and we shouldn't be in a 0-duration |
660 | // state for an arbitrarily long time. Also it's necessary for |
661 | // testing so we don't have a race with the font load. |
662 | switch (newStatus) { |
663 | case Status::Pending: |
664 | ASSERT_NOT_REACHED(); |
665 | break; |
666 | case Status::Loading: |
667 | if (blockPeriodTimeout == 0_s) |
668 | setStatus(Status::TimedOut); |
669 | else if (std::isfinite(blockPeriodTimeout.value())) |
670 | m_timeoutTimer.startOneShot(blockPeriodTimeout); |
671 | break; |
672 | case Status::TimedOut: |
673 | if (swapPeriodTimeout == 0_s) |
674 | setStatus(Status::Failure); |
675 | else if (std::isfinite(swapPeriodTimeout.value())) |
676 | m_timeoutTimer.startOneShot(swapPeriodTimeout); |
677 | break; |
678 | case Status::Success: |
679 | case Status::Failure: |
680 | m_timeoutTimer.stop(); |
681 | break; |
682 | } |
683 | } |
684 | |
685 | void CSSFontFace::fontLoaded(CSSFontFaceSource&) |
686 | { |
687 | Ref<CSSFontFace> protectedThis(*this); |
688 | |
689 | fontLoadEventOccurred(); |
690 | } |
691 | |
692 | bool CSSFontFace::shouldIgnoreFontLoadCompletions() const |
693 | { |
694 | if (m_fontSelector && m_fontSelector->document()) |
695 | return m_fontSelector->document()->settings().shouldIgnoreFontLoadCompletions(); |
696 | return false; |
697 | } |
698 | |
699 | void CSSFontFace::opportunisticallyStartFontDataURLLoading(CSSFontSelector& fontSelector) |
700 | { |
701 | // We don't want to go crazy here and blow the cache. Usually these data URLs are the first item in the src: list, so let's just check that one. |
702 | if (!m_sources.isEmpty()) |
703 | m_sources[0]->opportunisticallyStartFontDataURLLoading(fontSelector); |
704 | } |
705 | |
706 | size_t CSSFontFace::pump(ExternalResourceDownloadPolicy policy) |
707 | { |
708 | if (status() == Status::Failure) |
709 | return 0; |
710 | |
711 | size_t i; |
712 | for (i = 0; i < m_sources.size(); ++i) { |
713 | auto& source = m_sources[i]; |
714 | |
715 | if (source->status() == CSSFontFaceSource::Status::Pending) { |
716 | ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut); |
717 | // This is a little tricky. After calling CSSFontFace::font(Forbid), a font must never fail later in |
718 | // this turn of the runloop because the return value of CSSFontFace::font() shouldn't get nulled out |
719 | // from under an existing FontRanges object. Remote fonts are all downloaded asynchronously, so this |
720 | // isn't a problem for them because CSSFontFace::font() will always return the interstitial font. |
721 | // However, local fonts may synchronously fail when you call load() on them. Therefore, we have to call |
722 | // load() here in order to guarantee that, if the font synchronously fails, it happens now during the |
723 | // first call to CSSFontFace::font() and the FontRanges object sees a consistent view of the |
724 | // CSSFontFace. This means we eagerly create some internal font objects when they may not be needed, |
725 | // but it seems that this behavior is a requirement of the design of FontRanges. FIXME: Perhaps rethink |
726 | // this design. |
727 | if (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()) { |
728 | if (m_status == Status::Pending) |
729 | setStatus(Status::Loading); |
730 | source->load(m_fontSelector.get()); |
731 | } |
732 | } |
733 | |
734 | switch (source->status()) { |
735 | case CSSFontFaceSource::Status::Pending: |
736 | ASSERT(policy == ExternalResourceDownloadPolicy::Forbid); |
737 | return i; |
738 | case CSSFontFaceSource::Status::Loading: |
739 | ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Failure); |
740 | if (m_status == Status::Pending) |
741 | setStatus(Status::Loading); |
742 | return i; |
743 | case CSSFontFaceSource::Status::Success: |
744 | ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Success || m_status == Status::Failure); |
745 | if (m_status == Status::Pending) |
746 | setStatus(Status::Loading); |
747 | if (m_status == Status::Loading || m_status == Status::TimedOut) |
748 | setStatus(Status::Success); |
749 | return i; |
750 | case CSSFontFaceSource::Status::Failure: |
751 | if (m_status == Status::Pending) |
752 | setStatus(Status::Loading); |
753 | break; |
754 | } |
755 | } |
756 | if (m_sources.isEmpty() && m_status == Status::Pending) |
757 | setStatus(Status::Loading); |
758 | if (m_status == Status::Loading || m_status == Status::TimedOut) |
759 | setStatus(Status::Failure); |
760 | return m_sources.size(); |
761 | } |
762 | |
763 | void CSSFontFace::load() |
764 | { |
765 | pump(ExternalResourceDownloadPolicy::Allow); |
766 | } |
767 | |
768 | static Font::Visibility visibility(CSSFontFace::Status status, CSSFontFace::FontLoadTiming timing) |
769 | { |
770 | switch (status) { |
771 | case CSSFontFace::Status::Pending: |
772 | return timing.blockPeriod == 0_s ? Font::Visibility::Visible : Font::Visibility::Invisible; |
773 | case CSSFontFace::Status::Loading: |
774 | return Font::Visibility::Invisible; |
775 | case CSSFontFace::Status::TimedOut: |
776 | case CSSFontFace::Status::Failure: |
777 | case CSSFontFace::Status::Success: |
778 | default: |
779 | return Font::Visibility::Visible; |
780 | } |
781 | } |
782 | |
783 | RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic, ExternalResourceDownloadPolicy policy) |
784 | { |
785 | if (computeFailureState()) |
786 | return nullptr; |
787 | |
788 | Ref<CSSFontFace> protectedThis(*this); |
789 | |
790 | // Our status is derived from the first non-failed source. However, this source may |
791 | // return null from font(), which means we need to continue looping through the remainder |
792 | // of the sources to try to find a font to use. These subsequent tries should not affect |
793 | // our own state, though. |
794 | size_t startIndex = pump(policy); |
795 | |
796 | if (computeFailureState()) |
797 | return nullptr; |
798 | |
799 | for (size_t i = startIndex; i < m_sources.size(); ++i) { |
800 | auto& source = m_sources[i]; |
801 | if (source->status() == CSSFontFaceSource::Status::Pending && (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource())) |
802 | source->load(m_fontSelector.get()); |
803 | |
804 | switch (source->status()) { |
805 | case CSSFontFaceSource::Status::Pending: |
806 | case CSSFontFaceSource::Status::Loading: { |
807 | Font::Visibility visibility = WebCore::visibility(status(), fontLoadTiming()); |
808 | return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), Font::Origin::Local, Font::Interstitial::Yes, visibility); |
809 | } |
810 | case CSSFontFaceSource::Status::Success: |
811 | if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings, m_fontSelectionCapabilities)) |
812 | return result; |
813 | break; |
814 | case CSSFontFaceSource::Status::Failure: |
815 | break; |
816 | } |
817 | } |
818 | |
819 | return nullptr; |
820 | } |
821 | |
822 | bool CSSFontFace::purgeable() const |
823 | { |
824 | return cssConnection() && m_mayBePurged; |
825 | } |
826 | |
827 | void CSSFontFace::updateStyleIfNeeded() |
828 | { |
829 | if (m_fontSelector && m_fontSelector->document()) |
830 | m_fontSelector->document()->updateStyleIfNeeded(); |
831 | } |
832 | |
833 | #if ENABLE(SVG_FONTS) |
834 | bool CSSFontFace::hasSVGFontFaceSource() const |
835 | { |
836 | size_t size = m_sources.size(); |
837 | for (size_t i = 0; i < size; i++) { |
838 | if (m_sources[i]->isSVGFontFaceSource()) |
839 | return true; |
840 | } |
841 | return false; |
842 | } |
843 | #endif |
844 | |
845 | } |
846 | |