summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/browser_ext_tabs_remove.js
blob: 8e51494ed12442ec770e1baa14a1ef379e7b02a6 (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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

add_task(async function undoCloseAfterExtRemovesOneTab() {
  let initialTab = gBrowser.selectedTab;

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

    background: async function () {
      let tabs = await browser.tabs.query({});

      browser.test.assertEq(3, tabs.length, "Should have 3 tabs");

      let tabIdsToRemove = (
        await browser.tabs.query({
          url: "https://example.com/closeme/*",
        })
      ).map(tab => tab.id);

      await browser.tabs.remove(tabIdsToRemove);
      browser.test.sendMessage("removedtabs");
    },
  });

  await Promise.all([
    BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/1"),
    BrowserTestUtils.openNewForegroundTab(
      gBrowser,
      "https://example.com/closeme/2"
    ),
  ]);

  await extension.startup();
  await extension.awaitMessage("removedtabs");

  is(
    gBrowser.tabs.length,
    2,
    "Once extension has closed a tab, there should be 2 tabs open"
  );

  // The tabs.remove API makes no promises about SessionStore's updates
  // having been completed by the time it returns. So we need to wait separately
  // for the closed tab count to be updated the correct value. This is OK because
  // we can observe above that the tabs length has changed to reflect that
  // some were closed.
  await TestUtils.waitForCondition(
    () => SessionStore.getLastClosedTabCount(window) == 1,
    "SessionStore should know that one tab was closed"
  );

  undoCloseTab();

  is(
    gBrowser.tabs.length,
    3,
    "All tabs should be restored for a total of 3 tabs"
  );

  await BrowserTestUtils.waitForEvent(gBrowser.tabs[2], "SSTabRestored");

  is(
    gBrowser.tabs[2].linkedBrowser.currentURI.spec,
    "https://example.com/closeme/2",
    "Restored tab at index 2 should have expected URL"
  );

  await extension.unload();
  gBrowser.removeAllTabsBut(initialTab);
});

add_task(async function undoCloseAfterExtRemovesMultipleTabs() {
  let initialTab = gBrowser.selectedTab;

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

    background: async function () {
      let tabIds = (await browser.tabs.query({})).map(tab => tab.id);

      browser.test.assertEq(
        8,
        tabIds.length,
        "Should have 8 total tabs (4 in each window: the initial blank tab and the 3 opened by this test)"
      );

      let tabIdsToRemove = (
        await browser.tabs.query({
          url: "https://example.com/closeme/*",
        })
      ).map(tab => tab.id);

      await browser.tabs.remove(tabIdsToRemove);

      browser.test.sendMessage("removedtabs");
    },
  });

  await Promise.all([
    BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/1"),
    BrowserTestUtils.openNewForegroundTab(
      gBrowser,
      "https://example.com/closeme/2"
    ),
    BrowserTestUtils.openNewForegroundTab(
      gBrowser,
      "https://example.com/closeme/3"
    ),
  ]);

  let window2 = await BrowserTestUtils.openNewBrowserWindow();

  await Promise.all([
    BrowserTestUtils.openNewForegroundTab(
      window2.gBrowser,
      "https://example.com/4"
    ),
    BrowserTestUtils.openNewForegroundTab(
      window2.gBrowser,
      "https://example.com/closeme/5"
    ),
    BrowserTestUtils.openNewForegroundTab(
      window2.gBrowser,
      "https://example.com/closeme/6"
    ),
  ]);

  await extension.startup();
  await extension.awaitMessage("removedtabs");

  is(
    gBrowser.tabs.length,
    2,
    "Original window should have 2 tabs still open, after closing tabs"
  );

  is(
    window2.gBrowser.tabs.length,
    2,
    "Second window should have 2 tabs still open, after closing tabs"
  );

  // The tabs.remove API makes no promises about SessionStore's updates
  // having been completed by the time it returns. So we need to wait separately
  // for the closed tab count to be updated the correct value. This is OK because
  // we can observe above that the tabs length has changed to reflect that
  // some were closed.
  await TestUtils.waitForCondition(
    () => SessionStore.getLastClosedTabCount(window) == 2,
    "Last closed tab count is 2"
  );

  await TestUtils.waitForCondition(
    () => SessionStore.getLastClosedTabCount(window2) == 2,
    "Last closed tab count is 2"
  );

  undoCloseTab();
  window2.undoCloseTab();

  is(
    gBrowser.tabs.length,
    4,
    "All tabs in original window should be restored for a total of 4 tabs"
  );

  is(
    window2.gBrowser.tabs.length,
    4,
    "All tabs in second window should be restored for a total of 4 tabs"
  );

  await Promise.all([
    BrowserTestUtils.waitForEvent(gBrowser.tabs[2], "SSTabRestored"),
    BrowserTestUtils.waitForEvent(gBrowser.tabs[3], "SSTabRestored"),
    BrowserTestUtils.waitForEvent(window2.gBrowser.tabs[2], "SSTabRestored"),
    BrowserTestUtils.waitForEvent(window2.gBrowser.tabs[3], "SSTabRestored"),
  ]);

  is(
    gBrowser.tabs[2].linkedBrowser.currentURI.spec,
    "https://example.com/closeme/2",
    "Original window restored tab at index 2 should have expected URL"
  );

  is(
    gBrowser.tabs[3].linkedBrowser.currentURI.spec,
    "https://example.com/closeme/3",
    "Original window restored tab at index 3 should have expected URL"
  );

  is(
    window2.gBrowser.tabs[2].linkedBrowser.currentURI.spec,
    "https://example.com/closeme/5",
    "Second window restored tab at index 2 should have expected URL"
  );

  is(
    window2.gBrowser.tabs[3].linkedBrowser.currentURI.spec,
    "https://example.com/closeme/6",
    "Second window restored tab at index 3 should have expected URL"
  );

  await extension.unload();
  await BrowserTestUtils.closeWindow(window2);
  gBrowser.removeAllTabsBut(initialTab);
});

add_task(async function closeWindowIfExtClosesAllTabs() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.tabs.closeWindowWithLastTab", true],
      ["browser.tabs.warnOnClose", true],
    ],
  });

  let extension = ExtensionTestUtils.loadExtension({
    background: async function () {
      let tabsToRemove = await browser.tabs.query({ currentWindow: true });

      let currentWindowId = tabsToRemove[0].windowId;

      browser.test.assertEq(
        2,
        tabsToRemove.length,
        "Current window should have 2 tabs to remove"
      );

      await browser.tabs.remove(tabsToRemove.map(tab => tab.id));

      await browser.test.assertRejects(
        browser.windows.get(currentWindowId),
        RegExp(`Invalid window ID: ${currentWindowId}`),
        "After closing tabs, 2nd window should be closed and querying for it should be rejected"
      );

      browser.test.notifyPass("done");
    },
  });

  let window2 = await BrowserTestUtils.openNewBrowserWindow();

  await BrowserTestUtils.openNewForegroundTab(
    window2.gBrowser,
    "https://example.com/"
  );

  await extension.startup();
  await extension.awaitFinish("done");
  await extension.unload();
  await SpecialPowers.popPrefEnv();
});