summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/CSSFontFace.cpp
diff options
context:
space:
mode:
authorKonstantin Tokarev <annulen@yandex.ru>2016-08-25 19:20:41 +0300
committerKonstantin Tokarev <annulen@yandex.ru>2017-02-02 12:30:55 +0000
commit6882a04fb36642862b11efe514251d32070c3d65 (patch)
treeb7959826000b061fd5ccc7512035c7478742f7b0 /Source/WebCore/css/CSSFontFace.cpp
parentab6df191029eeeb0b0f16f127d553265659f739e (diff)
downloadqtwebkit-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.cpp387
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