0

[Keyboard A11y] Add shortcuts for focus on top Chrome

Bug: 360423850, 399498687
Change-Id: Ia7bae30507b10f1fff178de40cb641287904a2a8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6408452
Commit-Queue: Jenna Himawan <jhimawan@google.com>
Reviewed-by: Theresa Sullivan <twellington@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1439672}
This commit is contained in:
Jenna Himawan
2025-03-28 13:35:43 -07:00
committed by Chromium LUCI CQ
parent 7675c99656
commit dc1efa1af1
2 changed files with 89 additions and 72 deletions
chrome/android
java
src
org
chromium
javatests
src
org
chromium

@@ -698,14 +698,14 @@ public class KeyboardShortcuts {
/**
* This should be called from the Activity's onKeyDown() to handle keyboard shortcuts.
*
* Note: onKeyDown() is called after the active view or web page has had a chance to handle
* <p>Note: onKeyDown() is called after the active view or web page has had a chance to handle
* the key event. So the keys handled here *can* be overridden by any view or web page.
*
* @param event The KeyEvent to handle.
* @param isCurrentTabVisible Whether page-related actions are valid, e.g. reload, zoom in. This
* should be false when in the tab switcher.
* should be false when in the tab switcher.
* @param tabSwitchingEnabled Whether shortcuts that switch between tabs are enabled (e.g.
* Ctrl+Tab, Ctrl+3).
* Ctrl+Tab, Ctrl+3).
* @param tabModelSelector The current tab modelSelector.
* @param menuOrKeyboardActionController Controls keyboard actions.
* @param toolbarManager Manages the toolbar.
@@ -726,6 +726,7 @@ public class KeyboardShortcuts {
&& !event.isAltPressed()
&& keyCode != KeyEvent.KEYCODE_F3
&& keyCode != KeyEvent.KEYCODE_F5
&& keyCode != KeyEvent.KEYCODE_F6
&& keyCode != KeyEvent.KEYCODE_F10
&& keyCode != KeyEvent.KEYCODE_FORWARD
&& keyCode != KeyEvent.KEYCODE_REFRESH) {
@@ -894,6 +895,29 @@ public class KeyboardShortcuts {
case KeyboardShortcutsSemanticMeaning.OPEN_HELP:
menuOrKeyboardActionController.onMenuOrKeyboardAction(R.id.help_id, false);
return true;
case KeyboardShortcutsSemanticMeaning
.NOT_IMPLEMENTED_KEYBOARD_FOCUS_SWITCH_ROW_OF_TOP_ELEMENTS:
if (ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)) {
// TODO(crbug.com/360423850): Don't allow F6 to be overridden by websites.
return menuOrKeyboardActionController.onMenuOrKeyboardAction(
R.id.switch_keyboard_focus_row, /* fromMenu= */ false);
} else {
return false;
}
case KeyboardShortcutsSemanticMeaning.NOT_IMPLEMENTED_KEYBOARD_FOCUS_TOOLBAR:
if (ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)) {
toolbarManager.requestFocus();
return true;
} else {
return false;
}
case KeyboardShortcutsSemanticMeaning.NOT_IMPLEMENTED_KEYBOARD_FOCUS_BOOKMARKS:
if (ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)) {
return menuOrKeyboardActionController.onMenuOrKeyboardAction(
R.id.focus_bookmarks, /* fromMenu= */ false);
} else {
return false;
}
}
}

