blob: 3c5b6e8062e21201d91c35049fbc003906240f40 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import type {KeyCode} from './key_code.js';
import {TestImportManager} from './testing/test_import_manager.js';
export interface MouseClickParams {
// The amount of time to wait between press and release.
delayMs?: number;
mouseButton?: chrome.accessibilityPrivate.SyntheticMouseEventButton;
clickArgs?: MouseClickArgs;
}
interface MouseClick {
x: number;
y: number;
params: MouseClickParams;
}
interface MouseMoveArgs {
touchAccessibility?: boolean;
useRewriters?: boolean;
forceNotSynthetic?: boolean;
}
interface MouseClickArgs {
isDoubleClick?: boolean;
isTripleClick?: boolean;
forceNotSynthetic?: boolean;
}
/** Functions to send synthetic key and mouse events. */
export class EventGenerator {
static midMouseClickButton:
chrome.accessibilityPrivate.SyntheticMouseEventButton|undefined;
static mouseClickQueue: MouseClick[] = [];
/**
* Sends a single key stroke (down and up) with the given key code and
* keyboard modifiers (whether or not CTRL, ALT, SEARCH, and SHIFT are
* being held).
* @param useRewriters If true, uses rewriters for the key event;
* only allowed if used from Dictation. Otherwise indicates that rewriters
* should be skipped.
*/
static sendKeyPress(
keyCode: KeyCode,
modifiers: chrome.accessibilityPrivate.SyntheticKeyboardModifiers = {},
useRewriters = false): void {
EventGenerator.sendKeyDown(keyCode, modifiers, useRewriters);
EventGenerator.sendKeyUp(keyCode, modifiers, useRewriters);
}
/**
* Sends a single key down event with the given key code and
* keyboard modifiers (whether or not CTRL, ALT, SEARCH, and SHIFT are
* being held).
* @param useRewriters If true, uses rewriters for the key event;
* only allowed if used from Dictation. Otherwise indicates that rewriters
* should be skipped.
*/
static sendKeyDown(
keyCode: KeyCode,
modifiers: chrome.accessibilityPrivate.SyntheticKeyboardModifiers = {},
useRewriters = false, isRepeat = false): void {
const type = chrome.accessibilityPrivate.SyntheticKeyboardEventType.KEYDOWN;
chrome.accessibilityPrivate.sendSyntheticKeyEvent(
{type, keyCode, modifiers}, useRewriters, isRepeat);
}
/**
* Sends a single key up event with the given key code and
* keyboard modifiers (whether or not CTRL, ALT, SEARCH, and SHIFT are
* being held).
* @param useRewriters If true, uses rewriters for the key event;
* only allowed if used from Dictation. Otherwise indicates that rewriters
* should be skipped.
*/
static sendKeyUp(
keyCode: KeyCode,
modifiers: chrome.accessibilityPrivate.SyntheticKeyboardModifiers = {},
useRewriters = false): void {
const type = chrome.accessibilityPrivate.SyntheticKeyboardEventType.KEYUP;
chrome.accessibilityPrivate.sendSyntheticKeyEvent(
{type, keyCode, modifiers}, useRewriters);
}
/**
* Sends a synthetic mouse press (a mouse press and and a mouse release)
* to simulate a mouse click.
* If the optional arg forceNotSynthetic in MouseClickParams params is set to
* true, then the generated mouse event will not be marked as synthetic. This
* should only be used by FaceGaze.
*/
static sendMouseClick(x: number, y: number, params: MouseClickParams = {
delayMs: 0,
mouseButton: chrome.accessibilityPrivate.SyntheticMouseEventButton.LEFT,
clickArgs: {},
}): void {
const delayMs = params.delayMs ? params.delayMs : 0;
const mouseButton = params.mouseButton ?
params.mouseButton :
chrome.accessibilityPrivate.SyntheticMouseEventButton.LEFT;
const clickArgs = params.clickArgs ? params.clickArgs : {};
if (EventGenerator.midMouseClickButton !== undefined) {
// Add it to the queue for later.
EventGenerator.mouseClickQueue.push({x, y, params});
return;
}
EventGenerator.sendMousePress(x, y, mouseButton, clickArgs);
if (delayMs > 0) {
setTimeout(() => {
EventGenerator.sendMouseRelease(x, y, clickArgs);
}, params.delayMs);
} else {
EventGenerator.sendMouseRelease(x, y, clickArgs);
}
}
/**
* Sends a synthetic mouse press event, if we are not in the middle of a
* mouse click event. If we are in the middle of a mouse click, returns
* false as no press event was sent.
* If the optional arg forceNotSynthetic in MouseClickArgs clickArgs is set to
* true, then the generated mouse event will not be marked as synthetic. This
* should only be used by FaceGaze.
*/
static sendMousePress(
x: number, y: number,
mouseButton: chrome.accessibilityPrivate.SyntheticMouseEventButton,
clickArgs: MouseClickArgs = {}): boolean {
if (EventGenerator.midMouseClickButton !== undefined) {
return false;
}
EventGenerator.midMouseClickButton = mouseButton;
// chrome.accessibilityPrivate.sendSyntheticMouseEvent only accepts
// integers.
x = Math.round(x);
y = Math.round(y);
const {isDoubleClick, isTripleClick, forceNotSynthetic} = clickArgs;
const type = chrome.accessibilityPrivate.SyntheticMouseEventType.PRESS;
chrome.accessibilityPrivate.sendSyntheticMouseEvent({
type,
x,
y,
mouseButton,
isDoubleClick,
isTripleClick,
forceNotSynthetic,
});
return true;
}
/**
* Sends a synthetic mouse release event, if we are mid mouse click.
* Will start the next click from the click queue if there is one.
* If we are not mid mouse click, returns false as no release event
* was sent.
* If the optional arg forceNotSynthetic in MouseClickArgs clickArgs is set to
* true, then the generated mouse event will not be marked as synthetic. This
* should only be used by FaceGaze.
*/
static sendMouseRelease(x: number, y: number, clickArgs: MouseClickArgs = {}):
boolean {
if (EventGenerator.midMouseClickButton === undefined) {
return false;
}
// chrome.accessibilityPrivate.sendSyntheticMouseEvent only accepts
// integers.
x = Math.round(x);
y = Math.round(y);
const {isDoubleClick, isTripleClick, forceNotSynthetic} = clickArgs;
const type = chrome.accessibilityPrivate.SyntheticMouseEventType.RELEASE;
chrome.accessibilityPrivate.sendSyntheticMouseEvent({
type,
x,
y,
mouseButton: EventGenerator.midMouseClickButton,
isDoubleClick,
isTripleClick,
forceNotSynthetic,
});
EventGenerator.midMouseClickButton = undefined;
if (EventGenerator.mouseClickQueue.length > 0) {
const {x, y, params} = EventGenerator.mouseClickQueue.shift()!;
EventGenerator.sendMouseClick(x, y, params);
}
return true;
}
/**
* Sends a synthetic mouse event to simulate a move event.
* If the optional arg forceNotSynthetic in MouseMoveArgs optArgs is set to
* true, then the generated mouse event will not be marked as synthetic. This
* should only be used by FaceGaze.
*/
static sendMouseMove(x: number, y: number, optArgs: MouseMoveArgs = {}):
void {
const type = chrome.accessibilityPrivate.SyntheticMouseEventType.MOVE;
const touchAccessibility = optArgs.touchAccessibility;
const useRewriters = optArgs.useRewriters;
const forceNotSynthetic = optArgs.forceNotSynthetic;
chrome.accessibilityPrivate.sendSyntheticMouseEvent({
type,
x,
y,
touchAccessibility,
useRewriters,
forceNotSynthetic,
mouseButton: EventGenerator.midMouseClickButton,
});
}
}
TestImportManager.exportForTesting(EventGenerator);