a0f5fd65304d2965d673f22349e3e6256997ba83
[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 "CSSFontFamily.h"
30 #include "CSSFontSelector.h"
31 #include "CSSParser.h"
32 #include "CSSPrimitiveValue.h"
33 #include "CSSValueList.h"
34 #include "StyleProperties.h"
35
36 namespace WebCore {
37
38 CSSFontFaceSet::CSSFontFaceSet(CSSFontFaceSetClient& client)
39     : m_client(client)
40 {
41 }
42
43 CSSFontFaceSet::~CSSFontFaceSet()
44 {
45     for (auto& face : m_faces)
46         face->removeClient(*this);
47 }
48
49 void CSSFontFaceSet::incrementActiveCount()
50 {
51     ++m_activeCount;
52     if (m_activeCount == 1) {
53         m_status = Status::Loading;
54         m_client.startedLoading();
55     }
56 }
57
58 void CSSFontFaceSet::decrementActiveCount()
59 {
60     --m_activeCount;
61     if (!m_activeCount) {
62         m_status = Status::Loaded;
63         m_client.completedLoading();
64     }
65 }
66
67 bool CSSFontFaceSet::hasFace(const CSSFontFace& face) const
68 {
69     for (auto& myFace : m_faces) {
70         if (myFace.ptr() == &face)
71             return true;
72     }
73     return false;
74 }
75
76 void CSSFontFaceSet::add(CSSFontFace& face)
77 {
78     ASSERT(!hasFace(face));
79
80     m_faces.append(face);
81     face.addClient(*this);
82     if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
83         incrementActiveCount();
84 }
85
86 void CSSFontFaceSet::remove(const CSSFontFace& face)
87 {
88     for (size_t i = 0; i < m_faces.size(); ++i) {
89         if (m_faces[i].ptr() == &face) {
90             m_faces[i]->removeClient(*this);
91             m_faces.remove(i);
92             if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
93                 decrementActiveCount();
94             return;
95         }
96     }
97     ASSERT_NOT_REACHED();
98 }
99
100 static HashSet<String> extractFamilies(const CSSValueList& list)
101 {
102     HashSet<String> result;
103     for (auto& family : list) {
104         const CSSPrimitiveValue& primitive = downcast<CSSPrimitiveValue>(family.get());
105         if (!primitive.isFontFamily())
106             continue;
107         result.add(primitive.fontFamily().familyName);
108     }
109     return result;
110 }
111
112 static bool familiesIntersect(const CSSFontFace& face, const CSSValueList& request)
113 {
114     if (!face.families())
115         return false;
116
117     HashSet<String> faceFamilies = extractFamilies(*face.families());
118     HashSet<String> requestFamilies = extractFamilies(request);
119     for (auto& family1 : faceFamilies) {
120         if (requestFamilies.contains(family1))
121             return true;
122     }
123     return false;
124 }
125
126 Vector<std::reference_wrapper<CSSFontFace>> CSSFontFaceSet::matchingFaces(const String& font, const String&, ExceptionCode& ec)
127 {
128     Vector<std::reference_wrapper<CSSFontFace>> result;
129     Ref<MutableStyleProperties> style = MutableStyleProperties::create();
130     auto parseResult = CSSParser::parseValue(style.ptr(), CSSPropertyFont, font, true, CSSStrictMode, nullptr);
131     if (parseResult == CSSParser::ParseResult::Error) {
132         ec = SYNTAX_ERR;
133         return result;
134     }
135     bool desiredStyleIsNormal = true;
136     if (RefPtr<CSSValue> desiredStyle = style->getPropertyCSSValue(CSSPropertyFontStyle)) {
137         if (!is<CSSPrimitiveValue>(*desiredStyle)) {
138             ec = SYNTAX_ERR;
139             return result;
140         }
141         desiredStyleIsNormal = downcast<CSSPrimitiveValue>(*desiredStyle).getValueID() == CSSValueNormal;
142     }
143     RefPtr<CSSValue> family = style->getPropertyCSSValue(CSSPropertyFontFamily);
144     if (!is<CSSValueList>(family.get())) {
145         ec = SYNTAX_ERR;
146         return result;
147     }
148     CSSValueList& familyList = downcast<CSSValueList>(*family);
149
150     // Match CSSFontSelector::getFontFace()
151     for (auto& face : m_faces) {
152         if (!familiesIntersect(face, familyList) || (desiredStyleIsNormal && !(face->traitsMask() & FontStyleNormalMask)))
153             continue;
154         result.append(face.get());
155     }
156     return result;
157 }
158
159 void CSSFontFaceSet::load(const String& font, const String& text, ExceptionCode& ec)
160 {
161     auto matchingFaces = this->matchingFaces(font, text, ec);
162     if (ec)
163         return;
164
165     for (auto& face : matchingFaces)
166         face.get().load();
167 }
168
169 bool CSSFontFaceSet::check(const String& font, const String& text, ExceptionCode& ec)
170 {
171     auto matchingFaces = this->matchingFaces(font, text, ec);
172     if (ec)
173         return false;
174
175     for (auto& face : matchingFaces) {
176         if (face.get().status() == CSSFontFace::Status::Pending)
177             return false;
178     }
179     return true;
180 }
181
182 void CSSFontFaceSet::stateChanged(CSSFontFace& face, CSSFontFace::Status oldState, CSSFontFace::Status newState)
183 {
184     ASSERT(hasFace(face));
185     if (oldState == CSSFontFace::Status::Pending) {
186         ASSERT(newState == CSSFontFace::Status::Loading);
187         incrementActiveCount();
188     }
189     if (newState == CSSFontFace::Status::Success || newState == CSSFontFace::Status::Failure) {
190         ASSERT(oldState == CSSFontFace::Status::Loading || oldState == CSSFontFace::Status::TimedOut);
191         m_client.faceFinished(face, newState);
192         decrementActiveCount();
193     }
194 }
195
196 }