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 var AutomationEvent = require('automationEvent').AutomationEvent; | 5 var AutomationEvent = require('automationEvent').AutomationEvent; |
| 6 var automationInternal = | 6 var automationInternal = |
| 7 require('binding').Binding.create('automationInternal').generate(); | 7 require('binding').Binding.create('automationInternal').generate(); |
| 8 var IsInteractPermitted = | 8 var IsInteractPermitted = |
| 9 requireNative('automationInternal').IsInteractPermitted; | 9 requireNative('automationInternal').IsInteractPermitted; |
| 10 | 10 |
| 11 var lastError = require('lastError'); | 11 var lastError = require('lastError'); |
| 12 var logging = requireNative('logging'); | 12 var logging = requireNative('logging'); |
| 13 var schema = requireNative('automationInternal').GetSchemaAdditions(); | 13 var schema = requireNative('automationInternal').GetSchemaAdditions(); |
| 14 var utils = require('utils'); | 14 var utils = require('utils'); |
| 15 | 15 |
| 16 /** | 16 /** |
| 17 * Maps an id in the form: | |
| 18 * <process_id>_<routing_id> | |
| 19 * to an AutomationNode with role type webArea. | |
| 20 * @type {!Object.<string, string>} | |
| 21 */ | |
| 22 var idToWebArea_ = {}; | |
| 23 | |
| 24 /** | |
| 17 * A single node in the Automation tree. | 25 * A single node in the Automation tree. |
| 18 * @param {AutomationRootNodeImpl} root The root of the tree. | 26 * @param {AutomationRootNodeImpl} root The root of the tree. |
| 19 * @constructor | 27 * @constructor |
| 20 */ | 28 */ |
| 21 function AutomationNodeImpl(root) { | 29 function AutomationNodeImpl(root) { |
| 22 this.rootImpl = root; | 30 this.rootImpl = root; |
| 23 this.childIds = []; | 31 this.childIds = []; |
| 24 // Public attributes. No actual data gets set on this object. | 32 // Public attributes. No actual data gets set on this object. |
| 25 this.attributes = {}; | 33 this.attributes = {}; |
| 26 // Internal object holding all attributes. | 34 // Internal object holding all attributes. |
| 27 this.attributesInternal = {}; | 35 this.attributesInternal = {}; |
| 28 this.listeners = {}; | 36 this.listeners = {}; |
| 29 this.location = { left: 0, top: 0, width: 0, height: 0 }; | 37 this.location = { left: 0, top: 0, width: 0, height: 0 }; |
| 30 } | 38 } |
| 31 | 39 |
| 32 AutomationNodeImpl.prototype = { | 40 AutomationNodeImpl.prototype = { |
| 33 id: -1, | 41 id: -1, |
| 34 role: '', | 42 role: '', |
| 35 state: { busy: true }, | 43 state: { busy: true }, |
| 36 isRootNode: false, | 44 isRootNode: false, |
| 45 loaded: false, | |
| 37 | 46 |
| 38 get root() { | 47 get root() { |
| 39 return this.rootImpl.wrapper; | 48 return this.rootImpl.wrapper; |
| 40 }, | 49 }, |
| 41 | 50 |
| 42 parent: function() { | 51 parent: function() { |
| 52 if (this.role == schema.RoleType.rootWebArea) { | |
| 53 var parentId = this.processID + '_' + this.routingID; | |
| 54 if (idToWebArea_[parentId]) | |
| 55 return idToWebArea_[parentId]; | |
| 56 } | |
| 43 return this.rootImpl.get(this.parentID); | 57 return this.rootImpl.get(this.parentID); |
| 44 }, | 58 }, |
| 45 | 59 |
| 46 firstChild: function() { | 60 firstChild: function() { |
| 47 var node = this.rootImpl.get(this.childIds[0]); | 61 return this.lookupWebAreaChild_() || this.rootImpl.get(this.childIds[0]); |
| 48 return node; | |
| 49 }, | 62 }, |
| 50 | 63 |
| 51 lastChild: function() { | 64 lastChild: function() { |
| 52 var childIds = this.childIds; | 65 var childIds = this.childIds; |
| 53 var node = this.rootImpl.get(childIds[childIds.length - 1]); | 66 return this.lookupWebAreaChild_() || |
| 54 return node; | 67 this.rootImpl.get(childIds[childIds.length - 1]); |
| 55 }, | 68 }, |
| 56 | 69 |
| 57 children: function() { | 70 children: function() { |
| 58 var children = []; | 71 var children = []; |
| 59 for (var i = 0, childID; childID = this.childIds[i]; i++) { | 72 for (var i = 0, childID; childID = this.childIds[i]; i++) { |
| 60 logging.CHECK(this.rootImpl.get(childID)); | 73 logging.CHECK(this.rootImpl.get(childID)); |
| 61 children.push(this.rootImpl.get(childID)); | 74 children.push(this.rootImpl.get(childID)); |
| 62 } | 75 } |
| 63 return children; | 76 return children; |
| 64 }, | 77 }, |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 106 removeEventListener: function(eventType, callback) { | 119 removeEventListener: function(eventType, callback) { |
| 107 if (this.listeners[eventType]) { | 120 if (this.listeners[eventType]) { |
| 108 var listeners = this.listeners[eventType]; | 121 var listeners = this.listeners[eventType]; |
| 109 for (var i = 0; i < listeners.length; i++) { | 122 for (var i = 0; i < listeners.length; i++) { |
| 110 if (callback === listeners[i].callback) | 123 if (callback === listeners[i].callback) |
| 111 listeners.splice(i, 1); | 124 listeners.splice(i, 1); |
| 112 } | 125 } |
| 113 } | 126 } |
| 114 }, | 127 }, |
| 115 | 128 |
| 129 load: function(callback) { | |
| 130 if (this.role != schema.RoleType.webArea) | |
| 131 throw 'Unsupported state: can only load webArea nodes.'; | |
| 132 | |
| 133 if (!this.loaded) | |
| 134 automationInternal.enableFrame(this.processID, this.routingID); | |
| 135 | |
| 136 automationUtil.onTreeLoaded(this.processID, this.routingID, function(root) { | |
|
aboxhall
2014/10/29 19:29:11
What if instead of creating the AutomationRootNode
David Tseng
2014/10/29 20:35:30
Could you explain further?
The webArea node hosts
aboxhall
2014/10/29 20:53:29
So if I understand correctly, the webArea node has
David Tseng
2014/10/29 21:05:39
Good questions.
Again, to do that during unserial
aboxhall
2014/10/29 21:33:08
I don't think it would be that hard to do the post
David Tseng
2014/10/29 21:47:47
Right, it wouldn't be that hard at all...but the n
| |
| 137 if (callback) | |
|
aboxhall
2014/10/29 19:29:11
Perhaps onTreeLoaded should check for null/undefin
David Tseng
2014/10/29 20:35:30
Done.
| |
| 138 callback(root); | |
| 139 }.bind(this)); | |
| 140 }, | |
| 141 | |
| 116 dispatchEvent: function(eventType) { | 142 dispatchEvent: function(eventType) { |
| 117 var path = []; | 143 var path = []; |
| 118 var parent = this.parent(); | 144 var parent = this.parent(); |
| 119 while (parent) { | 145 while (parent) { |
| 120 path.push(parent); | 146 path.push(parent); |
| 121 // TODO(aboxhall/dtseng): handle unloaded parent node | 147 // TODO(aboxhall/dtseng): handle unloaded parent node |
| 122 parent = parent.parent(); | 148 parent = parent.parent(); |
| 123 } | 149 } |
| 124 var event = new AutomationEvent(eventType, this.wrapper); | 150 var event = new AutomationEvent(eventType, this.wrapper); |
| 125 | 151 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 137 | 163 |
| 138 toString: function() { | 164 toString: function() { |
| 139 return 'node id=' + this.id + | 165 return 'node id=' + this.id + |
| 140 ' role=' + this.role + | 166 ' role=' + this.role + |
| 141 ' state=' + $JSON.stringify(this.state) + | 167 ' state=' + $JSON.stringify(this.state) + |
| 142 ' parentID=' + this.parentID + | 168 ' parentID=' + this.parentID + |
| 143 ' childIds=' + $JSON.stringify(this.childIds) + | 169 ' childIds=' + $JSON.stringify(this.childIds) + |
| 144 ' attributes=' + $JSON.stringify(this.attributes); | 170 ' attributes=' + $JSON.stringify(this.attributes); |
| 145 }, | 171 }, |
| 146 | 172 |
| 173 lookupWebAreaChild_: function() { | |
| 174 if (this.role != schema.RoleType.webArea) | |
| 175 return null; | |
| 176 | |
| 177 return automationUtil.idToAutomationRootNode[ | |
| 178 automationUtil.createAutomationRootNodeID( | |
| 179 this.processID, this.routingID)]; | |
| 180 }, | |
| 181 | |
| 147 dispatchEventAtCapturing_: function(event, path) { | 182 dispatchEventAtCapturing_: function(event, path) { |
| 148 privates(event).impl.eventPhase = Event.CAPTURING_PHASE; | 183 privates(event).impl.eventPhase = Event.CAPTURING_PHASE; |
| 149 for (var i = path.length - 1; i >= 0; i--) { | 184 for (var i = path.length - 1; i >= 0; i--) { |
| 150 this.fireEventListeners_(path[i], event); | 185 this.fireEventListeners_(path[i], event); |
| 151 if (privates(event).impl.propagationStopped) | 186 if (privates(event).impl.propagationStopped) |
| 152 return false; | 187 return false; |
| 153 } | 188 } |
| 154 return true; | 189 return true; |
| 155 }, | 190 }, |
| 156 | 191 |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 346 null, | 381 null, |
| 347 chrome); | 382 chrome); |
| 348 return false; | 383 return false; |
| 349 } | 384 } |
| 350 return true; | 385 return true; |
| 351 }, | 386 }, |
| 352 | 387 |
| 353 destroy: function() { | 388 destroy: function() { |
| 354 this.dispatchEvent(schema.EventType.destroyed); | 389 this.dispatchEvent(schema.EventType.destroyed); |
| 355 this.invalidate_(this.wrapper); | 390 this.invalidate_(this.wrapper); |
| 391 idToWebArea_[this.processID + | |
| 392 '_' + | |
| 393 this.routingID] = undefined; | |
| 356 }, | 394 }, |
| 357 | 395 |
| 358 onAccessibilityEvent: function(eventParams) { | 396 onAccessibilityEvent: function(eventParams) { |
| 359 if (!this.unserialize(eventParams.update)) { | 397 if (!this.unserialize(eventParams.update)) { |
| 360 logging.WARNING('unserialization failed'); | 398 logging.WARNING('unserialization failed'); |
| 361 return false; | 399 return false; |
| 362 } | 400 } |
| 363 | 401 |
| 364 var targetNode = this.get(eventParams.targetID); | 402 var targetNode = this.get(eventParams.targetID); |
| 365 if (targetNode) { | 403 if (targetNode) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 399 | 437 |
| 400 // Retrieve the internal AutomationNodeImpl instance for this node. | 438 // Retrieve the internal AutomationNodeImpl instance for this node. |
| 401 // This object is not accessible outside of bindings code, but we can access | 439 // This object is not accessible outside of bindings code, but we can access |
| 402 // it here. | 440 // it here. |
| 403 var nodeImpl = privates(node).impl; | 441 var nodeImpl = privates(node).impl; |
| 404 var id = nodeImpl.id; | 442 var id = nodeImpl.id; |
| 405 for (var key in AutomationAttributeDefaults) { | 443 for (var key in AutomationAttributeDefaults) { |
| 406 nodeImpl[key] = AutomationAttributeDefaults[key]; | 444 nodeImpl[key] = AutomationAttributeDefaults[key]; |
| 407 } | 445 } |
| 408 nodeImpl.childIds = []; | 446 nodeImpl.childIds = []; |
| 409 nodeImpl.loaded = false; | |
| 410 nodeImpl.id = id; | 447 nodeImpl.id = id; |
| 411 delete this.axNodeDataCache_[id]; | 448 delete this.axNodeDataCache_[id]; |
| 412 }, | 449 }, |
| 413 | 450 |
| 414 load: function(callback) { | |
| 415 // TODO(dtseng/aboxhall): Implement. | |
| 416 if (!this.loaded) | |
| 417 throw 'Unsupported state: root node is not loaded.'; | |
| 418 | |
| 419 setTimeout(callback, 0); | |
| 420 }, | |
| 421 | |
| 422 deleteOldChildren_: function(node, newChildIds) { | 451 deleteOldChildren_: function(node, newChildIds) { |
| 423 // Create a set of child ids in |src| for fast lookup, and return false | 452 // Create a set of child ids in |src| for fast lookup, and return false |
| 424 // if a duplicate is found; | 453 // if a duplicate is found; |
| 425 var newChildIdSet = {}; | 454 var newChildIdSet = {}; |
| 426 for (var i = 0; i < newChildIds.length; i++) { | 455 for (var i = 0; i < newChildIds.length; i++) { |
| 427 var childId = newChildIds[i]; | 456 var childId = newChildIds[i]; |
| 428 if (newChildIdSet[childId]) { | 457 if (newChildIdSet[childId]) { |
| 429 logging.WARNING('Node ' + privates(node).impl.id + | 458 logging.WARNING('Node ' + privates(node).impl.id + |
| 430 ' has duplicate child id ' + childId); | 459 ' has duplicate child id ' + childId); |
| 431 lastError.set('automation', | 460 lastError.set('automation', |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 488 } | 517 } |
| 489 privates(childNode).impl.indexInParent = i; | 518 privates(childNode).impl.indexInParent = i; |
| 490 privates(childNode).impl.parentID = privates(node).impl.id; | 519 privates(childNode).impl.parentID = privates(node).impl.id; |
| 491 } | 520 } |
| 492 | 521 |
| 493 return success; | 522 return success; |
| 494 }, | 523 }, |
| 495 | 524 |
| 496 setData_: function(node, nodeData) { | 525 setData_: function(node, nodeData) { |
| 497 var nodeImpl = privates(node).impl; | 526 var nodeImpl = privates(node).impl; |
| 527 | |
| 528 if (nodeData.role == schema.RoleType.webArea) { | |
| 529 nodeImpl.processID = nodeData.intAttributes.frameId; | |
| 530 nodeImpl.routingID = nodeData.intAttributes.childFrameId; | |
| 531 idToWebArea_[nodeImpl.processID + | |
| 532 '_' + | |
| 533 nodeImpl.routingID] = node; | |
| 534 delete nodeData.intAttributes['processId']; | |
| 535 delete nodeData.intAttributes['routingId']; | |
| 536 } | |
| 498 for (var key in AutomationAttributeDefaults) { | 537 for (var key in AutomationAttributeDefaults) { |
| 499 if (key in nodeData) | 538 if (key in nodeData) |
| 500 nodeImpl[key] = nodeData[key]; | 539 nodeImpl[key] = nodeData[key]; |
| 501 else | 540 else |
| 502 nodeImpl[key] = AutomationAttributeDefaults[key]; | 541 nodeImpl[key] = AutomationAttributeDefaults[key]; |
| 503 } | 542 } |
| 504 for (var i = 0; i < AutomationAttributeTypes.length; i++) { | 543 for (var i = 0; i < AutomationAttributeTypes.length; i++) { |
| 505 var attributeType = AutomationAttributeTypes[i]; | 544 var attributeType = AutomationAttributeTypes[i]; |
| 506 for (var attributeName in nodeData[attributeType]) { | 545 for (var attributeName in nodeData[attributeType]) { |
| 507 nodeImpl.attributesInternal[attributeName] = | 546 nodeImpl.attributesInternal[attributeName] = |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 592 'firstChild', | 631 'firstChild', |
| 593 'lastChild', | 632 'lastChild', |
| 594 'children', | 633 'children', |
| 595 'previousSibling', | 634 'previousSibling', |
| 596 'nextSibling', | 635 'nextSibling', |
| 597 'doDefault', | 636 'doDefault', |
| 598 'focus', | 637 'focus', |
| 599 'makeVisible', | 638 'makeVisible', |
| 600 'setSelection', | 639 'setSelection', |
| 601 'addEventListener', | 640 'addEventListener', |
| 602 'removeEventListener'], | 641 'removeEventListener', |
| 642 'load'], | |
| 603 readonly: ['isRootNode', | 643 readonly: ['isRootNode', |
| 604 'role', | 644 'role', |
| 605 'state', | 645 'state', |
| 606 'location', | 646 'location', |
| 607 'attributes', | 647 'attributes', |
| 648 'loaded', | |
| 608 'root'] }); | 649 'root'] }); |
| 609 | 650 |
| 610 var AutomationRootNode = utils.expose('AutomationRootNode', | 651 var AutomationRootNode = utils.expose('AutomationRootNode', |
| 611 AutomationRootNodeImpl, | 652 AutomationRootNodeImpl, |
| 612 { superclass: AutomationNode, | 653 { superclass: AutomationNode, |
| 613 functions: ['load'], | 654 functions: ['load'] }); |
| 614 readonly: ['loaded'] }); | |
| 615 | 655 |
| 616 exports.AutomationNode = AutomationNode; | 656 exports.AutomationNode = AutomationNode; |
| 617 exports.AutomationRootNode = AutomationRootNode; | 657 exports.AutomationRootNode = AutomationRootNode; |
| OLD | NEW |