diff options
| author | Konstantin Tokarev <annulen@yandex.ru> | 2016-08-25 19:20:41 +0300 |
|---|---|---|
| committer | Konstantin Tokarev <annulen@yandex.ru> | 2017-02-02 12:30:55 +0000 |
| commit | 6882a04fb36642862b11efe514251d32070c3d65 (patch) | |
| tree | b7959826000b061fd5ccc7512035c7478742f7b0 /Source/WebCore/css/CSSFontFace.cpp | |
| parent | ab6df191029eeeb0b0f16f127d553265659f739e (diff) | |
| download | qtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz | |
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f
Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/WebCore/css/CSSFontFace.cpp')
| -rw-r--r-- | Source/WebCore/css/CSSFontFace.cpp | 387 |
1 files changed, 288 insertions, 99 deletions
diff --git a/Source/WebCore/css/CSSFontFace.cpp b/Source/WebCore/css/CSSFontFace.cpp index 99ac412ed..552e99cfa 100644 --- a/Source/WebCore/css/CSSFontFace.cpp +++ b/Source/WebCore/css/CSSFontFace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2011, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,155 +27,344 @@ #include "CSSFontFace.h" #include "CSSFontFaceSource.h" +#include "CSSFontFamily.h" +#include "CSSFontFeatureValue.h" #include "CSSFontSelector.h" +#include "CSSPrimitiveValueMappings.h" #include "CSSSegmentedFontFace.h" +#include "CSSUnicodeRangeValue.h" +#include "CSSValue.h" +#include "CSSValueList.h" #include "Document.h" +#include "Font.h" #include "FontDescription.h" #include "FontLoader.h" +#include "FontVariantBuilder.h" #include "RuntimeEnabledFeatures.h" -#include "SimpleFontData.h" +#include "StyleProperties.h" namespace WebCore { -bool CSSFontFace::isLoaded() const +CSSFontFace::CSSFontFace(CSSFontSelector& fontSelector, FontFace* wrapper, bool isLocalFallback) + : m_fontSelector(fontSelector) + , m_wrapper(wrapper) + , m_isLocalFallback(isLocalFallback) { - size_t size = m_sources.size(); - for (size_t i = 0; i < size; i++) { - if (!m_sources[i]->isLoaded()) - return false; - } - return true; } -bool CSSFontFace::isValid() const +CSSFontFace::~CSSFontFace() { - size_t size = m_sources.size(); - for (size_t i = 0; i < size; i++) { - if (m_sources[i]->isValid()) - return true; - } - return false; } -void CSSFontFace::addedToSegmentedFontFace(CSSSegmentedFontFace* segmentedFontFace) +bool CSSFontFace::setFamilies(CSSValue& family) { - m_segmentedFontFaces.add(segmentedFontFace); + if (!is<CSSValueList>(family)) + return false; + + CSSValueList& familyList = downcast<CSSValueList>(family); + if (!familyList.length()) + return false; + + m_families = &familyList; + return true; } -void CSSFontFace::removedFromSegmentedFontFace(CSSSegmentedFontFace* segmentedFontFace) +bool CSSFontFace::setStyle(CSSValue& style) { - m_segmentedFontFaces.remove(segmentedFontFace); + if (!is<CSSPrimitiveValue>(style)) + return false; + + unsigned styleMask = 0; + switch (downcast<CSSPrimitiveValue>(style).getValueID()) { + case CSSValueNormal: + styleMask = FontStyleNormalMask; + break; + case CSSValueItalic: + case CSSValueOblique: + styleMask = FontStyleItalicMask; + break; + default: + styleMask = FontStyleNormalMask; + break; + } + + m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontStyleMask)) | styleMask); + return true; } -void CSSFontFace::addSource(PassOwnPtr<CSSFontFaceSource> source) +bool CSSFontFace::setWeight(CSSValue& weight) { - source->setFontFace(this); - m_sources.append(source); + if (!is<CSSPrimitiveValue>(weight)) + return false; + + unsigned weightMask = 0; + switch (downcast<CSSPrimitiveValue>(weight).getValueID()) { + case CSSValueBold: + case CSSValueBolder: + case CSSValue700: + weightMask = FontWeight700Mask; + break; + case CSSValueNormal: + case CSSValue400: + weightMask = FontWeight400Mask; + break; + case CSSValue900: + weightMask = FontWeight900Mask; + break; + case CSSValue800: + weightMask = FontWeight800Mask; + break; + case CSSValue600: + weightMask = FontWeight600Mask; + break; + case CSSValue500: + weightMask = FontWeight500Mask; + break; + case CSSValue300: + weightMask = FontWeight300Mask; + break; + case CSSValueLighter: + case CSSValue200: + weightMask = FontWeight200Mask; + break; + case CSSValue100: + weightMask = FontWeight100Mask; + break; + default: + weightMask = FontWeight400Mask; + break; + } + + m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontWeightMask)) | weightMask); + return true; } -void CSSFontFace::fontLoaded(CSSFontFaceSource* source) +bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange) { - if (source != m_activeSource) - return; + if (!is<CSSValueList>(unicodeRange)) + return false; - // FIXME: Can we assert that m_segmentedFontFaces is not empty? That may - // require stopping in-progress font loading when the last - // CSSSegmentedFontFace is removed. - if (m_segmentedFontFaces.isEmpty()) - return; + m_ranges.clear(); + auto& list = downcast<CSSValueList>(unicodeRange); + for (auto& rangeValue : list) { + CSSUnicodeRangeValue& range = downcast<CSSUnicodeRangeValue>(rangeValue.get()); + m_ranges.append(UnicodeRange(range.from(), range.to())); + } + return true; +} - // Use one of the CSSSegmentedFontFaces' font selector. They all have - // the same font selector, so it's wasteful to store it in the CSSFontFace. - CSSFontSelector* fontSelector = (*m_segmentedFontFaces.begin())->fontSelector(); - fontSelector->fontLoaded(); +bool CSSFontFace::setVariantLigatures(CSSValue& variantLigatures) +{ + auto ligatures = extractFontVariantLigatures(variantLigatures); + m_variantSettings.commonLigatures = ligatures.commonLigatures; + m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures; + m_variantSettings.historicalLigatures = ligatures.historicalLigatures; + m_variantSettings.contextualAlternates = ligatures.contextualAlternates; + return true; +} -#if ENABLE(FONT_LOAD_EVENTS) - if (RuntimeEnabledFeatures::fontLoadEventsEnabled() && m_loadState == Loading) { - if (source->ensureFontData()) - notifyFontLoader(Loaded); - else if (!isValid()) - notifyFontLoader(Error); - } -#endif +bool CSSFontFace::setVariantPosition(CSSValue& variantPosition) +{ + if (!is<CSSPrimitiveValue>(variantPosition)) + return false; + m_variantSettings.position = downcast<CSSPrimitiveValue>(variantPosition); + return true; +} - HashSet<CSSSegmentedFontFace*>::iterator end = m_segmentedFontFaces.end(); - for (HashSet<CSSSegmentedFontFace*>::iterator it = m_segmentedFontFaces.begin(); it != end; ++it) - (*it)->fontLoaded(this); +bool CSSFontFace::setVariantCaps(CSSValue& variantCaps) +{ + if (!is<CSSPrimitiveValue>(variantCaps)) + return false; + m_variantSettings.caps = downcast<CSSPrimitiveValue>(variantCaps); + return true; +} -#if ENABLE(FONT_LOAD_EVENTS) - if (RuntimeEnabledFeatures::fontLoadEventsEnabled()) - notifyLoadingDone(); -#endif +bool CSSFontFace::setVariantNumeric(CSSValue& variantNumeric) +{ + auto numeric = extractFontVariantNumeric(variantNumeric); + m_variantSettings.numericFigure = numeric.figure; + m_variantSettings.numericSpacing = numeric.spacing; + m_variantSettings.numericFraction = numeric.fraction; + m_variantSettings.numericOrdinal = numeric.ordinal; + m_variantSettings.numericSlashedZero = numeric.slashedZero; + return true; } -PassRefPtr<SimpleFontData> CSSFontFace::getFontData(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic) +bool CSSFontFace::setVariantAlternates(CSSValue& variantAlternates) { - m_activeSource = 0; - if (!isValid()) - return 0; + if (!is<CSSPrimitiveValue>(variantAlternates)) + return false; + m_variantSettings.alternates = downcast<CSSPrimitiveValue>(variantAlternates); + return true; +} - ASSERT(!m_segmentedFontFaces.isEmpty()); - CSSFontSelector* fontSelector = (*m_segmentedFontFaces.begin())->fontSelector(); +bool CSSFontFace::setVariantEastAsian(CSSValue& variantEastAsian) +{ + auto eastAsian = extractFontVariantEastAsian(variantEastAsian); + m_variantSettings.eastAsianVariant = eastAsian.variant; + m_variantSettings.eastAsianWidth = eastAsian.width; + m_variantSettings.eastAsianRuby = eastAsian.ruby; + return true; +} -#if ENABLE(FONT_LOAD_EVENTS) - if (RuntimeEnabledFeatures::fontLoadEventsEnabled() && m_loadState == NotLoaded) - notifyFontLoader(Loading); -#endif +bool CSSFontFace::setFeatureSettings(CSSValue& featureSettings) +{ + if (!is<CSSValueList>(featureSettings)) + return false; - size_t size = m_sources.size(); - for (size_t i = 0; i < size; ++i) { - if (RefPtr<SimpleFontData> result = m_sources[i]->getFontData(fontDescription, syntheticBold, syntheticItalic, fontSelector)) { - m_activeSource = m_sources[i].get(); -#if ENABLE(FONT_LOAD_EVENTS) - if (RuntimeEnabledFeatures::fontLoadEventsEnabled() && m_loadState == Loading && m_sources[i]->isLoaded()) { - notifyFontLoader(Loaded); - notifyLoadingDone(); - } -#endif - return result.release(); - } + m_featureSettings = FontFeatureSettings(); + auto& list = downcast<CSSValueList>(featureSettings); + for (auto& rangeValue : list) { + CSSFontFeatureValue& feature = downcast<CSSFontFeatureValue>(rangeValue.get()); + m_featureSettings.insert(FontFeature(feature.tag(), feature.value())); } + return true; +} -#if ENABLE(FONT_LOAD_EVENTS) - if (RuntimeEnabledFeatures::fontLoadEventsEnabled() && m_loadState == Loading) { - notifyFontLoader(Error); - notifyLoadingDone(); +bool CSSFontFace::allSourcesFailed() const +{ + for (auto& source : m_sources) { + if (source->status() != CSSFontFaceSource::Status::Failure) + return false; } -#endif - return 0; + return true; } -#if ENABLE(FONT_LOAD_EVENTS) -void CSSFontFace::notifyFontLoader(LoadState newState) +void CSSFontFace::addClient(Client& client) { - m_loadState = newState; + m_clients.add(&client); +} - Document* document = (*m_segmentedFontFaces.begin())->fontSelector()->document(); - if (!document) - return; +void CSSFontFace::removeClient(Client& client) +{ + m_clients.remove(&client); +} - switch (newState) { - case Loading: - document->fontloader()->beginFontLoading(m_rule.get()); +void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source) +{ + m_sources.append(WTFMove(source)); + + // We should never add sources in the middle of loading. + ASSERT(!m_sourcesPopulated); +} + +void CSSFontFace::setStatus(Status newStatus) +{ + switch (newStatus) { + case Status::Pending: + ASSERT_NOT_REACHED(); break; - case Loaded: - document->fontloader()->fontLoaded(m_rule.get()); + case Status::Loading: + ASSERT(m_status == Status::Pending); break; - case Error: - document->fontloader()->loadError(m_rule.get(), m_activeSource); + case Status::TimedOut: + ASSERT(m_status == Status::Loading); break; - default: + case Status::Success: + ASSERT(m_status == Status::Loading || m_status == Status::TimedOut); + break; + case Status::Failure: + ASSERT(m_status == Status::Loading || m_status == Status::TimedOut); break; } + + for (auto& client : m_clients) + client->stateChanged(*this, m_status, newStatus); + + m_status = newStatus; } -void CSSFontFace::notifyLoadingDone() +void CSSFontFace::fontLoaded(CSSFontFaceSource&) { - Document* document = (*m_segmentedFontFaces.begin())->fontSelector()->document(); - if (document) - document->fontloader()->loadingDone(); + // If the font is already in the cache, CSSFontFaceSource may report it's loaded before it is added here as a source. + // Let's not pump the state machine until we've got all our sources. font() and load() are smart enough to act correctly + // when a source is failed or succeeded before we have asked it to load. + if (m_sourcesPopulated) + pump(); + + m_fontSelector->fontLoaded(); + + for (auto& client : m_clients) + client->fontLoaded(*this); +} + +size_t CSSFontFace::pump() +{ + size_t i; + for (i = 0; i < m_sources.size(); ++i) { + auto& source = m_sources[i]; + + if (source->status() == CSSFontFaceSource::Status::Pending) { + ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut); + if (m_status == Status::Pending) + setStatus(Status::Loading); + source->load(m_fontSelector.get()); + } + + switch (source->status()) { + case CSSFontFaceSource::Status::Pending: + ASSERT_NOT_REACHED(); + break; + case CSSFontFaceSource::Status::Loading: + ASSERT(m_status == Status::Pending || m_status == Status::Loading); + if (m_status == Status::Pending) + setStatus(Status::Loading); + return i; + case CSSFontFaceSource::Status::Success: + ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Success); + if (m_status == Status::Pending) + setStatus(Status::Loading); + if (m_status == Status::Loading || m_status == Status::TimedOut) + setStatus(Status::Success); + return i; + case CSSFontFaceSource::Status::Failure: + if (m_status == Status::Pending) + setStatus(Status::Loading); + break; + } + } + if (m_status == Status::Loading || m_status == Status::TimedOut) + setStatus(Status::Failure); + return m_sources.size(); +} + +void CSSFontFace::load() +{ + pump(); +} + +RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic) +{ + if (allSourcesFailed()) + return nullptr; + + // Our status is derived from the first non-failed source. However, this source may + // return null from font(), which means we need to continue looping through the remainder + // of the sources to try to find a font to use. These subsequent tries should not affect + // our own state, though. + size_t startIndex = pump(); + for (size_t i = startIndex; i < m_sources.size(); ++i) { + auto& source = m_sources[i]; + if (source->status() == CSSFontFaceSource::Status::Pending) + source->load(m_fontSelector.get()); + + switch (source->status()) { + case CSSFontFaceSource::Status::Pending: + ASSERT_NOT_REACHED(); + break; + case CSSFontFaceSource::Status::Loading: + return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), true, true); + case CSSFontFaceSource::Status::Success: + if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings)) + return result; + break; + case CSSFontFaceSource::Status::Failure: + break; + } + } + + return nullptr; } -#endif #if ENABLE(SVG_FONTS) bool CSSFontFace::hasSVGFontFaceSource() const |
