[Font Loading] Split CSSFontSelector into a FontFaceSet implementation and the rest...
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 Feb 2016 21:40:02 +0000 (21:40 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 Feb 2016 21:40:02 +0000 (21:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=153347

Reviewed by Antti Koivisto.

Source/WebCore:

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:

LayoutTests:

* fast/text/font-face-javascript.html:
* fast/text/font-face-set-document-expected.txt: Added.
* fast/text/font-face-set-document.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@196954 268f45cc-cd09-0410-ab3c-d52691b4dbfc

27 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/text/font-face-javascript.html
LayoutTests/fast/text/font-face-set-document-expected.txt [new file with mode: 0644]
LayoutTests/fast/text/font-face-set-document.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/fetch/FetchHeaders.cpp
Source/WebCore/Modules/fetch/FetchHeaders.h
Source/WebCore/bindings/js/JSKeyValueIterator.h
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/css/CSSFontFace.cpp
Source/WebCore/css/CSSFontFace.h
Source/WebCore/css/CSSFontFaceSet.cpp
Source/WebCore/css/CSSFontFaceSet.h
Source/WebCore/css/CSSFontFaceSource.h
Source/WebCore/css/CSSFontSelector.cpp
Source/WebCore/css/CSSFontSelector.h
Source/WebCore/css/CSSSegmentedFontFace.cpp
Source/WebCore/css/CSSSegmentedFontFace.h
Source/WebCore/css/FontFace.cpp
Source/WebCore/css/FontFace.h
Source/WebCore/css/FontFaceSet.cpp
Source/WebCore/css/FontFaceSet.h
Source/WebCore/css/FontFaceSet.idl
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/Document.idl
Source/WebCore/svg/SVGFontFaceElement.h

index c2fc32b..6abc7cb 100644 (file)
@@ -1,3 +1,14 @@
+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.
index 3d3a37d..8aedb3c 100644 (file)
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<html>
 <head>
 <script src="../../resources/js-test-pre.js"></script>
 </head>
diff --git a/LayoutTests/fast/text/font-face-set-document-expected.txt b/LayoutTests/fast/text/font-face-set-document-expected.txt
new file mode 100644 (file)
index 0000000..8244e0d
--- /dev/null
@@ -0,0 +1,24 @@
+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
diff --git a/LayoutTests/fast/text/font-face-set-document.html b/LayoutTests/fast/text/font-face-set-document.html
new file mode 100644 (file)
index 0000000..f7453d3
--- /dev/null
@@ -0,0 +1,105 @@
+<!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
index 602b9e8..9b1884c 100644 (file)
@@ -1,3 +1,160 @@
+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.
index 8123e86..108fa7b 100644 (file)
@@ -179,7 +179,7 @@ void FetchHeaders::fill(const FetchHeaders* headers)
     }
 }
 
-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++];
index 57ff73f..80d14e3 100644 (file)
 
 #include "HTTPHeaderMap.h"
 
+namespace JSC {
+class ExecState;
+}
+
 namespace WebCore {
 
 typedef int ExceptionCode;
@@ -65,7 +69,7 @@ public:
     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;
index 067bb96..c5f84ac 100644 (file)
@@ -129,7 +129,7 @@ JSC::EncodedJSValue keyValueIteratorForEach(JSC::ExecState& state, const char* p
     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));
@@ -153,7 +153,7 @@ bool JSKeyValueIterator<JSWrapper>::next(JSC::ExecState& state, JSC::JSValue& va
 {
     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;
     }
index fc09187..db9145a 100644 (file)
@@ -4039,6 +4039,7 @@ sub GetNativeType
     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};
 
index 552e99c..0e81d79 100644 (file)
@@ -27,6 +27,7 @@
 #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)
 {
 }
@@ -56,6 +89,15 @@ CSSFontFace::~CSSFontFace()
 {
 }
 
