"""Mouse class for mouse operations.""" from typing import TYPE_CHECKING if TYPE_CHECKING: from cdp_use.cdp.input.commands import DispatchMouseEventParameters, SynthesizeScrollGestureParameters from cdp_use.cdp.input.types import MouseButton from browser_use.browser.session import BrowserSession class Mouse: """Mouse operations for a target.""" def __init__(self, browser_session: 'BrowserSession', session_id: str | None = None, target_id: str | None = None): self._browser_session = browser_session self._client = browser_session.cdp_client self._session_id = session_id self._target_id = target_id async def click(self, x: int, y: int, button: 'MouseButton' = 'left', click_count: int = 1) -> None: """Click at the specified coordinates.""" # Mouse press press_params: 'DispatchMouseEventParameters' = { 'type': 'mousePressed', 'x': x, 'y': y, 'button': button, 'clickCount': click_count, } await self._client.send.Input.dispatchMouseEvent( press_params, session_id=self._session_id, ) # Mouse release release_params: 'DispatchMouseEventParameters' = { 'type': 'mouseReleased', 'x': x, 'y': y, 'button': button, 'clickCount': click_count, } await self._client.send.Input.dispatchMouseEvent( release_params, session_id=self._session_id, ) async def down(self, button: 'MouseButton' = 'left', click_count: int = 1) -> None: """Press mouse button down.""" params: 'DispatchMouseEventParameters' = { 'type': 'mousePressed', 'x': 0, # Will use last mouse position 'y': 0, 'button': button, 'clickCount': click_count, } await self._client.send.Input.dispatchMouseEvent( params, session_id=self._session_id, ) async def up(self, button: 'MouseButton' = 'left', click_count: int = 1) -> None: """Release mouse button.""" params: 'DispatchMouseEventParameters' = { 'type': 'mouseReleased', 'x': 0, # Will use last mouse position 'y': 0, 'button': button, 'clickCount': click_count, } await self._client.send.Input.dispatchMouseEvent( params, session_id=self._session_id, ) async def move(self, x: int, y: int, steps: int = 1) -> None: """Move mouse to the specified coordinates.""" # TODO: Implement smooth movement with multiple steps if needed _ = steps # Acknowledge parameter for future use params: 'DispatchMouseEventParameters' = {'type': 'mouseMoved', 'x': x, 'y': y} await self._client.send.Input.dispatchMouseEvent(params, session_id=self._session_id) async def scroll(self, x: int = 0, y: int = 0, delta_x: int | None = None, delta_y: int | None = None) -> None: """Scroll the page using robust CDP methods.""" if not self._session_id: raise RuntimeError('Session ID is required for scroll operations') # Method 1: Try mouse wheel event (most reliable) try: # Get viewport dimensions layout_metrics = await self._client.send.Page.getLayoutMetrics(session_id=self._session_id) viewport_width = layout_metrics['layoutViewport']['clientWidth'] viewport_height = layout_metrics['layoutViewport']['clientHeight'] # Use provided coordinates or center of viewport scroll_x = x if x > 0 else viewport_width / 2 scroll_y = y if y > 0 else viewport_height / 2 # Calculate scroll deltas (positive = down/right) scroll_delta_x = delta_x or 0 scroll_delta_y = delta_y or 0 # Dispatch mouse wheel event await self._client.send.Input.dispatchMouseEvent( params={ 'type': 'mouseWheel', 'x': scroll_x, 'y': scroll_y, 'deltaX': scroll_delta_x, 'deltaY': scroll_delta_y, }, session_id=self._session_id, ) return except Exception: pass # Method 2: Fallback to synthesizeScrollGesture try: params: 'SynthesizeScrollGestureParameters' = {'x': x, 'y': y, 'xDistance': delta_x or 0, 'yDistance': delta_y or 0} await self._client.send.Input.synthesizeScrollGesture( params, session_id=self._session_id, ) except Exception: # Method 3: JavaScript fallback scroll_js = f'window.scrollBy({delta_x or 0}, {delta_y or 0})' await self._client.send.Runtime.evaluate( params={'expression': scroll_js, 'returnByValue': True}, session_id=self._session_id, )