<!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>[212218] 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/212218">212218</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2017-02-12 20:21:03 -0800 (Sun, 12 Feb 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>parserRemoveChild should unload subframes
https://bugs.webkit.org/show_bug.cgi?id=168151

Reviewed by Darin Adler.

Source/WebCore:

Fix the bug that the adoption agency algorithm does not unload subframes as it disconnects nodes.

Also moved calls to nodeWillBeRemoved inside NoEventDispatchAssertion to expand on <a href="http://trac.webkit.org/projects/webkit/changeset/211965">r211965</a>.

Tests: fast/parser/adoption-agency-clear-focus-range.html
       fast/parser/adoption-agency-unload-iframe-1.html
       fast/parser/adoption-agency-unload-iframe-2.html

* dom/ContainerNode.cpp:
(WebCore::ContainerNode::takeAllChildrenFrom): Rewritten using idioms used in removeChildren and parserAppendChild.

Disconnect all subframes first since this can synchronously dispatch an unload event. Then update DOM ranges,
the focused element, and other states in the document.

Second, use the regular removeBetween, notifyChildNodeRemoved, childrenChanged sequence of calls to disconnect nodes
instead of a single call to removeDetachedChildren to properly disconnect child nodes since those nodes may have
already come live due to execution of synchronous scripts prior to the adoption agency algorithm has run, or in
response to the unload event we just dispatched.

Third, append these nodes using parserAppendChild to avoid dispatching mutation events.

(WebCore::willRemoveChild): Removed the call to nodeWillBeRemoved. It's now called within NoEventDispatchAssertion
in each call site of willRemoveChild and willRemoveChildren.
(WebCore::willRemoveChildren): Ditto.
(WebCore::ContainerNode::removeChild): Call nodeWillBeRemoved inside NoEventDispatchAssertion.
(WebCore::ContainerNode::replaceAllChildren): Call nodeWillBeRemoved inside NoEventDispatchAssertion.
(WebCore::ContainerNode::parserRemoveChild): Disconnect subframes and update document's states.

* html/parser/HTMLConstructionSite.cpp:
(WebCore::executeTakeAllChildrenAndReparentTask): Add a release assert that new parent does not already have a parent. 

LayoutTests:

Add two W3C-style testharness tests for unloading iframes inside the adoption agency algorithm.

Also added a test to make sure ContainerNode::takeAllChildrenFrom adjusts the focused element and DOM ranges.

