Home | History | Annotate | Download | only in css
      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 
     36 namespace WebCore {
     37 
     38 CSSFontFace::~CSSFontFace()
     39 {
     40     m_fontFace->cssFontFaceDestroyed();
     41 }
     42 
     43 PassRefPtr<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 
     62 bool 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 
     72 bool 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 
     82 void CSSFontFace::addSource(PassOwnPtr<CSSFontFaceSource> source)
     83 {
     84     source->setFontFace(this);
     85     m_sources.append(source);
     86 }
     87 
     88 void CSSFontFace::setSegmentedFontFace(CSSSegmentedFontFace* segmentedFontFace)
     89 {
     90     ASSERT(!m_segmentedFontFace);
     91     m_segmentedFontFace = segmentedFontFace;
     92 }
     93 
     94 void 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 
    108 void 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 
    136 PassRefPtr<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 
    161 void 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 
    189 void 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 
    215 bool 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