Loading...

This presentation is an HTML5 website

Press key to advance.

Third Party JavaScript

In the Third Person

Alex Sexton | CapitolJS 2011

About Me

var ThirdPartyDevs = ThirdPartyDevs || [];
FB.api('/me', {}, function( me ) {
  me.name === 'Alex Sexton'; // true
  me.location = 'Austin, TX';
  me.employer = 'Bazaarvoice';
  me.teams = [
    'modernizr',
    'yepnope',
    'yayquery'
  ];
  ThirdPartyDevs.push( me );
});
          

Define: Third Party JavaScript

Um.. ok. It's any piece of code that you include from someone else's servers that adds functionality to your page. Kind of.

Other Views

It could mean a number of things (and usually depends on perspective):

  • Any JS loaded from a different domain than the host domain.
  • Any JS not written by the author of the host domain.
  • Both of those at the same time?
  • Both of those, minus developer libraries? But only sometimes?

Examples in the wild

  • FB Like / FB SDK -- ui/api
  • Disqus
  • ga.js
  • Twitter @anywhere
  • Bazaarvoice R&R !!11010
  • Ads

Not Examples in the wild

  • jQuery
  • jQuery from the google cdn
  • Your site main.js served from Amazon S3

Cross-domain from the perspective of a security-focused person, perhaps, though.

The Situation

There are two monsters in third-party JavaScript development.

Monster 1

The Same Origin Policy

Monster 2

Other Developers

Keys to Success w/ Third Party JS

  1. Cross-Domain Communication
  2. Getting The Hell Out of the Way

Types of Cross Domain Communication

Cross-domain communication can really mean two quite distinct problems

  1. Between purely front-end contexts (frames, windows, documents)
  2. Between a page and a server (usually during the life of the document)

Types of Cross Domain Communication

And each of these categories of xd communication has two categories:

  1. Spec'd APIs
  2. Massive Hacks

The SOP is an SOB

The same origin policy prevents a document or script loaded from one origin from getting or setting properties of a document from another origin. -MDN

Bad news for the third parties (good for security)

How the SOP decides

All but the pathname must match.

How the SOP decides

Are these just called *slash* *slash*?

Double "meh" ?

://

A Few SOP Examples

 "http://taylorswift.com"
"https://taylorswift.com"
          

SOP FAIL

A Few SOP Examples

"http://diary.taylorswift.com"
"http://taylorswift.com"
          

SOP FAIL

A Few SOP Examples

"http://taylorswift.com:8080"
"http://taylorswift.com"
          

SOP FAIL

A Few SOP Examples

"http://taylorswift.com:80"
"http://taylorswift.com"
          

SOP FAIL (WTF INORITE?!)

A Few SOP Examples

"http://taylorswift.co.uk"
"http://taylorswift.com"
          

SOP FAIL

A Few SOP Examples

"http://taylorswift.com"
"http://taylorswift.com"
          

SOP PASS

A Few SOP Examples

"http://tswift.com/love-letter-4-slexaxton"
"http://tswift.com/my-fav-javascript-confs"
          

SOP PASS

SOP | A Brief History

  • The Same Origin Policy in JS dates all the way back to Netscape Navigator 2.0
  • But there was a HUGE (intentional) security hole in the fact that <script> tags were not included in this policy.
  • This was both a security hole in browsers, and imo, the thing that made the web successful.
  • This means that you can load executable code from any domain into the exact same context as the first party code.

What you need to know about XD Communication

  1. The Spec'd apis: CORS and postMessage
  2. How to use a shim: I like easyXDM

I have a whole talk about Cross Domain Stuffs!

  1. Breaking The Cross Domain Barrier
  2. Also, the Disqus guys, Ben and Anton, are putting out a book! Third Party JavaScript

JSONP is a clever hack

var NS = (function(d){
  var uniq = 0;
  return {
    jsonp : function ( url, cb ) {
      var s = d.createElement('script'),
          f = d.getElementsByTagName('script')[0],
          n = "cb" + (++uniq);
  
      s.src = url + "?callback=NS.CBs." + n;
      this.callbacks[ n ] = cb;
      f.parentNode.insertBefore( s, f );
    },
    CBs : {}
  };
})(document);
          

