Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / FontFace.cpp
1 /*
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
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "core/css/FontFace.h"
33
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "bindings/core/v8/ScriptState.h"
36 #include "core/CSSValueKeywords.h"
37 #include "core/css/BinaryDataFontFaceSource.h"
38 #include "core/css/CSSFontFace.h"
39 #include "core/css/CSSFontFaceSrcValue.h"
40 #include "core/css/CSSFontSelector.h"
41 #include "core/css/CSSPrimitiveValue.h"
42 #include "core/css/CSSUnicodeRangeValue.h"
43 #include "core/css/CSSValueList.h"
44 #include "core/css/FontFaceDescriptors.h"
45 #include "core/css/LocalFontFaceSource.h"
46 #include "core/css/RemoteFontFaceSource.h"
47 #include "core/css/StylePropertySet.h"
48 #include "core/css/StyleRule.h"
49 #include "core/css/parser/CSSParser.h"
50 #include "core/dom/DOMException.h"
51 #include "core/dom/Document.h"
52 #include "core/dom/ExceptionCode.h"
53 #include "core/dom/StyleEngine.h"
54 #include "core/frame/LocalFrame.h"
55 #include "core/frame/Settings.h"
56 #include "core/frame/UseCounter.h"
57 #include "core/svg/SVGFontFaceElement.h"
58 #include "core/svg/SVGFontFaceSource.h"
59 #include "core/svg/SVGRemoteFontFaceSource.h"
60 #include "platform/FontFamilyNames.h"
61 #include "platform/SharedBuffer.h"
62
63 namespace blink {
64
65 static PassRefPtrWillBeRawPtr<CSSValue> parseCSSValue(const Document* document, const String& s, CSSPropertyID propertyID)
66 {
67     CSSParserContext context(*document, UseCounter::getFrom(document));
68     return CSSParser::parseSingleValue(propertyID, s, context);
69 }
70
71 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, const String& source, const FontFaceDescriptors& descriptors)
72 {
73     RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors));
74
75     RefPtrWillBeRawPtr<CSSValue> src = parseCSSValue(toDocument(context), source, CSSPropertySrc);
76     if (!src || !src->isValueList())
77         fontFace->setError(DOMException::create(SyntaxError, "The source provided ('" + source + "') could not be parsed as a value list."));
78
79     fontFace->initCSSFontFace(toDocument(context), src);
80     return fontFace.release();
81 }
82
83 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBuffer> source, const FontFaceDescriptors& descriptors)
84 {
85     RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors));
86     fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->data()), source->byteLength());
87     return fontFace.release();
88 }
89
90 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBufferView> source, const FontFaceDescriptors& descriptors)
91 {
92     RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors));
93     fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->baseAddress()), source->byteLength());
94     return fontFace.release();
95 }
96
97 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(Document* document, const StyleRuleFontFace* fontFaceRule)
98 {
99     const StylePropertySet& properties = fontFaceRule->properties();
100
101     // Obtain the font-family property and the src property. Both must be defined.
102     RefPtrWillBeRawPtr<CSSValue> family = properties.getPropertyCSSValue(CSSPropertyFontFamily);
103     if (!family || !family->isValueList())
104         return nullptr;
105     RefPtrWillBeRawPtr<CSSValue> src = properties.getPropertyCSSValue(CSSPropertySrc);
106     if (!src || !src->isValueList())
107         return nullptr;
108
109     RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(document));
110
111     if (fontFace->setFamilyValue(toCSSValueList(family.get()))
112         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStyle)
113         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontWeight)
114         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStretch)
115         && fontFace->setPropertyFromStyle(properties, CSSPropertyUnicodeRange)
116         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontVariant)
117         && fontFace->setPropertyFromStyle(properties, CSSPropertyWebkitFontFeatureSettings)
118         && !fontFace->family().isEmpty()
119         && fontFace->traits().bitfield()) {
120         fontFace->initCSSFontFace(document, src);
121         return fontFace.release();
122     }
123     return nullptr;
124 }
125
126 FontFace::FontFace(ExecutionContext* context)
127     : ActiveDOMObject(context)
128     , m_status(Unloaded)
129 {
130     suspendIfNeeded();
131 }
132
133 FontFace::FontFace(ExecutionContext* context, const AtomicString& family, const FontFaceDescriptors& descriptors)
134     : ActiveDOMObject(context)
135     , m_family(family)
136     , m_status(Unloaded)
137 {
138     Document* document = toDocument(context);
139     setPropertyFromString(document, descriptors.style(), CSSPropertyFontStyle);
140     setPropertyFromString(document, descriptors.weight(), CSSPropertyFontWeight);
141     // FIXME: we don't implement 'font-strech' property yet so we can't set the property.
142     setPropertyFromString(document, descriptors.unicodeRange(), CSSPropertyUnicodeRange);
143     setPropertyFromString(document, descriptors.variant(), CSSPropertyFontVariant);
144     setPropertyFromString(document, descriptors.featureSettings(), CSSPropertyWebkitFontFeatureSettings);
145
146     suspendIfNeeded();
147 }
148
149 FontFace::~FontFace()
150 {
151 }
152
153 String FontFace::style() const
154 {
155     return m_style ? m_style->cssText() : "normal";
156 }
157
158 String FontFace::weight() const
159 {
160     return m_weight ? m_weight->cssText() : "normal";
161 }
162
163 String FontFace::stretch() const
164 {
165     return m_stretch ? m_stretch->cssText() : "normal";
166 }
167
168 String FontFace::unicodeRange() const
169 {
170     return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF";
171 }
172
173 String FontFace::variant() const
174 {
175     return m_variant ? m_variant->cssText() : "normal";
176 }
177
178 String FontFace::featureSettings() const
179 {
180     return m_featureSettings ? m_featureSettings->cssText() : "normal";
181 }
182
183 void FontFace::setStyle(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
184 {
185     setPropertyFromString(toDocument(context), s, CSSPropertyFontStyle, &exceptionState);
186 }
187
188 void FontFace::setWeight(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
189 {
190     setPropertyFromString(toDocument(context), s, CSSPropertyFontWeight, &exceptionState);
191 }
192
193 void FontFace::setStretch(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
194 {
195     setPropertyFromString(toDocument(context), s, CSSPropertyFontStretch, &exceptionState);
196 }
197
198 void FontFace::setUnicodeRange(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
199 {
200     setPropertyFromString(toDocument(context), s, CSSPropertyUnicodeRange, &exceptionState);
201 }
202
203 void FontFace::setVariant(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
204 {
205     setPropertyFromString(toDocument(context), s, CSSPropertyFontVariant, &exceptionState);
206 }
207
208 void FontFace::setFeatureSettings(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
209 {
210     setPropertyFromString(toDocument(context), s, CSSPropertyWebkitFontFeatureSettings, &exceptionState);
211 }
212
213 void FontFace::setPropertyFromString(const Document* document, const String& s, CSSPropertyID propertyID, ExceptionState* exceptionState)
214 {
215     RefPtrWillBeRawPtr<CSSValue> value = parseCSSValue(document, s, propertyID);
216     if (value && setPropertyValue(value, propertyID))
217         return;
218
219     String message = "Failed to set '" + s + "' as a property value.";
220     if (exceptionState)
221         exceptionState->throwDOMException(SyntaxError, message);
222     else
223         setError(DOMException::create(SyntaxError, message));
224 }
225
226 bool FontFace::setPropertyFromStyle(const StylePropertySet& properties, CSSPropertyID propertyID)
227 {
228     return setPropertyValue(properties.getPropertyCSSValue(propertyID), propertyID);
229 }
230
231 bool FontFace::setPropertyValue(PassRefPtrWillBeRawPtr<CSSValue> value, CSSPropertyID propertyID)
232 {
233     switch (propertyID) {
234     case CSSPropertyFontStyle:
235         m_style = value;
236         break;
237     case CSSPropertyFontWeight:
238         m_weight = value;
239         break;
240     case CSSPropertyFontStretch:
241         m_stretch = value;
242         break;
243     case CSSPropertyUnicodeRange:
244         if (value && !value->isValueList())
245             return false;
246         m_unicodeRange = value;
247         break;
248     case CSSPropertyFontVariant:
249         m_variant = value;
250         break;
251     case CSSPropertyWebkitFontFeatureSettings:
252         m_featureSettings = value;
253         break;
254     default:
255         ASSERT_NOT_REACHED();
256         return false;
257     }
258     return true;
259 }
260
261 bool FontFace::setFamilyValue(CSSValueList* familyList)
262 {
263     // The font-family descriptor has to have exactly one family name.
264     if (familyList->length() != 1)
265         return false;
266
267     CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->item(0));
268     AtomicString family;
269     if (familyValue->isString()) {
270         family = AtomicString(familyValue->getStringValue());
271     } else if (familyValue->isValueID()) {
272         // We need to use the raw text for all the generic family types, since @font-face is a way of actually
273         // defining what font to use for those types.
274         switch (familyValue->getValueID()) {
275         case CSSValueSerif:
276             family =  FontFamilyNames::webkit_serif;
277             break;
278         case CSSValueSansSerif:
279             family =  FontFamilyNames::webkit_sans_serif;
280             break;
281         case CSSValueCursive:
282             family =  FontFamilyNames::webkit_cursive;
283             break;
284         case CSSValueFantasy:
285             family =  FontFamilyNames::webkit_fantasy;
286             break;
287         case CSSValueMonospace:
288             family =  FontFamilyNames::webkit_monospace;
289             break;
290         case CSSValueWebkitPictograph:
291             family =  FontFamilyNames::webkit_pictograph;
292             break;
293         default:
294             return false;
295         }
296     }
297     m_family = family;
298     return true;
299 }
300
301 String FontFace::status() const
302 {
303     switch (m_status) {
304     case Unloaded:
305         return "unloaded";
306     case Loading:
307         return "loading";
308     case Loaded:
309         return "loaded";
310     case Error:
311         return "error";
312     default:
313         ASSERT_NOT_REACHED();
314     }
315     return emptyString();
316 }
317
318 void FontFace::setLoadStatus(LoadStatus status)
319 {
320     m_status = status;
321     ASSERT(m_status != Error || m_error);
322
323     if (m_status == Loaded || m_status == Error) {
324         if (m_loadedProperty) {
325             if (m_status == Loaded)
326                 m_loadedProperty->resolve(this);
327             else
328                 m_loadedProperty->reject(m_error.get());
329         }
330
331         WillBeHeapVector<RefPtrWillBeMember<LoadFontCallback> > callbacks;
332         m_callbacks.swap(callbacks);
333         for (size_t i = 0; i < callbacks.size(); ++i) {
334             if (m_status == Loaded)
335                 callbacks[i]->notifyLoaded(this);
336             else
337                 callbacks[i]->notifyError(this);
338         }
339     }
340 }
341
342 void FontFace::setError(PassRefPtrWillBeRawPtr<DOMException> error)
343 {
344     if (!m_error)
345         m_error = error ? error : DOMException::create(NetworkError);
346     setLoadStatus(Error);
347 }
348
349 ScriptPromise FontFace::fontStatusPromise(ScriptState* scriptState)
350 {
351     if (!m_loadedProperty) {
352         m_loadedProperty = new LoadedProperty(scriptState->executionContext(), this, LoadedProperty::Loaded);
353         if (m_status == Loaded)
354             m_loadedProperty->resolve(this);
355         else if (m_status == Error)
356             m_loadedProperty->reject(m_error.get());
357     }
358     return m_loadedProperty->promise(scriptState->world());
359 }
360
361 ScriptPromise FontFace::load(ScriptState* scriptState)
362 {
363     loadInternal(scriptState->executionContext());
364     return fontStatusPromise(scriptState);
365 }
366
367 void FontFace::loadWithCallback(PassRefPtrWillBeRawPtr<LoadFontCallback> callback, ExecutionContext* context)
368 {
369     loadInternal(context);
370     if (m_status == Loaded)
371         callback->notifyLoaded(this);
372     else if (m_status == Error)
373         callback->notifyError(this);
374     else
375         m_callbacks.append(callback);
376 }
377
378 void FontFace::loadInternal(ExecutionContext* context)
379 {
380     if (m_status != Unloaded)
381         return;
382
383     m_cssFontFace->load();
384     toDocument(context)->styleEngine()->fontSelector()->fontLoader()->loadPendingFonts();
385 }
386
387 FontTraits FontFace::traits() const
388 {
389     FontStyle style = FontStyleNormal;
390     if (m_style) {
391         if (!m_style->isPrimitiveValue())
392             return 0;
393
394         switch (toCSSPrimitiveValue(m_style.get())->getValueID()) {
395         case CSSValueNormal:
396             style = FontStyleNormal;
397             break;
398         case CSSValueItalic:
399         case CSSValueOblique:
400             style = FontStyleItalic;
401             break;
402         default:
403             break;
404         }
405     }
406
407     FontWeight weight = FontWeight400;
408     if (m_weight) {
409         if (!m_weight->isPrimitiveValue())
410             return 0;
411
412         switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) {
413         case CSSValueBold:
414         case CSSValue700:
415             weight = FontWeight700;
416             break;
417         case CSSValueNormal:
418         case CSSValue400:
419             weight = FontWeight400;
420             break;
421         case CSSValue900:
422             weight = FontWeight900;
423             break;
424         case CSSValue800:
425             weight = FontWeight800;
426             break;
427         case CSSValue600:
428             weight = FontWeight600;
429             break;
430         case CSSValue500:
431             weight = FontWeight500;
432             break;
433         case CSSValue300:
434             weight = FontWeight300;
435             break;
436         case CSSValue200:
437             weight = FontWeight200;
438             break;
439         case CSSValue100:
440             weight = FontWeight100;
441             break;
442         // Although 'lighter' and 'bolder' are valid keywords for font-weights, they are invalid
443         // inside font-face rules so they are ignored. Reference: http://www.w3.org/TR/css3-fonts/#descdef-font-weight.
444         case CSSValueLighter:
445         case CSSValueBolder:
446             break;
447         default:
448             ASSERT_NOT_REACHED();
449             break;
450         }
451     }
452
453     FontVariant variant = FontVariantNormal;
454     if (RefPtrWillBeRawPtr<CSSValue> fontVariant = m_variant) {
455         // font-variant descriptor can be a value list.
456         if (fontVariant->isPrimitiveValue()) {
457             RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
458             list->append(fontVariant);
459             fontVariant = list;
460         } else if (!fontVariant->isValueList()) {
461             return 0;
462         }
463
464         CSSValueList* variantList = toCSSValueList(fontVariant.get());
465         unsigned numVariants = variantList->length();
466         if (!numVariants)
467             return 0;
468
469         for (unsigned i = 0; i < numVariants; ++i) {
470             switch (toCSSPrimitiveValue(variantList->item(i))->getValueID()) {
471             case CSSValueNormal:
472                 variant = FontVariantNormal;
473                 break;
474             case CSSValueSmallCaps:
475                 variant = FontVariantSmallCaps;
476                 break;
477             default:
478                 break;
479             }
480         }
481     }
482
483     return FontTraits(style, variant, weight, FontStretchNormal);
484 }
485
486 static PassOwnPtrWillBeRawPtr<CSSFontFace> createCSSFontFace(FontFace* fontFace, CSSValue* unicodeRange)
487 {
488     Vector<CSSFontFace::UnicodeRange> ranges;
489     if (CSSValueList* rangeList = toCSSValueList(unicodeRange)) {
490         unsigned numRanges = rangeList->length();
491         for (unsigned i = 0; i < numRanges; i++) {
492             CSSUnicodeRangeValue* range = toCSSUnicodeRangeValue(rangeList->item(i));
493             ranges.append(CSSFontFace::UnicodeRange(range->from(), range->to()));
494         }
495     }
496
497     return adoptPtrWillBeNoop(new CSSFontFace(fontFace, ranges));
498 }
499
500 void FontFace::initCSSFontFace(Document* document, PassRefPtrWillBeRawPtr<CSSValue> src)
501 {
502     m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get());
503     if (m_error)
504         return;
505
506     // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
507     ASSERT(src);
508     ASSERT(src->isValueList());
509     CSSValueList* srcList = toCSSValueList(src.get());
510     int srcLength = srcList->length();
511
512     bool foundSVGFont = false;
513
514     for (int i = 0; i < srcLength; i++) {
515         // An item in the list either specifies a string (local font name) or a URL (remote font to download).
516         CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->item(i));
517         OwnPtrWillBeRawPtr<CSSFontFaceSource> source = nullptr;
518
519 #if ENABLE(SVG_FONTS)
520         foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
521 #endif
522         if (!item->isLocal()) {
523             Settings* settings = document ? document->frame() ? document->frame()->settings() : 0 : 0;
524             bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
525             if (allowDownloading && item->isSupportedFormat() && document) {
526                 FontResource* fetched = item->fetch(document);
527                 if (fetched) {
528                     FontLoader* fontLoader = document->styleEngine()->fontSelector()->fontLoader();
529
530 #if ENABLE(SVG_FONTS)
531                     if (foundSVGFont) {
532                         source = adoptPtrWillBeNoop(new SVGRemoteFontFaceSource(item->resource(), fetched, fontLoader));
533                     } else
534 #endif
535                     {
536                         source = adoptPtrWillBeNoop(new RemoteFontFaceSource(fetched, fontLoader));
537                     }
538                 }
539             }
540         } else {
541 #if ENABLE(SVG_FONTS)
542             if (item->svgFontFaceElement()) {
543                 RefPtrWillBeRawPtr<SVGFontFaceElement> fontfaceElement = item->svgFontFaceElement();
544                 // SVGFontFaceSource assumes that it is the case where <font-face> element resides in the same document.
545                 // We put a RELEASE_ASSERT here as it will cause UAF if the assumption is false.
546                 RELEASE_ASSERT(fontfaceElement->inDocument());
547                 RELEASE_ASSERT(fontfaceElement->document() == document);
548                 source = adoptPtrWillBeNoop(new SVGFontFaceSource(fontfaceElement.get()));
549             } else
550 #endif
551             {
552                 source = adoptPtrWillBeNoop(new LocalFontFaceSource(item->resource()));
553             }
554         }
555
556         if (source)
557             m_cssFontFace->addSource(source.release());
558     }
559 }
560
561 void FontFace::initCSSFontFace(const unsigned char* data, unsigned size)
562 {
563     m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get());
564     if (m_error)
565         return;
566
567     RefPtr<SharedBuffer> buffer = SharedBuffer::create(data, size);
568     OwnPtrWillBeRawPtr<BinaryDataFontFaceSource> source = adoptPtrWillBeNoop(new BinaryDataFontFaceSource(buffer.get()));
569     if (source->isValid())
570         setLoadStatus(Loaded);
571     else
572         setError(DOMException::create(SyntaxError, "Invalid font data in ArrayBuffer."));
573     m_cssFontFace->addSource(source.release());
574 }
575
576 void FontFace::trace(Visitor* visitor)
577 {
578     visitor->trace(m_src);
579     visitor->trace(m_style);
580     visitor->trace(m_weight);
581     visitor->trace(m_stretch);
582     visitor->trace(m_unicodeRange);
583     visitor->trace(m_variant);
584     visitor->trace(m_featureSettings);
585     visitor->trace(m_error);
586     visitor->trace(m_loadedProperty);
587     visitor->trace(m_cssFontFace);
588     visitor->trace(m_callbacks);
589 }
590
591 bool FontFace::hadBlankText() const
592 {
593     return m_cssFontFace->hadBlankText();
594 }
595
596 bool FontFace::hasPendingActivity() const
597 {
598     return m_status == Loading && executionContext() && !executionContext()->activeDOMObjectsAreStopped();
599 }
600
601 } // namespace blink