Commit acbcc9ba authored by Akihiro Ota's avatar Akihiro Ota Committed by Commit Bot

ChromeVox Panel: Style elements as disabled when signed out.

This change:
1. Disables the options button (next to the 'disable ChromeVox button')
when signed out.
2. Disables certain menu items when signed out.
3. Disabled elements are styled visually and are populated with the
aria-disabled property so their state is announced by ChromeVox.
4. Stores session state in the panel to make this change cleaner.
5. Marks the panel's annotations element as hidden so it's only
accessible when visible.
6. Checks that UserAnnotationHandler.instance is defined before
performing any logic inside its functions. There was a case where
code in the panel triggered a call to UserAnnotationHandler when
its instance was undefined.
7. Ensures that navigation via arrow keys skips disabled menus and
items.

Bug: 1020003
Change-Id: I2b4204970e474054e53bc8425e0e538a9c3de556
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2050789Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747561}
parent 2887e3fd
...@@ -95,7 +95,8 @@ UserAnnotationHandler = class { ...@@ -95,7 +95,8 @@ UserAnnotationHandler = class {
*/ */
static getAnnotationForNode(node) { static getAnnotationForNode(node) {
const url = node.root.docUrl || ''; const url = node.root.docUrl || '';
if (!UserAnnotationHandler.instance.enabled || !url) { if (!UserAnnotationHandler.instance ||
!UserAnnotationHandler.instance.enabled || !url) {
return null; return null;
} }
......
...@@ -89,6 +89,17 @@ CommandStore.commandsForCategory = function(category) { ...@@ -89,6 +89,17 @@ CommandStore.commandsForCategory = function(category) {
return ret; return ret;
}; };
/**
* @param {string} command The command to query.
* @return {boolean} Whether or not this command is disallowed in the OOBE.
*/
CommandStore.disallowOOBE = function(command) {
if (!CommandStore.CMD_WHITELIST[command]) {
return false;
}
return !!CommandStore.CMD_WHITELIST[command].disallowOOBE;
};
/** /**
* List of commands and their properties * List of commands and their properties
...@@ -102,7 +113,8 @@ CommandStore.commandsForCategory = function(category) { ...@@ -102,7 +113,8 @@ CommandStore.commandsForCategory = function(category) {
* nodeList: (undefined|string), * nodeList: (undefined|string),
* skipInput: (undefined|boolean), * skipInput: (undefined|boolean),
* allowEvents: (undefined|boolean), * allowEvents: (undefined|boolean),
* disallowContinuation: (undefined|boolean)}>} * disallowContinuation: (undefined|boolean),
* disallowOOBE: (undefined|boolean)}>}
* forward: Whether this command points forward. * forward: Whether this command points forward.
* backward: Whether this command points backward. If neither forward or * backward: Whether this command points backward. If neither forward or
* backward are specified, it stays facing in the current direction. * backward are specified, it stays facing in the current direction.
......
...@@ -28,6 +28,14 @@ button { ...@@ -28,6 +28,14 @@ button {
padding: 0; padding: 0;
} }
button:disabled,
.menu-item.disabled,
.menu-bar-item.disabled {
background-color: grey;
color: white;
}
#menus_button { #menus_button {
height: 35px; height: 35px;
min-width: 52px; min-width: 52px;
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
<div id="search-container"> <div id="search-container">
<input class="i18n" msgid="search_widget_intro" id="search" type="search"> <input class="i18n" msgid="search_widget_intro" id="search" type="search">
</div> </div>
<div id="annotations-container"> <div id="annotations-container" hidden>
<input class="i18n" msgid="annotations_widget_intro" id="annotations" <input class="i18n" msgid="annotations_widget_intro" id="annotations"
type="text"> type="text">
<div hidden id="save-annotation-label" class="i18n" <div hidden id="save-annotation-label" class="i18n"
......
...@@ -44,6 +44,16 @@ Panel = class { ...@@ -44,6 +44,16 @@ Panel = class {
* Initialize the panel. * Initialize the panel.
*/ */
static init() { static init() {
/** @type {string} */
Panel.sessionState = '';
const updateSessionState = (sessionState) => {
Panel.sessionState = sessionState;
$('options').disabled = sessionState !== 'IN_SESSION';
};
chrome.loginState.getSessionState(updateSessionState);
chrome.loginState.onSessionStateChanged.addListener(updateSessionState);
// Called directly for initialization. In the background page context, // Called directly for initialization. In the background page context,
// |Background| subclasses |ChromeVoxState| (who's constructor is called as // |Background| subclasses |ChromeVoxState| (who's constructor is called as
// part of |Background| construction). // part of |Background| construction).
...@@ -407,45 +417,47 @@ Panel = class { ...@@ -407,45 +417,47 @@ Panel = class {
const CommandHandler = const CommandHandler =
chrome.extension.getBackgroundPage()['CommandHandler']; chrome.extension.getBackgroundPage()['CommandHandler'];
CommandHandler['onCommand'](binding.command); CommandHandler['onCommand'](binding.command);
}); }, binding.command);
} }
}, this)); }, this));
// Only add all open tabs to the Tabs menu if we are logged in. // Add all open tabs to the Tabs menu.
chrome.loginState.getSessionState(function( bkgnd.chrome.windows.getLastFocused(function(lastFocusedWindow) {
/** @type {string} */ sessionState) { bkgnd.chrome.windows.getAll({'populate': true}, function(windows) {
if (sessionState === 'IN_OOBE_SCREEN' || for (let i = 0; i < windows.length; i++) {
sessionState === 'IN_LOGIN_SCREEN' || const tabs = windows[i].tabs;
sessionState === 'IN_LOCK_SCREEN') { for (let j = 0; j < tabs.length; j++) {
tabsMenu.addMenuItem( let title = tabs[j].title;
Msgs.getMsg('panel_menu_item_none'), '', '', '', function() {}); if (tabs[j].active && windows[i].id == lastFocusedWindow.id) {
return; title += ' ' + Msgs.getMsg('active_tab');
}
// Add all open tabs to the Tabs menu.
bkgnd.chrome.windows.getLastFocused(function(lastFocusedWindow) {
bkgnd.chrome.windows.getAll({'populate': true}, function(windows) {
for (let i = 0; i < windows.length; i++) {
const tabs = windows[i].tabs;
for (let j = 0; j < tabs.length; j++) {
let title = tabs[j].title;
if (tabs[j].active && windows[i].id == lastFocusedWindow.id) {
title += ' ' + Msgs.getMsg('active_tab');
}
tabsMenu.addMenuItem(
title, '', '', '', (function(win, tab) {
bkgnd.chrome.windows.update(
win.id, {focused: true}, function() {
bkgnd.chrome.tabs.update(
tab.id, {active: true});
});
}).bind(this, windows[i], tabs[j]));
} }
tabsMenu.addMenuItem(
title, '', '', '', (function(win, tab) {
bkgnd.chrome.windows.update(
win.id, {focused: true}, function() {
bkgnd.chrome.tabs.update(
tab.id, {active: true});
});
}).bind(this, windows[i], tabs[j]));
} }
}); }
}); });
}); });
if (Panel.sessionState !== 'IN_SESSION') {
tabsMenu.disable();
// Disable commands that contain the property 'disallowOOBE'.
for (let i = 0; i < Panel.menus_.length; ++i) {
const menu = Panel.menus_[i];
for (let j = 0; j < menu.items.length; ++j) {
const item = menu.items[j];
if (CommandStore.disallowOOBE(item.element.id)) {
item.disable();
}
}
}
}
// Add a menu item that disables / closes ChromeVox. // Add a menu item that disables / closes ChromeVox.
chromevoxMenu.addMenuItem( chromevoxMenu.addMenuItem(
Msgs.getMsg('disable_chromevox'), 'Ctrl+Alt+Z', '', '', function() { Msgs.getMsg('disable_chromevox'), 'Ctrl+Alt+Z', '', '', function() {
...@@ -795,9 +807,32 @@ Panel = class { ...@@ -795,9 +807,32 @@ Panel = class {
activeIndex = Panel.menus_.length - 1; activeIndex = Panel.menus_.length - 1;
} }
} }
activeIndex = Panel.findEnabledMenuIndex_(activeIndex, delta > 0 ? 1 : -1);
if (activeIndex === -1) {
return;
}
Panel.activateMenu(Panel.menus_[activeIndex], true /* activateFirstItem */); Panel.activateMenu(Panel.menus_[activeIndex], true /* activateFirstItem */);
} }
/**
* Starting at |startIndex|, looks for an enabled menu.
* @param {number} startIndex
* @param {number} delta
* @return {number} The index of the enabled menu. -1 if not found.
*/
static findEnabledMenuIndex_(startIndex, delta) {
const endIndex = (delta > 0) ? Panel.menus_.length : -1;
while (startIndex !== endIndex) {
if (Panel.menus_[startIndex].enabled) {
return startIndex;
}
startIndex += delta;
}
return -1;
}
/** /**
* Advance the index of the current active menu item by |delta|. * Advance the index of the current active menu item by |delta|.
* @param {number} delta The number to add to the active menu item index. * @param {number} delta The number to add to the active menu item index.
...@@ -1089,7 +1124,9 @@ Panel = class { ...@@ -1089,7 +1124,9 @@ Panel = class {
} }
const itemText = item.text.toLowerCase(); const itemText = item.text.toLowerCase();
const match = itemText.includes(query) && const match = itemText.includes(query) &&
(itemText !== Msgs.getMsg('panel_menu_item_none').toLowerCase()); (itemText !==
Msgs.getMsg('panel_menu_item_none').toLowerCase()) &&
item.enabled;
if (match) { if (match) {
Panel.searchMenu.copyAndAddMenuItem(item); Panel.searchMenu.copyAndAddMenuItem(item);
} }
......
...@@ -70,6 +70,9 @@ PanelMenu = class { ...@@ -70,6 +70,9 @@ PanelMenu = class {
this.menuElement.addEventListener( this.menuElement.addEventListener(
'keypress', this.onKeyPress_.bind(this), true); 'keypress', this.onKeyPress_.bind(this), true);
/** @private {boolean} */
this.enabled_ = true;
} }
/** /**
...@@ -122,6 +125,11 @@ PanelMenu = class { ...@@ -122,6 +125,11 @@ PanelMenu = class {
* first item. * first item.
*/ */
activate(activateFirstItem) { activate(activateFirstItem) {
if (!this.enabled_) {
this.menuBarItemElement.focus();
return;
}
this.menuContainerElement.style.visibility = 'visible'; this.menuContainerElement.style.visibility = 'visible';
this.menuContainerElement.style.opacity = 1; this.menuContainerElement.style.opacity = 1;
this.menuBarItemElement.classList.add('active'); this.menuBarItemElement.classList.add('active');
...@@ -145,6 +153,21 @@ PanelMenu = class { ...@@ -145,6 +153,21 @@ PanelMenu = class {
} }
} }
/**
* Disables this menu. When disabled, menu contents cannot be analyzed.
* When activated, focus gets placed on the menuBarItem (title element)
* instead of the first menu item.
*/
disable() {
this.enabled_ = false;
this.menuBarItemElement.classList.add('disabled');
this.menuBarItemElement.setAttribute('aria-disabled', true);
this.menuBarItemElement.setAttribute('tabindex', 0);
this.menuBarItemElement.setAttribute(
'aria-label', this.menuBarItemElement.textContent);
this.activeIndex_ = -1;
}
/** /**
* Hide this menu. Make it invisible first to minimize spurious * Hide this menu. Make it invisible first to minimize spurious
* accessibility events before the next menu activates. * accessibility events before the next menu activates.
...@@ -177,6 +200,10 @@ PanelMenu = class { ...@@ -177,6 +200,10 @@ PanelMenu = class {
* @param {number} delta The number to add to the active menu item index. * @param {number} delta The number to add to the active menu item index.
*/ */
advanceItemBy(delta) { advanceItemBy(delta) {
if (!this.enabled_) {
return;
}
if (this.activeIndex_ >= 0) { if (this.activeIndex_ >= 0) {
this.activeIndex_ += delta; this.activeIndex_ += delta;
this.activeIndex_ = this.activeIndex_ =
...@@ -189,6 +216,13 @@ PanelMenu = class { ...@@ -189,6 +216,13 @@ PanelMenu = class {
} }
} }
this.activeIndex_ = this.findEnabledItemIndex_(
this.activeIndex_, delta > 0 ? 1 : -1 /* delta */);
if (this.activeIndex_ === -1) {
return;
}
this.items_[this.activeIndex_].element.focus(); this.items_[this.activeIndex_].element.focus();
} }
...@@ -251,12 +285,37 @@ PanelMenu = class { ...@@ -251,12 +285,37 @@ PanelMenu = class {
} }
} }
/**
* @return {boolean} The enabled state of this menu.
*/
get enabled() {
return this.enabled_;
}
/** /**
* @return {Array<PanelMenuItem>} * @return {Array<PanelMenuItem>}
*/ */
get items() { get items() {
return this.items_; return this.items_;
} }
/**
* Starting at |startIndex|, looks for an enabled menu item.
* @param {number} startIndex
* @param {number} delta
* @return {number} The index of the enabled item. -1 if not found.
* @private
*/
findEnabledItemIndex_(startIndex, delta) {
const endIndex = (delta > 0) ? this.items_.length : -1;
while (startIndex !== endIndex) {
if (this.items_[startIndex].enabled) {
return startIndex;
}
startIndex += delta;
}
return -1;
}
}; };
......
...@@ -73,9 +73,31 @@ PanelMenuItem = class { ...@@ -73,9 +73,31 @@ PanelMenuItem = class {
braille.textContent = menuItemBraille; braille.textContent = menuItemBraille;
this.element.appendChild(braille); this.element.appendChild(braille);
} }
/** @type {boolean} */
this.enabled_ = true;
} }
/**
* @return {string} The text content of this menu item.
*/
get text() { get text() {
return this.element.textContent; return this.element.textContent;
} }
/**
* @return {boolean} The enabled state of this item.
*/
get enabled() {
return this.enabled_;
}
/**
* Marks this item as disabled.
*/
disable() {
this.enabled_ = false;
this.element.classList.add('disabled');
this.element.setAttribute('aria-disabled', true);
}
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment