summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js
blob: bab0182a3ff52ef44b5641cf5bfe354d6cdb06d2 (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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

/**
 * These tests ensure that the runAt argument to tabs.executeScript delays
 * script execution until the document has reached the correct state.
 *
 * Since tests of this nature are especially race-prone, it relies on a
 * server-JS script to delay the completion of our test page's load cycle long
 * enough for us to attempt to load our scripts in the earlies phase we support.
 *
 * And since we can't actually rely on that timing, it retries any attempts that
 * fail to load as early as expected, but don't load at any illegal time.
 */

add_task(async function testExecuteScript() {
  let tab = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "about:blank",
    true
  );

  async function background() {
    let tab;

    const BASE =
      "http://mochi.test:8888/browser/browser/components/extensions/test/browser/";
    const URL = BASE + "file_slowed_document.sjs";

    const MAX_TRIES = 10;

    let onUpdatedPromise = (tabId, url, status) => {
      return new Promise(resolve => {
        browser.tabs.onUpdated.addListener(function listener(_, changed, tab) {
          if (tabId == tab.id && changed.status == status && tab.url == url) {
            browser.tabs.onUpdated.removeListener(listener);
            resolve();
          }
        });
      });
    };

    try {
      [tab] = await browser.tabs.query({ active: true, currentWindow: true });

      let success = false;
      for (let tries = 0; !success && tries < MAX_TRIES; tries++) {
        let url = `${URL}?with-iframe&r=${Math.random()}`;

        let loadingPromise = onUpdatedPromise(tab.id, url, "loading");
        let completePromise = onUpdatedPromise(tab.id, url, "complete");

        // TODO: Test allFrames and frameId.

        await browser.tabs.update({ url });
        await loadingPromise;

        let states = await Promise.all(
          [
            // Send the executeScript requests in the reverse order that we expect
            // them to execute in, to avoid them passing only because of timing
            // races.
            browser.tabs.executeScript({
              code: "document.readyState",
              // Testing default `runAt`.
            }),
            browser.tabs.executeScript({
              code: "document.readyState",
              runAt: "document_idle",
            }),
            browser.tabs.executeScript({
              code: "document.readyState",
              runAt: "document_end",
            }),
            browser.tabs.executeScript({
              code: "document.readyState",
              runAt: "document_start",
            }),
          ].reverse()
        );

        browser.test.log(`Got states: ${states}`);

        // Make sure that none of our scripts executed earlier than expected,
        // regardless of retries.
        browser.test.assertTrue(
          states[1] == "interactive" || states[1] == "complete",
          `document_end state is valid: ${states[1]}`
        );
        browser.test.assertTrue(
          states[2] == "interactive" || states[2] == "complete",
          `document_idle state is valid: ${states[2]}`
        );

        // If we have the earliest valid states for each script, we're done.
        // Otherwise, try again.
        success =
          states[0] == "loading" &&
          states[1] == "interactive" &&
          states[2] == "interactive" &&
          states[3] == "interactive";

        await completePromise;
      }

      browser.test.assertTrue(
        success,
        "Got the earliest expected states at least once"
      );

      browser.test.notifyPass("executeScript-runAt");
    } catch (e) {
      browser.test.fail(`Error: ${e} :: ${e.stack}`);
      browser.test.notifyFail("executeScript-runAt");
    }
  }

  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      permissions: ["http://mochi.test/", "tabs"],
    },

    background,
  });

  await extension.startup();

  await extension.awaitFinish("executeScript-runAt");

  await extension.unload();

  BrowserTestUtils.removeTab(tab);
});