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

Side by Side Diff: chrome/renderer/resources/extensions/automation/automation_node.js

Issue 667713006: Implement automatic load of composed/embedded automation trees (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkcr
Patch Set: Created 6 years, 1 month 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 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
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
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
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
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
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
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;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698