Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / renderer / resources / extensions / automation / automation_node.js
index 19e4cb8..5f0c834 100644 (file)
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var AutomationEvent = require('automationEvent').AutomationEvent;
+var automationInternal =
+    require('binding').Binding.create('automationInternal').generate();
 var utils = require('utils');
 
 /**
@@ -11,56 +14,202 @@ var utils = require('utils');
  */
 var AutomationNodeImpl = function(owner) {
   this.owner = owner;
-  this.child_ids = [];
-
+  this.childIds = [];
   this.attributes = {};
+  this.listeners = {};
+  this.location = { left: 0, top: 0, width: 0, height: 0 };
 };
 
 AutomationNodeImpl.prototype = {
+  id: -1,
+  role: '',
+  loaded: false,
+  state: { busy: true },
+
   parent: function() {
-    return this.owner.get(this.parent_id);
+    return this.owner.get(this.parentID);
   },
 
   firstChild: function() {
-    var node = this.owner.get(this.child_ids[0]);
+    var node = this.owner.get(this.childIds[0]);
     return node;
   },
 
   lastChild: function() {
-    var child_ids = this.child_ids;
-    var node = this.owner.get(child_ids[child_ids.length - 1]);
+    var childIds = this.childIds;
+    var node = this.owner.get(childIds[childIds.length - 1]);
     return node;
   },
 
   children: function() {
     var children = [];
-    for (var i = 0, child_id; child_id = this.child_ids[i]; i++)
-      children.push(this.owner.get(child_id));
+    for (var i = 0, childID; childID = this.childIds[i]; i++)
+      children.push(this.owner.get(childID));
     return children;
   },
 
   previousSibling: function() {
     var parent = this.parent();
-    if (parent && this.index_in_parent > 0)
-      return parent.children()[this.index_in_parent - 1];
+    if (parent && this.indexInParent > 0)
+      return parent.children()[this.indexInParent - 1];
     return undefined;
   },
 
   nextSibling: function() {
     var parent = this.parent();
-    if (parent && this.index_in_parent < parent.children().length)
-      return parent.children()[this.index_in_parent + 1];
+    if (parent && this.indexInParent < parent.children().length)
+      return parent.children()[this.indexInParent + 1];
     return undefined;
   },
-};
 
+  doDefault: function() {
+    this.performAction_('doDefault');
+  },
+
+  focus: function() {
+    this.performAction_('focus');
+  },
+
+  makeVisible: function() {
+    this.performAction_('makeVisible');
+  },
+
+  setSelection: function(startIndex, endIndex) {
+    this.performAction_('setSelection',
+                        {startIndex: startIndex, endIndex: endIndex});
+  },
+
+  addEventListener: function(eventType, callback, capture) {
+    this.removeEventListener(eventType, callback);
+    if (!this.listeners[eventType])
+      this.listeners[eventType] = [];
+    this.listeners[eventType].push({callback: callback, capture: capture});
+  },
+
+  // TODO(dtseng/aboxhall): Check this impl against spec.
+  removeEventListener: function(eventType, callback) {
+    if (this.listeners[eventType]) {
+      var listeners = this.listeners[eventType];
+      for (var i = 0; i < listeners.length; i++) {
+        if (callback === listeners[i].callback)
+          listeners.splice(i, 1);
+      }
+    }
+  },
+
+  dispatchEvent: function(eventType) {
+    var path = [];
+    var parent = this.parent();
+    while (parent) {
+      path.push(parent);
+      // TODO(aboxhall/dtseng): handle unloaded parent node
+      parent = parent.parent();
+    }
+    path.push(this.owner.wrapper);
+    var event = new AutomationEvent(eventType, this.wrapper);
+
+    // Dispatch the event through the propagation path in three phases:
+    // - capturing: starting from the root and going down to the target's parent
+    // - targeting: dispatching the event on the target itself
+    // - bubbling: starting from the target's parent, going back up to the root.
+    // At any stage, a listener may call stopPropagation() on the event, which
+    // will immediately stop event propagation through this path.
+    if (this.dispatchEventAtCapturing_(event, path)) {
+      if (this.dispatchEventAtTargeting_(event, path))
+        this.dispatchEventAtBubbling_(event, path);
+    }
+  },
+
+  toString: function() {
+    return 'node id=' + this.id +
+        ' role=' + this.role +
+        ' state=' + JSON.stringify(this.state) +
+        ' childIds=' + JSON.stringify(this.childIds) +
+        ' attributes=' + JSON.stringify(this.attributes);
+  },
+
+  dispatchEventAtCapturing_: function(event, path) {
+    privates(event).impl.eventPhase = Event.CAPTURING_PHASE;
+    for (var i = path.length - 1; i >= 0; i--) {
+      this.fireEventListeners_(path[i], event);
+      if (privates(event).impl.propagationStopped)
+        return false;
+    }
+    return true;
+  },
+
+  dispatchEventAtTargeting_: function(event) {
+    privates(event).impl.eventPhase = Event.AT_TARGET;
+    this.fireEventListeners_(this.wrapper, event);
+    return !privates(event).impl.propagationStopped;
+  },
+
+  dispatchEventAtBubbling_: function(event, path) {
+    privates(event).impl.eventPhase = Event.BUBBLING_PHASE;
+    for (var i = 0; i < path.length; i++) {
+      this.fireEventListeners_(path[i], event);
+      if (privates(event).impl.propagationStopped)
+        return false;
+    }
+    return true;
+  },
+
+  fireEventListeners_: function(node, event) {
+    var nodeImpl = privates(node).impl;
+    var listeners = nodeImpl.listeners[event.type];
+    if (!listeners)
+      return;
+
+    var eventPhase = event.eventPhase;
+    for (var i = 0; i < listeners.length; i++) {
+      if (eventPhase == Event.CAPTURING_PHASE && !listeners[i].capture)
+        continue;
+      if (eventPhase == Event.BUBBLING_PHASE && listeners[i].capture)
+        continue;
+
+      try {
+        listeners[i].callback(event);
+      } catch (e) {
+        console.error('Error in event handler for ' + event.type +
+                      'during phase ' + eventPhase + ': ' +
+                      e.message + '\nStack trace: ' + e.stack);
+      }
+    }
+  },
+
+  performAction_: function(actionType, opt_args) {
+    // Not yet initialized.
+    if (!this.owner.processID ||
+        !this.owner.routingID ||
+        !this.wrapper.id)
+      return;
+    automationInternal.performAction({processID: this.owner.processID,
+                                      routingID: this.owner.routingID,
+                                      automationNodeID: this.wrapper.id,
+                                      actionType: actionType},
+                                     opt_args || {});
+  }
+};
 
 var AutomationNode = utils.expose('AutomationNode',
                                   AutomationNodeImpl,
-                                  ['parent',
-                                   'firstChild',
-                                   'lastChild',
-                                   'children',
-                                   'previousSibling',
-                                   'nextSibling']);
+                                  { functions: ['parent',
+                                                'firstChild',
+                                                'lastChild',
+                                                'children',
+                                                'previousSibling',
+                                                'nextSibling',
+                                                'doDefault',
+                                                'focus',
+                                                'makeVisible',
+                                                'setSelection',
+                                                'addEventListener',
+                                                'removeEventListener'],
+                                    readonly: ['id',
+                                               'role',
+                                               'state',
+                                               'location',
+                                               'attributes',
+                                               'loaded'] });
+
 exports.AutomationNode = AutomationNode;