@@ -464,6 +464,129 @@ function wrapCodeIfNeeded(code) {
464464}
465465
466466async function evalInTab ( tabId , code , awaitPromise , evalId ) {
467+
468+ /*
469+ * executes only for iframes by bypassing the CORS issue
470+ * so this implementatiton is something
471+ * first we'll get every single frames from the tab
472+ * then parse an special const name `IFRAMESELCTOR` from raw js code to
473+ get the selector of iframe from main document
474+ * after that we'll get the `frameId` of the iframe to execute
475+ raw js code inside the iframe's document to avoid CORS issue
476+ * rewrite the raw js code so it removes the unneccesary `IFRAMESELCTOR` from eval code
477+ * run user code safely by creating a Function rather than eval (still executes arbitrary code)
478+ */
479+ if ( code . includes ( "IFRAMESELCTOR" ) ) {
480+ const frames = await chrome . webNavigation . getAllFrames ( { tabId } ) ;
481+ log ( "Frames seen by extension:" , frames ) ;
482+
483+ const regex = / c o n s t \s + I F R A M E S E L C T O R \s * = \s * [ ' " ` ] ? \s * ( q u e r y S e l e c t o r | g e t E l e m e n t B y I d ) \s * \( \s * ( [ ' " ` ] ) ( .* ?) \2\s * \) \s * ; ? \s * [ ' " ` ] ? / s;
484+ const match = code . match ( regex ) ;
485+ const iframeInfo = {
486+ method : match [ 1 ] ,
487+ selector : match [ 3 ]
488+ } ;
489+ if ( ! match ) {
490+ throw new Error ( `No selector named 'IFRAMESELCTOR' have been defined please define
491+ this variable to execute code inside in iframe's document context` ) ;
492+ } ;
493+
494+ if ( iframeInfo ) {
495+ const iframeSelector = iframeInfo . selector ;
496+ if ( iframeSelector ) {
497+ log ( `Executing in given iframe of selector: ${ iframeSelector } , selector type: ${ iframeInfo . method } ` ) ;
498+ if ( ! attachedTabs . has ( tabId ) ) {
499+ await debuggerAttach ( tabId ) ;
500+ attachedTabs . add ( tabId ) ;
501+ await chrome . storage . session . set ( { attached : [ ...attachedTabs ] } ) ;
502+ log ( `Debugger attached to tab '${ tabId } ' for iframe evaluation` ) ;
503+ }
504+
505+ let frameId ;
506+ try {
507+ await sendCommand ( tabId , "Page.enable" , { } ) ;
508+ await sendCommand ( tabId , "DOM.enable" , { } ) ;
509+ const { root } = await sendCommand ( tabId , "DOM.getDocument" , { depth : - 1 } ) ;
510+ const { nodeId } = await sendCommand ( tabId , "DOM.querySelector" , {
511+ nodeId : root . nodeId ,
512+ selector : iframeSelector ,
513+ } ) ;
514+
515+ if ( ! nodeId ) {
516+ throw new Error ( `Iframe with selector "${ iframeSelector } " not found.` ) ;
517+ }
518+ const { node } = await sendCommand ( tabId , "DOM.describeNode" , { nodeId } ) ;
519+ if ( ! node ) {
520+ throw new Error ( `Could not retrieve node info for the specified iframe, selector ${ iframeInfo . selector } ` ) ;
521+ }
522+
523+ /* now here we've to get the actual correct `frameId` from by matching
524+ the `src` attributes of iframe to the all the existing iframe on that opened tab
525+ */
526+ const iframeSrc = node . attributes ?. [ node . attributes . indexOf ( "src" ) + 1 ] ;
527+ log ( "iframeSrc" , iframeSrc )
528+ const match = frames . find ( f => {
529+ try {
530+ const u1 = new URL ( f . url ) ;
531+ const u2 = new URL ( iframeSrc ) ;
532+ const host1 = u1 . hostname . replace ( / ^ w w w \. / , "" ) . toLowerCase ( ) ;
533+ const host2 = u2 . hostname . replace ( / ^ w w w \. / , "" ) . toLowerCase ( ) ;
534+ if ( host1 !== host2 ) return false ;
535+ const p1 = u1 . pathname . split ( "/" ) . filter ( Boolean ) ;
536+ const p2 = u2 . pathname . split ( "/" ) . filter ( Boolean ) ;
537+ for ( let i = 0 ; i < Math . min ( 3 , p1 . length , p2 . length ) ; i ++ ) {
538+ if ( p1 [ i ] !== p2 [ i ] ) return false ;
539+ }
540+ return true ;
541+ } catch {
542+ return false ;
543+ }
544+ } ) ;
545+ frameId = match . frameId ;
546+ log ( `matched frameId: ${ match . frameId } ` )
547+
548+ } catch ( err ) {
549+ log ( "Error finding frame:" , err ) ;
550+ try { await debuggerDetach ( tabId ) ; } catch ( _ ) { }
551+ attachedTabs . delete ( tabId ) ;
552+ throw err ;
553+ }
554+
555+ const lines = code . split ( "\n" ) . filter ( line => ! line . includes ( "IFRAMESELCTOR" ) ) ;
556+ const rewritten = lines . join ( "\n" )
557+ log ( `Rewritten code for iframe: ${ rewritten } ` ) ;
558+
559+ /* execute the rewritten code inside the found frame */
560+ try {
561+ const results = await chrome . scripting . executeScript ( {
562+ target : { tabId : tabId , frameIds : [ frameId ] } ,
563+ world : "MAIN" ,
564+ func : ( userScript , shouldAwait ) => {
565+ return ( async ( ) => {
566+ const fn = new Function ( userScript ) ;
567+ const result = fn ( ) ;
568+ if ( shouldAwait && result instanceof Promise ) {
569+ return await result ;
570+ }
571+ return result ;
572+ } ) ( ) ;
573+ } ,
574+ args : [ rewritten , ! ! awaitPromise ] ,
575+ } ) ;
576+
577+ if ( ! results || results . length === 0 ) {
578+ throw new Error ( "Script execution in frame produced no result." ) ;
579+ }
580+ return results [ 0 ] . result ;
581+ } catch ( err ) {
582+ log ( `Error executing script in frame ${ frameId } :` , err ) ;
583+ throw err ;
584+ }
585+ }
586+ }
587+ }
588+
589+
467590 const perfStart = performance . now ( ) ;
468591 const timings = { } ;
469592
0 commit comments