diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in index 275083ae5cd2..de15fad53673 100644 --- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -22,6 +22,7 @@ MOCHITEST_BROWSER_TESTS = \ 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 \ diff --git a/browser/devtools/debugger/test/browser_dbg_clean-exit-window.js b/browser/devtools/debugger/test/browser_dbg_clean-exit-window.js new file mode 100644 index 000000000000..aa0afc2acf0c --- /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; +}); diff --git a/browser/devtools/debugger/test/head.js b/browser/devtools/debugger/test/head.js index 2cfbc601d650..19c1582c8311 100644 --- a/browser/devtools/debugger/test/head.js +++ b/browser/devtools/debugger/test/head.js @@ -416,18 +416,18 @@ function backspaceText(aElement, aTimes) { } } -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(); @@ -445,7 +445,7 @@ function initDebugger(aTarget, aWindow) { info("Debugger client resumed successfully."); prepareDebugger(debuggerPanel); - deferred.resolve([aTab, debuggee, debuggerPanel]); + deferred.resolve([aTab, debuggee, debuggerPanel, aWindow]); }); }); diff --git a/toolkit/devtools/server/actors/script.js b/toolkit/devtools/server/actors/script.js index 1d9b3692b235..0ae4530dcf6c 100644 --- a/toolkit/devtools/server/actors/script.js +++ b/toolkit/devtools/server/actors/script.js @@ -288,9 +288,17 @@ EventLoopStack.prototype = { * 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; }, /** @@ -936,7 +944,7 @@ ThreadActor.prototype = { // 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",