+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))
@@ -65,78 +107,94 @@ bool CSSFontFace::setFamilies(CSSValue& 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)
@@ -150,6 +208,9 @@ bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
         CSSUnicodeRangeValue& range = downcast<CSSUnicodeRangeValue>(rangeValue.get());
         m_ranges.append(UnicodeRange(range.from(), range.to()));
     }
+
+    notifyClientsOfFontPropertyChange();
+
     return true;
 }
 
@@ -160,6 +221,9 @@ bool CSSFontFace::setVariantLigatures(CSSValue& variantLigatures)
     m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures;
     m_variantSettings.historicalLigatures = ligatures.historicalLigatures;
     m_variantSettings.contextualAlternates = ligatures.contextualAlternates;
+
+    notifyClientsOfFontPropertyChange();
+
     return true;
 }
 
@@ -168,6 +232,9 @@ bool CSSFontFace::setVariantPosition(CSSValue& variantPosition)
     if (!is<CSSPrimitiveValue>(variantPosition))
         return false;
     m_variantSettings.position = downcast<CSSPrimitiveValue>(variantPosition);
+
+    notifyClientsOfFontPropertyChange();
+
     return true;
 }
 
@@ -176,6 +243,9 @@ bool CSSFontFace::setVariantCaps(CSSValue& variantCaps)
     if (!is<CSSPrimitiveValue>(variantCaps))
         return false;
     m_variantSettings.caps = downcast<CSSPrimitiveValue>(variantCaps);
+
+    notifyClientsOfFontPropertyChange();
+
     return true;
 }
 
@@ -187,6 +257,9 @@ bool CSSFontFace::setVariantNumeric(CSSValue& variantNumeric)
     m_variantSettings.numericFraction = numeric.fraction;
     m_variantSettings.numericOrdinal = numeric.ordinal;
     m_variantSettings.numericSlashedZero = numeric.slashedZero;
+
+    notifyClientsOfFontPropertyChange();
+
     return true;
 }
 
@@ -195,6 +268,9 @@ bool CSSFontFace::setVariantAlternates(CSSValue& variantAlternates)
     if (!is<CSSPrimitiveValue>(variantAlternates))
         return false;
     m_variantSettings.alternates = downcast<CSSPrimitiveValue>(variantAlternates);
+
+    notifyClientsOfFontPropertyChange();
+
     return true;
 }
 
@@ -204,6 +280,9 @@ bool CSSFontFace::setVariantEastAsian(CSSValue& variantEastAsian)
     m_variantSettings.eastAsianVariant = eastAsian.variant;
     m_variantSettings.eastAsianWidth = eastAsian.width;
     m_variantSettings.eastAsianRuby = eastAsian.ruby;
+
+    notifyClientsOfFontPropertyChange();
+
     return true;
 }
 
@@ -218,6 +297,9 @@ bool CSSFontFace::setFeatureSettings(CSSValue& featureSettings)
         CSSFontFeatureValue& feature = downcast<CSSFontFeatureValue>(rangeValue.get());
         m_featureSettings.insert(FontFeature(feature.tag(), feature.value()));
     }
+
+    notifyClientsOfFontPropertyChange();
+
     return true;
 }
 
@@ -237,9 +319,39 @@ void CSSFontFace::addClient(Client& client)
 
 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));
@@ -268,8 +380,8 @@ void CSSFontFace::setStatus(Status newStatus)
         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;
 }
@@ -282,9 +394,10 @@ void CSSFontFace::fontLoaded(CSSFontFaceSource&)
     if (m_sourcesPopulated)
         pump();
 
+    ASSERT(m_fontSelector);
     m_fontSelector->fontLoaded();
 
-    for (auto& client : m_clients)
+    for (auto* client : m_clients)
         client->fontLoaded(*this);
 }
 
@@ -296,9 +409,10 @@ size_t CSSFontFace::pump()
 
         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()) {
@@ -345,8 +459,10 @@ RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool synt
     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:
index 107d9f2..a48eff5 100644 (file)
@@ -27,7 +27,6 @@
 #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();
 
@@ -78,6 +84,10 @@ public:
     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&);
