+2016-02-22 Myles C. Maxfield <mmaxfield@apple.com>
+
+ [Font Loading] Split CSSFontSelector into a FontFaceSet implementation and the rest of the class
+ https://bugs.webkit.org/show_bug.cgi?id=153347
+
+ Reviewed by Antti Koivisto.
+
+ * fast/text/font-face-javascript.html:
+ * fast/text/font-face-set-document-expected.txt: Added.
+ * fast/text/font-face-set-document.html: Added.
+
2016-02-22 Konstantin Tokarev <annulen@yandex.ru>
[JSC shell] Don't put empty arguments array to VM.
<!DOCTYPE html>
+<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
--- /dev/null
+PASS document.fonts.size is 2
+PASS object.done is false
+PASS object.value.family is "MyFont1"
+PASS object.done is false
+PASS object.value.family is "MyFont2"
+PASS object.done is true
+PASS fontFaceSet.size is 2
+PASS object.done is false
+PASS object.value.family is "MyFont1"
+PASS object.done is false
+PASS object.value.family is "MyFont2"
+PASS object.done is true
+PASS document.getElementById("testElement").offsetWidth is not originalWidth
+PASS document.getElementById("testElement").offsetWidth is originalWidth
+PASS object.length is 2
+PASS object[0].family is "MyFont3"
+PASS object[1].family is "MyFont3"
+PASS document.getElementById("testElement").offsetWidth is not originalWidth
+PASS document.getElementById("testElement").offsetWidth is originalWidth
+PASS successfullyParsed is true
+
+TEST COMPLETE
+On Load Executed.
+l
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style id="styleElement">
+@font-face {
+ font-family: "MyFont1";
+ src: url("../../resources/Ahem.ttf") format("truetype");
+}
+@font-face {
+ font-family: "MyFont2";
+ src: url("../../resources/Ahem.otf") format("opentype");
+}
+</style>
+</head>
+<body>
+<div id="onLoadElement"></div>
+<span id="testElement" style="font: 50px AppliedFont">l</span>
+<script>
+window.onload = function() {
+ document.getElementById("onLoadElement").innerText = "On Load Executed.";
+}
+var originalWidth = document.getElementById("testElement").offsetWidth;
+
+shouldBe("document.fonts.size", "2");
+var iterator = document.fonts.keys();
+var object = iterator.next();
+shouldBeFalse("object.done");
+shouldBeEqualToString("object.value.family", "MyFont1");
+object = iterator.next();
+shouldBeFalse("object.done");
+shouldBeEqualToString("object.value.family", "MyFont2");
+object = iterator.next();
+shouldBeTrue("object.done");
+
+var fontFaceSet = new FontFaceSet([]);
+fontFaceSet.add(new FontFace("MyFont2", "url(\"asdf\")", {}));
+fontFaceSet.add(document.fonts.keys().next().value);
+shouldBe("fontFaceSet.size", "2");
+iterator = fontFaceSet.keys();
+object = iterator.next();
+shouldBeFalse("object.done");
+shouldBeEqualToString("object.value.family", "MyFont1");
+object = iterator.next();
+shouldBeFalse("object.done");
+shouldBeEqualToString("object.value.family", "MyFont2");
+object = iterator.next();
+shouldBeTrue("object.done");
+
+iterator = undefined;
+object = undefined;
+
+self.jsTestIsAsync = true;
+function startLoading() {
+ var appliedFont = undefined;
+ document.fonts.keys().next().value.load().then(function() {
+ return document.fonts.keys().next().value.loaded;
+ }, function() {
+ testFailed("Loading should succeed.");
+ finishJSTest();
+ }).then(function() {
+ document.fonts.keys().next().value.family = "AppliedFont";
+ shouldNotBe("document.getElementById(\"testElement\").offsetWidth", "originalWidth");
+ document.fonts.keys().next().value.family = "MyFont1";
+ shouldBe("document.getElementById(\"testElement\").offsetWidth", "originalWidth");
+ document.fonts.add(new FontFace("MyFont3", "url(\"../../resources/Ahem.otf\")", {}));
+ return document.fonts.load("MyFont3");
+ }, function() {
+ testFailed("The same promise should not fail and then succeed.");
+ finishJSTest();
+ }).then(function() {
+ testFailed("Should not be able to parse as a font: property.");
+ finishJSTest();
+ }, function() {
+ document.fonts.add(new FontFace("MyFont3", "url(\"../../resources/Ahem.ttf\")", {'variant': 'small-caps'}));
+ return document.fonts.load("50px MyFont3");
+ }).then(function(x) {
+ object = x;
+ shouldBe("object.length", "2");
+ shouldBeEqualToString("object[0].family", "MyFont3");
+ shouldBeEqualToString("object[1].family", "MyFont3");
+ object = undefined;
+ appliedFont = new FontFace("AppliedFont", "url(\"../../resources/Ahem.otf\")", {});
+ document.fonts.add(appliedFont);
+ return appliedFont.loaded;
+ }, function() {
+ testFailed("Loading should succeed.");
+ finishJSTest();
+ }).then(function(x) {
+ shouldNotBe("document.getElementById(\"testElement\").offsetWidth", "originalWidth");
+ document.fonts.delete(appliedFont);
+ shouldBe("document.getElementById(\"testElement\").offsetWidth", "originalWidth");
+ finishJSTest();
+ }, function() {
+ testFailed("Loading should succeed.");
+ finishJSTest();
+ });
+}
+if (window.GCController)
+ window.GCController.collect();
+startLoading();
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
\ No newline at end of file
+2016-02-22 Myles C. Maxfield <mmaxfield@apple.com>
+
+ [Font Loading] Split CSSFontSelector into a FontFaceSet implementation and the rest of the class
+ https://bugs.webkit.org/show_bug.cgi?id=153347
+
+ Reviewed by Antti Koivisto.
+
+ This patch implements the document.fonts Javascript object. It does so by briding the
+ already-existing FontFaceSet Javascript object with the CSSFontSelector WebCore object.
+ CSSFontSelector used to hold internal objects for each @font-face object in the
+ Document. These objects have been moved into CSSFontFaceSet, so CSSFontSelector simply
+ just owns an instance of a CSSFontFaceSet.
+
+ The lifetime of the FontFace and FontFaceSet objects is a little interesting: because
+ all the ownership references are inside the WebCore CSSFontFace{,Set} objects, the
+ higher-level Javascript FontFace{,Set} objects are held through a WeakPtr. This means
+ that if all the references to these higher-level objects go away, and you re-query the
+ document for its FontFace objects, you may get a new object (albeit with the same
+ state as a previous object). However, this won't occur if there are any references to
+ the old object, which means it is almost not observable.
+
+ This patch doesn't implement the relationship between the CSSOM and the FontFace
+ objects. Changing one should result in a change in the other, but that will be
+ implemented in a forthcoming patch.
+
+ This patch also doesn't alter the lifetime of the CSSFontSelector, which means that all
+ the Document's fonts may be destroyed and recreated from CSS. There are a few things
+ which can trigger this. A subsequent patch will make the CSSFontSelector outlive the
+ Document.
+
+ This patch does implement (and test) the ability to add a new FontFace to the Document
+ to cause a relayout, as well as changing properties of existing FontFace objects already
+ in the Document to cause a relayout.
+
+ Test: fast/text/font-face-set-document.html
+
+ * Modules/fetch/FetchHeaders.cpp:
+ (WebCore::FetchHeaders::Iterator::next): Pass an extra argument.
+ * Modules/fetch/FetchHeaders.h:
+ * bindings/js/JSKeyValueIterator.h: The ExecState is necessary to build an external
+ wrapper from an existing CSSFontFace object.
+ (WebCore::JSKeyValueIterator<JSWrapper>::next):
+ * css/CSSFontFace.cpp:
+ (WebCore::CSSFontFace::appendSources): Moved from CSSFontSelector.
+ (WebCore::CSSFontFace::CSSFontFace):
+ (WebCore::CSSFontFace::notifyClientsOfFontPropertyChange):
+ (WebCore::CSSFontFace::setFamilies):
+ (WebCore::CSSFontFace::calculateStyle): Shared code between CSSFontFaceSet and
+ CSSFontFace.
+ (WebCore::CSSFontFace::setStyle): Update to use calculateStyle().
+ (WebCore::CSSFontFace::calculateWeight): Ditto.
+ (WebCore::CSSFontFace::setWeight): Update to use caculateWeight().
+ (WebCore::CSSFontFace::setUnicodeRange): Notify clients.
+ (WebCore::CSSFontFace::setVariantLigatures): Ditto.
+ (WebCore::CSSFontFace::setVariantPosition): Ditto.
+ (WebCore::CSSFontFace::setVariantCaps): Ditto.
+ (WebCore::CSSFontFace::setVariantNumeric): Ditto.
+ (WebCore::CSSFontFace::setVariantAlternates): Ditto.
+ (WebCore::CSSFontFace::setVariantEastAsian): Ditto.
+ (WebCore::CSSFontFace::setFeatureSettings): Ditto.
+ (WebCore::CSSFontFace::removeClient):
+ (WebCore::CSSFontFace::wrapper): Build a new wrapper if one doesn't already
+ exist. Note that this requires an ExecState to create a promise.
+ (WebCore::CSSFontFace::setStatus):
+ (WebCore::CSSFontFace::fontLoaded):
+ (WebCore::CSSFontFace::pump):
+ (WebCore::CSSFontFace::font):
+ * css/CSSFontFace.h:
+ * css/CSSFontFaceSet.cpp:
+ (WebCore::CSSFontFaceSet::CSSFontFaceSet): Moved code from CSSFontSelector.
+ (WebCore::CSSFontFaceSet::~CSSFontFaceSet):
+ (WebCore::CSSFontFaceSet::addClient): This object can now have multiple
+ clients.
+ (WebCore::CSSFontFaceSet::removeClient):
+ (WebCore::CSSFontFaceSet::incrementActiveCount): Update for multiple clients.
+ (WebCore::CSSFontFaceSet::decrementActiveCount): Ditto.
+ (WebCore::CSSFontFaceSet::hasFace):
+ (WebCore::CSSFontFaceSet::registerLocalFontFacesForFamily): Moved from
+ CSSFontSelector.
+ (WebCore::CSSFontFaceSet::familyNameFromPrimitive): Ditto.
+ (WebCore::CSSFontFaceSet::addToFacesLookupTable): This helper function can
+ be used when a property of a FontFace is changed.
+ (WebCore::CSSFontFaceSet::add): Update to use addToFacesLookupTable().
+ (WebCore::CSSFontFaceSet::removeFromFacesLookupTable): Same as
+ addToFacesLookupTable().
+ (WebCore::CSSFontFaceSet::remove): Update to use removeFromFacesLookupTable().
+ (WebCore::CSSFontFaceSet::clear):
+ (WebCore::CSSFontFaceSet::operator[]):
+ (WebCore::computeFontTraitsMask): Moved from CSSFontSelector.
+ (WebCore::CSSFontFaceSet::matchingFaces): Update to use new data structures.
+ (WebCore::FontFaceComparator::FontFaceComparator): Moved from
+ CSSFontSelector.
+ (WebCore::FontFaceComparator::operator()):
+ (WebCore::CSSFontFaceSet::getFontFace): Update to use new data structures.
+ (WebCore::CSSFontFaceSet::fontStateChanged): Update to use multiple clients.
+ (WebCore::CSSFontFaceSet::fontPropertyChanged): We must update our internal
+ data structure if the family name changed.
+ (WebCore::extractFamilies): Deleted.
+ (WebCore::familiesIntersect): Deleted.
+ (WebCore::CSSFontFaceSet::load): Deleted.
+ (WebCore::CSSFontFaceSet::stateChanged): Deleted.
+ * css/CSSFontFaceSet.h: Now needs to be RefCounted. New data structures are
+ taken from CSSFontSelector.
+ (WebCore::CSSFontFaceSetClient::faceFinished):
+ (WebCore::CSSFontFaceSetClient::fontModified):
+ (WebCore::CSSFontFaceSetClient::startedLoading):
+ (WebCore::CSSFontFaceSetClient::completedLoading):
+ * css/CSSFontFaceSource.h:
+ * css/CSSFontSelector.cpp: Move code into CSSFontFaceSet.
+ (WebCore::CSSFontSelector::CSSFontSelector):
+ (WebCore::CSSFontSelector::~CSSFontSelector):
+ (WebCore::CSSFontSelector::fontFaceSet):
+ (WebCore::CSSFontSelector::isEmpty):
+ (WebCore::CSSFontSelector::addFontFaceRule):
+ (WebCore::CSSFontSelector::fontModified):
+ (WebCore::CSSFontSelector::fontRangesForFamily):
+ (WebCore::CSSFontSelector::clearDocument):
+ (WebCore::CSSFontSelector::appendSources): Deleted.
+ (WebCore::CSSFontSelector::familyNameFromPrimitive): Deleted.
+ (WebCore::CSSFontSelector::registerLocalFontFacesForFamily): Deleted.
+ (WebCore::FontFaceComparator::FontFaceComparator): Deleted.
+ (WebCore::FontFaceComparator::operator()): Deleted.
+ (WebCore::CSSFontSelector::getFontFace): Deleted.
+ * css/CSSFontSelector.h:
+ * css/CSSSegmentedFontFace.cpp:
+ (WebCore::CSSSegmentedFontFace::CSSSegmentedFontFace):
+ * css/CSSSegmentedFontFace.h:
+ * css/FontFace.cpp:
+ (WebCore::FontFace::create):
+ (WebCore::FontFace::FontFace):
+ (WebCore::FontFace::createWeakPtr):
+ (WebCore::FontFace::fontStateChanged):
+ (WebCore::FontFace::stateChanged): Deleted.
+ * css/FontFace.h:
+ * css/FontFaceSet.cpp:
+ (WebCore::FontFaceSet::create):
+ (WebCore::FontFaceSet::FontFaceSet):
+ (WebCore::FontFaceSet::~FontFaceSet):
+ (WebCore::FontFaceSet::Iterator::next):
+ (WebCore::FontFaceSet::has):
+ (WebCore::FontFaceSet::size):
+ (WebCore::FontFaceSet::add):
+ (WebCore::FontFaceSet::remove):
+ (WebCore::FontFaceSet::clear):
+ (WebCore::FontFaceSet::load):
+ (WebCore::FontFaceSet::check):
+ (WebCore::FontFaceSet::status):
+ (WebCore::FontFaceSet::canSuspendForDocumentSuspension):
+ (WebCore::FontFaceSet::faceFinished):
+ * css/FontFaceSet.h:
+ * css/FontFaceSet.idl:
+ * dom/Document.cpp:
+ (WebCore::Document::fonts):
+ * dom/Document.h:
+ * dom/Document.idl:
+ * svg/SVGFontFaceElement.h:
+
2016-02-22 Konstantin Tokarev <annulen@yandex.ru>
[cmake] Moved library setup code to WEBKIT_FRAMEWORK macro.
}
}
-bool FetchHeaders::Iterator::next(String& nextKey, String& nextValue)
+bool FetchHeaders::Iterator::next(JSC::ExecState&, String& nextKey, String& nextValue)
{
while (m_currentIndex < m_keys.size()) {
auto& key = m_keys[m_currentIndex++];
#include "HTTPHeaderMap.h"
+namespace JSC {
+class ExecState;
+}
+
namespace WebCore {
typedef int ExceptionCode;
class Iterator {
public:
explicit Iterator(FetchHeaders&);
- bool next(String& nextKey, String& nextValue);
+ bool next(JSC::ExecState&, String& nextKey, String& nextValue);
private:
Ref<FetchHeaders> m_headers;
typename JSWrapper::IteratorKey nextKey;
typename JSWrapper::IteratorValue nextValue;
auto iterator = wrapper->wrapped().createIterator();
- while (!iterator.next(nextKey, nextValue)) {
+ while (!iterator.next(state, nextKey, nextValue)) {
JSC::MarkedArgumentBuffer arguments;
arguments.append(toJS(&state, wrapper->globalObject(), nextValue));
arguments.append(toJS(&state, wrapper->globalObject(), nextKey));
{
typename JSWrapper::IteratorKey nextKey;
typename JSWrapper::IteratorValue nextValue;
- if (m_iterator.next(nextKey, nextValue)) {
+ if (m_iterator.next(state, nextKey, nextValue)) {
value = JSC::jsUndefined();
return true;
}
my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($type);
return "${svgNativeType}*" if $svgNativeType;
return "RefPtr<DOMStringList>" if $type eq "DOMStringList";
+ return "RefPtr<FontFace>" if $type eq "FontFace";
return "RefPtr<${type}>" if $codeGenerator->IsTypedArrayType($type) and not $type eq "ArrayBuffer";
return $nativeType{$type} if exists $nativeType{$type};
#include "CSSFontFace.h"
#include "CSSFontFaceSource.h"
+#include "CSSFontFaceSrcValue.h"
#include "CSSFontFamily.h"
#include "CSSFontFeatureValue.h"
#include "CSSFontSelector.h"
#include "Document.h"
#include "Font.h"
#include "FontDescription.h"
-#include "FontLoader.h"
+#include "FontFace.h"
#include "FontVariantBuilder.h"
#include "RuntimeEnabledFeatures.h"
+#include "Settings.h"
#include "StyleProperties.h"
+#include "StyleRule.h"
namespace WebCore {
-CSSFontFace::CSSFontFace(CSSFontSelector& fontSelector, FontFace* wrapper, bool isLocalFallback)
+void CSSFontFace::appendSources(CSSFontFace& fontFace, CSSValueList& srcList, Document* document, bool isInitiatingElementInUserAgentShadowTree)
+{
+ for (auto& src : srcList) {
+ // An item in the list either specifies a string (local font name) or a URL (remote font to download).
+ CSSFontFaceSrcValue& item = downcast<CSSFontFaceSrcValue>(src.get());
+ std::unique_ptr<CSSFontFaceSource> source;
+ SVGFontFaceElement* fontFaceElement = nullptr;
+ bool foundSVGFont = false;
+
+#if ENABLE(SVG_FONTS)
+ foundSVGFont = item.isSVGFontFaceSrc() || item.svgFontFaceElement();
+ fontFaceElement = item.svgFontFaceElement();
+#endif
+ if (!item.isLocal()) {
+ Settings* settings = document ? document->settings() : nullptr;
+ bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
+ if (allowDownloading && item.isSupportedFormat() && document) {
+ if (CachedFont* cachedFont = item.cachedFont(document, foundSVGFont, isInitiatingElementInUserAgentShadowTree))
+ source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), cachedFont);
+ }
+ } else
+ source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), nullptr, fontFaceElement);
+
+ if (source)
+ fontFace.adoptSource(WTFMove(source));
+ }
+ fontFace.sourcesPopulated();
+}
+
+CSSFontFace::CSSFontFace(CSSFontSelector* fontSelector, StyleRuleFontFace* cssConnection, FontFace* wrapper, bool isLocalFallback)
: m_fontSelector(fontSelector)
- , m_wrapper(wrapper)
+ , m_cssConnection(cssConnection)
+ , m_wrapper(wrapper ? wrapper->createWeakPtr() : WeakPtr<FontFace>())
, m_isLocalFallback(isLocalFallback)
{
}
{
}
+void CSSFontFace::notifyClientsOfFontPropertyChange()
+{
+ auto clientsCopy = m_clients;
+ for (auto* client : clientsCopy) {
+ if (m_clients.contains(client))
+ client->fontPropertyChanged(*this);
+ }
+}
+
bool CSSFontFace::setFamilies(CSSValue& family)
{
if (!is<CSSValueList>(family))
if (!familyList.length())
return false;
+ RefPtr<CSSValueList> oldFamilies = m_families;
m_families = &familyList;
+
+ auto clientsCopy = m_clients;
+ for (auto* client : clientsCopy) {
+ if (m_clients.contains(client))
+ client->fontPropertyChanged(*this, oldFamilies.get());
+ }
+
return true;
}
-bool CSSFontFace::setStyle(CSSValue& style)
+Optional<FontTraitsMask> CSSFontFace::calculateStyleMask(CSSValue& style)
{
if (!is<CSSPrimitiveValue>(style))
- return false;
+ return Nullopt;
- unsigned styleMask = 0;
switch (downcast<CSSPrimitiveValue>(style).getValueID()) {
case CSSValueNormal:
- styleMask = FontStyleNormalMask;
- break;
+ return FontStyleNormalMask;
case CSSValueItalic:
case CSSValueOblique:
- styleMask = FontStyleItalicMask;
- break;
+ return FontStyleItalicMask;
default:
- styleMask = FontStyleNormalMask;
- break;
+ return FontStyleNormalMask;
}
- m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontStyleMask)) | styleMask);
- return true;
+ return FontStyleNormalMask;
}
-bool CSSFontFace::setWeight(CSSValue& weight)
+bool CSSFontFace::setStyle(CSSValue& style)
+{
+ if (auto mask = calculateStyleMask(style)) {
+ m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontStyleMask)) | mask.value());
+
+ notifyClientsOfFontPropertyChange();
+
+ return true;
+ }
+ return false;
+}
+
+Optional<FontTraitsMask> CSSFontFace::calculateWeightMask(CSSValue& weight)
{
if (!is<CSSPrimitiveValue>(weight))
- return false;
+ return Nullopt;
- unsigned weightMask = 0;
switch (downcast<CSSPrimitiveValue>(weight).getValueID()) {
case CSSValueBold:
case CSSValueBolder:
case CSSValue700:
- weightMask = FontWeight700Mask;
- break;
+ return FontWeight700Mask;
case CSSValueNormal:
case CSSValue400:
- weightMask = FontWeight400Mask;
- break;
+ return FontWeight400Mask;
case CSSValue900:
- weightMask = FontWeight900Mask;
- break;
+ return FontWeight900Mask;
case CSSValue800:
- weightMask = FontWeight800Mask;
- break;
+ return FontWeight800Mask;
case CSSValue600:
- weightMask = FontWeight600Mask;
- break;
+ return FontWeight600Mask;
case CSSValue500:
- weightMask = FontWeight500Mask;
- break;
+ return FontWeight500Mask;
case CSSValue300:
- weightMask = FontWeight300Mask;
- break;
+ return FontWeight300Mask;
case CSSValueLighter:
case CSSValue200:
- weightMask = FontWeight200Mask;
- break;
+ return FontWeight200Mask;
case CSSValue100:
- weightMask = FontWeight100Mask;
- break;
+ return FontWeight100Mask;
default:
- weightMask = FontWeight400Mask;
- break;
+ return FontWeight400Mask;
}
- m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontWeightMask)) | weightMask);
- return true;
+ return FontWeight400Mask;
+}
+
+bool CSSFontFace::setWeight(CSSValue& weight)
+{
+ if (auto mask = calculateWeightMask(weight)) {
+ m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontWeightMask)) | mask.value());
+
+ notifyClientsOfFontPropertyChange();
+
+ return true;
+ }
+
+ return false;
}
bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
CSSUnicodeRangeValue& range = downcast<CSSUnicodeRangeValue>(rangeValue.get());
m_ranges.append(UnicodeRange(range.from(), range.to()));
}
+
+ notifyClientsOfFontPropertyChange();
+
return true;
}
m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures;
m_variantSettings.historicalLigatures = ligatures.historicalLigatures;
m_variantSettings.contextualAlternates = ligatures.contextualAlternates;
+
+ notifyClientsOfFontPropertyChange();
+
return true;
}
if (!is<CSSPrimitiveValue>(variantPosition))
return false;
m_variantSettings.position = downcast<CSSPrimitiveValue>(variantPosition);
+
+ notifyClientsOfFontPropertyChange();
+
return true;
}
if (!is<CSSPrimitiveValue>(variantCaps))
return false;
m_variantSettings.caps = downcast<CSSPrimitiveValue>(variantCaps);
+
+ notifyClientsOfFontPropertyChange();
+
return true;
}
m_variantSettings.numericFraction = numeric.fraction;
m_variantSettings.numericOrdinal = numeric.ordinal;
m_variantSettings.numericSlashedZero = numeric.slashedZero;
+
+ notifyClientsOfFontPropertyChange();
+
return true;
}
if (!is<CSSPrimitiveValue>(variantAlternates))
return false;
m_variantSettings.alternates = downcast<CSSPrimitiveValue>(variantAlternates);
+
+ notifyClientsOfFontPropertyChange();
+
return true;
}
m_variantSettings.eastAsianVariant = eastAsian.variant;
m_variantSettings.eastAsianWidth = eastAsian.width;
m_variantSettings.eastAsianRuby = eastAsian.ruby;
+
+ notifyClientsOfFontPropertyChange();
+
return true;
}
CSSFontFeatureValue& feature = downcast<CSSFontFeatureValue>(rangeValue.get());
m_featureSettings.insert(FontFeature(feature.tag(), feature.value()));
}
+
+ notifyClientsOfFontPropertyChange();
+
return true;
}
void CSSFontFace::removeClient(Client& client)
{
+ ASSERT(m_clients.contains(&client));
m_clients.remove(&client);
}
+Ref<FontFace> CSSFontFace::wrapper(JSC::ExecState& execState)
+{
+ if (m_wrapper)
+ return Ref<FontFace>(*m_wrapper.get());
+
+ Ref<FontFace> wrapper = FontFace::create(execState, *this);
+ switch (m_status) {
+ case Status::Pending:
+ break;
+ case Status::Loading:
+ wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
+ break;
+ case Status::TimedOut:
+ wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
+ wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut);
+ break;
+ case Status::Success:
+ wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
+ wrapper->fontStateChanged(*this, Status::Pending, Status::Success);
+ break;
+ case Status::Failure:
+ wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
+ wrapper->fontStateChanged(*this, Status::Pending, Status::Failure);
+ break;
+ }
+ m_wrapper = wrapper->createWeakPtr();
+ return wrapper;
+}
+
void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source)
{
m_sources.append(WTFMove(source));
break;
}
- for (auto& client : m_clients)
- client->stateChanged(*this, m_status, newStatus);
+ for (auto* client : m_clients)
+ client->fontStateChanged(*this, m_status, newStatus);
m_status = newStatus;
}
if (m_sourcesPopulated)
pump();
+ ASSERT(m_fontSelector);
m_fontSelector->fontLoaded();
- for (auto& client : m_clients)
+ for (auto* client : m_clients)
client->fontLoaded(*this);
}
if (source->status() == CSSFontFaceSource::Status::Pending) {
ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
+ ASSERT(m_fontSelector);
if (m_status == Status::Pending)
setStatus(Status::Loading);
- source->load(m_fontSelector.get());
+ source->load(*m_fontSelector);
}
switch (source->status()) {
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());
+ if (source->status() == CSSFontFaceSource::Status::Pending) {
+ ASSERT(m_fontSelector);
+ source->load(*m_fontSelector);
+ }
switch (source->status()) {
case CSSFontFaceSource::Status::Pending:
#define CSSFontFace_h
#include "CSSFontFaceRule.h"
-#include "CSSFontFaceSource.h"
#include "FontFeatureSettings.h"
#include "TextFlags.h"
#include <memory>
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
+
+namespace JSC {
+class ExecState;
+}
namespace WebCore {
+class CSSFontFaceSource;
+class CSSFontSelector;
class CSSSegmentedFontFace;
class CSSValue;
class CSSValueList;
+class Document;
class FontDescription;
class Font;
class FontFace;
-// FIXME: This class does not need to be reference counted.
class CSSFontFace final : public RefCounted<CSSFontFace> {
public:
- static Ref<CSSFontFace> create(CSSFontSelector& fontSelector, FontFace* wrapper = nullptr, bool isLocalFallback = false)
+ static Ref<CSSFontFace> create(CSSFontSelector* fontSelector, StyleRuleFontFace* cssConnection = nullptr, FontFace* wrapper = nullptr, bool isLocalFallback = false)
{
- return adoptRef(*new CSSFontFace(fontSelector, wrapper, isLocalFallback));
+ return adoptRef(*new CSSFontFace(fontSelector, cssConnection, wrapper, isLocalFallback));
}
virtual ~CSSFontFace();
void setTraitsMask(FontTraitsMask traitsMask) { m_traitsMask = traitsMask; }
bool isLocalFallback() const { return m_isLocalFallback; }
Status status() const { return m_status; }
+ StyleRuleFontFace* cssConnection() const { return m_cssConnection.get(); }
+
+ static Optional<FontTraitsMask> calculateStyleMask(CSSValue& style);
+ static Optional<FontTraitsMask> calculateWeightMask(CSSValue& weight);
class Client;
void addClient(Client&);
void load();
RefPtr<Font> font(const FontDescription&, bool syntheticBold, bool syntheticItalic);
+ static void appendSources(CSSFontFace&, CSSValueList&, Document*, bool isInitiatingElementInUserAgentShadowTree);
+
class Client {
public:
virtual ~Client() { }
virtual void fontLoaded(CSSFontFace&) { };
- virtual void stateChanged(CSSFontFace&, Status oldState, Status newState) { UNUSED_PARAM(oldState); UNUSED_PARAM(newState); };
+ virtual void fontStateChanged(CSSFontFace&, Status oldState, Status newState) { UNUSED_PARAM(oldState); UNUSED_PARAM(newState); };
+ virtual void fontPropertyChanged(CSSFontFace&, CSSValueList* oldFamilies = nullptr) { UNUSED_PARAM(oldFamilies); };
};
// Pending => Loading => TimedOut
UChar32 m_to;
};
- FontFace* wrapper() const { return m_wrapper; }
+ // We don't guarantee that the FontFace wrapper will be the same every time you ask for it.
+ Ref<FontFace> wrapper(JSC::ExecState&);
#if ENABLE(SVG_FONTS)
bool hasSVGFontFaceSource() const;
#endif
private:
- CSSFontFace(CSSFontSelector&, FontFace*, bool isLocalFallback);
+ CSSFontFace(CSSFontSelector*, StyleRuleFontFace*, FontFace*, bool isLocalFallback);
size_t pump();
void setStatus(Status);
+ void notifyClientsOfFontPropertyChange();
RefPtr<CSSValueList> m_families;
FontTraitsMask m_traitsMask { static_cast<FontTraitsMask>(FontStyleNormalMask | FontWeight400Mask) };
Vector<UnicodeRange> m_ranges;
- HashSet<Client*> m_clients;
- Ref<CSSFontSelector> m_fontSelector;
- FontFace* m_wrapper;
FontFeatureSettings m_featureSettings;
FontVariantSettings m_variantSettings;
Vector<std::unique_ptr<CSSFontFaceSource>> m_sources;
+ RefPtr<CSSFontSelector> m_fontSelector;
+ RefPtr<StyleRuleFontFace> m_cssConnection;
+ HashSet<Client*> m_clients;
+ WeakPtr<FontFace> m_wrapper;
Status m_status { Status::Pending };
bool m_isLocalFallback { false };
bool m_sourcesPopulated { false };
#include "config.h"
#include "CSSFontFaceSet.h"
+#include "CSSFontFaceSource.h"
#include "CSSFontFamily.h"
#include "CSSFontSelector.h"
#include "CSSParser.h"
#include "CSSPrimitiveValue.h"
+#include "CSSSegmentedFontFace.h"
#include "CSSValueList.h"
+#include "CSSValuePool.h"
+#include "FontCache.h"
#include "StyleProperties.h"
namespace WebCore {
-CSSFontFaceSet::CSSFontFaceSet(CSSFontFaceSetClient& client)
- : m_client(client)
+CSSFontFaceSet::CSSFontFaceSet()
{
}
{
for (auto& face : m_faces)
face->removeClient(*this);
+
+ for (auto& pair : m_locallyInstalledFacesLookupTable) {
+ for (auto& face : pair.value)
+ face->removeClient(*this);
+ }
+}
+
+void CSSFontFaceSet::addClient(CSSFontFaceSetClient& client)
+{
+ m_clients.add(&client);
+}
+
+void CSSFontFaceSet::removeClient(CSSFontFaceSetClient& client)
+{
+ ASSERT(m_clients.contains(&client));
+ m_clients.remove(&client);
}
void CSSFontFaceSet::incrementActiveCount()
++m_activeCount;
if (m_activeCount == 1) {
m_status = Status::Loading;
- m_client.startedLoading();
+ for (auto* client : m_clients)
+ client->startedLoading();
}
}
--m_activeCount;
if (!m_activeCount) {
m_status = Status::Loaded;
- m_client.completedLoading();
+ for (auto* client : m_clients)
+ client->completedLoading();
}
}
if (myFace.ptr() == &face)
return true;
}
+
return false;
}
+void CSSFontFaceSet::registerLocalFontFacesForFamily(const String& familyName)
+{
+ ASSERT(!m_locallyInstalledFacesLookupTable.contains(familyName));
+
+ Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
+ if (traitsMasks.isEmpty())
+ return;
+
+ Vector<Ref<CSSFontFace>> faces;
+ for (auto mask : traitsMasks) {
+ Ref<CSSFontFace> face = CSSFontFace::create(nullptr, nullptr, nullptr, true);
+
+ Ref<CSSValueList> familyList = CSSValueList::createCommaSeparated();
+ familyList->append(CSSValuePool::singleton().createFontFamilyValue(familyName));
+ face->setFamilies(familyList.get());
+ face->setTraitsMask(mask);
+ face->adoptSource(std::make_unique<CSSFontFaceSource>(face.get(), familyName));
+ ASSERT(!face->allSourcesFailed());
+ faces.append(WTFMove(face));
+ }
+ m_locallyInstalledFacesLookupTable.add(familyName, WTFMove(faces));
+}
+
+String CSSFontFaceSet::familyNameFromPrimitive(const CSSPrimitiveValue& value)
+{
+ if (value.isFontFamily())
+ return value.fontFamily().familyName;
+ if (!value.isValueID())
+ return { };
+
+ // We need to use the raw text for all the generic family types, since @font-face is a way of actually
+ // defining what font to use for those types.
+ switch (value.getValueID()) {
+ case CSSValueSerif:
+ return serifFamily;
+ case CSSValueSansSerif:
+ return sansSerifFamily;
+ case CSSValueCursive:
+ return cursiveFamily;
+ case CSSValueFantasy:
+ return fantasyFamily;
+ case CSSValueMonospace:
+ return monospaceFamily;
+ case CSSValueWebkitPictograph:
+ return pictographFamily;
+ default:
+ return { };
+ }
+}
+
+void CSSFontFaceSet::addToFacesLookupTable(CSSFontFace& face)
+{
+ if (!face.families())
+ return;
+
+ for (auto& item : *face.families()) {
+ String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
+ if (familyName.isEmpty())
+ continue;
+
+ auto addResult = m_facesLookupTable.add(familyName, Vector<Ref<CSSFontFace>>());
+ auto& familyFontFaces = addResult.iterator->value;
+ if (addResult.isNewEntry) {
+ // m_locallyInstalledFontFaces grows without bound, eventually encorporating every font installed on the system.
+ // This is by design.
+ registerLocalFontFacesForFamily(familyName);
+ familyFontFaces = { };
+ }
+
+ familyFontFaces.append(face);
+ }
+}
+
void CSSFontFaceSet::add(CSSFontFace& face)
{
ASSERT(!hasFace(face));
- m_faces.append(face);
+ for (auto* client : m_clients)
+ client->fontModified();
+
face.addClient(*this);
+ m_cache.clear();
+
+ if (face.cssConnection())
+ m_faces.insert(m_facesPartitionIndex++, face);
+ else
+ m_faces.append(face);
+
+ addToFacesLookupTable(face);
+
if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
incrementActiveCount();
}
+void CSSFontFaceSet::removeFromFacesLookupTable(const CSSFontFace& face, const CSSValueList& familiesToSearchFor)
+{
+ for (auto& item : familiesToSearchFor) {
+ String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
+ if (familyName.isEmpty())
+ continue;
+
+ auto iterator = m_facesLookupTable.find(familyName);
+ ASSERT(iterator != m_facesLookupTable.end());
+ bool found = false;
+ for (size_t i = 0; i < iterator->value.size(); ++i) {
+ if (iterator->value[i].ptr() == &face) {
+ found = true;
+ iterator->value.remove(i);
+ break;
+ }
+ }
+ ASSERT_UNUSED(found, found);
+ if (!iterator->value.size())
+ m_facesLookupTable.remove(iterator);
+ }
+}
+
void CSSFontFaceSet::remove(const CSSFontFace& face)
{
+ m_cache.clear();
+
+ for (auto* client : m_clients)
+ client->fontModified();
+
+ if (face.families())
+ removeFromFacesLookupTable(face, *face.families());
+
for (size_t i = 0; i < m_faces.size(); ++i) {
if (m_faces[i].ptr() == &face) {
+ if (i < m_facesPartitionIndex)
+ --m_facesPartitionIndex;
m_faces[i]->removeClient(*this);
m_faces.remove(i);
if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
ASSERT_NOT_REACHED();
}
-static HashSet<String> extractFamilies(const CSSValueList& list)
+void CSSFontFaceSet::clear()
{
- HashSet<String> result;
- for (auto& family : list) {
- const CSSPrimitiveValue& primitive = downcast<CSSPrimitiveValue>(family.get());
- if (!primitive.isFontFamily())
- continue;
- result.add(primitive.fontFamily().familyName);
- }
- return result;
+ m_faces.clear();
+ m_facesLookupTable.clear();
+ m_locallyInstalledFacesLookupTable.clear();
+ m_cache.clear();
}
-static bool familiesIntersect(const CSSFontFace& face, const CSSValueList& request)
+CSSFontFace& CSSFontFaceSet::operator[](size_t i)
{
- if (!face.families())
- return false;
+ ASSERT(i < faceCount());
+ return m_faces[i];
+}
- HashSet<String> faceFamilies = extractFamilies(*face.families());
- HashSet<String> requestFamilies = extractFamilies(request);
- for (auto& family1 : faceFamilies) {
- if (requestFamilies.contains(family1))
- return true;
- }
- return false;
+static Optional<FontTraitsMask> computeFontTraitsMask(MutableStyleProperties& style)
+{
+ RefPtr<CSSValue> styleValue = style.getPropertyCSSValue(CSSPropertyFontStyle).get();
+ if (!styleValue)
+ styleValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
+
+ FontTraitsMask styleMask;
+ if (auto styleMaskOptional = CSSFontFace::calculateStyleMask(*styleValue))
+ styleMask = styleMaskOptional.value();
+ else
+ return Nullopt;
+
+ RefPtr<CSSValue> weightValue = style.getPropertyCSSValue(CSSPropertyFontWeight).get();
+ if (!weightValue)
+ weightValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
+
+ FontTraitsMask weightMask;
+ if (auto weightMaskOptional = CSSFontFace::calculateWeightMask(*weightValue))
+ weightMask = weightMaskOptional.value();
+ else
+ return Nullopt;
+
+ return static_cast<FontTraitsMask>(static_cast<unsigned>(styleMask) | static_cast<unsigned>(weightMask));
}
Vector<std::reference_wrapper<CSSFontFace>> CSSFontFaceSet::matchingFaces(const String& font, const String&, ExceptionCode& ec)
ec = SYNTAX_ERR;
return result;
}
- bool desiredStyleIsNormal = true;
- if (RefPtr<CSSValue> desiredStyle = style->getPropertyCSSValue(CSSPropertyFontStyle)) {
- if (!is<CSSPrimitiveValue>(*desiredStyle)) {
- ec = SYNTAX_ERR;
- return result;
- }
- desiredStyleIsNormal = downcast<CSSPrimitiveValue>(*desiredStyle).getValueID() == CSSValueNormal;
+
+ FontTraitsMask fontTraitsMask;
+ if (auto maskOptional = computeFontTraitsMask(style.get()))
+ fontTraitsMask = maskOptional.value();
+ else {
+ ec = SYNTAX_ERR;
+ return result;
}
+
RefPtr<CSSValue> family = style->getPropertyCSSValue(CSSPropertyFontFamily);
if (!is<CSSValueList>(family.get())) {
ec = SYNTAX_ERR;
}
CSSValueList& familyList = downcast<CSSValueList>(*family);
- // Match CSSFontSelector::getFontFace()
- for (auto& face : m_faces) {
- if (!familiesIntersect(face, familyList) || (desiredStyleIsNormal && !(face->traitsMask() & FontStyleNormalMask)))
+ HashSet<AtomicString> uniqueFamilies;
+ for (auto& family : familyList) {
+ const CSSPrimitiveValue& primitive = downcast<CSSPrimitiveValue>(family.get());
+ if (!primitive.isFontFamily())
continue;
- result.append(face.get());
+ uniqueFamilies.add(primitive.fontFamily().familyName);
}
- return result;
-}
-void CSSFontFaceSet::load(const String& font, const String& text, ExceptionCode& ec)
-{
- auto matchingFaces = this->matchingFaces(font, text, ec);
- if (ec)
- return;
+ for (auto& family : uniqueFamilies) {
+ CSSSegmentedFontFace* faces = getFontFace(fontTraitsMask, family);
+ if (!faces)
+ continue;
+ for (auto& constituentFace : faces->constituentFaces())
+ result.append(constituentFace.get());
+ }
- for (auto& face : matchingFaces)
- face.get().load();
+ return result;
}
bool CSSFontFaceSet::check(const String& font, const String& text, ExceptionCode& ec)
return true;
}
-void CSSFontFaceSet::stateChanged(CSSFontFace& face, CSSFontFace::Status oldState, CSSFontFace::Status newState)
+static bool fontFaceComparator(FontTraitsMask desiredTraitsMaskForComparison, const CSSFontFace& first, const CSSFontFace& second)
+{
+ FontTraitsMask firstTraitsMask = first.traitsMask();
+ FontTraitsMask secondTraitsMask = second.traitsMask();
+
+ bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
+ bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
+
+ if (firstHasDesiredStyle != secondHasDesiredStyle)
+ return firstHasDesiredStyle;
+
+ if ((desiredTraitsMaskForComparison & FontStyleItalicMask) && !first.isLocalFallback() && !second.isLocalFallback()) {
+ // Prefer a font that has indicated that it can only support italics to a font that claims to support
+ // all styles. The specialized font is more likely to be the one the author wants used.
+ bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask);
+ bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask);
+ if (firstRequiresItalics != secondRequiresItalics)
+ return firstRequiresItalics;
+ }
+
+ if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
+ return false;
+ if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
+ return true;
+
+ // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
+ // - If the desired weight is less than 400, weights below the desired weight are checked in descending order followed by weights above the desired weight in ascending order until a match is found.
+ // - If the desired weight is greater than 500, weights above the desired weight are checked in ascending order followed by weights below the desired weight in descending order until a match is found.
+ // - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
+ // - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
+
+ static const unsigned fallbackRuleSets = 9;
+ static const unsigned rulesPerSet = 8;
+ static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
+ { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
+ { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
+ { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
+ { FontWeight500Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
+ { FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
+ { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
+ { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
+ { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
+ { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
+ };
+
+ unsigned ruleSetIndex = 0;
+ for (; !(desiredTraitsMaskForComparison & (1 << (FontWeight100Bit + ruleSetIndex))); ruleSetIndex++) { }
+
+ const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
+ for (unsigned i = 0; i < rulesPerSet; ++i) {
+ if (secondTraitsMask & weightFallbackRule[i])
+ return false;
+ if (firstTraitsMask & weightFallbackRule[i])
+ return true;
+ }
+
+ return false;
+}
+
+CSSSegmentedFontFace* CSSFontFaceSet::getFontFace(FontTraitsMask traitsMask, const AtomicString& family)
+{
+ auto iterator = m_facesLookupTable.find(family);
+ if (iterator == m_facesLookupTable.end())
+ return nullptr;
+ auto& familyFontFaces = iterator->value;
+
+ auto& segmentedFontFaceCache = m_cache.add(family, HashMap<unsigned, std::unique_ptr<CSSSegmentedFontFace>>()).iterator->value;
+
+ auto& face = segmentedFontFaceCache.add(traitsMask, nullptr).iterator->value;
+ if (face)
+ return face.get();
+
+ face = std::make_unique<CSSSegmentedFontFace>();
+
+ Vector<std::reference_wrapper<CSSFontFace>, 32> candidateFontFaces;
+ for (int i = familyFontFaces.size() - 1; i >= 0; --i) {
+ CSSFontFace& candidate = familyFontFaces[i];
+ unsigned candidateTraitsMask = candidate.traitsMask();
+ if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
+ continue;
+ candidateFontFaces.append(candidate);
+ }
+
+ auto localIterator = m_locallyInstalledFacesLookupTable.find(family);
+ if (localIterator != m_locallyInstalledFacesLookupTable.end()) {
+ for (auto& candidate : localIterator->value) {
+ unsigned candidateTraitsMask = candidate->traitsMask();
+ if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
+ continue;
+ candidateFontFaces.append(candidate);
+ }
+ }
+
+ std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [traitsMask](const CSSFontFace& first, const CSSFontFace& second) {
+ return fontFaceComparator(traitsMask, first, second);
+ });
+ for (auto& candidate : candidateFontFaces)
+ face->appendFontFace(candidate.get());
+
+ return face.get();
+}
+
+void CSSFontFaceSet::fontStateChanged(CSSFontFace& face, CSSFontFace::Status oldState, CSSFontFace::Status newState)
{
ASSERT(hasFace(face));
if (oldState == CSSFontFace::Status::Pending) {
}
if (newState == CSSFontFace::Status::Success || newState == CSSFontFace::Status::Failure) {
ASSERT(oldState == CSSFontFace::Status::Loading || oldState == CSSFontFace::Status::TimedOut);
- m_client.faceFinished(face, newState);
+ for (auto* client : m_clients)
+ client->faceFinished(face, newState);
decrementActiveCount();
}
}
+void CSSFontFaceSet::fontPropertyChanged(CSSFontFace& face, CSSValueList* oldFamilies)
+{
+ m_cache.clear();
+
+ if (oldFamilies) {
+ removeFromFacesLookupTable(face, *oldFamilies);
+ addToFacesLookupTable(face);
+ }
+
+ for (auto* client : m_clients)
+ client->fontModified();
+}
+
}
#define CSSFontFaceSet_h
#include "CSSFontFace.h"
+#include <wtf/HashMap.h>
#include <wtf/Vector.h>
+#include <wtf/text/StringHash.h>
namespace WebCore {
+class CSSPrimitiveValue;
class FontFaceSet;
class CSSFontFaceSetClient {
public:
virtual ~CSSFontFaceSetClient() { }
- virtual void faceFinished(CSSFontFace&, CSSFontFace::Status) = 0;
- virtual void startedLoading() = 0;
- virtual void completedLoading() = 0;
+ virtual void faceFinished(CSSFontFace&, CSSFontFace::Status) { };
+ virtual void fontModified() { };
+ virtual void startedLoading() { };
+ virtual void completedLoading() { };
};
-class CSSFontFaceSet final : public CSSFontFace::Client {
+class CSSFontFaceSet final : public RefCounted<CSSFontFaceSet>, public CSSFontFace::Client {
public:
- CSSFontFaceSet(CSSFontFaceSetClient&);
+ static Ref<CSSFontFaceSet> create()
+ {
+ return adoptRef(*new CSSFontFaceSet());
+ }
~CSSFontFaceSet();
+ void addClient(CSSFontFaceSetClient&);
+ void removeClient(CSSFontFaceSetClient&);
+
bool hasFace(const CSSFontFace&) const;
size_t faceCount() const { return m_faces.size(); }
void add(CSSFontFace&);
void remove(const CSSFontFace&);
- const CSSFontFace& operator[](size_t i) const { return m_faces[i]; }
+ void clear();
+ CSSFontFace& operator[](size_t i);
- void load(const String& font, const String& text, ExceptionCode&);
bool check(const String& font, const String& text, ExceptionCode&);
+ CSSSegmentedFontFace* getFontFace(FontTraitsMask, const AtomicString& family);
+
enum class Status {
Loading,
Loaded
Vector<std::reference_wrapper<CSSFontFace>> matchingFaces(const String& font, const String& text, ExceptionCode&);
private:
+ CSSFontFaceSet();
+
+ void removeFromFacesLookupTable(const CSSFontFace&, const CSSValueList& familiesToSearchFor);
+ void addToFacesLookupTable(CSSFontFace&);
+
void incrementActiveCount();
void decrementActiveCount();
- virtual void stateChanged(CSSFontFace&, CSSFontFace::Status oldState, CSSFontFace::Status newState) override;
+ virtual void fontStateChanged(CSSFontFace&, CSSFontFace::Status oldState, CSSFontFace::Status newState) override;
+ virtual void fontPropertyChanged(CSSFontFace&, CSSValueList* oldFamilies = nullptr) override;
+
+ void registerLocalFontFacesForFamily(const String&);
+
+ static String familyNameFromPrimitive(const CSSPrimitiveValue&);
- Vector<Ref<CSSFontFace>> m_faces;
+ // m_faces should hold all the same fonts as the ones inside inside m_facesLookupTable.
+ Vector<Ref<CSSFontFace>> m_faces; // We should investigate moving m_faces to FontFaceSet and making it reference FontFaces. This may clean up the font loading design.
+ HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_facesLookupTable;
+ HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_locallyInstalledFacesLookupTable;
+ HashMap<String, HashMap<unsigned, std::unique_ptr<CSSSegmentedFontFace>>, ASCIICaseInsensitiveHash> m_cache;
+ size_t m_facesPartitionIndex { 0 }; // All entries in m_faces before this index are CSS-connected.
Status m_status { Status::Loaded };
- CSSFontFaceSetClient& m_client;
+ HashSet<CSSFontFaceSetClient*> m_clients;
unsigned m_activeCount { 0 };
};
class CSSFontFaceSource final : public CachedFontClient {
WTF_MAKE_FAST_ALLOCATED;
public:
+ CSSFontFaceSource(CSSFontFace& owner, const String& familyNameOrURI, CachedFont* = nullptr, SVGFontFaceElement* = nullptr);
+ virtual ~CSSFontFaceSource();
// => Success
// //
Success,
Failure
};
-
- CSSFontFaceSource(CSSFontFace& owner, const String& familyNameOrURI, CachedFont* = nullptr, SVGFontFaceElement* = nullptr);
- virtual ~CSSFontFaceSource();
-
Status status() const { return m_status; }
const AtomicString& familyNameOrURI() const { return m_familyNameOrURI; }
#include "CSSFontFace.h"
#include "CSSFontFaceRule.h"
#include "CSSFontFaceSource.h"
-#include "CSSFontFaceSrcValue.h"
#include "CSSFontFamily.h"
#include "CSSFontFeatureValue.h"
#include "CSSPrimitiveValue.h"
#include "Document.h"
#include "Font.h"
#include "FontCache.h"
+#include "FontFaceSet.h"
#include "FontVariantBuilder.h"
#include "Frame.h"
#include "FrameLoader.h"
CSSFontSelector::CSSFontSelector(Document& document)
: m_document(&document)
+ , m_cssFontFaceSet(CSSFontFaceSet::create())
, m_beginLoadingTimer(*this, &CSSFontSelector::beginLoadTimerFired)
, m_uniqueId(++fontSelectorId)
, m_version(0)
ASSERT(m_document);
FontCache::singleton().addClient(*this);
+ m_cssFontFaceSet->addClient(*this);
}
CSSFontSelector::~CSSFontSelector()
{
clearDocument();
+ m_cssFontFaceSet->removeClient(*this);
FontCache::singleton().removeClient(*this);
}
-bool CSSFontSelector::isEmpty() const
+FontFaceSet& CSSFontSelector::fontFaceSet()
{
- return m_fonts.isEmpty();
-}
-
-void CSSFontSelector::appendSources(CSSFontFace& fontFace, CSSValueList& srcList, Document* document, bool isInitiatingElementInUserAgentShadowTree)
-{
- for (auto& src : srcList) {
- // An item in the list either specifies a string (local font name) or a URL (remote font to download).
- CSSFontFaceSrcValue& item = downcast<CSSFontFaceSrcValue>(src.get());
- std::unique_ptr<CSSFontFaceSource> source;
- SVGFontFaceElement* fontFaceElement = nullptr;
- bool foundSVGFont = false;
-
-#if ENABLE(SVG_FONTS)
- foundSVGFont = item.isSVGFontFaceSrc() || item.svgFontFaceElement();
- fontFaceElement = item.svgFontFaceElement();
-#endif
- if (!item.isLocal()) {
- Settings* settings = document ? document->settings() : nullptr;
- bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
- if (allowDownloading && item.isSupportedFormat() && document) {
- if (CachedFont* cachedFont = item.cachedFont(document, foundSVGFont, isInitiatingElementInUserAgentShadowTree))
- source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), cachedFont);
- }
- } else
- source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), nullptr, fontFaceElement);
-
- if (source)
- fontFace.adoptSource(WTFMove(source));
+ if (!m_fontFaceSet) {
+ ASSERT(m_document);
+ m_fontFaceSet = FontFaceSet::create(*m_document, m_cssFontFaceSet.get());
}
- fontFace.sourcesPopulated();
-}
-String CSSFontSelector::familyNameFromPrimitive(const CSSPrimitiveValue& value)
-{
- if (value.isFontFamily())
- return value.fontFamily().familyName;
- if (!value.isValueID())
- return { };
-
- // We need to use the raw text for all the generic family types, since @font-face is a way of actually
- // defining what font to use for those types.
- switch (value.getValueID()) {
- case CSSValueSerif:
- return serifFamily;
- case CSSValueSansSerif:
- return sansSerifFamily;
- case CSSValueCursive:
- return cursiveFamily;
- case CSSValueFantasy:
- return fantasyFamily;
- case CSSValueMonospace:
- return monospaceFamily;
- case CSSValueWebkitPictograph:
- return pictographFamily;
- default:
- return { };
- }
+ return *m_fontFaceSet;
}
-void CSSFontSelector::registerLocalFontFacesForFamily(const String& familyName)
+bool CSSFontSelector::isEmpty() const
{
- ASSERT(!m_locallyInstalledFontFaces.contains(familyName));
-
- Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
- if (traitsMasks.isEmpty())
- return;
-
- Vector<Ref<CSSFontFace>> faces = { };
- for (auto mask : traitsMasks) {
- Ref<CSSFontFace> face = CSSFontFace::create(*this, nullptr, true);
-
- RefPtr<CSSValueList> familyList = CSSValueList::createCommaSeparated();
- familyList->append(CSSValuePool::singleton().createFontFamilyValue(familyName));
- face->setFamilies(*familyList);
- face->setTraitsMask(mask);
- face->adoptSource(std::make_unique<CSSFontFaceSource>(face.get(), familyName));
- ASSERT(!face->allSourcesFailed());
- faces.append(WTFMove(face));
- }
- m_locallyInstalledFontFaces.add(familyName, WTFMove(faces));
+ return !m_cssFontFaceSet->faceCount();
}
-void CSSFontSelector::addFontFaceRule(const StyleRuleFontFace& fontFaceRule, bool isInitiatingElementInUserAgentShadowTree)
+void CSSFontSelector::addFontFaceRule(StyleRuleFontFace& fontFaceRule, bool isInitiatingElementInUserAgentShadowTree)
{
const StyleProperties& style = fontFaceRule.properties();
RefPtr<CSSValue> fontFamily = style.getPropertyCSSValue(CSSPropertyFontFamily);
if (!srcList.length())
return;
- Ref<CSSFontFace> fontFace = CSSFontFace::create(*this);
+ m_creatingFont = true;
+ Ref<CSSFontFace> fontFace = CSSFontFace::create(this, &fontFaceRule);
if (!fontFace->setFamilies(*fontFamily))
return;
if (featureSettings && !fontFace->setFeatureSettings(*featureSettings))
return;
- appendSources(fontFace, srcList, m_document, isInitiatingElementInUserAgentShadowTree);
+ CSSFontFace::appendSources(fontFace, srcList, m_document, isInitiatingElementInUserAgentShadowTree);
if (fontFace->allSourcesFailed())
return;
- for (auto& item : familyList) {
- String familyName = familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
- if (familyName.isEmpty())
- continue;
-
- auto addResult = m_fontFaces.add(familyName, Vector<Ref<CSSFontFace>>());
- auto& familyFontFaces = addResult.iterator->value;
- if (addResult.isNewEntry) {
- registerLocalFontFacesForFamily(familyName);
- familyFontFaces = { };
- }
-
- familyFontFaces.append(fontFace.copyRef());
-
- ++m_version;
- }
+ m_cssFontFaceSet->add(fontFace.get());
+ m_creatingFont = false;
+ ++m_version;
}
void CSSFontSelector::registerForInvalidationCallbacks(FontSelectorClient& client)
dispatchInvalidationCallbacks();
}
+void CSSFontSelector::fontModified()
+{
+ if (!m_creatingFont)
+ dispatchInvalidationCallbacks();
+}
+
void CSSFontSelector::fontCacheInvalidated()
{
dispatchInvalidationCallbacks();
return familyName;
}
-class FontFaceComparator {
-public:
- FontFaceComparator(FontTraitsMask desiredTraitsMaskForComparison)
- : m_desiredTraitsMaskForComparison(desiredTraitsMaskForComparison)
- {
- ASSERT_WITH_SECURITY_IMPLICATION(m_desiredTraitsMaskForComparison & FontWeightMask);
- }
-
- bool operator()(const CSSFontFace& first, const CSSFontFace& second)
- {
- FontTraitsMask firstTraitsMask = first.traitsMask();
- FontTraitsMask secondTraitsMask = second.traitsMask();
-
- bool firstHasDesiredStyle = firstTraitsMask & m_desiredTraitsMaskForComparison & FontStyleMask;
- bool secondHasDesiredStyle = secondTraitsMask & m_desiredTraitsMaskForComparison & FontStyleMask;
-
- if (firstHasDesiredStyle != secondHasDesiredStyle)
- return firstHasDesiredStyle;
-
- if ((m_desiredTraitsMaskForComparison & FontStyleItalicMask) && !first.isLocalFallback() && !second.isLocalFallback()) {
- // Prefer a font that has indicated that it can only support italics to a font that claims to support
- // all styles. The specialized font is more likely to be the one the author wants used.
- bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask);
- bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask);
- if (firstRequiresItalics != secondRequiresItalics)
- return firstRequiresItalics;
- }
-
- if (secondTraitsMask & m_desiredTraitsMaskForComparison & FontWeightMask)
- return false;
- if (firstTraitsMask & m_desiredTraitsMaskForComparison & FontWeightMask)
- return true;
-
- // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
- // - If the desired weight is less than 400, weights below the desired weight are checked in descending order followed by weights above the desired weight in ascending order until a match is found.
- // - If the desired weight is greater than 500, weights above the desired weight are checked in ascending order followed by weights below the desired weight in descending order until a match is found.
- // - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
- // - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
-
- static const unsigned fallbackRuleSets = 9;
- static const unsigned rulesPerSet = 8;
- static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
- { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight500Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
- };
-
- unsigned ruleSetIndex = 0;
- for (; !(m_desiredTraitsMaskForComparison & (1 << (FontWeight100Bit + ruleSetIndex))); ruleSetIndex++) { }
-
- const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
- for (unsigned i = 0; i < rulesPerSet; ++i) {
- if (secondTraitsMask & weightFallbackRule[i])
- return false;
- if (firstTraitsMask & weightFallbackRule[i])
- return true;
- }
-
- return false;
- }
-
-private:
- FontTraitsMask m_desiredTraitsMaskForComparison;
-};
-
FontRanges CSSFontSelector::fontRangesForFamily(const FontDescription& fontDescription, const AtomicString& familyName)
{
// FIXME: The spec (and Firefox) says user specified generic families (sans-serif etc.) should be resolved before the @font-face lookup too.
bool resolveGenericFamilyFirst = familyName == standardFamily;
AtomicString familyForLookup = resolveGenericFamilyFirst ? resolveGenericFamily(m_document, fontDescription, familyName) : familyName;
- CSSSegmentedFontFace* face = getFontFace(fontDescription, familyForLookup);
+ CSSSegmentedFontFace* face = m_cssFontFaceSet->getFontFace(fontDescription.traitsMask(), familyForLookup);
if (!face) {
if (!resolveGenericFamilyFirst)
familyForLookup = resolveGenericFamily(m_document, fontDescription, familyName);
return face->fontRanges(fontDescription);
}
-CSSSegmentedFontFace* CSSFontSelector::getFontFace(const FontDescription& fontDescription, const AtomicString& family)
-{
- auto iterator = m_fontFaces.find(family);
- if (iterator == m_fontFaces.end())
- return nullptr;
- auto& familyFontFaces = iterator->value;
-
- auto& segmentedFontFaceCache = m_fonts.add(family, HashMap<unsigned, std::unique_ptr<CSSSegmentedFontFace>>()).iterator->value;
-
- FontTraitsMask traitsMask = fontDescription.traitsMask();
-
- auto& face = segmentedFontFaceCache.add(traitsMask, nullptr).iterator->value;
- if (face)
- return face.get();
-
- face = std::make_unique<CSSSegmentedFontFace>(*this);
-
- Vector<std::reference_wrapper<CSSFontFace>, 32> candidateFontFaces;
- for (int i = familyFontFaces.size() - 1; i >= 0; --i) {
- CSSFontFace& candidate = familyFontFaces[i];
- unsigned candidateTraitsMask = candidate.traitsMask();
- if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
- continue;
- candidateFontFaces.append(candidate);
- }
-
- auto localIterator = m_locallyInstalledFontFaces.find(family);
- if (localIterator != m_locallyInstalledFontFaces.end()) {
- for (auto& candidate : localIterator->value) {
- unsigned candidateTraitsMask = candidate->traitsMask();
- if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
- continue;
- candidateFontFaces.append(candidate);
- }
- }
-
- std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), FontFaceComparator(traitsMask));
- for (auto& candidate : candidateFontFaces)
- face->appendFontFace(candidate.get());
-
- return face.get();
-}
-
void CSSFontSelector::clearDocument()
{
if (!m_document) {
m_document = nullptr;
// FIXME: This object should outlive the Document.
- m_fontFaces.clear();
- m_locallyInstalledFontFaces.clear();
- m_fonts.clear();
+ m_cssFontFaceSet->clear();
m_clients.clear();
}
#define CSSFontSelector_h
#include "CSSFontFace.h"
+#include "CSSFontFaceSet.h"
#include "CachedResourceHandle.h"
#include "Font.h"
#include "FontSelector.h"
class Document;
class StyleRuleFontFace;
-class CSSFontSelector final : public FontSelector {
+class CSSFontSelector final : public FontSelector, public CSSFontFaceSetClient {
public:
static Ref<CSSFontSelector> create(Document& document)
{
virtual FontRanges fontRangesForFamily(const FontDescription&, const AtomicString&) override;
virtual size_t fallbackFontCount() override;
virtual RefPtr<Font> fallbackFontAt(const FontDescription&, size_t) override;
- CSSSegmentedFontFace* getFontFace(const FontDescription&, const AtomicString& family);
void clearDocument();
- static void appendSources(CSSFontFace&, CSSValueList&, Document*, bool isInitiatingElementInUserAgentShadowTree);
- void addFontFaceRule(const StyleRuleFontFace&, bool isInitiatingElementInUserAgentShadowTree);
+ void addFontFaceRule(StyleRuleFontFace&, bool isInitiatingElementInUserAgentShadowTree);
void fontLoaded();
virtual void fontCacheInvalidated() override;
void beginLoadingFontSoon(CachedFont*);
- static String familyNameFromPrimitive(const CSSPrimitiveValue&);
+ FontFaceSet& fontFaceSet();
private:
explicit CSSFontSelector(Document&);
void dispatchInvalidationCallbacks();
- void beginLoadTimerFired();
+ virtual void fontModified() override;
- void registerLocalFontFacesForFamily(const String&);
+ void beginLoadTimerFired();
Document* m_document;
- HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_fontFaces;
- HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_locallyInstalledFontFaces;
- HashMap<String, HashMap<unsigned, std::unique_ptr<CSSSegmentedFontFace>>, ASCIICaseInsensitiveHash> m_fonts;
+ RefPtr<FontFaceSet> m_fontFaceSet;
+ Ref<CSSFontFaceSet> m_cssFontFaceSet;
HashSet<FontSelectorClient*> m_clients;
Vector<CachedResourceHandle<CachedFont>> m_fontsToBeginLoading;
unsigned m_uniqueId;
unsigned m_version;
+ bool m_creatingFont { false };
};
} // namespace WebCore
namespace WebCore {
-CSSSegmentedFontFace::CSSSegmentedFontFace(CSSFontSelector& fontSelector)
- : m_fontSelector(fontSelector)
+CSSSegmentedFontFace::CSSSegmentedFontFace()
{
}
class CSSSegmentedFontFace final : public CSSFontFace::Client {
WTF_MAKE_FAST_ALLOCATED;
public:
- CSSSegmentedFontFace(CSSFontSelector&);
+ CSSSegmentedFontFace();
~CSSSegmentedFontFace();
- CSSFontSelector& fontSelector() const { return m_fontSelector; }
-
void appendFontFace(Ref<CSSFontFace>&&);
FontRanges fontRanges(const FontDescription&);
+ Vector<Ref<CSSFontFace>, 1>& constituentFaces() { return m_fontFaces; }
+
private:
virtual void fontLoaded(CSSFontFace&) override;
- CSSFontSelector& m_fontSelector;
HashMap<FontDescriptionKey, FontRanges, FontDescriptionKeyHash, WTF::SimpleClassHashTraits<FontDescriptionKey>> m_cache;
Vector<Ref<CSSFontFace>, 1> m_fontFaces;
};
auto value = FontFace::parseString(sourceString, CSSPropertySrc);
if (is<CSSValueList>(value.get())) {
CSSValueList& srcList = downcast<CSSValueList>(*value);
- CSSFontSelector::appendSources(result->backing(), srcList, &downcast<Document>(context), false);
+ CSSFontFace::appendSources(result->backing(), srcList, &downcast<Document>(context), false);
} else {
ec = SYNTAX_ERR;
return nullptr;
return result.ptr();
}
+Ref<FontFace> FontFace::create(JSC::ExecState& execState, CSSFontFace& face)
+{
+ return adoptRef(*new FontFace(execState, face));
+}
+
FontFace::FontFace(JSC::ExecState& execState, CSSFontSelector& fontSelector)
- : m_backing(CSSFontFace::create(fontSelector, this))
+ : m_weakPtrFactory(this)
+ , m_backing(CSSFontFace::create(&fontSelector, nullptr, this))
+ , m_promise(createPromise(execState))
+{
+ m_backing->addClient(*this);
+}
+
+FontFace::FontFace(JSC::ExecState& execState, CSSFontFace& face)
+ : m_weakPtrFactory(this)
+ , m_backing(face)
, m_promise(createPromise(execState))
{
m_backing->addClient(*this);
m_backing->removeClient(*this);
}
+WeakPtr<FontFace> FontFace::createWeakPtr() const
+{
+ return m_weakPtrFactory.createWeakPtr();
+}
+
RefPtr<CSSValue> FontFace::parseString(const String& string, CSSPropertyID propertyID)
{
Ref<MutableStyleProperties> style = MutableStyleProperties::create();
return String("error", String::ConstructFromLiteral);
}
-void FontFace::stateChanged(CSSFontFace& face, CSSFontFace::Status, CSSFontFace::Status newState)
+void FontFace::fontStateChanged(CSSFontFace& face, CSSFontFace::Status, CSSFontFace::Status newState)
{
ASSERT_UNUSED(face, &face == m_backing.ptr());
switch (newState) {
+ case CSSFontFace::Status::Loading:
+ // We still need to resolve promises when loading completes, even if all references to use have fallen out of scope.
+ ref();
+ break;
case CSSFontFace::Status::TimedOut:
rejectPromise(NETWORK_ERR);
+ deref();
return;
case CSSFontFace::Status::Success:
fulfillPromise();
+ deref();
return;
case CSSFontFace::Status::Failure:
rejectPromise(NETWORK_ERR);
+ deref();
return;
default:
return;
#include "JSDOMPromise.h"
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
+#include <wtf/WeakPtr.h>
#include <wtf/text/WTFString.h>
namespace Deprecated {
class FontFace final : public RefCounted<FontFace>, public CSSFontFace::Client {
public:
static RefPtr<FontFace> create(JSC::ExecState&, ScriptExecutionContext&, const String& family, const Deprecated::ScriptValue& source, const Dictionary& descriptors, ExceptionCode&);
+ static Ref<FontFace> create(JSC::ExecState&, CSSFontFace&);
virtual ~FontFace();
void setFamily(const String&, ExceptionCode&);
static RefPtr<CSSValue> parseString(const String&, CSSPropertyID);
+ virtual void fontStateChanged(CSSFontFace&, CSSFontFace::Status oldState, CSSFontFace::Status newState) override;
+
+ WeakPtr<FontFace> createWeakPtr() const;
+
private:
FontFace(JSC::ExecState&, CSSFontSelector&);
-
- virtual void stateChanged(CSSFontFace&, CSSFontFace::Status oldState, CSSFontFace::Status newState) override;
+ FontFace(JSC::ExecState&, CSSFontFace&);
void fulfillPromise();
void rejectPromise(ExceptionCode);
+ WeakPtrFactory<FontFace> m_weakPtrFactory;
Ref<CSSFontFace> m_backing;
Promise m_promise;
};
return FontFaceSet::Promise(DeferredWrapper(&exec, &globalObject, JSC::JSPromiseDeferred::create(&exec, &globalObject)));
}
-FontFaceSet::FontFaceSet(JSC::ExecState& execState, Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
+Ref<FontFaceSet> FontFaceSet::create(Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
+{
+ Ref<FontFaceSet> result = adoptRef(*new FontFaceSet(document, initialFaces));
+ result->suspendIfNeeded();
+ return result;
+}
+
+Ref<FontFaceSet> FontFaceSet::create(Document& document, CSSFontFaceSet& backing)
+{
+ Ref<FontFaceSet> result = adoptRef(*new FontFaceSet(document, backing));
+ result->suspendIfNeeded();
+ return result;
+}
+
+FontFaceSet::FontFaceSet(Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
: ActiveDOMObject(&document)
- , m_backing(*this)
- , m_promise(createPromise(execState))
+ , m_backing(CSSFontFaceSet::create())
{
+ m_backing->addClient(*this);
for (auto& face : initialFaces)
add(face.get());
}
+FontFaceSet::FontFaceSet(Document& document, CSSFontFaceSet& backing)
+ : ActiveDOMObject(&document)
+ , m_backing(backing)
+{
+ m_backing->addClient(*this);
+}
+
FontFaceSet::~FontFaceSet()
{
+ m_backing->removeClient(*this);
}
FontFaceSet::Iterator::Iterator(FontFaceSet& set)
{
}
-bool FontFaceSet::Iterator::next(FontFace*& key, FontFace*& value)
+bool FontFaceSet::Iterator::next(JSC::ExecState& execState, RefPtr<FontFace>& key, RefPtr<FontFace>& value)
{
if (m_index == m_target->size())
return true;
- key = m_target->m_backing[m_index++].wrapper();
+ key = m_target->backing()[m_index++].wrapper(execState);
value = key;
return false;
}
{
}
-bool FontFaceSet::has(FontFace* face) const
+bool FontFaceSet::has(RefPtr<WebCore::FontFace> face) const
{
if (!face)
return false;
- return m_backing.hasFace(face->backing());
+ return m_backing->hasFace(face->backing());
}
size_t FontFaceSet::size() const
{
- return m_backing.faceCount();
+ return m_backing->faceCount();
}
-FontFaceSet& FontFaceSet::add(FontFace* face)
+FontFaceSet& FontFaceSet::add(RefPtr<WebCore::FontFace> face)
{
- if (face && !m_backing.hasFace(face->backing()))
- m_backing.add(face->backing());
+ if (face && !m_backing->hasFace(face->backing()))
+ m_backing->add(face->backing());
return *this;
}
-bool FontFaceSet::remove(FontFace* face)
+bool FontFaceSet::remove(RefPtr<WebCore::FontFace> face)
{
if (!face)
return false;
- bool result = m_backing.hasFace(face->backing());
+ bool result = m_backing->hasFace(face->backing());
if (result)
- m_backing.remove(face->backing());
+ m_backing->remove(face->backing());
return result;
}
void FontFaceSet::clear()
{
- while (m_backing.faceCount())
- m_backing.remove(m_backing[0]);
+ while (m_backing->faceCount())
+ m_backing->remove(m_backing.get()[0]);
}
-void FontFaceSet::load(const String& font, const String& text, DeferredWrapper&& promise, ExceptionCode& ec)
+void FontFaceSet::load(JSC::ExecState& execState, const String& font, const String& text, DeferredWrapper&& promise, ExceptionCode& ec)
{
- auto matchingFaces = m_backing.matchingFaces(font, text, ec);
+ auto matchingFaces = m_backing->matchingFaces(font, text, ec);
if (ec)
return;
}
for (auto& face : matchingFaces) {
- pendingPromise->faces.append(face.get().wrapper());
+ pendingPromise->faces.append(face.get().wrapper(execState));
if (face.get().status() == CSSFontFace::Status::Success)
continue;
waiting = true;
- auto& vector = m_pendingPromises.add(RefPtr<FontFace>(face.get().wrapper()), Vector<Ref<PendingPromise>>()).iterator->value;
+ auto& vector = m_pendingPromises.add(RefPtr<CSSFontFace>(&face.get()), Vector<Ref<PendingPromise>>()).iterator->value;
vector.append(pendingPromise.copyRef());
}
bool FontFaceSet::check(const String& family, const String& text, ExceptionCode& ec)
{
- return m_backing.check(family, text, ec);
+ return m_backing->check(family, text, ec);
}
auto FontFaceSet::promise(JSC::ExecState& execState) -> Promise&
{
if (!m_promise) {
m_promise = createPromise(execState);
- if (m_backing.status() == CSSFontFaceSet::Status::Loaded)
+ if (m_backing->status() == CSSFontFaceSet::Status::Loaded)
fulfillPromise();
}
return m_promise.value();
String FontFaceSet::status() const
{
- switch (m_backing.status()) {
+ switch (m_backing->status()) {
case CSSFontFaceSet::Status::Loading:
return String("loading", String::ConstructFromLiteral);
case CSSFontFaceSet::Status::Loaded:
bool FontFaceSet::canSuspendForDocumentSuspension() const
{
- return m_backing.status() == CSSFontFaceSet::Status::Loaded;
+ return m_backing->status() == CSSFontFaceSet::Status::Loaded;
}
void FontFaceSet::startedLoading()
void FontFaceSet::faceFinished(CSSFontFace& face, CSSFontFace::Status newStatus)
{
- auto iterator = m_pendingPromises.find(face.wrapper());
+ auto iterator = m_pendingPromises.find(&face);
if (iterator == m_pendingPromises.end())
return;
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
+namespace JSC {
+class ExecState;
+}
+
namespace WebCore {
class Document;
class FontFaceSet final : public RefCounted<FontFaceSet>, public CSSFontFaceSetClient, public EventTargetWithInlineData, public ActiveDOMObject {
public:
- static Ref<FontFaceSet> create(JSC::ExecState& execState, Document& document, const Vector<RefPtr<FontFace>>& initialFaces)
- {
- Ref<FontFaceSet> result = adoptRef(*new FontFaceSet(execState, document, initialFaces));
- result->suspendIfNeeded();
- return result;
- }
+ static Ref<FontFaceSet> create(Document&, const Vector<RefPtr<FontFace>>& initialFaces);
+ static Ref<FontFaceSet> create(Document&, CSSFontFaceSet& backing);
virtual ~FontFaceSet();
- bool has(FontFace*) const;
+ bool has(RefPtr<WebCore::FontFace>) const;
size_t size() const;
- FontFaceSet& add(FontFace*);
- bool remove(FontFace*);
+ FontFaceSet& add(RefPtr<WebCore::FontFace>);
+ bool remove(RefPtr<WebCore::FontFace>);
void clear();
- void load(const String& font, DeferredWrapper&& promise, ExceptionCode& ec) { load(font, String(" ", String::ConstructFromLiteral), WTFMove(promise), ec); }
- void load(const String& font, const String& text, DeferredWrapper&& promise, ExceptionCode&);
+ void load(JSC::ExecState& execState, const String& font, DeferredWrapper&& promise, ExceptionCode& ec) { load(execState, font, String(" ", String::ConstructFromLiteral), WTFMove(promise), ec); }
+ void load(JSC::ExecState&, const String& font, const String& text, DeferredWrapper&& promise, ExceptionCode&);
bool check(const String& font, ExceptionCode& ec) { return check(font, String(" ", String::ConstructFromLiteral), ec); }
bool check(const String& font, const String& text, ExceptionCode&);
typedef DOMPromise<FontFaceSet&, DOMCoreException&> Promise;
Promise& promise(JSC::ExecState&);
+ CSSFontFaceSet& backing() { return m_backing; }
+
class Iterator {
public:
explicit Iterator(FontFaceSet&);
- bool next(FontFace*& nextKey, FontFace*& nextValue);
+ bool next(JSC::ExecState&, RefPtr<FontFace>& nextKey, RefPtr<FontFace>& nextValue);
private:
Ref<FontFaceSet> m_target;
- size_t m_index { 0 };
+ size_t m_index { 0 }; // FIXME: There needs to be a mechanism to handle when fonts are added or removed from the middle of the FontFaceSet.
};
Iterator createIterator() { return Iterator(*this); }
Promise promise;
};
- FontFaceSet(JSC::ExecState&, Document&, const Vector<RefPtr<FontFace>>&);
+ FontFaceSet(Document&, const Vector<RefPtr<FontFace>>&);
+ FontFaceSet(Document&, CSSFontFaceSet&);
void fulfillPromise();
virtual void refEventTarget() override { ref(); }
virtual void derefEventTarget() override { deref(); }
- CSSFontFaceSet m_backing;
- HashMap<RefPtr<FontFace>, Vector<Ref<PendingPromise>>> m_pendingPromises;
+ Ref<CSSFontFaceSet> m_backing;
+ HashMap<RefPtr<CSSFontFace>, Vector<Ref<PendingPromise>>> m_pendingPromises;
Optional<Promise> m_promise;
};
};
[
- ConstructorCallWith=ScriptState&Document,
+ ConstructorCallWith=Document,
Constructor(sequence<FontFace> initialFaces)
] interface FontFaceSet : EventTarget {
boolean has(FontFace font);
attribute EventHandler onloadingdone;
attribute EventHandler onloadingerror;
- [RaisesException] Promise load(DOMString font, optional DOMString text);
+ [RaisesException, CallWith=ScriptState] Promise load(DOMString font, optional DOMString text);
[RaisesException] boolean check(DOMString font, optional DOMString text);
[Custom] readonly attribute Promise ready;
#include "EventHandler.h"
#include "ExtensionStyleSheets.h"
#include "FocusController.h"
-#include "FontLoader.h"
+#include "FontFaceSet.h"
#include "FormController.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
}
#endif
-#if ENABLE(FONT_LOAD_EVENTS)
-RefPtr<FontLoader> Document::fonts()
+Ref<FontFaceSet> Document::fonts()
{
- if (!m_fontloader)
- m_fontloader = FontLoader::create(this);
- return m_fontloader;
+ return fontSelector().fontFaceSet();
}
-#endif
float Document::deviceScaleFactor() const
{
class DOMSecurityPolicy;
#endif
-#if ENABLE(FONT_LOAD_EVENTS)
-class FontLoader;
-#endif
+class FontFaceSet;
typedef int ExceptionCode;
WEBCORE_EXPORT virtual SecurityOrigin* topOrigin() const override final;
-#if ENABLE(FONT_LOAD_EVENTS)
- RefPtr<FontLoader> fonts();
-#endif
+ Ref<FontFaceSet> fonts();
void ensurePlugInsInjectedScript(DOMWrapperWorld&);
RefPtr<CSSFontSelector> m_fontSelector;
-#if ENABLE(FONT_LOAD_EVENTS)
- RefPtr<FontLoader> m_fontloader;
-#endif
-
#if ENABLE(WEB_REPLAY)
RefPtr<JSC::InputCursor> m_inputCursor;
#endif
[Conditional=CSS_REGIONS] DOMNamedFlowCollection webkitGetNamedFlows();
#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT
- [Conditional=FONT_LOAD_EVENTS] readonly attribute FontLoader fonts;
+ readonly attribute FontFaceSet fonts;
#endif
#if defined(ENABLE_IOS_TOUCH_EVENTS) && ENABLE_IOS_TOUCH_EVENTS
SVGFontElement* associatedFontElement() const;
void rebuildFontFace();
- const StyleRuleFontFace& fontFaceRule() const { return m_fontFaceRule.get(); }
+ StyleRuleFontFace& fontFaceRule() { return m_fontFaceRule.get(); }
private:
SVGFontFaceElement(const QualifiedName&, Document&);