Charlie Cheever

Posts Tagged ‘XFBML’

If you create a Facebook application, one of the most confusing parts of setting it up is choosing whether to make your app use IFrames or FBML as the default for your application’s canvas pages.

The text explaining the choice is:

Your application will be viewable in the Facebook navigation at this URL – either as rendered FBML or loaded in an iframe. If you aren’t sure what you should use here, choose FBML. You can use iframes within FBML on canvas pages with the <fb:iframe> tag, and most things you will want to do will be easier and faster with FBML.

But over the last few months, there have been a few developments which have made that explanation of the choice out of date. I think it is now easier to create a better app by using IFrame canvas pages than it is with FBML. I’ll try to explain the difference between the two paradigms, some of the things that made FBML a better choice originally, outline what’s changed since then, and make some conclusions about what techniques you can use to make the best performing applications. Ray wrote a post on the official Facebook Developers’ Blog about enhancing IFrame-base applications a few weeks ago, which overlaps a lot with this post, but I think its worthwhile to go into a little more detail around the choice between FBML and IFrames.

How a traditional IFrame canvas page worked

IFrame canvas pages are pretty straightforward. When the user loads a page on Facebook like http://apps.facebook.com/yourapp/somepage, Facebook serves up a webpage that has the Facebook chrome around a big IFrame. The IFrame that gets opened is something like http://www.yourdomain.com/callbackurl/somepage?fb_sig_in_iframe=1&fb_sig_locale=en_US&fb_sig_time=1221720862.9318&fb_sig_api_key=48102584111d14a9c2e41dd28ea637d7&fb_sig=e656792696ae913c1fc34eeff2d79f75

The fb_sig parameters give more information about which user is logged into your application, let you verify that the request is indeed coming through Facebook, etc. You can put whatever you want into the Iframe and it will be rendered there.

Usually in a Facebook app, you’ll want to include some social content like names and profile pictures, and sometimes you’ll want to get some data from the Facebook API to determine which content to show. The way you can show this content without using FBML is to get the information you need via the API and render it yourself.

Here’s a rough diagram of how all this works:

Traditional Iframe Canvas Page

How an FBML canvas page works

FBML canvas pages are a little bit different but are still pretty straightforward way. When the user requests a page like http://apps.facebook.com/yourapp/canvaspage, Facebook doesn’t send back a response right away; instead, the Facebook server will send a request to your app’s server. This will be an HTTP POST to some URL like http://www.yourserver.com/callbackurl/canvaspage. The fb_sig parameters will be sent as part of the POST rather than as part of the URL. Facebook will expect some FBML back and then turn that FBML into HTML and send it back to the user’s browser. FBML is mostly the same as HTML, but using FBML lets you include social content inline in your markup.

So, if you want to show things like names and pictures in FBML, you don’t need to make calls to the Facebook API; you can just use tags like <fb:name> and <fb:profile-pic> to reference the data directly. If you want to use data like birthdays, etc., then you’ll still need to use the API the same way you would on an Iframe page.

Here’s a diagram of how an FBML canvas page works. A lot of the time, you won’t need to make any API calls, so those arrows are dotted in the diagram.

FBML Canvas Page

Why We’ve Been Recommending FBML Up Until Now and What’s Changed

There are a bunch of nice things that FBML provides for you which is why we’ve been recommending that developers use it. But over the past few months, we’ve been doing a lot of work–mostly as part of Facebook Connect–building stuff that you can use to get most of those advantages and more for your IFrame canvas pages. The ones that I think are most important are:

Speed

FBML pages have tended to be faster for a bunch of reasons. For one thing, many FBML canvas pages don’t end up needing to make any API calls, and when that happens there is one fewer roundtrip that needs to be made if you are using FBML. Another thing that has tended to make FBML canvas pages perform better is that Facebook’s servers are directly peered with most of the large hosting companies that serve app pages, so the latency on the roundtrip from Facebook’s servers to your application can be much smaller than the latency from the user’s browser to your application’s server. There is also some non-trivial overhead from setting up an Iframe on most browsers but I believe this is fairly small compared to the other two things listed here.

What’s Changed: Facebook Chat and XFBML

