Cu.import("resource://gre/modules/UpdateUtils.jsm"); function getNotificationBox(aWindow) { return aWindow.document.getElementById("high-priority-global-notificationbox"); } function promiseNotificationShown(aWindow, aName) { return new Promise((resolve) => { let notification = getNotificationBox(aWindow); notification.addEventListener("AlertActive", function active() { notification.removeEventListener("AlertActive", active, true); is(notification.allNotifications.length, 1, "Notification Displayed."); resolve(notification); }); }); } function promiseReportCallMade(aValue) { return new Promise((resolve) => { let old = gTestHangReport.testCallback; gTestHangReport.testCallback = function (val) { gTestHangReport.testCallback = old; is(aValue, val, "was the correct method call made on the hang report object?"); resolve(); }; }); } function pushPrefs(...aPrefs) { return new Promise((resolve) => { SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve); resolve(); }); } function popPrefs() { return new Promise((resolve) => { SpecialPowers.popPrefEnv(resolve); resolve(); }); } let gTestHangReport = { SLOW_SCRIPT: 1, PLUGIN_HANG: 2, TEST_CALLBACK_CANCELED: 1, TEST_CALLBACK_TERMSCRIPT: 2, TEST_CALLBACK_TERMPLUGIN: 3, _hangType: 1, _tcb: function (aCallbackType) {}, get hangType() { return this._hangType; }, set hangType(aValue) { this._hangType = aValue; }, set testCallback(aValue) { this._tcb = aValue; }, QueryInterface: function (aIID) { if (aIID.equals(Components.interfaces.nsIHangReport) || aIID.equals(Components.interfaces.nsISupports)) return this; throw Components.results.NS_NOINTERFACE; }, userCanceled: function () { this._tcb(this.TEST_CALLBACK_CANCELED); }, terminateScript: function () { this._tcb(this.TEST_CALLBACK_TERMSCRIPT); }, terminatePlugin: function () { this._tcb(this.TEST_CALLBACK_TERMPLUGIN); }, isReportForBrowser: function(aFrameLoader) { return true; } }; // on dev edition we add a button for js debugging of hung scripts. let buttonCount = (UpdateUtils.UpdateChannel == "aurora" ? 3 : 2); /** * Test if hang reports receive a terminate script callback when the user selects * stop in response to a script hang. */ add_task(function* terminateScriptTest() { let promise = promiseNotificationShown(window, "process-hang"); Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null); let notification = yield promise; let buttons = notification.currentNotification.getElementsByTagName("button"); // Fails on aurora on-push builds, bug 1232204 // is(buttons.length, buttonCount, "proper number of buttons"); // Click the "Stop It" button, we should get a terminate script callback gTestHangReport.hangType = gTestHangReport.SLOW_SCRIPT; promise = promiseReportCallMade(gTestHangReport.TEST_CALLBACK_TERMSCRIPT); buttons[0].click(); yield promise; }); /** * Test if hang reports receive user canceled callbacks after a user selects wait * and the browser frees up from a script hang on its own. */ add_task(function* waitForScriptTest() { let promise = promiseNotificationShown(window, "process-hang"); Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null); let notification = yield promise; let buttons = notification.currentNotification.getElementsByTagName("button"); // Fails on aurora on-push builds, bug 1232204 // is(buttons.length, buttonCount, "proper number of buttons"); yield pushPrefs(["browser.hangNotification.waitPeriod", 1000], ["browser.hangNotification.expiration", 2000]); function nocbcheck() { ok(false, "received a callback?"); } let oldcb = gTestHangReport.testCallback; gTestHangReport.testCallback = nocbcheck; // Click the "Wait" button this time, we shouldn't get a callback at all. buttons[1].click(); gTestHangReport.testCallback = oldcb; // send another hang pulse, we should not get a notification here Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null); is(notification.currentNotification, null, "no notification should be visible"); // After selecting Wait, we should get a userCanceled callback after // HANG_EXPIRATION_TIME. yield promiseReportCallMade(gTestHangReport.TEST_CALLBACK_CANCELED); yield popPrefs(); }); /** * Test if hang reports receive user canceled callbacks after the content * process stops sending hang notifications. */ add_task(function* hangGoesAwayTest() { yield pushPrefs(["browser.hangNotification.expiration", 1000]); let promise = promiseNotificationShown(window, "process-hang"); Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null); yield promise; yield promiseReportCallMade(gTestHangReport.TEST_CALLBACK_CANCELED); yield popPrefs(); }); /** * Tests if hang reports receive a terminate plugin callback when the user selects * stop in response to a plugin hang. */ add_task(function* terminatePluginTest() { let promise = promiseNotificationShown(window, "process-hang"); Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null); let notification = yield promise; let buttons = notification.currentNotification.getElementsByTagName("button"); // Fails on aurora on-push builds, bug 1232204 // is(buttons.length, buttonCount, "proper number of buttons"); // Click the "Stop It" button, we should get a terminate script callback gTestHangReport.hangType = gTestHangReport.PLUGIN_HANG; promise = promiseReportCallMade(gTestHangReport.TEST_CALLBACK_TERMPLUGIN); buttons[0].click(); yield promise; });