Title: [201887] trunk
Revision
201887
Author
mmaxfi...@apple.com
Date
2016-06-09 15:03:53 -0700 (Thu, 09 Jun 2016)

Log Message

Deleting a CSSOM style rule invalidates any previously-added FontFaces
https://bugs.webkit.org/show_bug.cgi?id=158450

Reviewed by Darin Adler.

Source/WebCore:

This patch has two pieces: updating the CSSOM when the FontFace changes, and
updating the FontFace when the CSSOM changes.

1: Updating the CSSOM when the FontFace changes: CSSFontFaces already have a RefPtr
to their StyleRuleFontFace which represents their CSS-connection. When changing a
property of the CSSFontFace, we simply reach into the StyleRule and update it to
match. Our existing infrastructure of invalidation due to the attribute changes
makes sure that all the necessary updates occur.

2. Updating the FontFace when the CSSOM changes: If the CSSOM changes in a trivial
way (for example, a new @font-face is appended to the end of the last <style>
element), we can handle it directly. However, when something more invasive occurs,
we end up clearing the entire CSSFontSelector, and then adding all the style rules
from scratch. This involves three steps:
    a) CSSFontSelector::buildStarted() is run, which means "we're about to start
       building up all the @font-face rules from scratch." We take this opportunity
       to purge as many fonts as possible. This is valuable because, for example,
       this function gets run when the page gets put into the page cache, so we
       want to destroy as much as possible. Not everything can be purged, however -
       only CSS-connected fonts which have never been inspected by script are
       purgeable. We don't allow fonts inspected by script to be purged because
       purging might result in a font appearing from _javascript_ to transition from
       a success -> failure state, which we don't allow.
    b) Upon style recalc (possibly asynchronously) CSSFontSelector::addFontFaceRule()
       is called for each @font-face rule. We actually detect that we're in the
       middle of a style rebuild, and defer this step.
    c) When we're done adding all the font face rules, we call
       CSSFontSelector::buildCompleted(). This is where we compare the newly built-
       up list of font faces with what existed previously (as remembered in
       CSSFontSelector::buildStarted()) in order to detect font faces which were
       deleted from the document. Fonts which were newly added to the document
       are handled naturally.
       Fonts which have a property modified on them are created as if they were new.
       However, instead of simply adding the CSSFontFace, we search for the existing
       CSSFontFace (by CSS connection pointer) and tell the existing FontFace to
       adopt this new CSSFontFace. This means that the _javascript_ object will just
       pick up any newly-written values in the CSSOM. It also means that the
       "status" attribute of the _javascript_ object is reset, but this is expected
       and allowed by the spec. (For example, if you change the "src" attribute of
       an @font-face block via the CSSOM, all bets are off when you inspect the
       FontFace JS object representing that block.)

Test: fast/text/font-face-set-cssom.html

* css/CSSFontFace.cpp:
(WebCore::CSSFontFace::CSSFontFace):
(WebCore::CSSFontFace::setFamilies):
(WebCore::CSSFontFace::setStyle):
(WebCore::CSSFontFace::setWeight):
(WebCore::CSSFontFace::setUnicodeRange):
(WebCore::CSSFontFace::setVariantLigatures):
(WebCore::CSSFontFace::setVariantPosition):
(WebCore::CSSFontFace::setVariantCaps):
(WebCore::CSSFontFace::setVariantNumeric):
(WebCore::CSSFontFace::setVariantAlternates):
(WebCore::CSSFontFace::setVariantEastAsian):
(WebCore::CSSFontFace::setFeatureSettings):
(WebCore::CSSFontFace::initializeWrapper):
(WebCore::CSSFontFace::wrapper):
(WebCore::CSSFontFace::setWrapper):
(WebCore::CSSFontFace::purgeable):
(WebCore::CSSFontFace::updateStyleIfNeeded):
* css/CSSFontFace.h:
* css/CSSFontFaceSet.cpp:
(WebCore::CSSFontFaceSet::remove):
(WebCore::CSSFontFaceSet::containsCSSConnection):
(WebCore::CSSFontFaceSet::purge):
* css/CSSFontFaceSet.h:
* css/CSSFontSelector.cpp:
(WebCore::CSSFontSelector::buildStarted):
(WebCore::CSSFontSelector::buildCompleted):
(WebCore::CSSFontSelector::addFontFaceRule):
* css/CSSFontSelector.h:
* css/FontFace.cpp:
(WebCore::FontFace::family):
(WebCore::FontFace::style):
(WebCore::FontFace::weight):
(WebCore::FontFace::unicodeRange):
(WebCore::FontFace::variant):
(WebCore::FontFace::featureSettings):
(WebCore::FontFace::adopt):
* css/FontFace.h:

