diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 38949cc6d46..a02a166c0eb 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -64,6 +64,7 @@ support-files = offlineQuotaNotification.cacheManifest offlineQuotaNotification.html page_style_sample.html + parsingTestHelpers.jsm print_postdata.sjs redirect_bug623155.sjs searchSuggestionEngine.sjs @@ -342,6 +343,7 @@ skip-if = buildapp == 'mulet' || e10s # Bug 866413 - PageInfo doesn't work in e1 skip-if = e10s # Bug ?????? - test directly manipulates content [browser_parsable_css.js] +[browser_parsable_script.js] [browser_pinnedTabs.js] skip-if = e10s # Bug 921905 - pinTab/unpinTab fail in e10s diff --git a/browser/base/content/test/general/browser_parsable_css.js b/browser/base/content/test/general/browser_parsable_css.js index b366a7d4599..57393535d13 100644 --- a/browser/base/content/test/general/browser_parsable_css.js +++ b/browser/base/content/test/general/browser_parsable_css.js @@ -17,6 +17,9 @@ const kWhitelist = [ {sourceName: /loop\/.*sdk-content\/.*\.css$/i /* TokBox SDK assets, see bug 1032469 */} ]; +let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm"); +let {generateURIsFromDirTree} = Cu.import(moduleLocation, {}); + /** * Check if an error should be ignored due to matching one of the whitelist * objects defined in kWhitelist @@ -40,113 +43,12 @@ function ignoredError(aErrorObject) { return false; } - -/** - * Returns a promise that is resolved with a list of CSS files to check, - * represented by their nsIURI objects. - * - * @param appDir the application directory to scan for CSS files (nsIFile) - */ -function generateURIsFromDirTree(appDir) { - let rv = []; - let dirQueue = [appDir.path]; - return Task.spawn(function*() { - while (dirQueue.length) { - let nextDir = dirQueue.shift(); - let {subdirs, cssfiles} = yield iterateOverPath(nextDir); - dirQueue = dirQueue.concat(subdirs); - rv = rv.concat(cssfiles); - } - return rv; - }); -} - -/* Shorthand constructor to construct an nsI(Local)File */ -let LocalFile = Components.Constructor("@mozilla.org/file/local;1", Ci.nsIFile, "initWithPath"); - -/** - * Uses OS.File.DirectoryIterator to asynchronously iterate over a directory. - * It returns a promise that is resolved with an object with two properties: - * - cssfiles: an array of nsIURIs corresponding to CSS that needs checking - * - subdirs: an array of paths for subdirectories we need to recurse into - * (handled by generateURIsFromDirTree above) - * - * @param path the path to check (string) - */ -function iterateOverPath(path) { - let iterator = new OS.File.DirectoryIterator(path); - let parentDir = new LocalFile(path); - let subdirs = []; - let cssfiles = []; - // Iterate through the directory - let promise = iterator.forEach( - function onEntry(entry) { - if (entry.isDir) { - let subdir = parentDir.clone(); - subdir.append(entry.name); - subdirs.push(subdir.path); - } else if (entry.name.endsWith(".css")) { - let file = parentDir.clone(); - file.append(entry.name); - let uriSpec = getURLForFile(file); - cssfiles.push(Services.io.newURI(uriSpec, null, null)); - } else if (entry.name.endsWith(".ja")) { - let file = parentDir.clone(); - file.append(entry.name); - let subentries = [uri for (uri of generateEntriesFromJarFile(file))]; - cssfiles = cssfiles.concat(subentries); - } - } - ); - - let outerPromise = Promise.defer(); - promise.then(function() { - outerPromise.resolve({cssfiles: cssfiles, subdirs: subdirs}); - iterator.close(); - }, function(e) { - outerPromise.reject(e); - iterator.close(); - }); - return outerPromise.promise; -} - -/* Helper function to generate a URI spec (NB: not an nsIURI yet!) - * given an nsIFile object */ -function getURLForFile(file) { - let fileHandler = Services.io.getProtocolHandler("file"); - fileHandler = fileHandler.QueryInterface(Ci.nsIFileProtocolHandler); - return fileHandler.getURLSpecFromFile(file); -} - -/** - * A generator that generates nsIURIs for CSS files found in jar files - * like omni.ja. - * - * @param jarFile an nsIFile object for the jar file that needs checking. - */ -function* generateEntriesFromJarFile(jarFile) { - const ZipReader = new Components.Constructor("@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open"); - let zr = new ZipReader(jarFile); - let entryEnumerator = zr.findEntries("*.css$"); - - const kURIStart = getURLForFile(jarFile); - while (entryEnumerator.hasMore()) { - let entry = entryEnumerator.getNext(); - let entryURISpec = "jar:" + kURIStart + "!/" + entry; - yield Services.io.newURI(entryURISpec, null, null); - } - zr.close(); -} - -/** - * The actual test. - */ add_task(function checkAllTheCSS() { let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile); // This asynchronously produces a list of URLs (sadly, mostly sync on our // test infrastructure because it runs against jarfiles there, and // our zipreader APIs are all sync) - let uris = yield generateURIsFromDirTree(appDir); + let uris = yield generateURIsFromDirTree(appDir, ".css"); // Create a clean iframe to load all the files into: let hiddenWin = Services.appShell.hiddenDOMWindow; diff --git a/browser/base/content/test/general/browser_parsable_script.js b/browser/base/content/test/general/browser_parsable_script.js new file mode 100644 index 00000000000..01e907172d4 --- /dev/null +++ b/browser/base/content/test/general/browser_parsable_script.js @@ -0,0 +1,83 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* This list allows pre-existing or 'unfixable' JS issues to remain, while we + * detect newly occurring issues in shipping JS. It is a list of regexes + * matching files which have errors: + */ +const kWhitelist = new Set([ + /defaults\/profile\/prefs.js$/, +]); + + +let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm"); +let {generateURIsFromDirTree} = Cu.import(moduleLocation, {}); +let {Reflect} = Cu.import("resource://gre/modules/reflect.jsm", {}); + +/** + * Check if an error should be ignored due to matching one of the whitelist + * objects defined in kWhitelist + * + * @param uri the uri to check against the whitelist + * @return true if the uri should be skipped, false otherwise. + */ +function uriIsWhiteListed(uri) { + for (let whitelistItem of kWhitelist) { + if (whitelistItem.test(uri.spec)) { + return true; + } + } + return false; +} + +function parsePromise(uri) { + let promise = new Promise((resolve, reject) => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", uri, true); + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + let scriptText = this.responseText; + let ast; + try { + info("Checking " + uri); + ast = Reflect.parse(scriptText); + resolve(true); + } catch (ex) { + let errorMsg = "Script error reading " + uri + ": " + ex; + ok(false, errorMsg); + resolve(false); + } + } + }; + xhr.onerror = (error) => { + ok(false, "XHR error reading " + uri + ": " + error); + resolve(false); + }; + xhr.overrideMimeType("application/javascript"); + xhr.send(null); + }); + return promise; +} + +add_task(function* checkAllTheJS() { + let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile); + // This asynchronously produces a list of URLs (sadly, mostly sync on our + // test infrastructure because it runs against jarfiles there, and + // our zipreader APIs are all sync) + let uris = yield generateURIsFromDirTree(appDir, [".js", ".jsm"]); + + // We create an array of promises so we can parallelize all our parsing + // and file loading activity: + let allPromises = []; + for (let uri of uris) { + if (uriIsWhiteListed(uri)) { + info("Not checking " + uri.spec); + continue; + } + allPromises.push(parsePromise(uri.spec)); + } + + let promiseResults = yield Promise.all(allPromises); + is(promiseResults.filter((x) => !x).length, 0, "There should be 0 parsing errors"); +}); + diff --git a/browser/base/content/test/general/parsingTestHelpers.jsm b/browser/base/content/test/general/parsingTestHelpers.jsm new file mode 100644 index 00000000000..04a095bd7e4 --- /dev/null +++ b/browser/base/content/test/general/parsingTestHelpers.jsm @@ -0,0 +1,126 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["generateURIsFromDirTree"]; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +/* Shorthand constructors to construct an nsI(Local)File and zip reader: */ +const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", Ci.nsIFile, "initWithPath"); +const ZipReader = new Components.Constructor("@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open"); + + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); + + +/** + * Returns a promise that is resolved with a list of files that have one of the + * extensions passed, represented by their nsIURI objects, which exist inside + * the directory passed. + * + * @param dir the directory which to scan for files (nsIFile) + * @param extensions the extensions of files we're interested in (Array). + */ +function generateURIsFromDirTree(dir, extensions) { + if (!Array.isArray(extensions)) { + extensions = [extensions]; + } + let dirQueue = [dir.path]; + return Task.spawn(function*() { + let rv = []; + while (dirQueue.length) { + let nextDir = dirQueue.shift(); + let {subdirs, files} = yield iterateOverPath(nextDir, extensions); + dirQueue.push(...subdirs); + rv.push(...files); + } + return rv; + }); +} + +/** + * Uses OS.File.DirectoryIterator to asynchronously iterate over a directory. + * It returns a promise that is resolved with an object with two properties: + * - files: an array of nsIURIs corresponding to files that match the extensions passed + * - subdirs: an array of paths for subdirectories we need to recurse into + * (handled by generateURIsFromDirTree above) + * + * @param path the path to check (string) + * @param extensions the file extensions we're interested in. + */ +function iterateOverPath(path, extensions) { + let iterator = new OS.File.DirectoryIterator(path); + let parentDir = new LocalFile(path); + let subdirs = []; + let files = []; + + let pathEntryIterator = (entry) => { + if (entry.isDir) { + subdirs.push(entry.path); + } else if (extensions.some((extension) => entry.name.endsWith(extension))) { + let file = parentDir.clone(); + file.append(entry.name); + let uriSpec = getURLForFile(file); + files.push(Services.io.newURI(uriSpec, null, null)); + } else if (entry.name.endsWith(".ja") || entry.name.endsWith(".jar")) { + let file = parentDir.clone(); + file.append(entry.name); + for (let extension of extensions) { + let jarEntryIterator = generateEntriesFromJarFile(file, extension); + files.push(...jarEntryIterator); + } + } + }; + + return new Promise((resolve, reject) => { + Task.spawn(function* () { + try { + // Iterate through the directory + yield iterator.forEach(pathEntryIterator); + resolve({files: files, subdirs: subdirs}); + } catch (ex) { + reject(ex); + } finally { + iterator.close(); + } + }); + }); +} + +/* Helper function to generate a URI spec (NB: not an nsIURI yet!) + * given an nsIFile object */ +function getURLForFile(file) { + let fileHandler = Services.io.getProtocolHandler("file"); + fileHandler = fileHandler.QueryInterface(Ci.nsIFileProtocolHandler); + return fileHandler.getURLSpecFromActualFile(file); +} + +/** + * A generator that generates nsIURIs for particular files found in jar files + * like omni.ja. + * + * @param jarFile an nsIFile object for the jar file that needs checking. + * @param extension the extension we're interested in. + */ +function* generateEntriesFromJarFile(jarFile, extension) { + let zr = new ZipReader(jarFile); + let entryEnumerator = zr.findEntries("*" + extension + "$"); + + const kURIStart = getURLForFile(jarFile); + while (entryEnumerator.hasMore()) { + let entry = entryEnumerator.getNext(); + // Ignore the JS cache which is stored in omni.ja + if (entry.startsWith("jsloader") || entry.startsWith("jssubloader")) { + continue; + } + let entryURISpec = "jar:" + kURIStart + "!/" + entry; + yield Services.io.newURI(entryURISpec, null, null); + } + zr.close(); +} + +