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 "CSSFontFaceSet.h"
28
29#include "CSSFontFaceSource.h"
30#include "CSSFontFamily.h"
31#include "CSSFontSelector.h"
32#include "CSSFontStyleValue.h"
33#include "CSSParser.h"
34#include "CSSPrimitiveValue.h"
35#include "CSSSegmentedFontFace.h"
36#include "CSSValueList.h"
37#include "CSSValuePool.h"
38#include "FontCache.h"
39#include "StyleBuilderConverter.h"
40#include "StyleProperties.h"
41
42namespace WebCore {
43
44CSSFontFaceSet::CSSFontFaceSet(CSSFontSelector* owningFontSelector)
45 : m_owningFontSelector(makeWeakPtr(owningFontSelector))
46{
47}
48
49CSSFontFaceSet::~CSSFontFaceSet()
50{
51 for (auto& face : m_faces)
52 face->removeClient(*this);
53
54 for (auto& pair : m_locallyInstalledFacesLookupTable) {
55 for (auto& face : pair.value)
56 face->removeClient(*this);
57 }
58}
59
60void CSSFontFaceSet::addClient(CSSFontFaceSetClient& client)
61{
62 m_clients.add(&client);
63}
64
65void CSSFontFaceSet::removeClient(CSSFontFaceSetClient& client)
66{
67 ASSERT(m_clients.contains(&client));
68 m_clients.remove(&client);
69}
70
71void CSSFontFaceSet::incrementActiveCount()
72{
73 ++m_activeCount;
74 if (m_activeCount == 1) {
75 m_status = Status::Loading;
76 for (auto* client : m_clients)
77 client->startedLoading();
78 }
79}
80
81void CSSFontFaceSet::decrementActiveCount()
82{
83 --m_activeCount;
84 if (!m_activeCount) {
85 m_status = Status::Loaded;
86 for (auto* client : m_clients)
87 client->completedLoading();
88 }
89}
90
91bool CSSFontFaceSet::hasFace(const CSSFontFace& face) const
92{
93 for (auto& myFace : m_faces) {
94 if (myFace.ptr() == &face)
95 return true;
96 }
97
98 return false;
99}
100
101void CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered(const String& familyName)
102{
103 ASSERT(m_owningFontSelector);
104 if (m_locallyInstalledFacesLookupTable.contains(familyName))
105 return;
106
107 AllowUserInstalledFonts allowUserInstalledFonts = AllowUserInstalledFonts::Yes;
108 if (m_owningFontSelector->document())
109 allowUserInstalledFonts = m_owningFontSelector->document()->settings().shouldAllowUserInstalledFonts() ? AllowUserInstalledFonts::Yes : AllowUserInstalledFonts::No;
110 Vector<FontSelectionCapabilities> capabilities = FontCache::singleton().getFontSelectionCapabilitiesInFamily(familyName, allowUserInstalledFonts);
111 if (capabilities.isEmpty())
112 return;
113
114 Vector<Ref<CSSFontFace>> faces;
115 for (auto item : capabilities) {
116 Ref<CSSFontFace> face = CSSFontFace::create(m_owningFontSelector.get(), nullptr, nullptr, true);
117
118 Ref<CSSValueList> familyList = CSSValueList::createCommaSeparated();
119 familyList->append(CSSValuePool::singleton().createFontFamilyValue(familyName));
120 face->setFamilies(familyList.get());
121 face->setFontSelectionCapabilities(item);
122 face->adoptSource(std::make_unique<CSSFontFaceSource>(face.get(), familyName));
123 ASSERT(!face->computeFailureState());
124 faces.append(WTFMove(face));
125 }
126 m_locallyInstalledFacesLookupTable.add(familyName, WTFMove(faces));
127}
128
129String CSSFontFaceSet::familyNameFromPrimitive(const CSSPrimitiveValue& value)
130{
131 if (value.isFontFamily())
132 return value.fontFamily().familyName;
133 if (!value.isValueID())
134 return { };
135
136 // We need to use the raw text for all the generic family types, since @font-face is a way of actually
137 // defining what font to use for those types.
138 switch (value.valueID()) {
139 case CSSValueSerif:
140 return serifFamily.get();
141 case CSSValueSansSerif:
142 return sansSerifFamily.get();
143 case CSSValueCursive:
144 return cursiveFamily.get();
145 case CSSValueFantasy:
146 return fantasyFamily.get();
147 case CSSValueMonospace:
148 return monospaceFamily.get();
149 case CSSValueWebkitPictograph:
150 return pictographFamily.get();
151 case CSSValueSystemUi:
152 return systemUiFamily.get();
153 default:
154 return { };
155 }
156}
157
158void CSSFontFaceSet::addToFacesLookupTable(CSSFontFace& face)
159{
160 if (!face.families())
161 return;
162
163 for (auto& item : *face.families()) {
164 String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
165 if (familyName.isEmpty())
166 continue;
167
168 auto addResult = m_facesLookupTable.add(familyName, Vector<Ref<CSSFontFace>>());
169 auto& familyFontFaces = addResult.iterator->value;
170 if (addResult.isNewEntry) {
171 // m_locallyInstalledFontFaces grows without bound, eventually encorporating every font installed on the system.
172 // This is by design.
173 if (m_owningFontSelector)
174 ensureLocalFontFacesForFamilyRegistered(familyName);
175 familyFontFaces = { };
176 }
177
178 familyFontFaces.append(face);
179 }
180}
181
182void CSSFontFaceSet::add(CSSFontFace& face)
183{
184 ASSERT(!hasFace(face));
185
186 for (auto* client : m_clients)
187 client->fontModified();
188
189 face.addClient(*this);
190 m_cache.clear();
191
192 if (face.cssConnection())
193 m_faces.insert(m_facesPartitionIndex++, face);
194 else
195 m_faces.append(face);
196
197 addToFacesLookupTable(face);
198
199 if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
200 incrementActiveCount();
201
202 if (face.cssConnection()) {
203 ASSERT(!m_constituentCSSConnections.contains(face.cssConnection()));
204 m_constituentCSSConnections.add(face.cssConnection(), &face);
205 }
206}
207
208void CSSFontFaceSet::removeFromFacesLookupTable(const CSSFontFace& face, const CSSValueList& familiesToSearchFor)
209{
210 for (auto& item : familiesToSearchFor) {
211 String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
212 if (familyName.isEmpty())
213 continue;
214
215 auto iterator = m_facesLookupTable.find(familyName);
216 ASSERT(iterator != m_facesLookupTable.end());
217 bool found = false;
218 for (size_t i = 0; i < iterator->value.size(); ++i) {
219 if (iterator->value[i].ptr() == &face) {
220 found = true;
221 iterator->value.remove(i);
222 break;
223 }
224 }
225 ASSERT_UNUSED(found, found);
226 if (!iterator->value.size())
227 m_facesLookupTable.remove(iterator);
228 }
229}
230
231void CSSFontFaceSet::remove(const CSSFontFace& face)
232{
233 auto protect = makeRef(face);
234
235 m_cache.clear();
236
237 for (auto* client : m_clients)
238 client->fontModified();
239
240 if (face.families())
241 removeFromFacesLookupTable(face, *face.families());
242
243 if (face.cssConnection()) {
244 ASSERT(m_constituentCSSConnections.get(face.cssConnection()) == &face);
245 m_constituentCSSConnections.remove(face.cssConnection());
246 }
247
248 for (size_t i = 0; i < m_faces.size(); ++i) {
249 if (m_faces[i].ptr() == &face) {
250 if (i < m_facesPartitionIndex)
251 --m_facesPartitionIndex;
252 m_faces[i]->removeClient(*this);
253 m_faces.remove(i);
254 if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
255 decrementActiveCount();
256 return;
257 }
258 }
259 ASSERT_NOT_REACHED();
260}
261
262CSSFontFace* CSSFontFaceSet::lookUpByCSSConnection(StyleRuleFontFace& target)
263{
264 return m_constituentCSSConnections.get(&target);
265}
266
267void CSSFontFaceSet::purge()
268{
269 Vector<Ref<CSSFontFace>> toRemove;
270 for (auto& face : m_faces) {
271 if (face->purgeable())
272 toRemove.append(face.copyRef());
273 }
274
275 for (auto& item : toRemove)
276 remove(item.get());
277}
278
279void CSSFontFaceSet::emptyCaches()
280{
281 m_cache.clear();
282}
283
284void CSSFontFaceSet::clear()
285{
286 for (auto& face : m_faces)
287 face->removeClient(*this);
288 m_faces.clear();
289 m_facesLookupTable.clear();
290 m_locallyInstalledFacesLookupTable.clear();
291 m_cache.clear();
292 m_constituentCSSConnections.clear();
293 m_facesPartitionIndex = 0;
294 m_status = Status::Loaded;
295}
296
297CSSFontFace& CSSFontFaceSet::operator[](size_t i)
298{
299 ASSERT(i < faceCount());
300 return m_faces[i];
301}
302
303static FontSelectionRequest computeFontSelectionRequest(MutableStyleProperties& style)
304{
305 RefPtr<CSSValue> weightValue = style.getPropertyCSSValue(CSSPropertyFontWeight).get();
306 if (!weightValue)
307 weightValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
308
309 RefPtr<CSSValue> stretchValue = style.getPropertyCSSValue(CSSPropertyFontStretch).get();
310 if (!stretchValue)
311 stretchValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
312
313 RefPtr<CSSValue> styleValue = style.getPropertyCSSValue(CSSPropertyFontStyle).get();
314 if (!styleValue)
315 styleValue = CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueNormal));
316
317 auto weightSelectionValue = StyleBuilderConverter::convertFontWeightFromValue(*weightValue);
318 auto stretchSelectionValue = StyleBuilderConverter::convertFontStretchFromValue(*stretchValue);
319 auto styleSelectionValue = StyleBuilderConverter::convertFontStyleFromValue(*styleValue);
320
321 return { weightSelectionValue, stretchSelectionValue, styleSelectionValue };
322}
323
324static HashSet<UChar32> codePointsFromString(StringView stringView)
325{
326 HashSet<UChar32> result;
327 auto graphemeClusters = stringView.graphemeClusters();
328 for (auto cluster : graphemeClusters) {
329 ASSERT(cluster.length() > 0);
330 UChar32 character = 0;
331 if (cluster.is8Bit())
332 character = cluster[0];
333 else
334 U16_GET(cluster.characters16(), 0, 0, cluster.length(), character);
335 result.add(character);
336 }
337 return result;
338}
339
340ExceptionOr<Vector<std::reference_wrapper<CSSFontFace>>> CSSFontFaceSet::matchingFacesExcludingPreinstalledFonts(const String& font, const String& string)
341{
342 auto style = MutableStyleProperties::create();
343 auto parseResult = CSSParser::parseValue(style, CSSPropertyFont, font, true, HTMLStandardMode);
344 if (parseResult == CSSParser::ParseResult::Error)
345 return Exception { SyntaxError };
346
347 FontSelectionRequest request = computeFontSelectionRequest(style.get());
348
349 auto family = style->getPropertyCSSValue(CSSPropertyFontFamily);
350 if (!is<CSSValueList>(family))
351 return Exception { SyntaxError };
352 CSSValueList& familyList = downcast<CSSValueList>(*family);
353
354 HashSet<AtomicString> uniqueFamilies;
355 Vector<AtomicString> familyOrder;
356 for (auto& family : familyList) {
357 auto& primitive = downcast<CSSPrimitiveValue>(family.get());
358 if (!primitive.isFontFamily())
359 continue;
360 if (uniqueFamilies.add(primitive.fontFamily().familyName).isNewEntry)
361 familyOrder.append(primitive.fontFamily().familyName);
362 }
363
364 HashSet<CSSFontFace*> resultConstituents;
365 for (auto codePoint : codePointsFromString(string)) {
366 bool found = false;
367 for (auto& family : familyOrder) {
368 auto* faces = fontFace(request, family);
369 if (!faces)
370 continue;
371 for (auto& constituentFace : faces->constituentFaces()) {
372 if (constituentFace->isLocalFallback())
373 continue;
374 if (constituentFace->rangesMatchCodePoint(codePoint)) {
375 resultConstituents.add(constituentFace.ptr());
376 found = true;
377 break;
378 }
379 }
380 if (found)
381 break;
382 }
383 }
384
385 Vector<std::reference_wrapper<CSSFontFace>> result;
386 result.reserveInitialCapacity(resultConstituents.size());
387 for (auto* constituent : resultConstituents)
388 result.uncheckedAppend(*constituent);
389 return result;
390}
391
392ExceptionOr<bool> CSSFontFaceSet::check(const String& font, const String& text)
393{
394 auto matchingFaces = this->matchingFacesExcludingPreinstalledFonts(font, text);
395 if (matchingFaces.hasException())
396 return matchingFaces.releaseException();
397
398 for (auto& face : matchingFaces.releaseReturnValue()) {
399 if (face.get().status() == CSSFontFace::Status::Pending)
400 return false;
401 }
402 return true;
403}
404
405CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontSelectionRequest request, const AtomicString& family)
406{
407 auto iterator = m_facesLookupTable.find(family);
408 if (iterator == m_facesLookupTable.end())
409 return nullptr;
410 auto& familyFontFaces = iterator->value;
411
412 auto& segmentedFontFaceCache = m_cache.add(family, FontSelectionHashMap()).iterator->value;
413
414 auto& face = segmentedFontFaceCache.add(request, nullptr).iterator->value;
415 if (face)
416 return face.get();
417
418 face = CSSSegmentedFontFace::create();
419
420 Vector<std::reference_wrapper<CSSFontFace>, 32> candidateFontFaces;
421 for (int i = familyFontFaces.size() - 1; i >= 0; --i) {
422 CSSFontFace& candidate = familyFontFaces[i];
423 auto capabilities = candidate.fontSelectionCapabilities();
424 if (!isItalic(request.slope) && isItalic(capabilities.slope.minimum))
425 continue;
426 candidateFontFaces.append(candidate);
427 }
428
429 auto localIterator = m_locallyInstalledFacesLookupTable.find(family);
430 if (localIterator != m_locallyInstalledFacesLookupTable.end()) {
431 for (auto& candidate : localIterator->value) {
432 auto capabilities = candidate->fontSelectionCapabilities();
433 if (!isItalic(request.slope) && isItalic(capabilities.slope.minimum))
434 continue;
435 candidateFontFaces.append(candidate);
436 }
437 }
438
439 if (!candidateFontFaces.isEmpty()) {
440 Vector<FontSelectionCapabilities> capabilities;
441 capabilities.reserveInitialCapacity(candidateFontFaces.size());
442 for (auto& face : candidateFontFaces)
443 capabilities.uncheckedAppend(face.get().fontSelectionCapabilities());
444 FontSelectionAlgorithm fontSelectionAlgorithm(request, capabilities);
445 std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [&fontSelectionAlgorithm](const CSSFontFace& first, const CSSFontFace& second) {
446 auto firstCapabilities = first.fontSelectionCapabilities();
447 auto secondCapabilities = second.fontSelectionCapabilities();
448
449 auto stretchDistanceFirst = fontSelectionAlgorithm.stretchDistance(firstCapabilities).distance;
450 auto stretchDistanceSecond = fontSelectionAlgorithm.stretchDistance(secondCapabilities).distance;
451 if (stretchDistanceFirst < stretchDistanceSecond)
452 return true;
453 if (stretchDistanceFirst > stretchDistanceSecond)
454 return false;
455
456 auto styleDistanceFirst = fontSelectionAlgorithm.styleDistance(firstCapabilities).distance;
457 auto styleDistanceSecond = fontSelectionAlgorithm.styleDistance(secondCapabilities).distance;
458 if (styleDistanceFirst < styleDistanceSecond)
459 return true;
460 if (styleDistanceFirst > styleDistanceSecond)
461 return false;
462
463 auto weightDistanceFirst = fontSelectionAlgorithm.weightDistance(firstCapabilities).distance;
464 auto weightDistanceSecond = fontSelectionAlgorithm.weightDistance(secondCapabilities).distance;
465 if (weightDistanceFirst < weightDistanceSecond)
466 return true;
467 return false;
468 });
469 for (auto& candidate : candidateFontFaces)
470 face->appendFontFace(candidate.get());
471 }
472
473 return face.get();
474}
475
476void CSSFontFaceSet::fontStateChanged(CSSFontFace& face, CSSFontFace::Status oldState, CSSFontFace::Status newState)
477{
478 ASSERT(hasFace(face));
479 if (oldState == CSSFontFace::Status::Pending) {
480 ASSERT(newState == CSSFontFace::Status::Loading);
481 incrementActiveCount();
482 }
483 if (newState == CSSFontFace::Status::Success || newState == CSSFontFace::Status::Failure) {
484 ASSERT(oldState == CSSFontFace::Status::Loading || oldState == CSSFontFace::Status::TimedOut);
485 for (auto* client : m_clients)
486 client->faceFinished(face, newState);
487 decrementActiveCount();
488 }
489}
490
491void CSSFontFaceSet::fontPropertyChanged(CSSFontFace& face, CSSValueList* oldFamilies)
492{
493 m_cache.clear();
494
495 if (oldFamilies) {
496 removeFromFacesLookupTable(face, *oldFamilies);
497 addToFacesLookupTable(face);
498 }
499
500 for (auto* client : m_clients)
501 client->fontModified();
502}
503
504}
505