C3PO: Common 3rd-party objects

February 18th, 2013. Tagged: facebook, JavaScript, performance

Problem: too much JavaScript in your page to handle 3rd party widgets (e.g. Like buttons)
Possible solution: a common piece of JavaScript to handle all third parties' needs


What JavaScript?

If you've read the previous post, you see that the most features in a third party widget are possible only if you inject JavaScript from the third party provider into your page. Having "a secret agent" on your page, the provider can take care of problems such as appropriately resizing the widget.

Why is this a problem?

Third party scripts can be a SPOF (an outage), unless you load them asynchronously. They can block onload, unless the provider lets you load it in an iframe (and most don't). There can be security implications because you're hosting the script in your page with all permissions associated with that. And in any case, it's just too much JavaScript for the browser to parse and execute (think of mobile devices)

If you include the most common Like, Tweet and +1 buttons and throw in Disqus comments, you're looking at well over 100K (minified, gzipped) worth of JavaScript (wpt for this jsbin)

This is more than the whole of jQuery, which previous experiments show can take the noticeable 200ms just to parse and evaluate (assuming it's cached) on an iPhone or Android.

What does all of this JS do?

The JavaScript used by third parties is not always all about social widgets. The JS also provides API call utilities, other dialogs and so on. But the tasks related to social widgets are:

  1. Find html tags that say "there be widget here!" and insert an iframe at that location, pointing to a URL hosted by the third party
  2. Listen to requests from the new iframes fulfill these requests. The most common request is "resize me, please"

Now, creating an iframe and resizing it doesn't sound like much, right? But every provider has to do it over and over again. It's just a wasted code duplication that the browser has to deal with.

Can't we just not duplicate this JavaScript? Can we have a common library that can take care of all widgets there are?

C3PO draft

Here's a demo page of what I have in mind. The page is loading third party widgets: like, tweet, +1 and another one I created just for illustration of the messaging part.

It has a possible solution I drafted as the c3po object. View source, the JS is inline.

What does c3po do?

The idea is that the developer should not have to make any changes to existing sites, other than remove FB, G, Tw, etc JS files and replace with the single c3po library. In other words, only the JS loading part should be changed, not the individual widgets code.

c3po is a small utility which can be packaged together with the rest of your application code, so there will be no additional HTTP requests.

Parsing and inserting iframes

The first task for c3po is to insert iframes. It looks for HTML tags such as

<div class="fb-like" data-href="http://phpied.com"></div>

Similar tags are generated by each provider's "wizard" configuration tools.

In place of this tag, there should be an iframe, so the result (generated html) after c3po's parsing should roughly be like:

<div class="fb-like" data-href="http://phpied.com">

The way to do this across providers is to just have every data- attribute passed as a parameter to the 3rd party URL.

Third parties can be setup using a register() method:

// FB
// Tw
// ...

The only additional parameter passed to the third party URL is cpo-guid=..., a unique ID so that the iframe can identify itself when requesting services.

The parsing and inserting frames works today, as the demo shows. The only problem is you don't know how big the iframes should be. You can guess, but you'll be wrong, given i18n labels and different layouts for the widgets. It's best if the widget tells you (tells c3po) how big it should be by sending a message to it.

X-domain messaging

What we need here is the iframe hosted on the provider's domain to communicate with the page (and c3po script) hosted on your page. X-domain messaging is hard, it requires different methods for browsers and I'm not even going to pretend I know how it works. But, if the browser supports postMessage, it becomes pretty easy. At the time of writing 94.42% of the browsers support it. Should we let the other 5% drag us down? I'd say No!

c3po is meant to only work in the browsers that support postMessage, which means for IE7 and below, the implementers can resort to the old way of including all providers' JS. Or just have less-than-ideally-resized widgets with reasonable defaults.

When the widget wants something, it should send a message, e.g.

var msg = JSON.stringify({
  type: 'resize',
  guid: '2c23263549d648000',
  width: 200, 
  height: 300
parent && parent.postMessage(msg, '*');

See the example widget for some working code.

The c3po code that handles the message will check the GUID and the origin of the message and if all checks out it will do something with the iframe, e.g. resize it.

Again, take a look at the demo code to see how it all clicks together


As you see in the demo, only the example widget is resized properly. This is because it's the only one that sends messages that make sense to c3po.

Next step will be to have all widget providers agree on the messages and we're good to go! The ultimate benefit: one JS for all your widget-y needs. One JS you can package with your own code and have virtually 0 cost during initial load. And when you're ready: c3po.parse() and voila! - widgets appear.

Of course, this is just a draft for c3po, I'm surely missing a lot of things, but the idea is to have soemthing to start the dialogue and have this developed in the open. Here's the github repo for your forking pleasure.

Make sense? Let's talk.

Tell your friends about this post on Facebook and Twitter

Sorry, comments disabled and hidden due to excessive spam.

Meanwhile, hit me up on twitter @stoyanstefanov