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]