Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 17 additions & 43 deletions packages/react-dom-bindings/src/client/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
possibleRegistrationNames,
} from '../events/EventRegistry';

import {canUseDOM} from 'shared/ExecutionEnvironment';
import {checkHtmlStringCoercion} from 'shared/CheckStringCoercion';
import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';
import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
Expand Down Expand Up @@ -50,7 +49,6 @@ import {
} from './ReactDOMTextarea';
import {validateTextNesting} from './validateDOMNesting';
import {track} from './inputValueTracking';
import setInnerHTML from './setInnerHTML';
import setTextContent from './setTextContent';
import {
createDangerousStringForStyles,
Expand All @@ -66,7 +64,6 @@ import {validateProperties as validateUnknownProperties} from '../shared/ReactDO
import sanitizeURL from '../shared/sanitizeURL';

import {
disableIEWorkarounds,
enableTrustedTypesIntegration,
enableFilterEmptyStringAttributesDOM,
} from 'shared/ReactFeatureFlags';
Expand All @@ -83,19 +80,8 @@ let didWarnFormActionTarget = false;
let didWarnFormActionMethod = false;
let didWarnForNewBooleanPropsWithEmptyValue: {[string]: boolean};
let didWarnPopoverTargetObject = false;
let canDiffStyleForHydrationWarning;
if (__DEV__) {
didWarnForNewBooleanPropsWithEmptyValue = {};
// IE 11 parses & normalizes the style attribute as opposed to other
// browsers. It adds spaces and sorts the properties in some
// non-alphabetical order. Handling that would require sorting CSS
// properties in the client & server versions or applying
// `expectedStyle` to a temporary DOM node to read its `style` attribute
// normalized. Since it only affects IE, we're skipping style warnings
// in that browser completely in favor of doing all that work.
// See https://github.com/facebook/react/issues/11807
canDiffStyleForHydrationWarning =
disableIEWorkarounds || (canUseDOM && !document.documentMode);
}

function validatePropertiesInDevelopment(type: string, props: any) {
Expand Down Expand Up @@ -579,11 +565,7 @@ function setProp(
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
);
}
if (disableIEWorkarounds) {
domElement.innerHTML = nextHtml;
} else {
setInnerHTML(domElement, nextHtml);
}
domElement.innerHTML = nextHtml;
}
}
break;
Expand Down Expand Up @@ -939,11 +921,7 @@ function setPropOnCustomElement(
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
);
}
if (disableIEWorkarounds) {
domElement.innerHTML = nextHtml;
} else {
setInnerHTML(domElement, nextHtml);
}
domElement.innerHTML = nextHtml;
}
}
break;
Expand Down Expand Up @@ -1931,27 +1909,23 @@ function diffHydratedStyles(
}
return;
}
if (canDiffStyleForHydrationWarning) {
// First we compare the string form and see if it's equivalent.
// This lets us bail out on anything that used to pass in this form.
// It also lets us compare anything that's not parsed by this browser.
const clientValue = createDangerousStringForStyles(value);
const serverValue = domElement.getAttribute('style');
// First we compare the string form and see if it's equivalent.
// This lets us bail out on anything that used to pass in this form.
// It also lets us compare anything that's not parsed by this browser.
const clientValue = createDangerousStringForStyles(value);
const serverValue = domElement.getAttribute('style');

if (serverValue === clientValue) {
return;
}
const normalizedClientValue =
normalizeMarkupForTextOrAttribute(clientValue);
const normalizedServerValue =
normalizeMarkupForTextOrAttribute(serverValue);
if (normalizedServerValue === normalizedClientValue) {
return;
}

// Otherwise, we create the object from the DOM for the diff view.
serverDifferences.style = getStylesObjectFromElement(domElement);
if (serverValue === clientValue) {
return;
}
const normalizedClientValue = normalizeMarkupForTextOrAttribute(clientValue);
const normalizedServerValue = normalizeMarkupForTextOrAttribute(serverValue);
if (normalizedServerValue === normalizedClientValue) {
return;
}

// Otherwise, we create the object from the DOM for the diff view.
serverDifferences.style = getStylesObjectFromElement(domElement);
}

function hydrateAttribute(
Expand Down
82 changes: 0 additions & 82 deletions packages/react-dom-bindings/src/client/setInnerHTML.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ let useRef;
let useImperativeHandle;
let useInsertionEffect;
let useLayoutEffect;
let useResourceEffect;
let useDebugValue;
let forwardRef;
let yieldedValues;
Expand All @@ -51,6 +52,7 @@ function initModules() {
useImperativeHandle = React.useImperativeHandle;
useInsertionEffect = React.useInsertionEffect;
useLayoutEffect = React.useLayoutEffect;
useResourceEffect = React.experimental_useResourceEffect;
forwardRef = React.forwardRef;

yieldedValues = [];
Expand Down Expand Up @@ -653,6 +655,52 @@ describe('ReactDOMServerHooks', () => {
});
});

