<!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>[196629] trunk</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/196629">196629</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2016-02-16 00:20:58 -0800 (Tue, 16 Feb 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Optimize style invalidations for attribute selectors
https://bugs.webkit.org/show_bug.cgi?id=154242

Reviewed by Andreas Kling.

Source/WebCore:

Currently we invalidate the whole element subtree if there are any attribute selectors for the changed attribute.
This is slow as generally few if any elements are really affected. Using attribute selectors for dynamic styling
should be performant.

This patch implements optimization strategy for attributes similar to what we already have for classes:

- Collect a map of all rules that contains descendant-affecting attribute selectors for a given attribute.
- When an attribute value changes check if there are any such rules for it.
- Check if the value change affects the results of any of the attribute selectors.
- Only if it does invalidate the exact descendant elements affected by the rules.

Test: fast/css/style-invalidation-attribute-change-descendants.html

* WebCore.xcodeproj/project.pbxproj:
* css/DocumentRuleSets.cpp:
(WebCore::DocumentRuleSets::ancestorClassRules):
(WebCore::DocumentRuleSets::ancestorAttributeRulesForHTML):

    Create optimization RuleSets when needed.

* css/DocumentRuleSets.h:
(WebCore::DocumentRuleSets::uncommonAttribute):
(WebCore::DocumentRuleSets::features):
* css/RuleFeature.cpp:
(WebCore::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):
(WebCore::makeAttributeSelectorKey):
(WebCore::RuleFeatureSet::collectFeatures):

    Collect rules with descendant affecting attribute selectors.

(WebCore::RuleFeatureSet::add):
(WebCore::RuleFeatureSet::clear):
(WebCore::RuleFeatureSet::shrinkToFit):
* css/RuleFeature.h:
* css/SelectorChecker.cpp:
(WebCore::anyAttributeMatches):
(WebCore::SelectorChecker::attributeSelectorMatches):

    Expose function for matching single attribute selectors.

(WebCore::canMatchHoverOrActiveInQuirksMode):
* css/SelectorChecker.h:
* dom/Attr.cpp:
(WebCore::Attr::setValue):
(WebCore::Attr::childrenChanged):
* dom/Element.cpp:
(WebCore::Element::setAttributeInternal):
(WebCore::makeIdForStyleResolution):
(WebCore::Element::attributeChanged):
(WebCore::Element::removeAttributeInternal):
(WebCore::Element::addAttributeInternal):
(WebCore::Element::removeAttribute):

    Add AttributeChangeInvalidation where needed.

(WebCore::Element::needsStyleInvalidation):

    Move to Element from ClassChangeInvalidation.

(WebCore::Element::willModifyAttribute):

    No more full style invalidation on attribute change.

* style/AttributeChangeInvalidation.cpp: Added.
(WebCore::Style::AttributeChangeInvalidation::invalidateStyle):

    Invalidate local style.
    Check if we need to invalidate descendants by looking into ancestorAttributeRules.

(WebCore::Style::AttributeChangeInvalidation::invalidateDescendants):

    Use StyleInvalidationAnalysis to invalidate the subtree for the relevant rules.

* style/AttributeChangeInvalidation.h: Added.
(WebCore::Style::AttributeChangeInvalidation::needsInvalidation):
(WebCore::Style::AttributeChangeInvalidation::AttributeChangeInvalidation):
(WebCore::Style::AttributeChangeInvalidation::~AttributeChangeInvalidation):

    If needed, invalidate descendants before and after attribute change to catch rules that start and stop applying.

LayoutTests:

