Bug 1379148: Part 4 - Add tests for document.write() with document_start content script present. r?mixedpuppy
MozReview-Commit-ID: 5yxgpur1tvf
--- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
@@ -54,16 +54,21 @@ let BASE_MANIFEST = Object.freeze({
"version": "0",
});
function frameScript() {
Components.utils.import("resource://gre/modules/Services.jsm");
Services.obs.notifyObservers(this, "tab-content-frameloader-created");
+
+ // eslint-disable-next-line mozilla/balanced-listeners
+ addEventListener("MozHeapMinimize", () => {
+ Services.obs.notifyObservers(null, "memory-pressure", "heap-minimize");
+ }, true, true);
}
const FRAME_SCRIPT = `data:text/javascript,(${encodeURI(frameScript)}).call(this)`;
let kungFuDeathGrip = new Set();
function promiseBrowserLoaded(browser, url, redirectUrl) {
return new Promise(resolve => {
const listener = {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/data/file_document_write.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+ <iframe id="iframe"></iframe>
+
+ <script type="text/javascript">
+ "use strict";
+ addEventListener("load", () => {
+ // Send a heap-minimize observer notification so our script cache is
+ // cleared, and our content script isn't available for synchronous
+ // insertion.
+ window.dispatchEvent(new CustomEvent("MozHeapMinimize"));
+
+ let iframe = document.getElementById("iframe");
+ let doc = iframe.contentDocument;
+ doc.open("text/html");
+ // We need to do two writes here. The first creates the document element,
+ // which normally triggers parser blocking. The second triggers the
+ // creation of the element we're about to query for, which would normally
+ // happen asynchronously if the parser were blocked.
+ doc.write("<div id=meh>");
+ doc.write("<div id=beer></div>");
+
+ let elem = doc.getElementById("beer");
+ top.postMessage(elem instanceof HTMLDivElement ? "ok" : "fail",
+ "*");
+
+ doc.close();
+ }, {once: true});
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_scriptCreated.js
@@ -0,0 +1,49 @@
+"use strict";
+
+const server = createHttpServer();
+server.registerDirectory("/data/", do_get_file("data"));
+
+const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
+
+// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// to use the right timeout for content scripts executed at document_idle.
+ExtensionTestUtils.mockAppInfo();
+
+// Test that document_start content scripts don't block script-created
+// parsers.
+add_task(async function test_contentscript_scriptCreated() {
+ let extensionData = {
+ manifest: {
+ content_scripts: [{
+ "matches": ["http://*/*/file_document_write.html"],
+ "js": ["content_script.js"],
+ "run_at": "document_start",
+ "match_about_blank": true,
+ "all_frames": true,
+ }],
+ },
+
+ files: {
+ "content_script.js": function() {
+ if (window === top) {
+ addEventListener("message", msg => {
+ browser.test.assertEq("ok", msg.data, "document.write() succeeded");
+ browser.test.sendMessage("content-script-done");
+ }, {once: true});
+ }
+ },
+ },
+ };
+
+ let extension = ExtensionTestUtils.loadExtension(extensionData);
+
+ await extension.startup();
+
+ let contentPage = await ExtensionTestUtils.loadContentPage(`${BASE_URL}/file_document_write.html`);
+
+ await extension.awaitMessage("content-script-done");
+
+ await contentPage.close();
+
+ await extension.unload();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini
@@ -1,5 +1,6 @@
[test_ext_i18n.js]
skip-if = os == "android" || (os == "win" && debug)
[test_ext_i18n_css.js]
[test_ext_contentscript.js]
+[test_ext_contentscript_scriptCreated.js]
[test_ext_contentscript_xrays.js]