/**1* Copyright 2018 Google Inc. All rights reserved.2* Modifications copyright (c) Data Liberation Foundation Inc.3*4* Licensed under the Apache License, Version 2.0 (the "License");5* you may not use this file except in compliance with the License.6* You may obtain a copy of the License at7*8* http://www.apache.org/licenses/LICENSE-2.09*10* Unless required by applicable law or agreed to in writing, software11* distributed under the License is distributed on an "AS IS" BASIS,12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13* See the License for the specific language governing permissions and14* limitations under the License.15*/16import { IMouseButton } from '@secret-agent/interfaces/IInteractions';17import { IMouseOptions } from '@secret-agent/interfaces/IPuppetInput';18import IPoint from '@secret-agent/interfaces/IPoint';19import { DevtoolsSession } from './DevtoolsSession';20import { Keyboard } from './Keyboard';2122/**23* The Mouse class operates in main-frame CSS pixels24* relative to the top-left corner of the viewport.25* @remarks26* Every `page` object has its own Mouse, accessible with [`page.mouse`](#pagemouse).27*28* @example29* ```js30* // Using ‘page.mouse’ to trace a 100x100 square.31* await page.mouse.move(0, 0);32* await page.mouse.down();33* await page.mouse.move(0, 100);34* await page.mouse.move(100, 100);35* await page.mouse.move(100, 0);36* await page.mouse.move(0, 0);37* await page.mouse.up();38* ```39*40* **Note**: The mouse events trigger synthetic `MouseEvent`s.41* This means that it does not fully replicate the functionality of what a normal user42* would be able to do with their mouse.43*44* For example, dragging and selecting text is not possible using `page.mouse`.45* Instead, you can use the {@link https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/getSelection | `DocumentOrShadowRoot.getSelection()`} functionality implemented in the platform.46*47* @public48*/4950export default class Mouse {51public position: IPoint = { x: 0, y: 0 };5253private devtoolsSession: DevtoolsSession;54private keyboard: Keyboard;55private button: IMouseButton | 'none' = 'none';5657constructor(devtoolsSession: DevtoolsSession, keyboard: Keyboard) {58this.devtoolsSession = devtoolsSession;59this.keyboard = keyboard;60}6162async move(x: number, y: number): Promise<void> {63const roundedX = Math.round(x ?? 0);64const roundedY = Math.round(y ?? 0);65if (roundedX === this.position.x && roundedY === this.position.y) return;66this.position.x = roundedX;67this.position.y = roundedY;6869await this.devtoolsSession.send('Input.dispatchMouseEvent', {70type: 'mouseMoved',71button: this.button,72x: this.position.x,73y: this.position.y,74modifiers: this.keyboard.modifiers,75});76}7778async down(options: IMouseOptions = {}): Promise<void> {79const { button = 'left', clickCount = 1 } = options;80this.button = button;81await this.devtoolsSession.send('Input.dispatchMouseEvent', {82type: 'mousePressed',83button,84x: this.position.x,85y: this.position.y,86modifiers: this.keyboard.modifiers,87clickCount,88});89}9091async up(options: IMouseOptions = {}): Promise<void> {92const { button = 'left', clickCount = 1 } = options;93this.button = 'none';94await this.devtoolsSession.send('Input.dispatchMouseEvent', {95type: 'mouseReleased',96button,97x: this.position.x,98y: this.position.y,99modifiers: this.keyboard.modifiers,100clickCount,101});102}103104async wheel(options: { deltaX?: number; deltaY?: number } = {}): Promise<void> {105const deltaX = Math.round(options.deltaX ?? 0);106const deltaY = Math.round(options.deltaY ?? 0);107108if (deltaY === 0 && deltaY === 0) return;109110await this.devtoolsSession.send('Input.dispatchMouseEvent', {111type: 'mouseWheel',112x: 0,113y: 0, // don't scroll relative to points... not included in mouse events and just confusing114deltaX,115deltaY,116modifiers: this.keyboard.modifiers,117pointerType: 'mouse',118});119}120}121122123