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.
6 * Enum for WebDriver status codes.
10 STALE_ELEMENT_REFERENCE: 10,
15 * Enum for node types.
24 * Dictionary key to use for holding an element ID.
28 var ELEMENT_KEY = 'ELEMENT';
31 * A cache which maps IDs <-> cached objects for the purpose of identifying
32 * a script object remotely.
38 this.idPrefix_ = Math.random().toString();
44 * Stores a given item in the cache and returns a unique ID.
46 * @param {!Object} item The item to store in the cache.
47 * @return {number} The ID for the cached item.
49 storeItem: function(item) {
50 for (var i in this.cache_) {
51 if (item == this.cache_[i])
54 var id = this.idPrefix_ + ':' + this.nextId_;
55 this.cache_[id] = item;
61 * Retrieves the cached object for the given ID.
63 * @param {number} id The ID for the cached item to retrieve.
64 * @return {!Object} The retrieved item.
66 retrieveItem: function(id) {
67 var item = this.cache_[id];
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';
77 * Clears stale items from the cache.
79 clearStale: function() {
80 for (var id in this.cache_) {
81 var node = this.cache_[id];
85 node = node.parentNode;
88 delete this.cache_[id];
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.
99 function getPageCache(opt_doc) {
100 var doc = opt_doc || document;
101 // We use the same key as selenium's javascript/atoms/inject.js.
104 doc[key] = new Cache();
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.
115 function wrap(value) {
116 if (typeof(value) == 'object' && value != null) {
117 var nodeType = value['nodeType'];
118 if (nodeType == NodeType.ELEMENT || nodeType == NodeType.DOCUMENT) {
120 wrapped[ELEMENT_KEY] = getPageCache(value.ownerDocument).storeItem(value);
124 var obj = (typeof(value.length) == 'number') ? [] : {};
125 for (var prop in value)
126 obj[prop] = wrap(value[prop]);
133 * Unwraps the given value by converting from object IDs to the cached
136 * @param {*} value The value to unwrap.
137 * @param {Cache} cache The cache to retrieve wrapped elements from.
138 * @return {*} The unwrapped value.
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);
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
171 function callFunction(func, args, opt_unwrappedReturn) {
172 var cache = getPageCache();
175 if (opt_unwrappedReturn)
176 return func.apply(null, unwrap(args, cache));
180 var returnValue = wrap(func.apply(null, unwrap(args, cache)));
182 status = error.code || StatusCode.UNKNOWN_ERROR;
183 var returnValue = error.message;