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

Filter certain DOM attributes (e.g. src) if value is empty string #18513

Merged
merged 4 commits into from
Apr 7, 2020

Conversation

bvaughn
Copy link
Contributor

@bvaughn bvaughn commented Apr 6, 2020

This is a breaking change!

So I have put it behind a new feature flag, enableFilterEmptyStringAttributesDOM.

Scope

This flag affects the following attributes/elements:

Attribute Elements
action form
formAction button, input
href a, area, base, link
src audio, embed, iframe, img, input, script, source,, track, video

Behavior before

React HTML
<img src={emptyStringValue} /> <img src="">

Behavior after

React HTML
<img src={emptyStringValue} /> <img>

Implementation

I am not very familiar with the details of the DOM render, but I think it makes the most sense to add this to the DOMProperty file. It seems to fit with the other filtering logic we have there.

This approach has a possible downside, since the logic only applies to attributes and does not consider the element type. (It's possible we may want to make an exception for some of the impacted elements I listed above?)

Loosely related to PR #15047

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Apr 6, 2020
@codesandbox-ci
Copy link

codesandbox-ci bot commented Apr 6, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 2399b82:

Sandbox Source
happy-pond-7x32r Configuration

@sizebot
Copy link

sizebot commented Apr 6, 2020

Details of bundled changes.

Comparing: 3278d24...2399b82

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom-server.browser.development.js +0.3% +0.1% 156.14 KB 156.59 KB 39.56 KB 39.59 KB UMD_DEV
ReactTestUtils-dev.js +0.2% +0.1% 64.85 KB 64.95 KB 17.48 KB 17.5 KB FB_WWW_DEV
react-dom-server.browser.production.min.js 🔺+0.3% 🔺+0.3% 23.32 KB 23.39 KB 8.57 KB 8.6 KB UMD_PROD
react-dom.profiling.min.js +0.1% 0.0% 127.64 KB 127.71 KB 39.75 KB 39.77 KB NODE_PROFILING
ReactDOM-dev.js +0.1% +0.1% 972.33 KB 973.75 KB 215.42 KB 215.64 KB FB_WWW_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.1% 1.2 KB 1.2 KB 706 B 707 B UMD_PROD
ReactDOM-prod.js 🔺+0.1% 🔺+0.1% 407.39 KB 407.79 KB 73.57 KB 73.65 KB FB_WWW_PROD
ReactDOM-profiling.js +0.1% +0.1% 418.2 KB 418.61 KB 75.4 KB 75.48 KB FB_WWW_PROFILING
react-dom-unstable-fizz.browser.production.min.js 0.0% -0.2% 1.02 KB 1.02 KB 619 B 618 B NODE_PROD
react-dom-test-utils.development.js 0.0% 0.0% 75.14 KB 75.14 KB 20.12 KB 20.12 KB UMD_DEV
ReactDOMTesting-dev.js 0.0% 0.0% 925.85 KB 926.29 KB 206.06 KB 206.1 KB FB_WWW_DEV
ReactDOMTesting-prod.js 0.0% 0.0% 392.11 KB 392.27 KB 71.34 KB 71.38 KB FB_WWW_PROD
ReactDOMTesting-profiling.js 0.0% 0.0% 392.11 KB 392.27 KB 71.34 KB 71.38 KB FB_WWW_PROFILING
react-dom-server.node.development.js +0.3% +0.1% 148.15 KB 148.58 KB 39.07 KB 39.1 KB NODE_DEV
react-dom-test-utils.production.min.js 0.0% -0.0% 13.1 KB 13.1 KB 4.81 KB 4.81 KB NODE_PROD
react-dom-server.node.production.min.js 🔺+0.3% 🔺+0.3% 23.62 KB 23.69 KB 8.7 KB 8.72 KB NODE_PROD
react-dom-server.browser.development.js +0.3% +0.1% 146.94 KB 147.36 KB 38.81 KB 38.84 KB NODE_DEV
react-dom-server.browser.production.min.js 🔺+0.3% 🔺+0.3% 23.21 KB 23.28 KB 8.54 KB 8.57 KB NODE_PROD
react-dom.development.js 0.0% 0.0% 931.11 KB 931.57 KB 203.13 KB 203.17 KB UMD_DEV
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 56.11 KB 56.11 KB 13.85 KB 13.85 KB UMD_DEV
react-dom.production.min.js 🔺+0.1% 🔺+0.1% 123.47 KB 123.54 KB 39.28 KB 39.31 KB UMD_PROD
ReactDOMForked-dev.js +0.1% +0.1% 972.33 KB 973.75 KB 215.42 KB 215.64 KB FB_WWW_DEV
ReactDOMServer-dev.js +0.9% +0.6% 153.06 KB 154.48 KB 38.75 KB 38.97 KB FB_WWW_DEV
react-dom.profiling.min.js +0.1% +0.1% 127.29 KB 127.36 KB 40.56 KB 40.6 KB UMD_PROFILING
ReactDOMForked-prod.js 🔺+0.1% 🔺+0.1% 407.39 KB 407.79 KB 73.57 KB 73.65 KB FB_WWW_PROD
ReactDOMServer-prod.js 🔺+0.9% 🔺+0.7% 52.11 KB 52.56 KB 12.6 KB 12.68 KB FB_WWW_PROD
react-dom.development.js 0.0% 0.0% 886.59 KB 887.01 KB 200.64 KB 200.68 KB NODE_DEV
ReactDOMForked-profiling.js +0.1% +0.1% 418.2 KB 418.61 KB 75.4 KB 75.48 KB FB_WWW_PROFILING
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 53.21 KB 53.21 KB 13.65 KB 13.65 KB NODE_DEV
react-dom.production.min.js 🔺+0.1% 🔺+0.1% 123.66 KB 123.73 KB 38.51 KB 38.53 KB NODE_PROD
react-dom-unstable-fizz.node.production.min.js 0.0% -0.1% 1.17 KB 1.17 KB 669 B 668 B NODE_PROD

ReactDOM: size: 🔺+0.3%, gzip: 🔺+0.3%

Size changes (experimental)

Generated by 🚫 dangerJS against 2399b82

@sizebot
Copy link

sizebot commented Apr 6, 2020

Details of bundled changes.

Comparing: 3278d24...2399b82

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.production.min.js 🔺+0.1% 0.0% 119.51 KB 119.58 KB 37.36 KB 37.38 KB NODE_PROD
react-dom-test-utils.development.js 0.0% 0.0% 75.12 KB 75.12 KB 20.11 KB 20.11 KB UMD_DEV
ReactDOMTesting-profiling.js 0.0% 0.0% 404.08 KB 404.25 KB 73.13 KB 73.17 KB FB_WWW_PROFILING
react-dom-server.browser.production.min.js 🔺+0.3% 🔺+0.3% 22.75 KB 22.82 KB 8.46 KB 8.49 KB NODE_PROD
react-dom.profiling.min.js +0.1% +0.1% 123.37 KB 123.44 KB 38.53 KB 38.55 KB NODE_PROFILING
react-dom-server.node.development.js +0.3% +0.1% 146.64 KB 147.06 KB 38.85 KB 38.89 KB NODE_DEV
react-dom-server.node.production.min.js 🔺+0.3% 🔺+0.3% 23.16 KB 23.23 KB 8.63 KB 8.65 KB NODE_PROD
ReactDOMForked-dev.js +0.1% +0.1% 1000.21 KB 1001.63 KB 221.43 KB 221.66 KB FB_WWW_DEV
ReactDOMForked-prod.js 🔺+0.1% 🔺+0.1% 420.45 KB 420.85 KB 75.47 KB 75.54 KB FB_WWW_PROD
react-dom.development.js +0.1% 0.0% 901.28 KB 901.74 KB 197.85 KB 197.89 KB UMD_DEV
ReactDOMForked-profiling.js +0.1% +0.1% 431.33 KB 431.73 KB 77.34 KB 77.41 KB FB_WWW_PROFILING
react-dom-server.browser.development.js +0.3% +0.1% 154.55 KB 155 KB 39.36 KB 39.38 KB UMD_DEV
react-dom-unstable-fizz.node.production.min.js 0.0% -0.2% 1.16 KB 1.16 KB 661 B 660 B NODE_PROD
react-dom.production.min.js 🔺+0.1% 🔺+0.1% 119.39 KB 119.46 KB 38.17 KB 38.19 KB UMD_PROD
react-dom-server.browser.production.min.js 🔺+0.3% 🔺+0.4% 22.85 KB 22.92 KB 8.48 KB 8.52 KB UMD_PROD
react-dom.profiling.min.js +0.1% 0.0% 123.11 KB 123.18 KB 39.38 KB 39.4 KB UMD_PROFILING
ReactDOMTesting-dev.js 0.0% 0.0% 952.39 KB 952.84 KB 211.66 KB 211.71 KB FB_WWW_DEV
react-dom.development.js 0.0% 0.0% 858 KB 858.42 KB 195.38 KB 195.42 KB NODE_DEV
ReactDOMTesting-prod.js 0.0% 0.0% 404.08 KB 404.25 KB 73.13 KB 73.17 KB FB_WWW_PROD
react-dom-server.browser.development.js +0.3% +0.1% 145.42 KB 145.85 KB 38.6 KB 38.63 KB NODE_DEV
ReactDOM-dev.js +0.1% +0.1% 1000.21 KB 1001.63 KB 221.43 KB 221.66 KB FB_WWW_DEV
ReactDOMServer-dev.js +0.9% +0.6% 156.56 KB 157.98 KB 39.68 KB 39.92 KB FB_WWW_DEV
ReactDOM-prod.js 🔺+0.1% 🔺+0.1% 420.45 KB 420.85 KB 75.47 KB 75.54 KB FB_WWW_PROD
ReactDOMServer-prod.js 🔺+0.8% 🔺+0.6% 52.85 KB 53.29 KB 12.76 KB 12.84 KB FB_WWW_PROD
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 4.42 KB 4.42 KB 1.54 KB 1.54 KB NODE_DEV
ReactDOM-profiling.js +0.1% +0.1% 431.33 KB 431.73 KB 77.34 KB 77.41 KB FB_WWW_PROFILING
react-dom-test-utils.production.min.js 0.0% -0.0% 13.09 KB 13.09 KB 4.8 KB 4.8 KB NODE_PROD
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 56.1 KB 56.1 KB 13.84 KB 13.84 KB UMD_DEV
ReactTestUtils-dev.js +0.2% +0.1% 64.85 KB 64.95 KB 17.47 KB 17.5 KB FB_WWW_DEV
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 53.2 KB 53.2 KB 13.64 KB 13.65 KB NODE_DEV

Size changes (stable)

Generated by 🚫 dangerJS against 2399b82

@sebmarkbage
Copy link
Collaborator

Is <base href="" /> useful?

@sebmarkbage
Copy link
Collaborator

sebmarkbage commented Apr 6, 2020

SSR should probably just work, in the sense that it just removes it from the HTML, since it calls this.

if (shouldRemoveAttribute(name, value, propertyInfo, false)) {

You could add some tests to the server integration tests.

Note sure if that causes problems in practice but I can't think of one other than maybe <base href="" />.

) {
return true;
}
}
Copy link
Collaborator

