Java tutorial
/* * Copyright 2010 Google Inc. * * 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.gwt.core.linker; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import com.google.gwt.core.ext.LinkerContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.core.ext.linker.CompilationResult; import com.google.gwt.core.ext.linker.ConfigurationProperty; import com.google.gwt.core.ext.linker.EmittedArtifact; import com.google.gwt.core.ext.linker.EmittedArtifact.Visibility; import com.google.gwt.core.ext.linker.LinkerOrder; import com.google.gwt.core.ext.linker.LinkerOrder.Order; import com.google.gwt.core.ext.linker.ScriptReference; import com.google.gwt.core.ext.linker.Shardable; import com.google.gwt.core.ext.linker.impl.PropertiesMappingArtifact; import com.google.gwt.core.ext.linker.impl.PropertiesUtil; import com.google.gwt.core.ext.linker.impl.ResourceInjectionUtil; import com.google.gwt.core.ext.linker.impl.SelectionScriptLinker; import com.google.gwt.dev.About; import com.google.gwt.dev.util.DefaultTextOutput; import com.google.gwt.dev.util.TextOutput; import com.google.gwt.thirdparty.guava.common.base.Joiner; import com.google.gwt.thirdparty.guava.common.base.Splitter; import com.google.gwt.util.tools.Utility; import com.google.gwt.util.tools.shared.StringUtils; import cc.alcina.framework.common.client.util.Ax; import cc.alcina.framework.entity.ResourceUtilities; /** * This linker uses an iframe to hold the code and a script tag to download the * code. It can download code cross-site, because it uses a script tag to * download it and because it never uses XHR. The iframe, meanwhile, makes it * trivial to install additional code as the app runs. */ @LinkerOrder(Order.PRIMARY) @Shardable public class CrossSiteIframeLinker extends SelectionScriptLinker { /** * A configuration property that can be used to have the linker ignore the * script tags in gwt.xml rather than fail to compile if they are present. */ private static final String FAIL_IF_SCRIPT_TAG_PROPERTY = "xsiframe.failIfScriptTag"; @Override public String getDescription() { return "Cross-Site-Iframe"; } private void writeMagicComments(DefaultTextOutput out, LinkerContext context, int fragmentId, String strongName) { String sourceMapUrl = getSourceMapUrl(context, strongName, fragmentId); if (sourceMapUrl != null) { // This magic comment determines where a browser debugger looks for // a sourcemap, // except that it may be overridden by a "SourceMap" header in the // HTTP response when // loading the JavaScript. // (Note: even if you're using the HTTP header, you still have to // set this to an arbitrary // value, or Chrome won't enable sourcemaps.) out.print("\n//# sourceMappingURL=" + sourceMapUrl + " "); } // This magic comment determines the name of the JavaScript fragment in // a browser debugger. // (In Chrome it typically shows up under "(no domain)".) // We need to set it explicitly because the JavaScript code may be // installed via an "eval" // statement and even if we're not using an eval, the filename contains // the strongname which // isn't stable across recompiles. out.print("\n//# sourceURL=" + context.getModuleName() + "-" + fragmentId + ".js\n"); } @Override protected String fillSelectionScriptTemplate(StringBuffer ss, TreeLogger logger, LinkerContext context, ArtifactSet artifacts, CompilationResult result) throws UnableToCompleteException { if (shouldUseSelfForWindowAndDocument(context)) { replaceAll(ss, "__WINDOW_DEF__", "self"); replaceAll(ss, "__DOCUMENT_DEF__", "self"); } else { replaceAll(ss, "__WINDOW_DEF__", "window"); replaceAll(ss, "__DOCUMENT_DEF__", "document"); } // Must do installScript before waitForBodyLoaded and we must do // waitForBodyLoaded before isBodyLoaded includeJs(ss, logger, getJsInstallScript(context), "__INSTALL_SCRIPT__"); includeJs(ss, logger, getJsWaitForBodyLoaded(context), "__WAIT_FOR_BODY_LOADED__"); includeJs(ss, logger, getJsIsBodyLoaded(context), "__IS_BODY_LOADED__"); // Must do permutations before providers includeJs(ss, logger, getJsPermutations(context), "__PERMUTATIONS__"); includeJs(ss, logger, getJsProperties(context), "__PROPERTIES__"); // Order doesn't matter for the rest includeJs(ss, logger, getJsProcessMetas(context), "__PROCESS_METAS__"); includeJs(ss, logger, getJsInstallLocation(context), "__INSTALL_LOCATION__"); includeJs(ss, logger, getJsComputeScriptBase(context), "__COMPUTE_SCRIPT_BASE__"); includeJs(ss, logger, getJsComputeUrlForResource(context), "__COMPUTE_URL_FOR_RESOURCE__"); includeJs(ss, logger, getJsLoadExternalStylesheets(context), "__LOAD_STYLESHEETS__"); includeJs(ss, logger, getJsRunAsync(context), "__RUN_ASYNC__"); includeJs(ss, logger, getJsDevModeRedirectHook(context), "__DEV_MODE_REDIRECT_HOOK__"); // Must go after __DEV_MODE_REDIRECT_HOOK__ because they're found in // DevModeRedirectHook.js. includeJs(ss, logger, getJsDevModeRedirectHookPermitted(context), "__DEV_MODE_REDIRECT_HOOK_PERMITTED__"); includeJs(ss, logger, getJsDevModeUrlValidation(context), "__DEV_MODE_URL_VALIDATION__"); // This Linker does not support <script> tags in the gwt.xml SortedSet<ScriptReference> scripts = artifacts.find(ScriptReference.class); if (!scripts.isEmpty()) { String list = ""; for (ScriptReference script : scripts) { list += (script.getSrc() + "\n"); } boolean failIfScriptTags = true; for (ConfigurationProperty prop : context.getConfigurationProperties()) { if (prop.getName().equalsIgnoreCase(FAIL_IF_SCRIPT_TAG_PROPERTY)) { if (prop.getValues().get(0).equalsIgnoreCase("false")) { failIfScriptTags = false; } } } if (failIfScriptTags) { // CHECKSTYLE_OFF String msg = "The " + getDescription() + " linker does not support <script> tags in the gwt.xml files, but the" + " gwt.xml file (or the gwt.xml files which it includes) contains the" + " following script tags: \n" + list + "In order for your application to run correctly, you will need to" + " include these tags in your host page directly. In order to avoid" + " this error, you will need to remove the script tags from the" + " gwt.xml file, or add this property to the gwt.xml file:" + " <set-configuration-property name='xsiframe.failIfScriptTag' value='FALSE'/>"; // CHECKSTYLE_ON logger.log(TreeLogger.ERROR, msg); throw new UnableToCompleteException(); } else { if (logger.isLoggable(TreeLogger.INFO)) { logger.log(TreeLogger.INFO, "Ignoring the following script tags in the gwt.xml " + "file\n" + list); } } } ss = ResourceInjectionUtil.injectStylesheets(ss, artifacts); ss = permutationsUtil.addPermutationsJs(ss, logger, context); if (result != null) { replaceAll(ss, "__KNOWN_PROPERTIES__", PropertiesUtil.addKnownPropertiesJs(logger, result)); } replaceAll(ss, "__MODULE_FUNC__", context.getModuleFunctionName()); replaceAll(ss, "__MODULE_NAME__", context.getModuleName()); replaceAll(ss, "__HOSTED_FILENAME__", getHostedFilenameFull(context)); if (context.isOutputCompact()) { replaceAll(ss, "__START_OBFUSCATED_ONLY__", ""); replaceAll(ss, "__END_OBFUSCATED_ONLY__", ""); } else { replaceAll(ss, "__START_OBFUSCATED_ONLY__", "/*"); replaceAll(ss, "__END_OBFUSCATED_ONLY__", "*/"); } String jsModuleFunctionErrorCatch = getJsModuleFunctionErrorCatch(context); if (jsModuleFunctionErrorCatch != null) { replaceAll(ss, "__BEGIN_TRY_BLOCK__", "try {"); replaceAll(ss, "__END_TRY_BLOCK_AND_START_CATCH__", "} catch (moduleError) {"); includeJs(ss, logger, jsModuleFunctionErrorCatch, "__MODULE_FUNC_ERROR_CATCH__"); replaceAll(ss, "__END_CATCH_BLOCK__", "}"); } else { replaceAll(ss, "__BEGIN_TRY_BLOCK__", ""); replaceAll(ss, "__END_TRY_BLOCK_AND_START_CATCH__", ""); replaceAll(ss, "__MODULE_FUNC_ERROR_CATCH__", ""); replaceAll(ss, "__END_CATCH_BLOCK__", ""); } return ss.toString(); } protected boolean getBooleanConfigurationProperty(LinkerContext context, String name, boolean def) { String value = getStringConfigurationProperty(context, name, null); if (value != null) { if (value.equalsIgnoreCase("true")) { return true; } else if (value.equalsIgnoreCase("false")) { return false; } } return def; } @Override protected String getCompilationExtension(TreeLogger logger, LinkerContext context) { return ".cache.js"; } @Override protected String getDeferredFragmentSuffix2(TreeLogger logger, LinkerContext context, int fragment, String strongName) { DefaultTextOutput out = new DefaultTextOutput(context.isOutputCompact()); writeMagicComments(out, context, fragment, strongName); return out.toString(); } @Override protected String getHostedFilename() { return "devmode.js"; } protected String getHostedFilenameFull(LinkerContext context) { return context.getModuleName() + "." + getHostedFilename(); } /** * Returns the name of the {@code ComputeScriptBase} script. By default, * returns * {@code "com/google/gwt/core/ext/linker/impl/computeScriptBase.js"}. * * @param context * a LinkerContext */ protected String getJsComputeScriptBase(LinkerContext context) { return getStringConfigurationProperty(context, "computeScriptBaseJs", "com/google/gwt/core/ext/linker/impl/computeScriptBase.js"); } /** * Returns the name of the {@code UrlForResource} script. By default, * returns * {@code "com/google/gwt/core/ext/linker/impl/computeUrlForResource.js"}. * * @param context * a LinkerContext */ protected String getJsComputeUrlForResource(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/computeUrlForResource.js"; } /** * Returns a JavaScript fragment that starts Super Dev Mode, if enabled. * (May return either the JavaScript itself or the name of a Java resource * ending with ".js".) */ protected String getJsDevModeRedirectHook(LinkerContext context) { if (shouldUseSelfForWindowAndDocument(context)) { // Probably a Web Worker. Super Dev Mode isn't supported. return ""; } // Enable Super Dev Mode for this app if the devModeRedirectEnabled // config property is true. if (getBooleanConfigurationProperty(context, "devModeRedirectEnabled", true)) { return "com/google/gwt/core/linker/DevModeRedirectHook.js"; } else { return ""; } } /** * Returns a JavaScript expression that determines whether Super Dev Mode * may be turned on for the current page. (May return either the JavaScript * itself or the name of a Java resource ending with ".js".) * * <p> * The default implementation allows Super Dev Mode only on http: and file: * pages. It could be overridden to implement a blacklist or whitelist of * hostnames where Super Dev Mode may run. As a safety precaution, it's * recommended to return false for https and for the hostnames in URL's * visited by end users. * * <p> * If you override this method to allow https, it probably won't work anyway * because browsers often disallow loading JavaScript from http URL's into * https pages. To make it work, you will also have to find a way to run the * code server using https. */ protected String getJsDevModeRedirectHookPermitted(LinkerContext context) { return "$wnd.location.protocol == \"http:\" || $wnd.location.protocol == \"file:\""; } /** * Returns a JavaScript fragment that validates "devModeUrl"—the URL * that Super Dev Mode's JavaScript is loaded from—before it's used. * The variable may be modified to change what what URL is loaded, or it * maybe be set to "" to disable completely. (May return either the * JavaScript itself or the name of a Java resource ending with ".js".) */ protected String getJsDevModeUrlValidation(LinkerContext context) { // As a default, if the user provides devModeUrlWhitelistRegexp, then we // verify that it // matches devModeUrl. String regexp = getStringConfigurationProperty(context, "devModeUrlWhitelistRegexp", "http://(localhost|127\\.0\\.0\\.1)(:\\d+)?/.*"); if (!regexp.isEmpty()) { return "" + "if (!/^" + regexp.replace("/", "\\/") + "$/.test(devModeUrl)) {\n" + " if (devModeUrl && window.console && console.log) {\n" + " console.log('Ignoring non-whitelisted Dev Mode URL: ' + devModeUrl);\n" + " }\n" + " devModeUrl = '';" + "}"; } return ""; } /** * Returns the name of the {@code JsInstallLocation} script. By default, * returns * {@code "com/google/gwt/core/ext/linker/impl/installLocationIframe.js"}. * * @param context * a LinkerContext */ protected String getJsInstallLocation(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/installLocationIframe.js"; } /** * Returns the name of the {@code JsInstallScript} script. The default is * chosen based on the value of {@link #shouldInstallCode}. * * <p> * If you override this, verify that {@link #shouldInstallCode} is set * consistently or fragment loading won't work. */ protected String getJsInstallScript(LinkerContext context) { String defaultScript = "com/google/gwt/core/ext/linker/impl/installScriptDirect.js"; if (shouldInstallCode(context)) { defaultScript = "com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js"; } return getStringConfigurationProperty(context, "installScriptJs", defaultScript); } /** * Returns the name of the {@code JsIsBodyLoaded} script. By default, * returns {@code "com/google/gwt/core/ext/linker/impl/isBodyLoaded.js"}. * * @param context * a LinkerContext */ protected String getJsIsBodyLoaded(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/isBodyLoaded.js"; } /** * Returns the name of the {@code JsLoadExternalStylesheets} script. By * default, returns * {@code "com/google/gwt/core/ext/linker/impl/loadExternalStylesheets.js"}. * * @param context * a LinkerContext */ protected String getJsLoadExternalStylesheets(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/loadExternalStylesheets.js"; } /** * Returns the name of the {@code JsModuleFunctionErrorCatch} script. By * default returns null. This script executes if there's an error loading * the module function or executing it. The error will be available under a * local variable named "moduleError". If non-null, the module function and * the call to the module function will be placed in a try/catch block. * * @param context * a LinkerContext */ protected String getJsModuleFunctionErrorCatch(LinkerContext context) { return null; } /** * Returns the name of the {@code JsPermutations} script. By default, * returns {@code "com/google/gwt/core/ext/linker/impl/permutations.js"}. * * @param context * a LinkerContext */ protected String getJsPermutations(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/permutations.js"; } /** * Returns the name of the {@code JsProcessMetas} script. By default, * returns {@code "com/google/gwt/core/ext/linker/impl/processMetas.js"}. * * @param context * a LinkerContext */ protected String getJsProcessMetas(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/processMetas.js"; } /** * Returns the name of the {@code JsProperties} script. By default, returns * {@code "com/google/gwt/core/ext/linker/impl/properties.js"}. * * @param context * a LinkerContext */ protected String getJsProperties(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/properties.js"; } /** * Returns the name of the {@code JsRunAsync} script. By default, returns * {@code "com/google/gwt/core/ext/linker/impl/runAsync.js"}. * * @param context * a LinkerContext */ protected String getJsRunAsync(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/runAsync.js"; } /** * Returns the name of the {@code JsWaitForBodyLoaded} script. By default, * returns * {@code "com/google/gwt/core/ext/linker/impl/waitForBodyLoaded.js"}. * * @param context * a LinkerContext */ protected String getJsWaitForBodyLoaded(LinkerContext context) { return "com/google/gwt/core/ext/linker/impl/waitForBodyLoaded.js"; } @Override protected String getModulePrefix(TreeLogger logger, LinkerContext context, String strongName) throws UnableToCompleteException { TextOutput out = new DefaultTextOutput(context.isOutputCompact()); // Note: this code is included in both the primary fragment and // devmode.js // $wnd is the main window that the GWT code will affect and also the // location where the bootstrap function was defined. In iframe-based // linkers, // $wnd is set to window.parent. Usually, in others, $wnd = window. // By default, $wnd is not set when the module starts, but a replacement // for // installLocationIframe.js may set it. out.print("var $wnd = $wnd || window.parent;"); out.newlineOpt(); out.print("var __gwtModuleFunction = $wnd." + context.getModuleFunctionName() + ";"); out.newlineOpt(); out.print("var $sendStats = __gwtModuleFunction.__sendStats;"); out.newlineOpt(); out.print("$sendStats('moduleStartup', 'moduleEvalStart');"); out.newlineOpt(); out.print("var $gwt_version = \"" + About.getGwtVersionNum() + "\";"); out.newlineOpt(); out.print("var $strongName = '" + strongName + "';"); out.newlineOpt(); out.print("var $gwt = {};"); out.newlineOpt(); out.print("var $doc = $wnd.document;"); out.newlineOpt(); out.print("var $moduleName, $moduleBase;"); out.newlineOpt(); // The functions for runAsync are set up in the bootstrap script so they // can be overridden in the same way as other bootstrap code is, however // they will be called from, and expected to run in the scope of the GWT // code // (usually an iframe) so, here we set up those pointers. out.print("function __gwtStartLoadingFragment(frag) {"); out.newlineOpt(); String fragDir = getFragmentSubdir(logger, context) + '/'; out.print("var fragFile = '" + fragDir + "' + $strongName + '/' + frag + '" + FRAGMENT_EXTENSION + "';"); out.newlineOpt(); out.print("return __gwtModuleFunction.__startLoadingFragment(fragFile);"); out.newlineOpt(); out.print("}"); out.newlineOpt(); out.print("function __gwtInstallCode(code) {return __gwtModuleFunction.__installRunAsyncCode(code);}"); out.newlineOpt(); // The functions for property access are set up in the bootstrap script // however // they will be called from, and expected to run in the scope of the GWT // code // (usually an iframe) so, here we set up those pointers. out.print("function __gwt_isKnownPropertyValue(propName, propValue) {"); out.newlineOpt(); out.print("return __gwtModuleFunction.__gwt_isKnownPropertyValue(propName, propValue);"); out.newlineOpt(); out.print("}"); out.newlineOpt(); out.print("function __gwt_getMetaProperty(name) {"); out.newlineOpt(); out.print("return __gwtModuleFunction.__gwt_getMetaProperty(name);"); out.newlineOpt(); out.print("}"); out.newlineOpt(); // Even though we call the $sendStats function in the code written in // this // linker, some of the compilation code still needs the $stats and // $sessionId // variables to be available. out.print("var $stats = $wnd.__gwtStatsEvent ? function(a) {"); out.newlineOpt(); out.print("return $wnd.__gwtStatsEvent && $wnd.__gwtStatsEvent(a);"); out.newlineOpt(); out.print("} : null;"); out.newlineOpt(); out.print("var $sessionId = $wnd.__gwtStatsSessionId ? $wnd.__gwtStatsSessionId : null;"); out.newlineOpt(); return out.toString(); } @Override protected String getModuleSuffix2(TreeLogger logger, LinkerContext context, String strongName) { // Note: this method won't be called if getModuleSuffix() is overridden // and returns non-null. // Note: this code is included in both the primary fragment and // devmode.js. DefaultTextOutput out = new DefaultTextOutput(context.isOutputCompact()); out.print("$sendStats('moduleStartup', 'moduleEvalEnd');"); out.newlineOpt(); out.print("gwtOnLoad(" + "__gwtModuleFunction.__errFn, " + "__gwtModuleFunction.__moduleName, " + "__gwtModuleFunction.__moduleBase, " + "__gwtModuleFunction.__softPermutationId," + "__gwtModuleFunction.__computePropValue);"); out.newlineOpt(); out.print("$sendStats('moduleStartup', 'end');"); out.newlineOpt(); out.print("$gwt && $gwt.permProps && __gwtModuleFunction.__moduleStartupDone($gwt.permProps);"); writeMagicComments(out, context, 0, strongName); return out.toString(); } @Override protected String getScriptChunkSeparator(TreeLogger logger, LinkerContext context) { return shouldInstallCode(context) ? "__SCRIPT_CHUNK_SEPARATOR_MARKER__" : ""; } @Override protected String getSelectionScriptTemplate(TreeLogger logger, LinkerContext context) { return "com/google/gwt/core/linker/CrossSiteIframeTemplate.js"; } /** * Returns the sourcemap URL that will be put in the comment at the end of a * JavaScript fragment, or null if the comment should be omitted. The * default implementation uses the includeSourceMapUrl config property. */ protected String getSourceMapUrl(LinkerContext context, String strongName, int fragmentId) { String val = getStringConfigurationProperty(context, "includeSourceMapUrl", "false"); if ("false".equalsIgnoreCase(val)) { return null; } if ("true".equalsIgnoreCase(val)) { return SymbolMapsLinker.SourceMapArtifact.sourceMapFilenameForFragment(fragmentId); } return val.replaceAll("__HASH__", strongName).replaceAll("__FRAGMENT__", String.valueOf(fragmentId)) .replaceAll("__MODULE__", context.getModuleName()); } protected String getStringConfigurationProperty(LinkerContext context, String name, String def) { for (ConfigurationProperty property : context.getConfigurationProperties()) { if (property.getName().equals(name) && property.getValues().size() > 0) { if (property.getValues().get(0) != null) { return property.getValues().get(0); } } } return def; } protected void includeJs(StringBuffer selectionScript, TreeLogger logger, String jsSource, String templateVar) throws UnableToCompleteException { String js; if (jsSource.endsWith(".js")) { try { js = Utility.getFileFromClassPath(jsSource); } catch (IOException e) { logger.log(TreeLogger.ERROR, "Unable to read file: " + jsSource, e); throw new UnableToCompleteException(); } } else { js = jsSource; } replaceAll(selectionScript, templateVar, js); } @Override protected void maybeAddHostedModeFile(TreeLogger logger, LinkerContext context, ArtifactSet artifacts, CompilationResult result) throws UnableToCompleteException { String filename = getHostedFilename(); if ("".equals(filename)) { return; } // when we're including bootstrap in the primary fragment, we should be // generating devmode files for each permutation. Otherwise, we generate // it // only in the final link stage. boolean isSinglePermutation = (result != null); if (isSinglePermutation != shouldIncludeBootstrapInPrimaryFragment(context)) { return; } long lastModified = System.currentTimeMillis(); StringBuffer buffer = readFileToStringBuffer("com/google/gwt/core/ext/linker/impl/" + filename, logger); boolean injectJsCodeServerFiles = true; if (injectJsCodeServerFiles) { String[] scriptNames = { "GwtJsPlugin.js", "common/Message.js", "common/BrowserChannel.js", "impl/JavaObject.js", "impl/ScriptableInstance.js", "impl/LocalObjectTable.js", "common/ieee754.js", "common/Platform.js", "common/HashMap.js", "common/FatalErrorMessage.js", "common/HostChannel.js", "common/InvokeMessage.js", "common/LoadModuleMessage.js", "common/InvokeSpecialMessage.js", "common/AllowedConnections.js", "common/DebugLevel.js", "common/Socket.js", "common/Debug.js", "common/QuitMessage.js", "common/SwitchTransportMessage.js", "common/ProtocolVersionMessage.js", "common/ChooseTransportMessage.js", "common/SessionHandler.js", "common/ByteOrder.js", "common/ReturnMessage.js", "common/ServerMethods.js", "common/LoadJsniMessage.js", "common/Value.js", "common/CheckVersionsMessage.js", "common/FreeValueMessage.js" }; for (String fn : scriptNames) { String path = Ax .format("/g/alcina/framework/jscodeserver/src/cc/alcina/framework/jscodeserver/js/%s", fn); buffer.append("\n"); buffer.append(ResourceUtilities.read(path)); buffer.append("\n"); } } // Ax.out(buffer); String outputFilename = filename; if (result != null) { // If we're including bootstrap in the primary fragment, we generate // a // devmode.js for each permutation, and it's name is XXX.devmode.js, // where XXX is the md5 hash for this permutation outputFilename = result.getStrongName() + "." + outputFilename; } else { // If we're not including bootstrap in the primary fragment, we // generate // a devmode.js for this module and it's name is XXX.devmode.js, // where // XXX is the module name. outputFilename = getHostedFilenameFull(context); } replaceAll(buffer, "__MODULE_NAME__", context.getModuleName()); String script = generatePrimaryFragmentString(logger, context, result, buffer.toString(), 1, artifacts); EmittedArtifact devArtifact = emitString(logger, script, outputFilename, lastModified); artifacts.add(devArtifact); } // Output compilation-mappings.txt @Override protected void maybeOutputPropertyMap(TreeLogger logger, LinkerContext context, ArtifactSet toReturn) { if (permutationsUtil.getPermutationsMap() == null || permutationsUtil.getPermutationsMap().isEmpty()) { return; } PropertiesMappingArtifact mappingArtifact = new PropertiesMappingArtifact(CrossSiteIframeLinker.class, permutationsUtil.getPermutationsMap()); toReturn.add(mappingArtifact); EmittedArtifact serializedMap; try { String mappings = mappingArtifact.getSerialized(); // CHECKSTYLE_OFF mappings = mappings.concat("Devmode:" + getHostedFilename()); // CHECKSTYLE_ON serializedMap = emitString(logger, mappings, "compilation-mappings.txt"); // TODO(unnurg): make this Deploy serializedMap.setVisibility(Visibility.Public); toReturn.add(serializedMap); } catch (UnableToCompleteException e) { e.printStackTrace(); } } // If you set this to return true, you should also override // getJsPermutations() to return permutationsNull.js and // getJsInstallScript() to return installScriptAlreadyIncluded.js protected boolean shouldIncludeBootstrapInPrimaryFragment(LinkerContext context) { return false; } /** * Determines the strategy for installing JavaScript code into the iframe. * If set to false, a <script> tag pointing to the js file is added * directly to the iframe. Otherwise, GWT downloads the JavaScript code as a * list of strings and then adds it to the iframe. */ protected boolean shouldInstallCode(LinkerContext context) { return getBooleanConfigurationProperty(context, "installCode", context.isOutputCompact()); } /** * Returns whether to use "self" for $wnd and $doc references. Defaults to * false. Useful for worker threads. */ protected boolean shouldUseSelfForWindowAndDocument(LinkerContext context) { return false; } @Override protected String wrapDeferredFragment(TreeLogger logger, LinkerContext context, int fragment, String js, ArtifactSet artifacts) { // TODO(unnurg): This assumes that the xsiframe linker is using the // ScriptTagLoadingStrategy (since it is also xs compatible). However, // it should be completely valid to use the XhrLoadingStrategy with this // linker, in which case we would not want to wrap the deferred fragment // in this way. Ideally, we should make a way for this code to be // dependent // on what strategy is being used. Otherwise, we should make a property // which // users can set to turn this wrapping off if they override the loading // strategy. return String.format("$wnd.%s.runAsyncCallback%d(%s)\n", context.getModuleFunctionName(), fragment, StringUtils.javaScriptString(js)); } @Override protected String wrapPrimaryFragment(TreeLogger logger, LinkerContext context, String script, ArtifactSet artifacts, CompilationResult result) throws UnableToCompleteException { StringBuilder out = new StringBuilder(); if (shouldIncludeBootstrapInPrimaryFragment(context)) { out.append(generateSelectionScript(logger, context, artifacts, result)); // only needed in SSSS, breaks WebWorker linker if done all the time out.append("if (" + context.getModuleFunctionName() + ".succeeded) {\n"); } if (shouldInstallCode(context)) { // Rewrite the code so it can be installed with // __MODULE_FUNC__.onScriptDownloaded out.append(context.getModuleFunctionName()); out.append(".onScriptDownloaded(["); Iterable<String> chunks = Splitter.on(getScriptChunkSeparator(logger, context)).split(script); List<String> newChunks = new ArrayList<String>(); for (String chunk : chunks) { newChunks.add(StringUtils.javaScriptString(chunk)); } out.append(Joiner.on(",\n").join(newChunks)); out.append("]);\n"); } else { out.append(script); out.append("\n"); } if (shouldIncludeBootstrapInPrimaryFragment(context)) { out.append("}\n"); } return out.toString(); } }