LayoutTests:

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

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (201886 => 201887)


--- trunk/LayoutTests/ChangeLog	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/LayoutTests/ChangeLog	2016-06-09 22:03:53 UTC (rev 201887)
@@ -1,3 +1,13 @@
+2016-06-09  Myles C. Maxfield  <mmaxfi...@apple.com>
+
+        Deleting a CSSOM style rule invalidates any previously-added FontFaces
+        https://bugs.webkit.org/show_bug.cgi?id=158450
+
+        Reviewed by Darin Adler.
+
+        * fast/text/font-face-set-cssom-expected.txt: Added.
+        * fast/text/font-face-set-cssom.html: Added.
+
 2016-06-09  Ryan Haddad  <ryanhad...@apple.com>
 
         Marking webgl/webgl-backing-store-size-update.html as a flaky timeout on mac-wk1

Added: trunk/LayoutTests/fast/text/font-face-set-cssom-expected.txt (0 => 201887)


--- trunk/LayoutTests/fast/text/font-face-set-cssom-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/text/font-face-set-cssom-expected.txt	2016-06-09 22:03:53 UTC (rev 201887)
@@ -0,0 +1,48 @@
+This test makes sure that the CSS Font Loading API plays nicely with modifying @font-face rules with the CSSOM.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS item1.getBoundingClientRect().width is 48
+PASS item2.getBoundingClientRect().width is not 48
+PASS item3.getBoundingClientRect().width is not 48
+PASS item4.getBoundingClientRect().width is not 48
+PASS item5.getBoundingClientRect().width is not 48
+PASS item6.getBoundingClientRect().width is not 48
+PASS item1.getBoundingClientRect().width is 48
+PASS item2.getBoundingClientRect().width is 48
+PASS item3.getBoundingClientRect().width is not 48
+PASS item4.getBoundingClientRect().width is not 48
+PASS item5.getBoundingClientRect().width is not 48
+PASS item6.getBoundingClientRect().width is not 48
+PASS item1.getBoundingClientRect().width is 48
+PASS item2.getBoundingClientRect().width is 48
+PASS item3.getBoundingClientRect().width is 48
+PASS item4.getBoundingClientRect().width is not 48
+PASS item5.getBoundingClientRect().width is not 48
+PASS item6.getBoundingClientRect().width is not 48
+PASS item1.getBoundingClientRect().width is 48
+PASS item2.getBoundingClientRect().width is 48
+PASS item3.getBoundingClientRect().width is not 48
+PASS item4.getBoundingClientRect().width is not 48
+PASS item5.getBoundingClientRect().width is not 48
+PASS item6.getBoundingClientRect().width is not 48
+PASS connectedFontFace.family is "WebFont2"
+PASS connectedFontFace.family is "WebFont5"
+PASS item1.getBoundingClientRect().width is 48
+PASS item2.getBoundingClientRect().width is not 48
+PASS item3.getBoundingClientRect().width is not 48
+PASS item4.getBoundingClientRect().width is not 48
+PASS item5.getBoundingClientRect().width is 48
+PASS item6.getBoundingClientRect().width is not 48
+PASS styleSheet.cssRules[0].style.fontFamily is "WebFont6"
+PASS item1.getBoundingClientRect().width is 48
+PASS item2.getBoundingClientRect().width is not 48
+PASS item3.getBoundingClientRect().width is not 48
+PASS item4.getBoundingClientRect().width is not 48
+PASS item5.getBoundingClientRect().width is not 48
+PASS item6.getBoundingClientRect().width is 48
+PASS successfullyParsed is true
+
+TEST COMPLETE
+l l l l l l

Added: trunk/LayoutTests/fast/text/font-face-set-cssom.html (0 => 201887)


