summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_ext_scripting_startupCache.js
blob: 07445dafbee514748572a16cabf0e1bcce440662 (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
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
AddonTestUtils.createAppInfo(
  "xpcshell@tests.mozilla.org",
  "XPCShell",
  "1",
  "42"
);

const { ExtensionScriptingStore } = ChromeUtils.importESModule(
  "resource://gre/modules/ExtensionScriptingStore.sys.mjs"
);
const { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

const { sinon } = ChromeUtils.importESModule(
  "resource://testing-common/Sinon.sys.mjs"
);

add_task(async function test_hasPersistedScripts_startup_cache() {
  let extension1 = ExtensionTestUtils.loadExtension({
    manifest: {
      manifest_version: 2,
      permissions: ["scripting"],
    },
    // Set the startup reason to "APP_STARTUP", used to be able to simulate
    // the behavior expected on calls to `ExtensionScriptingStore.init(extension)`
    // when the addon has not been just installed, but it is being loaded as part
    // of the browser application starting up.
    startupReason: "APP_STARTUP",
    background() {
      browser.test.onMessage.addListener(async (msg, ...args) => {
        switch (msg) {
          case "registerContentScripts":
            await browser.scripting.registerContentScripts(...args);
            break;
          case "unregisterContentScripts":
            await browser.scripting.unregisterContentScripts(...args);
            break;
          default:
            browser.test.fail(`Unexpected test message: ${msg}`);
        }
        browser.test.sendMessage(`${msg}:done`);
      });
    },
    files: {
      "script-1.js": "",
    },
  });

  await extension1.startup();

  info(`Checking StartupCache for ${extension1.id} ${extension1.version}`);
  await assertHasPersistedScriptsCachedFlag(extension1);
  await assertIsPersistentScriptsCachedFlag(extension1, false);

  const store = ExtensionScriptingStore._getStoreForTesting();

  extension1.sendMessage("registerContentScripts", [
    {
      id: "some-script-id",
      js: ["script-1.js"],
      matches: ["http://*/*/file_sample.html"],
      persistAcrossSessions: true,
    },
  ]);
  await extension1.awaitMessage("registerContentScripts:done");

  // `registerContentScripts()` calls `ExtensionScriptingStore.persistAll()`
  // without await it, which isn't a problem in practice but this becomes a
  // problem in this test given that we should make sure the startup cache
  // is updated before checking it.
  await TestUtils.waitForCondition(async () => {
    const scripts = await store.getAll(extension1.id);
    return !!scripts.length;
  }, "Wait for stored scripts list to not be empty");
  await assertIsPersistentScriptsCachedFlag(extension1, true);

  extension1.sendMessage("unregisterContentScripts", {
    ids: ["some-script-id"],
  });
  await extension1.awaitMessage("unregisterContentScripts:done");

  await TestUtils.waitForCondition(async () => {
    const scripts = await store.getAll(extension1.id);
    return !scripts.length;
  }, "Wait for stored scripts list to be empty");
  await assertIsPersistentScriptsCachedFlag(extension1, false);

  const storeGetAllSpy = sinon.spy(store, "getAll");
  const cleanupSpies = () => {
    storeGetAllSpy.restore();
  };

  // NOTE: ExtensionScriptingStore.initExtension is usually only called once
  // during the extension startup.
  //
  // This test calls the method after startup was completed, which does not
  // happen in practice, but it allows us to simulate what happens under different
  // store and startup cache conditions and more explicitly cover the expectation
  // that store.getAll isn't going to be called more than once internally
  // when the hasPersistedScripts boolean flag wasn't in the StartupCache
  // and had to be recomputed.
  equal(
    extension1.extension.startupReason,
    "APP_STARTUP",
    "Got the expected extension.startupReason"
  );
  await ExtensionScriptingStore.initExtension(extension1.extension);
  equal(storeGetAllSpy.callCount, 0, "Expect store.getAll to not be called");

  Services.obs.notifyObservers(null, "startupcache-invalidate");

  await ExtensionScriptingStore.initExtension(extension1.extension);
  equal(storeGetAllSpy.callCount, 1, "Expect store.getAll to be called once");

  extension1.sendMessage("registerContentScripts", [
    {
      id: "some-script-id",
      js: ["script-1.js"],
      matches: ["http://*/*/file_sample.html"],
      persistAcrossSessions: true,
    },
  ]);
  await extension1.awaitMessage("registerContentScripts:done");

  await TestUtils.waitForCondition(async () => {
    const scripts = await store.getAll(extension1.id);
    return !!scripts.length;
  }, "Wait for stored scripts list to not be empty");
  await assertIsPersistentScriptsCachedFlag(extension1, true);

  // Make sure getAll is only called once when we don't have
  // scripting.hasPersistedScripts flag cached.
  storeGetAllSpy.resetHistory();
  Services.obs.notifyObservers(null, "startupcache-invalidate");
  await ExtensionScriptingStore.initExtension(extension1.extension);
  equal(storeGetAllSpy.callCount, 1, "Expect store.getAll to be called once");

  cleanupSpies();

  const extId = extension1.id;
  const extVersion = extension1.version;
  await assertIsPersistentScriptsCachedFlag(
    { id: extId, version: extVersion },
    true
  );
  await extension1.unload();
  await assertIsPersistentScriptsCachedFlag(
    { id: extId, version: extVersion },
    undefined
  );

  const { StartupCache } = ExtensionParent;
  const allCachedGeneral = StartupCache._data.get("general");
  equal(
    allCachedGeneral.has(extId),
    false,
    "Expect the extension to have been removed from the StartupCache"
  );
});