@sebmarkbage sebmarkbage Apr 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should move below line 178 here into the propertyInfo !== null check so we only do that once.

We also probably should not filter this for isCustomComponentTag since custom elements could have all kinds of semantics where this matters. We only try to fix the built-ins.

Copy link
Contributor Author

@bvaughn bvaughn Apr 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should move below line 178 here into the propertyInfo !== null check so we only do that once.

Oh, yeah. Duh.

We also probably should not filter this for isCustomComponentTag

Moving it to where we already check propertyInfo will prevent it from applying to custom component tags. 👍

@sebmarkbage
Copy link
Collaborator

Should this be accompanied with a warning too since this is likely a bug and you should be setting the value to undefined or null if you want this behavior?

@sebmarkbage
Copy link
Collaborator

Seems like a reasonable approach that's inline with sanitizeURL.

@bvaughn
Copy link
Contributor Author

bvaughn commented Apr 7, 2020

Is <base href="" /> useful?

I don't think so.

Mozilla says:

If the document contains no <base> elements, baseURI defaults to location.href.

Based on my own testing (Chrome, Firefox, Edge) adding <base href=""> does not affect the document.baseURI value (still the default location.href).

SSR should probably just work, in the sense that it just removes it from the HTML, since it calls this.

Great. I was hoping that was the case, and tests seemed to confirm.

