summaryrefslogtreecommitdiffstats
path: root/testing/mochitest/tests/SimpleTest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
commitfbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch)
tree4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /testing/mochitest/tests/SimpleTest
parentReleasing progress-linux version 124.0.1-1~progress7.99u1. (diff)
downloadfirefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz
firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/mochitest/tests/SimpleTest')
-rw-r--r--testing/mochitest/tests/SimpleTest/AccessibilityUtils.js261
-rw-r--r--testing/mochitest/tests/SimpleTest/EventUtils.js360
-rw-r--r--testing/mochitest/tests/SimpleTest/LogController.js2
-rw-r--r--testing/mochitest/tests/SimpleTest/SimpleTest.js2
-rw-r--r--testing/mochitest/tests/SimpleTest/TestRunner.js2
-rw-r--r--testing/mochitest/tests/SimpleTest/paint_listener.js2
-rw-r--r--testing/mochitest/tests/SimpleTest/setup.js4
7 files changed, 484 insertions, 149 deletions
diff --git a/testing/mochitest/tests/SimpleTest/AccessibilityUtils.js b/testing/mochitest/tests/SimpleTest/AccessibilityUtils.js
index 0e95565019..cf4daaf416 100644
--- a/testing/mochitest/tests/SimpleTest/AccessibilityUtils.js
+++ b/testing/mochitest/tests/SimpleTest/AccessibilityUtils.js
@@ -388,19 +388,23 @@ this.AccessibilityUtils = (function () {
if (accessible.role != Ci.nsIAccessibleRole.ROLE_PAGETAB) {
return false; // Not a tab.
}
- // ToDo: We may eventually need to support intervening generics between
- // a tab and its tablist here.
- const tablist = accessible.parent;
+ const tablist = findNonGenericParentAccessible(accessible);
if (!tablist || tablist.role != Ci.nsIAccessibleRole.ROLE_PAGETABLIST) {
return false; // The tab isn't inside a tablist.
}
// ToDo: We may eventually need to support tablists which use
// aria-activedescendant here.
// Check that there is only one keyboard reachable tab.
- const childCount = tablist.childCount;
let foundFocusable = false;
- for (let c = 0; c < childCount; c++) {
- const tab = tablist.getChildAt(c);
+ for (const tab of findNonGenericChildrenAccessible(tablist)) {
+ // Allow whitespaces to be included in the tablist for styling purposes
+ const isWhitespace =
+ tab.role == Ci.nsIAccessibleRole.ROLE_TEXT_LEAF &&
+ tab.DOMNode.textContent.trim().length === 0;
+ if (tab.role != Ci.nsIAccessibleRole.ROLE_PAGETAB && !isWhitespace) {
+ // The tablist includes children other than tabs or whitespaces
+ a11yFail("Only tabs should be included in a tablist", accessible);
+ }
// Use tabIndex rather than a11y focusable state because all tabs might
// have tabindex="-1".
if (tab.DOMNode.tabIndex == 0) {
@@ -461,6 +465,72 @@ this.AccessibilityUtils = (function () {
}
/**
+ * The gridcells are not expected to be interactive and focusable
+ * individually, but it is allowed to manually manage focus within the grid
+ * per ARIA Grid pattern (https://www.w3.org/WAI/ARIA/apg/patterns/grid/).
+ * Example of such grid would be a datepicker where one gridcell can be
+ * selected and the focus is moved with arrow keys once the user tabbed into
+ * the grid. In grids like a calendar, only one element would be included in
+ * the focus order and the rest of grid cells may not have an interactive
+ * accessible created. We need to special case the check for these gridcells.
+ */
+ function isAccessibleGridcell(node) {
+ if (!node || !node.ownerGlobal) {
+ return false;
+ }
+ const accessible = getAccessible(node);
+
+ if (!accessible || accessible.role != Ci.nsIAccessibleRole.ROLE_GRID_CELL) {
+ return false; // Not a grid cell.
+ }
+ // ToDo: We may eventually need to support intervening generics between
+ // a grid cell and its grid container here.
+ const gridRow = accessible.parent;
+ if (!gridRow || gridRow.role != Ci.nsIAccessibleRole.ROLE_ROW) {
+ return false; // The grid cell isn't inside a row.
+ }
+ let grid = gridRow.parent;
+ if (!grid) {
+ return false; // The grid cell isn't inside a grid.
+ }
+ if (grid.role == Ci.nsIAccessibleRole.ROLE_GROUPING) {
+ // Grid built on the HTML table may include <tbody> wrapper:
+ grid = grid.parent;
+ if (!grid || grid.role != Ci.nsIAccessibleRole.ROLE_GRID) {
+ return false; // The grid cell isn't inside a grid.
+ }
+ }
+ // Check that there is only one keyboard reachable grid cell.
+ let foundFocusable = false;
+ for (const gridCell of grid.DOMNode.querySelectorAll(
+ "td, [role=gridcell]"
+ )) {
+ // Grid cells are not expected to have a "tabindex" attribute and to be
+ // included in the focus order, with the exception of the only one cell
+ // that is included in the page tab sequence to provide access to the grid.
+ if (gridCell.tabIndex == 0) {
+ if (foundFocusable) {
+ // Only one grid cell within a grid should be focusable.
+ // ToDo: Fine-tune the a11y-check error message generated in this case.
+ // Strictly speaking, it's not ideal that we're performing an action
+ // from an is function, which normally only queries something without
+ // any externally observable behaviour. That said, fixing that would
+ // involve different return values for different cases (not a grid
+ // cell, too many focusable grid cells, etc) so we could move the
+ // a11yFail call to the caller.
+ a11yFail(
+ "Only one grid cell should be focusable in a grid",
+ accessible
+ );
+ return false;
+ }
+ foundFocusable = true;
+ }
+ }
+ return foundFocusable;
+ }
+
+ /**
* XUL treecol elements currently aren't focusable, making them inaccessible.
* For now, we don't flag these as a failure to avoid breaking multiple tests.
* ToDo: We should remove this exception after this is fixed in bug 1848397.
@@ -477,7 +547,7 @@ this.AccessibilityUtils = (function () {
}
/**
- * Determine if an accessible is a combobox container of the url bar. We
+ * Determine if a DOM node is a combobox container of the url bar. We
* intentionally leave this element unlabeled, because its child is a search
* input that is the target and main control of this component. In general, we
* want to avoid duplication in the label announcement when a user focuses the
@@ -486,68 +556,125 @@ this.AccessibilityUtils = (function () {
* difficult to keep the accessible name synchronized between the combobox and
* the input. Thus, we need to special case the label check for this control.
*/
- function isUnlabeledUrlBarCombobox(accessible) {
- const node = accessible.DOMNode;
+ function isUnlabeledUrlBarCombobox(node) {
if (!node || !node.ownerGlobal) {
return false;
}
- const ariaRoles = getAriaRoles(accessible);
+ let ariaRole = node.getAttribute("role");
// There are only two cases of this pattern: <moz-input-box> and <searchbar>
const isMozInputBox =
node.tagName == "moz-input-box" &&
node.classList.contains("urlbar-input-box");
const isSearchbar = node.tagName == "searchbar" && node.id == "searchbar";
- return (isMozInputBox || isSearchbar) && ariaRoles.includes("combobox");
+ return (isMozInputBox || isSearchbar) && ariaRole == "combobox";
}
/**
- * Determine if an accessible is an option within the url bar. We know each
+ * Determine if a DOM node is an option within the url bar. We know each
* url bar option is accessible, but it disappears as soon as it is clicked
* during tests and the a11y-checks do not have time to test the label,
* because the Fluent localization is not yet completed by then. Thus, we
* need to special case the label check for these controls.
*/
- function isUnlabeledUrlBarOption(accessible) {
- const node = accessible.DOMNode;
+ function isUnlabeledUrlBarOption(node) {
if (!node || !node.ownerGlobal) {
return false;
}
- const ariaRoles = getAriaRoles(accessible);
- return (
+ const role = getAccessible(node)?.role;
+ const isOption =
node.tagName == "span" &&
- ariaRoles.includes("option") &&
- node.classList.contains("urlbarView-row-inner") &&
- node.hasAttribute("data-l10n-id")
- );
+ node.getAttribute("role") == "option" &&
+ node.classList.contains("urlbarView-row-inner");
+ const isMenuItem =
+ node.tagName == "menuitem" &&
+ role == Ci.nsIAccessibleRole.ROLE_MENUITEM &&
+ node.classList.contains("urlbarView-result-menuitem");
+ // Not all options have "data-l10n-id" attributes in the URL Bar, because
+ // some of options are autocomplete options based on the user input and
+ // they are not expected to be localized.
+ return isOption || isMenuItem;
}
/**
- * Determine if an accessible is a menuitem within the XUL menu. We know each
+ * Determine if a DOM node is a menuitem within the XUL menu. We know each
* menuitem is accessible, but it disappears as soon as it is clicked during
* tests and the a11y-checks do not have time to test the label, because the
* Fluent localization is not yet completed by then. Thus, we need to special
* case the label check for these controls.
*/
- function isUnlabeledMenuitem(accessible) {
- const node = accessible.DOMNode;
+ function isUnlabeledMenuitem(node) {
if (!node || !node.ownerGlobal) {
return false;
}
- let hasLabel = false;
- for (const child of node.childNodes) {
- if (child.tagName == "label") {
- hasLabel = true;
- }
- }
+ const hasLabel = node.querySelector("label, description");
+ const isMenuItem =
+ node.getAttribute("role") == "menuitem" ||
+ (node.tagName == "richlistitem" &&
+ node.classList.contains("autocomplete-richlistitem")) ||
+ (node.tagName == "menuitem" &&
+ node.classList.contains("urlbarView-result-menuitem"));
+
+ const isParentMenu =
+ node.parentNode.getAttribute("role") == "menu" ||
+ (node.parentNode.tagName == "richlistbox" &&
+ node.parentNode.classList.contains("autocomplete-richlistbox")) ||
+ (node.parentNode.tagName == "menupopup" &&
+ node.parentNode.classList.contains("urlbarView-result-menu"));
return (
- accessible.role == Ci.nsIAccessibleRole.ROLE_MENUITEM &&
- accessible.parent.role == Ci.nsIAccessibleRole.ROLE_MENUPOPUP &&
+ isMenuItem &&
+ isParentMenu &&
hasLabel &&
- node.hasAttribute("data-l10n-id")
+ (node.hasAttribute("data-l10n-id") || node.tagName == "richlistitem")
+ );
+ }
+
+ /**
+ * Determine if the node is a "Show All" or one of image buttons on the
+ * about:config page, or a "X" close button on moz-message-bar. We know these
+ * buttons are accessible, but they disappear/are replaced as soon as they
+ * are clicked during tests and the a11y-checks do not have time to test the
+ * label, because the Fluent localization is not yet completed by then.
+ * Thus, we need to special case the label check for these controls.
+ */
+ function isUnlabeledImageButton(node) {
+ if (!node || !node.ownerGlobal) {
+ return false;
+ }
+ const isShowAllButton = node.id == "show-all";
+ const isReplacedImageButton =
+ node.classList.contains("button-add") ||
+ node.classList.contains("button-delete") ||
+ node.classList.contains("button-reset");
+ const isCloseMozMessageBarButton =
+ node.classList.contains("close") &&
+ node.getAttribute("data-l10n-id") == "moz-message-bar-close-button";
+ return (
+ node.tagName.toLowerCase() == "button" &&
+ node.hasAttribute("data-l10n-id") &&
+ (isShowAllButton || isReplacedImageButton || isCloseMozMessageBarButton)
);
}
/**
+ * Determine if a node is a XUL:button on a prompt popup. We know this button
+ * is accessible, but it disappears as soon as it is clicked during tests and
+ * the a11y-checks do not have time to test the label, because the Fluent
+ * localization is not yet completed by then. Thus, we need to special case
+ * the label check for these controls.
+ */
+ function isUnlabeledXulButton(node) {
+ if (!node || !node.ownerGlobal) {
+ return false;
+ }
+ const hasLabel = node.querySelector("label, xul\\:label");
+ const isButton =
+ node.getAttribute("role") == "button" ||
+ node.tagName == "button" ||
+ node.tagName == "xul:button";
+ return isButton && hasLabel && node.hasAttribute("data-l10n-id");
+ }
+
+ /**
* Determine if a node is a XUL element for which tabIndex should be ignored.
* Some XUL elements report -1 for the .tabIndex property, even though they
* are in fact keyboard focusable.
@@ -758,13 +885,6 @@ this.AccessibilityUtils = (function () {
const { DOMNode } = accessible;
let name = accessible.name;
if (!name) {
- if (
- isUnlabeledUrlBarCombobox(accessible) ||
- isUnlabeledUrlBarOption(accessible) ||
- isUnlabeledMenuitem(accessible)
- ) {
- return;
- }
// If text has just been inserted into the tree, the a11y engine might not
// have picked it up yet.
forceRefreshDriverTick(DOMNode);
@@ -773,6 +893,21 @@ this.AccessibilityUtils = (function () {
} catch (e) {
// The Accessible died because the DOM node was removed or hidden.
if (gEnv.labelRule) {
+ // Some elements disappear as soon as they are clicked during tests,
+ // their accessible dies before the Fluent localization is completed.
+ // We want to exclude these groups of nodes from the label check.
+ // Note: In other cases, this first block isn't necessarily hit
+ // because Fluent isn't finished yet. This might happen if a text
+ // node was inserted (whether by Fluent or something else) but a11y
+ // hasn't picked it up yet, but the node gets hidden before a11y
+ // can pick it up.
+ if (
+ isUnlabeledUrlBarOption(DOMNode) ||
+ isUnlabeledMenuitem(DOMNode) ||
+ isUnlabeledImageButton(DOMNode)
+ ) {
+ return;
+ }
a11yWarn("Unlabeled element removed before l10n finished", {
DOMNode,
});
@@ -795,6 +930,13 @@ this.AccessibilityUtils = (function () {
accessible.name;
} catch (e) {
// The Accessible died because the DOM node was removed or hidden.
+ if (
+ isUnlabeledUrlBarOption(DOMNode) ||
+ isUnlabeledImageButton(DOMNode) ||
+ isUnlabeledXulButton(DOMNode)
+ ) {
+ return;
+ }
a11yWarn("Unlabeled element removed before l10n finished", {
DOMNode,
});
@@ -811,6 +953,15 @@ this.AccessibilityUtils = (function () {
name = name.trim();
}
if (gEnv.labelRule && !name) {
+ // The URL and Search Bar comboboxes are purposefully unlabeled,
+ // since they include labeled inputs that are receiving focus.
+ // Or the Accessible died because the DOM node was removed or hidden.
+ if (
+ isUnlabeledUrlBarCombobox(DOMNode) ||
+ isUnlabeledUrlBarOption(DOMNode)
+ ) {
+ return;
+ }
a11yFail("Interactive elements must be labeled", accessible);
return;
@@ -935,6 +1086,36 @@ this.AccessibilityUtils = (function () {
return null;
}
+ /**
+ * Find the nearest non-generic ancestor for a node to account for generic
+ * containers to intervene between the ancestor and it child.
+ */
+ function findNonGenericParentAccessible(childAcc) {
+ for (let acc = childAcc.parent; acc; acc = acc.parent) {
+ if (acc.computedARIARole != "generic") {
+ return acc;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find the nearest non-generic children for a node to account for generic
+ * containers to intervene between the ancestor and its children.
+ */
+ function* findNonGenericChildrenAccessible(parentAcc) {
+ const count = parentAcc.childCount;
+ for (let c = 0; c < count; ++c) {
+ const child = parentAcc.getChildAt(c);
+ // When Gecko will consider only one role as generic, we'd use child.role
+ if (child.computedARIARole == "generic") {
+ yield* findNonGenericChildrenAccessible(child);
+ } else {
+ yield child;
+ }
+ }
+ }
+
function runIfA11YChecks(task) {
return (...args) => (gA11YChecks ? task(...args) : null);
}
@@ -956,7 +1137,7 @@ this.AccessibilityUtils = (function () {
// node might be the image.
const acc = findInteractiveAccessible(node);
if (!acc) {
- if (isInaccessibleXulTreecol(node)) {
+ if (isAccessibleGridcell(node) || isInaccessibleXulTreecol(node)) {
return;
}
if (gEnv.mustHaveAccessibleRule) {
diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js
index 739c7052ea..8833b8bc59 100644
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-nested-ternary */
/**
* EventUtils provides some utility methods for creating and sending DOM events.
*
@@ -569,14 +570,86 @@ function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) {
aWindow
);
}
-function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) {
- var rect = aTarget.getBoundingClientRect();
- return synthesizeTouchAtPoint(
- rect.left + aOffsetX,
- rect.top + aOffsetY,
- aEvent,
- aWindow
- );
+
+/**
+ * Synthesize one or more touches on aTarget. aTarget can be either Element
+ * or Array of Elements. aOffsetX, aOffsetY, aEvent.id, aEvent.rx, aEvent.ry,
+ * aEvent.angle, aEvent.force, aEvent.tiltX, aEvent.tiltY and aEvent.twist can
+ * be either Number or Array of Numbers (can be mixed). If you specify array
+ * to synthesize a multi-touch, you need to specify same length arrays. If
+ * you don't specify array to them, same values (or computed default values for
+ * aEvent.id) are used for all touches.
+ *
+ * @param {Element | Element[]} aTarget The target element which you specify
+ * relative offset from its top-left.
+ * @param {Number | Number[]} aOffsetX The relative offset from left of aTarget.
+ * @param {Number | Number[]} aOffsetY The relative offset from top of aTarget.
+ * @param {Object} aEvent
+ * type: The touch event type. If undefined, "touchstart" and "touchend" will
+ * be synthesized at same point.
+ *
+ * id: The touch id. If you don't specify this, default touch id will be used
+ * for first touch and further touch ids are the values incremented from the
+ * first id.
+ *
+ * rx, ry: The radii of the touch.
+ *
+ * angle: The angle in degree.
+ *
+ * force: The force of the touch. If the type is "touchend", this should be 0.
+ * If unspecified, this is default to 0 for "touchend" or 1 for the others.
+ *
+ * tiltX, tiltY: The tilt of the touch.
+ *
+ * twist: The twist of the touch.
+ * @param {Window} aWindow Default to `window`.
+ * @returns true if and only if aEvent.type is specified and default of the
+ * event is prevented.
+ */
+function synthesizeTouch(
+ aTarget,
+ aOffsetX,
+ aOffsetY,
+ aEvent = {},
+ aWindow = window
+) {
+ let rectX, rectY;
+ if (Array.isArray(aTarget)) {
+ let lastTarget, lastTargetRect;
+ aTarget.forEach(target => {
+ const rect =
+ target == lastTarget ? lastTargetRect : target.getBoundingClientRect();
+ rectX.push(rect.left);
+ rectY.push(rect.top);
+ lastTarget = target;
+ lastTargetRect = rect;
+ });
+ } else {
+ const rect = aTarget.getBoundingClientRect();
+ rectX = [rect.left];
+ rectY = [rect.top];
+ }
+ const offsetX = (() => {
+ if (Array.isArray(aOffsetX)) {
+ let ret = [];
+ aOffsetX.forEach((value, index) => {
+ ret.push(value + rectX[Math.min(index, rectX.length - 1)]);
+ });
+ return ret;
+ }
+ return aOffsetX + rectX[0];
+ })();
+ const offsetY = (() => {
+ if (Array.isArray(aOffsetY)) {
+ let ret = [];
+ aOffsetY.forEach((value, index) => {
+ ret.push(value + rectY[Math.min(index, rectY.length - 1)]);
+ });
+ return ret;
+ }
+ return aOffsetY + rectY[0];
+ })();
+ return synthesizeTouchAtPoint(offsetX, offsetY, aEvent, aWindow);
}
/**
@@ -776,68 +849,160 @@ function synthesizeMouseAtPoint(left, top, aEvent, aWindow = window) {
return defaultPrevented;
}
-function synthesizeTouchAtPoint(left, top, aEvent, aWindow = window) {
- var utils = _getDOMWindowUtils(aWindow);
- let defaultPrevented = false;
+/**
+ * Synthesize one or more touches at the points. aLeft, aTop, aEvent.id,
+ * aEvent.rx, aEvent.ry, aEvent.angle, aEvent.force, aEvent.tiltX, aEvent.tiltY
+ * and aEvent.twist can be either Number or Array of Numbers (can be mixed).
+ * If you specify array to synthesize a multi-touch, you need to specify same
+ * length arrays. If you don't specify array to them, same values are used for
+ * all touches.
+ *
+ * @param {Element | Element[]} aTarget The target element which you specify
+ * relative offset from its top-left.
+ * @param {Number | Number[]} aOffsetX The relative offset from left of aTarget.
+ * @param {Number | Number[]} aOffsetY The relative offset from top of aTarget.
+ * @param {Object} aEvent
+ * type: The touch event type. If undefined, "touchstart" and "touchend" will
+ * be synthesized at same point.
+ *
+ * id: The touch id. If you don't specify this, default touch id will be used
+ * for first touch and further touch ids are the values incremented from the
+ * first id.
+ *
+ * rx, ry: The radii of the touch.
+ *
+ * angle: The angle in degree.
+ *
+ * force: The force of the touch. If the type is "touchend", this should be 0.
+ * If unspecified, this is default to 0 for "touchend" or 1 for the others.
+ *
+ * tiltX, tiltY: The tilt of the touch.
+ *
+ * twist: The twist of the touch.
+ * @param {Window} aWindow Default to `window`.
+ * @returns true if and only if aEvent.type is specified and default of the
+ * event is prevented.
+ */
+function synthesizeTouchAtPoint(aLeft, aTop, aEvent = {}, aWindow = window) {
+ let utils = _getDOMWindowUtils(aWindow);
+ if (!utils) {
+ return false;
+ }
- if (utils) {
- var id = aEvent.id || utils.DEFAULT_TOUCH_POINTER_ID;
- var rx = aEvent.rx || 1;
- var ry = aEvent.ry || 1;
- var angle = aEvent.angle || 0;
- var force = aEvent.force || (aEvent.type === "touchend" ? 0 : 1);
- var tiltX = aEvent.tiltX || 0;
- var tiltY = aEvent.tiltY || 0;
- var twist = aEvent.twist || 0;
- var modifiers = _parseModifiers(aEvent, aWindow);
+ if (
+ Array.isArray(aLeft) &&
+ Array.isArray(aTop) &&
+ aLeft.length != aTop.length
+ ) {
+ throw new Error(`aLeft and aTop should be same length array`);
+ }
- if ("type" in aEvent && aEvent.type) {
- defaultPrevented = utils.sendTouchEvent(
- aEvent.type,
- [id],
- [left],
- [top],
- [rx],
- [ry],
- [angle],
- [force],
- [tiltX],
- [tiltY],
- [twist],
- modifiers
- );
- } else {
- utils.sendTouchEvent(
- "touchstart",
- [id],
- [left],
- [top],
- [rx],
- [ry],
- [angle],
- [force],
- [tiltX],
- [tiltY],
- [twist],
- modifiers
- );
- utils.sendTouchEvent(
- "touchend",
- [id],
- [left],
- [top],
- [rx],
- [ry],
- [angle],
- [force],
- [tiltX],
- [tiltY],
- [twist],
- modifiers
+ const arrayLength = Array.isArray(aLeft)
+ ? aLeft.length
+ : Array.isArray(aTop)
+ ? aTop.length
+ : 1;
+
+ function throwExceptionIfDifferentLengthArray(aArray, aName) {
+ if (Array.isArray(aArray) && arrayLength !== aArray.length) {
+ throw new Error(`${aName} is different length array`);
+ }
+ }
+ const leftArray = (() => {
+ if (Array.isArray(aLeft)) {
+ return aLeft;
+ }
+ return new Array(arrayLength).fill(aLeft);
+ })();
+ const topArray = (() => {
+ if (Array.isArray(aTop)) {
+ throwExceptionIfDifferentLengthArray(aTop, "aTop");
+ return aTop;
+ }
+ return new Array(arrayLength).fill(aTop);
+ })();
+ const idArray = (() => {
+ if ("id" in aEvent && Array.isArray(aEvent.id)) {
+ throwExceptionIfDifferentLengthArray(aEvent.id, "aEvent.id");
+ return aEvent.id;
+ }
+ let id = aEvent.id || utils.DEFAULT_TOUCH_POINTER_ID;
+ let ret = [];
+ for (let i = 0; i < arrayLength; i++) {
+ ret.push(id++);
+ }
+ return ret;
+ })();
+ function getSameLengthArrayOfEventProperty(aProperty, aDefaultValue) {
+ if (aProperty in aEvent && Array.isArray(aEvent[aProperty])) {
+ throwExceptionIfDifferentLengthArray(
+ aEvent.rx,
+ arrayLength,
+ `aEvent.${aProperty}`
);
+ return aEvent[aProperty];
}
+ return new Array(arrayLength).fill(aEvent[aProperty] || aDefaultValue);
}
- return defaultPrevented;
+ const rxArray = getSameLengthArrayOfEventProperty("rx", 1);
+ const ryArray = getSameLengthArrayOfEventProperty("ry", 1);
+ const angleArray = getSameLengthArrayOfEventProperty("angle", 0);
+ const forceArray = getSameLengthArrayOfEventProperty(
+ "force",
+ aEvent.type === "touchend" ? 0 : 1
+ );
+ const tiltXArray = getSameLengthArrayOfEventProperty("tiltX", 0);
+ const tiltYArray = getSameLengthArrayOfEventProperty("tiltY", 0);
+ const twistArray = getSameLengthArrayOfEventProperty("twist", 0);
+
+ const modifiers = _parseModifiers(aEvent, aWindow);
+
+ if ("type" in aEvent && aEvent.type) {
+ return utils.sendTouchEvent(
+ aEvent.type,
+ idArray,
+ leftArray,
+ topArray,
+ rxArray,
+ ryArray,
+ angleArray,
+ forceArray,
+ tiltXArray,
+ tiltYArray,
+ twistArray,
+ modifiers
+ );
+ }
+
+ utils.sendTouchEvent(
+ "touchstart",
+ idArray,
+ leftArray,
+ topArray,
+ rxArray,
+ ryArray,
+ angleArray,
+ forceArray,
+ tiltXArray,
+ tiltYArray,
+ twistArray,
+ modifiers
+ );
+ utils.sendTouchEvent(
+ "touchend",
+ idArray,
+ leftArray,
+ topArray,
+ rxArray,
+ ryArray,
+ angleArray,
+ forceArray,
+ tiltXArray,
+ tiltYArray,
+ twistArray,
+ modifiers
+ );
+ return false;
}
// Call synthesizeMouse with coordinates at the center of aTarget.
@@ -851,7 +1016,7 @@ function synthesizeMouseAtCenter(aTarget, aEvent, aWindow) {
aWindow
);
}
-function synthesizeTouchAtCenter(aTarget, aEvent, aWindow) {
+function synthesizeTouchAtCenter(aTarget, aEvent = {}, aWindow = window) {
var rect = aTarget.getBoundingClientRect();
synthesizeTouchAtPoint(
rect.left + rect.width / 2,
@@ -1020,7 +1185,9 @@ function _sendWheelAndPaint(
}
var onwheel = function () {
- SpecialPowers.removeSystemEventListener(window, "wheel", onwheel);
+ SpecialPowers.wrap(window).removeEventListener("wheel", onwheel, {
+ mozSystemGroup: true,
+ });
// Wait one frame since the wheel event has not caused a refresh observer
// to be added yet.
@@ -1055,7 +1222,9 @@ function _sendWheelAndPaint(
// Listen for the system wheel event, because it happens after all of
// the other wheel events, including legacy events.
- SpecialPowers.addSystemEventListener(aWindow, "wheel", onwheel);
+ SpecialPowers.wrap(aWindow).addEventListener("wheel", onwheel, {
+ mozSystemGroup: true,
+ });
if (aFlushMode === _FlushModes.FLUSH) {
synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
} else {
@@ -1405,13 +1574,10 @@ function synthesizeAndWaitNativeMouseMove(
);
let eventRegisteredPromise = new Promise(resolve => {
- mm.addMessageListener(
- "Test:MouseMoveRegistered",
- function processed(message) {
- mm.removeMessageListener("Test:MouseMoveRegistered", processed);
- resolve();
- }
- );
+ mm.addMessageListener("Test:MouseMoveRegistered", function processed() {
+ mm.removeMessageListener("Test:MouseMoveRegistered", processed);
+ resolve();
+ });
});
let eventReceivedPromise = ContentTask.spawn(
browser,
@@ -1579,7 +1745,7 @@ function synthesizeAndWaitKey(
);
let keyRegisteredPromise = new Promise(resolve => {
- mm.addMessageListener("Test:KeyRegistered", function processed(message) {
+ mm.addMessageListener("Test:KeyRegistered", function processed() {
mm.removeMessageListener("Test:KeyRegistered", processed);
resolve();
});
@@ -3853,21 +4019,19 @@ async function synthesizePlainDragAndCancel(
aExpectedDataTransferItems
);
}
- SpecialPowers.addSystemEventListener(
- srcElement.ownerDocument,
+ SpecialPowers.wrap(srcElement.ownerDocument).addEventListener(
"dragstart",
onDragStart,
- { capture: true }
+ { capture: true, mozSystemGroup: true }
);
try {
aParams.expectCancelDragStart = true;
await synthesizePlainDragAndDrop(aParams);
} finally {
- SpecialPowers.removeSystemEventListener(
- srcElement.ownerDocument,
+ SpecialPowers.wrap(srcElement.ownerDocument).removeEventListener(
"dragstart",
onDragStart,
- { capture: true }
+ { capture: true, mozSystemGroup: true }
);
}
return result;
@@ -3884,33 +4048,23 @@ class EventCounter {
// SpecialPowers is picky and needs to be passed an explicit reference to
// the function to be called. To avoid having to bind "this", we therefore
// define the method this way, via a property.
- this.handleEvent = aEvent => {
+ this.handleEvent = () => {
this.eventCount++;
};
- if (aOptions.mozSystemGroup) {
- SpecialPowers.addSystemEventListener(
- aTarget,
- aType,
- this.handleEvent,
- aOptions.capture
- );
- } else {
- aTarget.addEventListener(aType, this, aOptions);
- }
+ SpecialPowers.wrap(aTarget).addEventListener(
+ aType,
+ this.handleEvent,
+ aOptions
+ );
}
unregister() {
- if (this.options.mozSystemGroup) {
- SpecialPowers.removeSystemEventListener(
- this.target,
- this.type,
- this.handleEvent,
- this.options.capture
- );
- } else {
- this.target.removeEventListener(this.type, this, this.options);
- }
+ SpecialPowers.wrap(this.target).removeEventListener(
+ this.type,
+ this.handleEvent,
+ this.options
+ );
}
get count() {
diff --git a/testing/mochitest/tests/SimpleTest/LogController.js b/testing/mochitest/tests/SimpleTest/LogController.js
index 29580022f8..d0888d6032 100644
--- a/testing/mochitest/tests/SimpleTest/LogController.js
+++ b/testing/mochitest/tests/SimpleTest/LogController.js
@@ -48,7 +48,7 @@ LogController.extend = function (args, skip) {
};
/* logs message with given level. Currently used locally by log() and error() */
-LogController.logWithLevel = function (level, message /*, ...*/) {
+LogController.logWithLevel = function (level /*, ...*/) {
var msg = LogController.createLogMessage(
level,
LogController.extend(arguments, 1)
diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js
index a237c25103..16f5c3ae8a 100644
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -384,7 +384,7 @@ function usesFailurePatterns() {
* @return {boolean} Whether a matched failure pattern is found.
*/
function recordIfMatchesFailurePattern(name, diag) {
- let index = SimpleTest.expected.findIndex(([pat, count]) => {
+ let index = SimpleTest.expected.findIndex(([pat]) => {
return (
pat == null ||
(typeof name == "string" && name.includes(pat)) ||
diff --git a/testing/mochitest/tests/SimpleTest/TestRunner.js b/testing/mochitest/tests/SimpleTest/TestRunner.js
index 5f305a176b..2e48969d8d 100644
--- a/testing/mochitest/tests/SimpleTest/TestRunner.js
+++ b/testing/mochitest/tests/SimpleTest/TestRunner.js
@@ -63,7 +63,7 @@ function extend(obj, /* optional */ skip) {
return ret;
}
-function flattenArguments(lst /* ...*/) {
+function flattenArguments(/* ...*/) {
var res = [];
var args = extend(arguments);
while (args.length) {
diff --git a/testing/mochitest/tests/SimpleTest/paint_listener.js b/testing/mochitest/tests/SimpleTest/paint_listener.js
index 2fc6ab425a..89740454f2 100644
--- a/testing/mochitest/tests/SimpleTest/paint_listener.js
+++ b/testing/mochitest/tests/SimpleTest/paint_listener.js
@@ -100,7 +100,7 @@
window.promiseAllPaintsDone = function (subdoc = null, flush = false) {
var flushmode = flush ? FlushModes.FLUSH : FlushModes.NOFLUSH;
- return new Promise(function (resolve, reject) {
+ return new Promise(function (resolve) {
// The callback is given the components of the rect, but resolve() can
// only be given one arg, so we turn it back into an array.
waitForPaints((l, r, t, b) => resolve([l, r, t, b]), subdoc, flushmode);
diff --git a/testing/mochitest/tests/SimpleTest/setup.js b/testing/mochitest/tests/SimpleTest/setup.js
index 05ba8066a1..2a8509bc0a 100644
--- a/testing/mochitest/tests/SimpleTest/setup.js
+++ b/testing/mochitest/tests/SimpleTest/setup.js
@@ -247,7 +247,7 @@ TestRunner.logger.addListener(
var gTestList = [];
var RunSet = {};
-RunSet.runall = function (e) {
+RunSet.runall = function () {
// Filter tests to include|exclude tests based on data in params.filter.
// This allows for including or excluding tests from the gTestList
// TODO Only used by ipc tests, remove once those are implemented sanely
@@ -265,7 +265,7 @@ RunSet.runall = function (e) {
}
};
-RunSet.runtests = function (e) {
+RunSet.runtests = function () {
// Which tests we're going to run
var my_tests = gTestList;