OLD | NEW |
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 The entry point for all ChromeVox2 related code for the | 6 * @fileoverview The entry point for all ChromeVox2 related code for the |
7 * background page. | 7 * background page. |
8 */ | 8 */ |
9 | 9 |
10 goog.provide('Background'); | 10 goog.provide('Background'); |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 /** @type {!cvox.AbstractEarcons} @private */ | 86 /** @type {!cvox.AbstractEarcons} @private */ |
87 this.classicEarcons_ = cvox.ChromeVox.earcons || new cvox.ClassicEarcons(); | 87 this.classicEarcons_ = cvox.ChromeVox.earcons || new cvox.ClassicEarcons(); |
88 | 88 |
89 /** @type {!cvox.AbstractEarcons} @private */ | 89 /** @type {!cvox.AbstractEarcons} @private */ |
90 this.nextEarcons_ = new NextEarcons(); | 90 this.nextEarcons_ = new NextEarcons(); |
91 | 91 |
92 // Turn cvox.ChromeVox.earcons into a getter that returns either the | 92 // Turn cvox.ChromeVox.earcons into a getter that returns either the |
93 // Next earcons or the Classic earcons depending on the current mode. | 93 // Next earcons or the Classic earcons depending on the current mode. |
94 Object.defineProperty(cvox.ChromeVox, 'earcons', { | 94 Object.defineProperty(cvox.ChromeVox, 'earcons', { |
95 get: (function() { | 95 get: (function() { |
96 if (this.mode === ChromeVoxMode.FORCE_NEXT || | 96 if (this.mode === ChromeVoxMode.FORCE_NEXT || |
97 this.mode === ChromeVoxMode.NEXT) { | 97 this.mode === ChromeVoxMode.NEXT) { |
98 return this.nextEarcons_; | 98 return this.nextEarcons_; |
99 } else { | 99 } else { |
100 return this.classicEarcons_; | 100 return this.classicEarcons_; |
101 } | 101 } |
102 }).bind(this) | 102 }).bind(this) |
103 }); | 103 }); |
104 | 104 |
105 if (cvox.ChromeVox.isChromeOS) { | 105 if (cvox.ChromeVox.isChromeOS) { |
106 Object.defineProperty(cvox.ChromeVox, 'modKeyStr', { | 106 Object.defineProperty(cvox.ChromeVox, 'modKeyStr', { |
107 get: function() { | 107 get: function() { |
108 return (this.mode == ChromeVoxMode.CLASSIC || | 108 return (this.mode == ChromeVoxMode.CLASSIC || |
109 this.mode == ChromeVoxMode.CLASSIC_COMPAT) ? | 109 this.mode == ChromeVoxMode.CLASSIC_COMPAT) ? |
110 'Search+Shift' : 'Search'; | 110 'Search+Shift' : |
| 111 'Search'; |
111 }.bind(this) | 112 }.bind(this) |
112 }); | 113 }); |
113 | 114 |
114 Object.defineProperty(cvox.ChromeVox, 'typingEcho', { | 115 Object.defineProperty(cvox.ChromeVox, 'typingEcho', { |
115 get: function() { | 116 get: function() { |
116 return parseInt(localStorage['typingEcho'], 10); | 117 return parseInt(localStorage['typingEcho'], 10); |
117 }.bind(this), | 118 }.bind(this), |
118 set: function(v) { | 119 set: function(v) { |
119 localStorage['typingEcho'] = v; | 120 localStorage['typingEcho'] = v; |
120 }.bind(this) | 121 }.bind(this) |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 this.focusRecoveryMap_ = new WeakMap(); | 168 this.focusRecoveryMap_ = new WeakMap(); |
168 | 169 |
169 chrome.automation.getDesktop(function(desktop) { | 170 chrome.automation.getDesktop(function(desktop) { |
170 /** @type {string} */ | 171 /** @type {string} */ |
171 this.chromeChannel_ = desktop.chromeChannel; | 172 this.chromeChannel_ = desktop.chromeChannel; |
172 }.bind(this)); | 173 }.bind(this)); |
173 | 174 |
174 // Record a metric with the mode we're in on startup. | 175 // Record a metric with the mode we're in on startup. |
175 var useNext = localStorage['useClassic'] != 'true'; | 176 var useNext = localStorage['useClassic'] != 'true'; |
176 chrome.metricsPrivate.recordValue( | 177 chrome.metricsPrivate.recordValue( |
177 { metricName: 'Accessibility.CrosChromeVoxNext', | 178 { |
| 179 metricName: 'Accessibility.CrosChromeVoxNext', |
178 type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR, | 180 type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR, |
179 min: 1, // According to histogram.h, this should be 1 for enums. | 181 min: 1, // According to histogram.h, this should be 1 for enums. |
180 max: 2, // Maximum should be exclusive. | 182 max: 2, // Maximum should be exclusive. |
181 buckets: 3 }, // Number of buckets: 0, 1 and overflowing 2. | 183 buckets: 3 |
| 184 }, // Number of buckets: 0, 1 and overflowing 2. |
182 useNext ? 1 : 0); | 185 useNext ? 1 : 0); |
183 }; | 186 }; |
184 | 187 |
185 /** | 188 /** |
186 * Map from gesture names (AXGesture defined in ui/accessibility/ax_enums.idl) | 189 * Map from gesture names (AXGesture defined in ui/accessibility/ax_enums.idl) |
187 * to commands when in Classic mode. | 190 * to commands when in Classic mode. |
188 * @type {Object<string, string>} | 191 * @type {Object<string, string>} |
189 * @const | 192 * @const |
190 */ | 193 */ |
191 Background.GESTURE_CLASSIC_COMMAND_MAP = { | 194 Background.GESTURE_CLASSIC_COMMAND_MAP = { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
238 }); | 241 }); |
239 } else { | 242 } else { |
240 target = this.getCurrentRange().start.node; | 243 target = this.getCurrentRange().start.node; |
241 } | 244 } |
242 | 245 |
243 if (!target) | 246 if (!target) |
244 return useNext ? ChromeVoxMode.FORCE_NEXT : ChromeVoxMode.CLASSIC; | 247 return useNext ? ChromeVoxMode.FORCE_NEXT : ChromeVoxMode.CLASSIC; |
245 | 248 |
246 // Closure complains, but clearly, |target| is not null. | 249 // Closure complains, but clearly, |target| is not null. |
247 var topLevelRoot = | 250 var topLevelRoot = |
248 AutomationUtil.getTopLevelRoot(/** @type {!AutomationNode} */(target)); | 251 AutomationUtil.getTopLevelRoot(/** @type {!AutomationNode} */ (target)); |
249 if (!topLevelRoot) | 252 if (!topLevelRoot) |
250 return useNext ? ChromeVoxMode.FORCE_NEXT : | 253 return useNext ? ChromeVoxMode.FORCE_NEXT : ChromeVoxMode.CLASSIC_COMPAT; |
251 ChromeVoxMode.CLASSIC_COMPAT; | |
252 | 254 |
253 var docUrl = topLevelRoot.docUrl || ''; | 255 var docUrl = topLevelRoot.docUrl || ''; |
254 var nextSite = this.isWhitelistedForNext_(docUrl); | 256 var nextSite = this.isWhitelistedForNext_(docUrl); |
255 var classicCompat = | 257 var classicCompat = this.isWhitelistedForClassicCompat_(docUrl); |
256 this.isWhitelistedForClassicCompat_(docUrl); | |
257 if (classicCompat && !useNext) | 258 if (classicCompat && !useNext) |
258 return ChromeVoxMode.CLASSIC_COMPAT; | 259 return ChromeVoxMode.CLASSIC_COMPAT; |
259 else if (nextSite) | 260 else if (nextSite) |
260 return ChromeVoxMode.NEXT; | 261 return ChromeVoxMode.NEXT; |
261 else if (!useNext) | 262 else if (!useNext) |
262 return ChromeVoxMode.CLASSIC; | 263 return ChromeVoxMode.CLASSIC; |
263 else | 264 else |
264 return ChromeVoxMode.FORCE_NEXT; | 265 return ChromeVoxMode.FORCE_NEXT; |
265 }, | 266 }, |
266 | 267 |
(...skipping 16 matching lines...) Expand all Loading... |
283 | 284 |
284 // Classic modes do not use the new focus highlight. | 285 // Classic modes do not use the new focus highlight. |
285 if (newMode == ChromeVoxMode.CLASSIC) | 286 if (newMode == ChromeVoxMode.CLASSIC) |
286 chrome.accessibilityPrivate.setFocusRing([]); | 287 chrome.accessibilityPrivate.setFocusRing([]); |
287 | 288 |
288 // Switch on/off content scripts. | 289 // Switch on/off content scripts. |
289 // note that |this.currentRange_| can *change* because the request is | 290 // note that |this.currentRange_| can *change* because the request is |
290 // async. Save it to ensure we're looking at the currentRange at this moment | 291 // async. Save it to ensure we're looking at the currentRange at this moment |
291 // in time. | 292 // in time. |
292 var cur = this.currentRange_; | 293 var cur = this.currentRange_; |
293 chrome.tabs.query({active: true, | 294 chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) { |
294 lastFocusedWindow: true}, function(tabs) { | |
295 if (newMode == ChromeVoxMode.CLASSIC) { | 295 if (newMode == ChromeVoxMode.CLASSIC) { |
296 // Generally, we don't want to inject classic content scripts as it is | 296 // Generally, we don't want to inject classic content scripts as it is |
297 // done by the extension system at document load. The exception is when | 297 // done by the extension system at document load. The exception is when |
298 // we toggle classic on manually as part of a user command. | 298 // we toggle classic on manually as part of a user command. |
299 if (oldMode == ChromeVoxMode.FORCE_NEXT) { | 299 if (oldMode == ChromeVoxMode.FORCE_NEXT) { |
300 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); | 300 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); |
301 } | 301 } |
302 } else if (newMode === ChromeVoxMode.FORCE_NEXT) { | 302 } else if (newMode === ChromeVoxMode.FORCE_NEXT) { |
303 this.disableClassicChromeVox_(); | 303 this.disableClassicChromeVox_(); |
304 } else { | 304 } else { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
355 chrome.metricsPrivate.recordUserAction( | 355 chrome.metricsPrivate.recordUserAction( |
356 'Accessibility.ChromeVox.ToggleNextOff'); | 356 'Accessibility.ChromeVox.ToggleNextOff'); |
357 } | 357 } |
358 | 358 |
359 localStorage['useClassic'] = !useNext; | 359 localStorage['useClassic'] = !useNext; |
360 if (useNext) | 360 if (useNext) |
361 this.setCurrentRangeToFocus_(); | 361 this.setCurrentRangeToFocus_(); |
362 else | 362 else |
363 this.setCurrentRange(null); | 363 this.setCurrentRange(null); |
364 | 364 |
365 var announce = Msgs.getMsg(useNext ? | 365 var announce = |
366 'switch_to_next' : 'switch_to_classic'); | 366 Msgs.getMsg(useNext ? 'switch_to_next' : 'switch_to_classic'); |
367 cvox.ChromeVox.tts.speak( | 367 cvox.ChromeVox.tts.speak( |
368 announce, cvox.QueueMode.FLUSH, {doNotInterrupt: true}); | 368 announce, cvox.QueueMode.FLUSH, {doNotInterrupt: true}); |
369 | 369 |
370 // If the new mode is Classic, return false now so we don't announce | 370 // If the new mode is Classic, return false now so we don't announce |
371 // anything more. | 371 // anything more. |
372 return useNext; | 372 return useNext; |
373 }, | 373 }, |
374 | 374 |
375 /** | 375 /** |
376 * @override | 376 * @override |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
432 opt_focus = opt_focus === undefined ? true : opt_focus; | 432 opt_focus = opt_focus === undefined ? true : opt_focus; |
433 opt_speechProps = opt_speechProps || {}; | 433 opt_speechProps = opt_speechProps || {}; |
434 var prevRange = this.currentRange_; | 434 var prevRange = this.currentRange_; |
435 if (opt_focus) | 435 if (opt_focus) |
436 this.setFocusToRange_(range, prevRange); | 436 this.setFocusToRange_(range, prevRange); |
437 | 437 |
438 this.setCurrentRange(range); | 438 this.setCurrentRange(range); |
439 | 439 |
440 var o = new Output(); | 440 var o = new Output(); |
441 var selectedRange; | 441 var selectedRange; |
442 if (this.pageSel_ && | 442 if (this.pageSel_ && this.pageSel_.isValid() && range.isValid()) { |
443 this.pageSel_.isValid() && | |
444 range.isValid()) { | |
445 // Compute the direction of the endpoints of each range. | 443 // Compute the direction of the endpoints of each range. |
446 | 444 |
447 // Casts are ok because isValid checks node start and end nodes are | 445 // Casts are ok because isValid checks node start and end nodes are |
448 // non-null; Closure just doesn't eval enough to see it. | 446 // non-null; Closure just doesn't eval enough to see it. |
449 var startDir = | 447 var startDir = AutomationUtil.getDirection( |
450 AutomationUtil.getDirection(this.pageSel_.start.node, | 448 this.pageSel_.start.node, |
451 /** @type {!AutomationNode} */ (range.start.node)); | 449 /** @type {!AutomationNode} */ (range.start.node)); |
452 var endDir = | 450 var endDir = AutomationUtil.getDirection( |
453 AutomationUtil.getDirection(this.pageSel_.end.node, | 451 this.pageSel_.end.node, |
454 /** @type {!AutomationNode} */ (range.end.node)); | 452 /** @type {!AutomationNode} */ (range.end.node)); |
455 | 453 |
456 // Selection across roots isn't supported. | 454 // Selection across roots isn't supported. |
457 var pageRootStart = this.pageSel_.start.node.root; | 455 var pageRootStart = this.pageSel_.start.node.root; |
458 var pageRootEnd = this.pageSel_.end.node.root; | 456 var pageRootEnd = this.pageSel_.end.node.root; |
459 var curRootStart = range.start.node.root; | 457 var curRootStart = range.start.node.root; |
460 var curRootEnd = range.end.node.root; | 458 var curRootEnd = range.end.node.root; |
461 | 459 |
462 // Disallow crossing over the start of the page selection and roots. | 460 // Disallow crossing over the start of the page selection and roots. |
463 if (startDir == Dir.BACKWARD || | 461 if (startDir == Dir.BACKWARD || pageRootStart != pageRootEnd || |
464 pageRootStart != pageRootEnd || | 462 pageRootStart != curRootStart || pageRootEnd != curRootEnd) { |
465 pageRootStart != curRootStart || | |
466 pageRootEnd != curRootEnd) { | |
467 o.format('@end_selection'); | 463 o.format('@end_selection'); |
468 this.pageSel_ = null; | 464 this.pageSel_ = null; |
469 } else { | 465 } else { |
470 // Expand or shrink requires different feedback. | 466 // Expand or shrink requires different feedback. |
471 var msg; | 467 var msg; |
472 if (endDir == Dir.FORWARD && | 468 if (endDir == Dir.FORWARD && |
473 (this.pageSel_.end.node != range.end.node || | 469 (this.pageSel_.end.node != range.end.node || |
474 this.pageSel_.end.index <= range.end.index)) { | 470 this.pageSel_.end.index <= range.end.index)) { |
475 msg = '@selected'; | 471 msg = '@selected'; |
476 } else { | 472 } else { |
477 msg = '@unselected'; | 473 msg = '@unselected'; |
478 selectedRange = prevRange; | 474 selectedRange = prevRange; |
479 } | 475 } |
480 this.pageSel_ = new cursors.Range( | 476 this.pageSel_ = new cursors.Range(this.pageSel_.start, range.end); |
481 this.pageSel_.start, | |
482 range.end | |
483 ); | |
484 if (this.pageSel_) | 477 if (this.pageSel_) |
485 this.pageSel_.select(); | 478 this.pageSel_.select(); |
486 } | 479 } |
487 } else { | 480 } else { |
488 // Ensure we don't select the editable when we first encounter it. | 481 // Ensure we don't select the editable when we first encounter it. |
489 var lca = null; | 482 var lca = null; |
490 if (range.start.node && prevRange.start.node) { | 483 if (range.start.node && prevRange.start.node) { |
491 lca = AutomationUtil.getLeastCommonAncestor(prevRange.start.node, | 484 lca = AutomationUtil.getLeastCommonAncestor( |
492 range.start.node); | 485 prevRange.start.node, range.start.node); |
493 } | 486 } |
494 if (!lca || lca.state[StateType.EDITABLE] || | 487 if (!lca || lca.state[StateType.EDITABLE] || |
495 !range.start.node.state[StateType.EDITABLE]) | 488 !range.start.node.state[StateType.EDITABLE]) |
496 range.select(); | 489 range.select(); |
497 } | 490 } |
498 | 491 |
499 o.withRichSpeechAndBraille( | 492 o.withRichSpeechAndBraille( |
500 selectedRange || range, prevRange, Output.EventType.NAVIGATE) | 493 selectedRange || range, prevRange, Output.EventType.NAVIGATE) |
501 .withQueueMode(cvox.QueueMode.FLUSH); | 494 .withQueueMode(cvox.QueueMode.FLUSH); |
502 | 495 |
503 if (msg) | 496 if (msg) |
504 o.format(msg); | 497 o.format(msg); |
505 | 498 |
506 for (var prop in opt_speechProps) | 499 for (var prop in opt_speechProps) |
507 o.format('!' + prop); | 500 o.format('!' + prop); |
508 | 501 |
509 o.go(); | 502 o.go(); |
510 }, | 503 }, |
511 | 504 |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
566 return true; | 559 return true; |
567 }, | 560 }, |
568 | 561 |
569 /** | 562 /** |
570 * Returns true if the url should have Classic running. | 563 * Returns true if the url should have Classic running. |
571 * @return {boolean} | 564 * @return {boolean} |
572 * @private | 565 * @private |
573 */ | 566 */ |
574 shouldEnableClassicForUrl_: function(url) { | 567 shouldEnableClassicForUrl_: function(url) { |
575 return this.mode != ChromeVoxMode.FORCE_NEXT && | 568 return this.mode != ChromeVoxMode.FORCE_NEXT && |
576 !this.isBlacklistedForClassic_(url) && | 569 !this.isBlacklistedForClassic_(url) && !this.isWhitelistedForNext_(url); |
577 !this.isWhitelistedForNext_(url); | |
578 }, | 570 }, |
579 | 571 |
580 /** | 572 /** |
581 * Compat mode is on if any of the following are true: | 573 * Compat mode is on if any of the following are true: |
582 * 1. a url is blacklisted for Classic. | 574 * 1. a url is blacklisted for Classic. |
583 * 2. the current range is not within web content. | 575 * 2. the current range is not within web content. |
584 * @param {string} url | 576 * @param {string} url |
585 * @return {boolean} | 577 * @return {boolean} |
586 */ | 578 */ |
587 isWhitelistedForClassicCompat_: function(url) { | 579 isWhitelistedForClassicCompat_: function(url) { |
588 return (this.isBlacklistedForClassic_(url) || (this.getCurrentRange() && | 580 return (this.isBlacklistedForClassic_(url) || |
589 !this.getCurrentRange().isWebRange() && | 581 (this.getCurrentRange() && !this.getCurrentRange().isWebRange() && |
590 this.getCurrentRange().start.node.state[StateType.FOCUSED])) || false; | 582 this.getCurrentRange().start.node.state[StateType.FOCUSED])) || |
| 583 false; |
591 }, | 584 }, |
592 | 585 |
593 /** | 586 /** |
594 * @param {string} url | 587 * @param {string} url |
595 * @return {boolean} | 588 * @return {boolean} |
596 * @private | 589 * @private |
597 */ | 590 */ |
598 isBlacklistedForClassic_: function(url) { | 591 isBlacklistedForClassic_: function(url) { |
599 if (this.classicBlacklistRegExp_.test(url)) | 592 if (this.classicBlacklistRegExp_.test(url)) |
600 return true; | 593 return true; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
659 actionNode = actionNode.parent; | 652 actionNode = actionNode.parent; |
660 actionNode.doDefault(); | 653 actionNode.doDefault(); |
661 | 654 |
662 if (!selectionSpan) | 655 if (!selectionSpan) |
663 selectionSpan = actionNodeSpan; | 656 selectionSpan = actionNodeSpan; |
664 | 657 |
665 var start = text.getSpanStart(selectionSpan); | 658 var start = text.getSpanStart(selectionSpan); |
666 var targetPosition = position - start + offset; | 659 var targetPosition = position - start + offset; |
667 | 660 |
668 if (actionNode.state.richlyEditable) { | 661 if (actionNode.state.richlyEditable) { |
669 chrome.automation.setDocumentSelection( | 662 chrome.automation.setDocumentSelection({ |
670 { anchorObject: actionNode, | 663 anchorObject: actionNode, |
671 anchorOffset: targetPosition, | 664 anchorOffset: targetPosition, |
672 focusObject: actionNode, | 665 focusObject: actionNode, |
673 focusOffset: targetPosition }); | 666 focusOffset: targetPosition |
| 667 }); |
674 } else { | 668 } else { |
675 actionNode.setSelection(targetPosition, targetPosition); | 669 actionNode.setSelection(targetPosition, targetPosition); |
676 } | 670 } |
677 }, | 671 }, |
678 | 672 |
679 /** | 673 /** |
680 * @param {Object} msg A message sent from a content script. | 674 * @param {Object} msg A message sent from a content script. |
681 * @param {Port} port | 675 * @param {Port} port |
682 * @private | 676 * @private |
683 */ | 677 */ |
684 onMessage_: function(msg, port) { | 678 onMessage_: function(msg, port) { |
685 var target = msg['target']; | 679 var target = msg['target']; |
686 var action = msg['action']; | 680 var action = msg['action']; |
687 | 681 |
688 switch (target) { | 682 switch (target) { |
689 case 'next': | 683 case 'next': |
690 if (action == 'getIsClassicEnabled') { | 684 if (action == 'getIsClassicEnabled') { |
691 var url = msg['url']; | 685 var url = msg['url']; |
692 var isClassicEnabled = this.shouldEnableClassicForUrl_(url); | 686 var isClassicEnabled = this.shouldEnableClassicForUrl_(url); |
693 port.postMessage({ | 687 port.postMessage( |
694 target: 'next', | 688 {target: 'next', isClassicEnabled: isClassicEnabled}); |
695 isClassicEnabled: isClassicEnabled | |
696 }); | |
697 } else if (action == 'enableClassicCompatForUrl') { | 689 } else if (action == 'enableClassicCompatForUrl') { |
698 var url = msg['url']; | 690 var url = msg['url']; |
699 this.classicBlacklist_.add(url); | 691 this.classicBlacklist_.add(url); |
700 if (this.currentRange_ && this.currentRange_.start.node) | 692 if (this.currentRange_ && this.currentRange_.start.node) |
701 this.setCurrentRange(this.currentRange_); | 693 this.setCurrentRange(this.currentRange_); |
702 } else if (action == 'onCommand') { | 694 } else if (action == 'onCommand') { |
703 CommandHandler.onCommand(msg['command']); | 695 CommandHandler.onCommand(msg['command']); |
704 } else if (action == 'flushNextUtterance') { | 696 } else if (action == 'flushNextUtterance') { |
705 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH); | 697 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH); |
706 } | 698 } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
746 * @param {string} gesture The gesture to handle, based on the AXGesture enum | 738 * @param {string} gesture The gesture to handle, based on the AXGesture enum |
747 * defined in ui/accessibility/ax_enums.idl | 739 * defined in ui/accessibility/ax_enums.idl |
748 * @return {boolean} True if this gesture was handled. | 740 * @return {boolean} True if this gesture was handled. |
749 * @private | 741 * @private |
750 */ | 742 */ |
751 handleClassicGesture_: function(gesture) { | 743 handleClassicGesture_: function(gesture) { |
752 var command = Background.GESTURE_CLASSIC_COMMAND_MAP[gesture]; | 744 var command = Background.GESTURE_CLASSIC_COMMAND_MAP[gesture]; |
753 if (!command) | 745 if (!command) |
754 return false; | 746 return false; |
755 | 747 |
756 var msg = { | 748 var msg = {'message': 'USER_COMMAND', 'command': command}; |
757 'message': 'USER_COMMAND', | |
758 'command': command | |
759 }; | |
760 cvox.ExtensionBridge.send(msg); | 749 cvox.ExtensionBridge.send(msg); |
761 return true; | 750 return true; |
762 }, | 751 }, |
763 | 752 |
764 /** @private */ | 753 /** @private */ |
765 setCurrentRangeToFocus_: function() { | 754 setCurrentRangeToFocus_: function() { |
766 chrome.automation.getFocus(function(focus) { | 755 chrome.automation.getFocus(function(focus) { |
767 if (focus) | 756 if (focus) |
768 this.setCurrentRange(cursors.Range.fromNode(focus)); | 757 this.setCurrentRange(cursors.Range.fromNode(focus)); |
769 else | 758 else |
770 this.setCurrentRange(null); | 759 this.setCurrentRange(null); |
771 }.bind(this)); | 760 }.bind(this)); |
772 }, | 761 }, |
773 | 762 |
774 /** | 763 /** |
775 * @param {!cursors.Range} range | 764 * @param {!cursors.Range} range |
776 * @param {cursors.Range} prevRange | 765 * @param {cursors.Range} prevRange |
777 * @private | 766 * @private |
778 */ | 767 */ |
779 setFocusToRange_: function(range, prevRange) { | 768 setFocusToRange_: function(range, prevRange) { |
780 var start = range.start.node; | 769 var start = range.start.node; |
781 var end = range.end.node; | 770 var end = range.end.node; |
782 | 771 |
783 // First, see if we've crossed a root. Remove once webview handles focus | 772 // First, see if we've crossed a root. Remove once webview handles focus |
784 // correctly. | 773 // correctly. |
785 if (prevRange && prevRange.start.node && start) { | 774 if (prevRange && prevRange.start.node && start) { |
786 var entered = AutomationUtil.getUniqueAncestors( | 775 var entered = |
787 prevRange.start.node, start); | 776 AutomationUtil.getUniqueAncestors(prevRange.start.node, start); |
788 var embeddedObject = entered.find(function(f) { | 777 var embeddedObject = entered.find(function(f) { |
789 return f.role == RoleType.EMBEDDED_OBJECT; }); | 778 return f.role == RoleType.EMBEDDED_OBJECT; |
| 779 }); |
790 if (embeddedObject && !embeddedObject.state[StateType.FOCUSED]) | 780 if (embeddedObject && !embeddedObject.state[StateType.FOCUSED]) |
791 embeddedObject.focus(); | 781 embeddedObject.focus(); |
792 } | 782 } |
793 | 783 |
794 if (start.state[StateType.FOCUSED] || end.state[StateType.FOCUSED]) | 784 if (start.state[StateType.FOCUSED] || end.state[StateType.FOCUSED]) |
795 return; | 785 return; |
796 | 786 |
797 var isFocusableLinkOrControl = function(node) { | 787 var isFocusableLinkOrControl = function(node) { |
798 return node.state[StateType.FOCUSABLE] && | 788 return node.state[StateType.FOCUSABLE] && |
799 AutomationPredicate.linkOrControl(node); | 789 AutomationPredicate.linkOrControl(node); |
800 }; | 790 }; |
801 | 791 |
802 // Next, try to focus the start or end node. | 792 // Next, try to focus the start or end node. |
803 if (!AutomationPredicate.structuralContainer(start) && | 793 if (!AutomationPredicate.structuralContainer(start) && |
804 start.state[StateType.FOCUSABLE]) { | 794 start.state[StateType.FOCUSABLE]) { |
805 if (!start.state[StateType.FOCUSED]) | 795 if (!start.state[StateType.FOCUSED]) |
806 start.focus(); | 796 start.focus(); |
807 return; | 797 return; |
808 } else if (!AutomationPredicate.structuralContainer(end) && | 798 } else if ( |
| 799 !AutomationPredicate.structuralContainer(end) && |
809 end.state[StateType.FOCUSABLE]) { | 800 end.state[StateType.FOCUSABLE]) { |
810 if (!end.state[StateType.FOCUSED]) | 801 if (!end.state[StateType.FOCUSED]) |
811 end.focus(); | 802 end.focus(); |
812 return; | 803 return; |
813 } | 804 } |
814 | 805 |
815 // If a common ancestor of |start| and |end| is a link, focus that. | 806 // If a common ancestor of |start| and |end| is a link, focus that. |
816 var ancestor = AutomationUtil.getLeastCommonAncestor(start, end); | 807 var ancestor = AutomationUtil.getLeastCommonAncestor(start, end); |
817 while (ancestor && ancestor.root == start.root) { | 808 while (ancestor && ancestor.root == start.root) { |
818 if (isFocusableLinkOrControl(ancestor)) { | 809 if (isFocusableLinkOrControl(ancestor)) { |
(...skipping 13 matching lines...) Expand all Loading... |
832 }; | 823 }; |
833 | 824 |
834 /** | 825 /** |
835 * Converts a list of globs, as used in the extension manifest, to a regular | 826 * Converts a list of globs, as used in the extension manifest, to a regular |
836 * expression that matches if and only if any of the globs in the list matches. | 827 * expression that matches if and only if any of the globs in the list matches. |
837 * @param {!Array<string>} globs | 828 * @param {!Array<string>} globs |
838 * @return {!RegExp} | 829 * @return {!RegExp} |
839 * @private | 830 * @private |
840 */ | 831 */ |
841 Background.globsToRegExp_ = function(globs) { | 832 Background.globsToRegExp_ = function(globs) { |
842 return new RegExp('^(' + globs.map(function(glob) { | 833 return new RegExp( |
843 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') | 834 '^(' + |
844 .replace(/\*/g, '.*') | 835 globs |
845 .replace(/\?/g, '.'); | 836 .map(function(glob) { |
846 }).join('|') + ')$'); | 837 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') |
| 838 .replace(/\*/g, '.*') |
| 839 .replace(/\?/g, '.'); |
| 840 }) |
| 841 .join('|') + |
| 842 ')$'); |
847 }; | 843 }; |
848 | 844 |
849 new Background(); | 845 new Background(); |
850 | 846 |
851 }); // goog.scope | 847 }); // goog.scope |
OLD | NEW |