Firefox devtools - non-intrusive way to add debug code

2016-06-09 / Developer tools, Mozilla

The other day, I was working on a rather complex bug on This site overlays a weather radar animation on top of a Google Maps-based map.

The problem was that when the animation has been left open for a while, one of the “frames” of the animation will be stuck in the first state from whenever you opened the map.

The site renders radar data with layers of 256x256 px big CANVAS elements, about 6x6 elements in each layer. Layers are hidden with opacity:0 and one at a time is shown from an interval.

There’s a lot of JavaScript (including of course the Google maps API scripts and ads), in various stages of compression and obfuscation.

Since my first attempt at understanding the architecture was looking at the DOM in the inspector I spent quite some time digging around among the hundreds of CANVAS elements trying to figure out how they were used and which ones were part of the layer with outdated data.

Eventually I realised that the site loads radar graphic PNG URLs with embedded timestamp and coordinate data, like this one (where the ‘66’ and ‘94’ have something to do with coordinates of the square in a grid):

These images are painted into the corresponding CANVAS elements.

Each 6 minutes, the site decides to load a new set of graphics. While doing so, it will destroy all existing CANVAS tiles to re-add and repaint them. This quirk made for a rather annoying debugging experience - every once in a while when I was digging around in the DOM the whole thing would get reset. If I had added properties or IDs to CANVAS elements and their parents to be able to locate them more easily, those would go away too.

Searching for “radar” in the JavaScript debugger was a lucky shortcut to wundermap.min.js - quite a lot of code there seemed relevant. Also, there are fragments of debug code in the source:

Screenshot of code with a p._debug if statement highlighted

Within the JavaScript debugger in Firefox devtools, you can’t edit this code. But there’s a neat hack that lets you switch this._debug to true in this source anyway: add a conditional breakpoint that does it. Code to evaluate the condition of a conditional breakpoint will run in the scope of that line. Here’s an animation of how I triggered the site’s CanvasMapType debug code - the void() statement will return undefined and make sure the debugger does not stop at the breakpoint, and p is a reference to this:

Animation of adding a conditional breakpoint setting p._debug to true

Further down in the same method, it’s evident that enabling debugging will add some more information to the DOM. What I’m most interested in here is what URLs are used to paint each CANVAS element. There’s an l variable which refers to the URL - what if I set another conditional breakpoint just after the line where the site added its own debug data to the page? void(b.innerHTML += ‘ ‘ + l) might do:

Screenshot of debugger with a breakpoint whose condition sets innerHTML

This let me see the URLs that were used for each CANVAS element right there in the page itself:

Screenshot of site when the debug code has changed the rendering

And this led to the breakthrough: the URLs shown for the CANVAS elements that “jump back in time” have a different format than the others. These URLs do not have embedded timestamp data - they look like this:

My guess is that these images without any embedded time stamp are supposed to get the “current” state of things. If the browser decides to cache and not revalidate those images, the old graphics will be painted into the CANVAS elements when the JavaScript wants to show “now” state.

The site seems to leave such caching decisions up to the browser - it sends Last-Modified and ETag, but no Cache-Control. I am no expert on the caching side of HTTP though. Perhaps we should revalidate each time the URL is required? Perhaps not? At least now we know why it happens - and can draw a few lessons for debugging too:

  • It can be surprisingly useful to trigger inert debug code in websites. Several years ago, I had a hack replacing all instances of //\salert/( with just “alert(“ on all sites, and it could be quite entertaining
  • Avoid trying to inspect 500 CANVAS elements unless you really have to..
  • Using conditional breakpoints to “inject” code and change script behaviour can be very useful

If you didn’t know about this trick yet, I hope you’ll start using it and find it useful.