--- trunk/LayoutTests/fast/text/font-face-set-cssom.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/font-face-set-cssom.html	2016-06-09 22:03:53 UTC (rev 201887)
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style id="styleElement">
+</style>
+</head>
+<body>
+<span id="item1" style="font: 48px WebFont;">l</span>
+<span id="item2" style="font: 48px WebFont2;">l</span>
+<span id="item3" style="font: 48px WebFont3;">l</span>
+<span id="item4" style="font: 48px WebFont4;">l</span>
+<span id="item5" style="font: 48px WebFont5;">l</span>
+<span id="item6" style="font: 48px WebFont6;">l</span>
+<script>
+description("This test makes sure that the CSS Font Loading API plays nicely with modifying @font-face rules with the CSSOM.");
+
+var item1 = document.getElementById("item1");
+var item2 = document.getElementById("item2");
+var item3 = document.getElementById("item3");
+var item4 = document.getElementById("item4");
+var item5 = document.getElementById("item5");
+var item6 = document.getElementById("item6");
+
+var fontFace = new FontFace("WebFont", "local('Ahem')", {});
+document.fonts.add(fontFace);
+fontFace.load();
+
+shouldBe("item1.getBoundingClientRect().width", "48");
+shouldNotBe("item2.getBoundingClientRect().width", "48");
+shouldNotBe("item3.getBoundingClientRect().width", "48");
+shouldNotBe("item4.getBoundingClientRect().width", "48");
+shouldNotBe("item5.getBoundingClientRect().width", "48");
+shouldNotBe("item6.getBoundingClientRect().width", "48");
+
+var styleSheet = document.getElementById("styleElement").sheet;
+styleSheet.insertRule("@font-face { font-family: 'WebFont2'; src: local('Ahem'); }", 0);
+
+shouldBe("item1.getBoundingClientRect().width", "48");
+shouldBe("item2.getBoundingClientRect().width", "48");
+shouldNotBe("item3.getBoundingClientRect().width", "48");
+shouldNotBe("item4.getBoundingClientRect().width", "48");
+shouldNotBe("item5.getBoundingClientRect().width", "48");
+shouldNotBe("item6.getBoundingClientRect().width", "48");
+
+styleSheet.insertRule("@font-face { font-family: 'WebFont3'; src: local('Ahem'); }", 0);
+
+shouldBe("item1.getBoundingClientRect().width", "48");
+shouldBe("item2.getBoundingClientRect().width", "48");
+shouldBe("item3.getBoundingClientRect().width", "48");
+shouldNotBe("item4.getBoundingClientRect().width", "48");
+shouldNotBe("item5.getBoundingClientRect().width", "48");
+shouldNotBe("item6.getBoundingClientRect().width", "48");
+
+styleSheet.deleteRule(0);
+
+shouldBe("item1.getBoundingClientRect().width", "48");
+shouldBe("item2.getBoundingClientRect().width", "48");
+shouldNotBe("item3.getBoundingClientRect().width", "48");
+shouldNotBe("item4.getBoundingClientRect().width", "48");
+shouldNotBe("item5.getBoundingClientRect().width", "48");
+shouldNotBe("item6.getBoundingClientRect().width", "48");
+
+var connectedFontFace = document.fonts.keys().next().value;
+shouldBeEqualToString("connectedFontFace.family", "WebFont2");
+
+styleSheet.cssRules[0].style.fontFamily = "WebFont5";
+
+shouldBeEqualToString("connectedFontFace.family", "WebFont5");
+
+shouldBe("item1.getBoundingClientRect().width", "48");
+shouldNotBe("item2.getBoundingClientRect().width", "48");
+shouldNotBe("item3.getBoundingClientRect().width", "48");
+shouldNotBe("item4.getBoundingClientRect().width", "48");
+shouldBe("item5.getBoundingClientRect().width", "48");
+shouldNotBe("item6.getBoundingClientRect().width", "48");
+
+connectedFontFace.family = "WebFont6";
+
+shouldBeEqualToString("styleSheet.cssRules[0].style.fontFamily", "WebFont6");
+
+shouldBe("item1.getBoundingClientRect().width", "48");
+shouldNotBe("item2.getBoundingClientRect().width", "48");
+shouldNotBe("item3.getBoundingClientRect().width", "48");
+shouldNotBe("item4.getBoundingClientRect().width", "48");
+shouldNotBe("item5.getBoundingClientRect().width", "48");
+shouldBe("item6.getBoundingClientRect().width", "48");
+
+</script>
+<script src=""
+</body>
+<html>
\ No newline at end of file

Modified: trunk/Source/WebCore/ChangeLog (201886 => 201887)


