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