Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(191)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js

Issue 1942683005: Refactor event handler classes to make it easier to add new event type listeners. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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('AutomationObjectInstaller');
11 goog.require('BaseAutomationHandler'); 12 goog.require('BaseAutomationHandler');
12 goog.require('ChromeVoxState'); 13 goog.require('ChromeVoxState');
13 goog.require('editing.TextEditHandler'); 14 goog.require('editing.TextEditHandler');
14 15
15 goog.scope(function() { 16 goog.scope(function() {
16 var AutomationEvent = chrome.automation.AutomationEvent; 17 var AutomationEvent = chrome.automation.AutomationEvent;
17 var AutomationNode = chrome.automation.AutomationNode; 18 var AutomationNode = chrome.automation.AutomationNode;
18 var Dir = constants.Dir; 19 var Dir = constants.Dir;
20 var EventType = chrome.automation.EventType;
19 var RoleType = chrome.automation.RoleType; 21 var RoleType = chrome.automation.RoleType;
20 22
21 /** 23 /**
22 * @param {!AutomationNode} node 24 * @param {!AutomationNode} node
23 * @constructor 25 * @constructor
24 * @extends {BaseAutomationHandler} 26 * @extends {BaseAutomationHandler}
25 */ 27 */
26 DesktopAutomationHandler = function(node) { 28 DesktopAutomationHandler = function(node) {
27 BaseAutomationHandler.call(this, node); 29 BaseAutomationHandler.call(this, node);
28 30
29 /** 31 /**
30 * The object that speaks changes to an editable text field. 32 * The object that speaks changes to an editable text field.
31 * @type {editing.TextEditHandler} 33 * @type {editing.TextEditHandler}
32 */ 34 */
33 this.textEditHandler_ = null; 35 this.textEditHandler_ = null;
34 36
35 /** 37 /**
36 * The last time we handled a value changed event. 38 * The last time we handled a value changed event.
37 * @type {!Date} 39 * @type {!Date}
38 * @private 40 * @private
39 */ 41 */
40 this.lastValueChanged_ = new Date(0); 42 this.lastValueChanged_ = new Date(0);
41 43
44 var e = EventType;
45 this.addListener_(e.activedescendantchanged, this.onActiveDescendantChanged);
46 this.addListener_(e.alert, this.onAlert);
47 this.addListener_(e.ariaAttributeChanged, this.onEventIfInRange);
48 this.addListener_(e.checkedStateChanged, this.onEventIfInRange);
49 this.addListener_(e.focus, this.onFocus);
50 this.addListener_(e.hover, this.onEventWithFlushedOutput);
51 this.addListener_(e.loadComplete, this.onLoadComplete);
52 this.addListener_(e.menuEnd, this.onMenuEnd);
53 this.addListener_(e.menuListItemSelected, this.onEventDefault);
54 this.addListener_(e.menuStart, this.onMenuStart);
55 this.addListener_(e.scrollPositionChanged, this.onScrollPositionChanged);
56 this.addListener_(e.selection, this.onEventWithFlushedOutput);
57 this.addListener_(e.textChanged, this.onTextChanged);
58 this.addListener_(e.textSelectionChanged, this.onTextSelectionChanged);
59 this.addListener_(e.valueChanged, this.onValueChanged);
60
42 // The focused state gets set on the containing webView node. 61 // The focused state gets set on the containing webView node.
43 var webView = node.find({role: RoleType.webView, state: {focused: true}}); 62 var webView = node.find({role: RoleType.webView, state: {focused: true}});
44 if (webView) { 63 if (webView) {
45 var root = webView.find({role: RoleType.rootWebArea}); 64 var root = webView.find({role: RoleType.rootWebArea});
46 if (root) { 65 if (root) {
47 this.onLoadComplete( 66 this.onLoadComplete(
48 {target: root, 67 new chrome.automation.AutomationEvent(EventType.loadComplete, root));
dmazzoni 2016/05/02 22:15:48 Is there a possible race condition if the chrome.a
David Tseng 2016/05/03 20:18:27 Went with a callback as suggested below.
49 type: chrome.automation.EventType.loadComplete});
50 } 68 }
51 } 69 }
52 70
53 chrome.automation.getFocus((function(focus) { 71 chrome.automation.getFocus((function(focus) {
54 if (focus) 72 if (focus)
55 this.onFocus({target: focus, type: 'focus'}); 73 this.onFocus(new chrome.automation.AutomationEvent(EventType.focus, focus));
dmazzoni 2016/05/02 22:15:48 nit: indentation
David Tseng 2016/05/03 20:18:27 Done.
56 }).bind(this)); 74 }).bind(this));
75
76 AutomationObjectInstaller.init(node);
dmazzoni 2016/05/02 22:15:48 Perhaps this init should just take a callback argu
David Tseng 2016/05/03 20:18:27 Done.
57 }; 77 };
58 78
59 /** 79 /**
60 * Time to wait until processing more value changed events. 80 * Time to wait until processing more value changed events.
61 * @const {number} 81 * @const {number}
62 */ 82 */
63 DesktopAutomationHandler.VMIN_VALUE_CHANGE_DELAY_MS = 500; 83 DesktopAutomationHandler.VMIN_VALUE_CHANGE_DELAY_MS = 500;
64 84
65 DesktopAutomationHandler.prototype = { 85 DesktopAutomationHandler.prototype = {
66 __proto__: BaseAutomationHandler.prototype, 86 __proto__: BaseAutomationHandler.prototype,
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 output.withBraille( 131 output.withBraille(
112 ChromeVoxState.instance.currentRange, prevRange, evt.type); 132 ChromeVoxState.instance.currentRange, prevRange, evt.type);
113 } else { 133 } else {
114 // Delegate event handling to the text edit handler for braille. 134 // Delegate event handling to the text edit handler for braille.
115 this.textEditHandler_.onEvent(evt); 135 this.textEditHandler_.onEvent(evt);
116 } 136 }
117 output.go(); 137 output.go();
118 }, 138 },
119 139
120 /** 140 /**
141 * @param {!AutomationEvent} evt
142 */
143 onEventIfInRange: function(evt) {
144 // TODO(dtseng): Consider the end of the current range as well.
145 if (AutomationUtil.isDescendantOf(
146 global.backgroundObj.currentRange.start.node, evt.target) ||
147 evt.target.state.focused)
148 this.onEventDefault(evt);
149 },
150
151 /**
152 * @param {!AutomationEvent} evt
153 */
154 onEventWithFlushedOutput: function(evt) {
155 Output.flushNextSpeechUtterance();
156 this.onEventDefault(evt);
157 },
158
159 /**
121 * Makes an announcement without changing focus. 160 * Makes an announcement without changing focus.
122 * @param {Object} evt 161 * @param {!AutomationEvent} evt
162 */
163 onActiveDescendantChanged: function(evt) {
164 if (!evt.target.activeDescendant)
165 return;
166 this.onEventDefault(new chrome.automation.AutomationEvent(
167 EventType.focus, evt.target.activeDescendant));
168 },
169
170 /**
171 * Makes an announcement without changing focus.
172 * @param {!AutomationEvent} evt
123 */ 173 */
124 onAlert: function(evt) { 174 onAlert: function(evt) {
125 var node = evt.target; 175 var node = evt.target;
126 if (!node) 176 if (!node)
127 return; 177 return;
128 178
129 // Don't process nodes inside of web content if ChromeVox Next is inactive. 179 // Don't process nodes inside of web content if ChromeVox Next is inactive.
130 if (node.root.role != RoleType.desktop && 180 if (node.root.role != RoleType.desktop &&
131 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) { 181 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) {
132 return; 182 return;
133 } 183 }
134 184
135 var range = cursors.Range.fromNode(node); 185 var range = cursors.Range.fromNode(node);
136 186
137 new Output().withSpeechAndBraille(range, null, evt.type).go(); 187 new Output().withSpeechAndBraille(range, null, evt.type).go();
138 }, 188 },
139 189
140 /** 190 /**
141 * Provides all feedback once a focus event fires. 191 * Provides all feedback once a focus event fires.
142 * @param {Object} evt 192 * @param {!AutomationEvent} evt
143 */ 193 */
144 onFocus: function(evt) { 194 onFocus: function(evt) {
145 // Invalidate any previous editable text handler state. 195 // Invalidate any previous editable text handler state.
146 this.textEditHandler_ = null; 196 this.textEditHandler_ = null;
147 197
148 var node = evt.target; 198 var node = evt.target;
149 199
150 // Discard focus events on embeddedObject and client nodes. 200 // Discard focus events on embeddedObject and client nodes.
151 if (node.role == RoleType.embeddedObject || node.role == RoleType.client) 201 if (node.role == RoleType.embeddedObject || node.role == RoleType.client)
152 return; 202 return;
153 203
154 this.createTextEditHandlerIfNeeded_(evt.target); 204 this.createTextEditHandlerIfNeeded_(evt.target);
155 205
156 // Since we queue output mostly for live regions support and there isn't a 206 // Since we queue output mostly for live regions support and there isn't a
157 // reliable way to know if this focus event resulted from a user's explicit 207 // reliable way to know if this focus event resulted from a user's explicit
158 // action, only flush when the focused node is not web content. 208 // action, only flush when the focused node is not web content.
159 if (node.root.role == RoleType.desktop) 209 if (node.root.role == RoleType.desktop)
160 Output.flushNextSpeechUtterance(); 210 Output.flushNextSpeechUtterance();
161 211
162 this.onEventDefault( 212 this.onEventDefault(
163 /** @type {!AutomationEvent} */ 213 new chrome.automation.AutomationEvent(EventType.focus, node));
164 ({target: node, type: chrome.automation.EventType.focus}));
165 }, 214 },
166 215
167 /** 216 /**
168 * Provides all feedback once a load complete event fires. 217 * Provides all feedback once a load complete event fires.
169 * @param {Object} evt 218 * @param {!AutomationEvent} evt
170 */ 219 */
171 onLoadComplete: function(evt) { 220 onLoadComplete: function(evt) {
172 ChromeVoxState.instance.refreshMode(evt.target.docUrl); 221 ChromeVoxState.instance.refreshMode(evt.target.docUrl);
173 222
174 // Don't process nodes inside of web content if ChromeVox Next is inactive. 223 // Don't process nodes inside of web content if ChromeVox Next is inactive.
175 if (evt.target.root.role != RoleType.desktop && 224 if (evt.target.root.role != RoleType.desktop &&
176 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) 225 ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC)
177 return; 226 return;
178 227
179 chrome.automation.getFocus((function(focus) { 228 chrome.automation.getFocus((function(focus) {
180 if (!focus) 229 if (!focus)
181 return; 230 return;
182 231
183 // If initial focus was already placed on this page (e.g. if a user starts 232 // If initial focus was already placed on this page (e.g. if a user starts
184 // tabbing before load complete), then don't move ChromeVox's position on 233 // tabbing before load complete), then don't move ChromeVox's position on
185 // the page. 234 // the page.
186 if (ChromeVoxState.instance.currentRange && 235 if (ChromeVoxState.instance.currentRange &&
187 ChromeVoxState.instance.currentRange.start.node.root == focus.root) 236 ChromeVoxState.instance.currentRange.start.node.root == focus.root)
188 return; 237 return;
189 238
190 ChromeVoxState.instance.setCurrentRange(cursors.Range.fromNode(focus)); 239 ChromeVoxState.instance.setCurrentRange(cursors.Range.fromNode(focus));
191 new Output().withRichSpeechAndBraille( 240 new Output().withRichSpeechAndBraille(
192 ChromeVoxState.instance.currentRange, null, evt.type).go(); 241 ChromeVoxState.instance.currentRange, null, evt.type).go();
193 }).bind(this)); 242 }).bind(this));
194 }, 243 },
195 244
196 /** @override */ 245
246 /**
247 * Provides all feedback once a text changed event fires.
248 * @param {!AutomationEvent} evt
249 */
197 onTextChanged: function(evt) { 250 onTextChanged: function(evt) {
198 if (evt.target.state.editable) 251 if (evt.target.state.editable)
199 this.onEditableChanged_(evt); 252 this.onEditableChanged_(evt);
200 }, 253 },
201 254
202 /** @override */ 255 /**
256 * Provides all feedback once a text selection changed event fires.
257 * @param {!AutomationEvent} evt
258 */
203 onTextSelectionChanged: function(evt) { 259 onTextSelectionChanged: function(evt) {
204 if (evt.target.state.editable) 260 if (evt.target.state.editable)
205 this.onEditableChanged_(evt); 261 this.onEditableChanged_(evt);
206 }, 262 },
207 263
208 /** 264 /**
209 * Provides all feedback once a change event in a text field fires. 265 * Provides all feedback once a change event in a text field fires.
210 * @param {!AutomationEvent} evt 266 * @param {!AutomationEvent} evt
211 * @private 267 * @private
212 */ 268 */
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 315
260 this.lastValueChanged_ = new Date(); 316 this.lastValueChanged_ = new Date();
261 317
262 new Output().format('$value', evt.target) 318 new Output().format('$value', evt.target)
263 .go(); 319 .go();
264 } 320 }
265 }, 321 },
266 322
267 /** 323 /**
268 * Handle updating the active indicator when the document scrolls. 324 * Handle updating the active indicator when the document scrolls.
269 * @override 325 * @param {!AutomationEvent} evt
270 */ 326 */
271 onScrollPositionChanged: function(evt) { 327 onScrollPositionChanged: function(evt) {
272 if (ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC) 328 if (ChromeVoxState.instance.mode === ChromeVoxMode.CLASSIC)
273 return; 329 return;
274 330
275 var currentRange = ChromeVoxState.instance.currentRange; 331 var currentRange = ChromeVoxState.instance.currentRange;
276 if (currentRange) 332 if (currentRange)
277 new Output().withLocation(currentRange, null, evt.type).go(); 333 new Output().withLocation(currentRange, null, evt.type).go();
278 }, 334 },
279 335
280 /** @override */ 336 /**
337 * Provides all feedback once a menu start event fires.
338 * @param {!AutomationEvent} evt
339 */
281 onMenuStart: function(evt) { 340 onMenuStart: function(evt) {
282 global.backgroundObj.startExcursion(); 341 global.backgroundObj.startExcursion();
283 this.onEventDefault(evt); 342 this.onEventDefault(evt);
284 }, 343 },
285 344
286 /** @override */ 345 /**
346 * Provides all feedback once a menu end event fires.
347 * @param {!AutomationEvent} evt
348 */
287 onMenuEnd: function(evt) { 349 onMenuEnd: function(evt) {
288 this.onEventDefault(evt); 350 this.onEventDefault(evt);
289 global.backgroundObj.endExcursion(); 351 global.backgroundObj.endExcursion();
290 }, 352 },
291 353
292 /** 354 /**
293 * Create an editable text handler for the given node if needed. 355 * Create an editable text handler for the given node if needed.
294 * @param {!AutomationNode} node 356 * @param {!AutomationNode} node
295 */ 357 */
296 createTextEditHandlerIfNeeded_: function(node) { 358 createTextEditHandlerIfNeeded_: function(node) {
(...skipping 12 matching lines...) Expand all
309 if (cvox.ChromeVox.isMac) 371 if (cvox.ChromeVox.isMac)
310 return; 372 return;
311 chrome.automation.getDesktop(function(desktop) { 373 chrome.automation.getDesktop(function(desktop) {
312 global.desktopAutomationHandler = new DesktopAutomationHandler(desktop); 374 global.desktopAutomationHandler = new DesktopAutomationHandler(desktop);
313 }); 375 });
314 }; 376 };
315 377
316 DesktopAutomationHandler.init_(); 378 DesktopAutomationHandler.init_();
317 379
318 }); // goog.scope 380 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698