1/*
2 * Copyright (C) 2007, 2008, 2011 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 COMPUTER, 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 COMPUTER, 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 "core/css/CSSFontFace.h"
28
29#include "core/css/CSSFontSelector.h"
30#include "core/css/CSSSegmentedFontFace.h"
31#include "core/css/FontFaceSet.h"
32#include "core/dom/Document.h"
33#include "core/frame/UseCounter.h"
34#include "platform/fonts/SimpleFontData.h"
35
36namespace WebCore {
37
38CSSFontFace::~CSSFontFace()
39{
40    m_fontFace->cssFontFaceDestroyed();
41}
42
43PassRefPtr<CSSFontFace> CSSFontFace::createFromStyleRule(Document* document, const StyleRuleFontFace* fontFaceRule)
44{
45    RefPtr<FontFace> fontFace = FontFace::create(fontFaceRule);
46    if (!fontFace || fontFace->family().isEmpty())
47        return 0;
48
49    unsigned traitsMask = fontFace->traitsMask();
50    if (!traitsMask)
51        return 0;
52
53    // FIXME: Plumbing back into createCSSFontFace seems odd.
54    // Maybe move FontFace::createCSSFontFace logic here?
55    RefPtr<CSSFontFace> cssFontFace = fontFace->createCSSFontFace(document);
56    if (!cssFontFace || !cssFontFace->isValid())
57        return 0;
58
59    return cssFontFace;
60}
61
62bool CSSFontFace::isLoaded() const
63{
64    size_t size = m_sources.size();
65    for (size_t i = 0; i < size; i++) {
66        if (!m_sources[i]->isLoaded())
67            return false;
68    }
69    return true;
70}
71
72bool CSSFontFace::isValid() const
73{
74    size_t size = m_sources.size();
75    for (size_t i = 0; i < size; i++) {
76        if (m_sources[i]->isValid())
77            return true;
78    }
79    return false;
80}
81
82void CSSFontFace::addSource(PassOwnPtr<CSSFontFaceSource> source)
83{
84    source->setFontFace(this);
85    m_sources.append(source);
86}
87
88void CSSFontFace::setSegmentedFontFace(CSSSegmentedFontFace* segmentedFontFace)
89{
90    ASSERT(!m_segmentedFontFace);
91    m_segmentedFontFace = segmentedFontFace;
92}
93
94void CSSFontFace::beginLoadIfNeeded(CSSFontFaceSource* source)
95{
96    if (!m_segmentedFontFace)
97        return;
98
99    if (source->resource() && source->resource()->stillNeedsLoad()) {
100        CSSFontSelector* fontSelector = m_segmentedFontFace->fontSelector();
101        fontSelector->beginLoadingFontSoon(source->resource());
102    }
103
104    if (loadStatus() == FontFace::Unloaded)
105        setLoadStatus(FontFace::Loading);
106}
107
108void CSSFontFace::fontLoaded(CSSFontFaceSource* source)
109{
110    if (source != m_activeSource)
111        return;
112    m_activeSource = 0;
113
114    // FIXME: Can we assert that m_segmentedFontFace is non-null? That may
115    // require stopping in-progress font loading when the last
116    // CSSSegmentedFontFace is removed.
117    if (!m_segmentedFontFace)
118        return;
119
120    CSSFontSelector* fontSelector = m_segmentedFontFace->fontSelector();
121    fontSelector->fontLoaded();
122
123    if (fontSelector->document() && loadStatus() == FontFace::Loading) {
124        if (source->ensureFontData()) {
125            setLoadStatus(FontFace::Loaded);
126            if (source->isSVGFontFaceSource())
127                UseCounter::count(*fontSelector->document(), UseCounter::SVGFontInCSS);
128        }
129        else if (!isValid())
130            setLoadStatus(FontFace::Error);
131    }
132
133    m_segmentedFontFace->fontLoaded(this);
134}
135
136PassRefPtr<SimpleFontData> CSSFontFace::getFontData(const FontDescription& fontDescription)
137{
138    m_activeSource = 0;
139    if (!isValid())
140        return 0;
141
142    size_t size = m_sources.size();
143    for (size_t i = 0; i < size; ++i) {
144        if (RefPtr<SimpleFontData> result = m_sources[i]->getFontData(fontDescription)) {
145            m_activeSource = m_sources[i].get();
146            if (loadStatus() == FontFace::Unloaded && (m_sources[i]->isLoading() || m_sources[i]->isLoaded()))
147                setLoadStatus(FontFace::Loading);
148            if (loadStatus() == FontFace::Loading && m_sources[i]->isLoaded())
149                setLoadStatus(FontFace::Loaded);
150            return result.release();
151        }
152    }
153
154    if (loadStatus() == FontFace::Unloaded)
155        setLoadStatus(FontFace::Loading);
156    if (loadStatus() == FontFace::Loading)
157        setLoadStatus(FontFace::Error);
158    return 0;
159}
160
161void CSSFontFace::willUseFontData(const FontDescription& fontDescription)
162{
163    if (loadStatus() != FontFace::Unloaded || m_activeSource)
164        return;
165
166    // Kicks off font load here only if the @font-face has no unicode-range.
167    // @font-faces with unicode-range will be loaded when a GlyphPage for the
168    // font is created.
169    // FIXME: Pass around the text to render from RenderText, and kick download
170    // if m_ranges intersects with the text. Make sure this does not cause
171    // performance regression.
172    if (!m_ranges.isEntireRange())
173        return;
174
175    ASSERT(m_segmentedFontFace);
176
177    size_t size = m_sources.size();
178    for (size_t i = 0; i < size; ++i) {
179        if (!m_sources[i]->isValid() || (m_sources[i]->isLocal() && !m_sources[i]->isLocalFontAvailable(fontDescription)))
180            continue;
181        if (!m_sources[i]->isLocal() && !m_sources[i]->isLoaded()) {
182            m_activeSource = m_sources[i].get();
183            beginLoadIfNeeded(m_activeSource);
184        }
185        break;
186    }
187}
188
189void CSSFontFace::setLoadStatus(FontFace::LoadStatus newStatus)
190{
191    ASSERT(m_fontFace);
192    m_fontFace->setLoadStatus(newStatus);
193
194    if (!m_segmentedFontFace)
195        return;
196    Document* document = m_segmentedFontFace->fontSelector()->document();
197    if (!document)
198        return;
199
200    switch (newStatus) {
201    case FontFace::Loading:
202        FontFaceSet::from(document)->beginFontLoading(m_fontFace.get());
203        break;
204    case FontFace::Loaded:
205        FontFaceSet::from(document)->fontLoaded(m_fontFace.get());
206        break;
207    case FontFace::Error:
208        FontFaceSet::from(document)->loadError(m_fontFace.get());
209        break;
210    default:
211        break;
212    }
213}
214
215bool CSSFontFace::UnicodeRangeSet::intersectsWith(const String& text) const
216{
217    if (text.isEmpty())
218        return false;
219    if (isEntireRange())
220        return true;
221
222    // FIXME: This takes O(text.length() * m_ranges.size()) time. It would be
223    // better to make m_ranges sorted and use binary search.
224    unsigned index = 0;
225    while (index < text.length()) {
226        UChar32 c = text.characterStartingAt(index);
227        index += U16_LENGTH(c);
228        for (unsigned i = 0; i < m_ranges.size(); i++) {
229            if (m_ranges[i].contains(c))
230                return true;
231        }
232    }
233    return false;
234}
235
236}
237