--- trunk/Source/WebCore/ChangeLog	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/ChangeLog	2016-06-09 22:03:53 UTC (rev 201887)
@@ -1,3 +1,93 @@
+2016-06-09  Myles C. Maxfield  <mmaxfi...@apple.com>
+
+        Deleting a CSSOM style rule invalidates any previously-added FontFaces
+        https://bugs.webkit.org/show_bug.cgi?id=158450
+
+        Reviewed by Darin Adler.
+
+        This patch has two pieces: updating the CSSOM when the FontFace changes, and
+        updating the FontFace when the CSSOM changes.
+
+        1: Updating the CSSOM when the FontFace changes: CSSFontFaces already have a RefPtr
+        to their StyleRuleFontFace which represents their CSS-connection. When changing a
+        property of the CSSFontFace, we simply reach into the StyleRule and update it to
+        match. Our existing infrastructure of invalidation due to the attribute changes
+        makes sure that all the necessary updates occur.
+
+        2. Updating the FontFace when the CSSOM changes: If the CSSOM changes in a trivial
+        way (for example, a new @font-face is appended to the end of the last <style>
+        element), we can handle it directly. However, when something more invasive occurs,
+        we end up clearing the entire CSSFontSelector, and then adding all the style rules
+        from scratch. This involves three steps:
+            a) CSSFontSelector::buildStarted() is run, which means "we're about to start
+               building up all the @font-face rules from scratch." We take this opportunity
+               to purge as many fonts as possible. This is valuable because, for example,
+               this function gets run when the page gets put into the page cache, so we
+               want to destroy as much as possible. Not everything can be purged, however -
+               only CSS-connected fonts which have never been inspected by script are
+               purgeable. We don't allow fonts inspected by script to be purged because
+               purging might result in a font appearing from _javascript_ to transition from
+               a success -> failure state, which we don't allow.
+            b) Upon style recalc (possibly asynchronously) CSSFontSelector::addFontFaceRule()
+               is called for each @font-face rule. We actually detect that we're in the
+               middle of a style rebuild, and defer this step.
+            c) When we're done adding all the font face rules, we call
+               CSSFontSelector::buildCompleted(). This is where we compare the newly built-
+               up list of font faces with what existed previously (as remembered in
+               CSSFontSelector::buildStarted()) in order to detect font faces which were
+               deleted from the document. Fonts which were newly added to the document
+               are handled naturally.
+               Fonts which have a property modified on them are created as if they were new.
+               However, instead of simply adding the CSSFontFace, we search for the existing
+               CSSFontFace (by CSS connection pointer) and tell the existing FontFace to
+               adopt this new CSSFontFace. This means that the _javascript_ object will just
+               pick up any newly-written values in the CSSOM. It also means that the
+               "status" attribute of the _javascript_ object is reset, but this is expected
+               and allowed by the spec. (For example, if you change the "src" attribute of
+               an @font-face block via the CSSOM, all bets are off when you inspect the
+               FontFace JS object representing that block.)
+
+        Test: fast/text/font-face-set-cssom.html
+
+        * css/CSSFontFace.cpp:
+        (WebCore::CSSFontFace::CSSFontFace):
+        (WebCore::CSSFontFace::setFamilies):
+        (WebCore::CSSFontFace::setStyle):
+        (WebCore::CSSFontFace::setWeight):
+        (WebCore::CSSFontFace::setUnicodeRange):
+        (WebCore::CSSFontFace::setVariantLigatures):
+        (WebCore::CSSFontFace::setVariantPosition):
+        (WebCore::CSSFontFace::setVariantCaps):
+        (WebCore::CSSFontFace::setVariantNumeric):
+        (WebCore::CSSFontFace::setVariantAlternates):
+        (WebCore::CSSFontFace::setVariantEastAsian):
+        (WebCore::CSSFontFace::setFeatureSettings):
+        (WebCore::CSSFontFace::initializeWrapper):
+        (WebCore::CSSFontFace::wrapper):
+        (WebCore::CSSFontFace::setWrapper):
+        (WebCore::CSSFontFace::purgeable):
+        (WebCore::CSSFontFace::updateStyleIfNeeded):
+        * css/CSSFontFace.h:
+        * css/CSSFontFaceSet.cpp:
+        (WebCore::CSSFontFaceSet::remove):
+        (WebCore::CSSFontFaceSet::containsCSSConnection):
+        (WebCore::CSSFontFaceSet::purge):
+        * css/CSSFontFaceSet.h:
+        * css/CSSFontSelector.cpp:
+        (WebCore::CSSFontSelector::buildStarted):
+        (WebCore::CSSFontSelector::buildCompleted):
+        (WebCore::CSSFontSelector::addFontFaceRule):
+        * css/CSSFontSelector.h:
+        * css/FontFace.cpp:
+        (WebCore::FontFace::family):
+        (WebCore::FontFace::style):
+        (WebCore::FontFace::weight):
+        (WebCore::FontFace::unicodeRange):
+        (WebCore::FontFace::variant):
+        (WebCore::FontFace::featureSettings):
+        (WebCore::FontFace::adopt):
+        * css/FontFace.h:
+
 2016-06-09  Andy Estes  <aes...@apple.com>
 
         Define printing{Minimum,Maximum}ShrinkFactor in only one place

