1// Copyright (c) 2013 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.
4
5/**
6 * Enum for WebDriver status codes.
7 * @enum {number}
8 */
9var StatusCode = {
10  OK: 0,
11  UNKNOWN_ERROR: 13,
12  JAVASCRIPT_ERROR: 17,
13  SCRIPT_TIMEOUT: 28,
14};
15
16/**
17 * Dictionary key for asynchronous script info.
18 * @const
19 */
20var ASYNC_INFO_KEY = '$chrome_asyncScriptInfo';
21
22/**
23* Return the information of asynchronous script execution.
24*
25* @return {Object.<string, ?>} Information of asynchronous script execution.
26*/
27function getAsyncScriptInfo() {
28  if (!(ASYNC_INFO_KEY in document))
29    document[ASYNC_INFO_KEY] = {'id': 0};
30  return document[ASYNC_INFO_KEY];
31}
32
33/**
34* Execute the given script and save its asynchronous result.
35*
36* If script1 finishes after script2 is executed, then script1's result will be
37* discarded while script2's will be saved.
38*
39* @param {string} script The asynchronous script to be executed. The script
40*     should be a proper function body. It will be wrapped in a function and
41*     invoked with the given arguments and, as the final argument, a callback
42*     function to invoke to report the asynchronous result.
43* @param {!Array.<*>} args Arguments to be passed to the script.
44* @param {boolean} isUserSupplied Whether the script is supplied by the user.
45*     If not, UnknownError will be used instead of JavaScriptError if an
46*     exception occurs during the script, and an additional error callback will
47*     be supplied to the script.
48* @param {?number} opt_timeoutMillis The timeout, in milliseconds, to use.
49*     If the timeout is exceeded and the callback has not been invoked, a error
50*     result will be saved and future invocation of the callback will be
51*     ignored.
52*/
53function executeAsyncScript(script, args, isUserSupplied, opt_timeoutMillis) {
54  var info = getAsyncScriptInfo();
55  info.id++;
56  delete info.result;
57  var id = info.id;
58
59  function report(status, value) {
60    if (id != info.id)
61      return;
62    info.id++;
63    info.result = {status: status, value: value};
64  }
65  function reportValue(value) {
66    report(StatusCode.OK, value);
67  }
68  function reportScriptError(error) {
69    var code = isUserSupplied ? StatusCode.JAVASCRIPT_ERROR :
70                                (error.code || StatusCode.UNKNOWN_ERROR);
71    var message = error.message;
72    if (error.stack) {
73      message += "\nJavaScript stack:\n" + error.stack;
74    }
75    report(code, message);
76  }
77  args.push(reportValue);
78  if (!isUserSupplied)
79    args.push(reportScriptError);
80
81  try {
82    new Function(script).apply(null, args);
83  } catch (error) {
84    reportScriptError(error);
85    return;
86  }
87
88  if (typeof(opt_timeoutMillis) != 'undefined') {
89    window.setTimeout(function() {
90      var code = isUserSupplied ? StatusCode.SCRIPT_TIMEOUT :
91                                  StatusCode.UNKNOWN_ERROR;
92      var errorMsg = 'result was not received in ' + opt_timeoutMillis / 1000 +
93                     ' seconds';
94      report(code, errorMsg);
95    }, opt_timeoutMillis);
96  }
97}
98