JSONP is a clever hack

// Call it
NS.jsonp(
  'http://server.com/data.dart',
  function ( data ) {
    console.log( data.MY_DATA_YAY );
  }
);
          

JSONP is a clever hack

#! /usr/bin dart
## http://server.com/data.dart
## My best guess of DART syntax

## output a javascript header
super_mega_print (*_*)->>
  HEADER_GO( "text/javascript" )(|%|);

## output a function invocation, and pass in the data.
super_mega_print (*_*)->>
  GET.callback+"("+RAWRJSON.stringify(|%data%|)+");";

## close the file with Cheeseburger function invocation
ugh_so_much_better_than_javascript(|%|);
          

HTML Forms used to be cool before AJAX

  • You can hide iframes and set the target of forms to them
  • You can use this technique to support a POST capable json transport
  • Ben Vinegar expanded on this technique a bit and called it JSONPI

iFrames are awesome.

iFrames are awesome. - Me, just now.

Document Domain

// On sub.domain.com
document.domain = "domain.com";
            
  • BOTH have to set document domain -- that way all tlds aren't subject.
  • Must be less specific (sub.domain.com -> domain.com)
  • Can only be set once
  • IE6 kind of freaks out randomly :/

Nested iFrame Passthrough

Nested iFrame Passthrough

Nested iFrame Passthrough

  • You can pass messages across fragment identifiers in the 'src' of nested iframes.
  • 2006 era James Burke (re: requirejs/dojo fame)
  • This technique should not be your first choice, but it's a really cool hack.
  • Twitter and Facebook use this hack quite a bit.

The Takeaway

Use easyXDM

Get out of the way.

Performance

  • Everything should be non-blocking.
  • Meebo gains a few things by injecting an iframe on the host domain, forcing it to say that it's done loading, using it's built-ins, and then shipping information and parts back over to the dark side. (sub 10ms affect)
  • Minimally asynchronous inclusion of your script.

Integration

Whatever you tell someone to put on their page, you have to support forever.

Client deliverable

  • Give them as little as possible to integrate with.
  • async snippet that does the injection of extra markup, handles the initialization/customization in the initial request, and injects the correct styles.
  • Caching is a double edged sword, better performance/lower cost, but updates are hard to roll out

A Caching Technique

Do a quick double hop, if your code is suited for this.

  • Normally you have a control file that kicks everything off. Keep that small and light and cached for shorter amounts of time (1-7days?)
  • Then use a cachebusting/version system in your files.
  • Cache all these files forever. They will only rerequest on updates.

Sniff Test

The ulimate 'get-out-of-the-way-test' is to see if you can implement your widget twice on the same page.

Crazier Hacks for Natives

Crazy people may be interested in sandboxed natives. see @jdalton (and his Fusebox) for info on this.

Including jQuery Frameworks

Using a library for large 3rd party apps can be a good idea, but hard to guarantee no collisions.

What you might think...

  • You wanna serve jQuery async from google cdn?
  • Then you just call noConflict() on it?
  • Death from above.

Not in IE8 and lower.

In IE8 and lower, there is not an atomic callback for script loads.

The Fix

  • Include jQuery along with the noconflict in the body (bad for cdns)
  • Use @jaubourgs crazy onclick script trick

The Script onclick event?

<script
  id="scriptId"
  for="scriptId"
  event="onclick"
  src="script.js">
</script>

          

The Script onclick event?

scriptTag.event = "onclick";
scriptTag.id = scriptTag.htmlFor = generateNewId();

scriptTag.onreadystatechange = function() {
  if ( /loaded|complete/.test( scriptTag.readyState ) ) {
    try {
      scriptTag.onclick();
    } catch( e ) {}
    requestCallback();
  }
};
          

Fresh Like Button

Cached Like Button

Fresh Plus One Button

Cached Plus One Button

Thanks!