@@ -93,11 +103,14 @@ public:
     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
@@ -132,27 +145,30 @@ public:
         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 };
index a0f5fd6..dbcfb7a 100644 (file)
 #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()
 {
 }
 
@@ -44,6 +47,22 @@ 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()
@@ -51,7 +70,8 @@ void CSSFontFaceSet::incrementActiveCount()
     ++m_activeCount;
     if (m_activeCount == 1) {
         m_status = Status::Loading;
-        m_client.startedLoading();
+        for (auto* client : m_clients)
+            client->startedLoading();
     }
 }
 
@@ -60,7 +80,8 @@ void CSSFontFaceSet::decrementActiveCount()
     --m_activeCount;
     if (!m_activeCount) {
         m_status = Status::Loaded;
-        m_client.completedLoading();
+        for (auto* client : m_clients)
+            client->completedLoading();
     }
 }
 
@@ -70,23 +91,141 @@ bool CSSFontFaceSet::hasFace(const CSSFontFace& face) const
         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)
@@ -97,30 +236,43 @@ void CSSFontFaceSet::remove(const CSSFontFace& face)
     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)
@@ -132,14 +284,15 @@ Vector<std::reference_wrapper<CSSFontFace>> CSSFontFaceSet::matchingFaces(const
         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;
@@ -147,23 +300,23 @@ Vector<std::reference_wrapper<CSSFontFace>> CSSFontFaceSet::matchingFaces(const
     }
     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)
@@ -179,7 +332,109 @@ bool CSSFontFaceSet::check(const String& font, const String& text, ExceptionCode
     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) {
@@ -188,9 +443,23 @@ void CSSFontFaceSet::stateChanged(CSSFontFace& face, CSSFontFace::Status oldStat
     }
     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();
+}
+
 }
index 66947d7..7dfedf2 100644 (file)
 #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
@@ -64,14 +76,29 @@ public:
     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 };
 };
 
index 1e29b57..f62025d 100644 (file)
@@ -45,6 +45,8 @@ class SharedBuffer;
 class CSSFontFaceSource final : public CachedFontClient {
     WTF_MAKE_FAST_ALLOCATED;
 public:
+    CSSFontFaceSource(CSSFontFace& owner, const String& familyNameOrURI, CachedFont* = nullptr, SVGFontFaceElement* = nullptr);
+    virtual ~CSSFontFaceSource();
 
     //                      => Success
     //                    //
@@ -57,10 +59,6 @@ public:
         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; }
index 2c8c2c6..8a74c98 100644 (file)
@@ -31,7 +31,6 @@
 #include "CSSFontFace.h"
 #include "CSSFontFaceRule.h"
 #include "CSSFontFaceSource.h"
-#include "CSSFontFaceSrcValue.h"
 #include "CSSFontFamily.h"
 #include "CSSFontFeatureValue.h"
 #include "CSSPrimitiveValue.h"
@@ -46,6 +45,7 @@
 #include "Document.h"
 #include "Font.h"
 #include "FontCache.h"
+#include "FontFaceSet.h"
 #include "FontVariantBuilder.h"
 #include "Frame.h"
 #include "FrameLoader.h"
@@ -65,6 +65,7 @@ static unsigned fontSelectorId;
 
 CSSFontSelector::CSSFontSelector(Document& document)
     : m_document(&document)
+    , m_cssFontFaceSet(CSSFontFaceSet::create())
     , m_beginLoadingTimer(*this, &CSSFontSelector::beginLoadTimerFired)
     , m_uniqueId(++fontSelectorId)
     , m_version(0)
@@ -75,99 +76,32 @@ CSSFontSelector::CSSFontSelector(Document& document)
 
     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);
