<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[196383] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/196383">196383</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2016-02-10 12:47:04 -0800 (Wed, 10 Feb 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Optimize style invalidation after class attribute change
https://bugs.webkit.org/show_bug.cgi?id=154075
rdar://problem/12526450

Reviewed by Andreas Kling.

Currently a class attribute change invalidates style for the entire element subtree for any class found in the
active stylesheet set.

This patch optimizes class changes by building a new optimization structure called ancestorClassRules. It contains
rules that have class selectors in the portion of the complex selector that matches ancestor elements. The sets
of rules are hashes by the class name.

On class attribute change the existing StyleInvalidationAnalysis mechanism is used with ancestorClassRules to invalidate
exactly those descendants that are affected by the addition or removal of the class name. This is fast because the CSS JIT
makes selector matching cheap and the number of relevant rules is typically small.

This optimization is very effective on many dynamic pages. For example when focusing and unfocusing the web inspector it
cuts down the number of resolved elements from ~1000 to ~50. Even in PLT it reduces the number of resolved elements by ~11%.

* css/DocumentRuleSets.cpp:
(WebCore::DocumentRuleSets::collectFeatures):
(WebCore::DocumentRuleSets::ancestorClassRules):

    Create optimization RuleSets on-demand when there is an actual dynamic class change.

* css/DocumentRuleSets.h:
(WebCore::DocumentRuleSets::features):
(WebCore::DocumentRuleSets::sibling):
(WebCore::DocumentRuleSets::uncommonAttribute):
* css/ElementRuleCollector.cpp:
(WebCore::ElementRuleCollector::ElementRuleCollector):

    Add a new constructor that doesn't requires DocumentRuleSets. Only the user and author style is required.

(WebCore::ElementRuleCollector::matchAuthorRules):
(WebCore::ElementRuleCollector::matchUserRules):
* css/ElementRuleCollector.h:
* css/RuleFeature.cpp:
(WebCore::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):

    Collect class names that show up in the ancestor portion of the selector.
    Make this a member.

(WebCore::RuleFeatureSet::collectFeatures):

    Move this code from RuleData.
    Add the rule to ancestorClassRules if needed.

(WebCore::RuleFeatureSet::add):
(WebCore::RuleFeatureSet::clear):
(WebCore::RuleFeatureSet::shrinkToFit):
(WebCore::recursivelyCollectFeaturesFromSelector): Deleted.
(WebCore::RuleFeatureSet::collectFeaturesFromSelector): Deleted.
* css/RuleFeature.h:
(WebCore::RuleFeature::RuleFeature):
(WebCore::RuleFeatureSet::RuleFeatureSet): Deleted.
* css/RuleSet.cpp:
(WebCore::RuleData::RuleData):
(WebCore::RuleSet::RuleSet):
(WebCore::RuleSet::~RuleSet):
(WebCore::RuleSet::addToRuleSet):
(WebCore::RuleSet::addRule):
(WebCore::RuleSet::addRulesFromSheet):
(WebCore::collectFeaturesFromRuleData): Deleted.
* css/RuleSet.h:
(WebCore::RuleSet::tagRules):
(WebCore::RuleSet::RuleSet): Deleted.
* css/StyleInvalidationAnalysis.cpp:
(WebCore::shouldDirtyAllStyle):
(WebCore::StyleInvalidationAnalysis::StyleInvalidationAnalysis):

    Add a new constructor that takes a ready made RuleSet instead of a stylesheet.

(WebCore::StyleInvalidationAnalysis::invalidateIfNeeded):
(WebCore::StyleInvalidationAnalysis::invalidateStyleForTree):
(WebCore::StyleInvalidationAnalysis::invalidateStyle):
(WebCore::StyleInvalidationAnalysis::invalidateStyle):

    New function for invalidating a subtree instead of the whole document.

* css/StyleInvalidationAnalysis.h:
(WebCore::StyleInvalidationAnalysis::dirtiesAllStyle):
(WebCore::StyleInvalidationAnalysis::hasShadowPseudoElementRulesInAuthorSheet):
* dom/Element.cpp:
(WebCore::classStringHasClassName):
(WebCore::collectClasses):
(WebCore::computeClassChange):

    Factor to return the changed classes.

