From 936d1f091b270658dde16670d12c76d342c66628 Mon Sep 17 00:00:00 2001
From: Joey Arhar <jarhar@chromium.org>
Date: Mon, 16 Oct 2023 09:56:49 -0700
Subject: [PATCH] Implement parseHTMLUnsafe and setHTMLUnsafe

I am speccing this here: https://github.com/whatwg/html/pull/9538

Bug: 1478969
Change-Id: Ie55827cebdf349aadae13fbf1086baf6177bbff2
---
 ...nt-parseHTMLUnsafe-encoding.tentative.html | 35 +++++++++
 ...eHTMLUnsafe-style-attribute.tentative.html | 54 +++++++++++++
 ...MLUnsafe-url-base-pushstate.tentative.html | 15 ++++
 ...nt-parseHTMLUnsafe-url-base.tentative.html | 10 +++
 ...rseHTMLUnsafe-url-moretests.tentative.html | 42 ++++++++++
 ...rseHTMLUnsafe-url-pushstate.tentative.html | 14 ++++
 ...ocument-parseHTMLUnsafe-url.tentative.html |  9 +++
 .../Document-parseHTMLUnsafe.tentative.html   | 77 +++++++++++++++++++
 .../Element-setHTMLUnsafe-04.tentative.html   | 25 ++++++
 ...parseHTMLUnsafe-iframe-base-pushstate.html | 10 +++
 .../parseHTMLUnsafe-iframe-base.html          |  6 ++
 .../parseHTMLUnsafe-iframe-pushstate.html     |  9 +++
 .../resources/parseHTMLUnsafe-iframe.html     |  4 +
 .../resources/parseHTMLUnsafe-iframe.js       |  3 +
 .../resources/parseHTMLUnsafe-url-tests.js    | 36 +++++++++
 .../setHTMLUnsafe.tentative.html              | 55 +++++++++++++
 .../declarative-shadow-dom-attachment.html    |  2 +-
 .../declarative-shadow-dom-basic.html         | 13 ++--
 shadow-dom/declarative/support/helpers.js     |  4 -
 19 files changed, 411 insertions(+), 12 deletions(-)
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-encoding.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-style-attribute.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base-pushstate.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-moretests.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-pushstate.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Element-setHTMLUnsafe-04.tentative.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.html
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js
 create mode 100644 html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe.tentative.html
 delete mode 100644 shadow-dom/declarative/support/helpers.js

diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-encoding.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-encoding.tentative.html
new file mode 100644
index 000000000000000..1debcafa3b53402
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-encoding.tentative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="windows-1252"> <!-- intentional to make sure the results are UTF-8 anyway -->
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9538">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- This was adapted from DOMParser-parseFromString-encoding.html -->
+
+<script>
+function assertEncoding(doc) {
+  assert_equals(doc.charset, "UTF-8", "document.charset");
+  assert_equals(doc.characterSet, "UTF-8", "document.characterSet");
+  assert_equals(doc.inputEncoding, "UTF-8", "document.characterSet");
+}
+
+setup(() => {
+  assert_equals(document.characterSet, "windows-1252", "the meta charset must be in effect, making the main document windows-1252");
+});
+
+test(() => {
+  const doc = Document.parseHTMLUnsafe('');
+  assertEncoding(doc);
+}, 'Parse empty string');
+
+test(() => {
+  const doc = Document.parseHTMLUnsafe(`<meta charset="latin2">`);
+  assertEncoding(doc);
+}, "meta charset latin2");
+
+test(() => {
+  const doc = Document.parseHTMLUnsafe(`<?xml version="1.0" encoding="latin2"?><x/>`);
+  assertEncoding(doc);
+}, "XML declaration");
+</script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-style-attribute.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-style-attribute.tentative.html
new file mode 100644
index 000000000000000..cd092a6034602a4
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-style-attribute.tentative.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from style_attribute_html.html -->
+<meta charset=utf-8>
+<title>Style attribute in HTML</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+
+var div;
+setup(function() {
+    var input = '<div style="color: red">Foo</div>';
+    var doc = Document.parseHTMLUnsafe(input);
+    div = doc.querySelector('div');
+});
+
+test(function() {
+    var style = div.style;
+    assert_equals(style.cssText, 'color: red;');
+    assert_equals(style.color, 'red');
+    assert_equals(div.getAttribute("style"), 'color: red',
+                  'Value of style attribute should match the string value that was set');
+}, 'Parsing of initial style attribute');
+
+test(function() {
+    var style = div.style;
+    div.setAttribute('style', 'color:: invalid');
+    assert_equals(style.cssText, '');
+    assert_equals(style.color, '');
+    assert_equals(div.getAttribute('style'), 'color:: invalid',
+                  'Value of style attribute should match the string value that was set');
+}, 'Parsing of invalid style attribute');
+
+test(function() {
+    var style = div.style;
+    div.setAttribute('style', 'color: green');
+    assert_equals(style.cssText, 'color: green;');
+    assert_equals(style.color, 'green');
+    assert_equals(div.getAttribute('style'), 'color: green',
+                  'Value of style attribute should match the string value that was set');
+}, 'Parsing of style attribute');
+
+test(function() {
+    var style = div.style;
+    style.backgroundColor = 'blue';
+    assert_equals(style.cssText, 'color: green; background-color: blue;',
+                  'Should not drop the existing style');
+    assert_equals(style.color, 'green',
+                  'Should not drop the existing style');
+    assert_equals(div.getAttribute('style'), 'color: green; background-color: blue;',
+                  'Should update style attribute');
+}, 'Update style.backgroundColor');
+
+</script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base-pushstate.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base-pushstate.tentative.html
new file mode 100644
index 000000000000000..a8eab595116f0c6
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base-pushstate.tentative.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from DOMParser-parseFromString-url-base-pushstate.html -->
+<title>parseHTMLUnsafe test of how the document's URL is set (base, pushstate)</title>
+<base href="/fake/base-from-outer-frame">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html" onload="window.resolveLoadPromise();"></iframe>
+
+<script>
+"use strict";
+history.pushState(null, "", "/fake/push-state-from-outer-frame");
+</script>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js"></script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.tentative.html
new file mode 100644
index 000000000000000..62b2c09aaec5ecb
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.tentative.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This was adapted from DOMParser-parseFromString-url-base.html -->
+<title>parseHTMLUnsafe test of how the document's URL is set (base, no pushstate)</title>
+<base href="/fake/base-from-outer-frame">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html" onload="window.resolveLoadPromise();"></iframe>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js"></script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-moretests.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-moretests.tentative.html
new file mode 100644
index 000000000000000..41335ef035fae44
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-moretests.tentative.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from DOMParser-parseFromString-url-moretests.html -->
+<meta charset=utf-8>
+<title>Document.parseHTMLUnsafe: Document's url</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function() {
+  var iframe = document.createElement("iframe");
+  iframe.onload = this.step_func(function() {
+    var child = iframe.contentWindow;
+
+    test(function() {
+      var doc = Document.parseHTMLUnsafe("<html></html>");
+      assert_equals(doc.URL, "about:blank");
+    }, "Parent window");
+
+    test(function() {
+      var doc = child.Document.parseHTMLUnsafe("<html></html>");
+      assert_equals(doc.URL, "about:blank");
+    }, "Child window");
+
+    var dpBeforeNavigation = child.Document, urlBeforeNavigation = child.document.URL;
+    iframe.onload = this.step_func_done(function() {
+      test(function() {
+        var doc = dpBeforeNavigation.parseHTMLUnsafe("<html></html>");
+        assert_equals(doc.URL, "about:blank");
+      }, "Child window crossing navigation");
+
+      test(function() {
+        var doc = child.Document.parseHTMLUnsafe("<html></html>");
+        assert_equals(doc.URL, "about:blank");
+      }, "Child window after navigation");
+    });
+    iframe.src = "/common/blank.html?2";
+  });
+  iframe.src = "/common/blank.html?1";
+  document.body.appendChild(iframe);
+});
+</script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-pushstate.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-pushstate.tentative.html
new file mode 100644
index 000000000000000..7930bf0ddbf12a1
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-pushstate.tentative.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from DOMParser-parseFromString-url-pushstate.html -->
+<title>parseHTMLUnsafe test of how the document's URL is set (no base, pushstate)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html" onload="window.resolveLoadPromise();"></iframe>
+
+<script>
+"use strict";
+history.pushState(null, "", "/fake/push-state-from-outer-frame");
+</script>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js"></script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url.tentative.html
new file mode 100644
index 000000000000000..911e1837063ab49
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url.tentative.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from DOMParser-parseFromString-url.html -->
+<title>parseHTMLUnsafe test of how the document's URL is set (no pushstate, no base)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="resources/parseHTMLUnsafe-iframe.html" onload="window.resolveLoadPromise();"></iframe>
+<script src="resources/parseHTMLUnsafe-url-tests.js"></script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.tentative.html
new file mode 100644
index 000000000000000..2a89370825b6936
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.tentative.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This was adapted from DOMParser-parseFromString-html.html -->
+<title>parseHTMLUnsafe basic test of HTML parsing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// |expected| should be an object indicating the expected type of node.
+function assert_node(actual, expected) {
+    assert_true(actual instanceof expected.type,
+                'Node type mismatch: actual = ' + actual.constructor.name + ', expected = ' + expected.type.name);
+    if (typeof(expected.id) !== 'undefined')
+        assert_equals(actual.id, expected.id, expected.idMessage);
+}
+
+var doc;
+setup(function() {
+    doc = Document.parseHTMLUnsafe('<html id="root"><head></head><body></body></html>');
+});
+
+test(function() {
+    var root = doc.documentElement;
+    assert_node(root, { type: HTMLHtmlElement, id: 'root',
+                        idMessage: 'documentElement id attribute should be root.' });
+}, 'Parsing of id attribute');
+
+test(function() {
+    assert_equals(doc.contentType, "text/html")
+}, 'contentType');
+
+test(function() {
+    assert_equals(doc.compatMode, "BackCompat")
+}, 'compatMode');
+
+test(function() {
+    doc = Document.parseHTMLUnsafe('<!DOCTYPE html><html id="root"><head></head><body></body></html>');
+    assert_equals(doc.compatMode, "CSS1Compat")
+}, 'compatMode for a proper DOCTYPE');
+
+// URL- and encoding-related stuff tested separately.
+
+test(function() {
+    assert_equals(doc.location, null,
+                  'The document must have a location value of null.');
+}, 'Location value');
+
+test(function() {
+    var htmldoc = Document.parseHTMLUnsafe("<!DOCTYPE foo></><foo></multiple></>");
+    assert_equals(htmldoc.documentElement.localName, "html");
+    assert_equals(htmldoc.documentElement.namespaceURI, "http://www.w3.org/1999/xhtml");
+}, "Document.parseHTMLUnsafe parses HTML tag soup with no problems");
+
+test(function() {
+   const doc = Document.parseHTMLUnsafe('<noembed>&lt;a&gt;</noembed>');
+   assert_equals(doc.querySelector('noembed').textContent, '&lt;a&gt;');
+}, 'Document.parseHTMLUnsafe should handle the content of <noembed> as raw text');
+
+test(() => {
+   const doc = Document.parseHTMLUnsafe(`
+<html><body>
+<style>
+  @import url(/dummy.css)
+</style>
+<script>document.x = 8<\/script>
+</body></html>`);
+
+  assert_not_equals(doc.querySelector('script'), null, 'script must be found');
+  assert_equals(doc.x, undefined, 'script must not be executed on the inner document');
+  assert_equals(document.x, undefined, 'script must not be executed on the outer document');
+}, 'script is found synchronously even when there is a css import');
+
+test(() => {
+    const doc = Document.parseHTMLUnsafe(`<body><noscript><p id="test1">test1<p id="test2">test2</noscript>`);
+    assert_node(doc.body.firstChild.childNodes[0], { type: HTMLParagraphElement, id: 'test1' });
+    assert_node(doc.body.firstChild.childNodes[1], { type: HTMLParagraphElement, id: 'test2' });
+}, 'must be parsed with scripting disabled, so noscript works');
+</script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Element-setHTMLUnsafe-04.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Element-setHTMLUnsafe-04.tentative.html
new file mode 100644
index 000000000000000..4a9e300afc2f934
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Element-setHTMLUnsafe-04.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>setHTMLUnsafe in HTML</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel=author href="mailto:jarhar@chromium.org">
+<!-- This test was adapted from innerhtml-04.html -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function testIsChild(p, c) {
+  assert_equals(p.firstChild, c);
+  assert_equals(c.parentNode, p);
+}
+test(function() {
+  var p = document.createElement('p');
+  var b = p.appendChild(document.createElement('b'));
+  var t = b.appendChild(document.createTextNode("foo"));
+  testIsChild(p, b);
+  testIsChild(b, t);
+  assert_equals(t.data, "foo");
+  p.setHTMLUnsafe("");
+  testIsChild(b, t);
+  assert_equals(t.data, "foo");
+}, "setHTMLUnsafe should leave the removed children alone.")
+</script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html
new file mode 100644
index 000000000000000..53b855968c866e8
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base-pushstate.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>An iframe that does parseHTMLUnsafe stuff with base and pushstates itself</title>
+<base href="/fake/base-from-iframe">
+
+<script>
+"use strict";
+history.pushState(null, "", "/fake/push-state-from-iframe");
+</script>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js"></script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html
new file mode 100644
index 000000000000000..6977e938119561a
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-base.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>An iframe that does parseHTMLUnsafe stuff with base</title>
+<base href="/fake/base-from-iframe">
+
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js"></script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html
new file mode 100644
index 000000000000000..5e1fae04413cdfa
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe-pushstate.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>An iframe that does parseHTMLUnsafe stuff and pushstates itself</title>
+
+<script>
+"use strict";
+history.pushState(null, "", "/fake/push-state-from-iframe");
+</script>
+<script src="/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js"></script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.html
new file mode 100644
index 000000000000000..f47d9605fc85969
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>An iframe that does parseHTMLUnsafe stuff</title>
+<script src="parseHTMLUnsafe-iframe.js"></script>
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js
new file mode 100644
index 000000000000000..4a0b56869beb568
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-iframe.js
@@ -0,0 +1,3 @@
+window.doParse = (html) => {
+  return Document.parseHTMLUnsafe(html);
+};
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js
new file mode 100644
index 000000000000000..88344d127cee3cc
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/resources/parseHTMLUnsafe-url-tests.js
@@ -0,0 +1,36 @@
+const loadPromise = new Promise(resolve => { window.resolveLoadPromise = resolve; });
+
+function assertURL(doc) {
+  assert_equals(doc.URL, "about:blank", "document.URL");
+  assert_equals(doc.documentURI, "about:blank", "document.documentURI");
+  assert_equals(doc.baseURI, "about:blank", "document.baseURI");
+}
+
+const inputs = {
+  valid: "<html></html>",
+  "invalid XML": `<span x:test="testing">1</span>`
+};
+
+for (const [inputName, input] of Object.entries(inputs)) {
+  test(() => {
+    const doc = Document.parseHTMLUnsafe(input);
+
+    assertURL(doc);
+  }, `${inputName}: created normally`);
+
+  promise_test(async () => {
+    await loadPromise;
+
+    const doc = frames[0].Document.parseHTMLUnsafe(input);
+
+    assertURL(doc);
+  }, `${inputName}: created using another iframe's parseHTMLUnsafe from this frame`);
+
+  promise_test(async () => {
+    await loadPromise;
+
+    const doc = frames[0].doParse(input);
+
+    assertURL(doc);
+  }, `${inputName}: created using another iframe's parseHTMLUnsafe from that frame`);
+}
diff --git a/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe.tentative.html b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe.tentative.html
new file mode 100644
index 000000000000000..1c0b1155691cbcf
--- /dev/null
+++ b/html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9538">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+for (const containerType of ['Element', 'ShadowRoot']) {
+  const createContainer = () => {
+    if (containerType == 'ShadowRoot') {
+      return document.createElement('div').attachShadow({mode: 'open'});
+    } else if (containerType == 'Element') {
+      return document.createElement('div');
+    }
+  };
+
+  test(() => {
+    const container = createContainer();
+    container.setHTMLUnsafe('<span title=hello>world</span>');
+
+    assert_equals(container.children.length, 1, 'Only one child node should be created.');
+    assert_equals(container.firstChild.tagName, 'SPAN', 'The child element should be a span.');
+    assert_equals(container.firstChild.getAttribute('title'), 'hello',
+      'The title attribute should be set to hello.');
+    assert_equals(container.firstChild.childNodes.length, 1,
+      'The span should have one child.');
+    assert_true(container.firstChild.childNodes[0] instanceof Text,
+      'The spans child should be a text node.');
+    assert_equals(container.firstChild.textContent, 'world',
+      'The spans textContent should be world.');
+  }, `${containerType}: setHTMLUnsafe with no shadowdom.`);
+
+  test(() => {
+    const container = createContainer();
+    container.setHTMLUnsafe(`<div><template shadowrootmode=open><div>hello</div></template></div>`);
+
+    assert_equals(container.children.length, 1, 'One child should be created in the container.');
+    const shadowRoot = container.firstChild.shadowRoot;
+    assert_true(!!shadowRoot, 'The containers child should have a ShadowRoot.');
+    assert_equals(shadowRoot.children.length, 1, 'One child should be created in the ShadowRoot.');
+    assert_equals(shadowRoot.firstChild.textContent, 'hello',
+      'The ShadowRoots childs textContent should be hello.');
+  }, `${containerType}: setHTMLUnsafe with shadowdom.`);
+}
+
+test(() => {
+  const template = document.createElement('template');
+  template.setHTMLUnsafe('<div>hello world</div>');
+
+  assert_equals(template.children.length, 0, 'template should not have any child nodes.');
+  assert_equals(template.content.children.length, 1, 'template content should have a child div.');
+  assert_equals(template.content.children[0].textContent, 'hello world', 'text content should be set.');
+}, 'template.setHTMLUnsafe() should modify template content fragment rather than actual children.');
+</script>
diff --git a/shadow-dom/declarative/declarative-shadow-dom-attachment.html b/shadow-dom/declarative/declarative-shadow-dom-attachment.html
index d752b62d31d47fd..dcee6cf8a103ac5 100644
--- a/shadow-dom/declarative/declarative-shadow-dom-attachment.html
+++ b/shadow-dom/declarative/declarative-shadow-dom-attachment.html
@@ -19,7 +19,7 @@
   const declarativeString = `<${elementType} id=theelement>${getDeclarativeContent(mode, delegatesFocus)}
      <span class='lightdom'>${lightDomTextContent}</span></${elementType}>`;
   const wrapper = document.createElement('div');