* fast/css/stylesheet-candidate-nodes-crash-expected.txt: Rebaselined. The difference comes from the fact
iframe now is unloaded in parserRemoveChild as expected and then reloaded in parserAppendChild inside
insertErrorMessageBlock as opposed to after the parser had completed as if the iframe had never been detached.
* fast/parser/adoption-agency-clear-focus-range-expected.txt: Added.
* fast/parser/adoption-agency-clear-focus-range.html: Added.
* fast/parser/adoption-agency-unload-iframe-1-expected.txt: Added.
* fast/parser/adoption-agency-unload-iframe-1.html: Added.
* fast/parser/adoption-agency-unload-iframe-2-expected.txt: Added.
* fast/parser/adoption-agency-unload-iframe-2.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfastcssstylesheetcandidatenodescrashexpectedtxt">trunk/LayoutTests/fast/css/stylesheet-candidate-nodes-crash-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>
<li><a href="#trunkSourceWebCorehtmlparserHTMLConstructionSitecpp">trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastparseradoptionagencyclearfocusrangeexpectedtxt">trunk/LayoutTests/fast/parser/adoption-agency-clear-focus-range-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastparseradoptionagencyclearfocusrangehtml">trunk/LayoutTests/fast/parser/adoption-agency-clear-focus-range.html</a></li>
<li><a href="#trunkLayoutTestsfastparseradoptionagencyunloadiframe1expectedtxt">trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-1-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastparseradoptionagencyunloadiframe1html">trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-1.html</a></li>
<li><a href="#trunkLayoutTestsfastparseradoptionagencyunloadiframe2expectedtxt">trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-2-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastparseradoptionagencyunloadiframe2html">trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-2.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (212217 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-02-13 02:42:20 UTC (rev 212217)
+++ trunk/LayoutTests/ChangeLog        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -1,5 +1,26 @@
</span><span class="cx"> 2017-02-12  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><ins>+        parserRemoveChild should unload subframes
+        https://bugs.webkit.org/show_bug.cgi?id=168151
+
+        Reviewed by Darin Adler.
+
+        Add two W3C-style testharness tests for unloading iframes inside the adoption agency algorithm.
+
+        Also added a test to make sure ContainerNode::takeAllChildrenFrom adjusts the focused element and DOM ranges.
+
+        * fast/css/stylesheet-candidate-nodes-crash-expected.txt: Rebaselined. The difference comes from the fact
+        iframe now is unloaded in parserRemoveChild as expected and then reloaded in parserAppendChild inside
+        insertErrorMessageBlock as opposed to after the parser had completed as if the iframe had never been detached.
+        * fast/parser/adoption-agency-clear-focus-range-expected.txt: Added.
+        * fast/parser/adoption-agency-clear-focus-range.html: Added.
+        * fast/parser/adoption-agency-unload-iframe-1-expected.txt: Added.
+        * fast/parser/adoption-agency-unload-iframe-1.html: Added.
+        * fast/parser/adoption-agency-unload-iframe-2-expected.txt: Added.
+        * fast/parser/adoption-agency-unload-iframe-2.html: Added.
+
+2017-02-12  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
</ins><span class="cx">         REGRESSION (r179497): Crash inside setAttributeNode
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=168161
</span><span class="cx">         &lt;rdar://problem/30451581&gt;
</span></span></pre></div>
<a id="trunkLayoutTestsfastcssstylesheetcandidatenodescrashexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/css/stylesheet-candidate-nodes-crash-expected.txt (212217 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/stylesheet-candidate-nodes-crash-expected.txt        2017-02-13 02:42:20 UTC (rev 212217)
+++ trunk/LayoutTests/fast/css/stylesheet-candidate-nodes-crash-expected.txt        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -1 +1,7 @@
</span><ins>+This page contains the following errors:
+
+error on line 29 at column 9: Double hyphen within comment
+error on line 32 at column 1: Comment not terminated
+Below is a rendering of the page up to the first error.
+
</ins><span class="cx"> PASS
</span></span></pre></div>
<a id="trunkLayoutTestsfastparseradoptionagencyclearfocusrangeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/parser/adoption-agency-clear-focus-range-expected.txt (0 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/parser/adoption-agency-clear-focus-range-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/parser/adoption-agency-clear-focus-range-expected.txt        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+Active Element must be moved to body: PASS
+range.startContainer must be moved to p: PASS
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastparseradoptionagencyclearfocusrangehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/parser/adoption-agency-clear-focus-range.html (0 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/parser/adoption-agency-clear-focus-range.html                                (rev 0)
+++ trunk/LayoutTests/fast/parser/adoption-agency-clear-focus-range.html        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -0,0 +1,84 @@
</span><ins>+&lt;script&gt;
+
+function runTest() {
+    document.write(`&lt;a&gt;&lt;p&gt;&lt;span tabindex=&quot;0&quot;&gt;dummy&lt;/span&gt;&lt;iframe onload=&quot;didLoadIframe()&quot;&gt;&lt;/iframe&gt;&lt;script&gt; document.body.innerHTML = ''; &lt;\/script&gt;`);
+}
+
+let callCount = 0;
+let span;
+let range;
+function didLoadIframe() {
+    callCount++;
+    if (callCount == 1)
+        document.write('&lt;/a&gt;');
+    else if (callCount == 2) {
+        // Test ContainerNode::takeAllChildrenFrom adjusts focused node and update ranges.
+        span = document.querySelector('span');
+        span.focus();
+        range = new Range;
+        range.selectNode(span.firstChild);
+    } else if (callCount == 3) {
+        setTimeout(() =&gt; {
+            let activeElementResult = (document.activeElement == document.body) ? 'PASS' : `FAIL - it was set on ${document.activeElement}`;
+            let rangeStartResult = (range.startContainer == document.querySelector('p')) ? 'PASS' : `FAIL - it was set on ${range.startContainer}`;
+
+            document.documentElement.innerHTML = `&lt;body&gt;
+            Active Element must be moved to body: ${activeElementResult}&lt;br&gt;
+            range.startContainer must be moved to p: ${rangeStartResult}&lt;br&gt;`;
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }, 0);
+    }
+}
+
+/*
+
+ 0. The outer document.write creates the following tree:
+    body
+      + a
+        + p
+          + span
+          + iframe
+
+ 1. iframe's load event fires, and invokes the inner document.write,
+    which forces &lt;script&gt; from the outer document.write to be parsed:
+    body
+      + a
+        + p
+          + span
+          + iframe
+          + script
+
+   1.a. The script runs, and invokes document.body.innerHTML = '':
+        body
+
+   1.b. The adoption agency algorithm is involved, and p is removed from a
+        while both of them are detached from body.
+
+   1.c. The adoption agency algorithm inserts p back into body along with iframe.
+        body
+          + p
+            + span
+            + iframe
+            + script
+
+ 2. iframe's load event fires for the second time per step 1.c.
+
+    2.a. the focus is set on span and a range is created inside span.
+
+    2.b. The adoption agency algorithm now takes all children of p
+         and inserts them back under &quot;a&quot;, and then inserts &quot;p&quot; under body.
+
+3. iframe's load event fires for the third time per step 2.b.
+
+*/
+
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+runTest();
+
+&lt;/script&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsfastparseradoptionagencyunloadiframe1expectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-1-expected.txt (0 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-1-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-1-expected.txt        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+
+PASS An iframe removed by the adoption agency algorithm must be unloaded 
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastparseradoptionagencyunloadiframe1html"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-1.html (0 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-1.html                                (rev 0)
+++ trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-1.html        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -0,0 +1,38 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../resources/testharness.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;table&gt;&lt;a&gt;&lt;p&gt;&lt;script&gt;
+
+let oldBody = document.body;
+oldBody.remove();
+
+let a = oldBody.querySelector('a');
+document.documentElement.appendChild(a);
+/* html
+     + a
+       + p
+         + script */
+
+let iframe = document.createElement('iframe');
+a.firstChild.appendChild(iframe);
+/* html
+     + a
+       + p
+         + script
+         + iframe */
+
+let oldGlobal = iframe.contentWindow;
+
+window.onload = () =&gt; {
+    document.documentElement.appendChild(document.createElement('body'));
+    let test = async_test('An iframe removed by the adoption agency algorithm must be unloaded');
+    test.step(() =&gt; {
+        assert_not_equals(oldGlobal, iframe.contentWindow);
+    });
+    test.done();
+}
+
+&lt;/script&gt;&lt;/a&gt;&lt;/p&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsfastparseradoptionagencyunloadiframe2expectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-2-expected.txt (0 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-2-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-2-expected.txt        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+
+PASS An iframe removed by the adoption agency algorithm must be unloaded 
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastparseradoptionagencyunloadiframe2html"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-2.html (0 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-2.html                                (rev 0)
+++ trunk/LayoutTests/fast/parser/adoption-agency-unload-iframe-2.html        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -0,0 +1,38 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../resources/testharness.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;b&gt;&lt;p&gt;&lt;script&gt;
+
+let oldBody = document.body;
+oldBody.remove();
+
+let b = oldBody.querySelector('b');
+document.documentElement.appendChild(b);
+/* html
+     + b
+       + p
+         + script */
+
+let iframe = document.createElement('iframe');
+b.firstChild.appendChild(iframe);
+/* html
+     + b
+       + p
+         + script
+         + iframe */
+
+let oldGlobal = iframe.contentWindow;
+
+window.onload = () =&gt; {
+    document.documentElement.appendChild(document.createElement('body'));
+    let test = async_test('An iframe removed by the adoption agency algorithm must be unloaded');
+    test.step(() =&gt; {
+        assert_not_equals(oldGlobal, iframe.contentWindow);
+    });
+    test.done();
+}
+
+&lt;/script&gt;&lt;/b&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (212217 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-02-13 02:42:20 UTC (rev 212217)
+++ trunk/Source/WebCore/ChangeLog        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -1,5 +1,43 @@
</span><span class="cx"> 2017-02-12  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><ins>+        parserRemoveChild should unload subframes
+        https://bugs.webkit.org/show_bug.cgi?id=168151
+
+        Reviewed by Darin Adler.
+
+        Fix the bug that the adoption agency algorithm does not unload subframes as it disconnects nodes.
+
+        Also moved calls to nodeWillBeRemoved inside NoEventDispatchAssertion to expand on r211965.
+
+        Tests: fast/parser/adoption-agency-clear-focus-range.html
+               fast/parser/adoption-agency-unload-iframe-1.html
+               fast/parser/adoption-agency-unload-iframe-2.html
+
+        * dom/ContainerNode.cpp:
+        (WebCore::ContainerNode::takeAllChildrenFrom): Rewritten using idioms used in removeChildren and parserAppendChild.
+
+        Disconnect all subframes first since this can synchronously dispatch an unload event. Then update DOM ranges,
+        the focused element, and other states in the document.
+
+        Second, use the regular removeBetween, notifyChildNodeRemoved, childrenChanged sequence of calls to disconnect nodes
+        instead of a single call to removeDetachedChildren to properly disconnect child nodes since those nodes may have
+        already come live due to execution of synchronous scripts prior to the adoption agency algorithm has run, or in
+        response to the unload event we just dispatched.
+
+        Third, append these nodes using parserAppendChild to avoid dispatching mutation events.
+
+        (WebCore::willRemoveChild): Removed the call to nodeWillBeRemoved. It's now called within NoEventDispatchAssertion
+        in each call site of willRemoveChild and willRemoveChildren.
+        (WebCore::willRemoveChildren): Ditto.
+        (WebCore::ContainerNode::removeChild): Call nodeWillBeRemoved inside NoEventDispatchAssertion.
+        (WebCore::ContainerNode::replaceAllChildren): Call nodeWillBeRemoved inside NoEventDispatchAssertion.
+        (WebCore::ContainerNode::parserRemoveChild): Disconnect subframes and update document's states.
+
+        * html/parser/HTMLConstructionSite.cpp:
+        (WebCore::executeTakeAllChildrenAndReparentTask): Add a release assert that new parent does not already have a parent. 
+
+2017-02-12  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
</ins><span class="cx">         REGRESSION (r179497): Crash inside setAttributeNode
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=168161
</span><span class="cx">         &lt;rdar://problem/30451581&gt;
</span></span></pre></div>
<a id="trunkSourceWebCoredomContainerNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/ContainerNode.cpp (212217 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/ContainerNode.cpp        2017-02-13 02:42:20 UTC (rev 212217)
+++ trunk/Source/WebCore/dom/ContainerNode.cpp        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -130,21 +130,27 @@
</span><span class="cx">             mutation.willRemoveChild(child);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // FIXME: We need to do notifyMutationObserversNodeWillDetach() for each child,
-    // probably inside removeDetachedChildrenInContainer.
</del><ins>+    disconnectSubframesIfNeeded(*oldParent, DescendantsOnly);
+    {
+        NoEventDispatchAssertion assertNoEventDispatch;
</ins><span class="cx"> 
</span><del>-    oldParent-&gt;removeDetachedChildren();
</del><ins>+        oldParent-&gt;document().nodeChildrenWillBeRemoved(*oldParent);
</ins><span class="cx"> 
</span><ins>+        WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
+        while (RefPtr&lt;Node&gt; child = oldParent-&gt;m_firstChild) {
+            oldParent-&gt;removeBetween(nullptr, child-&gt;nextSibling(), *child);
+            notifyChildNodeRemoved(*oldParent, *child);
+        }
+        ChildChange change = { AllChildrenRemoved, nullptr, nullptr, ChildChangeSourceParser };
+        childrenChanged(change);
+    }
+
+    // FIXME: assert that we don't dispatch events here since this container node is still disconnected.
</ins><span class="cx">     for (auto&amp; child : children) {
</span><del>-        destroyRenderTreeIfNeeded(child);
-
-        // FIXME: We need a no mutation event version of adoptNode.
-        auto adoptedChild = document().adoptNode(child).releaseReturnValue();
-        parserAppendChild(adoptedChild);
-        // FIXME: Together with adoptNode above, the tree scope might get updated recursively twice
-        // (if the document changed or oldParent was in a shadow tree, AND *this is in a shadow tree).
-        // Can we do better?
-        treeScope().adoptIfNeeded(adoptedChild);
</del><ins>+        RELEASE_ASSERT(!child-&gt;parentNode() &amp;&amp; &amp;child-&gt;treeScope() == &amp;treeScope());
+        ASSERT(!ensurePreInsertionValidity(child, nullptr).hasException());
+        treeScope().adoptIfNeeded(child);
+        parserAppendChild(child);
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -486,11 +492,6 @@
</span><span class="cx"> 
</span><span class="cx">     if (is&lt;ContainerNode&gt;(child))
</span><span class="cx">         disconnectSubframesIfNeeded(downcast&lt;ContainerNode&gt;(child), RootAndDescendants);
</span><del>-
-    if (child.parentNode() != &amp;container)
-        return;
-
-    child.document().nodeWillBeRemoved(child); // e.g. mutation event listener can create a new range.
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static void willRemoveChildren(ContainerNode&amp; container)
</span><span class="lines">@@ -508,9 +509,6 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     disconnectSubframesIfNeeded(container, DescendantsOnly);
</span><del>-
-    container.document().nodeChildrenWillBeRemoved(container);
-
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ContainerNode::disconnectDescendantFrames()
</span><span class="lines">@@ -534,7 +532,7 @@
</span><span class="cx"> 
</span><span class="cx">     willRemoveChild(*this, child);
</span><span class="cx"> 
</span><del>-    // Mutation events might have moved this child into a different parent.
</del><ins>+    // Mutation events in willRemoveChild might have moved this child into a different parent.
</ins><span class="cx">     if (child-&gt;parentNode() != this)
</span><span class="cx">         return Exception { NOT_FOUND_ERR };
</span><span class="cx"> 
</span><span class="lines">@@ -542,6 +540,8 @@
</span><span class="cx">         WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
</span><span class="cx">         NoEventDispatchAssertion assertNoEventDispatch;
</span><span class="cx"> 
</span><ins>+        document().nodeWillBeRemoved(child);
+
</ins><span class="cx">         Node* prev = child-&gt;previousSibling();
</span><span class="cx">         Node* next = child-&gt;nextSibling();
</span><span class="cx">         removeBetween(prev, next, child);
</span><span class="lines">@@ -591,8 +591,12 @@
</span><span class="cx"> 
</span><span class="cx"> void ContainerNode::parserRemoveChild(Node&amp; oldChild)
</span><span class="cx"> {
</span><ins>+    disconnectSubframesIfNeeded(*this, DescendantsOnly);
+
</ins><span class="cx">     NoEventDispatchAssertion assertNoEventDispatch;
</span><span class="cx"> 
</span><ins>+    document().nodeChildrenWillBeRemoved(*this);
+
</ins><span class="cx">     ASSERT(oldChild.parentNode() == this);
</span><span class="cx">     ASSERT(!oldChild.isDocumentFragment());
</span><span class="cx"> 
</span><span class="lines">@@ -599,8 +603,6 @@
</span><span class="cx">     Node* prev = oldChild.previousSibling();
</span><span class="cx">     Node* next = oldChild.nextSibling();
</span><span class="cx"> 
</span><del>-    oldChild.updateAncestorConnectedSubframeCountForRemoval();
-
</del><span class="cx">     ChildListMutationScope(*this).willRemoveChild(oldChild);
</span><span class="cx">     oldChild.notifyMutationObserversNodeWillDetach();
</span><span class="cx"> 
</span><span class="lines">@@ -643,6 +645,9 @@
</span><span class="cx">         WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
</span><span class="cx">         {
</span><span class="cx">             NoEventDispatchAssertion assertNoEventDispatch;
</span><ins>+
+            document().nodeChildrenWillBeRemoved(*this);
+
</ins><span class="cx">             while (RefPtr&lt;Node&gt; child = m_firstChild) {
</span><span class="cx">                 removeBetween(nullptr, child-&gt;nextSibling(), *child);
</span><span class="cx">                 notifyChildNodeRemoved(*this, *child);
</span><span class="lines">@@ -686,6 +691,8 @@
</span><span class="cx">         WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
</span><span class="cx">         NoEventDispatchAssertion assertNoEventDispatch;
</span><span class="cx"> 
</span><ins>+        document().nodeChildrenWillBeRemoved(*this);
+
</ins><span class="cx">         while (RefPtr&lt;Node&gt; child = m_firstChild) {
</span><span class="cx">             removeBetween(0, child-&gt;nextSibling(), *child);
</span><span class="cx">             notifyChildNodeRemoved(*this, *child);
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlparserHTMLConstructionSitecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp (212217 => 212218)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp        2017-02-13 02:42:20 UTC (rev 212217)
+++ trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp        2017-02-13 04:21:03 UTC (rev 212218)
</span><span class="lines">@@ -150,6 +150,7 @@
</span><span class="cx">     auto* furthestBlock = task.oldParent();
</span><span class="cx">     task.parent-&gt;takeAllChildrenFrom(furthestBlock);
</span><span class="cx"> 
</span><ins>+    RELEASE_ASSERT(!task.parent-&gt;parentNode());
</ins><span class="cx">     furthestBlock-&gt;parserAppendChild(*task.parent);
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>