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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const { HttpServer } = ChromeUtils.importESModule(
"resource://testing-common/httpd.sys.mjs"
);
do_get_profile();
const server = new HttpServer();
server.registerDirectory("/", do_get_cwd());
server.start(-1);
const ROOT = `http://localhost:${server.identity.primaryPort}`;
const BASE = `${ROOT}/`;
const HEADLESS_URL = Services.io.newURI(`${BASE}/headless.html`);
const HEADLESS_BUTTON_URL = Services.io.newURI(`${BASE}/headless_button.html`);
registerCleanupFunction(() => {
server.stop(() => {});
});
// Refrences to the progress listeners to keep them from being gc'ed
// before they are called.
const progressListeners = new Map();
function loadContentWindow(windowlessBrowser, uri) {
return new Promise((resolve, reject) => {
let loadURIOptions = {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
};
windowlessBrowser.loadURI(uri, loadURIOptions);
let docShell = windowlessBrowser.docShell;
let webProgress = docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
let progressListener = {
onLocationChange(progress, request, location, flags) {
// Ignore inner-frame events
if (progress != webProgress) {
return;
}
// Ignore events that don't change the document
if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
return;
}
let contentWindow = docShell.domWindow;
webProgress.removeProgressListener(progressListener);
progressListeners.delete(progressListener);
contentWindow.addEventListener(
"load",
event => {
resolve(contentWindow);
},
{ once: true }
);
},
QueryInterface: ChromeUtils.generateQI([
"nsIWebProgressListener",
"nsISupportsWeakReference",
]),
};
progressListeners.set(progressListener, progressListener);
webProgress.addProgressListener(
progressListener,
Ci.nsIWebProgress.NOTIFY_LOCATION
);
});
}
add_setup(function () {
Services.prefs.setBoolPref("security.allow_unsafe_parent_loads", true);
});
registerCleanupFunction(function () {
Services.prefs.clearUserPref("security.allow_unsafe_parent_loads");
});
add_task(async function test_snapshot() {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
let contentWindow = await loadContentWindow(windowlessBrowser, HEADLESS_URL);
const contentWidth = 400;
const contentHeight = 300;
// Verify dimensions.
contentWindow.resizeTo(contentWidth, contentHeight);
equal(contentWindow.innerWidth, contentWidth);
equal(contentWindow.innerHeight, contentHeight);
// Snapshot the test page.
let canvas = contentWindow.document.createElementNS(
"http://www.w3.org/1999/xhtml",
"html:canvas"
);
let context = canvas.getContext("2d");
let width = contentWindow.innerWidth;
let height = contentWindow.innerHeight;
canvas.width = width;
canvas.height = height;
context.drawWindow(contentWindow, 0, 0, width, height, "rgb(255, 255, 255)");
let imageData = context.getImageData(0, 0, width, height).data;
ok(
imageData[0] === 0 &&
imageData[1] === 255 &&
imageData[2] === 0 &&
imageData[3] === 255,
"Page is green."
);
// Search for a blue pixel (a quick and dirty check to see if the blue text is
// on the page)
let found = false;
for (let i = 0; i < imageData.length; i += 4) {
if (imageData[i + 2] === 255) {
found = true;
break;
}
}
ok(found, "Found blue text on page.");
windowlessBrowser.close();
});
add_task(async function test_snapshot_widget_layers() {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
// nsIWindowlessBrowser inherits from nsIWebNavigation.
let contentWindow = await loadContentWindow(windowlessBrowser, HEADLESS_URL);
const contentWidth = 1;
const contentHeight = 2;
// Verify dimensions.
contentWindow.resizeTo(contentWidth, contentHeight);
equal(contentWindow.innerWidth, contentWidth);
equal(contentWindow.innerHeight, contentHeight);
// Snapshot the test page.
let canvas = contentWindow.document.createElementNS(
"http://www.w3.org/1999/xhtml",
"html:canvas"
);
let context = canvas.getContext("2d");
let width = contentWindow.innerWidth;
let height = contentWindow.innerHeight;
canvas.width = width;
canvas.height = height;
context.drawWindow(
contentWindow,
0,
0,
width,
height,
"rgb(255, 255, 255)",
context.DRAWWINDOW_DRAW_CARET |
context.DRAWWINDOW_DRAW_VIEW |
context.DRAWWINDOW_USE_WIDGET_LAYERS
);
ok(true, "Snapshot with widget layers didn't crash.");
windowlessBrowser.close();
});
// Ensure keydown events are triggered on the windowless browser.
add_task(async function test_keydown() {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
// nsIWindowlessBrowser inherits from nsIWebNavigation.
let contentWindow = await loadContentWindow(windowlessBrowser, HEADLESS_URL);
let keydown = new Promise(resolve => {
contentWindow.addEventListener(
"keydown",
() => {
resolve();
},
{ once: true }
);
});
let tip = Cc["@mozilla.org/text-input-processor;1"].createInstance(
Ci.nsITextInputProcessor
);
let begun = tip.beginInputTransactionForTests(contentWindow);
ok(
begun,
"nsITextInputProcessor.beginInputTransactionForTests() should succeed"
);
tip.keydown(
new contentWindow.KeyboardEvent("", {
key: "a",
code: "KeyA",
keyCode: contentWindow.KeyboardEvent.DOM_VK_A,
})
);
await keydown;
ok(true, "Send keydown didn't crash");
windowlessBrowser.close();
});
// Test dragging the mouse on a button to ensure the creation of the drag
// service doesn't crash in headless.
add_task(async function test_mouse_drag() {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
// nsIWindowlessBrowser inherits from nsIWebNavigation.
let contentWindow = await loadContentWindow(
windowlessBrowser,
HEADLESS_BUTTON_URL
);
contentWindow.resizeTo(400, 400);
let target = contentWindow.document.getElementById("btn");
let rect = target.getBoundingClientRect();
let left = rect.left;
let top = rect.top;
let utils = contentWindow.windowUtils;
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mousemove", left, top, 0, 1, 0, false, 0, 0);
// Wait for a turn of the event loop since the synthetic mouse event
// that creates the drag service is processed during the refresh driver.
await new Promise(r => {
executeSoon(r);
});
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
ok(true, "Send mouse event didn't crash");
windowlessBrowser.close();
});
|