summaryrefslogtreecommitdiffstats
path: root/devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_addons_eventpage_terminate_on_idle.js
blob: 1b43808fa277b35643e438f6cda0b33a15281384 (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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

/* import-globals-from helper-addons.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-addons.js", this);

add_setup(async function () {
  await SpecialPowers.pushPrefEnv({
    set: [["extensions.eventPages.enabled", true]],
  });
});

// Test that an extension event page isn't terminated on idle when a DevTools
// Toolbox is attached to the extension.
add_task(
  async function test_eventpage_no_idle_shutdown_with_toolbox_attached() {
    const { document, tab, window } = await openAboutDebugging();
    await selectThisFirefoxPage(document, window.AboutDebugging.store);

    const EXTENSION_ID = "test-devtools-eventpage@mozilla.org";
    const EXTENSION_NAME = "Temporary EventPage-based web extension";

    const promiseBackgroundLoaded =
      promiseBackgroundContextLoaded(EXTENSION_ID);

    let waitForBGStatusUpdate = promiseBackgroundStatusUpdate(window);

    // Install the extension using an event page (non persistent background page).
    await installTemporaryExtensionFromXPI(
      {
        id: EXTENSION_ID,
        name: EXTENSION_NAME,
        // The extension is expected to have a non persistent background script.
        extraProperties: {
          background: {
            scripts: ["bgpage.js"],
            persistent: false,
          },
        },
        files: {
          "bgpage.js": function () {
            // Emit a dump when the script is loaded to make it easier
            // to investigate intermittents.
            dump(`Background script loaded: ${window.location}\n`);
          },
        },
      },
      document
    );

    const target = findDebugTargetByText(EXTENSION_NAME, document);
    ok(
      !!target,
      "The EventPage-based extension is installed with the expected name"
    );

    info("Wait for the test extension background script to be loaded");
    await promiseBackgroundLoaded;

    info("Wait for the test extension background script status update");
    await waitForBGStatusUpdate;
    await assertBackgroundStatus(EXTENSION_NAME, {
      document,
      expectedStatus: "running",
    });

    waitForBGStatusUpdate = promiseBackgroundStatusUpdate(window);
    await triggerExtensionEventPageIdleTimeout(EXTENSION_ID);
    await waitForBGStatusUpdate;
    await assertBackgroundStatus(EXTENSION_NAME, {
      document,
      expectedStatus: "stopped",
    });

    info(
      "Respawn the extension background script on new WebExtension API event"
    );
    waitForBGStatusUpdate = promiseBackgroundStatusUpdate(window);
    await wakeupExtensionEventPage(EXTENSION_ID);
    await waitForBGStatusUpdate;
    await assertBackgroundStatus(EXTENSION_NAME, {
      document,
      expectedStatus: "running",
    });

    info("Open a DevTools toolbox on the target extension");
    const { devtoolsWindow } = await openAboutDevtoolsToolbox(
      document,
      tab,
      window,
      EXTENSION_NAME
    );

    info(
      "Verify event page terminated on terminate button clicked while the DevTools toolbox is open"
    );
    const terminateButton = target.querySelector(
      ".qa-temporary-extension-terminate-bgscript-button"
    );
    ok(
      !!terminateButton,
      `${EXTENSION_NAME} is expected to have a terminate button`
    );

    info(`Click on the terminate button for ${EXTENSION_NAME}`);
    const promiseBackgroundUnloaded =
      promiseBackgroundContextUnloaded(EXTENSION_ID);
    const waitForTerminateSuccess = waitForDispatch(
      window.AboutDebugging.store,
      "TERMINATE_EXTENSION_BGSCRIPT_SUCCESS"
    );
    waitForBGStatusUpdate = promiseBackgroundStatusUpdate(window);
    terminateButton.click();
    await waitForTerminateSuccess;

    info("Wait for the extension background script to be unloaded");
    await promiseBackgroundUnloaded;
    await waitForBGStatusUpdate;
    await assertBackgroundStatus(EXTENSION_NAME, {
      document,
      expectedStatus: "stopped",
      targetElement: target,
    });

    info(
      "Verify event page isn't terminated on idle while the DevTools toolbox is open"
    );

    // Make sure the event page is running again.
    waitForBGStatusUpdate = promiseBackgroundStatusUpdate(window);
    await wakeupExtensionEventPage(EXTENSION_ID);
    await waitForBGStatusUpdate;
    await assertBackgroundStatus(EXTENSION_NAME, {
      document,
      expectedStatus: "running",
      targetElement: target,
    });

    const waitForBGSuspendIgnored =
      promiseTerminateBackgroundScriptIgnored(EXTENSION_ID);
    waitForBGStatusUpdate = promiseBackgroundStatusUpdate(window);
    await triggerExtensionEventPageIdleTimeout(EXTENSION_ID);
    await Promise.race([waitForBGStatusUpdate, waitForBGSuspendIgnored]);

    await assertBackgroundStatus(EXTENSION_NAME, {
      document,
      expectedStatus: "running",
      // After opening the toolbox there will be an additional target listed
      // for the devtools toolbox tab, its card includes the extension name
      // and so while the toolbox is open we should make sure to look for
      // the background status inside the extension target card instead of
      // the one associated to the devtools toolbox tab.
      targetElement: target,
    });

    info(
      "Wait for warning message expected to be logged for the event page not terminated on idle"
    );
    const toolbox = getToolbox(devtoolsWindow);
    const webconsole = await toolbox.selectTool("webconsole");
    const { hud } = webconsole;
    const expectedWarning =
      "Background event page was not terminated on idle because a DevTools toolbox is attached to the extension.";
    let consoleElements;
    await waitUntil(() => {
      consoleElements = findMessagesByType(hud, expectedWarning, ".warn");
      return !!consoleElements.length;
    });

    const locationElement = consoleElements[0].querySelector(
      ".frame-link-filename"
    );
    ok(
      locationElement.textContent.endsWith("_generated_background_page.html"),
      "The warning message is associated to the event page url"
    );

    info(
      "Verify event page is terminated on idle after closing the DevTools toolbox"
    );

    await closeWebExtAboutDevtoolsToolbox(devtoolsWindow, window);
    await triggerExtensionEventPageIdleTimeout(EXTENSION_ID);
    await waitForBGStatusUpdate;
    await assertBackgroundStatus(EXTENSION_NAME, {
      document,
      expectedStatus: "stopped",
    });

    // Uninstall the test extensions.
    info("Unload extension and remove about:debugging tab");
    await AddonManager.getAddonByID(EXTENSION_ID).then(addon =>
      addon.uninstall()
    );
    info("Wait until the debug targets with test extensions disappears");
    await waitUntil(() => !findDebugTargetByText(EXTENSION_NAME, document));
    await removeTab(tab);
  }
);