blob: c731791dedc7bd94811e493fda6be5056f3a024f [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
2 * Copyright (C) 2013 Google 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 are met:
6 *
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. AND ITS CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
23 * DAMAGE.
24 */
25
26#include "config.h"
27#include "core/css/FontLoader.h"
28
29#include "bindings/v8/Dictionary.h"
30#include "core/css/CSSFontFaceLoadEvent.h"
31#include "core/css/CSSFontFaceSource.h"
32#include "core/css/CSSFontSelector.h"
33#include "core/css/CSSParser.h"
34#include "core/css/CSSSegmentedFontFace.h"
35#include "core/css/StylePropertySet.h"
Torne (Richard Coles)81a51572013-05-13 16:52:28 +010036#include "core/css/resolver/StyleResolver.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010037#include "core/dom/Document.h"
38#include "core/page/FrameView.h"
39
40namespace WebCore {
41
42static const int defaultFontSize = 10;
43static const char* const defaultFontFamily = "sans-serif";
44
45class LoadFontCallback : public CSSSegmentedFontFace::LoadFontCallback {
46public:
47 static PassRefPtr<LoadFontCallback> create(int numLoading, PassRefPtr<VoidCallback> loadCallback, PassRefPtr<VoidCallback> errorCallback)
48 {
49 return adoptRef<LoadFontCallback>(new LoadFontCallback(numLoading, loadCallback, errorCallback));
50 }
51
52 static PassRefPtr<LoadFontCallback> createFromParams(const Dictionary& params, const FontFamily& family)
53 {
54 RefPtr<VoidCallback> onsuccess;
55 RefPtr<VoidCallback> onerror;
56 params.get("onsuccess", onsuccess);
57 params.get("onerror", onerror);
58 if (!onsuccess && !onerror)
59 return 0;
60 int numFamilies = 0;
61 for (const FontFamily* f = &family; f; f = f->next())
62 numFamilies++;
63 return LoadFontCallback::create(numFamilies, onsuccess, onerror);
64 }
65
66 virtual void notifyLoaded(CSSSegmentedFontFace*) OVERRIDE;
67 virtual void notifyError(CSSSegmentedFontFace*) OVERRIDE;
68 void loaded(Document*);
69 void error(Document*);
70private:
71 LoadFontCallback(int numLoading, PassRefPtr<VoidCallback> loadCallback, PassRefPtr<VoidCallback> errorCallback)
72 : m_numLoading(numLoading)
73 , m_errorOccured(false)
74 , m_loadCallback(loadCallback)
75 , m_errorCallback(errorCallback)
76 { }
77
78 int m_numLoading;
79 bool m_errorOccured;
80 RefPtr<VoidCallback> m_loadCallback;
81 RefPtr<VoidCallback> m_errorCallback;
82};
83
84void LoadFontCallback::loaded(Document* document)
85{
86 m_numLoading--;
87 if (m_numLoading || !document)
88 return;
89
90 if (m_errorOccured) {
91 if (m_errorCallback)
92 document->fontloader()->scheduleCallback(m_errorCallback.release());
93 } else {
94 if (m_loadCallback)
95 document->fontloader()->scheduleCallback(m_loadCallback.release());
96 }
97}
98
99void LoadFontCallback::error(Document* document)
100{
101 m_errorOccured = true;
102 loaded(document);
103}
104
105void LoadFontCallback::notifyLoaded(CSSSegmentedFontFace* face)
106{
107 loaded(face->fontSelector()->document());
108}
109
110void LoadFontCallback::notifyError(CSSSegmentedFontFace* face)
111{
112 error(face->fontSelector()->document());
113}
114
115FontLoader::FontLoader(Document* document)
116 : ActiveDOMObject(document)
117 , m_document(document)
118 , m_loadingCount(0)
119 , m_timer(this, &FontLoader::timerFired)
120{
121 suspendIfNeeded();
122}
123
124FontLoader::~FontLoader()
125{
126}
127
128EventTargetData* FontLoader::eventTargetData()
129{
130 return &m_eventTargetData;
131}
132
133EventTargetData* FontLoader::ensureEventTargetData()
134{
135 return &m_eventTargetData;
136}
137
138const AtomicString& FontLoader::interfaceName() const
139{
140 return eventNames().interfaceForFontLoader;
141}
142
143ScriptExecutionContext* FontLoader::scriptExecutionContext() const
144{
145 return ActiveDOMObject::scriptExecutionContext();
146}
147
148void FontLoader::didLayout()
149{
150 if (m_loadingCount || (!m_pendingDoneEvent && m_fontsReadyCallbacks.isEmpty()))
151 return;
152 if (!m_timer.isActive())
153 m_timer.startOneShot(0);
154}
155
156void FontLoader::timerFired(Timer<FontLoader>*)
157{
158 firePendingEvents();
159 firePendingCallbacks();
160 fireDoneEventIfPossible();
161}
162
163void FontLoader::scheduleEvent(PassRefPtr<Event> event)
164{
165 m_pendingEvents.append(event);
166 if (!m_timer.isActive())
167 m_timer.startOneShot(0);
168}
169
170void FontLoader::firePendingEvents()
171{
172 if (m_pendingEvents.isEmpty())
173 return;
174
175 Vector<RefPtr<Event> > pendingEvents;
176 m_pendingEvents.swap(pendingEvents);
177 for (size_t index = 0; index < pendingEvents.size(); ++index)
178 dispatchEvent(pendingEvents[index].release());
179}
180
181void FontLoader::scheduleCallback(PassRefPtr<VoidCallback> callback)
182{
183 m_pendingCallbacks.append(callback);
184 if (!m_timer.isActive())
185 m_timer.startOneShot(0);
186}
187
188void FontLoader::firePendingCallbacks()
189{
190 if (m_pendingCallbacks.isEmpty())
191 return;
192
193 Vector<RefPtr<VoidCallback> > pendingCallbacks;
194 m_pendingCallbacks.swap(pendingCallbacks);
195 for (size_t index = 0; index < pendingCallbacks.size(); ++index)
196 pendingCallbacks[index]->handleEvent();
197}
198
199void FontLoader::beginFontLoading(CSSFontFaceRule* rule)
200{
201 ++m_loadingCount;
202 if (m_loadingCount == 1 && !m_pendingDoneEvent)
203 scheduleEvent(CSSFontFaceLoadEvent::createForFontFaceRule(eventNames().loadingEvent, rule));
204 scheduleEvent(CSSFontFaceLoadEvent::createForFontFaceRule(eventNames().loadstartEvent, rule));
205 m_pendingDoneEvent.clear();
206}
207
208void FontLoader::fontLoaded(CSSFontFaceRule* rule)
209{
210 scheduleEvent(CSSFontFaceLoadEvent::createForFontFaceRule(eventNames().loadEvent, rule));
211 queueDoneEvent(rule);
212}
213
214void FontLoader::loadError(CSSFontFaceRule* rule, CSSFontFaceSource* source)
215{
216 // FIXME: We should report NetworkError in case of timeout, etc.
217 String errorName = (source && source->isDecodeError()) ? "InvalidFontDataError" : ExceptionCodeDescription(NOT_FOUND_ERR).name;
218 scheduleEvent(CSSFontFaceLoadEvent::createForError(rule, DOMError::create(errorName)));
219 queueDoneEvent(rule);
220}
221
222void FontLoader::queueDoneEvent(CSSFontFaceRule* rule)
223{
224 ASSERT(m_loadingCount > 0);
225 --m_loadingCount;
226 if (!m_loadingCount) {
227 ASSERT(!m_pendingDoneEvent);
228 m_pendingDoneEvent = CSSFontFaceLoadEvent::createForFontFaceRule(eventNames().loadingdoneEvent, rule);
229 }
230}
231
232void FontLoader::notifyWhenFontsReady(PassRefPtr<VoidCallback> callback)
233{
234 m_fontsReadyCallbacks.append(callback);
235 if (!m_timer.isActive())
236 m_timer.startOneShot(0);
237}
238
239void FontLoader::fireDoneEventIfPossible()
240{
241 if (!m_pendingEvents.isEmpty() || !m_pendingCallbacks.isEmpty())
242 return;
243 if (m_loadingCount || (!m_pendingDoneEvent && m_fontsReadyCallbacks.isEmpty()))
244 return;
245
246 if (FrameView* view = m_document->view()) {
247 if (view->needsLayout())
248 return;
249 m_document->updateStyleIfNeeded();
250 if (view->needsLayout())
251 return;
252 }
253
254 if (m_pendingDoneEvent)
255 dispatchEvent(m_pendingDoneEvent.release());
256
257 if (!m_fontsReadyCallbacks.isEmpty()) {
258 Vector<RefPtr<VoidCallback> > callbacks;
259 m_fontsReadyCallbacks.swap(callbacks);
260 for (size_t index = 0; index < callbacks.size(); ++index)
261 callbacks[index]->handleEvent();
262 }
263}
264
265void FontLoader::loadFont(const Dictionary& params)
266{
267 // FIXME: The text member of params is ignored.
268 String fontString;
269 if (!params.get("font", fontString))
270 return;
271 Font font;
272 if (!resolveFontStyle(fontString, font))
273 return;
274 RefPtr<LoadFontCallback> callback = LoadFontCallback::createFromParams(params, font.family());
275
276 for (const FontFamily* f = &font.family(); f; f = f->next()) {
277 CSSSegmentedFontFace* face = m_document->styleResolver()->fontSelector()->getFontFace(font.fontDescription(), f->family());
278 if (!face) {
279 if (callback)
280 callback->error(m_document);
281 continue;
282 }
283 face->loadFont(font.fontDescription(), callback);
284 }
285}
286
287bool FontLoader::checkFont(const String& fontString, const String&)
288{
289 // FIXME: The second parameter (text) is ignored.
290 Font font;
291 if (!resolveFontStyle(fontString, font))
292 return false;
293 for (const FontFamily* f = &font.family(); f; f = f->next()) {
294 CSSSegmentedFontFace* face = m_document->styleResolver()->fontSelector()->getFontFace(font.fontDescription(), f->family());
295 if (!face || !face->checkFont())
296 return false;
297 }
298 return true;
299}
300
301static void applyPropertyToCurrentStyle(StyleResolver* styleResolver, CSSPropertyID id, const RefPtr<StylePropertySet>& parsedStyle)
302{
303 styleResolver->applyPropertyToCurrentStyle(id, parsedStyle->getPropertyCSSValue(id).get());
304}
305
306bool FontLoader::resolveFontStyle(const String& fontString, Font& font)
307{
308 // Interpret fontString in the same way as the 'font' attribute of CanvasRenderingContext2D.
309 RefPtr<StylePropertySet> parsedStyle = StylePropertySet::create();
310 CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, fontString, true, CSSStrictMode, 0);
311 if (parsedStyle->isEmpty())
312 return false;
313
314 String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
315 if (fontValue == "inherit" || fontValue == "initial")
316 return false;
317
318 RefPtr<RenderStyle> style = RenderStyle::create();
319
320 FontFamily fontFamily;
321 fontFamily.setFamily(defaultFontFamily);
322
323 FontDescription defaultFontDescription;
324 defaultFontDescription.setFamily(fontFamily);
325 defaultFontDescription.setSpecifiedSize(defaultFontSize);
326 defaultFontDescription.setComputedSize(defaultFontSize);
327
328 style->setFontDescription(defaultFontDescription);
329
330 style->font().update(style->font().fontSelector());
331
332 // Now map the font property longhands into the style.
333 StyleResolver* styleResolver = m_document->styleResolver();
334 styleResolver->applyPropertyToStyle(CSSPropertyFontFamily, parsedStyle->getPropertyCSSValue(CSSPropertyFontFamily).get(), style.get());
335 applyPropertyToCurrentStyle(styleResolver, CSSPropertyFontStyle, parsedStyle);
336 applyPropertyToCurrentStyle(styleResolver, CSSPropertyFontVariant, parsedStyle);
337 applyPropertyToCurrentStyle(styleResolver, CSSPropertyFontWeight, parsedStyle);
338
339 // As described in BUG66291, setting font-size and line-height on a font may entail a CSSPrimitiveValue::computeLengthDouble call,
340 // which assumes the fontMetrics are available for the affected font, otherwise a crash occurs (see http://trac.webkit.org/changeset/96122).
341 // The updateFont() calls below update the fontMetrics and ensure the proper setting of font-size and line-height.
342 styleResolver->updateFont();
343 applyPropertyToCurrentStyle(styleResolver, CSSPropertyFontSize, parsedStyle);
344 styleResolver->updateFont();
345 applyPropertyToCurrentStyle(styleResolver, CSSPropertyLineHeight, parsedStyle);
346
347 font = style->font();
348 font.update(styleResolver->fontSelector());
349 return true;
350}
351
352} // namespace WebCore