<!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>[164852] 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/164852">164852</a></dd>
<dt>Author</dt> <dd>benjamin@webkit.org</dd>
<dt>Date</dt> <dd>2014-02-27 21:52:33 -0800 (Thu, 27 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Compile attribute value matching
https://bugs.webkit.org/show_bug.cgi?id=129228

Reviewed by Geoffrey Garen.

Source/WebCore: 

Add support for compiling value matching when matching attributes in Selector.
This patch only adds exact matching, the other cases will follow.

There is a little infrastructure changes since FunctionCall now needs to support
calls taking 2 arguments. The fun begins when the arguments are not in the right
registers and need to be moved to the right place. Otherwise the code is straightforward.

In SelectorCompiler, it is necessary to handle two different cases of matching: case sensitive
and case insensitive. The choice is done in part at compilation time by asking HTMLDocument
if the name filter can include case insensitive attribute. The other part is done at runtime
by querying the element for its type and document.

Test: fast/selectors/case-insensitive-value-matching.html

* css/SelectorChecker.cpp:
(WebCore::attributeValueMatches): Null values matching should never happen, when an attribute
has no value, its value is empty.

* cssjit/FunctionCall.h:
(WebCore::FunctionCall::FunctionCall):
Add support for calls with two arguments.
(WebCore::FunctionCall::setOneArgument):
(WebCore::FunctionCall::setTwoArguments):
(WebCore::FunctionCall::swapArguments):
Here we need to swap two registers, but we cannot allocate a new register (because the context
of the function call may have taken all the available registers already).

On x86, the solution is simple, we can swap the two registers without side effects.

On other platforms, it is a little more complex. If there is any available register, we can just
use it as a temporary to make the swap.
If there are no available registers, we know that all the registers are taken. Since swapArguments()
was called after pushing all the arguments on the stack, we can safely trash the value of any of those.
We take the first available register that is not a function argument and use it as a temporary.

(WebCore::FunctionCall::prepareAndCall):
This is the fun part, we have two registers where the values must go before the function call. The values
can be in any combination of the allocated registers. The code here needs to move the two values to
their target register while avoiding conflicts.

* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::AttributeMatchingInfo::AttributeMatchingInfo):
(WebCore::SelectorCompiler::AttributeMatchingInfo::canDefaultToCaseSensitiveValueMatch):
(WebCore::SelectorCompiler::AttributeMatchingInfo::selector):
The value of HTMLDocument::isCaseSensitiveAttribute is needed at compilation time to compute the number
of required registers. As a result, we need to keep it along in the selector fragment.

(WebCore::SelectorCompiler::SelectorCodeGenerator::SelectorCodeGenerator):
(WebCore::SelectorCompiler::attributeNameTestingRequiresNamespaceRegister):
(WebCore::SelectorCompiler::attributeValueTestingRequiresExtraRegister):
(WebCore::SelectorCompiler::minimumRegisterRequirements):
(WebCore::SelectorCompiler::SelectorCodeGenerator::markParentElementIfResolvingStyle):
(WebCore::SelectorCompiler::canMatchStyleAttribute):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateSynchronizeStyleAttribute):
(WebCore::SelectorCompiler::canMatchAnimatableSVGAttribute):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateSynchronizeAllAnimatedSVGAttribute):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementAttributeMatching):
The matching code is moved in a local scope. This is done to recover the register of qualifiedNameImpl
before doing any value matching. That register can then be used to store the expected value when matching
an attribute value.
It is unfortunate there is so much register pressure in this part.

Value matching is done outside the loop. The idea is to keep the loop really small since in the vast majority
of cases, name matching fails.
If the value matching fails, we jump back into the tight loop.

This is not ideal in all situation. For example trivial name matching with trivial value matching should
be done in loop. There is a FIXME to improve those cases later.

(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementAttributeValueMatching):
(WebCore::SelectorCompiler::testIsHTMLClassOnDocument):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementAttributeValueExactMatching):
In the case sensitive branch, things are really simple. We have to AtomicStringImpl pointers, if they
don't match, it is a failure.

The case sensitive branch start by comparing the pointers in case the values are equal. This is a common
case and it simplifies the cases for SVG, XHTML, etc.
If the two values are not equal, we must first find if the context requires case insensitive comparison
(HTMLElement in HTMLDocument). If the conditions require case insensitive matching, we then fall back
to a function call.

(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementFunctionCallTest):
* dom/Attribute.h:
(WebCore::Attribute::valueMemoryOffset):
* dom/Document.h:
(WebCore::Document::documentClassesMemoryOffset):
(WebCore::Document::isHTMLDocumentClassFlag):
* dom/Node.h:
(WebCore::Node::treeScopeMemoryOffset):
* dom/TreeScope.h:
(WebCore::TreeScope::documentScopeMemoryOffset):

LayoutTests: 

* fast/selectors/case-insensitive-value-matching-expected.txt: Added.
* fast/selectors/case-insensitive-value-matching.html: Added.
Test the various cases that require more register than the common case.

