summaryrefslogtreecommitdiffstats
path: root/webkit
diff options
context:
space:
mode:
authordglazkov@google.com <dglazkov@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-10 19:11:09 +0000
committerdglazkov@google.com <dglazkov@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-10 19:11:09 +0000
commit15afbb51fd02caf9baebd5e406ec88bf59d8f835 (patch)
treeb0e5ce4fb8c334e6e52e94f5d3afcabb45097b8f /webkit
parent2a65160320c17d0c0f370d71fac51d6250b5b1fa (diff)
downloadchromium_src-15afbb51fd02caf9baebd5e406ec88bf59d8f835.zip
chromium_src-15afbb51fd02caf9baebd5e406ec88bf59d8f835.tar.gz
chromium_src-15afbb51fd02caf9baebd5e406ec88bf59d8f835.tar.bz2
Move forked, DevTools-specific JS files out of third_party/WebKit
R=darin BUG=3320 Review URL: http://codereview.chromium.org/44001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@11358 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r--webkit/build/port/port.vcproj13
-rw-r--r--webkit/inspector/DebuggerConsole.js173
-rw-r--r--webkit/inspector/DebuggerIPC.js109
-rw-r--r--webkit/inspector/DebuggerPanel.js201
-rw-r--r--webkit/inspector/DebuggerShell.js1458
-rw-r--r--webkit/inspector/README11
-rw-r--r--webkit/inspector/debugger.css35
-rw-r--r--webkit/inspector/debugger.html31
8 files changed, 2024 insertions, 7 deletions
diff --git a/webkit/build/port/port.vcproj b/webkit/build/port/port.vcproj
index 996d47c..a5e1dcf 100644
--- a/webkit/build/port/port.vcproj
+++ b/webkit/build/port/port.vcproj
@@ -110,28 +110,27 @@
>
</File>
<File
- RelativePath="..\..\..\third_party\WebKit\WebCore\inspector\front-end\debugger.css"
+ RelativePath="..\..\inspector\debugger.css"
>
</File>
<File
- RelativePath="..\..\..\third_party\WebKit\WebCore\inspector\front-end\debugger.html"
+ RelativePath="..\..\inspector\debugger.html"
>
</File>
<File
- RelativePath="..\..\..\third_party\WebKit\WebCore\inspector\front-end\DebuggerConsole.js"
+ RelativePath="..\..\inspector\DebuggerConsole.js"
>
</File>
<File
- RelativePath="..\..\..\third_party\WebKit\WebCore\inspector\front-end\DebuggerIPC.js"
+ RelativePath="..\..\inspector\DebuggerIPC.js"
>
</File>
<File
- RelativePath="..\..\..\third_party\WebKit\WebCore\inspector\front-end\DebuggerPanel.js"
- DeploymentContent="true"
+ RelativePath="..\..\inspector\DebuggerPanel.js"
>
</File>
<File
- RelativePath="..\..\..\third_party\WebKit\WebCore\inspector\front-end\DebuggerShell.js"
+ RelativePath="..\..\inspector\DebuggerShell.js"
>
</File>
<File
diff --git a/webkit/inspector/DebuggerConsole.js b/webkit/inspector/DebuggerConsole.js
new file mode 100644
index 0000000..0ba3b28
--- /dev/null
+++ b/webkit/inspector/DebuggerConsole.js
@@ -0,0 +1,173 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Helper functions and objects for the JS debugger UI.
+ * @see debugger.html
+ */
+
+/**
+ * Document load listener.
+ */
+function onLoad() {
+ var debuggerConsole = new DebuggerConsole();
+ DebuggerIPC.init(debuggerConsole);
+ DebugShell.initDebugShell(debuggerConsole);
+ debuggerConsole.focusOnCommandLine();
+}
+
+/**
+ * @constructor
+ */
+function DebuggerConsole()
+{
+ this._output = document.getElementById("output");
+
+ var input = document.getElementById("command-line-text");
+ var self = this;
+ input.addEventListener(
+ 'keydown',
+ function(e) {
+ return self._onInputKeyDown(e);
+ },
+ true);
+ input.addEventListener(
+ 'keypress',
+ function(e) {
+ return self._onInputKeyPress(e);
+ },
+ true);
+ this._commandLineInput = input;
+
+ // command object stores command-line history state.
+ this._command = {
+ history: [],
+ history_index: 0,
+ pending: null
+ };
+};
+
+DebuggerConsole.prototype = {
+ /**
+ * Sets focus to command-line-text element.
+ */
+ focusOnCommandLine: function() {
+ this._commandLineInput.focus();
+ },
+
+ /**
+ * Called by chrome code when there's output to display.
+ * @param {string} txt
+ */
+ appendText: function(txt)
+ {
+ this._output.appendChild(document.createTextNode(txt));
+ this._output.appendChild(document.createElement('br'));
+ document.body.scrollTop = document.body.scrollHeight;
+ },
+
+ /**
+ * Called by chrome code to set the current state as to whether the debugger
+ * is stopped at a breakpoint or is running.
+ * @param {boolean} isBroken
+ */
+ setDebuggerBreak: function(isBroken)
+ {
+ var out = this._output;
+ if (isBroken) {
+ out.style.color = "black";
+ this.focusOnCommandLine();
+ } else {
+ out.style.color = "gray";
+ }
+ },
+
+ /**
+ * Execute a debugger command, add it to the command history and display it in
+ * the output window.
+ * @param {string} str
+ */
+ executeCommand: function(str) {
+ this.appendText("$ " + str);
+ // Sends message to DebuggerContents.HandleCommand.
+ if (DebugShell.singleton) {
+ DebugShell.singleton.command(str);
+ } else {
+ this.appendText("FAILED to send the command as DebugShell is null");
+ }
+
+ this._command.history.push(str);
+ this._command.history_index = this._command.history.length;
+ this._command.pending = null;
+ },
+
+
+ /**
+ * Display the previous history item in the given text field.
+ * @param {HTMLInputElement} field
+ */
+ selectPreviousCommand_: function(field) {
+ var command = this._command;
+ if (command.history_index > 0) {
+ // Remember the current field value as a pending command if we're at the
+ // end (it's something the user typed in).
+ if (command.history_index == command.history.length)
+ command.pending = field.value;
+ command.history_index--;
+ field.value = command.history[command.history_index];
+ field.select();
+ }
+ },
+
+ /**
+ * Display the next history item in the given text field.
+ * @param {HTMLInputElement} field
+ */
+ selectNextCommand_: function(field) {
+ var command = this._command;
+ if (command.history_index < command.history.length) {
+ command.history_index++;
+ if (command.history_index == command.history.length) {
+ field.value = command.pending || "";
+ } else {
+ field.value = command.history[command.history_index];
+ }
+ field.select();
+ }
+ },
+
+ /**
+ * command-line-text's onkeydown handler.
+ * @param {KeyboardEvent} e
+ * @return {boolean}
+ */
+ _onInputKeyDown: function (e) {
+ var field = e.target;
+ var key = e.keyCode;
+ if (key == 38) { // up arrow
+ this.selectPreviousCommand_(field);
+ return false;
+ } else if (key == 40) { // down arrow
+ this.selectNextCommand_(field);
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * command-line-text's onkeypress handler.
+ * @param {KeyboardEvent} e
+ * @return {boolean}
+ */
+ _onInputKeyPress: function (e) {
+ var field = e.target;
+ var key = e.keyCode;
+ if (key == 13) { // enter
+ this.executeCommand(field.value);
+ field.value = "";
+ return false;
+ }
+ return true;
+ }
+};
diff --git a/webkit/inspector/DebuggerIPC.js b/webkit/inspector/DebuggerIPC.js
new file mode 100644
index 0000000..15835fc
--- /dev/null
+++ b/webkit/inspector/DebuggerIPC.js
@@ -0,0 +1,109 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Implementation of debugger inter-process communication.
+ * Debugger UI can send messages to the DebuggerHost object living in
+ * Chrome browser process. The messages are either processed by DebuggerHost
+ * itself or trigger IPC message(such as break, evaluate script etc) from
+ * browser process to the renderer where the v8 instance being debugged
+ * will process them.
+ */
+
+var DebuggerIPC = {};
+
+/**
+ * Set up default debugger UI.
+ * @param {DebuggerPanel|DebuggerConsole} debuggerUI
+ */
+DebuggerIPC.init = function(debuggerUI) {
+ DebuggerIPC.debuggerUI = debuggerUI;
+}
+
+/**
+ * Sends JSON message to DebuggerHost.
+ * @param {Array.<Object>} nameAndArguments
+ */
+DebuggerIPC.sendMessage = function(nameAndArguments) {
+ //alert("DebuggerIPC.sendMessage " + nameAndArguments);
+ dprint("DebuggerIPC.callMethod([" + nameAndArguments + "])");
+ // convert all arguments to strings
+ // TODO(yurys): extend chrome.send to accept array of any value
+ // objects not only strings
+ for (var i = 0; i < nameAndArguments.length; i++) {
+ if (typeof nameAndArguments[i] != "string") {
+ nameAndArguments[i] = "" + nameAndArguments[i];
+ }
+ }
+
+ chrome.send("DebuggerHostMessage", nameAndArguments);
+};
+
+/**
+ * Handles messages from DebuggerHost.
+ * @param {Object} msg An object representing the message.
+ */
+DebuggerIPC.onMessageReceived = function(msg) {
+ //alert("DebuggerIPC.onMessageReceived " + msg.event);
+ var ui = DebuggerIPC.debuggerUI;
+ dprint("onMessageReceived: " + (msg && msg.event));
+ if (msg.type == "event") {
+ if (msg.event == "setDebuggerBreak") {
+ var val = msg.body.argument;
+ ui.setDebuggerBreak(val);
+ } else if (msg.event == "appendText") {
+ var text = msg.body.text;
+ ui.appendText(text);
+ } else if (msg.event == "focusOnCommandLine") {
+ dprint("focusOnCommandLine event received");
+ // messages to DebugShell
+ } else if (msg.event == "initDebugShell") {
+ // DebugShell.initDebugShell();
+ dprint(msg.event + " done");
+ } else if (msg.event == "on_attach") {
+ if (DebugShell.singleton) {
+ var args = msg.body.arguments;
+ if (!args || args.length != 1) {
+ dprint(msg.event + " failed: invalid arguments");
+ return;
+ }
+ var title = args[0];
+ DebugShell.singleton.on_attach(title);
+ dprint(msg.event + " done");
+ } else {
+ dprint(msg.event + " failed: DebugShell.singleton == null");
+ }
+ } else if (msg.event == "on_disconnect") {
+ if (DebugShell.singleton) {
+ DebugShell.singleton.on_disconnect();
+ dprint(msg.event + " done");
+ } else {
+ dprint(msg.event + " failed: DebugShell.singleton == null");
+ }
+ } else if (msg.event == "exit") {
+ if (DebugShell.singleton) {
+ DebugShell.singleton.exit();
+ dprint(msg.event + " done");
+ } else {
+ dprint(msg.event + " failed: DebugShell.singleton == null");
+ }
+ } else if (msg.event == "response") {
+ if (DebugShell.singleton) {
+ var args = msg.body.arguments;
+ if (!args || args.length != 1) {
+ dprint(msg.event + " failed: invalid argument");
+ return;
+ }
+ DebugShell.singleton.response(args[0]);
+ dprint(msg.event + " done");
+ } else {
+ ui.appendText(msg.event + " failed: DebugShell.singleton == null");
+ }
+ } else {
+ ui.appendText("Unknown event: " + msg.event);
+ }
+ } else {
+ ui.appendText("Unknown message type: " + msg.type);
+ }
+};
diff --git a/webkit/inspector/DebuggerPanel.js b/webkit/inspector/DebuggerPanel.js
new file mode 100644
index 0000000..bb059ac
--- /dev/null
+++ b/webkit/inspector/DebuggerPanel.js
@@ -0,0 +1,201 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview WebInspector panel representing command line debugger.
+ */
+
+/**
+ * Command line debugger panel.
+ * @constructor
+ * @extends {WebInspector.Panel}
+ */
+WebInspector.DebuggerPanel = function()
+{
+ WebInspector.Panel.call(this);
+
+ this.contentElement = document.createElement("div");
+ this.contentElement.id = "debugger-content";
+
+ var table = document.createElement("table");
+ table.id = 'outer';
+ var tr = document.createElement("tr");
+ this._output = document.createElement("td");
+ this._output.id = "output";
+ this._output.valign = "bottom";
+ this.appendText("Chrome JavaScript Debugger");
+ this.appendText("Type 'help' for a list of commands.");
+
+ tr.appendChild(this._output);
+ table.appendChild(tr);
+ this.contentElement.appendChild(table);
+
+
+ var commandLine = document.createElement("div");
+ commandLine.id = "command-line";
+ var input = document.createElement("input");
+ input.id = "command-line-text";
+ input.addEventListener('keydown', this._onInputKeyDown.bind(this), true);
+ input.addEventListener('keypress', this._onInputKeyPress.bind(this), true);
+ input.type = "text";
+ this.commandLineInput_ = input;
+
+ commandLine.appendChild(input);
+ this.contentElement.appendChild(commandLine);
+
+ // command object stores command-line history state.
+ this._command = {
+ history: [],
+ history_index: 0,
+ pending: null
+ };
+
+ this.element.appendChild(this.contentElement);
+ p('DebuggerPanel created');
+};
+
+WebInspector.DebuggerPanel.prototype = {
+ toolbarItemClass: "debugger",
+
+ get toolbarItemLabel()
+ {
+ return "Debugger"; //WebInspector.UIString("Debugger");
+ },
+
+ show: function()
+ {
+ WebInspector.Panel.prototype.show.call(this);
+ this.focusOnCommandLine();
+ },
+
+ /**
+ * Sets focus to command-line-text element.
+ */
+ focusOnCommandLine: function()
+ {
+ if (this.visible) {
+ this.commandLineInput_.focus();
+ }
+ },
+
+ /**
+ * Called by chrome code when there's output to display.
+ * @param {string} txt
+ */
+ appendText: function(txt)
+ {
+ this._output.appendChild(document.createTextNode(txt));
+ this._output.appendChild(document.createElement('br'));
+ },
+
+ /**
+ * Called by chrome code to set the current state as to whether the debugger
+ * is stopped at a breakpoint or is running.
+ * @param {boolean} isBroken
+ */
+ setDebuggerBreak: function(isBroken)
+ {
+ var out = this._output;
+ if (isBroken) {
+ out.style.color = "black";
+ this.focusOnCommandLine();
+ } else {
+ out.style.color = "gray";
+ }
+ },
+
+ /**
+ * Execute a debugger command, add it to the command history and display
+ * it in the output window.
+ * @param {string} str
+ */
+ executeCommand: function(str)
+ {
+ this.appendText("$ " + str);
+ // Sends message to DebuggerContents.HandleCommand.
+ if (DebugShell.singleton) {
+ DebugShell.singleton.command(str);
+ } else {
+ this.appendText("FAILED to send the command as DebugShell is null");
+ }
+
+ this._command.history.push(str);
+ this._command.history_index = this._command.history.length;
+ this._command.pending = null;
+ },
+
+ /**
+ * Display the previous history item in the given text field.
+ * @param {HTMLInputElement} field
+ */
+ _selectPreviousCommand: function(field)
+ {
+ var command = this._command;
+ if (command.history_index > 0) {
+ // Remember the current field value as a pending command if we're at the
+ // end (it's something the user typed in).
+ if (command.history_index == command.history.length)
+ command.pending = field.value;
+ command.history_index--;
+ field.value = command.history[command.history_index];
+ field.select();
+ }
+ },
+
+ /**
+ * Display the next history item in the given text field.
+ * @param {HTMLInputElement} field
+ */
+ _selectNextCommand: function(field)
+ {
+ var command = this._command;
+ if (command.history_index < command.history.length) {
+ command.history_index++;
+ if (command.history_index == command.history.length) {
+ field.value = command.pending || "";
+ } else {
+ field.value = command.history[command.history_index];
+ }
+ field.select();
+ }
+ },
+
+ /**
+ * command-line-text's onkeydown handler.
+ * @param {KeyboardEvent} e
+ * @return {boolean}
+ */
+ _onInputKeyDown: function (e)
+ {
+ var field = e.target;
+ var key = e.keyCode;
+ if (key == 38) { // up arrow
+ this._selectPreviousCommand(field);
+ return false;
+ } else if (key == 40) { // down arrow
+ this._selectNextCommand(field);
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * command-line-text's onkeypress handler.
+ * @param {KeyboardEvent} e
+ * @return {boolean}
+ */
+ _onInputKeyPress: function (e)
+ {
+ var field = e.target;
+ var key = e.keyCode;
+ if (key == 13) { // enter
+ this.executeCommand(field.value);
+ field.value = "";
+ return false;
+ }
+ return true;
+ }
+};
+
+WebInspector.DebuggerPanel.prototype.__proto__ = WebInspector.Panel.prototype;
diff --git a/webkit/inspector/DebuggerShell.js b/webkit/inspector/DebuggerShell.js
new file mode 100644
index 0000000..907dce7
--- /dev/null
+++ b/webkit/inspector/DebuggerShell.js
@@ -0,0 +1,1458 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Shell objects and global helper functions for Chrome
+ * automation shell / debugger. This file is loaded into the global namespace
+ * of the interactive shell, so users can simply call global functions
+ * directly.
+ */
+
+// TODO(erikkay): look into how this can be split up into multiple files
+// It's currently loaded explicitly by Chrome, so maybe I need an "include"
+// or "source" builtin to allow a core source file to reference multiple
+// sub-files.
+
+/**
+ * Sequence number of the DebugCommand.
+ */
+DebugCommand.next_seq_ = 0;
+
+/**
+ * Command messages to be sent to the debugger.
+ * @constructor
+ */
+function DebugCommand(str) {
+ this.command = undefined;
+ // first, strip off of the leading word as the command
+ var argv = str.split(' ');
+ this.user_command = argv.shift();
+ // the rest of the string is argv to the command
+ str = argv.join(' ');
+ if (DebugCommand.aliases[this.user_command])
+ this.user_command = DebugCommand.aliases[this.user_command];
+ if (this.parseArgs_(str) == 1)
+ this.type = "request";
+ if (this.command == undefined)
+ this.command = this.user_command;
+};
+
+// Mapping of some control characters to avoid the \uXXXX syntax for most
+// commonly used control cahracters.
+const ctrlCharMap_ = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+};
+
+// Regular expression matching ", \ and control characters (0x00 - 0x1F)
+// globally.
+const ctrlCharMatch_ = /["\\\\\x00-\x1F]/g;
+
+/**
+ * Convert a String to its JSON representation.
+ * @param {String} value - String to be converted
+ * @return {String} JSON formatted String
+ */
+DebugCommand.stringToJSON = function(value) {
+ // Check for" , \ and control characters (0x00 - 0x1F).
+ if (ctrlCharMatch_.test(value)) {
+ // Replace ", \ and control characters (0x00 - 0x1F).
+ return '"' + value.replace(ctrlCharMatch_, function (char) {
+ // Use charmap if possible.
+ var mapped = ctrlCharMap_[char];
+ if (mapped) return mapped;
+ mapped = char.charCodeAt();
+ // Convert control character to unicode escape sequence.
+ var dig1 = (Math.floor(mapped / 16));
+ var dig2 = (mapped % 16)
+ return '\\u00' + dig1.toString(16) + dig2.toString(16);
+ })
+ + '"';
+ }
+
+ // Simple string with no special characters.
+ return '"' + value + '"';
+};
+
+/**
+ * @return {bool} True if x is an integer.
+ */
+DebugCommand.isInt = function(x) {
+ var y = parseInt(x);
+ if (isNaN(y))
+ return false;
+ return x == y && x.toString() == y.toString();
+};
+
+/**
+ * @return {float} log base 10 of num
+ */
+DebugCommand.log10 = function(num) {
+ return Math.log(num)/Math.log(10);
+};
+
+/**
+ * Take an object and encode it (non-recursively) as a JSON dict.
+ * @param {Object} obj - object to encode
+ */
+DebugCommand.toJSON = function(obj) {
+ // TODO(erikkay): use a real JSON library
+ var json = '{';
+ for (var key in obj) {
+ if (json.length > 1)
+ json += ",";
+ var val = obj[key];
+ if (!DebugCommand.isInt(val)) {
+ val = DebugCommand.stringToJSON(val.toString());
+ }
+ json += '"' + key + '":' + val;
+ }
+ json += '}';
+ return json;
+};
+
+/**
+ * Encode the DebugCommand object into the V8 debugger JSON protocol format.
+ */
+DebugCommand.prototype.toJSONProtocol = function() {
+ // TODO(erikkay): use a real JSON library
+ var json = '{';
+ json += '"seq":"' + this.seq;
+ json += '","type":"' + this.type;
+ json += '","command":"' + this.command + '"';
+ if (this.arguments) {
+ json += ',"arguments":' + DebugCommand.toJSON(this.arguments);
+ }
+ json += '}'
+ return json;
+}
+
+/**
+ * Encode the contents of this message and send it to the debugger.
+ * @param {Object} tab - tab being debugged. This is an internal
+ * Chrome object.
+ */
+DebugCommand.prototype.sendToDebugger = function(tab) {
+ this.seq = DebugCommand.next_seq_++;
+ str = this.toJSONProtocol();
+ dprint("sending: " + str);
+ tab.sendToDebugger(str);
+};
+
+DebugCommand.trim = function(str) {
+ return str.replace(/^\s*/, '').replace(/\s*$/, '');
+};
+
+/**
+ * Strip off a trailing parameter after a ':'. As the identifier for the
+ * source can contain ':' characters (e.g. 'http://www....) something after
+ * a ':' is only considered a parameter if it is numeric.
+ * @return {Array} two element array, the trimmed string and the parameter,
+ * or -1 if no parameter
+ */
+DebugCommand.stripTrailingParameter = function(str, opt_separator) {
+ var sep = opt_separator || ':';
+ var index = str.lastIndexOf(sep);
+ // If a separator character if found strip if numeric.
+ if (index != -1) {
+ var value = parseInt(str.substring(index + 1, str.length), 10);
+ if (isNaN(value) || value < 0) {
+ return [str, -1];
+ }
+ str = str.substring(0, index);
+ return [str, value];
+ }
+ return [str, -1];
+};
+
+/**
+ * Format source and location strings based on source location input data.
+ * @param {Object} script - script information object
+ * @param {String} source - source code for the current location
+ * @param {int} line - line number (0-based)
+ * @param {String} func - function name
+ * @return {array} [location(string), source line(string), line number(int)]
+ */
+DebugCommand.getSourceLocation = function(script, source, line, func) {
+ // source line is 0-based, we present as 1-based
+ line++;
+
+ // TODO(erikkay): take column into account as well
+ if (source)
+ source = "" + line + ": " + source;
+ var location = '';
+ if (func) {
+ location = func + ", ";
+ }
+ location += script ? script.name : '[no source]';
+ return [location, source, line];
+};
+
+/**
+ * Aliases for debugger commands.
+ */
+DebugCommand.aliases = {
+ 'b': 'break',
+ 'bi': 'break_info',
+ 'br': 'break',
+ 'bt': 'backtrace',
+ 'c': 'continue',
+ 'f': 'frame',
+ 'h': 'help',
+ '?': 'help',
+ 'ls': 'source',
+ 'n': 'next',
+ 'p': 'print',
+ 's': 'step',
+ 'so': 'stepout',
+};
+
+/**
+ * Parses arguments to "args" and "locals" command, and initializes
+ * the underlying DebugCommand (which is a frame request).
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseArgsAndLocals_ = function(str) {
+ this.command = "frame";
+ return str.length ? -1 : 1;
+};
+
+/**
+ * Parses arguments to "break_info" command, and executes it.
+ * "break_info" has an optional argument, which is the breakpoint
+ * identifier.
+ * @see DebugCommand.commands
+ * @param {string} str - The arguments to be parsed.
+ * @return -1 for usage error, 0 for success
+ */
+DebugCommand.prototype.parseBreakInfo_ = function(str) {
+ this.type = "shell";
+
+ // Array of breakpoints to be printed by this command
+ // (default to all breakpoints)
+ var breakpointsToPrint = shell_.breakpoints;
+
+ if (str.length > 0) {
+ // User specified an invalid breakpoint (not a number)
+ if (!str.match(/^\s*\d+\s*$/))
+ return -1; // invalid usage
+
+ // Check that the specified breakpoint identifier exists
+ var id = parseInt(str);
+ var info = shell_.breakpoints[id];
+ if (!info) {
+ print("Error: Invalid breakpoint");
+ return 0; // success (of sorts)
+ }
+ breakpointsToPrint = [info];
+ } else {
+ // breakpointsToPrint.length isn't accurate, because of
+ // deletions
+ var num_breakpoints = 0;
+ for (var i in breakpointsToPrint) num_breakpoints++;
+
+ print("Num breakpoints: " + num_breakpoints);
+ }
+
+ DebugShell.printBreakpoints_(breakpointsToPrint);
+
+ return 0; // success
+}
+
+/**
+ * Parses arguments to "step" command.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseStep_ = function(str, opt_stepaction) {
+ this.command = "continue";
+ action = opt_stepaction || "in";
+ this.arguments = {"stepaction" : action}
+ if (str.length) {
+ count = parseInt(str);
+ if (count > 0) {
+ this.arguments["stepcount"] = count;
+ } else {
+ return -1;
+ }
+ }
+ return 1;
+};
+
+/**
+ * Parses arguments to "step" command.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseStepOut_ = function(str) {
+ return this.parseStep_(str, "out");
+};
+
+/**
+ * Parses arguments to "next" command.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseNext_ = function(str) {
+ return this.parseStep_(str, "next");
+};
+
+/**
+ * Parse the arguments to "print" command.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return 1 - always succeeds
+ */
+DebugCommand.prototype.parsePrint_ = function(str) {
+ this.command = "evaluate";
+ this.arguments = { "expression" : str };
+ // If the page is in the running state, then we force the expression to
+ // evaluate in the global context to avoid evaluating in a random context.
+ if (shell_.running)
+ this.arguments["global"] = true;
+ return 1;
+};
+
+/**
+ * Handle the response to a "print" command and display output to user.
+ * @param {ProtocolPacket} evaluate_response - the V8 debugger response object
+ */
+DebugCommand.responsePrint_ = function(evaluate_response) {
+ body = evaluate_response.body();
+ if (body['text'] != undefined) {
+ print(body['text']);
+ } else {
+ // TODO(erikkay): is "text" ever not set?
+ print("can't print response");
+ }
+};
+
+/**
+ * Parse the arguments to "dir" command.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return 1 - always succeeds
+ */
+DebugCommand.prototype.parseDir_ = function(str) {
+ this.command = "evaluate";
+ this.arguments = { "expression" : str };
+ // If the page is in the running state, then we force the expression to
+ // evaluate in the global context to avoid evaluating in a random context.
+ if (shell_.running)
+ this.arguments["global"] = true;
+ return 1;
+};
+
+/**
+ * Handle the response to a "dir" command and display output to user.
+ * @see http://wiki/Main/V8Debugger
+ * @param {ProtocolPacket} evaluate_response - the V8 debugger response object
+ */
+DebugCommand.responseDir_ = function(evaluate_response) {
+ var body = evaluate_response.body();
+ if (body.properties) {
+ print(body.properties.length + ' properties');
+ for (var n in body.properties) {
+ var property_info = body.properties[n].name;
+ property_info += ': ';
+ var value = evaluate_response.lookup(body.properties[n].ref);
+ if (value && value.type) {
+ property_info += value.type;
+ } else {
+ property_info += '<no type>';
+ }
+ property_info += ' (#';
+ property_info += body.properties[n].ref;
+ property_info += '#)';
+ print(property_info);
+ }
+ }
+};
+
+/**
+ * Parses arguments to "break" command. See DebugCommand.commands below
+ * for syntax details.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success, 0 for handled internally
+ */
+DebugCommand.prototype.parseBreak_ = function(str) {
+ function stripTrailingParameter() {
+ var ret = DebugCommand.stripTrailingParameter(str, ':');
+ str = ret[0];
+ return ret[1];
+ }
+
+ if (str.length == 0) {
+ this.command = "break";
+ return 1;
+ } else {
+ var parts = str.split(/\s+/);
+ var condition = null;
+ if (parts.length > 1) {
+ str = parts.shift();
+ condition = parts.join(" ");
+ }
+
+ this.command = "setbreakpoint";
+
+ // Locate ...[:line[:column]] if present.
+ var line = -1;
+ var column = -1;
+ line = stripTrailingParameter();
+ if (line != -1) {
+ line -= 1;
+ var l = stripTrailingParameter();
+ if (l != -1) {
+ column = line;
+ line = l - 1;
+ }
+ }
+
+ if (line == -1 && column == -1) {
+ this.arguments = { 'type' : 'function',
+ 'target' : str };
+ } else {
+ var script = shell_.matchScript(str, line);
+ if (script) {
+ this.arguments = { 'type' : 'script',
+ 'target' : script.name };
+ } else {
+ this.arguments = { 'type' : 'function',
+ 'target' : str };
+ }
+ this.arguments.line = line;
+ if (column != -1)
+ this.arguments.position = column;
+ }
+ if (condition)
+ this.arguments.condition = condition;
+ }
+ return 1;
+};
+
+/**
+ * Handle the response to a "break" command and display output to user.
+ * @param {ResponsePacket} setbreakpoint_response - the V8 debugger response
+ * object
+ */
+DebugCommand.responseBreak_ = function(setbreakpoint_response) {
+ var body = setbreakpoint_response.body();
+ var info = new BreakpointInfo(
+ parseInt(body.breakpoint),
+ setbreakpoint_response.command.arguments.type,
+ setbreakpoint_response.command.arguments.target,
+ setbreakpoint_response.command.arguments.line,
+ setbreakpoint_response.command.arguments.position,
+ setbreakpoint_response.command.arguments.condition);
+ shell_.addedBreakpoint(info);
+};
+
+/**
+ * Parses arguments to "backtrace" command. See DebugCommand.commands below
+ * for syntax details.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+ DebugCommand.prototype.parseBacktrace_ = function(str) {
+ if (str.length > 0) {
+ var parts = str.split(/\s+/);
+ var non_empty_parts = parts.filter(function(s) { return s.length > 0; });
+ // We need exactly two arguments.
+ if (non_empty_parts.length != 2) {
+ return -1;
+ }
+ var from = parseInt(non_empty_parts[0], 10);
+ var to = parseInt(non_empty_parts[1], 10);
+ // The two arguments have to be integers.
+ if (from != non_empty_parts[0] || to != non_empty_parts[1]) {
+ return -1;
+ }
+ this.arguments = { 'fromFrame': from, 'toFrame': to + 1 };
+ } else {
+ // Default to fetching the first 10 frames.
+ this.arguments = { 'fromFrame': 0, 'toFrame': 10 };
+ }
+ return 1;
+};
+
+/**
+ * Handle the response to a "backtrace" command and display output to user.
+ * @param {ResponsePacket} backtrace_response - the V8 debugger response object
+ */
+DebugCommand.responseBacktrace_ = function(backtrace_response) {
+ body = backtrace_response.body();
+ if (body && body.totalFrames) {
+ print('Frames #' + body.fromFrame + ' to #' + (body.toFrame - 1) +
+ ' of ' + body.totalFrames + ":");
+ for (var i = 0; i < body.frames.length; i++) {
+ print(body.frames[i].text);
+ }
+ } else {
+ print("unimplemented (sorry)");
+ }
+};
+
+
+/**
+ * Parses arguments to "clear" command. See DebugCommand.commands below
+ * for syntax details.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseClearCommand_ = function(str) {
+ this.command = "clearbreakpoint";
+ if (str.length > 0) {
+ var i = parseInt(str, 10);
+ if (i != str) {
+ return -1;
+ }
+ this.arguments = { 'breakpoint': i };
+ }
+ return 1;
+}
+
+/**
+ * Handle the response to a "clear" command and display output to user.
+ * @param {ResponsePacket} clearbreakpoint_response - the V8 debugger response
+ * object
+ */
+DebugCommand.responseClear_ = function(clearbreakpoint_response) {
+ var body = clearbreakpoint_response.body();
+ shell_.clearedBreakpoint(parseInt(msg.command.arguments.breakpoint));
+}
+
+
+/**
+ * Parses arguments to "continue" command. See DebugCommand.commands below
+ * for syntax details.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseContinueCommand_ = function(str) {
+ this.command = "continue";
+ if (str.length > 0) {
+ return -1;
+ }
+ return 1;
+}
+
+/**
+ * Parses arguments to "frame" command. See DebugCommand.commands below
+ * for syntax details.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseFrame_ = function(str) {
+ if (str.length > 0) {
+ var i = parseInt(str, 10);
+ if (i != str) {
+ return -1;
+ }
+ this.arguments = { 'number': i };
+ }
+ return 1;
+};
+
+/**
+ * Handle the response to a "frame" command and display output to user.
+ * @param {ResponsePacket} frame_response - the V8 debugger response object
+ */
+DebugCommand.responseFrame_ = function(frame_response) {
+ var body = frame_response.body();
+ var func = frame_response.lookup(body.func.ref);
+ loc = DebugCommand.getSourceLocation(func.script,
+ body.sourceLineText, body.line, func.name);
+ print("#" + (body.index <= 9 ? '0' : '') + body.index + " " + loc[0]);
+ print(loc[1]);
+ shell_.current_frame = body.index;
+ shell_.current_line = loc[2];
+ shell_.current_script = func.script;
+};
+
+/**
+ * Handle the response to a "args" command and display output to user.
+ * @param {ProtocolPacket} frame_response - the V8 debugger response object (for
+ * "frame" command)
+ */
+DebugCommand.responseArgs_ = function(frame_response) {
+ var body = frame_response.body();
+ DebugCommand.printVariables_(body.arguments, frame_response);
+}
+
+/**
+ * Handle the response to a "locals" command and display output to user.
+ * @param {Object} msg - the V8 debugger response object (for "frame" command)
+ */
+DebugCommand.responseLocals_ = function(frame_response) {
+ var body = frame_response.body();
+ DebugCommand.printVariables_(body.locals, frame_response);
+}
+
+DebugCommand.printVariables_ = function(variables, protocol_packet) {
+ for (var i = 0; i < variables.length; i++) {
+ print(variables[i].name + " = " +
+ DebugCommand.toPreviewString_(protocol_packet.lookup(variables[i].value.ref)));
+ }
+}
+
+DebugCommand.toPreviewString_ = function(value) {
+ // TODO(ericroman): pretty print arrays and objects, recursively.
+ // TODO(ericroman): truncate length of preview if too long?
+ if (value.type == "string") {
+ // Wrap the string in quote marks and JS-escape
+ return DebugCommand.stringToJSON(value.text);
+ }
+ return value.text;
+}
+
+/**
+ * Parses arguments to "scripts" command.
+ * @see DebugCommand.commands
+ * @param {string} str - The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseScripts_ = function(str) {
+ return 1
+};
+
+/**
+ * Handle the response to a "scripts" command and display output to user.
+ * @param {ResponsePacket} scripts_response - the V8 debugger response object
+ */
+DebugCommand.responseScripts_ = function(scripts_response) {
+ scripts = scripts_response.body();
+ shell_.scripts = [];
+ for (var i in scripts) {
+ var script = scripts[i];
+
+ // Add this script to the internal list of scripts.
+ shell_.scripts.push(script);
+
+ // Print result if this response was the result of a user command.
+ if (scripts_response.command.from_user) {
+ var name = script.name;
+ if (name) {
+ if (script.lineOffset > 0) {
+ print(name + " (lines " + script.lineOffset + "-" +
+ (script.lineOffset + script.lineCount - 1) + ")");
+ } else {
+ print(name + " (lines " + script.lineCount + ")");
+ }
+ } else {
+ // For unnamed scripts (typically eval) display some source.
+ var sourceStart = script.sourceStart;
+ if (sourceStart.length > 40)
+ sourceStart = sourceStart.substring(0, 37) + '...';
+ print("[unnamed] (source:\"" + sourceStart + "\")");
+ }
+ }
+ }
+};
+
+/**
+ * Parses arguments to "source" command.
+ * @see DebugCommand.commands
+ * @param {string} str - The arguments to be parsed.
+ * @return -1 for usage error, 1 for success
+ */
+DebugCommand.prototype.parseSource_ = function(str) {
+ this.arguments = {};
+ if (this.current_frame > 0)
+ this.arguments.frame = this.current_frame;
+ if (str.length) {
+ var args = str.split(" ");
+ if (args.length == 1) {
+ // with 1 argument n, we print 10 lines starting at n
+ var num = parseInt(args[0]);
+ if (num > 0) {
+ this.arguments.fromLine = num - 1;
+ this.arguments.toLine = this.arguments.fromLine + 10;
+ } else {
+ return -1;
+ }
+ } else if (args.length == 2) {
+ // with 2 arguments x and y, we print from line x to line x + y
+ var from = parseInt(args[0]);
+ var len = parseInt(args[1]);
+ if (from > 0 && len > 0) {
+ this.arguments.fromLine = from - 1;
+ this.arguments.toLine = this.arguments.fromLine + len;
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ if (this.arguments.fromLine < 0)
+ return -1;
+ if (this.arguments.toLine <= this.arguments.fromLine)
+ return -1;
+ } else if (shell_.current_line > 0) {
+ // with no arguments, we print 11 lines with the current line as the center
+ this.arguments.fromLine =
+ Math.max(0, shell_.current_line - 6);
+ this.arguments.toLine = this.arguments.fromLine + 11;
+ }
+ return 1;
+};
+
+/**
+ * Handle the response to a "source" command and display output to user.
+ * @param {ProtocolPacket} source_response - the V8 debugger response object
+ */
+DebugCommand.responseSource_ = function(source_response) {
+ var body = source_response.body();
+ var from_line = parseInt(body.fromLine) + 1;
+ var source = body.source;
+ var lines = source.split('\n');
+ var maxdigits = 1 + Math.floor(DebugCommand.log10(from_line + lines.length))
+ for (var num in lines) {
+ // there's an extra newline at the end
+ if (num >= (lines.length - 1) && lines[num].length == 0)
+ break;
+ spacer = maxdigits - (1 + Math.floor(DebugCommand.log10(from_line)))
+ var line = "";
+ if (from_line == shell_.current_line) {
+ for (var i = 0; i < (maxdigits + 2); i++)
+ line += ">";
+ } else {
+ for (var i = 0; i < spacer; i++)
+ line += " ";
+ line += from_line + ": ";
+ }
+ line += lines[num];
+ print(line);
+ from_line++;
+ }
+};
+
+/**
+ * Parses arguments to "help" command. See DebugCommand.commands below
+ * for syntax details.
+ * @see DebugCommand.commands
+ * @param {string} str The arguments to be parsed.
+ * @return 0 for handled internally
+ */
+DebugCommand.parseHelp_ = function(str) {
+ DebugCommand.help(str);
+ return 0;
+};
+
+/**
+ * Takes argument and evaluates it in the context of the shell to allow commands
+ * to be escaped to the outer shell. Used primarily for development purposes.
+ * @see DebugCommand.commands
+ * @param {string} str The expression to be evaluated
+ * @return 0 for handled internally
+ */
+DebugCommand.parseShell_ = function(str) {
+ print(eval(str));
+ return 0;
+}
+
+DebugCommand.parseShellDebug_ = function(str) {
+ shell_.debug = !shell_.debug;
+ if (shell_.debug) {
+ print("shell debugging enabled");
+ } else {
+ print("shell debugging disabled");
+ }
+ return 0;
+}
+
+/**
+ * Parses a user-entered command string.
+ * @param {string} str The arguments to be parsed.
+ */
+DebugCommand.prototype.parseArgs_ = function(str) {
+ if (str.length)
+ str = DebugCommand.trim(str);
+ var cmd = DebugCommand.commands[this.user_command];
+ if (cmd) {
+ var parse = cmd['parse'];
+ if (parse == undefined) {
+ print('>>>can\'t find parse func for ' + this.user_command);
+ this.type = "error";
+ } else {
+ var ret = parse.call(this, str);
+ if (ret > 0) {
+ // Command gererated a debugger request.
+ this.type = "request";
+ } else if (ret == 0) {
+ // Command handeled internally.
+ this.type = "handled";
+ } else if (ret < 0) {
+ // Command error.
+ this.type = "handled";
+ DebugCommand.help(this.user_command);
+ }
+ }
+ } else {
+ this.type = "handled";
+ print('unknown command: ' + this.user_command);
+ DebugCommand.help();
+ }
+};
+
+/**
+ * Displays command help or all help.
+ * @param {string} opt_str Which command to print help for.
+ */
+DebugCommand.help = function(opt_str) {
+ if (opt_str) {
+ var cmd = DebugCommand.commands[opt_str];
+ var usage = cmd.usage;
+ print('usage: ' + usage);
+ // Print additional details for the command.
+ if (cmd.help) {
+ print(cmd.help);
+ }
+ } else {
+ if (shell_.running) {
+ print('Status: page is running');
+ } else {
+ print('Status: page is paused');
+ }
+ print('Available commands:');
+ for (var key in DebugCommand.commands) {
+ var cmd = DebugCommand.commands[key];
+ if (!cmd['hidden'] && (!shell_.running || cmd['while_running'])) {
+ var usage = cmd.usage;
+ print(' ' + usage);
+ }
+ }
+ }
+};
+
+/**
+ * Valid commands, their argument parser and their associated usage text.
+ */
+DebugCommand.commands = {
+ 'args': { 'parse': DebugCommand.prototype.parseArgsAndLocals_,
+ 'usage': 'args',
+ 'help': 'summarize the arguments to the current function.',
+ 'response': DebugCommand.responseArgs_ },
+ 'break': { 'parse': DebugCommand.prototype.parseBreak_,
+ 'response': DebugCommand.responseBreak_,
+ 'usage': 'break [location] <condition>',
+ 'help': 'location is one of <function> | <script:function> | <script:line> | <script:line:pos>',
+ 'while_running': true },
+ 'break_info': { 'parse': DebugCommand.prototype.parseBreakInfo_,
+ 'usage': 'break_info [breakpoint #]',
+ 'help': 'list the current breakpoints, or the details on a single one',
+ 'while_running': true },
+ 'backtrace': { 'parse': DebugCommand.prototype.parseBacktrace_,
+ 'response': DebugCommand.responseBacktrace_,
+ 'usage': 'backtrace [from frame #] [to frame #]' },
+ 'clear': { 'parse': DebugCommand.prototype.parseClearCommand_,
+ 'response': DebugCommand.responseClear_,
+ 'usage': 'clear <breakpoint #>',
+ 'while_running': true },
+ 'continue': { 'parse': DebugCommand.prototype.parseContinueCommand_,
+ 'usage': 'continue' },
+ 'dir': { 'parse': DebugCommand.prototype.parseDir_,
+ 'response': DebugCommand.responseDir_,
+ 'usage': 'dir <expression>',
+ 'while_running': true },
+ 'frame': { 'parse': DebugCommand.prototype.parseFrame_,
+ 'response': DebugCommand.responseFrame_,
+ 'usage': 'frame <frame #>' },
+ 'help': { 'parse': DebugCommand.parseHelp_,
+ 'usage': 'help [command]',
+ 'while_running': true },
+ 'locals': { 'parse': DebugCommand.prototype.parseArgsAndLocals_,
+ 'usage': 'locals',
+ 'help': 'summarize the local variables for current frame',
+ 'response': DebugCommand.responseLocals_ },
+ 'next': { 'parse': DebugCommand.prototype.parseNext_,
+ 'usage': 'next' } ,
+ 'print': { 'parse': DebugCommand.prototype.parsePrint_,
+ 'response': DebugCommand.responsePrint_,
+ 'usage': 'print <expression>',
+ 'while_running': true },
+ 'scripts': { 'parse': DebugCommand.prototype.parseScripts_,
+ 'response': DebugCommand.responseScripts_,
+ 'usage': 'scripts',
+ 'while_running': true },
+ 'source': { 'parse': DebugCommand.prototype.parseSource_,
+ 'response': DebugCommand.responseSource_,
+ 'usage': 'source [from line] | [<from line> <num lines>]' },
+ 'step': { 'parse': DebugCommand.prototype.parseStep_,
+ 'usage': 'step' },
+ 'stepout': { 'parse': DebugCommand.prototype.parseStepOut_,
+ 'usage': 'stepout' },
+ // local eval for debugging - remove this later
+ 'shell': { 'parse': DebugCommand.parseShell_,
+ 'usage': 'shell <expression>',
+ 'while_running': true,
+ 'hidden': true },
+ 'shelldebug': { 'parse': DebugCommand.parseShellDebug_,
+ 'usage': 'shelldebug',
+ 'while_running': true,
+ 'hidden': true },
+};
+
+
+/**
+ * Debug shell using the new JSON protocol
+ * @param {Object} tab - which tab is to be debugged. This is an internal
+ * Chrome object.
+ * @constructor
+ */
+function DebugShell(tab) {
+ this.tab = tab;
+ this.tab.attach();
+ this.ready = true;
+ this.running = true;
+ this.current_command = undefined;
+ this.pending_commands = [];
+ // The auto continue flag is used to indicate whether the JavaScript execution
+ // should automatically continue after a break event and the processing of
+ // pending commands. This is used to make it possible for the user to issue
+ // commands, e.g. setting break points, without making an explicit break. In
+ // this case the debugger will silently issue a forced break issue the command
+ // and silently continue afterwards.
+ this.auto_continue = false;
+ this.debug = false;
+ this.current_line = -1;
+ this.current_pos = -1;
+ this.current_frame = 0;
+ this.current_script = undefined;
+ this.scripts = [];
+
+ // Mapping of breakpoints id --> info.
+ // Must use numeric keys.
+ this.breakpoints = [];
+};
+
+DebugShell.prototype.set_ready = function(ready) {
+ if (ready != this.ready) {
+ this.ready = ready;
+ ChromeNode.setDebuggerReady(this.ready);
+ }
+};
+
+DebugShell.prototype.set_running = function(running) {
+ if (running != this.running) {
+ this.running = running;
+ ChromeNode.setDebuggerBreak(!this.running);
+ }
+};
+
+/**
+ * Execute a constructed DebugCommand object if possible, otherwise pend.
+ * @param cmd {DebugCommand} - command to execute
+ */
+DebugShell.prototype.process_command = function(cmd) {
+ dprint("Running: " + (this.running ? "yes" : "no"));
+
+ // The "break" commands needs to be handled seperatly
+ if (cmd.command == "break") {
+ if (this.running) {
+ // Schedule a break.
+ print("Stopping JavaScript execution...");
+ this.tab.debugBreak(false);
+ } else {
+ print("JavaScript execution already stopped.");
+ }
+ return;
+ }
+
+ // If page is running an break needs to be issued.
+ if (this.running) {
+ // Some requests are not valid when the page is running.
+ var cmd_info = DebugCommand.commands[cmd.user_command];
+ if (!cmd_info['while_running']) {
+ print(cmd.user_command + " can only be run while paused");
+ return;
+ }
+
+ // Add the command as pending before scheduling a break.
+ this.pending_commands.push(cmd);
+ dprint("pending command: " + cmd.toJSONProtocol());
+
+ // Schedule a forced break and enable auto continue.
+ this.tab.debugBreak(true);
+ this.auto_continue = true;
+ this.set_ready(false);
+ return;
+ }
+
+ // If waiting for a response add command as pending otherwise send the
+ // command.
+ if (this.current_command) {
+ this.pending_commands.push(cmd);
+ dprint("pending command: " + cmd.toJSONProtocol());
+ } else {
+ this.current_command = cmd;
+ cmd.sendToDebugger(this.tab);
+ this.set_ready(false);
+ }
+};
+
+/**
+ * Handle a break event from the debugger.
+ * @param msg {ResponsePacket} - break_event protocol message to handle
+ */
+DebugShell.prototype.event_break = function(break_event) {
+ this.current_frame = 0;
+ this.set_running(false);
+ var body = break_event.body();
+ if (body) {
+ this.current_script = body.script;
+ var loc = DebugCommand.getSourceLocation(body.script,
+ body.sourceLineText, body.sourceLine, body.invocationText);
+ var location = loc[0];
+ var source = loc[1];
+ this.current_line = loc[2];
+ if (body.breakpoints) {
+ // Always disable auto continue if a real break point is hit.
+ this.auto_continue = false;
+ var breakpoints = body.breakpoints;
+ print("paused at breakpoint " + breakpoints.join(",") + ": " +
+ location);
+ for (var i = 0; i < breakpoints.length; i++)
+ this.didHitBreakpoint(parseInt(breakpoints[i]));
+ } else if (body.scriptData == "") {
+ print("paused");
+ } else {
+ // step, stepout, next, "break" and a "debugger" line in the code
+ // are all treated the same (they're not really distinguishable anyway)
+ if (location != this.last_break_location) {
+ // We only print the location (function + script) when it changes,
+ // so as we step, you only see the source line when you transition
+ // to a new script and/or function. Also if auto continue is enables
+ // don't print the break location.
+ if (!this.auto_continue)
+ print(location);
+ }
+ }
+ // Print th current source line unless auto continue is enabled.
+ if (source && !this.auto_continue)
+ print(source);
+ this.last_break_location = location;
+ }
+ if (!this.auto_continue)
+ this.set_ready(true);
+};
+
+/**
+ * Handle an exception event from the debugger.
+ * @param msg {ResponsePacket} - exception_event protocol message to handle
+ */
+DebugShell.prototype.event_exception = function(exception_event) {
+ this.set_running(false);
+ this.set_ready(true);
+ var body = exception_event.body();
+ if (body) {
+ if (body["uncaught"]) {
+ print("uncaught exception " + body["exception"].text);
+ } else {
+ print("paused at exception " + body["exception"].text);
+ }
+ }
+};
+
+DebugShell.prototype.matchScript = function(script_match, line) {
+ var script = null;
+ // In the v8 debugger, all scripts have a name, line offset and line count
+ // Script names are usually URLs which are a pain to have to type again and
+ // again, so we match the tail end of the script name. This makes it easy
+ // to type break foo.js:23 rather than
+ // http://www.foo.com/bar/baz/quux/test/foo.js:23. In addition to the tail
+ // of the name we also look at the lines the script cover. If there are
+ // several scripts with the same tail including the requested line we match
+ // the first one encountered.
+ // TODO(sgjesse) Find how to handle several matching scripts.
+ var candidate_scripts = [];
+ for (var i in this.scripts) {
+ if (this.scripts[i].name &&
+ this.scripts[i].name.indexOf(script_match) >= 0) {
+ candidate_scripts.push(this.scripts[i]);
+ }
+ }
+ for (var i in candidate_scripts) {
+ var s = candidate_scripts[i];
+ var from = s.lineOffset;
+ var to = from + s.lineCount;
+ if (from <= line && line < to) {
+ script = s;
+ break;
+ }
+ }
+ if (script)
+ return script;
+ else
+ return null;
+}
+
+// The Chrome Subshell interface requires:
+// prompt(), command(), response(), exit() and on_disconnect()
+
+/**
+ * Called by Chrome Shell to get a prompt string to display.
+ */
+DebugShell.prototype.prompt = function() {
+ if (this.current_command)
+ return '';
+ if (!this.running)
+ return 'v8(paused)> ';
+ else
+ return 'v8(running)> ';
+};
+
+/**
+ * Called by Chrome Shell when command input has been received from the user.
+ */
+DebugShell.prototype.command = function(str) {
+ if (this.tab) {
+ str = DebugCommand.trim(str);
+ if (str.length) {
+ var cmd = new DebugCommand(str);
+ cmd.from_user = true;
+ if (cmd.type == "request")
+ this.process_command(cmd);
+ }
+ } else {
+ print(">>not connected to a tab");
+ }
+};
+
+/**
+ * Called by Chrome Shell when a response to a previous command has been
+ * received.
+ * @param {Object} msg Message object.
+ */
+DebugShell.prototype.response = function(msg) {
+ dprint("received: " + (msg && msg.type));
+ var response;
+ try {
+ response = new ProtocolPackage(msg);
+ } catch (error) {
+ print(error.toString(), str);
+ return;
+ }
+ if (response.type() == "event") {
+ ev = response.event();
+ if (ev == "break") {
+ this.event_break(response);
+ } else if (ev == "exception") {
+ this.event_exception(response);
+ }
+ } else if (response.type() == "response") {
+ if (response.requestSeq() != undefined) {
+ if (!this.current_command || this.current_command.seq != response.requestSeq()){
+ throw("received response to unknown command " + str);
+ }
+ } else {
+ // TODO(erikkay): should we reject these when they happen?
+ print(">>no request_seq in response " + str);
+ }
+ var cmd = DebugCommand.commands[this.current_command.user_command]
+ response.command = this.current_command;
+ this.current_command = null
+ this.set_running(response.running());
+ if (!response.success()) {
+ print(response.message());
+ } else {
+ var handler = cmd['response'];
+ if (handler != undefined) {
+ handler.call(this, response);
+ }
+ }
+ this.set_ready(true);
+ }
+
+ // Process next pending command if any.
+ if (this.pending_commands.length) {
+ this.process_command(this.pending_commands.shift());
+ } else if (this.auto_continue) {
+ // If no more pending commands and auto continue is active issue a continue command.
+ this.auto_continue = false;
+ this.process_command(new DebugCommand("continue"));
+ }
+};
+
+/**
+ * Called when a breakpoint has been set.
+ * @param {BreakpointInfo} info - details of breakpoint set.
+ */
+DebugShell.prototype.addedBreakpoint = function(info) {
+ print("set breakpoint #" + info.id);
+ this.breakpoints[info.id] = info;
+}
+
+/**
+ * Called when a breakpoint has been cleared.
+ * @param {int} id - the breakpoint number that was cleared.
+ */
+DebugShell.prototype.clearedBreakpoint = function(id) {
+ assertIsNumberType(id, "clearedBreakpoint called with invalid id");
+
+ print("cleared breakpoint #" + id);
+ delete this.breakpoints[id];
+}
+
+/**
+ * Called when a breakpoint has been reached.
+ * @param {int} id - the breakpoint number that was hit.
+ */
+DebugShell.prototype.didHitBreakpoint = function(id) {
+ assertIsNumberType(id, "didHitBreakpoint called with invalid id");
+
+ var info = this.breakpoints[id];
+ if (!info)
+ throw "Could not find breakpoint #" + id;
+
+ info.hit_count ++;
+}
+
+/**
+ * Print a summary of the specified breakpoints.
+ *
+ * @param {Array<BreakpointInfo>} breakpointsToPrint - List of breakpoints. The
+ * index is unused (id is determined from the info).
+ */
+DebugShell.printBreakpoints_ = function(breakpoints) {
+ // TODO(ericroman): this would look much nicer if we could output as an HTML
+ // table. I tried outputting as formatted text table, but this looks aweful
+ // once it triggers wrapping (which is very likely if the target is a script)
+
+ // Output as a comma separated list of key=value
+ for (var i in breakpoints) {
+ var b = breakpoints[i];
+ var props = ["id", "hit_count", "type", "target", "line", "position",
+ "condition"];
+ var propertyList = [];
+ for (var i = 0; i < props.length; i++) {
+ var prop = props[i];
+ var val = b[prop];
+ if (val != undefined)
+ propertyList.push(prop + "=" + val);
+ }
+ print(propertyList.join(", "));
+ }
+}
+
+/**
+ * Called by Chrome Shell when the outer shell is detaching from debugging
+ * this tab.
+ */
+DebugShell.prototype.exit = function() {
+ if (this.tab) {
+ this.tab.detach();
+ this.tab = null;
+ }
+};
+
+/**
+ * Called by the Chrome Shell when the tab that the shell is debugging
+ * have attached.
+ */
+DebugShell.prototype.on_attach = function(title) {
+ if (!title)
+ title = "Untitled";
+ print('attached to ' + title);
+ // on attach, we update our current script list
+ var cmd = new DebugCommand("scripts");
+ cmd.from_user = false;
+ this.process_command(cmd);
+};
+
+
+/**
+ * Called by the Chrome Shell when the tab that the shell is debugging
+ * went away.
+ */
+DebugShell.prototype.on_disconnect = function() {
+ print(">>lost connection to tab");
+ this.tab = null;
+};
+
+
+/**
+ * Protocol packages send from the debugger.
+ * @param {string} json - raw protocol packet as JSON string.
+ * @constructor
+ */
+function ProtocolPackage(msg) {
+ this.packet_ = msg;
+ this.refs_ = [];
+ if (this.packet_.refs) {
+ for (var i = 0; i < this.packet_.refs.length; i++) {
+ this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i];
+ }
+ }
+}
+
+
+/**
+ * Get the packet type.
+ * @return {String} the packet type
+ */
+ProtocolPackage.prototype.type = function() {
+ return this.packet_.type;
+}
+
+
+/**
+ * Get the packet event.
+ * @return {Object} the packet event
+ */
+ProtocolPackage.prototype.event = function() {
+ return this.packet_.event;
+}
+
+
+/**
+ * Get the packet request sequence.
+ * @return {number} the packet request sequence
+ */
+ProtocolPackage.prototype.requestSeq = function() {
+ return this.packet_.request_seq;
+}
+
+
+/**
+ * Get the packet request sequence.
+ * @return {number} the packet request sequence
+ */
+ProtocolPackage.prototype.running = function() {
+ return this.packet_.running ? true : false;
+}
+
+
+ProtocolPackage.prototype.success = function() {
+ return this.packet_.success ? true : false;
+}
+
+
+ProtocolPackage.prototype.message = function() {
+ return this.packet_.message;
+}
+
+
+ProtocolPackage.prototype.body = function() {
+ return this.packet_.body;
+}
+
+
+ProtocolPackage.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
+
+/**
+ * Structure that holds the details about a breakpoint.
+ * @constructor
+ *
+ * @param {int} id - breakpoint number
+ * @param {string} type - "script" or "function"
+ * @param {string} target - either a function name, or script url
+ * @param {int} line - line number in the script, or undefined
+ * @param {int} position - column in the script, or undefined
+ * @param {string} condition - boolean expression, or undefined
+ */
+function BreakpointInfo(id, type, target, line, position, condition) {
+ this.id = id;
+ this.type = type;
+ this.target = target;
+
+ if (line != undefined)
+ this.line = line;
+ if (position != undefined)
+ this.position = position;
+ if (condition != undefined)
+ this.condition = condition;
+
+ this.hit_count = 0;
+
+ // Check that the id is numeric, otherwise will run into problems later
+ assertIsNumberType(this.id, "id is not a number");
+}
+
+var shell_ = null;
+DebugShell.initDebugShell = function(debuggerUI) {
+ if (!DebugShell.singleton) {
+ DebugShell.ui = debuggerUI;
+ DebugShell.singleton = new DebugShell(TabNode);
+ shell_ = DebugShell.singleton;
+
+ // enable debug output
+ //shell_.debug = true;
+ }
+};
+
+/**
+ * Print debugging message when DebugShell's debug flag is true.
+ */
+function dprint(str) {
+ if (shell_ && shell_.debug) {
+ print(str);
+ }
+};
+
+/**
+ * Helper that throws error if x is not a number
+ * @param x {object} - object to test type of
+ * @param error_message {string} - error to throw on failure
+ */
+function assertIsNumberType(x, error_message) {
+ if (typeof x != "number")
+ throw error_message;
+}
+
+////////////////////// migration staff //////////////////////////
+// This file was copied from chrome\browser\debugger\resources\debugger_shell.js
+
+function print(txt) {
+ var ui = DebugShell.ui;
+ if (ui) {
+ ui.appendText(txt);
+ }
+}
+
+var TabNode = {
+ debugBreak: function(force) {
+ DebuggerIPC.sendMessage(["debugBreak", force]);
+ },
+ attach: function() {
+ DebuggerIPC.sendMessage(["attach"]);
+ },
+ detach: function() {
+ // TODO(yurys): send this from DebugHandler when it's being destroyed?
+ DebuggerIPC.sendMessage(["detach"]);
+ },
+ sendToDebugger: function(str) {
+ DebuggerIPC.sendMessage(["sendToDebugger", str]);
+ }
+};
+
+var ChromeNode = {
+ setDebuggerReady: function(isReady) {
+ DebuggerIPC.sendMessage(["setDebuggerReady", isReady]);
+ },
+ setDebuggerBreak: function(isBreak) {
+ var ui = DebugShell.ui;
+ if (ui) {
+ ui.setDebuggerBreak(isBreak);
+ }
+ DebuggerIPC.sendMessage(["setDebuggerBreak", isBreak]);
+ }
+};
diff --git a/webkit/inspector/README b/webkit/inspector/README
new file mode 100644
index 0000000..c4ccbe6
--- /dev/null
+++ b/webkit/inspector/README
@@ -0,0 +1,11 @@
+This directory contains Inspector front-end files that are currently forked from
+the implementation upstream.
+
+Our intent is to upstream or unfork these as soon as possible. For all intents
+and purposes, we consider the presence of this directory a ghastly hack that we
+all regret but have to live with to move forward.
+
+Please do not add more files to this directory as this will aggravate the pain
+and suffering already inflicted by this forked state. If you need to make
+changes to this directory, please consider spending your energy on upstreaming
+or unforking them instead.
diff --git a/webkit/inspector/debugger.css b/webkit/inspector/debugger.css
new file mode 100644
index 0000000..ed9df2f
--- /dev/null
+++ b/webkit/inspector/debugger.css
@@ -0,0 +1,35 @@
+/**
+ * Style for javascript debugger. See debugger.html.
+ */
+
+html,body {
+ margin: 0px;
+ padding: 0px;
+ height: 100%;
+}
+#output {
+ font-family: monospace;
+ background-color: #ffffff;
+ min-height: 100%;
+}
+#outer {
+ height: 100%;
+ width: 100%;
+ white-space: pre-wrap;
+ padding: 0px 0px 24px 0px;
+}
+#command-line {
+ bottom: 0px;
+ /* not quite sure why this 5px is necessary */
+ right: 5px;
+ left: 0px;
+ position: fixed;
+ padding: 0px;
+ margin: 0px;
+}
+#command-line-text {
+ height: 20px;
+ display: block;
+ width: 100%;
+ font-family: monospace;
+}
diff --git a/webkit/inspector/debugger.html b/webkit/inspector/debugger.html
new file mode 100644
index 0000000..f6bb917
--- /dev/null
+++ b/webkit/inspector/debugger.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<!--
+ The UI for the javascript debugger window.
+-->
+ <head>
+ <title>JavaScript Debugger</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF8" />
+ <link rel="stylesheet" href="debugger.css" type="text/css" />
+ <script type="text/javascript" src="DebuggerConsole.js"></script>
+ <script type="text/javascript" src="DebuggerIPC.js"></script>
+ <script type="text/javascript" src="DebuggerShell.js"></script>
+ </head>
+
+ <body onload="onLoad();">
+
+ <table id='outer'>
+ <tr>
+ <td valign='bottom' id='output'>Chrome JavaScript Debugger<br />Type 'help' for a list of commands.<br /></td>
+ </tr>
+ </table>
+
+ <div id='command-line'>
+ <!-- TODO(erikkay) - use addEventListener instead -->
+ <input id='command-line-text'
+ type="text" />
+ </div>
+
+ </body>
+</html>