@@ -201,7 +135,8 @@ void CSSFontSelector::addFontFaceRule(const StyleRuleFontFace& fontFaceRule, boo
     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;
@@ -226,26 +161,13 @@ void CSSFontSelector::addFontFaceRule(const StyleRuleFontFace& fontFaceRule, boo
     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)
@@ -273,6 +195,12 @@ void CSSFontSelector::fontLoaded()
     dispatchInvalidationCallbacks();
 }
 
+void CSSFontSelector::fontModified()
+{
+    if (!m_creatingFont)
+        dispatchInvalidationCallbacks();
+}
+
 void CSSFontSelector::fontCacheInvalidated()
 {
     dispatchInvalidationCallbacks();
@@ -304,84 +232,13 @@ static const AtomicString& resolveGenericFamily(Document* document, const FontDe
     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);
@@ -391,49 +248,6 @@ FontRanges CSSFontSelector::fontRangesForFamily(const FontDescription& fontDescr
     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) {
@@ -454,9 +268,7 @@ void CSSFontSelector::clearDocument()
     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();
 }
 
index 9a27fe2..c112e88 100644 (file)
@@ -27,6 +27,7 @@
 #define CSSFontSelector_h
 
 #include "CSSFontFace.h"
+#include "CSSFontFaceSet.h"
 #include "CachedResourceHandle.h"
 #include "Font.h"
 #include "FontSelector.h"
@@ -48,7 +49,7 @@ class CachedFont;
 class Document;
 class StyleRuleFontFace;
 
-class CSSFontSelector final : public FontSelector {
+class CSSFontSelector final : public FontSelector, public CSSFontFaceSetClient {
 public:
     static Ref<CSSFontSelector> create(Document& document)
     {
@@ -62,12 +63,10 @@ public:
     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;
@@ -81,21 +80,20 @@ public:
 
     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;
@@ -103,6 +101,7 @@ private:
 
     unsigned m_uniqueId;
     unsigned m_version;
+    bool m_creatingFont { false };
 };
 
 } // namespace WebCore
index f5fcbae..4c20d38 100644 (file)
@@ -37,8 +37,7 @@
 
 namespace WebCore {
 
-CSSSegmentedFontFace::CSSSegmentedFontFace(CSSFontSelector& fontSelector)
-    : m_fontSelector(fontSelector)
+CSSSegmentedFontFace::CSSSegmentedFontFace()
 {
 }
 
index 07b71f8..9203612 100644 (file)
@@ -42,19 +42,18 @@ class FontDescription;
 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;
 };
index c45e8d2..a0bd824 100644 (file)
@@ -75,7 +75,7 @@ RefPtr<FontFace> FontFace::create(JSC::ExecState& execState, ScriptExecutionCont
         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;
@@ -110,8 +110,22 @@ RefPtr<FontFace> FontFace::create(JSC::ExecState& execState, ScriptExecutionCont
     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);
@@ -122,6 +136,11 @@ FontFace::~FontFace()
     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();
@@ -317,18 +336,25 @@ String FontFace::status() const
     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;
index f66fb9a..10a1770 100644 (file)
@@ -33,6 +33,7 @@
 #include "JSDOMPromise.h"
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
+#include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
 namespace Deprecated {
@@ -48,6 +49,7 @@ class Dictionary;
 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&);
@@ -76,14 +78,18 @@ public:
 
     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;
 };
index 8c46aee..4094b19 100644 (file)
@@ -42,17 +42,39 @@ static FontFaceSet::Promise createPromise(JSC::ExecState& exec)
     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)
@@ -60,11 +82,11 @@ 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;
 }
@@ -78,45 +100,45 @@ FontFaceSet::PendingPromise::~PendingPromise()
 {
 }
 
-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;
 
@@ -139,11 +161,11 @@ void FontFaceSet::load(const String& font, const String& text, DeferredWrapper&&
     }
 
     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());
     }
 
