From b7df6c1480d5745df1ae3f6bcf658e81955cc5d4 Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Fri, 19 Feb 2010 16:47:17 -0800 Subject: [PATCH] Fix support for MHTML. This uses feature detection to determine if DataURLs, and then if MHTML is supported, and falls back on the original unsprited images. Closes #376. --- Objective-J/CFBundle.js | 64 ++++++++----- .../objective-j/jake/RESOURCES/MHTMLTest.txt | 9 ++ .../lib/objective-j/jake/bundletask.js | 90 ++++++++++--------- 3 files changed, 96 insertions(+), 67 deletions(-) create mode 100644 Objective-J/CommonJS/lib/objective-j/jake/RESOURCES/MHTMLTest.txt diff --git a/Objective-J/CFBundle.js b/Objective-J/CFBundle.js index a516c722e..a992f2fcf 100644 --- a/Objective-J/CFBundle.js +++ b/Objective-J/CFBundle.js @@ -283,7 +283,7 @@ function loadExecutableForBundle(/*Bundle*/ aBundle, success, failure) { try { - decompileStaticFile(aBundle, anEvent.request.responseText()); + decompileStaticFile(aBundle, anEvent.request.responseText(), aBundle.executablePath()); aBundle._loadStatus &= ~CFBundleLoadingExecutable; success(); } @@ -319,7 +319,7 @@ function loadSpritedImagesForBundle(/*Bundle*/ aBundle, success, failure) { try { - decompileStaticFile(aBundle, anEvent.request.responseText()); + decompileStaticFile(aBundle, anEvent.request.responseText(), spritedImagesPath); aBundle._loadStatus &= ~CFBundleLoadingSpritedImages; success(); } @@ -356,9 +356,10 @@ function CFBundleTestSpriteSupport(/*String*/ MHTMLPath, /*Function*/ aCallback) CFBundleDataURLSpriteType, "data:image/gif;base64,R0lGODlhAQABAIAAAMc9BQAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==", CFBundleMHTMLSpriteType, - MHTMLPath, + MHTMLPath+"!test", CFBundleMHTMLUncachedSpriteType, - MHTMLPath+"?"+CFCacheBuster]); + MHTMLPath+"?"+CFCacheBuster+"!test" + ]); } function CFBundleNotifySpriteSupportListeners() @@ -371,6 +372,13 @@ function CFBundleNotifySpriteSupportListeners() function CFBundleTestSpriteTypes(/*Array*/ spriteTypes) { + if (spriteTypes.length < 2) + { + CFBundleSupportedSpriteType = CFBundleNoSpriteType; + CFBundleNotifySpriteSupportListeners(); + return; + } + var image = new Image(); image.onload = function() @@ -386,21 +394,25 @@ function CFBundleTestSpriteTypes(/*Array*/ spriteTypes) image.onerror = function() { - if (spriteTypes.length === 2) - { - CFBundleSupportedSpriteType = CFBundleNoSpriteType; - CFBundleNotifySpriteSupportListeners(); - } - else - CFBundleTestSpriteTypes(spriteTypes.slice(2)); + CFBundleTestSpriteTypes(spriteTypes.slice(2)); } image.src = spriteTypes[1]; } +function mhtmlBasePath() +{ +#ifdef BROWSER + //FIXME: URL stuff is kind of broken + return window.location.protocol + "//" + window.location.hostname + (window.location.port ? (":" + window.location.port) : ""); +#else + return ""; +#endif +} + function spritedImagesTestPathForBundle(/*Bundle*/ aBundle) { - return FILE.join(aBundle.path(), aBundle.mostEligibleEnvironment() + ".environment", "MHTMLTest.txt"); + return "mhtml:" + mhtmlBasePath() + FILE.join(aBundle.path(), aBundle.mostEligibleEnvironment() + ".environment", "MHTMLTest.txt"); } function spritedImagesPathForBundle(/*Bundle*/ aBundle) @@ -408,12 +420,9 @@ function spritedImagesPathForBundle(/*Bundle*/ aBundle) if (CFBundleSupportedSpriteType === CFBundleDataURLSpriteType) return FILE.join(aBundle.path(), aBundle.mostEligibleEnvironment() + ".environment", "dataURLs.txt"); - if (CFBundleSupportedSpriteType === CFBundleMHTMLSpriteType) - return FILE.join(aBundle.path(), aBundle.mostEligibleEnvironment() + ".environment", "MHTML.txt"); + if (CFBundleSupportedSpriteType === CFBundleMHTMLSpriteType || CFBundleSupportedSpriteType === CFBundleMHTMLUncachedSpriteType) + return mhtmlBasePath() + FILE.join(aBundle.path(), aBundle.mostEligibleEnvironment() + ".environment", "MHTMLPaths.txt"); - if (CFBundleSupportedSpriteType === CFBundleMHTMLUncachedSpriteType) - return FILE.join(aBundle.path(), aBundle.mostEligibleEnvironment() + ".environment", "MHTML.txt?" + CFCacheBuster); - return NULL; } @@ -464,8 +473,7 @@ function executeBundle(/*Bundle*/ aBundle, /*Function*/ aCallback) var children = staticResource.children(); for (var name in children) - if (hasOwnProperty.call(children, name)) - staticResources.push(children[name]); + staticResources.push(children[name]); } } @@ -483,15 +491,15 @@ var STATIC_MAGIC_NUMBER = "@STATIC", MARKER_IMPORT_STD = 'I', MARKER_IMPORT_LOCAL = 'i'; -function decompileStaticFile(/*Bundle*/ aBundle, /*String*/ aString) +function decompileStaticFile(/*Bundle*/ aBundle, /*String*/ aString, /*String*/ aPath) { var stream = new MarkedStream(aString); if (stream.magicNumber() !== STATIC_MAGIC_NUMBER) - throw new Error("Could not read static file."); + throw new Error("Could not read static file: "+aPath); if (stream.version() !== "1.0") - throw new Error("Could not read static file."); + throw new Error("Could not read static file: "+aPath); var marker, bundlePath = aBundle.path(), @@ -514,8 +522,18 @@ function decompileStaticFile(/*Bundle*/ aBundle, /*String*/ aString) var URI = stream.getString(); if (URI.toLowerCase().indexOf("mhtml:") === 0) - URI = "mhtml:" + FILE.join(bundlePath, URI.substr("mhtml:".length)); + { + URI = "mhtml:" + mhtmlBasePath() + FILE.join(bundlePath, URI.substr("mhtml:".length)); + if (CFBundleSupportedSpriteType === CFBundleMHTMLUncachedSpriteType) + { + var exclamationIndex = URI.indexOf("!"), + firstPart = URI.substring(0, exclamationIndex), + lastPart = URI.substring(exclamationIndex); + + URI = firstPart + "?" + CFCacheBuster + lastPart; + } + } aBundle._URIMap[text] = URI; // The unresolved directories must not be bundles. diff --git a/Objective-J/CommonJS/lib/objective-j/jake/RESOURCES/MHTMLTest.txt b/Objective-J/CommonJS/lib/objective-j/jake/RESOURCES/MHTMLTest.txt new file mode 100644 index 000000000..6d8ab99f5 --- /dev/null +++ b/Objective-J/CommonJS/lib/objective-j/jake/RESOURCES/MHTMLTest.txt @@ -0,0 +1,9 @@ +/* +Content-Type: multipart/related; boundary="_SEPARATOR_" + +--_SEPARATOR_ +Content-Location:test +Content-Transfer-Encoding:base64 + +R0lGODlhAQABAIAAAMc9BQAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw== +*/ \ No newline at end of file diff --git a/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js b/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js index 24d618d13..e59951ca8 100644 --- a/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js +++ b/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js @@ -317,7 +317,17 @@ BundleTask.prototype.buildProductStaticPathForEnvironment = function(anEnvironme BundleTask.prototype.buildProductMHTMLPathForEnvironment = function(anEnvironment) { - return FILE.join(this.buildProductPath(), anEnvironment.name() + ".environment", "MHTML.txt"); + return FILE.join(this.buildProductPath(), anEnvironment.name() + ".environment", "MHTMLPaths.txt"); +} + +BundleTask.prototype.buildProductMHTMLDataPathForEnvironment = function(anEnvironment) +{ + return FILE.join(this.buildProductPath(), anEnvironment.name() + ".environment", "MHTMLData.txt"); +} + +BundleTask.prototype.buildProductMHTMLTestPathForEnvironment = function(anEnvironment) +{ + return FILE.join(this.buildProductPath(), anEnvironment.name() + ".environment", "MHTMLTest.txt"); } BundleTask.prototype.buildProductDataURLPathForEnvironment = function(anEnvironment) @@ -460,8 +470,7 @@ BundleTask.prototype.defineResourceTask = function(aResourcePath, aDestinationPa FILE.write(spritedDestinationPath, base64.encode(FILE.read(aResourcePath, "b")), { charset:"UTF-8" }); }); - filedir (this.buildProductDataURLPathForEnvironment(anEnvironment), [spritedDestinationPath]); - filedir (this.buildProductMHTMLPathForEnvironment(anEnvironment), [spritedDestinationPath]); + task (anEnvironment.name() + "-sprites", [spritedDestinationPath]); }, this); } @@ -571,6 +580,10 @@ BundleTask.prototype.defineResourceTasks = function() }, this); } + +var RESOURCES_PATH = FILE.join(FILE.absolute(FILE.dirname(module.path)), "RESOURCES"), + MHTMLTestPath = FILE.join(RESOURCES_PATH, "MHTMLTest.txt"); + BundleTask.prototype.defineSpritedImagesTask = function() { this.environments().forEach(function(/*Environment*/ anEnvironment) @@ -580,16 +593,23 @@ BundleTask.prototype.defineSpritedImagesTask = function() var folder = anEnvironment.name() + ".environment", resourcesPath = FILE.join(this.buildIntermediatesProductPath(), folder, "Resources", ""), - productName = this.productName(), - dataURLPath = this.buildProductDataURLPathForEnvironment(anEnvironment); + dataURLPath = this.buildProductDataURLPathForEnvironment(anEnvironment), + MHTMLPath = this.buildProductMHTMLPathForEnvironment(anEnvironment), + MHTMLDataPath = this.buildProductMHTMLDataPathForEnvironment(anEnvironment), + MHTMLTestDestinationPath = this.buildProductMHTMLTestPathForEnvironment(anEnvironment), + productName = this.productName(); - filedir (dataURLPath, function(aTask) + task(anEnvironment.name() + "-sprites", function(aTask) { - TERM.stream.print("Creating data URLs file... \0green(" + dataURLPath + "\0)"); + TERM.stream.print("Creating sprited images file... \0green(" + dataURLPath +"\0)"); - var dataURLStream = FILE.open(dataURLPath, "w+", { charset:"UTF-8" }); + var dataURLStream = FILE.open(dataURLPath, "w+", { charset:"UTF-8" }), + MHTMLStream = FILE.open(MHTMLPath, "w+", { charset:"UTF-8" }), + MHTMLDataStream = FILE.open(MHTMLDataPath, "w+", { charset:"UTF-8" }); dataURLStream.write("@STATIC;1.0;"); + MHTMLStream.write("@STATIC;1.0;"); + MHTMLDataStream.write("/*\r\nContent-Type: multipart/related; boundary=\"_ANY_STRING_WILL_DO_AS_A_SEPARATOR\"\r\n\r\n"); aTask.prerequisites().forEach(function(aFilename) { @@ -599,56 +619,39 @@ BundleTask.prototype.defineSpritedImagesTask = function() var resourcePath = "Resources/" + FILE.relative(resourcesPath, aFilename); dataURLStream.write("u;" + resourcePath.length + ";" + resourcePath); + MHTMLStream.write("u;" + resourcePath.length + ";" + resourcePath); + // As data URL... var contents = "data:" + mimeType(aFilename) + ";base64," + FILE.read(aFilename, "b").decodeToString("UTF-8"); dataURLStream.write(contents.length + ";" + contents); - }); - - dataURLStream.write("e;"); - dataURLStream.close(); - }); - - this.enhance([dataURLPath]); - - var MHTMLPath = this.buildProductMHTMLPathForEnvironment(anEnvironment); - - filedir (MHTMLPath, function(aTask) - { - TERM.stream.print("Creating MHTML images file... \0green(" + MHTMLPath +"\0)"); - - var MHTMLStream = FILE.open(MHTMLPath, "w+", { charset:"UTF-8" }), - MHTMLContents = "/*\r\nContent-Type: multipart/related; boundary=\"_ANY_STRING_WILL_DO_AS_A_SEPARATOR\"\r\n\r\n"; - - MHTMLStream.write("@STATIC;1.0;"); - - aTask.prerequisites().forEach(function(aFilename) - { - if (!FILE.isFile(aFilename) || aFilename.indexOf(resourcesPath) !== 0 || !isImage(aFilename)) - return; - - var resourcePath = "Resources/" + FILE.relative(resourcesPath, aFilename); - - MHTMLStream.write("u;" + resourcePath.length + ";" + resourcePath); // As MHTML... - contents = "mhtml:" + FILE.join(folder, productName + ".sj!") + resourcePath; + contents = "mhtml:" + FILE.join(folder, "MHTMLData.txt!") + resourcePath; - MHTMLContents += "--_ANY_STRING_WILL_DO_AS_A_SEPARATOR\r\n"; - MHTMLContents += "Content-Location:" + resourcePath + "\r\nContent-Transfer-Encoding:base64\r\n\r\n"; - MHTMLContents += FILE.read(aFilename, "b").decodeToString("UTF-8"); - MHTMLContents += "\r\n"; + MHTMLDataStream.write("--_ANY_STRING_WILL_DO_AS_A_SEPARATOR\r\n"); + MHTMLDataStream.write("Content-Location:" + resourcePath + "\r\nContent-Transfer-Encoding:base64\r\n\r\n"); + MHTMLDataStream.write(FILE.read(aFilename, "b").decodeToString("UTF-8")); + MHTMLDataStream.write("\r\n"); MHTMLStream.write(contents.length + ";" + contents); }); + dataURLStream.write("e;"); + dataURLStream.close(); + MHTMLStream.write("e;"); - MHTMLStream.write(MHTMLContents + "*/"); MHTMLStream.close(); + + MHTMLDataStream.write("*/"); + MHTMLDataStream.close(); + + // copy the MHTML test file into this environment + FILE.copy(MHTMLTestPath, MHTMLTestDestinationPath); }); - this.enhance([MHTMLPath]); + this.enhance([anEnvironment.name() + "-sprites"]); }, this); } @@ -668,8 +671,7 @@ BundleTask.prototype.defineStaticTask = function() { TERM.stream.print("Creating static file... \0green(" + staticPath +"\0)"); - var fileStream = FILE.open(staticPath, "w+", { charset:"UTF-8" }), - MHTMLContents = ""; + var fileStream = FILE.open(staticPath, "w+", { charset:"UTF-8" }); fileStream.write("@STATIC;1.0;");