Skip to content

Commit e02d07f

Browse files
authored
Merge pull request #56 from WICG/noamr-patch-3
Update patching-explainer.md
2 parents f43f5a0 + 8b7c020 commit e02d07f

File tree

1 file changed

+32
-24
lines changed

1 file changed

+32
-24
lines changed

patching-explainer.md

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Patching (Partial, interleaved HTML streaming)
1+
# Interleaved HTML streaming (patching)
22

33
## Motivation
44
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
2020
## Anatomy of a patch
2121

2222
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.
2425

2526
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.
2628
It is similar to calling `document.write()`, scoped to an node.
2729

2830
## One-off patching
2931

3032
The most atomic form of patching is opening a container node for writing, creating a `WritableStream` for it.
3133
This can be done with an API as such:
3234
```js
33-
const writable = elementOrShadowRoot.patch();
35+
const writable = elementOrShadowRoot.streamHTML();
3436
byteOrTextStream.pipeTo(writable);
3537
```
3638

3739
A few details about one-off patching:
3840
- 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.
4041
- Replacing the contents of an existing script would only execute the script if the original contents were empty (equivalent to `innerHTML` behavior).
4142

42-
4343
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):
4444
```js
45-
byteOrTextStream.pipeTo(elementOrShadowRoot.patch({santizer}));
46-
byteOrTextStream.pipeTo(elementOrShadowRoot.patchUnsafe({santizer}));
45+
byteOrTextStream.pipeTo(elementOrShadowRoot.streamHTML({santizer}));
46+
byteOrTextStream.pipeTo(elementOrShadowRoot.streamHTMLUnsafe({santizer}));
4747
```
4848

49-
The API shape and naming is open to discussion. See https://github.com/WICG/declarative-partial-updates/issues/42.
49+
Also see detailed discussion at https://github.com/whatwg/html/issues/11669, will amend this explainer once that's settled.
5050

5151
## Interleaved patching
5252

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
5454
parses its content as raw text, finds the target element using attributes, and reroutes the raw text content to the target element:
5555

5656
```html
57-
<option patchid=gallery>Loading...</section>
57+
<section contentname=gallery>Loading...</section>
5858

5959
<!-- later -->
60-
<template patchfor=gallery>Actual gallery content</template>
60+
<template contentfor=gallery>Actual gallery content</template>
6161
```
6262

63-
* Note: there is an ongoing discussion whether this should be a `<script>` element instead. See https://github.com/whatwg/html/issues/11542.
64-
6563
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.
7082

7183
## Reflection
7284

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.
7486

7587
### CSS reflection
7688
See https://github.com/w3c/csswg-drafts/issues/12579 and https://github.com/w3c/csswg-drafts/issues/12578.
7789

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).
7991

8092
### JS status
8193

@@ -90,10 +102,6 @@ Note that the HTML sanitizer works closely with the HTML parser, so it shouldn't
90102

91103
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.
92104

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-
97105
## Enhancement - JS API for interleaved patching
98106
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:
99107
```js
@@ -106,7 +114,7 @@ When calling `patchInterleaved`, discovered patches are applied to the target co
106114
## Potential enhancement - patch contents from URL
107115

108116
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.
110118

111119
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.
112120

0 commit comments

Comments
 (0)