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
|
Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
var gTestBrowser = null;
var config = {};
add_task(function* () {
// The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables plugin
// crash reports. This test needs them enabled. The test also needs a mock
// report server, and fortunately one is already set up by toolkit/
// crashreporter/test/Makefile.in. Assign its URL to MOZ_CRASHREPORTER_URL,
// which CrashSubmit.jsm uses as a server override.
let env = Components.classes["@mozilla.org/process/environment;1"]
.getService(Components.interfaces.nsIEnvironment);
let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT");
let serverUrl = env.get("MOZ_CRASHREPORTER_URL");
env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
env.set("MOZ_CRASHREPORTER_URL", SERVER_URL);
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
// Crash immediately
Services.prefs.setIntPref("dom.ipc.plugins.timeoutSecs", 0);
registerCleanupFunction(Task.async(function*() {
Services.prefs.clearUserPref("dom.ipc.plugins.timeoutSecs");
env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
env.set("MOZ_CRASHREPORTER_URL", serverUrl);
env = null;
config = null;
gTestBrowser = null;
gBrowser.removeCurrentTab();
window.focus();
}));
});
add_task(function* () {
config = {
shouldSubmissionUIBeVisible: true,
comment: "",
urlOptIn: false
};
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED);
let pluginCrashed = promisePluginCrashed();
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_crashCommentAndURL.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
// Wait for the plugin to crash
yield pluginCrashed;
let crashReportStatus = TestUtils.topicObserved("crash-report-status", onSubmitStatus);
// Test that the crash submission UI is actually visible and submit the crash report.
yield ContentTask.spawn(gTestBrowser, config, function* (aConfig) {
let doc = content.document;
let plugin = doc.getElementById("plugin");
let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
let submitButton = doc.getAnonymousElementByAttribute(plugin, "anonid", "submitButton");
// Test that we don't send the URL when urlOptIn is false.
doc.getAnonymousElementByAttribute(plugin, "anonid", "submitURLOptIn").checked = aConfig.urlOptIn;
submitButton.click();
Assert.equal(content.getComputedStyle(pleaseSubmit).display == "block",
aConfig.shouldSubmissionUIBeVisible, "The crash UI should be visible");
});
yield crashReportStatus;
});
add_task(function* () {
config = {
shouldSubmissionUIBeVisible: true,
comment: "a test comment",
urlOptIn: true
};
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED);
let pluginCrashed = promisePluginCrashed();
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_crashCommentAndURL.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
// Wait for the plugin to crash
yield pluginCrashed;
let crashReportStatus = TestUtils.topicObserved("crash-report-status", onSubmitStatus);
// Test that the crash submission UI is actually visible and submit the crash report.
yield ContentTask.spawn(gTestBrowser, config, function* (aConfig) {
let doc = content.document;
let plugin = doc.getElementById("plugin");
let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
let submitButton = doc.getAnonymousElementByAttribute(plugin, "anonid", "submitButton");
// Test that we send the URL when urlOptIn is true.
doc.getAnonymousElementByAttribute(plugin, "anonid", "submitURLOptIn").checked = aConfig.urlOptIn;
doc.getAnonymousElementByAttribute(plugin, "anonid", "submitComment").value = aConfig.comment;
submitButton.click();
Assert.equal(content.getComputedStyle(pleaseSubmit).display == "block",
aConfig.shouldSubmissionUIBeVisible, "The crash UI should be visible");
});
yield crashReportStatus;
});
add_task(function* () {
config = {
shouldSubmissionUIBeVisible: false,
comment: "",
urlOptIn: true
};
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED);
let pluginCrashed = promisePluginCrashed();
// Make sure that the plugin container is too small to display the crash submission UI
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_crashCommentAndURL.html?" +
encodeURIComponent(JSON.stringify({width: 300, height: 300})));
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
// Wait for the plugin to crash
yield pluginCrashed;
// Test that the crash submission UI is not visible and do not submit a crash report.
yield ContentTask.spawn(gTestBrowser, config, function* (aConfig) {
let doc = content.document;
let plugin = doc.getElementById("plugin");
let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
Assert.equal(!!pleaseSubmit && content.getComputedStyle(pleaseSubmit).display == "block",
aConfig.shouldSubmissionUIBeVisible, "Plugin crash UI should not be visible");
});
});
function promisePluginCrashed() {
return new ContentTask.spawn(gTestBrowser, {}, function* () {
yield new Promise((resolve) => {
addEventListener("PluginCrashReporterDisplayed", function onPluginCrashed() {
removeEventListener("PluginCrashReporterDisplayed", onPluginCrashed);
resolve();
});
});
})
}
function onSubmitStatus(aSubject, aData) {
// Wait for success or failed, doesn't matter which.
if (aData != "success" && aData != "failed")
return false;
let propBag = aSubject.QueryInterface(Ci.nsIPropertyBag);
if (aData == "success") {
let remoteID = getPropertyBagValue(propBag, "serverCrashID");
ok(!!remoteID, "serverCrashID should be set");
// Remove the submitted report file.
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(Services.crashmanager._submittedDumpsDir);
file.append(remoteID + ".txt");
ok(file.exists(), "Submitted report file should exist");
file.remove(false);
}
let extra = getPropertyBagValue(propBag, "extra");
ok(extra instanceof Ci.nsIPropertyBag, "Extra data should be property bag");
let val = getPropertyBagValue(extra, "PluginUserComment");
if (config.comment)
is(val, config.comment,
"Comment in extra data should match comment in textbox");
else
ok(val === undefined,
"Comment should be absent from extra data when textbox is empty");
val = getPropertyBagValue(extra, "PluginContentURL");
if (config.urlOptIn)
is(val, gBrowser.currentURI.spec,
"URL in extra data should match browser URL when opt-in checked");
else
ok(val === undefined,
"URL should be absent from extra data when opt-in not checked");
return true;
}
function getPropertyBagValue(bag, key) {
try {
var val = bag.getProperty(key);
}
catch (e) {
if (e.result != Cr.NS_ERROR_FAILURE) {
throw e;
}
}
return val;
}
|