diff --git a/browser/components/sessionstore/test/browser_625016.js b/browser/components/sessionstore/test/browser_625016.js index 80644973fa9..dda710afcfc 100644 --- a/browser/components/sessionstore/test/browser_625016.js +++ b/browser/components/sessionstore/test/browser_625016.js @@ -23,6 +23,10 @@ function test() { Services.prefs.clearUserPref("browser.sessionstore.interval"); }); + waitForSaveState(setup); +} + +function setup() { // We'll clear all closed windows to make sure our state is clean // forgetClosedWindow doesn't trigger a delayed save while (ss.getClosedWindowCount()) { diff --git a/browser/components/tabview/groupitems.js b/browser/components/tabview/groupitems.js index 8bff7ef0abb..9f5088d8700 100644 --- a/browser/components/tabview/groupitems.js +++ b/browser/components/tabview/groupitems.js @@ -1191,6 +1191,10 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), { // dontAdjustTray - (boolean) if true, do not adjust the tray. addAppTab: function GroupItem_addAppTab(xulTab, options) { GroupItems.getAppTabFavIconUrl(xulTab, function(iconUrl) { + // The tab might have been removed or unpinned while waiting. + if (xulTab.closing || !xulTab.parentNode || !xulTab.pinned) + return; + let self = this; let $appTab = iQ("") .addClass("appTabIcon") diff --git a/browser/devtools/debugger/DebuggerUI.jsm b/browser/devtools/debugger/DebuggerUI.jsm index 18222e4d4f6..521260a7de9 100644 --- a/browser/devtools/debugger/DebuggerUI.jsm +++ b/browser/devtools/debugger/DebuggerUI.jsm @@ -578,6 +578,7 @@ DebuggerUI.prototype = { script.contentType = aContentType; elt.setUserData("sourceScript", script, null); dbg._updateEditorBreakpoints(); + dbg.debuggerWindow.StackFrames.updateEditor(); } }; diff --git a/browser/devtools/debugger/debugger-view.js b/browser/devtools/debugger/debugger-view.js index ea4d3bca2e2..6b335e19eff 100644 --- a/browser/devtools/debugger/debugger-view.js +++ b/browser/devtools/debugger/debugger-view.js @@ -280,6 +280,27 @@ DebuggerView.Stackframes = { } }, + /** + * Listener handling the step over button click event. + */ + _onStepOverClick: function DVF__onStepOverClick() { + ThreadState.activeThread.stepOver(); + }, + + /** + * Listener handling the step in button click event. + */ + _onStepInClick: function DVF__onStepInClick() { + ThreadState.activeThread.stepIn(); + }, + + /** + * Listener handling the step out button click event. + */ + _onStepOutClick: function DVF__onStepOutClick() { + ThreadState.activeThread.stepOut(); + }, + /** * Specifies if the active thread has more frames which need to be loaded. */ @@ -296,10 +317,16 @@ DebuggerView.Stackframes = { initialize: function DVF_initialize() { let close = document.getElementById("close"); let resume = document.getElementById("resume"); + let stepOver = document.getElementById("step-over"); + let stepIn = document.getElementById("step-in"); + let stepOut = document.getElementById("step-out"); let frames = document.getElementById("stackframes"); close.addEventListener("click", this._onCloseButtonClick, false); resume.addEventListener("click", this._onResumeButtonClick, false); + stepOver.addEventListener("click", this._onStepOverClick, false); + stepIn.addEventListener("click", this._onStepInClick, false); + stepOut.addEventListener("click", this._onStepOutClick, false); frames.addEventListener("scroll", this._onFramesScroll, false); window.addEventListener("resize", this._onFramesScroll, false); @@ -312,10 +339,16 @@ DebuggerView.Stackframes = { destroy: function DVF_destroy() { let close = document.getElementById("close"); let resume = document.getElementById("resume"); + let stepOver = document.getElementById("step-over"); + let stepIn = document.getElementById("step-in"); + let stepOut = document.getElementById("step-out"); let frames = this._frames; close.removeEventListener("click", this._onCloseButtonClick, false); resume.removeEventListener("click", this._onResumeButtonClick, false); + stepOver.removeEventListener("click", this._onStepOverClick, false); + stepIn.removeEventListener("click", this._onStepInClick, false); + stepOut.removeEventListener("click", this._onStepOutClick, false); frames.removeEventListener("click", this._onFramesClick, false); frames.removeEventListener("scroll", this._onFramesScroll, false); window.removeEventListener("resize", this._onFramesScroll, false); @@ -1198,3 +1231,6 @@ let DVF = DebuggerView.Stackframes; DVF._onFramesScroll = DVF._onFramesScroll.bind(DVF); DVF._onCloseButtonClick = DVF._onCloseButtonClick.bind(DVF); DVF._onResumeButtonClick = DVF._onResumeButtonClick.bind(DVF); +DVF._onStepOverClick = DVF._onStepOverClick.bind(DVF); +DVF._onStepInClick = DVF._onStepInClick.bind(DVF); +DVF._onStepOutClick = DVF._onStepOutClick.bind(DVF); diff --git a/browser/devtools/debugger/debugger.js b/browser/devtools/debugger/debugger.js index 3eba415a490..0a93b3e5fc4 100644 --- a/browser/devtools/debugger/debugger.js +++ b/browser/devtools/debugger/debugger.js @@ -24,6 +24,7 @@ * Dave Camp * Panos Astithas * Victor Porof + * Mihai Sucan * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -175,6 +176,7 @@ var StackFrames = { this.activeThread = aThreadClient; aThreadClient.addListener("paused", this.onPaused); + aThreadClient.addListener("resumed", this.onResume); aThreadClient.addListener("framesadded", this.onFrames); aThreadClient.addListener("framescleared", this.onFramesCleared); this.onFramesCleared(); @@ -186,6 +188,7 @@ var StackFrames = { */ disconnect: function TS_disconnect() { this.activeThread.removeListener("paused", this.onPaused); + this.activeThread.removeListener("resumed", this.onResume); this.activeThread.removeListener("framesadded", this.onFrames); this.activeThread.removeListener("framescleared", this.onFramesCleared); }, @@ -197,6 +200,13 @@ var StackFrames = { this.activeThread.fillFrames(this.pageSize); }, + /** + * Handler for the thread client's resumed notification. + */ + onResume: function SF_onResume() { + window.editor.setDebugLocation(-1); + }, + /** * Handler for the thread client's framesadded notification. */ @@ -265,11 +275,15 @@ var StackFrames = { // Move the editor's caret to the proper line. if (DebuggerView.Scripts.isSelected(frame.where.url) && frame.where.line) { window.editor.setCaretPosition(frame.where.line - 1); + window.editor.setDebugLocation(frame.where.line - 1); } else if (DebuggerView.Scripts.contains(frame.where.url)) { DebuggerView.Scripts.selectScript(frame.where.url); SourceScripts.onChange({ target: DebuggerView.Scripts._scripts }); window.editor.setCaretPosition(frame.where.line - 1); + } else { + window.editor.setDebugLocation(-1); } + // Display the local variables. let localScope = DebuggerView.Properties.localScope; localScope.empty(); @@ -301,6 +315,28 @@ var StackFrames = { } }, + /** + * Update the source editor current debug location based on the selected frame + * and script. + */ + updateEditor: function SF_updateEditor() { + if (this.selectedFrame === null) { + return; + } + + let frame = this.activeThread.cachedFrames[this.selectedFrame]; + if (!frame) { + return; + } + + // Move the editor's caret to the proper line. + if (DebuggerView.Scripts.isSelected(frame.where.url) && frame.where.line) { + window.editor.setDebugLocation(frame.where.line - 1); + } else { + window.editor.setDebugLocation(-1); + } + }, + _addExpander: function SF_addExpander(aVar, aObject) { // No need for expansion for null and undefined values, but we do need them // for frame.arguments which is a regular array. @@ -408,6 +444,7 @@ var StackFrames = { }; StackFrames.onPaused = StackFrames.onPaused.bind(StackFrames); +StackFrames.onResume = StackFrames.onResume.bind(StackFrames); StackFrames.onFrames = StackFrames.onFrames.bind(StackFrames); StackFrames.onFramesCleared = StackFrames.onFramesCleared.bind(StackFrames); StackFrames.onClick = StackFrames.onClick.bind(StackFrames); @@ -614,6 +651,7 @@ var SourceScripts = { } else { window.editor.setText(aScript.text); window.updateEditorBreakpoints(); + StackFrames.updateEditor(); } window.editor.resetUndo(); } diff --git a/browser/devtools/debugger/debugger.xul b/browser/devtools/debugger/debugger.xul index c51f331befc..0496062db49 100644 --- a/browser/devtools/debugger/debugger.xul +++ b/browser/devtools/debugger/debugger.xul @@ -73,6 +73,9 @@ &debuggerUI.closeButton; + &debuggerUI.stepOverButton; + &debuggerUI.stepInButton; + &debuggerUI.stepOutButton;
diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in index 7f433f5b9e2..8e4d47d449e 100644 --- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -67,6 +67,7 @@ _BROWSER_TEST_FILES = \ browser_dbg_stack-02.js \ browser_dbg_stack-03.js \ browser_dbg_stack-04.js \ + browser_dbg_stack-05.js \ browser_dbg_location-changes.js \ browser_dbg_script-switching.js \ browser_dbg_pause-resume.js \ diff --git a/browser/devtools/debugger/test/browser_dbg_script-switching.js b/browser/devtools/debugger/test/browser_dbg_script-switching.js index 000b93d326d..1a08ff7f2b6 100644 --- a/browser/devtools/debugger/test/browser_dbg_script-switching.js +++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js @@ -61,6 +61,9 @@ function testScriptsDisplay() { ok(gDebugger.editor.getText().search(/debugger/) != -1, "The correct script was loaded initially."); + is(gDebugger.editor.getDebugLocation(), 5, + "editor debugger location is correct."); + gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, function onChange() { gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, @@ -83,6 +86,9 @@ function testSwitchPaused() ok(gDebugger.editor.getText().search(/firstCall/) != -1, "The first script is displayed."); + is(gDebugger.editor.getDebugLocation(), -1, + "editor debugger location has been cleared."); + gDebugger.StackFrames.activeThread.resume(function() { gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, function onSecondChange() { @@ -103,6 +109,9 @@ function testSwitchRunning() ok(gDebugger.editor.getText().search(/firstCall/) == -1, "The first script is no longer displayed."); + is(gDebugger.editor.getDebugLocation(), -1, + "editor debugger location is still -1."); + closeDebuggerAndFinish(gTab); } diff --git a/browser/devtools/debugger/test/browser_dbg_stack-05.js b/browser/devtools/debugger/test/browser_dbg_stack-05.js new file mode 100644 index 00000000000..d5884d15e7d --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_stack-05.js @@ -0,0 +1,92 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Test that switching between stack frames properly sets the current debugger +// location in the source editor. + +const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html"; + +var gPane = null; +var gTab = null; +var gDebuggee = null; +var gDebugger = null; + +function test() { + debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { + gTab = aTab; + gDebuggee = aDebuggee; + gPane = aPane; + gDebugger = gPane.debuggerWindow; + + testRecurse(); + }); +} + +function testRecurse() { + gPane.activeThread.addOneTimeListener("scriptsadded", function() { + Services.tm.currentThread.dispatch({ run: function() { + let frames = gDebugger.DebuggerView.Stackframes._frames; + let childNodes = frames.childNodes; + + is(frames.querySelectorAll(".dbg-stackframe").length, 4, + "Correct number of frames."); + + is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length, + "All children should be frames."); + + ok(frames.querySelector("#stackframe-0").classList.contains("selected"), + "First frame should be selected by default."); + + ok(!frames.querySelector("#stackframe-2").classList.contains("selected"), + "Third frame should not be selected."); + + is(gDebugger.editor.getDebugLocation(), 5, + "editor debugger location is correct."); + + EventUtils.sendMouseEvent({ type: "click" }, + frames.querySelector("#stackframe-2"), + gDebugger); + + ok(!frames.querySelector("#stackframe-0").classList.contains("selected"), + "First frame should not be selected after click."); + + ok(frames.querySelector("#stackframe-2").classList.contains("selected"), + "Third frame should be selected after click."); + + is(gDebugger.editor.getDebugLocation(), 4, + "editor debugger location is correct after click."); + + EventUtils.sendMouseEvent({ type: "click" }, + frames.querySelector("#stackframe-0 .dbg-stackframe-name"), + gDebugger); + + ok(frames.querySelector("#stackframe-0").classList.contains("selected"), + "First frame should be selected after click inside the first frame."); + + ok(!frames.querySelector("#stackframe-2").classList.contains("selected"), + "Third frame should not be selected after click inside the first frame."); + + is(gDebugger.editor.getDebugLocation(), 5, + "editor debugger location is correct (frame 0 again)."); + + gDebugger.StackFrames.activeThread.resume(function() { + is(gDebugger.editor.getDebugLocation(), -1, + "editor debugger location is correct after resume."); + closeDebuggerAndFinish(gTab); + }); + }}, 0); + }); + + gDebuggee.firstCall(); +} + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/highlighter/inspector.jsm b/browser/devtools/highlighter/inspector.jsm index 31219064ca5..4498e60a458 100644 --- a/browser/devtools/highlighter/inspector.jsm +++ b/browser/devtools/highlighter/inspector.jsm @@ -1247,6 +1247,10 @@ InspectorUI.prototype = { if (computed) { computed.parentNode.removeChild(computed); } + let autosizer = node.querySelector(".autosizer"); + if (autosizer) { + autosizer.parentNode.removeChild(autosizer); + } } let text = node.textContent; diff --git a/browser/devtools/sourceeditor/source-editor-orion.jsm b/browser/devtools/sourceeditor/source-editor-orion.jsm index e4d78b88413..f6d379618c5 100644 --- a/browser/devtools/sourceeditor/source-editor-orion.jsm +++ b/browser/devtools/sourceeditor/source-editor-orion.jsm @@ -25,6 +25,7 @@ * Spyros Livathinos * Allen Eubank * Girish Sharma + * Pranav Ravichandran * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -112,6 +113,7 @@ const ORION_ANNOTATION_TYPES = { breakpoint: "orion.annotation.breakpoint", task: "orion.annotation.task", currentLine: "orion.annotation.currentLine", + debugLocation: "mozilla.annotation.debugLocation", }; /** @@ -150,6 +152,17 @@ const DEFAULT_KEYBINDINGS = [ ctrl: Services.appinfo.OS == "Darwin", alt: true, }, + { + action: "Comment", + code: Ci.nsIDOMKeyEvent.DOM_VK_SLASH, + accel: true, + }, + { + action: "Uncomment", + code: Ci.nsIDOMKeyEvent.DOM_VK_SLASH, + accel: true, + shift: true, + }, ]; var EXPORTED_SYMBOLS = ["SourceEditor"]; @@ -342,12 +355,7 @@ SourceEditor.prototype = { {styleClass: "ruler annotations"}); this._annotationRuler.onClick = this._annotationRulerClick.bind(this); this._annotationRuler.addAnnotationType(ORION_ANNOTATION_TYPES.breakpoint); - this._annotationRuler.setMultiAnnotation({ - html: "
" - }); - this._annotationRuler.setMultiAnnotationOverlay({ - html: "
" - }); + this._annotationRuler.addAnnotationType(ORION_ANNOTATION_TYPES.debugLocation); this._view.addRuler(this._annotationRuler); } @@ -373,6 +381,7 @@ SourceEditor.prototype = { this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.matchingBracket); this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.currentBracket); this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.breakpoint); + this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.debugLocation); this._overviewRuler.addAnnotationType(ORION_ANNOTATION_TYPES.task); this._view.addRuler(this._overviewRuler); } @@ -394,6 +403,8 @@ SourceEditor.prototype = { "Find Previous Occurrence": [this.ui.findPrevious, this.ui], "Goto Line...": [this.ui.gotoLine, this.ui], "Move Lines Down": [this._moveLines, this], + "Comment": [this._doComment, this], + "Uncomment": [this._doUncomment, this], }; for (let name in actions) { @@ -486,7 +497,7 @@ SourceEditor.prototype = { let selection = this.getSelection(); let model = this._model; let firstLine = model.getLineAtOffset(selection.start); - let firstLineStart = model.getLineStart(firstLine); + let firstLineStart = this.getLineStart(firstLine); let lastLineOffset = selection.end > selection.start ? selection.end - 1 : selection.end; let lastLine = model.getLineAtOffset(lastLineOffset); @@ -501,7 +512,7 @@ SourceEditor.prototype = { // Do selection indentation. if (firstLine != lastLine) { let lines = [""]; - let lastLineEnd = model.getLineEnd(lastLine, true); + let lastLineEnd = this.getLineEnd(lastLine, true); let selectedLines = lastLine - firstLine + 1; for (let i = firstLine; i <= lastLine; i++) { @@ -558,9 +569,9 @@ SourceEditor.prototype = { lines.push(line.substring(indent.length)); } - let firstLineStart = model.getLineStart(firstLine); - let lastLineStart = model.getLineStart(lastLine); - let lastLineEnd = model.getLineEnd(lastLine, true); + let firstLineStart = this.getLineStart(firstLine); + let lastLineStart = this.getLineStart(lastLine); + let lastLineEnd = this.getLineEnd(lastLine, true); this.startCompoundChange(); @@ -602,7 +613,7 @@ SourceEditor.prototype = { let model = this._model; let lineIndex = model.getLineAtOffset(selection.start); let lineText = model.getLine(lineIndex, true); - let lineStart = model.getLineStart(lineIndex); + let lineStart = this.getLineStart(lineIndex); let index = 0; let lineOffset = selection.start - lineStart; while (index < lineOffset && /[ \t]/.test(lineText.charAt(index))) { @@ -646,8 +657,8 @@ SourceEditor.prototype = { } let lastLine = model.getLineAtOffset(selection.end); - let firstLineStart = model.getLineStart(firstLine); - let lastLineStart = model.getLineStart(lastLine); + let firstLineStart = this.getLineStart(firstLine); + let lastLineStart = this.getLineStart(lastLine); if (selection.start != selection.end && lastLineStart == selection.end) { lastLine--; } @@ -655,17 +666,17 @@ SourceEditor.prototype = { return true; } - let lastLineEnd = model.getLineEnd(lastLine, true); + let lastLineEnd = this.getLineEnd(lastLine, true); let text = this.getText(firstLineStart, lastLineEnd); if (aLineAbove) { let aboveLine = firstLine - 1; - let aboveLineStart = model.getLineStart(aboveLine); + let aboveLineStart = this.getLineStart(aboveLine); this.startCompoundChange(); if (lastLine == (this.getLineCount() - 1)) { - let delimiterStart = model.getLineEnd(aboveLine); - let delimiterEnd = model.getLineEnd(aboveLine, true); + let delimiterStart = this.getLineEnd(aboveLine); + let delimiterEnd = this.getLineEnd(aboveLine, true); let lineDelimiter = this.getText(delimiterStart, delimiterEnd); text += lineDelimiter; this.setText("", firstLineStart - lineDelimiter.length, lastLineEnd); @@ -677,12 +688,12 @@ SourceEditor.prototype = { this.setSelection(aboveLineStart, aboveLineStart + text.length); } else { let belowLine = lastLine + 1; - let belowLineEnd = model.getLineEnd(belowLine, true); + let belowLineEnd = this.getLineEnd(belowLine, true); let insertAt = belowLineEnd - lastLineEnd + firstLineStart; let lineDelimiter = ""; if (belowLine == this.getLineCount() - 1) { - let delimiterStart = model.getLineEnd(lastLine); + let delimiterStart = this.getLineEnd(lastLine); lineDelimiter = this.getText(delimiterStart, lastLineEnd); text = lineDelimiter + text.substr(0, text.length - lineDelimiter.length); @@ -803,8 +814,8 @@ SourceEditor.prototype = { } let line = model.getLineAtOffset(newSelection.start); - let lineStart = model.getLineStart(line); - let lineEnd = model.getLineEnd(line); + let lineStart = this.getLineStart(line); + let lineEnd = this.getLineEnd(line); let title = oldAnnotation ? oldAnnotation.title : SourceEditorUI.strings.GetStringFromName("annotation.currentLine"); @@ -846,9 +857,9 @@ SourceEditor.prototype = { let selectionLineStart = model.getLineAtOffset(selection.start); let selectionLineEnd = model.getLineAtOffset(selection.end); let newStart = aLineIndex <= selectionLineStart ? - model.getLineStart(aLineIndex) : selection.start; + this.getLineStart(aLineIndex) : selection.start; let newEnd = aLineIndex <= selectionLineStart ? - selection.end : model.getLineEnd(aLineIndex); + selection.end : this.getLineEnd(aLineIndex); this.setSelection(newStart, newEnd); } else { this.setCaretPosition(aLineIndex); @@ -871,8 +882,8 @@ SourceEditor.prototype = { return; } - let newStart = this._model.getLineStart(aLineIndex); - let newEnd = this._model.getLineEnd(aLineIndex); + let newStart = this.getLineStart(aLineIndex); + let newEnd = this.getLineEnd(aLineIndex); this.setSelection(newStart, newEnd); }, @@ -897,6 +908,7 @@ SourceEditor.prototype = { styler.addAnnotationType(ORION_ANNOTATION_TYPES.matchingBracket); styler.addAnnotationType(ORION_ANNOTATION_TYPES.currentBracket); styler.addAnnotationType(ORION_ANNOTATION_TYPES.task); + styler.addAnnotationType(ORION_ANNOTATION_TYPES.debugLocation); if (this._config.highlightCurrentLine) { styler.addAnnotationType(ORION_ANNOTATION_TYPES.currentLine); @@ -946,8 +958,8 @@ SourceEditor.prototype = { return; } - let lineStart = this._model.getLineStart(aLineIndex); - let lineEnd = this._model.getLineEnd(aLineIndex); + let lineStart = this.getLineStart(aLineIndex); + let lineEnd = this.getLineEnd(aLineIndex); let annotations = this._getAnnotationsByType("breakpoint", lineStart, lineEnd); if (annotations.length > 0) { this.removeBreakpoint(aLineIndex); @@ -973,8 +985,8 @@ SourceEditor.prototype = { } let model = this._model; - let lineStart = model.getLineStart(aLineIndex); - let lineEnd = model.getLineEnd(aLineIndex); + let lineStart = this.getLineStart(aLineIndex); + let lineEnd = this.getLineEnd(aLineIndex); let annotations = this._annotationModel.getAnnotations(lineStart, lineEnd); let annotation = annotations.next(); @@ -997,6 +1009,158 @@ SourceEditor.prototype = { return this._iframe; }, + /** + * Helper function to retrieve the strings used for comments in the current + * editor mode. + * + * @private + * @return object + * An object that holds the following properties: + * - line: the comment string used for the start of a single line + * comment. + * - blockStart: the comment string used for the start of a comment + * block. + * - blockEnd: the comment string used for the end of a block comment. + * Null is returned for unsupported editor modes. + */ + _getCommentStrings: function SE__getCommentStrings() + { + let line = ""; + let blockCommentStart = ""; + let blockCommentEnd = ""; + + switch (this.getMode()) { + case SourceEditor.MODES.JAVASCRIPT: + line = "//"; + blockCommentStart = "/*"; + blockCommentEnd = "*/"; + break; + case SourceEditor.MODES.CSS: + blockCommentStart = "/*"; + blockCommentEnd = "*/"; + break; + case SourceEditor.MODES.HTML: + case SourceEditor.MODES.XML: + blockCommentStart = ""; + break; + default: + return null; + } + return {line: line, blockStart: blockCommentStart, blockEnd: blockCommentEnd}; + }, + + /** + * Wrap the selected text in comments. If nothing is selected the current + * caret line is commented out. Single line and block comments depend on the + * current editor mode. + * + * @private + */ + _doComment: function SE__doComment() + { + if (this.readOnly) { + return false; + } + + let commentObject = this._getCommentStrings(); + if (!commentObject) { + return false; + } + + let selection = this.getSelection(); + + if (selection.start == selection.end) { + let selectionLine = this._model.getLineAtOffset(selection.start); + let lineStartOffset = this.getLineStart(selectionLine); + if (commentObject.line) { + this.setText(commentObject.line, lineStartOffset, lineStartOffset); + } else { + let lineEndOffset = this.getLineEnd(selectionLine); + this.startCompoundChange(); + this.setText(commentObject.blockStart, lineStartOffset, lineStartOffset); + this.setText(commentObject.blockEnd, + lineEndOffset + commentObject.blockStart.length, + lineEndOffset + commentObject.blockStart.length); + this.endCompoundChange(); + } + } else { + this.startCompoundChange(); + this.setText(commentObject.blockStart, selection.start, selection.start); + this.setText(commentObject.blockEnd, + selection.end + commentObject.blockStart.length, + selection.end + commentObject.blockStart.length); + this.endCompoundChange(); + } + + return true; + }, + + /** + * Uncomment the selected text. If nothing is selected the current caret line + * is umcommented. Single line and block comments depend on the current editor + * mode. + * + * @private + */ + _doUncomment: function SE__doUncomment() + { + if (this.readOnly) { + return false; + } + + let commentObject = this._getCommentStrings(); + if (!commentObject) { + return false; + } + + let selection = this.getSelection(); + let firstLine = this._model.getLineAtOffset(selection.start); + let lastLine = this._model.getLineAtOffset(selection.end); + + // Uncomment a block of text. + let firstLineText = this._model.getLine(firstLine); + let lastLineText = this._model.getLine(lastLine); + let openIndex = firstLineText.indexOf(commentObject.blockStart); + let closeIndex = lastLineText.lastIndexOf(commentObject.blockEnd); + if (openIndex != -1 && closeIndex != -1) { + let firstLineStartOffset = this.getLineStart(firstLine); + let lastLineStartOffset = this.getLineStart(lastLine); + let openOffset = firstLineStartOffset + openIndex; + let closeOffset = lastLineStartOffset + closeIndex; + + this.startCompoundChange(); + this.setText("", closeOffset, closeOffset + commentObject.blockEnd.length); + this.setText("", openOffset, openOffset + commentObject.blockStart.length); + this.endCompoundChange(); + + return true; + } + + if (!commentObject.line) { + return true; + } + + // If the selected text is not a block of comment, then uncomment each line. + this.startCompoundChange(); + let lineCaret = firstLine; + while (lineCaret <= lastLine) { + let currentLine = this._model.getLine(lineCaret); + let lineStart = this.getLineStart(lineCaret); + let openIndex = currentLine.indexOf(commentObject.line); + let openOffset = lineStart + openIndex; + let textUntilComment = this.getText(lineStart, openOffset); + if (openIndex != -1 && + (!textUntilComment || /^\s+$/.test(textUntilComment))) { + this.setText("", openOffset, openOffset + commentObject.line.length); + } + lineCaret++; + } + this.endCompoundChange(); + + return true; + }, + /** * Add an event listener to the editor. You can use one of the known events. * @@ -1216,6 +1380,37 @@ SourceEditor.prototype = { return this._view.getText(aStart, aEnd); }, + /** + * Get the start character offset of the line with index aLineIndex. + * + * @param number aLineIndex + * Zero based index of the line. + * @return number + * Line start offset or -1 if out of range. + */ + getLineStart: function SE_getLineStart(aLineIndex) + { + return this._model.getLineStart(aLineIndex); + }, + + /** + * Get the end character offset of the line with index aLineIndex, + * excluding the end offset. When the line delimiter is present, + * the offset is the start offset of the next line or the char count. + * Otherwise, it is the offset of the line delimiter. + * + * @param number aLineIndex + * Zero based index of the line. + * @param boolean [aIncludeDelimiter = false] + * Optional, whether or not to include the line delimiter. + * @return number + * Line end offset or -1 if out of range. + */ + getLineEnd: function SE_getLineEnd(aLineIndex, aIncludeDelimiter) + { + return this._model.getLineEnd(aLineIndex, aIncludeDelimiter); + }, + /** * Get the number of characters in the editor content. * @@ -1323,7 +1518,7 @@ SourceEditor.prototype = { { let offset = this.getCaretOffset(); let line = this._model.getLineAtOffset(offset); - let lineStart = this._model.getLineStart(line); + let lineStart = this.getLineStart(line); let column = offset - lineStart; return {line: line, col: column}; }, @@ -1350,7 +1545,7 @@ SourceEditor.prototype = { let halfVisible = Math.round(linesVisible/2); let firstVisible = this.getTopIndex(); let lastVisible = this._view.getBottomIndex(); - let caretOffset = this._model.getLineStart(aLine) + (aColumn || 0); + let caretOffset = this.getLineStart(aLine) + (aColumn || 0); this._view.setSelection(caretOffset, caretOffset, false); @@ -1495,6 +1690,65 @@ SourceEditor.prototype = { return this._view.getOptions("readonly"); }, + /** + * Set the current debugger location at the given line index. This is useful in + * a debugger or in any other context where the user needs to track the + * current state, where a debugger-like environment is at. + * + * @param number aLineIndex + * Line index of the current debugger location, starting from 0. + * Use any negative number to clear the current location. + */ + setDebugLocation: function SE_setDebugLocation(aLineIndex) + { + let annotations = this._getAnnotationsByType("debugLocation", 0, + this.getCharCount()); + if (annotations.length > 0) { + annotations.forEach(this._annotationModel.removeAnnotation, + this._annotationModel); + } + if (aLineIndex < 0) { + return; + } + + let lineStart = this._model.getLineStart(aLineIndex); + let lineEnd = this._model.getLineEnd(aLineIndex); + let lineText = this._model.getLine(aLineIndex); + let title = SourceEditorUI.strings. + formatStringFromName("annotation.debugLocation.title", + [lineText], 1); + + let annotation = { + type: ORION_ANNOTATION_TYPES.debugLocation, + start: lineStart, + end: lineEnd, + title: title, + style: {styleClass: "annotation debugLocation"}, + html: "
", + overviewStyle: {styleClass: "annotationOverview debugLocation"}, + rangeStyle: {styleClass: "annotationRange debugLocation"}, + lineStyle: {styleClass: "annotationLine debugLocation"}, + }; + this._annotationModel.addAnnotation(annotation); + }, + + /** + * Retrieve the current debugger line index configured for this editor. + * + * @return number + * The line index starting from 0 where the current debugger is + * paused. If no debugger location has been set -1 is returned. + */ + getDebugLocation: function SE_getDebugLocation() + { + let annotations = this._getAnnotationsByType("debugLocation", 0, + this.getCharCount()); + if (annotations.length > 0) { + return this._model.getLineAtOffset(annotations[0].start); + } + return -1; + }, + /** * Add a breakpoint at the given line index. * @@ -1505,8 +1759,8 @@ SourceEditor.prototype = { */ addBreakpoint: function SE_addBreakpoint(aLineIndex, aCondition) { - let lineStart = this._model.getLineStart(aLineIndex); - let lineEnd = this._model.getLineEnd(aLineIndex); + let lineStart = this.getLineStart(aLineIndex); + let lineEnd = this.getLineEnd(aLineIndex); let annotations = this._getAnnotationsByType("breakpoint", lineStart, lineEnd); if (annotations.length > 0) { @@ -1550,8 +1804,8 @@ SourceEditor.prototype = { */ removeBreakpoint: function SE_removeBreakpoint(aLineIndex) { - let lineStart = this._model.getLineStart(aLineIndex); - let lineEnd = this._model.getLineEnd(aLineIndex); + let lineStart = this.getLineStart(aLineIndex); + let lineEnd = this.getLineEnd(aLineIndex); let event = { type: SourceEditor.EVENTS.BREAKPOINT_CHANGE, diff --git a/browser/devtools/sourceeditor/test/Makefile.in b/browser/devtools/sourceeditor/test/Makefile.in index b7c1c5d2309..5ff930550d8 100644 --- a/browser/devtools/sourceeditor/test/Makefile.in +++ b/browser/devtools/sourceeditor/test/Makefile.in @@ -61,6 +61,8 @@ _BROWSER_TEST_FILES = \ browser_bug725618_moveLines_shortcut.js \ browser_bug700893_dirty_state.js \ browser_bug729480_line_vertical_align.js \ + browser_bug725430_comment_uncomment.js \ + browser_bug731721_debugger_stepping.js \ head.js \ libs:: $(_BROWSER_TEST_FILES) diff --git a/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js b/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js new file mode 100644 index 00000000000..fca682a692b --- /dev/null +++ b/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js @@ -0,0 +1,151 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function test() { + + let temp = {}; + Cu.import("resource:///modules/source-editor.jsm", temp); + let SourceEditor = temp.SourceEditor; + + waitForExplicitFinish(); + + let editor; + + const windowUrl = "data:text/xml," + + ""; + const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no"; + + let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null); + testWin.addEventListener("load", function onWindowLoad() { + testWin.removeEventListener("load", onWindowLoad, false); + waitForFocus(initEditor, testWin); + }, false); + + function initEditor() + { + let hbox = testWin.document.querySelector("hbox"); + editor = new SourceEditor(); + editor.init(hbox, {showLineNumbers: true}, editorLoaded); + } + + function editorLoaded() + { + editor.focus(); + let text = "firstline\nsecondline\nthirdline\nfourthline"; + + editor.setMode(SourceEditor.MODES.JAVASCRIPT); + editor.setText(text) + + editor.setCaretPosition(0); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), "//" + text, "JS Single line Commenting Works"); + editor.undo(); + is(editor.getText(), text, "Undo Single Line Commenting action works"); + editor.redo(); + is(editor.getText(), "//" + text, "Redo works"); + editor.setCaretPosition(0); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), text, "JS Single Line Uncommenting works"); + + editor.setText(text); + + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), "/*" + text + "*/", "JS Block Commenting works"); + editor.undo(); + is(editor.getText(), text, "Undo Block Commenting action works"); + editor.redo(); + is(editor.getText(), "/*" + text + "*/", "Redo works"); + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), text, "JS Block Uncommenting works"); + editor.undo(); + is(editor.getText(), "/*" + text + "*/", "Undo Block Uncommenting works"); + editor.redo(); + is(editor.getText(), text, "Redo works"); + + let regText = "//firstline\n // secondline\nthird//line\nfourthline//"; + let expText = "firstline\n secondline\nthird//line\nfourthline//"; + editor.setText(regText); + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), expText, "JS Multiple Line Uncommenting works"); + editor.undo(); + is(editor.getText(), regText, "Undo Multiple Line Uncommenting works"); + editor.redo(); + is(editor.getText(), expText, "Redo works"); + + editor.setMode(SourceEditor.MODES.CSS); + editor.setText(text); + + expText = "/*firstline*/\nsecondline\nthirdline\nfourthline"; + editor.setCaretPosition(0); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), expText, "CSS Commenting without selection works"); + editor.setCaretPosition(0); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), text, "CSS Uncommenting without selection works"); + + editor.setText(text); + + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), "/*" + text + "*/", "CSS Multiple Line Commenting works"); + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), text, "CSS Multiple Line Uncommenting works"); + + editor.setMode(SourceEditor.MODES.HTML); + editor.setText(text); + + expText = "\nsecondline\nthirdline\nfourthline"; + editor.setCaretPosition(0); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), expText, "HTML Commenting without selection works"); + editor.setCaretPosition(0); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), text, "HTML Uncommenting without selection works"); + + editor.setText(text); + + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), "", "HTML Multiple Line Commenting works"); + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), text, "HTML Multiple Line Uncommenting works"); + + editor.setMode(SourceEditor.MODES.TEXT); + editor.setText(text); + + editor.setCaretPosition(0); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), text, "Commenting disabled in Text mode"); + editor.setText("//" + text); + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), "//" + text, "Uncommenting disabled in Text mode"); + + editor.setText(text); + editor.readOnly = true; + + editor.setCaretPosition(0); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), text, "Commenting disabled in ReadOnly mode"); + editor.setText("//" + text); + EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + is(editor.getText(), "//" + text, "Uncommenting disabled in ReadOnly mode"); + + editor.destroy(); + + testWin.close(); + testWin = editor = null; + + waitForFocus(finish, window); + } +} diff --git a/browser/devtools/sourceeditor/test/browser_bug731721_debugger_stepping.js b/browser/devtools/sourceeditor/test/browser_bug731721_debugger_stepping.js new file mode 100644 index 00000000000..3326f563e6d --- /dev/null +++ b/browser/devtools/sourceeditor/test/browser_bug731721_debugger_stepping.js @@ -0,0 +1,59 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function test() { + + let temp = {}; + Cu.import("resource:///modules/source-editor.jsm", temp); + let SourceEditor = temp.SourceEditor; + + waitForExplicitFinish(); + + let editor; + + const windowUrl = "data:text/xml," + + ""; + const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no"; + + let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null); + testWin.addEventListener("load", function onWindowLoad() { + testWin.removeEventListener("load", onWindowLoad, false); + waitForFocus(initEditor, testWin); + }, false); + + function initEditor() + { + let hbox = testWin.document.querySelector("hbox"); + editor = new SourceEditor(); + editor.init(hbox, {showAnnotationRuler: true}, editorLoaded); + } + + function editorLoaded() + { + editor.focus(); + + editor.setText("line1\nline2\nline3\nline4"); + + is(editor.getDebugLocation(), -1, "no debugger location"); + + editor.setDebugLocation(1); + is(editor.getDebugLocation(), 1, "set debugger location works"); + + editor.setDebugLocation(3); + is(editor.getDebugLocation(), 3, "change debugger location works"); + + editor.setDebugLocation(-1); + is(editor.getDebugLocation(), -1, "clear debugger location works"); + + editor.destroy(); + + testWin.close(); + testWin = editor = null; + + waitForFocus(finish, window); + } +} diff --git a/browser/devtools/styleinspector/CssRuleView.jsm b/browser/devtools/styleinspector/CssRuleView.jsm index 8454afa5249..179708e8703 100644 --- a/browser/devtools/styleinspector/CssRuleView.jsm +++ b/browser/devtools/styleinspector/CssRuleView.jsm @@ -1375,6 +1375,7 @@ InplaceEditor.prototype = { // up to the client), and b) without tweaking the style of the // original element, it might wrap differently or something. this._measurement = this.doc.createElementNS(HTML_NS, "span"); + this._measurement.className = "autosizer"; this.elt.parentNode.appendChild(this._measurement); let style = this._measurement.style; style.visibility = "hidden"; diff --git a/browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js b/browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js index 0c7b58b4fed..02606375e00 100644 --- a/browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js +++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js @@ -44,6 +44,7 @@ function whenHighlighting() { "Highlighting a node that's not already visible should trigger a reset!"); executeSoon(function() { + Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING); Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false); presenter.highlightNode(null); }); @@ -56,14 +57,13 @@ function whenUnhighlighting() { "After unhighlighting a node, it shouldn't be highlighted anymore. D'oh."); executeSoon(function() { + Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING); Services.obs.addObserver(cleanup, DESTROYED, false); InspectorUI.closeInspectorUI(); }); } function cleanup() { - Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING); - Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING); Services.obs.removeObserver(cleanup, DESTROYED); gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js b/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js index 95910e86bca..cf021dcc00a 100644 --- a/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js +++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js @@ -43,6 +43,7 @@ function whenHighlighting() { "Highlighting a node that's already visible shouldn't trigger a reset."); executeSoon(function() { + Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING); Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false); presenter.highlightNode(null); }); @@ -55,14 +56,13 @@ function whenUnhighlighting() { "After unhighlighting a node, it shouldn't be highlighted anymore. D'oh."); executeSoon(function() { + Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING); Services.obs.addObserver(cleanup, DESTROYED, false); InspectorUI.closeInspectorUI(); }); } function cleanup() { - Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING); - Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING); Services.obs.removeObserver(cleanup, DESTROYED); gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js b/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js index 85656bb192c..f824b27312d 100644 --- a/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js +++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js @@ -38,6 +38,7 @@ function whenHighlighting() { "After highlighting a node, it should be highlighted. D'oh."); executeSoon(function() { + Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING); Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false); presenter.highlightNodeAt(-1, -1); }); @@ -50,14 +51,13 @@ function whenUnhighlighting() { "After unhighlighting a node, it shouldn't be highlighted anymore. D'oh."); executeSoon(function() { + Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING); Services.obs.addObserver(cleanup, DESTROYED, false); InspectorUI.closeInspectorUI(); }); } function cleanup() { - Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING); - Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING); Services.obs.removeObserver(cleanup, DESTROYED); gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/tilt/test/browser_tilt_picking_highlight03.js b/browser/devtools/tilt/test/browser_tilt_picking_highlight03.js index 66756081159..5e7cb9af5dc 100644 --- a/browser/devtools/tilt/test/browser_tilt_picking_highlight03.js +++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight03.js @@ -38,6 +38,7 @@ function whenHighlighting() { "After highlighting a node, it should be highlighted. D'oh."); executeSoon(function() { + Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING); Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false); presenter.highlightNodeFor(-1); }); @@ -50,14 +51,13 @@ function whenUnhighlighting() { "After unhighlighting a node, it shouldn't be highlighted anymore. D'oh."); executeSoon(function() { + Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING); Services.obs.addObserver(cleanup, DESTROYED, false); InspectorUI.closeInspectorUI(); }); } function cleanup() { - Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING); - Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING); Services.obs.removeObserver(cleanup, DESTROYED); gBrowser.removeCurrentTab(); finish(); diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in index f16a2084273..126ca9893ab 100644 --- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -91,6 +91,10 @@ DEFINES += -DMOZ_D3DX9_DLL=$(MOZ_D3DX9_DLL) DEFINES += -DMOZ_D3DCOMPILER_DLL=$(MOZ_D3DCOMPILER_DLL) endif +ifdef WIN32_REDIST_DIR +DEFINES += -DMOZ_WIN32_REDIST=1 +endif + include $(topsrcdir)/ipc/app/defs.mk DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index bec89bc47a0..542dbd36a8d 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -69,6 +69,7 @@ @BINPATH@/@MOZ_CHILD_PROCESS_NAME@ #endif #ifdef XP_WIN32 +#ifdef MOZ_WIN32_REDIST #ifndef MOZ_DEBUG #if _MSC_VER == 1400 @BINPATH@/Microsoft.VC80.CRT.manifest @@ -89,6 +90,7 @@ #endif #endif #endif +#endif [browser] ; [Base Browser Files] diff --git a/browser/installer/removed-files.in b/browser/installer/removed-files.in index 9b81d1ffc37..defc14353ab 100644 --- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -1352,29 +1352,35 @@ xpicleanup@BIN_SUFFIX@ components/jsd3250.dll components/nsPostUpdateWin.js js3250.dll - plugins/npnul32.dll - #if _MSC_VER != 1400 + mozcpp19.dll + mozcrt19.dll + #ifndef MOZ_WIN32_REDIST + #define _MSC_VER_REDIST 0000 + #else + #define _MSC_VER_REDIST _MSC_VER + #endif + #if _MSC_VER_REDIST != 1400 Microsoft.VC80.CRT.manifest msvcm80.dll msvcp80.dll msvcr80.dll #endif - #if _MSC_VER != 1500 + #if _MSC_VER_REDIST != 1500 Microsoft.VC90.CRT.manifest msvcm90.dll msvcp90.dll msvcr90.dll #endif - #if _MSC_VER != 1600 + #if _MSC_VER_REDIST != 1600 msvcp100.dll msvcr100.dll #endif - #if _MSC_VER != 1700 + #if _MSC_VER_REDIST != 1700 msvcp110.dll msvcr110.dll #endif - mozcrt19.dll - mozcpp19.dll + #undef _MSC_VER_REDIST + plugins/npnul32.dll #endif @DLL_PREFIX@xpcom_core@DLL_SUFFIX@ components/@DLL_PREFIX@jar50@DLL_SUFFIX@ diff --git a/browser/locales/en-US/chrome/browser/devtools/debugger.dtd b/browser/locales/en-US/chrome/browser/devtools/debugger.dtd index 1419c70cd6a..5c4649e18e2 100644 --- a/browser/locales/en-US/chrome/browser/devtools/debugger.dtd +++ b/browser/locales/en-US/chrome/browser/devtools/debugger.dtd @@ -19,6 +19,18 @@ - button that closes the debugger UI. --> + + + + + + + + + diff --git a/browser/locales/en-US/chrome/browser/devtools/sourceeditor.properties b/browser/locales/en-US/chrome/browser/devtools/sourceeditor.properties index f58544bb8c1..35746064a89 100644 --- a/browser/locales/en-US/chrome/browser/devtools/sourceeditor.properties +++ b/browser/locales/en-US/chrome/browser/devtools/sourceeditor.properties @@ -38,3 +38,9 @@ annotation.breakpoint.title=Breakpoint: %S # a tooltip displayed in any of the editor gutters when the user hovers the # current line. annotation.currentLine=Current line + +# LOCALIZATION NOTE (annotation.debugLocation.title): This is the text shown in +# a tooltip displayed in any of the editor gutters when the user hovers the +# current debugger location. The debugger can pause the JavaScript execution at +# user-defined lines. +annotation.debugLocation.title=Current step: %S diff --git a/browser/themes/gnomestripe/devtools/orion-debug-location.png b/browser/themes/gnomestripe/devtools/orion-debug-location.png new file mode 100644 index 00000000000..ebb8d8d86d8 Binary files /dev/null and b/browser/themes/gnomestripe/devtools/orion-debug-location.png differ diff --git a/browser/themes/gnomestripe/devtools/orion.css b/browser/themes/gnomestripe/devtools/orion.css index 0820c6cf221..ae504a566a2 100644 --- a/browser/themes/gnomestripe/devtools/orion.css +++ b/browser/themes/gnomestripe/devtools/orion.css @@ -61,6 +61,9 @@ .annotationHTML.breakpoint { background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png"); } +.annotationHTML.debugLocation { + background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"); +} /* Styles for the overview ruler */ .annotationOverview { @@ -77,6 +80,10 @@ background-color: lightblue; border: 1px solid blue; } +.annotationOverview.debugLocation { + background-color: white; + border: 1px solid green; +} .annotationOverview.currentBracket { background-color: lightgray; border: 1px solid red; diff --git a/browser/themes/gnomestripe/jar.mn b/browser/themes/gnomestripe/jar.mn index 49a91812feb..556cd6d09cc 100644 --- a/browser/themes/gnomestripe/jar.mn +++ b/browser/themes/gnomestripe/jar.mn @@ -98,6 +98,7 @@ browser.jar: skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css) skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png) skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png) + skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png) skin/classic/browser/devtools/breadcrumbs/ltr-end-pressed.png (devtools/breadcrumbs/ltr-end-pressed.png) skin/classic/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png (devtools/breadcrumbs/ltr-end-selected-pressed.png) skin/classic/browser/devtools/breadcrumbs/ltr-end-selected.png (devtools/breadcrumbs/ltr-end-selected.png) diff --git a/browser/themes/pinstripe/devtools/orion-debug-location.png b/browser/themes/pinstripe/devtools/orion-debug-location.png new file mode 100644 index 00000000000..ebb8d8d86d8 Binary files /dev/null and b/browser/themes/pinstripe/devtools/orion-debug-location.png differ diff --git a/browser/themes/pinstripe/devtools/orion.css b/browser/themes/pinstripe/devtools/orion.css index 0820c6cf221..ae504a566a2 100644 --- a/browser/themes/pinstripe/devtools/orion.css +++ b/browser/themes/pinstripe/devtools/orion.css @@ -61,6 +61,9 @@ .annotationHTML.breakpoint { background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png"); } +.annotationHTML.debugLocation { + background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"); +} /* Styles for the overview ruler */ .annotationOverview { @@ -77,6 +80,10 @@ background-color: lightblue; border: 1px solid blue; } +.annotationOverview.debugLocation { + background-color: white; + border: 1px solid green; +} .annotationOverview.currentBracket { background-color: lightgray; border: 1px solid red; diff --git a/browser/themes/pinstripe/jar.mn b/browser/themes/pinstripe/jar.mn index fcae4458152..a93e17eb1d7 100644 --- a/browser/themes/pinstripe/jar.mn +++ b/browser/themes/pinstripe/jar.mn @@ -134,6 +134,7 @@ browser.jar: skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css) skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png) skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png) + skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png) skin/classic/browser/devtools/toolbarbutton-close.png (devtools/toolbarbutton-close.png) * skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css) skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css) diff --git a/browser/themes/winstripe/devtools/orion-debug-location.png b/browser/themes/winstripe/devtools/orion-debug-location.png new file mode 100644 index 00000000000..ebb8d8d86d8 Binary files /dev/null and b/browser/themes/winstripe/devtools/orion-debug-location.png differ diff --git a/browser/themes/winstripe/devtools/orion.css b/browser/themes/winstripe/devtools/orion.css index 0820c6cf221..ae504a566a2 100644 --- a/browser/themes/winstripe/devtools/orion.css +++ b/browser/themes/winstripe/devtools/orion.css @@ -61,6 +61,9 @@ .annotationHTML.breakpoint { background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png"); } +.annotationHTML.debugLocation { + background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"); +} /* Styles for the overview ruler */ .annotationOverview { @@ -77,6 +80,10 @@ background-color: lightblue; border: 1px solid blue; } +.annotationOverview.debugLocation { + background-color: white; + border: 1px solid green; +} .annotationOverview.currentBracket { background-color: lightgray; border: 1px solid red; diff --git a/browser/themes/winstripe/jar.mn b/browser/themes/winstripe/jar.mn index 7a5d07ea592..5be185c937c 100644 --- a/browser/themes/winstripe/jar.mn +++ b/browser/themes/winstripe/jar.mn @@ -121,6 +121,7 @@ browser.jar: skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css) skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png) skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png) + skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png) skin/classic/browser/devtools/toolbarbutton-close.png (devtools/toolbarbutton-close.png) skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css) skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css) @@ -293,6 +294,7 @@ browser.jar: skin/classic/aero/browser/devtools/orion-container.css (devtools/orion-container.css) skin/classic/aero/browser/devtools/orion-task.png (devtools/orion-task.png) skin/classic/aero/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png) + skin/classic/aero/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png) skin/classic/aero/browser/devtools/toolbarbutton-close.png (devtools/toolbarbutton-close.png) skin/classic/aero/browser/devtools/webconsole.css (devtools/webconsole.css) skin/classic/aero/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css) diff --git a/layout/base/tests/test_bug518777.html b/layout/base/tests/test_bug518777.html index f60b34fd89d..f16ab30cfa4 100644 --- a/layout/base/tests/test_bug518777.html +++ b/layout/base/tests/test_bug518777.html @@ -24,6 +24,8 @@ iframe source is /** Test for Bug 518777 **/ +SimpleTest.waitForExplicitFinish(); + function dotest() { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); @@ -44,6 +46,8 @@ function dotest() { } } ok(!failed, "all pixels blue"); + + SimpleTest.finish(); } diff --git a/toolkit/devtools/debugger/dbg-client.jsm b/toolkit/devtools/debugger/dbg-client.jsm index 0b5d6e00972..36156207e2d 100644 --- a/toolkit/devtools/debugger/dbg-client.jsm +++ b/toolkit/devtools/debugger/dbg-client.jsm @@ -541,12 +541,16 @@ ThreadClient.prototype = { }, /** - * Resume a paused thread. + * Resume a paused thread. If the optional aLimit parameter is present, then + * the thread will also pause when that limit is reached. * * @param function aOnResponse * Called with the response packet. + * @param [optional] object aLimit + * An object with a type property set to the appropriate limit (next, + * step, or finish) per the remote debugging protocol specification. */ - resume: function TC_resume(aOnResponse) { + resume: function TC_resume(aOnResponse, aLimit) { this._assertPaused("resume"); // Put the client in a tentative "resuming" state so we can prevent @@ -554,7 +558,8 @@ ThreadClient.prototype = { this._state = "resuming"; let self = this; - let packet = { to: this._actor, type: DebugProtocolTypes.resume }; + let packet = { to: this._actor, type: DebugProtocolTypes.resume, + resumeLimit: aLimit }; this._client.request(packet, function(aResponse) { if (aResponse.error) { // There was an error resuming, back to paused state. @@ -566,6 +571,36 @@ ThreadClient.prototype = { }); }, + /** + * Step over a function call. + * + * @param function aOnResponse + * Called with the response packet. + */ + stepOver: function TC_stepOver(aOnResponse) { + this.resume(aOnResponse, { type: "next" }); + }, + + /** + * Step into a function call. + * + * @param function aOnResponse + * Called with the response packet. + */ + stepIn: function TC_stepIn(aOnResponse) { + this.resume(aOnResponse, { type: "step" }); + }, + + /** + * Step out of a function call. + * + * @param function aOnResponse + * Called with the response packet. + */ + stepOut: function TC_stepOut(aOnResponse) { + this.resume(aOnResponse, { type: "finish" }); + }, + /** * Interrupt a running thread. * diff --git a/toolkit/devtools/debugger/server/dbg-script-actors.js b/toolkit/devtools/debugger/server/dbg-script-actors.js index 67bec248907..f4ecb28a8e2 100644 --- a/toolkit/devtools/debugger/server/dbg-script-actors.js +++ b/toolkit/devtools/debugger/server/dbg-script-actors.js @@ -197,12 +197,130 @@ ThreadActor.prototype = { return { type: "detached" }; }, + /** + * Pause the debuggee, by entering a nested event loop, and return a 'paused' + * packet to the client. + * + * @param Debugger.Frame aFrame + * The newest debuggee frame in the stack. + * @param object aReason + * An object with a 'type' property containing the reason for the pause. + */ + _pauseAndRespond: function TA__pauseAndRespond(aFrame, aReason) { + try { + let packet = this._paused(aFrame); + if (!packet) { + return undefined; + } + packet.why = aReason; + this.conn.send(packet); + return this._nest(); + } catch(e) { + Cu.reportError("Got an exception during TA__pauseAndRespond: " + e + + ": " + e.stack); + return undefined; + } + }, + + /** + * Handle a protocol request to resume execution of the debuggee. + */ onResume: function TA_onResume(aRequest) { + if (aRequest && aRequest.forceCompletion) { + // TODO: remove this when Debugger.Frame.prototype.pop is implemented in + // bug 736733. + if (typeof this.frame.pop != "function") { + return { error: "notImplemented", + message: "forced completion is not yet implemented." }; + } + + this.dbg.getNewestFrame().pop(aRequest.completionValue); + let packet = this._resumed(); + DebuggerServer.xpcInspector.exitNestedEventLoop(); + return { type: "resumeLimit", frameFinished: aRequest.forceCompletion }; + } + + if (aRequest && aRequest.resumeLimit) { + // Bind these methods because some of the hooks are called with 'this' + // set to the current frame. + let pauseAndRespond = this._pauseAndRespond.bind(this); + let createValueGrip = this.createValueGrip.bind(this); + + let startFrame = this._youngestFrame; + let startLine; + if (this._youngestFrame.script) { + let offset = this._youngestFrame.offset; + startLine = this._youngestFrame.script.getOffsetLine(offset); + } + + // Define the JS hook functions for stepping. + + let onEnterFrame = function TA_onEnterFrame(aFrame) { + return pauseAndRespond(aFrame, { type: "resumeLimit" }); + }; + + let onPop = function TA_onPop(aCompletion) { + // onPop is called with 'this' set to the current frame. + + // Note that we're popping this frame; we need to watch for + // subsequent step events on its caller. + this.reportedPop = true; + + return pauseAndRespond(this, { type: "resumeLimit" }); + } + + let onStep = function TA_onStep() { + // onStep is called with 'this' set to the current frame. + + // If we've changed frame or line, then report that. + if (this !== startFrame || + (this.script && + this.script.getOffsetLine(this.offset) != startLine)) { + return pauseAndRespond(this, { type: "resumeLimit" }); + } + + // Otherwise, let execution continue. + return undefined; + } + + switch (aRequest.resumeLimit.type) { + case "step": + this.dbg.onEnterFrame = onEnterFrame; + // Fall through. + case "next": + let stepFrame = this._getNextStepFrame(startFrame); + if (stepFrame) { + stepFrame.onStep = onStep; + stepFrame.onPop = onPop; + } + break; + case "finish": + stepFrame = this._getNextStepFrame(startFrame); + if (stepFrame) { + stepFrame.onPop = onPop; + } + break; + default: + return { error: "badParameterType", + message: "Unknown resumeLimit type" }; + } + } let packet = this._resumed(); DebuggerServer.xpcInspector.exitNestedEventLoop(); return packet; }, + /** + * Helper method that returns the next frame when stepping. + */ + _getNextStepFrame: function TA__getNextStepFrame(aFrame) { + let stepFrame = aFrame.reportedPop ? aFrame.older : aFrame; + if (!stepFrame || !stepFrame.script) { + stepFrame = null; + } + return stepFrame; + }, + onClientEvaluate: function TA_onClientEvaluate(aRequest) { if (this.state !== "paused") { return { error: "wrongState", @@ -472,6 +590,13 @@ ThreadActor.prototype = { return undefined; } + // Clear stepping hooks. + this.dbg.onEnterFrame = undefined; + if (aFrame) { + aFrame.onStep = undefined; + aFrame.onPop = undefined; + } + this._state = "paused"; // Save the pause frame (if any) as the youngest frame for @@ -730,19 +855,7 @@ ThreadActor.prototype = { * The stack frame that contained the debugger statement. */ onDebuggerStatement: function TA_onDebuggerStatement(aFrame) { - try { - let packet = this._paused(aFrame); - if (!packet) { - return undefined; - } - packet.why = { type: "debuggerStatement" }; - this.conn.send(packet); - return this._nest(); - } catch(e) { - Cu.reportError("Got an exception during onDebuggerStatement: " + e + - ": " + e.stack); - return undefined; - } + return this._pauseAndRespond(aFrame, { type: "debuggerStatement" }); }, /** @@ -1153,8 +1266,20 @@ FrameActor.prototype = { * The protocol request object. */ onPop: function FA_onPop(aRequest) { - return { error: "notImplemented", - message: "Popping frames is not yet implemented." }; + // TODO: remove this when Debugger.Frame.prototype.pop is implemented + if (typeof this.frame.pop != "function") { + return { error: "notImplemented", + message: "Popping frames is not yet implemented." }; + } + + while (this.frame != this.threadActor.dbg.getNewestFrame()) { + this.threadActor.dbg.getNewestFrame().pop(); + } + this.frame.pop(aRequest.completionValue); + + // TODO: return the watches property when frame pop watch actors are + // implemented. + return { from: this.actorID }; } }; @@ -1189,19 +1314,9 @@ BreakpointActor.prototype = { * The stack frame that contained the breakpoint. */ hit: function BA_hit(aFrame) { - try { - let packet = this.threadActor._paused(aFrame); - if (!packet) { - return undefined; - } - // TODO: add the rest of the breakpoints on that line. - packet.why = { type: "breakpoint", actors: [ this.actorID ] }; - this.conn.send(packet); - return this.threadActor._nest(); - } catch(e) { - Cu.reportError("Got an exception during hit: " + e + ': ' + e.stack); - return undefined; - } + // TODO: add the rest of the breakpoints on that line (bug 676602). + let reason = { type: "breakpoint", actors: [ this.actorID ] }; + return this.threadActor._pauseAndRespond(aFrame, reason); }, /** diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-01.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-01.js index f460cb6401f..6cc6feb77dd 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-01.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-01.js @@ -15,7 +15,9 @@ function run_test() gDebuggee = addTestGlobal("test-stack"); gClient = new DebuggerClient(DebuggerServer.connectPipe()); gClient.connect(function () { - attachTestGlobalClientAndResume(gClient, "test-stack", function (aResponse, aThreadClient) { + attachTestGlobalClientAndResume(gClient, + "test-stack", + function (aResponse, aThreadClient) { gThreadClient = aThreadClient; test_simple_breakpoint(); }); @@ -27,10 +29,13 @@ function test_simple_breakpoint() { gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { let path = getFilePath('test_breakpoint-01.js'); - gThreadClient.setBreakpoint({ url: path, line: gDebuggee.line0 + 3}, function (aResponse, bpClient) { + let location = { url: path, line: gDebuggee.line0 + 3}; + gThreadClient.setBreakpoint(location, function (aResponse, bpClient) { gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location.line); do_check_eq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.actors[0], bpClient.actor); // Check that the breakpoint worked. diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-02.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-02.js index bcbe8591175..ab5df24536b 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-02.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-02.js @@ -15,7 +15,9 @@ function run_test() gDebuggee = addTestGlobal("test-stack"); gClient = new DebuggerClient(DebuggerServer.connectPipe()); gClient.connect(function () { - attachTestGlobalClientAndResume(gClient, "test-stack", function (aResponse, aThreadClient) { + attachTestGlobalClientAndResume(gClient, + "test-stack", + function (aResponse, aThreadClient) { gThreadClient = aThreadClient; test_breakpoint_running(); }); @@ -26,6 +28,7 @@ function run_test() function test_breakpoint_running() { let path = getFilePath('test_breakpoint-01.js'); + let location = { url: path, line: gDebuggee.line0 + 3}; gDebuggee.eval("var line0 = Error().lineNumber;\n" + "var a = 1;\n" + // line0 + 1 @@ -34,10 +37,12 @@ function test_breakpoint_running() // Setting the breakpoint later should interrupt the debuggee. gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location); do_check_eq(aPacket.why.type, "interrupted"); }); - gThreadClient.setBreakpoint({ url: path, line: gDebuggee.line0 + 3}, function(aResponse) { + gThreadClient.setBreakpoint(location, function(aResponse) { // Eval scripts don't stick around long enough for the breakpoint to be set, // so just make sure we got the expected response from the actor. do_check_eq(aResponse.error, "noScript"); diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-03.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-03.js index 5f5dac6f5b7..8f30827dd70 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-03.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-03.js @@ -37,6 +37,8 @@ function test_skip_breakpoint() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location.line + 1); do_check_eq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.actors[0], bpClient.actor); // Check that the breakpoint worked. diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-04.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-04.js index b0b6fc120d0..6c91aa956ef 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-04.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-04.js @@ -36,6 +36,8 @@ function test_child_breakpoint() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location.line); do_check_eq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.actors[0], bpClient.actor); // Check that the breakpoint worked. diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-05.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-05.js index 452703641db..6a55bc4c361 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-05.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-05.js @@ -38,6 +38,8 @@ function test_child_skip_breakpoint() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location.line + 1); do_check_eq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.actors[0], bpClient.actor); // Check that the breakpoint worked. diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-06.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-06.js index 70f1915c80a..60271c3904e 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-06.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-06.js @@ -38,6 +38,8 @@ function test_nested_breakpoint() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location.line + 1); do_check_eq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.actors[0], bpClient.actor); // Check that the breakpoint worked. diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-07.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-07.js index e3b8de41cf8..249dc0e4a15 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-07.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-07.js @@ -38,6 +38,8 @@ function test_second_child_skip_breakpoint() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location.line + 1); do_check_eq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.actors[0], bpClient.actor); // Check that the breakpoint worked. diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-08.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-08.js index e4d77dceac2..979c68a7366 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-08.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-08.js @@ -38,6 +38,8 @@ function test_child_skip_breakpoint() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location.line + 1); do_check_eq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.actors[0], bpClient.actor); // Check that the breakpoint worked. diff --git a/toolkit/devtools/debugger/tests/unit/test_breakpoint-09.js b/toolkit/devtools/debugger/tests/unit/test_breakpoint-09.js index 0bbf0b699b2..ca1686a87a8 100644 --- a/toolkit/devtools/debugger/tests/unit/test_breakpoint-09.js +++ b/toolkit/devtools/debugger/tests/unit/test_breakpoint-09.js @@ -32,9 +32,14 @@ function test_remove_breakpoint() let path = getFilePath('test_breakpoint-09.js'); let location = { url: path, line: gDebuggee.line0 + 1}; gThreadClient.setBreakpoint(location, function (aResponse, bpClient) { + // Check that the breakpoint has properly skipped forward one line. + do_check_eq(aResponse.actualLocation.url, location.url); + do_check_eq(aResponse.actualLocation.line, location.line + 1); gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.url, path); + do_check_eq(aPacket.frame.where.line, location.line + 1); do_check_eq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.actors[0], bpClient.actor); // Check that the breakpoint worked. @@ -43,7 +48,8 @@ function test_remove_breakpoint() // Remove the breakpoint. bpClient.remove(function (aResponse) { done = true; - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + gThreadClient.addOneTimeListener("paused", + function (aEvent, aPacket) { // The breakpoint should not be hit again. gThreadClient.resume(function () { do_check_true(false); diff --git a/toolkit/devtools/debugger/tests/unit/test_stepping-01.js b/toolkit/devtools/debugger/tests/unit/test_stepping-01.js new file mode 100644 index 00000000000..d6d1e9f6e6c --- /dev/null +++ b/toolkit/devtools/debugger/tests/unit/test_stepping-01.js @@ -0,0 +1,76 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Check basic step-over functionality. + */ + +var gDebuggee; +var gClient; +var gThreadClient; + +function run_test() +{ + initTestDebuggerServer(); + gDebuggee = addTestGlobal("test-stack"); + gClient = new DebuggerClient(DebuggerServer.connectPipe()); + gClient.connect(function () { + attachTestGlobalClientAndResume(gClient, + "test-stack", + function (aResponse, aThreadClient) { + gThreadClient = aThreadClient; + test_simple_stepping(); + }); + }); + do_test_pending(); +} + +function test_simple_stepping() +{ + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 2); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, undefined); + do_check_eq(gDebuggee.b, undefined); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, undefined); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + // When leaving a stack frame the line number doesn't change. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, 2); + + gThreadClient.resume(function () { + finishClient(gClient); + }); + }); + gThreadClient.stepOver(); + }); + gThreadClient.stepOver(); + + }); + gThreadClient.stepOver(); + + }); + + gDebuggee.eval("var line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "var a = 1;\n" + // line0 + 2 + "var b = 2;\n"); // line0 + 3 +} diff --git a/toolkit/devtools/debugger/tests/unit/test_stepping-02.js b/toolkit/devtools/debugger/tests/unit/test_stepping-02.js new file mode 100644 index 00000000000..6865ff25fc3 --- /dev/null +++ b/toolkit/devtools/debugger/tests/unit/test_stepping-02.js @@ -0,0 +1,76 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Check basic step-in functionality. + */ + +var gDebuggee; +var gClient; +var gThreadClient; + +function run_test() +{ + initTestDebuggerServer(); + gDebuggee = addTestGlobal("test-stack"); + gClient = new DebuggerClient(DebuggerServer.connectPipe()); + gClient.connect(function () { + attachTestGlobalClientAndResume(gClient, + "test-stack", + function (aResponse, aThreadClient) { + gThreadClient = aThreadClient; + test_simple_stepping(); + }); + }); + do_test_pending(); +} + +function test_simple_stepping() +{ + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 2); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, undefined); + do_check_eq(gDebuggee.b, undefined); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, undefined); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + // When leaving a stack frame the line number doesn't change. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, 2); + + gThreadClient.resume(function () { + finishClient(gClient); + }); + }); + gThreadClient.stepIn(); + }); + gThreadClient.stepIn(); + + }); + gThreadClient.stepIn(); + + }); + + gDebuggee.eval("var line0 = Error().lineNumber;\n" + + "debugger;\n" + // line0 + 1 + "var a = 1;\n" + // line0 + 2 + "var b = 2;\n"); // line0 + 3 +} diff --git a/toolkit/devtools/debugger/tests/unit/test_stepping-03.js b/toolkit/devtools/debugger/tests/unit/test_stepping-03.js new file mode 100644 index 00000000000..e0a6a0ca365 --- /dev/null +++ b/toolkit/devtools/debugger/tests/unit/test_stepping-03.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Check basic step-out functionality. + */ + +var gDebuggee; +var gClient; +var gThreadClient; + +function run_test() +{ + initTestDebuggerServer(); + gDebuggee = addTestGlobal("test-stack"); + gClient = new DebuggerClient(DebuggerServer.connectPipe()); + gClient.connect(function () { + attachTestGlobalClientAndResume(gClient, + "test-stack", + function (aResponse, aThreadClient) { + gThreadClient = aThreadClient; + test_simple_stepping(); + }); + }); + do_test_pending(); +} + +function test_simple_stepping() +{ + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 4); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, 2); + + gThreadClient.resume(function () { + finishClient(gClient); + }); + }); + gThreadClient.stepOut(); + + }); + + gDebuggee.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " debugger;\n" + // line0 + 2 + " this.a = 1;\n" + // line0 + 3 + " this.b = 2;\n" + // line0 + 4 + "}\n" + // line0 + 5 + "f();\n"); // line0 + 6 +} diff --git a/toolkit/devtools/debugger/tests/unit/test_stepping-04.js b/toolkit/devtools/debugger/tests/unit/test_stepping-04.js new file mode 100644 index 00000000000..58c7a260877 --- /dev/null +++ b/toolkit/devtools/debugger/tests/unit/test_stepping-04.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Check that stepping over a function call does not pause inside the function. + */ + +var gDebuggee; +var gClient; +var gThreadClient; + +function run_test() +{ + initTestDebuggerServer(); + gDebuggee = addTestGlobal("test-stack"); + gClient = new DebuggerClient(DebuggerServer.connectPipe()); + gClient.connect(function () { + attachTestGlobalClientAndResume(gClient, + "test-stack", + function (aResponse, aThreadClient) { + gThreadClient = aThreadClient; + test_simple_stepping(); + }); + }); + do_test_pending(); +} + +function test_simple_stepping() +{ + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, undefined); + do_check_eq(gDebuggee.b, undefined); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check the return value. + do_check_eq(aPacket.type, "paused"); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); + do_check_eq(aPacket.why.type, "resumeLimit"); + // Check that stepping worked. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, undefined); + + gThreadClient.resume(function () { + finishClient(gClient); + }); + }); + gThreadClient.stepOver(); + + }); + gThreadClient.stepOver(); + + }); + + gDebuggee.eval("var line0 = Error().lineNumber;\n" + + "function f() {\n" + // line0 + 1 + " this.a = 1;\n" + // line0 + 2 + "}\n" + // line0 + 3 + "debugger;\n" + // line0 + 4 + "f();\n" + // line0 + 5 + "let b = 2;\n"); // line0 + 6 +} diff --git a/toolkit/devtools/debugger/tests/unit/xpcshell.ini b/toolkit/devtools/debugger/tests/unit/xpcshell.ini index f590d3ead3f..43f6ce50b29 100644 --- a/toolkit/devtools/debugger/tests/unit/xpcshell.ini +++ b/toolkit/devtools/debugger/tests/unit/xpcshell.ini @@ -49,3 +49,7 @@ tail = [test_objectgrips-03.js] [test_objectgrips-04.js] [test_interrupt.js] +[test_stepping-01.js] +[test_stepping-02.js] +[test_stepping-03.js] +[test_stepping-04.js]