blob: b319db14589a6448a45aaddfc07381e82ab8caac [file] [log] [blame]
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gwtexpui.linker.server;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
/** A single permutation of the compiled GWT application. */
public class Permutation {
private final PermutationSelector selector;
private final String cacheHTML;
private final String[] values;
Permutation(PermutationSelector sel, String cacheHTML, String[] values) {
this.selector = sel;
this.cacheHTML = cacheHTML;
this.values = values;
}
boolean matches(String[] r) {
return Arrays.equals(values, r);
}
/**
* Append GWT bootstrap for this permutation onto the end of the body.
* <p>
* The GWT bootstrap for this particular permutation is appended onto the end
* of the {@code body} element of the passed host page.
* <p>
* To keep the bootstrap code small and simple, not all GWT features are
* actually supported. The {@code gwt:property}, {@code gwt:onPropertyErrorFn}
* and {@code gwt:onLoadErrorFn} meta tags are ignored and not handled.
* <p>
* Load order may differ from the standard GWT {@code nocache.js}. The browser
* is asked to load the iframe immediately, rather than after the body has
* finished loading.
*
* @param dom host page HTML document.
*/
public void inject(Document dom) {
String moduleName = selector.getModuleName();
String moduleFunc = moduleName;
StringBuilder s = new StringBuilder();
s.append("\n");
s.append("function " + moduleFunc + "(){");
s.append("var s,l,t");
s.append(",w=window");
s.append(",d=document");
s.append(",n='" + moduleName + "'");
s.append(",f=d.createElement('iframe')");
s.append(";");
// Callback to execute the module once both s and l are true.
//
s.append("function m(){");
s.append("if(s&&l){");
// Base path needs to be absolute. There isn't an easy way to do this
// other than forcing an image to load and then pulling the URL back.
//
s.append("var b,i=d.createElement('img');");
s.append("i.src=n+'/clear.cache.gif';");
s.append("b=i.src;");
s.append("b=b.substring(0,b.lastIndexOf('/')+1);");
s.append(moduleFunc + "=null;"); // allow us to GC
s.append("f.contentWindow.gwtOnLoad(undefined,n,b);");
s.append("}");
s.append("}");
// Set s true when the module script has finished loading. The
// exact name here is known to the IFrameLinker and is called by
// the code in the iframe.
//
s.append(moduleFunc + ".onScriptLoad=function(){");
s.append("s=1;m();");
s.append("};");
// Set l true when the browser has finished processing the iframe
// tag, and everything else on the page.
//
s.append(moduleFunc + ".r=function(){");
s.append("l=1;m();");
s.append("};");
// Prevents mixed mode security in IE6/7.
s.append("f.src=\"javascript:''\";");
s.append("f.id=n;");
s.append("f.style.cssText"
+ "='position:absolute;width:0;height:0;border:none';");
s.append("f.tabIndex=-1;");
s.append("d.body.appendChild(f);");
// The src has to be set after the iframe is attached to the DOM to avoid
// refresh quirks in Safari. We have to use the location.replace trick to
// avoid FF2 refresh quirks.
//
s.append("f.contentWindow.location.replace(n+'/" + cacheHTML + "');");
// defer attribute here is to workaround IE running immediately.
//
s.append("d.write('<script defer=\"defer\">" //
+ moduleFunc + ".r()</'+'script>');");
s.append("}");
s.append(moduleFunc + "();");
s.append("\n//");
final Element html = dom.getDocumentElement();
final Element head = (Element) html.getElementsByTagName("head").item(0);
final Element body = (Element) html.getElementsByTagName("body").item(0);
for (String css : selector.getCSS()) {
if (isRelativeURL(css)) {
css = moduleName + '/' + css;
}
final Element link = dom.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("href", css);
head.appendChild(link);
}
final Element script = dom.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("language", "javascript");
script.appendChild(dom.createComment(s.toString()));
body.appendChild(script);
}
private static boolean isRelativeURL(String src) {
if (src.startsWith("/")) {
return false;
}
try {
// If it parses as a URL, assume it is not relative.
//
new URL(src);
return false;
} catch (MalformedURLException e) {
}
return true;
}
}