summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_extension_permissions_migrate_kvstore_path.js
blob: 5f1c73dd3b2147c4e5e1cb83c20d767c4bb53696 (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
"use strict";

const {
  ExtensionPermissions,
  OLD_RKV_DIRNAME,
  RKV_DIRNAME,
  VERSION_KEY,
  VERSION_VALUE,
} = ChromeUtils.importESModule(
  "resource://gre/modules/ExtensionPermissions.sys.mjs"
);
const { FileTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/FileTestUtils.sys.mjs"
);
const { FileUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/FileUtils.sys.mjs"
);
const { KeyValueService } = ChromeUtils.importESModule(
  "resource://gre/modules/kvstore.sys.mjs"
);

add_setup(async () => {
  // Bug 1646182: Force ExtensionPermissions to run in rkv mode, because this
  // test does not make sense with the legacy method (which will be removed in
  // the above bug).
  ExtensionPermissions._useLegacyStorageBackend = false;
  await ExtensionPermissions._uninit();
});

// NOTE: this test lives in its own test file to make sure it is isolated
// from other tests that would be creating the kvstore instance and
// would prevent this test to properly simulate the kvstore path migration.
add_task(async function test_migrate_to_separate_kvstore_store_path() {
  const ADDON_ID_01 = "test-addon-01@test-extension";
  const ADDON_ID_02 = "test-addon-02@test-extension";
  // This third test extension is only used as the one that should
  // have some content scripts stored in ExtensionScriptingStore.
  const ADDON_ID_03 = "test-addon-03@test-extension";

  const oldStorePath = FileUtils.getDir("ProfD", [OLD_RKV_DIRNAME]).path;
  const newStorePath = FileUtils.getDir("ProfD", [RKV_DIRNAME]).path;

  // Verify that we are going to be using the expected backend, and that
  // the rkv path migration is only enabled by default in Nightly builds.
  info("Verify test environment match the expected pre-conditions");

  const permsStore = ExtensionPermissions._getStore();
  equal(
    permsStore.constructor.name,
    "PermissionStore",
    "active ExtensionPermissions store should be an instance of PermissionStore"
  );

  equal(
    permsStore._shouldMigrateFromOldKVStorePath,
    AppConstants.NIGHTLY_BUILD,
    "ExtensionPermissions rkv migration expected to be enabled by default only in Nightly"
  );

  info(
    "Uninitialize ExtensionPermissions and make sure no existing kvstore dir"
  );
  await ExtensionPermissions._uninit({ recreateStore: false });
  equal(
    ExtensionPermissions._getStore(),
    null,
    "PermissionStore has been nullified"
  );
  await IOUtils.remove(oldStorePath, { ignoreAbsent: true, recursive: true });
  await IOUtils.remove(newStorePath, { ignoreAbsent: true, recursive: true });

  info("Create an existing kvstore dir on the old path");

  // Populated the kvstore with some expected permissions.
  const expectedPermsAddon01 = {
    permissions: ["tabs"],
    origins: ["http://*/*"],
  };
  const expectedPermsAddon02 = {
    permissions: ["proxy"],
    origins: ["https://*/*"],
  };

  const expectedScriptAddon01 = {
    id: "script-addon-01",
    allFrames: false,
    matches: ["<all_urls>"],
    js: ["/test-script-addon-01.js"],
    persistAcrossSessions: true,
    runAt: "document_end",
  };

  const expectedScriptAddon02 = {
    id: "script-addon-02",
    allFrames: false,
    matches: ["<all_urls"],
    css: ["/test-script-addon-02.css"],
    persistAcrossSessions: true,
    runAt: "document_start",
  };

  {
    // Make sure the folder exists
    await IOUtils.makeDirectory(oldStorePath, { ignoreExisting: true });
    // Create a permission kvstore dir on the old file path.
    const kvstore = await KeyValueService.getOrCreate(
      oldStorePath,
      "permissions"
    );
    await kvstore.writeMany([
      ["_version", 1],
      [`id-${ADDON_ID_01}`, JSON.stringify(expectedPermsAddon01)],
      [`id-${ADDON_ID_02}`, JSON.stringify(expectedPermsAddon02)],
    ]);
  }

  {
    // Add also scripting kvstore data into the same temp dir path.
    const kvstore = await KeyValueService.getOrCreate(
      oldStorePath,
      "scripting-contentScripts"
    );
    await kvstore.writeMany([
      [
        `${ADDON_ID_03}/${expectedScriptAddon01.id}`,
        JSON.stringify(expectedScriptAddon01),
      ],
      [
        `${ADDON_ID_03}/${expectedScriptAddon02.id}`,
        JSON.stringify(expectedScriptAddon02),
      ],
    ]);
  }

  ok(
    await IOUtils.exists(oldStorePath),
    "Found kvstore dir for the old store path"
  );
  ok(
    !(await IOUtils.exists(newStorePath)),
    "Expect kvstore dir for the new store path to don't exist yet"
  );

  info("Re-initialize the ExtensionPermission store and assert migrated data");
  await ExtensionPermissions._uninit({ recreateStore: true });

  // Explicitly enable migration (needed to make sure we hit the migration code
  // that is only enabled by default on Nightly).
  if (!AppConstants.NIGHTLY_BUILD) {
    info("Enable ExtensionPermissions rkv migration on non-nightly channel");
    const newStoreInstance = ExtensionPermissions._getStore();
    newStoreInstance._shouldMigrateFromOldKVStorePath = true;
  }

  const permsAddon01 = await ExtensionPermissions._get(ADDON_ID_01);
  const permsAddon02 = await ExtensionPermissions._get(ADDON_ID_02);

  Assert.deepEqual(
    { permsAddon01, permsAddon02 },
    {
      permsAddon01: expectedPermsAddon01,
      permsAddon02: expectedPermsAddon02,
    },
    "Got the expected permissions migrated to the new store file path"
  );

  await ExtensionPermissions._uninit({ recreateStore: false });

  ok(
    await IOUtils.exists(newStorePath),
    "Found kvstore dir for the new store path"
  );

  {
    const newKVStore = await KeyValueService.getOrCreate(
      newStorePath,
      "permissions"
    );
    Assert.equal(
      await newKVStore.get(VERSION_KEY),
      VERSION_VALUE,
      "Got the expected value set on the kvstore _version key"
    );
  }

  // kvstore internally caching behavior doesn't make it easy to make sure
  // we would be hitting a failure if the ExtensionPermissions kvstore migration
  // would be mistakenly removing the old kvstore dir as part of that migration,
  // and so the test case is explicitly verifying that the directory does still
  // exist and then it copies it into a new path to confirm that the expected
  // data have been kept in the old kvstore dir.
  ok(
    await IOUtils.exists(oldStorePath),
    "Found kvstore dir for the old store path"
  );
  const oldStoreCopiedPath = FileTestUtils.getTempFile("kvstore-dir").path;
  await IOUtils.copy(oldStorePath, oldStoreCopiedPath, { recursive: true });

  // Confirm that the content scripts have not been copied into
  // the new kvstore path.
  async function assertStoredContentScripts(storePath, expectedKeys) {
    const kvstore = await KeyValueService.getOrCreate(
      storePath,
      "scripting-contentScripts"
    );
    const enumerator = await kvstore.enumerate();
    const keys = [];
    while (enumerator.hasMoreElements()) {
      keys.push(enumerator.getNext().key);
    }
    Assert.deepEqual(
      keys,
      expectedKeys,
      `Got the expected scripts in the kvstore path ${storePath}`
    );
  }

  info(
    "Verify that no content scripts are stored in the new kvstore dir reserved for permissions"
  );
  await assertStoredContentScripts(newStorePath, []);
  info(
    "Verify that existing content scripts have been not been removed old kvstore dir"
  );
  await assertStoredContentScripts(oldStoreCopiedPath, [
    `${ADDON_ID_03}/${expectedScriptAddon01.id}`,
    `${ADDON_ID_03}/${expectedScriptAddon02.id}`,
  ]);

  await ExtensionPermissions._uninit({ recreateStore: true });

  await IOUtils.remove(newStorePath, { recursive: true });
  await IOUtils.remove(oldStorePath, { recursive: true });
});