|Author:||Patrick Mueller - [blog] [web]|
2010-02-02 - updated Crossfire link|
2010-02-01 - initial write-up
(Note that some areas of the image above have been redacted in white, as they contained host names / ip addresses)
This is a baling-wire-and-duct-tape effort to get a version of the Web Inspector debugger that ships with WebKit to run targeting a remote browser. In this case, an iPhone. Well, the picture shows the iPhone simulator, but this actually runs on my iPod Touch also.
You're looking at three programs running, each potentially on a separate machine:
the debug client - on the upper left is Safari running on Windows (in VNC), loaded with the Web Inspector code running as a "web app"
the debug target - on the right is the iPhone Simulator running as a Mac app, running a very simple page which responds to the "click me" link by writing some text to the console.
the debug agent - on the bottom is a web server running in Python in a green Terminal window that is talking to both of the other programs, acting as a switchboard.
Technically, this effort is less functional that Joe Hewitt's "Firebug for iPhone" code. Namely, the REPL for the console is non-operational; the only thing that works is console.log() invocations being forwarded to Web Inspector's console. (Note however, I wasn't able to get Joe's code linked to in the blog post to run correctly.) The big upside to this hack over "Firebug for iPhone" is that this is running with the current Web Inspector code, unchanged, but with some of "overridden" function added.
Basically what's happening is that the two web apps - the debug target and the debug client - are communicating via XHR to the debug agent, which is a Python program; it's acting as an HTTP server (obviously). When one of the web apps wants to send a message to the other web app, it sends it via XHR to the agent, which forwards it to the other web app.
Both web apps also poll the agent to wait for a command from the other web app. Because I was too lazy to figure out how to run the Python server threaded, this isn't using a "long poll", but a "short/frequent" poll. Survivable, for the moment, but this has to change. You can see the frequent polling in the debug agent Terminal window - each line is the terminal output is an HTTP request.
Also note that the debug agent also serves up the Web Inspector code (HTML/CSS/JS) to the debug client, and so the XHRs between the two are same-origin. However, it's a pain to have the debug target have to use same-origin for the code being debugged (your app), as well as for talking to the debug agent. For this linkage, I took advantage of Mobile Safari's CORS support so the debug agent doesn't have to be same origin as the code being debugged.
This is literally baby steps; there's lots of work to do here.
Here's the code which makes up the debug target, which is the code being debugged. Note that the idea here is that this is actually the application being debugged, so I've highlighted the extra code needed to enable the debugging.
Notes on the highlighted lines above:
1 - This line sets the base URL for the debug agent, used by the code in the remwi-*.js files.
2 - Mobile Safari doesn't include the JSON object! And my code makes use of JSON, so I pulled in Crockford's version.
3 - These contain the code which provides the linkages to the debug agent; the remwi-common.js file contains code used by both the debug target (this file) and the debug client.
4 - The lines make calls to the standard console.log() function, which in this case is overridden by the remwi-target.js file.
The debug client is literally the Web Inspector code as shipped with WebKit nightlies, with some additional scripts added to override bits of functionality here and there.
Because Web Inspector needs to support Chrome's multi-process architecture, it turns out that a lot of the interaction between the debug client and the target has been spec'd out as a set of functional interfaces. Which makes an effort like this a lot easier.
Since we've got the Web Inspector code running, kind of, it's easy to look at the function it provides and wonder about how we could get that to work with this technique. Let's go panel-by-panel:
At least some bits of the Storage pane seem possible to implement, but will require overriding the existing APIs. For instance, for the SQL databases, there's no API to determine the name of the databases being used. The existing APIs could be wrappered so that you could at least get the names of the databases your code is accessing.
Here's the panel-by-panel breakdown of what we can't easily do with this technique:
But see below for thoughts on instrumenting existing libraries to generate some Timeline-ish information.
So while you might not be able to step through lines of a function, you could probably have breakpoints just before a function is called, and just after it returns (or throws an exception). Might be good enough.
You could also perhaps generate some information that could be used by the Profiles pane.
It's not a stretch to imagine library-specific extensions for debuggers; libraries/frameworks like Dojo, Closure, and CommonJS seem like they'd be amenable, technically, to being instrumented to provide information to the debugger. They could, for instance, provide the list of class/modules loaded, the relationships between the classes/modules, the contents of them, etc.
Many of these libraries also provide various eventing capabilities, which could also be instrumented to provide information like the Timeline panel does.
At the end of the day, you don't want to have to run something like the debug agent I've written, nor do you want to have Web Inspector running as a web page, since it already runs fine within WebKit-enabled browsers. The debug agent needs to be embedded into WebKit, and the current Web Inspector needs to become able to connect to (or accept connections from) a remote browser.
Presumably, if this is all in WebKit, all of the panels, including the Scripts panel, can be supported, for any debug targets that include the relevant support.