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 "CSSValueKeywords.h"
35#include "FontFamilyNames.h"
36#include "bindings/v8/Dictionary.h"
37#include "bindings/v8/ExceptionState.h"
38#include "bindings/v8/ScriptPromiseResolver.h"
39#include "bindings/v8/ScriptScope.h"
40#include "bindings/v8/ScriptState.h"
41#include "core/css/CSSFontFace.h"
42#include "core/css/CSSFontFaceSrcValue.h"
43#include "core/css/CSSParser.h"
44#include "core/css/CSSPrimitiveValue.h"
45#include "core/css/CSSUnicodeRangeValue.h"
46#include "core/css/CSSValueList.h"
47#include "core/css/StylePropertySet.h"
48#include "core/css/StyleRule.h"
49#include "core/dom/DOMError.h"
50#include "core/dom/Document.h"
51#include "core/dom/ExceptionCode.h"
52#include "core/frame/Frame.h"
53#include "core/frame/Settings.h"
54#include "core/svg/SVGFontFaceElement.h"
55#include "platform/fonts/FontDescription.h"
56#include "platform/fonts/FontTraitsMask.h"
57#include "platform/fonts/SimpleFontData.h"
58
59namespace WebCore {
60
61class FontFaceReadyPromiseResolver {
62public:
63    static PassOwnPtr<FontFaceReadyPromiseResolver> create(ScriptPromise promise, ExecutionContext* context)
64    {
65        return adoptPtr(new FontFaceReadyPromiseResolver(promise, context));
66    }
67
68    void resolve(PassRefPtr<FontFace> fontFace)
69    {
70        ScriptScope scope(m_scriptState);
71        switch (fontFace->loadStatus()) {
72        case FontFace::Loaded:
73            m_resolver->resolve(fontFace);
74            break;
75        case FontFace::Error:
76            m_resolver->reject(DOMError::create(NetworkError));
77            break;
78        default:
79            ASSERT_NOT_REACHED();
80        }
81    }
82
83private:
84    FontFaceReadyPromiseResolver(ScriptPromise promise, ExecutionContext* context)
85        : m_scriptState(ScriptState::current())
86        , m_resolver(ScriptPromiseResolver::create(promise, context))
87    { }
88    ScriptState* m_scriptState;
89    RefPtr<ScriptPromiseResolver> m_resolver;
90};
91
92static PassRefPtr<CSSValue> parseCSSValue(const String& s, CSSPropertyID propertyID)
93{
94    if (s.isEmpty())
95        return 0;
96    RefPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create();
97    CSSParser::parseValue(parsedStyle.get(), propertyID, s, true, HTMLStandardMode, 0);
98    return parsedStyle->getPropertyCSSValue(propertyID);
99}
100
101PassRefPtr<FontFace> FontFace::create(const AtomicString& family, const String& source, const Dictionary& descriptors, ExceptionState& exceptionState)
102{
103    RefPtr<CSSValue> src = parseCSSValue(source, CSSPropertySrc);
104    if (!src || !src->isValueList()) {
105        exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
106        return 0;
107    }
108
109    RefPtr<FontFace> fontFace = adoptRef<FontFace>(new FontFace(src));
110    fontFace->setFamily(family, exceptionState);
111    if (exceptionState.hadException())
112        return 0;
113
114    String value;
115    if (descriptors.get("style", value)) {
116        fontFace->setStyle(value, exceptionState);
117        if (exceptionState.hadException())
118            return 0;
119    }
120    if (descriptors.get("weight", value)) {
121        fontFace->setWeight(value, exceptionState);
122        if (exceptionState.hadException())
123            return 0;
124    }
125    if (descriptors.get("stretch", value)) {
126        fontFace->setStretch(value, exceptionState);
127        if (exceptionState.hadException())
128            return 0;
129    }
130    if (descriptors.get("unicodeRange", value)) {
131        fontFace->setUnicodeRange(value, exceptionState);
132        if (exceptionState.hadException())
133            return 0;
134    }
135    if (descriptors.get("variant", value)) {
136        fontFace->setVariant(value, exceptionState);
137        if (exceptionState.hadException())
138            return 0;
139    }
140    if (descriptors.get("featureSettings", value)) {
141        fontFace->setFeatureSettings(value, exceptionState);
142        if (exceptionState.hadException())
143            return 0;
144    }
145
146    return fontFace;
147}
148
149PassRefPtr<FontFace> FontFace::create(const StyleRuleFontFace* fontFaceRule)
150{
151    const StylePropertySet* properties = fontFaceRule->properties();
152
153    // Obtain the font-family property and the src property. Both must be defined.
154    RefPtr<CSSValue> family = properties->getPropertyCSSValue(CSSPropertyFontFamily);
155    if (!family || !family->isValueList())
156        return 0;
157    RefPtr<CSSValue> src = properties->getPropertyCSSValue(CSSPropertySrc);
158    if (!src || !src->isValueList())
159        return 0;
160
161    RefPtr<FontFace> fontFace = adoptRef<FontFace>(new FontFace(src));
162
163    if (fontFace->setFamilyValue(toCSSValueList(family.get()))
164        && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStyle)
165        && fontFace->setPropertyFromStyle(properties, CSSPropertyFontWeight)
166        && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStretch)
167        && fontFace->setPropertyFromStyle(properties, CSSPropertyUnicodeRange)
168        && fontFace->setPropertyFromStyle(properties, CSSPropertyFontVariant)
169        && fontFace->setPropertyFromStyle(properties, CSSPropertyWebkitFontFeatureSettings))
170        return fontFace;
171    return 0;
172}
173
174FontFace::FontFace(PassRefPtr<CSSValue> src)
175    : m_src(src)
176    , m_status(Unloaded)
177    , m_cssFontFace(0)
178{
179}
180
181FontFace::~FontFace()
182{
183}
184
185String FontFace::style() const
186{
187    return m_style ? m_style->cssText() : "normal";
188}
189
190String FontFace::weight() const
191{
192    return m_weight ? m_weight->cssText() : "normal";
193}
194
195String FontFace::stretch() const
196{
197    return m_stretch ? m_stretch->cssText() : "normal";
198}
199
200String FontFace::unicodeRange() const
201{
202    return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF";
203}
204
205String FontFace::variant() const
206{
207    return m_variant ? m_variant->cssText() : "normal";
208}
209
210String FontFace::featureSettings() const
211{
212    return m_featureSettings ? m_featureSettings->cssText() : "normal";
213}
214
215void FontFace::setStyle(const String& s, ExceptionState& exceptionState)
216{
217    setPropertyFromString(s, CSSPropertyFontStyle, exceptionState);
218}
219
220void FontFace::setWeight(const String& s, ExceptionState& exceptionState)
221{
222    setPropertyFromString(s, CSSPropertyFontWeight, exceptionState);
223}
224
225void FontFace::setStretch(const String& s, ExceptionState& exceptionState)
226{
227    setPropertyFromString(s, CSSPropertyFontStretch, exceptionState);
228}
229
230void FontFace::setUnicodeRange(const String& s, ExceptionState& exceptionState)
231{
232    setPropertyFromString(s, CSSPropertyUnicodeRange, exceptionState);
233}
234
235void FontFace::setVariant(const String& s, ExceptionState& exceptionState)
236{
237    setPropertyFromString(s, CSSPropertyFontVariant, exceptionState);
238}
239
240void FontFace::setFeatureSettings(const String& s, ExceptionState& exceptionState)
241{
242    setPropertyFromString(s, CSSPropertyWebkitFontFeatureSettings, exceptionState);
243}
244
245void FontFace::setPropertyFromString(const String& s, CSSPropertyID propertyID, ExceptionState& exceptionState)
246{
247    RefPtr<CSSValue> value = parseCSSValue(s, propertyID);
248    if (!value || !setPropertyValue(value, propertyID))
249        exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
250}
251
252bool FontFace::setPropertyFromStyle(const StylePropertySet* properties, CSSPropertyID propertyID)
253{
254    return setPropertyValue(properties->getPropertyCSSValue(propertyID), propertyID);
255}
256
257bool FontFace::setPropertyValue(PassRefPtr<CSSValue> value, CSSPropertyID propertyID)
258{
259    switch (propertyID) {
260    case CSSPropertyFontStyle:
261        m_style = value;
262        break;
263    case CSSPropertyFontWeight:
264        m_weight = value;
265        break;
266    case CSSPropertyFontStretch:
267        m_stretch = value;
268        break;
269    case CSSPropertyUnicodeRange:
270        if (value && !value->isValueList())
271            return false;
272        m_unicodeRange = value;
273        break;
274    case CSSPropertyFontVariant:
275        m_variant = value;
276        break;
277    case CSSPropertyWebkitFontFeatureSettings:
278        m_featureSettings = value;
279        break;
280    default:
281        ASSERT_NOT_REACHED();
282        return false;
283    }
284    return true;
285}
286
287bool FontFace::setFamilyValue(CSSValueList* familyList)
288{
289    // The font-family descriptor has to have exactly one family name.
290    if (familyList->length() != 1)
291        return false;
292
293    CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->itemWithoutBoundsCheck(0));
294    AtomicString family;
295    if (familyValue->isString()) {
296        family = AtomicString(familyValue->getStringValue());
297    } else if (familyValue->isValueID()) {
298        // We need to use the raw text for all the generic family types, since @font-face is a way of actually
299        // defining what font to use for those types.
300        switch (familyValue->getValueID()) {
301        case CSSValueSerif:
302            family =  FontFamilyNames::webkit_serif;
303            break;
304        case CSSValueSansSerif:
305            family =  FontFamilyNames::webkit_sans_serif;
306            break;
307        case CSSValueCursive:
308            family =  FontFamilyNames::webkit_cursive;
309            break;
310        case CSSValueFantasy:
311            family =  FontFamilyNames::webkit_fantasy;
312            break;
313        case CSSValueMonospace:
314            family =  FontFamilyNames::webkit_monospace;
315            break;
316        case CSSValueWebkitPictograph:
317            family =  FontFamilyNames::webkit_pictograph;
318            break;
319        default:
320            return false;
321        }
322    }
323    m_family = family;
324    return true;
325}
326
327String FontFace::status() const
328{
329    switch (m_status) {
330    case Unloaded:
331        return "unloaded";
332    case Loading:
333        return "loading";
334    case Loaded:
335        return "loaded";
336    case Error:
337        return "error";
338    default:
339        ASSERT_NOT_REACHED();
340    }
341    return emptyString();
342}
343
344void FontFace::setLoadStatus(LoadStatus status)
345{
346    m_status = status;
347    if (m_status == Loaded || m_status == Error)
348        resolveReadyPromises();
349}
350
351void FontFace::load()
352{
353    // FIXME: This does not load FontFace created by JavaScript, since m_cssFontFace is null.
354    if (m_status != Unloaded || !m_cssFontFace)
355        return;
356
357    FontDescription fontDescription;
358    FontFamily fontFamily;
359    fontFamily.setFamily(m_family);
360    fontDescription.setFamily(fontFamily);
361    fontDescription.setTraitsMask(static_cast<FontTraitsMask>(traitsMask()));
362
363    RefPtr<SimpleFontData> fontData = m_cssFontFace->getFontData(fontDescription);
364    if (fontData && fontData->customFontData())
365        fontData->customFontData()->beginLoadIfNeeded();
366}
367
368ScriptPromise FontFace::ready(ExecutionContext* context)
369{
370    ScriptPromise promise = ScriptPromise::createPending(context);
371    OwnPtr<FontFaceReadyPromiseResolver> resolver = FontFaceReadyPromiseResolver::create(promise, context);
372    if (m_status == Loaded || m_status == Error)
373        resolver->resolve(this);
374    else
375        m_readyResolvers.append(resolver.release());
376    return promise;
377}
378
379void FontFace::resolveReadyPromises()
380{
381    for (size_t i = 0; i < m_readyResolvers.size(); i++)
382        m_readyResolvers[i]->resolve(this);
383    m_readyResolvers.clear();
384}
385
386unsigned FontFace::traitsMask() const
387{
388    unsigned traitsMask = 0;
389
390    if (m_style) {
391        if (!m_style->isPrimitiveValue())
392            return 0;
393
394        switch (toCSSPrimitiveValue(m_style.get())->getValueID()) {
395        case CSSValueNormal:
396            traitsMask |= FontStyleNormalMask;
397            break;
398        case CSSValueItalic:
399        case CSSValueOblique:
400            traitsMask |= FontStyleItalicMask;
401            break;
402        default:
403            break;
404        }
405    } else {
406        traitsMask |= FontStyleNormalMask;
407    }
408
409    if (m_weight) {
410        if (!m_weight->isPrimitiveValue())
411            return 0;
412
413        switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) {
414        case CSSValueBold:
415        case CSSValue700:
416            traitsMask |= FontWeight700Mask;
417            break;
418        case CSSValueNormal:
419        case CSSValue400:
420            traitsMask |= FontWeight400Mask;
421            break;
422        case CSSValue900:
423            traitsMask |= FontWeight900Mask;
424            break;
425        case CSSValue800:
426            traitsMask |= FontWeight800Mask;
427            break;
428        case CSSValue600:
429            traitsMask |= FontWeight600Mask;
430            break;
431        case CSSValue500:
432            traitsMask |= FontWeight500Mask;
433            break;
434        case CSSValue300:
435            traitsMask |= FontWeight300Mask;
436            break;
437        case CSSValue200:
438            traitsMask |= FontWeight200Mask;
439            break;
440        case CSSValue100:
441            traitsMask |= FontWeight100Mask;
442            break;
443        default:
444            break;
445        }
446    } else {
447        traitsMask |= FontWeight400Mask;
448    }
449
450    if (RefPtr<CSSValue> fontVariant = m_variant) {
451        // font-variant descriptor can be a value list.
452        if (fontVariant->isPrimitiveValue()) {
453            RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
454            list->append(fontVariant);
455            fontVariant = list;
456        } else if (!fontVariant->isValueList()) {
457            return 0;
458        }
459
460        CSSValueList* variantList = toCSSValueList(fontVariant.get());
461        unsigned numVariants = variantList->length();
462        if (!numVariants)
463            return 0;
464
465        for (unsigned i = 0; i < numVariants; ++i) {
466            switch (toCSSPrimitiveValue(variantList->itemWithoutBoundsCheck(i))->getValueID()) {
467            case CSSValueNormal:
468                traitsMask |= FontVariantNormalMask;
469                break;
470            case CSSValueSmallCaps:
471                traitsMask |= FontVariantSmallCapsMask;
472                break;
473            default:
474                break;
475            }
476        }
477    } else {
478        traitsMask |= FontVariantNormalMask;
479    }
480    return traitsMask;
481}
482
483PassRefPtr<CSSFontFace> FontFace::createCSSFontFace(Document* document)
484{
485    if (m_cssFontFace)
486        return m_cssFontFace;
487
488    RefPtr<CSSFontFace> cssFontFace = CSSFontFace::create(this);
489    m_cssFontFace = cssFontFace.get();
490
491    // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
492    CSSValueList* srcList = toCSSValueList(m_src.get());
493    int srcLength = srcList->length();
494
495    bool foundSVGFont = false;
496
497    for (int i = 0; i < srcLength; i++) {
498        // An item in the list either specifies a string (local font name) or a URL (remote font to download).
499        CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->itemWithoutBoundsCheck(i));
500        OwnPtr<CSSFontFaceSource> source;
501
502#if ENABLE(SVG_FONTS)
503        foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
504#endif
505        if (!item->isLocal()) {
506            Settings* settings = document ? document->frame() ? document->frame()->settings() : 0 : 0;
507            bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
508            if (allowDownloading && item->isSupportedFormat() && document) {
509                FontResource* fetched = item->fetch(document);
510                if (fetched) {
511                    source = adoptPtr(new CSSFontFaceSource(item->resource(), fetched));
512#if ENABLE(SVG_FONTS)
513                    if (foundSVGFont)
514                        source->setHasExternalSVGFont(true);
515#endif
516                }
517            }
518        } else {
519            source = adoptPtr(new CSSFontFaceSource(item->resource()));
520        }
521
522        if (source) {
523#if ENABLE(SVG_FONTS)
524            source->setSVGFontFaceElement(item->svgFontFaceElement());
525#endif
526            cssFontFace->addSource(source.release());
527        }
528    }
529
530    if (CSSValueList* rangeList = toCSSValueList(m_unicodeRange.get())) {
531        unsigned numRanges = rangeList->length();
532        for (unsigned i = 0; i < numRanges; i++) {
533            CSSUnicodeRangeValue* range = toCSSUnicodeRangeValue(rangeList->itemWithoutBoundsCheck(i));
534            cssFontFace->ranges().add(range->from(), range->to());
535        }
536    }
537    return cssFontFace;
538}
539
540} // namespace WebCore
541