var rootDir = getRootDirectory(gTestPath); const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; const PLUGIN_PAGE = gTestRoot + "plugin_big.html"; const PLUGIN_SMALL_PAGE = gTestRoot + "plugin_small.html"; /** * Takes an nsIPropertyBag and converts it into a JavaScript Object. It * will also convert any nsIPropertyBag's within the nsIPropertyBag * recursively. * * @param aBag * The nsIPropertyBag to convert. * @return Object * Keyed on the names of the nsIProperty's within the nsIPropertyBag, * and mapping to their values. */ function convertPropertyBag(aBag) { let result = {}; let enumerator = aBag.enumerator; while(enumerator.hasMoreElements()) { let { name, value } = enumerator.getNext().QueryInterface(Ci.nsIProperty); if (value instanceof Ci.nsIPropertyBag) { value = convertPropertyBag(value); } result[name] = value; } return result; } add_task(function* setup() { setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); // 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 = Cc["@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); Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); registerCleanupFunction(function cleanUp() { clearAllPluginPermissions(); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport); env.set("MOZ_CRASHREPORTER_URL", serverURL); Services.prefs.clearUserPref("plugins.click_to_play"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); window.focus(); }); }); /** * Test that plugin crash submissions still work properly after * click-to-play activation. */ add_task(function*() { yield BrowserTestUtils.withNewTab({ gBrowser, url: PLUGIN_PAGE, }, function* (browser) { // Work around for delayed PluginBindingAttached yield promiseUpdatePluginBindings(browser); let pluginInfo = yield promiseForPluginInfo("test", browser); ok(!pluginInfo.activated, "Plugin should not be activated"); // Simulate clicking the "Allow Always" button. let notification = PopupNotifications.getNotification("click-to-play-plugins", browser); yield promiseForNotificationShown(notification, browser); PopupNotifications.panel.firstChild._primaryButton.click(); // Prepare a crash report topic observer that only returns when // the crash report has been successfully sent. let crashReportChecker = (subject, data) => { return (data == "success"); }; let crashReportPromise = TestUtils.topicObserved("crash-report-status", crashReportChecker); yield ContentTask.spawn(browser, null, function*() { let plugin = content.document.getElementById("test"); plugin.QueryInterface(Ci.nsIObjectLoadingContent); yield ContentTaskUtils.waitForCondition(() => { return plugin.activated; }, "Waited too long for plugin to activate."); try { Components.utils.waiveXrays(plugin).crash(); } catch(e) { } let doc = plugin.ownerDocument; let getUI = (anonid) => { return doc.getAnonymousElementByAttribute(plugin, "anonid", anonid); }; // Now wait until the plugin crash report UI shows itself, which is // asynchronous. let statusDiv; yield ContentTaskUtils.waitForCondition(() => { statusDiv = getUI("submitStatus"); return statusDiv.getAttribute("status") == "please"; }, "Waited too long for plugin to show crash report UI"); // Make sure the UI matches our expectations... let style = content.getComputedStyle(getUI("pleaseSubmit")); if (style.display != "block") { return Promise.reject(`Submission UI visibility is not correct. ` + `Expected block style, got ${style.display}.`); } // Fill the crash report in with some test values that we'll test for in // the parent. getUI("submitComment").value = "a test comment"; let optIn = getUI("submitURLOptIn"); if (!optIn.checked) { return Promise.reject("URL opt-in should default to true."); } // Submit the report. optIn.click(); getUI("submitButton").click(); // And wait for the parent to say that the crash report was submitted // successfully. yield ContentTaskUtils.waitForCondition(() => { return statusDiv.getAttribute("status") == "success"; }, "Timed out waiting for plugin binding to be in success state"); }); let [subject, data] = yield crashReportPromise; ok(subject instanceof Ci.nsIPropertyBag, "The crash report subject should be an nsIPropertyBag."); let crashData = convertPropertyBag(subject); ok(crashData.serverCrashID, "Should have a serverCrashID set."); // Remove the submitted report file after ensuring it exists. let file = Cc["@mozilla.org/file/local;1"] .createInstance(Ci.nsILocalFile); file.initWithPath(Services.crashmanager._submittedDumpsDir); file.append(crashData.serverCrashID + ".txt"); ok(file.exists(), "Submitted report file should exist"); file.remove(false); ok(crashData.extra, "Extra data should exist"); is(crashData.extra.PluginUserComment, "a test comment", "Comment in extra data should match comment in textbox"); is(crashData.extra.PluginContentURL, undefined, "URL should be absent from extra data when opt-in not checked"); }); }); /** * Test that plugin crash submissions still work properly after * click-to-play with the notification bar. */ add_task(function*() { yield BrowserTestUtils.withNewTab({ gBrowser, url: PLUGIN_SMALL_PAGE, }, function* (browser) { // Work around for delayed PluginBindingAttached yield promiseUpdatePluginBindings(browser); let pluginInfo = yield promiseForPluginInfo("test", browser); ok(pluginInfo.activated, "Plugin should be activated from previous test"); // Prepare a crash report topic observer that only returns when // the crash report has been successfully sent. let crashReportChecker = (subject, data) => { return (data == "success"); }; let crashReportPromise = TestUtils.topicObserved("crash-report-status", crashReportChecker); yield ContentTask.spawn(browser, null, function*() { let plugin = content.document.getElementById("test"); plugin.QueryInterface(Ci.nsIObjectLoadingContent); yield ContentTaskUtils.waitForCondition(() => { return plugin.activated; }, "Waited too long for plugin to activate."); try { Components.utils.waiveXrays(plugin).crash(); } catch(e) {} }); // Wait for the notification bar to be displayed. let notification = yield waitForNotificationBar("plugin-crashed", browser); // Then click the button to submit the crash report. let buttons = notification.querySelectorAll(".notification-button"); is(buttons.length, 2, "Should have two buttons."); // The "Submit Crash Report" button should be the second one. let submitButton = buttons[1]; submitButton.click(); let [subject, data] = yield crashReportPromise; ok(subject instanceof Ci.nsIPropertyBag, "The crash report subject should be an nsIPropertyBag."); let crashData = convertPropertyBag(subject); ok(crashData.serverCrashID, "Should have a serverCrashID set."); // Remove the submitted report file after ensuring it exists. let file = Cc["@mozilla.org/file/local;1"] .createInstance(Ci.nsILocalFile); file.initWithPath(Services.crashmanager._submittedDumpsDir); file.append(crashData.serverCrashID + ".txt"); ok(file.exists(), "Submitted report file should exist"); file.remove(false); is(crashData.extra.PluginContentURL, undefined, "URL should be absent from extra data when opt-in not checked"); }); });