(WebCore::invalidateStyleForClassChange):

    First filter out classes that don't show up in stylesheets. If something remains invalidate the current
    element for inline style change (that is a style change that doesn't affect descendants).

    Next check if there are any ancestorClassRules for the changed class. If so use the StyleInvalidationAnalysis
    to find any affected descendants and invalidate them with inline style change as well.

(WebCore::Element::classAttributeChanged):

    Invalidate for removed classes before setting new attribute value, invalidate for added classes afterwards.

(WebCore::Element::absoluteLinkURL):
(WebCore::checkSelectorForClassChange): Deleted.
* dom/ElementData.h:
(WebCore::ElementData::setClassNames):
(WebCore::ElementData::classNames):
(WebCore::ElementData::classNamesMemoryOffset):
(WebCore::ElementData::clearClass): Deleted.
(WebCore::ElementData::setClass): Deleted.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorecssDocumentRuleSetscpp">trunk/Source/WebCore/css/DocumentRuleSets.cpp</a></li>
<li><a href="#trunkSourceWebCorecssDocumentRuleSetsh">trunk/Source/WebCore/css/DocumentRuleSets.h</a></li>
<li><a href="#trunkSourceWebCorecssElementRuleCollectorcpp">trunk/Source/WebCore/css/ElementRuleCollector.cpp</a></li>
<li><a href="#trunkSourceWebCorecssElementRuleCollectorh">trunk/Source/WebCore/css/ElementRuleCollector.h</a></li>
<li><a href="#trunkSourceWebCorecssRuleFeaturecpp">trunk/Source/WebCore/css/RuleFeature.cpp</a></li>
<li><a href="#trunkSourceWebCorecssRuleFeatureh">trunk/Source/WebCore/css/RuleFeature.h</a></li>
<li><a href="#trunkSourceWebCorecssRuleSetcpp">trunk/Source/WebCore/css/RuleSet.cpp</a></li>
<li><a href="#trunkSourceWebCorecssRuleSeth">trunk/Source/WebCore/css/RuleSet.h</a></li>
<li><a href="#trunkSourceWebCorecssStyleInvalidationAnalysiscpp">trunk/Source/WebCore/css/StyleInvalidationAnalysis.cpp</a></li>
<li><a href="#trunkSourceWebCorecssStyleInvalidationAnalysish">trunk/Source/WebCore/css/StyleInvalidationAnalysis.h</a></li>
<li><a href="#trunkSourceWebCoredomElementcpp">trunk/Source/WebCore/dom/Element.cpp</a></li>
<li><a href="#trunkSourceWebCoredomElementDatah">trunk/Source/WebCore/dom/ElementData.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/ChangeLog        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -1,3 +1,117 @@
</span><ins>+2016-02-10  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        Optimize style invalidation after class attribute change
+        https://bugs.webkit.org/show_bug.cgi?id=154075
+        rdar://problem/12526450
+
+        Reviewed by Andreas Kling.
+
+        Currently a class attribute change invalidates style for the entire element subtree for any class found in the
+        active stylesheet set.
+
+        This patch optimizes class changes by building a new optimization structure called ancestorClassRules. It contains
+        rules that have class selectors in the portion of the complex selector that matches ancestor elements. The sets
+        of rules are hashes by the class name.
+
+        On class attribute change the existing StyleInvalidationAnalysis mechanism is used with ancestorClassRules to invalidate
+        exactly those descendants that are affected by the addition or removal of the class name. This is fast because the CSS JIT
+        makes selector matching cheap and the number of relevant rules is typically small.
+
+        This optimization is very effective on many dynamic pages. For example when focusing and unfocusing the web inspector it
+        cuts down the number of resolved elements from ~1000 to ~50. Even in PLT it reduces the number of resolved elements by ~11%.
+
+        * css/DocumentRuleSets.cpp:
+        (WebCore::DocumentRuleSets::collectFeatures):
+        (WebCore::DocumentRuleSets::ancestorClassRules):
+
+            Create optimization RuleSets on-demand when there is an actual dynamic class change.
+
+        * css/DocumentRuleSets.h:
+        (WebCore::DocumentRuleSets::features):
+        (WebCore::DocumentRuleSets::sibling):
+        (WebCore::DocumentRuleSets::uncommonAttribute):
+        * css/ElementRuleCollector.cpp:
+        (WebCore::ElementRuleCollector::ElementRuleCollector):
+
+            Add a new constructor that doesn't requires DocumentRuleSets. Only the user and author style is required.
+
+        (WebCore::ElementRuleCollector::matchAuthorRules):
+        (WebCore::ElementRuleCollector::matchUserRules):
+        * css/ElementRuleCollector.h:
+        * css/RuleFeature.cpp:
+        (WebCore::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):
+
+            Collect class names that show up in the ancestor portion of the selector.
+            Make this a member.
+
+        (WebCore::RuleFeatureSet::collectFeatures):
+
+            Move this code from RuleData.
+            Add the rule to ancestorClassRules if needed.
+
+        (WebCore::RuleFeatureSet::add):
+        (WebCore::RuleFeatureSet::clear):
+        (WebCore::RuleFeatureSet::shrinkToFit):
+        (WebCore::recursivelyCollectFeaturesFromSelector): Deleted.
+        (WebCore::RuleFeatureSet::collectFeaturesFromSelector): Deleted.
+        * css/RuleFeature.h:
+        (WebCore::RuleFeature::RuleFeature):
+        (WebCore::RuleFeatureSet::RuleFeatureSet): Deleted.
+        * css/RuleSet.cpp:
+        (WebCore::RuleData::RuleData):
+        (WebCore::RuleSet::RuleSet):
+        (WebCore::RuleSet::~RuleSet):
+        (WebCore::RuleSet::addToRuleSet):
+        (WebCore::RuleSet::addRule):
+        (WebCore::RuleSet::addRulesFromSheet):
+        (WebCore::collectFeaturesFromRuleData): Deleted.
+        * css/RuleSet.h:
+        (WebCore::RuleSet::tagRules):
+        (WebCore::RuleSet::RuleSet): Deleted.
+        * css/StyleInvalidationAnalysis.cpp:
+        (WebCore::shouldDirtyAllStyle):
+        (WebCore::StyleInvalidationAnalysis::StyleInvalidationAnalysis):
+
+            Add a new constructor that takes a ready made RuleSet instead of a stylesheet.
+
+        (WebCore::StyleInvalidationAnalysis::invalidateIfNeeded):
+        (WebCore::StyleInvalidationAnalysis::invalidateStyleForTree):
+        (WebCore::StyleInvalidationAnalysis::invalidateStyle):
+        (WebCore::StyleInvalidationAnalysis::invalidateStyle):
+
+            New function for invalidating a subtree instead of the whole document.
+
+        * css/StyleInvalidationAnalysis.h:
+        (WebCore::StyleInvalidationAnalysis::dirtiesAllStyle):
+        (WebCore::StyleInvalidationAnalysis::hasShadowPseudoElementRulesInAuthorSheet):
+        * dom/Element.cpp:
+        (WebCore::classStringHasClassName):
+        (WebCore::collectClasses):
+        (WebCore::computeClassChange):
+
+            Factor to return the changed classes.
+
+        (WebCore::invalidateStyleForClassChange):
+
+            First filter out classes that don't show up in stylesheets. If something remains invalidate the current
+            element for inline style change (that is a style change that doesn't affect descendants).
+
+            Next check if there are any ancestorClassRules for the changed class. If so use the StyleInvalidationAnalysis
+            to find any affected descendants and invalidate them with inline style change as well.
+
+        (WebCore::Element::classAttributeChanged):
+
+            Invalidate for removed classes before setting new attribute value, invalidate for added classes afterwards.
+
+        (WebCore::Element::absoluteLinkURL):
+        (WebCore::checkSelectorForClassChange): Deleted.
+        * dom/ElementData.h:
+        (WebCore::ElementData::setClassNames):
+        (WebCore::ElementData::classNames):
+        (WebCore::ElementData::classNamesMemoryOffset):
+        (WebCore::ElementData::clearClass): Deleted.
+        (WebCore::ElementData::setClass): Deleted.
+
</ins><span class="cx"> 2016-02-10  Myles C. Maxfield  &lt;mmaxfield@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Addressing post-review comments after r196322
</span></span></pre></div>
<a id="trunkSourceWebCorecssDocumentRuleSetscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/DocumentRuleSets.cpp (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/DocumentRuleSets.cpp        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/DocumentRuleSets.cpp        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -115,4 +115,14 @@
</span><span class="cx">     m_uncommonAttributeRuleSet = makeRuleSet(m_features.uncommonAttributeRules);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+RuleSet* DocumentRuleSets::ancestorClassRules(AtomicStringImpl* className) const
+{
+    auto addResult = m_ancestorClassRuleSet.add(className, nullptr);
+    if (addResult.isNewEntry) {
+        if (auto* rules = m_features.ancestorClassRules.get(className))
+            addResult.iterator-&gt;value = makeRuleSet(*rules);
+    }
+    return addResult.iterator-&gt;value.get();
+}
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorecssDocumentRuleSetsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/DocumentRuleSets.h (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/DocumentRuleSets.h        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/DocumentRuleSets.h        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #include &quot;RuleFeature.h&quot;
</span><span class="cx"> #include &quot;RuleSet.h&quot;
</span><span class="cx"> #include &lt;memory&gt;
</span><ins>+#include &lt;wtf/HashMap.h&gt;
</ins><span class="cx"> #include &lt;wtf/RefPtr.h&gt;
</span><span class="cx"> #include &lt;wtf/Vector.h&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -48,6 +49,7 @@
</span><span class="cx">     const RuleFeatureSet&amp; features() const { return m_features; }
</span><span class="cx">     RuleSet* sibling() const { return m_siblingRuleSet.get(); }
</span><span class="cx">     RuleSet* uncommonAttribute() const { return m_uncommonAttributeRuleSet.get(); }
</span><ins>+    RuleSet* ancestorClassRules(AtomicStringImpl* className) const;
</ins><span class="cx"> 
</span><span class="cx">     void initUserStyle(ExtensionStyleSheets&amp;, const MediaQueryEvaluator&amp;, StyleResolver&amp;);
</span><span class="cx">     void resetAuthorStyle();
</span><span class="lines">@@ -62,6 +64,7 @@
</span><span class="cx">     RuleFeatureSet m_features;
</span><span class="cx">     std::unique_ptr&lt;RuleSet&gt; m_siblingRuleSet;
</span><span class="cx">     std::unique_ptr&lt;RuleSet&gt; m_uncommonAttributeRuleSet;
</span><ins>+    mutable HashMap&lt;AtomicStringImpl*, std::unique_ptr&lt;RuleSet&gt;&gt; m_ancestorClassRuleSet;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorecssElementRuleCollectorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/ElementRuleCollector.cpp (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/ElementRuleCollector.cpp        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/ElementRuleCollector.cpp        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -80,12 +80,21 @@
</span><span class="cx"> ElementRuleCollector::ElementRuleCollector(Element&amp; element, RenderStyle* style, const DocumentRuleSets&amp; ruleSets, const SelectorFilter* selectorFilter)
</span><span class="cx">     : m_element(element)
</span><span class="cx">     , m_style(style)
</span><del>-    , m_ruleSets(ruleSets)
</del><ins>+    , m_authorStyle(*ruleSets.authorStyle())
+    , m_userStyle(ruleSets.userStyle())
</ins><span class="cx">     , m_selectorFilter(selectorFilter)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!m_selectorFilter || m_selectorFilter-&gt;parentStackIsConsistent(element.parentNode()));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+ElementRuleCollector::ElementRuleCollector(Element&amp; element, const RuleSet&amp; authorStyle, const SelectorFilter* selectorFilter)
+    : m_element(element)
+    , m_authorStyle(authorStyle)
+    , m_selectorFilter(selectorFilter)
+{
+    ASSERT(!m_selectorFilter || m_selectorFilter-&gt;parentStackIsConsistent(element.parentNode()));
+}
+
</ins><span class="cx"> StyleResolver::MatchResult&amp; ElementRuleCollector::matchedResult()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_mode == SelectorChecker::Mode::ResolvingStyle);
</span><span class="lines">@@ -203,7 +212,7 @@
</span><span class="cx">     m_result.ranges.lastAuthorRule = m_result.matchedProperties().size() - 1;
</span><span class="cx"> 
</span><span class="cx">     // Match global author rules.
</span><del>-    MatchRequest matchRequest(m_ruleSets.authorStyle(), includeEmptyRules);
</del><ins>+    MatchRequest matchRequest(&amp;m_authorStyle, includeEmptyRules);
</ins><span class="cx">     StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange();
</span><span class="cx">     collectMatchingRules(matchRequest, ruleRange);
</span><span class="cx">     collectMatchingRulesForRegion(matchRequest, ruleRange);
</span><span class="lines">@@ -236,13 +245,13 @@
</span><span class="cx"> 
</span><span class="cx"> void ElementRuleCollector::matchUserRules(bool includeEmptyRules)
</span><span class="cx"> {
</span><del>-    if (!m_ruleSets.userStyle())
</del><ins>+    if (!m_userStyle)
</ins><span class="cx">         return;
</span><span class="cx">     
</span><span class="cx">     clearMatchedRules();
</span><span class="cx"> 
</span><span class="cx">     m_result.ranges.lastUserRule = m_result.matchedProperties().size() - 1;
</span><del>-    MatchRequest matchRequest(m_ruleSets.userStyle(), includeEmptyRules);
</del><ins>+    MatchRequest matchRequest(m_userStyle, includeEmptyRules);
</ins><span class="cx">     StyleResolver::RuleRange ruleRange = m_result.ranges.userRuleRange();
</span><span class="cx">     collectMatchingRules(matchRequest, ruleRange);
</span><span class="cx">     collectMatchingRulesForRegion(matchRequest, ruleRange);
</span></span></pre></div>
<a id="trunkSourceWebCorecssElementRuleCollectorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/ElementRuleCollector.h (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/ElementRuleCollector.h        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/ElementRuleCollector.h        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -46,6 +46,7 @@
</span><span class="cx"> class ElementRuleCollector {
</span><span class="cx"> public:
</span><span class="cx">     ElementRuleCollector(Element&amp;, RenderStyle*, const DocumentRuleSets&amp;, const SelectorFilter*);
</span><ins>+    ElementRuleCollector(Element&amp;, const RuleSet&amp; authorStyle, const SelectorFilter*);
</ins><span class="cx"> 
</span><span class="cx">     void matchAllRules(bool matchAuthorAndUserStyles, bool includeSMILProperties);
</span><span class="cx">     void matchUARules();
</span><span class="lines">@@ -87,9 +88,10 @@
</span><span class="cx">     void commitStyleRelations(const SelectorChecker::StyleRelations&amp;);
</span><span class="cx"> 
</span><span class="cx">     Element&amp; m_element;
</span><del>-    RenderStyle* m_style;
-    const DocumentRuleSets&amp; m_ruleSets;
-    const SelectorFilter* m_selectorFilter;
</del><ins>+    RenderStyle* m_style { nullptr };
+    const RuleSet&amp; m_authorStyle;
+    const RuleSet* m_userStyle { nullptr };
+    const SelectorFilter* m_selectorFilter { nullptr };
</ins><span class="cx"> 
</span><span class="cx">     bool m_isPrintStyle { false };
</span><span class="cx">     const RenderRegion* m_regionForStyling { nullptr };
</span></span></pre></div>
<a id="trunkSourceWebCorecssRuleFeaturecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/RuleFeature.cpp (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/RuleFeature.cpp        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/RuleFeature.cpp        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -31,52 +31,67 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;CSSSelector.h&quot;
</span><span class="cx"> #include &quot;CSSSelectorList.h&quot;
</span><ins>+#include &quot;RuleSet.h&quot;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-static void recursivelyCollectFeaturesFromSelector(RuleFeatureSet&amp; features, const CSSSelector&amp; firstSelector, bool&amp; hasSiblingSelector)
</del><ins>+void RuleFeatureSet::recursivelyCollectFeaturesFromSelector(SelectorFeatures&amp; selectorFeatures, const CSSSelector&amp; firstSelector, bool matchesAncestor)
</ins><span class="cx"> {
</span><span class="cx">     const CSSSelector* selector = &amp;firstSelector;
</span><span class="cx">     do {
</span><span class="cx">         if (selector-&gt;match() == CSSSelector::Id)
</span><del>-            features.idsInRules.add(selector-&gt;value().impl());
-        else if (selector-&gt;match() == CSSSelector::Class)
-            features.classesInRules.add(selector-&gt;value().impl());
-        else if (selector-&gt;isAttributeSelector()) {
-            features.attributeCanonicalLocalNamesInRules.add(selector-&gt;attributeCanonicalLocalName().impl());
-            features.attributeLocalNamesInRules.add(selector-&gt;attribute().localName().impl());
</del><ins>+            idsInRules.add(selector-&gt;value().impl());
+        else if (selector-&gt;match() == CSSSelector::Class) {
+            classesInRules.add(selector-&gt;value().impl());
+            if (matchesAncestor)
+                selectorFeatures.classesMatchingAncestors.append(selector-&gt;value().impl());
+        } else if (selector-&gt;isAttributeSelector()) {
+            attributeCanonicalLocalNamesInRules.add(selector-&gt;attributeCanonicalLocalName().impl());
+            attributeLocalNamesInRules.add(selector-&gt;attribute().localName().impl());
</ins><span class="cx">         } else if (selector-&gt;match() == CSSSelector::PseudoElement) {
</span><span class="cx">             switch (selector-&gt;pseudoElementType()) {
</span><span class="cx">             case CSSSelector::PseudoElementFirstLine:
</span><del>-                features.usesFirstLineRules = true;
</del><ins>+                usesFirstLineRules = true;
</ins><span class="cx">                 break;
</span><span class="cx">             case CSSSelector::PseudoElementFirstLetter:
</span><del>-                features.usesFirstLetterRules = true;
</del><ins>+                usesFirstLetterRules = true;
</ins><span class="cx">                 break;
</span><span class="cx">             default:
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (!hasSiblingSelector &amp;&amp; selector-&gt;isSiblingSelector())
-            hasSiblingSelector = true;
</del><ins>+        if (!selectorFeatures.hasSiblingSelector &amp;&amp; selector-&gt;isSiblingSelector())
+            selectorFeatures.hasSiblingSelector = true;
</ins><span class="cx"> 
</span><span class="cx">         if (const CSSSelectorList* selectorList = selector-&gt;selectorList()) {
</span><span class="cx">             for (const CSSSelector* subSelector = selectorList-&gt;first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) {
</span><del>-                if (!hasSiblingSelector &amp;&amp; selector-&gt;isSiblingSelector())
-                    hasSiblingSelector = true;
-                recursivelyCollectFeaturesFromSelector(features, *subSelector, hasSiblingSelector);
</del><ins>+                if (!selectorFeatures.hasSiblingSelector &amp;&amp; selector-&gt;isSiblingSelector())
+                    selectorFeatures.hasSiblingSelector = true;
+                recursivelyCollectFeaturesFromSelector(selectorFeatures, *subSelector, matchesAncestor);
</ins><span class="cx">             }
</span><span class="cx">         }
</span><ins>+        if (selector-&gt;relation() == CSSSelector::Child || selector-&gt;relation() == CSSSelector::Descendant)
+            matchesAncestor = true;
</ins><span class="cx"> 
</span><span class="cx">         selector = selector-&gt;tagHistory();
</span><span class="cx">     } while (selector);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector&amp; firstSelector, bool&amp; hasSiblingSelector)
</del><ins>+void RuleFeatureSet::collectFeatures(const RuleData&amp; ruleData)
</ins><span class="cx"> {
</span><del>-    hasSiblingSelector = false;
-    recursivelyCollectFeaturesFromSelector(*this, firstSelector, hasSiblingSelector);
</del><ins>+    SelectorFeatures selectorFeatures;
+    recursivelyCollectFeaturesFromSelector(selectorFeatures, *ruleData.selector());
+    if (selectorFeatures.hasSiblingSelector)
+        siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
+    if (ruleData.containsUncommonAttributeSelector())
+        uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
+    for (auto* className : selectorFeatures.classesMatchingAncestors) {
+        auto addResult = ancestorClassRules.add(className, nullptr);
+        if (addResult.isNewEntry)
+            addResult.iterator-&gt;value = std::make_unique&lt;Vector&lt;RuleFeature&gt;&gt;();
+        addResult.iterator-&gt;value-&gt;append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void RuleFeatureSet::add(const RuleFeatureSet&amp; other)
</span><span class="lines">@@ -87,6 +102,13 @@
</span><span class="cx">     attributeLocalNamesInRules.add(other.attributeLocalNamesInRules.begin(), other.attributeLocalNamesInRules.end());
</span><span class="cx">     siblingRules.appendVector(other.siblingRules);
</span><span class="cx">     uncommonAttributeRules.appendVector(other.uncommonAttributeRules);
</span><ins>+    for (auto&amp; keyValuePair : other.ancestorClassRules) {
+        auto addResult = ancestorClassRules.add(keyValuePair.key, nullptr);
+        if (addResult.isNewEntry)
+            addResult.iterator-&gt;value = std::make_unique&lt;Vector&lt;RuleFeature&gt;&gt;(*keyValuePair.value);
+        else
+            addResult.iterator-&gt;value-&gt;appendVector(*keyValuePair.value);
+    }
</ins><span class="cx">     usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules;
</span><span class="cx">     usesFirstLetterRules = usesFirstLetterRules || other.usesFirstLetterRules;
</span><span class="cx"> }
</span><span class="lines">@@ -99,6 +121,7 @@
</span><span class="cx">     attributeLocalNamesInRules.clear();
</span><span class="cx">     siblingRules.clear();
</span><span class="cx">     uncommonAttributeRules.clear();
</span><ins>+    ancestorClassRules.clear();
</ins><span class="cx">     usesFirstLineRules = false;
</span><span class="cx">     usesFirstLetterRules = false;
</span><span class="cx"> }
</span><span class="lines">@@ -107,6 +130,8 @@
</span><span class="cx"> {
</span><span class="cx">     siblingRules.shrinkToFit();
</span><span class="cx">     uncommonAttributeRules.shrinkToFit();
</span><ins>+    for (auto&amp; rules : ancestorClassRules.values())
+        rules-&gt;shrinkToFit();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorecssRuleFeatureh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/RuleFeature.h (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/RuleFeature.h        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/RuleFeature.h        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -29,8 +29,9 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+class CSSSelector;
+class RuleData;
</ins><span class="cx"> class StyleRule;
</span><del>-class CSSSelector;
</del><span class="cx"> 
</span><span class="cx"> struct RuleFeature {
</span><span class="cx">     RuleFeature(StyleRule* rule, unsigned selectorIndex, bool hasDocumentSecurityOrigin)
</span><span class="lines">@@ -45,15 +46,10 @@
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> struct RuleFeatureSet {
</span><del>-    RuleFeatureSet()
-        : usesFirstLineRules(false)
-        , usesFirstLetterRules(false)
-    { }
-
</del><span class="cx">     void add(const RuleFeatureSet&amp;);
</span><span class="cx">     void clear();
</span><span class="cx">     void shrinkToFit();
</span><del>-    void collectFeaturesFromSelector(const CSSSelector&amp;, bool&amp; hasSiblingSelector);
</del><ins>+    void collectFeatures(const RuleData&amp;);
</ins><span class="cx"> 
</span><span class="cx">     HashSet&lt;AtomicStringImpl*&gt; idsInRules;
</span><span class="cx">     HashSet&lt;AtomicStringImpl*&gt; classesInRules;
</span><span class="lines">@@ -61,8 +57,16 @@
</span><span class="cx">     HashSet&lt;AtomicStringImpl*&gt; attributeLocalNamesInRules;
</span><span class="cx">     Vector&lt;RuleFeature&gt; siblingRules;
</span><span class="cx">     Vector&lt;RuleFeature&gt; uncommonAttributeRules;
</span><del>-    bool usesFirstLineRules;
-    bool usesFirstLetterRules;
</del><ins>+    HashMap&lt;AtomicStringImpl*, std::unique_ptr&lt;Vector&lt;RuleFeature&gt;&gt;&gt; ancestorClassRules;
+    bool usesFirstLineRules { false };
+    bool usesFirstLetterRules { false };
+
+private:
+    struct SelectorFeatures {
+        bool hasSiblingSelector { false };
+        Vector&lt;AtomicStringImpl*&gt; classesMatchingAncestors;
+    };
+    void recursivelyCollectFeaturesFromSelector(SelectorFeatures&amp;, const CSSSelector&amp;, bool matchesAncestor = false);
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorecssRuleSetcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/RuleSet.cpp (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/RuleSet.cpp        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/RuleSet.cpp        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -172,15 +172,12 @@
</span><span class="cx">     SelectorFilter::collectIdentifierHashes(selector(), m_descendantSelectorIdentifierHashes, maximumIdentifierCount);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void collectFeaturesFromRuleData(RuleFeatureSet&amp; features, const RuleData&amp; ruleData)
</del><ins>+RuleSet::RuleSet()
</ins><span class="cx"> {
</span><del>-    bool hasSiblingSelector;
-    features.collectFeaturesFromSelector(*ruleData.selector(), hasSiblingSelector);
</del><ins>+}
</ins><span class="cx"> 
</span><del>-    if (hasSiblingSelector)
-        features.siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
-    if (ruleData.containsUncommonAttributeSelector())
-        features.uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
</del><ins>+RuleSet::~RuleSet()
+{
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void RuleSet::addToRuleSet(AtomicStringImpl* key, AtomRuleMap&amp; map, const RuleData&amp; ruleData)
</span><span class="lines">@@ -203,7 +200,7 @@
</span><span class="cx"> void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex, AddRuleFlags addRuleFlags)
</span><span class="cx"> {
</span><span class="cx">     RuleData ruleData(rule, selectorIndex, m_ruleCount++, addRuleFlags);
</span><del>-    collectFeaturesFromRuleData(m_features, ruleData);
</del><ins>+    m_features.collectFeatures(ruleData);
</ins><span class="cx"> 
</span><span class="cx">     unsigned classBucketSize = 0;
</span><span class="cx">     const CSSSelector* tagSelector = nullptr;
</span></span></pre></div>
<a id="trunkSourceWebCorecssRuleSeth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/RuleSet.h (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/RuleSet.h        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/RuleSet.h        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -157,6 +157,7 @@
</span><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     RuleSet();
</span><ins>+    ~RuleSet();
</ins><span class="cx"> 
</span><span class="cx">     typedef Vector&lt;RuleData, 1&gt; RuleDataVector;
</span><span class="cx">     typedef HashMap&lt;AtomicStringImpl*, std::unique_ptr&lt;RuleDataVector&gt;&gt; AtomRuleMap;
</span><span class="lines">@@ -213,18 +214,12 @@
</span><span class="cx">     RuleDataVector m_focusPseudoClassRules;
</span><span class="cx">     RuleDataVector m_universalRules;
</span><span class="cx">     Vector&lt;StyleRulePage*&gt; m_pageRules;
</span><del>-    unsigned m_ruleCount;
-    bool m_autoShrinkToFitEnabled;
</del><ins>+    unsigned m_ruleCount { 0 };
+    bool m_autoShrinkToFitEnabled { false };
</ins><span class="cx">     RuleFeatureSet m_features;
</span><span class="cx">     Vector&lt;RuleSetSelectorPair&gt; m_regionSelectorsAndRuleSets;
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-inline RuleSet::RuleSet()
-    : m_ruleCount(0)
-    , m_autoShrinkToFitEnabled(true)
-{
-}
-
</del><span class="cx"> inline const RuleSet::RuleDataVector* RuleSet::tagRules(AtomicStringImpl* key, bool isHTMLName) const
</span><span class="cx"> {
</span><span class="cx">     const AtomRuleMap* tagRules;
</span></span></pre></div>
<a id="trunkSourceWebCorecssStyleInvalidationAnalysiscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/StyleInvalidationAnalysis.cpp (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/StyleInvalidationAnalysis.cpp        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/StyleInvalidationAnalysis.cpp        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -75,20 +75,28 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> StyleInvalidationAnalysis::StyleInvalidationAnalysis(const Vector&lt;StyleSheetContents*&gt;&amp; sheets, const MediaQueryEvaluator&amp; mediaQueryEvaluator)
</span><del>-    : m_dirtiesAllStyle(shouldDirtyAllStyle(sheets))
</del><ins>+    : m_ownedRuleSet(std::make_unique&lt;RuleSet&gt;())
+    , m_ruleSet(*m_ownedRuleSet)
+    , m_dirtiesAllStyle(shouldDirtyAllStyle(sheets))
</ins><span class="cx"> {
</span><span class="cx">     if (m_dirtiesAllStyle)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_ruleSets.resetAuthorStyle();
</del><ins>+    m_ownedRuleSet-&gt;disableAutoShrinkToFit();
</ins><span class="cx">     for (auto&amp; sheet : sheets)
</span><del>-        m_ruleSets.authorStyle()-&gt;addRulesFromSheet(*sheet, mediaQueryEvaluator);
</del><ins>+        m_ownedRuleSet-&gt;addRulesFromSheet(*sheet, mediaQueryEvaluator);
</ins><span class="cx"> 
</span><del>-    m_hasShadowPseudoElementRulesInAuthorSheet = m_ruleSets.authorStyle()-&gt;hasShadowPseudoElementRules();
</del><ins>+    m_hasShadowPseudoElementRulesInAuthorSheet = m_ruleSet.hasShadowPseudoElementRules();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-StyleInvalidationAnalysis::CheckDescendants StyleInvalidationAnalysis::invalidateIfNeeded(Element&amp; element, SelectorFilter&amp; filter)
</del><ins>+StyleInvalidationAnalysis::StyleInvalidationAnalysis(const RuleSet&amp; ruleSet)
+    : m_ruleSet(ruleSet)
+    , m_hasShadowPseudoElementRulesInAuthorSheet(ruleSet.hasShadowPseudoElementRules())
</ins><span class="cx"> {
</span><ins>+}
+
+StyleInvalidationAnalysis::CheckDescendants StyleInvalidationAnalysis::invalidateIfNeeded(Element&amp; element, const SelectorFilter* filter)
+{
</ins><span class="cx">     if (m_hasShadowPseudoElementRulesInAuthorSheet) {
</span><span class="cx">         // FIXME: This could do actual rule matching too.
</span><span class="cx">         if (auto* shadowRoot = element.shadowRoot())
</span><span class="lines">@@ -97,7 +105,7 @@
</span><span class="cx"> 
</span><span class="cx">     switch (element.styleChangeType()) {
</span><span class="cx">     case NoStyleChange: {
</span><del>-        ElementRuleCollector ruleCollector(element, nullptr, m_ruleSets, &amp;filter);
</del><ins>+        ElementRuleCollector ruleCollector(element, m_ruleSet, filter);
</ins><span class="cx">         ruleCollector.setMode(SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements);
</span><span class="cx">         ruleCollector.matchAuthorRules(false);
</span><span class="cx"> 
</span><span class="lines">@@ -116,7 +124,7 @@
</span><span class="cx">     return CheckDescendants::Yes;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void StyleInvalidationAnalysis::invalidateStyleForTree(Element&amp; root, SelectorFilter&amp; filter)
</del><ins>+void StyleInvalidationAnalysis::invalidateStyleForTree(Element&amp; root, SelectorFilter* filter)
</ins><span class="cx"> {
</span><span class="cx">     if (invalidateIfNeeded(root, filter) == CheckDescendants::No)
</span><span class="cx">         return;
</span><span class="lines">@@ -130,11 +138,13 @@
</span><span class="cx">         if (parentStack.isEmpty() || parentStack.last() != parent) {
</span><span class="cx">             if (parent == previousElement) {
</span><span class="cx">                 parentStack.append(parent);
</span><del>-                filter.pushParent(parent);
</del><ins>+                if (filter)
+                    filter-&gt;pushParent(parent);
</ins><span class="cx">             } else {
</span><span class="cx">                 while (parentStack.last() != parent) {
</span><span class="cx">                     parentStack.removeLast();
</span><del>-                    filter.popParent();
</del><ins>+                    if (filter)
+                        filter-&gt;popParent();
</ins><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="lines">@@ -150,15 +160,21 @@
</span><span class="cx"> void StyleInvalidationAnalysis::invalidateStyle(Document&amp; document)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!m_dirtiesAllStyle);
</span><del>-    if (!m_ruleSets.authorStyle())
-        return;
</del><span class="cx"> 
</span><span class="cx">     Element* documentElement = document.documentElement();
</span><span class="cx">     if (!documentElement)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     SelectorFilter filter;
</span><del>-    invalidateStyleForTree(*documentElement, filter);
</del><ins>+    invalidateStyleForTree(*documentElement, &amp;filter);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void StyleInvalidationAnalysis::invalidateStyle(Element&amp; element)
+{
+    ASSERT(!m_dirtiesAllStyle);
+
+    // Don't use SelectorFilter as the rule sets here tend to be small and the filter would have setup cost deep in the tree.
+    invalidateStyleForTree(element, nullptr);
</ins><span class="cx"> }
</span><ins>+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCorecssStyleInvalidationAnalysish"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/StyleInvalidationAnalysis.h (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/StyleInvalidationAnalysis.h        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/css/StyleInvalidationAnalysis.h        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -26,32 +26,37 @@
</span><span class="cx"> #ifndef StyleInvalidationAnalysis_h
</span><span class="cx"> #define StyleInvalidationAnalysis_h
</span><span class="cx"> 
</span><del>-#include &quot;DocumentRuleSets.h&quot;
</del><span class="cx"> #include &lt;wtf/HashSet.h&gt;
</span><span class="cx"> #include &lt;wtf/text/AtomicStringImpl.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> class Document;
</span><ins>+class Element;
+class MediaQueryEvaluator;
+class RuleSet;
</ins><span class="cx"> class SelectorFilter;
</span><span class="cx"> class StyleSheetContents;
</span><span class="cx"> 
</span><span class="cx"> class StyleInvalidationAnalysis {
</span><span class="cx"> public:
</span><span class="cx">     StyleInvalidationAnalysis(const Vector&lt;StyleSheetContents*&gt;&amp;, const MediaQueryEvaluator&amp;);
</span><ins>+    StyleInvalidationAnalysis(const RuleSet&amp;);
</ins><span class="cx"> 
</span><span class="cx">     bool dirtiesAllStyle() const { return m_dirtiesAllStyle; }
</span><span class="cx">     bool hasShadowPseudoElementRulesInAuthorSheet() const { return m_hasShadowPseudoElementRulesInAuthorSheet; }
</span><span class="cx">     void invalidateStyle(Document&amp;);
</span><ins>+    void invalidateStyle(Element&amp;);
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     enum class CheckDescendants { Yes, No };
</span><del>-    CheckDescendants invalidateIfNeeded(Element&amp;, SelectorFilter&amp;);
-    void invalidateStyleForTree(Element&amp;, SelectorFilter&amp;);
</del><ins>+    CheckDescendants invalidateIfNeeded(Element&amp;, const SelectorFilter*);
+    void invalidateStyleForTree(Element&amp;, SelectorFilter*);
</ins><span class="cx"> 
</span><ins>+    std::unique_ptr&lt;RuleSet&gt; m_ownedRuleSet;
+    const RuleSet&amp; m_ruleSet;
</ins><span class="cx">     bool m_dirtiesAllStyle { false };
</span><span class="cx">     bool m_hasShadowPseudoElementRulesInAuthorSheet { false };
</span><del>-    DocumentRuleSets m_ruleSets;
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoredomElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Element.cpp (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Element.cpp        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/dom/Element.cpp        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -76,6 +76,7 @@
</span><span class="cx"> #include &quot;ScrollLatchingState.h&quot;
</span><span class="cx"> #include &quot;SelectorQuery.h&quot;
</span><span class="cx"> #include &quot;Settings.h&quot;
</span><ins>+#include &quot;StyleInvalidationAnalysis.h&quot;
</ins><span class="cx"> #include &quot;StyleProperties.h&quot;
</span><span class="cx"> #include &quot;StyleResolver.h&quot;
</span><span class="cx"> #include &quot;StyleTreeResolver.h&quot;
</span><span class="lines">@@ -1299,25 +1300,39 @@
</span><span class="cx">     return classStringHasClassName(newClassString.characters16(), length);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool checkSelectorForClassChange(const SpaceSplitString&amp; changedClasses, const StyleResolver&amp; styleResolver)
</del><ins>+static Vector&lt;AtomicStringImpl*, 4&gt; collectClasses(const SpaceSplitString&amp; classes)
</ins><span class="cx"> {
</span><del>-    unsigned changedSize = changedClasses.size();
-    for (unsigned i = 0; i &lt; changedSize; ++i) {
-        if (styleResolver.hasSelectorForClass(changedClasses[i]))
-            return true;
-    }
-    return false;
</del><ins>+    Vector&lt;AtomicStringImpl*, 4&gt; result;
+    result.reserveCapacity(classes.size());
+    for (unsigned i = 0; i &lt; classes.size(); ++i)
+        result.uncheckedAppend(classes[i].impl());
+    return result;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool checkSelectorForClassChange(const SpaceSplitString&amp; oldClasses, const SpaceSplitString&amp; newClasses, const StyleResolver&amp; styleResolver)
</del><ins>+struct ClassChange {
+    Vector&lt;AtomicStringImpl*, 4&gt; added;
+    Vector&lt;AtomicStringImpl*, 4&gt; removed;
+};
+
+static ClassChange computeClassChange(const SpaceSplitString&amp; oldClasses, const SpaceSplitString&amp; newClasses)
</ins><span class="cx"> {
</span><ins>+    ClassChange classChange;
+
</ins><span class="cx">     unsigned oldSize = oldClasses.size();
</span><del>-    if (!oldSize)
-        return checkSelectorForClassChange(newClasses, styleResolver);
</del><ins>+    unsigned newSize = newClasses.size();
+
+    if (!oldSize) {
+        classChange.added = collectClasses(newClasses);
+        return classChange;
+    }
+    if (!newSize) {
+        classChange.removed = collectClasses(oldClasses);
+        return classChange;
+    }
+
</ins><span class="cx">     BitVector remainingClassBits;
</span><span class="cx">     remainingClassBits.ensureSize(oldSize);
</span><span class="cx">     // Class vectors tend to be very short. This is faster than using a hash table.
</span><del>-    unsigned newSize = newClasses.size();
</del><span class="cx">     for (unsigned i = 0; i &lt; newSize; ++i) {
</span><span class="cx">         bool foundFromBoth = false;
</span><span class="cx">         for (unsigned j = 0; j &lt; oldSize; ++j) {
</span><span class="lines">@@ -1328,47 +1343,81 @@
</span><span class="cx">         }
</span><span class="cx">         if (foundFromBoth)
</span><span class="cx">             continue;
</span><del>-        if (styleResolver.hasSelectorForClass(newClasses[i]))
-            return true;
</del><ins>+        classChange.added.append(newClasses[i].impl());
</ins><span class="cx">     }
</span><span class="cx">     for (unsigned i = 0; i &lt; oldSize; ++i) {
</span><span class="cx">         // If the bit is not set the the corresponding class has been removed.
</span><span class="cx">         if (remainingClassBits.quickGet(i))
</span><span class="cx">             continue;
</span><del>-        if (styleResolver.hasSelectorForClass(oldClasses[i]))
-            return true;
</del><ins>+        classChange.removed.append(oldClasses[i].impl());
</ins><span class="cx">     }
</span><del>-    return false;
</del><ins>+
+    return classChange;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static void invalidateStyleForClassChange(Element&amp; element, const Vector&lt;AtomicStringImpl*, 4&gt;&amp; changedClasses, const DocumentRuleSets&amp; ruleSets)
+{
+    Vector&lt;AtomicStringImpl*, 4&gt; changedClassesAffectingStyle;
+    for (auto* changedClass : changedClasses) {
+        if (ruleSets.features().classesInRules.contains(changedClass))
+            changedClassesAffectingStyle.append(changedClass);
+    };
+
+    if (changedClassesAffectingStyle.isEmpty())
+        return;
+
+    if (element.shadowRoot() &amp;&amp; ruleSets.authorStyle()-&gt;hasShadowPseudoElementRules()) {
+        element.setNeedsStyleRecalc(FullStyleChange);
+        return;
+    }
+
+    element.setNeedsStyleRecalc(InlineStyleChange);
+
+    if (!element.firstElementChild())
+        return;
+
+    for (auto* changedClass : changedClassesAffectingStyle) {
+        auto* ancestorClassRules = ruleSets.ancestorClassRules(changedClass);
+        if (!ancestorClassRules)
+            continue;
+        StyleInvalidationAnalysis invalidationAnalysis(*ancestorClassRules);
+        invalidationAnalysis.invalidateStyle(element);
+    }
+}
+
</ins><span class="cx"> void Element::classAttributeChanged(const AtomicString&amp; newClassString)
</span><span class="cx"> {
</span><ins>+    // Note: We'll need ElementData, but it doesn't have to be UniqueElementData.
+    if (!elementData())
+        ensureUniqueElementData();
+
+    bool shouldFoldCase = document().inQuirksMode();
+    bool newStringHasClasses = classStringHasClassName(newClassString);
+
+    auto oldClassNames = elementData()-&gt;classNames();
+    auto newClassNames = newStringHasClasses ? SpaceSplitString(newClassString, shouldFoldCase) : SpaceSplitString();
+
</ins><span class="cx">     StyleResolver* styleResolver = document().styleResolverIfExists();
</span><del>-    bool testShouldInvalidateStyle = inRenderedDocument() &amp;&amp; styleResolver &amp;&amp; styleChangeType() &lt; FullStyleChange;
-    bool shouldInvalidateStyle = false;
</del><ins>+    bool shouldInvalidateStyle = inRenderedDocument() &amp;&amp; styleResolver &amp;&amp; styleChangeType() &lt; FullStyleChange;
</ins><span class="cx"> 
</span><del>-    if (classStringHasClassName(newClassString)) {
-        const bool shouldFoldCase = document().inQuirksMode();
-        // Note: We'll need ElementData, but it doesn't have to be UniqueElementData.
-        if (!elementData())
-            ensureUniqueElementData();
-        const SpaceSplitString oldClasses = elementData()-&gt;classNames();
-        elementData()-&gt;setClass(newClassString, shouldFoldCase);
-        const SpaceSplitString&amp; newClasses = elementData()-&gt;classNames();
-        shouldInvalidateStyle = testShouldInvalidateStyle &amp;&amp; checkSelectorForClassChange(oldClasses, newClasses, *styleResolver);
-    } else if (elementData()) {
-        const SpaceSplitString&amp; oldClasses = elementData()-&gt;classNames();
-        shouldInvalidateStyle = testShouldInvalidateStyle &amp;&amp; checkSelectorForClassChange(oldClasses, *styleResolver);
-        elementData()-&gt;clearClass();
</del><ins>+    ClassChange classChange;
+    if (shouldInvalidateStyle) {
+        classChange = computeClassChange(oldClassNames, newClassNames);
+        if (!classChange.removed.isEmpty())
+            invalidateStyleForClassChange(*this, classChange.removed, styleResolver-&gt;ruleSets());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    elementData()-&gt;setClassNames(newClassNames);
+
+    if (shouldInvalidateStyle) {
+        if (!classChange.added.isEmpty())
+            invalidateStyleForClassChange(*this, classChange.added, styleResolver-&gt;ruleSets());
+    }
+
</ins><span class="cx">     if (hasRareData()) {
</span><span class="cx">         if (auto* classList = elementRareData()-&gt;classList())
</span><span class="cx">             classList-&gt;attributeValueChanged(newClassString);
</span><span class="cx">     }
</span><del>-
-    if (shouldInvalidateStyle)
-        setNeedsStyleRecalc();
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> URL Element::absoluteLinkURL() const
</span></span></pre></div>
<a id="trunkSourceWebCoredomElementDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/ElementData.h (196382 => 196383)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/ElementData.h        2016-02-10 20:46:38 UTC (rev 196382)
+++ trunk/Source/WebCore/dom/ElementData.h        2016-02-10 20:47:04 UTC (rev 196383)
</span><span class="lines">@@ -85,8 +85,7 @@
</span><span class="cx"> 
</span><span class="cx">     static const unsigned attributeNotFound = static_cast&lt;unsigned&gt;(-1);
</span><span class="cx"> 
</span><del>-    void clearClass() const { m_classNames.clear(); }
-    void setClass(const AtomicString&amp; className, bool shouldFoldCase) const { m_classNames.set(className, shouldFoldCase); }
</del><ins>+    void setClassNames(const SpaceSplitString&amp; classNames) const { m_classNames = classNames; }
</ins><span class="cx">     const SpaceSplitString&amp; classNames() const { return m_classNames; }
</span><span class="cx">     static ptrdiff_t classNamesMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_classNames); }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>