193 lines
6.2 KiB
JavaScript
193 lines
6.2 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
"use strict";
|
|
|
|
declTest("destroy actor by iframe remove", {
|
|
allFrames: true,
|
|
|
|
async test(browser) {
|
|
await SpecialPowers.spawn(browser, [], async function () {
|
|
// Create and append an iframe into the window's document.
|
|
let frame = content.document.createElement("iframe");
|
|
frame.id = "frame";
|
|
content.document.body.appendChild(frame);
|
|
await ContentTaskUtils.waitForEvent(frame, "load");
|
|
is(content.window.frames.length, 1, "There should be an iframe.");
|
|
let child = frame.contentWindow.windowGlobalChild;
|
|
let actorChild = child.getActor("TestWindow");
|
|
ok(actorChild, "JSWindowActorChild should have value.");
|
|
|
|
{
|
|
let error = actorChild.uninitializedGetterError;
|
|
const prop = "contentWindow";
|
|
Assert.ok(
|
|
error,
|
|
`Should get error accessing '${prop}' before actor initialization`
|
|
);
|
|
if (error) {
|
|
Assert.equal(
|
|
error.name,
|
|
"InvalidStateError",
|
|
"Error should be an InvalidStateError"
|
|
);
|
|
Assert.equal(
|
|
error.message,
|
|
`JSWindowActorChild.${prop} getter: Cannot access property '${prop}' before actor is initialized`,
|
|
"Error should have informative message"
|
|
);
|
|
}
|
|
}
|
|
|
|
let didDestroyPromise = new Promise(resolve => {
|
|
const TOPIC = "test-js-window-actor-diddestroy";
|
|
Services.obs.addObserver(function obs(subject, topic, data) {
|
|
ok(data, "didDestroyCallback data should be true.");
|
|
is(subject, actorChild, "Should have this value");
|
|
|
|
Services.obs.removeObserver(obs, TOPIC);
|
|
// Make a trip through the event loop to ensure that the
|
|
// actor's manager has been cleared before running remaining
|
|
// checks.
|
|
Services.tm.dispatchToMainThread(resolve);
|
|
}, TOPIC);
|
|
});
|
|
|
|
info("Remove frame");
|
|
content.document.getElementById("frame").remove();
|
|
await didDestroyPromise;
|
|
|
|
Assert.throws(
|
|
() => child.getActor("TestWindow"),
|
|
/InvalidStateError/,
|
|
"Should throw if frame destroy."
|
|
);
|
|
|
|
for (let prop of [
|
|
"document",
|
|
"browsingContext",
|
|
"docShell",
|
|
"contentWindow",
|
|
]) {
|
|
let error;
|
|
try {
|
|
void actorChild[prop];
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
Assert.ok(
|
|
error,
|
|
`Should get error accessing '${prop}' after actor destruction`
|
|
);
|
|
if (error) {
|
|
Assert.equal(
|
|
error.name,
|
|
"InvalidStateError",
|
|
"Error should be an InvalidStateError"
|
|
);
|
|
Assert.equal(
|
|
error.message,
|
|
`JSWindowActorChild.${prop} getter: Cannot access property '${prop}' after actor 'TestWindow' has been destroyed`,
|
|
"Error should have informative message"
|
|
);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
});
|
|
|
|
declTest("destroy actor by page navigates", {
|
|
allFrames: true,
|
|
|
|
async test(browser) {
|
|
info("creating an in-process frame");
|
|
await SpecialPowers.spawn(browser, [URL], async function (url) {
|
|
let frame = content.document.createElement("iframe");
|
|
frame.src = url;
|
|
content.document.body.appendChild(frame);
|
|
});
|
|
|
|
info("navigating page");
|
|
await SpecialPowers.spawn(browser, [TEST_URL], async function (url) {
|
|
let frame = content.document.querySelector("iframe");
|
|
frame.contentWindow.location = url;
|
|
let child = frame.contentWindow.windowGlobalChild;
|
|
let actorChild = child.getActor("TestWindow");
|
|
ok(actorChild, "JSWindowActorChild should have value.");
|
|
|
|
let didDestroyPromise = new Promise(resolve => {
|
|
const TOPIC = "test-js-window-actor-diddestroy";
|
|
Services.obs.addObserver(function obs(subject, topic, data) {
|
|
ok(data, "didDestroyCallback data should be true.");
|
|
is(subject, actorChild, "Should have this value");
|
|
|
|
Services.obs.removeObserver(obs, TOPIC);
|
|
resolve();
|
|
}, TOPIC);
|
|
});
|
|
|
|
await Promise.all([
|
|
didDestroyPromise,
|
|
ContentTaskUtils.waitForEvent(frame, "load"),
|
|
]);
|
|
|
|
Assert.throws(
|
|
() => child.getActor("TestWindow"),
|
|
/InvalidStateError/,
|
|
"Should throw if frame destroy."
|
|
);
|
|
});
|
|
},
|
|
});
|
|
|
|
declTest("destroy actor by tab being closed", {
|
|
allFrames: true,
|
|
|
|
async test() {
|
|
info("creating a new tab");
|
|
let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
|
|
let newTabBrowser = newTab.linkedBrowser;
|
|
|
|
let parent =
|
|
newTabBrowser.browsingContext.currentWindowGlobal.getActor("TestWindow");
|
|
ok(parent, "JSWindowActorParent should have value.");
|
|
|
|
// We can't depend on `SpecialPowers.spawn` to resolve our promise, as the
|
|
// frame message manager will be being shut down at the same time. Instead
|
|
// send messages over the per-process message manager which should still be
|
|
// active.
|
|
let didDestroyPromise = new Promise(resolve => {
|
|
Services.ppmm.addMessageListener(
|
|
"test-jswindowactor-diddestroy",
|
|
function onmessage() {
|
|
Services.ppmm.removeMessageListener(
|
|
"test-jswindowactor-diddestroy",
|
|
onmessage
|
|
);
|
|
resolve();
|
|
}
|
|
);
|
|
});
|
|
|
|
info("setting up destroy listeners");
|
|
await SpecialPowers.spawn(newTabBrowser, [], () => {
|
|
let child = content.windowGlobalChild;
|
|
let actorChild = child.getActor("TestWindow");
|
|
ok(actorChild, "JSWindowActorChild should have value.");
|
|
|
|
Services.obs.addObserver(function obs(subject, topic, data) {
|
|
if (subject != actorChild) {
|
|
return;
|
|
}
|
|
dump("DidDestroy called\n");
|
|
Services.obs.removeObserver(obs, "test-js-window-actor-diddestroy");
|
|
Services.cpmm.sendAsyncMessage("test-jswindowactor-diddestroy", data);
|
|
}, "test-js-window-actor-diddestroy");
|
|
});
|
|
|
|
info("removing new tab");
|
|
await BrowserTestUtils.removeTab(newTab);
|
|
info("waiting for destroy callbacks to fire");
|
|
await didDestroyPromise;
|
|
info("got didDestroy callback");
|
|
},
|
|
});
|