Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / speech_rules / base_rule_store.js
blob079e4191ac28e9778d776925d4d364c6d802aba9
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 Base class for all speech rule stores.
7  *
8  * The base rule store implements some basic functionality that is common to
9  * most speech rule stores.
10  */
12 goog.provide('cvox.BaseRuleStore');
14 goog.require('cvox.MathUtil');
15 goog.require('cvox.SpeechRule');
16 goog.require('cvox.SpeechRuleEvaluator');
17 goog.require('cvox.SpeechRuleFunctions');
18 goog.require('cvox.SpeechRuleStore');
21 /**
22  * @constructor
23  * @implements {cvox.SpeechRuleEvaluator}
24  * @implements {cvox.SpeechRuleStore}
25  */
26 cvox.BaseRuleStore = function() {
27   /**
28    * Set of custom query functions for the store.
29    * @type {cvox.SpeechRuleFunctions.CustomQueries}
30    */
31   this.customQueries = new cvox.SpeechRuleFunctions.CustomQueries();
33   /**
34    * Set of custom strings for the store.
35    * @type {cvox.SpeechRuleFunctions.CustomStrings}
36    */
37   this.customStrings = new cvox.SpeechRuleFunctions.CustomStrings();
39   /**
40    * Set of context functions for the store.
41    * @type {cvox.SpeechRuleFunctions.ContextFunctions}
42    */
43   this.contextFunctions = new cvox.SpeechRuleFunctions.ContextFunctions();
45   /**
46    * Set of speech rules in the store.
47    * @type {!Array<cvox.SpeechRule>}
48    * @private
49    */
50   this.speechRules_ = [];
52   /**
53    * A priority list of dynamic constraint attributes.
54    * @type {!Array<cvox.SpeechRule.DynamicCstrAttrib>}
55    */
56   this.dynamicCstrAttribs = [cvox.SpeechRule.DynamicCstrAttrib.STYLE];
58   /**
59    * List of TTS properties overridden by the store when it is active.
60    * @type {!Array<string>}
61    */
62   this.defaultTtsProps = [];
66 /**
67  * @override
68  */
69 cvox.BaseRuleStore.prototype.lookupRule = function(node, dynamic) {
70   if (!node ||
71       (node.nodeType != Node.ELEMENT_NODE && node.nodeType != Node.TEXT_NODE)) {
72     return null;
73   }
74   var matchingRules = this.speechRules_.filter(
75       goog.bind(
76           function(rule) {
77             return this.testDynamicConstraints(dynamic, rule) &&
78                 this.testPrecondition_(/** @type {!Node} */ (node), rule);},
79           this));
80   return (matchingRules.length > 0) ?
81     this.pickMostConstraint_(dynamic, matchingRules) : null;
85 /**
86  * @override
87  */
88 cvox.BaseRuleStore.prototype.defineRule = function(
89     name, dynamic, action, prec, cstr) {
90   try {
91     var postc = cvox.SpeechRule.Action.fromString(action);
92     var cstrList = Array.prototype.slice.call(arguments, 4);
93     var fullPrec = new cvox.SpeechRule.Precondition(prec, cstrList);
94     var dynamicCstr = {};
95     dynamicCstr[cvox.SpeechRule.DynamicCstrAttrib.STYLE] = dynamic;
96     var rule = new cvox.SpeechRule(name, dynamicCstr, fullPrec, postc);
97   } catch (err) {
98     if (err.name == 'RuleError') {
99       console.log('Rule Error ', prec, '(' + dynamic + '):', err.message);
100       return null;
101     }
102     else {
103       throw err;
104     }
105   }
106   this.addRule(rule);
107   return rule;
112  * @override
113  */
114 cvox.BaseRuleStore.prototype.addRule = function(rule) {
115   this.speechRules_.unshift(rule);
120  * @override
121  */
122 cvox.BaseRuleStore.prototype.deleteRule = function(rule) {
123   var index = this.speechRules_.indexOf(rule);
124   if (index != -1) {
125     this.speechRules_.splice(index, 1);
126   }
131  * @override
132  */
133 cvox.BaseRuleStore.prototype.findRule = function(pred) {
134   for (var i = 0, rule; rule = this.speechRules_[i]; i++) {
135     if (pred(rule)) {
136       return rule;
137     }
138   }
139   return null;
144  * @override
145  */
146 cvox.BaseRuleStore.prototype.findAllRules = function(pred) {
147   return this.speechRules_.filter(pred);
152  * @override
153  */
154 cvox.BaseRuleStore.prototype.evaluateDefault = function(node) {
155   return [new cvox.NavDescription({'text': node.textContent})];
160  * Test the applicability of a speech rule in debugging mode.
161  * @param {string} name Rule to debug.
162  * @param {!Node} node DOM node to test applicability of given rule.
163  */
164 cvox.BaseRuleStore.prototype.debugSpeechRule = goog.abstractMethod;
168  * Function to initialize the store with speech rules. It is called by the
169  * speech rule engine upon parametrization with this store. The function allows
170  * us to define sets of rules in separate files while depending on functionality
171  * that is defined in the rule store.
172  * Essentially it is a way of getting around dependencies.
173  */
174 cvox.BaseRuleStore.prototype.initialize = goog.abstractMethod;
178  * Removes duplicates of the given rule from the rule store. Thereby duplicates
179  * are identified by having the same precondition and dynamic constraint.
180  * @param {cvox.SpeechRule} rule The rule.
181  */
182 cvox.BaseRuleStore.prototype.removeDuplicates = function(rule) {
183   for (var i = this.speechRules_.length - 1, oldRule;
184        oldRule = this.speechRules_[i]; i--) {
185          if (oldRule != rule &&
186              cvox.BaseRuleStore.compareDynamicConstraints_(
187                  oldRule.dynamicCstr, rule.dynamicCstr) &&
188                      cvox.BaseRuleStore.comparePreconditions_(oldRule, rule)) {
189            this.speechRules_.splice(i, 1);
190          }
191        }
195 // TODO (sorge) These should move into the speech rule functions.
197  * Checks if we have a custom query and applies it. Otherwise returns null.
198  * @param {!Node} node The initial node.
199  * @param {string} funcName A function name.
200  * @return {Array<Node>} The list of resulting nodes.
201  */
202 cvox.BaseRuleStore.prototype.applyCustomQuery = function(
203     node, funcName) {
204   var func = this.customQueries.lookup(funcName);
205   return func ? func(node) : null;
210  * Applies either an Xpath selector or a custom query to the node
211  * and returns the resulting node list.
212  * @param {!Node} node The initial node.
213  * @param {string} expr An Xpath expression string or a name of a custom
214  *     query.
215  * @return {Array<Node>} The list of resulting nodes.
216  */
217 cvox.BaseRuleStore.prototype.applySelector = function(node, expr) {
218   var result = this.applyCustomQuery(node, expr);
219   return result || cvox.XpathUtil.evalXPath(expr, node);
224  * Applies either an Xpath selector or a custom query to the node
225  * and returns the first result.
226  * @param {!Node} node The initial node.
227  * @param {string} expr An Xpath expression string or a name of a custom
228  *     query.
229  * @return {Node} The resulting node.
230  */
231 cvox.BaseRuleStore.prototype.applyQuery = function(node, expr) {
232   var results = this.applySelector(node, expr);
233   if (results.length > 0) {
234     return results[0];
235   }
236   return null;
241  * Applies either an Xpath selector or a custom query to the node and returns
242  * true if the application yields a non-empty result.
243  * @param {!Node} node The initial node.
244  * @param {string} expr An Xpath expression string or a name of a custom
245  *     query.
246  * @return {boolean} True if application was successful.
247  */
248 cvox.BaseRuleStore.prototype.applyConstraint = function(node, expr) {
249   var result = this.applyQuery(node, expr);
250   return !!result || cvox.XpathUtil.evaluateBoolean(expr, node);
255  * Tests whether a speech rule satisfies a set of dynamic constraints.
256  * @param {!cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraints.
257  * @param {cvox.SpeechRule} rule The rule.
258  * @return {boolean} True if the preconditions apply to the node.
259  * @protected
260  */
261 cvox.BaseRuleStore.prototype.testDynamicConstraints = function(
262     dynamic, rule) {
263   // We allow a default value for each dynamic constraints attribute.
264   // The idea is that when we can not find a speech rule matching the value for
265   // a particular attribute in the dynamic constraintwe choose the one that has
266   // the value 'default'.
267   var allKeys = /** @type {Array<cvox.SpeechRule.DynamicCstrAttrib>} */ (
268       Object.keys(dynamic));
269   return allKeys.every(
270       function(key) {
271         return dynamic[key] == rule.dynamicCstr[key] ||
272             rule.dynamicCstr[key] == 'default';
273       });
278  * Get a set of all dynamic constraint values.
279  * @return {!Object<cvox.SpeechRule.DynamicCstrAttrib, Array<string>>} The
280  *     object with all annotations.
281  */
282 cvox.BaseRuleStore.prototype.getDynamicConstraintValues = function() {
283   var result = {};
284   for (var i = 0, rule; rule = this.speechRules_[i]; i++) {
285     for (var key in rule.dynamicCstr) {
286       var newKey = [rule.dynamicCstr[key]];
287       if (result[key]) {
288         result[key] = cvox.MathUtil.union(result[key], newKey);
289       } else {
290         result[key] = newKey;
291       }
292     }
293   }
294   return result;
299  * Counts how many dynamic constraint values match exactly in the order
300  * specified by the store.
301  * @param {cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraints.
302  * @param {cvox.SpeechRule} rule The speech rule to match.
303  * @return {number} The number of matching dynamic constraint values.
304  * @private
305  */
306 cvox.BaseRuleStore.prototype.countMatchingDynamicConstraintValues_ = function(
307     dynamic, rule) {
308   var result = 0;
309   for (var i = 0, key; key = this.dynamicCstrAttribs[i]; i++) {
310     if (dynamic[key] == rule.dynamicCstr[key]) {
311       result++;
312     } else break;
313   }
314   return result;
319  * Picks the result of the most constraint rule by prefering those:
320  * 1) that best match the dynamic constraints.
321  * 2) with the most additional constraints.
322  * @param {cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraints.
323  * @param {!Array<cvox.SpeechRule>} rules An array of rules.
324  * @return {cvox.SpeechRule} The most constraint rule.
325  * @private
326  */
327 cvox.BaseRuleStore.prototype.pickMostConstraint_ = function(dynamic, rules) {
328   rules.sort(goog.bind(
329       function(r1, r2) {
330         var count1 = this.countMatchingDynamicConstraintValues_(dynamic, r1);
331         var count2 = this.countMatchingDynamicConstraintValues_(dynamic, r2);
332         // Rule one is a better match, don't swap.
333         if (count1 > count2) {
334           return -1;
335         }
336         // Rule two is a better match, swap.
337         if (count2 > count1) {
338           return 1;
339         }
340         // When same number of dynamic constraint attributes matches for
341         // both rules, compare length of static constraints.
342         return (r2.precondition.constraints.length -
343             r1.precondition.constraints.length);},
344       this));
345   return rules[0];
350  * Test the precondition of a speech rule.
351  * @param {!Node} node on which to test applicability of the rule.
352  * @param {cvox.SpeechRule} rule The rule to be tested.
353  * @return {boolean} True if the preconditions apply to the node.
354  * @private
355  */
356 cvox.BaseRuleStore.prototype.testPrecondition_ = function(node, rule) {
357   var prec = rule.precondition;
358   return this.applyQuery(node, prec.query) === node &&
359       prec.constraints.every(
360           goog.bind(function(cstr) {
361                       return this.applyConstraint(node, cstr);},
362                     this));
366 // TODO (sorge) Define the following methods directly on the dynamic constraint
367 //     and precondition classes, respectively.
369  * Compares two dynamic constraints and returns true if they are equal.
370  * @param {cvox.SpeechRule.DynamicCstr} cstr1 First dynamic constraints.
371  * @param {cvox.SpeechRule.DynamicCstr} cstr2 Second dynamic constraints.
372  * @return {boolean} True if the dynamic constraints are equal.
373  * @private
374  */
375 cvox.BaseRuleStore.compareDynamicConstraints_ = function(
376     cstr1, cstr2) {
377   if (Object.keys(cstr1).length != Object.keys(cstr2).length) {
378     return false;
379   }
380   for (var key in cstr1) {
381     if (!cstr2[key] || cstr1[key] !== cstr2[key]) {
382       return false;
383     }
384   }
385   return true;
390  * Compares two static constraints (i.e., lists of precondition constraints) and
391  * returns true if they are equal.
392  * @param {Array<string>} cstr1 First static constraints.
393  * @param {Array<string>} cstr2 Second static constraints.
394  * @return {boolean} True if the static constraints are equal.
395  * @private
396  */
397 cvox.BaseRuleStore.compareStaticConstraints_ = function(
398     cstr1, cstr2) {
399   if (cstr1.length != cstr2.length) {
400     return false;
401   }
402   for (var i = 0, cstr; cstr = cstr1[i]; i++) {
403     if (cstr2.indexOf(cstr) == -1) {
404       return false;
405     }
406   }
407   return true;
412  * Compares the preconditions of two speech rules.
413  * @param {cvox.SpeechRule} rule1 The first speech rule.
414  * @param {cvox.SpeechRule} rule2 The second speech rule.
415  * @return {boolean} True if the preconditions are equal.
416  * @private
417  */
418 cvox.BaseRuleStore.comparePreconditions_ = function(rule1, rule2) {
419   var prec1 = rule1.precondition;
420   var prec2 = rule2.precondition;
421   if (prec1.query != prec2.query) {
422     return false;
423     }
424   return cvox.BaseRuleStore.compareStaticConstraints_(
425       prec1.constraints, prec2.constraints);