Should this be accompanied with a warning too since this is likely a bug and you should be setting the value to undefined or null if you want this behavior?

Seems reasonable to add a warning.

Seems odd to suggest "null or undefined" rather than "" when we treat them the same. (undefined seems at least as likely to be a potential mistake as ""?)

How about:

Invalid value "" (empty string) for attribute %s. Use null instead to indicate an empty value.

…pty strings

This prevents e.g. <img src=""> from making an unnecessar HTTP request for certain browsers.
@bvaughn bvaughn force-pushed the dom-empty-string-attribute-filter branch from 06d07c0 to f8ac921 Compare April 7, 2020 00:45
@bvaughn bvaughn marked this pull request as ready for review April 7, 2020 00:46
@bvaughn bvaughn requested a review from sebmarkbage April 7, 2020 00:55
@sebmarkbage
Copy link
Collaborator

Based on my own testing (Chrome, Firefox, Edge) adding does not affect the document.baseURI value (still the default location.href).

What if there's another <base href="somethingelse" /> before it?

<base href="something/else/" />
<base href="" />

vs

<base href="something/else/" />
<base />

@bvaughn
Copy link
Contributor Author

bvaughn commented Apr 7, 2020

What if there's another <base href="somethingelse" /> before it?

Same behavior. The empty one does not modify the document.baseURI value.