Facebook’s released two things in the last year that really change this up: Facebook Chat and XFBML. The reason that the launch of Facebook Chat is important is that its now really expensive to setup a page with the Facebook chrome since the chat bar involves a lot of JavaScript and CSS which have to get executed and rendered by the browser from scratch on each page load even if the files are cached on the browser. On the first page that a user loads from your app, both FBML and Iframe apps will have to pay this cost; but on subsequent page loads, an FBML app will incur this cost each time but an Iframe app will just involve switching the page that is inside the IFrame. IFrames always benefitted from this, but now that setting up a Facebook page is a bigger chore, this is a bigger deal.

While Facebook Chat makes FBML canvas pages slower, XFBML can make IFrame canvas pages faster. The big disadvantage for IFrame canvas pages in terms of speed was having to make an API call to Facebook before sending back content to the user’s browser. XFBML lets you avoid that. With XFBML, you can embed some simple FBML tags like <fb:name> and <fb:profile-pic> directly into the HTML that your app sends to the user’s browser, and when you include some JavaScript from Facebook, code will execute that scans the DOM for those tags and then figures out all the data needed to render that content and batch that up into one API call from the user’s browser to Facebook. The rest of the page that isn’t social content can render to the user before this happens, and in XFBML, we cache data on the browser so that in many cases, it isn’t even necessary to make any API call to Facebook at all. This all adds up to making IFrames a snappier experience in most cases — though sometimes you might still need to make server side API calls to Facebook if you want to do something like show a page with upcoming birthdays.

We developed XFBML as part of Facebook Connect as a way to quickly get social content onto any webpage, and since IFrames are just webpages, XFBML can speed those up as well.

Here’s a diagram of how an IFrame canvas page works using XFBML on the first page a user loads from your app:

IFrame Canvas Page Using XFBML - First Page Load by a User

At a glance, this looks worse than the scenario for Iframe canvas pages that don’t use XFBML, but if you use XFBML, then you usually won’t need to make an API call to Facebook from your server (which means (4) and (5) don’t happen), and the user’s browser will be able to start rendering most of the page–everything except the XFBML content its waiting to get from Facebook–right after (6).

On subsequent page loads, the benefits are even greater. Here is a diagram of what that looks like:

Iframe Canvas Page Using XFBML - Subsequent Page Loads by a User

Note that in this case, the original request to Facebook and response to the browser don’t need to happen anymore, and the JS API calls will sometimes be unnecessary here as well if the necessary data has been cached on the client. In this scenario, your app should be basically just as fast as a regular website except that the social content will fill in just after the page has rendered which can result a little flicker on some browsers.

Convenience

We’ve heard from developers that FBML has been simpler than using the API in two ways.

FBML is really nice when its important to batch your data access

It is actually a pretty gnarly problem to figure out all the data you are going to want on a page up front, and as your pages get more complicated, this problem gets more complicated too. One solution is to produce code that has two sections: the first which figures out all the data you need and fetches it from the API and then stores the data in some variables in your program, and the second part which renders all the HTML that you want using the data stored in those variables. This technique can work, but it is a little bit tedious and hard to maintain if you want to change anything since you have to make changes in two places. You’re also likely to make mistakes in this scenario where you remove something from the rendering section but forget to remove the corresponding data access entry and then are requesting data you don’t need which is hard to notice and inefficient. Things get even worse if you try to build modules that can be reused across different pages. An example of this would be a module that displays which of your friends are online and prompts you to interact with them. In that scenario, you either end up writing two sections of code for each of your modules, which is the same pain as described above but multiplied by the number of modules you have.

The other solution you could use besides a two-pass approach would be to create a system of placeholders that describe the data they need. If you do this, you can have one piece of code that doesn’t change that scans all the placeholders and makes a big request for all the necessary data and then fills it in right before you send your rendered response back. This is essentially what FBML is, and so FBML is convenient in that we do this for you and optimize it as much as we can so you don’t have to build this system or work on it.

FBML is nice for Facebook-provided widgets and dialogs

