gecko-dev/dom/ipc/tests/test_sharedMap.js
Kris Maglione e930b89c34 Bug 1514594: Part 3 - Change ChromeUtils.import API.
***
Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8

This changes the behavior of ChromeUtils.import() to return an exports object,
rather than a module global, in all cases except when `null` is passed as a
second argument, and changes the default behavior not to pollute the global
scope with the module's exports. Thus, the following code written for the old
model:

  ChromeUtils.import("resource://gre/modules/Services.jsm");

is approximately the same as the following, in the new model:

  var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

Since the two behaviors are mutually incompatible, this patch will land with a
scripted rewrite to update all existing callers to use the new model rather
than the old.
***
Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs

This was done using the followng script:

https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm
***
Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8

Differential Revision: https://phabricator.services.mozilla.com/D16747
***
Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16748
***
Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16749
***
Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs
***
Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16750

--HG--
extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895
extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 10:18:31 -08:00

260 lines
8.3 KiB
JavaScript

"use strict";
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {ExtensionUtils} = ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
const {ExtensionTestUtils} = ChromeUtils.import("resource://testing-common/ExtensionXPCShellUtils.jsm");
const PROCESS_COUNT_PREF = "dom.ipc.processCount";
const remote = AppConstants.platform !== "android";
ExtensionTestUtils.init(this);
let contentPage;
Cu.importGlobalProperties(["Blob", "FileReader"]);
async function readBlob(key, sharedData = Services.cpmm.sharedData) {
let reader = new FileReader();
reader.readAsText(sharedData.get(key));
await ExtensionUtils.promiseEvent(reader, "loadend");
return reader.result;
}
function getKey(key, sharedData = Services.cpmm.sharedData) {
return sharedData.get(key);
}
function getContents(sharedMap = Services.cpmm.sharedData) {
return {
keys: Array.from(sharedMap.keys()),
values: Array.from(sharedMap.values()),
entries: Array.from(sharedMap.entries()),
getValues: Array.from(sharedMap.keys(),
key => sharedMap.get(key)),
};
}
function checkMap(contents, expected) {
expected = Array.from(expected);
// Remove keys already defined by ActorManagerParent.jsm
for (let i = contents.keys.length - 1; i >= 0; i--) {
if (/^Child(Singleton)?Actors/.test(contents.keys[i])) {
contents.keys.splice(i, 1);
contents.values.splice(i, 1);
contents.entries.splice(i, 1);
}
}
equal(contents.keys.length, expected.length,
"Got correct number of keys");
equal(contents.values.length, expected.length,
"Got correct number of values");
equal(contents.entries.length, expected.length,
"Got correct number of entries");
for (let [i, [key, val]] of contents.entries.entries()) {
equal(key, contents.keys[i], `keys()[${i}] matches entries()[${i}]`);
deepEqual(val, contents.values[i], `values()[${i}] matches entries()[${i}]`);
}
expected.sort(([a], [b]) => a.localeCompare(b));
contents.entries.sort(([a], [b]) => a.localeCompare(b));
for (let [i, [key, val]] of contents.entries.entries()) {
equal(key, expected[i][0], `expected[${i}].key matches entries()[${i}].key`);
deepEqual(val, expected[i][1], `expected[${i}].value matches entries()[${i}].value`);
}
}
function checkParentMap(expected) {
info("Checking parent map");
checkMap(getContents(Services.ppmm.sharedData), expected);
}
async function checkContentMaps(expected, parentOnly = false) {
info("Checking in-process content map");
checkMap(getContents(Services.cpmm.sharedData), expected);
if (!parentOnly) {
info("Checking out-of-process content map");
let contents = await contentPage.spawn(undefined, getContents);
checkMap(contents, expected);
}
}
async function loadContentPage() {
let page = await ExtensionTestUtils.loadContentPage("about:blank", {remote});
registerCleanupFunction(() => page.close());
page.addFrameScriptHelper(`
var {ExtensionUtils} = ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
Cu.importGlobalProperties(["FileReader"]);
`);
return page;
}
add_task(async function setup() {
// Start with one content process so that we can increase the number
// later and test the behavior of a fresh content process.
Services.prefs.setIntPref(PROCESS_COUNT_PREF, 1);
contentPage = await loadContentPage();
});
add_task(async function test_sharedMap() {
let {sharedData} = Services.ppmm;
info("Check that parent and child maps are both initially empty");
checkParentMap([]);
await checkContentMaps([]);
let expected = [
["foo-a", {"foo": "a"}],
["foo-b", {"foo": "b"}],
["bar-c", null],
["bar-d", 42],
];
function setKey(key, val) {
sharedData.set(key, val);
expected = expected.filter(([k]) => k != key);
expected.push([key, val]);
}
function deleteKey(key) {
sharedData.delete(key);
expected = expected.filter(([k]) => k != key);
}
for (let [key, val] of expected) {
sharedData.set(key, val);
}
info("Add some entries, test that they are initially only available in the parent");
checkParentMap(expected);
await checkContentMaps([]);
info("Flush. Check that changes are visible in both parent and children");
sharedData.flush();
checkParentMap(expected);
await checkContentMaps(expected);
info("Add another entry. Check that it is initially only available in the parent");
let oldExpected = Array.from(expected);
setKey("baz-a", {meh: "meh"});
// When we do several checks in a row, we can't check the values in
// the content process, since the async checks may allow the idle
// flush task to run, and update it before we're ready.
checkParentMap(expected);
checkContentMaps(oldExpected, true);
info("Add another entry. Check that both new entries are only available in the parent");
setKey("baz-a", {meh: 12});
checkParentMap(expected);
checkContentMaps(oldExpected, true);
info("Delete an entry. Check that all changes are only visible in the parent");
deleteKey("foo-b");
checkParentMap(expected);
checkContentMaps(oldExpected, true);
info("Flush. Check that all entries are available in both parent and children");
sharedData.flush();
checkParentMap(expected);
await checkContentMaps(expected);
info("Test that entries are automatically flushed on idle:");
info("Add a new entry. Check that it is initially only available in the parent");
// Test the idle flush task.
oldExpected = Array.from(expected);
setKey("thing", "stuff");
checkParentMap(expected);
checkContentMaps(oldExpected, true);
info("Wait for an idle timeout. Check that changes are now visible in all children");
await new Promise(resolve => ChromeUtils.idleDispatch(resolve));
checkParentMap(expected);
await checkContentMaps(expected);
});
add_task(async function test_blobs() {
let {sharedData} = Services.ppmm;
let text = [
"The quick brown fox jumps over the lazy dog",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit",
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
];
let blobs = text.map(str => new Blob([str]));
let data = {foo: {bar: "baz"}};
sharedData.set("blob0", blobs[0]);
sharedData.set("blob1", blobs[1]);
sharedData.set("data", data);
equal(await readBlob("blob0", sharedData), text[0], "Expected text for blob0 in parent ppmm");
sharedData.flush();
equal(await readBlob("blob0", sharedData), text[0], "Expected text for blob0 in parent ppmm");
equal(await readBlob("blob1", sharedData), text[1], "Expected text for blob1 in parent ppmm");
equal(await readBlob("blob0"), text[0], "Expected text for blob0 in parent cpmm");
equal(await readBlob("blob1"), text[1], "Expected text for blob1 in parent cpmm");
equal(await contentPage.spawn("blob0", readBlob), text[0], "Expected text for blob0 in child 1 cpmm");
equal(await contentPage.spawn("blob1", readBlob), text[1], "Expected text for blob1 in child 1 cpmm");
// Start a second child process
Services.prefs.setIntPref(PROCESS_COUNT_PREF, 2);
let page2 = await loadContentPage();
equal(await page2.spawn("blob0", readBlob), text[0], "Expected text for blob0 in child 2 cpmm");
equal(await page2.spawn("blob1", readBlob), text[1], "Expected text for blob1 in child 2 cpmm");
sharedData.set("blob0", blobs[2]);
equal(await readBlob("blob0", sharedData), text[2], "Expected text for blob0 in parent ppmm");
sharedData.flush();
equal(await readBlob("blob0", sharedData), text[2], "Expected text for blob0 in parent ppmm");
equal(await readBlob("blob1", sharedData), text[1], "Expected text for blob1 in parent ppmm");
equal(await readBlob("blob0"), text[2], "Expected text for blob0 in parent cpmm");
equal(await readBlob("blob1"), text[1], "Expected text for blob1 in parent cpmm");
equal(await contentPage.spawn("blob0", readBlob), text[2], "Expected text for blob0 in child 1 cpmm");
equal(await contentPage.spawn("blob1", readBlob), text[1], "Expected text for blob1 in child 1 cpmm");
equal(await page2.spawn("blob0", readBlob), text[2], "Expected text for blob0 in child 2 cpmm");
equal(await page2.spawn("blob1", readBlob), text[1], "Expected text for blob1 in child 2 cpmm");
deepEqual(await page2.spawn("data", getKey), data, "Expected data for data key in child 2 cpmm");
});