Edit Is it even valid to have multiple ones on the same page? Looks like the browser only pays attention to the first one, regardless.

@bvaughn
Copy link
Contributor Author

bvaughn commented Apr 7, 2020

The spec says:

There must be no more than one base element per document.

@JeromeDeLeon
Copy link

What about class attribute? Can it be added?

@sebmarkbage
Copy link
Collaborator

The warning should probably recommend not rendering the element at all as the primary recommendation and only null if that doesn’t work (i.e. if someone is too lazy to refactor).

@sebmarkbage
Copy link
Collaborator

I could see someone using <a href=“”> for a reload link or open this page in a new tab.

@bvaughn
Copy link
Contributor Author

bvaughn commented Apr 7, 2020

The warning should probably recommend not rendering the element at all as the primary recommendation and only null if that doesn’t work (i.e. if someone is too lazy to refactor).

Yeah, that sounds like kind of a long and confusing warning 😉 I can change the rec. to "don't render at all" though I guess.

I could see someone using <a href=“”> for a reload link or open this page in a new tab.

Yeah... that's the use case that seems most likely I guess. They could always use <a href=“#”> instead?

What about class attribute? Can it be added?

@JeromeDeLeon No. This is something different.

@bvaughn
Copy link
Contributor Author

bvaughn commented Apr 7, 2020

I dunno. Is this warning better?

Invalid value "" (empty string) for attribute %s. This will be a no-op. Either do not render the element at all or use null instead to indicate an empty value.

@koenpunt
Copy link

