Suppression for crbug/241044.
[chromium-blink-merge.git] / chrome / test / chromedriver / js / call_function.js
blobd9aa957e68af4d513108a431f1a16c0a43e01520
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * Enum for WebDriver status codes.
7  * @enum {number}
8  */
9 var StatusCode = {
10   STALE_ELEMENT_REFERENCE: 10,
11   UNKNOWN_ERROR: 13,
14 /**
15  * Enum for node types.
16  * @enum {number}
17  */
18 var NodeType = {
19   ELEMENT: 1,
20   DOCUMENT: 9,
23 /**
24  * Dictionary key to use for holding an element ID.
25  * @const
26  * @type {string}
27  */
28 var ELEMENT_KEY = 'ELEMENT';
30 /**
31  * A cache which maps IDs <-> cached objects for the purpose of identifying
32  * a script object remotely.
33  * @constructor
34  */
35 function Cache() {
36   this.cache_ = {};
37   this.nextId_ = 1;
38   this.idPrefix_ = Math.random().toString();
41 Cache.prototype = {
43   /**
44    * Stores a given item in the cache and returns a unique ID.
45    *
46    * @param {!Object} item The item to store in the cache.
47    * @return {number} The ID for the cached item.
48    */
49   storeItem: function(item) {
50     for (var i in this.cache_) {
51       if (item == this.cache_[i])
52         return i;
53     }
54     var id = this.idPrefix_  + ':' + this.nextId_;
55     this.cache_[id] = item;
56     this.nextId_++;
57     return id;
58   },
60   /**
61    * Retrieves the cached object for the given ID.
62    *
63    * @param {number} id The ID for the cached item to retrieve.
64    * @return {!Object} The retrieved item.
65    */
66   retrieveItem: function(id) {
67     var item = this.cache_[id];
68     if (item)
69       return item;
70     var error = new Error('not in cache');
71     error.code = StatusCode.STALE_ELEMENT_REFERENCE;
72     error.message = 'element is not attached to the page document';
73     throw error;
74   },
76   /**
77    * Clears stale items from the cache.
78    */
79   clearStale: function() {
80     for (var id in this.cache_) {
81       var node = this.cache_[id];
82       while (node) {
83         if (node == document)
84           break;
85         node = node.parentNode;
86       }
87       if (!node)
88         delete this.cache_[id];
89     }
90   }
93 /**
94  * Returns the global object cache for the page.
95  * @param {Document=} opt_doc The document whose cache to retrieve. Defaults to
96  *     the current document.
97  * @return {!Cache} The page's object cache.
98  */
99 function getPageCache(opt_doc) {
100   var doc = opt_doc || document;
101   // We use the same key as selenium's javascript/atoms/inject.js.
102   var key = '$wdc_';
103   if (!(key in doc))
104     doc[key] = new Cache();
105   return doc[key];
109  * Wraps the given value to be transmitted remotely by converting
110  * appropriate objects to cached object IDs.
112  * @param {*} value The value to wrap.
113  * @return {*} The wrapped value.
114  */
115 function wrap(value) {
116   if (typeof(value) == 'object' && value != null) {
117     var nodeType = value['nodeType'];
118     if (nodeType == NodeType.ELEMENT || nodeType == NodeType.DOCUMENT) {
119       var wrapped = {};
120       wrapped[ELEMENT_KEY] = getPageCache(value.ownerDocument).storeItem(value);
121       return wrapped;
122     }
124     var obj = (typeof(value.length) == 'number') ? [] : {};
125     for (var prop in value)
126       obj[prop] = wrap(value[prop]);
127     return obj;
128   }
129   return value;
133  * Unwraps the given value by converting from object IDs to the cached
134  * objects.
136  * @param {*} value The value to unwrap.
137  * @param {Cache} cache The cache to retrieve wrapped elements from.
138  * @return {*} The unwrapped value.
139  */
140 function unwrap(value, cache) {
141   if (typeof(value) == 'object' && value != null) {
142     if (ELEMENT_KEY in value)
143       return cache.retrieveItem(value[ELEMENT_KEY]);
145     var obj = (typeof(value.length) == 'number') ? [] : {};
146     for (var prop in value)
147       obj[prop] = unwrap(value[prop], cache);
148     return obj;
149   }
150   return value;
154  * Calls a given function and returns its value.
156  * The inputs to and outputs of the function will be unwrapped and wrapped
157  * respectively, unless otherwise specified. This wrapping involves converting
158  * between cached object reference IDs and actual JS objects. The cache will
159  * automatically be pruned each call to remove stale references.
161  * @param {function(...[*]) : *} func The function to invoke.
162  * @param {!Array.<*>} args The array of arguments to supply to the function,
163  *     which will be unwrapped before invoking the function.
164  * @param {boolean=} opt_unwrappedReturn Whether the function's return value
165  *     should be left unwrapped.
166  * @return {*} An object containing a status and value property, where status
167  *     is a WebDriver status code and value is the wrapped value. If an
168  *     unwrapped return was specified, this will be the function's pure return
169  *     value.
170  */
171 function callFunction(func, args, opt_unwrappedReturn) {
172   var cache = getPageCache();
173   cache.clearStale();
175   if (opt_unwrappedReturn)
176     return func.apply(null, unwrap(args, cache));
178   var status = 0;
179   try {
180     var returnValue = wrap(func.apply(null, unwrap(args, cache)));
181   } catch (error) {
182     status = error.code || StatusCode.UNKNOWN_ERROR;
183     var returnValue = error.message;
184   }
185   return {
186       status: status,
187       value: returnValue
188   }