| 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 |