<!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>[204322] 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/204322">204322</a></dd>
<dt>Author</dt> <dd>cdumez@apple.com</dd>
<dt>Date</dt> <dd>2016-08-09 20:47:58 -0700 (Tue, 09 Aug 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Optimization in Node::appendChild() is not spec-compliant
https://bugs.webkit.org/show_bug.cgi?id=160728

Reviewed by Ryosuke Niwa.

LayoutTests/imported/w3c:

Rebaseline W3C test now that more checks are passing.

* web-platform-tests/dom/ranges/Range-mutations-expected.txt:

Source/WebCore:

We have an optimization in Node::appendChild() that avoid doing any work
if the node to be appended is already the last child. This optimization
is not spec-compliant:
- https://dom.spec.whatwg.org/#dom-node-appendchild

The issue is that this is observable via DOM Mutation observers / listeners
or DOM ranges.

To address the issue, this patch drops the optimization in appendChild().
This seems like an odd case to optimize for as I am not convinced it is
common to call parent.appendChild(parent.lastChild). If it turns out to
regress the performance of things we care about though, we could fall
back to do the optimization only when it is not observable.

Test: fast/dom/Node/appendChild-no-op-mutationobserver.html

* dom/ContainerNode.cpp:
(WebCore::ContainerNode::appendChild): Deleted.

LayoutTests:

Add layout test to make sure that mutation events are always fired
when calling Node::appendChild(), even if the new node is already
the last child.

* fast/dom/Node/appendChild-no-op-mutationobserver-expected.txt: Added.
* fast/dom/Node/appendChild-no-op-mutationobserver.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsimportedw3cChangeLog">trunk/LayoutTests/imported/w3c/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestsdomrangesRangemutationsexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/dom/ranges/Range-mutations-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomContainerNodecpp">trunk/Source/WebCore/dom/ContainerNode.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastdomNodeappendChildnoopmutationobserverexpectedtxt">trunk/LayoutTests/fast/dom/Node/appendChild-no-op-mutationobserver-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastdomNodeappendChildnoopmutationobserverhtml">trunk/LayoutTests/fast/dom/Node/appendChild-no-op-mutationobserver.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (204321 => 204322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-08-10 03:39:05 UTC (rev 204321)
+++ trunk/LayoutTests/ChangeLog        2016-08-10 03:47:58 UTC (rev 204322)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2016-08-09  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Optimization in Node::appendChild() is not spec-compliant
+        https://bugs.webkit.org/show_bug.cgi?id=160728
+
+        Reviewed by Ryosuke Niwa.
+
+        Add layout test to make sure that mutation events are always fired
+        when calling Node::appendChild(), even if the new node is already
+        the last child.
+
+        * fast/dom/Node/appendChild-no-op-mutationobserver-expected.txt: Added.
+        * fast/dom/Node/appendChild-no-op-mutationobserver.html: Added.
+
</ins><span class="cx"> 2016-08-09  Saam Barati  &lt;sbarati@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         JSBoundFunction should lazily generate its name string
</span></span></pre></div>
<a id="trunkLayoutTestsfastdomNodeappendChildnoopmutationobserverexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/dom/Node/appendChild-no-op-mutationobserver-expected.txt (0 => 204322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/dom/Node/appendChild-no-op-mutationobserver-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/dom/Node/appendChild-no-op-mutationobserver-expected.txt        2016-08-10 03:47:58 UTC (rev 204322)
</span><span class="lines">@@ -0,0 +1,11 @@
</span><ins>+Tests that calls of appendChild() lead to mutation events even if they are no-ops.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS Mutation observer was notified
+PASS wasMutationEventListenerCalled is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastdomNodeappendChildnoopmutationobserverhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/dom/Node/appendChild-no-op-mutationobserver.html (0 => 204322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/dom/Node/appendChild-no-op-mutationobserver.html                                (rev 0)
+++ trunk/LayoutTests/fast/dom/Node/appendChild-no-op-mutationobserver.html        2016-08-10 03:47:58 UTC (rev 204322)
</span><span class="lines">@@ -0,0 +1,40 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;div id=&quot;testDiv&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;
+&lt;script&gt;
+description(&quot;Tests that calls of appendChild() lead to mutation events even if they are no-ops.&quot;);
+jsTestIsAsync = true;
+
+var testDiv = document.getElementById(&quot;testDiv&quot;);
+
+function testMutationObserver()
+{
+    var config = { childList: true };
+    var observer = new MutationObserver(function(mutations) {
+        testPassed(&quot;Mutation observer was notified&quot;);
+        observer.disconnect();
+        testMutationEventListener();
+    });
+
+    observer.observe(testDiv, config);
+    testDiv.appendChild(testDiv.lastChild);
+}
+
+function testMutationEventListener()
+{
+    wasMutationEventListenerCalled = false;
+    testDiv.addEventListener(&quot;DOMSubtreeModified&quot;, function() {
+        wasMutationEventListenerCalled = true;
+    });
+    testDiv.appendChild(testDiv.lastChild);
+    shouldBeTrue(&quot;wasMutationEventListenerCalled&quot;);
+    finishJSTest();
+}
+
+testMutationObserver();
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/ChangeLog (204321 => 204322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/ChangeLog        2016-08-10 03:39:05 UTC (rev 204321)
+++ trunk/LayoutTests/imported/w3c/ChangeLog        2016-08-10 03:47:58 UTC (rev 204322)
</span><span class="lines">@@ -1,5 +1,16 @@
</span><span class="cx"> 2016-08-09  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><ins>+        Optimization in Node::appendChild() is not spec-compliant
+        https://bugs.webkit.org/show_bug.cgi?id=160728
+
+        Reviewed by Ryosuke Niwa.
+
+        Rebaseline W3C test now that more checks are passing.
+
+        * web-platform-tests/dom/ranges/Range-mutations-expected.txt:
+
+2016-08-09  Chris Dumez  &lt;cdumez@apple.com&gt;
+
</ins><span class="cx">         CharacterData.data setter optimization is not spec-compliant and is observable
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=160712
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestsdomrangesRangemutationsexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/dom/ranges/Range-mutations-expected.txt (204321 => 204322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/dom/ranges/Range-mutations-expected.txt        2016-08-10 03:39:05 UTC (rev 204321)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/dom/ranges/Range-mutations-expected.txt        2016-08-10 03:47:58 UTC (rev 204322)
</span><span class="lines">@@ -5706,40 +5706,37 @@
</span><span class="cx"> PASS paras[0].replaceChild(document.doctype, paras[0].firstChild), with unselected range on paras[0] from 0 to 1 
</span><span class="cx"> FAIL paras[0].replaceChild(document.doctype, paras[0].firstChild), with selected range on paras[0] from 0 to 1 assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;Äb̈c̈d̈ëf̈g̈ḧ
</span><span class="cx"> &quot; but got object &quot;Äb̈c̈d̈ëf̈g̈ḧ&quot;
</span><del>-FAIL testDiv.appendChild(testDiv.lastChild), with unselected range collapsed at (testDiv.lastChild, 0) assert_equals: Wrong start container expected Element node &lt;div id=&quot;test&quot;&gt;&lt;p id=&quot;a&quot;&gt;Äb̈c̈d̈ëf̈g̈ḧ
-&lt;/p&gt;&lt;p id=&quot;b&quot; s... but got Comment node &lt;!--Alphabet soup?--&gt;
</del><ins>+PASS testDiv.appendChild(testDiv.lastChild), with unselected range collapsed at (testDiv.lastChild, 0) 
</ins><span class="cx"> FAIL testDiv.appendChild(testDiv.lastChild), with selected range collapsed at (testDiv.lastChild, 0) assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;&quot; but got object &quot;&quot;
</span><del>-FAIL testDiv.appendChild(testDiv.lastChild), with unselected range on testDiv.lastChild from 0 to 1 assert_equals: Wrong start container expected Element node &lt;div id=&quot;test&quot;&gt;&lt;p id=&quot;a&quot;&gt;Äb̈c̈d̈ëf̈g̈ḧ
-&lt;/p&gt;&lt;p id=&quot;b&quot; s... but got Comment node &lt;!--Alphabet soup?--&gt;
</del><ins>+PASS testDiv.appendChild(testDiv.lastChild), with unselected range on testDiv.lastChild from 0 to 1 
</ins><span class="cx"> FAIL testDiv.appendChild(testDiv.lastChild), with selected range on testDiv.lastChild from 0 to 1 assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;&quot; but got object &quot;&quot;
</span><del>-FAIL testDiv.appendChild(testDiv.lastChild), with unselected range collapsed at (testDiv.lastChild, 1) assert_equals: Wrong start container expected Element node &lt;div id=&quot;test&quot;&gt;&lt;p id=&quot;a&quot;&gt;Äb̈c̈d̈ëf̈g̈ḧ
-&lt;/p&gt;&lt;p id=&quot;b&quot; s... but got Comment node &lt;!--Alphabet soup?--&gt;
</del><ins>+PASS testDiv.appendChild(testDiv.lastChild), with unselected range collapsed at (testDiv.lastChild, 1) 
</ins><span class="cx"> FAIL testDiv.appendChild(testDiv.lastChild), with selected range collapsed at (testDiv.lastChild, 1) assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;&quot; but got object &quot;&quot;
</span><del>-FAIL testDiv.appendChild(testDiv.lastChild), with unselected range on testDiv from testDiv.childNodes.length - 2 to testDiv.childNodes.length assert_equals: Wrong end offset expected 5 but got 6
</del><ins>+PASS testDiv.appendChild(testDiv.lastChild), with unselected range on testDiv from testDiv.childNodes.length - 2 to testDiv.childNodes.length 
</ins><span class="cx"> FAIL testDiv.appendChild(testDiv.lastChild), with selected range on testDiv from testDiv.childNodes.length - 2 to testDiv.childNodes.length assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;Ghijklmn&quot; but got object &quot;&quot;
</span><span class="cx"> PASS testDiv.appendChild(testDiv.lastChild), with unselected range on testDiv from testDiv.childNodes.length - 2 to testDiv.childNodes.length - 1 
</span><span class="cx"> FAIL testDiv.appendChild(testDiv.lastChild), with selected range on testDiv from testDiv.childNodes.length - 2 to testDiv.childNodes.length - 1 assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;Ghijklmn&quot; but got object &quot;&quot;
</span><del>-FAIL testDiv.appendChild(testDiv.lastChild), with unselected range on testDiv from testDiv.childNodes.length - 1 to testDiv.childNodes.length assert_equals: Wrong end offset expected 5 but got 6
</del><ins>+PASS testDiv.appendChild(testDiv.lastChild), with unselected range on testDiv from testDiv.childNodes.length - 1 to testDiv.childNodes.length 
</ins><span class="cx"> FAIL testDiv.appendChild(testDiv.lastChild), with selected range on testDiv from testDiv.childNodes.length - 1 to testDiv.childNodes.length assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;&quot; but got object &quot;&quot;
</span><span class="cx"> PASS testDiv.appendChild(testDiv.lastChild), with unselected range collapsed at (testDiv, testDiv.childNodes.length - 1) 
</span><span class="cx"> FAIL testDiv.appendChild(testDiv.lastChild), with selected range collapsed at (testDiv, testDiv.childNodes.length - 1) assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;&quot; but got object &quot;&quot;
</span><del>-FAIL testDiv.appendChild(testDiv.lastChild), with unselected range collapsed at (testDiv, testDiv.childNodes.length) assert_equals: Wrong start offset expected 5 but got 6
</del><ins>+PASS testDiv.appendChild(testDiv.lastChild), with unselected range collapsed at (testDiv, testDiv.childNodes.length) 
</ins><span class="cx"> FAIL testDiv.appendChild(testDiv.lastChild), with selected range collapsed at (testDiv, testDiv.childNodes.length) assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;&quot; but got object &quot;&quot;
</span><del>-FAIL detachedDiv.appendChild(detachedDiv.lastChild), with unselected range collapsed at (detachedDiv.lastChild, 0) assert_equals: Wrong start container expected Element node &lt;div&gt;&lt;p&gt;Opqrstuv&lt;/p&gt;&lt;p&gt;Wxyzabcd&lt;/p&gt;&lt;/div&gt; but got Element node &lt;p&gt;Wxyzabcd&lt;/p&gt;
</del><ins>+PASS detachedDiv.appendChild(detachedDiv.lastChild), with unselected range collapsed at (detachedDiv.lastChild, 0) 
</ins><span class="cx"> FAIL detachedDiv.appendChild(detachedDiv.lastChild), with selected range collapsed at (detachedDiv.lastChild, 0) assert_equals: Sanity check: selection must have exactly one range after adding the range expected 1 but got 0
</span><del>-FAIL detachedDiv.appendChild(detachedDiv.lastChild), with unselected range on detachedDiv.lastChild from 0 to 1 assert_equals: Wrong start container expected Element node &lt;div&gt;&lt;p&gt;Opqrstuv&lt;/p&gt;&lt;p&gt;Wxyzabcd&lt;/p&gt;&lt;/div&gt; but got Element node &lt;p&gt;Wxyzabcd&lt;/p&gt;
</del><ins>+PASS detachedDiv.appendChild(detachedDiv.lastChild), with unselected range on detachedDiv.lastChild from 0 to 1 
</ins><span class="cx"> FAIL detachedDiv.appendChild(detachedDiv.lastChild), with selected range on detachedDiv.lastChild from 0 to 1 assert_equals: Sanity check: selection must have exactly one range after adding the range expected 1 but got 0
</span><del>-FAIL detachedDiv.appendChild(detachedDiv.lastChild), with unselected range collapsed at (detachedDiv.lastChild, 1) assert_equals: Wrong start container expected Element node &lt;div&gt;&lt;p&gt;Opqrstuv&lt;/p&gt;&lt;p&gt;Wxyzabcd&lt;/p&gt;&lt;/div&gt; but got Element node &lt;p&gt;Wxyzabcd&lt;/p&gt;
</del><ins>+PASS detachedDiv.appendChild(detachedDiv.lastChild), with unselected range collapsed at (detachedDiv.lastChild, 1) 
</ins><span class="cx"> FAIL detachedDiv.appendChild(detachedDiv.lastChild), with selected range collapsed at (detachedDiv.lastChild, 1) assert_equals: Sanity check: selection must have exactly one range after adding the range expected 1 but got 0
</span><del>-FAIL detachedDiv.appendChild(detachedDiv.lastChild), with unselected range on detachedDiv from detachedDiv.childNodes.length - 2 to detachedDiv.childNodes.length assert_equals: Wrong end offset expected 1 but got 2
</del><ins>+PASS detachedDiv.appendChild(detachedDiv.lastChild), with unselected range on detachedDiv from detachedDiv.childNodes.length - 2 to detachedDiv.childNodes.length 
</ins><span class="cx"> FAIL detachedDiv.appendChild(detachedDiv.lastChild), with selected range on detachedDiv from detachedDiv.childNodes.length - 2 to detachedDiv.childNodes.length assert_equals: Sanity check: selection must have exactly one range after adding the range expected 1 but got 0
</span><span class="cx"> PASS detachedDiv.appendChild(detachedDiv.lastChild), with unselected range on detachedDiv from detachedDiv.childNodes.length - 2 to detachedDiv.childNodes.length - 1 
</span><span class="cx"> FAIL detachedDiv.appendChild(detachedDiv.lastChild), with selected range on detachedDiv from detachedDiv.childNodes.length - 2 to detachedDiv.childNodes.length - 1 assert_equals: Sanity check: selection must have exactly one range after adding the range expected 1 but got 0
</span><del>-FAIL detachedDiv.appendChild(detachedDiv.lastChild), with unselected range on detachedDiv from detachedDiv.childNodes.length - 1 to detachedDiv.childNodes.length assert_equals: Wrong end offset expected 1 but got 2
</del><ins>+PASS detachedDiv.appendChild(detachedDiv.lastChild), with unselected range on detachedDiv from detachedDiv.childNodes.length - 1 to detachedDiv.childNodes.length 
</ins><span class="cx"> FAIL detachedDiv.appendChild(detachedDiv.lastChild), with selected range on detachedDiv from detachedDiv.childNodes.length - 1 to detachedDiv.childNodes.length assert_equals: Sanity check: selection must have exactly one range after adding the range expected 1 but got 0
</span><span class="cx"> PASS detachedDiv.appendChild(detachedDiv.lastChild), with unselected range collapsed at (detachedDiv, detachedDiv.childNodes.length - 1) 
</span><span class="cx"> FAIL detachedDiv.appendChild(detachedDiv.lastChild), with selected range collapsed at (detachedDiv, detachedDiv.childNodes.length - 1) assert_equals: Sanity check: selection must have exactly one range after adding the range expected 1 but got 0
</span><del>-FAIL detachedDiv.appendChild(detachedDiv.lastChild), with unselected range collapsed at (detachedDiv, detachedDiv.childNodes.length) assert_equals: Wrong start offset expected 1 but got 2
</del><ins>+PASS detachedDiv.appendChild(detachedDiv.lastChild), with unselected range collapsed at (detachedDiv, detachedDiv.childNodes.length) 
</ins><span class="cx"> FAIL detachedDiv.appendChild(detachedDiv.lastChild), with selected range collapsed at (detachedDiv, detachedDiv.childNodes.length) assert_equals: Sanity check: selection must have exactly one range after adding the range expected 1 but got 0
</span><span class="cx"> PASS paras[0].appendChild(paras[1]), with unselected range collapsed at (paras[0], 0) 
</span><span class="cx"> FAIL paras[0].appendChild(paras[1]), with selected range collapsed at (paras[0], 0) assert_equals: Sanity check: selection's range must initially be the same as the range we added expected object &quot;&quot; but got object &quot;&quot;
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (204321 => 204322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-08-10 03:39:05 UTC (rev 204321)
+++ trunk/Source/WebCore/ChangeLog        2016-08-10 03:47:58 UTC (rev 204322)
</span><span class="lines">@@ -1,3 +1,29 @@
</span><ins>+2016-08-09  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Optimization in Node::appendChild() is not spec-compliant
+        https://bugs.webkit.org/show_bug.cgi?id=160728
+
+        Reviewed by Ryosuke Niwa.
+
+        We have an optimization in Node::appendChild() that avoid doing any work
+        if the node to be appended is already the last child. This optimization
+        is not spec-compliant:
+        - https://dom.spec.whatwg.org/#dom-node-appendchild
+
+        The issue is that this is observable via DOM Mutation observers / listeners
+        or DOM ranges.
+
+        To address the issue, this patch drops the optimization in appendChild().
+        This seems like an odd case to optimize for as I am not convinced it is
+        common to call parent.appendChild(parent.lastChild). If it turns out to
+        regress the performance of things we care about though, we could fall
+        back to do the optimization only when it is not observable.
+
+        Test: fast/dom/Node/appendChild-no-op-mutationobserver.html
+
+        * dom/ContainerNode.cpp:
+        (WebCore::ContainerNode::appendChild): Deleted.
+
</ins><span class="cx"> 2016-08-09  Anders Carlsson  &lt;andersca@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Instantiate WebKit plug-ins at layout time, instead of at style resolution time
</span></span></pre></div>
<a id="trunkSourceWebCoredomContainerNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/ContainerNode.cpp (204321 => 204322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/ContainerNode.cpp        2016-08-10 03:39:05 UTC (rev 204321)
+++ trunk/Source/WebCore/dom/ContainerNode.cpp        2016-08-10 03:47:58 UTC (rev 204322)
</span><span class="lines">@@ -648,9 +648,6 @@
</span><span class="cx">     if (!ensurePreInsertionValidity(newChild, nullptr, ec))
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    if (&amp;newChild == m_lastChild) // nothing to do
-        return true;
-
</del><span class="cx">     NodeVector targets;
</span><span class="cx">     collectChildrenAndRemoveFromOldParent(newChild, targets, ec);
</span><span class="cx">     if (ec)
</span></span></pre>
</div>
</div>

</body>
</html>