@@ -345,6 +345,46 @@ async function insertText(tabId, text) {
345345 await ensureAttached ( tabId ) ;
346346 await chrome . debugger . sendCommand ( { tabId } , "Input.insertText" , { text } ) ;
347347}
348+ async function pasteClipboardFiles ( tabId , files , selector ) {
349+ await ensureAttached ( tabId ) ;
350+ const targetExpr = selector ? `document.querySelector(${ JSON . stringify ( selector ) } )` : "document.activeElement && document.activeElement !== document.body ? document.activeElement : document.body" ;
351+ const expression = `
352+ (() => {
353+ const target = ${ targetExpr } ;
354+ if (!target) return { ok: false, reason: 'no_target' };
355+ const files = ${ JSON . stringify ( files ) } ;
356+ const dt = new DataTransfer();
357+ for (const f of files) {
358+ const binary = atob(f.base64);
359+ const bytes = new Uint8Array(binary.length);
360+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
361+ const blob = new Blob([bytes], { type: f.mimeType });
362+ dt.items.add(new File([blob], f.name, { type: f.mimeType }));
363+ }
364+ const event = new ClipboardEvent('paste', {
365+ clipboardData: dt,
366+ bubbles: true,
367+ cancelable: true,
368+ });
369+ const delivered = target.dispatchEvent(event);
370+ return { ok: true, count: files.length, delivered };
371+ })()
372+ ` ;
373+ const result = await chrome . debugger . sendCommand ( { tabId } , "Runtime.evaluate" , {
374+ expression,
375+ returnByValue : true
376+ } ) ;
377+ if ( result . exceptionDetails ) {
378+ const description = result . exceptionDetails . exception ?. description ?? result . exceptionDetails . text ?? "Unknown error" ;
379+ throw new Error ( `paste-files evaluate failed: ${ description } ` ) ;
380+ }
381+ const value = result . result ?. value ;
382+ if ( ! value ?. ok ) {
383+ const reason = value ?. reason === "no_target" ? "paste-files target not found (no focused element and no --target selector match)" : "paste-files dispatch returned no acknowledgement" ;
384+ throw new Error ( reason ) ;
385+ }
386+ return value . count ?? files . length ;
387+ }
348388function registerFrameTracking ( ) {
349389 registerFrameTargetCleanup ( ) ;
350390 chrome . debugger . onEvent . addListener ( ( source , method , params ) => {
@@ -1475,6 +1515,8 @@ async function handleCommand(cmd) {
14751515 return await handleSetFileInput ( cmd , leaseKey ) ;
14761516 case "insert-text" :
14771517 return await handleInsertText ( cmd , leaseKey ) ;
1518+ case "paste-files" :
1519+ return await handlePasteFiles ( cmd , leaseKey ) ;
14781520 case "bind" :
14791521 return await handleBind ( cmd , leaseKey ) ;
14801522 case "network-capture-start" :
@@ -2020,6 +2062,20 @@ async function handleInsertText(cmd, leaseKey) {
20202062 return { id : cmd . id , ok : false , error : err instanceof Error ? err . message : String ( err ) } ;
20212063 }
20222064}
2065+ async function handlePasteFiles ( cmd , leaseKey ) {
2066+ const files = cmd . clipboardFiles ;
2067+ if ( ! Array . isArray ( files ) || files . length === 0 ) {
2068+ return { id : cmd . id , ok : false , error : "Missing or empty clipboardFiles array" } ;
2069+ }
2070+ const cmdTabId = await resolveCommandTabId ( cmd ) ;
2071+ const tabId = await resolveTabId ( cmdTabId , leaseKey ) ;
2072+ try {
2073+ const count = await pasteClipboardFiles ( tabId , files , cmd . selector ) ;
2074+ return pageScopedResult ( cmd . id , tabId , { count } ) ;
2075+ } catch ( err ) {
2076+ return { id : cmd . id , ok : false , error : err instanceof Error ? err . message : String ( err ) } ;
2077+ }
2078+ }
20232079async function handleNetworkCaptureStart ( cmd , leaseKey ) {
20242080 const cmdTabId = await resolveCommandTabId ( cmd ) ;
20252081 const tabId = await resolveTabId ( cmdTabId , leaseKey ) ;
0 commit comments