<!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>[170121] 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/170121">170121</a></dd>
<dt>Author</dt> <dd>benjamin@webkit.org</dd>
<dt>Date</dt> <dd>2014-06-18 15:10:36 -0700 (Wed, 18 Jun 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Subtrees with :first-child and :last-child are not invalidated when siblings are added/removed
https://bugs.webkit.org/show_bug.cgi?id=133934

Reviewed by Antti Koivisto.


Source/WebCore: 
When adding/removing nodes on an element, we try to invalidate only the elements that are
affected. In the case of :first-child and :last-child, that optimizations is implemented
through two types of flags that are updated during style resolution.

The first flag is childrenAffectedByFirstChildRules (childrenAffectedByLastChildRules),
set on the parent of any element that could be affected by :first-child (:last-child).

The other part of the optimization is marking the style itself with firstChildState (lastChildState)
to further reduce invalidations.

The problem in this case happen with a subtree of element is detached. Since there is no renderer,
the computed style is resolved ad-hoc and stored directly on the element. When the element is moved,
the computed style was never cleared because the invalidation optimizations were not handling
elements without style.

Credit to Yusuke Suzuki for discovering the issue and creating test cases.

Tests: fast/css/getComputedStyle/empty-update-without-renderer.html
       fast/css/getComputedStyle/first-child-update-without-renderer.html
       fast/css/getComputedStyle/last-child-update-without-renderer.html

* dom/Element.cpp:
(WebCore::checkForEmptyStyleChange):
Clean up: pull the style directly from the function instead of expection the call sites to do that.
Refine the checks to avoid invalidation.

(WebCore::checkForSiblingStyleChanges):
Do not early return if the parent is detached, the children may still need invalidation.

When there is no renderer, assume the worst first-child/last-child and force the invalidation.

(WebCore::Element::childrenChanged):

LayoutTests: 
* fast/css/getComputedStyle/empty-update-without-renderer-expected.txt: Added.
* fast/css/getComputedStyle/empty-update-without-renderer.html: Added.
* fast/css/getComputedStyle/first-child-update-without-renderer-expected.txt: Added.
* fast/css/getComputedStyle/first-child-update-without-renderer.html: Added.
* fast/css/getComputedStyle/last-child-update-without-renderer-expected.txt: Added.
* fast/css/getComputedStyle/last-child-update-without-renderer.html: Added.</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="#trunkSourceWebCoredomElementcpp">trunk/Source/WebCore/dom/Element.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastcssgetComputedStyleemptyupdatewithoutrendererexpectedtxt">trunk/LayoutTests/fast/css/getComputedStyle/empty-update-without-renderer-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastcssgetComputedStyleemptyupdatewithoutrendererhtml">trunk/LayoutTests/fast/css/getComputedStyle/empty-update-without-renderer.html</a></li>
<li><a href="#trunkLayoutTestsfastcssgetComputedStylefirstchildupdatewithoutrendererexpectedtxt">trunk/LayoutTests/fast/css/getComputedStyle/first-child-update-without-renderer-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastcssgetComputedStylefirstchildupdatewithoutrendererhtml">trunk/LayoutTests/fast/css/getComputedStyle/first-child-update-without-renderer.html</a></li>
<li><a href="#trunkLayoutTestsfastcssgetComputedStylelastchildupdatewithoutrendererexpectedtxt">trunk/LayoutTests/fast/css/getComputedStyle/last-child-update-without-renderer-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastcssgetComputedStylelastchildupdatewithoutrendererhtml">trunk/LayoutTests/fast/css/getComputedStyle/last-child-update-without-renderer.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (170120 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-06-18 21:52:45 UTC (rev 170120)
+++ trunk/LayoutTests/ChangeLog        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2014-06-18  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        Subtrees with :first-child and :last-child are not invalidated when siblings are added/removed
+        https://bugs.webkit.org/show_bug.cgi?id=133934
+
+        Reviewed by Antti Koivisto.
+
+        * fast/css/getComputedStyle/empty-update-without-renderer-expected.txt: Added.
+        * fast/css/getComputedStyle/empty-update-without-renderer.html: Added.
+        * fast/css/getComputedStyle/first-child-update-without-renderer-expected.txt: Added.
+        * fast/css/getComputedStyle/first-child-update-without-renderer.html: Added.
+        * fast/css/getComputedStyle/last-child-update-without-renderer-expected.txt: Added.
+        * fast/css/getComputedStyle/last-child-update-without-renderer.html: Added.
+
</ins><span class="cx"> 2014-06-18  Daniel Bates  &lt;dabates@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION (r167856): Unable to log into HSBC app
</span></span></pre></div>
<a id="trunkLayoutTestsfastcssgetComputedStyleemptyupdatewithoutrendererexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/css/getComputedStyle/empty-update-without-renderer-expected.txt (0 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/getComputedStyle/empty-update-without-renderer-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/css/getComputedStyle/empty-update-without-renderer-expected.txt        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -0,0 +1,11 @@
</span><ins>+When updating the tree, the style needs to be invalidated when the :empty state, even if there is no renderer.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS getComputedStyle(document.getElementById(&quot;target&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target&quot;)).color is &quot;rgb(1, 2, 3)&quot;
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastcssgetComputedStyleemptyupdatewithoutrendererhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/css/getComputedStyle/empty-update-without-renderer.html (0 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/getComputedStyle/empty-update-without-renderer.html                                (rev 0)
+++ trunk/LayoutTests/fast/css/getComputedStyle/empty-update-without-renderer.html        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -0,0 +1,32 @@
</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;
+target {
+    color:rgb(0, 0, 0);
+}
+target:empty {
+    color:rgb(1, 2, 3);
+}
+&lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;div style=&quot;display:none&quot;&gt;
+    &lt;target id=target&gt;
+        &lt;filling&gt;&lt;/filling&gt;
+    &lt;/target&gt;
+&lt;/div&gt;
+&lt;/body&gt;
+&lt;script&gt;
+description(&quot;When updating the tree, the style needs to be invalidated when the :empty state, even if there is no renderer.&quot;);
+
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target&quot;)).color', 'rgb(0, 0, 0)');
+
+var target = document.getElementById('target');
+while (target.firstChild)
+    target.removeChild(target.firstChild);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target&quot;)).color', 'rgb(1, 2, 3)');
+&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="trunkLayoutTestsfastcssgetComputedStylefirstchildupdatewithoutrendererexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/css/getComputedStyle/first-child-update-without-renderer-expected.txt (0 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/getComputedStyle/first-child-update-without-renderer-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/css/getComputedStyle/first-child-update-without-renderer-expected.txt        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -0,0 +1,47 @@
</span><ins>+When updating the tree, the style needs to be invalidated when the :first-child changes, even if there is no renderer.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+Base case for rightmost element with :first-child
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).color is &quot;rgb(1, 2, 3)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+Removed first child (target1)
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).color is &quot;rgb(1, 2, 3)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+Add back target1 at the end
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).color is &quot;rgb(1, 2, 3)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+Base case for a styled element with an ancestor affected by :first-child
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).color is &quot;rgb(7, 8, 9)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+Removed first child (target4's parent)
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).color is &quot;rgb(7, 8, 9)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+Add back target4's parent at the end
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).color is &quot;rgb(7, 8, 9)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastcssgetComputedStylefirstchildupdatewithoutrendererhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/css/getComputedStyle/first-child-update-without-renderer.html (0 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/getComputedStyle/first-child-update-without-renderer.html                                (rev 0)
+++ trunk/LayoutTests/fast/css/getComputedStyle/first-child-update-without-renderer.html        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -0,0 +1,110 @@
</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;
+rightmost .target {
+    color:rgb(0,0,0);
+    background-color:rgb(0, 0, 0);
+}
+rightmost .target:first-child {
+    color:rgb(1,2,3);
+}
+rightmost .target:not(:first-child) {
+    background-color:rgb(4, 5, 6);
+}
+
+ancestor .target {
+    color:rgb(255, 255, 255);
+    background-color:rgb(255, 255, 255);
+}
+ancestor .parent:first-child .target {
+    color:rgb(7, 8, 9);
+}
+ancestor .parent:not(:first-child) .target {
+    background-color:rgb(10, 11, 12);
+}
+&lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;div style=&quot;display:none&quot;&gt;
+    &lt;rightmost&gt;
+        &lt;span id=&quot;target1&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;span id=&quot;target2&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;span id=&quot;target3&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+    &lt;/rightmost&gt;
+    &lt;ancestor&gt;
+        &lt;span class=&quot;parent&quot;&gt;
+            &lt;span id=&quot;target4&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;/span&gt;
+        &lt;span class=&quot;parent&quot;&gt;
+            &lt;span id=&quot;target5&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;/span&gt;
+        &lt;span class=&quot;parent&quot;&gt;
+            &lt;span id=&quot;target6&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;/span&gt;
+    &lt;/ancestor&gt;
+&lt;/div&gt;
+&lt;/body&gt;
+&lt;script&gt;
+description(&quot;When updating the tree, the style needs to be invalidated when the :first-child changes, even if there is no renderer.&quot;);
+
+debug(&quot;Base case for rightmost element with :first-child&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).color', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+
+
+var target1 = document.getElementById(&quot;target1&quot;);
+var parentElement = target1.parentElement;
+parentElement.removeChild(target1);
+
+debug(&quot;Removed first child (target1)&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).color', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+
+parentElement.appendChild(target1);
+
+debug(&quot;Add back target1 at the end&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).color', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+
+
+debug(&quot;Base case for a styled element with an ancestor affected by :first-child&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).color', 'rgb(7, 8, 9)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+
+var target4Parent = document.getElementById(&quot;target4&quot;).parentElement;
+var parentElement = target4Parent.parentElement;
+parentElement.removeChild(target4Parent);
+
+debug(&quot;Removed first child (target4's parent)&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).color', 'rgb(7, 8, 9)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+
+debug(&quot;Add back target4's parent at the end&quot;);
+parentElement.appendChild(target4Parent);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).color', 'rgb(7, 8, 9)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+&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="trunkLayoutTestsfastcssgetComputedStylelastchildupdatewithoutrendererexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/css/getComputedStyle/last-child-update-without-renderer-expected.txt (0 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/getComputedStyle/last-child-update-without-renderer-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/css/getComputedStyle/last-child-update-without-renderer-expected.txt        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -0,0 +1,47 @@
</span><ins>+When updating the tree, the style needs to be invalidated when the :last-child changes, even if there is no renderer.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+Base case for rightmost element with :last-child
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).color is &quot;rgb(1, 2, 3)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor is &quot;rgb(0, 0, 0)&quot;
+Removed first child (target3)
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).color is &quot;rgb(1, 2, 3)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor is &quot;rgb(0, 0, 0)&quot;
+Add back target3 at the start
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).color is &quot;rgb(0, 0, 0)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).color is &quot;rgb(1, 2, 3)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor is &quot;rgb(4, 5, 6)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor is &quot;rgb(0, 0, 0)&quot;
+Base case for a styled element with an ancestor affected by :last-child
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).color is &quot;rgb(7, 8, 9)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor is &quot;rgb(255, 255, 255)&quot;
+Removed last child (target6's parent)
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).color is &quot;rgb(7, 8, 9)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor is &quot;rgb(255, 255, 255)&quot;
+Add back target6's parent at the end
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).color is &quot;rgb(255, 255, 255)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).color is &quot;rgb(7, 8, 9)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor is &quot;rgb(10, 11, 12)&quot;
+PASS getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor is &quot;rgb(255, 255, 255)&quot;
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastcssgetComputedStylelastchildupdatewithoutrendererhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/css/getComputedStyle/last-child-update-without-renderer.html (0 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/getComputedStyle/last-child-update-without-renderer.html                                (rev 0)
+++ trunk/LayoutTests/fast/css/getComputedStyle/last-child-update-without-renderer.html        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -0,0 +1,108 @@
</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;
+rightmost .target {
+    color:rgb(0,0,0);
+    background-color:rgb(0, 0, 0);
+}
+rightmost .target:last-child {
+    color:rgb(1,2,3);
+}
+rightmost .target:not(:last-child) {
+    background-color:rgb(4, 5, 6);
+}
+
+ancestor .target {
+    color:rgb(255, 255, 255);
+    background-color:rgb(255, 255, 255);
+}
+ancestor .parent:last-child .target {
+    color:rgb(7, 8, 9);
+}
+ancestor .parent:not(:last-child) .target {
+    background-color:rgb(10, 11, 12);
+}
+&lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;div style=&quot;display:none&quot;&gt;
+    &lt;rightmost&gt;
+        &lt;span id=&quot;target1&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;span id=&quot;target2&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;span id=&quot;target3&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+    &lt;/rightmost&gt;
+    &lt;ancestor&gt;
+        &lt;span class=&quot;parent&quot;&gt;
+            &lt;span id=&quot;target4&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;/span&gt;
+        &lt;span class=&quot;parent&quot;&gt;
+            &lt;span id=&quot;target5&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;/span&gt;
+        &lt;span class=&quot;parent&quot;&gt;
+            &lt;span id=&quot;target6&quot; class=&quot;target&quot;&gt;&lt;/span&gt;
+        &lt;/span&gt;
+    &lt;/ancestor&gt;
+&lt;/div&gt;
+&lt;/body&gt;
+&lt;script&gt;
+description(&quot;When updating the tree, the style needs to be invalidated when the :last-child changes, even if there is no renderer.&quot;);
+
+debug(&quot;Base case for rightmost element with :last-child&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).color', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor', 'rgb(0, 0, 0)');
+
+
+var target3 = document.getElementById(&quot;target3&quot;);
+var parentElement = target1.parentElement;
+parentElement.removeChild(target3);
+
+debug(&quot;Removed first child (target3)&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).color', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor', 'rgb(0, 0, 0)');
+
+parentElement.insertBefore(target3, parentElement.firstChild);
+
+debug(&quot;Add back target3 at the start&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).color', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target3&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target1&quot;)).backgroundColor', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target2&quot;)).backgroundColor', 'rgb(0, 0, 0)');
+
+debug(&quot;Base case for a styled element with an ancestor affected by :last-child&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).color', 'rgb(7, 8, 9)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor', 'rgb(255, 255, 255)');
+
+var target6Parent = document.getElementById(&quot;target6&quot;).parentElement;
+var parentElement = target6Parent.parentElement;
+parentElement.removeChild(target6Parent);
+
+debug(&quot;Removed last child (target6's parent)&quot;);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).color', 'rgb(7, 8, 9)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor', 'rgb(255, 255, 255)');
+debug(&quot;Add back target6's parent at the end&quot;);
+parentElement.insertBefore(target6Parent, parentElement.firstChild);
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).color', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).color', 'rgb(7, 8, 9)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target6&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target4&quot;)).backgroundColor', 'rgb(10, 11, 12)');
+shouldBeEqualToString('getComputedStyle(document.getElementById(&quot;target5&quot;)).backgroundColor', 'rgb(255, 255, 255)');
+&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 (170120 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-06-18 21:52:45 UTC (rev 170120)
+++ trunk/Source/WebCore/ChangeLog        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -1,3 +1,43 @@
</span><ins>+2014-06-18  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        Subtrees with :first-child and :last-child are not invalidated when siblings are added/removed
+        https://bugs.webkit.org/show_bug.cgi?id=133934
+
+        Reviewed by Antti Koivisto.
+
+        When adding/removing nodes on an element, we try to invalidate only the elements that are
+        affected. In the case of :first-child and :last-child, that optimizations is implemented
+        through two types of flags that are updated during style resolution.
+
+        The first flag is childrenAffectedByFirstChildRules (childrenAffectedByLastChildRules),
+        set on the parent of any element that could be affected by :first-child (:last-child).
+
+        The other part of the optimization is marking the style itself with firstChildState (lastChildState)
+        to further reduce invalidations.
+
+        The problem in this case happen with a subtree of element is detached. Since there is no renderer,
+        the computed style is resolved ad-hoc and stored directly on the element. When the element is moved,
+        the computed style was never cleared because the invalidation optimizations were not handling
+        elements without style.
+
+        Credit to Yusuke Suzuki for discovering the issue and creating test cases.
+
+        Tests: fast/css/getComputedStyle/empty-update-without-renderer.html
+               fast/css/getComputedStyle/first-child-update-without-renderer.html
+               fast/css/getComputedStyle/last-child-update-without-renderer.html
+
+        * dom/Element.cpp:
+        (WebCore::checkForEmptyStyleChange):
+        Clean up: pull the style directly from the function instead of expection the call sites to do that.
+        Refine the checks to avoid invalidation.
+
+        (WebCore::checkForSiblingStyleChanges):
+        Do not early return if the parent is detached, the children may still need invalidation.
+
+        When there is no renderer, assume the worst first-child/last-child and force the invalidation.
+
+        (WebCore::Element::childrenChanged):
+
</ins><span class="cx"> 2014-06-18  Daniel Bates  &lt;dabates@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION (r167856): Unable to log into HSBC app
</span></span></pre></div>
<a id="trunkSourceWebCoredomElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Element.cpp (170120 => 170121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Element.cpp        2014-06-18 21:52:45 UTC (rev 170120)
+++ trunk/Source/WebCore/dom/Element.cpp        2014-06-18 22:10:36 UTC (rev 170121)
</span><span class="lines">@@ -1544,24 +1544,23 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void checkForEmptyStyleChange(Element* element, RenderStyle* style)
</del><ins>+static void checkForEmptyStyleChange(Element&amp; element)
</ins><span class="cx"> {
</span><del>-    if (!style &amp;&amp; !element-&gt;styleAffectedByEmpty())
-        return;
-
-    if (!style || (element-&gt;styleAffectedByEmpty() &amp;&amp; (!style-&gt;emptyState() || element-&gt;hasChildNodes())))
-        element-&gt;setNeedsStyleRecalc();
</del><ins>+    if (element.styleAffectedByEmpty()) {
+        RenderStyle* style = element.renderStyle();
+        if (!style || (!style-&gt;emptyState() || element.hasChildNodes()))
+            element.setNeedsStyleRecalc();
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> enum SiblingCheckType { FinishedParsingChildren, SiblingElementRemoved, Other };
</span><span class="cx"> 
</span><span class="cx"> static void checkForSiblingStyleChanges(Element* parent, SiblingCheckType checkType, Element* elementBeforeChange, Element* elementAfterChange)
</span><span class="cx"> {
</span><del>-    RenderStyle* style = parent-&gt;renderStyle();
</del><span class="cx">     // :empty selector.
</span><del>-    checkForEmptyStyleChange(parent, style);
</del><ins>+    checkForEmptyStyleChange(*parent);
</ins><span class="cx">     
</span><del>-    if (!style || (parent-&gt;needsStyleRecalc() &amp;&amp; parent-&gt;childrenAffectedByPositionalRules()))
</del><ins>+    if (parent-&gt;needsStyleRecalc() &amp;&amp; parent-&gt;childrenAffectedByPositionalRules())
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     // :first-child.  In the parser callback case, we don't have to check anything, since we were right the first time.
</span><span class="lines">@@ -1573,12 +1572,18 @@
</span><span class="cx">         // Find the first element node following |afterChange|
</span><span class="cx"> 
</span><span class="cx">         // This is the insert/append case.
</span><del>-        if (newFirstElement != elementAfterChange &amp;&amp; elementAfterChange-&gt;renderStyle() &amp;&amp; elementAfterChange-&gt;renderStyle()-&gt;firstChildState())
-            elementAfterChange-&gt;setNeedsStyleRecalc();
-            
</del><ins>+        if (newFirstElement != elementAfterChange) {
+            RenderStyle* style = elementAfterChange-&gt;renderStyle();
+            if (!style || style-&gt;firstChildState())
+                elementAfterChange-&gt;setNeedsStyleRecalc();
+        }
+
</ins><span class="cx">         // We also have to handle node removal.
</span><del>-        if (checkType == SiblingElementRemoved &amp;&amp; newFirstElement == elementAfterChange &amp;&amp; newFirstElement &amp;&amp; (!newFirstElement-&gt;renderStyle() || !newFirstElement-&gt;renderStyle()-&gt;firstChildState()))
-            newFirstElement-&gt;setNeedsStyleRecalc();
</del><ins>+        if (checkType == SiblingElementRemoved &amp;&amp; newFirstElement == elementAfterChange &amp;&amp; newFirstElement) {
+            RenderStyle* style = newFirstElement-&gt;renderStyle();
+            if (!style || !style-&gt;firstChildState())
+                newFirstElement-&gt;setNeedsStyleRecalc();
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // :last-child.  In the parser callback case, we don't have to check anything, since we were right the first time.
</span><span class="lines">@@ -1587,21 +1592,25 @@
</span><span class="cx">         // Find our new last child.
</span><span class="cx">         Element* newLastElement = ElementTraversal::lastChild(parent);
</span><span class="cx"> 
</span><del>-        if (newLastElement != elementBeforeChange &amp;&amp; elementBeforeChange-&gt;renderStyle() &amp;&amp; elementBeforeChange-&gt;renderStyle()-&gt;lastChildState())
-            elementBeforeChange-&gt;setNeedsStyleRecalc();
-            
</del><ins>+        if (newLastElement != elementBeforeChange) {
+            RenderStyle* style = elementBeforeChange-&gt;renderStyle();
+            if (!style || style-&gt;lastChildState())
+                elementBeforeChange-&gt;setNeedsStyleRecalc();
+        }
+
</ins><span class="cx">         // We also have to handle node removal.  The parser callback case is similar to node removal as well in that we need to change the last child
</span><span class="cx">         // to match now.
</span><del>-        if ((checkType == SiblingElementRemoved || checkType == FinishedParsingChildren) &amp;&amp; newLastElement == elementBeforeChange &amp;&amp; newLastElement
-            &amp;&amp; (!newLastElement-&gt;renderStyle() || !newLastElement-&gt;renderStyle()-&gt;lastChildState()))
-            newLastElement-&gt;setNeedsStyleRecalc();
</del><ins>+        if ((checkType == SiblingElementRemoved || checkType == FinishedParsingChildren) &amp;&amp; newLastElement == elementBeforeChange &amp;&amp; newLastElement) {
+            RenderStyle* style = newLastElement-&gt;renderStyle();
+            if (!style || !style-&gt;lastChildState())
+                newLastElement-&gt;setNeedsStyleRecalc();
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // The + selector.  We need to invalidate the first element following the insertion point.  It is the only possible element
</span><span class="cx">     // that could be affected by this DOM change.
</span><del>-    if (parent-&gt;childrenAffectedByDirectAdjacentRules() &amp;&amp; elementAfterChange) {
</del><ins>+    if (parent-&gt;childrenAffectedByDirectAdjacentRules() &amp;&amp; elementAfterChange)
</ins><span class="cx">         elementAfterChange-&gt;setNeedsStyleRecalc();
</span><del>-    }
</del><span class="cx"> 
</span><span class="cx">     // Forward positional selectors include the ~ selector, nth-child, nth-of-type, first-of-type and only-of-type.
</span><span class="cx">     // Backward positional selectors include nth-last-child, nth-last-of-type, last-of-type and only-of-type.
</span><span class="lines">@@ -1620,7 +1629,7 @@
</span><span class="cx"> {
</span><span class="cx">     ContainerNode::childrenChanged(change);
</span><span class="cx">     if (change.source == ChildChangeSourceParser)
</span><del>-        checkForEmptyStyleChange(this, renderStyle());
</del><ins>+        checkForEmptyStyleChange(*this);
</ins><span class="cx">     else {
</span><span class="cx">         SiblingCheckType checkType = change.type == ElementRemoved ? SiblingElementRemoved : Other;
</span><span class="cx">         checkForSiblingStyleChanges(this, checkType, change.previousSiblingElement, change.nextSiblingElement);
</span></span></pre>
</div>
</div>

</body>
</html>