Modified: trunk/Source/WebCore/css/CSSFontFace.cpp (201886 => 201887)


--- trunk/Source/WebCore/css/CSSFontFace.cpp	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/css/CSSFontFace.cpp	2016-06-09 22:03:53 UTC (rev 201887)
@@ -94,6 +94,7 @@
     , m_cssConnection(cssConnection)
     , m_wrapper(wrapper ? wrapper->createWeakPtr() : WeakPtr<FontFace>())
     , m_isLocalFallback(isLocalFallback)
+    , m_mayBePurged(!wrapper)
 {
 }
 
@@ -113,6 +114,9 @@
     RefPtr<CSSValueList> oldFamilies = m_families;
     m_families = &familyList;
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFamily, &family);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this, oldFamilies.get());
     });
@@ -143,6 +147,9 @@
     if (auto mask = calculateStyleMask(style)) {
         m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontStyleMask)) | mask.value());
 
+        if (m_cssConnection)
+            m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStyle, &style);
+
         iterateClients(m_clients, [&](Client& client) {
             client.fontPropertyChanged(*this);
         });
@@ -192,6 +199,9 @@
     if (auto mask = calculateWeightMask(weight)) {
         m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontWeightMask)) | mask.value());
 
+        if (m_cssConnection)
+            m_cssConnection->mutableProperties().setProperty(CSSPropertyFontWeight, &weight);
+
         iterateClients(m_clients, [&](Client& client) {
             client.fontPropertyChanged(*this);
         });
@@ -214,6 +224,9 @@
         m_ranges.append({ range.from(), range.to() });
     }
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyUnicodeRange, &unicodeRange);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this);
     });
@@ -230,6 +243,9 @@
     m_variantSettings.historicalLigatures = ligatures.historicalLigatures;
     m_variantSettings.contextualAlternates = ligatures.contextualAlternates;
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantLigatures, &variantLigatures);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this);
     });
@@ -244,6 +260,9 @@
 
     m_variantSettings.position = downcast<CSSPrimitiveValue>(variantPosition);
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantPosition, &variantPosition);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this);
     });
@@ -258,6 +277,9 @@
 
     m_variantSettings.caps = downcast<CSSPrimitiveValue>(variantCaps);
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantCaps, &variantCaps);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this);
     });
@@ -275,6 +297,9 @@
     m_variantSettings.numericOrdinal = numeric.ordinal;
     m_variantSettings.numericSlashedZero = numeric.slashedZero;
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantNumeric, &variantNumeric);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this);
     });
@@ -289,6 +314,9 @@
 
     m_variantSettings.alternates = downcast<CSSPrimitiveValue>(variantAlternates);
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantAlternates, &variantAlternates);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this);
     });
@@ -304,6 +332,9 @@
     m_variantSettings.eastAsianWidth = eastAsian.width;
     m_variantSettings.eastAsianRuby = eastAsian.ruby;
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantEastAsian, &variantEastAsian);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this);
     });
@@ -331,6 +362,9 @@
 
     m_featureSettings = WTFMove(settings);
 
+    if (m_cssConnection)
+        m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFeatureSettings, &featureSettings);
+
     iterateClients(m_clients, [&](Client& client) {
         client.fontPropertyChanged(*this);
     });
@@ -381,35 +415,47 @@
     m_clients.remove(&client);
 }
 
-Ref<FontFace> CSSFontFace::wrapper()
+void CSSFontFace::initializeWrapper()
 {
-    if (m_wrapper)
-        return Ref<FontFace>(*m_wrapper.get());
-
-    Ref<FontFace> wrapper = FontFace::create(*this);
     switch (m_status) {
     case Status::Pending:
         break;
     case Status::Loading:
-        wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
+        m_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);
+        m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
+        m_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);
+        m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
+        m_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);
+        m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
+        m_wrapper->fontStateChanged(*this, Status::Pending, Status::Failure);
         break;
     }