@@ -153,14 +175,14 @@ void FontFaceSet::load(const String& font, const String& text, DeferredWrapper&&
 
 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();
@@ -168,7 +190,7 @@ auto FontFaceSet::promise(JSC::ExecState& execState) -> Promise&
     
 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:
@@ -180,7 +202,7 @@ String FontFaceSet::status() const
 
 bool FontFaceSet::canSuspendForDocumentSuspension() const
 {
-    return m_backing.status() == CSSFontFaceSet::Status::Loaded;
+    return m_backing->status() == CSSFontFaceSet::Status::Loaded;
 }
 
 void FontFaceSet::startedLoading()
@@ -209,7 +231,7 @@ void FontFaceSet::fulfillPromise()
 
 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;
 
index 2906e27..1fdecb8 100644 (file)
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
 
+namespace JSC {
+class ExecState;
+}
+
 namespace WebCore {
 
 class Document;
@@ -45,22 +49,18 @@ class FontFace;
 
 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&);
 
@@ -69,14 +69,16 @@ public:
     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); }
 
@@ -100,7 +102,8 @@ private:
         Promise promise;
     };
 
-    FontFaceSet(JSC::ExecState&, Document&, const Vector<RefPtr<FontFace>>&);
+    FontFaceSet(Document&, const Vector<RefPtr<FontFace>>&);
+    FontFaceSet(Document&, CSSFontFaceSet&);
 
     void fulfillPromise();
 
@@ -119,8 +122,8 @@ private:
     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;
 };
 
index de7492d..bf65938 100644 (file)
@@ -29,7 +29,7 @@ enum FontFaceSetLoadStatus {
 };
 
 [
-    ConstructorCallWith=ScriptState&Document,
+    ConstructorCallWith=Document,
     Constructor(sequence<FontFace> initialFaces)
 ] interface FontFaceSet : EventTarget {
     boolean has(FontFace font);
@@ -48,7 +48,7 @@ enum FontFaceSetLoadStatus {
     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;
index 7ded30c..e381b34 100644 (file)
@@ -62,7 +62,7 @@
 #include "EventHandler.h"
 #include "ExtensionStyleSheets.h"
 #include "FocusController.h"
-#include "FontLoader.h"
+#include "FontFaceSet.h"
 #include "FormController.h"
 #include "FrameLoader.h"
 #include "FrameLoaderClient.h"
@@ -6684,14 +6684,10 @@ Document& Document::ensureTemplateDocument()
 }
 #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
 {
index f15b006..9e9dfa7 100644 (file)
@@ -201,9 +201,7 @@ class TextAutosizer;
 class DOMSecurityPolicy;
 #endif
 
-#if ENABLE(FONT_LOAD_EVENTS)
-class FontLoader;
-#endif
+class FontFaceSet;
 
 typedef int ExceptionCode;
 
@@ -1267,9 +1265,7 @@ public:
 
     WEBCORE_EXPORT virtual SecurityOrigin* topOrigin() const override final;
 
-#if ENABLE(FONT_LOAD_EVENTS)
-    RefPtr<FontLoader> fonts();
-#endif
+    Ref<FontFaceSet> fonts();
 
     void ensurePlugInsInjectedScript(DOMWrapperWorld&);
 
@@ -1751,10 +1747,6 @@ private:
 
     RefPtr<CSSFontSelector> m_fontSelector;
 
-#if ENABLE(FONT_LOAD_EVENTS)
-    RefPtr<FontLoader> m_fontloader;
-#endif
-
 #if ENABLE(WEB_REPLAY)
     RefPtr<JSC::InputCursor> m_inputCursor;
 #endif
index 61c67c4..da6ed53 100644 (file)
     [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
index d28283a..5e4c500 100644 (file)
@@ -50,7 +50,7 @@ public:
     SVGFontElement* associatedFontElement() const;
     void rebuildFontFace();
     
-    const StyleRuleFontFace& fontFaceRule() const { return m_fontFaceRule.get(); }
+    StyleRuleFontFace& fontFaceRule() { return m_fontFaceRule.get(); }
 
 private:
     SVGFontFaceElement(const QualifiedName&, Document&);