@samitouri / QOS-React-1 / commits / d4d099f05b

[flags] make `enableTrustedTypesIntegration` dynamic (#35646)

Co-authored-by: Rick Hanlon <rickhanlonii@meta.com>

Jan Olaf Martin committed Jan 28, 2026 at 10:15 UTC d4d099f05bead14cc78787f97f005b00feae56f9
8 files changed +93 -22
packages/react-dom/src/__tests__/ReactDOMAttribute-test.js
+7 -1
@@ -171,7 +171,13 @@ describe('ReactDOM unknown attribute', () => {
171 const test = () =>
172 testUnknownAttributeAssignment(new TemporalLike(), null);
173
174 - await expect(test).rejects.toThrowError(new TypeError('prod message'));
174 + if (gate('enableTrustedTypesIntegration') && !__DEV__) {
175 + // TODO: this still throws in DEV even though it's not toString'd in prod.
176 + await expect(test).rejects.toThrowError('2020-01-01');
177 + } else {
178 + await expect(test).rejects.toThrowError(new TypeError('prod message'));
179 + }
180 +
181 assertConsoleErrorDev([
182 'The provided `unknown` attribute is an unsupported type TemporalLike.' +
183 ' This value must be coerced to a string before using it here.\n' +
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+16
@@ -602,6 +602,14 @@ describe('ReactDOMFloat', () => {
602 '> <script href="foo">\n' +
603 '\n' +
604 ' in script (at **)',
605 + ...(gate('enableTrustedTypesIntegration')
606 + ? [
607 + 'Encountered a script tag while rendering React component. ' +
608 + 'Scripts inside React components are never executed when rendering on the client. ' +
609 + 'Consider using template tag instead (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).\n' +
610 + ' in script (at **)',
611 + ]
612 + : []),
613 ]);
614
615 root.render(
@@ -2745,6 +2753,14 @@ body {
2753 '> <script itemProp="foo">\n' +
2754 '\n' +
2755 ' in script (at **)',
2756 + ...(gate('enableTrustedTypesIntegration')
2757 + ? [
2758 + 'Encountered a script tag while rendering React component. ' +
2759 + 'Scripts inside React components are never executed when rendering on the client. ' +
2760 + 'Consider using template tag instead (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).\n' +
2761 + ' in script (at **)',
2762 + ]
2763 + : []),
2764 ]);
2765 });
2766
packages/react-dom/src/__tests__/ReactDOMForm-test.js
+9 -3
@@ -2277,15 +2277,21 @@ describe('ReactDOMForm', () => {
2277 await submit(formRef.current);
2278 assertLog([actionFn]);
2279
2280 - // Everything else is toString-ed
2280 + // Everything else is toString-ed, unless trusted types are enabled.
2281 class MyAction {
2282 toString() {
2283 return 'stringified action';
2284 }
2285 }
2286 - await act(() => root.render(<Form action={new MyAction()} />));
2286 + const instance = new MyAction();
2287 +
2288 + await act(() => root.render(<Form action={instance} />));
2289 await submit(formRef.current);
2288 - assertLog(['stringified action']);
2290 + assertLog(
2291 + gate('enableTrustedTypesIntegration')
2292 + ? [instance]
2293 + : ['stringified action'],
2294 + );
2295 });
2296
2297 it('form actions should retain status when nested state changes', async () => {
packages/react-dom/src/__tests__/ReactDOMServerIntegrationUntrustedURL-test.js
+5
@@ -212,6 +212,11 @@ describe('ReactDOMServerIntegration - Untrusted URLs', () => {
212 expectedToStringCalls *= 2;
213 }
214
215 + if (gate('enableTrustedTypesIntegration') && render === clientCleanRender) {
216 + // Trusted types does another toString.
217 + expectedToStringCalls += 1;
218 + }
219 +
220 let toStringCalls = 0;
221 const firstIsSafe = {
222 toString() {
packages/react-dom/src/__tests__/ReactEmptyComponent-test.js
+14
@@ -17,6 +17,7 @@ let TogglingComponent;
17 let act;
18 let Scheduler;
19 let assertLog;
20 +let assertConsoleErrorDev;
21
22 let container;
23
@@ -34,6 +35,7 @@ describe('ReactEmptyComponent', () => {
35 const InternalTestUtils = require('internal-test-utils');
36 act = InternalTestUtils.act;
37 assertLog = InternalTestUtils.assertLog;
38 + assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
39
40 container = document.createElement('div');
41
@@ -175,6 +177,17 @@ describe('ReactEmptyComponent', () => {
177 });
178 }).not.toThrow();
179
180 + expect(container.innerHTML).toBe('<script></script>');
181 + if (gate('enableTrustedTypesIntegration')) {
182 + assertConsoleErrorDev([
183 + 'Encountered a script tag while rendering React component. ' +
184 + 'Scripts inside React components are never executed when rendering on the client. ' +
185 + 'Consider using template tag instead (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).\n' +
186 + ' in script (at **)\n' +
187 + ' in TogglingComponent (at **)',
188 + ]);
189 + }
190 +
191 const container2 = document.createElement('div');
192 const root2 = ReactDOMClient.createRoot(container2);
193 expect(() => {
@@ -189,6 +202,7 @@ describe('ReactEmptyComponent', () => {
202 'mount SCRIPT',
203 'update undefined',
204 ]);
205 + expect(container2.innerHTML).toBe('');
206 });
207
208 it(
packages/react-dom/src/client/__tests__/trustedTypes-test.internal.js
+39 -16
@@ -12,7 +12,6 @@
12 describe('when Trusted Types are available in global object', () => {
13 let React;
14 let ReactDOMClient;
15 - let ReactFeatureFlags;
15 let act;
16 let assertConsoleErrorDev;
17 let container;
@@ -33,8 +32,6 @@ describe('when Trusted Types are available in global object', () => {
32 isScript: () => false,
33 isScriptURL: () => false,
34 };
36 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
37 - ReactFeatureFlags.enableTrustedTypesIntegration = true;
35 React = require('react');
36 ReactDOMClient = require('react-dom/client');
37 ({act, assertConsoleErrorDev} = require('internal-test-utils'));
@@ -118,7 +115,11 @@ describe('when Trusted Types are available in global object', () => {
115 expect(setAttributeCalls[0][0]).toBe(container.firstChild);
116 expect(setAttributeCalls[0][1]).toBe('data-foo');
117 // Ensure it didn't get stringified when passed to a DOM sink:
121 - expect(setAttributeCalls[0][2]).toBe(ttObject1);
118 + if (gate('enableTrustedTypesIntegration')) {
119 + expect(setAttributeCalls[0][2]).toBe(ttObject1);
120 + } else {
121 + expect(setAttributeCalls[0][2]).toBe('<b>Hi</b>');
122 + }
123
124 setAttributeCalls.length = 0;
125 await act(() => {
@@ -129,7 +130,11 @@ describe('when Trusted Types are available in global object', () => {
130 expect(setAttributeCalls[0][0]).toBe(container.firstChild);
131 expect(setAttributeCalls[0][1]).toBe('data-foo');
132 // Ensure it didn't get stringified when passed to a DOM sink:
132 - expect(setAttributeCalls[0][2]).toBe(ttObject2);
133 + if (gate('enableTrustedTypesIntegration')) {
134 + expect(setAttributeCalls[0][2]).toBe(ttObject2);
135 + } else {
136 + expect(setAttributeCalls[0][2]).toBe('<b>Bye</b>');
137 + }
138 } finally {
139 Element.prototype.setAttribute = setAttribute;
140 }
@@ -153,7 +158,11 @@ describe('when Trusted Types are available in global object', () => {
158 expect(setAttributeCalls[0][0]).toBe(container.firstChild);
159 expect(setAttributeCalls[0][1]).toBe('class');
160 // Ensure it didn't get stringified when passed to a DOM sink:
156 - expect(setAttributeCalls[0][2]).toBe(ttObject1);
161 + if (gate('enableTrustedTypesIntegration')) {
162 + expect(setAttributeCalls[0][2]).toBe(ttObject1);
163 + } else {
164 + expect(setAttributeCalls[0][2]).toBe('<b>Hi</b>');
165 + }
166
167 setAttributeCalls.length = 0;
168 await act(() => {
@@ -164,7 +173,11 @@ describe('when Trusted Types are available in global object', () => {
173 expect(setAttributeCalls[0][0]).toBe(container.firstChild);
174 expect(setAttributeCalls[0][1]).toBe('class');
175 // Ensure it didn't get stringified when passed to a DOM sink:
167 - expect(setAttributeCalls[0][2]).toBe(ttObject2);
176 + if (gate('enableTrustedTypesIntegration')) {
177 + expect(setAttributeCalls[0][2]).toBe(ttObject2);
178 + } else {
179 + expect(setAttributeCalls[0][2]).toBe('<b>Bye</b>');
180 + }
181 } finally {
182 Element.prototype.setAttribute = setAttribute;
183 }
@@ -189,7 +202,11 @@ describe('when Trusted Types are available in global object', () => {
202 expect(setAttributeNSCalls[0][1]).toBe('http://www.w3.org/1999/xlink');
203 expect(setAttributeNSCalls[0][2]).toBe('xlink:href');
204 // Ensure it didn't get stringified when passed to a DOM sink:
192 - expect(setAttributeNSCalls[0][3]).toBe(ttObject1);
205 + if (gate('enableTrustedTypesIntegration')) {
206 + expect(setAttributeNSCalls[0][3]).toBe(ttObject1);
207 + } else {
208 + expect(setAttributeNSCalls[0][3]).toBe('<b>Hi</b>');
209 + }
210
211 setAttributeNSCalls.length = 0;
212 await act(() => {
@@ -201,7 +218,11 @@ describe('when Trusted Types are available in global object', () => {
218 expect(setAttributeNSCalls[0][1]).toBe('http://www.w3.org/1999/xlink');
219 expect(setAttributeNSCalls[0][2]).toBe('xlink:href');
220 // Ensure it didn't get stringified when passed to a DOM sink:
204 - expect(setAttributeNSCalls[0][3]).toBe(ttObject2);
221 + if (gate('enableTrustedTypesIntegration')) {
222 + expect(setAttributeNSCalls[0][3]).toBe(ttObject2);
223 + } else {
224 + expect(setAttributeNSCalls[0][3]).toBe('<b>Bye</b>');
225 + }
226 } finally {
227 Element.prototype.setAttributeNS = setAttributeNS;
228 }
@@ -212,13 +233,15 @@ describe('when Trusted Types are available in global object', () => {
233 await act(() => {
234 root.render(<script>alert("I am not executed")</script>);
235 });
215 - assertConsoleErrorDev([
216 - 'Encountered a script tag while rendering React component. ' +
217 - 'Scripts inside React components are never executed when rendering ' +
218 - 'on the client. Consider using template tag instead ' +
219 - '(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).\n' +
220 - ' in script (at **)',
221 - ]);
236 + if (gate('enableTrustedTypesIntegration')) {
237 + assertConsoleErrorDev([
238 + 'Encountered a script tag while rendering React component. ' +
239 + 'Scripts inside React components are never executed when rendering ' +
240 + 'on the client. Consider using template tag instead ' +
241 + '(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).\n' +
242 + ' in script (at **)',
243 + ]);
244 + }
245
246 // check that the warning is printed only once
247 await act(() => {
packages/shared/CheckStringCoercion.js
+2
@@ -76,6 +76,8 @@ export function checkAttributeStringCoercion(
76 attributeName: string,
77 ): void | string {
78 if (__DEV__) {
79 + // TODO: for enableTrustedTypesIntegration we don't toString this
80 + // so we shouldn't need the DEV warning.
81 if (willCoercionThrow(value)) {
82 console.error(
83 'The provided `%s` attribute is an unsupported type %s.' +
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+1 -2
@@ -35,12 +35,11 @@ export const enableScrollEndPolyfill: boolean = __VARIANT__;
35 export const enableFragmentRefs: boolean = __VARIANT__;
36 export const enableFragmentRefsScrollIntoView: boolean = __VARIANT__;
37 export const enableAsyncDebugInfo: boolean = __VARIANT__;
38 -
38 export const enableInternalInstanceMap: boolean = __VARIANT__;
39 +export const enableTrustedTypesIntegration: boolean = __VARIANT__;
40
41 // TODO: These flags are hard-coded to the default values used in open source.
42 // Update the tests so that they pass in either mode, then set these
43 // to __VARIANT__.
44 -export const enableTrustedTypesIntegration: boolean = false;
44 // You probably *don't* want to add more hardcoded ones.
45 // Instead, try to add them above with the __VARIANT__ value.