Skip to content

fix(cdk/a11y): fake mousedown detection not working #23029

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/cdk/a11y/fake-event-detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@

/** Gets whether an event could be a faked `mousedown` event dispatched by a screen reader. */
export function isFakeMousedownFromScreenReader(event: MouseEvent): boolean {
// We can typically distinguish between these faked mousedown events and real mousedown events
// using the "buttons" property. While real mousedowns will indicate the mouse button that was
// pressed (e.g. "1" for the left mouse button), faked mousedowns will usually set the property
// value to 0.
return event.buttons === 0;
// Some screen readers will dispatch a fake `mousedown` event when pressing enter or space on
// a clickable element. We can distinguish these events when both `offsetX` and `offsetY` are
// zero. Note that there's an edge case where the user could click the 0x0 spot of the screen
// themselves, but that is unlikely to contain interaction elements. Historially we used to check
// `event.buttons === 0`, however that no longer works on recent versions of NVDA.
return event.offsetX === 0 && event.offsetY === 0;
}

/** Gets whether an event could be a faked `touchstart` event dispatched by a screen reader. */
Expand Down
2 changes: 1 addition & 1 deletion src/cdk/a11y/focus-monitor/focus-monitor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe('FocusMonitor', () => {
// Simulate focus via a fake mousedown from a screen reader.
dispatchMouseEvent(buttonElement, 'mousedown');
const event = createMouseEvent('mousedown');
Object.defineProperty(event, 'buttons', {get: () => 0});
Object.defineProperties(event, {offsetX: {get: () => 0}, offsetY: {get: () => 0}});
dispatchEvent(buttonElement, event);

buttonElement.focus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ describe('InputModalityDetector', () => {

// Create a fake screen-reader mouse event.
const event = createMouseEvent('mousedown');
Object.defineProperty(event, 'buttons', {get: () => 0});
Object.defineProperties(event, {offsetX: {get: () => 0}, offsetY: {get: () => 0}});
dispatchEvent(document, event);

expect(detector.mostRecentModality).toBe('keyboard');
Expand Down
6 changes: 4 additions & 2 deletions src/cdk/testing/testbed/fake-events/event-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ export function createMouseEvent(
/* button */ button,
/* relatedTarget */ null);

// `initMouseEvent` doesn't allow us to pass the `buttons` and
// defaults it to 0 which looks like a fake event.
// `initMouseEvent` doesn't allow us to pass these properties into the constructor.
// Override them to 1, because they're used for fake screen reader event detection.
defineReadonlyEventProperty(event, 'buttons', 1);
defineReadonlyEventProperty(event, 'offsetX', 1);
defineReadonlyEventProperty(event, 'offsetY', 1);

// IE won't set `defaultPrevented` on synthetic events so we need to do it manually.
event.preventDefault = function() {
Expand Down
2 changes: 1 addition & 1 deletion src/material/core/ripple/ripple.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ describe('MatRipple', () => {

it('should ignore fake mouse events from screen readers', fakeAsync(() => {
const event = createMouseEvent('mousedown');
Object.defineProperty(event, 'buttons', {get: () => 0});
Object.defineProperties(event, {offsetX: {get: () => 0}, offsetY: {get: () => 0}});

dispatchEvent(rippleTarget, event);
tick(enterDuration);
Expand Down