REGRESSION (r251110): Crash on https://developer.apple.com/tutorials/swiftui/creating-and-combining-views
https://bugs.webkit.org/show_bug.cgi?id=206337

Reviewed by Geoffrey Garen.

Source/WebCore:

The crash was caused by RadioButtonGroups::hasCheckedButton getting called by RadioInputType's
matchesIndeterminatePseudoClass during a style update which happens before the input element had a chance
to register itself with RadioButtonGroups in HTMLInputElement::didFinishInsertingNode.

This happens, in particular, when didFinishInsertingNode of other nodes that appear before the input element
executes arbitrary author scripts or otherwise update the style.

Test: fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash.html

* dom/RadioButtonGroups.cpp:
(WebCore::RadioButtonGroups::hasCheckedButton const):

LayoutTests:

Added a regression test. The test crashes on trunk and causes an infinite loop before r251110.

* fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash-expected.txt: Added.
* fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@254722 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index ef3d167..d81ea9ac 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,15 @@
+2020-01-16  Ryosuke Niwa  <rniwa@webkit.org>
+
+        REGRESSION (r251110): Crash on https://developer.apple.com/tutorials/swiftui/creating-and-combining-views
+        https://bugs.webkit.org/show_bug.cgi?id=206337
+
+        Reviewed by Geoffrey Garen.
+
+        Added a regression test. The test crashes on trunk and causes an infinite loop before r251110.
+
+        * fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash-expected.txt: Added.
+        * fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash.html: Added.
+
 2020-01-16  Lauro Moura  <lmoura@igalia.com>
 
         [GTK] Gardening indexeddb tests
diff --git a/LayoutTests/fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash-expected.txt b/LayoutTests/fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash-expected.txt
new file mode 100644
index 0000000..ba503e7
--- /dev/null
+++ b/LayoutTests/fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash-expected.txt
@@ -0,0 +1,6 @@
+This tests updating the pseudo class state of a radio button in the middle of node insertions.
+The test passes if WebKit does not crash or hang.
+
+PASS
+
+
diff --git a/LayoutTests/fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash.html b/LayoutTests/fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash.html
new file mode 100644
index 0000000..5a48d0a
--- /dev/null
+++ b/LayoutTests/fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>This tests updating the pseudo class state of a radio button in the middle of node insertions.<br>
+The test passes if WebKit does not crash or hang.</p>
+<div id="result"></div>
+<style>
+:indeterminate { color: green; }
+</style>
+<script>
+
+if (window.testRunner)
+    testRunner.dumpAsText();
+
+const div = document.createElement('div');
+
+const script = document.createElement('script');
+script.textContent = 'window.c = getComputedStyle(input).color; document.getElementById("result").textContent = "PASS"';
+div.appendChild(script);
+
+const input = document.createElement('input');
+input.type = 'radio';
+input.name = 'baz';
+input.form = 'foo';
+div.appendChild(input);
+
+const input2 = document.createElement('input');
+input2.type = 'radio';
+input2.name = 'bar';
+input2.form = 'foo';
+document.body.appendChild(input2);
+
+document.body.appendChild(div);
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 0661d9e..433a2b8 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,22 @@
+2020-01-16  Ryosuke Niwa  <rniwa@webkit.org>
+
+        REGRESSION (r251110): Crash on https://developer.apple.com/tutorials/swiftui/creating-and-combining-views
+        https://bugs.webkit.org/show_bug.cgi?id=206337
+
+        Reviewed by Geoffrey Garen.
+
+        The crash was caused by RadioButtonGroups::hasCheckedButton getting called by RadioInputType's
+        matchesIndeterminatePseudoClass during a style update which happens before the input element had a chance
+        to register itself with RadioButtonGroups in HTMLInputElement::didFinishInsertingNode.
+
+        This happens, in particular, when didFinishInsertingNode of other nodes that appear before the input element
+        executes arbitrary author scripts or otherwise update the style.
+
+        Test: fast/forms/match-pseudo-on-radio-before-finalizing-tree-insertion-crash.html
+
+        * dom/RadioButtonGroups.cpp:
+        (WebCore::RadioButtonGroups::hasCheckedButton const):
+
 2020-01-16  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][IFC] Optimize LineCandidateContent for the most common type of content
diff --git a/Source/WebCore/dom/RadioButtonGroups.cpp b/Source/WebCore/dom/RadioButtonGroups.cpp
index 74b4f89..b98eaa8 100644
--- a/Source/WebCore/dom/RadioButtonGroups.cpp
+++ b/Source/WebCore/dom/RadioButtonGroups.cpp
@@ -254,7 +254,10 @@
     const AtomString& name = element.name();
     if (name.isEmpty())
         return element.checked();
-    return m_nameToGroupMap.get(name.impl())->checkedButton();
+    auto* group = m_nameToGroupMap.get(name.impl());
+    if (!group)
+        return false; // FIXME: Update the radio button group before author script had a chance to run in didFinishInsertingNode().
+    return group->checkedButton();
 }
 
 bool RadioButtonGroups::isInRequiredGroup(HTMLInputElement& element) const