[Extensions] Make extension message bubble factory platform-abstract
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / speech_rules / speech_rule_engine.js
blobd7be38210ecbcf710eb5e8b55c206c681ed63717
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
3 // found in the LICENSE file.
5 /**
6 * @fileoverview Implementation of the speech rule engine.
8 * The speech rule engine chooses and applies speech rules. Rules are chosen
9 * from a set of rule stores wrt. their applicability to a node in a particular
10 * markup type such as MathML or HTML. Rules are dispatched either by
11 * recursively computing new nodes and applicable rules or, if no further rule
12 * is applicable to a current node, by computing a speech object in the form of
13 * an array of navigation descriptions.
15 * Consequently the rule engine is parameterisable wrt. rule stores and
16 * evaluator function.
19 goog.provide('cvox.SpeechRuleEngine');
21 goog.require('cvox.BaseRuleStore');
22 goog.require('cvox.NavDescription');
23 goog.require('cvox.NavMathDescription');
24 goog.require('cvox.SpeechRule');
27 /**
28 * @constructor
30 cvox.SpeechRuleEngine = function() {
31 /**
32 * The currently active speech rule store.
33 * @type {cvox.BaseRuleStore}
34 * @private
36 this.activeStore_ = null;
38 /**
39 * Dynamic constraint annotation.
40 * @type {!cvox.SpeechRule.DynamicCstr}
42 this.dynamicCstr = {};
43 this.dynamicCstr[cvox.SpeechRule.DynamicCstrAttrib.STYLE] = 'short';
45 goog.addSingletonGetter(cvox.SpeechRuleEngine);
48 /**
49 * Parameterizes the speech rule engine.
50 * @param {cvox.BaseRuleStore} store A speech rule store.
52 cvox.SpeechRuleEngine.prototype.parameterize = function(store) {
53 try {
54 store.initialize();
55 } catch (err) {
56 if (err.name == 'StoreError') {
57 console.log('Store Error:', err.message);
59 else {
60 throw err;
63 this.activeStore_ = store;
67 /**
68 * Parameterizes the dynamic constraint annotation for the speech rule
69 * engine. This is a separate function as this can be done interactively, while
70 * a particular speech rule store is active.
71 * @param {cvox.SpeechRule.DynamicCstr} dynamic The new dynamic constraint.
73 cvox.SpeechRuleEngine.prototype.setDynamicConstraint = function(dynamic) {
74 if (dynamic) {
75 this.dynamicCstr = dynamic;
80 /**
81 * Constructs a string from the node and the given expression.
82 * @param {!Node} node The initial node.
83 * @param {string} expr An Xpath expression string, a name of a custom
84 * function or a string.
85 * @return {string} The result of applying expression to node.
87 cvox.SpeechRuleEngine.prototype.constructString = function(node, expr) {
88 if (!expr) {
89 return '';
91 if (expr.charAt(0) == '"') {
92 return expr.slice(1, -1);
94 var func = this.activeStore_.customStrings.lookup(expr);
95 if (func) {
96 // We always return the result of the custom function, in case it
97 // deliberately computes the empty string!
98 return func(node);
100 // Finally we assume expr to be an xpath expression and calculate a string
101 // value from the node.
102 return cvox.XpathUtil.evaluateString(expr, node);
106 // Dispatch functionality.
108 * Computes a speech object for a given node. Returns the empty list if
109 * no node is given.
110 * @param {Node} node The node to be evaluated.
111 * @return {!Array<cvox.NavDescription>} A list of navigation descriptions for
112 * that node.
114 cvox.SpeechRuleEngine.prototype.evaluateNode = function(node) {
115 if (!node) {
116 return [];
118 return this.evaluateTree_(node);
123 * Applies rules recursively to compute the final speech object.
124 * @param {!Node} node Node to apply the speech rule to.
125 * @return {!Array<cvox.NavDescription>} A list of Navigation descriptions.
126 * @private
128 cvox.SpeechRuleEngine.prototype.evaluateTree_ = function(node) {
129 var rule = this.activeStore_.lookupRule(node, this.dynamicCstr);
130 if (!rule) {
131 return this.activeStore_.evaluateDefault(node);
133 var components = rule.action.components;
134 var result = [];
135 for (var i = 0, component; component = components[i]; i++) {
136 var navs = [];
137 var content = component['content'] || '';
138 switch (component.type) {
139 case cvox.SpeechRule.Type.NODE:
140 var selected = this.activeStore_.applyQuery(node, content);
141 if (selected) {
142 navs = this.evaluateTree_(selected);
144 break;
145 case cvox.SpeechRule.Type.MULTI:
146 selected = this.activeStore_.applySelector(node, content);
147 if (selected.length > 0) {
148 navs = this.evaluateNodeList_(
149 selected,
150 component['sepFunc'],
151 this.constructString(node, component['separator']),
152 component['ctxtFunc'],
153 this.constructString(node, component['context']));
155 break;
156 case cvox.SpeechRule.Type.TEXT:
157 selected = this.constructString(node, content);
158 if (selected) {
159 navs = [new cvox.NavDescription({text: selected})];
161 break;
162 case cvox.SpeechRule.Type.PERSONALITY:
163 default:
164 navs = [new cvox.NavDescription({text: content})];
166 // Adding overall context if it exists.
167 if (navs[0] && component['context'] &&
168 component.type != cvox.SpeechRule.Type.MULTI) {
169 navs[0]['context'] =
170 this.constructString(node, component['context']) +
171 (navs[0]['context'] || '');
173 // Adding personality to the nav descriptions.
174 result = result.concat(this.addPersonality_(navs, component));
176 return result;
181 * Evaluates a list of nodes into a list of navigation descriptions.
182 * @param {!Array<Node>} nodes Array of nodes.
183 * @param {string} sepFunc Name of a function used to compute a separator
184 * between every element.
185 * @param {string} separator A string that is used as argument to the sepFunc or
186 * interspersed directly between each node if sepFunc is not supplied.
187 * @param {string} ctxtFunc Name of a function applied to compute the context
188 * for every element in the list.
189 * @param {string} context Additional context string that is given to the
190 * ctxtFunc function or used directly if ctxtFunc is not supplied.
191 * @return {Array<cvox.NavDescription>} A list of Navigation descriptions.
192 * @private
194 cvox.SpeechRuleEngine.prototype.evaluateNodeList_ = function(
195 nodes, sepFunc, separator, ctxtFunc, context) {
196 if (nodes == []) {
197 return [];
199 var sep = separator || '';
200 var cont = context || '';
201 var cFunc = this.activeStore_.contextFunctions.lookup(ctxtFunc);
202 var ctxtClosure = cFunc ? cFunc(nodes, cont) : function() {return cont;};
203 var sFunc = this.activeStore_.contextFunctions.lookup(sepFunc);
204 var sepClosure = sFunc ? sFunc(nodes, sep) : function() {return sep;};
205 var result = [];
206 for (var i = 0, node; node = nodes[i]; i++) {
207 var navs = this.evaluateTree_(node);
208 if (navs.length > 0) {
209 navs[0]['context'] = ctxtClosure() + (navs[0]['context'] || '');
210 result = result.concat(navs);
211 if (i < nodes.length - 1) {
212 var text = sepClosure();
213 if (text) {
214 result.push(new cvox.NavDescription({text: text}));
219 return result;
224 * Maps properties in speech rules to personality properties.
225 * @type {{pitch : string,
226 * rate: string,
227 * volume: string,
228 * pause: string}}
229 * @const
231 cvox.SpeechRuleEngine.propMap = {'pitch': cvox.AbstractTts.RELATIVE_PITCH,
232 'rate': cvox.AbstractTts.RELATIVE_RATE,
233 'volume': cvox.AbstractTts.RELATIVE_VOLUME,
234 'pause': cvox.AbstractTts.PAUSE
239 * Adds personality to every Navigation Descriptions in input list.
240 * @param {Array<cvox.NavDescription>} navs A list of Navigation descriptions.
241 * @param {Object} props Property dictionary.
242 * TODO (sorge) Fully specify, when we have finalised the speech rule
243 * format.
244 * @return {Array<cvox.NavDescription>} The modified array.
245 * @private
247 cvox.SpeechRuleEngine.prototype.addPersonality_ = function(navs, props) {
248 var personality = {};
249 for (var key in cvox.SpeechRuleEngine.propMap) {
250 var value = parseFloat(props[key]);
251 if (!isNaN(value)) {
252 personality[cvox.SpeechRuleEngine.propMap[key]] = value;
255 navs.forEach(goog.bind(function(nav) {
256 this.addRelativePersonality_(nav, personality);
257 this.resetPersonality_(nav);
258 }, this));
259 return navs;
264 * Adds relative personality entries to the personality of a Navigation
265 * Description.
266 * @param {cvox.NavDescription|cvox.NavMathDescription} nav Nav Description.
267 * @param {!Object} personality Dictionary with relative personality entries.
268 * @return {cvox.NavDescription|cvox.NavMathDescription} Updated description.
269 * @private
271 cvox.SpeechRuleEngine.prototype.addRelativePersonality_ = function(
272 nav, personality) {
273 if (!nav['personality']) {
274 nav['personality'] = personality;
275 return nav;
277 var navPersonality = nav['personality'];
278 for (var p in personality) {
279 // Although values could exceed boundaries, they will be limited to the
280 // correct interval via the call to
281 // cvox.AbstractTts.prototype.mergeProperties in
282 // cvox.TtsBackground.prototype.speak
283 if (navPersonality[p] && typeof(navPersonality[p]) == 'number') {
284 navPersonality[p] = navPersonality[p] + personality[p];
285 } else {
286 navPersonality[p] = personality[p];
289 return nav;
294 * Resets personalities to default values if necessary.
295 * @param {cvox.NavDescription|cvox.NavMathDescription} nav Nav Description.
296 * @private
298 cvox.SpeechRuleEngine.prototype.resetPersonality_ = function(nav) {
299 if (this.activeStore_.defaultTtsProps) {
300 for (var i = 0, prop; prop = this.activeStore_.defaultTtsProps[i]; i++) {
301 nav.personality[prop] = cvox.ChromeVox.tts.getDefaultProperty(prop);
308 * Flag for the debug mode of the speech rule engine.
309 * @type {boolean}
311 cvox.SpeechRuleEngine.debugMode = false;
315 * Give debug output.
316 * @param {...*} output Rest elements of debug output.
318 cvox.SpeechRuleEngine.outputDebug = function(output) {
319 if (cvox.SpeechRuleEngine.debugMode) {
320 var outputList = Array.prototype.slice.call(arguments, 0);
321 console.log.apply(console,
322 ['Speech Rule Engine Debugger:'].concat(outputList));
328 * Prints the list of all current rules in ChromeVox to the console.
329 * @return {string} A textual representation of all rules in the speech rule
330 * engine.
332 cvox.SpeechRuleEngine.prototype.toString = function() {
333 var allRules = this.activeStore_.findAllRules(function(x) {return true;});
334 return allRules.map(function(rule) {return rule.toString();}).
335 join('\n');
340 * Test the precondition of a speech rule in debugging mode.
341 * @param {cvox.SpeechRule} rule A speech rule.
342 * @param {!Node} node DOM node to test applicability of the rule.
344 cvox.SpeechRuleEngine.debugSpeechRule = function(rule, node) {
345 var store = cvox.SpeechRuleEngine.getInstance().activeStore_;
346 if (store) {
347 var prec = rule.precondition;
348 cvox.SpeechRuleEngine.outputDebug(
349 prec.query, store.applyQuery(node, prec.query));
350 prec.constraints.forEach(
351 function(cstr) {
352 cvox.SpeechRuleEngine.outputDebug(
353 cstr, store.applyConstraint(node, cstr));});
359 * Test the precondition of a speech rule in debugging mode.
360 * @param {string} name Rule to debug.
361 * @param {!Node} node DOM node to test applicability of the rule.
363 cvox.SpeechRuleEngine.debugNamedSpeechRule = function(name, node) {
364 var store = cvox.SpeechRuleEngine.getInstance().activeStore_;
365 var allRules = store.findAllRules(
366 function(rule) {return rule.name == name;});
367 for (var i = 0, rule; rule = allRules[i]; i++) {
368 cvox.SpeechRuleEngine.outputDebug('Rule', name, 'number', i);
369 cvox.SpeechRuleEngine.debugSpeechRule(rule, node);