Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fizz] Support special HTML/SVG/MathML tags to suspend #21113

Merged
merged 6 commits into from
Mar 27, 2021

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Mar 26, 2021

When we write a new segment to be inserted by script, the parent needs to be a certain tag so that the children get the correct namespace. Additionally, table tags are ignored by the parser unless they're inside a table. So I needed to encode those specially.

I needed access to the insertion mode in the writing phase which is after a Task is complete. So I opted to just always store the formatContext on the Segment instead of the Task.

We allow the root of the stream to be non-HTML and in that case we don't know what namespace we're already in. So we allow that to be passed as an option now using namespaceURI. I tried to be clever and use custom elements that work in either environment but there are really two ways to stream into the root that makes this tricky. <svg>shell .... rest-of-stream... end-of-file or <svg>shell</svg>... rest-of-stream ... end-of-file So I caved and just added a root option instead. Probably good for warnings etc. anyway.

Additionally we insert dummy leaf nodes in two cases. Either as placeholders or to identify text fallbacks. These nodes needs to be allowed in the parent. Interestingly in <colgroup> there's only one tag other than <col> that's allowed and that's <template>. I switched these dummy leaf nodes to <template> instead because that works in every context. I'm not sure if that might have other negative performance considerations. We do have access to the parent context now so we could switch these tags based on the parent.

Notably, we don't use <template> as intended. I.e. we still don't use them as container wrappers. Because our purpose is to move these nodes, not clone them. They also don't automatically let us switch namespaces. So it's not better for us.

cc @nickbalestra

The table modes are special in that its children can't be created outside
a table context so we need the segment container to be wrapped in a table.
It works the same otherwise. It's just that this context needs to outlive
the task so that I can use it when writing the segment.
These can be used in any parent. At least outside IE11. Not sure yet what
happens in IE11 to these.

Not sure if these are bad for perf since they're special nodes.
This allows us to insert the correct wrappers when streaming into an
existing non-HTML tree.
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Mar 26, 2021
@sizebot
Copy link

sizebot commented Mar 26, 2021

Comparing: 556644e...91e035e

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 122.93 kB 122.93 kB = 39.53 kB 39.53 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 129.45 kB 129.45 kB = 41.61 kB 41.61 kB
facebook-www/ReactDOM-prod.classic.js = 407.25 kB 407.25 kB = 75.67 kB 75.66 kB
facebook-www/ReactDOM-prod.modern.js = 395.51 kB 395.51 kB = 73.71 kB 73.71 kB
facebook-www/ReactDOMForked-prod.classic.js = 407.25 kB 407.25 kB = 75.67 kB 75.67 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.node.production.min.js +13.95% 12.47 kB 14.21 kB +9.91% 4.43 kB 4.87 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.browser.production.min.js +13.09% 12.28 kB 13.89 kB +9.68% 4.41 kB 4.84 kB
oss-experimental/react-dom/umd/react-dom-unstable-fizz.browser.production.min.js +12.83% 12.48 kB 14.08 kB +9.57% 4.50 kB 4.93 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.node.development.js +10.70% 53.23 kB 58.93 kB +6.53% 13.61 kB 14.50 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.browser.development.js +10.68% 53.29 kB 58.98 kB +6.37% 13.71 kB 14.58 kB
oss-experimental/react-dom/umd/react-dom-unstable-fizz.browser.development.js +10.66% 56.03 kB 62.00 kB +6.41% 13.87 kB 14.76 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.node.production.min.js +13.95% 12.47 kB 14.21 kB +9.91% 4.43 kB 4.87 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.browser.production.min.js +13.09% 12.28 kB 13.89 kB +9.68% 4.41 kB 4.84 kB
oss-experimental/react-dom/umd/react-dom-unstable-fizz.browser.production.min.js +12.83% 12.48 kB 14.08 kB +9.57% 4.50 kB 4.93 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.node.development.js +10.70% 53.23 kB 58.93 kB +6.53% 13.61 kB 14.50 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.browser.development.js +10.68% 53.29 kB 58.98 kB +6.37% 13.71 kB 14.58 kB
oss-experimental/react-dom/umd/react-dom-unstable-fizz.browser.development.js +10.66% 56.03 kB 62.00 kB +6.41% 13.87 kB 14.76 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-server.development.js +0.55% 5.41 kB 5.44 kB +0.45% 1.55 kB 1.56 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-server.development.js +0.55% 5.41 kB 5.44 kB +0.45% 1.55 kB 1.56 kB
oss-experimental/react-server/cjs/react-server.production.min.js +0.39% 9.50 kB 9.53 kB +0.70% 3.30 kB 3.32 kB
oss-stable/react-server/cjs/react-server.production.min.js +0.39% 9.50 kB 9.53 kB +0.70% 3.30 kB 3.32 kB
oss-experimental/react-server/cjs/react-server.development.js +0.32% 36.43 kB 36.55 kB +0.23% 9.53 kB 9.56 kB
oss-stable/react-server/cjs/react-server.development.js +0.32% 36.43 kB 36.55 kB +0.23% 9.53 kB 9.56 kB