-  setInnerHTML(wrapper, declarativeString);
+  wrapper.setHTMLUnsafe(declarativeString);
   const element = wrapper.querySelector('#theelement');
   return {wrapper: wrapper, element: element};
 }
diff --git a/shadow-dom/declarative/declarative-shadow-dom-basic.html b/shadow-dom/declarative/declarative-shadow-dom-basic.html
index b71f7d1a3778e72..5d59c9bb2f70c37 100644
--- a/shadow-dom/declarative/declarative-shadow-dom-basic.html
+++ b/shadow-dom/declarative/declarative-shadow-dom-basic.html
@@ -5,7 +5,6 @@
 <link rel="help" href="https://github.com/whatwg/dom/issues/831">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="support/helpers.js"></script>
 
 <div id="host" style="display:none">
   <template shadowrootmode="open">
@@ -47,7 +46,7 @@
 
 test(() => {
   const div = document.createElement('div');
-  setInnerHTML(div,`
+  div.setHTMLUnsafe(`
     <div id="host">
       <template shadowrootmode="open">
         <slot id="s1" name="slot1"></slot>
@@ -68,7 +67,7 @@
 
 test(() => {
   const div = document.createElement('div');
-  setInnerHTML(div,`
+  div.setHTMLUnsafe(`
     <div id="host">
       <template shadowrootmode="invalid">
       </template>
@@ -84,7 +83,7 @@
 
 test(() => {
   const div = document.createElement('div');
-  setInnerHTML(div,`
+  div.setHTMLUnsafe(`
     <div id="host">
       <template shadowrootmode="closed">
       </template>
@@ -97,7 +96,7 @@
 
 test(() => {
   const div = document.createElement('div');
-  setInnerHTML(div,`
+  div.setHTMLUnsafe(`
     <div id="host">
       <template shadowrootmode="open">
         <slot id="s1" name="slot1"></slot>
@@ -113,7 +112,7 @@
 
 test(() => {
   const div = document.createElement('div');
-  setInnerHTML(div,`
+  div.setHTMLUnsafe(`
     <div id="host">
       <template shadowrootmode="open" shadowrootdelegatesfocus>
       </template>
@@ -122,7 +121,7 @@
   var host = div.querySelector('#host');
   assert_true(!!host.shadowRoot,"No shadow root found");
   assert_true(host.shadowRoot.delegatesFocus,"delegatesFocus should be true");
-  setInnerHTML(div,`
+  div.setHTMLUnsafe(`
     <div id="host">
       <template shadowrootmode="open">
       </template>
diff --git a/shadow-dom/declarative/support/helpers.js b/shadow-dom/declarative/support/helpers.js
deleted file mode 100644
index 0be3add620065a9..000000000000000
--- a/shadow-dom/declarative/support/helpers.js
+++ /dev/null
@@ -1,4 +0,0 @@
-function setInnerHTML(el,content) {
-  const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true});
-  (el instanceof HTMLTemplateElement ? el.content : el).replaceChildren(...fragment.body.firstChild.childNodes);
-}