Make sure to focus inner text field when creating / rebuilding edit fields in datetime widget.

The blur is a regression from bug 1740989, but it's really uncovering a
regression from bug 1729342, sorta. Before that, we used to focus the
inner text field on rebuild via focusInnerTextBox().

One could argue that the focus fixup rule should really deal with this,
but I guess since content can't really unattach shadow DOM, it's less of
a general issue and more of an implementation detail.

The test uncovers another pre-existing issue (switching from date ->
test was also broken, and we left a bogus ElementState::FOCUS state in
the element).

Differential Revision: https://phabricator.services.mozilla.com/D158167

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1792057
gecko-commit: 14c3be47e10268481ac05a0405554953e394208a
gecko-reviewers: smaug
diff --git a/html/semantics/forms/the-input-element/focus-dynamic-type-change.html b/html/semantics/forms/the-input-element/focus-dynamic-type-change.html
new file mode 100644
index 0000000..a1d3dfa
--- /dev/null
+++ b/html/semantics/forms/the-input-element/focus-dynamic-type-change.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Input type switch on focused input shouldn't blur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script type=module>
+import inputTypes from "./input-types.js";
+
+function tick() {
+  return new Promise(resolve => {
+    requestAnimationFrame(() => requestAnimationFrame(resolve));
+  });
+}
+
+function test_from_to(fromType, toType) {
+  if (fromType == toType) {
+    return;
+  }
+  promise_test(async function(t) {
+    const input = document.createElement("input");
+    input.type = fromType;
+    document.body.appendChild(input);
+    input.focus();
+    assert_equals(document.activeElement, input, `${fromType} input should be focused`);
+    function onFocus() {
+      t.assert_unreached("shouldn't be getting spurious focus events");
+    }
+    function onBlur() {
+      t.assert_unreached("shouldn't be getting spurious blur events");
+    }
+    input.addEventListener("focus", onFocus);
+    input.addEventListener("blur", onBlur);
+    input.type = toType;
+    assert_equals(document.activeElement, input, `${fromType} input should be focused after change to ${toType}`);
+    await tick();
+    assert_equals(document.activeElement, input, `${fromType} input should still be focused after change to ${toType}`);
+    input.removeEventListener("focus", onFocus);
+    input.removeEventListener("blur", onBlur);
+  }, `${fromType} -> ${toType}`);
+}
+
+for (let type of inputTypes) {
+  if (type == "hidden") {
+    continue; // hidden inputs are not focusable
+  }
+  test_from_to(type, "text");
+  test_from_to("text", type);
+}
+</script>
+</body>