There are ways to render things like the multi-friend selector widget and feed forms when using Iframes, but these are often a bit clunky compared to doing these in FBML. There are also some useful widgets that are provided as part of FBML like fb:share-button that are a pain to recreate on your own (though certainly possible). I’ve heard from developers that fb:dialog is a really nice way to make a popup dialog with a few options. If you want to make an application that is consistent with Facebook’s look and feel, these tags can make that task much easier (though some of them can cause headaches because they aren’t tweakable enough).

What’s Changed: XFBML gives you access to all this within Iframes

In addition to being able to embed things like fb:name and fb:profile directly into your HTML which makes doing efficient data access easier, you can use any FBML tag in XFBML by using server-side FBML. If you wrap your FBML inside the tags <fb:serverfbml><script type="text/fbml"> ... </script></fb:serverfbml> then an Iframe will be opened to Facebook on the page being served from your domain, and we’ll render the FBML in there.

There’s a fair amount of overhead involved in opening the IFrame and your CSS won’t propagate into the FBML IFrame, so you won’t want to put too many blocks of server-side FBML on a page, but if you just need one or two snippets of FBML to make your life easier, or you want a large chunk of your page to use FBML, this makes that possbile and pretty easy.

Looks

One thing that often makes IFrames look ugly is that they have to be declared to have some fixed size and then if any content exceeds that size, nasty scroll bars appear (or the overflowing content is inaccessible to the user). Early IFrame apps on Facebook often looked very ugly for this reason despite a cursory “smartsizing” solution that we implemented. Most developers ended up creating giant IFrames with heights larger than they ever expected their content to get which was a little better but obscured the page footer and made the scrollbar unintuitive.

What’s Changed: Now, there’s an easy way to get your IFrame to resize automatically

Preload FQL and the List of Friends for Free

It’spretty common to know what FQL queries you need to make to generate a page even before the request comes in from a user. For example, if you want to make a page that shows a calendar of friends’ birthdays, then you know you’ll need to make an FQL query that returns all the birthdays of all the friends of the user. We added a feature about a year ago called Preload FQL that has let you do this on FBML canvas pages. You just have to specify a few regular expressions that map to lists of FQL queries, and if the page being requested matches any of the regular expressions, the corresponding FQL queries will be executed by Facebook when the user’s browser sends the request to Facebook and the results will be sent along to your app’s servers when Facebook requests the FBML from you. Most of the time, this makes calls to Facebook’s API unnecessary, even when you need fairly complicated data from Facebook to generate the page.

This is what the flow would look like for an FBML canvas page using preload FQL:

FBML Canvas Page Using Preload FQL

In the diagram above, the FQL query is executed after (1) and the results are sent along to your application server with (2).

What’s Changed: Now, there is a way to do something similar in IFrames

With IFrames, there isn’t an easy way for Facebook to send data to your server along with the request for the IFrame except by encoding data in the URL being requested, so In both cases, instead of sending the FQL result set to your server, the Facebook server sends the FQL result data down to the user’s browser–attached to the outer frame that contains the Facebook chrome on the canvas. You can choose whether you want to have a lightweight redirect happen which will encode the FQL results in the request made for your IFrame page, or if you only want to access that data on the client side, you can skip that and just use the PreloadFQL_get method from the JavaScript client library and rewrite parts of the page on the fly using the data after its initially rendered.

Here’s a diagram of how this works:

IFrame Canvas Page using Preload FQL

Note that the preload FQL will only work on the first page that loads in your IFrame. (If you point your links to apps.facebook.com URLs using target="_top" instead of just linking to different pages within the frame, you could use preload FQL on every page, but this would make all your pages load slowly. We may make some mechanism for directing the containing frame to make API calls which could then be fetched across pageloads within the IFrame if there is a lot of demand for that. In the meantime, if this is important to you, it should be possible to roll your own mechanism for this by wrapping your IFrame canvas pages within your own extra IFrame.)

This paradigm is a bit different from the way the Preload FQL works with FBML canvas pages, and if you’re a traditional web developer, having to send down JavaScript that will access the data and then rewrite the page is awkward. But developers who have written substantial OpenSocial applications or AJAX-intensive web apps should be pretty comfortable with the way this works.

