summaryrefslogtreecommitdiffstats
path: root/js/src/tests/web-platform-test-shims.js
blob: 97122ab860eb84c74a02f123cd8b1194b002cad2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// This is used when jstests.py runs web-platform-tests in the shell.
// Some tests require browser features that the shell doesn't have built-in.

// window.self
// <https://html.spec.whatwg.org/multipage/window-object.html#dom-self>
if (!("self" in this)) {
  this.self = this;
}

// window.setTimeout, clearTimeout, setInterval, clearInterval
// <https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout>
if (!("setTimeout" in this)) {
  // Not-very-compliant polyfill for setTimeout, based on Promise.

  // When this array becomes non-empty, call scheduleWork.
  let timeouts = [];

  // Fake clock used to order timeouts.
  let now = 0;

  // Least positive integer not yet used as a timer id.
  let next_id = 1;

  // Add an active timer record to timeouts.
  function enqueue(id, t, cb) {
    if (typeof cb !== 'function') {
      let code = "" + cb;
      cb = () => eval(code);
    }
    t += now;
    timeouts.push({id, t, cb});
    timeouts.sort((a, b) => a.t - b.t);
    if (timeouts.length === 1) {
      scheduleWork();
    }
  }

  // Register a microtask to make sure timers are actually processed. There
  // should be at most one of these microtasks pending at a time.
  function scheduleWork() {
    // Number of times to let the microtask queue spin before running a
    // timeout. The idea is to let all Promises resolve between consecutive
    // timer callbacks. Of course, this is a hack; whatever number is used
    // here, an async function can simply await that many times plus one to
    // observe that timeouts are being handled as microtasks, not events.
    let delay = 10;

    function handleTimeouts() {
      if (--delay > 0) {
        Promise.resolve().then(handleTimeouts);
        return;
      }
      if (timeouts.length > 0) {
        let {t, cb} = timeouts.shift();
        if (now < t) {
          now = t;
        }
        if (timeouts.length > 0) {
          scheduleWork();  // There's more to do after this.
        }
        cb();
      }
    }
    Promise.resolve().then(handleTimeouts);
  }

  this.setTimeout = function (cb, t, ...args) {
    let id = next_id++;
    enqueue(id, t, () => cb(...args));
    return id;
  };

  this.clearTimeout = function (id) {
    timeouts = timeouts.filter(obj => obj.id !== id);
  };

  this.setInterval = function (cb, t, ...args) {
    let id = next_id++;
    let func = () => {
      enqueue(id, t, func);
      cb(...args);
    };
    enqueue(id, t, func);
    return id;
  };

  this.clearInterval = this.clearTimeout;
}

// Some tests in web platform tests leave promise rejections unhandled.
if ("ignoreUnhandledRejections" in this) {
  ignoreUnhandledRejections();
}