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
|
"use strict";
const { TestUtils } = ChromeUtils.importESModule(
"resource://testing-common/TestUtils.sys.mjs"
);
const { XPCShellContentUtils } = ChromeUtils.importESModule(
"resource://testing-common/XPCShellContentUtils.sys.mjs"
);
const { setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
XPCShellContentUtils.init(this);
function delay() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
const server = XPCShellContentUtils.createHttpServer({
hosts: ["example.com"],
});
// XML document with only a <script> tag as the document element.
const PAGE_URL = "http://example.com/";
server.registerPathHandler("/", (request, response) => {
response.setHeader("Content-Type", "application/xhtml+xml");
response.write(String.raw`<!DOCTYPE html>
<script xmlns="http://www.w3.org/1999/xhtml" src="slow.js"/>
`);
});
let resolveResumeScriptPromise;
let resumeScriptPromise = new Promise(resolve => {
resolveResumeScriptPromise = resolve;
});
let resolveScriptRequestPromise;
let scriptRequestPromise = new Promise(resolve => {
resolveScriptRequestPromise = resolve;
});
// An empty script which waits to complete until `resumeScriptPromise`
// resolves.
server.registerPathHandler("/slow.js", async (request, response) => {
response.processAsync();
resolveScriptRequestPromise();
await resumeScriptPromise;
response.setHeader("Content-type", "text/javascript");
response.write("");
response.finish();
});
add_setup(function () {
Services.prefs.setBoolPref("security.allow_unsafe_parent_loads", true);
});
registerCleanupFunction(function () {
Services.prefs.clearUserPref("security.allow_unsafe_parent_loads");
});
// Tests that attempting to block parsing for a <script> tag while the
// parser is already blocked is handled correctly, and does not cause
// crashes or hangs.
add_task(async function test_nested_blockParser() {
// Wait for the document element of the page to be inserted, and block
// the parser when it is.
let resolveBlockerPromise;
let blockerPromise;
let docElementPromise = TestUtils.topicObserved(
"document-element-inserted",
doc => {
if (doc.location.href === PAGE_URL) {
blockerPromise = new Promise(resolve => {
resolveBlockerPromise = resolve;
});
doc.blockParsing(blockerPromise);
return true;
}
return false;
}
);
// Begin loading the page.
let pagePromise = XPCShellContentUtils.loadContentPage(PAGE_URL, {
remote: false,
});
// Wait for the document element to be inserted.
await docElementPromise;
// Wait for the /slow.js script request to initiate.
await scriptRequestPromise;
// Make some trips through the event loop to be safe.
await delay();
await delay();
// Allow the /slow.js script request to complete.
resolveResumeScriptPromise();
// Make some trips through the event loop so that the <script> request
// unblocks the parser.
await delay();
await delay();
// Release the parser blocker added in the observer above.
resolveBlockerPromise();
// Make some trips through the event loop to allow the parser to
// unblock.
await delay();
await delay();
// Wait for the document to finish loading, and then close it.
let page = await pagePromise;
await page.close();
});
|