gwtgwt-2.8

What is the best way to customize or entirely skip GWT's *.nocache.js


Here's the deal - we have only one permutation so there is no need to perform any browser detection. We are optimizing for mobile devices and latency and network bandwidth are a problem. We have an alternate way to obtain the main fragment other than using an HTTP request and would like to utilize that but *.nocache.js only knows about creating a script tag that requests it via http.

I was trying to create a custom version of *.nocache.js (presently hacking) that will do enough of a setup to allow us to load the fragment code differently (as just directly loading the fragment does not seem to work, even when I unescape it - all code is inside a list of quoted strings passed to a function call). I managed to get that code running but it fails.

Any ideas?


Solution

  • Well, after a comment from Thomas Broyer above I was able to get further and find what feels like (presently) definitive answer: "This is NOT possible cleanly." (without writing a custom linker)... but there is a less-than clean way that I am still pursuing further.

    Specifically, there are three official GWT linkers and two of these produce code that itself wants to request the main code fragment via http. The third one, "sso" produces a single file and only works for a single permutation (which is acceptable in my case). However, the resulting code executes document.write(), which obliterates the host document when invoked dynamically.

    It does this to create a marker tag right after the tag it expects to be coming from so that it can extract its own URL and the script base URI from it. In my case the host page is in the parent folder of the GWT module folder and would normally contain something like:

    <script ... src="moduleName/moduleName.nocache.js"></script>
    

    ... and moduleName.nocache.js wants to figure out that "host-base/moduleName" base URI for all other module-related requests (such as other code fragments, etc).

    In any case, if it fails to find the marker tag it writes with document.write() it will try to look up the tag and use that instead. Trouble is, that tag neither exists normally nor would be correct - in our case it would reference the parent of what is needed. However, this tag can be dynamically inserted as well before we actually inject moduleName.nocache.js... and then corrected back after injection.

    Something like:

          // Obtain the main fragment script code using alternate means:
          var scriptCode = ...;
    
          // Create the script tag for the main code fragment
          var codeElement = document.createElement("script");
          codeElement.type = "text/javascript";
          
          // Prepare the environment to run it, to hack around
          // its document.write and base URI needs.
          
          // First remember the normal/original document.write method:
          var originalWriteMethod = document.write;
          
          // ... and replace it with a dummy one (we'll put this back later):
          document.write = function() {}
          
          // ... and also remember the document base
          var originalBase = document.baseURI;
          
          // Figure out the script base based on the originalBase:
          var scriptBase = originalBase + "moduleName/";
          
          // Failing document.write case, the main code fragment will look for
          // the last <base> tag to get the script base. Add that element and
          // set it to reference the scriptBase (we'll have to undo this later):
          var baseElement = document.createElement("base");
          baseElement.href = scriptBase;
          document.head.appendChild(baseElement);
          
          // Different browsers work differently...  try to cover two cases:
          try {
            codeElement.appendChild.document.createTextNode(scriptCode);
            document.head.appendChild(codeElement);
          } catch (e) {
            codeElement.text = scriptCode;
            document.head.appendChild(codeElement);
          } finally {
            // Whatever case occurred above, we must return the document.write()
            // to normal implementation and undo our temporary document base 
            // change:
            document.write = originalWriteMethod;
            baseElement.href = originalBase;
          }

    This gets the code to "load", initialize and run. I still have some more testing to do but this is the closest I came so far.