Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!doctype html>
<meta charset="utf-8" />
<title>HTML partial updates - streamAppendHTML</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style id="style"></style>
<p id="target"></p>
<div id="container"></div>
<script>
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "A";
const writable = placeholder.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"node.streamAppendHTML() returns a writable stream",
);
const response = new Response("<span>B</span>", {
headers: { "Content-Type": "text/html" },
});
await response.body.pipeThrough(new TextDecoderStream()).pipeTo(writable);
assert_equals(placeholder.textContent, "AB");
assert_true(response.bodyUsed);
}, "piping a response into streamAppendHTML()");
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "<span>A</span>";
const writable = placeholder.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"node.streamAppendHTML() returns a writable stream",
);
const writer = writable.getWriter();
await writer.write("B");
await writer.close();
assert_equals(placeholder.textContent, "AB");
}, "writing a string to streamAppendHTML()");
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "<span>A</span>";
const writable = placeholder.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"node.streamAppendHTML() returns a writable stream",
);
assert_equals(placeholder.textContent, "A");
const writer = writable.getWriter();
await writer.write("B");
await writer.close();
assert_equals(placeholder.textContent, "AB");
}, "streamAppendHTML() should append to existing content");
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "<span>A</span>";
const writable = placeholder.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"node.streamAppendHTML() returns a writable stream",
);
const writer = writable.getWriter();
await writer.write("<span>B</span>");
assert_equals(placeholder.textContent, "AB");
await writer.close();
}, "streamAppendHTML() does not buffer until close()");
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "Before";
const writable = placeholder.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"streamAppendHTML() returns a writable stream",
);
const writer = writable.getWriter();
await promise_rejects_js(t, TypeError, writer.write(Symbol("sym")));
assert_equals(placeholder.textContent, "Before");
}, "writing a Symbol to streamAppendHTML()");
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "12";
const writable = placeholder.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"node.streamAppendHTML() returns a writable stream",
);
const writer = writable.getWriter();
await writer.write(345);
await writer.close();
assert_equals(placeholder.textContent, "12345");
assert_equals(placeholder.childNodes.length, 1);
assert_equals(placeholder.firstChild.nodeType, Node.TEXT_NODE);
assert_equals(placeholder.firstChild.textContent, "12345");
}, "writing numbers to streamAppendHTML()");
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "start ";
const writable = placeholder.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"node.streamAppendHTML() returns a writable stream",
);
const writer = writable.getWriter();
await writer.write(null);
await writer.write(" ");
await writer.write(undefined);
await writer.close();
assert_equals(placeholder.textContent, "start null undefined");
}, "writing null or undefined to streamAppendHTML()");
promise_test(async (t) => {
const style = document.querySelector("#style");
const writable = style.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"node.streamAppendHTML() returns a writable stream",
);
const writer = await writable.getWriter();
await writer.write("#target { color: rgba(100, 0, 100); }");
await writer.close();
assert_equals(
getComputedStyle(document.querySelector("#target")).color,
"rgb(100, 0, 100)",
);
}, "streamAppendHTML() can stream into <style>");
promise_test(async (t) => {
const style = document.createElement("style");
style.innerHTML = "#target { color: rgba";
const writable = style.streamAppendHTML();
assert_true(
writable instanceof WritableStream,
"node.streamAppendHTML() returns a writable stream",
);
const writer = await writable.getWriter();
await writer.write("(100, 0, 100); }");
await writer.close();
assert_equals(
getComputedStyle(document.querySelector("#target")).color,
"rgb(100, 0, 100)",
);
}, "streamAppendHTML() can stream into a non-empty <style>");
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "start ";
const writable = placeholder.streamAppendHTML();
const writer = writable.getWriter();
await writer.write("ABC");
await writer.abort("abort-reason");
await writer.write("DEF").catch(() => {});
assert_equals(placeholder.textContent, "start ABC");
}, "Aborting streamAppendHTML()");
promise_test(async (t) => {
const placeholder = document.createElement("div");
placeholder.innerHTML = "before";
const writer1 = placeholder.streamAppendHTML().getWriter();
const writer2 = placeholder.streamAppendHTML().getWriter();
await writer1.write("<span>A</span>");
await writer2.write("<span>B</span>");
await new Promise((resolve) => requestAnimationFrame(resolve));
await writer1.write("C");
await writer1.close();
await writer2.close();
assert_equals(placeholder.textContent, "beforeABC");
}, "Interleaved streamAppendHTML() writes");
promise_test(async (t) => {
const element = document.createElement("div");
document.body.append(element);
window.did_eval_script = false;
const writer = element.streamAppendHTML({ runScripts: true }).getWriter();
assert_false(window.did_eval_script);
await writer.write("<script>window.did_eval_script = true;");
assert_false(window.did_eval_script);
await writer.write("<");
assert_false(window.did_eval_script);
await writer.write("/script>");
assert_false(window.did_eval_script);
}, "streamAppendHTML should not execute scripts when connected");
promise_test(async (t) => {
const element = document.createElement("div");
element.innerHTML = "<p>before;</p>";
document.body.append(element);
const writer = element
.streamAppendHTML({ sanitizer: { removeElements: ["p"] } })
.getWriter();
await writer.write("<p>forbidden</p>");
await writer.write("<span>allowed</span>");
await writer.close();
assert_equals(element.textContent, "before;allowed");
}, "streamAppendHTML should pass sanitizer options");
promise_test(async (t) => {
const element = document.createElement("div");
const shadow = element.attachShadow({mode: "open"});
shadow.innerHTML = "<p>before;</p>";
document.body.append(element);
const writer = shadow
.streamAppendHTML({ sanitizer: { removeElements: ["p"] } })
.getWriter();
await writer.write("<p>forbidden</p>");
await writer.write("<span>allowed</span>");
await writer.close();
assert_equals(shadow.textContent, "before;allowed");
}, "streamAppendHTML on ShadowRoot");
promise_test(async (t) => {
const element = document.createElement("div");
element.innerHTML = "before;";
document.body.append(element);
const writer = element.streamAppendHTML().getWriter();
await writer.write("<script id=forbidden><" + "/script><span id=after>after</span>");
await writer.close();
assert_equals(element.querySelector("script"), null);
assert_equals(element.textContent, "before;after");
}, "streamAppendHTML uses safe sanitizer");
promise_test(async (t) => {
const element = document.createElement("div");
const shadow = element.attachShadow({mode: "open"});
document.body.append(element);
const writer = shadow.streamAppendHTML().getWriter();
await writer.write("<script id=forbidden<" + "/script>");
await writer.close();
assert_equals(shadow.querySelector("script"), null);
}, "streamAppendHTML on ShadowRoot uses safe sanitizer");</script>