[Fizz][Float] When src or srcSet is a data URI we should not preload the image (#27218)
Data URI's in images can't effectively be preloaded (the URI contains the data so preloading only duplicates the data in the stream. If we encounter an image with this protocol in the src attribute we should avoid preloading it.
Josh Story committed
Aug 12, 2023 at 09:21 UTC
1a001dac6220a64d98ae33a28e89dde78100d6c5
2 files changed
+51
-3
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+21
-3
@@ -2392,14 +2392,32 @@ function pushImg(
2392
props: Object,
2393
resources: Resources,
2394
): null {
2395
+ const {src, srcSet} = props;
2396
if (
2397
props.loading !== 'lazy' &&
2397
- typeof props.src === 'string' &&
2398
- props.fetchPriority !== 'low'
2398
+ (typeof src === 'string' || typeof srcSet === 'string') &&
2399
+ props.fetchPriority !== 'low' &&
2400
+ // We exclude data URIs in src and srcSet since these should not be preloaded
2401
+ !(
2402
+ typeof src === 'string' &&
2403
+ src[4] === ':' &&
2404
+ (src[0] === 'd' || src[0] === 'D') &&
2405
+ (src[1] === 'a' || src[1] === 'A') &&
2406
+ (src[2] === 't' || src[2] === 'T') &&
2407
+ (src[3] === 'a' || src[3] === 'A')
2408
+ ) &&
2409
+ !(
2410
+ typeof srcSet === 'string' &&
2411
+ srcSet[4] === ':' &&
2412
+ (srcSet[0] === 'd' || srcSet[0] === 'D') &&
2413
+ (srcSet[1] === 'a' || srcSet[1] === 'A') &&
2414
+ (srcSet[2] === 't' || srcSet[2] === 'T') &&
2415
+ (srcSet[3] === 'a' || srcSet[3] === 'A')
2416
+ )
2417
) {
2418
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
2419
// resources.
2402
- const {src, srcSet, sizes} = props;
2420
+ const {sizes} = props;
2421
const key = getImagePreloadKey(src, srcSet, sizes);
2422
let resource = resources.preloadsMap.get(key);
2423
if (!resource) {
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+30
@@ -3969,6 +3969,36 @@ body {
3969
);
3970
});
3971
3972
+ it('should not preload images that have a data URIs for src or srcSet', async () => {
3973
+ function App() {
3974
+ return (
3975
+ <html>
3976
+ <body>
3977
+ <img src="data:1" />
3978
+ <img src="data:2" srcSet="ss2" />
3979
+ <img srcSet="data:3a, data:3b 2x" />
3980
+ <img src="4" srcSet="data:4a, data4b 2x" />
3981
+ </body>
3982
+ </html>
3983
+ );
3984
+ }
3985
+ await act(() => {
3986
+ renderToPipeableStream(<App />).pipe(writable);
3987
+ });
3988
+
3989
+ expect(getMeaningfulChildren(document)).toEqual(
3990
+ <html>
3991
+ <head />
3992
+ <body>
3993
+ <img src="data:1" />
3994
+ <img src="data:2" srcset="ss2" />
3995
+ <img srcset="data:3a, data:3b 2x" />
3996
+ <img src="4" srcset="data:4a, data4b 2x" />
3997
+ </body>
3998
+ </html>,
3999
+ );
4000
+ });
4001
+
4002
describe('ReactDOM.prefetchDNS(href)', () => {
4003
it('creates a dns-prefetch resource when called', async () => {
4004
function App({url}) {