Null check frame in Document::dispatchDisabledAdaptationsDidChangeForMainFrame and a few other places
https://bugs.webkit.org/show_bug.cgi?id=214715
<rdar://problem/65467702>

Reviewed by Geoffrey Garen.

Source/WebCore:

Test: security/mutation-observer-frame-detach.html

* dom/Document.cpp:
(WebCore::Document::didBecomeCurrentDocumentInFrame):
(WebCore::Document::initContentSecurityPolicy):
* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::commitData):
Add some null checks and early returns if the frame detaches.
* loader/SubframeLoader.cpp:
(WebCore::FrameLoader::SubframeLoader::loadSubframe):
Balance the call to incrementLoadEventDelayCount in the early return case or this test never finishes loading.

LayoutTests:

* security/mutation-observer-frame-detach-expected.txt: Added.
* security/mutation-observer-frame-detach.html: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@264880 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 6aa38ab..e3fd183 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,5 +1,16 @@
 2020-07-24  Alex Christensen  <achristensen@webkit.org>
 
+        Null check frame in Document::dispatchDisabledAdaptationsDidChangeForMainFrame and a few other places
+        https://bugs.webkit.org/show_bug.cgi?id=214715
+        <rdar://problem/65467702>
+
+        Reviewed by Geoffrey Garen.
+
+        * security/mutation-observer-frame-detach-expected.txt: Added.
+        * security/mutation-observer-frame-detach.html: Added.
+
+2020-07-24  Alex Christensen  <achristensen@webkit.org>
+
         Fix assertion when highlighting in detached frames and make loads complete
         https://bugs.webkit.org/show_bug.cgi?id=214770
 
diff --git a/LayoutTests/security/mutation-observer-frame-detach-expected.txt b/LayoutTests/security/mutation-observer-frame-detach-expected.txt
new file mode 100644
index 0000000..aec0300
--- /dev/null
+++ b/LayoutTests/security/mutation-observer-frame-detach-expected.txt
@@ -0,0 +1,2 @@
+ALERT: test passed because nothing crashed
+
diff --git a/LayoutTests/security/mutation-observer-frame-detach.html b/LayoutTests/security/mutation-observer-frame-detach.html
new file mode 100644
index 0000000..a5f6351
--- /dev/null
+++ b/LayoutTests/security/mutation-observer-frame-detach.html
@@ -0,0 +1,29 @@
+<script>
+    function runTest() {
+        var observer = new MutationObserver(()=>{p1.replaceWith(p2)});
+        observer.observe(select,{childList:true});
+        select[2] = option;
+        document.head.appendChild(p2);
+        var object = document.createElement("object");
+        var frame = document.createElement("frame");
+        audio.appendChild(option);
+        p1.appendChild(object);
+        object.data = "abc";
+        document.all[9].appendChild(frame);
+		if (window.testRunner) {
+			testRunner.dumpAsText();
+			alert("test passed because nothing crashed");
+		}
+    }
+</script>
+<body onload=runTest()>
+    <p id="p1">
+        <p id="p2">
+            <audio id="audio">
+                <select id="select">
+                    <option id="option"></option>
+                </select>
+            </audio>
+        </p>
+    </p>
+</body>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 307ae6f..49a267e 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,5 +1,25 @@
 2020-07-24  Alex Christensen  <achristensen@webkit.org>
 
+        Null check frame in Document::dispatchDisabledAdaptationsDidChangeForMainFrame and a few other places
+        https://bugs.webkit.org/show_bug.cgi?id=214715
+        <rdar://problem/65467702>
+
+        Reviewed by Geoffrey Garen.
+
+        Test: security/mutation-observer-frame-detach.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::didBecomeCurrentDocumentInFrame):
+        (WebCore::Document::initContentSecurityPolicy):
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::commitData):
+        Add some null checks and early returns if the frame detaches.
+        * loader/SubframeLoader.cpp:
+        (WebCore::FrameLoader::SubframeLoader::loadSubframe):
+        Balance the call to incrementLoadEventDelayCount in the early return case or this test never finishes loading.
+
+2020-07-24  Alex Christensen  <achristensen@webkit.org>
+
         Fix assertion when highlighting in detached frames and make loads complete
         https://bugs.webkit.org/show_bug.cgi?id=214770
 
diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp
index 48927c3..3d742e7 100644
--- a/Source/WebCore/dom/Document.cpp
+++ b/Source/WebCore/dom/Document.cpp
@@ -2386,16 +2386,24 @@
 
 void Document::didBecomeCurrentDocumentInFrame()
 {
-    // FIXME: Are there cases where the document can be dislodged from the frame during the event handling below?
-    // If so, then m_frame could become 0, and we need to do something about that.
-
     m_frame->script().updateDocument();
 
+    // Many of these functions have event handlers which can detach the frame synchronously, so we must check repeatedly in this function.
+    if (!m_frame)
+        return;
+
     if (!hasLivingRenderTree())
         createRenderTree();
+    if (!m_frame)
+        return;
 
     dispatchDisabledAdaptationsDidChangeForMainFrame();
+    if (!m_frame)
+        return;
+
     updateViewportArguments();
+    if (!m_frame)
+        return;
 
     // FIXME: Doing this only for the main frame is insufficient.
     // Changing a subframe can also change the wheel event handler count.
@@ -2406,6 +2414,8 @@
     // unless the documents they are replacing had wheel event handlers.
     if (page() && m_frame->isMainFrame())
         wheelEventHandlersChanged();
+    if (!m_frame)
+        return;
 
     // Ensure that the scheduled task state of the document matches the DOM suspension state of the frame. It can
     // be out of sync if the DOM suspension state changed while the document was not in the frame (possibly in the
@@ -6098,6 +6108,8 @@
 
 void Document::initContentSecurityPolicy()
 {
+    if (!m_frame)
+        return;
     auto* parentFrame = m_frame->tree().parent();
     if (parentFrame)
         contentSecurityPolicy()->copyUpgradeInsecureRequestStateFrom(*parentFrame->document()->contentSecurityPolicy());
diff --git a/Source/WebCore/loader/DocumentLoader.cpp b/Source/WebCore/loader/DocumentLoader.cpp
index 2fc2149..408a13e 100644
--- a/Source/WebCore/loader/DocumentLoader.cpp
+++ b/Source/WebCore/loader/DocumentLoader.cpp
@@ -1087,7 +1087,10 @@
         bool hasBegun = m_writer.begin(documentURL(), false);
         m_writer.setDocumentWasLoadedAsPartOfNavigation();
 
-        auto& document = *m_frame->document();
+        auto* documentOrNull = m_frame ? m_frame->document() : nullptr;
+        if (!documentOrNull)
+            return;
+        auto& document = *documentOrNull;
 
         if (SecurityPolicy::allowSubstituteDataAccessToLocal() && m_originalSubstituteDataWasValid) {
             // If this document was loaded with substituteData, then the document can