| 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('AutomationObjectConstructorInstaller'); |
| 11 goog.require('BaseAutomationHandler'); | 12 goog.require('BaseAutomationHandler'); |
| 12 goog.require('ChromeVoxState'); | 13 goog.require('ChromeVoxState'); |
| 13 goog.require('CustomAutomationEvent'); | |
| 14 goog.require('Stubs'); | 14 goog.require('Stubs'); |
| 15 goog.require('editing.TextEditHandler'); | 15 goog.require('editing.TextEditHandler'); |
| 16 | 16 |
| 17 goog.scope(function() { | 17 goog.scope(function() { |
| 18 var AutomationEvent = chrome.automation.AutomationEvent; | 18 var AutomationEvent = chrome.automation.AutomationEvent; |
| 19 var AutomationNode = chrome.automation.AutomationNode; | 19 var AutomationNode = chrome.automation.AutomationNode; |
| 20 var Dir = constants.Dir; | 20 var Dir = constants.Dir; |
| 21 var EventType = chrome.automation.EventType; | 21 var EventType = chrome.automation.EventType; |
| 22 var RoleType = chrome.automation.RoleType; | 22 var RoleType = chrome.automation.RoleType; |
| 23 | 23 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 36 */ | 36 */ |
| 37 this.textEditHandler_ = null; | 37 this.textEditHandler_ = null; |
| 38 | 38 |
| 39 /** | 39 /** |
| 40 * The last time we handled a value changed event. | 40 * The last time we handled a value changed event. |
| 41 * @type {!Date} | 41 * @type {!Date} |
| 42 * @private | 42 * @private |
| 43 */ | 43 */ |
| 44 this.lastValueChanged_ = new Date(0); | 44 this.lastValueChanged_ = new Date(0); |
| 45 | 45 |
| 46 this.addListener_(EventType.ACTIVEDESCENDANTCHANGED, | 46 var e = EventType; |
| 47 this.onActiveDescendantChanged); | 47 this.addListener_(e.activedescendantchanged, this.onActiveDescendantChanged); |
| 48 this.addListener_(EventType.ALERT, | 48 this.addListener_(e.alert, this.onAlert); |
| 49 this.onAlert); | 49 this.addListener_(e.ariaAttributeChanged, this.onAriaAttributeChanged); |
| 50 this.addListener_(EventType.ARIA_ATTRIBUTE_CHANGED, | 50 this.addListener_(e.autocorrectionOccured, this.onEventIfInRange); |
| 51 this.onAriaAttributeChanged); | 51 this.addListener_(e.checkedStateChanged, this.onCheckedStateChanged); |
| 52 this.addListener_(EventType.AUTOCORRECTION_OCCURED, | 52 this.addListener_(e.childrenChanged, this.onActiveDescendantChanged); |
| 53 this.onEventIfInRange); | 53 this.addListener_(e.expandedChanged, this.onEventIfInRange); |
| 54 this.addListener_(EventType.CHECKED_STATE_CHANGED, | 54 this.addListener_(e.focus, this.onFocus); |
| 55 this.onCheckedStateChanged); | 55 this.addListener_(e.hover, this.onHover); |
| 56 this.addListener_(EventType.CHILDREN_CHANGED, | 56 this.addListener_(e.invalidStatusChanged, this.onEventIfInRange); |
| 57 this.onActiveDescendantChanged); | 57 this.addListener_(e.loadComplete, this.onLoadComplete); |
| 58 this.addListener_(EventType.EXPANDED_CHANGED, | 58 this.addListener_(e.menuEnd, this.onMenuEnd); |
| 59 this.onEventIfInRange); | 59 this.addListener_(e.menuListItemSelected, this.onEventIfSelected); |
| 60 this.addListener_(EventType.FOCUS, | 60 this.addListener_(e.menuStart, this.onMenuStart); |
| 61 this.onFocus); | 61 this.addListener_(e.rowCollapsed, this.onEventIfInRange); |
| 62 this.addListener_(EventType.HOVER, | 62 this.addListener_(e.rowExpanded, this.onEventIfInRange); |
| 63 this.onHover); | 63 this.addListener_(e.scrollPositionChanged, this.onScrollPositionChanged); |
| 64 this.addListener_(EventType.INVALID_STATUS_CHANGED, | 64 this.addListener_(e.selection, this.onSelection); |
| 65 this.onEventIfInRange); | 65 this.addListener_(e.textChanged, this.onTextChanged); |
| 66 this.addListener_(EventType.LOAD_COMPLETE, | 66 this.addListener_(e.textSelectionChanged, this.onTextSelectionChanged); |
| 67 this.onLoadComplete); | 67 this.addListener_(e.valueChanged, this.onValueChanged); |
| 68 this.addListener_(EventType.MENU_END, | |
| 69 this.onMenuEnd); | |
| 70 this.addListener_(EventType.MENU_LIST_ITEM_SELECTED, | |
| 71 this.onEventIfSelected); | |
| 72 this.addListener_(EventType.MENU_START, | |
| 73 this.onMenuStart); | |
| 74 this.addListener_(EventType.ROW_COLLAPSED, | |
| 75 this.onEventIfInRange); | |
| 76 this.addListener_(EventType.ROW_EXPANDED, | |
| 77 this.onEventIfInRange); | |
| 78 this.addListener_(EventType.SCROLL_POSITION_CHANGED, | |
| 79 this.onScrollPositionChanged); | |
| 80 this.addListener_(EventType.SELECTION, | |
| 81 this.onSelection); | |
| 82 this.addListener_(EventType.TEXT_CHANGED, | |
| 83 this.onTextChanged); | |
| 84 this.addListener_(EventType.TEXT_SELECTION_CHANGED, | |
| 85 this.onTextSelectionChanged); | |
| 86 this.addListener_(EventType.VALUE_CHANGED, | |
| 87 this.onValueChanged); | |
| 88 | 68 |
| 89 chrome.automation.getFocus((function(focus) { | 69 AutomationObjectConstructorInstaller.init(node, function() { |
| 90 if (ChromeVoxState.instance.mode != ChromeVoxMode.FORCE_NEXT) | 70 chrome.automation.getFocus((function(focus) { |
| 91 return; | 71 if (ChromeVoxState.instance.mode != ChromeVoxMode.FORCE_NEXT) |
| 72 return; |
| 92 | 73 |
| 93 if (focus) { | 74 if (focus) { |
| 94 var event = new CustomAutomationEvent(EventType.FOCUS, focus, 'page'); | 75 this.onFocus( |
| 95 this.onFocus(event); | 76 new chrome.automation.AutomationEvent( |
| 96 } | 77 EventType.focus, focus, 'page')); |
| 97 }).bind(this)); | 78 } |
| 79 }).bind(this)); |
| 80 }.bind(this)); |
| 98 }; | 81 }; |
| 99 | 82 |
| 100 /** | 83 /** |
| 101 * Time to wait until processing more value changed events. | 84 * Time to wait until processing more value changed events. |
| 102 * @const {number} | 85 * @const {number} |
| 103 */ | 86 */ |
| 104 DesktopAutomationHandler.VMIN_VALUE_CHANGE_DELAY_MS = 500; | 87 DesktopAutomationHandler.VMIN_VALUE_CHANGE_DELAY_MS = 500; |
| 105 | 88 |
| 106 /** | 89 /** |
| 107 * Controls announcement of non-user-initiated events. | 90 * Controls announcement of non-user-initiated events. |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 215 this.onEventDefault(evt); | 198 this.onEventDefault(evt); |
| 216 }, | 199 }, |
| 217 | 200 |
| 218 /** | 201 /** |
| 219 * Makes an announcement without changing focus. | 202 * Makes an announcement without changing focus. |
| 220 * @param {!AutomationEvent} evt | 203 * @param {!AutomationEvent} evt |
| 221 */ | 204 */ |
| 222 onActiveDescendantChanged: function(evt) { | 205 onActiveDescendantChanged: function(evt) { |
| 223 if (!evt.target.activeDescendant || !evt.target.state.focused) | 206 if (!evt.target.activeDescendant || !evt.target.state.focused) |
| 224 return; | 207 return; |
| 225 var event = new CustomAutomationEvent( | 208 this.onEventDefault(new chrome.automation.AutomationEvent( |
| 226 EventType.FOCUS, evt.target.activeDescendant, evt.eventFrom); | 209 EventType.focus, evt.target.activeDescendant, evt.eventFrom)); |
| 227 this.onEventDefault(event); | |
| 228 }, | 210 }, |
| 229 | 211 |
| 230 /** | 212 /** |
| 231 * Makes an announcement without changing focus. | 213 * Makes an announcement without changing focus. |
| 232 * @param {!AutomationEvent} evt | 214 * @param {!AutomationEvent} evt |
| 233 */ | 215 */ |
| 234 onAlert: function(evt) { | 216 onAlert: function(evt) { |
| 235 var node = evt.target; | 217 var node = evt.target; |
| 236 if (!node || !this.shouldOutput_(evt)) | 218 if (!node || !this.shouldOutput_(evt)) |
| 237 return; | 219 return; |
| 238 | 220 |
| 239 var range = cursors.Range.fromNode(node); | 221 var range = cursors.Range.fromNode(node); |
| 240 | 222 |
| 241 new Output().withSpeechAndBraille(range, null, evt.type).go(); | 223 new Output().withSpeechAndBraille(range, null, evt.type).go(); |
| 242 }, | 224 }, |
| 243 | 225 |
| 244 /** | 226 /** |
| 245 * Provides all feedback once a checked state changed event fires. | 227 * Provides all feedback once a checked state changed event fires. |
| 246 * @param {!AutomationEvent} evt | 228 * @param {!AutomationEvent} evt |
| 247 */ | 229 */ |
| 248 onCheckedStateChanged: function(evt) { | 230 onCheckedStateChanged: function(evt) { |
| 249 if (!AutomationPredicate.checkable(evt.target)) | 231 if (!AutomationPredicate.checkable(evt.target)) |
| 250 return; | 232 return; |
| 251 | 233 |
| 252 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.CATEGORY_FLUSH); | 234 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.CATEGORY_FLUSH); |
| 253 var event = new CustomAutomationEvent( | 235 this.onEventIfInRange( |
| 254 EventType.CHECKED_STATE_CHANGED, evt.target, evt.eventFrom); | 236 new chrome.automation.AutomationEvent( |
| 255 this.onEventIfInRange(event); | 237 EventType.checkedStateChanged, evt.target, evt.eventFrom)); |
| 256 }, | 238 }, |
| 257 | 239 |
| 258 /** | 240 /** |
| 259 * Provides all feedback once a focus event fires. | 241 * Provides all feedback once a focus event fires. |
| 260 * @param {!AutomationEvent} evt | 242 * @param {!AutomationEvent} evt |
| 261 */ | 243 */ |
| 262 onFocus: function(evt) { | 244 onFocus: function(evt) { |
| 263 // Invalidate any previous editable text handler state. | 245 // Invalidate any previous editable text handler state. |
| 264 this.textEditHandler_ = null; | 246 this.textEditHandler_ = null; |
| 265 | 247 |
| 266 var node = evt.target; | 248 var node = evt.target; |
| 267 | 249 |
| 268 // Discard focus events on embeddedObject. | 250 // Discard focus events on embeddedObject. |
| 269 if (node.role == RoleType.EMBEDDED_OBJECT) | 251 if (node.role == RoleType.embeddedObject) |
| 270 return; | 252 return; |
| 271 | 253 |
| 272 this.createTextEditHandlerIfNeeded_(evt.target); | 254 this.createTextEditHandlerIfNeeded_(evt.target); |
| 273 | 255 |
| 274 // Category flush speech triggered by events with no source. This includes | 256 // Category flush speech triggered by events with no source. This includes |
| 275 // views. | 257 // views. |
| 276 if (evt.eventFrom == '') | 258 if (evt.eventFrom == '') |
| 277 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.CATEGORY_FLUSH); | 259 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.CATEGORY_FLUSH); |
| 278 if (!node.root) | 260 if (!node.root) |
| 279 return; | 261 return; |
| 280 | 262 |
| 281 var root = AutomationUtil.getTopLevelRoot(node.root); | 263 var root = AutomationUtil.getTopLevelRoot(node.root); |
| 264 |
| 282 // If we're crossing out of a root, save it in case it needs recovering. | 265 // If we're crossing out of a root, save it in case it needs recovering. |
| 283 var prevRange = ChromeVoxState.instance.currentRange; | 266 var prevRange = ChromeVoxState.instance.currentRange; |
| 284 var prevNode = prevRange ? prevRange.start.node : null; | 267 var prevNode = prevRange ? prevRange.start.node : null; |
| 285 if (prevNode) { | 268 if (prevNode) { |
| 286 var prevRoot = AutomationUtil.getTopLevelRoot(prevNode); | 269 var prevRoot = AutomationUtil.getTopLevelRoot(prevNode); |
| 287 if (prevRoot && prevRoot !== root) | 270 if (prevRoot && prevRoot !== root) |
| 288 ChromeVoxState.instance.focusRecoveryMap.set(prevRoot, prevRange); | 271 ChromeVoxState.instance.focusRecoveryMap.set(prevRoot, prevRange); |
| 289 } | 272 } |
| 273 |
| 290 // If a previous node was saved for this focus, restore it. | 274 // If a previous node was saved for this focus, restore it. |
| 291 var savedRange = ChromeVoxState.instance.focusRecoveryMap.get(root); | 275 var savedRange = ChromeVoxState.instance.focusRecoveryMap.get(root); |
| 292 ChromeVoxState.instance.focusRecoveryMap.delete(root); | 276 ChromeVoxState.instance.focusRecoveryMap.delete(root); |
| 293 if (savedRange) { | 277 if (savedRange) { |
| 294 ChromeVoxState.instance.navigateToRange(savedRange, false); | 278 ChromeVoxState.instance.navigateToRange(savedRange, false); |
| 295 return; | 279 return; |
| 296 } | 280 } |
| 297 var event = new CustomAutomationEvent(EventType.FOCUS, node, evt.eventFrom); | 281 |
| 298 this.onEventDefault(event); | 282 this.onEventDefault(new chrome.automation.AutomationEvent( |
| 283 EventType.focus, node, evt.eventFrom)); |
| 299 }, | 284 }, |
| 300 | 285 |
| 301 /** | 286 /** |
| 302 * Provides all feedback once a load complete event fires. | 287 * Provides all feedback once a load complete event fires. |
| 303 * @param {!AutomationEvent} evt | 288 * @param {!AutomationEvent} evt |
| 304 */ | 289 */ |
| 305 onLoadComplete: function(evt) { | 290 onLoadComplete: function(evt) { |
| 306 chrome.automation.getFocus(function(focus) { | 291 chrome.automation.getFocus(function(focus) { |
| 307 if (!focus || !AutomationUtil.isDescendantOf(focus, evt.target)) | 292 if (!focus || !AutomationUtil.isDescendantOf(focus, evt.target)) |
| 308 return; | 293 return; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 322 } | 307 } |
| 323 | 308 |
| 324 // If initial focus was already placed on this page (e.g. if a user starts | 309 // If initial focus was already placed on this page (e.g. if a user starts |
| 325 // tabbing before load complete), then don't move ChromeVox's position on | 310 // tabbing before load complete), then don't move ChromeVox's position on |
| 326 // the page. | 311 // the page. |
| 327 if (ChromeVoxState.instance.currentRange && | 312 if (ChromeVoxState.instance.currentRange && |
| 328 ChromeVoxState.instance.currentRange.start.node.root == focus.root) | 313 ChromeVoxState.instance.currentRange.start.node.root == focus.root) |
| 329 return; | 314 return; |
| 330 | 315 |
| 331 var o = new Output(); | 316 var o = new Output(); |
| 332 if (focus.role == RoleType.ROOT_WEB_AREA) { | 317 if (focus.role == RoleType.rootWebArea) { |
| 333 // Restore to previous position. | 318 // Restore to previous position. |
| 334 var url = focus.docUrl; | 319 var url = focus.docUrl; |
| 335 url = url.substring(0, url.indexOf('#')) || url; | 320 url = url.substring(0, url.indexOf('#')) || url; |
| 336 var pos = cvox.ChromeVox.position[url]; | 321 var pos = cvox.ChromeVox.position[url]; |
| 337 if (pos) { | 322 if (pos) { |
| 338 focus = AutomationUtil.hitTest(focus.root, pos) || focus; | 323 focus = AutomationUtil.hitTest(focus.root, pos) || focus; |
| 339 if (focus != focus.root) | 324 if (focus != focus.root) |
| 340 o.format('$name', focus.root); | 325 o.format('$name', focus.root); |
| 341 } | 326 } |
| 342 } | 327 } |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 384 if (!ChromeVoxState.instance.currentRange) { | 369 if (!ChromeVoxState.instance.currentRange) { |
| 385 this.onEventDefault(evt); | 370 this.onEventDefault(evt); |
| 386 ChromeVoxState.instance.setCurrentRange( | 371 ChromeVoxState.instance.setCurrentRange( |
| 387 cursors.Range.fromNode(evt.target)); | 372 cursors.Range.fromNode(evt.target)); |
| 388 } | 373 } |
| 389 | 374 |
| 390 this.createTextEditHandlerIfNeeded_(evt.target); | 375 this.createTextEditHandlerIfNeeded_(evt.target); |
| 391 | 376 |
| 392 // Sync the ChromeVox range to the editable, if a selection exists. | 377 // Sync the ChromeVox range to the editable, if a selection exists. |
| 393 var anchorObject = evt.target.root.anchorObject; | 378 var anchorObject = evt.target.root.anchorObject; |
| 394 var anchorOffset = evt.target.root.anchorOffset || 0; | 379 var anchorOffset = evt.target.root.anchorOffset; |
| 395 var focusObject = evt.target.root.focusObject; | 380 var focusObject = evt.target.root.focusObject; |
| 396 var focusOffset = evt.target.root.focusOffset || 0; | 381 var focusOffset = evt.target.root.focusOffset; |
| 397 if (anchorObject && focusObject) { | 382 if (anchorObject && focusObject) { |
| 398 var selectedRange = new cursors.Range( | 383 var selectedRange = new cursors.Range( |
| 399 new cursors.WrappingCursor(anchorObject, anchorOffset), | 384 new cursors.WrappingCursor(anchorObject, anchorOffset), |
| 400 new cursors.WrappingCursor(focusObject, focusOffset)); | 385 new cursors.WrappingCursor(focusObject, focusOffset)); |
| 401 | 386 |
| 402 // Sync ChromeVox range with selection. | 387 // Sync ChromeVox range with selection. |
| 403 ChromeVoxState.instance.setCurrentRange(selectedRange); | 388 ChromeVoxState.instance.setCurrentRange(selectedRange); |
| 404 } | 389 } |
| 405 | 390 |
| 406 // TODO(plundblad): This can currently be null for contenteditables. | 391 // TODO(plundblad): This can currently be null for contenteditables. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 418 if (evt.target.state.editable) { | 403 if (evt.target.state.editable) { |
| 419 this.onEditableChanged_(evt); | 404 this.onEditableChanged_(evt); |
| 420 return; | 405 return; |
| 421 } | 406 } |
| 422 | 407 |
| 423 if (!this.shouldOutput_(evt)) | 408 if (!this.shouldOutput_(evt)) |
| 424 return; | 409 return; |
| 425 | 410 |
| 426 var t = evt.target; | 411 var t = evt.target; |
| 427 if (t.state.focused || | 412 if (t.state.focused || |
| 428 t.root.role == RoleType.DESKTOP || | 413 t.root.role == RoleType.desktop || |
| 429 AutomationUtil.isDescendantOf( | 414 AutomationUtil.isDescendantOf( |
| 430 ChromeVoxState.instance.currentRange.start.node, t)) { | 415 ChromeVoxState.instance.currentRange.start.node, t)) { |
| 431 if (new Date() - this.lastValueChanged_ <= | 416 if (new Date() - this.lastValueChanged_ <= |
| 432 DesktopAutomationHandler.VMIN_VALUE_CHANGE_DELAY_MS) | 417 DesktopAutomationHandler.VMIN_VALUE_CHANGE_DELAY_MS) |
| 433 return; | 418 return; |
| 434 | 419 |
| 435 this.lastValueChanged_ = new Date(); | 420 this.lastValueChanged_ = new Date(); |
| 436 | 421 |
| 437 var output = new Output(); | 422 var output = new Output(); |
| 438 | 423 |
| 439 if (t.root.role == RoleType.DESKTOP) | 424 if (t.root.role == RoleType.desktop) |
| 440 output.withQueueMode(cvox.QueueMode.FLUSH); | 425 output.withQueueMode(cvox.QueueMode.FLUSH); |
| 441 | 426 |
| 442 output.format('$value', evt.target).go(); | 427 output.format('$value', evt.target).go(); |
| 443 } | 428 } |
| 444 }, | 429 }, |
| 445 | 430 |
| 446 /** | 431 /** |
| 447 * Handle updating the active indicator when the document scrolls. | 432 * Handle updating the active indicator when the document scrolls. |
| 448 * @param {!AutomationEvent} evt | 433 * @param {!AutomationEvent} evt |
| 449 */ | 434 */ |
| 450 onScrollPositionChanged: function(evt) { | 435 onScrollPositionChanged: function(evt) { |
| 451 var currentRange = ChromeVoxState.instance.currentRange; | 436 var currentRange = ChromeVoxState.instance.currentRange; |
| 452 if (currentRange && currentRange.isValid() && this.shouldOutput_(evt)) | 437 if (currentRange && currentRange.isValid() && this.shouldOutput_(evt)) |
| 453 new Output().withLocation(currentRange, null, evt.type).go(); | 438 new Output().withLocation(currentRange, null, evt.type).go(); |
| 454 }, | 439 }, |
| 455 | 440 |
| 456 /** | 441 /** |
| 457 * @param {!AutomationEvent} evt | 442 * @param {!AutomationEvent} evt |
| 458 */ | 443 */ |
| 459 onSelection: function(evt) { | 444 onSelection: function(evt) { |
| 460 // Invalidate any previous editable text handler state since some nodes, | 445 // Invalidate any previous editable text handler state since some nodes, |
| 461 // like menuitems, can receive selection while focus remains on an editable | 446 // like menuitems, can receive selection while focus remains on an editable |
| 462 // leading to braille output routing to the editable. | 447 // leading to braille output routing to the editable. |
| 463 this.textEditHandler_ = null; | 448 this.textEditHandler_ = null; |
| 464 | 449 |
| 465 chrome.automation.getFocus(function(focus) { | 450 chrome.automation.getFocus(function(focus) { |
| 466 // Desktop tabs get "selection" when there's a focused webview during tab | 451 // Desktop tabs get "selection" when there's a focused webview during tab |
| 467 // switching. | 452 // switching. |
| 468 if (focus.role == RoleType.WEB_VIEW && evt.target.role == RoleType.TAB) { | 453 if (focus.role == RoleType.webView && evt.target.role == RoleType.tab) { |
| 469 ChromeVoxState.instance.setCurrentRange( | 454 ChromeVoxState.instance.setCurrentRange( |
| 470 cursors.Range.fromNode(focus.firstChild)); | 455 cursors.Range.fromNode(focus.firstChild)); |
| 471 return; | 456 return; |
| 472 } | 457 } |
| 473 | 458 |
| 474 // Some cases (e.g. in overview mode), require overriding the assumption | 459 // Some cases (e.g. in overview mode), require overriding the assumption |
| 475 // that focus is an ancestor of a selection target. | 460 // that focus is an ancestor of a selection target. |
| 476 var override = evt.target.role == RoleType.MENU_ITEM || | 461 var override = evt.target.role == RoleType.menuItem || |
| 477 (evt.target.root == focus.root && | 462 (evt.target.root == focus.root && |
| 478 focus.root.role == RoleType.DESKTOP); | 463 focus.root.role == RoleType.desktop); |
| 479 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH); | 464 Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH); |
| 480 if (override || AutomationUtil.isDescendantOf(evt.target, focus)) | 465 if (override || AutomationUtil.isDescendantOf(evt.target, focus)) |
| 481 this.onEventDefault(evt); | 466 this.onEventDefault(evt); |
| 482 }.bind(this)); | 467 }.bind(this)); |
| 483 }, | 468 }, |
| 484 | 469 |
| 485 /** | 470 /** |
| 486 * Provides all feedback once a menu start event fires. | 471 * Provides all feedback once a menu start event fires. |
| 487 * @param {!AutomationEvent} evt | 472 * @param {!AutomationEvent} evt |
| 488 */ | 473 */ |
| 489 onMenuStart: function(evt) { | 474 onMenuStart: function(evt) { |
| 490 ChromeVoxState.instance.markCurrentRange(); | 475 ChromeVoxState.instance.markCurrentRange(); |
| 491 this.onEventDefault(evt); | 476 this.onEventDefault(evt); |
| 492 }, | 477 }, |
| 493 | 478 |
| 494 /** | 479 /** |
| 495 * Provides all feedback once a menu end event fires. | 480 * Provides all feedback once a menu end event fires. |
| 496 * @param {!AutomationEvent} evt | 481 * @param {!AutomationEvent} evt |
| 497 */ | 482 */ |
| 498 onMenuEnd: function(evt) { | 483 onMenuEnd: function(evt) { |
| 499 this.onEventDefault(evt); | 484 this.onEventDefault(evt); |
| 500 | 485 |
| 501 // This is a work around for Chrome context menus not firing a focus event | 486 // This is a work around for Chrome context menus not firing a focus event |
| 502 // after you close them. | 487 // after you close them. |
| 503 chrome.automation.getFocus(function(focus) { | 488 chrome.automation.getFocus(function(focus) { |
| 504 if (focus) { | 489 if (focus) { |
| 505 var event = new CustomAutomationEvent(EventType.FOCUS, focus, 'page'); | 490 this.onFocus( |
| 506 this.onFocus(event); | 491 new chrome.automation.AutomationEvent( |
| 492 EventType.focus, focus, 'page')); |
| 507 } | 493 } |
| 508 }.bind(this)); | 494 }.bind(this)); |
| 509 }, | 495 }, |
| 510 | 496 |
| 511 /** | 497 /** |
| 512 * Create an editable text handler for the given node if needed. | 498 * Create an editable text handler for the given node if needed. |
| 513 * @param {!AutomationNode} node | 499 * @param {!AutomationNode} node |
| 514 */ | 500 */ |
| 515 createTextEditHandlerIfNeeded_: function(node) { | 501 createTextEditHandlerIfNeeded_: function(node) { |
| 516 if (!this.textEditHandler_ || | 502 if (!this.textEditHandler_ || |
| 517 this.textEditHandler_.node !== node) { | 503 this.textEditHandler_.node !== node) { |
| 518 this.textEditHandler_ = editing.TextEditHandler.createForNode(node); | 504 this.textEditHandler_ = editing.TextEditHandler.createForNode(node); |
| 519 } | 505 } |
| 520 }, | 506 }, |
| 521 | 507 |
| 522 /** | 508 /** |
| 523 * Once an event handler updates ChromeVox's range based on |evt| | 509 * Once an event handler updates ChromeVox's range based on |evt| |
| 524 * which updates mode, returns whether |evt| should be outputted. | 510 * which updates mode, returns whether |evt| should be outputted. |
| 525 * @return {boolean} | 511 * @return {boolean} |
| 526 */ | 512 */ |
| 527 shouldOutput_: function(evt) { | 513 shouldOutput_: function(evt) { |
| 528 var mode = ChromeVoxState.instance.mode; | 514 var mode = ChromeVoxState.instance.mode; |
| 529 // Only output desktop rooted nodes or web nodes for next engine modes. | 515 // Only output desktop rooted nodes or web nodes for next engine modes. |
| 530 return evt.target.root.role == RoleType.DESKTOP || | 516 return evt.target.root.role == RoleType.desktop || |
| 531 (mode == ChromeVoxMode.NEXT || | 517 (mode == ChromeVoxMode.NEXT || |
| 532 mode == ChromeVoxMode.FORCE_NEXT || | 518 mode == ChromeVoxMode.FORCE_NEXT || |
| 533 mode == ChromeVoxMode.CLASSIC_COMPAT); | 519 mode == ChromeVoxMode.CLASSIC_COMPAT); |
| 534 } | 520 } |
| 535 }; | 521 }; |
| 536 | 522 |
| 537 /** | 523 /** |
| 538 * Initializes global state for DesktopAutomationHandler. | 524 * Initializes global state for DesktopAutomationHandler. |
| 539 * @private | 525 * @private |
| 540 */ | 526 */ |
| 541 DesktopAutomationHandler.init_ = function() { | 527 DesktopAutomationHandler.init_ = function() { |
| 542 chrome.automation.getDesktop(function(desktop) { | 528 chrome.automation.getDesktop(function(desktop) { |
| 543 ChromeVoxState.desktopAutomationHandler = | 529 ChromeVoxState.desktopAutomationHandler = |
| 544 new DesktopAutomationHandler(desktop); | 530 new DesktopAutomationHandler(desktop); |
| 545 }); | 531 }); |
| 546 }; | 532 }; |
| 547 | 533 |
| 548 DesktopAutomationHandler.init_(); | 534 DesktopAutomationHandler.init_(); |
| 549 | 535 |
| 550 }); // goog.scope | 536 }); // goog.scope |
| OLD | NEW |