summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/browser_ext_tabs_autoDiscardable.js
blob: 74043e2a3af8ea48a6aac8c4dc19ddf7f8e73dec (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
"use strict";

add_task(async function test_autoDiscardable() {
  let files = {
    "schema.json": JSON.stringify([
      {
        namespace: "experiments",
        functions: [
          {
            name: "unload",
            type: "function",
            async: true,
            description:
              "Unload the least recently used tab using Firefox's built-in tab unloader mechanism",
            parameters: [],
          },
        ],
      },
    ]),
    "parent.js": () => {
      const { TabUnloader } = ChromeUtils.importESModule(
        "resource:///modules/TabUnloader.sys.mjs"
      );
      const { ExtensionError } = ExtensionUtils;
      this.experiments = class extends ExtensionAPI {
        getAPI(context) {
          return {
            experiments: {
              async unload() {
                try {
                  await TabUnloader.unloadLeastRecentlyUsedTab(null);
                } catch (error) {
                  // We need to do this, otherwise failures won't bubble up to the test properly.
                  throw ExtensionError(error);
                }
              },
            },
          };
        }
      };
    },
  };

  async function background() {
    let firstTab = await browser.tabs.create({
      active: false,
      url: "https://example.org/",
    });

    // Make sure setting and getting works properly
    browser.test.assertTrue(
      firstTab.autoDiscardable,
      "autoDiscardable should always be true by default"
    );
    let result = await browser.tabs.update(firstTab.id, {
      autoDiscardable: false,
    });
    browser.test.assertFalse(
      result.autoDiscardable,
      "autoDiscardable should be false after setting it as such"
    );
    result = await browser.tabs.update(firstTab.id, {
      autoDiscardable: true,
    });
    browser.test.assertTrue(
      result.autoDiscardable,
      "autoDiscardable should be true after setting it as such"
    );
    result = await browser.tabs.update(firstTab.id, {
      autoDiscardable: false,
    });
    browser.test.assertFalse(
      result.autoDiscardable,
      "autoDiscardable should be false after setting it as such"
    );

    // Make sure the tab can't be unloaded when autoDiscardable is false
    await browser.experiments.unload();
    result = await browser.tabs.get(firstTab.id);
    browser.test.assertFalse(
      result.discarded,
      "Tab should not unload when autoDiscardable is false"
    );

    // Make sure the tab CAN be unloaded when autoDiscardable is true
    await browser.tabs.update(firstTab.id, {
      autoDiscardable: true,
    });
    await browser.experiments.unload();
    result = await browser.tabs.get(firstTab.id);
    browser.test.assertTrue(
      result.discarded,
      "Tab should unload when autoDiscardable is true"
    );

    // Make sure filtering for discardable tabs works properly
    result = await browser.tabs.query({ autoDiscardable: true });
    browser.test.assertEq(
      2,
      result.length,
      "tabs.query should return 2 when autoDiscardable is true "
    );
    await browser.tabs.update(firstTab.id, {
      autoDiscardable: false,
    });
    result = await browser.tabs.query({ autoDiscardable: true });
    browser.test.assertEq(
      1,
      result.length,
      "tabs.query should return 1 when autoDiscardable is false"
    );

    let onUpdatedPromise = {};
    onUpdatedPromise.promise = new Promise(
      resolve => (onUpdatedPromise.resolve = resolve)
    );

    // Make sure onUpdated works
    async function testOnUpdatedEvent(autoDiscardable) {
      browser.test.log(`Testing autoDiscardable = ${autoDiscardable}`);
      let onUpdated;
      let promise = new Promise(resolve => {
        onUpdated = (tabId, changeInfo, tabInfo) => {
          browser.test.assertEq(
            firstTab.id,
            tabId,
            "The updated tab's ID should match the correct tab"
          );
          browser.test.assertDeepEq(
            { autoDiscardable },
            changeInfo,
            "The updated tab's changeInfo should be correct"
          );
          browser.test.assertEq(
            tabInfo.autoDiscardable,
            autoDiscardable,
            "The updated tab's tabInfo should be correct"
          );
          resolve();
        };
      });
      browser.tabs.onUpdated.addListener(onUpdated, {
        properties: ["autoDiscardable"],
      });
      await browser.tabs.update(firstTab.id, { autoDiscardable });
      await promise;
      browser.tabs.onUpdated.removeListener(onUpdated);
    }

    await testOnUpdatedEvent(true);
    await testOnUpdatedEvent(false);

    await browser.tabs.remove(firstTab.id); // Cleanup
    browser.test.notifyPass("autoDiscardable");
  }
  let extension = ExtensionTestUtils.loadExtension({
    isPrivileged: true,
    manifest: {
      permissions: ["tabs"],
      experiment_apis: {
        experiments: {
          schema: "schema.json",
          parent: {
            scopes: ["addon_parent"],
            script: "parent.js",
            paths: [["experiments"]],
          },
        },
      },
    },
    background,
    files,
  });
  await extension.startup();
  await extension.awaitFinish("autoDiscardable");
  await extension.unload();
});