diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt index a890ac475a123..660a5d1f25115 100644 --- a/scripts/fiber/tests-passing.txt +++ b/scripts/fiber/tests-passing.txt @@ -1531,6 +1531,13 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js * should error reconnecting missing attributes * should error reconnecting added attributes * should error reconnecting different attribute values +* should error reconnecting missing style attribute +* should error reconnecting added style attribute +* should error reconnecting empty style attribute +* should error reconnecting added style values +* should error reconnecting different style values +* should reconnect number and string versions of a number +* should error reconnecting reordered style values * should error reconnecting different text * should reconnect a div with a number and string version of number * should error reconnecting different numbers diff --git a/src/renderers/dom/fiber/ReactDOMFiberComponent.js b/src/renderers/dom/fiber/ReactDOMFiberComponent.js index 46f91e986d0d2..9bc7e30a618e8 100644 --- a/src/renderers/dom/fiber/ReactDOMFiberComponent.js +++ b/src/renderers/dom/fiber/ReactDOMFiberComponent.js @@ -915,7 +915,13 @@ var ReactDOMFiberComponent = { } else if (propKey === STYLE) { // $FlowFixMe - Should be inferred as not undefined. extraAttributeNames.delete(propKey); - // TOOD: Validate style sheets. + const expectedStyle = CSSPropertyOperations.createDangerousStringForStyles( + nextProp, + ); + serverValue = domElement.getAttribute('style'); + if (expectedStyle !== serverValue) { + warnForPropDifference(propKey, serverValue, expectedStyle); + } } else if ( isCustomComponentTag || DOMProperty.isCustomAttribute(propKey) diff --git a/src/renderers/dom/shared/CSSPropertyOperations.js b/src/renderers/dom/shared/CSSPropertyOperations.js index a4d953335a0d2..8e285f5eea6b4 100644 --- a/src/renderers/dom/shared/CSSPropertyOperations.js +++ b/src/renderers/dom/shared/CSSPropertyOperations.js @@ -207,7 +207,6 @@ var CSSPropertyOperations = { serialized += dangerousStyleValue( styleName, styleValue, - component, isCustomProperty, ); @@ -217,6 +216,40 @@ var CSSPropertyOperations = { return serialized || null; }, + /** + * This creates a string that is expected to be equivalent to the style + * attribute generated by server-side rendering. It by-passes warnings and + * security checks so it's not safe to use this value for anything other than + * comparison. It is only used in DEV for SSR validation. This is duplicated + * from createMarkupForStyles because createMarkupForStyles is expected to + * move out of the client-side renderer and it would be nice to make a clean + * break. + */ + createDangerousStringForStyles: function(styles) { + if (__DEV__) { + var serialized = ''; + var delimiter = ''; + for (var styleName in styles) { + if (!styles.hasOwnProperty(styleName)) { + continue; + } + var styleValue = styles[styleName]; + if (styleValue != null) { + var isCustomProperty = styleName.indexOf('--') === 0; + serialized += delimiter + hyphenateStyleName(styleName) + ':'; + serialized += dangerousStyleValue( + styleName, + styleValue, + isCustomProperty, + ); + + delimiter = ';'; + } + } + return serialized || null; + } + }, + /** * Sets the value for multiple styles on a node. If a value is specified as * '' (empty string), the corresponding style property will be unset. @@ -240,7 +273,6 @@ var CSSPropertyOperations = { var styleValue = dangerousStyleValue( styleName, styles[styleName], - component, isCustomProperty, ); if (styleName === 'float') { diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js index 2fd7ae398061b..08fc0ebe95c83 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js @@ -2075,6 +2075,44 @@ describe('ReactDOMServerIntegration', () => { expectMarkupMismatch(
,
)); }); + describe('inline styles', function() { + it('should error reconnecting missing style attribute', () => + expectMarkupMismatch(
,
)); + + it('should error reconnecting added style attribute', () => + expectMarkupMismatch(
,
)); + + it('should error reconnecting empty style attribute', () => + expectMarkupMismatch( +
, +
, + )); + + it('should error reconnecting added style values', () => + expectMarkupMismatch( +
, +
, + )); + + it('should error reconnecting different style values', () => + expectMarkupMismatch( +
, +
, + )); + + it('should reconnect number and string versions of a number', () => + expectMarkupMatch( +
, +
, + )); + + it('should error reconnecting reordered style values', () => + expectMarkupMismatch( +
, +
, + )); + }); + describe('text nodes', function() { it('should error reconnecting different text', () => expectMarkupMismatch(
Text
,
Other Text
)); diff --git a/src/renderers/dom/shared/dangerousStyleValue.js b/src/renderers/dom/shared/dangerousStyleValue.js index 194debcb6559b..033fa2dbbb5f3 100644 --- a/src/renderers/dom/shared/dangerousStyleValue.js +++ b/src/renderers/dom/shared/dangerousStyleValue.js @@ -22,10 +22,9 @@ var isUnitlessNumber = CSSProperty.isUnitlessNumber; * * @param {string} name CSS property name such as `topMargin`. * @param {*} value CSS property value such as `10px`. - * @param {ReactDOMComponent} component * @return {string} Normalized style value with dimensions applied. */ -function dangerousStyleValue(name, value, component, isCustomProperty) { +function dangerousStyleValue(name, value, isCustomProperty) { // Note that we've removed escapeTextForBrowser() calls here since the // whole string will be escaped when the attribute is injected into // the markup. If you provide unsafe user data here they can inject