head.js (6051B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /* Shorthand constructors to construct an nsI(Local)File and zip reader: */ 7 const LocalFile = new Components.Constructor( 8 "@mozilla.org/file/local;1", 9 Ci.nsIFile, 10 "initWithPath" 11 ); 12 const ZipReader = new Components.Constructor( 13 "@mozilla.org/libjar/zip-reader;1", 14 "nsIZipReader", 15 "open" 16 ); 17 18 const IS_ALPHA = /^[a-z]+$/i; 19 20 var { PerfTestHelpers } = ChromeUtils.importESModule( 21 "resource://testing-common/PerfTestHelpers.sys.mjs" 22 ); 23 24 const kESModuleList = new Set([ 25 /browser\/lockwise-card.js$/, 26 /browser\/monitor-card.js$/, 27 /browser\/proxy-card.js$/, 28 /browser\/vpn-card.js$/, 29 /toolkit\/content\/global\/certviewer\/components\/.*\.js$/, 30 /toolkit\/content\/global\/certviewer\/.*\.js$/, 31 /toolkit\/content\/global\/ml\/transformers.*\.js$/, 32 /chrome\/pdfjs\/content\/web\/.*\.js$/, 33 ]); 34 35 /** 36 * Check if a URI should be parsed as an ES module. 37 * 38 * @param uri the uri to check against the ES module list 39 * @return true if the uri should be parsed as a module, otherwise parse it as a script. 40 */ 41 function uriIsESModule(uri) { 42 if (uri.filePath.endsWith(".mjs")) { 43 return true; 44 } 45 46 for (let allowlistItem of kESModuleList) { 47 if (allowlistItem.test(uri.spec)) { 48 return true; 49 } 50 } 51 return false; 52 } 53 54 /** 55 * Returns a promise that is resolved with a list of files that have one of the 56 * extensions passed, represented by their nsIURI objects, which exist inside 57 * the directory passed. 58 * 59 * @param dir the directory which to scan for files (nsIFile) 60 * @param extensions the extensions of files we're interested in (Array). 61 */ 62 function generateURIsFromDirTree(dir, extensions) { 63 if (!Array.isArray(extensions)) { 64 extensions = [extensions]; 65 } 66 let dirQueue = [dir.path]; 67 return (async function () { 68 let rv = []; 69 while (dirQueue.length) { 70 let nextDir = dirQueue.shift(); 71 let { subdirs, files } = await iterateOverPath(nextDir, extensions); 72 dirQueue.push(...subdirs); 73 rv.push(...files); 74 } 75 return rv; 76 })(); 77 } 78 79 /** 80 * Iterate over the children of |path| and find subdirectories and files with 81 * the given extension. 82 * 83 * This function recurses into ZIP and JAR archives as well. 84 * 85 * @param {string} path The path to check. 86 * @param {string[]} extensions The file extensions we're interested in. 87 * 88 * @returns {Promise<object>} 89 * A promise that resolves to an object containing the following 90 * properties: 91 * - files: an array of nsIURIs corresponding to 92 * files that match the extensions passed 93 * - subdirs: an array of paths for subdirectories we need to recurse 94 * into (handled by generateURIsFromDirTree above) 95 */ 96 async function iterateOverPath(path, extensions) { 97 const children = await IOUtils.getChildren(path); 98 99 const files = []; 100 const subdirs = []; 101 102 for (const entry of children) { 103 let stat; 104 try { 105 stat = await IOUtils.stat(entry); 106 } catch (error) { 107 if (error.name === "NotFoundError") { 108 // Ignore symlinks from prior builds to subsequently removed files 109 continue; 110 } 111 throw error; 112 } 113 114 if (stat.type === "directory") { 115 subdirs.push(entry); 116 } else if (extensions.some(extension => entry.endsWith(extension))) { 117 if (await IOUtils.exists(entry)) { 118 const spec = PathUtils.toFileURI(entry); 119 files.push(Services.io.newURI(spec)); 120 } 121 } else if ( 122 entry.endsWith(".ja") || 123 entry.endsWith(".jar") || 124 entry.endsWith(".zip") || 125 entry.endsWith(".xpi") 126 ) { 127 const file = new LocalFile(entry); 128 for (const extension of extensions) { 129 files.push(...generateEntriesFromJarFile(file, extension)); 130 } 131 } 132 } 133 134 return { files, subdirs }; 135 } 136 137 /* Helper function to generate a URI spec (NB: not an nsIURI yet!) 138 * given an nsIFile object */ 139 function getURLForFile(file) { 140 let fileHandler = Services.io.getProtocolHandler("file"); 141 fileHandler = fileHandler.QueryInterface(Ci.nsIFileProtocolHandler); 142 return fileHandler.getURLSpecFromActualFile(file); 143 } 144 145 /** 146 * A generator that generates nsIURIs for particular files found in jar files 147 * like omni.ja. 148 * 149 * @param jarFile an nsIFile object for the jar file that needs checking. 150 * @param extension the extension we're interested in. 151 */ 152 function* generateEntriesFromJarFile(jarFile, extension) { 153 let zr = new ZipReader(jarFile); 154 const kURIStart = getURLForFile(jarFile); 155 156 for (let entry of zr.findEntries("*" + extension + "$")) { 157 // Ignore the JS cache which is stored in omni.ja 158 if (entry.startsWith("jsloader") || entry.startsWith("jssubloader")) { 159 continue; 160 } 161 let entryURISpec = "jar:" + kURIStart + "!/" + entry; 162 yield Services.io.newURI(entryURISpec); 163 } 164 zr.close(); 165 } 166 167 function fetchFile(uri) { 168 return new Promise(resolve => { 169 let xhr = new XMLHttpRequest(); 170 xhr.responseType = "text"; 171 xhr.open("GET", uri, true); 172 xhr.onreadystatechange = function () { 173 if (this.readyState != this.DONE) { 174 return; 175 } 176 try { 177 resolve(this.responseText); 178 } catch (ex) { 179 ok(false, `Script error reading ${uri}: ${ex}`); 180 resolve(""); 181 } 182 }; 183 xhr.onerror = error => { 184 ok(false, `XHR error reading ${uri}: ${error}`); 185 resolve(""); 186 }; 187 xhr.send(null); 188 }); 189 } 190 191 /** 192 * Returns whether or not a word (presumably in en-US) is capitalized per 193 * expectations. 194 * 195 * @param {string} word The single word to check. 196 * @param {boolean} expectCapitalized True if the word should be capitalized. 197 * @returns {boolean} True if the word matches the expected capitalization. 198 */ 199 function hasExpectedCapitalization(word, expectCapitalized) { 200 let firstChar = word[0]; 201 if (!IS_ALPHA.test(firstChar)) { 202 return true; 203 } 204 205 let isCapitalized = firstChar == firstChar.toLocaleUpperCase("en-US"); 206 return isCapitalized == expectCapitalized; 207 }