Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js

Issue 2649373002: Re-land: Update json_schema_compiler to handle the Automation extension API (Closed)
Patch Set: Fix presubmit Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview Provides output services for ChromeVox. 6 * @fileoverview Provides output services for ChromeVox.
7 */ 7 */
8 8
9 goog.provide('Output'); 9 goog.provide('Output');
10 goog.provide('Output.EventType'); 10 goog.provide('Output.EventType');
(...skipping 12 matching lines...) Expand all
23 goog.require('cvox.TtsCategory'); 23 goog.require('cvox.TtsCategory');
24 goog.require('cvox.ValueSelectionSpan'); 24 goog.require('cvox.ValueSelectionSpan');
25 goog.require('cvox.ValueSpan'); 25 goog.require('cvox.ValueSpan');
26 goog.require('goog.i18n.MessageFormat'); 26 goog.require('goog.i18n.MessageFormat');
27 27
28 goog.scope(function() { 28 goog.scope(function() {
29 var AutomationNode = chrome.automation.AutomationNode; 29 var AutomationNode = chrome.automation.AutomationNode;
30 var Dir = constants.Dir; 30 var Dir = constants.Dir;
31 var EventType = chrome.automation.EventType; 31 var EventType = chrome.automation.EventType;
32 var RoleType = chrome.automation.RoleType; 32 var RoleType = chrome.automation.RoleType;
33 var StateType = chrome.automation.StateType;
33 34
34 /** 35 /**
35 * An Output object formats a cursors.Range into speech, braille, or both 36 * An Output object formats a cursors.Range into speech, braille, or both
36 * representations. This is typically a |Spannable|. 37 * representations. This is typically a |Spannable|.
37 * 38 *
38 * The translation from Range to these output representations rely upon format 39 * The translation from Range to these output representations rely upon format
39 * rules which specify how to convert AutomationNode objects into annotated 40 * rules which specify how to convert AutomationNode objects into annotated
40 * strings. 41 * strings.
41 * The format of these rules is as follows. 42 * The format of these rules is as follows.
42 * 43 *
(...skipping 953 matching lines...) Expand 10 before | Expand all | Expand 10 after
996 if (prevRange && !prevRange.isValid()) 997 if (prevRange && !prevRange.isValid())
997 prevRange = null; 998 prevRange = null;
998 999
999 // Scan unique ancestors to get the value of |outputContextFirst|. 1000 // Scan unique ancestors to get the value of |outputContextFirst|.
1000 var parent = range.start.node; 1001 var parent = range.start.node;
1001 var prevParent = prevRange ? prevRange.start.node : parent; 1002 var prevParent = prevRange ? prevRange.start.node : parent;
1002 if (!parent || !prevParent) 1003 if (!parent || !prevParent)
1003 return; 1004 return;
1004 var uniqueAncestors = AutomationUtil.getUniqueAncestors(prevParent, parent); 1005 var uniqueAncestors = AutomationUtil.getUniqueAncestors(prevParent, parent);
1005 for (var i = 0; parent = uniqueAncestors[i]; i++) { 1006 for (var i = 0; parent = uniqueAncestors[i]; i++) {
1006 if (parent.role == RoleType.window) 1007 if (parent.role == RoleType.WINDOW)
1007 break; 1008 break;
1008 if (Output.ROLE_INFO_[parent.role] && 1009 if (Output.ROLE_INFO_[parent.role] &&
1009 Output.ROLE_INFO_[parent.role].outputContextFirst) { 1010 Output.ROLE_INFO_[parent.role].outputContextFirst) {
1010 this.outputContextFirst_ = true; 1011 this.outputContextFirst_ = true;
1011 break; 1012 break;
1012 } 1013 }
1013 } 1014 }
1014 1015
1015 if (range.isSubNode()) 1016 if (range.isSubNode())
1016 this.subNode_(range, prevRange, type, buff); 1017 this.subNode_(range, prevRange, type, buff);
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
1062 if (options.isUnique) 1063 if (options.isUnique)
1063 token = token.substring(0, token.length - 1); 1064 token = token.substring(0, token.length - 1);
1064 1065
1065 // Process token based on prefix. 1066 // Process token based on prefix.
1066 var prefix = token[0]; 1067 var prefix = token[0];
1067 token = token.slice(1); 1068 token = token.slice(1);
1068 1069
1069 // All possible tokens based on prefix. 1070 // All possible tokens based on prefix.
1070 if (prefix == '$') { 1071 if (prefix == '$') {
1071 if (token == 'value') { 1072 if (token == 'value') {
1072 var text = node.value; 1073 var text = node.value || '';
1073 if (!node.state.editable && node.name == text) 1074 if (!node.state[StateType.EDITABLE] && node.name == text)
1074 return; 1075 return;
1075 1076
1076 var selectedText = ''; 1077 var selectedText = '';
1077 if (text !== undefined) { 1078 if (node.textSelStart !== undefined) {
1078 if (node.textSelStart !== undefined) { 1079 options.annotation.push(new Output.SelectionSpan(
1079 options.annotation.push(new Output.SelectionSpan( 1080 node.textSelStart || 0,
1080 node.textSelStart, 1081 node.textSelEnd || 0));
1081 node.textSelEnd));
1082 1082
1083 selectedText = 1083 selectedText =
1084 node.value.substring(node.textSelStart, node.textSelEnd); 1084 node.value.substring(node.textSelStart || 0,
1085 } 1085 node.textSelEnd || 0);
1086 } 1086 }
1087 options.annotation.push(token); 1087 options.annotation.push(token);
1088 if (selectedText && !this.formatOptions_.braille) { 1088 if (selectedText && !this.formatOptions_.braille) {
1089 this.append_(buff, selectedText, options); 1089 this.append_(buff, selectedText, options);
1090 this.append_(buff, Msgs.getMsg('selected')); 1090 this.append_(buff, Msgs.getMsg('selected'));
1091 } else { 1091 } else {
1092 this.append_(buff, text, options); 1092 this.append_(buff, text, options);
1093 } 1093 }
1094 } else if (token == 'name') { 1094 } else if (token == 'name') {
1095 options.annotation.push(token); 1095 options.annotation.push(token);
1096 var earcon = node ? this.findEarcon_(node, opt_prevNode) : null; 1096 var earcon = node ? this.findEarcon_(node, opt_prevNode) : null;
1097 if (earcon) 1097 if (earcon)
1098 options.annotation.push(earcon); 1098 options.annotation.push(earcon);
1099 this.append_(buff, node.name || '', options); 1099 this.append_(buff, node.name || '', options);
1100 } else if (token == 'description') { 1100 } else if (token == 'description') {
1101 if (node.name == node.description) 1101 if (node.name == node.description)
1102 return; 1102 return;
1103 1103
1104 options.annotation.push(token); 1104 options.annotation.push(token);
1105 this.append_(buff, node.description || '', options); 1105 this.append_(buff, node.description || '', options);
1106 } else if (token == 'urlFilename') { 1106 } else if (token == 'urlFilename') {
1107 options.annotation.push('name'); 1107 options.annotation.push('name');
1108 var url = node.url; 1108 var url = node.url || '';
1109 var filename = ''; 1109 var filename = '';
1110 if (url.substring(0, 4) != 'data') { 1110 if (url.substring(0, 4) != 'data') {
1111 filename = 1111 filename =
1112 url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.')); 1112 url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.'));
1113 1113
1114 // Hack to not speak the filename if it's ridiculously long. 1114 // Hack to not speak the filename if it's ridiculously long.
1115 if (filename.length >= 30) 1115 if (filename.length >= 30)
1116 filename = filename.substring(0, 16) + '...'; 1116 filename = filename.substring(0, 16) + '...';
1117 } 1117 }
1118 this.append_(buff, filename, options); 1118 this.append_(buff, filename, options);
1119 } else if (token == 'nameFromNode') { 1119 } else if (token == 'nameFromNode') {
1120 if (chrome.automation.NameFromType[node.nameFrom] == 1120 if (node.nameFrom == chrome.automation.NameFromType.CONTENTS)
1121 'contents')
1122 return; 1121 return;
1123 1122
1124 options.annotation.push('name'); 1123 options.annotation.push('name');
1125 this.append_(buff, node.name, options); 1124 this.append_(buff, node.name || '', options);
1126 } else if (token == 'nameOrDescendants') { 1125 } else if (token == 'nameOrDescendants') {
1127 options.annotation.push(token); 1126 options.annotation.push(token);
1128 if (node.name) 1127 if (node.name)
1129 this.append_(buff, node.name, options); 1128 this.append_(buff, node.name || '', options);
1130 else 1129 else
1131 this.format_(node, '$descendants', buff); 1130 this.format_(node, '$descendants', buff);
1132 } else if (token == 'description') { 1131 } else if (token == 'description') {
1133 if (node.name == node.description || node.value == node.description) 1132 if (node.name == node.description || node.value == node.description)
1134 return; 1133 return;
1135 options.annotation.push(token); 1134 options.annotation.push(token);
1136 this.append_(buff, node.description, options); 1135 this.append_(buff, node.description || '', options);
1137 } else if (token == 'indexInParent') { 1136 } else if (token == 'indexInParent') {
1138 if (node.parent) { 1137 if (node.parent) {
1139 options.annotation.push(token); 1138 options.annotation.push(token);
1140 var count = 0; 1139 var count = 0;
1141 for (var i = 0, child; child = node.parent.children[i]; i++) { 1140 for (var i = 0, child; child = node.parent.children[i]; i++) {
1142 if (node.role == child.role) 1141 if (node.role == child.role)
1143 count++; 1142 count++;
1144 if (node === child) 1143 if (node === child)
1145 break; 1144 break;
1146 } 1145 }
(...skipping 14 matching lines...) Expand all
1161 case 'mixed': 1160 case 'mixed':
1162 msg = 'aria_checked_mixed'; 1161 msg = 'aria_checked_mixed';
1163 break; 1162 break;
1164 case 'true': 1163 case 'true':
1165 msg = 'aria_checked_true'; 1164 msg = 'aria_checked_true';
1166 break; 1165 break;
1167 case 'false': 1166 case 'false':
1168 msg = 'aria_checked_false'; 1167 msg = 'aria_checked_false';
1169 break; 1168 break;
1170 default: 1169 default:
1171 msg = 1170 msg = node.state[StateType.CHECKED] ?
1172 node.state.checked ? 'aria_checked_true' : 'aria_checked_false'; 1171 'aria_checked_true' : 'aria_checked_false';
1173 } 1172 }
1174 this.format_(node, '@' + msg, buff); 1173 this.format_(node, '@' + msg, buff);
1175 } else if (token == 'state') { 1174 } else if (token == 'state') {
1176 Object.getOwnPropertyNames(node.state).forEach(function(s) { 1175 if (node.state) {
1177 var stateInfo = Output.STATE_INFO_[s]; 1176 Object.getOwnPropertyNames(node.state).forEach(function(s) {
1178 if (stateInfo && !stateInfo.isRoleSpecific && stateInfo.on) 1177 var stateInfo = Output.STATE_INFO_[s];
1178 if (stateInfo && !stateInfo.isRoleSpecific && stateInfo.on)
1179 this.format_(node, '@' + stateInfo.on.msgId, buff); 1179 this.format_(node, '@' + stateInfo.on.msgId, buff);
1180 }.bind(this)); 1180 }.bind(this));
1181 }
1181 } else if (token == 'find') { 1182 } else if (token == 'find') {
1182 // Find takes two arguments: JSON query string and format string. 1183 // Find takes two arguments: JSON query string and format string.
1183 if (tree.firstChild) { 1184 if (tree.firstChild) {
1184 var jsonQuery = tree.firstChild.value; 1185 var jsonQuery = tree.firstChild.value;
1185 node = node.find( 1186 node = node.find(
1186 /** @type {Object}*/(JSON.parse(jsonQuery))); 1187 /** @type {chrome.automation.FindParams}*/(
1188 JSON.parse(jsonQuery)));
1187 var formatString = tree.firstChild.nextSibling; 1189 var formatString = tree.firstChild.nextSibling;
1188 if (node) 1190 if (node)
1189 this.format_(node, formatString, buff); 1191 this.format_(node, formatString, buff);
1190 } 1192 }
1191 } else if (token == 'descendants') { 1193 } else if (token == 'descendants') {
1192 if (!node || AutomationPredicate.leafOrStaticText(node)) 1194 if (!node || AutomationPredicate.leafOrStaticText(node))
1193 return; 1195 return;
1194 1196
1195 // Construct a range to the leftmost and rightmost leaves. 1197 // Construct a range to the leftmost and rightmost leaves.
1196 var leftmost = AutomationUtil.findNodePre( 1198 var leftmost = AutomationUtil.findNodePre(
(...skipping 26 matching lines...) Expand all
1223 var msg = node.role; 1225 var msg = node.role;
1224 var info = Output.ROLE_INFO_[node.role]; 1226 var info = Output.ROLE_INFO_[node.role];
1225 if (info) { 1227 if (info) {
1226 if (this.formatOptions_.braille) 1228 if (this.formatOptions_.braille)
1227 msg = Msgs.getMsg(info.msgId + '_brl'); 1229 msg = Msgs.getMsg(info.msgId + '_brl');
1228 else 1230 else
1229 msg = Msgs.getMsg(info.msgId); 1231 msg = Msgs.getMsg(info.msgId);
1230 } else { 1232 } else {
1231 console.error('Missing role info for ' + node.role); 1233 console.error('Missing role info for ' + node.role);
1232 } 1234 }
1233 this.append_(buff, msg, options); 1235 this.append_(buff, msg || '', options);
1234 } else if (token == 'inputType') { 1236 } else if (token == 'inputType') {
1235 if (!node.inputType) 1237 if (!node.inputType)
1236 return; 1238 return;
1237 options.annotation.push(token); 1239 options.annotation.push(token);
1238 var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] || 1240 var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] ||
1239 'input_type_text'; 1241 'input_type_text';
1240 if (this.formatOptions_.braille) 1242 if (this.formatOptions_.braille)
1241 msgId = msgId + '_brl'; 1243 msgId = msgId + '_brl';
1242 this.append_(buff, Msgs.getMsg(msgId), options); 1244 this.append_(buff, Msgs.getMsg(msgId), options);
1243 } else if (token == 'tableCellRowIndex' || 1245 } else if (token == 'tableCellRowIndex' ||
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1278 this.append_(buff, value, options); 1280 this.append_(buff, value, options);
1279 } else if (Output.STATE_INFO_[token]) { 1281 } else if (Output.STATE_INFO_[token]) {
1280 options.annotation.push('state'); 1282 options.annotation.push('state');
1281 var stateInfo = Output.STATE_INFO_[token]; 1283 var stateInfo = Output.STATE_INFO_[token];
1282 var resolvedInfo = {}; 1284 var resolvedInfo = {};
1283 resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off; 1285 resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off;
1284 if (!resolvedInfo) 1286 if (!resolvedInfo)
1285 return; 1287 return;
1286 if (this.formatOptions_.speech && resolvedInfo.earconId) { 1288 if (this.formatOptions_.speech && resolvedInfo.earconId) {
1287 options.annotation.push( 1289 options.annotation.push(
1288 new Output.EarconAction(resolvedInfo.earconId), node.location); 1290 new Output.EarconAction(resolvedInfo.earconId),
1291 node.location || undefined);
1289 } 1292 }
1290 var msgId = 1293 var msgId =
1291 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' : 1294 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' :
1292 resolvedInfo.msgId; 1295 resolvedInfo.msgId;
1293 var msg = Msgs.getMsg(msgId); 1296 var msg = Msgs.getMsg(msgId);
1294 this.append_(buff, msg, options); 1297 this.append_(buff, msg, options);
1295 } else if (tree.firstChild) { 1298 } else if (tree.firstChild) {
1296 // Custom functions. 1299 // Custom functions.
1297 if (token == 'if') { 1300 if (token == 'if') {
1298 var cond = tree.firstChild; 1301 var cond = tree.firstChild;
1299 var attrib = cond.value.slice(1); 1302 var attrib = cond.value.slice(1);
1300 if (node[attrib] !== undefined || node.state[attrib]) 1303 if (node[attrib] !== undefined || node.state[attrib])
1301 this.format_(node, cond.nextSibling, buff); 1304 this.format_(node, cond.nextSibling, buff);
1302 else 1305 else
1303 this.format_(node, cond.nextSibling.nextSibling, buff); 1306 this.format_(node, cond.nextSibling.nextSibling, buff);
1304 } else if (token == 'earcon') { 1307 } else if (token == 'earcon') {
1305 // Ignore unless we're generating speech output. 1308 // Ignore unless we're generating speech output.
1306 if (!this.formatOptions_.speech) 1309 if (!this.formatOptions_.speech)
1307 return; 1310 return;
1308 1311
1309 options.annotation.push( 1312 options.annotation.push(
1310 new Output.EarconAction(tree.firstChild.value, node.location)); 1313 new Output.EarconAction(tree.firstChild.value,
1314 node.location || undefined));
1311 this.append_(buff, '', options); 1315 this.append_(buff, '', options);
1312 } else if (token == 'countChildren') { 1316 } else if (token == 'countChildren') {
1313 var role = tree.firstChild.value; 1317 var role = tree.firstChild.value;
1314 var count = node.children.filter(function(e) { 1318 var count = node.children.filter(function(e) {
1315 return e.role == role; 1319 return e.role == role;
1316 }).length; 1320 }).length;
1317 this.append_(buff, String(count)); 1321 this.append_(buff, String(count));
1318 } 1322 }
1319 } 1323 }
1320 } else if (prefix == '@') { 1324 } else if (prefix == '@') {
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
1467 */ 1471 */
1468 ancestry_: function(node, prevNode, type, buff) { 1472 ancestry_: function(node, prevNode, type, buff) {
1469 // Expects |ancestors| to be ordered from root down to leaf. Outputs in 1473 // Expects |ancestors| to be ordered from root down to leaf. Outputs in
1470 // reverse; place context first nodes at the end. 1474 // reverse; place context first nodes at the end.
1471 function byContextFirst(ancestors) { 1475 function byContextFirst(ancestors) {
1472 var contextFirst = []; 1476 var contextFirst = [];
1473 var rest = []; 1477 var rest = [];
1474 for (i = 0; i < ancestors.length - 1; i++) { 1478 for (i = 0; i < ancestors.length - 1; i++) {
1475 var node = ancestors[i]; 1479 var node = ancestors[i];
1476 // Discard ancestors of deepest window. 1480 // Discard ancestors of deepest window.
1477 if (node.role == RoleType.window) { 1481 if (node.role == RoleType.WINDOW) {
1478 contextFirst = []; 1482 contextFirst = [];
1479 rest = []; 1483 rest = [];
1480 } 1484 }
1481 if ((Output.ROLE_INFO_[node.role] || {}).outputContextFirst) 1485 if ((Output.ROLE_INFO_[node.role] || {}).outputContextFirst)
1482 contextFirst.push(node); 1486 contextFirst.push(node);
1483 else 1487 else
1484 rest.push(node); 1488 rest.push(node);
1485 } 1489 }
1486 return rest.concat(contextFirst.reverse()); 1490 return rest.concat(contextFirst.reverse());
1487 } 1491 }
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after
1631 } 1635 }
1632 } 1636 }
1633 1637
1634 if (this.outputContextFirst_) 1638 if (this.outputContextFirst_)
1635 this.ancestry_(node, prevNode, type, buff); 1639 this.ancestry_(node, prevNode, type, buff);
1636 var earcon = this.findEarcon_(node, prevNode); 1640 var earcon = this.findEarcon_(node, prevNode);
1637 if (earcon) 1641 if (earcon)
1638 options.annotation.push(earcon); 1642 options.annotation.push(earcon);
1639 var text = ''; 1643 var text = '';
1640 1644
1641 if (this.formatOptions_.braille && !node.state.editable) { 1645 if (this.formatOptions_.braille && !node.state[StateType.EDITABLE]) {
1642 // In braille, we almost always want to show the entire contents and 1646 // In braille, we almost always want to show the entire contents and
1643 // simply place the cursor under the SelectionSpan we set above. 1647 // simply place the cursor under the SelectionSpan we set above.
1644 text = range.start.getText(); 1648 text = range.start.getText();
1645 } else { 1649 } else {
1646 // This is output for speech or editable braille. 1650 // This is output for speech or editable braille.
1647 text = range.start.getText().substring(rangeStart, rangeEnd); 1651 text = range.start.getText().substring(rangeStart, rangeEnd);
1648 } 1652 }
1649 1653
1650 this.append_(buff, text, options); 1654 this.append_(buff, text, options);
1651 1655
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
1774 return result; 1778 return result;
1775 } 1779 }
1776 1780
1777 // Keep track of if there's an inline node associated with 1781 // Keep track of if there's an inline node associated with
1778 // |cur|. 1782 // |cur|.
1779 var hasInlineNode = cur.getSpansInstanceOf(Output.NodeSpan) 1783 var hasInlineNode = cur.getSpansInstanceOf(Output.NodeSpan)
1780 .some(function(s) { 1784 .some(function(s) {
1781 if (!s.node) 1785 if (!s.node)
1782 return false; 1786 return false;
1783 return s.node.display == 'inline' || 1787 return s.node.display == 'inline' ||
1784 s.node.role == RoleType.inlineTextBox; 1788 s.node.role == RoleType.INLINE_TEXT_BOX;
1785 }); 1789 });
1786 1790
1787 var isName = cur.hasSpan('name'); 1791 var isName = cur.hasSpan('name');
1788 1792
1789 // Now, decide whether we should include separators between the previous 1793 // Now, decide whether we should include separators between the previous
1790 // span and |cur|. 1794 // span and |cur|.
1791 // Never separate chunks without something already there at this point. 1795 // Never separate chunks without something already there at this point.
1792 1796
1793 // The only case where we know for certain that a separator is not needed 1797 // The only case where we know for certain that a separator is not needed
1794 // is when the previous and current values are in-lined and part of the 1798 // is when the previous and current values are in-lined and part of the
(...skipping 30 matching lines...) Expand all
1825 var earconFinder = node; 1829 var earconFinder = node;
1826 var ancestors; 1830 var ancestors;
1827 if (opt_prevNode) 1831 if (opt_prevNode)
1828 ancestors = AutomationUtil.getUniqueAncestors(opt_prevNode, node); 1832 ancestors = AutomationUtil.getUniqueAncestors(opt_prevNode, node);
1829 else 1833 else
1830 ancestors = AutomationUtil.getAncestors(node); 1834 ancestors = AutomationUtil.getAncestors(node);
1831 1835
1832 while (earconFinder = ancestors.pop()) { 1836 while (earconFinder = ancestors.pop()) {
1833 var info = Output.ROLE_INFO_[earconFinder.role]; 1837 var info = Output.ROLE_INFO_[earconFinder.role];
1834 if (info && info.earconId) { 1838 if (info && info.earconId) {
1835 return new Output.EarconAction(info.earconId, node.location); 1839 return new Output.EarconAction(info.earconId,
1840 node.location || undefined);
1836 break; 1841 break;
1837 } 1842 }
1838 earconFinder = earconFinder.parent; 1843 earconFinder = earconFinder.parent;
1839 } 1844 }
1840 } 1845 }
1841 return null; 1846 return null;
1842 }, 1847 },
1843 1848
1844 /** 1849 /**
1845 * Gets a human friendly string with the contents of output. 1850 * Gets a human friendly string with the contents of output.
(...skipping 25 matching lines...) Expand all
1871 /** 1876 /**
1872 * Gets the output buffer for braille. 1877 * Gets the output buffer for braille.
1873 * @return {!Spannable} 1878 * @return {!Spannable}
1874 */ 1879 */
1875 get brailleOutputForTest() { 1880 get brailleOutputForTest() {
1876 return this.mergeBraille_(this.brailleBuffer_); 1881 return this.mergeBraille_(this.brailleBuffer_);
1877 } 1882 }
1878 }; 1883 };
1879 1884
1880 }); // goog.scope 1885 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698