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.
7 * @fileoverview This class acts as the persistent store for all static data
10 * This store can safely be used within either a content or background script
13 * If you are looking to add a user command, follow the below steps for best
14 * integration with existing components:
15 * 1. Add a command below in cvox.CommandStore.CMD_WHITELIST. Pick a
16 * programmatic name and fill in each of the relevant JSON keys.
17 * Be sure to add a msg id and define it in chromevox/messages/messages.js which
18 * describes the command. Please also add a category msg id so that the command
19 * will show up in the options page.
20 * 2. Add the command's logic to cvox.UserCommands inside of our switch-based
21 * dispatch method (doCommand_).
22 * 3. Add a key binding in chromevox/background/keymaps/classic_keymap.json and
23 * chromevox/background/keymaps/flat_keymap.json.
26 * This class is entirely static and holds a JSON structure that stores
27 * commands and their associated metadata.
29 * From this metadata, we compute relevant subsets of data such as all present
34 goog.provide('cvox.CommandStore');
36 goog.require('cvox.PlatformFilter');
40 * Returns all of the categories in the store as an array.
41 * @return {Array<string>} The collection of categories.
43 cvox.CommandStore.categories = function() {
45 for (var cmd in cvox.CommandStore.CMD_WHITELIST) {
46 var struct = cvox.CommandStore.CMD_WHITELIST[cmd];
47 if (struct.category) {
48 categorySet[struct.category] = true;
52 for (var category in categorySet) {
60 * Gets a message given a command.
61 * @param {string} command The command to query.
62 * @return {string|undefined} The message id, if any.
64 cvox.CommandStore.messageForCommand = function(command) {
65 return (cvox.CommandStore.CMD_WHITELIST[command] || {}).msgId;
70 * Gets a category given a command.
71 * @param {string} command The command to query.
72 * @return {string|undefined} The command, if any.
74 cvox.CommandStore.categoryForCommand = function(command) {
75 return (cvox.CommandStore.CMD_WHITELIST[command] || {}).category;
80 * Gets all commands for a category.
81 * @param {string} category The category to query.
82 * @return {Array<string>} The commands, if any.
84 cvox.CommandStore.commandsForCategory = function(category) {
86 for (var cmd in cvox.CommandStore.CMD_WHITELIST) {
87 var struct = cvox.CommandStore.CMD_WHITELIST[cmd];
88 if (category == struct.category) {
97 * List of commands and their properties
98 * @type {Object<{forward: (undefined|boolean),
99 * backward: (undefined|boolean),
101 * category: (undefined|string),
102 * findNext: (undefined|string),
103 * doDefault: (undefined|boolean),
104 * msgId: (undefined|string),
105 * nodeList: (undefined|string),
106 * platformFilter: (undefined|cvox.PlatformFilter),
107 * skipInput: (undefined|boolean),
108 * allowEvents: (undefined|boolean),
109 * disallowContinuation: (undefined|boolean)}>}
110 * forward: Whether this command points forward.
111 * backward: Whether this command points backward. If neither forward or
112 * backward are specified, it stays facing in the current direction.
113 * announce: Whether to call finishNavCommand and announce the current
114 * position after the command is done.
115 * findNext: The id from the map above if this command is used for
116 * finding next/previous of something.
117 * category: The message resource describing the command's category.
118 * doDefault: Whether to do the default action. This means that keys will be
119 * passed through to the usual DOM capture/bubble phases.
120 * msgId: The message resource describing the command.
121 * nodeList: The id from the map above if this command is used for
122 * showing a list of nodes.
123 * platformFilter: Specifies to which platforms this command applies. If left
124 * undefined, the command applies to all platforms.
125 * skipInput: Explicitly skips this command when text input has focus.
127 * disallowOOBE: Explicitly disallows this command when on chrome://oobe/*.
129 * allowEvents: Allows EventWatcher to continue processing events which can
131 * disallowContinuation: Disallows continuous read to proceed. Defaults to
134 cvox.CommandStore.CMD_WHITELIST = {
135 'toggleStickyMode': {announce: false,
136 msgId: 'toggle_sticky_mode',
137 'disallowOOBE': true,
138 category: 'modifier_keys'},
139 'toggleKeyPrefix': {announce: false,
142 'disallowOOBE': true,
143 category: 'modifier_keys'},
144 'passThroughMode': {announce: false,
145 msgId: 'pass_through_key_description',
146 category: 'modifier_keys'},
148 'stopSpeech': {announce: false,
149 disallowContinuation: true,
151 msgId: 'stop_speech_key',
152 category: 'controlling_speech'},
153 'toggleChromeVox': {announce: false,
154 platformFilter: cvox.PlatformFilter.WML,
155 msgId: 'toggle_chromevox_active',
156 category: 'controlling_speech'},
157 'decreaseTtsRate': {announce: false,
158 msgId: 'decrease_tts_rate',
159 category: 'controlling_speech'},
160 'increaseTtsRate': {announce: false,
161 msgId: 'increase_tts_rate',
162 category: 'controlling_speech'},
163 'decreaseTtsPitch': {announce: false,
164 msgId: 'decrease_tts_pitch',
165 category: 'controlling_speech'},
166 'increaseTtsPitch': {announce: false,
167 msgId: 'increase_tts_pitch',
168 category: 'controlling_speech'},
169 'decreaseTtsVolume': {announce: false,
170 msgId: 'decrease_tts_volume',
171 category: 'controlling_speech'},
172 'increaseTtsVolume': {announce: false,
173 msgId: 'increase_tts_volume',
174 category: 'controlling_speech'},
175 'cyclePunctuationEcho': {announce: false,
176 msgId: 'cycle_punctuation_echo',
177 category: 'controlling_speech'},
178 'cycleTypingEcho': {announce: false,
179 msgId: 'cycle_typing_echo',
180 category: 'controlling_speech'},
183 'toggleEarcons': {announce: true,
184 msgId: 'toggle_earcons',
185 category: 'controlling_speech'},
189 msgId: 'handle_tab_next',
190 disallowContinuation: true,
191 category: 'navigation'},
194 msgId: 'handle_tab_prev',
195 disallowContinuation: true,
196 category: 'navigation'},
197 'forward': {forward: true,
200 category: 'navigation'},
201 'backward': {backward: true,
204 category: 'navigation'},
205 'right': {forward: true,
208 category: 'navigation'},
209 'left': {backward: true,
212 category: 'navigation'},
213 'previousGranularity': {announce: true,
214 msgId: 'previous_granularity',
215 category: 'navigation'},
216 'nextGranularity': {announce: true,
217 msgId: 'next_granularity',
218 category: 'navigation'},
220 'previousCharacter': {backward: true,
222 msgId: 'previous_character',
224 category: 'navigation'},
225 'nextCharacter': {forward: true,
227 msgId: 'next_character',
229 category: 'navigation'},
230 'previousWord': {backward: true,
232 msgId: 'previous_word',
234 category: 'navigation'},
235 'nextWord': {forward: true,
239 category: 'navigation'},
240 'previousLine': {backward: true,
242 msgId: 'previous_line',
243 category: 'navigation'},
244 'nextLine': {forward: true,
247 category: 'navigation'},
248 'previousSentence': {backward: true,
250 msgId: 'previous_sentence',
252 category: 'navigation'},
253 'nextSentence': {forward: true,
255 msgId: 'next_sentence',
257 category: 'navigation'},
258 'previousObject': {backward: true,
260 msgId: 'previous_object',
262 category: 'navigation'},
263 'nextObject': {forward: true,
265 msgId: 'next_object',
267 category: 'navigation'},
268 'previousGroup': {backward: true,
270 msgId: 'previous_group',
272 category: 'navigation'},
273 'nextGroup': {forward: true,
277 category: 'navigation'},
279 'jumpToTop': {forward: true,
281 msgId: 'jump_to_top',
282 category: 'navigation'
284 'jumpToBottom': {backward: true,
286 msgId: 'jump_to_bottom',
287 category: 'navigation'},
288 // Intentionally uncategorized.
289 'moveToStartOfLine': {forward: true, announce: true},
290 'moveToEndOfLine': {backward: true, announce: true},
292 'readFromHere': {forward: true,
294 msgId: 'read_from_here',
295 category: 'navigation'},
297 'performDefaultAction': {disallowContinuation: true,
298 msgId: 'perform_default_action',
301 category: 'navigation'},
302 'forceClickOnCurrentItem': {announce: true,
303 disallowContinuation: true,
305 msgId: 'force_click_on_current_item',
306 category: 'navigation'},
307 'forceDoubleClickOnCurrentItem': {announce: true,
309 disallowContinuation: true},
311 'readLinkURL': {announce: false,
312 msgId: 'read_link_url',
313 category: 'information'},
314 'readCurrentTitle': {announce: false,
315 msgId: 'read_current_title',
316 category: 'information'},
317 'readCurrentURL': {announce: false,
318 msgId: 'read_current_url',
319 category: 'information'},
321 'fullyDescribe': {announce: false,
322 msgId: 'fully_describe',
323 category: 'information'},
324 'speakTimeAndDate': {announce: false,
325 msgId: 'speak_time_and_date',
326 category: 'information'},
327 'toggleSelection': {announce: true,
328 msgId: 'toggle_selection',
329 category: 'information'},
331 'toggleSearchWidget': {announce: false,
332 disallowContinuation: true,
333 msgId: 'toggle_search_widget',
334 category: 'information'},
336 'toggleKeyboardHelp': {announce: false,
337 disallowContinuation: true,
338 msgId: 'show_power_key',
339 category: 'help_commands'},
340 'help': {announce: false,
342 'disallowOOBE': true,
343 disallowContinuation: true,
344 category: 'help_commands'},
345 'contextMenu': {announce: false,
346 disallowContinuation: true},
348 'showOptionsPage': {announce: false,
349 disallowContinuation: true,
350 msgId: 'show_options_page',
351 'disallowOOBE': true,
352 category: 'help_commands'},
353 'showKbExplorerPage': {announce: false,
354 disallowContinuation: true,
355 msgId: 'show_kb_explorer_page',
356 'disallowOOBE': true,
357 category: 'help_commands'},
360 'showFormsList': {announce: false,
361 disallowContinuation: true,
362 nodeList: 'formField',
363 msgId: 'show_forms_list',
364 category: 'overview'},
365 'showHeadingsList': {announce: false, nodeList: 'heading',
366 disallowContinuation: true,
367 msgId: 'show_headings_list',
368 category: 'overview'},
369 'showLandmarksList': {announce: false, nodeList: 'landmark',
370 disallowContinuation: true,
371 msgId: 'show_landmarks_list',
372 category: 'overview'},
373 'showLinksList': {announce: false, nodeList: 'link',
374 disallowContinuation: true,
375 msgId: 'show_links_list',
376 category: 'overview'},
377 'showTablesList': {announce: false, nodeList: 'table',
378 disallowContinuation: true,
379 msgId: 'show_tables_list',
380 category: 'overview'},
382 'nextArticle': {forward: true,
383 findNext: 'article'},
385 'nextButton': {forward: true,
387 msgId: 'next_button',
388 category: 'jump_commands'},
389 'nextCheckbox': {forward: true,
390 findNext: 'checkbox',
391 msgId: 'next_checkbox',
392 category: 'jump_commands'},
393 'nextComboBox': {forward: true,
394 findNext: 'combobox',
395 msgId: 'next_combo_box',
396 category: 'jump_commands'},
397 'nextControl': {forward: true, findNext: 'control'},
398 'nextEditText': {forward: true,
399 findNext: 'editText',
400 msgId: 'next_edit_text',
401 category: 'jump_commands'},
402 'nextFormField': {forward: true,
403 findNext: 'formField',
404 msgId: 'next_form_field',
405 category: 'jump_commands'},
406 'nextGraphic': {forward: true,
408 msgId: 'next_graphic',
409 category: 'jump_commands'},
410 'nextHeading': {forward: true,
412 msgId: 'next_heading',
413 category: 'jump_commands'},
414 'nextHeading1': {forward: true,
415 findNext: 'heading1',
416 msgId: 'next_heading1',
417 category: 'jump_commands'},
418 'nextHeading2': {forward: true,
419 findNext: 'heading2',
420 msgId: 'next_heading2',
421 category: 'jump_commands'},
422 'nextHeading3': {forward: true,
423 findNext: 'heading3',
424 msgId: 'next_heading3',
425 category: 'jump_commands'},
426 'nextHeading4': {forward: true,
427 findNext: 'heading4',
428 msgId: 'next_heading4',
429 category: 'jump_commands'},
430 'nextHeading5': {forward: true,
431 findNext: 'heading5',
432 msgId: 'next_heading5',
433 category: 'jump_commands'},
434 'nextHeading6': {forward: true,
435 findNext: 'heading6',
436 msgId: 'next_heading6',
437 category: 'jump_commands'},
439 'nextLandmark': {forward: true,
440 findNext: 'landmark',
441 msgId: 'next_landmark',
442 category: 'jump_commands'},
443 'nextLink': {forward: true,
446 category: 'jump_commands'},
447 'nextList': {forward: true,
450 category: 'jump_commands'},
451 'nextListItem': {forward: true,
452 findNext: 'listItem',
453 msgId: 'next_list_item',
454 category: 'jump_commands'},
455 'nextMath': {forward: true,
458 category: 'jump_commands'},
459 'nextMedia': {forward: true,
462 category: 'jump_commands'},
463 'nextRadio': {forward: true,
466 category: 'jump_commands'},
467 'nextSection': {forward: true, findNext: 'section'},
468 'nextSlider': {forward: true, findNext: 'slider'},
469 'nextTable': {forward: true,
472 category: 'jump_commands'},
473 'nextVisitedLink': {forward: true,
474 findNext: 'visitedLink',
475 msgId: 'next_visited_link',
476 category: 'jump_commands'},
479 'previousArticle': {backward: true,
480 findNext: 'article'},
482 'previousButton': {backward: true,
484 msgId: 'previous_button',
485 category: 'jump_commands'},
486 'previousCheckbox': {backward: true,
487 findNext: 'checkbox',
488 msgId: 'previous_checkbox',
489 category: 'jump_commands'},
490 'previousComboBox': {backward: true,
491 findNext: 'combobox',
492 msgId: 'previous_combo_box',
493 category: 'jump_commands'},
494 'previousControl': {backward: true, findNext: 'control'},
495 'previousEditText': {backward: true,
496 findNext: 'editText',
497 msgId: 'previous_edit_text',
498 category: 'jump_commands'},
499 'previousFormField': {backward: true,
500 findNext: 'formField',
501 msgId: 'previous_form_field',
502 category: 'jump_commands'},
503 'previousGraphic': {backward: true,
505 msgId: 'previous_graphic',
506 category: 'jump_commands'},
507 'previousHeading': {backward: true,
509 msgId: 'previous_heading',
510 category: 'jump_commands'},
511 'previousHeading1': {backward: true,
512 findNext: 'heading1',
513 msgId: 'previous_heading1',
514 category: 'jump_commands'},
515 'previousHeading2': {backward: true,
516 findNext: 'heading2',
517 msgId: 'previous_heading2',
518 category: 'jump_commands'},
519 'previousHeading3': {backward: true,
520 findNext: 'heading3',
521 msgId: 'previous_heading3',
522 category: 'jump_commands'},
523 'previousHeading4': {backward: true,
524 findNext: 'heading4',
525 msgId: 'previous_heading4',
526 category: 'jump_commands'},
527 'previousHeading5': {backward: true,
528 findNext: 'heading5',
529 msgId: 'previous_heading5',
530 category: 'jump_commands'},
531 'previousHeading6': {backward: true,
532 findNext: 'heading6',
533 msgId: 'previous_heading6',
534 category: 'jump_commands'},
536 'previousLandmark': {backward: true,
537 findNext: 'landmark',
538 msgId: 'previous_landmark',
539 category: 'jump_commands'},
540 'previousLink': {backward: true,
542 msgId: 'previous_link',
543 category: 'jump_commands'},
544 'previousList': {backward: true,
546 msgId: 'previous_list',
547 category: 'jump_commands'},
548 'previousListItem': {backward: true,
549 findNext: 'listItem',
550 msgId: 'previous_list_item',
551 category: 'jump_commands'},
552 'previousMath': {backward: true,
554 msgId: 'previous_math',
555 category: 'jump_commands'},
556 'previousMedia': {backward: true,
558 msgId: 'previous_media',
559 category: 'jump_commands'},
560 'previousRadio': {backward: true,
562 msgId: 'previous_radio',
563 category: 'jump_commands'},
564 'previousSection': {backward: true, findNext: 'section'},
565 'previousSlider': {backward: true, findNext: 'slider'},
566 'previousTable': {backward: true,
568 msgId: 'previous_table',
569 category: 'jump_commands'},
570 'previousVisitedLink': {backward: true,
571 findNext: 'visitedLink',
572 msgId: 'previous_visited_link',
573 category: 'jump_commands'},
577 'announceHeaders': {announce: false,
578 msgId: 'announce_headers',
580 'speakTableLocation': {announce: false,
581 msgId: 'speak_table_location',
583 'goToFirstCell': {announce: true,
584 msgId: 'skip_to_beginning',
586 'goToLastCell': {announce: true,
587 msgId: 'skip_to_end',
589 'goToRowFirstCell': {announce: true,
590 msgId: 'skip_to_row_beginning',
592 'goToRowLastCell': {announce: true,
593 msgId: 'skip_to_row_end',
595 'goToColFirstCell': {announce: true,
596 msgId: 'skip_to_col_beginning',
598 'goToColLastCell': {announce: true,
599 msgId: 'skip_to_col_end',
601 // These commands are left out of the options page because they involve
602 // multiple, non-user configurable modifiers.
603 'previousRow': {backward: true, announce: true, skipInput: true},
604 'previousCol': {backward: true, announce: true, skipInput: true},
605 'nextRow': {forward: true, announce: true, skipInput: true},
606 'nextCol': {forward: true, announce: true, skipInput: true},
609 'enterShifter': {announce: true,
610 msgId: 'enter_content',
611 category: 'navigation'},
612 'exitShifter': {announce: true,
613 msgId: 'exit_content',
614 category: 'navigation'},
615 'exitShifterContent': {announce: true},
617 'openLongDesc': {announce: false,
618 msgId: 'open_long_desc',
619 category: 'information'},
621 'pauseAllMedia': {announce: false,
622 msgId: 'pause_all_media',
623 category: 'information'},
625 // Math specific commands.
626 'toggleSemantics': {announce: false,
627 msgId: 'toggle_semantics',
628 category: 'information'},
630 // Braille specific commands.
631 'routing': {announce: false,
633 msgId: 'braille_routing',
634 category: 'braille'},
635 'pan_left': {backward: true,
637 msgId: 'braille_pan_left',
638 category: 'braille'},
639 'pan_right': {forward: true,
641 msgId: 'braille_pan_right',
642 category: 'braille'},
643 'line_up': {backward: true,
645 msgId: 'braille_line_up',
646 category: 'braille'},
647 'line_down': {forward: true,
649 msgId: 'braille_line_down',
650 category: 'braille'},
651 'top': {forward: true,
653 msgId: 'braille_top',
654 category: 'braille'},
655 'bottom': {backward: true,
657 msgId: 'braille_bottom',
658 category: 'braille'},
660 // Developer commands.
661 'enableConsoleTts': {announce: false,
662 msgId: 'enable_tts_log',
663 category: 'developer'},
664 'toggleBrailleCaptions': {announce: false,
665 msgId: 'braille_captions',
666 category: 'developer'},
668 'startHistoryRecording': {announce: false},
669 'stopHistoryRecording': {announce: false},
670 'autorunner': {announce: false},
672 'debug': {announce: false},
674 'nop': {announce: false}
679 * List of find next commands and their associated data.
680 * @type {Object<{predicate: string,
681 * forwardError: string,
682 * backwardError: string}>}
683 * predicate: The name of the predicate. This must be defined in DomPredicates.
684 * forwardError: The message id of the error string when moving forward.
685 * backwardError: The message id of the error string when moving backward.
687 cvox.CommandStore.NODE_INFO_MAP = {
688 'checkbox': {predicate: 'checkboxPredicate',
689 forwardError: 'no_next_checkbox',
690 backwardError: 'no_previous_checkbox',
691 typeMsg: 'aria_role_checkbox'},
692 'radio': {predicate: 'radioPredicate',
693 forwardError: 'no_next_radio_button',
694 backwardError: 'no_previous_radio_button',
695 typeMsg: 'aria_role_radio'},
696 'slider': {predicate: 'sliderPredicate',
697 forwardError: 'no_next_slider',
698 backwardError: 'no_previous_slider',
699 typeMsg: 'aria_role_slider'},
700 'graphic': {predicate: 'graphicPredicate',
701 forwardError: 'no_next_graphic',
702 backwardError: 'no_previous_graphic',
704 'article': {predicate: 'articlePredicate',
705 forwardError: 'no_next_ARTICLE',
706 backwardError: 'no_previous_ARTICLE',
707 typeMsg: 'TAG_ARTICLE'},
708 'button': {predicate: 'buttonPredicate',
709 forwardError: 'no_next_button',
710 backwardError: 'no_previous_button',
711 typeMsg: 'aria_role_button'},
712 'combobox': {predicate: 'comboBoxPredicate',
713 forwardError: 'no_next_combo_box',
714 backwardError: 'no_previous_combo_box',
715 typeMsg: 'aria_role_combobox'},
716 'editText': {predicate: 'editTextPredicate',
717 forwardError: 'no_next_edit_text',
718 backwardError: 'no_previous_edit_text',
719 typeMsg: 'input_type_text'},
720 'heading': {predicate: 'headingPredicate',
721 forwardError: 'no_next_heading',
722 backwardError: 'no_previous_heading',
723 typeMsg: 'aria_role_heading'},
724 'heading1': {predicate: 'heading1Predicate',
725 forwardError: 'no_next_heading_1',
726 backwardError: 'no_previous_heading_1'},
727 'heading2': {predicate: 'heading2Predicate',
728 forwardError: 'no_next_heading_2',
729 backwardError: 'no_previous_heading_2'},
730 'heading3': {predicate: 'heading3Predicate',
731 forwardError: 'no_next_heading_3',
732 backwardError: 'no_previous_heading_3'},
733 'heading4': {predicate: 'heading4Predicate',
734 forwardError: 'no_next_heading_4',
735 backwardError: 'no_previous_heading_4'},
736 'heading5': {predicate: 'heading5Predicate',
737 forwardError: 'no_next_heading_5',
738 backwardError: 'no_previous_heading_5'},
739 'heading6': {predicate: 'heading6Predicate',
740 forwardError: 'no_next_heading_6',
741 backwardError: 'no_previous_heading_6'},
743 'link': {predicate: 'linkPredicate',
744 forwardError: 'no_next_link',
745 backwardError: 'no_previous_link',
746 typeMsg: 'aria_role_link'},
747 'table': {predicate: 'tablePredicate',
748 forwardError: 'no_next_table',
749 backwardError: 'no_previous_table',
750 typeMsg: 'table_strategy'},
751 'visitedLink': {predicate: 'visitedLinkPredicate',
752 forwardError: 'no_next_visited_link',
753 backwardError: 'no_previous_visited_link',
754 typeMsg: 'tag_link'},
755 'list': {predicate: 'listPredicate',
756 forwardError: 'no_next_list',
757 backwardError: 'no_previous_list',
758 typeMsg: 'aria_role_list'},
759 'listItem': {predicate: 'listItemPredicate',
760 forwardError: 'no_next_list_item',
761 backwardError: 'no_previous_list_item',
762 typeMsg: 'aria_role_listitem'},
763 'formField': {predicate: 'formFieldPredicate',
764 forwardError: 'no_next_form_field',
765 backwardError: 'no_previous_form_field',
766 typeMsg: 'aria_role_form'},
767 'landmark': {predicate: 'landmarkPredicate',
768 forwardError: 'no_next_landmark',
769 backwardError: 'no_previous_landmark',
770 typeMsg: 'role_landmark'},
771 'math': {predicate: 'mathPredicate',
772 forwardError: 'no_next_math',
773 backwardError: 'no_previous_math',
774 typeMsg: 'math_expr'},
775 'media': {predicate: 'mediaPredicate',
776 forwardError: 'no_next_media_widget',
777 backwardError: 'no_previous_media_widget'},
778 'section': {predicate: 'sectionPredicate',
779 forwardError: 'no_next_section',
780 backwardError: 'no_previous_section'},
781 'control': {predicate: 'controlPredicate',
782 forwardError: 'no_next_control',
783 backwardError: 'no_previous_control'}