Generated by 🚫 dangerJS against 91e035e

@gaearon
Copy link
Collaborator

gaearon commented Mar 26, 2021

Woah. I haven’t even thought of this as a possible problem.

@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Mar 26, 2021

There are other tags that are technically not allowed but they do work in that they're not ignored inside a div. As far as I could tell by testing a bunch of tags.

@sophiebits
Copy link
Contributor

The HTML parsing spec is accurate to what real browsers do, I believe, and in React’s validateDOMNesting I attempted to boil down the problematic cases (only the ones that would cause parsing issues). And IE11 doesn’t support template – I imagine you’re still trying to support it?

@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Mar 27, 2021

Yea I've been using the validation code as a quick reference along with the spec. Easier to read. :)

I left a comment about IE11 in one of the commits. One thing I wasn't sure about is what it actually does though and whether it'll work for our purposes.

We don't actually use its runtime features where it's child nodes go into this separate thing. It's only a leaf.

That should work at least for the common case in IE11 since browsers do parse unknown tags in most contexts.

It might not work in special contexts though. The question is how common will that combination be and what actually happens because it might not be the same as the spec.

I haven't tested.

@sebmarkbage sebmarkbage merged commit 32d6f39 into facebook:master Mar 27, 2021
facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Apr 6, 2021
Summary:
This sync includes the following changes:
- **[c9aab1c9d](facebook/react@c9aab1c9d )**: react-refresh@0.10.0 //<Dan Abramov>//
- **[516b76b9a](facebook/react@516b76b9a )**: [Fast Refresh] Support callthrough HOCs ([#21104](facebook/react#21104)) //<Dan Abramov>//
- **[0853aab74](facebook/react@0853aab74 )**: Log all errors to console.error by default ([#21130](facebook/react#21130)) //<Sebastian Markbåge>//
- **[d1294c9d4](facebook/react@d1294c9d4 )**: Add global onError handler ([#21129](facebook/react#21129)) //<Sebastian Markbåge>//
- **[64983aab5](facebook/react@64983aab5 )**: Remove redundant setUpdatePriority call ([#21127](facebook/react#21127)) //<Andrew Clark>//
- **[634cc52e6](facebook/react@634cc52e6 )**: Delete dead variable: currentEventWipLanes ([#21123](facebook/react#21123)) //<Andrew Clark>//
- **[1102224bb](facebook/react@1102224bb )**: Fix: flushSync changes priority inside effect ([#21122](facebook/react#21122)) //<Andrew Clark>//
- **[dbe98a5aa](facebook/react@dbe98a5aa )**: Move sync task queue to its own module ([#21109](facebook/react#21109)) //<Andrew Clark>//
- **[3ba5c8737](facebook/react@3ba5c8737 )**: Remove Scheduler indirection ([#21107](facebook/react#21107)) //<Andrew Clark>//
- **[46b68eaf6](facebook/react@46b68eaf6 )**: Delete LanePriority type ([#21090](facebook/react#21090)) //<Andrew Clark>//
- **[dcd13045e](facebook/react@dcd13045e )**: Use Lane to track root callback priority ([#21089](facebook/react#21089)) //<Andrew Clark>//
- **[5f21a9fca](facebook/react@5f21a9fca )**: Clean up host pointers in level 2 of clean-up flag ([#21112](facebook/react#21112)) //<Andrew Clark>//
- **[32d6f39ed](facebook/react@32d6f39ed )**: [Fizz] Support special HTML/SVG/MathML tags to suspend ([#21113](facebook/react#21113)) //<Sebastian Markbåge>//
- **[a77dd13ed](facebook/react@a77dd13ed )**: Delete enableDiscreteEventFlushingChange ([#21110](facebook/react#21110)) //<Andrew Clark>//
- **[048ee4c0c](facebook/react@048ee4c0c )**: Use `act` in fuzz tester to flush expired work ([#21108](facebook/react#21108)) //<Andrew Clark>//
- **[556644e23](facebook/react@556644e23 )**: Fix plurals ([#21106](facebook/react#21106)) //<Sebastian Markbåge>//
- **[8b741437b](facebook/react@8b741437b )**: Rename SuspendedWork to Task ([#21105](facebook/react#21105)) //<Sebastian Markbåge>//
- **[38a1aedb4](facebook/react@38a1aedb4 )**: [Fizz] Add FormatContext and Refactor Work ([#21103](facebook/react#21103)) //<Sebastian Markbåge>//
- **[1b7e471b9](facebook/react@1b7e471b9 )**: React Native New Architecture: Support passing nativeViewTag to getInspectorDataForViewAtPoint callback, for React DevTools compat ([#21080](facebook/react#21080)) //<Joshua Gross>//
- **[4a99c5c3a](facebook/react@4a99c5c3a )**: Use highest priority lane to detect interruptions ([#21088](facebook/react#21088)) //<Andrew Clark>//
- **[77be52729](facebook/react@77be52729 )**: Remove LanePriority from computeExpirationTime ([#21087](facebook/react#21087)) //<Andrew Clark>//
- **[3221e8fba](facebook/react@3221e8fba )**: Remove LanePriority from getBumpedLaneForHydration ([#21086](facebook/react#21086)) //<Andrew Clark>//
- **[05ec0d764](facebook/react@05ec0d764 )**: Entangled expired lanes with SyncLane ([#21083](facebook/react#21083)) //<Andrew Clark>//
- **[03ede83d2](facebook/react@03ede83d2 )**: Use EventPriority to track update priority ([#21082](facebook/react#21082)) //<Andrew Clark>//
- **[a63f0953b](facebook/react@a63f0953b )**: Delete SyncBatchedLane ([#21061](facebook/react#21061)) //<Ricky>//
- **[fa868d6be](facebook/react@fa868d6be )**: Make opaque EventPriority type a Lane internally ([#21065](facebook/react#21065)) //<Andrew Clark>//
- **[eb58c3909](facebook/react@eb58c3909 )**: react-hooks/exhaustive-deps: Handle optional chained methods as dependency ([#20204](facebook/react#20204)) ([#20247](facebook/react#20247)) //<Ari Perkkiö>//
- **[7b84dbd16](facebook/react@7b84dbd16 )**: Fail build on deep requires in npm packages ([#21063](facebook/react#21063)) //<Dan Abramov>//
- **[2c9d8efc8](facebook/react@2c9d8efc8 )**: Add react-reconciler/constants entry point ([#21062](facebook/react#21062)) //<Dan Abramov>//
- **[d0eaf7829](facebook/react@d0eaf7829 )**: Move priorities to separate import to break cycle ([#21060](facebook/react#21060)) //<Andrew Clark>//
- **[435cff986](facebook/react@435cff986 )**: [Fizz] Expose callbacks in options for when various stages of the content is done ([#21056](facebook/react#21056)) //<Sebastian Markbåge>//
- **[25bfa287f](facebook/react@25bfa287f )**: [Experiment] Add feature flag for more aggressive memory clean-up of deleted fiber trees ([#21039](facebook/react#21039)) //<Benoit Girard>//
- **[8fe7810e7](facebook/react@8fe7810e7 )**: Remove already completed comment ([#21054](facebook/react#21054)) //<Sebastian Markbåge>//
- **[6c3202b1e](facebook/react@6c3202b1e )**: [Fizz] Use identifierPrefix to avoid conflicts within the same response ([#21037](facebook/react#21037)) //<Sebastian Markbåge>//
- **[dcdf8de7e](facebook/react@dcdf8de7e )**: Remove discrete lanes and priorities ([#21040](facebook/react#21040)) //<Andrew Clark>//
- **[ca99ae97b](facebook/react@ca99ae97b )**: Replace some flushExpired callsites ([#20975](facebook/react#20975)) //<Ricky>//
- **[1fafac002](facebook/react@1fafac002 )**: Use SyncLane for discrete event hydration ([#21038](facebook/react#21038)) //<Andrew Clark>//

Changelog:
[General][Changed] - React Native sync for revisions 6d3ecb7...c9aab1c

jest_e2e[run_all_tests]

Reviewed By: JoshuaGross

Differential Revision: D27436763

fbshipit-source-id: da79a41e26bffdcdacd293178062edf098e9b58a
acdlite pushed a commit to acdlite/react that referenced this pull request Apr 11, 2021
* Encode tables as a special insertion mode

The table modes are special in that its children can't be created outside
a table context so we need the segment container to be wrapped in a table.

* Move formatContext from Task to Segment

It works the same otherwise. It's just that this context needs to outlive
the task so that I can use it when writing the segment.

* Use template tag for placeholders and inserted dummy nodes with IDs

These can be used in any parent. At least outside IE11. Not sure yet what
happens in IE11 to these.

Not sure if these are bad for perf since they're special nodes.

* Add special wrappers around inserted segments depending on their insertion mode

* Allow the root namespace to be configured

This allows us to insert the correct wrappers when streaming into an
existing non-HTML tree.

* Add comment
acdlite pushed a commit to acdlite/react that referenced this pull request Apr 13, 2021
* Encode tables as a special insertion mode

The table modes are special in that its children can't be created outside
a table context so we need the segment container to be wrapped in a table.

* Move formatContext from Task to Segment

It works the same otherwise. It's just that this context needs to outlive
the task so that I can use it when writing the segment.

* Use template tag for placeholders and inserted dummy nodes with IDs

These can be used in any parent. At least outside IE11. Not sure yet what
happens in IE11 to these.

Not sure if these are bad for perf since they're special nodes.

* Add special wrappers around inserted segments depending on their insertion mode

* Allow the root namespace to be configured

This allows us to insert the correct wrappers when streaming into an
existing non-HTML tree.

* Add comment
acdlite pushed a commit to acdlite/react that referenced this pull request Apr 16, 2021
* Encode tables as a special insertion mode

The table modes are special in that its children can't be created outside
a table context so we need the segment container to be wrapped in a table.

* Move formatContext from Task to Segment

It works the same otherwise. It's just that this context needs to outlive
the task so that I can use it when writing the segment.

* Use template tag for placeholders and inserted dummy nodes with IDs

These can be used in any parent. At least outside IE11. Not sure yet what
happens in IE11 to these.

Not sure if these are bad for perf since they're special nodes.

* Add special wrappers around inserted segments depending on their insertion mode

* Allow the root namespace to be configured

This allows us to insert the correct wrappers when streaming into an
existing non-HTML tree.

* Add comment
acdlite pushed a commit to acdlite/react that referenced this pull request Apr 16, 2021
* Encode tables as a special insertion mode

The table modes are special in that its children can't be created outside
a table context so we need the segment container to be wrapped in a table.

* Move formatContext from Task to Segment

It works the same otherwise. It's just that this context needs to outlive
the task so that I can use it when writing the segment.

* Use template tag for placeholders and inserted dummy nodes with IDs

These can be used in any parent. At least outside IE11. Not sure yet what
happens in IE11 to these.

Not sure if these are bad for perf since they're special nodes.

* Add special wrappers around inserted segments depending on their insertion mode

* Allow the root namespace to be configured

This allows us to insert the correct wrappers when streaming into an
existing non-HTML tree.

* Add comment
acdlite pushed a commit to acdlite/react that referenced this pull request Apr 19, 2021
* Encode tables as a special insertion mode

The table modes are special in that its children can't be created outside
a table context so we need the segment container to be wrapped in a table.

* Move formatContext from Task to Segment

It works the same otherwise. It's just that this context needs to outlive
the task so that I can use it when writing the segment.

* Use template tag for placeholders and inserted dummy nodes with IDs

These can be used in any parent. At least outside IE11. Not sure yet what
happens in IE11 to these.

Not sure if these are bad for perf since they're special nodes.

* Add special wrappers around inserted segments depending on their insertion mode

* Allow the root namespace to be configured

This allows us to insert the correct wrappers when streaming into an
existing non-HTML tree.

* Add comment
koto pushed a commit to koto/react that referenced this pull request Jun 15, 2021
* Encode tables as a special insertion mode

The table modes are special in that its children can't be created outside
a table context so we need the segment container to be wrapped in a table.

* Move formatContext from Task to Segment

It works the same otherwise. It's just that this context needs to outlive
the task so that I can use it when writing the segment.

* Use template tag for placeholders and inserted dummy nodes with IDs

These can be used in any parent. At least outside IE11. Not sure yet what
happens in IE11 to these.

Not sure if these are bad for perf since they're special nodes.

* Add special wrappers around inserted segments depending on their insertion mode

* Allow the root namespace to be configured

This allows us to insert the correct wrappers when streaming into an
existing non-HTML tree.

* Add comment
sebmarkbage added a commit that referenced this pull request Sep 5, 2023
In #21113 I moved this over to the
segment from the task. This partially reverts this two use two fields
instead. I was just trying to micro-optimize by reusing a single field.

This is really conceptually two different values. Task is keeping track
of the working state of the currently executing context.

The segment just needs to keep track of which parent context it was
created in so that it can be wrapped correctly when a segment is
written. We just happened to rely on the working state returning to the
top before completing.

The main motivation is that there is no `segment` for replaying.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
In facebook#21113 I moved this over to the
segment from the task. This partially reverts this two use two fields
instead. I was just trying to micro-optimize by reusing a single field.

This is really conceptually two different values. Task is keeping track
of the working state of the currently executing context.

The segment just needs to keep track of which parent context it was
created in so that it can be wrapped correctly when a segment is
written. We just happened to rely on the working state returning to the
top before completing.

The main motivation is that there is no `segment` for replaying.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants