diff --git a/static/workspace.js b/static/workspace.js index 608637ac..71989348 100644 --- a/static/workspace.js +++ b/static/workspace.js @@ -1425,16 +1425,22 @@ async function _collectFilesFromEntry(entry, relPrefix) { async function _collectOsDropUploads(dataTransfer) { const out = []; const items = dataTransfer.items ? [...dataTransfer.items] : []; - if (items.length && typeof items[0].webkitGetAsEntry === 'function') { + const files = dataTransfer.files ? [...dataTransfer.files] : []; + if (items.length) { + const entries = []; for (const item of items) { if (item.kind !== 'file') continue; - const entry = item.webkitGetAsEntry(); + const getAsEntry = item.getAsEntry || item.webkitGetAsEntry; + const entry = typeof getAsEntry === 'function' ? getAsEntry.call(item) : null; if (!entry) continue; + entries.push(entry); + } + for (const entry of entries) { out.push(...await _collectFilesFromEntry(entry, '')); } if (out.length) return out; } - for (const file of dataTransfer.files) { + for (const file of files) { out.push({ file, relDir: '' }); } return out; diff --git a/tests/test_issue3402_workspace_os_import.py b/tests/test_issue3402_workspace_os_import.py index 7fa472ea..e37482ac 100644 --- a/tests/test_issue3402_workspace_os_import.py +++ b/tests/test_issue3402_workspace_os_import.py @@ -81,3 +81,39 @@ console.log(JSON.stringify(cases.map(([a,b]) => b))); ".", "docs", "docs/reports", "src/lib/utils", "projects", "projects/bundle", "bundle/sub", ] + + +def test_os_drop_snapshots_entries_before_async_traversal(): + node = shutil.which("node") + if not node: + return + js = r""" +const fs=require('fs'),vm=require('vm'); +const src=fs.readFileSync('static/workspace.js','utf8'); +const block=src.slice( + src.indexOf('async function _readAllDirectoryEntries'), + src.indexOf('function _clearWorkspaceOsUploadDragOver')); +const uploaded=[],acquired=[],atTraversal=[]; +const ctx={ + S:{session:true,currentDir:'.'},_workspacePathIsReadOnly:()=>false, + showToast(){},t:x=>x,_targetDirForRelDir:(d,r)=>r||d, + uploadToWorkspace:async f=>uploaded.push(f.name),loadDir:async()=>{}}; +vm.createContext(ctx);vm.runInContext(block+';this.collect=_collectOsDropUploads;this.upload=uploadOsDropToWorkspace',ctx); +let locked=false; +const fileEntry=name=>({isFile:true,file:resolve=>queueMicrotask(()=>{atTraversal.push(acquired.length);locked=true;resolve({name})})}); +const names=['alpha.txt','beta.txt','gamma.txt']; +const items=names.map(name=>({kind:'file',webkitGetAsEntry(){acquired.push(name);return locked?null:fileEntry(name)}})); +(async()=>{ + await ctx.upload({items,files:[]},'.'); + let reads=0;const nested={name:'nested',isDirectory:true,createReader:()=>({readEntries:resolve=>queueMicrotask(()=>resolve(reads++?[]:[fileEntry('inside.txt')]))})}; + const nestedFiles=await ctx.collect({items:[{kind:'file',getAsEntry:()=>nested}],files:[]}); + const fallback=await ctx.collect({items:[{kind:'file',getAsEntry:()=>null}],files:[{name:'fallback.txt'}]}); + console.log(JSON.stringify([acquired,atTraversal.slice(0,3),uploaded,nestedFiles.map(x=>[x.file.name,x.relDir]),fallback.map(x=>x.file.name)])); +})().catch(error=>{console.error(error);process.exit(1)}); +""" + out = subprocess.check_output([node, "-e", js], text=True).strip() + assert json.loads(out) == [ + ["alpha.txt", "beta.txt", "gamma.txt"], [3, 3, 3], + ["alpha.txt", "beta.txt", "gamma.txt"], + [["inside.txt", "nested/"]], ["fallback.txt"], + ]