Skip to content

Commit 3483f66

Browse files
jeremenichelliHeroProtagonist
authored andcommitted
Drop .textContent IE8 polyfill and rewrite escaping tests against public API (facebook#11331)
* Rename escapeText util. Test quoteAttributeValueForBrowser through ReactDOMServer API * Fix lint errors * Prettier reformatting * Change syntax to prevent prettier escape doble quote * Name and description gardening. Add tests for escapeTextForBrowser. Add missing tests * Improve script tag as text content test * Update escapeTextForBrowser-test.js * Update quoteAttributeValueForBrowser-test.js * Simplify tests * Move utilities to server folder
1 parent af2764b commit 3483f66

9 files changed

+129
-107
lines changed

packages/react-dom/src/__tests__/ReactServerRendering-test.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,6 @@ describe('ReactDOMServer', () => {
4242
expect(response).toMatch(new RegExp('<img data-reactroot=""' + '/>'));
4343
});
4444

45-
it('should generate simple markup for attribute with `>` symbol', () => {
46-
var response = ReactDOMServer.renderToString(<img data-attr=">" />);
47-
expect(response).toMatch(
48-
new RegExp('<img data-attr="&gt;" data-reactroot=""' + '/>'),
49-
);
50-
});
51-
5245
it('should generate comment markup for component returns null', () => {
5346
class NullComponent extends React.Component {
5447
render() {

packages/react-dom/src/__tests__/escapeTextContentForBrowser-test.js

Lines changed: 0 additions & 48 deletions
This file was deleted.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright (c) 2013-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @emails react-core
8+
*/
9+
10+
'use strict';
11+
12+
var React;
13+
var ReactDOMServer;
14+
15+
describe('escapeTextForBrowser', () => {
16+
beforeEach(() => {
17+
jest.resetModules();
18+
React = require('react');
19+
ReactDOMServer = require('react-dom/server');
20+
});
21+
22+
it('ampersand is escaped when passed as text content', () => {
23+
var response = ReactDOMServer.renderToString(<span>{'&'}</span>);
24+
expect(response).toMatch('<span data-reactroot="">&amp;</span>');
25+
});
26+
27+
it('double quote is escaped when passed as text content', () => {
28+
var response = ReactDOMServer.renderToString(<span>{'"'}</span>);
29+
expect(response).toMatch('<span data-reactroot="">&quot;</span>');
30+
});
31+
32+
it('single quote is escaped when passed as text content', () => {
33+
var response = ReactDOMServer.renderToString(<span>{"'"}</span>);
34+
expect(response).toMatch('<span data-reactroot="">&#x27;</span>');
35+
});
36+
37+
it('greater than entity is escaped when passed as text content', () => {
38+
var response = ReactDOMServer.renderToString(<span>{'>'}</span>);
39+
expect(response).toMatch('<span data-reactroot="">&gt;</span>');
40+
});
41+
42+
it('lower than entity is escaped when passed as text content', () => {
43+
var response = ReactDOMServer.renderToString(<span>{'<'}</span>);
44+
expect(response).toMatch('<span data-reactroot="">&lt;</span>');
45+
});
46+
47+
it('number is correctly passed as text content', () => {
48+
var response = ReactDOMServer.renderToString(<span>{42}</span>);
49+
expect(response).toMatch('<span data-reactroot="">42</span>');
50+
});
51+
52+
it('number is escaped to string when passed as text content', () => {
53+
var response = ReactDOMServer.renderToString(<img data-attr={42} />);
54+
expect(response).toMatch('<img data-attr="42" data-reactroot=""/>');
55+
});
56+
57+
it('escape text content representing a script tag', () => {
58+
var response = ReactDOMServer.renderToString(
59+
<span>{'<script type=\'\' src=""></script>'}</span>,
60+
);
61+
expect(response).toMatch(
62+
'<span data-reactroot="">&lt;script type=&#x27;&#x27; ' +
63+
'src=&quot;&quot;&gt;&lt;/script&gt;</span>',
64+
);
65+
});
66+
});

packages/react-dom/src/__tests__/quoteAttributeValueForBrowser-test.js

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,67 @@
99

1010
'use strict';
1111

12+
var React;
13+
var ReactDOMServer;
14+
1215
describe('quoteAttributeValueForBrowser', () => {
13-
// TODO: can we express this test with only public API?
14-
var quoteAttributeValueForBrowser = require('../shared/quoteAttributeValueForBrowser')
15-
.default;
16+
beforeEach(() => {
17+
jest.resetModules();
18+
React = require('react');
19+
ReactDOMServer = require('react-dom/server');
20+
});
1621

17-
it('should escape boolean to string', () => {
18-
expect(quoteAttributeValueForBrowser(true)).toBe('"true"');
19-
expect(quoteAttributeValueForBrowser(false)).toBe('"false"');
22+
it('ampersand is escaped inside attributes', () => {
23+
var response = ReactDOMServer.renderToString(<img data-attr="&" />);
24+
expect(response).toMatch('<img data-attr="&amp;" data-reactroot=""/>');
2025
});
2126

22-
it('should escape object to string', () => {
23-
var escaped = quoteAttributeValueForBrowser({
24-
toString: function() {
25-
return 'ponys';
26-
},
27-
});
27+
it('double quote is escaped inside attributes', () => {
28+
var response = ReactDOMServer.renderToString(<img data-attr={'"'} />);
29+
expect(response).toMatch('<img data-attr="&quot;" data-reactroot=""/>');
30+
});
31+
32+
it('single quote is escaped inside attributes', () => {
33+
var response = ReactDOMServer.renderToString(<img data-attr="'" />);
34+
expect(response).toMatch('<img data-attr="&#x27;" data-reactroot=""/>');
35+
});
2836

29-
expect(escaped).toBe('"ponys"');
37+
it('greater than entity is escaped inside attributes', () => {
38+
var response = ReactDOMServer.renderToString(<img data-attr=">" />);
39+
expect(response).toMatch('<img data-attr="&gt;" data-reactroot=""/>');
3040
});
3141

32-
it('should escape number to string', () => {
33-
expect(quoteAttributeValueForBrowser(42)).toBe('"42"');
42+
it('lower than entity is escaped inside attributes', () => {
43+
var response = ReactDOMServer.renderToString(<img data-attr="<" />);
44+
expect(response).toMatch('<img data-attr="&lt;" data-reactroot=""/>');
3445
});
3546

36-
it('should escape string', () => {
37-
var escaped = quoteAttributeValueForBrowser(
38-
'<script type=\'\' src=""></script>',
47+
it('number is escaped to string inside attributes', () => {
48+
var response = ReactDOMServer.renderToString(<img data-attr={42} />);
49+
expect(response).toMatch('<img data-attr="42" data-reactroot=""/>');
50+
});
51+
52+
it('object is passed to a string inside attributes', () => {
53+
var sampleObject = {
54+
toString: function() {
55+
return 'ponys';
56+
},
57+
};
58+
59+
var response = ReactDOMServer.renderToString(
60+
<img data-attr={sampleObject} />,
3961
);
40-
expect(escaped).not.toContain('<');
41-
expect(escaped).not.toContain('>');
42-
expect(escaped).not.toContain("'");
43-
expect(escaped.substr(1, -1)).not.toContain('"');
62+
expect(response).toMatch('<img data-attr="ponys" data-reactroot=""/>');
63+
});
4464

45-
escaped = quoteAttributeValueForBrowser('&');
46-
expect(escaped).toBe('"&amp;"');
65+
it('script tag is escaped inside attributes', () => {
66+
var response = ReactDOMServer.renderToString(
67+
<img data-attr={'<script type=\'\' src=""></script>'} />,
68+
);
69+
expect(response).toMatch(
70+
'<img data-attr="&lt;script type=&#x27;&#x27; ' +
71+
'src=&quot;&quot;&gt;&lt;/script&gt;" ' +
72+
'data-reactroot=""/>',
73+
);
4774
});
4875
});

packages/react-dom/src/client/setTextContent.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
9-
10-
import setInnerHTML from './setInnerHTML';
11-
import escapeTextContentForBrowser from '../shared/escapeTextContentForBrowser';
128
import {TEXT_NODE} from '../shared/HTMLNodeType';
139

1410
/**
@@ -37,16 +33,4 @@ let setTextContent = function(node, text) {
3733
node.textContent = text;
3834
};
3935

40-
if (ExecutionEnvironment.canUseDOM) {
41-
if (!('textContent' in document.documentElement)) {
42-
setTextContent = function(node, text) {
43-
if (node.nodeType === TEXT_NODE) {
44-
node.nodeValue = text;
45-
return;
46-
}
47-
setInnerHTML(node, escapeTextContentForBrowser(text));
48-
};
49-
}
50-
}
51-
5236
export default setTextContent;

packages/react-dom/src/server/DOMMarkupOperations.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
shouldAttributeAcceptBooleanValue,
1515
shouldSetAttribute,
1616
} from '../shared/DOMProperty';
17-
import quoteAttributeValueForBrowser from '../shared/quoteAttributeValueForBrowser';
17+
import quoteAttributeValueForBrowser from './quoteAttributeValueForBrowser';
1818
import warning from 'fbjs/lib/warning';
1919

2020
// isAttributeNameSafe() is currently duplicated in DOMPropertyOperations.

packages/react-dom/src/server/ReactPartialRenderer.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
createMarkupForProperty,
2727
createMarkupForRoot,
2828
} from './DOMMarkupOperations';
29+
import escapeTextForBrowser from './escapeTextForBrowser';
2930
import {
3031
Namespaces,
3132
getIntrinsicNamespace,
@@ -34,7 +35,6 @@ import {
3435
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
3536
import assertValidProps from '../shared/assertValidProps';
3637
import dangerousStyleValue from '../shared/dangerousStyleValue';
37-
import escapeTextContentForBrowser from '../shared/escapeTextContentForBrowser';
3838
import isCustomComponent from '../shared/isCustomComponent';
3939
import omittedCloseTags from '../shared/omittedCloseTags';
4040
import warnValidStyle from '../shared/warnValidStyle';
@@ -204,7 +204,7 @@ function getNonChildrenInnerMarkup(props) {
204204
} else {
205205
var content = props.children;
206206
if (typeof content === 'string' || typeof content === 'number') {
207-
return escapeTextContentForBrowser(content);
207+
return escapeTextForBrowser(content);
208208
}
209209
}
210210
return null;
@@ -572,13 +572,13 @@ class ReactDOMServerRenderer {
572572
return '';
573573
}
574574
if (this.makeStaticMarkup) {
575-
return escapeTextContentForBrowser(text);
575+
return escapeTextForBrowser(text);
576576
}
577577
if (this.previousWasTextNode) {
578-
return '<!-- -->' + escapeTextContentForBrowser(text);
578+
return '<!-- -->' + escapeTextForBrowser(text);
579579
}
580580
this.previousWasTextNode = true;
581-
return escapeTextContentForBrowser(text);
581+
return escapeTextForBrowser(text);
582582
} else {
583583
var nextChild;
584584
({child: nextChild, context} = resolve(child, context));

packages/react-dom/src/shared/escapeTextContentForBrowser.js renamed to packages/react-dom/src/server/escapeTextForBrowser.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939
var matchHtmlRegExp = /["'&<>]/;
4040

4141
/**
42-
* Escape special characters in the given string of html.
42+
* Escapes special characters and HTML entities in a given html string.
4343
*
44-
* @param {string} string The string to escape for inserting into HTML
44+
* @param {string} string HTML string to escape for later insertion
4545
* @return {string}
4646
* @public
4747
*/
@@ -98,7 +98,7 @@ function escapeHtml(string) {
9898
* @param {*} text Text value to escape.
9999
* @return {string} An escaped string.
100100
*/
101-
function escapeTextContentForBrowser(text) {
101+
function escapeTextForBrowser(text) {
102102
if (typeof text === 'boolean' || typeof text === 'number') {
103103
// this shortcircuit helps perf for types that we know will never have
104104
// special characters, especially given that this function is used often
@@ -108,4 +108,4 @@ function escapeTextContentForBrowser(text) {
108108
return escapeHtml(text);
109109
}
110110

111-
export default escapeTextContentForBrowser;
111+
export default escapeTextForBrowser;

packages/react-dom/src/shared/quoteAttributeValueForBrowser.js renamed to packages/react-dom/src/server/quoteAttributeValueForBrowser.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import escapeTextContentForBrowser from './escapeTextContentForBrowser';
8+
import escapeTextForBrowser from './escapeTextForBrowser';
99

1010
/**
1111
* Escapes attribute value to prevent scripting attacks.
@@ -14,7 +14,7 @@ import escapeTextContentForBrowser from './escapeTextContentForBrowser';
1414
* @return {string} An escaped string.
1515
*/
1616
function quoteAttributeValueForBrowser(value) {
17-
return '"' + escapeTextContentForBrowser(value) + '"';
17+
return '"' + escapeTextForBrowser(value) + '"';
1818
}
1919

2020
export default quoteAttributeValueForBrowser;

0 commit comments

Comments
 (0)