5

I simply want to load a GWT(Google Web Toolkit) app by adding a script tag to the DOM, however because the GWT linker uses document.write() I'm unable to find any good way of doing so. I've found some hacks for doing so on various blog posts but they all seem to fail with the latest version of GWT. Any reasonably non-invasive approach for doing this come to mind?

Clarification:

Normal way to start up a GWT app, in your host html page:

<script type="text/javascript" language="javascript" src="myapp.nocache.js"></script> 

This, of course, starts up as soon as the page loads. I want to do it at a later time:

function startapp() {
    var head = document.getElementsByTagName('head');
    var s = document.createElement('script');
    s.setAttribute('type', 'text/javascript');
    s.setAttribute('src', 'myapp.nocache.js');
    head[0].appendChild(s);
}
user861594
  • 5,733
  • 3
  • 29
  • 45
Abdullah Jibaly
  • 53,220
  • 42
  • 124
  • 197
  • I am extremely sorry but could you please rephrase. An example would help. – Amey Dec 14 '10 at 16:30
  • @Amey checkout the update above. Thanks! – Abdullah Jibaly Dec 14 '10 at 18:51
  • Your code snippet should work as long as you're executing it on your site. `nocache.js` loads a `cache.html` file as needed based on deferred binding, and probably doesn't use an absolute URL. – Jason Hall Dec 14 '10 at 19:04
  • 1
    It doesn't work. As I mention in the question the myapp.nocache.js generated by GWT contains document.write() calls. This does not work if it is executed after the page has already loaded (such as calling it in an onclick event handler). – Abdullah Jibaly Dec 14 '10 at 19:24

3 Answers3

5

Here's what seems to work so far:

Add this to the top of your App.gwt.xml:

<!-- Cross site linker -->
<inherits name="com.google.gwt.core.Core" />
<add-linker name="xs" />

After compiling your app with the above setting, modify (or copy) the generated app.nocache.js as follows:

1) Comment the last $doc.write... statement

2) Copy this portion from the $doc.write statement you just commented out and eval it. Example:

eval('window.__gwtStatsEvent && window.__gwtStatsEvent({' + 'moduleName:"app", sessionId:window.__gwtStatsSessionId, subSystem:"startup",' + 'evtGroup: "loadExternalRefs", millis:(new Date()).getTime(),' + 'type: "end"});' + 'window.__gwtStatsEvent && window.__gwtStatsEvent({' + 'moduleName:"app", sessionId:window.__gwtStatsSessionId, subSystem:"startup",' + 'evtGroup: "moduleStartup", millis:(new Date()).getTime(),' + 'type: "moduleRequested"});');

3) Add this line right after.

document.body.appendChild(document.createElement('script')).src=base + strongName + ".cache.js";

So you're basically replacing the $doc.write with those two lines.

Now, your bookmarklet will look something like:

<a href="javascript:(function(){document.body.appendChild(document.createElement('script')).src='http://abc.com/app.nocache.js';})();">My App</a>
Abdullah Jibaly
  • 53,220
  • 42
  • 124
  • 197
  • Update: looks like you can actually avoid all this now by simply using the xsiframe linker instead! – Abdullah Jibaly Jun 01 '11 at 20:59
  • Excellent question and discussion Abdullah! I am trying to do the exact same as you, add a GWT generated JS from a bookmarklet. Can you give me any pointers to the xsiframe linker you mention.....while I go off and try your solution documented above. Thanks! – Andrew Mackenzie Oct 07 '11 at 15:05
  • Here's the code for the xsiframe linker: http://code.google.com/p/google-web-toolkit/source/browse/trunk/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java?r=9161 – Abdullah Jibaly Oct 07 '11 at 17:38
  • Thanks Abdullah. As you suspected, I just used the xsiframe linked and it all worked, without having to do any hacks. Thanks! – Andrew Mackenzie Oct 16 '11 at 10:57
1

I'm assuming you are already using the cross-domain linker and this does not resolve your problem with document.write. If not, it might be worth a look (sorry, not enough experience with it to say.)

One approach that I am fairly sure could be made to work is this:

Your bookmarklet adds a script tag to the page (as now)

This script is not GWT compiler output. It is a plain-old javascript that adds an IFrame to the page, and the src of that IFrame is pointed at an HTML page on your server that loads your GWT module.

Presumably the goal is for your GWT module to get things out of the page it was loaded into. Of course, it can't do this directly in this case because the IFrame comes from a different domain than the parent page.

In order to make this work you would have to use window.postMessage and window.addEventListener to communicate between your GWT module in the IFrame and your javascript stub in the parent (using JSNI on the GWT side.)

If you have to support older browsers, postMessage won't work - but you might be able to get away with hash manipulation - but this is probably where I'd draw a line on practicality.

  • Good comments thanks. I've actually come up with a solution that seems to work (I was using the xs linker). I basically replaced a couple lines in the generated .nocache.js to dynamically insert a script tag instead of do the doc.write thing. I'm going to test it a little more before I'm sure this works though... – Abdullah Jibaly Dec 16 '10 at 22:59
  • Great. It does seem like the "correct" solution to this problem would be to write a GWT linker that does the right things - but it seems like that capability is still in the future. – Mark Allerton Dec 16 '10 at 23:34
0

Whenever a browser loads a javascript file, its also execute every line of it inorder to build the symbol tables etc.

In your case, the app loads in the browser and after the dom is loaded, your GWT module js gets loaded. At this point, the browser will try to execute every line of the GWT module javascript, possibly causing your earlier loaded DOM to go for a toss.

What exactly is your use case? If your requirement is conditionally loading the GWT module then your could try something like this: Include this in your head:

<script src="gwtmoduleloader.js"></script>

Here, gwtmoduleloader.js is infact a servlet that will hold logic to figure out if the gwt module is to be loaded.

If the GWT module is to be loaded, the sevlet can print a

document.write('<script src="myapp.nocache.js"></script>') 

or else return silently.

When browser evaluates the contents of gwtmoduleloader.js, it may find a document.write for another script (in your case the gwt module), which it will load and evaluate. This is thus a conditional load and can be achieved before the body begins loading.

Is this what you were looking for?

Amey
  • 2,214
  • 2
  • 19
  • 28
  • I don't know how much more I can clarify my question, do you understand what a bookmarklet is? It injects javascript into your page waaaaaaaaay after the page has already loaded. Example: On this stackoverflow page I'm reading and looking around, and now I decide I want to run my bookmarklet. I need to inject javascript into the page. GWT's loader will not work because the onload event has come and gone eons ago, and document.write() will no longer work. – Abdullah Jibaly Dec 14 '10 at 22:36