55
66import type { BeforeSendResponse , BrowserWindow , BrowserWindowConstructorOptions , Event , OnBeforeSendHeadersListenerDetails } from 'electron' ;
77import { Queue , raceTimeout , TimeoutTimer } from '../../../base/common/async.js' ;
8- import { CancellationTokenSource } from '../../../base/common/cancellation.js' ;
8+ import { CancellationToken , CancellationTokenSource } from '../../../base/common/cancellation.js' ;
99import { createSingleCallFunction } from '../../../base/common/functional.js' ;
1010import { Disposable , toDisposable } from '../../../base/common/lifecycle.js' ;
1111import { URI } from '../../../base/common/uri.js' ;
@@ -21,6 +21,17 @@ type NetworkRequestEventParams = Readonly<{
2121 type ?: string ;
2222} > ;
2323
24+ type FrameInfo = Readonly < {
25+ id : string ;
26+ url ?: string ;
27+ name ?: string ;
28+ } > ;
29+
30+ type FrameTreeNode = Readonly < {
31+ frame : FrameInfo ;
32+ childFrames ?: FrameTreeNode [ ] ;
33+ } > ;
34+
2435/**
2536 * A web page loader that uses Electron to load web pages and extract their content.
2637 */
@@ -336,7 +347,7 @@ export class WebPageLoader extends Disposable {
336347 try {
337348 await raceTimeout ( ( async ( ) => {
338349 if ( ! cts . token . isCancellationRequested ) {
339- result = await this . extractAccessibilityTreeContent ( ) ?? '' ;
350+ result = await this . extractAccessibilityTreeContent ( cts . token ) ?? '' ;
340351 }
341352
342353 if ( ! cts . token . isCancellationRequested && result . length < WebPageLoader . MIN_CONTENT_LENGTH ) {
@@ -371,13 +382,44 @@ export class WebPageLoader extends Disposable {
371382
372383 /**
373384 * Extracts content from the Accessibility tree of the loaded web page.
374- * @return The extracted content, or undefined if extraction fails.
385+ * @param token Cancellation token to abort the operation.
386+ * @return The extracted content, or undefined if extraction fails or is cancelled.
375387 */
376- private async extractAccessibilityTreeContent ( ) : Promise < string | undefined > {
388+ private async extractAccessibilityTreeContent ( token : CancellationToken ) : Promise < string | undefined > {
377389 this . trace ( `Extracting content using Accessibility domain` ) ;
378390 try {
379- const { nodes } = await this . _debugger . sendCommand ( 'Accessibility.getFullAXTree' ) as { nodes : AXNode [ ] } ;
380- return convertAXTreeToMarkdown ( this . _uri , nodes ) ;
391+ // Enable the Page domain to get frame information
392+ await this . _debugger . sendCommand ( 'Page.enable' ) ;
393+ if ( token . isCancellationRequested ) {
394+ return undefined ;
395+ }
396+
397+ // Get all frames including iframes
398+ const { frameTree } = await this . _debugger . sendCommand ( 'Page.getFrameTree' ) as { frameTree : FrameTreeNode } ;
399+ if ( token . isCancellationRequested ) {
400+ return undefined ;
401+ }
402+
403+ const frameNodes = [ frameTree ] ;
404+ for ( let i = 0 ; i < frameNodes . length ; i ++ ) {
405+ frameNodes . push ( ...frameNodes [ i ] . childFrames ?? [ ] ) ;
406+ }
407+
408+ // Collect accessibility nodes from all frames
409+ const allNodes : AXNode [ ] = [ ] ;
410+ for ( const { frame } of frameNodes ) {
411+ try {
412+ const { nodes } = await this . _debugger . sendCommand ( 'Accessibility.getFullAXTree' , { frameId : frame . id } ) as { nodes : AXNode [ ] } ;
413+ allNodes . push ( ...nodes ) ;
414+ if ( token . isCancellationRequested ) {
415+ return undefined ;
416+ }
417+ } catch {
418+ // ignore
419+ }
420+ }
421+
422+ return convertAXTreeToMarkdown ( this . _uri , allNodes ) ;
381423 } catch ( error ) {
382424 this . trace ( `Accessibility tree extraction failed: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
383425 return undefined ;
0 commit comments