From 11fe8e5d830e20976c5e428783ae8ee5874467fa Mon Sep 17 00:00:00 2001 From: Girish Sharma Date: Tue, 30 Apr 2013 22:28:04 +0530 Subject: [PATCH 1/9] Bug 862398 - Maintain the order of tools in the tab and the options panel list, r=past --- browser/devtools/framework/gDevTools.jsm | 24 +++++++++-------- .../framework/test/browser_toolbox_options.js | 21 ++++++++++++++- browser/devtools/framework/toolbox-options.js | 5 ++++ browser/devtools/framework/toolbox.js | 26 ++++++++++++++++--- 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/browser/devtools/framework/gDevTools.jsm b/browser/devtools/framework/gDevTools.jsm index 89f75b22ddb7..e49c2c532fca 100644 --- a/browser/devtools/framework/gDevTools.jsm +++ b/browser/devtools/framework/gDevTools.jsm @@ -192,6 +192,7 @@ this.devtools = { }; const FORBIDDEN_IDS = new Set(["toolbox", ""]); +const MAX_ORDINAL = 99; /** * DevTools is a class that represents a set of developer tools, it holds a @@ -274,8 +275,17 @@ DevTools.prototype = { } }, + /** + * Sorting function used for sorting tools based on their ordinals. + */ + ordinalSort: function DT_ordinalSort(d1, d2) { + let o1 = (typeof d1.ordinal == "number") ? d1.ordinal : MAX_ORDINAL; + let o2 = (typeof d2.ordinal == "number") ? d2.ordinal : MAX_ORDINAL; + return o1 - o2; + }, + getDefaultTools: function DT_getDefaultTools() { - return devtools.defaultTools; + return devtools.defaultTools.sort(this.ordinalSort); }, getAdditionalTools: function DT_getAdditionalTools() { @@ -285,7 +295,7 @@ DevTools.prototype = { tools.push(value); } } - return tools; + return tools.sort(this.ordinalSort); }, /** @@ -327,20 +337,12 @@ DevTools.prototype = { * A sorted array of the tool definitions registered in this instance */ getToolDefinitionArray: function DT_getToolDefinitionArray() { - const MAX_ORDINAL = 99; - let definitions = []; for (let [id, definition] of this.getToolDefinitionMap()) { definitions.push(definition); } - definitions.sort(function(d1, d2) { - let o1 = (typeof d1.ordinal == "number") ? d1.ordinal : MAX_ORDINAL; - let o2 = (typeof d2.ordinal == "number") ? d2.ordinal : MAX_ORDINAL; - return o1 - o2; - }); - - return definitions; + return definitions.sort(this.ordinalSort); }, /** diff --git a/browser/devtools/framework/test/browser_toolbox_options.js b/browser/devtools/framework/test/browser_toolbox_options.js index 6cdb5fcd6992..34d203ef545a 100644 --- a/browser/devtools/framework/test/browser_toolbox_options.js +++ b/browser/devtools/framework/test/browser_toolbox_options.js @@ -86,6 +86,10 @@ function checkTools() { for (let tool of toolsPref) { prefNodes.push(tool); } + // Randomize the order in which we remove the tool and then add them back so + // that we get to know if the tabs are correctly placed as per their ordinals. + prefNodes = prefNodes.sort(() => Math.random() > 0.5 ? 1: -1); + // Wait for the next turn of the event loop to avoid stack overflow errors. executeSoon(toggleTools); } @@ -124,7 +128,22 @@ function checkRegistered(event, data) { if (data == prefNodes[index - prefNodes.length].getAttribute("id")) { ok(true, "Correct tool added back"); // checking tab on the toolbox - ok(doc.getElementById("toolbox-tab-" + data), "Tab added back for " + data); + let radio = doc.getElementById("toolbox-tab-" + data); + ok(radio, "Tab added back for " + data); + if (radio.previousSibling) { + ok(+radio.getAttribute("ordinal") >= + +radio.previousSibling.getAttribute("ordinal"), + "Inserted tab's ordinal is greater than equal to its previous tab." + + "Expected " + radio.getAttribute("ordinal") + " >= " + + radio.previousSibling.getAttribute("ordinal")); + } + if (radio.nextSibling) { + ok(+radio.getAttribute("ordinal") < + +radio.nextSibling.getAttribute("ordinal"), + "Inserted tab's ordinal is less than its next tab. Expected " + + radio.getAttribute("ordinal") + " < " + + radio.nextSibling.getAttribute("ordinal")); + } index++; // Wait for the next turn of the event loop to avoid stack overflow errors. executeSoon(toggleTools); diff --git a/browser/devtools/framework/toolbox-options.js b/browser/devtools/framework/toolbox-options.js index b50d7a8dc459..3411c3712a0c 100644 --- a/browser/devtools/framework/toolbox-options.js +++ b/browser/devtools/framework/toolbox-options.js @@ -1,3 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; const { utils: Cu } = Components; const DISABLED_TOOLS = "devtools.toolbox.disabledTools"; diff --git a/browser/devtools/framework/toolbox.js b/browser/devtools/framework/toolbox.js index 12e74f4c2091..fdce3b5c8473 100644 --- a/browser/devtools/framework/toolbox.js +++ b/browser/devtools/framework/toolbox.js @@ -5,7 +5,7 @@ "use strict"; const {Cc, Ci, Cu} = require("chrome"); - +const MAX_ORDINAL = 99; let Promise = require("sdk/core/promise"); let EventEmitter = require("devtools/shared/event-emitter"); @@ -360,6 +360,10 @@ Toolbox.prototype = { radio.id = "toolbox-tab-" + id; radio.setAttribute("flex", "1"); radio.setAttribute("toolid", id); + if (toolDefinition.ordinal == undefined || toolDefinition.ordinal < 0) { + toolDefinition.ordinal = MAX_ORDINAL; + } + radio.setAttribute("ordinal", toolDefinition.ordinal); radio.setAttribute("tooltiptext", toolDefinition.tooltip); radio.addEventListener("command", function(id) { @@ -382,8 +386,24 @@ Toolbox.prototype = { vbox.id = "toolbox-panel-" + id; radio.appendChild(label); - tabs.appendChild(radio); - deck.appendChild(vbox); + + // If there is no tab yet, or the ordinal to be added is the largest one. + if (tabs.childNodes.length == 0 || + +tabs.lastChild.getAttribute("ordinal") <= toolDefinition.ordinal) { + tabs.appendChild(radio); + deck.appendChild(vbox); + } + // else, iterate over all the tabs to get the correct location. + else { + Array.some(tabs.childNodes, (node, i) => { + if (+node.getAttribute("ordinal") > toolDefinition.ordinal) { + tabs.insertBefore(radio, node); + deck.insertBefore(vbox, deck.childNodes[i + 1]); + // + 1 because of options panel. + return true; + } + }); + } this._addKeysToWindow(); }, From abeacccc51aa837b7f65d2f172cb8eea5a786118 Mon Sep 17 00:00:00 2001 From: Brandon Benvie Date: Tue, 30 Apr 2013 16:45:00 -0400 Subject: [PATCH 2/9] bug 867450 - Type in JSTerm.prototype._fetVarProperties; r=msucan --- browser/devtools/webconsole/webconsole.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index 920faf95e2ee..81c9de0e128f 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -3483,7 +3483,7 @@ JSTerm.prototype = { aProperty.evaluationMacro = this._variablesViewSimpleValueEvalMacro; } - let grips = [aProperty.value, aProperty.gettter, aProperty.settter]; + let grips = [aProperty.value, aProperty.getter, aProperty.setter]; grips.forEach(addActorForDescriptor); let inspectable = !VariablesView.isPrimitive({ value: aProperty.value }); From a12d04326171e902923574ac4f6cb3ed83d4bf1e Mon Sep 17 00:00:00 2001 From: Diogo Dauster Pontual Date: Fri, 26 Apr 2013 11:24:26 -0300 Subject: [PATCH 3/9] Bug 865680 - Remove the last of the two devtools.gcli.allowSet lines; r=mratcliffe --- browser/app/profile/firefox.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 8419aec5de4d..b294cb5c1d2a 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1109,9 +1109,6 @@ pref("devtools.gcli.hideIntro", false); // How eager are we to show help: never=1, sometimes=2, always=3 pref("devtools.gcli.eagerHelper", 2); -// Do we allow the 'pref set' command -pref("devtools.gcli.allowSet", false); - // Remember the Web Console filters pref("devtools.webconsole.filter.network", true); pref("devtools.webconsole.filter.networkinfo", true); From 6f0e8150abc73c36ce611aeec71cfd1ed8860ad9 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 30 Apr 2013 16:05:22 -0500 Subject: [PATCH 4/9] Bug 795978 - Error: this._containers is undefined in MarkupView.jsm Line: 327; r=mratcliffe --- browser/devtools/markupview/markup-view.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js index d5394e64873c..c0720e189e34 100644 --- a/browser/devtools/markupview/markup-view.js +++ b/browser/devtools/markupview/markup-view.js @@ -68,6 +68,8 @@ function MarkupView(aInspector, aFrame, aControllerWindow) this._boundFocus = this._onFocus.bind(this); this._frame.addEventListener("focus", this._boundFocus, false); + this._boundOnLoadRootNode = this._onLoadRootNode.bind(this); + this._initPreview(); } @@ -313,10 +315,7 @@ MarkupView.prototype = { var container = new RootContainer(this, aNode); this._elt.appendChild(container.elt); this._rootNode = aNode; - aNode.addEventListener("load", function MP_watch_contentLoaded(aEvent) { - // Fake a childList mutation here. - this._mutationObserver([{target: aEvent.target, type: "childList"}]); - }.bind(this), true); + aNode.addEventListener("load", this._boundOnLoadRootNode, true); } this._containers.set(aNode, container); @@ -334,6 +333,14 @@ MarkupView.prototype = { return container; }, + /** + * Update the state of the tree once the root node is loaded. + */ + _onLoadRootNode: function MV__onLoadRootNode(aEvent) { + // Fake a childList mutation here. + this._mutationObserver([{target: aEvent.target, type: "childList"}]); + }, + /** * Mutation observer used for included nodes. */ @@ -654,6 +661,9 @@ MarkupView.prototype = { this._inspector.selection.off("new-node", this._boundOnNewSelection); delete this._boundOnNewSelection; + this._rootNode.removeEventListener("load", this._boundOnLoadRootNode, true); + delete this._boundOnLoadRootNode; + delete this._elt; delete this._containers; From 80cfd9ffb670045b4ae03aa80106bad755e8433b Mon Sep 17 00:00:00 2001 From: Panos Astithas Date: Wed, 1 May 2013 18:29:33 +0300 Subject: [PATCH 5/9] Bug 832231 - After a reload, breakpoints require multiple resumes to allow execution to continue; r=vporof --- browser/devtools/debugger/test/Makefile.in | 3 + .../test/browser_dbg_location-changes-bp.js | 163 ++++++++++++++++++ .../test/test-location-changes-bp.html | 17 ++ .../debugger/test/test-location-changes-bp.js | 7 + .../debugger/server/dbg-browser-actors.js | 4 +- .../debugger/server/dbg-script-actors.js | 40 ++++- 6 files changed, 224 insertions(+), 10 deletions(-) create mode 100644 browser/devtools/debugger/test/browser_dbg_location-changes-bp.js create mode 100644 browser/devtools/debugger/test/test-location-changes-bp.html create mode 100644 browser/devtools/debugger/test/test-location-changes-bp.js diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in index d8f9b1ee8f0b..9f36eba1a313 100644 --- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -61,6 +61,7 @@ MOCHITEST_BROWSER_TESTS = \ browser_dbg_location-changes.js \ browser_dbg_location-changes-new.js \ browser_dbg_location-changes-blank.js \ + browser_dbg_location-changes-bp.js \ browser_dbg_sources-cache.js \ browser_dbg_scripts-switching.js \ browser_dbg_scripts-sorting.js \ @@ -133,6 +134,8 @@ MOCHITEST_BROWSER_PAGES = \ binary_search.coffee \ binary_search.js \ binary_search.map \ + test-location-changes-bp.js \ + test-location-changes-bp.html \ $(NULL) MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES diff --git a/browser/devtools/debugger/test/browser_dbg_location-changes-bp.js b/browser/devtools/debugger/test/browser_dbg_location-changes-bp.js new file mode 100644 index 000000000000..b5be16bf929b --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_location-changes-bp.js @@ -0,0 +1,163 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Make sure that reloading a page with a breakpoint set does not cause it to + * fire more than once. + */ + +const TAB_URL = EXAMPLE_URL + "test-location-changes-bp.html"; +const SCRIPT_URL = EXAMPLE_URL + "test-location-changes-bp.js"; + +var gPane = null; +var gTab = null; +var gDebuggee = null; +var gDebugger = null; +var sourcesShown = false; +var tabNavigated = false; + +function test() +{ + debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { + gTab = aTab; + gDebuggee = aDebuggee; + gPane = aPane; + gDebugger = gPane.panelWin; + + testAddBreakpoint(); + }); +} + +function testAddBreakpoint() +{ + let controller = gDebugger.DebuggerController; + controller.activeThread.addOneTimeListener("framesadded", function() { + Services.tm.currentThread.dispatch({ run: function() { + + var frames = gDebugger.DebuggerView.StackFrames._container._list; + + is(controller.activeThread.state, "paused", + "The debugger statement was reached."); + + is(frames.querySelectorAll(".dbg-stackframe").length, 1, + "Should have one frame."); + + gPane.addBreakpoint({ url: SCRIPT_URL, line: 5 }, testResume); + }}, 0); + }); + + gDebuggee.runDebuggerStatement(); +} + +function testResume() +{ + is(gDebugger.DebuggerController.activeThread.state, "paused", + "The breakpoint wasn't hit yet."); + + let thread = gDebugger.DebuggerController.activeThread; + thread.addOneTimeListener("resumed", function() { + thread.addOneTimeListener("paused", function() { + executeSoon(testBreakpointHit); + }); + + EventUtils.sendMouseEvent({ type: "click" }, + content.document.querySelector("button")); + }); + + thread.resume(); +} + +function testBreakpointHit() +{ + is(gDebugger.DebuggerController.activeThread.state, "paused", + "The breakpoint was hit."); + + let thread = gDebugger.DebuggerController.activeThread; + thread.addOneTimeListener("paused", function test(aEvent, aPacket) { + thread.addOneTimeListener("resumed", function() { + executeSoon(testReloadPage); + }); + + is(aPacket.why.type, "debuggerStatement", "Execution has advanced to the next line."); + isnot(aPacket.why.type, "breakpoint", "No ghost breakpoint was hit."); + thread.resume(); + }); + + thread.resume(); +} + +function testReloadPage() +{ + let controller = gDebugger.DebuggerController; + controller._target.once("navigate", function onTabNavigated(aEvent, aPacket) { + tabNavigated = true; + ok(true, "tabNavigated event was fired."); + info("Still attached to the tab."); + clickAgain(); + }); + + gDebugger.addEventListener("Debugger:SourceShown", function onSourcesShown() { + sourcesShown = true; + gDebugger.removeEventListener("Debugger:SourceShown", onSourcesShown); + clickAgain(); + }); + + content.location.reload(); +} + +function clickAgain() +{ + if (!sourcesShown || !tabNavigated) { + return; + } + + let controller = gDebugger.DebuggerController; + controller.activeThread.addOneTimeListener("framesadded", function() { + is(gDebugger.DebuggerController.activeThread.state, "paused", + "The breakpoint was hit."); + + 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(); + }); + + thread.resume(); + }); + + EventUtils.sendMouseEvent({ type: "click" }, + content.document.querySelector("button")); +} + +function testBreakpointHitAfterReload() +{ + is(gDebugger.DebuggerController.activeThread.state, "paused", + "The breakpoint was hit."); + + 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(); + }); + + thread.resume(); +} + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/test-location-changes-bp.html b/browser/devtools/debugger/test/test-location-changes-bp.html new file mode 100644 index 000000000000..0e18318947e2 --- /dev/null +++ b/browser/devtools/debugger/test/test-location-changes-bp.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/browser/devtools/debugger/test/test-location-changes-bp.js b/browser/devtools/debugger/test/test-location-changes-bp.js new file mode 100644 index 000000000000..d164b8bdf64d --- /dev/null +++ b/browser/devtools/debugger/test/test-location-changes-bp.js @@ -0,0 +1,7 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function myFunction() { + var a = 1; + debugger; +} diff --git a/toolkit/devtools/debugger/server/dbg-browser-actors.js b/toolkit/devtools/debugger/server/dbg-browser-actors.js index 6e541c7005dd..5645991a754f 100644 --- a/toolkit/devtools/debugger/server/dbg-browser-actors.js +++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js @@ -668,9 +668,6 @@ DebuggerProgressListener.prototype = { } if (isStart && aRequest instanceof Ci.nsIChannel) { - // If the request is about to happen in a new window, we are not concerned - // about the request. - // Proceed normally only if the debuggee is not paused. if (this._tabActor.threadActor.state == "paused") { aRequest.suspend(); @@ -679,6 +676,7 @@ DebuggerProgressListener.prototype = { this._tabActor._pendingNavigation = aRequest; } + this._tabActor.threadActor.disableAllBreakpoints(); this._tabActor.conn.send({ from: this._tabActor.actorID, type: "tabNavigated", diff --git a/toolkit/devtools/debugger/server/dbg-script-actors.js b/toolkit/devtools/debugger/server/dbg-script-actors.js index 1eea8b4c13f1..c1a846833eda 100644 --- a/toolkit/devtools/debugger/server/dbg-script-actors.js +++ b/toolkit/devtools/debugger/server/dbg-script-actors.js @@ -740,6 +740,22 @@ ThreadActor.prototype = { }); }, + /** + * Disassociate all breakpoint actors from their scripts and clear the + * breakpoint handlers. This method can be used when the thread actor intends + * to keep the breakpoint store, but needs to clear any actual breakpoints, + * e.g. due to a page navigation. This way the breakpoint actors' script + * caches won't hold on to the Debugger.Script objects leaking memory. + */ + disableAllBreakpoints: function () { + for (let url in this._breakpointStore) { + for (let line in this._breakpointStore[url]) { + let bp = this._breakpointStore[url][line]; + bp.actor.removeScripts(); + } + } + }, + /** * Handle a protocol request to pause the debuggee. */ @@ -1268,8 +1284,11 @@ ThreadActor.prototype = { // affect the loop. for (let line = existing.length - 1; line >= 0; line--) { let bp = existing[line]; - // Limit search to the line numbers contained in the new script. - if (bp && line >= aScript.startLine && line <= endLine) { + // Only consider breakpoints that are not already associated with + // scripts, and limit search to the line numbers contained in the new + // script. + if (bp && !bp.actor.scripts.length && + line >= aScript.startLine && line <= endLine) { this._setBreakpoint(bp); } } @@ -2050,6 +2069,16 @@ BreakpointActor.prototype = { this.scripts.push(aScript); }, + /** + * Remove the breakpoints from associated scripts and clear the script cache. + */ + removeScripts: function () { + for (let script of this.scripts) { + script.clearBreakpoint(this); + } + this.scripts = []; + }, + /** * A function that the engine calls when a breakpoint has been hit. * @@ -2079,12 +2108,9 @@ BreakpointActor.prototype = { // Remove from the breakpoint store. let scriptBreakpoints = this.threadActor._breakpointStore[this.location.url]; delete scriptBreakpoints[this.location.line]; - // Remove the actual breakpoint. this.threadActor._hooks.removeFromParentPool(this); - for (let script of this.scripts) { - script.clearBreakpoint(this); - } - this.scripts = null; + // Remove the actual breakpoint from the associated scripts. + this.removeScripts(); return { from: this.actorID }; } From 2307ce5a06274934200e86b6df7f2a409e50538f Mon Sep 17 00:00:00 2001 From: Rob Campbell Date: Mon, 6 May 2013 16:55:43 -0400 Subject: [PATCH 6/9] Bug 795978 - Backout - Error: this._containers is undefined in MarkupView.jsm Line: 327; r=orange --- browser/devtools/markupview/markup-view.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js index c0720e189e34..d5394e64873c 100644 --- a/browser/devtools/markupview/markup-view.js +++ b/browser/devtools/markupview/markup-view.js @@ -68,8 +68,6 @@ function MarkupView(aInspector, aFrame, aControllerWindow) this._boundFocus = this._onFocus.bind(this); this._frame.addEventListener("focus", this._boundFocus, false); - this._boundOnLoadRootNode = this._onLoadRootNode.bind(this); - this._initPreview(); } @@ -315,7 +313,10 @@ MarkupView.prototype = { var container = new RootContainer(this, aNode); this._elt.appendChild(container.elt); this._rootNode = aNode; - aNode.addEventListener("load", this._boundOnLoadRootNode, true); + aNode.addEventListener("load", function MP_watch_contentLoaded(aEvent) { + // Fake a childList mutation here. + this._mutationObserver([{target: aEvent.target, type: "childList"}]); + }.bind(this), true); } this._containers.set(aNode, container); @@ -333,14 +334,6 @@ MarkupView.prototype = { return container; }, - /** - * Update the state of the tree once the root node is loaded. - */ - _onLoadRootNode: function MV__onLoadRootNode(aEvent) { - // Fake a childList mutation here. - this._mutationObserver([{target: aEvent.target, type: "childList"}]); - }, - /** * Mutation observer used for included nodes. */ @@ -661,9 +654,6 @@ MarkupView.prototype = { this._inspector.selection.off("new-node", this._boundOnNewSelection); delete this._boundOnNewSelection; - this._rootNode.removeEventListener("load", this._boundOnLoadRootNode, true); - delete this._boundOnLoadRootNode; - delete this._elt; delete this._containers; From 744abbee169eb7d24d5e03f4dde9f2c2d0f424e1 Mon Sep 17 00:00:00 2001 From: Brandon Benvie Date: Mon, 6 May 2013 19:11:44 -0400 Subject: [PATCH 7/9] bug 808369 - Use VariablesView in the Scratchpad; r=msucan --- browser/devtools/scratchpad/scratchpad.js | 224 +++++++++++++----- browser/devtools/scratchpad/scratchpad.xul | 15 +- .../test/browser_scratchpad_inspect.js | 42 ++-- browser/themes/linux/devtools/scratchpad.css | 5 + browser/themes/linux/jar.mn | 1 + browser/themes/osx/devtools/scratchpad.css | 5 + browser/themes/osx/jar.mn | 1 + .../themes/shared/devtools/scratchpad.inc.css | 10 + .../themes/windows/devtools/scratchpad.css | 5 + browser/themes/windows/jar.mn | 2 + 10 files changed, 221 insertions(+), 89 deletions(-) create mode 100644 browser/themes/linux/devtools/scratchpad.css create mode 100644 browser/themes/osx/devtools/scratchpad.css create mode 100644 browser/themes/shared/devtools/scratchpad.inc.css create mode 100644 browser/themes/windows/devtools/scratchpad.css diff --git a/browser/devtools/scratchpad/scratchpad.js b/browser/devtools/scratchpad/scratchpad.js index 68b22c2c77b4..26c7f61399d5 100644 --- a/browser/devtools/scratchpad/scratchpad.js +++ b/browser/devtools/scratchpad/scratchpad.js @@ -30,6 +30,12 @@ Cu.import("resource:///modules/devtools/gDevTools.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js"); +XPCOMUtils.defineLazyModuleGetter(this, "VariablesView", + "resource:///modules/devtools/VariablesView.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "devtools", + "resource:///modules/devtools/gDevTools.jsm"); + const SCRATCHPAD_CONTEXT_CONTENT = 1; const SCRATCHPAD_CONTEXT_BROWSER = 2; const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties"; @@ -38,7 +44,9 @@ const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax"; const BUTTON_POSITION_SAVE = 0; const BUTTON_POSITION_CANCEL = 1; const BUTTON_POSITION_DONT_SAVE = 2; -const BUTTON_POSITION_REVERT=0; +const BUTTON_POSITION_REVERT = 0; +const VARIABLES_VIEW_URL = "chrome://browser/content/devtools/widgets/VariablesView.xul"; + /** * The scratchpad object handles the Scratchpad window functionality. @@ -253,6 +261,18 @@ var Scratchpad = { return "Scratchpad/" + this._instanceId; }, + + /** + * Sidebar that contains the VariablesView for object inspection. + */ + get sidebar() + { + if (!this._sidebar) { + this._sidebar = new ScratchpadSidebar(); + } + return this._sidebar; + }, + /** * Get the Cu.Sandbox object for the active tab content window object. Note * that the returned object is cached for later reuse. The cached object is @@ -423,25 +443,35 @@ var Scratchpad = { /** * Execute the selected text (if any) or the entire editor content in the - * current context. The resulting object is opened up in the Property Panel - * for inspection. + * current context. If the result is primitive then it is written as a + * comment. Otherwise, the resulting object is inspected up in the sidebar. * * @return Promise * The promise for the script evaluation result. */ inspect: function SP_inspect() { - let promise = this.execute(); - promise.then(([aString, aError, aResult]) => { + let deferred = Promise.defer(); + let reject = aReason => deferred.reject(aReason); + + this.execute().then(([aString, aError, aResult]) => { + let resolve = () => deferred.resolve([aString, aError, aResult]); + if (aError) { this.writeAsErrorComment(aError); + resolve(); + } + else if (!isObject(aResult)) { + this.writeAsComment(aResult); + resolve(); } else { this.deselect(); - this.openPropertyPanel(aString, aResult); + this.sidebar.open(aString, aResult).then(resolve, reject); } - }); - return promise; + }, reject); + + return deferred.promise; }, /** @@ -552,58 +582,6 @@ var Scratchpad = { this.writeAsComment(newComment); }, - /** - * Open the Property Panel to inspect the given object. - * - * @param string aEvalString - * The string that was evaluated. This is re-used when the user updates - * the properties list, by clicking the Update button. - * @param object aOutputObject - * The object to inspect, which is the aEvalString evaluation result. - * @return object - * The PropertyPanel object instance. - */ - openPropertyPanel: function SP_openPropertyPanel(aEvalString, aOutputObject) - { - let propPanel; - // The property panel has a button: - // `Update`: reexecutes the string executed on the command line. The - // result will be inspected by this panel. - let buttons = []; - - // If there is a evalString passed to this function, then add a `Update` - // button to the panel so that the evalString can be reexecuted to update - // the content of the panel. - if (aEvalString !== null) { - buttons.push({ - label: this.strings. - GetStringFromName("propertyPanel.updateButton.label"), - accesskey: this.strings. - GetStringFromName("propertyPanel.updateButton.accesskey"), - oncommand: () => { - this.evalForContext(aEvalString).then(([, aError, aResult]) => { - if (!aError) { - propPanel.treeView.data = { object: aResult }; - } - }); - } - }); - } - - let doc = this.browserWindow.document; - let parent = doc.getElementById("mainPopupSet"); - let title = String(aOutputObject); - propPanel = new PropertyPanel(parent, title, { object: aOutputObject }, - buttons); - - let panel = propPanel.panel; - panel.setAttribute("class", "scratchpad_propertyPanel"); - panel.openPopup(null, "after_pointer", 0, 0, false, false); - panel.sizeTo(200, 400); - - return propPanel; - }, - // Menu Operations /** @@ -1495,6 +1473,132 @@ var Scratchpad = { }, }; + +/** + * Encapsulates management of the sidebar containing the VariablesView for + * object inspection. + */ +function ScratchpadSidebar() +{ + let ToolSidebar = devtools.require("devtools/framework/sidebar").ToolSidebar; + let tabbox = document.querySelector("#scratchpad-sidebar"); + this._sidebar = new ToolSidebar(tabbox, this); + this._splitter = document.querySelector(".devtools-side-splitter"); +} + +ScratchpadSidebar.prototype = { + /* + * The ToolSidebar for this sidebar. + */ + _sidebar: null, + + /* + * The splitter element between the sidebar and the editor. + */ + _splitter: null, + + /* + * The VariablesView for this sidebar. + */ + variablesView: null, + + /* + * Whether the sidebar is currently shown. + */ + visible: false, + + /** + * Open the sidebar, if not open already, and populate it with the properties + * of the given object. + * + * @param string aString + * The string that was evaluated. + * @param object aObject + * The object to inspect, which is the aEvalString evaluation result. + * @return Promise + * A promise that will resolve once the sidebar is open. + */ + open: function SS_open(aEvalString, aObject) + { + this.show(); + + let deferred = Promise.defer(); + + let onTabReady = () => { + if (!this.variablesView) { + let window = this._sidebar.getWindowForTab("variablesview"); + let container = window.document.querySelector("#variables"); + this.variablesView = new VariablesView(container); + } + this._update(aObject).then(() => deferred.resolve()); + }; + + if (this._sidebar.getCurrentTabID() == "variablesview") { + onTabReady(); + } + else { + this._sidebar.once("variablesview-ready", onTabReady); + this._sidebar.addTab("variablesview", VARIABLES_VIEW_URL, true); + } + + return deferred.promise; + }, + + /** + * Show the sidebar. + */ + show: function SS_show() + { + if (!this.visible) { + this.visible = true; + this._sidebar.show(); + this._splitter.setAttribute("state", "open"); + } + }, + + /** + * Hide the sidebar. + */ + hide: function SS_hide() + { + if (this.visible) { + this.visible = false; + this._sidebar.hide(); + this._splitter.setAttribute("state", "collapsed"); + } + }, + + /** + * Update the object currently inspected by the sidebar. + * + * @param object aObject + * The object to inspect in the sidebar. + * @return Promise + * A promise that resolves when the update completes. + */ + _update: function SS__update(aObject) + { + let deferred = Promise.defer(); + + this.variablesView.rawObject = aObject; + + // In the future this will work on remote values (bug 825039). + setTimeout(() => deferred.resolve(), 0); + return deferred.promise; + } +}; + + +/** + * Check whether a value is non-primitive. + */ +function isObject(aValue) +{ + let type = typeof aValue; + return type == "object" ? aValue != null : type == "function"; +} + + /** * The PreferenceObserver listens for preference changes while Scratchpad is * running. diff --git a/browser/devtools/scratchpad/scratchpad.xul b/browser/devtools/scratchpad/scratchpad.xul index dd6edd351c4d..27a9aaa3da72 100644 --- a/browser/devtools/scratchpad/scratchpad.xul +++ b/browser/devtools/scratchpad/scratchpad.xul @@ -8,7 +8,9 @@ %scratchpadDTD; ]> - + + + @@ -280,7 +282,16 @@ - + + + + + + + + diff --git a/browser/devtools/scratchpad/test/browser_scratchpad_inspect.js b/browser/devtools/scratchpad/test/browser_scratchpad_inspect.js index 26e4b32bf179..115f7cd76d3c 100644 --- a/browser/devtools/scratchpad/test/browser_scratchpad_inspect.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_inspect.js @@ -12,47 +12,35 @@ function test() openScratchpad(runTests); }, true); - content.location = "data:text/html,foobarBug636725" + - "

test inspect() in Scratchpad"; + content.location = "data:text/html;charset=utf8,

test inspect() in Scratchpad

"; } function runTests() { let sp = gScratchpadWindow.Scratchpad; - sp.setText("document"); + sp.setText("({ a: 'foobarBug636725' })"); sp.inspect().then(function() { + let sidebar = sp.sidebar; + ok(sidebar.visible, "sidebar is open"); - let propPanel = document.querySelector(".scratchpad_propertyPanel"); - ok(propPanel, "property panel is open"); - propPanel.addEventListener("popupshown", function onPopupShown() { - propPanel.removeEventListener("popupshown", onPopupShown, false); + let found = false; - let tree = propPanel.querySelector("tree"); - ok(tree, "property panel tree found"); - - let column = tree.columns[0]; - let found = false; - - for (let i = 0; i < tree.view.rowCount; i++) { - let cell = tree.view.getCellText(i, column); - if (cell == 'title: "foobarBug636725"') { - found = true; - break; + outer: for (let scope in sidebar.variablesView) { + for (let [, obj] in scope) { + for (let [, prop] in obj) { + if (prop.name == "a" && prop.value == "foobarBug636725") { + found = true; + break outer; + } } } - ok(found, "found the document.title property"); + } - executeSoon(function() { - propPanel.hidePopup(); + ok(found, "found the property"); - finish(); - }); - }, false); - }, function() { - notok(true, "document not found"); finish(); }); -} +} \ No newline at end of file diff --git a/browser/themes/linux/devtools/scratchpad.css b/browser/themes/linux/devtools/scratchpad.css new file mode 100644 index 000000000000..86b4b167b0fe --- /dev/null +++ b/browser/themes/linux/devtools/scratchpad.css @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%include ../../shared/devtools/scratchpad.inc.css \ No newline at end of file diff --git a/browser/themes/linux/jar.mn b/browser/themes/linux/jar.mn index bb1ad03a3797..97cee9dcfc8e 100644 --- a/browser/themes/linux/jar.mn +++ b/browser/themes/linux/jar.mn @@ -171,6 +171,7 @@ browser.jar: skin/classic/browser/devtools/debugger.css (devtools/debugger.css) * skin/classic/browser/devtools/profiler.css (devtools/profiler.css) skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css) +* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css) skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png) skin/classic/browser/devtools/option-icon.png (devtools/option-icon.png) skin/classic/browser/devtools/itemToggle.png (devtools/itemToggle.png) diff --git a/browser/themes/osx/devtools/scratchpad.css b/browser/themes/osx/devtools/scratchpad.css new file mode 100644 index 000000000000..86b4b167b0fe --- /dev/null +++ b/browser/themes/osx/devtools/scratchpad.css @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%include ../../shared/devtools/scratchpad.inc.css \ No newline at end of file diff --git a/browser/themes/osx/jar.mn b/browser/themes/osx/jar.mn index 1f7dd632f380..804333358462 100644 --- a/browser/themes/osx/jar.mn +++ b/browser/themes/osx/jar.mn @@ -262,6 +262,7 @@ browser.jar: * skin/classic/browser/devtools/debugger.css (devtools/debugger.css) * skin/classic/browser/devtools/profiler.css (devtools/profiler.css) skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css) +* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css) skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png) skin/classic/browser/devtools/option-icon.png (devtools/option-icon.png) skin/classic/browser/devtools/itemToggle.png (devtools/itemToggle.png) diff --git a/browser/themes/shared/devtools/scratchpad.inc.css b/browser/themes/shared/devtools/scratchpad.inc.css new file mode 100644 index 000000000000..3895498a0998 --- /dev/null +++ b/browser/themes/shared/devtools/scratchpad.inc.css @@ -0,0 +1,10 @@ +%if 0 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +%endif + +#scratchpad-sidebar > tabs { + height: 0; + border: none; +} diff --git a/browser/themes/windows/devtools/scratchpad.css b/browser/themes/windows/devtools/scratchpad.css new file mode 100644 index 000000000000..86b4b167b0fe --- /dev/null +++ b/browser/themes/windows/devtools/scratchpad.css @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%include ../../shared/devtools/scratchpad.inc.css \ No newline at end of file diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn index 669f32d710a7..1f17b9249385 100644 --- a/browser/themes/windows/jar.mn +++ b/browser/themes/windows/jar.mn @@ -199,6 +199,7 @@ browser.jar: skin/classic/browser/devtools/debugger.css (devtools/debugger.css) * skin/classic/browser/devtools/profiler.css (devtools/profiler.css) skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css) +* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css) skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png) skin/classic/browser/devtools/option-icon.png (devtools/option-icon.png) skin/classic/browser/devtools/itemToggle.png (devtools/itemToggle.png) @@ -446,6 +447,7 @@ browser.jar: skin/classic/aero/browser/devtools/debugger.css (devtools/debugger.css) * skin/classic/aero/browser/devtools/profiler.css (devtools/profiler.css) skin/classic/aero/browser/devtools/netmonitor.css (devtools/netmonitor.css) +* skin/classic/aero/browser/devtools/scratchpad.css (devtools/scratchpad.css) skin/classic/aero/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png) skin/classic/aero/browser/devtools/option-icon.png (devtools/option-icon.png) skin/classic/aero/browser/devtools/itemToggle.png (devtools/itemToggle.png) From 594b2941b87665f04ddeab7c6ca46c9a85d28d7a Mon Sep 17 00:00:00 2001 From: Brandon Benvie Date: Tue, 30 Apr 2013 11:56:00 -0400 Subject: [PATCH 8/9] bug 679364 - Use content/browser/devtools/* for aliases; r=rcampbell --- browser/devtools/debugger/DebuggerUI.jsm | 2 +- browser/devtools/debugger/debugger.xul | 4 +- browser/devtools/jar.mn | 126 +++++++++--------- browser/devtools/main.js | 6 +- browser/devtools/profiler/ProfilerPanel.jsm | 2 +- browser/devtools/profiler/profiler.xul | 2 +- .../scratchpad/scratchpad-manager.jsm | 2 +- browser/devtools/scratchpad/scratchpad.xul | 4 +- .../sourceeditor/source-editor-orion.jsm | 2 +- browser/devtools/styleeditor/styleeditor.xul | 6 +- browser/devtools/webconsole/NetworkPanel.jsm | 2 +- 11 files changed, 79 insertions(+), 79 deletions(-) diff --git a/browser/devtools/debugger/DebuggerUI.jsm b/browser/devtools/debugger/DebuggerUI.jsm index 0cff12f81630..0795375134ad 100644 --- a/browser/devtools/debugger/DebuggerUI.jsm +++ b/browser/devtools/debugger/DebuggerUI.jsm @@ -9,7 +9,7 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; -const DBG_XUL = "chrome://browser/content/debugger.xul"; +const DBG_XUL = "chrome://browser/content/devtools/debugger.xul"; const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties"; const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger"; diff --git a/browser/devtools/debugger/debugger.xul b/browser/devtools/debugger/debugger.xul index 31eb74fb9fd0..ee8ab9d9cbf3 100644 --- a/browser/devtools/debugger/debugger.xul +++ b/browser/devtools/debugger/debugger.xul @@ -4,7 +4,7 @@ - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - + @@ -13,7 +13,7 @@ %debuggerDTD; ]> - +