The values match Firefox behavior.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorecssSelectorCheckercpp">trunk/Source/WebCore/css/SelectorChecker.cpp</a></li>
<li><a href="#trunkSourceWebCorecssjitFunctionCallh">trunk/Source/WebCore/cssjit/FunctionCall.h</a></li>
<li><a href="#trunkSourceWebCorecssjitSelectorCompilercpp">trunk/Source/WebCore/cssjit/SelectorCompiler.cpp</a></li>
<li><a href="#trunkSourceWebCoredomAttributeh">trunk/Source/WebCore/dom/Attribute.h</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCoredomNodeh">trunk/Source/WebCore/dom/Node.h</a></li>
<li><a href="#trunkSourceWebCoredomTreeScopeh">trunk/Source/WebCore/dom/TreeScope.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastselectorscaseinsensitivevaluematchingexpectedtxt">trunk/LayoutTests/fast/selectors/case-insensitive-value-matching-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastselectorscaseinsensitivevaluematchinghtml">trunk/LayoutTests/fast/selectors/case-insensitive-value-matching.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/LayoutTests/ChangeLog        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2014-02-27  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        Compile attribute value matching
+        https://bugs.webkit.org/show_bug.cgi?id=129228
+
+        Reviewed by Geoffrey Garen.
+
+        * fast/selectors/case-insensitive-value-matching-expected.txt: Added.
+        * fast/selectors/case-insensitive-value-matching.html: Added.
+        Test the various cases that require more register than the common case.
+
+        The values match Firefox behavior.
+
</ins><span class="cx"> 2014-02-27  Alexey Proskuryakov  &lt;ap@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         css3/compositing/isolation-isolate-blended-child.html fails
</span></span></pre></div>
<a id="trunkLayoutTestsfastselectorscaseinsensitivevaluematchingexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/selectors/case-insensitive-value-matching-expected.txt (0 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/selectors/case-insensitive-value-matching-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/selectors/case-insensitive-value-matching-expected.txt        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+Some HTML attribute require case insensitive comparison when matching their value. Test those cases with and without backtracking.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+No backtracking, simple matches per level.
+PASS document.querySelectorAll(&quot;[checked=false]&quot;).length is 1
+PASS document.querySelectorAll(&quot;[checked=false]&quot;)[0].id is &quot;target1&quot;
+PASS getComputedStyle(document.querySelectorAll(&quot;[checked=false]&quot;)[0]).backgroundColor is &quot;rgb(255, 0, 0)&quot;
+PASS document.querySelectorAll(&quot;[lang=en]&gt;[checked=false]&quot;).length is 1
+PASS document.querySelectorAll(&quot;[lang=en]&gt;[checked=false]&quot;)[0].id is &quot;target1&quot;
+PASS getComputedStyle(document.querySelectorAll(&quot;[lang=en]&gt;[checked=false]&quot;)[0]).textIndent is &quot;21px&quot;
+PASS document.querySelectorAll(&quot;[clear=yes]&gt;[lang=en]&gt;[checked=false]&quot;).length is 1
+PASS document.querySelectorAll(&quot;[clear=yes]&gt;[lang=en]&gt;[checked=false]&quot;)[0].id is &quot;target1&quot;
+PASS getComputedStyle(document.querySelectorAll(&quot;[clear=yes]&gt;[lang=en]&gt;[checked=false]&quot;)[0]).fontFamily is &quot;uncomon&quot;
+No backtracking, multiple matches per level.
+PASS document.querySelectorAll(&quot;[dir=webkit][disabled=no]&gt;[type=awesome][charset=latin1]&gt;[lang=fr][checked=false]&quot;).length is 1
+PASS document.querySelectorAll(&quot;[dir=webkit][disabled=no]&gt;[type=awesome][charset=latin1]&gt;[lang=fr][checked=false]&quot;)[0].id is &quot;target1&quot;
+PASS getComputedStyle(document.querySelectorAll(&quot;[dir=webkit][disabled=no]&gt;[type=awesome][charset=latin1]&gt;[lang=fr][checked=false]&quot;)[0]).opacity is &quot;0&quot;
+PASS document.querySelectorAll(&quot;[dir=webkit][disabled=no][clear=yes]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=false]&quot;).length is 1
+PASS document.querySelectorAll(&quot;[dir=webkit][disabled=no][clear=yes]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=false]&quot;)[0].id is &quot;target1&quot;
+PASS getComputedStyle(document.querySelectorAll(&quot;[dir=webkit][disabled=no][clear=yes]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=false]&quot;)[0]).color is &quot;rgb(0, 128, 0)&quot;
+Backtracking, single matches per level.
+PASS document.querySelectorAll(&quot;[dir=webkit]&gt;[charset=latin1]&gt;[scrolling=fast] ul&gt;li&quot;).length is 1
+PASS document.querySelectorAll(&quot;[dir=webkit]&gt;[charset=latin1]&gt;[scrolling=fast] ul&gt;li&quot;)[0].id is &quot;target2&quot;
+PASS getComputedStyle(document.querySelectorAll(&quot;[dir=webkit]&gt;[charset=latin1]&gt;[scrolling=fast] ul&gt;li&quot;)[0]).backgroundColor is &quot;rgb(0, 0, 255)&quot;
+Backtracking, multiple matches per level.
+PASS document.querySelectorAll(&quot;[dir=webkit][disabled=yes][clear=no]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=maybe][scrolling=fast] ul&gt;li&quot;).length is 1
+PASS document.querySelectorAll(&quot;[dir=webkit][disabled=yes][clear=no]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=maybe][scrolling=fast] ul&gt;li&quot;)[0].id is &quot;target2&quot;
+PASS getComputedStyle(document.querySelectorAll(&quot;[dir=webkit][disabled=yes][clear=no]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=maybe][scrolling=fast] ul&gt;li&quot;)[0]).fontFamily is &quot;hipster&quot;
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastselectorscaseinsensitivevaluematchinghtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/selectors/case-insensitive-value-matching.html (0 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/selectors/case-insensitive-value-matching.html                                (rev 0)
+++ trunk/LayoutTests/fast/selectors/case-insensitive-value-matching.html        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -0,0 +1,130 @@
</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;
+[checked=false] {
+    background-color: red;
+}
+
+[lang=en]&gt;[checked=false] {
+    text-indent: 21px;
+}
+
+[clear=yes]&gt;[lang=en]&gt;[checked=false] {
+    font-family: uncomon;
+}
+
+[dir=webkit][disabled=no]&gt;[type=awesome][charset=latin1]&gt;[lang=fr][checked=false] {
+    opacity: 0;
+}
+
+[dir=webkit][disabled=no][clear=yes]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=false] {
+    color: green;
+}
+
+[dir=webkit]&gt;[charset=latin1]&gt;[scrolling=fast] ul&gt;li {
+    background-color: blue;
+}
+
+[dir=webkit][disabled=yes][clear=no]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=maybe][scrolling=fast] ul&gt;li {
+    font-family: hipster;
+}
+
+&lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;div style=&quot;display:none&quot;&gt;
+    &lt;!-- No backtracking tree --&gt;
+    &lt;div clear=Yes dir=WebKit disabled=No&gt;
+        &lt;div charset=Latin1 lang=En type=Awesome&gt;
+            &lt;div lang=Fr checked=False id=target1&gt;
+            &lt;/div&gt;
+        &lt;/div&gt;
+    &lt;/div&gt;
+    &lt;!-- Backtracking tree --&gt;
+    &lt;div clear=No dir=WebKit disabled=Yes&gt;
+        &lt;div charset=Latin1 lang=En type=Awesome&gt;
+            &lt;div lang=Fr checked=Maybe scrolling=Fast&gt;
+
+                &lt;!-- First backtracking level, does not match the top level [div=webkit]--&gt;
+                &lt;div clear=No dir=WebKit2 disabled=Yes&gt;
+                    &lt;div charset=Latin1 lang=En type=Awesome&gt;
+                        &lt;div lang=Fr checked=Maybe scrolling=Fast&gt;
+
+                            &lt;!-- Fails on the second level, on the charset--&gt;
+                            &lt;div clear=No dir=WebKit disabled=Yes&gt;
+                                &lt;div charset=None lang=En type=Awesome&gt;
+                                    &lt;div lang=Fr checked=Maybe scrolling=Fast&gt;
+
+                                        &lt;!-- Fails on the third level, on the scrolling attribute. --&gt;
+                                        &lt;div clear=No dir=WebKit disabled=Yes&gt;
+                                            &lt;div charset=None lang=En type=Awesome&gt;
+                                                &lt;div lang=Fr checked=Maybe scrolling=Slow&gt;
+
+                                                    &lt;!-- Finaly, the target. Congratulation if you read this far :) --&gt;
+                                                    &lt;ul&gt;
+                                                        &lt;li id=target2&gt;&lt;/li&gt;
+                                                    &lt;/ul&gt;
+
+                                                &lt;/div&gt;
+                                            &lt;/div&gt;
+                                        &lt;/div&gt;
+                                        &lt;!-- End of third level failure. --&gt;
+                                    &lt;/div&gt;
+                                &lt;/div&gt;
+                            &lt;/div&gt;
+                            &lt;!-- End of second level failure --&gt;
+                        &lt;/div&gt;
+                    &lt;/div&gt;
+                &lt;/div&gt;
+                &lt;!-- End of first level failure --&gt;
+            &lt;/div&gt;
+        &lt;/div&gt;
+    &lt;/div&gt;
+&lt;/div&gt;
+&lt;/body&gt;
+&lt;script&gt;
+description('Some HTML attribute require case insensitive comparison when matching their value. Test those cases with and without backtracking.');
+
+debug(&quot;No backtracking, simple matches per level.&quot;)
+// One level.
+shouldBe('document.querySelectorAll(&quot;[checked=false]&quot;).length', '1');
+shouldBeEqualToString('document.querySelectorAll(&quot;[checked=false]&quot;)[0].id', 'target1');
+shouldBeEqualToString('getComputedStyle(document.querySelectorAll(&quot;[checked=false]&quot;)[0]).backgroundColor', 'rgb(255, 0, 0)');
+
+// Two level.
+shouldBe('document.querySelectorAll(&quot;[lang=en]&gt;[checked=false]&quot;).length', '1');
+shouldBeEqualToString('document.querySelectorAll(&quot;[lang=en]&gt;[checked=false]&quot;)[0].id', 'target1');
+shouldBeEqualToString('getComputedStyle(document.querySelectorAll(&quot;[lang=en]&gt;[checked=false]&quot;)[0]).textIndent', '21px');
+
+// Three level.
+shouldBe('document.querySelectorAll(&quot;[clear=yes]&gt;[lang=en]&gt;[checked=false]&quot;).length', '1');
+shouldBeEqualToString('document.querySelectorAll(&quot;[clear=yes]&gt;[lang=en]&gt;[checked=false]&quot;)[0].id', 'target1');
+shouldBeEqualToString('getComputedStyle(document.querySelectorAll(&quot;[clear=yes]&gt;[lang=en]&gt;[checked=false]&quot;)[0]).fontFamily', 'uncomon');
+
+debug(&quot;No backtracking, multiple matches per level.&quot;);
+// Three level, 2 matches per level.
+shouldBe('document.querySelectorAll(&quot;[dir=webkit][disabled=no]&gt;[type=awesome][charset=latin1]&gt;[lang=fr][checked=false]&quot;).length', '1');
+shouldBeEqualToString('document.querySelectorAll(&quot;[dir=webkit][disabled=no]&gt;[type=awesome][charset=latin1]&gt;[lang=fr][checked=false]&quot;)[0].id', 'target1');
+shouldBeEqualToString('getComputedStyle(document.querySelectorAll(&quot;[dir=webkit][disabled=no]&gt;[type=awesome][charset=latin1]&gt;[lang=fr][checked=false]&quot;)[0]).opacity', '0');
+
+// Three level, 3 matches per level.
+shouldBe('document.querySelectorAll(&quot;[dir=webkit][disabled=no][clear=yes]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=false]&quot;).length', '1');
+shouldBeEqualToString('document.querySelectorAll(&quot;[dir=webkit][disabled=no][clear=yes]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=false]&quot;)[0].id', 'target1');
+shouldBeEqualToString('getComputedStyle(document.querySelectorAll(&quot;[dir=webkit][disabled=no][clear=yes]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=false]&quot;)[0]).color', 'rgb(0, 128, 0)');
+
+debug(&quot;Backtracking, single matches per level.&quot;);
+// A single sequence of backtracking, 3 level to force one slow-path backtracking.
+shouldBe('document.querySelectorAll(&quot;[dir=webkit]&gt;[charset=latin1]&gt;[scrolling=fast] ul&gt;li&quot;).length', '1');
+shouldBeEqualToString('document.querySelectorAll(&quot;[dir=webkit]&gt;[charset=latin1]&gt;[scrolling=fast] ul&gt;li&quot;)[0].id', 'target2');
+shouldBeEqualToString('getComputedStyle(document.querySelectorAll(&quot;[dir=webkit]&gt;[charset=latin1]&gt;[scrolling=fast] ul&gt;li&quot;)[0]).backgroundColor', 'rgb(0, 0, 255)');
+
+debug(&quot;Backtracking, multiple matches per level.&quot;);
+// Multiple matches per level to make the attribute match loop more complex.
+shouldBe('document.querySelectorAll(&quot;[dir=webkit][disabled=yes][clear=no]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=maybe][scrolling=fast] ul&gt;li&quot;).length', '1');
+shouldBeEqualToString('document.querySelectorAll(&quot;[dir=webkit][disabled=yes][clear=no]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=maybe][scrolling=fast] ul&gt;li&quot;)[0].id', 'target2');
+shouldBeEqualToString('getComputedStyle(document.querySelectorAll(&quot;[dir=webkit][disabled=yes][clear=no]&gt;[type=awesome][charset=latin1][lang=en]&gt;[lang=fr][checked=maybe][scrolling=fast] ul&gt;li&quot;)[0]).fontFamily', 'hipster');
+&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="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/Source/WebCore/ChangeLog        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -1,3 +1,102 @@
</span><ins>+2014-02-27  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        Compile attribute value matching
+        https://bugs.webkit.org/show_bug.cgi?id=129228
+
+        Reviewed by Geoffrey Garen.
+
+        Add support for compiling value matching when matching attributes in Selector.
+        This patch only adds exact matching, the other cases will follow.
+
+        There is a little infrastructure changes since FunctionCall now needs to support
+        calls taking 2 arguments. The fun begins when the arguments are not in the right
+        registers and need to be moved to the right place. Otherwise the code is straightforward.
+
+        In SelectorCompiler, it is necessary to handle two different cases of matching: case sensitive
+        and case insensitive. The choice is done in part at compilation time by asking HTMLDocument
+        if the name filter can include case insensitive attribute. The other part is done at runtime
+        by querying the element for its type and document.
+
+        Test: fast/selectors/case-insensitive-value-matching.html
+
+        * css/SelectorChecker.cpp:
+        (WebCore::attributeValueMatches): Null values matching should never happen, when an attribute
+        has no value, its value is empty.
+
+        * cssjit/FunctionCall.h:
+        (WebCore::FunctionCall::FunctionCall):
+        Add support for calls with two arguments.
+        (WebCore::FunctionCall::setOneArgument):
+        (WebCore::FunctionCall::setTwoArguments):
+        (WebCore::FunctionCall::swapArguments):
+        Here we need to swap two registers, but we cannot allocate a new register (because the context
+        of the function call may have taken all the available registers already).
+
+        On x86, the solution is simple, we can swap the two registers without side effects.
+
+        On other platforms, it is a little more complex. If there is any available register, we can just
+        use it as a temporary to make the swap.
+        If there are no available registers, we know that all the registers are taken. Since swapArguments()
+        was called after pushing all the arguments on the stack, we can safely trash the value of any of those.
+        We take the first available register that is not a function argument and use it as a temporary.
+
+        (WebCore::FunctionCall::prepareAndCall):
+        This is the fun part, we have two registers where the values must go before the function call. The values
+        can be in any combination of the allocated registers. The code here needs to move the two values to
+        their target register while avoiding conflicts.
+
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::AttributeMatchingInfo::AttributeMatchingInfo):
+        (WebCore::SelectorCompiler::AttributeMatchingInfo::canDefaultToCaseSensitiveValueMatch):
+        (WebCore::SelectorCompiler::AttributeMatchingInfo::selector):
+        The value of HTMLDocument::isCaseSensitiveAttribute is needed at compilation time to compute the number
+        of required registers. As a result, we need to keep it along in the selector fragment.
+
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::SelectorCodeGenerator):
+        (WebCore::SelectorCompiler::attributeNameTestingRequiresNamespaceRegister):
+        (WebCore::SelectorCompiler::attributeValueTestingRequiresExtraRegister):
+        (WebCore::SelectorCompiler::minimumRegisterRequirements):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::markParentElementIfResolvingStyle):
+        (WebCore::SelectorCompiler::canMatchStyleAttribute):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateSynchronizeStyleAttribute):
+        (WebCore::SelectorCompiler::canMatchAnimatableSVGAttribute):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateSynchronizeAllAnimatedSVGAttribute):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementAttributeMatching):
+        The matching code is moved in a local scope. This is done to recover the register of qualifiedNameImpl
+        before doing any value matching. That register can then be used to store the expected value when matching
+        an attribute value.
+        It is unfortunate there is so much register pressure in this part.
+
+        Value matching is done outside the loop. The idea is to keep the loop really small since in the vast majority
+        of cases, name matching fails.
+        If the value matching fails, we jump back into the tight loop.
+
+        This is not ideal in all situation. For example trivial name matching with trivial value matching should
+        be done in loop. There is a FIXME to improve those cases later.
+
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementAttributeValueMatching):
+        (WebCore::SelectorCompiler::testIsHTMLClassOnDocument):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementAttributeValueExactMatching):
+        In the case sensitive branch, things are really simple. We have to AtomicStringImpl pointers, if they
+        don't match, it is a failure.
+
+        The case sensitive branch start by comparing the pointers in case the values are equal. This is a common
+        case and it simplifies the cases for SVG, XHTML, etc.
+        If the two values are not equal, we must first find if the context requires case insensitive comparison
+        (HTMLElement in HTMLDocument). If the conditions require case insensitive matching, we then fall back
+        to a function call.
+
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementFunctionCallTest):
+        * dom/Attribute.h:
+        (WebCore::Attribute::valueMemoryOffset):
+        * dom/Document.h:
+        (WebCore::Document::documentClassesMemoryOffset):
+        (WebCore::Document::isHTMLDocumentClassFlag):
+        * dom/Node.h:
+        (WebCore::Node::treeScopeMemoryOffset):
+        * dom/TreeScope.h:
+        (WebCore::TreeScope::documentScopeMemoryOffset):
+
</ins><span class="cx"> 2014-02-27  Ryuan Choi  &lt;ryuan.choi@samsung.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Build break when disabled CSS_GRID_LAYOUT
</span></span></pre></div>
<a id="trunkSourceWebCorecssSelectorCheckercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/SelectorChecker.cpp        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -277,8 +277,7 @@
</span><span class="cx"> static bool attributeValueMatches(const Attribute&amp; attribute, CSSSelector::Match match, const AtomicString&amp; selectorValue, bool caseSensitive)
</span><span class="cx"> {
</span><span class="cx">     const AtomicString&amp; value = attribute.value();
</span><del>-    if (value.isNull())
-        return false;
</del><ins>+    ASSERT(!value.isNull());
</ins><span class="cx"> 
</span><span class="cx">     switch (match) {
</span><span class="cx">     case CSSSelector::Exact:
</span></span></pre></div>
<a id="trunkSourceWebCorecssjitFunctionCallh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/cssjit/FunctionCall.h (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/cssjit/FunctionCall.h        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/Source/WebCore/cssjit/FunctionCall.h        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -42,7 +42,8 @@
</span><span class="cx">         , m_registerAllocator(registerAllocator)
</span><span class="cx">         , m_stackAllocator(stackAllocator)
</span><span class="cx">         , m_callRegistry(callRegistry)
</span><del>-        , m_firstArgument(0)
</del><ins>+        , m_firstArgument(nullptr)
+        , m_secondArgument(nullptr)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -51,11 +52,17 @@
</span><span class="cx">         m_functionAddress = functionAddress;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void setFirstArgument(const JSC::MacroAssembler::RegisterID&amp; registerID)
</del><ins>+    void setOneArgument(const JSC::MacroAssembler::RegisterID&amp; registerID)
</ins><span class="cx">     {
</span><span class="cx">         m_firstArgument = &amp;registerID;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void setTwoArguments(const JSC::MacroAssembler::RegisterID&amp; firstRegisterID, const JSC::MacroAssembler::RegisterID&amp; secondRegisterID)
+    {
+        m_firstArgument = &amp;firstRegisterID;
+        m_secondArgument = &amp;secondRegisterID;
+    }
+
</ins><span class="cx">     void call()
</span><span class="cx">     {
</span><span class="cx">         prepareAndCall();
</span><span class="lines">@@ -71,14 +78,69 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx"> private:
</span><ins>+    void swapArguments()
+    {
+        JSC::MacroAssembler::RegisterID a = *m_firstArgument;
+        JSC::MacroAssembler::RegisterID b = *m_secondArgument;
+        // x86 can swap without a temporary register. On other architectures, we need allocate a temporary register to switch the values.
+#if CPU(X86) || CPU(X86_64)
+        m_assembler.swap(a, b);
+#else
+        if (m_registerAllocator.availableRegisterCount()) {
+            // Usually we can just use a free register.
+            LocalRegister tempValue(m_registerAllocator);
+            m_assembler.move(a, tempValue);
+            m_assembler.move(b, a);
+            m_assembler.move(tempValue, b);
+        } else {
+            // If there is no free register, everything should be on the stack at this point. We can take
+            // the first of those saved registers and use it as a temporary.
+            JSC::MacroAssembler::RegisterID pushedRegister;
+            for (unsigned i = 0; i &lt; m_registerAllocator.allocatedRegisters().size(); ++i) {
+                pushedRegister = m_registerAllocator.allocatedRegisters()[i];
+                if (pushedRegister != a &amp;&amp; pushedRegister != b)
+                    break;
+            }
+            ASSERT(pushedRegister != a &amp;&amp; pushedRegister != b);
+            m_assembler.move(a, pushedRegister);
+            m_assembler.move(b, a);
+            m_assembler.move(pushedRegister, b);
+        }
+#endif
+    }
+
</ins><span class="cx">     void prepareAndCall()
</span><span class="cx">     {
</span><span class="cx">         ASSERT(m_functionAddress.executableAddress());
</span><ins>+        ASSERT(!m_firstArgument || (m_firstArgument &amp;&amp; !m_secondArgument) || (m_firstArgument &amp;&amp; m_secondArgument));
</ins><span class="cx"> 
</span><span class="cx">         saveAllocatedRegisters();
</span><span class="cx">         m_stackAllocator.alignStackPreFunctionCall();
</span><span class="cx"> 
</span><del>-        if (m_firstArgument &amp;&amp; *m_firstArgument != JSC::GPRInfo::argumentGPR0)
</del><ins>+        if (m_secondArgument) {
+            if (*m_firstArgument != JSC::GPRInfo::argumentGPR0) {
+                // If firstArgument is not in argumentGPR0, we need to handle potential conflicts:
+                // -if secondArgument and firstArgument are in inversted registers, just swap the values.
+                // -if secondArgument is in argumentGPR0 but firstArgument is not taking argumentGPR1, we can move in order secondArgument-&gt;argumentGPR1, firstArgument-&gt;argumentGPR0
+                // -if secondArgument does not take argumentGPR0, firstArgument and secondArgument can be moved safely to destination.
+                if (*m_secondArgument == JSC::GPRInfo::argumentGPR0) {
+                    if (*m_firstArgument == JSC::GPRInfo::argumentGPR1)
+                        swapArguments();
+                    else {
+                        m_assembler.move(JSC::GPRInfo::argumentGPR0, JSC::GPRInfo::argumentGPR1);
+                        m_assembler.move(*m_firstArgument, JSC::GPRInfo::argumentGPR0);
+                    }
+                } else {
+                    m_assembler.move(*m_firstArgument, JSC::GPRInfo::argumentGPR0);
+                    if (*m_secondArgument != JSC::GPRInfo::argumentGPR1)
+                        m_assembler.move(*m_secondArgument, JSC::GPRInfo::argumentGPR1);
+                }
+            } else {
+                // We know firstArgument is already in place, we can safely move secondArgument.
+                if (*m_secondArgument != JSC::GPRInfo::argumentGPR1)
+                    m_assembler.move(*m_secondArgument, JSC::GPRInfo::argumentGPR1);
+            }
+        } else if (m_firstArgument &amp;&amp; *m_firstArgument != JSC::GPRInfo::argumentGPR0)
</ins><span class="cx">             m_assembler.move(*m_firstArgument, JSC::GPRInfo::argumentGPR0);
</span><span class="cx"> 
</span><span class="cx">         JSC::MacroAssembler::Call call = m_assembler.call();
</span><span class="lines">@@ -120,6 +182,7 @@
</span><span class="cx"> 
</span><span class="cx">     JSC::FunctionPtr m_functionAddress;
</span><span class="cx">     const JSC::MacroAssembler::RegisterID* m_firstArgument;
</span><ins>+    const JSC::MacroAssembler::RegisterID* m_secondArgument;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorecssjitSelectorCompilercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/cssjit/SelectorCompiler.cpp (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/cssjit/SelectorCompiler.cpp        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/Source/WebCore/cssjit/SelectorCompiler.cpp        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -32,6 +32,7 @@
</span><span class="cx"> #include &quot;Element.h&quot;
</span><span class="cx"> #include &quot;ElementData.h&quot;
</span><span class="cx"> #include &quot;FunctionCall.h&quot;
</span><ins>+#include &quot;HTMLDocument.h&quot;
</ins><span class="cx"> #include &quot;HTMLNames.h&quot;
</span><span class="cx"> #include &quot;NodeRenderStyle.h&quot;
</span><span class="cx"> #include &quot;QualifiedName.h&quot;
</span><span class="lines">@@ -90,6 +91,22 @@
</span><span class="cx">     CannotCompile
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+class AttributeMatchingInfo {
+public:
+    AttributeMatchingInfo(const CSSSelector* selector, bool canDefaultToCaseSensitiveValueMatch)
+        : m_selector(selector)
+        , m_canDefaultToCaseSensitiveValueMatch(canDefaultToCaseSensitiveValueMatch)
+    {
+    }
+
+    bool canDefaultToCaseSensitiveValueMatch() const { return m_canDefaultToCaseSensitiveValueMatch; }
+    const CSSSelector&amp; selector() const { return *m_selector; }
+
+private:
+    const CSSSelector* m_selector;
+    bool m_canDefaultToCaseSensitiveValueMatch;
+};
+
</ins><span class="cx"> struct SelectorFragment {
</span><span class="cx">     SelectorFragment()
</span><span class="cx">         : traversalBacktrackingAction(BacktrackingAction::NoBacktracking)
</span><span class="lines">@@ -111,7 +128,7 @@
</span><span class="cx">     Vector&lt;const AtomicStringImpl*, 1&gt; classNames;
</span><span class="cx">     HashSet&lt;unsigned&gt; pseudoClasses;
</span><span class="cx">     Vector&lt;JSC::FunctionPtr&gt; unoptimizedPseudoClasses;
</span><del>-    Vector&lt;const CSSSelector*&gt; attributes;
</del><ins>+    Vector&lt;AttributeMatchingInfo&gt; attributes;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> typedef JSC::MacroAssembler Assembler;
</span><span class="lines">@@ -154,7 +171,9 @@
</span><span class="cx">     void generateSynchronizeStyleAttribute(Assembler::RegisterID elementDataArraySizeAndFlags);
</span><span class="cx">     void generateSynchronizeAllAnimatedSVGAttribute(Assembler::RegisterID elementDataArraySizeAndFlags);
</span><span class="cx">     void generateElementAttributesMatching(Assembler::JumpList&amp; failureCases, const LocalRegister&amp; elementDataAddress, const SelectorFragment&amp;);
</span><del>-    void generateElementAttributeMatching(Assembler::JumpList&amp; failureCases, Assembler::RegisterID currentAttributeAddress, Assembler::RegisterID decIndexRegister, const CSSSelector* attributeSelector);
</del><ins>+    void generateElementAttributeMatching(Assembler::JumpList&amp; failureCases, Assembler::RegisterID currentAttributeAddress, Assembler::RegisterID decIndexRegister, const AttributeMatchingInfo&amp; attributeInfo);
+    void generateElementAttributeValueMatching(Assembler::JumpList&amp; failureCases, Assembler::RegisterID currentAttributeAddress, const AttributeMatchingInfo&amp; attributeInfo);
+    void generateElementAttributeValueExactMatching(Assembler::JumpList&amp; failureCases, Assembler::RegisterID currentAttributeAddress, const AtomicString&amp; expectedValue, bool caseSensitive);
</ins><span class="cx">     void generateElementHasTagName(Assembler::JumpList&amp; failureCases, const QualifiedName&amp; nameToMatch);
</span><span class="cx">     void generateElementHasId(Assembler::JumpList&amp; failureCases, const LocalRegister&amp; elementDataAddress, const AtomicString&amp; idToMatch);
</span><span class="cx">     void generateElementHasClasses(Assembler::JumpList&amp; failureCases, const LocalRegister&amp; elementDataAddress, const Vector&lt;const AtomicStringImpl*&gt;&amp; classNames);
</span><span class="lines">@@ -328,11 +347,13 @@
</span><span class="cx">             if (m_functionType == FunctionType::CannotCompile || m_functionType == FunctionType::CannotMatchAnything)
</span><span class="cx">                 return;
</span><span class="cx">             break;
</span><ins>+        case CSSSelector::Exact:
+            fragment.attributes.append(AttributeMatchingInfo(selector, HTMLDocument::isCaseSensitiveAttribute(selector-&gt;attribute())));
+            break;
</ins><span class="cx">         case CSSSelector::Set:
</span><del>-            fragment.attributes.append(selector);
</del><ins>+            fragment.attributes.append(AttributeMatchingInfo(selector, false));
</ins><span class="cx">             break;
</span><span class="cx">         case CSSSelector::Unknown:
</span><del>-        case CSSSelector::Exact:
</del><span class="cx">         case CSSSelector::List:
</span><span class="cx">         case CSSSelector::Hyphen:
</span><span class="cx">         case CSSSelector::PseudoElement:
</span><span class="lines">@@ -372,6 +393,16 @@
</span><span class="cx">     m_functionType = FunctionType::CannotCompile;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static inline bool attributeNameTestingRequiresNamespaceRegister(const CSSSelector&amp; attributeSelector)
+{
+    return attributeSelector.attribute().prefix() != starAtom &amp;&amp; !attributeSelector.attribute().namespaceURI().isNull();
+}
+
+static inline bool attributeValueTestingRequiresCaseFoldingRegister(const AttributeMatchingInfo&amp; attributeInfo)
+{
+    return !attributeInfo.canDefaultToCaseSensitiveValueMatch();
+}
+
</ins><span class="cx"> static inline unsigned minimumRegisterRequirements(const SelectorFragmentList&amp; selectorFragments)
</span><span class="cx"> {
</span><span class="cx">     // Strict minimum to match anything interesting:
</span><span class="lines">@@ -381,10 +412,10 @@
</span><span class="cx">     // Attributes cause some register pressure.
</span><span class="cx">     for (unsigned selectorFragmentIndex = 0; selectorFragmentIndex &lt; selectorFragments.size(); ++selectorFragmentIndex) {
</span><span class="cx">         const SelectorFragment&amp; selectorFragment = selectorFragments[selectorFragmentIndex];
</span><del>-        const Vector&lt;const CSSSelector*&gt;&amp; attributes = selectorFragment.attributes;
</del><ins>+        const Vector&lt;AttributeMatchingInfo&gt;&amp; attributes = selectorFragment.attributes;
</ins><span class="cx"> 
</span><span class="cx">         for (unsigned attributeIndex = 0; attributeIndex &lt; attributes.size(); ++attributeIndex) {
</span><del>-            // Element + ElementData + scratchRegister + attributeArrayPointer + expectedLocalName + qualifiedNameImpl.
</del><ins>+            // Element + ElementData + scratchRegister + attributeArrayPointer + expectedLocalName + (qualifiedNameImpl &amp;&amp; expectedValue).
</ins><span class="cx">             unsigned attributeMinimum = 6;
</span><span class="cx">             if (selectorFragment.traversalBacktrackingAction == BacktrackingAction::JumpToDescendantTail
</span><span class="cx">                 || selectorFragment.matchingBacktrackingAction == BacktrackingAction::JumpToDescendantTail)
</span><span class="lines">@@ -393,9 +424,11 @@
</span><span class="cx">             if (attributes.size() != 1)
</span><span class="cx">                 attributeMinimum += 2; // For the local copy of the counter and attributeArrayPointer.
</span><span class="cx"> 
</span><del>-            const CSSSelector* attributeSelector = attributes[attributeIndex];
-            if (attributeSelector-&gt;attribute().prefix() != starAtom &amp;&amp; !attributeSelector-&gt;attribute().namespaceURI().isNull())
-                attributeMinimum += 1; // Additional register for the expected namespace.
</del><ins>+            const AttributeMatchingInfo&amp; attributeInfo = attributes[attributeIndex];
+            const CSSSelector&amp; attributeSelector = attributeInfo.selector();
+            if (attributeNameTestingRequiresNamespaceRegister(attributeSelector)
+                || attributeValueTestingRequiresCaseFoldingRegister(attributeInfo))
+                attributeMinimum += 1;
</ins><span class="cx"> 
</span><span class="cx">             minimum = std::max(minimum, attributeMinimum);
</span><span class="cx">         }
</span><span class="lines">@@ -823,7 +856,7 @@
</span><span class="cx">     // Invoke the marking function on the parent element.
</span><span class="cx">     FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
</span><span class="cx">     functionCall.setFunctionAddress(markingFunction);
</span><del>-    functionCall.setFirstArgument(parentElement);
</del><ins>+    functionCall.setOneArgument(parentElement);
</ins><span class="cx">     functionCall.call();
</span><span class="cx"> 
</span><span class="cx">     notResolvingStyle.link(&amp;m_assembler);
</span><span class="lines">@@ -958,14 +991,14 @@
</span><span class="cx"> static inline bool canMatchStyleAttribute(const SelectorFragment&amp; fragment)
</span><span class="cx"> {
</span><span class="cx">     for (unsigned i = 0; i &lt; fragment.attributes.size(); ++i) {
</span><del>-        const CSSSelector* attributeSelector = fragment.attributes[i];
-        const QualifiedName&amp; attributeName = attributeSelector-&gt;attribute();
</del><ins>+        const CSSSelector&amp; attributeSelector = fragment.attributes[i].selector();
+        const QualifiedName&amp; attributeName = attributeSelector.attribute();
</ins><span class="cx">         if (Attribute::nameMatchesFilter(HTMLNames::styleAttr, attributeName.prefix(), attributeName.localName(), attributeName.namespaceURI()))
</span><span class="cx">             return true;
</span><span class="cx"> 
</span><del>-        const AtomicString&amp; canonicalLocalName = attributeSelector-&gt;attributeCanonicalLocalName();
</del><ins>+        const AtomicString&amp; canonicalLocalName = attributeSelector.attributeCanonicalLocalName();
</ins><span class="cx">         if (attributeName.localName() != canonicalLocalName
</span><del>-            &amp;&amp; Attribute::nameMatchesFilter(HTMLNames::styleAttr, attributeName.prefix(), attributeSelector-&gt;attributeCanonicalLocalName(), attributeName.namespaceURI())) {
</del><ins>+            &amp;&amp; Attribute::nameMatchesFilter(HTMLNames::styleAttr, attributeName.prefix(), attributeSelector.attributeCanonicalLocalName(), attributeName.namespaceURI())) {
</ins><span class="cx">             return true;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="lines">@@ -980,7 +1013,7 @@
</span><span class="cx">     FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
</span><span class="cx">     functionCall.setFunctionAddress(StyledElement::synchronizeStyleAttributeInternal);
</span><span class="cx">     Assembler::RegisterID elementAddress = elementAddressRegister;
</span><del>-    functionCall.setFirstArgument(elementAddress);
</del><ins>+    functionCall.setOneArgument(elementAddress);
</ins><span class="cx">     functionCall.call();
</span><span class="cx"> 
</span><span class="cx">     styleAttributeNotDirty.link(&amp;m_assembler);
</span><span class="lines">@@ -989,14 +1022,14 @@
</span><span class="cx"> static inline bool canMatchAnimatableSVGAttribute(const SelectorFragment&amp; fragment)
</span><span class="cx"> {
</span><span class="cx">     for (unsigned i = 0; i &lt; fragment.attributes.size(); ++i) {
</span><del>-        const CSSSelector* attributeSelector = fragment.attributes[i];
-        const QualifiedName&amp; selectorAttributeName = attributeSelector-&gt;attribute();
</del><ins>+        const CSSSelector&amp; attributeSelector = fragment.attributes[i].selector();
+        const QualifiedName&amp; selectorAttributeName = attributeSelector.attribute();
</ins><span class="cx"> 
</span><span class="cx">         const QualifiedName&amp; candidateForLocalName = SVGElement::animatableAttributeForName(selectorAttributeName.localName());
</span><span class="cx">         if (Attribute::nameMatchesFilter(candidateForLocalName, selectorAttributeName.prefix(), selectorAttributeName.localName(), selectorAttributeName.namespaceURI()))
</span><span class="cx">             return true;
</span><span class="cx"> 
</span><del>-        const AtomicString&amp; canonicalLocalName = attributeSelector-&gt;attributeCanonicalLocalName();
</del><ins>+        const AtomicString&amp; canonicalLocalName = attributeSelector.attributeCanonicalLocalName();
</ins><span class="cx">         if (selectorAttributeName.localName() != canonicalLocalName) {
</span><span class="cx">             const QualifiedName&amp; candidateForCanonicalLocalName = SVGElement::animatableAttributeForName(selectorAttributeName.localName());
</span><span class="cx">             if (Attribute::nameMatchesFilter(candidateForCanonicalLocalName, selectorAttributeName.prefix(), selectorAttributeName.localName(), selectorAttributeName.namespaceURI()))
</span><span class="lines">@@ -1015,7 +1048,7 @@
</span><span class="cx">     FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
</span><span class="cx">     functionCall.setFunctionAddress(SVGElement::synchronizeAllAnimatedSVGAttribute);
</span><span class="cx">     Assembler::RegisterID elementAddress = elementAddressRegister;
</span><del>-    functionCall.setFirstArgument(elementAddress);
</del><ins>+    functionCall.setOneArgument(elementAddress);
</ins><span class="cx">     functionCall.call();
</span><span class="cx"> 
</span><span class="cx">     animatedSVGAttributesNotDirty.link(&amp;m_assembler);
</span><span class="lines">@@ -1083,15 +1116,16 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SelectorCodeGenerator::generateElementAttributeMatching(Assembler::JumpList&amp; failureCases, Assembler::RegisterID currentAttributeAddress, Assembler::RegisterID decIndexRegister, const CSSSelector* attributeSelector)
</del><ins>+void SelectorCodeGenerator::generateElementAttributeMatching(Assembler::JumpList&amp; failureCases, Assembler::RegisterID currentAttributeAddress, Assembler::RegisterID decIndexRegister, const AttributeMatchingInfo&amp; attributeInfo)
</ins><span class="cx"> {
</span><span class="cx">     // Get the localName used for comparison. HTML elements use a lowercase local name known in selectors as canonicalLocalName.
</span><span class="cx">     LocalRegister localNameToMatch(m_registerAllocator);
</span><span class="cx"> 
</span><span class="cx">     // In general, canonicalLocalName and localName are the same. When they differ, we have to check if the node is HTML to know
</span><span class="cx">     // which one to use.
</span><del>-    const AtomicStringImpl* canonicalLocalName = attributeSelector-&gt;attributeCanonicalLocalName().impl();
-    const AtomicStringImpl* localName = attributeSelector-&gt;attribute().localName().impl();
</del><ins>+    const CSSSelector&amp; attributeSelector = attributeInfo.selector();
+    const AtomicStringImpl* canonicalLocalName = attributeSelector.attributeCanonicalLocalName().impl();
+    const AtomicStringImpl* localName = attributeSelector.attribute().localName().impl();
</ins><span class="cx">     if (canonicalLocalName == localName)
</span><span class="cx">         m_assembler.move(Assembler::TrustedImmPtr(canonicalLocalName), localNameToMatch);
</span><span class="cx">     else {
</span><span class="lines">@@ -1104,39 +1138,105 @@
</span><span class="cx">     Assembler::JumpList successCases;
</span><span class="cx">     Assembler::Label loopStart(m_assembler.label());
</span><span class="cx"> 
</span><del>-    LocalRegister qualifiedNameImpl(m_registerAllocator);
-    m_assembler.loadPtr(Assembler::Address(currentAttributeAddress, Attribute::nameMemoryOffset()), qualifiedNameImpl);
</del><ins>+    {
+        LocalRegister qualifiedNameImpl(m_registerAllocator);
+        m_assembler.loadPtr(Assembler::Address(currentAttributeAddress, Attribute::nameMemoryOffset()), qualifiedNameImpl);
</ins><span class="cx"> 
</span><del>-    bool shouldCheckNamespace = attributeSelector-&gt;attribute().prefix() != starAtom;
-    if (shouldCheckNamespace) {
-        Assembler::Jump nameDoesNotMatch = m_assembler.branchPtr(Assembler::NotEqual, Assembler::Address(qualifiedNameImpl, QualifiedName::QualifiedNameImpl::localNameMemoryOffset()), localNameToMatch);
</del><ins>+        bool shouldCheckNamespace = attributeSelector.attribute().prefix() != starAtom;
+        if (shouldCheckNamespace) {
+            Assembler::Jump nameDoesNotMatch = m_assembler.branchPtr(Assembler::NotEqual, Assembler::Address(qualifiedNameImpl, QualifiedName::QualifiedNameImpl::localNameMemoryOffset()), localNameToMatch);
</ins><span class="cx"> 
</span><del>-        const AtomicStringImpl* namespaceURI = attributeSelector-&gt;attribute().namespaceURI().impl();
-        if (namespaceURI) {
-            LocalRegister namespaceToMatch(m_registerAllocator);
-            m_assembler.move(Assembler::TrustedImmPtr(namespaceURI), namespaceToMatch);
-            successCases.append(m_assembler.branchPtr(Assembler::Equal, Assembler::Address(qualifiedNameImpl, QualifiedName::QualifiedNameImpl::namespaceMemoryOffset()), namespaceToMatch));
</del><ins>+            const AtomicStringImpl* namespaceURI = attributeSelector.attribute().namespaceURI().impl();
+            if (namespaceURI) {
+                LocalRegister namespaceToMatch(m_registerAllocator);
+                m_assembler.move(Assembler::TrustedImmPtr(namespaceURI), namespaceToMatch);
+                successCases.append(m_assembler.branchPtr(Assembler::Equal, Assembler::Address(qualifiedNameImpl, QualifiedName::QualifiedNameImpl::namespaceMemoryOffset()), namespaceToMatch));
+            } else
+                successCases.append(m_assembler.branchTestPtr(Assembler::Zero, Assembler::Address(qualifiedNameImpl, QualifiedName::QualifiedNameImpl::namespaceMemoryOffset())));
+            nameDoesNotMatch.link(&amp;m_assembler);
</ins><span class="cx">         } else
</span><del>-            successCases.append(m_assembler.branchTestPtr(Assembler::Zero, Assembler::Address(qualifiedNameImpl, QualifiedName::QualifiedNameImpl::namespaceMemoryOffset())));
-        nameDoesNotMatch.link(&amp;m_assembler);
-    } else
-        successCases.append(m_assembler.branchPtr(Assembler::Equal, Assembler::Address(qualifiedNameImpl, QualifiedName::QualifiedNameImpl::localNameMemoryOffset()), localNameToMatch));
</del><ins>+            successCases.append(m_assembler.branchPtr(Assembler::Equal, Assembler::Address(qualifiedNameImpl, QualifiedName::QualifiedNameImpl::localNameMemoryOffset()), localNameToMatch));
+    }
</ins><span class="cx"> 
</span><ins>+    Assembler::Label loopReEntry(m_assembler.label());
+
</ins><span class="cx">     // If we reached the last element -&gt; failure.
</span><span class="cx">     failureCases.append(m_assembler.branchSub32(Assembler::Zero, Assembler::TrustedImm32(1), decIndexRegister));
</span><span class="cx"> 
</span><span class="cx">     // Otherwise just loop over.
</span><span class="cx">     m_assembler.addPtr(Assembler::TrustedImm32(sizeof(Attribute)), currentAttributeAddress);
</span><span class="cx">     m_assembler.jump().linkTo(loopStart, &amp;m_assembler);
</span><ins>+
</ins><span class="cx">     successCases.link(&amp;m_assembler);
</span><ins>+
+    // We make the assumption that name matching fails in most cases and we keep value matching outside
+    // of the loop. We re-enter the loop if needed.
+    // FIXME: exact case sensitive value matching is so simple that it should be done in the loop.
+    Assembler::JumpList localFailureCases;
+    generateElementAttributeValueMatching(localFailureCases, currentAttributeAddress, attributeInfo);
+    localFailureCases.linkTo(loopReEntry, &amp;m_assembler);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void SelectorCodeGenerator::generateElementAttributeValueMatching(Assembler::JumpList&amp; failureCases, Assembler::RegisterID currentAttributeAddress, const AttributeMatchingInfo&amp; attributeInfo)
+{
+    const CSSSelector&amp; attributeSelector = attributeInfo.selector();
+    if (attributeSelector.m_match == CSSSelector::Set)
+        return;
+
+    const AtomicString&amp; expectedValue = attributeSelector.value();
+    ASSERT(!expectedValue.isNull());
+
+    RELEASE_ASSERT(attributeSelector.m_match == CSSSelector::Exact);
+    generateElementAttributeValueExactMatching(failureCases, currentAttributeAddress, expectedValue, attributeInfo.canDefaultToCaseSensitiveValueMatch());
+}
+
+static inline Assembler::Jump testIsHTMLClassOnDocument(Assembler::ResultCondition condition, Assembler&amp; assembler, Assembler::RegisterID documentAddress)
+{
+    return assembler.branchTest32(condition, Assembler::Address(documentAddress, Document::documentClassesMemoryOffset()), Assembler::TrustedImm32(Document::isHTMLDocumentClassFlag()));
+}
+
+void SelectorCodeGenerator::generateElementAttributeValueExactMatching(Assembler::JumpList&amp; failureCases, Assembler::RegisterID currentAttributeAddress, const AtomicString&amp; expectedValue, bool canDefaultToCaseSensitiveValueMatch)
+{
+    LocalRegister expectedValueRegister(m_registerAllocator);
+    m_assembler.move(Assembler::TrustedImmPtr(expectedValue.impl()), expectedValueRegister);
+
+    if (canDefaultToCaseSensitiveValueMatch)
+        failureCases.append(m_assembler.branchPtr(Assembler::NotEqual, Assembler::Address(currentAttributeAddress, Attribute::valueMemoryOffset()), expectedValueRegister));
+    else {
+        Assembler::Jump skipCaseInsensitiveComparison = m_assembler.branchPtr(Assembler::Equal, Assembler::Address(currentAttributeAddress, Attribute::valueMemoryOffset()), expectedValueRegister);
+
+        // If the element is an HTML element, in a HTML dcoument (not including XHTML), value matching is case insensitive.
+        // Taking the contrapositive, if we find the element is not HTML or is not in a HTML document, the condition above
+        // sould be sufficient and we can fail early.
+        failureCases.append(testIsHTMLFlagOnNode(Assembler::Zero, m_assembler, elementAddressRegister));
+
+        {
+            LocalRegister scratchRegister(m_registerAllocator);
+            // scratchRegister = pointer to treeScope.
+            m_assembler.loadPtr(Assembler::Address(elementAddressRegister, Node::treeScopeMemoryOffset()), scratchRegister);
+            // scratchRegister = pointer to document.
+            m_assembler.loadPtr(Assembler::Address(scratchRegister, TreeScope::documentScopeMemoryOffset()), scratchRegister);
+            failureCases.append(testIsHTMLClassOnDocument(Assembler::Zero, m_assembler, scratchRegister));
+        }
+
+        LocalRegister valueStringImpl(m_registerAllocator);
+        m_assembler.loadPtr(Assembler::Address(currentAttributeAddress, Attribute::valueMemoryOffset()), valueStringImpl);
+
+        FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+        functionCall.setFunctionAddress(WTF::equalIgnoringCaseNonNull);
+        functionCall.setTwoArguments(expectedValueRegister, valueStringImpl);
+        failureCases.append(functionCall.callAndBranchOnCondition(Assembler::Zero));
+
+        skipCaseInsensitiveComparison.link(&amp;m_assembler);
+    }
+}
+
</ins><span class="cx"> void SelectorCodeGenerator::generateElementFunctionCallTest(Assembler::JumpList&amp; failureCases, JSC::FunctionPtr testFunction)
</span><span class="cx"> {
</span><span class="cx">     Assembler::RegisterID elementAddress = elementAddressRegister;
</span><span class="cx">     FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
</span><span class="cx">     functionCall.setFunctionAddress(testFunction);
</span><del>-    functionCall.setFirstArgument(elementAddress);
</del><ins>+    functionCall.setOneArgument(elementAddress);
</ins><span class="cx">     failureCases.append(functionCall.callAndBranchOnCondition(Assembler::Zero));
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomAttributeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Attribute.h (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Attribute.h        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/Source/WebCore/dom/Attribute.h        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -44,6 +44,7 @@
</span><span class="cx">     // as the Attribute stays in place. For example, calling a function that mutates
</span><span class="cx">     // an Element's internal attribute storage may invalidate them.
</span><span class="cx">     const AtomicString&amp; value() const { return m_value; }
</span><ins>+    static ptrdiff_t valueMemoryOffset() { return OBJECT_OFFSETOF(Attribute, m_value); }
</ins><span class="cx">     const AtomicString&amp; prefix() const { return m_name.prefix(); }
</span><span class="cx">     const AtomicString&amp; localName() const { return m_name.localName(); }
</span><span class="cx">     const AtomicString&amp; namespaceURI() const { return m_name.namespaceURI(); }
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.h        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/Source/WebCore/dom/Document.h        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -518,6 +518,9 @@
</span><span class="cx">     bool hasSVGRootNode() const;
</span><span class="cx">     virtual bool isFrameSet() const { return false; }
</span><span class="cx"> 
</span><ins>+    static ptrdiff_t documentClassesMemoryOffset() { return OBJECT_OFFSETOF(Document, m_documentClasses); }
+    static uint32_t isHTMLDocumentClassFlag() { return HTMLDocumentClass; }
+
</ins><span class="cx">     bool isSrcdocDocument() const { return m_isSrcdocDocument; }
</span><span class="cx"> 
</span><span class="cx">     StyleResolver* styleResolverIfExists() const { return m_styleResolver.get(); }
</span></span></pre></div>
<a id="trunkSourceWebCoredomNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Node.h (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Node.h        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/Source/WebCore/dom/Node.h        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -393,6 +393,7 @@
</span><span class="cx">         ASSERT(m_treeScope);
</span><span class="cx">         return *m_treeScope;
</span><span class="cx">     }
</span><ins>+    static ptrdiff_t treeScopeMemoryOffset() { return OBJECT_OFFSETOF(Node, m_treeScope); }
</ins><span class="cx"> 
</span><span class="cx">     // Returns true if this node is associated with a document and is in its associated document's
</span><span class="cx">     // node tree, false otherwise.
</span></span></pre></div>
<a id="trunkSourceWebCoredomTreeScopeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/TreeScope.h (164851 => 164852)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/TreeScope.h        2014-02-28 05:30:09 UTC (rev 164851)
+++ trunk/Source/WebCore/dom/TreeScope.h        2014-02-28 05:52:33 UTC (rev 164852)
</span><span class="lines">@@ -69,6 +69,7 @@
</span><span class="cx">     void removeElementByName(const AtomicStringImpl&amp;, Element&amp;);
</span><span class="cx"> 
</span><span class="cx">     Document&amp; documentScope() const { return *m_documentScope; }
</span><ins>+    static ptrdiff_t documentScopeMemoryOffset() { return OBJECT_OFFSETOF(TreeScope, m_documentScope); }
</ins><span class="cx"> 
</span><span class="cx">     Node* ancestorInThisScope(Node*) const;
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>