1/*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 *           (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "CSSFontSelector.h"
29
30#include "AtomicString.h"
31#include "CachedFont.h"
32#include "CSSFontFace.h"
33#include "CSSFontFaceRule.h"
34#include "CSSFontFaceSource.h"
35#include "CSSFontFaceSrcValue.h"
36#include "CSSMutableStyleDeclaration.h"
37#include "CSSPrimitiveValue.h"
38#include "CSSPropertyNames.h"
39#include "CSSSegmentedFontFace.h"
40#include "CSSUnicodeRangeValue.h"
41#include "CSSValueKeywords.h"
42#include "CSSValueList.h"
43#include "DocLoader.h"
44#include "Document.h"
45#include "FontCache.h"
46#include "FontFamilyValue.h"
47#include "Frame.h"
48#include "RenderObject.h"
49#include "Settings.h"
50#include "SimpleFontData.h"
51
52#if ENABLE(SVG)
53#include "SVGFontFaceElement.h"
54#include "SVGNames.h"
55#endif
56
57namespace WebCore {
58
59CSSFontSelector::CSSFontSelector(Document* document)
60    : m_document(document)
61{
62    // FIXME: An old comment used to say there was no need to hold a reference to m_document
63    // because "we are guaranteed to be destroyed before the document". But there does not
64    // seem to be any such guarantee.
65
66    ASSERT(m_document);
67    fontCache()->addClient(this);
68}
69
70CSSFontSelector::~CSSFontSelector()
71{
72    fontCache()->removeClient(this);
73    deleteAllValues(m_fontFaces);
74    deleteAllValues(m_locallyInstalledFontFaces);
75    deleteAllValues(m_fonts);
76}
77
78bool CSSFontSelector::isEmpty() const
79{
80    return m_fonts.isEmpty();
81}
82
83DocLoader* CSSFontSelector::docLoader() const
84{
85    return m_document ? m_document->docLoader() : 0;
86}
87
88void CSSFontSelector::addFontFaceRule(const CSSFontFaceRule* fontFaceRule)
89{
90    // Obtain the font-family property and the src property.  Both must be defined.
91    const CSSMutableStyleDeclaration* style = fontFaceRule->style();
92    RefPtr<CSSValue> fontFamily = style->getPropertyCSSValue(CSSPropertyFontFamily);
93    RefPtr<CSSValue> src = style->getPropertyCSSValue(CSSPropertySrc);
94    RefPtr<CSSValue> unicodeRange = style->getPropertyCSSValue(CSSPropertyUnicodeRange);
95    if (!fontFamily || !src || !fontFamily->isValueList() || !src->isValueList() || (unicodeRange && !unicodeRange->isValueList()))
96        return;
97
98    CSSValueList* familyList = static_cast<CSSValueList*>(fontFamily.get());
99    if (!familyList->length())
100        return;
101
102    CSSValueList* srcList = static_cast<CSSValueList*>(src.get());
103    if (!srcList->length())
104        return;
105
106    CSSValueList* rangeList = static_cast<CSSValueList*>(unicodeRange.get());
107
108    unsigned traitsMask = 0;
109
110    if (RefPtr<CSSValue> fontStyle = style->getPropertyCSSValue(CSSPropertyFontStyle)) {
111        if (fontStyle->isPrimitiveValue()) {
112            RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
113            list->append(fontStyle);
114            fontStyle = list;
115        } else if (!fontStyle->isValueList())
116            return;
117
118        CSSValueList* styleList = static_cast<CSSValueList*>(fontStyle.get());
119        unsigned numStyles = styleList->length();
120        if (!numStyles)
121            return;
122
123        for (unsigned i = 0; i < numStyles; ++i) {
124            switch (static_cast<CSSPrimitiveValue*>(styleList->itemWithoutBoundsCheck(i))->getIdent()) {
125                case CSSValueAll:
126                    traitsMask |= FontStyleMask;
127                    break;
128                case CSSValueNormal:
129                    traitsMask |= FontStyleNormalMask;
130                    break;
131                case CSSValueItalic:
132                case CSSValueOblique:
133                    traitsMask |= FontStyleItalicMask;
134                    break;
135                default:
136                    break;
137            }
138        }
139    } else
140        traitsMask |= FontStyleMask;
141
142    if (RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight)) {
143        if (fontWeight->isPrimitiveValue()) {
144            RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
145            list->append(fontWeight);
146            fontWeight = list;
147        } else if (!fontWeight->isValueList())
148            return;
149
150        CSSValueList* weightList = static_cast<CSSValueList*>(fontWeight.get());
151        unsigned numWeights = weightList->length();
152        if (!numWeights)
153            return;
154
155        for (unsigned i = 0; i < numWeights; ++i) {
156            switch (static_cast<CSSPrimitiveValue*>(weightList->itemWithoutBoundsCheck(i))->getIdent()) {
157                case CSSValueAll:
158                    traitsMask |= FontWeightMask;
159                    break;
160                case CSSValueBolder:
161                case CSSValueBold:
162                case CSSValue700:
163                    traitsMask |= FontWeight700Mask;
164                    break;
165                case CSSValueNormal:
166                case CSSValue400:
167                    traitsMask |= FontWeight400Mask;
168                    break;
169                case CSSValue900:
170                    traitsMask |= FontWeight900Mask;
171                    break;
172                case CSSValue800:
173                    traitsMask |= FontWeight800Mask;
174                    break;
175                case CSSValue600:
176                    traitsMask |= FontWeight600Mask;
177                    break;
178                case CSSValue500:
179                    traitsMask |= FontWeight500Mask;
180                    break;
181                case CSSValue300:
182                    traitsMask |= FontWeight300Mask;
183                    break;
184                case CSSValueLighter:
185                case CSSValue200:
186                    traitsMask |= FontWeight200Mask;
187                    break;
188                case CSSValue100:
189                    traitsMask |= FontWeight100Mask;
190                    break;
191                default:
192                    break;
193            }
194        }
195    } else
196        traitsMask |= FontWeightMask;
197
198    if (RefPtr<CSSValue> fontVariant = style->getPropertyCSSValue(CSSPropertyFontVariant)) {
199        if (fontVariant->isPrimitiveValue()) {
200            RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
201            list->append(fontVariant);
202            fontVariant = list;
203        } else if (!fontVariant->isValueList())
204            return;
205
206        CSSValueList* variantList = static_cast<CSSValueList*>(fontVariant.get());
207        unsigned numVariants = variantList->length();
208        if (!numVariants)
209            return;
210
211        for (unsigned i = 0; i < numVariants; ++i) {
212            switch (static_cast<CSSPrimitiveValue*>(variantList->itemWithoutBoundsCheck(i))->getIdent()) {
213                case CSSValueAll:
214                    traitsMask |= FontVariantMask;
215                    break;
216                case CSSValueNormal:
217                    traitsMask |= FontVariantNormalMask;
218                    break;
219                case CSSValueSmallCaps:
220                    traitsMask |= FontVariantSmallCapsMask;
221                    break;
222                default:
223                    break;
224            }
225        }
226    } else
227        traitsMask |= FontVariantNormalMask;
228
229    // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
230    RefPtr<CSSFontFace> fontFace;
231
232    int srcLength = srcList->length();
233
234    bool foundLocal = false;
235    bool foundSVGFont = false;
236
237    for (int i = 0; i < srcLength; i++) {
238        // An item in the list either specifies a string (local font name) or a URL (remote font to download).
239        CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i));
240        CSSFontFaceSource* source = 0;
241
242#if ENABLE(SVG_FONTS)
243        foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
244#endif
245        if (!item->isLocal()) {
246            Settings* settings = m_document ? m_document->frame() ? m_document->frame()->settings() : 0 : 0;
247            bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
248            if (allowDownloading && item->isSupportedFormat() && m_document) {
249                CachedFont* cachedFont = m_document->docLoader()->requestFont(item->resource());
250                if (cachedFont) {
251#if ENABLE(SVG_FONTS)
252                    if (foundSVGFont)
253                        cachedFont->setSVGFont(true);
254#endif
255                    source = new CSSFontFaceSource(item->resource(), cachedFont);
256                }
257            }
258        } else {
259            source = new CSSFontFaceSource(item->resource());
260            foundLocal = true;
261        }
262
263        if (!fontFace)
264            fontFace = CSSFontFace::create(static_cast<FontTraitsMask>(traitsMask));
265
266        if (source) {
267#if ENABLE(SVG_FONTS)
268            source->setSVGFontFaceElement(item->svgFontFaceElement());
269#endif
270            fontFace->addSource(source);
271        }
272    }
273
274    ASSERT(fontFace);
275
276    if (fontFace && !fontFace->isValid())
277        return;
278
279    if (rangeList) {
280        unsigned numRanges = rangeList->length();
281        for (unsigned i = 0; i < numRanges; i++) {
282            CSSUnicodeRangeValue* range = static_cast<CSSUnicodeRangeValue*>(rangeList->itemWithoutBoundsCheck(i));
283            fontFace->addRange(range->from(), range->to());
284        }
285    }
286
287    // Hash under every single family name.
288    int familyLength = familyList->length();
289    for (int i = 0; i < familyLength; i++) {
290        CSSPrimitiveValue* item = static_cast<CSSPrimitiveValue*>(familyList->itemWithoutBoundsCheck(i));
291        String familyName;
292        if (item->primitiveType() == CSSPrimitiveValue::CSS_STRING)
293            familyName = static_cast<FontFamilyValue*>(item)->familyName();
294        else if (item->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
295            // We need to use the raw text for all the generic family types, since @font-face is a way of actually
296            // defining what font to use for those types.
297            String familyName;
298            switch (item->getIdent()) {
299                case CSSValueSerif:
300                    familyName = "-webkit-serif";
301                    break;
302                case CSSValueSansSerif:
303                    familyName = "-webkit-sans-serif";
304                    break;
305                case CSSValueCursive:
306                    familyName = "-webkit-cursive";
307                    break;
308                case CSSValueFantasy:
309                    familyName = "-webkit-fantasy";
310                    break;
311                case CSSValueMonospace:
312                    familyName = "-webkit-monospace";
313                    break;
314                default:
315                    break;
316            }
317        }
318
319        if (familyName.isEmpty())
320            continue;
321
322#if ENABLE(SVG_FONTS)
323        // SVG allows several <font> elements with the same font-family, differing only
324        // in ie. font-variant. Be sure to pick up the right one - in getFontData below.
325        if (foundSVGFont && (traitsMask & FontVariantSmallCapsMask))
326            familyName += "-webkit-svg-small-caps";
327#endif
328
329        Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(familyName);
330        if (!familyFontFaces) {
331            familyFontFaces = new Vector<RefPtr<CSSFontFace> >;
332            m_fontFaces.set(familyName, familyFontFaces);
333
334            ASSERT(!m_locallyInstalledFontFaces.contains(familyName));
335            Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFaces;
336
337            Vector<unsigned> locallyInstalledFontsTraitsMasks;
338            fontCache()->getTraitsInFamily(familyName, locallyInstalledFontsTraitsMasks);
339            unsigned numLocallyInstalledFaces = locallyInstalledFontsTraitsMasks.size();
340            if (numLocallyInstalledFaces) {
341                familyLocallyInstalledFaces = new Vector<RefPtr<CSSFontFace> >;
342                m_locallyInstalledFontFaces.set(familyName, familyLocallyInstalledFaces);
343
344                for (unsigned i = 0; i < numLocallyInstalledFaces; ++i) {
345                    RefPtr<CSSFontFace> locallyInstalledFontFace = CSSFontFace::create(static_cast<FontTraitsMask>(locallyInstalledFontsTraitsMasks[i]));
346                    locallyInstalledFontFace->addSource(new CSSFontFaceSource(familyName));
347                    ASSERT(locallyInstalledFontFace->isValid());
348                    familyLocallyInstalledFaces->append(locallyInstalledFontFace);
349                }
350            }
351        }
352
353        familyFontFaces->append(fontFace);
354    }
355}
356
357void CSSFontSelector::fontLoaded()
358{
359    if (!m_document || m_document->inPageCache() || !m_document->renderer())
360        return;
361    m_document->recalcStyle(Document::Force);
362    m_document->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
363}
364
365void CSSFontSelector::fontCacheInvalidated()
366{
367    if (!m_document || m_document->inPageCache() || !m_document->renderer())
368        return;
369    m_document->recalcStyle(Document::Force);
370    m_document->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
371}
372
373static FontData* fontDataForGenericFamily(Document* document, const FontDescription& fontDescription, const AtomicString& familyName)
374{
375    if (!document || !document->frame())
376        return 0;
377
378    const Settings* settings = document->frame()->settings();
379    if (!settings)
380        return 0;
381
382    AtomicString genericFamily;
383    if (familyName == "-webkit-serif")
384        genericFamily = settings->serifFontFamily();
385    else if (familyName == "-webkit-sans-serif")
386        genericFamily = settings->sansSerifFontFamily();
387    else if (familyName == "-webkit-cursive")
388        genericFamily = settings->cursiveFontFamily();
389    else if (familyName == "-webkit-fantasy")
390        genericFamily = settings->fantasyFontFamily();
391    else if (familyName == "-webkit-monospace")
392        genericFamily = settings->fixedFontFamily();
393    else if (familyName == "-webkit-standard")
394        genericFamily = settings->standardFontFamily();
395
396    if (!genericFamily.isEmpty())
397        return fontCache()->getCachedFontData(fontDescription, genericFamily);
398
399    return 0;
400}
401
402static FontTraitsMask desiredTraitsMaskForComparison;
403
404static inline bool compareFontFaces(CSSFontFace* first, CSSFontFace* second)
405{
406    FontTraitsMask firstTraitsMask = first->traitsMask();
407    FontTraitsMask secondTraitsMask = second->traitsMask();
408
409    bool firstHasDesiredVariant = firstTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
410    bool secondHasDesiredVariant = secondTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
411
412    if (firstHasDesiredVariant != secondHasDesiredVariant)
413        return firstHasDesiredVariant;
414
415    bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
416    bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
417
418    if (firstHasDesiredStyle != secondHasDesiredStyle)
419        return firstHasDesiredStyle;
420
421    if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
422        return false;
423    if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
424        return true;
425
426    // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802/#q46 says: "If there are fewer then 9 weights in the family, the default algorithm
427    // for filling the "holes" is as follows. If '500' is unassigned, it will be assigned the same font as '400'. If any of the values '600',
428    // '700', '800', or '900' remains unassigned, they are assigned to the same face as the next darker assigned keyword, if any, or the next
429    // lighter one otherwise. If any of '300', '200', or '100' remains unassigned, it is assigned to the next lighter assigned keyword, if any,
430    // or the next darker otherwise."
431    // For '400', we made up our own rule (which then '500' follows).
432
433    static const unsigned fallbackRuleSets = 9;
434    static const unsigned rulesPerSet = 8;
435    static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
436        { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
437        { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
438        { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
439        { FontWeight500Mask, FontWeight300Mask, FontWeight600Mask, FontWeight200Mask, FontWeight700Mask, FontWeight100Mask, FontWeight800Mask, FontWeight900Mask },
440        { FontWeight400Mask, FontWeight300Mask, FontWeight600Mask, FontWeight200Mask, FontWeight700Mask, FontWeight100Mask, FontWeight800Mask, FontWeight900Mask },
441        { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
442        { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
443        { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
444        { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
445    };
446
447    unsigned ruleSetIndex = 0;
448    unsigned w = FontWeight100Bit;
449    while (!(desiredTraitsMaskForComparison & (1 << w))) {
450        w++;
451        ruleSetIndex++;
452    }
453
454    ASSERT(ruleSetIndex < fallbackRuleSets);
455    const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
456    for (unsigned i = 0; i < rulesPerSet; ++i) {
457        if (secondTraitsMask & weightFallbackRule[i])
458            return false;
459        if (firstTraitsMask & weightFallbackRule[i])
460            return true;
461    }
462
463    return false;
464}
465
466FontData* CSSFontSelector::getFontData(const FontDescription& fontDescription, const AtomicString& familyName)
467{
468    if (m_fontFaces.isEmpty()) {
469        if (familyName.startsWith("-webkit-"))
470            return fontDataForGenericFamily(m_document, fontDescription, familyName);
471        return 0;
472    }
473
474    String family = familyName.string();
475
476#if ENABLE(SVG_FONTS)
477    if (fontDescription.smallCaps())
478        family += "-webkit-svg-small-caps";
479#endif
480
481    Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(family);
482    // If no face was found, then return 0 and let the OS come up with its best match for the name.
483    if (!familyFontFaces || familyFontFaces->isEmpty()) {
484        // If we were handed a generic family, but there was no match, go ahead and return the correct font based off our
485        // settings.
486        return fontDataForGenericFamily(m_document, fontDescription, familyName);
487    }
488
489    HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >* segmentedFontFaceCache = m_fonts.get(family);
490    if (!segmentedFontFaceCache) {
491        segmentedFontFaceCache = new HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >;
492        m_fonts.set(family, segmentedFontFaceCache);
493    }
494
495    FontTraitsMask traitsMask = fontDescription.traitsMask();
496
497    RefPtr<CSSSegmentedFontFace> face = segmentedFontFaceCache->get(traitsMask);
498
499    if (!face) {
500        face = CSSSegmentedFontFace::create(this);
501        segmentedFontFaceCache->set(traitsMask, face);
502        // Collect all matching faces and sort them in order of preference.
503        Vector<CSSFontFace*, 32> candidateFontFaces;
504        for (int i = familyFontFaces->size() - 1; i >= 0; --i) {
505            CSSFontFace* candidate = familyFontFaces->at(i).get();
506            unsigned candidateTraitsMask = candidate->traitsMask();
507            if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
508                continue;
509            if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
510                continue;
511            candidateFontFaces.append(candidate);
512        }
513
514        if (Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFontFaces = m_locallyInstalledFontFaces.get(family)) {
515            unsigned numLocallyInstalledFontFaces = familyLocallyInstalledFontFaces->size();
516            for (unsigned i = 0; i < numLocallyInstalledFontFaces; ++i) {
517                CSSFontFace* candidate = familyLocallyInstalledFontFaces->at(i).get();
518                unsigned candidateTraitsMask = candidate->traitsMask();
519                if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
520                    continue;
521                if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
522                    continue;
523                candidateFontFaces.append(candidate);
524            }
525        }
526
527        desiredTraitsMaskForComparison = traitsMask;
528        std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), compareFontFaces);
529        unsigned numCandidates = candidateFontFaces.size();
530        for (unsigned i = 0; i < numCandidates; ++i)
531            face->appendFontFace(candidateFontFaces[i]);
532    }
533
534    // We have a face.  Ask it for a font data.  If it cannot produce one, it will fail, and the OS will take over.
535    return face->getFontData(fontDescription);
536}
537
538}
539