describe('useResourceEffect', () => {
gate(flags => {
if (flags.enableUseResourceEffectHook) {
const yields = [];
itRenders(
'should ignore resource effects on the server',
async render => {
function Counter(props) {
useResourceEffect(
() => {
yieldValue('created on client');
return {resource_counter: props.count};
},
[props.count],
resource => {
resource.resource_counter = props.count;
yieldValue('updated on client');
},
[props.count],
() => {
yieldValue('cleanup on client');
},
);
return <Text text={'Count: ' + props.count} />;
}

const domNode = await render(<Counter count={0} />);
yields.push(clearLog());
expect(domNode.tagName).toEqual('SPAN');
expect(domNode.textContent).toEqual('Count: 0');
},
);

it('verifies yields in order', () => {
expect(yields).toEqual([
['Count: 0'], // server render
['Count: 0'], // server stream
['Count: 0', 'created on client'], // clean render
['Count: 0', 'created on client'], // hydrated render
// nothing yielded for bad markup
]);
});
}
});
});

describe('useContext', () => {
itThrowsWhenRendering(
'if used inside a class component',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,73 +27,4 @@ describe('dangerouslySetInnerHTML', () => {
expect(container.firstChild.innerHTML).toBe('<h1>Hello</h1>');
});
});

describe('when the node does not have an innerHTML property', () => {
let innerHTMLDescriptor;

// In some versions of IE (TODO: which ones?) SVG nodes don't have
// innerHTML. To simulate this, we will take it off the Element prototype
// and put it onto the HTMLDivElement prototype. We expect that the logic
// checks for existence of innerHTML on SVG, and if one doesn't exist, falls
// back to using appendChild and removeChild.

beforeEach(() => {
innerHTMLDescriptor = Object.getOwnPropertyDescriptor(
Element.prototype,
'innerHTML',
);
delete Element.prototype.innerHTML;
Object.defineProperty(
HTMLDivElement.prototype,
'innerHTML',
innerHTMLDescriptor,
);
});

afterEach(() => {
delete HTMLDivElement.prototype.innerHTML;
Object.defineProperty(
Element.prototype,
'innerHTML',
innerHTMLDescriptor,
);
});

// @gate !disableIEWorkarounds
it('sets innerHTML on it', async () => {
const html = '<circle></circle>';
const container = document.createElementNS(
'http://www.w3.org/2000/svg',
'svg',
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<g dangerouslySetInnerHTML={{__html: html}} />);
});
const circle = container.firstChild.firstChild;
expect(circle.tagName).toBe('circle');
});

// @gate !disableIEWorkarounds
it('clears previous children', async () => {
const firstHtml = '<rect></rect>';
const secondHtml = '<circle></circle>';

const container = document.createElementNS(
'http://www.w3.org/2000/svg',
'svg',
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<g dangerouslySetInnerHTML={{__html: firstHtml}} />);
});
const rect = container.firstChild.firstChild;
expect(rect.tagName).toBe('rect');
await act(() => {
root.render(<g dangerouslySetInnerHTML={{__html: secondHtml}} />);
});
const circle = container.firstChild.firstChild;
expect(circle.tagName).toBe('circle');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -206,56 +206,6 @@ describe('when Trusted Types are available in global object', () => {
}
});

describe('dangerouslySetInnerHTML in svg elements in Internet Explorer', () => {
let innerHTMLDescriptor;

// simulate svg elements in Internet Explorer which don't have 'innerHTML' property
beforeEach(() => {
innerHTMLDescriptor = Object.getOwnPropertyDescriptor(
Element.prototype,
'innerHTML',
);
delete Element.prototype.innerHTML;
Object.defineProperty(
HTMLDivElement.prototype,
'innerHTML',
innerHTMLDescriptor,
);
});

afterEach(() => {
delete HTMLDivElement.prototype.innerHTML;
Object.defineProperty(
Element.prototype,
'innerHTML',
innerHTMLDescriptor,
);
});

// @gate !disableIEWorkarounds
it('should log a warning', async () => {
class Component extends React.Component {
render() {
return <svg dangerouslySetInnerHTML={{__html: 'unsafe html'}} />;
}
}
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => {
root.render(<Component />);
});
}).toErrorDev(
"Using 'dangerouslySetInnerHTML' in an svg element with " +
'Trusted Types enabled in an Internet Explorer will cause ' +
'the trusted value to be converted to string. Assigning string ' +
"to 'innerHTML' will throw an error if Trusted Types are enforced. " +
"You can try to wrap your svg element inside a div and use 'dangerouslySetInnerHTML' " +
'on the enclosing div instead.',
);
expect(container.innerHTML).toBe('<svg>unsafe html</svg>');
});
});

it('should warn once when rendering script tag in jsx on client', async () => {
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
Expand Down
Loading
Loading