YSlow/Chrome hacking

March 24th, 2011. Tagged: performance, tools, yslow

If you haven't seen it yet, YSlow for Chrome hit the streets couple of weeks ago. (And Google's own PageSpeed did too yesterday. (And there's now DynaTrace for Firefox. (And WebPageTest for Chrome. (What a month for x-browsering (word?) the performance tools! (And the month's not even over yet)))))

BTW, I closed all the parentheses (or else...).

So anyway, I was eavesdropping on a twitter conversation where Sergey (of ShowSlow) was asking for beacons from YSlow for Chrome, more specifically - when will they start working. I thought I should check how my old baby YSlow 2.0 (this presentation is still pretty relevant) is doing in its new environment.

YSlow 2.0

In YSlow 2.0 things are pretty decoupled. Makes it easier to bring to any possible environment or browser. So rules are rules (you can add, remove, tweak them, combine them into rulesets), results are results, presentation is separate, and so are the additional tools, HAR import/export (forthcoming), etc. Only (ideally) small additions are needed to glue the core of YSlow (the linting part) with a new environment.

In Chrome

It's my first time touching anything Chrome-y, but turned out its pretty easy. Just a bit of file system hunting revealed where code for the extension goes.

/Users/[USERNAME]/Library/Application Support/Google/Chrome/Default/Extensions/[WEIRD-EXTENSION-ID]/

e.g.

/Users/stoyanstefanov/Library/Application Support/Google/Chrome/Default/Extensions/ninejjcohidippngpapiilnmkgllmakh/

On Windows:

C:\Documents and Settings\[USERNAME]\Local Settings\Application Data\Google\Chrome\User Data\Default\Extensions\ninejjcohidippngpapiilnmkgllmakh\

In there you can find three JavaScript files. I could be wrong but here's what I think goes in there:

  1. yslow-chrome.js looks like it contains the reusable parts - rules, etc - packaged for Chrome minus the Firefox stuff and bundled into a single file
  2. content.js is small and not too exciting
  3. controller.js is the Chrome-related parts

In controller.js is where we hack.

YSlow events

In YSlow 2.0 we decided to make use of a simple observer pattern implementation and fire events whenever interesting stuff happens. Especially useful since figuring all the data for all resources better be asynchronous.

Once yslow "peels" the page figuring out the components, gets each component data, headers, etc, then runs the lint rules, finally it fires a lintResultsReady event.

("Peeling a page" I heard for the first time from Steve Souders and for what I know he should be credited with this term describing the activity of figuring all components that go into a page)

All we need to do is listen to this event and send the beacon.

YSLOW.util.event.addListener(
  'lintResultReady', 
  function (o) {
    //...b-b-beacon! ...
  }
);

There's a YSLOW.util.sendBeacon() which does precisely that, so we need to call it and we're done.

Preferences

Firefox has a built-in (native) system to manage preferences. You know, the stuff you tweak in about:config. This is where we put the preferences - beacon yes/no, beacon URL, beacon data verbosity.

In Chrome such native preference system probably exists, but YSlow is currently not taking advantage. (Just guessing here.)

Luckily all calls to get preferences are abstracted in YSLOW.util.Preferences.getPref(prefname, defaultvalue). The default value is returned if a better one cannot be found.

So we can just overwrite the getPref() method to return the default value, unless it's a preference we care about, such as the beacon URL:

YSLOW.util.Preference.getPref = function(what, defaultval) {
  switch (what) {
    case 'beaconUrl':
      return 'https://www.phpied.com/beacon.png';
    case 'beaconInfo':
      return 'all'; // or "basic"
    default:
      return defaultval;
  }
};

Integration

As mentioned we'll hack into the controller.js, we don't want to touch the yslow core stuff. The controller.js is just one immediate function and our hack goes right before the last line. (Or even after it, probably doesn't matter)

(function () {
    // ... yslowy stuff ...
 
    // hack start
    // ...
    // hack end
 
    doc.ysview.setSplashView(...
}());

The complete thing is something like:

(function () {
 
    // ... slo, slo ...
 
    // hack start
    YSLOW.util.Preference.getPref = function(what, defaultval) {
      switch (what) {
        case 'beaconUrl':
          return 'https://www.phpied.com/beacon.png';
        case 'beaconInfo':
          return 'all'; // or "basic"
        default:
          return defaultval;
      }
    };
 
    YSLOW.util.event.addListener('lintResultReady', function (o) {
      var con = o.yslowContext,
          result = con.result_set;
      YSLOW.util.sendBeacon(result.url, result.overall_score, con);
    });
    // hack end
 
    doc.ysview.setSplashView(...
}());

Conclusion

So there - you can run YSlow in Chrome and send yourself (or showslow.com) beacons.

You probably don't need that so bad that you can't wait till next YSlow for Chrome ships with this thing working. But here it is.

And hopefully you learned a bit about YSlow internals so that you can start hacking yourself and/or wait till YSlow shows up on github (soon!) and start sending diffs. I personally can't wait.

Shoutout goes out to Marcel and Betty who are doing awesome stuff with YSlow (slides). And looks like even more is to come!

Comments? Feedback? Find me on Twitter, Mastodon, Bluesky, LinkedIn, Threads