| 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 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 671 */ | 664 */ |
| 672 onMessage_: function(msg, port) { | 665 onMessage_: function(msg, port) { |
| 673 var target = msg['target']; | 666 var target = msg['target']; |
| 674 var action = msg['action']; | 667 var action = msg['action']; |
| 675 | 668 |
| 676 switch (target) { | 669 switch (target) { |
| 677 case 'next': | 670 case 'next': |
| 678 if (action == 'getIsClassicEnabled') { | 671 if (action == 'getIsClassicEnabled') { |
| 679 var url = msg['url']; | 672 var url = msg['url']; |
| 680 var isClassicEnabled = this.shouldEnableClassicForUrl_(url); | 673 var isClassicEnabled = this.shouldEnableClassicForUrl_(url); |
| 681 port.postMessage({ | 674 port.postMessage( |
| 682 target: 'next', | 675 {target: 'next', isClassicEnabled: isClassicEnabled}); |
| 683 isClassicEnabled: isClassicEnabled | |
| 684 }); | |
| 685 } else if (action == 'enableClassicCompatForUrl') { | 676 } else if (action == 'enableClassicCompatForUrl') { |
| 686 var url = msg['url']; | 677 var url = msg['url']; |
| 687 this.classicBlacklist_.add(url); | 678 this.classicBlacklist_.add(url); |
| 688 if (this.currentRange_ && this.currentRange_.start.node) | 679 if (this.currentRange_ && this.currentRange_.start.node) |
| 689 this.setCurrentRange(this.currentRange_); | 680 this.setCurrentRange(this.currentRange_); |
| 690 } else if (action == 'onCommand') { | 681 } else if (action == 'onCommand') { |
| 691 CommandHandler.onCommand(msg['command']); | 682 CommandHandler.onCommand(msg['command']); |
| 692 } else if (action == 'flushNextUtterance') { | 683 } else if (action == 'flushNextUtterance') { |
| 693 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH); | 684 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH); |
| 694 } | 685 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 734 * @param {string} gesture The gesture to handle, based on the AXGesture enum | 725 * @param {string} gesture The gesture to handle, based on the AXGesture enum |
| 735 * defined in ui/accessibility/ax_enums.idl | 726 * defined in ui/accessibility/ax_enums.idl |
| 736 * @return {boolean} True if this gesture was handled. | 727 * @return {boolean} True if this gesture was handled. |
| 737 * @private | 728 * @private |
| 738 */ | 729 */ |
| 739 handleClassicGesture_: function(gesture) { | 730 handleClassicGesture_: function(gesture) { |
| 740 var command = Background.GESTURE_CLASSIC_COMMAND_MAP[gesture]; | 731 var command = Background.GESTURE_CLASSIC_COMMAND_MAP[gesture]; |
| 741 if (!command) | 732 if (!command) |
| 742 return false; | 733 return false; |
| 743 | 734 |
| 744 var msg = { | 735 var msg = {'message': 'USER_COMMAND', 'command': command}; |
| 745 'message': 'USER_COMMAND', | |
| 746 'command': command | |
| 747 }; | |
| 748 cvox.ExtensionBridge.send(msg); | 736 cvox.ExtensionBridge.send(msg); |
| 749 return true; | 737 return true; |
| 750 }, | 738 }, |
| 751 | 739 |
| 752 /** @private */ | 740 /** @private */ |
| 753 setCurrentRangeToFocus_: function() { | 741 setCurrentRangeToFocus_: function() { |
| 754 chrome.automation.getFocus(function(focus) { | 742 chrome.automation.getFocus(function(focus) { |
| 755 if (focus) | 743 if (focus) |
| 756 this.setCurrentRange(cursors.Range.fromNode(focus)); | 744 this.setCurrentRange(cursors.Range.fromNode(focus)); |
| 757 else | 745 else |
| 758 this.setCurrentRange(null); | 746 this.setCurrentRange(null); |
| 759 }.bind(this)); | 747 }.bind(this)); |
| 760 }, | 748 }, |
| 761 | 749 |
| 762 /** | 750 /** |
| 763 * @param {!cursors.Range} range | 751 * @param {!cursors.Range} range |
| 764 * @param {cursors.Range} prevRange | 752 * @param {cursors.Range} prevRange |
| 765 * @private | 753 * @private |
| 766 */ | 754 */ |
| 767 setFocusToRange_: function(range, prevRange) { | 755 setFocusToRange_: function(range, prevRange) { |
| 768 var start = range.start.node; | 756 var start = range.start.node; |
| 769 var end = range.end.node; | 757 var end = range.end.node; |
| 770 | 758 |
| 771 // First, see if we've crossed a root. Remove once webview handles focus | 759 // First, see if we've crossed a root. Remove once webview handles focus |
| 772 // correctly. | 760 // correctly. |
| 773 if (prevRange && prevRange.start.node && start) { | 761 if (prevRange && prevRange.start.node && start) { |
| 774 var entered = AutomationUtil.getUniqueAncestors( | 762 var entered = |
| 775 prevRange.start.node, start); | 763 AutomationUtil.getUniqueAncestors(prevRange.start.node, start); |
| 776 var embeddedObject = entered.find(function(f) { | 764 var embeddedObject = entered.find(function(f) { |
| 777 return f.role == RoleType.EMBEDDED_OBJECT; }); | 765 return f.role == RoleType.EMBEDDED_OBJECT; |
| 766 }); |
| 778 if (embeddedObject && !embeddedObject.state[StateType.FOCUSED]) | 767 if (embeddedObject && !embeddedObject.state[StateType.FOCUSED]) |
| 779 embeddedObject.focus(); | 768 embeddedObject.focus(); |
| 780 } | 769 } |
| 781 | 770 |
| 782 if (start.state[StateType.FOCUSED] || end.state[StateType.FOCUSED]) | 771 if (start.state[StateType.FOCUSED] || end.state[StateType.FOCUSED]) |
| 783 return; | 772 return; |
| 784 | 773 |
| 785 var isFocusableLinkOrControl = function(node) { | 774 var isFocusableLinkOrControl = function(node) { |
| 786 return node.state[StateType.FOCUSABLE] && | 775 return node.state[StateType.FOCUSABLE] && |
| 787 AutomationPredicate.linkOrControl(node); | 776 AutomationPredicate.linkOrControl(node); |
| 788 }; | 777 }; |
| 789 | 778 |
| 790 // Next, try to focus the start or end node. | 779 // Next, try to focus the start or end node. |
| 791 if (!AutomationPredicate.structuralContainer(start) && | 780 if (!AutomationPredicate.structuralContainer(start) && |
| 792 start.state[StateType.FOCUSABLE]) { | 781 start.state[StateType.FOCUSABLE]) { |
| 793 if (!start.state[StateType.FOCUSED]) | 782 if (!start.state[StateType.FOCUSED]) |
| 794 start.focus(); | 783 start.focus(); |
| 795 return; | 784 return; |
| 796 } else if (!AutomationPredicate.structuralContainer(end) && | 785 } else if ( |
| 786 !AutomationPredicate.structuralContainer(end) && |
| 797 end.state[StateType.FOCUSABLE]) { | 787 end.state[StateType.FOCUSABLE]) { |
| 798 if (!end.state[StateType.FOCUSED]) | 788 if (!end.state[StateType.FOCUSED]) |
| 799 end.focus(); | 789 end.focus(); |
| 800 return; | 790 return; |
| 801 } | 791 } |
| 802 | 792 |
| 803 // If a common ancestor of |start| and |end| is a link, focus that. | 793 // If a common ancestor of |start| and |end| is a link, focus that. |
| 804 var ancestor = AutomationUtil.getLeastCommonAncestor(start, end); | 794 var ancestor = AutomationUtil.getLeastCommonAncestor(start, end); |
| 805 while (ancestor && ancestor.root == start.root) { | 795 while (ancestor && ancestor.root == start.root) { |
| 806 if (isFocusableLinkOrControl(ancestor)) { | 796 if (isFocusableLinkOrControl(ancestor)) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 820 }; | 810 }; |
| 821 | 811 |
| 822 /** | 812 /** |
| 823 * Converts a list of globs, as used in the extension manifest, to a regular | 813 * Converts a list of globs, as used in the extension manifest, to a regular |
| 824 * expression that matches if and only if any of the globs in the list matches. | 814 * expression that matches if and only if any of the globs in the list matches. |
| 825 * @param {!Array<string>} globs | 815 * @param {!Array<string>} globs |
| 826 * @return {!RegExp} | 816 * @return {!RegExp} |
| 827 * @private | 817 * @private |
| 828 */ | 818 */ |
| 829 Background.globsToRegExp_ = function(globs) { | 819 Background.globsToRegExp_ = function(globs) { |
| 830 return new RegExp('^(' + globs.map(function(glob) { | 820 return new RegExp( |
| 831 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') | 821 '^(' + |
| 832 .replace(/\*/g, '.*') | 822 globs |
| 833 .replace(/\?/g, '.'); | 823 .map(function(glob) { |
| 834 }).join('|') + ')$'); | 824 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') |
| 825 .replace(/\*/g, '.*') |
| 826 .replace(/\?/g, '.'); |
| 827 }) |
| 828 .join('|') + |
| 829 ')$'); |
| 835 }; | 830 }; |
| 836 | 831 |
| 837 new Background(); | 832 new Background(); |
| 838 | 833 |
| 839 }); // goog.scope | 834 }); // goog.scope |
| OLD | NEW |