diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js index c7f7500c1da8..720b74b2a657 100644 --- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -72,14 +72,13 @@ let DebuggerController = { this._isDestroyed = true; window.removeEventListener("unload", this._shutdownDebugger, true); - DebuggerView.destroyPanes(); - DebuggerView.destroyEditor(); DebuggerView.Scripts.destroy(); DebuggerView.StackFrames.destroy(); DebuggerView.Breakpoints.destroy(); DebuggerView.Properties.destroy(); + DebuggerView.destroyPanes(); + DebuggerView.destroyEditor(); - DebuggerController.Breakpoints.destroy(); DebuggerController.SourceScripts.disconnect(); DebuggerController.StackFrames.disconnect(); DebuggerController.ThreadState.disconnect(); diff --git a/browser/devtools/debugger/debugger-view.js b/browser/devtools/debugger/debugger-view.js index 453ba4f6cd1b..4be670002481 100644 --- a/browser/devtools/debugger/debugger-view.js +++ b/browser/devtools/debugger/debugger-view.js @@ -57,6 +57,14 @@ let DebuggerView = { let variables = document.getElementById("variables"); Prefs.variablesWidth = variables.getAttribute("width"); + + let bkps = document.getElementById("breakpoints"); + let frames = document.getElementById("stackframes"); + bkps.parentNode.removeChild(bkps); + frames.parentNode.removeChild(frames); + + stackframes.parentNode.removeChild(stackframes); + variables.parentNode.removeChild(variables); }, /** @@ -544,6 +552,8 @@ ScriptsView.prototype = { this._searchbox.removeEventListener("select", this._onScriptsSearch, false); this._searchbox.removeEventListener("input", this._onScriptsSearch, false); this._searchbox.removeEventListener("keyup", this._onScriptsKeyUp, false); + + this.empty(); this._scripts = null; this._searchbox = null; } @@ -859,6 +869,7 @@ StackFramesView.prototype = { frames.removeEventListener("scroll", this._onFramesScroll, false); window.removeEventListener("resize", this._onFramesScroll, false); + this.empty(); this._frames = null; } }; @@ -878,6 +889,7 @@ BreakpointsView.prototype = { */ empty: function DVB_empty() { let firstChild; + while (firstChild = this._breakpoints.firstChild) { this._destroyContextMenu(firstChild); this._breakpoints.removeChild(firstChild); @@ -1287,8 +1299,8 @@ BreakpointsView.prototype = { let commandsetId = "breakpointMenuCommands-" + aBreakpoint.id; let menupopupId = "breakpointContextMenu-" + aBreakpoint.id; - let commandsset = document.createElement("commandsset"); - commandsset.setAttribute("id", commandsetId); + let commandset = document.createElement("commandset"); + commandset.setAttribute("id", commandsetId); let menupopup = document.createElement("menupopup"); menupopup.setAttribute("id", menupopupId); @@ -1321,7 +1333,7 @@ BreakpointsView.prototype = { menuitem.setAttribute("command", commandId); menuitem.setAttribute("hidden", aHiddenFlag); - commandsset.appendChild(command); + commandset.appendChild(command); menupopup.appendChild(menuitem); aBreakpoint[aName] = { @@ -1354,7 +1366,10 @@ BreakpointsView.prototype = { let popupset = document.getElementById("debugger-popups"); popupset.appendChild(menupopup); - document.documentElement.appendChild(commandsset); + document.documentElement.appendChild(commandset); + + aBreakpoint.commandsetId = commandsetId; + aBreakpoint.menupopupId = menupopupId; return menupopupId; }, @@ -1366,18 +1381,15 @@ BreakpointsView.prototype = { * An element representing a breakpoint. */ _destroyContextMenu: function DVB__destroyContextMenu(aBreakpoint) { - let commandsetId = "breakpointMenuCommands-" + aBreakpoint.id; - let menupopupId = "breakpointContextMenu-" + aBreakpoint.id; - - let commandset = document.getElementById(commandsetId); - let menupopup = document.getElementById(menupopupId); - - if (commandset) { - commandset.parentNode.removeChild(commandset); - } - if (menupopup) { - menupopup.parentNode.removeChild(menupopup); + if (!aBreakpoint.commandsetId || !aBreakpoint.menupopupId) { + return; } + + let commandset = document.getElementById(aBreakpoint.commandsetId); + let menupopup = document.getElementById(aBreakpoint.menupopupId); + + commandset.parentNode.removeChild(commandset); + menupopup.parentNode.removeChild(menupopup); }, /** @@ -1398,6 +1410,7 @@ BreakpointsView.prototype = { let breakpoints = this._breakpoints; breakpoints.removeEventListener("click", this._onBreakpointClick, false); + this.empty(); this._breakpoints = null; } }; @@ -2499,6 +2512,8 @@ PropertiesView.prototype = { * Destruction function, called when the debugger is shut down. */ destroy: function DVP_destroy() { + this.empty(); + this._currHierarchy = null; this._prevHierarchy = null; this._vars = null; diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in index 573b5db9b911..ae5a6da797fe 100644 --- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -11,6 +11,7 @@ relativesrcdir = browser/devtools/debugger/test include $(DEPTH)/config/autoconf.mk MOCHITEST_BROWSER_TESTS = \ + browser_dbg_leaktest.js \ browser_dbg_createRemote.js \ browser_dbg_createChrome.js \ browser_dbg_debugger-tab-switch.js \ diff --git a/browser/devtools/debugger/test/browser_dbg_breakpoint-new-script.js b/browser/devtools/debugger/test/browser_dbg_breakpoint-new-script.js index a5ac45e6bd3f..bccfd6ffe132 100644 --- a/browser/devtools/debugger/test/browser_dbg_breakpoint-new-script.js +++ b/browser/devtools/debugger/test/browser_dbg_breakpoint-new-script.js @@ -49,24 +49,22 @@ function testAddBreakpoint() function testResume() { - gDebugger.DebuggerController.activeThread.addOneTimeListener("resumed", function test() { - gDebugger.DebuggerController.activeThread.addOneTimeListener("paused", function test() { + let thread = gDebugger.DebuggerController.activeThread; + thread.addOneTimeListener("resumed", function() { + thread.addOneTimeListener("paused", function() { executeSoon(testBreakpointHit); - }, false); + }); EventUtils.sendMouseEvent({ type: "click" }, - content.document.querySelector("button"), - content.window); + content.document.querySelector("button")); }); - gDebugger.DebuggerController.activeThread.resume(); + thread.resume(); } function testBreakpointHit() { - var frames = gDebugger.DebuggerView.StackFrames._frames; - is(gDebugger.DebuggerController.activeThread.state, "paused", "The breakpoint was hit."); @@ -76,10 +74,14 @@ function testBreakpointHit() function resumeAndFinish() { let thread = gDebugger.DebuggerController.activeThread; thread.addOneTimeListener("paused", function test(aEvent, aPacket) { + thread.addOneTimeListener("resumed", function() { + executeSoon(closeDebuggerAndFinish); + }); + is(aPacket.why.type, "debuggerStatement", "Execution has advanced to the next line."); isnot(aPacket.why.type, "breakpoint", "No ghost breakpoint was hit."); + thread.resume(); - closeDebuggerAndFinish(); }); thread.resume(); diff --git a/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js index 11d5cfe3e414..a9776846a5dd 100644 --- a/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js +++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js @@ -275,15 +275,21 @@ function test() is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct"); executeSoon(function() { - gDebugger.DebuggerController.activeThread.resume(finish); + gDebugger.gClient.addOneTimeListener("resumed", function() { + finalCheck(); + closeDebuggerAndFinish(); + }); + gDebugger.DebuggerController.activeThread.resume(); }); } - registerCleanupFunction(function() { + function finalCheck() { is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger"); ok(!gPane.getBreakpoint(gScripts.scriptLocations[0], 5), "getBreakpoint(scriptLocations[0], 5) returns no breakpoint"); + } + registerCleanupFunction(function() { removeTab(gTab); is(breakpointsAdded, 2, "correct number of breakpoints have been added"); is(breakpointsRemoved, 1, "correct number of breakpoints have been removed"); diff --git a/browser/devtools/debugger/test/browser_dbg_bug723071_editor-breakpoints-pane.js b/browser/devtools/debugger/test/browser_dbg_bug723071_editor-breakpoints-pane.js index 943da48a14f5..3feedb206d59 100644 --- a/browser/devtools/debugger/test/browser_dbg_bug723071_editor-breakpoints-pane.js +++ b/browser/devtools/debugger/test/browser_dbg_bug723071_editor-breakpoints-pane.js @@ -142,7 +142,13 @@ function test() gBreakpointsElement.querySelectorAll(".list-item.empty").length, "Found junk in the breakpoints container."); - finish(); + executeSoon(function() { + gDebugger.gClient.addOneTimeListener("resumed", function() { + finalCheck(); + closeDebuggerAndFinish(); + }); + gDebugger.DebuggerController.activeThread.resume(); + }); }); }); }); @@ -261,11 +267,13 @@ function test() } } - registerCleanupFunction(function() { + function finalCheck() { is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger"); ok(!gPane.getBreakpoint(gScripts.scriptLocations[0], 5), "getBreakpoint(scriptLocations[0], 5) returns no breakpoint"); + } + registerCleanupFunction(function() { is(breakpointsAdded, 3, "correct number of breakpoints have been added"); is(breakpointsDisabled, 3, "correct number of breakpoints have been disabled"); is(breakpointsRemoved, 3, "correct number of breakpoints have been removed"); diff --git a/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js b/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js index 95c4cb275e8e..217e9be5a4a0 100644 --- a/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js +++ b/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js @@ -113,7 +113,7 @@ function test() executeSoon(function() { contextMenu.hidePopup(); - gDebugger.DebuggerController.activeThread.resume(finish); + closeDebuggerAndFinish(); }); } diff --git a/browser/devtools/debugger/test/browser_dbg_leaktest.js b/browser/devtools/debugger/test/browser_dbg_leaktest.js new file mode 100644 index 000000000000..a50e3a18de3d --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_leaktest.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * This tests if the debugger leaks. + * If leaks happen here, there's something very, very fishy going on. + */ + +const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html"; + +let gPane = null; +let gTab = null; +let gDebuggee = null; +let gDebugger = null; + +function test() +{ + let scriptShown = false; + let framesAdded = false; + let resumed = false; + let testStarted = false; + + debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { + gTab = aTab; + gDebuggee = aDebuggee; + gPane = aPane; + gDebugger = gPane.contentWindow; + resumed = true; + + gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() { + framesAdded = true; + executeSoon(startTest); + }); + + executeSoon(function() { + gDebuggee.firstCall(); + }); + }); + + function onScriptShown(aEvent) + { + scriptShown = aEvent.detail.url.indexOf("-02.js") != -1; + executeSoon(startTest); + } + + window.addEventListener("Debugger:ScriptShown", onScriptShown); + + function startTest() + { + if (scriptShown && framesAdded && resumed && !testStarted) { + window.removeEventListener("Debugger:ScriptShown", onScriptShown); + testStarted = true; + Services.tm.currentThread.dispatch({ run: performTest }, 0); + } + } + + function performTest() + { + closeDebuggerAndFinish(); + } + + registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; + }); +}