What I ended up doing was investigating a little further and finding that instead of adding the manifest reference in the host page I could add it to all permutation scripts. The manifest itself can be a generic (and practically empty) file:
Authors are encouraged to include the main page in the manifest also, but in practice the page that referenced the manifest is automatically cached even if it isn't explicitly mentioned. (source)
Thus, I wrote a simple Linker
that:
- Creates an offline manifest file
- Adds a
manifest
attribute to the<html>
tag of all permutation scripts (*.cache.html
).
The Linker
is called ManifestLinker
and is rather simple:
package com.example.linker;
// Imports
@LinkerOrder(Order.POST)
public class ManifestLinker extends AbstractLinker {
private static final String MANIFEST_FILE = "manifest.nocache.appcache";
private static final String HTML_FIND = "<html>";
private static final String HTML_REPLACE = "<html manifest=\"" + MANIFEST_FILE + "\">";
/* (non-Javadoc)
* @see com.google.gwt.core.ext.Linker#getDescription()
*/
@Override
public String getDescription() {
return "`Manifest Linker`: Adds AppCache support for static `.cache.html` resources.";
}
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException {
ArtifactSet output = new ArtifactSet(artifacts);
output.add(buildManifest(logger));
for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) {
if (artifact.getVisibility() == Visibility.Public && artifact.getPartialPath().endsWith(".cache.html")) {
logger.log(TreeLogger.TRACE, "Processing file: " + artifact.getPartialPath());
String cacheHtml = Util.readStreamAsString(artifact.getContents(logger));
if (cacheHtml.startsWith(HTML_FIND)) {
cacheHtml = HTML_REPLACE + cacheHtml.substring(HTML_FIND.length()); // Replace `<html>` tag.
output.replace(copyArtifact(logger, artifact, cacheHtml));
}
}
}
logger.log(TreeLogger.INFO, "Manifest created and linked successfully.");
return output;
}
private EmittedArtifact copyArtifact(TreeLogger logger, EmittedArtifact original, String contents) throws UnableToCompleteException {
EmittedArtifact copy = emitString(logger, contents, original.getPartialPath());
copy.setVisibility(original.getVisibility());
return copy;
}
private EmittedArtifact buildManifest(TreeLogger logger) throws UnableToCompleteException {
StringBuilder builder = new StringBuilder();
builder.append("CACHE MANIFEST\n")
.append("# Generated by ")
.append(getClass().getSimpleName())
.append(": ")
.append(System.currentTimeMillis())
.append(".\n\n")
.append("NETWORK:\n")
.append("*\n");
SyntheticArtifact manifest = emitString(logger, builder.toString(), MANIFEST_FILE);
return manifest;
}
}
In my <module>.gwt.xml
file I define and add the linker:
<?xml version="1.0" encoding="UTF-8"?>
<module>
...
<define-linker name="manifest" class="com.example.linker.ManifestLinker" />
<add-linker name="manifest" />
...
Also, I make sure that the right content type is set via my web.xml
:
...
<mime-mapping>
<extension>appcache</extension>
<mime-type>text/cache-manifest</mime-type>
</mime-mapping>
...
The emitted manifest.nocache.appcache
is simple too:
CACHE MANIFEST
# Generated by ManifestLinker: 1366702621298.
NETWORK:
*