Chromium Code Reviews| 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 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 58 /** | 58 /** |
| 59 * Which variant of ChromeVox is active. | 59 * Which variant of ChromeVox is active. |
| 60 * @type {ChromeVoxMode} | 60 * @type {ChromeVoxMode} |
| 61 * @private | 61 * @private |
| 62 */ | 62 */ |
| 63 this.mode_ = ChromeVoxMode.CLASSIC; | 63 this.mode_ = ChromeVoxMode.CLASSIC; |
| 64 | 64 |
| 65 /** @type {!ClassicCompatibility} @private */ | 65 /** @type {!ClassicCompatibility} @private */ |
| 66 this.compat_ = new ClassicCompatibility(); | 66 this.compat_ = new ClassicCompatibility(); |
| 67 | 67 |
| 68 /** @type {Array<Object>} A queue of incoming events. @private */ | |
| 69 this.eventQueue_ = []; | |
|
Peter Lundblad
2015/08/20 08:30:05
I suggest putting the event queue into its own cla
| |
| 70 | |
| 71 /** | |
| 72 * @type {?number} | |
| 73 * The window.setTimeout timer ID of the event queue timer. | |
| 74 * @private | |
| 75 */ | |
| 76 this.eventQueueTimerId_ = null; | |
|
Peter Lundblad
2015/08/20 08:30:05
Since your code already relies on timer ids being
| |
| 77 | |
| 78 /** | |
| 79 * @type {number} | |
| 80 * How long to hold events in the event queue before executing them. | |
| 81 * @const | |
| 82 */ | |
| 83 this.EVENT_QUEUE_DELAY_MS = 10; | |
|
David Tseng
2015/08/19 17:19:25
nit: make private.
Peter Lundblad
2015/08/20 08:30:05
Put constants outside of the constructor, directly
| |
| 84 | |
| 68 // Manually bind all functions to |this|. | 85 // Manually bind all functions to |this|. |
| 69 for (var func in this) { | 86 for (var func in this) { |
| 70 if (typeof(this[func]) == 'function') | 87 if (typeof(this[func]) == 'function') |
| 71 this[func] = this[func].bind(this); | 88 this[func] = this[func].bind(this); |
| 72 } | 89 } |
| 73 | 90 |
| 74 /** | 91 /** |
| 75 * Maps an automation event to its listener. | 92 * Maps an automation event to its listener. |
| 76 * @type {!Object<EventType, function(Object) : void>} | 93 * @type {!Object<EventType, function(Object) : void>} |
| 77 */ | 94 */ |
| 78 this.listeners_ = { | 95 this.listeners_ = { |
| 79 alert: this.onEventDefault, | 96 alert: this.onEventDefault, |
| 80 focus: this.onFocus, | 97 focus: this.onFocus, |
| 81 hover: this.onEventDefault, | 98 hover: this.onEventDefault, |
| 82 loadComplete: this.onLoadComplete, | 99 loadComplete: this.onLoadComplete, |
| 83 menuStart: this.onEventDefault, | 100 menuStart: this.onEventDefault, |
| 84 menuEnd: this.onEventDefault, | 101 menuEnd: this.onEventDefault, |
| 85 menuListValueChanged: this.onEventDefault, | 102 menuListValueChanged: this.onEventDefault, |
| 86 textChanged: this.onTextOrTextSelectionChanged, | 103 textChanged: this.onTextOrTextSelectionChanged, |
| 87 textSelectionChanged: this.onTextOrTextSelectionChanged, | 104 textSelectionChanged: this.onTextOrTextSelectionChanged, |
| 88 valueChanged: this.onValueChanged | 105 valueChanged: this.onValueChanged |
| 89 }; | 106 }; |
| 90 | 107 |
| 108 /** | |
| 109 * The object that speaks changes to an editable text field. | |
| 110 * @type {?cvox.ChromeVoxEditableTextBase} | |
|
Peter Lundblad
2015/08/20 08:30:05
Document when this is null.
| |
| 111 */ | |
| 112 this.editableTextHandler_ = null; | |
| 113 | |
| 91 chrome.automation.getDesktop(this.onGotDesktop); | 114 chrome.automation.getDesktop(this.onGotDesktop); |
| 92 | 115 |
| 93 // Handle messages directed to the Next background page. | 116 // Handle messages directed to the Next background page. |
| 94 cvox.ExtensionBridge.addMessageListener(function(msg, port) { | 117 cvox.ExtensionBridge.addMessageListener(function(msg, port) { |
| 95 var target = msg['target']; | 118 var target = msg['target']; |
| 96 var action = msg['action']; | 119 var action = msg['action']; |
| 97 | 120 |
| 98 switch (target) { | 121 switch (target) { |
| 99 case 'next': | 122 case 'next': |
| 100 if (action == 'getIsClassicEnabled') { | 123 if (action == 'getIsClassicEnabled') { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 116 this.setChromeVoxMode(ChromeVoxMode.FORCE_NEXT); | 139 this.setChromeVoxMode(ChromeVoxMode.FORCE_NEXT); |
| 117 }, | 140 }, |
| 118 | 141 |
| 119 /** | 142 /** |
| 120 * Handles all setup once a new automation tree appears. | 143 * Handles all setup once a new automation tree appears. |
| 121 * @param {chrome.automation.AutomationNode} desktop | 144 * @param {chrome.automation.AutomationNode} desktop |
| 122 */ | 145 */ |
| 123 onGotDesktop: function(desktop) { | 146 onGotDesktop: function(desktop) { |
| 124 // Register all automation event listeners. | 147 // Register all automation event listeners. |
| 125 for (var eventType in this.listeners_) | 148 for (var eventType in this.listeners_) |
| 126 desktop.addEventListener(eventType, this.listeners_[eventType], true); | 149 desktop.addEventListener(eventType, this.addToEventQueue_, true); |
| 127 | 150 |
| 128 // Register a tree change observer. | 151 // Register a tree change observer. |
| 129 chrome.automation.addTreeChangeObserver(this.onTreeChange); | 152 chrome.automation.addTreeChangeObserver(this.onTreeChange); |
| 130 | 153 |
| 131 // The focused state gets set on the containing webView node. | 154 // The focused state gets set on the containing webView node. |
| 132 var webView = desktop.find({role: chrome.automation.RoleType.webView, | 155 var webView = desktop.find({role: chrome.automation.RoleType.webView, |
| 133 state: {focused: true}}); | 156 state: {focused: true}}); |
| 134 if (webView) { | 157 if (webView) { |
| 135 var root = webView.find({role: chrome.automation.RoleType.rootWebArea}); | 158 var root = webView.find({role: chrome.automation.RoleType.rootWebArea}); |
| 136 if (root) { | 159 if (root) { |
| (...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 394 case cvox.BrailleKeyCommand.BOTTOM: | 417 case cvox.BrailleKeyCommand.BOTTOM: |
| 395 this.onGotCommand('goToEnd', true); | 418 this.onGotCommand('goToEnd', true); |
| 396 break; | 419 break; |
| 397 default: | 420 default: |
| 398 return false; | 421 return false; |
| 399 } | 422 } |
| 400 return true; | 423 return true; |
| 401 }, | 424 }, |
| 402 | 425 |
| 403 /** | 426 /** |
| 427 * Add an event to the event queue so that we can avoid ignore | |
|
David Tseng
2015/08/19 17:19:25
avoid or ignore
| |
| 428 * transient states - for example if focus moves to one node and | |
| 429 * then immediately another, or if a node gains focus and | |
| 430 * immediately changes its selection. | |
| 431 * | |
| 432 * @param {Object} evt | |
| 433 */ | |
|
David Tseng
2015/08/19 17:19:25
nit: @private
| |
| 434 addToEventQueue_: function(evt) { | |
| 435 evt.enqueueTime = new Date(); | |
|
Peter Lundblad
2015/08/20 08:30:05
Should we extend the automation api with a time fi
| |
| 436 this.eventQueue_.push(evt); | |
| 437 if (!this.eventQueueTimerId_) { | |
| 438 this.eventQueueTimerId_ = window.setTimeout( | |
| 439 this.executeNextEventFromQueue_, this.EVENT_QUEUE_DELAY_MS); | |
| 440 } | |
| 441 }, | |
| 442 | |
| 443 /** | |
| 444 * Filter duplicates from the event queue, then pop the first item | |
| 445 * off and if it's time to execute it, do so, otherwise set the timer | |
| 446 * to wake up again. | |
|
David Tseng
2015/08/19 17:19:25
nit: @private
| |
| 447 */ | |
| 448 executeNextEventFromQueue_: function() { | |
|
Peter Lundblad
2015/08/20 08:30:05
nit: s/execute/handle/ ?
| |
| 449 this.eventQueueTimerId_ = null; | |
| 450 | |
| 451 if (this.eventQueue_.length == 0) { | |
| 452 return; | |
| 453 } | |
|
David Tseng
2015/08/19 17:19:25
nit: remove braces.
| |
| 454 | |
| 455 // Assuming the queue is in increasing order of enqueue time, remove all exc ept the | |
| 456 // last item of each type. | |
| 457 var lastTimeForEventType = {}; | |
|
Peter Lundblad
2015/08/20 08:30:05
Why not just store the object reference instead of
| |
| 458 this.eventQueue_.forEach(function(evt) { | |
| 459 lastTimeForEventType[evt.type] = evt.enqueueTime; | |
| 460 }); | |
| 461 this.eventQueue_ = this.eventQueue_.filter(function(evt) { | |
| 462 return evt.enqueueTime == lastTimeForEventType[evt.type]; | |
|
David Tseng
2015/08/19 17:19:25
Any issues doing this generically? For example, do
| |
| 463 }); | |
| 464 | |
| 465 while (this.eventQueue_.length) { | |
| 466 var now = new Date(); | |
| 467 var delta = now - this.eventQueue_[0].enqueueTime; | |
| 468 if (delta < this.EVENT_QUEUE_DELAY_MS) { | |
| 469 this.eventQueueTimerId_ = window.setTimeout( | |
| 470 this.executeNextEventFromQueue_, this.EVENT_QUEUE_DELAY_MS - delta); | |
| 471 return; | |
| 472 } | |
| 473 | |
| 474 var evt = this.eventQueue_.shift(); | |
| 475 this.listeners_[evt.type](evt); | |
| 476 } | |
| 477 }, | |
| 478 | |
| 479 /** | |
| 404 * Provides all feedback once ChromeVox's focus changes. | 480 * Provides all feedback once ChromeVox's focus changes. |
| 405 * @param {Object} evt | 481 * @param {Object} evt |
| 406 */ | 482 */ |
| 407 onEventDefault: function(evt) { | 483 onEventDefault: function(evt) { |
| 408 var node = evt.target; | 484 var node = evt.target; |
| 409 | 485 |
| 410 if (!node) | 486 if (!node) |
| 411 return; | 487 return; |
| 412 | 488 |
| 413 var prevRange = this.currentRange_; | 489 var prevRange = this.currentRange_; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 447 onFocus: function(evt) { | 523 onFocus: function(evt) { |
| 448 var node = evt.target; | 524 var node = evt.target; |
| 449 | 525 |
| 450 // It doesn't make sense to focus the containing web area if a descendant | 526 // It doesn't make sense to focus the containing web area if a descendant |
| 451 // has focused state. | 527 // has focused state. |
| 452 if (node.role == 'rootWebArea') { | 528 if (node.role == 'rootWebArea') { |
| 453 node = AutomationUtil.findNodePost(node, | 529 node = AutomationUtil.findNodePost(node, |
| 454 Dir.FORWARD, | 530 Dir.FORWARD, |
| 455 AutomationPredicate.focused) || node; | 531 AutomationPredicate.focused) || node; |
| 456 } | 532 } |
| 533 | |
| 534 if (evt.target.role == 'textField' || evt.target.role == 'textBox') { | |
|
David Tseng
2015/08/19 17:19:25
nit: please use RoleType.textField and RoleType.te
| |
| 535 this.createEditableTextHandlerIfNeeded_(evt.target); | |
| 536 } | |
| 537 | |
| 457 this.onEventDefault({target: node, type: 'focus'}); | 538 this.onEventDefault({target: node, type: 'focus'}); |
| 458 }, | 539 }, |
| 459 | 540 |
| 460 /** | 541 /** |
| 461 * Provides all feedback once a load complete event fires. | 542 * Provides all feedback once a load complete event fires. |
| 462 * @param {Object} evt | 543 * @param {Object} evt |
| 463 */ | 544 */ |
| 464 onLoadComplete: function(evt) { | 545 onLoadComplete: function(evt) { |
| 465 this.setupChromeVoxVariants_(evt.target.docUrl); | 546 this.setupChromeVoxVariants_(evt.target.docUrl); |
| 466 | 547 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 504 return; | 585 return; |
| 505 | 586 |
| 506 if (!evt.target.state.focused) | 587 if (!evt.target.state.focused) |
| 507 return; | 588 return; |
| 508 | 589 |
| 509 if (!this.currentRange_) { | 590 if (!this.currentRange_) { |
| 510 this.onEventDefault(evt); | 591 this.onEventDefault(evt); |
| 511 this.currentRange_ = cursors.Range.fromNode(evt.target); | 592 this.currentRange_ = cursors.Range.fromNode(evt.target); |
| 512 } | 593 } |
| 513 | 594 |
| 595 this.createEditableTextHandlerIfNeeded_(evt.target); | |
| 514 var textChangeEvent = new cvox.TextChangeEvent( | 596 var textChangeEvent = new cvox.TextChangeEvent( |
| 515 evt.target.value, | 597 evt.target.value, |
| 516 evt.target.textSelStart, | 598 evt.target.textSelStart, |
| 517 evt.target.textSelEnd, | 599 evt.target.textSelEnd, |
| 518 true); // triggered by user | 600 true); // triggered by user |
| 519 if (!this.editableTextHandler || | 601 this.editableTextHandler_.changed(textChangeEvent); |
| 520 evt.target != this.currentRange_.start.node) { | |
| 521 this.editableTextHandler = | |
| 522 new cvox.ChromeVoxEditableTextBase( | |
| 523 textChangeEvent.value, | |
| 524 textChangeEvent.start, | |
| 525 textChangeEvent.end, | |
| 526 evt.target.state['protected'], | |
| 527 cvox.ChromeVox.tts); | |
| 528 } | |
| 529 | 602 |
| 530 this.editableTextHandler.changed(textChangeEvent); | |
| 531 new Output().withBraille( | 603 new Output().withBraille( |
| 532 this.currentRange_, null, evt.type) | 604 this.currentRange_, null, evt.type) |
| 533 .go(); | 605 .go(); |
| 534 }, | 606 }, |
| 535 | 607 |
| 536 /** | 608 /** |
| 537 * Provides all feedback once a value changed event fires. | 609 * Provides all feedback once a value changed event fires. |
| 538 * @param {Object} evt | 610 * @param {Object} evt |
| 539 */ | 611 */ |
| 540 onValueChanged: function(evt) { | 612 onValueChanged: function(evt) { |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 682 // When in compat mode, if the focus is within the desktop tree proper, | 754 // When in compat mode, if the focus is within the desktop tree proper, |
| 683 // then do not disable content scripts. | 755 // then do not disable content scripts. |
| 684 if (this.currentRange_.start.node.root.role == 'desktop') | 756 if (this.currentRange_.start.node.root.role == 'desktop') |
| 685 return; | 757 return; |
| 686 | 758 |
| 687 this.disableClassicChromeVox_(); | 759 this.disableClassicChromeVox_(); |
| 688 } | 760 } |
| 689 }.bind(this)); | 761 }.bind(this)); |
| 690 | 762 |
| 691 this.mode_ = mode; | 763 this.mode_ = mode; |
| 692 } | 764 }, |
| 765 | |
| 766 /** | |
| 767 * Create an editable text handler for the given node if needed. | |
| 768 * @param {Object} node | |
|
David Tseng
2015/08/19 17:19:25
s/Object/AutomationNode
| |
| 769 */ | |
|
David Tseng
2015/08/19 17:19:25
@private
| |
| 770 createEditableTextHandlerIfNeeded_: function(node) { | |
| 771 if (!this.editableTextHandler_ || node != this.currentRange_.start.node) { | |
| 772 this.editableTextHandler_ = | |
| 773 new cvox.ChromeVoxEditableTextBase( | |
| 774 node.value, | |
| 775 node.textSelStart, | |
| 776 node.textSelEnd, | |
| 777 node.state['protected'], | |
| 778 cvox.ChromeVox.tts); | |
| 779 } | |
| 780 }, | |
| 693 }; | 781 }; |
| 694 | 782 |
| 695 /** @type {Background} */ | 783 /** @type {Background} */ |
| 696 global.backgroundObj = new Background(); | 784 global.backgroundObj = new Background(); |
| 697 | 785 |
| 698 }); // goog.scope | 786 }); // goog.scope |
| OLD | NEW |