summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/speculation-rules/prerender/resources/deferred-promise-utils.js
blob: 19bc981a2ace513ce33f71d5f86aae292b3e8f1d (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
/**
 * This file co-works with a html file and utils.js to test a promise that
 * should be deferred during prerendering.
 *
 * Usage example:
 *  Suppose the html is "prerender-promise-test.html"
 *  On prerendering page, prerender-promise-test.html?prerendering:
 *    const prerenderEventCollector = new PrerenderEventCollector();
 *    const promise = {a promise that should be deferred during prerendering};
 *    prerenderEventCollector.start(promise, {promise name});
 *
 *  On the initiator page, prerender-promise-test.html:
 *   execute
 *    `loadInitiatorPage();`
 */

// Collects events that happen relevant to a prerendering page.
// An event is added when:
// 1. start() is called.
// 2. a prerenderingchange event is dispatched on this document.
// 3. the promise passed to start() is resolved.
// 4. addEvent() is called manually.
class PrerenderEventCollector {
  constructor() {
    this.eventsSeen_ = [];
    new PrerenderChannel('close').addEventListener('message', () => {
      window.close();
    });
  }

  // Adds an event to `eventsSeen_` along with the prerendering state of the
  // page.
  addEvent(eventMessage) {
    this.eventsSeen_.push(
        {event: eventMessage, prerendering: document.prerendering});
  }

  // Starts collecting events until the promise resolves. Triggers activation by
  // telling the initiator page that it is ready for activation.
  async start(promise, promiseName) {
    assert_true(document.prerendering);
    this.addEvent(`started waiting ${promiseName}`);
    promise
        .then(
            () => {
              this.addEvent(`finished waiting ${promiseName}`);
            },
            (error) => {
              if (error instanceof Error)
                error = error.name;
              this.addEvent(`${promiseName} rejected: ${error}`);
            })
        .finally(() => {
          // Used to communicate with the main test page.
          const testChannel = new PrerenderChannel('test-channel');
          // Send the observed events back to the main test page.
          testChannel.postMessage(this.eventsSeen_);
          testChannel.close();
        });
    document.addEventListener('prerenderingchange', () => {
      this.addEvent('prerendering change');
    });

    // Post a task to give the implementation a chance to fail in case it
    // resolves a promise without waiting for activation.
    setTimeout(() => {
      // Used to communicate with the initiator page.
      const prerenderChannel = new PrerenderChannel('prerender-channel');
      // Inform the initiator page that this page is ready to be activated.
      prerenderChannel.postMessage('readyToActivate');
      prerenderChannel.close();
    }, 0);
  }
}