+}
+
+Ref<FontFace> CSSFontFace::wrapper()
+{
+    if (m_wrapper)
+        return *m_wrapper.get();
+
+    auto wrapper = FontFace::create(*this);
     m_wrapper = wrapper->createWeakPtr();
+    initializeWrapper();
+    m_mayBePurged = false;
     return wrapper;
 }
 
+void CSSFontFace::setWrapper(FontFace& newWrapper)
+{
+    m_wrapper = newWrapper.createWeakPtr();
+    initializeWrapper();
+}
+
 void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source)
 {
     m_sources.append(WTFMove(source));
@@ -552,6 +598,17 @@
     return nullptr;
 }
 
+bool CSSFontFace::purgeable() const
+{
+    return cssConnection() && m_mayBePurged;
+}
+
+void CSSFontFace::updateStyleIfNeeded()
+{
+    if (m_fontSelector && m_fontSelector->document())
+        m_fontSelector->document()->updateStyleIfNeeded();
+}
+
 #if ENABLE(SVG_FONTS)
 bool CSSFontFace::hasSVGFontFaceSource() const
 {

Modified: trunk/Source/WebCore/css/CSSFontFace.h (201886 => 201887)


--- trunk/Source/WebCore/css/CSSFontFace.h	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/css/CSSFontFace.h	2016-06-09 22:03:53 UTC (rev 201887)
@@ -135,9 +135,15 @@
 
     // We don't guarantee that the FontFace wrapper will be the same every time you ask for it.
     Ref<FontFace> wrapper();
+    void setWrapper(FontFace&);
+    FontFace* existingWrapper() { return m_wrapper.get(); }
 
     bool webFontsShouldAlwaysFallBack() const;
 
+    bool purgeable() const;
+
+    void updateStyleIfNeeded();
+
 #if ENABLE(SVG_FONTS)
     bool hasSVGFontFaceSource() const;
 #endif
@@ -149,6 +155,8 @@
     void setStatus(Status);
     void notifyClientsOfFontPropertyChange();
 
+    void initializeWrapper();
+
     void fontLoadEventOccurred();
     void timeoutFired();
 
@@ -166,6 +174,7 @@
     Status m_status { Status::Pending };
     bool m_isLocalFallback { false };
     bool m_sourcesPopulated { false };
+    bool m_mayBePurged { true };
 };
 
 }

Modified: trunk/Source/WebCore/css/CSSFontFaceSet.cpp (201886 => 201887)


--- trunk/Source/WebCore/css/CSSFontFaceSet.cpp	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/css/CSSFontFaceSet.cpp	2016-06-09 22:03:53 UTC (rev 201887)
@@ -95,9 +95,10 @@
     return false;
 }
 
-void CSSFontFaceSet::registerLocalFontFacesForFamily(const String& familyName)
+void CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered(const String& familyName)
 {
-    ASSERT(!m_locallyInstalledFacesLookupTable.contains(familyName));
+    if (m_locallyInstalledFacesLookupTable.contains(familyName))
+        return;
 
     Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
     if (traitsMasks.isEmpty())
@@ -160,7 +161,7 @@
         if (addResult.isNewEntry) {
             // m_locallyInstalledFontFaces grows without bound, eventually encorporating every font installed on the system.
             // This is by design.
-            registerLocalFontFacesForFamily(familyName);
+            ensureLocalFontFacesForFamilyRegistered(familyName);
             familyFontFaces = { };
         }
 
@@ -187,6 +188,11 @@
 
     if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
         incrementActiveCount();
+
+    if (face.cssConnection()) {
+        auto addResult = m_constituentCSSConnections.add(face.cssConnection(), &face);
+        ASSERT_UNUSED(addResult, addResult.isNewEntry);
+    }
 }
 
 void CSSFontFaceSet::removeFromFacesLookupTable(const CSSFontFace& face, const CSSValueList& familiesToSearchFor)
@@ -222,6 +228,11 @@
     if (face.families())
         removeFromFacesLookupTable(face, *face.families());
 
