Commit 50aabeac authored by Anastasia Helfinstein's avatar Anastasia Helfinstein Committed by Commit Bot

[Switch Access] Add metrics for errors and unexpected states

Bug: 1018327
Change-Id: I667be3f714fcea00d2238e3f60144d8118c99bbb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1884822Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Commit-Queue: Anastasia Helfinstein <anastasi@google.com>
Cr-Commit-Position: refs/heads/master@{#711812}
parent e2656f11
...@@ -388,6 +388,7 @@ js_library("switch_access") { ...@@ -388,6 +388,7 @@ js_library("switch_access") {
externs_list = [ externs_list = [
"$externs_path/automation.js", "$externs_path/automation.js",
"$externs_path/command_line_private.js", "$externs_path/command_line_private.js",
"$externs_path/metrics_private.js",
] ]
} }
......
...@@ -69,9 +69,10 @@ class FocusRingManager { ...@@ -69,9 +69,10 @@ class FocusRingManager {
*/ */
setColor(color) { setColor(color) {
if (this.colorPattern_.test(color) !== true) { if (this.colorPattern_.test(color) !== true) {
const errorString = 'Problem setting focus ring color: color is not' + throw SwitchAccess.error(
'a valid CSS color string.'; SAConstants.ErrorType.INVALID_COLOR,
throw new Error(errorString); 'Problem setting focus ring color: color is not' +
'a valid CSS color string.');
} }
this.rings_.forEach((ring) => ring.color = color); this.rings_.forEach((ring) => ring.color = color);
} }
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
"clipboard", "clipboard",
"clipboardRead", "clipboardRead",
"commandLinePrivate", "commandLinePrivate",
"metricsPrivate",
"settingsPrivate", "settingsPrivate",
"tabs" "tabs"
], ],
......
...@@ -261,14 +261,18 @@ class Panel { ...@@ -261,14 +261,18 @@ class Panel {
const elements = document.querySelectorAll('.i18n'); const elements = document.querySelectorAll('.i18n');
for (const element of elements) { for (const element of elements) {
const messageId = element.getAttribute('msgid'); const messageId = element.getAttribute('msgid');
if (!messageId) if (!messageId) {
throw new Error('Element has no msgid attribute: ' + element); throw SwitchAccess.error(
SAConstants.ErrorType.UNTRANSLATED_STRING,
'Element has no msgid attribute: ' + element);
}
const translatedMessage = const translatedMessage =
chrome.i18n.getMessage('switch_access_' + messageId); chrome.i18n.getMessage('switch_access_' + messageId);
if (element.tagName == 'INPUT') if (element.tagName == 'INPUT') {
element.setAttribute('placeholder', translatedMessage); element.setAttribute('placeholder', translatedMessage);
else } else {
element.textContent = translatedMessage; element.textContent = translatedMessage;
}
element.classList.add('i18n-processed'); element.classList.add('i18n-processed');
} }
} }
......
...@@ -201,7 +201,9 @@ class RootNodeWrapper extends SARootNode { ...@@ -201,7 +201,9 @@ class RootNodeWrapper extends SARootNode {
const interestingChildren = RootNodeWrapper.getInterestingChildren(root); const interestingChildren = RootNodeWrapper.getInterestingChildren(root);
if (interestingChildren.length < 1) { if (interestingChildren.length < 1) {
throw new Error('Root node must have at least 1 interesting child.'); throw SwitchAccess.error(
SAConstants.ErrorType.NO_CHILDREN,
'Root node must have at least 1 interesting child.');
} }
let children = interestingChildren.map(childConstructor); let children = interestingChildren.map(childConstructor);
...@@ -221,7 +223,9 @@ class RootNodeWrapper extends SARootNode { ...@@ -221,7 +223,9 @@ class RootNodeWrapper extends SARootNode {
const interestingChildren = RootNodeWrapper.getInterestingChildren(root); const interestingChildren = RootNodeWrapper.getInterestingChildren(root);
if (interestingChildren.length < 1) { if (interestingChildren.length < 1) {
throw new Error('Desktop node must have at least 1 interesting child.'); throw SwitchAccess.error(
SAConstants.ErrorType.MALFORMED_DESKTOP,
'Desktop node must have at least 1 interesting child.');
} }
const childConstructor = (autoNode) => new NodeWrapper(autoNode, root); const childConstructor = (autoNode) => new NodeWrapper(autoNode, root);
......
...@@ -114,7 +114,8 @@ class SAChildNode { ...@@ -114,7 +114,8 @@ class SAChildNode {
*/ */
get next() { get next() {
if (!this.next_) { if (!this.next_) {
throw new Error( throw SwitchAccess.error(
SAConstants.ErrorType.NEXT_UNDEFINED,
'Next node must be set on all SAChildNodes before navigating'); 'Next node must be set on all SAChildNodes before navigating');
} }
return this.next_; return this.next_;
...@@ -126,7 +127,8 @@ class SAChildNode { ...@@ -126,7 +127,8 @@ class SAChildNode {
*/ */
get previous() { get previous() {
if (!this.previous_) { if (!this.previous_) {
throw new Error( throw SwitchAccess.error(
SAConstants.ErrorType.PREVIOUS_UNDEFINED,
'Previous node must be set on all SAChildNodes before navigating'); 'Previous node must be set on all SAChildNodes before navigating');
} }
return this.previous_; return this.previous_;
...@@ -200,7 +202,10 @@ class SARootNode { ...@@ -200,7 +202,10 @@ class SARootNode {
let result = true; let result = true;
for (let i = 0; i < this.children_.length; i++) { for (let i = 0; i < this.children_.length; i++) {
if (!this.children_[i]) throw new Error('Child cannot be null.'); if (!this.children_[i]) {
throw SwitchAccess.error(
SAConstants.ErrorType.NULL_CHILD, 'Child cannot be null.');
}
result = result && this.children_[i].equals(other.children_[i]); result = result && this.children_[i].equals(other.children_[i]);
} }
...@@ -217,7 +222,9 @@ class SARootNode { ...@@ -217,7 +222,9 @@ class SARootNode {
if (this.children_.length > 0) { if (this.children_.length > 0) {
return this.children_[0]; return this.children_[0];
} else { } else {
throw new Error('Root nodes must contain children.'); throw SwitchAccess.error(
SAConstants.ErrorType.NO_CHILDREN,
'Root nodes must contain children.');
} }
} }
...@@ -226,7 +233,9 @@ class SARootNode { ...@@ -226,7 +233,9 @@ class SARootNode {
if (this.children_.length > 0) { if (this.children_.length > 0) {
return this.children_[this.children_.length - 1]; return this.children_[this.children_.length - 1];
} else { } else {
throw new Error('Root nodes must contain children.'); throw SwitchAccess.error(
SAConstants.ErrorType.NO_CHILDREN,
'Root nodes must contain children.');
} }
} }
...@@ -287,7 +296,9 @@ class SARootNode { ...@@ -287,7 +296,9 @@ class SARootNode {
*/ */
static connectChildren(children) { static connectChildren(children) {
if (children.length < 1) { if (children.length < 1) {
throw new Error('Root node must have at least 1 interesting child.'); throw SwitchAccess.error(
SAConstants.ErrorType.NO_CHILDREN,
'Root node must have at least 1 interesting child.');
} }
let previous = children[children.length - 1]; let previous = children[children.length - 1];
......
...@@ -99,7 +99,7 @@ class SwitchAccessPreferences { ...@@ -99,7 +99,7 @@ class SwitchAccessPreferences {
} }
/** /**
* Get the boolean value for the given name. Will throw a type error if the * Get the boolean value for the given name. Will throw an error if the
* value associated with |name| is not a boolean, or is undefined. * value associated with |name| is not a boolean, or is undefined.
* *
* @param {SAConstants.Preference} name * @param {SAConstants.Preference} name
...@@ -107,10 +107,13 @@ class SwitchAccessPreferences { ...@@ -107,10 +107,13 @@ class SwitchAccessPreferences {
*/ */
getBooleanPreference(name) { getBooleanPreference(name) {
const pref = this.preferences_.get(name); const pref = this.preferences_.get(name);
if (pref && pref.type === chrome.settingsPrivate.PrefType.BOOLEAN) if (pref && pref.type === chrome.settingsPrivate.PrefType.BOOLEAN) {
return /** @type {boolean} */ (pref.value); return /** @type {boolean} */ (pref.value);
else } else {
throw new TypeError('No value of boolean type named \'' + name + '\''); throw SwitchAccess.error(
SAConstants.ErrorType.PREFERENCE_TYPE,
'No value of boolean type named \'' + name + '\'');
}
} }
/** /**
...@@ -122,10 +125,13 @@ class SwitchAccessPreferences { ...@@ -122,10 +125,13 @@ class SwitchAccessPreferences {
*/ */
getStringPreference(name) { getStringPreference(name) {
const pref = this.preferences_.get(name); const pref = this.preferences_.get(name);
if (pref && pref.type === chrome.settingsPrivate.PrefType.STRING) if (pref && pref.type === chrome.settingsPrivate.PrefType.STRING) {
return /** @type {string} */ (pref.value); return /** @type {string} */ (pref.value);
else } else {
throw new TypeError('No value of string type named \'' + name + '\''); throw SwitchAccess.error(
SAConstants.ErrorType.PREFERENCE_TYPE,
'No value of string type named \'' + name + '\'');
}
} }
/** /**
...@@ -137,8 +143,11 @@ class SwitchAccessPreferences { ...@@ -137,8 +143,11 @@ class SwitchAccessPreferences {
*/ */
getNumberPreference(name) { getNumberPreference(name) {
const value = this.getNumberPreferenceIfDefined(name); const value = this.getNumberPreferenceIfDefined(name);
if (!value) if (!value) {
throw new TypeError('No value of number type named \'' + name + '\''); throw SwitchAccess.error(
SAConstants.ErrorType.PREFERENCE_TYPE,
'No value of number type named \'' + name + '\'');
}
return value; return value;
} }
......
...@@ -206,7 +206,7 @@ class SwitchAccess { ...@@ -206,7 +206,7 @@ class SwitchAccess {
} }
/** /**
* Get the boolean value for the given name. Will throw a type error if the * Get the boolean value for the given name. Will throw an error if the
* value associated with |name| is not a boolean, or undefined. * value associated with |name| is not a boolean, or undefined.
* *
* @override * @override
...@@ -218,7 +218,7 @@ class SwitchAccess { ...@@ -218,7 +218,7 @@ class SwitchAccess {
} }
/** /**
* Get the string value for the given name. Will throw a type error if the * Get the string value for the given name. Will throw an error if the
* value associated with |name| is not a string, or is undefined. * value associated with |name| is not a string, or is undefined.
* *
* @override * @override
...@@ -230,7 +230,7 @@ class SwitchAccess { ...@@ -230,7 +230,7 @@ class SwitchAccess {
} }
/** /**
* Get the number value for the given name. Will throw a type error if the * Get the number value for the given name. Will throw an error if the
* value associated with |name| is not a number, or undefined. * value associated with |name| is not a number, or undefined.
* *
* @override * @override
...@@ -301,6 +301,19 @@ class SwitchAccess { ...@@ -301,6 +301,19 @@ class SwitchAccess {
.node; .node;
} }
/*
* Creates and records the specified error.
* @param {SAConstants.ErrorType} errorType
* @param {string} errorString
* @return {!Error}
*/
static error(errorType, errorString) {
chrome.metricsPrivate.recordEnumerationValue(
'Accessibility.CrosSwitchAccess.Error', errorType,
SAConstants.ErrorTypeCountForUMA);
return new Error(errorString);
}
/** /**
* Prints out the current Switch Access tree for debugging. * Prints out the current Switch Access tree for debugging.
* @param {boolean=} wholeTree whether to print the whole tree, or the current * @param {boolean=} wholeTree whether to print the whole tree, or the current
......
...@@ -171,3 +171,30 @@ SAConstants.MenuAction = { ...@@ -171,3 +171,30 @@ SAConstants.MenuAction = {
// Set the beginning of a text selection. // Set the beginning of a text selection.
SELECT_START: 'selectStart' SELECT_START: 'selectStart'
}; };
/**
* The types of error or unexpected state that can be encountered by Switch
* Access.
* These values are persisted to logs and should not be renumbered or re-used.
* See tools/metrics/histograms/enums.xml.
* @enum {number}
* @const
*/
SAConstants.ErrorType = {
UNKNOWN: 0,
PREFERENCE_TYPE: 1,
UNTRANSLATED_STRING: 2,
INVALID_COLOR: 3,
NEXT_UNDEFINED: 4,
PREVIOUS_UNDEFINED: 5,
NULL_CHILD: 6,
NO_CHILDREN: 7,
MALFORMED_DESKTOP: 8,
};
/**
* The number of values available in the enum SAConstants.ErrorType.
* @type {number}
* @const
*/
SAConstants.ErrorTypeCountForUMA = Object.keys(SAConstants.ErrorType).length;
...@@ -67,7 +67,7 @@ class SwitchAccessInterface { ...@@ -67,7 +67,7 @@ class SwitchAccessInterface {
setPreference(name, value) {} setPreference(name, value) {}
/** /**
* Get the boolean value for the given name. Will throw a type error if the * Get the boolean value for the given name. Will throw an error if the
* value associated with |name| is not a boolean, or undefined. * value associated with |name| is not a boolean, or undefined.
* *
* @param {SAConstants.Preference} name * @param {SAConstants.Preference} name
...@@ -76,7 +76,7 @@ class SwitchAccessInterface { ...@@ -76,7 +76,7 @@ class SwitchAccessInterface {
getBooleanPreference(name) {} getBooleanPreference(name) {}
/** /**
* Get the string value for the given name. Will throw a type error if the * Get the string value for the given name. Will throw an error if the
* value associated with |name| is not a string, or is undefined. * value associated with |name| is not a string, or is undefined.
* *
* @param {SAConstants.Preference} name * @param {SAConstants.Preference} name
...@@ -85,7 +85,7 @@ class SwitchAccessInterface { ...@@ -85,7 +85,7 @@ class SwitchAccessInterface {
getStringPreference(name) {} getStringPreference(name) {}
/** /**
* Get the number value for the given name. Will throw a type error if the * Get the number value for the given name. Will throw an error if the
* value associated with |name| is not a number, or undefined. * value associated with |name| is not a number, or undefined.
* *
* @param {SAConstants.Preference} name * @param {SAConstants.Preference} name
......
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