* fast/css/style-invalidation-attribute-change-descendants-expected.txt: Added.
* fast/css/style-invalidation-attribute-change-descendants.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreCMakeListstxt">trunk/Source/WebCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreWebCorevcxprojWebCorevcxproj">trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</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="#trunkSourceWebCorecssRuleFeaturecpp">trunk/Source/WebCore/css/RuleFeature.cpp</a></li>
<li><a href="#trunkSourceWebCorecssRuleFeatureh">trunk/Source/WebCore/css/RuleFeature.h</a></li>
<li><a href="#trunkSourceWebCorecssSelectorCheckercpp">trunk/Source/WebCore/css/SelectorChecker.cpp</a></li>
<li><a href="#trunkSourceWebCorecssSelectorCheckerh">trunk/Source/WebCore/css/SelectorChecker.h</a></li>
<li><a href="#trunkSourceWebCoredomAttrcpp">trunk/Source/WebCore/dom/Attr.cpp</a></li>
<li><a href="#trunkSourceWebCoredomElementcpp">trunk/Source/WebCore/dom/Element.cpp</a></li>
<li><a href="#trunkSourceWebCoredomElementh">trunk/Source/WebCore/dom/Element.h</a></li>
<li><a href="#trunkSourceWebCorestyleClassChangeInvalidationh">trunk/Source/WebCore/style/ClassChangeInvalidation.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastcssstyleinvalidationattributechangedescendantsexpectedtxt">trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastcssstyleinvalidationattributechangedescendantshtml">trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants.html</a></li>
<li><a href="#trunkSourceWebCorestyleAttributeChangeInvalidationcpp">trunk/Source/WebCore/style/AttributeChangeInvalidation.cpp</a></li>
<li><a href="#trunkSourceWebCorestyleAttributeChangeInvalidationh">trunk/Source/WebCore/style/AttributeChangeInvalidation.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/LayoutTests/ChangeLog        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2016-02-15  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        Optimize style invalidations for attribute selectors
+        https://bugs.webkit.org/show_bug.cgi?id=154242
+
+        Reviewed by Andreas Kling.
+
+        * fast/css/style-invalidation-attribute-change-descendants-expected.txt: Added.
+        * fast/css/style-invalidation-attribute-change-descendants.html: Added.
+
</ins><span class="cx"> 2016-02-16  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Do security checks early in JSDOMWindow::put*()
</span></span></pre></div>
<a id="trunkLayoutTestsfastcssstyleinvalidationattributechangedescendantsexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants-expected.txt (0 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants-expected.txt        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -0,0 +1,149 @@
</span><ins>+Test that we invalidate the element subtree minimally on class attribute change
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS hasExpectedStyle is true
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value ''
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value2'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Removing attribute 'myattr'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value3'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'dummy value3'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value4-foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value4-foobar'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'dummy value4-foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value5foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value5foobar'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'foovalue5'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'foovalue6'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myAttr' value 'foobarvalue6'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'MYATTR' value 'value6foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value7'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value7foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myATTR' value 'foovalue7foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'VALUE7foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'Value8'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value8foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myATTR' value 'FOOVALue8foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'VALUE8foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'VALUE 8foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr2' value ''
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr2' value 'foo'
+PASS testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;target&quot;, &quot;NoStyleChange&quot;) is true
+PASS testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;) is true
+PASS hasExpectedStyle is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastcssstyleinvalidationattributechangedescendantshtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants.html (0 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants.html                                (rev 0)
+++ trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants.html        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -0,0 +1,242 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;style&gt;
+* {
+    color: black;
+}
+[myattr] target {
+    color: rgb(1, 0, 0);
+}
+
+[myattr=value2] &gt; inert target {
+    color: rgb(2, 0, 0);
+}
+
+[myattr~=value3] inert + target {
+    color: rgb(3, 0, 0);
+}
+
+[MYATTR|=value4] inert ~ target {
+    color: rgb(4, 0, 0);
+}
+
+[myAttr^=value5] target {
+    color: rgb(5, 0, 0);
+}
+
+[myattr$=value6] target {
+    color: rgb(6, 0, 0);
+}
+
+[myATTR*=value7] target {
+    color: rgb(7, 0, 0);
+}
+
+[myattr*=vaLUE8 i] target {
+    color: rgb(8, 0, 0);
+}
+
+[myattr2] &gt; target {
+    color: rgb(9, 0, 0);
+}
+
+&lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+    &lt;root&gt;
+        &lt;!-- With renderer --&gt;
+        &lt;inert&gt;
+            &lt;inert&gt;
+                &lt;inert&gt;&lt;/inert&gt;
+                &lt;target&gt;
+                    &lt;inert&gt;&lt;/inert&gt;
+                    &lt;target&gt;&lt;/target&gt;
+                &lt;/target&gt;
+            &lt;/inert&gt;
+            &lt;target&gt;&lt;/target&gt;
+            &lt;inert&gt;&lt;/inert&gt;
+        &lt;/inert&gt;
+    &lt;/root&gt;
+    &lt;root style=&quot;display:none;&quot;&gt;
+        &lt;!-- Without renderer --&gt;
+        &lt;inert&gt;
+            &lt;inert&gt;
+                &lt;inert&gt;&lt;/inert&gt;
+                &lt;target&gt;
+                    &lt;inert&gt;&lt;/inert&gt;
+                    &lt;target&gt;&lt;/target&gt;
+                &lt;/target&gt;
+            &lt;/inert&gt;
+            &lt;target&gt;&lt;/target&gt;
+            &lt;inert&gt;&lt;/inert&gt;
+        &lt;/inert&gt;
+    &lt;/root&gt;
+&lt;/body&gt;
+&lt;script&gt;
+
+description('Test that we invalidate the element subtree minimally on class attribute change');
+
+function testStyleChangeType(tag, type)
+{
+    var elements = document.querySelectorAll(tag);
+    for (var i = 0; i &lt; elements.length; ++i) {
+        if (window.internals.styleChangeType(elements[i]) != type)
+            return false;
+    }
+    return true;
+}
+
+function testStyleInvalidation(expectedDescendantStyleChange) {
+    // Ideally we would't invalidate the root at all.
+    shouldBeTrue('testStyleChangeType(&quot;root&quot;, &quot;NoStyleChange&quot;) || testStyleChangeType(&quot;root&quot;, &quot;InlineStyleChange&quot;)');
+
+    shouldBeTrue('testStyleChangeType(&quot;target&quot;, &quot;' + expectedDescendantStyleChange +'&quot;)');
+
+    shouldBeTrue('testStyleChangeType(&quot;inert&quot;, &quot;NoStyleChange&quot;)');
+}
+
+function setAttribute(name, value) {
+    debug(&quot;Setting attribute '&quot; + name + &quot;' value '&quot; + value + &quot;'&quot;);
+    var allRoots = document.querySelectorAll(&quot;root&quot;);
+    allRoots[0].setAttribute(name, value);
+    allRoots[1].setAttribute(name, value);
+}
+
+function removeAttribute(name) {
+    debug(&quot;Removing attribute '&quot; + name + &quot;'&quot;);
+    var allRoots = document.querySelectorAll(&quot;root&quot;);
+    allRoots[0].removeAttribute(name);
+    allRoots[1].removeAttribute(name);
+}
+
+function checkStyle(n) {
+    document.documentElement.offsetTop;
+
+    hasExpectedStyle = true;
+    expectedColor = 'rgb('+n+', 0, 0)';
+    var targets = document.querySelectorAll(&quot;target&quot;);
+    for (var i = 0; i &lt; targets.length; ++i) {
+        hasExpectedStyle = getComputedStyle(targets[i]).color == expectedColor;
+        if (!hasExpectedStyle)
+            break;
+    }
+    shouldBeTrue(&quot;hasExpectedStyle&quot;);
+}
+
+checkStyle(0);
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(0);
+
+setAttribute('myattr', '');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(1);
+
+setAttribute('myattr', 'foo');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(1);
+
+setAttribute('myattr', 'value2');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(2);
+
+setAttribute('myattr', 'foo');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(1);
+
+removeAttribute('myattr');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(0);
+
+setAttribute('myattr', 'value3');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(3);
+
+setAttribute('myattr', 'dummy value3');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(3);
+
+setAttribute('myattr', 'value4-foo');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(4);
+
+setAttribute('myattr', 'value4-foobar');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(4);
+
+setAttribute('myattr', 'dummy value4-foo');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(1);
+
+setAttribute('myattr', 'value5foo');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(5);
+
+setAttribute('myattr', 'value5foobar');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(5);
+
+setAttribute('myattr', 'foovalue5');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(1);
+
+setAttribute('myattr', 'foovalue6');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(6);
+
+setAttribute('myAttr', 'foobarvalue6');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(6);
+
+setAttribute('MYATTR', 'value6foo');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(1);
+
+setAttribute('myattr', 'value7');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(7);
+
+setAttribute('myattr', 'value7foo');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(7);
+
+setAttribute('myATTR', 'foovalue7foo');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(7);
+
+setAttribute('myattr', 'VALUE7foo');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(1);
+
+setAttribute('myattr', 'Value8');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(8);
+
+setAttribute('myattr', 'value8foo');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(8);
+
+setAttribute('myATTR', 'FOOVALue8foo');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(8);
+
+setAttribute('myattr', 'VALUE8foo');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(8);
+
+setAttribute('myattr', 'VALUE 8foo');
+testStyleInvalidation(&quot;InlineStyleChange&quot;);
+checkStyle(1);
+
+setAttribute('myattr2', '');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(1);
+
+setAttribute('myattr2', 'foo');
+testStyleInvalidation(&quot;NoStyleChange&quot;);
+checkStyle(1);
+
+&lt;/script&gt;
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/CMakeLists.txt (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/CMakeLists.txt        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/CMakeLists.txt        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -2630,6 +2630,7 @@
</span><span class="cx">     storage/StorageMap.cpp
</span><span class="cx">     storage/StorageNamespaceProvider.cpp
</span><span class="cx"> 
</span><ins>+    style/AttributeChangeInvalidation.cpp
</ins><span class="cx">     style/ClassChangeInvalidation.cpp
</span><span class="cx">     style/InlineTextBoxStyle.cpp
</span><span class="cx">     style/RenderTreePosition.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/ChangeLog        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -1,3 +1,90 @@
</span><ins>+2016-02-15  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        Optimize style invalidations for attribute selectors
+        https://bugs.webkit.org/show_bug.cgi?id=154242
+
+        Reviewed by Andreas Kling.
+
+        Currently we invalidate the whole element subtree if there are any attribute selectors for the changed attribute.
+        This is slow as generally few if any elements are really affected. Using attribute selectors for dynamic styling
+        should be performant.
+
+        This patch implements optimization strategy for attributes similar to what we already have for classes:
+
+        - Collect a map of all rules that contains descendant-affecting attribute selectors for a given attribute.
+        - When an attribute value changes check if there are any such rules for it.
+        - Check if the value change affects the results of any of the attribute selectors.
+        - Only if it does invalidate the exact descendant elements affected by the rules.
+
+        Test: fast/css/style-invalidation-attribute-change-descendants.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * css/DocumentRuleSets.cpp:
+        (WebCore::DocumentRuleSets::ancestorClassRules):
+        (WebCore::DocumentRuleSets::ancestorAttributeRulesForHTML):
+
+            Create optimization RuleSets when needed.
+
+        * css/DocumentRuleSets.h:
+        (WebCore::DocumentRuleSets::uncommonAttribute):
+        (WebCore::DocumentRuleSets::features):
+        * css/RuleFeature.cpp:
+        (WebCore::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):
+        (WebCore::makeAttributeSelectorKey):
+        (WebCore::RuleFeatureSet::collectFeatures):
+
+            Collect rules with descendant affecting attribute selectors.
+
+        (WebCore::RuleFeatureSet::add):
+        (WebCore::RuleFeatureSet::clear):
+        (WebCore::RuleFeatureSet::shrinkToFit):
+        * css/RuleFeature.h:
+        * css/SelectorChecker.cpp:
+        (WebCore::anyAttributeMatches):
+        (WebCore::SelectorChecker::attributeSelectorMatches):
+
+            Expose function for matching single attribute selectors.
+
+        (WebCore::canMatchHoverOrActiveInQuirksMode):
+        * css/SelectorChecker.h:
+        * dom/Attr.cpp:
+        (WebCore::Attr::setValue):
+        (WebCore::Attr::childrenChanged):
+        * dom/Element.cpp:
+        (WebCore::Element::setAttributeInternal):
+        (WebCore::makeIdForStyleResolution):
+        (WebCore::Element::attributeChanged):
+        (WebCore::Element::removeAttributeInternal):
+        (WebCore::Element::addAttributeInternal):
+        (WebCore::Element::removeAttribute):
+
+            Add AttributeChangeInvalidation where needed.
+
+        (WebCore::Element::needsStyleInvalidation):
+
+            Move to Element from ClassChangeInvalidation.
+
+        (WebCore::Element::willModifyAttribute):
+
+            No more full style invalidation on attribute change.
+
+        * style/AttributeChangeInvalidation.cpp: Added.
+        (WebCore::Style::AttributeChangeInvalidation::invalidateStyle):
+
+            Invalidate local style.
+            Check if we need to invalidate descendants by looking into ancestorAttributeRules.
+
+        (WebCore::Style::AttributeChangeInvalidation::invalidateDescendants):
+
+            Use StyleInvalidationAnalysis to invalidate the subtree for the relevant rules.
+
+        * style/AttributeChangeInvalidation.h: Added.
+        (WebCore::Style::AttributeChangeInvalidation::needsInvalidation):
+        (WebCore::Style::AttributeChangeInvalidation::AttributeChangeInvalidation):
+        (WebCore::Style::AttributeChangeInvalidation::~AttributeChangeInvalidation):
+
+            If needed, invalidate descendants before and after attribute change to catch rules that start and stop applying.
+
</ins><span class="cx"> 2016-02-16  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Do security checks early in JSDOMWindow::put*()
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorevcxprojWebCorevcxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -19295,6 +19295,7 @@
</span><span class="cx">     &lt;ClCompile Include=&quot;..\storage\StorageEventDispatcher.cpp&quot; /&gt;
</span><span class="cx">     &lt;ClCompile Include=&quot;..\storage\StorageMap.cpp&quot; /&gt;
</span><span class="cx">     &lt;ClCompile Include=&quot;..\storage\StorageNamespaceProvider.cpp&quot; /&gt;
</span><ins>+    &lt;ClCompile Include=&quot;..\style\AttributeChangeInvalidation.cpp&quot; /&gt;
</ins><span class="cx">     &lt;ClCompile Include=&quot;..\style\ClassChangeInvalidation.cpp&quot; /&gt;
</span><span class="cx">     &lt;ClCompile Include=&quot;..\style\InlineTextBoxStyle.cpp&quot; /&gt;
</span><span class="cx">     &lt;ClCompile Include=&quot;..\style\RenderTreePosition.cpp&quot; /&gt;
</span><span class="lines">@@ -22865,6 +22866,7 @@
</span><span class="cx">     &lt;ClInclude Include=&quot;..\storage\StorageMap.h&quot; /&gt;
</span><span class="cx">     &lt;ClInclude Include=&quot;..\storage\StorageNamespace.h&quot; /&gt;
</span><span class="cx">     &lt;ClInclude Include=&quot;..\storage\StorageNamespaceProvider.h&quot; /&gt;
</span><ins>+    &lt;ClInclude Include=&quot;..\style\AttributeChangeInvalidation.h&quot; /&gt;
</ins><span class="cx">     &lt;ClInclude Include=&quot;..\style\ClassChangeInvalidation.h&quot; /&gt;
</span><span class="cx">     &lt;ClInclude Include=&quot;..\style\InlineTextBoxStyle.h&quot; /&gt;
</span><span class="cx">     &lt;ClInclude Include=&quot;..\style\RenderTreePosition.h&quot; /&gt;
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -6637,6 +6637,8 @@
</span><span class="cx">                 E4A007851B820ED3002C5A6E /* DataURLDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A007841B820ED3002C5A6E /* DataURLDecoder.cpp */; };
</span><span class="cx">                 E4A814D41C6DEC4000BF85AC /* ClassChangeInvalidation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4A814D31C6DEC4000BF85AC /* ClassChangeInvalidation.h */; };
</span><span class="cx">                 E4A814D61C6DEE8D00BF85AC /* ClassChangeInvalidation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A814D51C6DEE8D00BF85AC /* ClassChangeInvalidation.cpp */; };
</span><ins>+                E4A814D81C70E10500BF85AC /* AttributeChangeInvalidation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A814D71C70E10500BF85AC /* AttributeChangeInvalidation.cpp */; };
+                E4A814DA1C70E10D00BF85AC /* AttributeChangeInvalidation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4A814D91C70E10D00BF85AC /* AttributeChangeInvalidation.h */; };
</ins><span class="cx">                 E4AE7C1617D1BB950009FB31 /* ElementIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E4AE7C1517D1BB950009FB31 /* ElementIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 E4AE7C1A17D232350009FB31 /* ElementAncestorIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E4AE7C1917D232350009FB31 /* ElementAncestorIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 E4AFCFA50DAF29A300F5F55C /* UnitBezier.h in Headers */ = {isa = PBXBuildFile; fileRef = E4AFCFA40DAF29A300F5F55C /* UnitBezier.h */; };
</span><span class="lines">@@ -14643,6 +14645,8 @@
</span><span class="cx">                 E4A007841B820ED3002C5A6E /* DataURLDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataURLDecoder.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 E4A814D31C6DEC4000BF85AC /* ClassChangeInvalidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClassChangeInvalidation.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 E4A814D51C6DEE8D00BF85AC /* ClassChangeInvalidation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClassChangeInvalidation.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                E4A814D71C70E10500BF85AC /* AttributeChangeInvalidation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AttributeChangeInvalidation.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                E4A814D91C70E10D00BF85AC /* AttributeChangeInvalidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttributeChangeInvalidation.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 E4AE7C1517D1BB950009FB31 /* ElementIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementIterator.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 E4AE7C1917D232350009FB31 /* ElementAncestorIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementAncestorIterator.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 E4AFCFA40DAF29A300F5F55C /* UnitBezier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnitBezier.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -23433,6 +23437,8 @@
</span><span class="cx">                 E4763D4A17B2704900D35206 /* style */ = {
</span><span class="cx">                         isa = PBXGroup;
</span><span class="cx">                         children = (
</span><ins>+                                E4A814D71C70E10500BF85AC /* AttributeChangeInvalidation.cpp */,
+                                E4A814D91C70E10D00BF85AC /* AttributeChangeInvalidation.h */,
</ins><span class="cx">                                 E4A814D51C6DEE8D00BF85AC /* ClassChangeInvalidation.cpp */,
</span><span class="cx">                                 E4A814D31C6DEC4000BF85AC /* ClassChangeInvalidation.h */,
</span><span class="cx">                                 1C0106FE192594DF008A4201 /* InlineTextBoxStyle.cpp */,
</span><span class="lines">@@ -25909,6 +25915,7 @@
</span><span class="cx">                                 31313F661443B35F006E2A90 /* FilterEffectRenderer.h in Headers */,
</span><span class="cx">                                 49ECEB6E1499790D00CDD3A4 /* FilterOperation.h in Headers */,
</span><span class="cx">                                 49ECEB701499790D00CDD3A4 /* FilterOperations.h in Headers */,
</span><ins>+                                E4A814DA1C70E10D00BF85AC /* AttributeChangeInvalidation.h in Headers */,
</ins><span class="cx">                                 372C00D9129619F8005C9575 /* FindOptions.h in Headers */,
</span><span class="cx">                                 A8CFF04F0A154F09000A4234 /* FixedTableLayout.h in Headers */,
</span><span class="cx">                                 BC073BAA0C399B1F000F5979 /* FloatConversion.h in Headers */,
</span><span class="lines">@@ -30440,6 +30447,7 @@
</span><span class="cx">                                 B59DD6AA11902A71007E9684 /* JSSQLStatementErrorCallback.cpp in Sources */,
</span><span class="cx">                                 9BD4E9161C462872005065BC /* JSCustomElementInterface.cpp in Sources */,
</span><span class="cx">                                 514C76380CE9225E007EF3CD /* JSSQLTransaction.cpp in Sources */,
</span><ins>+                                E4A814D81C70E10500BF85AC /* AttributeChangeInvalidation.cpp in Sources */,
</ins><span class="cx">                                 B59DD69E11902A42007E9684 /* JSSQLTransactionCallback.cpp in Sources */,
</span><span class="cx">                                 1AD2316E0CD269E700C1F194 /* JSSQLTransactionCustom.cpp in Sources */,
</span><span class="cx">                                 B59DD6A211902A52007E9684 /* JSSQLTransactionErrorCallback.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceWebCorecssDocumentRuleSetscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/DocumentRuleSets.cpp (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/DocumentRuleSets.cpp        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/DocumentRuleSets.cpp        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -118,7 +118,7 @@
</span><span class="cx"> 
</span><span class="cx"> RuleSet* DocumentRuleSets::ancestorClassRules(AtomicStringImpl* className) const
</span><span class="cx"> {
</span><del>-    auto addResult = m_ancestorClassRuleSet.add(className, nullptr);
</del><ins>+    auto addResult = m_ancestorClassRuleSets.add(className, nullptr);
</ins><span class="cx">     if (addResult.isNewEntry) {
</span><span class="cx">         if (auto* rules = m_features.ancestorClassRules.get(className))
</span><span class="cx">             addResult.iterator-&gt;value = makeRuleSet(*rules);
</span><span class="lines">@@ -126,4 +126,20 @@
</span><span class="cx">     return addResult.iterator-&gt;value.get();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+const DocumentRuleSets::AttributeRules* DocumentRuleSets::ancestorAttributeRulesForHTML(AtomicStringImpl* attributeName) const
+{
+    auto addResult = m_ancestorAttributeRuleSetsForHTML.add(attributeName, nullptr);
+    auto&amp; value = addResult.iterator-&gt;value;
+    if (addResult.isNewEntry) {
+        if (auto* rules = m_features.ancestorAttributeRulesForHTML.get(attributeName)) {
+            value = std::make_unique&lt;AttributeRules&gt;();
+            value-&gt;attributeSelectors.reserveCapacity(rules-&gt;selectors.size());
+            for (auto* selector : rules-&gt;selectors.values())
+                value-&gt;attributeSelectors.uncheckedAppend(selector);
+            value-&gt;ruleSet = makeRuleSet(rules-&gt;features);
+        }
+    }
+    return 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 (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/DocumentRuleSets.h        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/DocumentRuleSets.h        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -52,6 +52,12 @@
</span><span class="cx">     RuleSet* uncommonAttribute() const { return m_uncommonAttributeRuleSet.get(); }
</span><span class="cx">     RuleSet* ancestorClassRules(AtomicStringImpl* className) const;
</span><span class="cx"> 
</span><ins>+    struct AttributeRules {
+        Vector&lt;const CSSSelector*&gt; attributeSelectors;
+        std::unique_ptr&lt;RuleSet&gt; ruleSet;
+    };
+    const AttributeRules* ancestorAttributeRulesForHTML(AtomicStringImpl*) const;
+
</ins><span class="cx">     void initUserStyle(ExtensionStyleSheets&amp;, const MediaQueryEvaluator&amp;, StyleResolver&amp;);
</span><span class="cx">     void resetAuthorStyle();
</span><span class="cx">     void appendAuthorStyleSheets(const Vector&lt;RefPtr&lt;CSSStyleSheet&gt;&gt;&amp;, MediaQueryEvaluator*, InspectorCSSOMWrappers&amp;, StyleResolver*);
</span><span class="lines">@@ -67,7 +73,8 @@
</span><span class="cx">     mutable unsigned m_defaultStyleVersionOnFeatureCollection { 0 };
</span><span class="cx">     mutable std::unique_ptr&lt;RuleSet&gt; m_siblingRuleSet;
</span><span class="cx">     mutable std::unique_ptr&lt;RuleSet&gt; m_uncommonAttributeRuleSet;
</span><del>-    mutable HashMap&lt;AtomicStringImpl*, std::unique_ptr&lt;RuleSet&gt;&gt; m_ancestorClassRuleSet;
</del><ins>+    mutable HashMap&lt;AtomicStringImpl*, std::unique_ptr&lt;RuleSet&gt;&gt; m_ancestorClassRuleSets;
+    mutable HashMap&lt;AtomicStringImpl*, std::unique_ptr&lt;AttributeRules&gt;&gt; m_ancestorAttributeRuleSetsForHTML;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> inline const RuleFeatureSet&amp; DocumentRuleSets::features() const
</span></span></pre></div>
<a id="trunkSourceWebCorecssRuleFeaturecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/RuleFeature.cpp (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/RuleFeature.cpp        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/RuleFeature.cpp        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -46,8 +46,12 @@
</span><span class="cx">             if (matchesAncestor)
</span><span class="cx">                 selectorFeatures.classesMatchingAncestors.append(selector-&gt;value().impl());
</span><span class="cx">         } else if (selector-&gt;isAttributeSelector()) {
</span><del>-            attributeCanonicalLocalNamesInRules.add(selector-&gt;attributeCanonicalLocalName().impl());
-            attributeLocalNamesInRules.add(selector-&gt;attribute().localName().impl());
</del><ins>+            auto* canonicalLocalName = selector-&gt;attributeCanonicalLocalName().impl();
+            auto* localName = selector-&gt;attribute().localName().impl();
+            attributeCanonicalLocalNamesInRules.add(canonicalLocalName);
+            attributeLocalNamesInRules.add(localName);
+            if (matchesAncestor)
+                selectorFeatures.attributeSelectorsMatchingAncestors.append(selector);
</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><span class="lines">@@ -78,6 +82,13 @@
</span><span class="cx">     } while (selector);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static std::pair&lt;AtomicStringImpl*, unsigned&gt; makeAttributeSelectorKey(const CSSSelector&amp; selector)
+{
+    bool caseInsensitive = selector.attributeValueMatchingIsCaseInsensitive();
+    unsigned matchAndCase = static_cast&lt;unsigned&gt;(selector.match()) &lt;&lt; 1 | caseInsensitive;
+    return std::make_pair(selector.attributeCanonicalLocalName().impl(), matchAndCase);
+}
+
</ins><span class="cx"> void RuleFeatureSet::collectFeatures(const RuleData&amp; ruleData)
</span><span class="cx"> {
</span><span class="cx">     SelectorFeatures selectorFeatures;
</span><span class="lines">@@ -92,6 +103,16 @@
</span><span class="cx">             addResult.iterator-&gt;value = std::make_unique&lt;Vector&lt;RuleFeature&gt;&gt;();
</span><span class="cx">         addResult.iterator-&gt;value-&gt;append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
</span><span class="cx">     }
</span><ins>+    for (auto* selector : selectorFeatures.attributeSelectorsMatchingAncestors) {
+        // Hashing by attributeCanonicalLocalName makes this HTML specific.
+        auto addResult = ancestorAttributeRulesForHTML.add(selector-&gt;attributeCanonicalLocalName().impl(), nullptr);
+        if (addResult.isNewEntry)
+            addResult.iterator-&gt;value = std::make_unique&lt;AttributeRules&gt;();
+        auto&amp; rules = *addResult.iterator-&gt;value;
+        rules.features.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
+        // Deduplicate selectors.
+        rules.selectors.add(makeAttributeSelectorKey(*selector), selector);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void RuleFeatureSet::add(const RuleFeatureSet&amp; other)
</span><span class="lines">@@ -109,6 +130,15 @@
</span><span class="cx">         else
</span><span class="cx">             addResult.iterator-&gt;value-&gt;appendVector(*keyValuePair.value);
</span><span class="cx">     }
</span><ins>+    for (auto&amp; keyValuePair : other.ancestorAttributeRulesForHTML) {
+        auto addResult = ancestorAttributeRulesForHTML.add(keyValuePair.key, nullptr);
+        if (addResult.isNewEntry)
+            addResult.iterator-&gt;value = std::make_unique&lt;AttributeRules&gt;();
+        auto&amp; rules = *addResult.iterator-&gt;value;
+        rules.features.appendVector(keyValuePair.value-&gt;features);
+        for (auto&amp; selectorPair : keyValuePair.value-&gt;selectors)
+            rules.selectors.add(selectorPair.key, selectorPair.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">@@ -122,6 +152,7 @@
</span><span class="cx">     siblingRules.clear();
</span><span class="cx">     uncommonAttributeRules.clear();
</span><span class="cx">     ancestorClassRules.clear();
</span><ins>+    ancestorAttributeRulesForHTML.clear();
</ins><span class="cx">     usesFirstLineRules = false;
</span><span class="cx">     usesFirstLetterRules = false;
</span><span class="cx"> }
</span><span class="lines">@@ -132,6 +163,8 @@
</span><span class="cx">     uncommonAttributeRules.shrinkToFit();
</span><span class="cx">     for (auto&amp; rules : ancestorClassRules.values())
</span><span class="cx">         rules-&gt;shrinkToFit();
</span><ins>+    for (auto&amp; rules : ancestorAttributeRulesForHTML.values())
+        rules-&gt;features.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 (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/RuleFeature.h        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/RuleFeature.h        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -22,6 +22,7 @@
</span><span class="cx"> #ifndef RuleFeature_h
</span><span class="cx"> #define RuleFeature_h
</span><span class="cx"> 
</span><ins>+#include &quot;CSSSelector.h&quot;
</ins><span class="cx"> #include &lt;wtf/Forward.h&gt;
</span><span class="cx"> #include &lt;wtf/HashMap.h&gt;
</span><span class="cx"> #include &lt;wtf/HashSet.h&gt;
</span><span class="lines">@@ -29,7 +30,6 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-class CSSSelector;
</del><span class="cx"> class RuleData;
</span><span class="cx"> class StyleRule;
</span><span class="cx"> 
</span><span class="lines">@@ -58,6 +58,12 @@
</span><span class="cx">     Vector&lt;RuleFeature&gt; siblingRules;
</span><span class="cx">     Vector&lt;RuleFeature&gt; uncommonAttributeRules;
</span><span class="cx">     HashMap&lt;AtomicStringImpl*, std::unique_ptr&lt;Vector&lt;RuleFeature&gt;&gt;&gt; ancestorClassRules;
</span><ins>+
+    struct AttributeRules {
+        HashMap&lt;std::pair&lt;AtomicStringImpl*, unsigned&gt;, const CSSSelector*&gt; selectors;
+        Vector&lt;RuleFeature&gt; features;
+    };
+    HashMap&lt;AtomicStringImpl*, std::unique_ptr&lt;AttributeRules&gt;&gt; ancestorAttributeRulesForHTML;
</ins><span class="cx">     bool usesFirstLineRules { false };
</span><span class="cx">     bool usesFirstLetterRules { false };
</span><span class="cx"> 
</span><span class="lines">@@ -65,6 +71,7 @@
</span><span class="cx">     struct SelectorFeatures {
</span><span class="cx">         bool hasSiblingSelector { false };
</span><span class="cx">         Vector&lt;AtomicStringImpl*&gt; classesMatchingAncestors;
</span><ins>+        Vector&lt;const CSSSelector*&gt; attributeSelectorsMatchingAncestors;
</ins><span class="cx">     };
</span><span class="cx">     void recursivelyCollectFeaturesFromSelector(SelectorFeatures&amp;, const CSSSelector&amp;, bool matchesAncestor = false);
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCorecssSelectorCheckercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/SelectorChecker.cpp        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -518,6 +518,21 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool SelectorChecker::attributeSelectorMatches(const Element&amp; element, const QualifiedName&amp; attributeName, const AtomicString&amp; attributeValue, const CSSSelector&amp; selector)
+{
+    ASSERT(selector.isAttributeSelector());
+    auto&amp; selectorAttribute = selector.attribute();
+    auto&amp; selectorName = element.isHTMLElement() ? selector.attributeCanonicalLocalName() : selectorAttribute.localName();
+    if (!Attribute::nameMatchesFilter(attributeName, selectorAttribute.prefix(), selectorName, selectorAttribute.namespaceURI()))
+        return false;
+    bool caseSensitive = true;
+    if (selector.attributeValueMatchingIsCaseInsensitive())
+        caseSensitive = false;
+    else if (element.document().isHTMLDocument() &amp;&amp; element.isHTMLElement() &amp;&amp; !HTMLDocument::isCaseSensitiveAttribute(selector.attribute()))
+        caseSensitive = false;
+    return attributeValueMatches(Attribute(attributeName, attributeValue), selector.match(), selector.value(), caseSensitive);
+}
+
</ins><span class="cx"> static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::LocalContext&amp; context)
</span><span class="cx"> {
</span><span class="cx">     // For quirks mode, follow this: http://quirks.spec.whatwg.org/#the-:active-and-:hover-quirk
</span></span></pre></div>
<a id="trunkSourceWebCorecssSelectorCheckerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/SelectorChecker.h (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/SelectorChecker.h        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/SelectorChecker.h        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -119,6 +119,7 @@
</span><span class="cx"> 
</span><span class="cx">     static bool isCommonPseudoClassSelector(const CSSSelector*);
</span><span class="cx">     static bool matchesFocusPseudoClass(const Element&amp;);
</span><ins>+    static bool attributeSelectorMatches(const Element&amp;, const QualifiedName&amp;, const AtomicString&amp; attributeValue, const CSSSelector&amp;);
</ins><span class="cx"> 
</span><span class="cx">     enum LinkMatchMask { MatchDefault = 0, MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };
</span><span class="cx">     static unsigned determineLinkMatchType(const CSSSelector*);
</span></span></pre></div>
<a id="trunkSourceWebCoredomAttrcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Attr.cpp (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Attr.cpp        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/dom/Attr.cpp        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -23,6 +23,7 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;Attr.h&quot;
</span><span class="cx"> 
</span><ins>+#include &quot;AttributeChangeInvalidation.h&quot;
</ins><span class="cx"> #include &quot;Event.h&quot;
</span><span class="cx"> #include &quot;ExceptionCode.h&quot;
</span><span class="cx"> #include &quot;ScopedEventQueue.h&quot;
</span><span class="lines">@@ -108,9 +109,10 @@
</span><span class="cx">     EventQueueScope scope;
</span><span class="cx">     m_ignoreChildrenChanged++;
</span><span class="cx">     removeChildren();
</span><del>-    if (m_element)
</del><ins>+    if (m_element) {
+        Style::AttributeChangeInvalidation styleInvalidation(*m_element, qualifiedName(), elementAttribute().value(), value);
</ins><span class="cx">         elementAttribute().setValue(value);
</span><del>-    else
</del><ins>+    } else
</ins><span class="cx">         m_standaloneValue = value;
</span><span class="cx">     createTextChild();
</span><span class="cx">     m_ignoreChildrenChanged--;
</span><span class="lines">@@ -163,9 +165,10 @@
</span><span class="cx">     if (m_element)
</span><span class="cx">         m_element-&gt;willModifyAttribute(qualifiedName(), oldValue, newValue);
</span><span class="cx"> 
</span><del>-    if (m_element)
</del><ins>+    if (m_element) {
+        Style::AttributeChangeInvalidation styleInvalidation(*m_element, qualifiedName(), oldValue, newValue);
</ins><span class="cx">         elementAttribute().setValue(newValue);
</span><del>-    else
</del><ins>+    } else
</ins><span class="cx">         m_standaloneValue = newValue;
</span><span class="cx"> 
</span><span class="cx">     if (m_element)
</span></span></pre></div>
<a id="trunkSourceWebCoredomElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Element.cpp (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Element.cpp        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/dom/Element.cpp        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;AXObjectCache.h&quot;
</span><span class="cx"> #include &quot;Attr.h&quot;
</span><ins>+#include &quot;AttributeChangeInvalidation.h&quot;
</ins><span class="cx"> #include &quot;CSSParser.h&quot;
</span><span class="cx"> #include &quot;Chrome.h&quot;
</span><span class="cx"> #include &quot;ChromeClient.h&quot;
</span><span class="lines">@@ -1182,26 +1183,30 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if (inSynchronizationOfLazyAttribute) {
+        ensureUniqueElementData().attributeAt(index).setValue(newValue);
+        return;
+    }
+
</ins><span class="cx">     const Attribute&amp; attribute = attributeAt(index);
</span><ins>+    QualifiedName attributeName = attribute.name();
</ins><span class="cx">     AtomicString oldValue = attribute.value();
</span><del>-    bool valueChanged = newValue != oldValue;
-    QualifiedName attributeName = (!inSynchronizationOfLazyAttribute || valueChanged) ? attribute.name() : name;
</del><span class="cx"> 
</span><del>-    if (!inSynchronizationOfLazyAttribute)
-        willModifyAttribute(attributeName, oldValue, newValue);
</del><ins>+    willModifyAttribute(attributeName, oldValue, newValue);
</ins><span class="cx"> 
</span><del>-    if (valueChanged) {
</del><ins>+    if (newValue != oldValue) {
</ins><span class="cx">         // If there is an Attr node hooked to this attribute, the Attr::setValue() call below
</span><span class="cx">         // will write into the ElementData.
</span><span class="cx">         // FIXME: Refactor this so it makes some sense.
</span><del>-        if (RefPtr&lt;Attr&gt; attrNode = inSynchronizationOfLazyAttribute ? nullptr : attrIfExists(attributeName))
</del><ins>+        if (RefPtr&lt;Attr&gt; attrNode = attrIfExists(attributeName))
</ins><span class="cx">             attrNode-&gt;setValue(newValue);
</span><del>-        else
</del><ins>+        else {
+            Style::AttributeChangeInvalidation styleInvalidation(*this, name, oldValue, newValue);
</ins><span class="cx">             ensureUniqueElementData().attributeAt(index).setValue(newValue);
</span><ins>+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (!inSynchronizationOfLazyAttribute)
-        didModifyAttribute(attributeName, oldValue, newValue);
</del><ins>+    didModifyAttribute(attributeName, oldValue, newValue);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static inline AtomicString makeIdForStyleResolution(const AtomicString&amp; value, bool inQuirksMode)
</span><span class="lines">@@ -1268,9 +1273,6 @@
</span><span class="cx"> 
</span><span class="cx">     invalidateNodeListAndCollectionCachesInAncestors(&amp;name, this);
</span><span class="cx"> 
</span><del>-    // If there is currently no StyleResolver, we can't be sure that this attribute change won't affect style.
-    shouldInvalidateStyle |= !styleResolver;
-
</del><span class="cx">     if (shouldInvalidateStyle)
</span><span class="cx">         setNeedsStyleRecalc();
</span><span class="cx"> 
</span><span class="lines">@@ -2050,27 +2052,38 @@
</span><span class="cx">     QualifiedName name = elementData.attributeAt(index).name();
</span><span class="cx">     AtomicString valueBeingRemoved = elementData.attributeAt(index).value();
</span><span class="cx"> 
</span><del>-    if (!inSynchronizationOfLazyAttribute) {
-        if (!valueBeingRemoved.isNull())
-            willModifyAttribute(name, valueBeingRemoved, nullAtom);
-    }
-
</del><span class="cx">     if (RefPtr&lt;Attr&gt; attrNode = attrIfExists(name))
</span><span class="cx">         detachAttrNodeFromElementWithValue(attrNode.get(), elementData.attributeAt(index).value());
</span><span class="cx"> 
</span><del>-    elementData.removeAttribute(index);
</del><ins>+    if (inSynchronizationOfLazyAttribute) {
+        elementData.removeAttribute(index);
+        return;
+    }
</ins><span class="cx"> 
</span><del>-    if (!inSynchronizationOfLazyAttribute)
-        didRemoveAttribute(name, valueBeingRemoved);
</del><ins>+    if (!valueBeingRemoved.isNull())
+        willModifyAttribute(name, valueBeingRemoved, nullAtom);
+
+    {
+        Style::AttributeChangeInvalidation styleInvalidation(*this, name, valueBeingRemoved, nullAtom);
+        elementData.removeAttribute(index);
+    }
+
+    didRemoveAttribute(name, valueBeingRemoved);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void Element::addAttributeInternal(const QualifiedName&amp; name, const AtomicString&amp; value, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
</span><span class="cx"> {
</span><del>-    if (!inSynchronizationOfLazyAttribute)
-        willModifyAttribute(name, nullAtom, value);
-    ensureUniqueElementData().addAttribute(name, value);
-    if (!inSynchronizationOfLazyAttribute)
-        didAddAttribute(name, value);
</del><ins>+    if (inSynchronizationOfLazyAttribute) {
+        ensureUniqueElementData().addAttribute(name, value);
+        return;
+    }
+
+    willModifyAttribute(name, nullAtom, value);
+    {
+        Style::AttributeChangeInvalidation styleInvalidation(*this, name, nullAtom, value);
+        ensureUniqueElementData().addAttribute(name, value);
+    }
+    didAddAttribute(name, value);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool Element::removeAttribute(const AtomicString&amp; name)
</span><span class="lines">@@ -2485,6 +2498,18 @@
</span><span class="cx">     return style;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool Element::needsStyleInvalidation() const
+{
+    if (!inRenderedDocument())
+        return false;
+    if (styleChangeType() &gt;= FullStyleChange)
+        return false;
+    if (!document().styleResolverIfExists())
+        return false;
+
+    return true;
+}
+
</ins><span class="cx"> void Element::setStyleAffectedByEmpty()
</span><span class="cx"> {
</span><span class="cx">     ensureElementRareData().setStyleAffectedByEmpty(true);
</span><span class="lines">@@ -3080,12 +3105,6 @@
</span><span class="cx">             updateLabel(treeScope(), oldValue, newValue);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (oldValue != newValue) {
-        auto styleResolver = document().styleResolverIfExists();
-        if (styleResolver &amp;&amp; styleResolver-&gt;hasSelectorForAttribute(*this, name.localName()))
-            setNeedsStyleRecalc();
-    }
-
</del><span class="cx">     if (std::unique_ptr&lt;MutationObserverInterestGroup&gt; recipients = MutationObserverInterestGroup::createForAttributesMutation(*this, name))
</span><span class="cx">         recipients-&gt;enqueueMutationRecord(MutationRecord::createAttributes(*this, name, oldValue));
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Element.h (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Element.h        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/dom/Element.h        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -279,6 +279,8 @@
</span><span class="cx"> 
</span><span class="cx">     virtual RenderStyle* computedStyle(PseudoId = NOPSEUDO) override;
</span><span class="cx"> 
</span><ins>+    bool needsStyleInvalidation() const;
+
</ins><span class="cx">     // Methods for indicating the style is affected by dynamic updates (e.g., children changing, our position changing in our sibling list, etc.)
</span><span class="cx">     bool styleAffectedByEmpty() const { return hasRareData() &amp;&amp; rareDataStyleAffectedByEmpty(); }
</span><span class="cx">     bool childrenAffectedByHover() const { return getFlag(ChildrenAffectedByHoverRulesFlag); }
</span></span></pre></div>
<a id="trunkSourceWebCorestyleAttributeChangeInvalidationcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/style/AttributeChangeInvalidation.cpp (0 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/style/AttributeChangeInvalidation.cpp                                (rev 0)
+++ trunk/Source/WebCore/style/AttributeChangeInvalidation.cpp        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -0,0 +1,90 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include &quot;config.h&quot;
+#include &quot;AttributeChangeInvalidation.h&quot;
+
+#include &quot;DocumentRuleSets.h&quot;
+#include &quot;ElementIterator.h&quot;
+#include &quot;StyleInvalidationAnalysis.h&quot;
+#include &quot;StyleResolver.h&quot;
+
+namespace WebCore {
+namespace Style {
+
+void AttributeChangeInvalidation::invalidateStyle(const QualifiedName&amp; attributeName, const AtomicString&amp; oldValue, const AtomicString&amp; newValue)
+{
+    if (newValue == oldValue)
+        return;
+
+    auto&amp; ruleSets = m_element.styleResolver().ruleSets();
+    bool isHTML = m_element.isHTMLElement();
+
+    auto&amp; nameSet = isHTML ? ruleSets.features().attributeCanonicalLocalNamesInRules : ruleSets.features().attributeLocalNamesInRules;
+    bool shouldInvalidate = nameSet.contains(attributeName.localName().impl());
+    if (!shouldInvalidate)
+        return;
+
+    if (!isHTML) {
+        m_element.setNeedsStyleRecalc(FullStyleChange);
+        return;
+    }
+
+    if (m_element.shadowRoot() &amp;&amp; ruleSets.authorStyle()-&gt;hasShadowPseudoElementRules()) {
+        m_element.setNeedsStyleRecalc(FullStyleChange);
+        return;
+    }
+
+    m_element.setNeedsStyleRecalc(InlineStyleChange);
+
+    if (!childrenOfType&lt;Element&gt;(m_element).first())
+        return;
+
+    auto* attributeRules = ruleSets.ancestorAttributeRulesForHTML(attributeName.localName().impl());
+    if (!attributeRules)
+        return;
+
+    // Check if descendants may be affected by this attribute change.
+    for (auto* selector : attributeRules-&gt;attributeSelectors) {
+        bool oldMatches = oldValue.isNull() ? false : SelectorChecker::attributeSelectorMatches(m_element, attributeName, oldValue, *selector);
+        bool newMatches = newValue.isNull() ? false : SelectorChecker::attributeSelectorMatches(m_element, attributeName, newValue, *selector);
+
+        if (oldMatches != newMatches) {
+            m_descendantInvalidationRuleSet = attributeRules-&gt;ruleSet.get();
+            return;
+        }
+    }
+}
+
+void AttributeChangeInvalidation::invalidateDescendants()
+{
+    if (!m_descendantInvalidationRuleSet)
+        return;
+    StyleInvalidationAnalysis invalidationAnalysis(*m_descendantInvalidationRuleSet);
+    invalidationAnalysis.invalidateStyle(m_element);
+}
+
+}
+}
</ins></span></pre></div>
<a id="trunkSourceWebCorestyleAttributeChangeInvalidationh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/style/AttributeChangeInvalidation.h (0 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/style/AttributeChangeInvalidation.h                                (rev 0)
+++ trunk/Source/WebCore/style/AttributeChangeInvalidation.h        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -0,0 +1,73 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AttributeChangeInvalidation_h
+#define AttributeChangeInvalidation_h
+
+#include &quot;Element.h&quot;
+
+namespace WebCore {
+
+class RuleSet;
+
+namespace Style {
+
+class AttributeChangeInvalidation {
+public:
+    AttributeChangeInvalidation(Element&amp;, const QualifiedName&amp;, const AtomicString&amp; oldValue, const AtomicString&amp; newValue);
+    ~AttributeChangeInvalidation();
+
+private:
+    void invalidateStyle(const QualifiedName&amp;, const AtomicString&amp; oldValue, const AtomicString&amp; newValue);
+    void invalidateDescendants();
+
+    const bool m_isEnabled;
+    Element&amp; m_element;
+
+    RuleSet* m_descendantInvalidationRuleSet { nullptr };
+};
+
+inline AttributeChangeInvalidation::AttributeChangeInvalidation(Element&amp; element, const QualifiedName&amp; attributeName, const AtomicString&amp; oldValue, const AtomicString&amp; newValue)
+    : m_isEnabled(element.needsStyleInvalidation())
+    , m_element(element)
+{
+    if (!m_isEnabled)
+        return;
+    invalidateStyle(attributeName, oldValue, newValue);
+    invalidateDescendants();
+}
+
+inline AttributeChangeInvalidation::~AttributeChangeInvalidation()
+{
+    if (!m_isEnabled)
+        return;
+    invalidateDescendants();
+}
+    
+}
+}
+
+#endif
+
</ins></span></pre></div>
<a id="trunkSourceWebCorestyleClassChangeInvalidationh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/style/ClassChangeInvalidation.h (196628 => 196629)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/style/ClassChangeInvalidation.h        2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/style/ClassChangeInvalidation.h        2016-02-16 08:20:58 UTC (rev 196629)
</span><span class="lines">@@ -46,7 +46,6 @@
</span><span class="cx"> private:
</span><span class="cx">     using ClassChangeVector = Vector&lt;AtomicStringImpl*, 4&gt;;
</span><span class="cx"> 
</span><del>-    static bool needsInvalidation(const Element&amp;);
</del><span class="cx">     void computeClassChange(const SpaceSplitString&amp; oldClasses, const SpaceSplitString&amp; newClasses);
</span><span class="cx">     void invalidateStyle(const ClassChangeVector&amp;);
</span><span class="cx"> 
</span><span class="lines">@@ -59,19 +58,8 @@
</span><span class="cx">     ClassChangeVector m_removedClasses;
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-inline bool ClassChangeInvalidation::needsInvalidation(const Element&amp; element)
-{
-    if (!element.inRenderedDocument())
-        return false;
-    if (element.styleChangeType() &gt;= FullStyleChange)
-        return false;
-    if (!element.document().styleResolverIfExists())
-        return false;
-    return true;
-}
-
</del><span class="cx"> inline ClassChangeInvalidation::ClassChangeInvalidation(Element&amp; element, const SpaceSplitString&amp; oldClasses, const SpaceSplitString&amp; newClasses)
</span><del>-    : m_isEnabled(needsInvalidation(element))
</del><ins>+    : m_isEnabled(element.needsStyleInvalidation())
</ins><span class="cx">     , m_element(element)
</span><span class="cx"> 
</span><span class="cx"> {
</span></span></pre>
</div>
</div>

</body>
</html>