- We design and build extraordinary applications for companies looking to make the next great idea a reality.
- learn more
BJAX: A Quick Hack for Using GWT with Bookmarklets
BJAX = Browser Extensions + Ajax
I've continued my explorations on how to use GWT in unconventional ways, starting with how to load a GWT application through a bookmarklet. That's been made much easier with GWT 1.4 through the introduction of a "cross site" feature:
Cross-site script inclusion is now supported. The compiler produces a "-xs" (meaning "cross-site") version of your module's startup script that can be included without being restricted by the same-origin policy. Be careful, though. Including scripts from other sites that you don't fully trust is a big security risk.
There has been some confusion about this "option." Some developers thought that "-xs" was a command-line flag you had to pass to the GWT compiler. In fact, the GWT compiler produceds a file with "-xs" in its name as a part of its normal workings -- no special flags necessary. This file, when included in a page via a script tag, loads the GWT application as a Javascript (a ".js" file) into the current browser window. That's opposed to the iframe/html way it's been up to now.
Obviously iframe/html way is not suitable for things like bookmarklets, as the cross-domain thing gets in the way. With the pure Javascript approach, our problem is solved, right? Not so fast. If you try including the "-xs" Javascript file into a page via a bookmarklet, you get the dreaded hanging page with a perpetual "Loading..." message in the status bar. Why is that?
A little tour through an "-xs" file reveals that GWT assumes that the Javascript file is loaded during the normal loading/rendering of the page. Specifically, it uses document.write to insert script tags and uses an onload event handler to kick off the application code.
var $wnd = window, $doc = document, external = $wnd.external, gwtOnLoad, bodyDone, base = '',metaProps = {}, values = [], providers = [], answers = [], onLoadErrorFunc, propertyErrorFunc;[...]var oldOnLoad = $wnd.onload;$wnd.onload = function(evt){ if (oldOnLoad) { $wnd.onload = oldOnLoad; $wnd.onload(evt); } bodyDone = true; maybeStartModule();}[...]$doc.write('<\/script>');
Of course this doesn't work so well with a bookmarklet. For one, the onload event is already long gone by the time we run our code, and for another, document.write after a page has rendered will cause precisely the "hanging page" behavior we see when we try to load the GWT app via a bookmarklet. Really, this is less of a specific bookmarklet problem and more of a general "how do I get GWT to load after a page has already rendered" problem. So, what can we do to remedy this it?
First, we can simply use document.appendChild to insert the script tag, second, we can assume that the body has finished loading, and third, we will have to tell GWT where to find the application code. Why this last step? Because GWT uses a "marker script" to figure out the source URL of the GWT application:
function computeScriptBase(){ var thisScript, markerScript; $doc.write('<\/script />'); markerScript = $doc.getElementById('__gwt_marker_MyApp'); if (markerScript) { thisScript = markerScript.previousSibling; } function getDirectoryOfFile(path){ var eq = path.lastIndexOf('/'); return eq >= 0?path.substring(0, eq + 1):''; }
; if (thisScript && thisScript.src) { base = getDirectoryOfFile(thisScript.src); } if (base == '') { base = getDirectoryOfFile($doc.location.href); } else if (base.match(/^\w+:\/\//)) { } else { var img = $doc.createElement('img'); img.src = base + 'clear.cache.gif'; base = getDirectoryOfFile(img.src); } if (markerScript) { markerScript.parentNode.removeChild(markerScript); }}
When you use document.appendChild, this code stops working. Now there's probably a way to make this code work with appendChild, but I haven't noodled about it enough. That's why this post is termed a "Quick Hack." We do some simple surgery with the "-xs" file as follows:
function insertScript(src) { var script = document.createElement("script"); script.type = "text/javascript"; script.src = src; $doc.body.appendChild(script);}[...]function computeScriptBase(){ base = "http://labs.pathf.com/MyApp/"; bodyDone = true;}[...]insertScript(base + strongName);
With these changes, the GWT app can now be load via a bookmarklet.
Next week, I hope to demonstrate a few interesting hacks using GWT and bookmarklets. Maybe I'll even give the dowdy Craig's list a much needed overhaul. Stay tuned...
Technorati Tags: ajax, bjax, bookmarklet, gwt
Comments: 1 so far
Leave a comment
About Pathfinder
Recent
- Walk-Through Test Coverage
- Where minimalism fails: The problem with Apple’s less-is-more approach
- jQuery goodness with ASP .NET
- Design Thinking
- Bullseye Diagram
- Roles Testing For Security
- Blackbird takes the pain out of JavaScript logging
- Making GWT JSON not Quite so Painful
- IDEA - preconference workshop 06 Oct 08
- HTML5, Ajax history management, and The Ajax Experience 2008 Boston
Archives
- October 2008
- September 2008
- August 2008
- July 2008
- June 2008
- May 2008
- April 2008
- March 2008
- February 2008
- January 2008
- December 2007
- November 2007
- October 2007
- September 2007
- August 2007
- July 2007
- June 2007
- May 2007
- April 2007
- March 2007
- February 2007
- January 2007
- December 2006
- November 2006
- October 2006
- September 2006
- August 2006
- July 2006
- June 2006
- May 2006
- April 2006
- March 2006


Thank you so much! I had not the courage to change the bootstrapping scripts myself and nearly gave up on this. It would be really great to use 1.5’s Linkers to create a BookmarkletLinker that automatically creates all files necessary. Have you thought about that?
thanks again, christian
Comment by Christian Voigt, Monday, September 8, 2008 @ 8:19 am