Bug 916458 - Ignore failures to get the lastPausedUrl when a tab is closing. r=fitzgen, a=lsblakk
authorPanos Astithas <past@mozilla.com>
Wed, 18 Sep 2013 12:58:37 +0300
changeset 155765 69175fe3bd87fa2ece9f21d94b13ca141c502d33
parent 155764 fb1206d6a41caee144aa74ba50948404cbea3e0b
child 155766 bbea2bbc46eace0b31a7c531a25a171764a6d4b6
push id4402
push userryanvm@gmail.com
push dateTue, 15 Oct 2013 15:23:47 +0000
treeherdermozilla-aurora@bbea2bbc46ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen, lsblakk
bugs916458
milestone26.0a2
Bug 916458 - Ignore failures to get the lastPausedUrl when a tab is closing. r=fitzgen, a=lsblakk
browser/devtools/debugger/test/Makefile.in
browser/devtools/debugger/test/browser_dbg_clean-exit-window.js
browser/devtools/debugger/test/head.js
toolkit/devtools/server/actors/script.js
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -17,16 +17,17 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_breakpoints-contextmenu.js \
 	browser_dbg_breakpoints-disabled-reload.js \
 	browser_dbg_breakpoints-editor.js \
 	browser_dbg_breakpoints-highlight.js \
 	browser_dbg_breakpoints-new-script.js \
 	browser_dbg_breakpoints-pane.js \
 	browser_dbg_chrome-debugging.js \
 	browser_dbg_clean-exit.js \
+	browser_dbg_clean-exit-window.js \
 	browser_dbg_cmd-blackbox.js \
 	browser_dbg_cmd-break.js \
 	browser_dbg_cmd-dbg.js \
 	browser_dbg_conditional-breakpoints-01.js \
 	browser_dbg_conditional-breakpoints-02.js \
 	browser_dbg_debugger-statement.js \
 	browser_dbg_editor-contextmenu.js \
 	browser_dbg_editor-mode.js \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_clean-exit-window.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that closing a window with the debugger in a paused state exits cleanly.
