Debugger Client API: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
Besides the low-level [https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Guide JS Debugger API], there is a debugger client module that allows one to write an application that uses the [https://wiki.mozilla.org/Remote_Debugging_Protocol Remote Debugging Protocol] to drive the debugger. | |||
<pre>/* | <pre>/* | ||
* Debugger API demo. | * Debugger API demo. | ||
| Line 187: | Line 188: | ||
</pre> | </pre> | ||
<br> | <br> See [http://past.pastebin.mozilla.org/1376464 here] for the full example program. | ||
See [http://past.pastebin.mozilla.org/1376464 here] for the full example program. | |||
Revision as of 16:34, 14 November 2011
Besides the low-level JS Debugger API, there is a debugger client module that allows one to write an application that uses the Remote Debugging Protocol to drive the debugger.
/*
* Debugger API demo.
* Try it in Scratchpad with Environment -> Browser, using
* http://htmlpad.org/debugger/ as the current page.
*/
let client;
let threadClient;
function startDebugger()
{
// Start the server.
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
// Listen to an nsIPipe
let transport = DebuggerServer.connectPipe();
// For an nsIServerSocket we do this:
// DebuggerServer.openListener(aPort, aLocalOnly);
// ...and this at the client:
// let transport = debuggerSocketConnect(aHost, aPort);
// Start the client.
client = new DebuggerClient(transport);
// Attach listeners for client events.
client.addListener("tabNavigated", onTab);
client.addListener("newScript", fooListener);
client.ready(function(aType, aTraits) {
// Now the client is conected to the server.
debugTab();
});
}
function shutdownDebugger()
{
client.close();
}
/**
* Start debugging the current tab.
*/
function debugTab()
{
// Get the list of tabs to find the one to attach to.
client.listTabs(function(aResponse) {
// Find the active tab.
let tab = aResponse.tabs[aResponse.selected];
// Attach to the tab.
client.attachTab(tab.actor, function(aResponse, aTabClient) {
if (!aTabClient) return;
// Attach to the thread (context).
client.attachThread(aResponse.threadActor, function(aResponse, aThreadClient) {
if (!aThreadClient) return;
threadClient = aThreadClient;
// Attach listeners for thread events.
threadClient.addListener("paused", onPause);
threadClient.addListener("resumed", fooListener);
threadClient.addListener("detached", fooListener);
threadClient.addListener("framesadded", onFrames);
threadClient.addListener("framescleared", fooListener);
threadClient.addListener("scriptsadded", onScripts);
threadClient.addListener("scriptscleared", fooListener);
// Resume the thread.
threadClient.resume();
// Debugger is now ready and debuggee is running.
});
});
});
}
/**
* Handler for location changes.
*/
function onTab()
{
// Detach from the previous thread.
client.activeThread.detach(function() {
// Detach from the previous tab.
client.activeTab.detach(function() {
// Start debugging the new tab.
debugTab();
});
});
}
/**
* Handler for entering pause state.
*/
function onPause()
{
// Get the top 20 frames in the server's frame stack cache.
client.activeThread.fillFrames(20);
// Get the scripts loaded in the server's source script cache.
client.activeThread.fillScripts();
}
/**
* Handler for framesadded events.
*/
function onFrames()
{
// Get the list of frames in the server.
for each (let frame in client.activeThread.cachedFrames) {
// frame is a Debugger.Frame grip.
dump("frame: " + frame.toSource() + "\n");
inspectFrame(frame);
}
}
/**
* Handler for scriptsadded events.
*/
function onScripts()
{
// Get the list of scripts in the server.
for each (let script in client.activeThread.cachedScripts) {
// script is a Debugger.Script grip.
dump("script: " + script.toSource() + "\n");
}
// Resume execution, since this is the last thing going on in the paused
// state and there is no UI in this program. Wait a bit so that object
// inspection has a chance to finish.
setTimeout(function() {
threadClient.resume();
}, 1000);
}
/**
* Helper function to inspect the provided frame.
*/
function inspectFrame(aFrame)
{
// Get the "this" object.
if (aFrame["this"]) {
getObjectProperties(aFrame["this"]);
}
// Add "arguments".
if (aFrame.arguments && aFrame.arguments.length > 0) {
// frame.arguments is a regular Array.
dump("frame.arguments: " + aFrame.arguments.toSource() + "\n");
// Add variables for every argument.
let objClient = client.activeThread.pauseGrip(aFrame.callee);
objClient.nameAndParameters(function(aResponse) {
for (let i = 0; i < aResponse.parameters.length; i++) {
let name = aResponse.parameters[i];
let value = aFrame.arguments[i];
if (typeof value == "object" && value.type == "object") {
getObjectProperties(value);
}
}
});
}
}
/**
* Helper function that retrieves the specified object's properties.
*/
function getObjectProperties(aObject)
{
let thisClient = client.activeThread.pauseGrip(aObject);
thisClient.prototypeAndProperties(function(aResponse) {
// Get prototype as a protocol-specified grip.
if (aResponse.prototype.type != "null") {
dump("__proto__: " + aResponse.prototype.toSource() + "\n");
}
// Get the rest of the object's own properties as protocol-specified grips.
for each (let prop in Object.keys(aResponse.ownProperties)) {
dump(prop + ": " + aResponse.ownProperties[prop].toSource() + "\n");
}
});
}
/**
* Generic event listener.
*/
function fooListener(aEvent)
{
dump(aEvent + "\n");
}
// Run the program.
startDebugger();
// Execute the following line to stop the program.
//shutdownDebugger();
See here for the full example program.