diff options
Diffstat (limited to 'gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java')
-rw-r--r-- | gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java new file mode 100644 index 0000000000..b319db1458 --- /dev/null +++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java @@ -0,0 +1,160 @@ +// 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; + } +} |