Bug 1864614 - [remote] Refactor DoubleClickTracker to work with multiple clicks sequences. r=webdriver-reviewers,whimboo

Differential Revision: https://phabricator.services.mozilla.com/D194613
This commit is contained in:
Alexandra Borovova 2023-11-29 09:51:47 +00:00
Родитель 5665a0b5c8
Коммит 2742cacf14
5 изменённых файлов: 103 добавлений и 65 удалений

Просмотреть файл

@ -7,7 +7,6 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
event: "chrome://remote/content/marionette/event.sys.mjs",
Log: "chrome://remote/content/shared/Log.sys.mjs",
});
@ -67,16 +66,6 @@ export class MarionetteEventsChild extends JSWindowActorChild {
windowId: this.innerWindowId,
});
break;
// Listen for click event to indicate one click has happened, so actions
// code can send dblclick event
case "click":
lazy.event.DoubleClickTracker.setClick();
break;
case "dblclick":
case "unload":
lazy.event.DoubleClickTracker.resetClick();
break;
}
}
}

Просмотреть файл

@ -13,10 +13,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
/** Provides functionality for creating and sending DOM events. */
export const event = {};
ChromeUtils.defineLazyGetter(lazy, "dblclickTimer", () => {
return Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
});
const _eventUtils = new WeakMap();
function _getEventUtils(win) {
@ -36,9 +32,6 @@ function _getEventUtils(win) {
return _eventUtils.get(win);
}
// Max interval between two clicks that should result in a dblclick (in ms)
const DBLCLICK_INTERVAL = 640;
event.MouseEvents = {
click: 0,
dblclick: 1,
@ -67,33 +60,6 @@ event.MouseButton = {
},
};
event.DoubleClickTracker = {
firstClick: false,
isClicked() {
return event.DoubleClickTracker.firstClick;
},
setClick() {
if (!event.DoubleClickTracker.firstClick) {
event.DoubleClickTracker.firstClick = true;
event.DoubleClickTracker.startTimer();
}
},
resetClick() {
event.DoubleClickTracker.firstClick = false;
event.DoubleClickTracker.cancelTimer();
},
startTimer() {
lazy.dblclickTimer.initWithCallback(
event.DoubleClickTracker.resetClick,
DBLCLICK_INTERVAL,
Ci.nsITimer.TYPE_ONE_SHOT
);
},
cancelTimer() {
lazy.dblclickTimer.cancel();
},
};
/**
* Synthesise a mouse event at a point.
*

Просмотреть файл

@ -10,6 +10,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs",
assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
clearTimeout: "resource://gre/modules/Timer.sys.mjs",
dom: "chrome://remote/content/shared/DOM.sys.mjs",
error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
event: "chrome://remote/content/marionette/event.sys.mjs",
@ -17,6 +18,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
Log: "chrome://remote/content/shared/Log.sys.mjs",
pprint: "chrome://remote/content/shared/Format.sys.mjs",
Sleep: "chrome://remote/content/marionette/sync.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "logger", () =>
@ -38,6 +40,9 @@ ChromeUtils.defineLazyGetter(lazy, "logger", () =>
*/
export const action = {};
// Max interval between two clicks that should result in a dblclick or a tripleclick (in ms)
export const CLICK_INTERVAL = 640;
/** Map from normalized key value to UI Events modifier key name */
const MODIFIER_NAME_LOOKUP = {
Alt: "alt",
@ -53,6 +58,7 @@ const MODIFIER_NAME_LOOKUP = {
*/
action.State = class {
constructor() {
this.clickTracker = new ClickTracker();
/**
* A map between input ID and the device state for that input
* source, with one entry for each active input source.
@ -87,7 +93,6 @@ action.State = class {
async release(win) {
this.inputsToCancel.reverse();
await this.inputsToCancel.dispatch(this, win);
lazy.event.DoubleClickTracker.resetClick();
}
/**
@ -169,6 +174,60 @@ action.State = class {
}
};
export class ClickTracker {
#count;
#lastButtonClicked;
#timer;
constructor() {
this.#count = 0;
this.#lastButtonClicked = null;
}
get count() {
return this.#count;
}
#cancelTimer() {
lazy.clearTimeout(this.#timer);
}
#startTimer() {
this.#timer = lazy.setTimeout(this.reset.bind(this), CLICK_INTERVAL);
}
/**
* Reset tracking mouse click counter.
*/
reset() {
this.#cancelTimer();
this.#count = 0;
this.#lastButtonClicked = null;
}
/**
* Track |button| click to identify possible double or triple click.
*
* @param {number} button
* A positive integer that refers to a mouse button.
*/
setClick(button) {
this.#cancelTimer();
if (
this.#lastButtonClicked === null ||
this.#lastButtonClicked === button
) {
this.#count++;
} else {
this.#count = 1;
}
this.#lastButtonClicked = button;
this.#startTimer();
}
}
/**
* Device state for an input source.
*/
@ -1790,10 +1849,10 @@ class MousePointer extends Pointer {
if (mouseEvent.ctrlKey) {
if (lazy.AppInfo.isMac) {
mouseEvent.button = 2;
lazy.event.DoubleClickTracker.resetClick();
state.clickTracker.reset();
}
} else if (lazy.event.DoubleClickTracker.isClicked()) {
mouseEvent.clickCount = 2;
} else {
mouseEvent.clickCount = state.clickTracker.count + 1;
}
lazy.event.synthesizeMouseAtPoint(
@ -1823,9 +1882,8 @@ class MousePointer extends Pointer {
});
mouseEvent.update(state, inputSource);
if (lazy.event.DoubleClickTracker.isClicked()) {
mouseEvent.clickCount = 2;
}
state.clickTracker.setClick(action.button);
mouseEvent.clickCount = state.clickTracker.count;
lazy.event.synthesizeMouseAtPoint(
inputSource.x,
@ -1891,6 +1949,10 @@ action.Chain = class extends Array {
}
})();
// Reset the current click tracker counter. We shouldn't be able to simulate
// a double click with multiple action chains.
state.clickTracker.reset();
return chainEvents;
}

Просмотреть файл

@ -4,10 +4,14 @@
"use strict";
const { action } = ChromeUtils.importESModule(
const { action, CLICK_INTERVAL, ClickTracker } = ChromeUtils.importESModule(
"chrome://remote/content/shared/webdriver/Actions.sys.mjs"
);
const { setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
const XHTMLNS = "http://www.w3.org/1999/xhtml";
const domEl = {
@ -681,6 +685,35 @@ add_task(function test_computeTickDuration_noDurations() {
equal(0, chain[0].getDuration());
});
add_task(function test_ClickTracker_setClick() {
const clickTracker = new ClickTracker();
const button1 = 1;
const button2 = 2;
clickTracker.setClick(button1);
equal(1, clickTracker.count);
// Make sure that clicking different mouse buttons doesn't increase the count.
clickTracker.setClick(button2);
equal(1, clickTracker.count);
clickTracker.setClick(button2);
equal(2, clickTracker.count);
clickTracker.reset();
equal(0, clickTracker.count);
});
add_task(function test_ClickTracker_reset_after_timeout() {
const clickTracker = new ClickTracker();
clickTracker.setClick(1);
equal(1, clickTracker.count);
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => equal(0, clickTracker.count), CLICK_INTERVAL + 10);
});
// helpers
function getTypeString(obj) {
return Object.prototype.toString.call(obj);

Просмотреть файл

@ -1511,12 +1511,6 @@
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[click.spec] Page.click should double click the button",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[click.spec] Page.click should scroll and click with disabled javascript",
"platforms": ["darwin", "linux", "win32"],
@ -1535,12 +1529,6 @@
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL", "PASS"]
},
{
"testIdPattern": "[click.spec] Page.click should select the text by triple clicking",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[cookies.spec] Cookie specs Page.cookies should get cookies from multiple urls",
"platforms": ["darwin", "linux", "win32"],