2015-12-03 23:02:55 +08:00
|
|
|
/**
|
2018-09-08 06:11:23 +08:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2015-12-03 23:02:55 +08:00
|
|
|
*
|
2017-09-25 04:48:13 +08:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2015-12-03 23:02:55 +08:00
|
|
|
*/
|
|
|
|
|
|
2017-11-05 19:58:36 +08:00
|
|
|
import {
|
|
|
|
|
registrationNameModules,
|
|
|
|
|
possibleRegistrationNames,
|
2019-08-14 22:32:42 +08:00
|
|
|
} from 'legacy-events/EventPluginRegistry';
|
2018-06-19 23:03:45 +08:00
|
|
|
import warning from 'shared/warning';
|
2017-10-26 02:07:54 +08:00
|
|
|
|
2017-11-05 19:58:36 +08:00
|
|
|
import {
|
|
|
|
|
ATTRIBUTE_NAME_CHAR,
|
2018-08-15 03:51:33 +08:00
|
|
|
BOOLEAN,
|
Refactor DOM attribute code (take two) (#11815)
* Harden tests around init/addition/update/removal of aliased attributes
I noticed some patterns weren't being tested.
* Call setValueForProperty() for null and undefined
The branching before the call is unnecessary because setValueForProperty() already
has an internal branch that delegates to deleteValueForProperty() for null and
undefined through the shouldIgnoreValue() check.
The goal is to start unifying these methods because their separation doesn't
reflect the current behavior (e.g. for unknown properties) anymore, and obscures
what actually happens with different inputs.
* Inline deleteValueForProperty() into setValueForProperty()
Now we don't read propertyInfo twice in this case.
I also dropped a few early returns. I added them a while ago when we had
Stack-only tracking of DOM operations, and some operations were being
counted twice because of how this code is structured. This isn't a problem
anymore (both because we don't track operations, and because I've just
inlined this method call).
* Inline deleteValueForAttribute() into setValueForAttribute()
The special cases for null and undefined already exist in setValueForAttribute().
* Delete some dead code
* Make setValueForAttribute() a branch of setValueForProperty()
Their naming is pretty confusing by now. For example setValueForProperty()
calls setValueForAttribute() when shouldSetAttribute() is false (!). I want
to refactor (as in, inline and then maybe factor it out differently) the relation
between them. For now, I'm consolidating the callers to use setValueForProperty().
* Make it more obvious where we skip and when we reset attributes
The naming of these methods is still very vague and conflicting in some cases.
Will need further work.
* Rewrite setValueForProperty() with early exits
This makes the flow clearer in my opinion.
* Move shouldIgnoreValue() into DOMProperty
It was previously duplicated.
It's also suspiciously similar in purpose to shouldTreatAttributeValueAsNull()
so I want to see if there is a way to unify them.
* Use more specific methods for testing validity
* Unify shouldTreatAttributeValueAsNull() and shouldIgnoreValue()
* Remove shouldSetAttribute()
Its naming was confusing and it was used all over the place instead of more specific checks.
Now that we only have one call site, we might as well inline and get rid of it.
* Remove unnecessary condition
* Remove another unnecessary condition
* Add Flow coverage
* Oops
* Fix lint (ESLint complains about Flow suppression)
* Fix treatment of Symbol/Function values on boolean attributes
They weren't being properly skipped because of the early return.
I added tests for this case.
* Avoid getPropertyInfo() calls
I think this PR looks worse on benchmarks because we have to read propertyInfo in different places.
Originally I tried to get rid of propertyInfo, but looks like it's important for performance after all.
So now I'm going into the opposite direction, and precompute propertyInfo as early as possible, and then just pass it around.
This way we can avoid extra lookups but keep functions nice and modular.
* Pass propertyInfo as argument to getValueForProperty()
It always exists because this function is only called for known properties.
* Make it clearer this branch is boolean-specific
I wrote this and then got confused myself.
* Memoize whether propertyInfo accepts boolean value
Since we run these checks for all booleans, might as well remember it.
* Fix a crash when numeric property is given a Symbol
* Record attribute table
The changes reflect that SSR doesn't crash with symbols anymore (and just warns, consistently with the client).
* Refactor attribute initialization
Instead of using flags, explicitly group similar attributes/properties.
* Optimization: we know built-in attributes are never invalid
* Use strict comparison
* Rename methods for clarity
* Lint nit
* Minor tweaks
* Document all the different attribute types
2017-12-11 00:58:38 +08:00
|
|
|
RESERVED,
|
|
|
|
|
shouldRemoveAttributeWithWarning,
|
|
|
|
|
getPropertyInfo,
|
2017-11-05 19:58:36 +08:00
|
|
|
} from './DOMProperty';
|
2017-11-03 03:50:03 +08:00
|
|
|
import isCustomComponent from './isCustomComponent';
|
|
|
|
|
import possibleStandardNames from './possibleStandardNames';
|
2017-07-15 06:36:24 +08:00
|
|
|
|
2017-12-05 21:47:57 +08:00
|
|
|
let validateProperty = () => {};
|
|
|
|
|
|
2015-12-03 23:02:55 +08:00
|
|
|
if (__DEV__) {
|
2017-12-05 21:47:57 +08:00
|
|
|
const warnedProperties = {};
|
|
|
|
|
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
|
|
const EVENT_NAME_REGEX = /^on./;
|
|
|
|
|
const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
|
|
|
|
|
const rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
|
|
|
|
|
const rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
|
2015-12-03 23:02:55 +08:00
|
|
|
|
2017-12-05 21:47:57 +08:00
|
|
|
validateProperty = function(tagName, name, value, canUseEventSystem) {
|
2017-08-26 06:44:37 +08:00
|
|
|
if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) {
|
2016-07-01 07:13:32 +08:00
|
|
|
return true;
|
2015-12-03 23:02:55 +08:00
|
|
|
}
|
2017-08-16 00:00:45 +08:00
|
|
|
|
2017-12-05 21:47:57 +08:00
|
|
|
const lowerCasedName = name.toLowerCase();
|
2017-11-24 01:44:58 +08:00
|
|
|
if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') {
|
2018-07-18 03:15:03 +08:00
|
|
|
warning(
|
2016-08-11 02:52:46 +08:00
|
|
|
false,
|
2017-11-24 01:44:58 +08:00
|
|
|
'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' +
|
|
|
|
|
'All React events are normalized to bubble, so onFocusIn and onFocusOut ' +
|
|
|
|
|
'are not needed/supported by React.',
|
2016-05-25 21:58:41 +08:00
|
|
|
);
|
2017-08-16 00:00:45 +08:00
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-24 01:44:58 +08:00
|
|
|
// We can't rely on the event system being injected on the server.
|
|
|
|
|
if (canUseEventSystem) {
|
|
|
|
|
if (registrationNameModules.hasOwnProperty(name)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-12-05 21:47:57 +08:00
|
|
|
const registrationName = possibleRegistrationNames.hasOwnProperty(
|
2017-11-24 01:44:58 +08:00
|
|
|
lowerCasedName,
|
|
|
|
|
)
|
|
|
|
|
? possibleRegistrationNames[lowerCasedName]
|
|
|
|
|
: null;
|
|
|
|
|
if (registrationName != null) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
2018-07-17 05:31:59 +08:00
|
|
|
'Invalid event handler property `%s`. Did you mean `%s`?',
|
2017-11-24 01:44:58 +08:00
|
|
|
name,
|
|
|
|
|
registrationName,
|
|
|
|
|
);
|
|
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (EVENT_NAME_REGEX.test(name)) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
2018-07-17 05:31:59 +08:00
|
|
|
'Unknown event handler property `%s`. It will be ignored.',
|
2017-11-24 01:44:58 +08:00
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} else if (EVENT_NAME_REGEX.test(name)) {
|
|
|
|
|
// If no event plugins have been injected, we are in a server environment.
|
|
|
|
|
// So we can't tell if the event name is correct for sure, but we can filter
|
|
|
|
|
// out known bad ones like `onclick`. We can't suggest a specific replacement though.
|
|
|
|
|
if (INVALID_EVENT_NAME_REGEX.test(name)) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
|
|
|
|
'Invalid event handler property `%s`. ' +
|
2018-07-17 05:31:59 +08:00
|
|
|
'React events use the camelCase naming convention, for example `onClick`.',
|
2017-11-24 01:44:58 +08:00
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
Changes to attribute whitelist logic (#10564)
* Remove HTMLPropertyConfig entries for non-boolean values
When we originally removed attributes from the whitelist, we assumed a
few attributes were string booleans, but they are not:
Autocomplete ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocomplete
Autocapitalize ("none", "sentence", "words", ...)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocapitalize
Autocorrect ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocorrect
Autosave (string)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autosave
* Only HAS_BOOLEAN_VALUE attribute flag can assign booleans
* Use a non-boolean attribute in object assignment tests
* Add HAS_STRING_BOOLEAN_VALUE attribute flag
* Fix boolean tests, add boolean warning.
* Reserved props should allow booleans
* Remove outdated comments
* Style tweaks
* Don't treat dashed SVG tags as custom elements
* SVG elements like font-face are not custom attributes
- Adds exceptions to isCustomAttribute for dashed SVG elements
- Use consistent custom element check across all modules
* Move namespace check to isCustomAttribute. Add caveat for stack.
* Remove unused namespace variable assignment
* Fix the DEV-only whitelist
* Don't read property twice
* Ignore and warn about non-string `is` attribute
* Blacklist "aria" and "data" attributes
* Don't pass unknown on* attributes through
* Remove dead code
* Avoid accessing namespace when possible
* Drop .only in ReactDOMComponent-test
* Make isCustomComponent logic more solid
* Do attribute name check earlier
* Fix fbjs import
* Revert unintentional edit
* Re-allow "data" attribute
We intentionally allowed it.
* Use stricter check when attaching events
* Pass SVG boolean attributes with correct casing
* Fix the test
* Undo the SVG dashed-name fix
Per conversation with @sebmarkbage we decided that the fix is too complicated, and it's unfortunate it depends on the DOM element.
It's only relevant for super rare tags that aren't even working consistently across browsers so we'll leave it unfixed for now.
* Prettier
* Fix lint
* Fix flow
* Pass "aria" through but still warn
* Remove special cases for onfocusin, onfocusout
They're covered by event handler code now.
* Add a more specific warning for unknown events
* Pass badly cased React attributes through with warning
2017-08-31 09:28:55 +08:00
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-16 00:00:45 +08:00
|
|
|
// Let the ARIA attribute hook validate ARIA attributes
|
2017-09-14 01:13:13 +08:00
|
|
|
if (rARIA.test(name) || rARIACamel.test(name)) {
|
2017-08-16 00:00:45 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lowerCasedName === 'innerhtml') {
|
2018-07-18 03:15:03 +08:00
|
|
|
warning(
|
2016-08-11 02:52:46 +08:00
|
|
|
false,
|
2017-08-16 00:00:45 +08:00
|
|
|
'Directly setting property `innerHTML` is not permitted. ' +
|
|
|
|
|
'For more information, lookup documentation on `dangerouslySetInnerHTML`.',
|
|
|
|
|
);
|
|
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
Changes to attribute whitelist logic (#10564)
* Remove HTMLPropertyConfig entries for non-boolean values
When we originally removed attributes from the whitelist, we assumed a
few attributes were string booleans, but they are not:
Autocomplete ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocomplete
Autocapitalize ("none", "sentence", "words", ...)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocapitalize
Autocorrect ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocorrect
Autosave (string)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autosave
* Only HAS_BOOLEAN_VALUE attribute flag can assign booleans
* Use a non-boolean attribute in object assignment tests
* Add HAS_STRING_BOOLEAN_VALUE attribute flag
* Fix boolean tests, add boolean warning.
* Reserved props should allow booleans
* Remove outdated comments
* Style tweaks
* Don't treat dashed SVG tags as custom elements
* SVG elements like font-face are not custom attributes
- Adds exceptions to isCustomAttribute for dashed SVG elements
- Use consistent custom element check across all modules
* Move namespace check to isCustomAttribute. Add caveat for stack.
* Remove unused namespace variable assignment
* Fix the DEV-only whitelist
* Don't read property twice
* Ignore and warn about non-string `is` attribute
* Blacklist "aria" and "data" attributes
* Don't pass unknown on* attributes through
* Remove dead code
* Avoid accessing namespace when possible
* Drop .only in ReactDOMComponent-test
* Make isCustomComponent logic more solid
* Do attribute name check earlier
* Fix fbjs import
* Revert unintentional edit
* Re-allow "data" attribute
We intentionally allowed it.
* Use stricter check when attaching events
* Pass SVG boolean attributes with correct casing
* Fix the test
* Undo the SVG dashed-name fix
Per conversation with @sebmarkbage we decided that the fix is too complicated, and it's unfortunate it depends on the DOM element.
It's only relevant for super rare tags that aren't even working consistently across browsers so we'll leave it unfixed for now.
* Prettier
* Fix lint
* Fix flow
* Pass "aria" through but still warn
* Remove special cases for onfocusin, onfocusout
They're covered by event handler code now.
* Add a more specific warning for unknown events
* Pass badly cased React attributes through with warning
2017-08-31 09:28:55 +08:00
|
|
|
if (lowerCasedName === 'aria') {
|
2018-07-18 03:15:03 +08:00
|
|
|
warning(
|
Changes to attribute whitelist logic (#10564)
* Remove HTMLPropertyConfig entries for non-boolean values
When we originally removed attributes from the whitelist, we assumed a
few attributes were string booleans, but they are not:
Autocomplete ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocomplete
Autocapitalize ("none", "sentence", "words", ...)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocapitalize
Autocorrect ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocorrect
Autosave (string)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autosave
* Only HAS_BOOLEAN_VALUE attribute flag can assign booleans
* Use a non-boolean attribute in object assignment tests
* Add HAS_STRING_BOOLEAN_VALUE attribute flag
* Fix boolean tests, add boolean warning.
* Reserved props should allow booleans
* Remove outdated comments
* Style tweaks
* Don't treat dashed SVG tags as custom elements
* SVG elements like font-face are not custom attributes
- Adds exceptions to isCustomAttribute for dashed SVG elements
- Use consistent custom element check across all modules
* Move namespace check to isCustomAttribute. Add caveat for stack.
* Remove unused namespace variable assignment
* Fix the DEV-only whitelist
* Don't read property twice
* Ignore and warn about non-string `is` attribute
* Blacklist "aria" and "data" attributes
* Don't pass unknown on* attributes through
* Remove dead code
* Avoid accessing namespace when possible
* Drop .only in ReactDOMComponent-test
* Make isCustomComponent logic more solid
* Do attribute name check earlier
* Fix fbjs import
* Revert unintentional edit
* Re-allow "data" attribute
We intentionally allowed it.
* Use stricter check when attaching events
* Pass SVG boolean attributes with correct casing
* Fix the test
* Undo the SVG dashed-name fix
Per conversation with @sebmarkbage we decided that the fix is too complicated, and it's unfortunate it depends on the DOM element.
It's only relevant for super rare tags that aren't even working consistently across browsers so we'll leave it unfixed for now.
* Prettier
* Fix lint
* Fix flow
* Pass "aria" through but still warn
* Remove special cases for onfocusin, onfocusout
They're covered by event handler code now.
* Add a more specific warning for unknown events
* Pass badly cased React attributes through with warning
2017-08-31 09:28:55 +08:00
|
|
|
false,
|
|
|
|
|
'The `aria` attribute is reserved for future use in React. ' +
|
|
|
|
|
'Pass individual `aria-` attributes instead.',
|
|
|
|
|
);
|
|
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
lowerCasedName === 'is' &&
|
|
|
|
|
value !== null &&
|
|
|
|
|
value !== undefined &&
|
|
|
|
|
typeof value !== 'string'
|
|
|
|
|
) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
2017-10-31 21:02:41 +08:00
|
|
|
'Received a `%s` for a string attribute `is`. If this is expected, cast ' +
|
2018-07-17 05:31:59 +08:00
|
|
|
'the value to a string.',
|
Changes to attribute whitelist logic (#10564)
* Remove HTMLPropertyConfig entries for non-boolean values
When we originally removed attributes from the whitelist, we assumed a
few attributes were string booleans, but they are not:
Autocomplete ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocomplete
Autocapitalize ("none", "sentence", "words", ...)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocapitalize
Autocorrect ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocorrect
Autosave (string)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autosave
* Only HAS_BOOLEAN_VALUE attribute flag can assign booleans
* Use a non-boolean attribute in object assignment tests
* Add HAS_STRING_BOOLEAN_VALUE attribute flag
* Fix boolean tests, add boolean warning.
* Reserved props should allow booleans
* Remove outdated comments
* Style tweaks
* Don't treat dashed SVG tags as custom elements
* SVG elements like font-face are not custom attributes
- Adds exceptions to isCustomAttribute for dashed SVG elements
- Use consistent custom element check across all modules
* Move namespace check to isCustomAttribute. Add caveat for stack.
* Remove unused namespace variable assignment
* Fix the DEV-only whitelist
* Don't read property twice
* Ignore and warn about non-string `is` attribute
* Blacklist "aria" and "data" attributes
* Don't pass unknown on* attributes through
* Remove dead code
* Avoid accessing namespace when possible
* Drop .only in ReactDOMComponent-test
* Make isCustomComponent logic more solid
* Do attribute name check earlier
* Fix fbjs import
* Revert unintentional edit
* Re-allow "data" attribute
We intentionally allowed it.
* Use stricter check when attaching events
* Pass SVG boolean attributes with correct casing
* Fix the test
* Undo the SVG dashed-name fix
Per conversation with @sebmarkbage we decided that the fix is too complicated, and it's unfortunate it depends on the DOM element.
It's only relevant for super rare tags that aren't even working consistently across browsers so we'll leave it unfixed for now.
* Prettier
* Fix lint
* Fix flow
* Pass "aria" through but still warn
* Remove special cases for onfocusin, onfocusout
They're covered by event handler code now.
* Add a more specific warning for unknown events
* Pass badly cased React attributes through with warning
2017-08-31 09:28:55 +08:00
|
|
|
typeof value,
|
|
|
|
|
);
|
|
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-16 00:00:45 +08:00
|
|
|
if (typeof value === 'number' && isNaN(value)) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
2017-10-31 21:02:41 +08:00
|
|
|
'Received NaN for the `%s` attribute. If this is expected, cast ' +
|
2018-07-17 05:31:59 +08:00
|
|
|
'the value to a string.',
|
2016-05-25 21:58:41 +08:00
|
|
|
name,
|
|
|
|
|
);
|
2017-08-16 00:00:45 +08:00
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
Refactor DOM attribute code (take two) (#11815)
* Harden tests around init/addition/update/removal of aliased attributes
I noticed some patterns weren't being tested.
* Call setValueForProperty() for null and undefined
The branching before the call is unnecessary because setValueForProperty() already
has an internal branch that delegates to deleteValueForProperty() for null and
undefined through the shouldIgnoreValue() check.
The goal is to start unifying these methods because their separation doesn't
reflect the current behavior (e.g. for unknown properties) anymore, and obscures
what actually happens with different inputs.
* Inline deleteValueForProperty() into setValueForProperty()
Now we don't read propertyInfo twice in this case.
I also dropped a few early returns. I added them a while ago when we had
Stack-only tracking of DOM operations, and some operations were being
counted twice because of how this code is structured. This isn't a problem
anymore (both because we don't track operations, and because I've just
inlined this method call).
* Inline deleteValueForAttribute() into setValueForAttribute()
The special cases for null and undefined already exist in setValueForAttribute().
* Delete some dead code
* Make setValueForAttribute() a branch of setValueForProperty()
Their naming is pretty confusing by now. For example setValueForProperty()
calls setValueForAttribute() when shouldSetAttribute() is false (!). I want
to refactor (as in, inline and then maybe factor it out differently) the relation
between them. For now, I'm consolidating the callers to use setValueForProperty().
* Make it more obvious where we skip and when we reset attributes
The naming of these methods is still very vague and conflicting in some cases.
Will need further work.
* Rewrite setValueForProperty() with early exits
This makes the flow clearer in my opinion.
* Move shouldIgnoreValue() into DOMProperty
It was previously duplicated.
It's also suspiciously similar in purpose to shouldTreatAttributeValueAsNull()
so I want to see if there is a way to unify them.
* Use more specific methods for testing validity
* Unify shouldTreatAttributeValueAsNull() and shouldIgnoreValue()
* Remove shouldSetAttribute()
Its naming was confusing and it was used all over the place instead of more specific checks.
Now that we only have one call site, we might as well inline and get rid of it.
* Remove unnecessary condition
* Remove another unnecessary condition
* Add Flow coverage
* Oops
* Fix lint (ESLint complains about Flow suppression)
* Fix treatment of Symbol/Function values on boolean attributes
They weren't being properly skipped because of the early return.
I added tests for this case.
* Avoid getPropertyInfo() calls
I think this PR looks worse on benchmarks because we have to read propertyInfo in different places.
Originally I tried to get rid of propertyInfo, but looks like it's important for performance after all.
So now I'm going into the opposite direction, and precompute propertyInfo as early as possible, and then just pass it around.
This way we can avoid extra lookups but keep functions nice and modular.
* Pass propertyInfo as argument to getValueForProperty()
It always exists because this function is only called for known properties.
* Make it clearer this branch is boolean-specific
I wrote this and then got confused myself.
* Memoize whether propertyInfo accepts boolean value
Since we run these checks for all booleans, might as well remember it.
* Fix a crash when numeric property is given a Symbol
* Record attribute table
The changes reflect that SSR doesn't crash with symbols anymore (and just warns, consistently with the client).
* Refactor attribute initialization
Instead of using flags, explicitly group similar attributes/properties.
* Optimization: we know built-in attributes are never invalid
* Use strict comparison
* Rename methods for clarity
* Lint nit
* Minor tweaks
* Document all the different attribute types
2017-12-11 00:58:38 +08:00
|
|
|
const propertyInfo = getPropertyInfo(name);
|
|
|
|
|
const isReserved = propertyInfo !== null && propertyInfo.type === RESERVED;
|
2017-09-14 01:13:13 +08:00
|
|
|
|
2017-08-16 00:00:45 +08:00
|
|
|
// Known attributes should match the casing specified in the property config.
|
|
|
|
|
if (possibleStandardNames.hasOwnProperty(lowerCasedName)) {
|
2017-12-05 21:47:57 +08:00
|
|
|
const standardName = possibleStandardNames[lowerCasedName];
|
2017-08-16 00:00:45 +08:00
|
|
|
if (standardName !== name) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
2018-07-17 05:31:59 +08:00
|
|
|
'Invalid DOM property `%s`. Did you mean `%s`?',
|
2017-08-16 00:00:45 +08:00
|
|
|
name,
|
|
|
|
|
standardName,
|
|
|
|
|
);
|
|
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-09-14 01:13:13 +08:00
|
|
|
} else if (!isReserved && name !== lowerCasedName) {
|
|
|
|
|
// Unknown attributes should have lowercase casing since that's how they
|
|
|
|
|
// will be cased anyway with server rendering.
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
|
|
|
|
'React does not recognize the `%s` prop on a DOM element. If you ' +
|
|
|
|
|
'intentionally want it to appear in the DOM as a custom ' +
|
|
|
|
|
'attribute, spell it as lowercase `%s` instead. ' +
|
|
|
|
|
'If you accidentally passed it from a parent component, remove ' +
|
2018-07-17 05:31:59 +08:00
|
|
|
'it from the DOM element.',
|
2017-09-14 01:13:13 +08:00
|
|
|
name,
|
|
|
|
|
lowerCasedName,
|
|
|
|
|
);
|
|
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
2017-08-16 00:00:45 +08:00
|
|
|
}
|
|
|
|
|
|
2017-10-31 21:02:41 +08:00
|
|
|
if (
|
|
|
|
|
typeof value === 'boolean' &&
|
Refactor DOM attribute code (take two) (#11815)
* Harden tests around init/addition/update/removal of aliased attributes
I noticed some patterns weren't being tested.
* Call setValueForProperty() for null and undefined
The branching before the call is unnecessary because setValueForProperty() already
has an internal branch that delegates to deleteValueForProperty() for null and
undefined through the shouldIgnoreValue() check.
The goal is to start unifying these methods because their separation doesn't
reflect the current behavior (e.g. for unknown properties) anymore, and obscures
what actually happens with different inputs.
* Inline deleteValueForProperty() into setValueForProperty()
Now we don't read propertyInfo twice in this case.
I also dropped a few early returns. I added them a while ago when we had
Stack-only tracking of DOM operations, and some operations were being
counted twice because of how this code is structured. This isn't a problem
anymore (both because we don't track operations, and because I've just
inlined this method call).
* Inline deleteValueForAttribute() into setValueForAttribute()
The special cases for null and undefined already exist in setValueForAttribute().
* Delete some dead code
* Make setValueForAttribute() a branch of setValueForProperty()
Their naming is pretty confusing by now. For example setValueForProperty()
calls setValueForAttribute() when shouldSetAttribute() is false (!). I want
to refactor (as in, inline and then maybe factor it out differently) the relation
between them. For now, I'm consolidating the callers to use setValueForProperty().
* Make it more obvious where we skip and when we reset attributes
The naming of these methods is still very vague and conflicting in some cases.
Will need further work.
* Rewrite setValueForProperty() with early exits
This makes the flow clearer in my opinion.
* Move shouldIgnoreValue() into DOMProperty
It was previously duplicated.
It's also suspiciously similar in purpose to shouldTreatAttributeValueAsNull()
so I want to see if there is a way to unify them.
* Use more specific methods for testing validity
* Unify shouldTreatAttributeValueAsNull() and shouldIgnoreValue()
* Remove shouldSetAttribute()
Its naming was confusing and it was used all over the place instead of more specific checks.
Now that we only have one call site, we might as well inline and get rid of it.
* Remove unnecessary condition
* Remove another unnecessary condition
* Add Flow coverage
* Oops
* Fix lint (ESLint complains about Flow suppression)
* Fix treatment of Symbol/Function values on boolean attributes
They weren't being properly skipped because of the early return.
I added tests for this case.
* Avoid getPropertyInfo() calls
I think this PR looks worse on benchmarks because we have to read propertyInfo in different places.
Originally I tried to get rid of propertyInfo, but looks like it's important for performance after all.
So now I'm going into the opposite direction, and precompute propertyInfo as early as possible, and then just pass it around.
This way we can avoid extra lookups but keep functions nice and modular.
* Pass propertyInfo as argument to getValueForProperty()
It always exists because this function is only called for known properties.
* Make it clearer this branch is boolean-specific
I wrote this and then got confused myself.
* Memoize whether propertyInfo accepts boolean value
Since we run these checks for all booleans, might as well remember it.
* Fix a crash when numeric property is given a Symbol
* Record attribute table
The changes reflect that SSR doesn't crash with symbols anymore (and just warns, consistently with the client).
* Refactor attribute initialization
Instead of using flags, explicitly group similar attributes/properties.
* Optimization: we know built-in attributes are never invalid
* Use strict comparison
* Rename methods for clarity
* Lint nit
* Minor tweaks
* Document all the different attribute types
2017-12-11 00:58:38 +08:00
|
|
|
shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)
|
2017-10-31 21:02:41 +08:00
|
|
|
) {
|
|
|
|
|
if (value) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
|
|
|
|
'Received `%s` for a non-boolean attribute `%s`.\n\n' +
|
|
|
|
|
'If you want to write it to the DOM, pass a string instead: ' +
|
2018-07-17 05:31:59 +08:00
|
|
|
'%s="%s" or %s={value.toString()}.',
|
2017-10-31 21:02:41 +08:00
|
|
|
value,
|
|
|
|
|
name,
|
|
|
|
|
name,
|
|
|
|
|
value,
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
|
|
|
|
'Received `%s` for a non-boolean attribute `%s`.\n\n' +
|
|
|
|
|
'If you want to write it to the DOM, pass a string instead: ' +
|
|
|
|
|
'%s="%s" or %s={value.toString()}.\n\n' +
|
|
|
|
|
'If you used to conditionally omit it with %s={condition && value}, ' +
|
2018-07-17 05:31:59 +08:00
|
|
|
'pass %s={condition ? value : undefined} instead.',
|
2017-10-31 21:02:41 +08:00
|
|
|
value,
|
|
|
|
|
name,
|
|
|
|
|
name,
|
|
|
|
|
value,
|
|
|
|
|
name,
|
|
|
|
|
name,
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
Changes to attribute whitelist logic (#10564)
* Remove HTMLPropertyConfig entries for non-boolean values
When we originally removed attributes from the whitelist, we assumed a
few attributes were string booleans, but they are not:
Autocomplete ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocomplete
Autocapitalize ("none", "sentence", "words", ...)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocapitalize
Autocorrect ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocorrect
Autosave (string)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autosave
* Only HAS_BOOLEAN_VALUE attribute flag can assign booleans
* Use a non-boolean attribute in object assignment tests
* Add HAS_STRING_BOOLEAN_VALUE attribute flag
* Fix boolean tests, add boolean warning.
* Reserved props should allow booleans
* Remove outdated comments
* Style tweaks
* Don't treat dashed SVG tags as custom elements
* SVG elements like font-face are not custom attributes
- Adds exceptions to isCustomAttribute for dashed SVG elements
- Use consistent custom element check across all modules
* Move namespace check to isCustomAttribute. Add caveat for stack.
* Remove unused namespace variable assignment
* Fix the DEV-only whitelist
* Don't read property twice
* Ignore and warn about non-string `is` attribute
* Blacklist "aria" and "data" attributes
* Don't pass unknown on* attributes through
* Remove dead code
* Avoid accessing namespace when possible
* Drop .only in ReactDOMComponent-test
* Make isCustomComponent logic more solid
* Do attribute name check earlier
* Fix fbjs import
* Revert unintentional edit
* Re-allow "data" attribute
We intentionally allowed it.
* Use stricter check when attaching events
* Pass SVG boolean attributes with correct casing
* Fix the test
* Undo the SVG dashed-name fix
Per conversation with @sebmarkbage we decided that the fix is too complicated, and it's unfortunate it depends on the DOM element.
It's only relevant for super rare tags that aren't even working consistently across browsers so we'll leave it unfixed for now.
* Prettier
* Fix lint
* Fix flow
* Pass "aria" through but still warn
* Remove special cases for onfocusin, onfocusout
They're covered by event handler code now.
* Add a more specific warning for unknown events
* Pass badly cased React attributes through with warning
2017-08-31 09:28:55 +08:00
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-16 00:00:45 +08:00
|
|
|
// Now that we've validated casing, do not validate
|
|
|
|
|
// data types for reserved props
|
2017-09-14 01:13:13 +08:00
|
|
|
if (isReserved) {
|
2016-07-01 07:13:32 +08:00
|
|
|
return true;
|
2017-08-16 00:00:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Warn when a known attribute is a bad type
|
Refactor DOM attribute code (take two) (#11815)
* Harden tests around init/addition/update/removal of aliased attributes
I noticed some patterns weren't being tested.
* Call setValueForProperty() for null and undefined
The branching before the call is unnecessary because setValueForProperty() already
has an internal branch that delegates to deleteValueForProperty() for null and
undefined through the shouldIgnoreValue() check.
The goal is to start unifying these methods because their separation doesn't
reflect the current behavior (e.g. for unknown properties) anymore, and obscures
what actually happens with different inputs.
* Inline deleteValueForProperty() into setValueForProperty()
Now we don't read propertyInfo twice in this case.
I also dropped a few early returns. I added them a while ago when we had
Stack-only tracking of DOM operations, and some operations were being
counted twice because of how this code is structured. This isn't a problem
anymore (both because we don't track operations, and because I've just
inlined this method call).
* Inline deleteValueForAttribute() into setValueForAttribute()
The special cases for null and undefined already exist in setValueForAttribute().
* Delete some dead code
* Make setValueForAttribute() a branch of setValueForProperty()
Their naming is pretty confusing by now. For example setValueForProperty()
calls setValueForAttribute() when shouldSetAttribute() is false (!). I want
to refactor (as in, inline and then maybe factor it out differently) the relation
between them. For now, I'm consolidating the callers to use setValueForProperty().
* Make it more obvious where we skip and when we reset attributes
The naming of these methods is still very vague and conflicting in some cases.
Will need further work.
* Rewrite setValueForProperty() with early exits
This makes the flow clearer in my opinion.
* Move shouldIgnoreValue() into DOMProperty
It was previously duplicated.
It's also suspiciously similar in purpose to shouldTreatAttributeValueAsNull()
so I want to see if there is a way to unify them.
* Use more specific methods for testing validity
* Unify shouldTreatAttributeValueAsNull() and shouldIgnoreValue()
* Remove shouldSetAttribute()
Its naming was confusing and it was used all over the place instead of more specific checks.
Now that we only have one call site, we might as well inline and get rid of it.
* Remove unnecessary condition
* Remove another unnecessary condition
* Add Flow coverage
* Oops
* Fix lint (ESLint complains about Flow suppression)
* Fix treatment of Symbol/Function values on boolean attributes
They weren't being properly skipped because of the early return.
I added tests for this case.
* Avoid getPropertyInfo() calls
I think this PR looks worse on benchmarks because we have to read propertyInfo in different places.
Originally I tried to get rid of propertyInfo, but looks like it's important for performance after all.
So now I'm going into the opposite direction, and precompute propertyInfo as early as possible, and then just pass it around.
This way we can avoid extra lookups but keep functions nice and modular.
* Pass propertyInfo as argument to getValueForProperty()
It always exists because this function is only called for known properties.
* Make it clearer this branch is boolean-specific
I wrote this and then got confused myself.
* Memoize whether propertyInfo accepts boolean value
Since we run these checks for all booleans, might as well remember it.
* Fix a crash when numeric property is given a Symbol
* Record attribute table
The changes reflect that SSR doesn't crash with symbols anymore (and just warns, consistently with the client).
* Refactor attribute initialization
Instead of using flags, explicitly group similar attributes/properties.
* Optimization: we know built-in attributes are never invalid
* Use strict comparison
* Rename methods for clarity
* Lint nit
* Minor tweaks
* Document all the different attribute types
2017-12-11 00:58:38 +08:00
|
|
|
if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) {
|
2017-08-16 00:00:45 +08:00
|
|
|
warnedProperties[name] = true;
|
2016-07-01 07:13:32 +08:00
|
|
|
return false;
|
2016-05-25 21:58:41 +08:00
|
|
|
}
|
2017-08-16 00:00:45 +08:00
|
|
|
|
2018-08-15 03:51:33 +08:00
|
|
|
// Warn when passing the strings 'false' or 'true' into a boolean prop
|
|
|
|
|
if (
|
|
|
|
|
(value === 'false' || value === 'true') &&
|
|
|
|
|
propertyInfo !== null &&
|
|
|
|
|
propertyInfo.type === BOOLEAN
|
|
|
|
|
) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
|
|
|
|
'Received the string `%s` for the boolean attribute `%s`. ' +
|
|
|
|
|
'%s ' +
|
|
|
|
|
'Did you mean %s={%s}?',
|
|
|
|
|
value,
|
|
|
|
|
name,
|
|
|
|
|
value === 'false'
|
|
|
|
|
? 'The browser will interpret it as a truthy value.'
|
|
|
|
|
: 'Although this works, it will not work as expected if you pass the string "false".',
|
|
|
|
|
name,
|
|
|
|
|
value,
|
|
|
|
|
);
|
|
|
|
|
warnedProperties[name] = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-16 00:00:45 +08:00
|
|
|
return true;
|
2016-05-18 06:06:31 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-05 21:47:57 +08:00
|
|
|
const warnUnknownProperties = function(type, props, canUseEventSystem) {
|
2019-12-07 02:25:54 +08:00
|
|
|
if (__DEV__) {
|
|
|
|
|
const unknownProps = [];
|
|
|
|
|
for (const key in props) {
|
|
|
|
|
const isValid = validateProperty(
|
|
|
|
|
type,
|
|
|
|
|
key,
|
|
|
|
|
props[key],
|
|
|
|
|
canUseEventSystem,
|
|
|
|
|
);
|
|
|
|
|
if (!isValid) {
|
|
|
|
|
unknownProps.push(key);
|
|
|
|
|
}
|
2016-07-01 07:13:32 +08:00
|
|
|
}
|
|
|
|
|
|
2019-12-07 02:25:54 +08:00
|
|
|
const unknownPropString = unknownProps
|
|
|
|
|
.map(prop => '`' + prop + '`')
|
|
|
|
|
.join(', ');
|
|
|
|
|
if (unknownProps.length === 1) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
|
|
|
|
'Invalid value for prop %s on <%s> tag. Either remove it from the element, ' +
|
|
|
|
|
'or pass a string or number value to keep it in the DOM. ' +
|
|
|
|
|
'For details, see https://fb.me/react-attribute-behavior',
|
|
|
|
|
unknownPropString,
|
|
|
|
|
type,
|
|
|
|
|
);
|
|
|
|
|
} else if (unknownProps.length > 1) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
|
|
|
|
'Invalid values for props %s on <%s> tag. Either remove them from the element, ' +
|
|
|
|
|
'or pass a string or number value to keep them in the DOM. ' +
|
|
|
|
|
'For details, see https://fb.me/react-attribute-behavior',
|
|
|
|
|
unknownPropString,
|
|
|
|
|
type,
|
|
|
|
|
);
|
|
|
|
|
}
|
2016-07-01 07:13:32 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-11-24 01:44:58 +08:00
|
|
|
export function validateProperties(type, props, canUseEventSystem) {
|
Changes to attribute whitelist logic (#10564)
* Remove HTMLPropertyConfig entries for non-boolean values
When we originally removed attributes from the whitelist, we assumed a
few attributes were string booleans, but they are not:
Autocomplete ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocomplete
Autocapitalize ("none", "sentence", "words", ...)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocapitalize
Autocorrect ("on", "off")
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autocorrect
Autosave (string)
https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/Attributes.html#autosave
* Only HAS_BOOLEAN_VALUE attribute flag can assign booleans
* Use a non-boolean attribute in object assignment tests
* Add HAS_STRING_BOOLEAN_VALUE attribute flag
* Fix boolean tests, add boolean warning.
* Reserved props should allow booleans
* Remove outdated comments
* Style tweaks
* Don't treat dashed SVG tags as custom elements
* SVG elements like font-face are not custom attributes
- Adds exceptions to isCustomAttribute for dashed SVG elements
- Use consistent custom element check across all modules
* Move namespace check to isCustomAttribute. Add caveat for stack.
* Remove unused namespace variable assignment
* Fix the DEV-only whitelist
* Don't read property twice
* Ignore and warn about non-string `is` attribute
* Blacklist "aria" and "data" attributes
* Don't pass unknown on* attributes through
* Remove dead code
* Avoid accessing namespace when possible
* Drop .only in ReactDOMComponent-test
* Make isCustomComponent logic more solid
* Do attribute name check earlier
* Fix fbjs import
* Revert unintentional edit
* Re-allow "data" attribute
We intentionally allowed it.
* Use stricter check when attaching events
* Pass SVG boolean attributes with correct casing
* Fix the test
* Undo the SVG dashed-name fix
Per conversation with @sebmarkbage we decided that the fix is too complicated, and it's unfortunate it depends on the DOM element.
It's only relevant for super rare tags that aren't even working consistently across browsers so we'll leave it unfixed for now.
* Prettier
* Fix lint
* Fix flow
* Pass "aria" through but still warn
* Remove special cases for onfocusin, onfocusout
They're covered by event handler code now.
* Add a more specific warning for unknown events
* Pass badly cased React attributes through with warning
2017-08-31 09:28:55 +08:00
|
|
|
if (isCustomComponent(type, props)) {
|
2016-05-18 06:06:31 +08:00
|
|
|
return;
|
|
|
|
|
}
|
2017-11-24 01:44:58 +08:00
|
|
|
warnUnknownProperties(type, props, canUseEventSystem);
|
2015-12-03 23:02:55 +08:00
|
|
|
}
|