Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Tabs executeScript runAt Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
/**
* These tests ensure that the runAt argument to tabs.executeScript delays
* script execution until the document has reached the correct state.
*
* Since tests of this nature are especially race-prone, it relies on a
* server-JS script to delay the completion of our test page's load cycle long
* enough for us to attempt to load our scripts in the earlies phase we support.
*
* And since we can't actually rely on that timing, it retries any attempts that
* fail to load as early as expected, but don't load at any illegal time.
*/
add_task(async function testExecuteScript() {
const win = window.open("about:blank");
async function background(DEBUG) {
let tab;
const URL = BASE + "file_slowed_document.sjs";
const MAX_TRIES = 30;
const onUpdatedPromise = (tabId, url, status) => {
return new Promise(resolve => {
browser.tabs.onUpdated.addListener(function listener(_, changed, tab) {
if (tabId == tab.id && changed.status == status && tab.url == url) {
browser.tabs.onUpdated.removeListener(listener);
resolve();
}
});
});
};
try {
[tab] = await browser.tabs.query({active: true, currentWindow: true});
let success = false;
for (let tries = 0; !success && tries < MAX_TRIES; tries++) {
const url = `${URL}?with-iframe&r=${Math.random()}`;
const loadingPromise = onUpdatedPromise(tab.id, url, "loading");
const completePromise = onUpdatedPromise(tab.id, url, "complete");
// TODO: Test allFrames and frameId.
await browser.tabs.update({url});
await loadingPromise;
const states = await Promise.all([
// Send the executeScript requests in the reverse order that we expect
// them to execute in, to avoid them passing only because of timing
// races.
browser.tabs.executeScript({
code: "document.readyState",
runAt: "document_idle",
}),
browser.tabs.executeScript({
code: "document.readyState",
runAt: "document_end",
}),
browser.tabs.executeScript({
code: "document.readyState",
runAt: "document_start",
}),
].reverse());
browser.test.log(`Got states: ${states}`);
// Make sure that none of our scripts executed earlier than expected,
// regardless of retries.
browser.test.assertTrue(states[1] == "interactive" || states[1] == "complete",
`document_end state is valid: ${states[1]}`);
browser.test.assertTrue(states[2] == "interactive" || states[2] == "complete",
`document_idle state is valid: ${states[2]}`);
// If we have the earliest valid states for each script, we're done.
// Otherwise, try again.
success = ((states[0] == "loading" || DEBUG) &&
states[1] == "interactive" &&
(states[2] == "interactive" || states[2] == "complete"));
await completePromise;
}
browser.test.assertTrue(success, "Got the earliest expected states at least once");
browser.test.notifyPass("executeScript-runAt");
} catch (e) {
browser.test.fail(`Error: ${e} :: ${e.stack}`);
browser.test.notifyFail("executeScript-runAt");
}
}
const extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["http://mochi.test/", "tabs"],
},
background: `(${background})(${AppConstants.DEBUG})`,
});
await extension.startup();
await extension.awaitFinish("executeScript-runAt");
await extension.unload();
win.close();
});
</script>
</body>
</html>