diff options
Diffstat (limited to 'browser/base/content/test/general/browser_parsable_css.js')
-rw-r--r-- | browser/base/content/test/general/browser_parsable_css.js | 376 |
1 files changed, 0 insertions, 376 deletions
diff --git a/browser/base/content/test/general/browser_parsable_css.js b/browser/base/content/test/general/browser_parsable_css.js deleted file mode 100644 index 72954d2e5..000000000 --- a/browser/base/content/test/general/browser_parsable_css.js +++ /dev/null @@ -1,376 +0,0 @@ -/* 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. */ -let whitelist = [ - // CodeMirror is imported as-is, see bug 1004423. - {sourceName: /codemirror\.css$/i, - isFromDevTools: true}, - // The debugger uses cross-browser CSS. - {sourceName: /devtools\/client\/debugger\/new\/styles.css/i, - isFromDevTools: true}, - // PDFjs is futureproofing its pseudoselectors, and those rules are dropped. - {sourceName: /web\/viewer\.css$/i, - errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i, - isFromDevTools: false}, - // Tracked in bug 1004428. - {sourceName: /aboutaccounts\/(main|normalize)\.css$/i, - isFromDevTools: false}, - // Highlighter CSS uses a UA-only pseudo-class, see bug 985597. - {sourceName: /highlighters\.css$/i, - errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i, - isFromDevTools: true}, - // Responsive Design Mode CSS uses a UA-only pseudo-class, see Bug 1241714. - {sourceName: /responsive-ua\.css$/i, - errorMessage: /Unknown pseudo-class.*moz-dropdown-list/i, - isFromDevTools: true}, - - {sourceName: /\b(contenteditable|EditorOverride|svg|forms|html|mathml|ua)\.css$/i, - errorMessage: /Unknown pseudo-class.*-moz-/i, - isFromDevTools: false}, - {sourceName: /\b(html|mathml|ua)\.css$/i, - errorMessage: /Unknown property.*-moz-/i, - isFromDevTools: false}, - // Reserved to UA sheets unless layout.css.overflow-clip-box.enabled flipped to true. - {sourceName: /res\/forms\.css$/i, - errorMessage: /Unknown property.*overflow-clip-box/i, - isFromDevTools: false}, - {sourceName: /res\/(ua|html)\.css$/i, - errorMessage: /Unknown pseudo-class .*\bfullscreen\b/i, - isFromDevTools: false}, - {sourceName: /skin\/timepicker\.css$/i, - errorMessage: /Error in parsing.*mask/i, - isFromDevTools: false}, -]; - -// Platform can be "linux", "macosx" or "win". If omitted, the exception applies to all platforms. -let allowedImageReferences = [ - // Bug 1302691 - {file: "chrome://devtools/skin/images/dock-bottom-minimize@2x.png", - from: "chrome://devtools/skin/toolbox.css", - isFromDevTools: true}, - {file: "chrome://devtools/skin/images/dock-bottom-maximize@2x.png", - from: "chrome://devtools/skin/toolbox.css", - isFromDevTools: true}, -]; - -var moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm"); -var {generateURIsFromDirTree} = Cu.import(moduleLocation, {}); - -// Add suffix to stylesheets' URI so that we always load them here and -// have them parsed. Add a random number so that even if we run this -// test multiple times, it would be unlikely to affect each other. -const kPathSuffix = "?always-parse-css-" + Math.random(); - -/** - * Check if an error should be ignored due to matching one of the whitelist - * objects defined in whitelist - * - * @param aErrorObject the error to check - * @return true if the error should be ignored, false otherwise. - */ -function ignoredError(aErrorObject) { - for (let whitelistItem of whitelist) { - let matches = true; - for (let prop of ["sourceName", "errorMessage"]) { - if (whitelistItem.hasOwnProperty(prop) && - !whitelistItem[prop].test(aErrorObject[prop] || "")) { - matches = false; - break; - } - } - if (matches) { - whitelistItem.used = true; - return true; - } - } - return false; -} - -function once(target, name) { - return new Promise((resolve, reject) => { - let cb = () => { - target.removeEventListener(name, cb); - resolve(); - }; - target.addEventListener(name, cb); - }); -} - -function fetchFile(uri) { - return new Promise((resolve, reject) => { - let xhr = new XMLHttpRequest(); - xhr.responseType = "text"; - xhr.open("GET", uri, true); - xhr.onreadystatechange = function() { - if (this.readyState != this.DONE) { - return; - } - try { - resolve(this.responseText); - } catch (ex) { - ok(false, `Script error reading ${uri}: ${ex}`); - resolve(""); - } - }; - xhr.onerror = error => { - ok(false, `XHR error reading ${uri}: ${error}`); - resolve(""); - }; - xhr.send(null); - }); -} - -var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"] - .getService(Ci.nsIChromeRegistry); -var gChromeMap = new Map(); - -function getBaseUriForChromeUri(chromeUri) { - let chromeFile = chromeUri + "gobbledygooknonexistentfile.reallynothere"; - let uri = Services.io.newURI(chromeFile, null, null); - let fileUri = gChromeReg.convertChromeURL(uri); - return fileUri.resolve("."); -} - -function parseManifest(manifestUri) { - return fetchFile(manifestUri.spec).then(data => { - for (let line of data.split('\n')) { - let [type, ...argv] = line.split(/\s+/); - let component; - if (type == "content" || type == "skin") { - [component] = argv; - } else { - // skip unrelated lines - continue; - } - let chromeUri = `chrome://${component}/${type}/`; - gChromeMap.set(getBaseUriForChromeUri(chromeUri), chromeUri); - } - }); -} - -function convertToChromeUri(fileUri) { - let baseUri = fileUri.spec; - let path = ""; - while (true) { - let slashPos = baseUri.lastIndexOf("/", baseUri.length - 2); - if (slashPos < 0) { - info(`File not accessible from chrome protocol: ${fileUri.path}`); - return fileUri; - } - path = baseUri.slice(slashPos + 1) + path; - baseUri = baseUri.slice(0, slashPos + 1); - if (gChromeMap.has(baseUri)) { - let chromeBaseUri = gChromeMap.get(baseUri); - let chromeUri = `${chromeBaseUri}${path}`; - return Services.io.newURI(chromeUri, null, null); - } - } -} - -function messageIsCSSError(msg) { - // Only care about CSS errors generated by our iframe: - if ((msg instanceof Ci.nsIScriptError) && - msg.category.includes("CSS") && - msg.sourceName.endsWith(kPathSuffix)) { - let sourceName = msg.sourceName.slice(0, -kPathSuffix.length); - let msgInfo = { sourceName, errorMessage: msg.errorMessage }; - // Check if this error is whitelisted in whitelist - if (!ignoredError(msgInfo)) { - ok(false, `Got error message for ${sourceName}: ${msg.errorMessage}`); - return true; - } - info(`Ignored error for ${sourceName} because of filter.`); - } - return false; -} - -let imageURIsToReferencesMap = new Map(); - -function processCSSRules(sheet) { - for (let rule of sheet.cssRules) { - if (rule instanceof CSSMediaRule) { - processCSSRules(rule); - continue; - } - if (!(rule instanceof CSSStyleRule)) - continue; - - // Extract urls from the css text. - // Note: CSSStyleRule.cssText always has double quotes around URLs even - // when the original CSS file didn't. - let urls = rule.cssText.match(/url\("[^"]*"\)/g); - if (!urls) - continue; - - for (let url of urls) { - // Remove the url(" prefix and the ") suffix. - url = url.replace(/url\("(.*)"\)/, "$1"); - if (url.startsWith("data:")) - continue; - - // Make the url absolute and remove the ref. - let baseURI = Services.io.newURI(rule.parentStyleSheet.href, null, null); - url = Services.io.newURI(url, null, baseURI).specIgnoringRef; - - // Store the image url along with the css file referencing it. - let baseUrl = baseURI.spec.split("?always-parse-css")[0]; - if (!imageURIsToReferencesMap.has(url)) { - imageURIsToReferencesMap.set(url, new Set([baseUrl])); - } else { - imageURIsToReferencesMap.get(url).add(baseUrl); - } - } - } -} - -function chromeFileExists(aURI) -{ - let available = 0; - try { - let channel = NetUtil.newChannel({uri: aURI, loadUsingSystemPrincipal: true}); - let stream = channel.open(); - let sstream = Cc["@mozilla.org/scriptableinputstream;1"] - .createInstance(Ci.nsIScriptableInputStream); - sstream.init(stream); - available = sstream.available(); - sstream.close(); - } catch (e) { - if (e.result != Components.results.NS_ERROR_FILE_NOT_FOUND) { - dump("Checking " + aURI + ": " + e + "\n"); - Cu.reportError(e); - } - } - return available > 0; -} - -add_task(function* checkAllTheCSS() { - let appDir = Services.dirsvc.get("GreD", 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, [".css", ".manifest"]); - - // Create a clean iframe to load all the files into. This needs to live at a - // chrome URI so that it's allowed to load and parse any styles. - let testFile = getRootDirectory(gTestPath) + "dummy_page.html"; - let windowless = Services.appShell.createWindowlessBrowser(); - let iframe = windowless.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe"); - windowless.document.documentElement.appendChild(iframe); - let iframeLoaded = once(iframe, 'load'); - iframe.contentWindow.location = testFile; - yield iframeLoaded; - let doc = iframe.contentWindow.document; - - // Parse and remove all manifests from the list. - // NOTE that this must be done before filtering out devtools paths - // so that all chrome paths can be recorded. - let manifestPromises = []; - uris = uris.filter(uri => { - if (uri.path.endsWith(".manifest")) { - manifestPromises.push(parseManifest(uri)); - return false; - } - return true; - }); - // Wait for all manifest to be parsed - yield Promise.all(manifestPromises); - - // We build a list of promises that get resolved when their respective - // files have loaded and produced no errors. - let allPromises = []; - - // filter out either the devtools paths or the non-devtools paths: - let isDevtools = SimpleTest.harnessParameters.subsuite == "devtools"; - let devtoolsPathBits = ["webide", "devtools"]; - uris = uris.filter(uri => isDevtools == devtoolsPathBits.some(path => uri.spec.includes(path))); - - for (let uri of uris) { - let linkEl = doc.createElement("link"); - linkEl.setAttribute("rel", "stylesheet"); - let promiseForThisSpec = Promise.defer(); - let onLoad = (e) => { - processCSSRules(linkEl.sheet); - promiseForThisSpec.resolve(); - linkEl.removeEventListener("load", onLoad); - linkEl.removeEventListener("error", onError); - }; - let onError = (e) => { - ok(false, "Loading " + linkEl.getAttribute("href") + " threw an error!"); - promiseForThisSpec.resolve(); - linkEl.removeEventListener("load", onLoad); - linkEl.removeEventListener("error", onError); - }; - linkEl.addEventListener("load", onLoad); - linkEl.addEventListener("error", onError); - linkEl.setAttribute("type", "text/css"); - let chromeUri = convertToChromeUri(uri); - linkEl.setAttribute("href", chromeUri.spec + kPathSuffix); - allPromises.push(promiseForThisSpec.promise); - doc.head.appendChild(linkEl); - } - - // Wait for all the files to have actually loaded: - yield Promise.all(allPromises); - - // Check if all the files referenced from CSS actually exist. - for (let [image, references] of imageURIsToReferencesMap) { - if (!chromeFileExists(image)) { - for (let ref of references) { - let ignored = false; - for (let item of allowedImageReferences) { - if (image.endsWith(item.file) && ref.endsWith(item.from) && - isDevtools == item.isFromDevTools && - (!item.platforms || item.platforms.includes(AppConstants.platform))) { - item.used = true; - ignored = true; - break; - } - } - if (!ignored) - ok(false, "missing " + image + " referenced from " + ref); - } - } - } - - let messages = Services.console.getMessageArray(); - // Count errors (the test output will list actual issues for us, as well - // as the ok(false) in messageIsCSSError. - let errors = messages.filter(messageIsCSSError); - is(errors.length, 0, "All the styles (" + allPromises.length + ") loaded without errors."); - - // Confirm that all whitelist rules have been used. - for (let item of whitelist) { - if (!item.used && isDevtools == item.isFromDevTools) { - ok(false, "Unused whitelist item. " + - (item.sourceName ? " sourceName: " + item.sourceName : "") + - (item.errorMessage ? " errorMessage: " + item.errorMessage : "")); - } - } - - // Confirm that all file whitelist rules have been used. - for (let item of allowedImageReferences) { - if (!item.used && isDevtools == item.isFromDevTools && - (!item.platforms || item.platforms.includes(AppConstants.platform))) { - ok(false, "Unused file whitelist item. " + - " file: " + item.file + - " from: " + item.from); - } - } - - // Clean up to avoid leaks: - iframe.remove(); - doc.head.innerHTML = ''; - doc = null; - iframe = null; - windowless.close(); - windowless = null; - imageURIsToReferencesMap = null; -}); |