[Cocoa] Add SPI to disallow user-installed fonts
[WebKit-https.git] / Source / WebCore / css / CSSFontFaceSet.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 "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
42 namespace WebCore {
43
44 CSSFontFaceSet::CSSFontFaceSet(CSSFontSelector* owningFontSelector)
45     : m_owningFontSelector(owningFontSelector)
46 {
47 }
48
49 CSSFontFaceSet::~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
60 void CSSFontFaceSet::addClient(CSSFontFaceSetClient& client)
61 {
62     m_clients.add(&client);
63 }
64
65 void CSSFontFaceSet::removeClient(CSSFontFaceSetClient& client)
66 {
67     ASSERT(m_clients.contains(&client));
68     m_clients.remove(&client);
69 }
70
71 void 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
81 void 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
91 bool 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
101 void CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered(const String& familyName)
102 {
103     ASSERT(m_owningFontSelector);
104     if (m_locallyInstalledFacesLookupTable.contains(familyName))
105         return;
106
107     FontCache::AllowUserInstalledFonts allowUserInstalledFonts = FontCache::AllowUserInstalledFonts::Yes;
108     if (m_owningFontSelector->document() && m_owningFontSelector->document()->settings().shouldDisallowUserInstalledFonts())
109         allowUserInstalledFonts = FontCache::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(nullptr, 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
129 String 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
158 void 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
182 void 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
208 void 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
231 void CSSFontFaceSet::remove(const CSSFontFace& face)
232 {
233     m_cache.clear();
234
235     for (auto* client : m_clients)
236         client->fontModified();
237
238     if (face.families())
239         removeFromFacesLookupTable(face, *face.families());
240
241     if (face.cssConnection()) {
242         ASSERT(m_constituentCSSConnections.get(face.cssConnection()) == &face);
243         m_constituentCSSConnections.remove(face.cssConnection());
244     }
245
246     for (size_t i = 0; i < m_faces.size(); ++i) {
247         if (m_faces[i].ptr() == &face) {
248             if (i < m_facesPartitionIndex)
249                 --m_facesPartitionIndex;
250             m_faces[i]->removeClient(*this);
251             m_faces.remove(i);
252             if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
253                 decrementActiveCount();
254             return;
255         }
256     }
257     ASSERT_NOT_REACHED();
258 }
259
260 CSSFontFace* CSSFontFaceSet::lookUpByCSSConnection(StyleRuleFontFace& target)
261 {
262     return m_constituentCSSConnections.get(&target);
263 }
264
265 void CSSFontFaceSet::purge()
266 {
267     Vector<Ref<CSSFontFace>> toRemove;
268     for (auto& face : m_faces) {
269         if (face->purgeable())
270             toRemove.append(face.copyRef());
271     }
272
273     for (auto& item : toRemove)
274         remove(item.get());
275 }
276
277 void CSSFontFaceSet::emptyCaches()
278 {
279     m_cache.clear();
280 }
281
282 void CSSFontFaceSet::clear()
283 {
284     for (auto& face : m_faces)
285         face->removeClient(*this);
286     m_faces.clear();
287     m_facesLookupTable.clear();
288     m_locallyInstalledFacesLookupTable.clear();
289     m_cache.clear();
290     m_constituentCSSConnections.clear();
291     m_facesPartitionIndex = 0;
292     m_status = Status::Loaded;
293 }
294
295 CSSFontFace& CSSFontFaceSet::operator[](size_t i)
296 {
297     ASSERT(i < faceCount());
298     return m_faces[i];
299 }
300
301 static FontSelectionRequest computeFontSelectionRequest(MutableStyleProperties& style)
302 {
303     RefPtr<CSSValue> weightValue = style.getPropertyCSSValue(CSSPropertyFontWeight).get();
304     if (!weightValue)
305         weightValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
306
307     RefPtr<CSSValue> stretchValue = style.getPropertyCSSValue(CSSPropertyFontStretch).get();
308     if (!stretchValue)
309         stretchValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
310
311     RefPtr<CSSValue> styleValue = style.getPropertyCSSValue(CSSPropertyFontStyle).get();
312     if (!styleValue)
313         styleValue = CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueNormal));
314
315     FontSelectionValue weightSelectionValue = StyleBuilderConverter::convertFontWeightFromValue(*weightValue);
316     FontSelectionValue stretchSelectionValue = StyleBuilderConverter::convertFontStretchFromValue(*stretchValue);
317     FontSelectionValue styleSelectionValue = StyleBuilderConverter::convertFontStyleFromValue(*styleValue);
318
319     return { weightSelectionValue, stretchSelectionValue, styleSelectionValue };
320 }
321
322 static HashSet<UChar32> codePointsFromString(StringView stringView)
323 {
324     HashSet<UChar32> result;
325     auto graphemeClusters = stringView.graphemeClusters();
326     for (auto cluster : graphemeClusters) {
327         ASSERT(cluster.length() > 0);
328         UChar32 character = 0;
329         if (cluster.is8Bit())
330             character = cluster[0];
331         else
332             U16_GET(cluster.characters16(), 0, 0, cluster.length(), character);
333         result.add(character);
334     }
335     return result;
336 }
337
338 ExceptionOr<Vector<std::reference_wrapper<CSSFontFace>>> CSSFontFaceSet::matchingFacesExcludingPreinstalledFonts(const String& font, const String& string)
339 {
340     auto style = MutableStyleProperties::create();
341     auto parseResult = CSSParser::parseValue(style, CSSPropertyFont, font, true, HTMLStandardMode);
342     if (parseResult == CSSParser::ParseResult::Error)
343         return Exception { SyntaxError };
344
345     FontSelectionRequest request = computeFontSelectionRequest(style.get());
346
347     auto family = style->getPropertyCSSValue(CSSPropertyFontFamily);
348     if (!is<CSSValueList>(family))
349         return Exception { SyntaxError };
350     CSSValueList& familyList = downcast<CSSValueList>(*family);
351
352     HashSet<AtomicString> uniqueFamilies;
353     Vector<AtomicString> familyOrder;
354     for (auto& family : familyList) {
355         auto& primitive = downcast<CSSPrimitiveValue>(family.get());
356         if (!primitive.isFontFamily())
357             continue;
358         if (uniqueFamilies.add(primitive.fontFamily().familyName).isNewEntry)
359             familyOrder.append(primitive.fontFamily().familyName);
360     }
361
362     HashSet<CSSFontFace*> resultConstituents;
363     for (auto codePoint : codePointsFromString(string)) {
364         bool found = false;
365         for (auto& family : familyOrder) {
366             auto* faces = fontFace(request, family);
367             if (!faces)
368                 continue;
369             for (auto& constituentFace : faces->constituentFaces()) {
370                 if (constituentFace->isLocalFallback())
371                     continue;
372                 if (constituentFace->rangesMatchCodePoint(codePoint)) {
373                     resultConstituents.add(constituentFace.ptr());
374                     found = true;
375                     break;
376                 }
377             }
378             if (found)
379                 break;
380         }
381     }
382
383     Vector<std::reference_wrapper<CSSFontFace>> result;
384     result.reserveInitialCapacity(resultConstituents.size());
385     for (auto* constituent : resultConstituents)
386         result.uncheckedAppend(*constituent);
387     return WTFMove(result);
388 }
389
390 ExceptionOr<bool> CSSFontFaceSet::check(const String& font, const String& text)
391 {
392     auto matchingFaces = this->matchingFacesExcludingPreinstalledFonts(font, text);
393     if (matchingFaces.hasException())
394         return matchingFaces.releaseException();
395
396     for (auto& face : matchingFaces.releaseReturnValue()) {
397         if (face.get().status() == CSSFontFace::Status::Pending)
398             return false;
399     }
400     return true;
401 }
402
403 CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontSelectionRequest request, const AtomicString& family)
404 {
405     auto iterator = m_facesLookupTable.find(family);
406     if (iterator == m_facesLookupTable.end())
407         return nullptr;
408     auto& familyFontFaces = iterator->value;
409
410     auto& segmentedFontFaceCache = m_cache.add(family, FontSelectionHashMap()).iterator->value;
411
412     auto& face = segmentedFontFaceCache.add(request, nullptr).iterator->value;
413     if (face)
414         return face.get();
415
416     face = CSSSegmentedFontFace::create();
417
418     Vector<std::reference_wrapper<CSSFontFace>, 32> candidateFontFaces;
419     for (int i = familyFontFaces.size() - 1; i >= 0; --i) {
420         CSSFontFace& candidate = familyFontFaces[i];
421         auto capabilities = candidate.fontSelectionCapabilities();
422         if (!isItalic(request.slope) && isItalic(capabilities.slope.minimum))
423             continue;
424         candidateFontFaces.append(candidate);
425     }
426
427     auto localIterator = m_locallyInstalledFacesLookupTable.find(family);
428     if (localIterator != m_locallyInstalledFacesLookupTable.end()) {
429         for (auto& candidate : localIterator->value) {
430             auto capabilities = candidate->fontSelectionCapabilities();
431             if (!isItalic(request.slope) && isItalic(capabilities.slope.minimum))
432                 continue;
433             candidateFontFaces.append(candidate);
434         }
435     }
436
437     if (!candidateFontFaces.isEmpty()) {
438         Vector<FontSelectionCapabilities> capabilities;
439         capabilities.reserveInitialCapacity(candidateFontFaces.size());
440         for (auto& face : candidateFontFaces)
441             capabilities.uncheckedAppend(face.get().fontSelectionCapabilities());
442         FontSelectionAlgorithm fontSelectionAlgorithm(request, capabilities);
443         std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [&fontSelectionAlgorithm](const CSSFontFace& first, const CSSFontFace& second) {
444             auto firstCapabilities = first.fontSelectionCapabilities();
445             auto secondCapabilities = second.fontSelectionCapabilities();
446
447             auto stretchDistanceFirst = fontSelectionAlgorithm.stretchDistance(firstCapabilities).distance;
448             auto stretchDistanceSecond = fontSelectionAlgorithm.stretchDistance(secondCapabilities).distance;
449             if (stretchDistanceFirst < stretchDistanceSecond)
450                 return true;
451             if (stretchDistanceFirst > stretchDistanceSecond)
452                 return false;
453
454             auto styleDistanceFirst = fontSelectionAlgorithm.styleDistance(firstCapabilities).distance;
455             auto styleDistanceSecond = fontSelectionAlgorithm.styleDistance(secondCapabilities).distance;
456             if (styleDistanceFirst < styleDistanceSecond)
457                 return true;
458             if (styleDistanceFirst > styleDistanceSecond)
459                 return false;
460
461             auto weightDistanceFirst = fontSelectionAlgorithm.weightDistance(firstCapabilities).distance;
462             auto weightDistanceSecond = fontSelectionAlgorithm.weightDistance(secondCapabilities).distance;
463             if (weightDistanceFirst < weightDistanceSecond)
464                 return true;
465             return false;
466         });
467         for (auto& candidate : candidateFontFaces)
468             face->appendFontFace(candidate.get());
469     }
470
471     return face.get();
472 }
473
474 void CSSFontFaceSet::fontStateChanged(CSSFontFace& face, CSSFontFace::Status oldState, CSSFontFace::Status newState)
475 {
476     ASSERT(hasFace(face));
477     if (oldState == CSSFontFace::Status::Pending) {
478         ASSERT(newState == CSSFontFace::Status::Loading);
479         incrementActiveCount();
480     }
481     if (newState == CSSFontFace::Status::Success || newState == CSSFontFace::Status::Failure) {
482         ASSERT(oldState == CSSFontFace::Status::Loading || oldState == CSSFontFace::Status::TimedOut);
483         for (auto* client : m_clients)
484             client->faceFinished(face, newState);
485         decrementActiveCount();
486     }
487 }
488
489 void CSSFontFaceSet::fontPropertyChanged(CSSFontFace& face, CSSValueList* oldFamilies)
490 {
491     m_cache.clear();
492
493     if (oldFamilies) {
494         removeFromFacesLookupTable(face, *oldFamilies);
495         addToFacesLookupTable(face);
496     }
497
498     for (auto* client : m_clients)
499         client->fontModified();
500 }
501
502 }