@@ -31,7 +31,9 @@ import org.mockito.junit.MockitoRule;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -109,30 +111,6 @@ public class KeyboardShortcutsTest {
/* expectHandled= */ false, /* isCurrentTabVisible= */ true, /* metaState= */ 0);
}
private void testOpenBookmarks(
boolean expectHandled, boolean isCurrentTabVisible, int metaState) {
assertEquals(
expectHandled,
KeyboardShortcuts.onKeyDown(
new KeyEvent(
/* downTime= */ SystemClock.uptimeMillis(),
/* eventTime= */ SystemClock.uptimeMillis(),
KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_O,
/* repeat= */ 0,
metaState),
isCurrentTabVisible,
/* tabSwitchingEnabled= */ true,
mTabModelSelector,
mMenuOrKeyboardActionController,
mToolbarManager));
verify(mMenuOrKeyboardActionController, expectHandled ? times(1) : never())
.onMenuOrKeyboardAction(
/* id= */ eq(R.id.all_bookmarks_menu_id),
/* fromMenu= */ expectHandled ? eq(false) : anyBoolean());
}
@Test
@SmallTest
public void testToggleBookmarkBar() {
@@ -245,21 +223,7 @@ public class KeyboardShortcutsTest {
"expected handling of key event with keycode %s and metaState %s to"
+ " be %s",
keyCode, metaState, expectHandled);
assertTrue(
message,
KeyboardShortcuts.onKeyDown(
new KeyEvent(
/* downTime= */ SystemClock.uptimeMillis(),
/* eventTime= */ SystemClock.uptimeMillis(),
KeyEvent.ACTION_DOWN,
keyCode,
/* repeat= */ 0,
metaState),
/* isCurrentTabVisible= */ true,
/* tabSwitchingEnabled= */ true,
mTabModelSelector,
mMenuOrKeyboardActionController,
mToolbarManager));
assertTrue(message, keyDown(keyCode, metaState, true));
verify(mTabModel, times(1))
.setIndex(SMALL_NUMBER_OF_TABS - 1, TabSelectionType.FROM_USER);
clearInvocations(mTabModel);
@@ -278,21 +242,7 @@ public class KeyboardShortcutsTest {
String.format(
"expected key event with keycode %s and metaState %s to be handled",
keyCode, metaState);
assertTrue(
message,
KeyboardShortcuts.onKeyDown(
new KeyEvent(
/* downTime= */ SystemClock.uptimeMillis(),
/* eventTime= */ SystemClock.uptimeMillis(),
KeyEvent.ACTION_DOWN,
keyCode,
/* repeat= */ 0,
metaState),
/* isCurrentTabVisible= */ true,
/* tabSwitchingEnabled= */ true,
mTabModelSelector,
mMenuOrKeyboardActionController,
mToolbarManager));
assertTrue(message, keyDown(keyCode, metaState, true));
verify(mTabModel, times(1))
.setIndex(LARGE_NUMBER_OF_TABS - 1, TabSelectionType.FROM_USER);
clearInvocations(mTabModel);
@@ -300,6 +250,47 @@ public class KeyboardShortcutsTest {
}
}
// Tests for focus placement on top Clank.
@Test
@SmallTest
@EnableFeatures(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)
public void testGoToToolbar() {
assertTrue(
keyDown(KeyEvent.KEYCODE_T, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON, true));
verify(mToolbarManager, times(1)).requestFocus();
}
@Test
@SmallTest
@EnableFeatures(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)
public void testGoToBookmarksBar() {
keyDown(KeyEvent.KEYCODE_B, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON, true);
verify(mMenuOrKeyboardActionController, times(1))
.onMenuOrKeyboardAction(
/* id= */ eq(R.id.focus_bookmarks), /* fromMenu= */ eq(false));
}
@Test
@SmallTest
@EnableFeatures(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)
public void testFocusSwitch() {
keyDown(KeyEvent.KEYCODE_F6, 0, true);
verify(mMenuOrKeyboardActionController, times(1))
.onMenuOrKeyboardAction(
/* id= */ eq(R.id.switch_keyboard_focus_row), /* fromMenu= */ eq(false));
}
private void testOpenBookmarks(
boolean expectHandled, boolean isCurrentTabVisible, int metaState) {
assertEquals(expectHandled, keyDown(KeyEvent.KEYCODE_O, metaState, isCurrentTabVisible));
verify(mMenuOrKeyboardActionController, expectHandled ? times(1) : never())
.onMenuOrKeyboardAction(
/* id= */ eq(R.id.all_bookmarks_menu_id),
/* fromMenu= */ expectHandled ? eq(false) : anyBoolean());
}
private void assertGoToTab(boolean expectHandled, int keyCode, int metaState) {
String message =
String.format(
@@ -308,21 +299,7 @@ public class KeyboardShortcutsTest {
// Note: we always expect (CTRL or ALT) + [1-9] to be a "go to tab" shortcut; we expect
// onKeyDown to always be true. However, setting the index of the tab model won't happen if
// the number is out of range.
assertTrue(
message,
KeyboardShortcuts.onKeyDown(
new KeyEvent(
/* downTime= */ SystemClock.uptimeMillis(),
/* eventTime= */ SystemClock.uptimeMillis(),
KeyEvent.ACTION_DOWN,
keyCode,
/* repeat= */ 0,
metaState),
/* isCurrentTabVisible= */ true,
/* tabSwitchingEnabled= */ true,
mTabModelSelector,
mMenuOrKeyboardActionController,
mToolbarManager));
assertTrue(message, keyDown(keyCode, metaState, true));
int numCode =
(KeyEvent.KEYCODE_1 <= keyCode && keyCode <= KeyEvent.KEYCODE_8)
? keyCode - KeyEvent.KEYCODE_0
@@ -330,4 +307,20 @@ public class KeyboardShortcutsTest {
verify(mTabModel, expectHandled ? times(1) : never())
.setIndex(numCode - 1, TabSelectionType.FROM_USER);
}
private boolean keyDown(int keyCode, int metaState, boolean isCurrentTabVisible) {
return KeyboardShortcuts.onKeyDown(
new KeyEvent(
/* downTime= */ SystemClock.uptimeMillis(),
/* eventTime= */ SystemClock.uptimeMillis(),
KeyEvent.ACTION_DOWN,
keyCode,
/* repeat= */ 0,
metaState),
isCurrentTabVisible,
/* tabSwitchingEnabled= */ true,
mTabModelSelector,
mMenuOrKeyboardActionController,
mToolbarManager);
}
}