| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 Handles automation from a desktop automation node. | 6 * @fileoverview Handles automation from a desktop automation node. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 goog.provide('DesktopAutomationHandler'); | 9 goog.provide('DesktopAutomationHandler'); |
| 10 | 10 |
| 11 goog.require('Background'); | |
| 12 goog.require('BaseAutomationHandler'); | 11 goog.require('BaseAutomationHandler'); |
| 12 goog.require('ChromeVoxState'); |
| 13 | 13 |
| 14 goog.scope(function() { | 14 goog.scope(function() { |
| 15 var AutomationEvent = chrome.automation.AutomationEvent; | 15 var AutomationEvent = chrome.automation.AutomationEvent; |
| 16 var AutomationNode = chrome.automation.AutomationNode; | 16 var AutomationNode = chrome.automation.AutomationNode; |
| 17 var Dir = AutomationUtil.Dir; | 17 var Dir = AutomationUtil.Dir; |
| 18 var RoleType = chrome.automation.RoleType; | 18 var RoleType = chrome.automation.RoleType; |
| 19 | 19 |
| 20 /** | 20 /** |
| 21 * @param {!AutomationNode} node | 21 * @param {!AutomationNode} node |
| 22 * @constructor | 22 * @constructor |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 54 /** | 54 /** |
| 55 * Provides all feedback once ChromeVox's focus changes. | 55 * Provides all feedback once ChromeVox's focus changes. |
| 56 * @param {Object} evt | 56 * @param {Object} evt |
| 57 */ | 57 */ |
| 58 onEventDefault: function(evt) { | 58 onEventDefault: function(evt) { |
| 59 var node = evt.target; | 59 var node = evt.target; |
| 60 | 60 |
| 61 if (!node) | 61 if (!node) |
| 62 return; | 62 return; |
| 63 | 63 |
| 64 var prevRange = global.backgroundObj.currentRange; | 64 var prevRange = ChromeVoxState.instance.currentRange; |
| 65 | 65 |
| 66 global.backgroundObj.currentRange = cursors.Range.fromNode(node); | 66 ChromeVoxState.instance.setCurrentRange(cursors.Range.fromNode(node)); |
| 67 | 67 |
| 68 // Check to see if we've crossed roots. Continue if we've crossed roots or | 68 // Check to see if we've crossed roots. Continue if we've crossed roots or |
| 69 // are not within web content. | 69 // are not within web content. |
| 70 if (node.root.role == RoleType.desktop || | 70 if (node.root.role == RoleType.desktop || |
| 71 !prevRange || | 71 !prevRange || |
| 72 prevRange.start.node.root != node.root) | 72 prevRange.start.node.root != node.root) |
| 73 global.backgroundObj.refreshMode(node.root.docUrl || ''); | 73 ChromeVoxState.instance.refreshMode(node.root.docUrl || ''); |
| 74 | 74 |
| 75 // Don't process nodes inside of web content if ChromeVox Next is inactive. | 75 // Don't process nodes inside of web content if ChromeVox Next is inactive. |
| 76 if (node.root.role != RoleType.desktop && | 76 if (node.root.role != RoleType.desktop && |
| 77 global.backgroundObj.mode === ChromeVoxMode.CLASSIC) { | 77 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) { |
| 78 if (cvox.ChromeVox.isChromeOS) | 78 if (cvox.ChromeVox.isChromeOS) |
| 79 chrome.accessibilityPrivate.setFocusRing([]); | 79 chrome.accessibilityPrivate.setFocusRing([]); |
| 80 return; | 80 return; |
| 81 } | 81 } |
| 82 | 82 |
| 83 // Don't output if focused node hasn't changed. | 83 // Don't output if focused node hasn't changed. |
| 84 if (prevRange && | 84 if (prevRange && |
| 85 evt.type == 'focus' && | 85 evt.type == 'focus' && |
| 86 global.backgroundObj.currentRange.equals(prevRange)) | 86 ChromeVoxState.instance.currentRange.equals(prevRange)) |
| 87 return; | 87 return; |
| 88 | 88 |
| 89 new Output().withSpeechAndBraille( | 89 new Output().withSpeechAndBraille( |
| 90 global.backgroundObj.currentRange, prevRange, evt.type) | 90 ChromeVoxState.instance.currentRange, prevRange, evt.type) |
| 91 .go(); | 91 .go(); |
| 92 }, | 92 }, |
| 93 | 93 |
| 94 /** | 94 /** |
| 95 * Makes an announcement without changing focus. | 95 * Makes an announcement without changing focus. |
| 96 * @param {Object} evt | 96 * @param {Object} evt |
| 97 */ | 97 */ |
| 98 onAlert: function(evt) { | 98 onAlert: function(evt) { |
| 99 var node = evt.target; | 99 var node = evt.target; |
| 100 if (!node) | 100 if (!node) |
| 101 return; | 101 return; |
| 102 | 102 |
| 103 // Don't process nodes inside of web content if ChromeVox Next is inactive. | 103 // Don't process nodes inside of web content if ChromeVox Next is inactive. |
| 104 if (node.root.role != RoleType.desktop && | 104 if (node.root.role != RoleType.desktop && |
| 105 global.backgroundObj.mode === ChromeVoxMode.CLASSIC) { | 105 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) { |
| 106 return; | 106 return; |
| 107 } | 107 } |
| 108 | 108 |
| 109 var range = cursors.Range.fromNode(node); | 109 var range = cursors.Range.fromNode(node); |
| 110 | 110 |
| 111 new Output().withSpeechAndBraille(range, null, evt.type).go(); | 111 new Output().withSpeechAndBraille(range, null, evt.type).go(); |
| 112 }, | 112 }, |
| 113 | 113 |
| 114 /** | 114 /** |
| 115 * Provides all feedback once a focus event fires. | 115 * Provides all feedback once a focus event fires. |
| 116 * @param {Object} evt | 116 * @param {Object} evt |
| 117 */ | 117 */ |
| 118 onFocus: function(evt) { | 118 onFocus: function(evt) { |
| 119 // Invalidate any previous editable text handler state. | 119 // Invalidate any previous editable text handler state. |
| 120 this.editableTextHandler_ = null; | 120 this.editableTextHandler_ = null; |
| 121 | 121 |
| 122 var node = evt.target; | 122 var node = evt.target; |
| 123 | 123 |
| 124 // Discard focus events on embeddedObject nodes. | 124 // Discard focus events on embeddedObject nodes. |
| 125 if (node.role == RoleType.embeddedObject) | 125 if (node.role == RoleType.embeddedObject) |
| 126 return; | 126 return; |
| 127 | 127 |
| 128 // It almost never makes sense to place focus directly on a rootWebArea. | 128 // It almost never makes sense to place focus directly on a rootWebArea. |
| 129 if (node.role == RoleType.rootWebArea) { | 129 if (node.role == RoleType.rootWebArea) { |
| 130 // Discard focus events for root web areas when focus was previously | 130 // Discard focus events for root web areas when focus was previously |
| 131 // placed on a descendant. | 131 // placed on a descendant. |
| 132 var currentRange = global.backgroundObj.currentRange; | 132 var currentRange = ChromeVoxState.instance.currentRange; |
| 133 if (currentRange && currentRange.start.node.root == node) | 133 if (currentRange && currentRange.start.node.root == node) |
| 134 return; | 134 return; |
| 135 | 135 |
| 136 // Discard focused root nodes without focused state set. | 136 // Discard focused root nodes without focused state set. |
| 137 if (!node.state.focused) | 137 if (!node.state.focused) |
| 138 return; | 138 return; |
| 139 | 139 |
| 140 // Try to find a focusable descendant. | 140 // Try to find a focusable descendant. |
| 141 node = node.find({state: {focused: true}}) || node; | 141 node = node.find({state: {focused: true}}) || node; |
| 142 } | 142 } |
| 143 | 143 |
| 144 if (this.isEditable_(evt.target)) | 144 if (this.isEditable_(evt.target)) |
| 145 this.createEditableTextHandlerIfNeeded_(evt.target); | 145 this.createEditableTextHandlerIfNeeded_(evt.target); |
| 146 | 146 |
| 147 this.onEventDefault({target: node, type: 'focus'}); | 147 this.onEventDefault({target: node, type: 'focus'}); |
| 148 }, | 148 }, |
| 149 | 149 |
| 150 /** | 150 /** |
| 151 * Provides all feedback once a load complete event fires. | 151 * Provides all feedback once a load complete event fires. |
| 152 * @param {Object} evt | 152 * @param {Object} evt |
| 153 */ | 153 */ |
| 154 onLoadComplete: function(evt) { | 154 onLoadComplete: function(evt) { |
| 155 global.backgroundObj.refreshMode(evt.target.docUrl); | 155 ChromeVoxState.instance.refreshMode(evt.target.docUrl); |
| 156 | 156 |
| 157 // Don't process nodes inside of web content if ChromeVox Next is inactive. | 157 // Don't process nodes inside of web content if ChromeVox Next is inactive. |
| 158 if (evt.target.root.role != RoleType.desktop && | 158 if (evt.target.root.role != RoleType.desktop && |
| 159 global.backgroundObj.mode === ChromeVoxMode.CLASSIC) | 159 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) |
| 160 return; | 160 return; |
| 161 | 161 |
| 162 // If initial focus was already placed on this page (e.g. if a user starts | 162 // If initial focus was already placed on this page (e.g. if a user starts |
| 163 // tabbing before load complete), then don't move ChromeVox's position on | 163 // tabbing before load complete), then don't move ChromeVox's position on |
| 164 // the page. | 164 // the page. |
| 165 if (global.backgroundObj.currentRange && | 165 if (ChromeVoxState.instance.currentRange && |
| 166 global.backgroundObj.currentRange.start.node.role != | 166 ChromeVoxState.instance.currentRange.start.node.role != |
| 167 RoleType.rootWebArea && | 167 RoleType.rootWebArea && |
| 168 global.backgroundObj.currentRange.start.node.root.docUrl == | 168 ChromeVoxState.instance.currentRange.start.node.root.docUrl == |
| 169 evt.target.docUrl) | 169 evt.target.docUrl) |
| 170 return; | 170 return; |
| 171 | 171 |
| 172 var root = evt.target; | 172 var root = evt.target; |
| 173 var webView = root; | 173 var webView = root; |
| 174 while (webView && webView.role != RoleType.webView) | 174 while (webView && webView.role != RoleType.webView) |
| 175 webView = webView.parent; | 175 webView = webView.parent; |
| 176 | 176 |
| 177 if (!webView || !webView.state.focused) | 177 if (!webView || !webView.state.focused) |
| 178 return; | 178 return; |
| 179 | 179 |
| 180 var node = AutomationUtil.findNodePost(root, | 180 var node = AutomationUtil.findNodePost(root, |
| 181 Dir.FORWARD, | 181 Dir.FORWARD, |
| 182 AutomationPredicate.leaf); | 182 AutomationPredicate.leaf); |
| 183 | 183 |
| 184 if (node) | 184 if (node) |
| 185 global.backgroundObj.currentRange = cursors.Range.fromNode(node); | 185 ChromeVoxState.instance.setCurrentRange(cursors.Range.fromNode(node)); |
| 186 | 186 |
| 187 if (global.backgroundObj.currentRange) | 187 if (ChromeVoxState.instance.currentRange) |
| 188 new Output().withSpeechAndBraille( | 188 new Output().withSpeechAndBraille( |
| 189 global.backgroundObj.currentRange, null, evt.type) | 189 ChromeVoxState.instance.currentRange, null, evt.type) |
| 190 .go(); | 190 .go(); |
| 191 }, | 191 }, |
| 192 | 192 |
| 193 /** | 193 /** |
| 194 * Provides all feedback once a text selection change event fires. | 194 * Provides all feedback once a text selection change event fires. |
| 195 * @param {Object} evt | 195 * @param {Object} evt |
| 196 */ | 196 */ |
| 197 onTextOrTextSelectionChanged: function(evt) { | 197 onTextOrTextSelectionChanged: function(evt) { |
| 198 if (!this.isEditable_(evt.target)) | 198 if (!this.isEditable_(evt.target)) |
| 199 return; | 199 return; |
| 200 | 200 |
| 201 // Don't process nodes inside of web content if ChromeVox Next is inactive. | 201 // Don't process nodes inside of web content if ChromeVox Next is inactive. |
| 202 if (evt.target.root.role != RoleType.desktop && | 202 if (evt.target.root.role != RoleType.desktop && |
| 203 global.backgroundObj.mode === ChromeVoxMode.CLASSIC) | 203 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) |
| 204 return; | 204 return; |
| 205 | 205 |
| 206 if (!evt.target.state.focused) | 206 if (!evt.target.state.focused) |
| 207 return; | 207 return; |
| 208 | 208 |
| 209 if (!global.backgroundObj.currentRange) { | 209 if (!ChromeVoxState.instance.currentRange) { |
| 210 this.onEventDefault(evt); | 210 this.onEventDefault(evt); |
| 211 global.backgroundObj.currentRange = cursors.Range.fromNode(evt.target); | 211 ChromeVoxState.instance.setCurrentRange( |
| 212 cursors.Range.fromNode(evt.target)); |
| 212 } | 213 } |
| 213 | 214 |
| 214 this.createEditableTextHandlerIfNeeded_(evt.target); | 215 this.createEditableTextHandlerIfNeeded_(evt.target); |
| 215 | 216 |
| 216 var textChangeEvent = new cvox.TextChangeEvent( | 217 var textChangeEvent = new cvox.TextChangeEvent( |
| 217 evt.target.value, | 218 evt.target.value, |
| 218 evt.target.textSelStart, | 219 evt.target.textSelStart, |
| 219 evt.target.textSelEnd, | 220 evt.target.textSelEnd, |
| 220 true); // triggered by user | 221 true); // triggered by user |
| 221 | 222 |
| 222 this.editableTextHandler_.changed(textChangeEvent); | 223 this.editableTextHandler_.changed(textChangeEvent); |
| 223 | 224 |
| 224 new Output().withBraille( | 225 new Output().withBraille( |
| 225 global.backgroundObj.currentRange, null, evt.type) | 226 ChromeVoxState.instance.currentRange, null, evt.type) |
| 226 .go(); | 227 .go(); |
| 227 }, | 228 }, |
| 228 | 229 |
| 229 /** | 230 /** |
| 230 * Provides all feedback once a value changed event fires. | 231 * Provides all feedback once a value changed event fires. |
| 231 * @param {Object} evt | 232 * @param {Object} evt |
| 232 */ | 233 */ |
| 233 onValueChanged: function(evt) { | 234 onValueChanged: function(evt) { |
| 234 // Don't process nodes inside of web content if ChromeVox Next is inactive. | 235 // Don't process nodes inside of web content if ChromeVox Next is inactive. |
| 235 if (evt.target.root.role != RoleType.desktop && | 236 if (evt.target.root.role != RoleType.desktop && |
| 236 global.backgroundObj.mode === ChromeVoxMode.CLASSIC) | 237 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) |
| 237 return; | 238 return; |
| 238 | 239 |
| 239 if (!evt.target.state.focused) | 240 if (!evt.target.state.focused) |
| 240 return; | 241 return; |
| 241 | 242 |
| 242 // Value change events fire on web editables when typing. Suppress them. | 243 // Value change events fire on web editables when typing. Suppress them. |
| 243 if (!global.backgroundObj.currentRange || !this.isEditable_(evt.target)) { | 244 if (!ChromeVoxState.instance.currentRange || |
| 245 !this.isEditable_(evt.target)) { |
| 244 this.onEventDefault(evt); | 246 this.onEventDefault(evt); |
| 245 global.backgroundObj.currentRange = cursors.Range.fromNode(evt.target); | 247 ChromeVoxState.instance.setCurrentRange( |
| 248 cursors.Range.fromNode(evt.target)); |
| 246 } | 249 } |
| 247 }, | 250 }, |
| 248 | 251 |
| 249 /** | 252 /** |
| 250 * Handle updating the active indicator when the document scrolls. | 253 * Handle updating the active indicator when the document scrolls. |
| 251 * @override | 254 * @override |
| 252 */ | 255 */ |
| 253 onScrollPositionChanged: function(evt) { | 256 onScrollPositionChanged: function(evt) { |
| 254 var currentRange = global.backgroundObj.currentRange; | 257 var currentRange = ChromeVoxState.instance.currentRange; |
| 255 if (currentRange) | 258 if (currentRange) |
| 256 new Output().withLocation(currentRange, null, evt.type).go(); | 259 new Output().withLocation(currentRange, null, evt.type).go(); |
| 257 }, | 260 }, |
| 258 | 261 |
| 259 /** | 262 /** |
| 260 * Create an editable text handler for the given node if needed. | 263 * Create an editable text handler for the given node if needed. |
| 261 * @param {Object} node | 264 * @param {Object} node |
| 262 */ | 265 */ |
| 263 createEditableTextHandlerIfNeeded_: function(node) { | 266 createEditableTextHandlerIfNeeded_: function(node) { |
| 264 if (!this.editableTextHandler_ || | 267 if (!this.editableTextHandler_ || |
| 265 node != global.backgroundObj.currentRange.start.node) { | 268 node != ChromeVoxState.instance.currentRange.start.node) { |
| 266 var start = node.textSelStart; | 269 var start = node.textSelStart; |
| 267 var end = node.textSelEnd; | 270 var end = node.textSelEnd; |
| 268 if (start > end) { | 271 if (start > end) { |
| 269 var tempOffset = end; | 272 var tempOffset = end; |
| 270 end = start; | 273 end = start; |
| 271 start = tempOffset; | 274 start = tempOffset; |
| 272 } | 275 } |
| 273 | 276 |
| 274 this.editableTextHandler_ = | 277 this.editableTextHandler_ = |
| 275 new cvox.ChromeVoxEditableTextBase( | 278 new cvox.ChromeVoxEditableTextBase( |
| (...skipping 26 matching lines...) Expand all Loading... |
| 302 if (cvox.ChromeVox.isMac) | 305 if (cvox.ChromeVox.isMac) |
| 303 return; | 306 return; |
| 304 chrome.automation.getDesktop(function(desktop) { | 307 chrome.automation.getDesktop(function(desktop) { |
| 305 global.desktopAutomationHandler = new DesktopAutomationHandler(desktop); | 308 global.desktopAutomationHandler = new DesktopAutomationHandler(desktop); |
| 306 }); | 309 }); |
| 307 }; | 310 }; |
| 308 | 311 |
| 309 DesktopAutomationHandler.init_(); | 312 DesktopAutomationHandler.init_(); |
| 310 | 313 |
| 311 }); // goog.scope | 314 }); // goog.scope |
| OLD | NEW |