Index: dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java =================================================================== --- dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java (revision 1523) +++ dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java (working copy) @@ -275,28 +275,31 @@ HttpServletResponse response, TreeLogger logger, String partialPath, String moduleName) throws IOException { - // If the request is of the form ".../moduleName.nocache.js" or - // ".../moduleName-xs.nocache.js" then generate the selection script for - // them. - boolean nocacheHtml = partialPath.equals(moduleName + ".nocache.js"); - boolean nocacheScript = !nocacheHtml - && partialPath.equals(moduleName + "-xs.nocache.js"); - if (nocacheHtml || nocacheScript) { - // If the '?compiled' request property is specified, don't auto-generate. - if (request.getParameter("compiled") == null) { - // Generate the .js file. - try { - String js = genSelectionScript(logger, moduleName, nocacheScript); - setResponseCacheHeaders(response, 0); // do not cache selection script - response.setStatus(HttpServletResponse.SC_OK); - response.setContentType("text/javascript"); - response.getWriter().println(js); - return true; - } catch (UnableToCompleteException e) { - // The error will have already been logged. Continue, since this could - // actually be a request for a static file that happens to have an - // unfortunately confusing name. - } + // Check whether the requested filename matches the filename of + // one of our generated scripts. + if (!partialPath.startsWith(moduleName)) { + return false; + } + SelectionScriptGenerator.Type type = SelectionScriptGenerator.Type + .forFilenameSuffix(partialPath.substring(moduleName.length())); + if (type == null) { + return false; + } + + // If the '?compiled' request property is specified, don't auto-generate. + if (request.getParameter("compiled") == null) { + // Generate the .js file. + try { + String js = genSelectionScript(logger, moduleName, type); + setResponseCacheHeaders(response, 0); // do not cache selection script + response.setStatus(HttpServletResponse.SC_OK); + response.setContentType("text/javascript"); + response.getWriter().println(js); + return true; + } catch (UnableToCompleteException e) { + // The error will have already been logged. Continue, since this could + // actually be a request for a static file that happens to have an + // unfortunately confusing name. } } @@ -492,15 +495,14 @@ * definition of "hosted mode" JavaScript hasn't been compiled at this point. */ private String genSelectionScript(TreeLogger logger, String moduleName, - boolean asScript) throws UnableToCompleteException { - String msg = asScript ? "Generating a script selection script for module " - : "Generating an html selection script for module "; - msg += moduleName; + SelectionScriptGenerator.Type type) throws UnableToCompleteException { + String msg = "Generating a(n) " + type + + " selection script for module " + moduleName; logger.log(TreeLogger.TRACE, msg, null); ModuleDef moduleDef = getModuleDef(logger, moduleName); SelectionScriptGenerator gen = new SelectionScriptGenerator(moduleDef); - return gen.generateSelectionScript(false, asScript); + return gen.generateSelectionScript(false, type); } /** Index: dev/core/src/com/google/gwt/dev/GWTCompiler.java =================================================================== --- dev/core/src/com/google/gwt/dev/GWTCompiler.java (revision 1523) +++ dev/core/src/com/google/gwt/dev/GWTCompiler.java (working copy) @@ -902,23 +902,15 @@ private void writeSelectionScripts(TreeLogger logger, SelectionScriptGenerator selGen) { - { - String html = selGen.generateSelectionScript(obfuscate, false); - String fn = module.getName() + ".nocache.js"; + for (SelectionScriptGenerator.Type type : + SelectionScriptGenerator.Type.values()) { + String content = selGen.generateSelectionScript(obfuscate, type); + String fn = module.getName() + type.getFilenameSuffix(); File selectionFile = new File(outDir, fn); - Util.writeStringAsFile(selectionFile, html); + Util.writeStringAsFile(selectionFile, content); String msg = "Compilation selection script written to " + selectionFile.getAbsolutePath(); logger.log(TreeLogger.TRACE, msg, null); } - { - String html = selGen.generateSelectionScript(obfuscate, true); - String fn = module.getName() + "-xs.nocache.js"; - File selectionFile = new File(outDir, fn); - Util.writeStringAsFile(selectionFile, html); - String msg = "Compilation selection script written to " - + selectionFile.getAbsolutePath(); - logger.log(TreeLogger.TRACE, msg, null); - } } } Index: dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js =================================================================== --- dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js (revision 1523) +++ dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js (working copy) @@ -88,6 +88,20 @@ } } + // How to dynamically inject an external script reference. + // All templates must provide a version of this function. + // + function insertScript(uri) { + $doc.write(''); + } + + // How to dynamically inject an external stylesheet reference. + // All templates must provide a version of this function. + // + function insertStylesheet(uri) { + $doc.write(''); + } + // Determine our own script's URL via magic :) // This function produces one side-effect, it sets base to the module's // base url. @@ -279,7 +293,7 @@ // __SHELL_SERVLET_ONLY_BEGIN__ // Force shell servlet to serve compiled output for web mode if (!isHostedMode()) { - $doc.write(''); + insertScript(base + '__MODULE_NAME__.nocache.js?compiled'); return; } Index: dev/core/src/com/google/gwt/dev/util/SelectionScriptGenerator.java =================================================================== --- dev/core/src/com/google/gwt/dev/util/SelectionScriptGenerator.java (revision 1523) +++ dev/core/src/com/google/gwt/dev/util/SelectionScriptGenerator.java (working copy) @@ -55,21 +55,88 @@ */ public class SelectionScriptGenerator { + /** + * Specifies which type of selection script should be generated. + */ + public enum Type { + + /** + * For hosted mode. + */ + SCRIPT("-xs"), + + /** + * For traditional HTML documents. + */ + HTML(""), + + /** + * For SVG documents. + */ + SVG("-svg"), + +// /** +// * For XHTML documents. +// */ +// XHTML("-xhtml"), + +// /** +// * For XUL documents. +// */ +// XUL("-xul"), + + ; + + /** + * Derive the {@link Type} based on a filename suffix. + * This is the inverse of {@link #getFilenameSuffix}. + * + *

+ * Note that some suffixes are suffixes of other suffixes, so give + * the "whole" suffix. + * + * @param suffix filename suffix + * @return corresponding {@link Type}, or null if none + */ + public static Type forFilenameSuffix(String suffix) { + for (Type type : values()) { + if (type.suffix.equals(suffix)) { + return type; + } + } + return null; + } + + private final String suffix; + + Type(String suffix) { + this.suffix = suffix; + } + + /** + * Get the filename suffix for generated scripts of this type. + */ + public String getFilenameSuffix() { + return suffix + ".nocache.js"; + } + + /** + * Get the resource suffix for the internal template for this type. + */ + public String getTemplateSuffix() { + return suffix + ".js"; + } + }; + private static String cssInjector(String cssUrl) { if (isRelativeURL(cssUrl)) { - return " if (!__gwt_stylesLoaded['" - + cssUrl - + "']) {\n" - + " __gwt_stylesLoaded['" - + cssUrl - + "'] = true;\n" - + " document.write('');\n" + " }\n"; + return " if (!__gwt_stylesLoaded['" + cssUrl + "']) {\n" + + " __gwt_stylesLoaded['" + cssUrl + "'] = true;\n" + + " insertStylesheet(base + '" + cssUrl + "');\n" + " }\n"; } else { return " if (!__gwt_stylesLoaded['" + cssUrl + "']) {\n" + " __gwt_stylesLoaded['" + cssUrl + "'] = true;\n" - + " document.write('');\n" + " }\n"; + + " insertStylesheet('" + cssUrl + "');\n" + " }\n"; } } @@ -114,19 +181,13 @@ private static String scriptInjector(String scriptUrl) { if (isRelativeURL(scriptUrl)) { - return " if (!__gwt_scriptsLoaded['" - + scriptUrl - + "']) {\n" - + " __gwt_scriptsLoaded['" - + scriptUrl - + "'] = true;\n" - + " document.write('');\n" + " }\n"; + return " if (!__gwt_scriptsLoaded['" + scriptUrl + "']) {\n" + + " __gwt_scriptsLoaded['" + scriptUrl + "'] = true;\n" + + " insertScript(base + '" + scriptUrl + "');\n" + " }\n"; } else { return " if (!__gwt_scriptsLoaded['" + scriptUrl + "']) {\n" + " __gwt_scriptsLoaded['" + scriptUrl + "'] = true;\n" - + " document.write('');\n" + " }\n"; + + " insertScript('" + scriptUrl + "');\n" + " }\n"; } } @@ -182,19 +243,21 @@ /** * Generates a selection script based on the current settings. * + * @param obfuscate whether to obfuscate the generated script + * @param type type of script to generate * @return an JavaScript whose contents are the definition of a module.js file */ - public String generateSelectionScript(boolean obfuscate, boolean asScript) { + public String generateSelectionScript(boolean obfuscate, Type type) { try { String rawSource; { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw, true); - String template = Utility.getFileFromClassPath(asScript - ? "com/google/gwt/dev/util/SelectionScriptTemplate-xs.js" - : "com/google/gwt/dev/util/SelectionScriptTemplate.js"); - genScript(pw, template); + String template = Utility.getFileFromClassPath( + "com/google/gwt/dev/util/SelectionScriptTemplate" + + type.getTemplateSuffix()); + genScript(pw, template, type); pw.close(); rawSource = sw.toString(); @@ -348,10 +411,11 @@ * * @param pw */ - private void genScript(PrintWriter mainPw, String template) { + private void genScript(PrintWriter mainPw, String template, Type type) { StringBuffer buf = new StringBuffer(template); replaceAll(buf, "__MODULE_FUNC__", moduleFunction); replaceAll(buf, "__MODULE_NAME__", moduleName); + replaceAll(buf, "__SCRIPT_SUFFIX__", type.getFilenameSuffix()); if (orderedProps != null) { // Remove shell servlet only stuff (hosted mode support) Index: dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-xs.js =================================================================== --- dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-xs.js (revision 1523) +++ dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-xs.js (working copy) @@ -77,6 +77,20 @@ } } + // How to dynamically inject an external script reference. + // All templates must provide a version of this function + // + function insertScript(uri) { + $doc.write(''); + } + + // How to dynamically inject an external stylesheet reference. + // All templates must provide a version of this function. + // + function insertStylesheet(uri) { + $doc.write(''); + } + // Determine our own script's URL via magic :) // function computeScriptBase() { @@ -255,12 +269,12 @@ // __SHELL_SERVLET_ONLY_BEGIN__ if (!isHostedMode()) { // Force shell servlet to serve compiled output for web mode - $doc.write(''); + insertScript(base + '__MODULE_NAME__-xs.nocache.js?compiled'); return; } else { // This script cannot run hosted mode properly; redirect to the html version // TODO: figure out how to run hosted mode in the main window - $doc.write(''); + insertScript(base + '__MODULE_NAME__.nocache.js'); return; } // __SHELL_SERVLET_ONLY_END__ @@ -309,7 +323,7 @@ // __MODULE_DEPS_BEGIN__ // Module dependencies, such as scripts and css // __MODULE_DEPS_END__ - $doc.write(''); + insertScript(base + strongName); } // Called from compiled code to hook the window's resize & load events (the Index: dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-svg.js =================================================================== --- dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-svg.js (revision 1520) +++ dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-svg.js (working copy) @@ -22,8 +22,8 @@ ,$doc = document ,external = $wnd.external - // These variables gate calling gwtOnLoad; all must be true to start - ,scriptsDone, loadDone, bodyDone + // Wait for body to load before inserting '); - markerScript = $doc.getElementById(markerId); + // Parse an URI + function parseURI(uri) { + var pat = /^(([^\/:?#]+):)?(\/\/([^\/?#]*))?([^?#]*)((\?([^#]*))?(#(.*))?)$/; + var parse = pat.exec(uri); + return { + scheme: parse[1] ? parse[1] : "", + authority: parse[3] ? parse[3] : "", + path: parse[5] ? parse[5] : "", + extra: parse[6] ? parse[6] : "" + }; + } - // Our script element is assumed to be the closest previous script element - // to the marker, so start at the marker and walk backwards until we find - // a script. - thisScript = markerScript && markerScript.previousSibling; - while (thisScript && thisScript.tagName != 'SCRIPT') { - thisScript = thisScript.previousSibling; + // Resolve an URI reference relative to a base URI + function resolveURI(base, uri) { + + // Parse uri + var uriParse = parseURI(uri); + + // If URI is absolute, we ignore base URI + if (uriParse.authority) { + return uri; + } + + // Combine base and uri paths + var baseParse = parseURI(base); + var path = uriParse.path && uriParse.path.match(/^\//) ? + uriParse.path : getDirectoryOfFile(baseParse.path) + uriParse.path; + + // Return combined URI + return baseParse.scheme + baseParse.authority + path + uriParse.extra; } + // Get the directory part of a path function getDirectoryOfFile(path) { var eq = path.lastIndexOf('/'); return (eq >= 0) ? path.substring(0, eq + 1) : ''; - }; + } - if (thisScript && thisScript.src) { - // Compute our base url - base = getDirectoryOfFile(thisScript.src); + // Get the last filename component of a path + function getBasenameOfFile(path) { + return path.substring(path.lastIndexOf('/') + 1); } - // Make the base URL absolute - if (base == '') { - // If there's a base tag, use it. - var baseElements = $doc.getElementsByTagName('base'); - if (baseElements.length > 0) { - // It's always the last parsed base tag that will apply to this script. - base = baseElements[baseElements.length - 1].href; - } else { - // No base tag; the base must be the same as the document location. - var loc = $doc.location; - var href = loc.href; - base = getDirectoryOfFile(href.substr(0, href.length - - loc.hash.length)); + // Determine the absolute base URI for an element. We must walk the + // parent chain looking for "xml:base" tags and apply them top down. + function getXMLBase(elem) { + + // Walk up to the document root gathering xml:base attributes + var bases = []; + for (node = elem; node; node = node.parentNode) { + if (node.getAttributeNS) { + var base = node.getAttributeNS(xmlNS, "base"); + if (base) { + bases.push(base); + } + } } - } else if ((base.match(/^\w+:\/\//))) { - // If the URL is obviously absolute, do nothing. - } else { - // Probably a relative URL; use magic to make the browser absolutify it. - // I wish there were a better way to do this, but this seems the only - // sure way! (A side benefit is it preloads clear.cache.gif) - // Note: this trick is harmless if the URL was really already absolute. - var img = $doc.createElement("img"); - img.src = base + 'clear.cache.gif'; - base = getDirectoryOfFile(img.src); + + // Now collapse them into a single base URI + var base = elem.ownerDocument.documentURI; + while (bases.length > 0) { + base = resolveURI(base, bases.pop()); + } + return base; } - if (markerScript) { - // remove the marker element - markerScript.parentNode.removeChild(markerScript); + // Find our script tag. We look for a '); + insertScript(base + '__MODULE_NAME____SCRIPT_SUFFIX__?compiled'); return; } @@ -322,56 +372,33 @@ // intentionally silent on property failure return; } - strongName += '.cache.html'; + strongName += '.cache.js'; } var onBodyDoneTimerId; function onBodyDone() { if (!bodyDone) { bodyDone = true; - maybeStartModule(); - if ($doc.removeEventListener) { $doc.removeEventListener("DOMContentLoaded", onBodyDone, false); } if (onBodyDoneTimerId) { clearInterval(onBodyDoneTimerId); } - } - } - var frameInjected; - function maybeInjectFrame() { - if (!frameInjected) { - frameInjected = true; - var iframe = $doc.createElement('iframe'); - // Prevents mixed mode security in IE6/7. - iframe.src = "javascript:''"; - iframe.id = "__MODULE_NAME__"; - iframe.style.cssText = "position:absolute;width:0;height:0;border:none"; - // Due to an IE6/7 refresh quirk, this must be an appendChild. - $doc.body.appendChild(iframe); - - /* - * The src has to be set after the iframe is attached to the DOM to avoid - * refresh quirks in Safari. - */ - iframe.src = base + strongName; + // Inject chosen script once DOM is fully loaded + insertScript(base + strongName, "__MODULE_NAME__"); } } // For everyone that supports DOMContentLoaded. if ($doc.addEventListener) { - $doc.addEventListener("DOMContentLoaded", function() { - maybeInjectFrame(); - onBodyDone(); - }, false); + $doc.addEventListener("DOMContentLoaded", onBodyDone, false); } // Fallback. If onBodyDone() gets fired twice, it's not a big deal. var onBodyDoneTimerId = setInterval(function() { - if (/loaded|complete/.test($doc.readyState)) { - maybeInjectFrame(); + if ($doc.getElementById('__MODULE_NAME__')) { onBodyDone(); } }, 50); @@ -379,7 +406,6 @@ // __MODULE_DEPS_BEGIN__ // Module dependencies, such as scripts and css // __MODULE_DEPS_END__ - $doc.write(''); } // Called from compiled code to hook the window's resize & load events (the Index: user/src/com/google/gwt/user/DocType.gwt.xml =================================================================== --- user/src/com/google/gwt/user/DocType.gwt.xml (revision 0) +++ user/src/com/google/gwt/user/DocType.gwt.xml (revision 0) @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Property changes on: user/src/com/google/gwt/user/DocType.gwt.xml ___________________________________________________________________ Name: svn:mime-type + text/xml Name: svn:keywords + Id Name: svn:eol-style + native