+ */
+
+let gDebuggee, gPanel, gDebugger, gWindow;
+
+const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
+
+function test() {
+  addWindow(TAB_URL)
+    .then(win => initDebugger(TAB_URL, win))
+    .then(([aTab, aDebuggee, aPanel, aWindow]) => {
+      gDebuggee = aDebuggee;
+      gPanel = aPanel;
+      gDebugger = gPanel.panelWin;
+      gWindow = aWindow;
+
+      return testCleanExit(gWindow);
+    })
+    .then(null, aError => {
+      ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+    });
+}
+
+function testCleanExit(aWindow) {
+  let deferred = promise.defer();
+
+  gWindow = aWindow;
+  ok(!!gWindow, "Second window created.");
+
+  gWindow.focus();
+
+  let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
+  is(topWindow, gWindow,
+    "The second window is on top.");
+
+  let isActive = promise.defer();
+  let isLoaded = promise.defer();
+
+  promise.all([isActive.promise, isLoaded.promise]).then(() => {
+    gWindow.BrowserChromeTest.runWhenReady(() => {
+      waitForSourceAndCaretAndScopes(gPanel, ".html", 16).then(() => {
+        is(gDebugger.gThreadClient.paused, true,
+          "Should be paused after the debugger statement.");
+        gWindow.close();
+        deferred.resolve();
+        finish();
+      });
+
+      gDebuggee.runDebuggerStatement();
+    });
+  });
+
+  let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+  if (focusManager.activeWindow != gWindow) {
+    gWindow.addEventListener("activate", function onActivate(aEvent) {
+      if (aEvent.target != gWindow) {
+        return;
+      }
+      gWindow.removeEventListener("activate", onActivate, true);
+      isActive.resolve();
+    }, true);
+  } else {
+    isActive.resolve();
+  }
+
+  let contentLocation = gWindow.content.location.href;
+  if (contentLocation != TAB_URL) {
+    gWindow.document.addEventListener("load", function onLoad(aEvent) {
+      if (aEvent.target.documentURI != TAB_URL) {
+        return;
+      }
+      gWindow.document.removeEventListener("load", onLoad, true);
+      isLoaded.resolve();
+    }, true);
+  } else {
+    isLoaded.resolve();
+  }
+  return deferred.promise;
+}
+
+registerCleanupFunction(function() {
+  gWindow = null;
+  gDebuggee = null;
+  gPanel = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -411,28 +411,28 @@ function typeText(aElement, aText) {
 function backspaceText(aElement, aTimes) {
   info("Pressing backspace " + aTimes + " times.");
   for (let i = 0; i < aTimes; i++) {
     aElement.focus();
     EventUtils.sendKey("BACK_SPACE", aElement.ownerDocument.defaultView);
   }
 }
 
-function getTab(aTarget) {
+function getTab(aTarget, aWindow) {
   if (aTarget instanceof XULElement) {
     return promise.resolve(aTarget);
   } else {
-    return addTab(aTarget);
+    return addTab(aTarget, aWindow);
   }
 }
 
 function initDebugger(aTarget, aWindow) {
   info("Initializing a debugger panel.");
 
-  return getTab(aTarget).then(aTab => {
+  return getTab(aTarget, aWindow).then(aTab => {
     info("Debugee tab added successfully: " + aTarget);
 
     let deferred = promise.defer();
     let debuggee = aTab.linkedBrowser.contentWindow.wrappedJSObject;
     let target = TargetFactory.forTab(aTab);
 
     gDevTools.showToolbox(target, "jsdebugger").then(aToolbox => {
       info("Debugger panel shown successfully.");
@@ -440,17 +440,17 @@ function initDebugger(aTarget, aWindow) 
       let debuggerPanel = aToolbox.getCurrentPanel();
       let panelWin = debuggerPanel.panelWin;
 
       // Wait for the initial resume...
       panelWin.gClient.addOneTimeListener("resumed", () => {
         info("Debugger client resumed successfully.");
 
         prepareDebugger(debuggerPanel);
-        deferred.resolve([aTab, debuggee, debuggerPanel]);
+        deferred.resolve([aTab, debuggee, debuggerPanel, aWindow]);
       });
     });
 
     return deferred.promise;
   });
 }
 
 function initChromeDebugger(aOnClose) {
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -283,19 +283,27 @@ EventLoopStack.prototype = {
   get size() {
     return this._inspector.eventLoopNestLevel;
   },
 
   /**
    * The URL of the debuggee who pushed the event loop on top of the stack.
    */
   get lastPausedUrl() {
-    return this.size > 0
-      ? this._inspector.lastNestRequestor.url
-      : null;
+    let url = null;
+    if (this.size > 0) {
+      try {
+        url = this._inspector.lastNestRequestor.url
+      } catch (e) {
+        // The tab's URL getter may throw if the tab is destroyed by the time
+        // this code runs, but we don't really care at this point.
+        dumpn(e);
+      }
+    }
+    return url;
   },
 
   /**
    * Push a new nested event loop onto the stack.
    *
    * @returns EventLoop
    */
   push: function () {
@@ -931,17 +939,17 @@ ThreadActor.prototype = {
         message: "Can't resume when debuggee isn't paused. Current state is '"
           + this._state + "'"
       };
     }
 
     // In case of multiple nested event loops (due to multiple debuggers open in
     // different tabs or multiple debugger clients connected to the same tab)
     // only allow resumption in a LIFO order.
-    if (this._nestedEventLoops.size
+    if (this._nestedEventLoops.size && this._nestedEventLoops.lastPausedUrl
         && this._nestedEventLoops.lastPausedUrl !== this._hooks.url) {
       return {
         error: "wrongOrder",
         message: "trying to resume in the wrong order.",
         lastPausedUrl: this._nestedEventLoops.lastPausedUrl
       };
     }