From c190310335e46dde92af790c67d937bdd21a6c65 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 3 Sep 2020 10:09:03 -0700 Subject: [PATCH] fix(setInputFiles): make it work with CSP enabled (#3756) We used to do fetch() to decode the file buffer. However, this is blocked by strict CSP policy. Instead, we can use explicit string -> bytes conversion, and trade performance for CSP compliance. --- src/server/injected/injectedScript.ts | 10 +++++----- test/page-set-input-files.spec.ts | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/server/injected/injectedScript.ts b/src/server/injected/injectedScript.ts index 3a92e9467ea9e..e26d690b78c6d 100644 --- a/src/server/injected/injectedScript.ts +++ b/src/server/injected/injectedScript.ts @@ -452,7 +452,7 @@ export class InjectedScript { throw new Error('Not a checkbox'); } - async setInputFiles(node: Node, payloads: { name: string, mimeType: string, buffer: string }[]) { + setInputFiles(node: Node, payloads: { name: string, mimeType: string, buffer: string }[]) { if (node.nodeType !== Node.ELEMENT_NODE) return 'Node is not of type HTMLElement'; const element: Element | undefined = node as Element; @@ -463,10 +463,10 @@ export class InjectedScript { if (type !== 'file') return 'Not an input[type=file] element'; - const files = await Promise.all(payloads.map(async file => { - const result = await fetch(`data:${file.mimeType};base64,${file.buffer}`); - return new File([await result.blob()], file.name, {type: file.mimeType}); - })); + const files = payloads.map(file => { + const bytes = Uint8Array.from(atob(file.buffer), c => c.charCodeAt(0)); + return new File([bytes], file.name, { type: file.mimeType }); + }); const dt = new DataTransfer(); for (const file of files) dt.items.add(file); diff --git a/test/page-set-input-files.spec.ts b/test/page-set-input-files.spec.ts index a01a8a65fbacd..a81b064c8d6d5 100644 --- a/test/page-set-input-files.spec.ts +++ b/test/page-set-input-files.spec.ts @@ -115,6 +115,15 @@ it('should work when file input is not attached to DOM', async ({page, server}) expect(chooser).toBeTruthy(); }); +it('should work with CSP', async ({page, server}) => { + server.setCSP('/empty.html', 'default-src "none"'); + await page.goto(server.EMPTY_PAGE); + await page.setContent(``); + await page.setInputFiles('input', path.join(__dirname, '/assets/file-to-upload.txt')); + expect(await page.$eval('input', input => input.files.length)).toBe(1); + expect(await page.$eval('input', input => input.files[0].name)).toBe('file-to-upload.txt'); +}); + it('should respect timeout', async ({page, playwright}) => { let error = null; await page.waitForEvent('filechooser', {timeout: 1}).catch(e => error = e);