We also send the list of the user’s friends to the containing frame right away so calling the friends_get method from the JS API client also won’t require a roundtrip to the server now.

URLs

One thing that’s annoying about IFrame canvas pages is that when a user navigates through your app, the location in the URL bar never changes; it always stays on whatever the user went to (unless you specify target="_top" on your links, but that means that you’re incurring all the costs of a first page load again which is much slower.)

Unfortunately, we don’t have a solution for this right now. For many applications this isn’t a big deal at all, but its inconvenient for users who want to make bookmarks or copy links to send around, etc. We’re planning to add a mechanism similar to automatic resizing where some JavaScript from Facebook that executes in your IFrame communicates its location to the embedding Facebook page which can then manipulate the fragment of the URL (the part of the URL after the #) without causing the browser to reload the page, but we haven’t gotten around to doing this yet. (I’ll try to remember to update this post when we do.)

Authentication

On FBML canvas pages, the request for FBML that is sent to your application server contains POST data that lets you learn and verify that the request is coming from Facebook on behalf of a particular user. If that user has a session with your application, then the session data you need to make API calls on behalf of the user will be passed along as fb_sig_session_key in the POST. Most client libraries handle this pretty seamlessly, and so you don’t have to think about it.

On IFrame canvas pages, things don’t work as well. Facebook passes fb_sig_session_key to your IFrame as a GET parameter in the querystring of the IFrame’s URL. As with FBML canvas pages, most client libraries make this pretty painless and invisible to the app developer; but there are two pretty big flaws with this approach.

Browsers can leak the session key via the HTTP_REFERER field. This is a pretty serious security flaw in our design, and it is pretty easily exploitable if you let users post public links or images. One way you can fix the problem with links is by sending all links through a redirect, but that doesn’t solve the problem for images. It is currently not safe in an IFrame canvas page to reference images that users have given you the URLs for without proxying or caching those images yourself.

The other problem with passing the session information around as part of the URL of your page is that it makes it inconvenient to link to pages within your app without losing the session information. One way to handle this is to make your links point to canvas pages (like http://apps.facebook.com/youriframeapp/nextpage.py) and set target="_top" on the link. This technique means that you don’t lose session information but means that your application incurs all of the overhead of setting up an initial IFrame canvas page which leads to a pretty clunky feeling application experience. Another technique you can use is to link to other pages that are off your canvas URL (ex. http://yourdomain.com/appcallback/nextpage.py) and manually append all of the fb_sig parameters to the querystring. The major problems with this approach are that if a user opens that page in a new tab or copies and pastes any of those links, then the Facebook chrome gets lost, and all sorts of problems can happen with the session information (the session may have expired, the session might be for a different user than the person viewing the URL, etc.), so this isn’t a desirable choice either.

How to Deal with This Problem

Unfortunately, Facebook doesn’t provide any great out-of-the-box solution here, but a solution that I think might work well could be something like this: the session information gets sent to the containing frame and is stored in the DOM. The URL for the IFrame doesn’t have any fb_sig session information encoded in the querystring. Instead, that information gets stored in a cookie on your app’s domain so that the user’s browser passes it to your server with every request. If there’s no authentication information in the cookies when your server gets a request, then your server can send back a page that is just some JavaScript that communicates with the containing frame to find out the session information and then sets the cookie and then reloads itself. In this solution, it each IFrame page that your server sends to the browser should also probably contain some JavaScript that would verify that the session information in the cookies matched the session information in the containing frame, and if it didn’t, then the cookie would be set to the right information and the page reloaded. It is probably also useful to pass to the first IFrame page that is loaded some fb_sig parameters that your server can use to verify that the session in the cookies is still valid and for the correct user to prevent exploits where a malicious user could load a canvas page with stale cookies and capture the contents sent to the browse for another user before the JavaScript reset the cookies and forced the reload. If you see any major flaws with this solution or can think of a better one, please share that in the comments on this post or e-mail me.

Update: Ted Suzman reminds me that setting cookies doesn’t work in some browsers (Safari) for IFrames of domains other than the one in the location bar, so this solution won’t work for all browsers. I’m sure there’s some other good solution to this though.

I think we’re fairly likely to get around to implementing something to make this easy in the next month or two, but until then, it might be worth rolling your own workaround for these issues.

What All This Means

Now that we’ve made all these improvements that can make IFrame canvas pages better, here is how I would list out the advantages of the two methods:

FBML

Is likely to be faster on first page loads

Fewer moving parts are involved and the paradigm is closer to that of the traditional web

Gives you easy access to lots of Facebook elements

Lets pages in your app have nice URLs

Has a sensible authorization mechanism

IFrames

Is likely to lead to a faster experience for users over the long run

Lets you use the JavaScript, HTML, and CSS that you are used to

Even though there are more bullet points listed as FBML advantages, I think using IFrames for canvas pages is probably a better choice since the two advantages of IFrames are pretty important. In particular, if you are doing a lot of AJAX in your application, it will be snappier in IFrames since the requests don’t need to go through Facebook proxy. And while FBML and FBJS are trivial to learn and shouldn’t scare anyone at all, it is probably easier to debug regular HTML and JavaScript than FBML and FBJS given the tools available today. And if you want to use a popular JavaScript library like jQuery in your application, you can do that the regular way in an IFrame, but you’d have to make some modifications to the library to use it in FBJS, and some things just wouldn’t work.

That said, there are a number of things we’re likely to release in the future that will bring FBML and IFrames even closer to converging on a point of parity. I’m not sure of a timeline for any of these things, and this list certainly isn’t exhaustive, but some obvious improvements that we should execute on are:

Making a mechanism for an application IFrame to communicate page transitions to the outer frame so that the URL changes in a linkable way when a user navigates around an IFrame application.

For IFrame apps, building a way for API calls to be executed in the outer frame so that results can be requested prior to page transitions of the IFrame. It might also be useful to provide a general cross-page client-side caching mechanism by attaching cached data to the containing frame.

For FBML apps, make it so that the content inside the chrome can change without a full browser page load. We do this for many pages on our site (you might notice funny URLs like http://www.facebook.com/profile.php?id=1160#/profile.php?id=219770 that are a side-effect of this), and between avoiding the generation and render time of the chrome and avoiding the processing of all the JavaScript and CSS, it turns out to be a big win.

Create a new version of FBJS that is much closer to regular JavaScript (so you can include your favorite JS libraries without modification)

Once those things are done, FBML and IFrame canvas pages should be similar in performance and ease of use. And now that fb:serverfbml has been added to complement fb:iframe, what you choose to use for your app isn’t that important since you can switch between the modes fairly easily. For most applications, I don’t think IFrames are so much better right now that it would make sense to convert an existing FBML-based application to one using IFrames instead, but if you’re unhappy with the speed of your app, it may be worth a try.

In brief, if you use the new features we recently released, using IFrames for canvas pages is probably a little bit better choice than using FBML for canvas pages.

I’ve heard from a few developers who’ve switched over to IFrames and used the new stuff that they’ve seen good results doing this, but I haven’t had time to do any benchmarking myself. If you try switching from FBML to Iframes and measure your performance before and after, please leave a comment about how much faster (or slower) the load time became. If IFrame performance is significantly better for most apps then it might be worth doing the conversion from FBML to IFrames for some developers.

Two Last Tips

If you’re using FBML for your canvas pages and you decide you want one of your pages to just be an IFrame, don’t use fb:iframe for that. Instead, you should use the fb_force_mode querystring parameter to switch modes which will accomplish the same thing without incurring a roundtrip to your server just to fetch the fb:iframe tag. Similarly, if you are using IFrames for your app but want some particular page to be an FBML page, you can use fb_force_mode to force a switch to FBML. If you find documentation that advises using fb:iframe for switching from FBML to IFrame mode, it was probably written before fb_force_mode was released; we should have all those instances cleaned up soon.

If you’re using IFrames for your canvas pages, don’t set all the links to have target="_top". That will cause a full reload of the Facebook chrome plus an extra to request to do that and generate a new IFrame which will be pretty slow. If you are using an IFrame within an IFrame (or even deeper nesting) and so you need to set a link target explicitly, the IFrame that is your canvas is named iframe_canvas so you should just be able to use that as a target.