| // Copyright 2015 The Cobalt Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| function DebuggerClient() { | |
| this.DEBUGGER_DETACHED = 0; | |
| this.DEBUGGER_ATTACHING = 1; | |
| this.DEBUGGER_ATTACHED = 2; | |
| this.scripts = []; | |
| this.attachState = this.DEBUGGER_DETACHED; | |
| this.onAttachCallback = this.onAttach.bind(this); | |
| this.onEventCallback = this.onEvent.bind(this); | |
| this.executionContext = undefined; | |
| } | |
| // Attaches to the debugger and listens for debug events. | |
| // Enables the domains we care about here. | |
| DebuggerClient.prototype.attach = function() { | |
| if (this.attachState == this.DEBUGGER_DETACHED) { | |
| this.attachState = this.DEBUGGER_ATTACHING; | |
| printToMessageLog(MessageLog.INTERACTIVE, | |
| 'Attempting to attach to debugger...'); | |
| this.scripts = []; | |
| debugHub.onEvent.addListener(this.onEventCallback); | |
| debugHub.attach(this.onAttachCallback); | |
| this.sendCommand('Console.enable'); | |
| this.sendCommand('Log.enable'); | |
| this.sendCommand('Runtime.enable'); | |
| } else if (this.attachState == this.DEBUGGER_ATTACHING) { | |
| printToMessageLog(MessageLog.INTERACTIVE, | |
| 'Still attempting to attach to debugger...'); | |
| } | |
| } | |
| // Local method to list the parsed scripts the client has been notifed of | |
| // via the |Debugger.scriptParsed| event. Maps the possibly very long script id | |
| // from the debug dispatcher to a more human-readable 0-based index. | |
| DebuggerClient.prototype.getScripts = function(scriptId) { | |
| for (var i in this.scripts) { | |
| var index = this.pad(i, 3); | |
| var scriptUrl = this.scripts[i].url; | |
| printToMessageLog(MessageLog.INTERACTIVE, index + ': ' + scriptUrl); | |
| } | |
| } | |
| //--- Commands. | |
| // Each debugger command has an associated callback to get the result. | |
| DebuggerClient.prototype.getScriptSource = function(scriptId) { | |
| // If the id looks like an index into the local script array, look it up there. | |
| if (scriptId >= 0 && scriptId < this.scripts.length) { | |
| scriptId = this.scripts[scriptId].scriptId; | |
| } | |
| var method = 'Debugger.getScriptSource'; | |
| var params = { 'scriptId': scriptId.toString() }; | |
| var callback = this.getScriptSourceCallback.bind(this); | |
| this.sendCommand(method, params, callback); | |
| } | |
| DebuggerClient.prototype.getScriptSourceCallback = function(result) { | |
| var scriptSource = result.scriptSource; | |
| var lines = scriptSource.split('\n'); | |
| for (var i = 0; i < lines.length; i++) { | |
| var index = this.pad(i + 1, 4); | |
| printToMessageLog(MessageLog.INFO, index + ': ' + lines[i]); | |
| } | |
| } | |
| DebuggerClient.prototype.evaluate = function(expression, callback) { | |
| var method = 'Runtime.evaluate'; | |
| var params = {}; | |
| params.contextId = this.executionContext; | |
| params.expression = expression; | |
| params.generatePreview = true; | |
| params.includeCommandLineAPI = true; | |
| params.objectGroup = 'console'; | |
| params.returnByValue = false; | |
| this.sendCommand(method, params, callback); | |
| } | |
| // All debugger commands are routed through this method. Converts the command | |
| // parameters into a JSON string to pass to the debug dispatcher. | |
| DebuggerClient.prototype.sendCommand = function(method, commandParams, | |
| callback) { | |
| var jsonParams = JSON.stringify(commandParams); | |
| var responseCallback = this.responseCallback.bind(this, method, callback); | |
| debugHub.sendCommand(method, jsonParams, responseCallback); | |
| } | |
| // All command responses are routed through this method. Parses the JSON | |
| // response from the debug dispatcher, checks for errors and passes on to the | |
| // command-specific callback to handle the result. | |
| DebuggerClient.prototype.responseCallback = function(method, callback, | |
| responseString) { | |
| var response = JSON.parse(responseString); | |
| if (response && response.error) { | |
| printToMessageLog( | |
| MessageLog.ERROR, | |
| '[ERROR(' + response.error.code + '):' + method + '] ' + | |
| response.error.message); | |
| } else if (callback) { | |
| if (response) { | |
| callback(response.result); | |
| } else { | |
| callback(null); | |
| } | |
| } | |
| } | |
| //-- Events. | |
| DebuggerClient.prototype.onAttach = function() { | |
| if (debugHub.lastError) { | |
| printToMessageLog(MessageLog.WARNING, 'Could not attach to debugger.'); | |
| this.attachState = this.DEBUGGER_DETACHED; | |
| } else { | |
| printToMessageLog(MessageLog.INTERACTIVE, 'Debugger attached.'); | |
| this.attachState = this.DEBUGGER_ATTACHED; | |
| } | |
| } | |
| // All events generated by the debug dispatcher are routed through this method. | |
| // Parses the JSON string and passes on to the appropriate handler according to | |
| // the method name. | |
| DebuggerClient.prototype.onEvent = function(method, paramString) { | |
| var params = JSON.parse(paramString); | |
| if (method == 'Console.messageAdded') { | |
| this.onConsoleMessageAdded(params) | |
| } else if (method == 'Debugger.scriptParsed') { | |
| this.onScriptParsed(params); | |
| } else if (method == 'Inspector.detached') { | |
| this.onDetached(params); | |
| } else if (method == 'Log.browserEntryAdded' || method == 'Log.entryAdded') { | |
| this.onLogEntryAdded(params); | |
| } else if (method == 'Runtime.consoleAPICalled') { | |
| this.onConsoleApiCalled(params); | |
| } else if (method == 'Runtime.executionContextCreated') { | |
| this.onExecutionContextCreated(params); | |
| } | |
| } | |
| DebuggerClient.prototype.onDetached = function() { | |
| printToMessageLog(MessageLog.INTERACTIVE, 'Debugger detached.'); | |
| this.attachState = this.DEBUGGER_DETACHED; | |
| } | |
| DebuggerClient.prototype.onExecutionContextCreated = function(params) { | |
| this.executionContext = params.context.id; | |
| printToMessageLog(MessageLog.INFO, | |
| 'Execution context created: ' + this.executionContext); | |
| } | |
| DebuggerClient.prototype.onLogEntryAdded = function(params) { | |
| printToMessageLog(params.entry.level, params.entry.text); | |
| } | |
| DebuggerClient.prototype.onConsoleMessageAdded = function(params) { | |
| // Translate Console.messageAdded params to Runtime.consoleAPICalled params. | |
| var consoleApiParams = { | |
| type: params.message.level, | |
| args: [ { type: 'string', value: params.message.text } ] | |
| }; | |
| this.onConsoleApiCalled(consoleApiParams); | |
| } | |
| DebuggerClient.prototype.onConsoleApiCalled = function(params) { | |
| var severity = params.type; | |
| if (severity === "assert") { | |
| severity = MessageLog.ERROR; | |
| } else if (severity === "log") { | |
| severity = MessageLog.INFO; | |
| } | |
| var message = ''; | |
| if (params.args.length) { | |
| var arg = params.args[0]; | |
| if (arg.unserializableValue) { | |
| message = arg.unserializableValue; | |
| } else if (typeof arg.value !== 'object' || arg.value === null) { | |
| message = arg.value + ''; | |
| } else if (arg.description) { | |
| message = arg.description; | |
| } | |
| } | |
| printToMessageLog(MessageLog.CONSOLE + severity, message); | |
| } | |
| DebuggerClient.prototype.onScriptParsed = function(params) { | |
| this.scripts.push(params); | |
| } | |
| //--- Utils. | |
| DebuggerClient.prototype.pad = function(number, minLength) { | |
| var result = number.toString(); | |
| while (result.length < minLength) { | |
| result = ' ' + result; | |
| } | |
| return result; | |
| } |