koenpunt commented Apr 7, 2020

I'm wondering why it would be necessary to filter these?

@Zeko369
Copy link

Zeko369 commented Apr 7, 2020

@koenpunt to prevent unnecessary network requests, for example <img src=""/> would make a GET request to the current url

@gaearon
Copy link
Collaborator

gaearon commented Apr 7, 2020

@bvaughn We were recently going through React warnings with @rachelnabors and she mentioned “no-op” is unfamiliar jargon. We’ll need to take a pass over them. But for now, I suggest:

An empty string ("") was passed to the %s attribute. This may cause the browser to download the whole page again over the network. To fix this, either do not render the element at all or pass null to %s instead of an empty string.

@bvaughn
Copy link
Contributor Author

bvaughn commented Apr 7, 2020

I appreciate the re-worked error message, @gaearon! These things are difficult to write 😅

That being said, I think the middle sentence of the one you mentioned is too img+src specific. Happy to use the first and third sentence though.

I think this makes sense for every case except maybe <a href="">. (I'm still a little uncertain about that one.)

@gaearon
Copy link
Collaborator

gaearon commented Apr 7, 2020

I think the middle sentence of the one you mentioned is too img+src specific

Maybe, but can we leave this wording for src then? The reason I'm asking is because it looks like we're just being pedantic. It's not obvious from the warning just how bad it is.

@bvaughn
Copy link
Contributor Author

bvaughn commented Apr 7, 2020

Sure.

@bvaughn bvaughn merged commit dc49ea1 into facebook:master Apr 7, 2020
@bvaughn bvaughn deleted the dom-empty-string-attribute-filter branch April 7, 2020 16:52
eps1lon added a commit that referenced this pull request Jan 30, 2024
Treat `<a href="" />` the same with and without
`enableFilterEmptyStringAttributesDOM`

in #18513 we started to warn and
ignore for empty `href` and `src` props since it usually hinted at a
mistake. However, for anchor tags there's a valid use case since `<a
href=""></a>` will by spec render a link to the current page. It could
be used to reload the page without having to rely on browser
affordances.

The implementation for Fizz is in the spirit of
#21153. I gated the fork behind
the flag so that the fork is DCE'd when the flag is off.
github-actions bot pushed a commit that referenced this pull request Jan 30, 2024
Treat `<a href="" />` the same with and without
`enableFilterEmptyStringAttributesDOM`

in #18513 we started to warn and
ignore for empty `href` and `src` props since it usually hinted at a
mistake. However, for anchor tags there's a valid use case since `<a
href=""></a>` will by spec render a link to the current page. It could
be used to reload the page without having to rely on browser
affordances.

The implementation for Fizz is in the spirit of
#21153. I gated the fork behind
the flag so that the fork is DCE'd when the flag is off.

DiffTrain build for [f3ce87a](f3ce87a)
gaearon pushed a commit that referenced this pull request Feb 3, 2024
Treat `<a href="" />` the same with and without
`enableFilterEmptyStringAttributesDOM`

in #18513 we started to warn and
ignore for empty `href` and `src` props since it usually hinted at a
mistake. However, for anchor tags there's a valid use case since `<a
href=""></a>` will by spec render a link to the current page. It could
be used to reload the page without having to rely on browser
affordances.

The implementation for Fizz is in the spirit of
#21153. I gated the fork behind
the flag so that the fork is DCE'd when the flag is off.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…28124)

Treat `<a href="" />` the same with and without
`enableFilterEmptyStringAttributesDOM`

in facebook#18513 we started to warn and
ignore for empty `href` and `src` props since it usually hinted at a
mistake. However, for anchor tags there's a valid use case since `<a
href=""></a>` will by spec render a link to the current page. It could
be used to reload the page without having to rely on browser
affordances.

The implementation for Fizz is in the spirit of
facebook#21153. I gated the fork behind
the flag so that the fork is DCE'd when the flag is off.
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.

8 participants