Bug 1391594 - Test that closing a window with a <xul:browser> with a hanging script causes that script to terminate. r?billm draft
authorMike Conley <mconley@mozilla.com>
Fri, 13 Oct 2017 16:23:37 -0400
changeset 682844 64a1ec0f8fdff8482ad10e938ac786d024cc8c96
parent 682843 7d138e2aef65a9ce97220893951fdc9116eaf2b5
child 736460 c749223ea9e430f1cf336b223f18b16835df9445
push id85178
push usermconley@mozilla.com
push dateWed, 18 Oct 2017 21:08:28 +0000
reviewersbillm
bugs1391594
milestone58.0a1
Bug 1391594 - Test that closing a window with a <xul:browser> with a hanging script causes that script to terminate. r?billm MozReview-Commit-ID: HwG0hcFdBtG
browser/modules/test/browser/browser_ProcessHangNotifications.js
--- a/browser/modules/test/browser/browser_ProcessHangNotifications.js
+++ b/browser/modules/test/browser/browser_ProcessHangNotifications.js
@@ -38,30 +38,38 @@ const ADDON_HANG = 3;
 const ADDON_ID = "fake-addon";
 
 /**
  * A mock nsIHangReport that we can pass through nsIObserverService
  * to trigger notifications.
  *
  * @param hangType
  *        One of SLOW_SCRIPT, PLUGIN_HANG, ADDON_HANG.
+ * @param browser (optional)
+ *        The <xul:browser> that this hang should be associated with.
+ *        If not supplied, the hang will be associated with every browser,
+ *        but the nsIHangReport.scriptBrowser attribute will return the
+ *        currently selected browser in this window's gBrowser.
  */
-let TestHangReport = function(hangType = SLOW_SCRIPT) {
+let TestHangReport = function(hangType = SLOW_SCRIPT,
+                              browser = gBrowser.selectedBrowser) {
   this.promise = new Promise((resolve, reject) => {
     this._resolver = resolve;
   });
 
   if (hangType == ADDON_HANG) {
     // Add-on hangs are actually script hangs, but have an associated
     // add-on ID for us to blame.
     this._hangType = SLOW_SCRIPT;
     this._addonId = ADDON_ID;
   } else {
     this._hangType = hangType;
   }
+
+  this._browser = browser;
 }
 
 TestHangReport.prototype = {
   SLOW_SCRIPT,
   PLUGIN_HANG,
 
   get addonId() {
     return this._addonId;
@@ -90,17 +98,25 @@ TestHangReport.prototype = {
     this._resolver(TEST_ACTION_TERMPLUGIN);
   },
 
   terminateGlobal() {
     this._resolver(TEST_ACTION_TERMGLOBAL);
   },
 
   isReportForBrowser(aFrameLoader) {
+    if (this._browser) {
+      return this._browser.frameLoader === aFrameLoader;
+    }
+
     return true;
+  },
+
+  get scriptBrowser() {
+    return this._browser;
   }
 };
 
 // on dev edition we add a button for js debugging of hung scripts.
 let buttonCount = (UpdateUtils.UpdateChannel == "aurora" ? 3 : 2);
 
 add_task(async function setup() {
   // Create a fake WebExtensionPolicy that we can use for
@@ -301,17 +317,17 @@ add_task(async function terminateAtShutd
 /**
  * Test that if there happens to be no open browser windows, that any
  * hang reports that exist or appear while in this state will be handled
  * automatically.
  */
 add_task(async function terminateNoWindows() {
   let testWin = await BrowserTestUtils.openNewBrowserWindow();
 
-  let pausedHang = new TestHangReport(SLOW_SCRIPT);
+  let pausedHang = new TestHangReport(SLOW_SCRIPT, testWin.gBrowser.selectedBrowser);
   Services.obs.notifyObservers(pausedHang, "process-hang-report");
   ProcessHangMonitor.waitLonger(testWin);
   ok(ProcessHangMonitor.findPausedReport(testWin.gBrowser.selectedBrowser),
      "There should be a paused report for the selected browser.");
 
   let pluginHang = new TestHangReport(PLUGIN_HANG);
   let scriptHang = new TestHangReport(SLOW_SCRIPT);
   let addonHang = new TestHangReport(ADDON_HANG);
@@ -365,8 +381,50 @@ add_task(async function terminateNoWindo
      "With no open windows, should have terminated plugin for plugin hang.");
   is(scriptAction2, TEST_ACTION_TERMSCRIPT,
      "With no open windows, should have terminated script for script hang.");
   is(addonAction2, TEST_ACTION_TERMGLOBAL,
      "With no open windows, should have terminated global for add-on hang.");
 
   document.documentElement.setAttribute("windowtype", "navigator:browser");
 });
+
+/**
+ * Test that if a script hang occurs in one browser window, and that
+ * browser window goes away, that we clear the hang. For plug-in hangs,
+ * we do the conservative thing and terminate any plug-in hangs when a
+ * window closes, even though we don't exactly know which window it
+ * belongs to.
+ */
+add_task(async function terminateClosedWindow() {
+  let testWin = await BrowserTestUtils.openNewBrowserWindow();
+  let testBrowser = testWin.gBrowser.selectedBrowser;
+
+  let pausedHang = new TestHangReport(SLOW_SCRIPT, testBrowser);
+  Services.obs.notifyObservers(pausedHang, "process-hang-report");
+  ProcessHangMonitor.waitLonger(testWin);
+  ok(ProcessHangMonitor.findPausedReport(testWin.gBrowser.selectedBrowser),
+     "There should be a paused report for the selected browser.");
+
+  let pluginHang = new TestHangReport(PLUGIN_HANG, testBrowser);
+  let scriptHang = new TestHangReport(SLOW_SCRIPT, testBrowser);
+  let addonHang = new TestHangReport(ADDON_HANG, testBrowser);
+
+  [pluginHang, scriptHang, addonHang].forEach(hangReport => {
+    Services.obs.notifyObservers(hangReport, "process-hang-report");
+  });
+
+  await BrowserTestUtils.closeWindow(testWin);
+
+  let pausedAction = await pausedHang.promise;
+  let pluginAction = await pluginHang.promise;
+  let scriptAction = await scriptHang.promise;
+  let addonAction = await addonHang.promise;
+
+  is(pausedAction, TEST_ACTION_TERMSCRIPT,
+     "When closing window, should have terminated script for a paused script hang.");
+  is(pluginAction, TEST_ACTION_TERMPLUGIN,
+     "When closing window, should have terminated hung plug-in.");
+  is(scriptAction, TEST_ACTION_TERMSCRIPT,
+     "When closing window, should have terminated script for script hang.");
+  is(addonAction, TEST_ACTION_TERMGLOBAL,
+     "When closing window, should have terminated global for add-on hang.");
+});