Getters and setters as debugging shortcuts

Webcompat.com’s issue 1449 was a fun little challenge this morning. So the site thetiebar.com runs some JavaScript to adapt to the device’s screen size and resolution. One of the things it does is to select images that fit the available space.

This is not a bad idea - the problem is that in Firefox on Android, it tends to end up with an enlarged 24px wide image, which looks rather poor. Certainly doesn’t look like a tie you might want to buy.

Image of a tie poorly rendered on thetiebar.com

So what is taking place here, and is it the site’s or Firefox’s fault?

We knew the generated markup was basically like this:

    <img class="tbs-slider-img" 
     src="24234sk.jpg?w=24" 
     data-retina="24234sk.jpg?w=auto">

so the likely challenge is simply to find and review the script that reads the data-retina attribute and replaces the w=auto part with an actual measurement.

Easy? Not quite. Obvious shortcuts like searching for “retina” didn’t find interesting code. Some of the JavaScript was “packed” in rather obfuscated ways, but still.. surprising how hard it was to find this stuff. Keywords like src, w or auto are too common and useless for search fodder.

Now, we know the code we want to look at sets src on an image. What if we can just add a breakpoint when .src is set?

I used a HTTP debugging proxy to insert a SCRIPT element with this code at the top of the main page before it was sent to the browser:

    HTMLImageElement.prototype.__defineSetter__('src', function(val){debugger;});

And voila - during loading it hits the debugger statement a few times, first for images with ?h=auto and then finally for some with ?w=auto.

(If you’re curious, the problem is that the JavaScript makes the assumption that an image element with greater than zero width is filling the available space, while Firefox makes the image element take up 24px by default - perhaps for alt text or an error icon or something?)

Such getters and setters tricks can be excellent shortcuts when you need to find a specific part of a foreign code base. You can even define a non-enumerable getter or setter that can trigger a breakpoint when a named property is set on any object defined by a site. Here’s one I used for debugging another really complex issue recently:

    Object.defineProperty(Object.prototype, 
        'top', 
        {enumerable:false, set: function(value){ 
            if(parseInt(value)<0)debugger; 
        }}
    );

This lets the debugger break on code like

    var obj = {};
    obj.top = '-20px';

So if you’re looking for somewhere a specific property is being set to a characteristic value, you now know the fastest way to get there in your JavaScript debugger.