Skip to content

Commit 109d5a1

Browse files
crisbetoandrewseguin
authored andcommitted
fix(cdk/a11y): not detecting fake mousedown on firefox (#23493)
A few months ago we changed the fake `mousedown` detection to use coordinates instead of the `buttons` property. This seems to have broken the detection on NVDA + Firefox on Windows where `buttons === 0`, but `offsetX` and `offsetY` are simulated to non-zero numbers. These changes re-add the `buttons` check and keep the `offsetX` and `offsetY` checks for Webkit-based browsers. (cherry picked from commit 33a07d2)
1 parent 761f9f2 commit 109d5a1

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

src/cdk/a11y/fake-event-detection.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
export function isFakeMousedownFromScreenReader(event: MouseEvent): boolean {
1111
// Some screen readers will dispatch a fake `mousedown` event when pressing enter or space on
1212
// a clickable element. We can distinguish these events when both `offsetX` and `offsetY` are
13-
// zero. Note that there's an edge case where the user could click the 0x0 spot of the screen
14-
// themselves, but that is unlikely to contain interaction elements. Historically we used to
15-
// check `event.buttons === 0`, however that no longer works on recent versions of NVDA.
16-
return event.offsetX === 0 && event.offsetY === 0;
13+
// zero or `event.buttons` is zero, depending on the browser:
14+
// - `event.buttons` works on Firefox, but fails on Chrome.
15+
// - `offsetX` and `offsetY` work on Chrome, but fail on Firefox.
16+
// Note that there's an edge case where the user could click the 0x0 spot of the
17+
// screen themselves, but that is unlikely to contain interactive elements.
18+
return event.buttons === 0 || (event.offsetX === 0 && event.offsetY === 0);
1719
}
1820

1921
/** Gets whether an event could be a faked `touchstart` event dispatched by a screen reader. */

src/cdk/a11y/focus-monitor/focus-monitor.spec.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ describe('FocusMonitor', () => {
161161
expect(changeHandler).toHaveBeenCalledWith('program');
162162
}));
163163

164-
it('should detect fake mousedown from a screen reader', fakeAsync(() => {
164+
it('should detect fake mousedown from a screen reader on Chrome', fakeAsync(() => {
165165
// Simulate focus via a fake mousedown from a screen reader.
166166
dispatchMouseEvent(buttonElement, 'mousedown');
167167
const event = createMouseEvent('mousedown');
@@ -184,6 +184,26 @@ describe('FocusMonitor', () => {
184184
expect(changeHandler).toHaveBeenCalledWith('keyboard');
185185
}));
186186

187+
it('should detect fake mousedown from a screen reader on Firefox', fakeAsync(() => {
188+
// Simulate focus via a fake mousedown from a screen reader.
189+
dispatchMouseEvent(buttonElement, 'mousedown');
190+
const event = createMouseEvent('mousedown');
191+
Object.defineProperties(event, {buttons: {get: () => 0}});
192+
dispatchEvent(buttonElement, event);
193+
194+
buttonElement.focus();
195+
fixture.detectChanges();
196+
flush();
197+
198+
expect(buttonElement.classList.length)
199+
.withContext('button should have exactly 2 focus classes').toBe(2);
200+
expect(buttonElement.classList.contains('cdk-focused'))
201+
.withContext('button should have cdk-focused class').toBe(true);
202+
expect(buttonElement.classList.contains('cdk-keyboard-focused'))
203+
.withContext('button should have cdk-keyboard-focused class').toBe(true);
204+
expect(changeHandler).toHaveBeenCalledWith('keyboard');
205+
}));
206+
187207
it('focusVia keyboard should simulate keyboard focus', fakeAsync(() => {
188208
focusMonitor.focusVia(buttonElement, 'keyboard');
189209
flush();

src/cdk/a11y/input-modality/input-modality-detector.spec.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ describe('InputModalityDetector', () => {
133133
expect(emitted).toEqual(['keyboard', 'mouse', 'touch', 'keyboard']);
134134
});
135135

136-
it('should detect fake screen reader mouse events as keyboard input modality', () => {
136+
it('should detect fake screen reader mouse events as keyboard input modality on Chrome', () => {
137137
setupTest();
138138

139139
// Create a fake screen-reader mouse event.
@@ -144,6 +144,17 @@ describe('InputModalityDetector', () => {
144144
expect(detector.mostRecentModality).toBe('keyboard');
145145
});
146146

147+
it('should detect fake screen reader mouse events as keyboard input modality on Firefox', () => {
148+
setupTest();
149+
150+
// Create a fake screen-reader mouse event.
151+
const event = createMouseEvent('mousedown');
152+
Object.defineProperties(event, {buttons: {get: () => 0}});
153+
dispatchEvent(document, event);
154+
155+
expect(detector.mostRecentModality).toBe('keyboard');
156+
});
157+
147158
it('should detect fake screen reader touch events as keyboard input modality', () => {
148159
setupTest();
149160

0 commit comments

Comments
 (0)