From 70f23628191d2ea765b035df2d90f7272b2f48e1 Mon Sep 17 00:00:00 2001 From: Roland Groza Date: Thu, 21 Nov 2024 20:47:46 +0900 Subject: [PATCH] fix: use getAsFileSystemHandle only in secure ctx --- jest.config.js | 5 ++++- src/file-selector.spec.ts | 32 ++++++++++++++++++++++++++++++-- src/file-selector.ts | 8 +++++++- test-setup.js | 6 ++++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 test-setup.js diff --git a/jest.config.js b/jest.config.js index 8678d5c..776ec03 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,9 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} **/ +/** @type {import('ts-jest').ProjectConfigTsJest} **/ module.exports = { testEnvironment: 'jsdom', + "setupFilesAfterEnv": [ + "/test-setup.js" + ], transform: { '^.+.tsx?$': [ 'ts-jest', diff --git a/src/file-selector.spec.ts b/src/file-selector.spec.ts index 2493ece..edd1a40 100644 --- a/src/file-selector.spec.ts +++ b/src/file-selector.spec.ts @@ -304,6 +304,35 @@ it('should use getAsFileSystemHandle when available', async () => { expect(file.path).toBe(`./${name}`); }); +it('should not use getAsFileSystemHandle when not in a secure context', async () => { + const f1Name = 'test.nosec.json'; + const f1 = createFile(f1Name, {ping: false}, { + type: 'application/json' + }); + const [_, h] = createFileSystemFileHandle('test.sec.json', {ping: true}, { + type: 'application/json' + }); + const evt = dragEvtFromItems([ + dataTransferItemWithFsHandle(f1, h) + ]); + + window.isSecureContext = false; + + const files = await fromEvent(evt); + expect(files).toHaveLength(1); + expect(files.every(file => file instanceof File)).toBe(true); + + const [file] = files as FileWithPath[]; + + expect(file.name).toBe(f1.name); + expect(file.type).toBe(f1.type); + expect(file.size).toBe(f1.size); + expect(file.lastModified).toBe(f1.lastModified); + expect(file.path).toBe(`./${f1Name}`); + + window.isSecureContext = true; +}); + function dragEvtFromItems(items: DataTransferItem | DataTransferItem[], type: string = 'drop'): DragEvent { return { type, @@ -466,8 +495,7 @@ function createFile(name: string, data: T, options?: FilePropertyBag) { } function createFileSystemFileHandle(name: string, data: T, options?: FilePropertyBag): [File, FileSystemFileHandle] { - const json = JSON.stringify(data); - const file = new File([json], name, options); + const file = createFile(name, data, options); return [file, { getFile() { return Promise.resolve(file); diff --git a/src/file-selector.ts b/src/file-selector.ts index 4aaee19..d5d4dfa 100644 --- a/src/file-selector.ts +++ b/src/file-selector.ts @@ -121,7 +121,13 @@ function flatten(items: any[]): T[] { } function fromDataTransferItem(item: DataTransferItem, entry?: FileSystemEntry | null) { - if (typeof (item as any).getAsFileSystemHandle === 'function') { + // Check if we're in a secure context; due to a bug in Chrome (as far as we know) + // the browser crashes when calling this API (yet to be confirmed as a consistent behaviour). + // + // See: + // - https://issues.chromium.org/issues/40186242 + // - https://github.com/react-dropzone/react-dropzone/issues/1397 + if (globalThis.isSecureContext && typeof (item as any).getAsFileSystemHandle === 'function') { return (item as any).getAsFileSystemHandle() .then(async (h: any) => { const file = await h.getFile(); diff --git a/test-setup.js b/test-setup.js new file mode 100644 index 0000000..671e925 --- /dev/null +++ b/test-setup.js @@ -0,0 +1,6 @@ +// NOTE: Let us test {isSecureContext}! +Object.defineProperty(globalThis, "isSecureContext", { + value: true, + writable: true, + enumerable: true, +});