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 |