1818import * as input from '../../input' ;
1919
2020import type * as types from '../../types' ;
21- import type { WVSession } from './wvConnection' ;
21+ import type * as frames from '../../frames' ;
22+ import type { Func1 } from '../../javascript' ;
2223import type { Progress } from '../../progress' ;
24+ import type { WVPage } from './wvPage' ;
2325
2426function modifierFlags ( modifiers : Set < types . KeyboardModifier > ) {
2527 return {
@@ -51,11 +53,16 @@ function toButtonsMask(buttons: Set<types.MouseButton>): number {
5153 return mask ;
5254}
5355
56+ async function evaluateInFrame < Arg > ( progress : Progress , frame : frames . Frame , pageFunction : Func1 < Arg , void > , arg : Arg ) : Promise < void > {
57+ const context = await progress . race ( frame . mainContext ( ) ) ;
58+ await progress . race ( context . evaluate ( pageFunction , arg ) ) ;
59+ }
60+
5461export class RawKeyboardImpl implements input . RawKeyboard {
55- private _session : WVSession | undefined ;
62+ private _page : WVPage ;
5663
57- setSession ( session : WVSession ) {
58- this . _session = session ;
64+ constructor ( page : WVPage ) {
65+ this . _page = page ;
5966 }
6067
6168 async keydown ( progress : Progress , modifiers : Set < types . KeyboardModifier > , keyName : string , description : input . KeyDescription , autoRepeat : boolean ) : Promise < void > {
@@ -65,31 +72,48 @@ export class RawKeyboardImpl implements input.RawKeyboard {
6572 ...modifierFlags ( modifiers ) ,
6673 ...( text ? { text } : { } ) ,
6774 } ;
68- await callWebViewInput ( progress , this . _session , 'keydown' , params ) ;
75+ const frame = await this . _page . deepestFocusedFrame ( progress ) ;
76+ await evaluateInFrame ( progress , frame , p => ( globalThis as any ) . __pwWebViewInput . keydown ( p ) , params ) ;
6977 }
7078
7179 async keyup ( progress : Progress , modifiers : Set < types . KeyboardModifier > , keyName : string , description : input . KeyDescription ) : Promise < void > {
7280 const { code, keyCode, key, location } = description ;
7381 const params = { code, key, keyCode, location, ...modifierFlags ( modifiers ) } ;
74- await callWebViewInput ( progress , this . _session , 'keyup' , params ) ;
82+ const frame = await this . _page . deepestFocusedFrame ( progress ) ;
83+ await evaluateInFrame ( progress , frame , p => ( globalThis as any ) . __pwWebViewInput . keyup ( p ) , params ) ;
7584 }
7685
7786 async sendText ( progress : Progress , text : string ) : Promise < void > {
78- await callWebViewInput ( progress , this . _session , 'insertText' , text ) ;
87+ const frame = await this . _page . deepestFocusedFrame ( progress ) ;
88+ await evaluateInFrame ( progress , frame , t => ( globalThis as any ) . __pwWebViewInput . insertText ( t ) , text ) ;
7989 }
8090}
8191
8292export class RawMouseImpl implements input . RawMouse {
83- private _session : WVSession | undefined ;
93+ private _page : WVPage ;
94+ private _lastHoveredFrames : frames . Frame [ ] = [ ] ;
8495
85- setSession ( session : WVSession ) {
86- this . _session = session ;
96+ constructor ( page : WVPage ) {
97+ this . _page = page ;
8798 }
8899
89100 async move ( progress : Progress , x : number , y : number , button : types . MouseButton | 'none' , buttons : Set < types . MouseButton > , modifiers : Set < types . KeyboardModifier > , forClick : boolean ) : Promise < void > {
90- await callWebViewInput ( progress , this . _session , 'mouseMove' , {
91- x, y, button : buttonToNumber ( button ) , buttons : toButtonsMask ( buttons ) , ...modifierFlags ( modifiers ) ,
92- } ) ;
101+ const path = await this . _page . framePointerPath ( progress , x , y ) ;
102+ const params = { button : buttonToNumber ( button ) , buttons : toButtonsMask ( buttons ) , ...modifierFlags ( modifiers ) } ;
103+ // Each frame tracks its own hover target, so as the pointer crosses an <iframe>
104+ // boundary it must leave the frames it is no longer within (deepest first) before
105+ // entering the frames along the new path. A move that stays within the same frames
106+ // leaves none of them, so no cross-frame mouseout/mouseleave is dispatched.
107+ const hoveredFrames = path . map ( entry => entry . frame ) ;
108+ const attachedFrames = this . _page . _page . frameManager . frames ( ) ;
109+ for ( const frame of this . _lastHoveredFrames . reverse ( ) ) {
110+ if ( hoveredFrames . includes ( frame ) || ! attachedFrames . includes ( frame ) )
111+ continue ;
112+ await evaluateInFrame ( progress , frame , ( ) => ( globalThis as any ) . __pwWebViewInput . clearHover ( ) , undefined ) ;
113+ }
114+ this . _lastHoveredFrames = hoveredFrames ;
115+ for ( const { frame, point } of path )
116+ await evaluateInFrame ( progress , frame , p => ( globalThis as any ) . __pwWebViewInput . mouseMove ( p ) , { ...params , ...point } ) ;
93117 }
94118
95119 async down ( progress : Progress , x : number , y : number , button : types . MouseButton , buttons : Set < types . MouseButton > , modifiers : Set < types . KeyboardModifier > , clickCount : number ) : Promise < void > {
@@ -114,43 +138,27 @@ export class RawMouseImpl implements input.RawMouse {
114138 }
115139
116140 async wheel ( progress : Progress , x : number , y : number , buttons : Set < types . MouseButton > , modifiers : Set < types . KeyboardModifier > , deltaX : number , deltaY : number ) : Promise < void > {
117- await callWebViewInput ( progress , this . _session , 'wheel' , { x, y, deltaX, deltaY, ...modifierFlags ( modifiers ) } ) ;
141+ const { frame, point } = await this . _page . deepestFrameForPoint ( progress , x , y ) ;
142+ await evaluateInFrame ( progress , frame , p => ( globalThis as any ) . __pwWebViewInput . wheel ( p ) , { ...point , deltaX, deltaY, ...modifierFlags ( modifiers ) } ) ;
118143 }
119144
120145 private async _mouseEvent ( progress : Progress , type : string , x : number , y : number , button : number , buttons : number , modifiers : Set < types . KeyboardModifier > , clickCount : number ) {
121- await callWebViewInput ( progress , this . _session , 'mouseEvent' , {
122- type, x, y, button, buttons, clickCount, ...modifierFlags ( modifiers ) ,
146+ const { frame, point } = await this . _page . deepestFrameForPoint ( progress , x , y ) ;
147+ await evaluateInFrame ( progress , frame , p => ( globalThis as any ) . __pwWebViewInput . mouseEvent ( p ) , {
148+ type, ...point , button, buttons, clickCount, ...modifierFlags ( modifiers ) ,
123149 } ) ;
124150 }
125151}
126152
127153export class RawTouchscreenImpl implements input . RawTouchscreen {
128- private _session : WVSession | undefined ;
154+ private _page : WVPage ;
129155
130- setSession ( session : WVSession ) {
131- this . _session = session ;
156+ constructor ( page : WVPage ) {
157+ this . _page = page ;
132158 }
133159
134160 async tap ( progress : Progress , x : number , y : number , modifiers : Set < types . KeyboardModifier > ) {
135- await callWebViewInput ( progress , this . _session , 'tap' , { x, y, ...modifierFlags ( modifiers ) } ) ;
136- }
137- }
138-
139- async function callWebViewInput ( progress : Progress , session : WVSession | undefined , method : string , arg : any ) : Promise < void > {
140- if ( ! session )
141- throw new Error ( 'Page is not initialized' ) ;
142- const expression = `window.__pwWebViewInput.${ method } (${ JSON . stringify ( arg ) } )` ;
143- // Some dispatchers are async — they spread events across event-loop tasks the
144- // way a real device does. Await the returned promise so the action only
145- // resolves once every event has been delivered. Stock WebKit's Runtime.evaluate
146- // has no awaitPromise option, so use the separate Runtime.awaitPromise command.
147- const { result } = await progress . race ( session . send ( 'Runtime.evaluate' , { expression, returnByValue : false } ) ) ;
148- // `result` is absent if evaluation failed (e.g. the frame navigated away).
149- // Only promises carry an objectId here — every __pwWebViewInput method returns
150- // void or a Promise — so this both awaits async dispatch and avoids leaking a
151- // handle for the synchronous (void) case.
152- if ( result ?. className === 'Promise' && result . objectId ) {
153- await progress . race ( session . send ( 'Runtime.awaitPromise' , { promiseObjectId : result . objectId , returnByValue : true } ) ) ;
154- session . sendMayFail ( 'Runtime.releaseObject' , { objectId : result . objectId } ) ;
161+ const { frame, point } = await this . _page . deepestFrameForPoint ( progress , x , y ) ;
162+ await evaluateInFrame ( progress , frame , p => ( globalThis as any ) . __pwWebViewInput . tap ( p ) , { ...point , ...modifierFlags ( modifiers ) } ) ;
155163 }
156164}
0 commit comments