Skip to content

Commit

Permalink
Fix html validation for Image component (vercel#18903)
Browse files Browse the repository at this point in the history
This PR fixes two bugs causing HTML validators to complain.

- Error: Bad value data:image/svg+xml;charset=utf-8, for attribute src on element img: Illegal character in scheme data: < is not allowed.
  - Fixed by using base64 for svg during `layout=intrinsic` to avoid angle brackets
- Error: Element img is missing required attribute src.
  - Fixed by using base64 transparent gif for `loading=lazy` placeholder

Fixes vercel#18850
  • Loading branch information
styfle authored Nov 7, 2020
1 parent 19febb1 commit b2a8a2f
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 17 deletions.
18 changes: 8 additions & 10 deletions packages/next/client/image.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { ReactElement } from 'react'
import Head from '../next-server/lib/head'
import { toBase64 } from '../next-server/lib/to-base-64'
import { useIntersection } from './use-intersection'

const VALID_LOADING_VALUES = ['lazy', 'eager', undefined] as const
Expand Down Expand Up @@ -396,17 +397,14 @@ export default function Image({
quality: qualityInt,
})

let imgAttributes:
| Pick<JSX.IntrinsicElements['img'], 'src' | 'srcSet'>
| undefined
const imgAttributes: Pick<JSX.IntrinsicElements['img'], 'src' | 'srcSet'> = {
src:
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
}

if (isVisible) {
imgAttributes = {
src: imgSrc,
}
if (imgSrcSet) {
imgAttributes.srcSet = imgSrcSet
}
imgAttributes.src = imgSrc
imgAttributes.srcSet = imgSrcSet
}

// No need to add preloads on the client side--by the time the application is hydrated,
Expand Down Expand Up @@ -438,7 +436,7 @@ export default function Image({
alt=""
aria-hidden={true}
role="presentation"
src={`data:image/svg+xml;charset=utf-8,${sizerSvg}`}
src={`data:image/svg+xml;base64,${toBase64(sizerSvg)}`}
/>
) : null}
</div>
Expand Down
10 changes: 10 additions & 0 deletions packages/next/next-server/lib/to-base-64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Isomorphic base64 that works on the server and client
*/
export function toBase64(str: string) {
if (typeof window === 'undefined') {
return Buffer.from(str).toString('base64')
} else {
return window.btoa(str)
}
}
16 changes: 9 additions & 7 deletions test/integration/image-component/basic/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const appDir = join(__dirname, '../')
let appPort
let app
let browser
const emptyImage =
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'

function runTests() {
it('should render an image tag', async () => {
Expand Down Expand Up @@ -97,9 +99,9 @@ function lazyLoadingTests() {
)
})
it('should not have loaded the second image immediately', async () => {
expect(
await browser.elementById('lazy-mid').getAttribute('src')
).toBeFalsy()
expect(await browser.elementById('lazy-mid').getAttribute('src')).toBe(
emptyImage
)
expect(
await browser.elementById('lazy-mid').getAttribute('srcset')
).toBeFalsy()
Expand Down Expand Up @@ -128,9 +130,9 @@ function lazyLoadingTests() {
}, 'https://example.com/myaccount/foo2.jpg?auto=format&fit=max&w=480 1x, https://example.com/myaccount/foo2.jpg?auto=format&fit=max&w=1024 2x')
})
it('should not have loaded the third image after scrolling down', async () => {
expect(
await browser.elementById('lazy-bottom').getAttribute('src')
).toBeFalsy()
expect(await browser.elementById('lazy-bottom').getAttribute('src')).toBe(
emptyImage
)
expect(
await browser.elementById('lazy-bottom').getAttribute('srcset')
).toBeFalsy()
Expand All @@ -155,7 +157,7 @@ function lazyLoadingTests() {
it('should load the fourth image lazily after scrolling down', async () => {
expect(
await browser.elementById('lazy-without-attribute').getAttribute('src')
).toBeFalsy()
).toBe(emptyImage)
expect(
await browser.elementById('lazy-without-attribute').getAttribute('srcset')
).toBeFalsy()
Expand Down

0 comments on commit b2a8a2f

Please sign in to comment.