Wednesday, February 16, 2011

Facebook's Cross-Domain (XD) receiver, IE7, jQuery woes

Facebook's JavaScript SDK contains several ways of setting up a cross-domain (XD) receiver. The motivation behind using XD receiver is obviously to get around the browser security so that Facebook can invoke JavaScript-based commands on the client browser. If you de-minify the Facebook connect code or inspect the hidden div, you'll see that the initialization tries to use Adobe Flash SWF objects first.

If you use the following snippet code with jQuery on IE7 with the Facebook Connect JavaScript, you'll notice that the alert() statement will appear twice during a page reload. If you inspect your DOM tree (look for a div ID of fb-root or class fb_reset), you'll notice that the Facebook JS SDK actually injected the same code within another hidden IFRAME element!


$(document).ready(function () {
  alert('here');
});

I first noticed that the problem didn't happen for Chrome and Internet Explorer 8 but did occur for IE7. Apparently a consequence of the cross-domain receiver on IE7 (and other browsers that don't have a minimum of Adobe Flash 10) is that page loads occur twice. This page load can cause all sorts of issues, such as conflicting ID tags or other strange issues (i.e. onload events don't trigger for IFrame elements).

IE7 by default runs Flash 9.0, and Adobe Flash 10.0 seems to be the minimum for the SWF-based approach to work. Apparently Facebook seems to have found that the SWF-approach is perhaps more browser compatible (or doesn't cause multiple page loads) and therefore tries to use it before falling back to the IFRAME-based approach.

The code that handles this logic is stored inside the all.js for the Facebook Connect:

FB.provide('XD', {
  _origin: null,
  _transport: null,
  _callbacks: {},
  _forever: {},
  init: function(a) {
    if (FB.XD._origin) return;
    if (window.addEventListener && !window.attachEvent && window.postMessage) {
      FB.XD._origin = (window.location.protocol + '//' + window.location.host + '/' + FB.guid());
      FB.XD.PostMessage.init();
      FB.XD._transport = 'postmessage';
    } else if (!a && FB.Flash.hasMinVersion()) {
      FB.XD._origin = (window.location.protocol + '//' + document.domain + '/' + FB.guid());
      FB.XD.Flash.init();
      FB.XD._transport = 'flash';
    } else {
      FB.XD._transport = 'fragment';
      FB.XD.Fragment._channelUrl = a || window.location.toString();
    }
  },

Facebook seems to know about this issue and has provided the custom channel URL parameter when invoking FB.init. The document is located here:

http://developers.facebook.com/docs/reference/javascript/fb.init/
Custom Channel URL

This is an option that can help address three specific known issues. First, when auto playing audio/video is involved, the user may hear two streams of audio because the page has been loaded a second time in the background for cross domain communication. Second, if you have frame busting code, then you would see a blank page. Third, this will prevent inclusion of extra hits in your server-side logs. In these scenarios, you may provide the optional channelUrl parameter:

 <script src="http://connect.facebook.net/en_US/all.js"></script>
 <script>
   FB.init({
     appId  : 'YOUR APP ID',
     channelUrl  : 'http://example.com/channel.html'  // custom channel
   });
 </script>
The contents of the channel.html file should be this single line:

 <script src="http://connect.facebook.net/en_US/all.js"></script>

The channelUrl MUST be a fully qualified absolute URL. If you modify the document.domain, it is your responsibility to make the same document.domain change in the channel.html file as well. Remember the protocols must also match. If your application is https, your channelUrl must also be https. Remember to use the matching protocol for the script src as well. You MUST send valid Expires headers and ensure the channel file is cached by the browser. We recommend caching indefinitely. This is very important for a smooth user experience, as without it cross domain communication becomes very slow.

The bug report is being tracked here:

http://bugs.developers.facebook.net/show_bug.cgi?id=9777

3 comments:

  1. "You MUST send valid Expires headers and ensure the channel
    file is cached by the browser. We recommend caching indefinitely."

    Any idea how we do that?

    :)

    ReplyDelete
  2. Can the custom channel URL be on a different domain, like on a CDN?

    That is, can my script on foo.com set the channelURL = "mycdn.com/channel.html"?

    Thanks!

    ReplyDelete
  3. Trying to connect facebook comment system with my site http://www.diolt.com but xd receiver file web page is unavailable. Where to download ?

    ReplyDelete