You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: patching-explainer.md
+32-24Lines changed: 32 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,4 +1,4 @@
1
-
# Patching (Partial, interleaved HTML streaming)
1
+
# Interleaved HTML streaming (patching)
2
2
3
3
## Motivation
4
4
Streaming of HTML existed from the early days of the web, serving an important purpose for perceieved performance when loading long articles etc.
@@ -20,62 +20,74 @@ A patch can be streamed directly into that position using JavaScript, and multip
20
20
## Anatomy of a patch
21
21
22
22
A patch is a [stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) that targets a [parent node](https://developer.mozilla.org/en-US/docs/Web/API/Node) (usually an element, but potentially a shadow root).
23
-
It can handle strings or bytes. When it receives bytes, it decodes them using the node's document's charset. Anything other than strings or bytes is stringified.
23
+
It can handle strings, bytes, or `TrustedHTMLString`. When it receives bytes, it decodes them using UTF8.
24
+
Anything other than strings or bytes is stringified.
24
25
25
26
When a patch is active, it is essentially a [WritableStream](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream) that feeds a [fragment-mode parser](https://html.spec.whatwg.org/multipage/parsing.html#html-fragment-parsing-algorithm) with strings from that stream.
27
+
Unlike the usual fragment parser, nodes are inserted directly into the target and not buffered into the fragment first. The fragment parser is only used to set up the parser context.
26
28
It is similar to calling `document.write()`, scoped to an node.
27
29
28
30
## One-off patching
29
31
30
32
The most atomic form of patching is opening a container node for writing, creating a `WritableStream` for it.
31
33
This can be done with an API as such:
32
34
```js
33
-
constwritable=elementOrShadowRoot.patch();
35
+
constwritable=elementOrShadowRoot.streamHTML();
34
36
byteOrTextStream.pipeTo(writable);
35
37
```
36
38
37
39
A few details about one-off patching:
38
40
- Trying to patch an element that is currently being patched would abort the original stream.
39
-
- It is unclear whether patching should execute `<script>` elements found in the patch. It would potentially be an opt-in. See https://github.com/WICG/declarative-partial-updates/issues/40.
40
41
- Replacing the contents of an existing script would only execute the script if the original contents were empty (equivalent to `innerHTML` behavior).
41
42
42
-
43
43
To account for HTML sanitation, this API would have an "Unsafe" version and would accept a sanitizer in its option, like [`setHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML):
The API shape and naming is open to discussion. Seehttps://github.com/WICG/declarative-partial-updates/issues/42.
49
+
Also see detailed discussion athttps://github.com/whatwg/html/issues/11669, will amend this explainer once that's settled.
50
50
51
51
## Interleaved patching
52
52
53
-
In addition to invoking streaming using script, this proposal includes patching interleaved inside HTML content. An element such as `<script>` or`<template>` would have a special attribute that
53
+
In addition to invoking streaming using script, this proposal includes patching interleaved inside HTML content. A`<template>` would have a special attribute that
54
54
parses its content as raw text, finds the target element using attributes, and reroutes the raw text content to the target element:
* Note: there is an ongoing discussion whether this should be a `<script>` element instead. See https://github.com/whatwg/html/issues/11542.
64
-
65
63
A few details about interleaved patching:
66
-
- If a target is not found, the patching element remains in the DOM.
67
-
- The first patch for a target in the document stream replaces its content, and the next ones append. This allows interleaved streaming into the same target.
68
-
- If the patching element is not a direct child of `<body>`, the target element has to have a common ancestor with the patching element's parent.
69
-
- The patching element reference is tree scoped.
64
+
- If an outlet (an element with `contentname`) is not found, the patch template remains in the DOM.
65
+
- If the patching element is not a direct child of `<body>`, the outlet has to have a common ancestor with the patching element's parent.
66
+
- The patch template has to be in the same tree scope as the outlet.
67
+
- By default, the first patch in the stream replaces the entire contents of the outlet, like `replaceChildren`, and the next ones behave like `append`.
68
+
69
+
## Manipulating without replacing: `contentcommand`
70
+
71
+
To allow the patch to manipulate the DOM without replacing all the children, A `contentcommand` attribute
72
+
(values `replaceChildren`, `replaceWith`, `append`, `prepend`, `before`, `after`) can control where the contents of the patch are positioned.
73
+
74
+
An empty string means the default behavior (`replaceChildren` at start, then `append`).
75
+
76
+
Those behave with the same semantics as the equivalent DOM manipulation methods, and specifically as defined in https://github.com/whatwg/html/issues/11669.
77
+
78
+
## Avoiding overwriting with identical content
79
+
80
+
Some content might need to remain unchanged in certain conditions. For example, displaying a chat widget in all pages but the home, but not reloading it between pages.
81
+
For this, both the outlet and the patch can have a `contentrevision` attribute. If those match, the content is not applied.
70
82
71
83
## Reflection
72
84
73
-
Besides invoking a patch, there should be a few ways to reflect about the current status of patching and receive events/callbacks when a patch is underway.
85
+
Besides applying a patch, there should be a few ways to reflect about the current status of patching and receive events/callbacks when a patch is underway.
74
86
75
87
### CSS reflection
76
88
See https://github.com/w3c/csswg-drafts/issues/12579 and https://github.com/w3c/csswg-drafts/issues/12578.
77
89
78
-
Suggesting to add a couple of pseudo-classes: `:patching` and `:patch-pending`, that reflect the patching status of an element, in addition to a pseudo-class that reflects that an element's parser state is open (regardless of patching).
90
+
Suggesting to add a couple of pseudo-classes: `:updating` and `:pending`, that reflect the patching status of an element, in addition to a pseudo-class that reflects that an element's parser state is open (regardless of patching).
79
91
80
92
### JS status
81
93
@@ -90,10 +102,6 @@ Note that the HTML sanitizer works closely with the HTML parser, so it shouldn't
90
102
91
103
In addition, [Trusted types](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) would need streaming support in its policy, potentially by injecting a `TransformStream` into the patch. See https://github.com/w3c/trusted-types/issues/594.
92
104
93
-
## Enhancement - replace range instead of whole contents
94
-
Some use cases for out of order streaming require replacing parts of the element's children but not other parts, e.g. adding `<meta>` tags to the `<head>` or replacing only some `<option>`s in a `<select>`.
95
-
To achive that, the script and HTML APIs would receive an optional children range to replace, e.g. by having `patchstartafter` and `patchendbefore` attributes, and also allow similar options in the various JS APIs.
96
-
97
105
## Enhancement - JS API for interleaved patching
98
106
In addition to invoking interleaved patching as part of the main response, allow parsing an HTML stream and extracting patches from it into an existing element:
99
107
```js
@@ -106,7 +114,7 @@ When calling `patchInterleaved`, discovered patches are applied to the target co
106
114
## Potential enhancement - patch contents from URL
107
115
108
116
In addition to patching from a stream or interleaved in HTML, there are use-cases for patching by fetching a URL.
109
-
This can be done with a `patchsrc` attribute, or by reusing the `src` attribute if we go with the `<script>` element.
117
+
This can be done with a `patchsrc` attribute.
110
118
111
119
Enabling remote fetching of patch content would act as a script in terms of CSP, with a CORS-only request, and would be sanitized with the same HTML/trusted-types restrictions as patching using script.
0 commit comments