mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
f268225683
--HG-- extra : rebase_source : 3f17fbda86aa13155c970e157dcec0ae67042696
219 lines
7.6 KiB
JavaScript
219 lines
7.6 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
/* This list allows pre-existing or 'unfixable' CSS issues to remain, while we
|
|
* detect newly occurring issues in shipping CSS. It is a list of objects
|
|
* specifying conditions under which an error should be ignored.
|
|
*
|
|
* Every property of the objects in it needs to consist of a regular expression
|
|
* matching the offending error. If an object has multiple regex criteria, they
|
|
* ALL need to match an error in order for that error not to cause a test
|
|
* failure. */
|
|
const kWhitelist = [
|
|
{sourceName: /cleopatra.*(tree|ui)\.css/i}, /* Cleopatra is imported as-is, see bug 1004421 */
|
|
{sourceName: /codemirror\.css/i}, /* CodeMirror is imported as-is, see bug 1004423 */
|
|
{sourceName: /web\/viewer\.css/i, errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i }, /* PDFjs is futureproofing its pseudoselectors, and those rules are dropped. */
|
|
{sourceName: /aboutaccounts\/(main|normalize)\.css/i}, /* Tracked in bug 1004428 */
|
|
];
|
|
|
|
/**
|
|
* Check if an error should be ignored due to matching one of the whitelist
|
|
* objects defined in kWhitelist
|
|
*
|
|
* @param aErrorObject the error to check
|
|
* @return true if the error should be ignored, false otherwise.
|
|
*/
|
|
function ignoredError(aErrorObject) {
|
|
for (let whitelistItem of kWhitelist) {
|
|
let matches = true;
|
|
for (let prop in whitelistItem) {
|
|
if (!whitelistItem[prop].test(aErrorObject[prop] || "")) {
|
|
matches = false;
|
|
break;
|
|
}
|
|
}
|
|
if (matches) {
|
|
return true;
|
|
}
|
|
}
|
|
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);
|
|
|
|
// Create a clean iframe to load all the files into:
|
|
let hiddenWin = Services.appShell.hiddenDOMWindow;
|
|
let iframe = hiddenWin.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
|
|
hiddenWin.document.documentElement.appendChild(iframe);
|
|
let doc = iframe.contentWindow.document;
|
|
|
|
|
|
// Listen for errors caused by the CSS:
|
|
let errorListener = {
|
|
observe: function(aMessage) {
|
|
if (!aMessage || !(aMessage instanceof Ci.nsIScriptError)) {
|
|
return;
|
|
}
|
|
// Only care about CSS errors generated by our iframe:
|
|
if (aMessage.category.contains("CSS") && aMessage.innerWindowID === 0 && aMessage.outerWindowID === 0) {
|
|
// Check if this error is whitelisted in kWhitelist
|
|
if (!ignoredError(aMessage)) {
|
|
ok(false, "Got error message for " + aMessage.sourceName + ": " + aMessage.errorMessage);
|
|
errors++;
|
|
} else {
|
|
info("Ignored error for " + aMessage.sourceName + " because of filter.");
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// We build a list of promises that get resolved when their respective
|
|
// files have loaded and produced no errors.
|
|
let allPromises = [];
|
|
let errors = 0;
|
|
// Register the error listener to keep track of errors.
|
|
Services.console.registerListener(errorListener);
|
|
for (let uri of uris) {
|
|
let linkEl = doc.createElement("link");
|
|
linkEl.setAttribute("rel", "stylesheet");
|
|
let promiseForThisSpec = Promise.defer();
|
|
let onLoad = (e) => {
|
|
promiseForThisSpec.resolve();
|
|
linkEl.removeEventListener("load", onLoad);
|
|
linkEl.removeEventListener("error", onError);
|
|
};
|
|
let onError = (e) => {
|
|
promiseForThisSpec.reject({error: e, href: linkEl.getAttribute("href")});
|
|
linkEl.removeEventListener("load", onLoad);
|
|
linkEl.removeEventListener("error", onError);
|
|
};
|
|
linkEl.addEventListener("load", onLoad);
|
|
linkEl.addEventListener("error", onError);
|
|
linkEl.setAttribute("type", "text/css");
|
|
linkEl.setAttribute("href", uri.spec);
|
|
allPromises.push(promiseForThisSpec.promise);
|
|
doc.head.appendChild(linkEl);
|
|
}
|
|
|
|
// Wait for all the files to have actually loaded:
|
|
yield Promise.all(allPromises);
|
|
// Count errors (the test output will list actual issues for us, as well
|
|
// as the ok(false) in the error listener)
|
|
is(errors, 0, "All the styles (" + allPromises.length + ") loaded without errors.");
|
|
|
|
// Clean up to avoid leaks:
|
|
Services.console.unregisterListener(errorListener);
|
|
iframe.remove();
|
|
doc.head.innerHTML = '';
|
|
doc = null;
|
|
iframe = null;
|
|
});
|
|
|
|
|