+    if (face.cssConnection()) {
+        bool removed = m_constituentCSSConnections.remove(face.cssConnection());
+        ASSERT_UNUSED(removed, removed);
+    }
+
     for (size_t i = 0; i < m_faces.size(); ++i) {
         if (m_faces[i].ptr() == &face) {
             if (i < m_facesPartitionIndex)
@@ -236,6 +247,23 @@
     ASSERT_NOT_REACHED();
 }
 
+CSSFontFace* CSSFontFaceSet::lookupByCSSConnection(StyleRuleFontFace& target)
+{
+    return m_constituentCSSConnections.get(&target);
+}
+
+void CSSFontFaceSet::purge()
+{
+    Vector<std::reference_wrapper<CSSFontFace>> toRemove;
+    for (auto& face : m_faces) {
+        if (face->purgeable())
+            toRemove.append(face.get());
+    }
+
+    for (auto& item : toRemove)
+        remove(item.get());
+}
+
 void CSSFontFaceSet::clear()
 {
     for (auto& face : m_faces)
@@ -244,6 +272,7 @@
     m_facesLookupTable.clear();
     m_locallyInstalledFacesLookupTable.clear();
     m_cache.clear();
+    m_constituentCSSConnections.clear();
     m_facesPartitionIndex = 0;
     m_status = Status::Loaded;
 }

Modified: trunk/Source/WebCore/css/CSSFontFaceSet.h (201886 => 201887)


--- trunk/Source/WebCore/css/CSSFontFaceSet.h	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/css/CSSFontFaceSet.h	2016-06-09 22:03:53 UTC (rev 201887)
@@ -60,9 +60,12 @@
     size_t faceCount() const { return m_faces.size(); }
     void add(CSSFontFace&);
     void remove(const CSSFontFace&);
+    void purge();
     void clear();
     CSSFontFace& operator[](size_t i);
 
+    CSSFontFace* lookupByCSSConnection(StyleRuleFontFace&);
+
     bool check(const String& font, const String& text, ExceptionCode&);
 
     CSSSegmentedFontFace* getFontFace(FontTraitsMask, const AtomicString& family);
@@ -91,7 +94,7 @@
     void fontStateChanged(CSSFontFace&, CSSFontFace::Status oldState, CSSFontFace::Status newState) override;
     void fontPropertyChanged(CSSFontFace&, CSSValueList* oldFamilies = nullptr) override;
 
-    void registerLocalFontFacesForFamily(const String&);
+    void ensureLocalFontFacesForFamilyRegistered(const String&);
 
     static String familyNameFromPrimitive(const CSSPrimitiveValue&);
 
@@ -100,6 +103,7 @@
     HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_facesLookupTable;
     HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_locallyInstalledFacesLookupTable;
     HashMap<String, HashMap<unsigned, RefPtr<CSSSegmentedFontFace>>, ASCIICaseInsensitiveHash> m_cache;
+    HashMap<StyleRuleFontFace*, CSSFontFace*> m_constituentCSSConnections;
     size_t m_facesPartitionIndex { 0 }; // All entries in m_faces before this index are CSS-connected.
     Status m_status { Status::Loaded };
     HashSet<CSSFontFaceSetClient*> m_clients;

Modified: trunk/Source/WebCore/css/CSSFontSelector.cpp (201886 => 201887)


--- trunk/Source/WebCore/css/CSSFontSelector.cpp	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/css/CSSFontSelector.cpp	2016-06-09 22:03:53 UTC (rev 201887)
@@ -45,6 +45,7 @@
 #include "Document.h"
 #include "Font.h"
 #include "FontCache.h"
+#include "FontFace.h"
 #include "FontFaceSet.h"
 #include "FontSelectorClient.h"
 #include "FontVariantBuilder.h"
@@ -101,7 +102,17 @@
 void CSSFontSelector::buildStarted()
 {
     m_buildIsUnderway = true;
+    m_stagingArea.clear();
+    m_cssFontFaceSet->purge();
     ++m_version;
+
+    m_cssConnectionsPossiblyToRemove.clear();
+    m_cssConnectionsEncounteredDuringBuild.clear();
+    for (size_t i = 0; i < m_cssFontFaceSet->faceCount(); ++i) {
+        CSSFontFace& face = m_cssFontFaceSet.get()[i];
+        if (face.cssConnection())
+            m_cssConnectionsPossiblyToRemove.add(&face);
+    }
 }
 
 void CSSFontSelector::buildCompleted()
@@ -111,7 +122,13 @@
 
     m_buildIsUnderway = false;
 
-    m_cssFontFaceSet->clear();
+    // Some font faces weren't re-added during the build process.
+    for (auto& face : m_cssConnectionsPossiblyToRemove) {
+        auto* connection = face->cssConnection();
+        ASSERT(connection);
+        if (!m_cssConnectionsEncounteredDuringBuild.contains(connection))
+            m_cssFontFaceSet->remove(*face);
+    }
 
     for (auto& item : m_stagingArea)
         addFontFaceRule(item.styleRuleFontFace, item.isInitiatingElementInUserAgentShadowTree);
@@ -121,6 +138,7 @@
 void CSSFontSelector::addFontFaceRule(StyleRuleFontFace& fontFaceRule, bool isInitiatingElementInUserAgentShadowTree)
 {
     if (m_buildIsUnderway) {
+        m_cssConnectionsEncounteredDuringBuild.add(&fontFaceRule);
         m_stagingArea.append({fontFaceRule, isInitiatingElementInUserAgentShadowTree});
         return;
     }
@@ -187,6 +205,12 @@
     if (fontFace->allSourcesFailed())
         return;
 
+    if (RefPtr<CSSFontFace> existingFace = m_cssFontFaceSet->lookupByCSSConnection(fontFaceRule)) {
+        m_cssFontFaceSet->remove(*existingFace);
+        if (auto* existingWrapper = existingFace->existingWrapper())
+            existingWrapper->adopt(fontFace.get());
+    }
+
     m_cssFontFaceSet->add(fontFace.get());
     m_creatingFont = false;
     ++m_version;

Modified: trunk/Source/WebCore/css/CSSFontSelector.h (201886 => 201887)


--- trunk/Source/WebCore/css/CSSFontSelector.h	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/css/CSSFontSelector.h	2016-06-09 22:03:53 UTC (rev 201887)
@@ -105,6 +105,8 @@
     HashSet<FontSelectorClient*> m_clients;
 
     Vector<CachedResourceHandle<CachedFont>> m_fontsToBeginLoading;
+    HashSet<RefPtr<CSSFontFace>> m_cssConnectionsPossiblyToRemove;
+    HashSet<RefPtr<StyleRuleFontFace>> m_cssConnectionsEncounteredDuringBuild;
     Timer m_beginLoadingTimer;
 
     unsigned m_uniqueId;

Modified: trunk/Source/WebCore/css/FontFace.cpp (201886 => 201887)


--- trunk/Source/WebCore/css/FontFace.cpp	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/css/FontFace.cpp	2016-06-09 22:03:53 UTC (rev 201887)
@@ -270,11 +270,13 @@
 
 String FontFace::family() const
 {
+    const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded();
     return m_backing->families()->cssText();
 }
 
 String FontFace::style() const
 {
+    const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded();
     switch (m_backing->traitsMask() & FontStyleMask) {
     case FontStyleNormalMask:
         return String("normal", String::ConstructFromLiteral);
@@ -287,6 +289,7 @@
 
 String FontFace::weight() const
 {
+    const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded();
     switch (m_backing->traitsMask() & FontWeightMask) {
     case FontWeight100Mask:
         return String("100", String::ConstructFromLiteral);
@@ -318,6 +321,7 @@
 
 String FontFace::unicodeRange() const
 {
+    const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded();
     if (!m_backing->ranges().size())
         return "U+0-10FFFF";
     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
@@ -328,11 +332,13 @@
 
 String FontFace::variant() const
 {
+    const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded();
     return computeFontVariant(m_backing->variantSettings())->cssText();
 }
 
 String FontFace::featureSettings() const
 {
+    const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded();
     if (!m_backing->featureSettings().size())
         return "normal";
     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
@@ -359,6 +365,15 @@
     return LoadStatus::Error;
 }
 
+void FontFace::adopt(CSSFontFace& newFace)
+{
+    m_promise = Nullopt;
+    m_backing->removeClient(*this);
+    m_backing = newFace;
+    m_backing->addClient(*this);
+    newFace.setWrapper(*this);
+}
+
 void FontFace::fontStateChanged(CSSFontFace& face, CSSFontFace::Status, CSSFontFace::Status newState)
 {
     ASSERT_UNUSED(face, &face == m_backing.ptr());

Modified: trunk/Source/WebCore/css/FontFace.h (201886 => 201887)


--- trunk/Source/WebCore/css/FontFace.h	2016-06-09 21:41:39 UTC (rev 201886)
+++ trunk/Source/WebCore/css/FontFace.h	2016-06-09 22:03:53 UTC (rev 201887)
@@ -69,6 +69,8 @@
     Optional<Promise>& promise() { return m_promise; }
     void registerLoaded(Promise&&);
 
+    void adopt(CSSFontFace&);
+
     void load();
 
     CSSFontFace& backing() { return m_backing; }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to