blob: a601a0201c5eea43ff07738cc2b810c607dabac8 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* The top-level class for the Switch Access accessibility feature. Handles
* initialization and small matters that don't fit anywhere else in the
* codebase.
*/
class SwitchAccess {
static initialize() {
SwitchAccess.instance = new SwitchAccess();
chrome.virtualKeyboardPrivate.setKeyboardState(
chrome.virtualKeyboardPrivate.KeyboardState.ENABLED);
chrome.automation.getDesktop((desktop) => {
// NavigationManager must be initialized first.
NavigationManager.initialize(desktop);
Commands.initialize();
SwitchAccessPreferences.initialize();
// This can throw an error, so it is done last.
KeyboardRootNode.startWatchingVisibility();
});
}
/** @private */
constructor() {
/**
* Feature flag controlling improvement of text input capabilities.
* @private {boolean}
*/
this.enableImprovedTextInput_ = false;
chrome.commandLinePrivate.hasSwitch(
'enable-experimental-accessibility-switch-access-text', (result) => {
this.enableImprovedTextInput_ = result;
});
}
/**
* Returns whether or not the feature flag
* for improved text input is enabled.
* @return {boolean}
*/
improvedTextInputEnabled() {
return this.enableImprovedTextInput_;
}
/**
* Helper function to robustly find a node fitting a given predicate, even if
* that node has not yet been created.
* Used to find the menu and back button.
* @param {!function(!AutomationNode): boolean} predicate
* @param {!function(!AutomationNode): void} foundCallback
*/
static findNodeMatchingPredicate(predicate, foundCallback) {
const desktop = NavigationManager.desktopNode;
// First, check if the node is currently in the tree.
const treeWalker = new AutomationTreeWalker(
desktop, constants.Dir.FORWARD, {visit: predicate});
treeWalker.next();
if (treeWalker.node) {
foundCallback(treeWalker.node);
return;
}
// If it's not currently in the tree, listen for changes to the desktop
// tree.
const onDesktopChildrenChanged = (event) => {
if (predicate(event.target)) {
// If the event target is the node we're looking for, we've found it.
desktop.removeEventListener(
chrome.automation.EventType.CHILDREN_CHANGED,
onDesktopChildrenChanged, false);
foundCallback(event.target);
} else if (event.target.children.length > 0) {
// Otherwise, see if one of its children is the node we're looking for.
const treeWalker = new AutomationTreeWalker(
event.target, constants.Dir.FORWARD,
{visit: predicate, root: (node) => node == event.target});
treeWalker.next();
if (treeWalker.node) {
desktop.removeEventListener(
chrome.automation.EventType.CHILDREN_CHANGED,
onDesktopChildrenChanged, false);
foundCallback(treeWalker.node);
}
}
};
desktop.addEventListener(
chrome.automation.EventType.CHILDREN_CHANGED, onDesktopChildrenChanged,
false);
}
/*
* Creates and records the specified error.
* @param {SAConstants.ErrorType} errorType
* @param {string} errorString
* @param {boolean} shouldRecover
* @return {!Error}
*/
static error(errorType, errorString, shouldRecover = false) {
if (shouldRecover) {
setTimeout(NavigationManager.moveToValidNode, 0);
}
const errorTypeCountForUMA = Object.keys(SAConstants.ErrorType).length;
chrome.metricsPrivate.recordEnumerationValue(
'Accessibility.CrosSwitchAccess.Error', errorType,
errorTypeCountForUMA);
return new Error(errorString);
}
}