lighthouse = function (url, flags = {}, configJSON) { return new Promise((resolve, reject) => { if (!url) { return reject(new Error('Lighthouse requires a URL')); } // set logging preferences, assume quiet flags.logLevel = flags.logLevel || 'error'; log.setLevel(flags.logLevel); // Use ConfigParser to generate a valid config file const config = new Config(configJSON, flags.configPath); const connection = new ChromeProtocol(flags.port); // kick off a lighthouse run resolve(Runner.run(connection, {url, flags, config})); }); }
n/a
class Audit {
/**
* @return {!string}
*/
static get DEFAULT_PASS() {
return DEFAULT_PASS;
}
/**
* @throws {Error}
*/
static get meta() {
throw new Error('Audit meta information must be overridden.');
}
/**
* @param {string} debugString
* @return {!AuditResult}
*/
static generateErrorAuditResult(debugString) {
return this.generateAuditResult({
rawValue: null,
error: true,
debugString
});
}
/**
* @param {!AuditResultInput} result
* @return {!AuditResult}
*/
static generateAuditResult(result) {
if (typeof result.rawValue === 'undefined') {
throw new Error('generateAuditResult requires a rawValue');
}
const score = typeof result.score === 'undefined' ? result.rawValue : result.score;
let displayValue = result.displayValue;
if (typeof displayValue === 'undefined') {
displayValue = result.rawValue ? result.rawValue : '';
}
// The same value or true should be '' it doesn't add value to the report
if (displayValue === score) {
displayValue = '';
}
return {
score,
displayValue: `${displayValue}`,
rawValue: result.rawValue,
error: result.error,
debugString: result.debugString,
optimalValue: result.optimalValue,
extendedInfo: result.extendedInfo,
informative: this.meta.informative,
name: this.meta.name,
category: this.meta.category,
description: this.meta.description,
helpText: this.meta.helpText
};
}
}
n/a
class Gatherer {
/**
* @return {string}
*/
get name() {
return this.constructor.name;
}
/* eslint-disable no-unused-vars */
/**
* Called before navigation to target url.
* @param {!Object} options
*/
beforePass(options) { }
/**
* Called after target page is loaded. If a trace is enabled for this pass,
* the trace is still being recorded.
* @param {!Object} options
*/
pass(options) { }
/**
* Called after target page is loaded, all gatherer `pass` methods have been
* executed, and — if generated in this pass — the trace is ended. The trace
* and record of network activity are provided in `loadData`.
* @param {!Object} options
* @param {networkRecords: !Array, trace: {traceEvents: !Array}} loadData
* @return {*|!Promise<*>}
*/
afterPass(options, loadData) { }
/* eslint-enable no-unused-vars */
}
n/a
class ConsoleQuieter { static mute(opts) { ConsoleQuieter._logs = ConsoleQuieter._logs || []; console.log = function(...args) { ConsoleQuieter._logs.push({type: 'log', args, prefix: opts.prefix}); }; console.warn = function(...args) { ConsoleQuieter._logs.push({type: 'warn', args, prefix: opts.prefix}); }; console.error = function(...args) { ConsoleQuieter._logs.push({type: 'error', args, prefix: opts.prefix}); }; } static unmuteAndFlush() { console.log = ConsoleQuieter._consolelog; console.warn = ConsoleQuieter._consolewarn; console.error = ConsoleQuieter._consoleerror; ConsoleQuieter._logs.forEach(entry => { log.verbose(`${entry.prefix}-${entry.type}`, ...entry.args); }); ConsoleQuieter._logs = []; } }
n/a
getAuditList() { const ignoredFiles = [ 'audit.js', 'accessibility/axe-audit.js', 'byte-efficiency/byte-efficiency-audit.js' ]; const fileList = [ ...fs.readdirSync(path.join(__dirname, './audits')), ...fs.readdirSync(path.join(__dirname, './audits/dobetterweb')).map(f => `dobetterweb/${f}`), ...fs.readdirSync(path.join(__dirname, './audits/accessibility')) .map(f => `accessibility/${f}`), ...fs.readdirSync(path.join(__dirname, './audits/byte-efficiency')) .map(f => `byte-efficiency/${f}`) ]; return fileList.filter(f => { return /\.js$/.test(f) && !ignoredFiles.includes(f); }).sort(); }
...
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const lighthouse = require('../../lighthouse-core');
function ListAudits() {
const audits = lighthouse.getAuditList().map((i) => i.replace(/\.js$/, '
;'));
process.stdout.write(JSON.stringify({ audits }, null, 2));
process.exit(0);
}
exports.default = ListAudits;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdC1hdWRpdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJsaXN0LWF1ZGl0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7OztBQUVILE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0FBRXBEO0lBQ0UsTUFBTSxNQUFNLEdBQUcsVUFBVSxDQUFDLFlBQVksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQVMsS0FBSyxDQUFDLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRXBGLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBQyxNQUFNLEVBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN4RCxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2xCLENBQUM7QUFMRCw2QkFLQyJ9
...
function URL(url) { if (!this || this[impl] || !(this instanceof URL)) { throw new TypeError("Failed to construct 'URL': Please use the 'new' operator, this DOM object constructor cannot be called as a function."); } if (arguments.length < 1) { throw new TypeError("Failed to construct 'URL': 1 argument required, but only " + arguments.length + " present."); } const args = []; for (let i = 0; i < arguments.length && i < 2; ++i) { args[i] = arguments[i]; } args[0] = conversions["USVString"](args[0]); if (args[1] !== undefined) { args[1] = conversions["USVString"](args[1]); } module.exports.setup(this, args); }
n/a
function ask(question, options) { return new Promise((resolve, reject) => { const iface = readline.createInterface(process.stdin, process.stdout); const optionsStr = options.map((o, i) => i + 1 + '. ' + o).join('\r\n'); iface.setPrompt(question + '\r\n' + optionsStr + '\r\nChoice: '); iface.prompt(); iface.on('line', (_answer) => { const answer = toInt(_answer); if (answer > 0 && answer <= options.length) { iface.close(); resolve(options[answer - 1]); } else { iface.prompt(); } }); }); }
...
const installations = chromeFinder[process.platform]();
if (installations.length < 1) {
return Promise.reject(new Error('No Chrome Installations Found'));
}
else if (installations.length === 1 || this.autoSelectChrome) {
return installations[0];
}
return ask_1.ask('Choose a Chrome installation to use with Lighthouse'
;, installations);
})
.then(execPath => this.spawn(execPath));
}
spawn(execPath) {
return new Promise((resolve, reject) => {
if (this.chrome) {
log.log('ChromeLauncher', `Chrome already running with pid ${this.chrome.pid}.`);
...
function run() { return runLighthouse(url, cliFlags, config); }
...
const launcher = new ChromeLauncher({port: 9222, autoSelectChrome: true});
return launcher.isDebuggerReady()
.catch(() => {
if (flags.skipAutolaunch) {
return;
}
return launcher.run(); // Launch Chrome.
})
.then(() => Lighthouse(url, flags, config)) // Run Lighthouse.
.then(results => launcher.kill().then(() => results)) // Kill Chrome and return results.
.catch(err => {
// Kill Chrome if there's an error.
return launcher.kill().then(() => {
throw err;
...
function runLighthouse(url, flags, config) { return __awaiter(this, void 0, void 0, function* () { let chromeLauncher = undefined; try { yield initPort(flags); const chromeLauncher = yield getDebuggableChrome(flags); const results = yield lighthouse(url, flags, config); const artifacts = results.artifacts; delete results.artifacts; yield saveResults(results, artifacts, flags); if (flags.interactive) { yield performanceXServer.hostExperiment({ url, flags, config }, results); } return yield chromeLauncher.kill(); } catch (err) { if (typeof chromeLauncher !== 'undefined') { yield chromeLauncher.kill(); } return handleError(err); } }); }
n/a
function darwin() { const suffixes = [ '/Contents/MacOS/Google Chrome Canary', '/Contents/MacOS/Google Chrome' ]; const LSREGISTER = '/System/Library/Frameworks/CoreServices.framework' + '/Versions/A/Frameworks/LaunchServices.framework' + '/Versions/A/Support/lsregister'; const installations = []; execSync(`${LSREGISTER} -dump` + ' | grep -i \'google chrome\\( canary\\)\\?.app$\'' + ' | awk \'{$1=""; print $0}\'').toString() .split(newLineRegex) .forEach((inst) => { suffixes.forEach(suffix => { const execPath = path.join(inst.trim(), suffix); if (canAccess(execPath)) { installations.push(execPath); } }); }); const priorities = [{ regex: new RegExp(`^${process.env.HOME}/Applications/.*Chrome.app`), weight: 50 }, { regex: new RegExp(`^${process.env.HOME}/Applications/.*Chrome Canary.app`), weight: 51 }, { regex: /^\/Applications\/.*Chrome.app/, weight: 100 }, { regex: /^\/Applications\/.*Chrome Canary.app/, weight: 101 }, { regex: /^\/Volumes\/.*Chrome.app/, weight: -2 }, { regex: /^\/Volumes\/.*Chrome Canary.app/, weight: -1 }]; return sort(installations, priorities); }
n/a
function linux() { let installations = []; // 1. Look into LIGHTHOUSE_CHROMIUM_PATH env variable if (canAccess(process.env.LIGHTHOUSE_CHROMIUM_PATH)) { installations.push(process.env.LIGHTHOUSE_CHROMIUM_PATH); } // 2. Look into the directories where .desktop are saved on gnome based distro's const desktopInstallationFolders = [ path.join(require('os').homedir(), '.local/share/applications/'), '/usr/share/applications/', ]; desktopInstallationFolders.forEach(folder => { installations = installations.concat(findChromeExecutables(folder)); }); // Look for google-chrome-stable & google-chrome executables by using the which command const executables = [ 'google-chrome-stable', 'google-chrome', ]; executables.forEach((executable) => { try { const chromePath = execFileSync('which', [executable]) .toString() .split(newLineRegex)[0]; if (canAccess(chromePath)) { installations.push(chromePath); } } catch (e) { // Not installed. } }); if (!installations.length) { throw new Error('The environment variable LIGHTHOUSE_CHROMIUM_PATH must be set to ' + 'executable of a build of Chromium version 54.0 or later.'); } const priorities = [{ regex: /chrome-wrapper$/, weight: 51 }, { regex: /google-chrome-stable$/, weight: 50 }, { regex: /google-chrome$/, weight: 49 }, { regex: new RegExp(process.env.LIGHTHOUSE_CHROMIUM_PATH), weight: 100 }]; return sort(uniq(installations.filter(Boolean)), priorities); }
n/a
function win32() { const installations = []; const suffixes = [ '\\Google\\Chrome SxS\\Application\\chrome.exe', '\\Google\\Chrome\\Application\\chrome.exe' ]; const prefixes = [ process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)'] ]; if (canAccess(process.env.LIGHTHOUSE_CHROMIUM_PATH)) { installations.push(process.env.LIGHTHOUSE_CHROMIUM_PATH); } prefixes.forEach(prefix => suffixes.forEach(suffix => { const chromePath = path.join(prefix, suffix); if (canAccess(chromePath)) { installations.push(chromePath); } })); return installations; }
n/a
class ChromeLauncher { // We can not use default args here due to support node pre 6. constructor(opts) { this.prepared = false; this.pollInterval = 500; opts = opts || {}; // choose the first one (default) this.autoSelectChrome = defaults(opts.autoSelectChrome, true); this.startingUrl = defaults(opts.startingUrl, 'about:blank'); this.additionalFlags = defaults(opts.additionalFlags, []); this.port = defaults(opts.port, 9222); } flags() { const flags = [ `--remote-debugging-port=${this.port}`, // Disable built-in Google Translate service '--disable-translate', // Disable all chrome extensions entirely '--disable-extensions', // Disable various background network services, including extension updating, // safe browsing service, upgrade detector, translate, UMA '--disable-background-networking', // Disable fetching safebrowsing lists, likely redundant due to disable-background-networking '--safebrowsing-disable-auto-update', // Disable syncing to a Google account '--disable-sync', // Disable reporting to UMA, but allows for collection '--metrics-recording-only', // Disable installation of default apps on first run '--disable-default-apps', // Skip first run wizards '--no-first-run', // Place Chrome profile in a custom location we'll rm -rf later `--user-data-dir=${this.TMP_PROFILE_DIR}` ]; if (process.platform === 'linux') { flags.push('--disable-setuid-sandbox'); } flags.push(...this.additionalFlags); flags.push(this.startingUrl); return flags; } prepare() { switch (process.platform) { case 'darwin': case 'linux': this.TMP_PROFILE_DIR = unixTmpDir(); break; case 'win32': this.TMP_PROFILE_DIR = win32TmpDir(); break; default: throw new Error('Platform ' + process.platform + ' is not supported'); } this.outFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-out.log`, 'a'); this.errFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-err.log`, 'a'); // fix for Node4 // you can't pass a fd to fs.writeFileSync this.pidFile = `${this.TMP_PROFILE_DIR}/chrome.pid`; log.verbose('ChromeLauncher', `created ${this.TMP_PROFILE_DIR}`); this.prepared = true; } run() { if (!this.prepared) { this.prepare(); } return Promise.resolve() .then(() => { const installations = chromeFinder[process.platform](); if (installations.length < 1) { return Promise.reject(new Error('No Chrome Installations Found')); } else if (installations.length === 1 || this.autoSelectChrome) { return installations[0]; } return ask_1.ask('Choose a Chrome installation to use with Lighthouse', installations); }) .then(execPath => this.spawn(execPath)); } spawn(execPath) { return new Promise((resolve, reject) => { if (this.chrome) { log.log('ChromeLauncher', `Chrome already running with pid ${this.chrome.pid}.`); return resolve(this.chrome.pid); } const chrome = spawn(execPath, this.flags(), { detached: true, stdio: ['ignore', this.outFile, this.errFile] }); this.chrome = chrome; fs.writeFileSync(this.pidFile, chrome.pid.toString()); log.verbose('ChromeLauncher', `Chrome running with pid ${chrome.pid} on port ${this.port}.`); resolve(chrome.pid); }) .then(pid => Promise.all([pid, this.waitUntilReady()])); } cleanu ...
...
}
/**
* Attempts to connect to an instance of Chrome with an open remote-debugging
* port. If none is found and the `skipAutolaunch` flag is not true, launches
* a debuggable instance.
*/
function getDebuggableChrome(flags) {
const chromeLauncher = new chrome_launcher_1.ChromeLauncher({
port: flags.port,
additionalFlags: flags.chromeFlags.split(' '),
autoSelectChrome: !flags.selectChrome,
});
// Kill spawned Chrome process in case of ctrl-C.
process.on(_SIGINT, () => {
chromeLauncher.kill().then(() => process.exit(_SIGINT_EXIT_CODE), handleError);
...
function ListAudits() { const audits = lighthouse.getAuditList().map((i) => i.replace(/\.js$/, '')); process.stdout.write(JSON.stringify({ audits }, null, 2)); process.exit(0); }
...
throw new Error('Please provide a url');
}
return true;
})
.argv;
// Process terminating command
if (cliFlags.listAllAudits) {
Commands.ListAudits();
}
// Process terminating command
if (cliFlags.listTraceCategories) {
Commands.ListTraceCategories();
}
const url = cliFlags._[0];
let config = null;
...
function listTraceCategories() { const traceCategories = lighthouse.traceCategories; process.stdout.write(JSON.stringify({ traceCategories })); process.exit(0); }
...
.argv;
// Process terminating command
if (cliFlags.listAllAudits) {
Commands.ListAudits();
}
// Process terminating command
if (cliFlags.listTraceCategories) {
Commands.ListTraceCategories();
}
const url = cliFlags._[0];
let config = null;
if (cliFlags.configPath) {
// Resolve the config file path relative to where cli was called.
cliFlags.configPath = path.resolve(process.cwd(), cliFlags.configPath);
config = require(cliFlags.configPath);
...
class ConsoleQuieter { static mute(opts) { ConsoleQuieter._logs = ConsoleQuieter._logs || []; console.log = function(...args) { ConsoleQuieter._logs.push({type: 'log', args, prefix: opts.prefix}); }; console.warn = function(...args) { ConsoleQuieter._logs.push({type: 'warn', args, prefix: opts.prefix}); }; console.error = function(...args) { ConsoleQuieter._logs.push({type: 'error', args, prefix: opts.prefix}); }; } static unmuteAndFlush() { console.log = ConsoleQuieter._consolelog; console.warn = ConsoleQuieter._consolewarn; console.error = ConsoleQuieter._consoleerror; ConsoleQuieter._logs.forEach(entry => { log.verbose(`${entry.prefix}-${entry.type}`, ...entry.args); }); ConsoleQuieter._logs = []; } }
n/a
_consoleerror = function () { [native code] }
n/a
_consolelog = function () { [native code] }
n/a
_consolewarn = function () { [native code] }
n/a
function disableCPUThrottling(driver) { return driver.sendCommand('Emulation.setCPUThrottlingRate', NO_CPU_THROTTLE_METRICS); }
...
setThrottling(flags, passConfig) {
const p = [];
if (passConfig.useThrottling) {
if (!flags.disableNetworkThrottling) p.push(emulation.enableNetworkThrottling(this));
if (!flags.disableCpuThrottling) p.push(emulation.enableCPUThrottling(this));
} else {
p.push(emulation.disableNetworkThrottling(this));
p.push(emulation.disableCPUThrottling(this));
}
return Promise.all(p);
}
/**
* Emulate internet disconnection.
* @return {!Promise}
...
function disableNetworkThrottling(driver) { return driver.sendCommand('Network.emulateNetworkConditions', NO_THROTTLING_METRICS); }
...
setThrottling(flags, passConfig) {
const p = [];
if (passConfig.useThrottling) {
if (!flags.disableNetworkThrottling) p.push(emulation.enableNetworkThrottling(this));
if (!flags.disableCpuThrottling) p.push(emulation.enableCPUThrottling(this));
} else {
p.push(emulation.disableNetworkThrottling(this));
p.push(emulation.disableCPUThrottling(this));
}
return Promise.all(p);
}
/**
* Emulate internet disconnection.
...
function enableCPUThrottling(driver) { return driver.sendCommand('Emulation.setCPUThrottlingRate', CPU_THROTTLE_METRICS); }
...
}).then(_ => this.setThrottling(flags, {useThrottling: true}));
}
setThrottling(flags, passConfig) {
const p = [];
if (passConfig.useThrottling) {
if (!flags.disableNetworkThrottling) p.push(emulation.enableNetworkThrottling(this));
if (!flags.disableCpuThrottling) p.push(emulation.enableCPUThrottling(this));
} else {
p.push(emulation.disableNetworkThrottling(this));
p.push(emulation.disableCPUThrottling(this));
}
return Promise.all(p);
}
...
function enableNetworkThrottling(driver) { return driver.sendCommand('Network.emulateNetworkConditions', TYPICAL_MOBILE_THROTTLING_METRICS); }
...
if (!flags.disableDeviceEmulation) return emulation.enableNexus5X(this);
}).then(_ => this.setThrottling(flags, {useThrottling: true}));
}
setThrottling(flags, passConfig) {
const p = [];
if (passConfig.useThrottling) {
if (!flags.disableNetworkThrottling) p.push(emulation.enableNetworkThrottling(this
));
if (!flags.disableCpuThrottling) p.push(emulation.enableCPUThrottling(this));
} else {
p.push(emulation.disableNetworkThrottling(this));
p.push(emulation.disableCPUThrottling(this));
}
return Promise.all(p);
}
...
function enableNexus5X(driver) {
/**
* Finalizes touch emulation by enabling `"ontouchstart" in window` feature detect
* to work. Messy hack, though copied verbatim from DevTools' emulation/TouchModel.js
* where it's been working for years. addScriptToEvaluateOnLoad runs before any of the
* page's JavaScript executes.
*/
/* eslint-disable no-proto */ /* global window, document */ /* istanbul ignore next */
const injectedTouchEventsFunction = function() {
const touchEvents = ['ontouchstart', 'ontouchend', 'ontouchmove', 'ontouchcancel'];
const recepients = [window.__proto__, document.__proto__];
for (let i = 0; i < touchEvents.length; ++i) {
for (let j = 0; j < recepients.length; ++j) {
if (!(touchEvents[i] in recepients[j])) {
Object.defineProperty(recepients[j], touchEvents[i], {
value: null, writable: true, configurable: true, enumerable: true
});
}
}
}
};
/* eslint-enable */
return Promise.all([
driver.sendCommand('Emulation.setDeviceMetricsOverride', NEXUS5X_EMULATION_METRICS),
// Network.enable must be called for UA overriding to work
driver.sendCommand('Network.enable'),
driver.sendCommand('Network.setUserAgentOverride', NEXUS5X_USERAGENT),
driver.sendCommand('Emulation.setTouchEmulationEnabled', {
enabled: true,
configuration: 'mobile'
}),
driver.sendCommand('Page.addScriptToEvaluateOnLoad', {
scriptSource: '(' + injectedTouchEventsFunction.toString() + ')()'
})
]);
}
...
enableRuntimeEvents() {
return this.sendCommand('Runtime.enable');
}
beginEmulation(flags) {
return Promise.resolve().then(_ => {
if (!flags.disableDeviceEmulation) return emulation.enableNexus5X(this);
}).then(_ => this.setThrottling(flags, {useThrottling: true}));
}
setThrottling(flags, passConfig) {
const p = [];
if (passConfig.useThrottling) {
if (!flags.disableNetworkThrottling) p.push(emulation.enableNetworkThrottling(this));
...
function getEmulationDesc() { const {latency, downloadThroughput, uploadThroughput} = TYPICAL_MOBILE_THROTTLING_METRICS; const byteToMbit = bytes => (bytes / 1024 / 1024 * 8).toFixed(1); return { 'deviceEmulation': 'Nexus 5X', 'cpuThrottling': `${CPU_THROTTLE_METRICS.rate}x slowdown`, 'networkThrottling': `${latency}ms RTT, ${byteToMbit(downloadThroughput)}Mbps down, ` + `${byteToMbit(uploadThroughput)}Mbps up` }; }
...
/**
* Get runtime configuration specified by the flags
* @param {!Object} flags
* @return {!Object} runtime config
*/
static getRuntimeConfig(flags) {
const emulationDesc = emulation.getEmulationDesc();
const environment = [
{
name: 'Device Emulation',
enabled: !flags.disableDeviceEmulation,
description: emulationDesc['deviceEmulation']
},
{
...
function goOffline(driver) { return driver.sendCommand('Network.emulateNetworkConditions', OFFLINE_METRICS); }
...
/**
* Emulate internet disconnection.
* @return {!Promise}
*/
goOffline() {
return this.sendCommand('Network.enable')
.then(_ => emulation.goOffline(this))
.then(_ => this.online = false);
}
/**
* Enable internet connection, using emulated mobile settings if
* `options.flags.disableNetworkThrottling` is false.
* @param {!Object} options
...
function addFormattedCodeSnippet(listener) { const handler = listener.handler ? listener.handler.description : '...'; const objectName = listener.objectName.toLowerCase().replace('#document', 'document'); return Object.assign({ label: `line: ${listener.line}, col: ${listener.col}`, pre: `${objectName}.addEventListener('${listener.type}', ${handler})` }, listener); }
n/a
function groupCodeSnippetsByLocation(listeners) { const locToListenersMap = new Map(); listeners.forEach(loc => { const key = JSON.stringify({line: loc.line, col: loc.col, url: loc.url, type: loc.type}); if (locToListenersMap.has(key)) { locToListenersMap.get(key).push(loc); } else { locToListenersMap.set(key, [loc]); } }); const results = []; locToListenersMap.forEach((listenersForLocation, key) => { const lineColUrlObj = JSON.parse(key); // Aggregate the code snippets. const codeSnippets = listenersForLocation.reduce((prev, loc) => { return prev + loc.pre.trim() + '\n\n'; }, ''); lineColUrlObj.pre = codeSnippets; // All listeners under this bucket have the same line/col. We use the first's // label as the label for all of them. lineColUrlObj.label = listenersForLocation[0].label; results.push(lineColUrlObj); }); return results; }
n/a
function getFilenamePrefix(results) { // eslint-disable-next-line no-undef const hostname = new (URLConstructor || URL)(results.url).hostname; const date = (results.generatedTime && new Date(results.generatedTime)) || new Date(); const timeStr = date.toLocaleTimeString('en-US', {hour12: false}); const dateParts = date.toLocaleDateString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit' }).split('/'); dateParts.unshift(dateParts.pop()); const dateStr = dateParts.join('-'); const filenamePrefix = `${hostname}_${dateStr}_${timeStr}`; // replace characters that are unfriendly to filenames return filenamePrefix.replace(/[\/\?<>\\:\*\|":]/g, '-'); }
n/a
(...args) => { let arg = false; for (let i = 0, n = args.length - 1; i < n; i++) { arg = args[i]; if (!arg) { break; } } return arg; }
n/a
number => { if (number && number.toFixed) { return number.toFixed(2); } return number; }
n/a
date => { const options = { day: 'numeric', month: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' }; let formatter = new Intl.DateTimeFormat('en-US', options); // Force UTC if runtime timezone could not be detected. // See https://github.com/GoogleChrome/lighthouse/issues/1056 const tz = formatter.resolvedOptions().timeZone; if (!tz || tz.toLowerCase() === 'etc/unknown') { options.timeZone = 'UTC'; formatter = new Intl.DateTimeFormat('en-US', options); } return formatter.format(new Date(date)); }
n/a
score => { return calculateRating(Math.round(score * 100)); }
n/a
getItemRating = function (value) { if (typeof value === 'boolean') { return value ? RATINGS.GOOD.label : RATINGS.POOR.label; } return calculateRating(value); }
n/a
(informative, additional) => (informative || additional) ? 'warning score-warning-bg':'poor score-poor-bg'
n/a
informative => informative ? 'info' : 'good'
n/a
getTotalScore = function (aggregation) { return Math.round(aggregation.total * 100); }
n/a
aggregation => { const totalScore = getTotalScore(aggregation); return calculateRating(totalScore); }
n/a
ifEq = function (lhs, rhs, options) { if (lhs === rhs) { // eslint-disable-next-line no-invalid-this return options.fn(this); } else { // eslint-disable-next-line no-invalid-this return options.inverse(this); } }
n/a
ifNotEq = function (lhs, rhs, options) { if (lhs !== rhs) { // eslint-disable-next-line no-invalid-this return options.fn(this); } else { // eslint-disable-next-line no-invalid-this return options.inverse(this); } }
n/a
value => (typeof value === 'boolean')
n/a
str => { return (str || '') // break up camelCase tokens .split(/([A-Z]+[a-z0-9]*)/) // replace all special characters and whitespace with hyphens .map(part => part.toLowerCase().replace(/[^a-z0-9]+/gi, '-')) // rejoin into a single string .join('-') // de-dupe hyphens .replace(/-+/g, '-') // remove leading or trailing hyphens .replace(/(^-|-$)/g, ''); }
n/a
name => { return name.toLowerCase().replace(/\s/g, '-'); }
n/a
value => !value
n/a
(str, opts) => { // const isViewer = opts.data.root.reportContext === 'viewer'; // Allow the report to inject HTML, but sanitize it first. // Viewer in particular, allows user's to upload JSON. To mitigate against // XSS, define a renderer that only transforms a few types of markdown blocks. // All other markdown and HTML is ignored. const renderer = new marked.Renderer(); renderer.em = str => `<em>${str}</em>`; renderer.link = (href, title, text) => { const titleAttr = title ? `title="${title}"` : ''; return `<a href="${href}" target="_blank" rel="noopener" ${titleAttr}>${text}</a>`; }; renderer.codespan = function(str) { return `<code>${str}</code>`; }; // eslint-disable-next-line no-unused-vars renderer.code = function(code, language) { return `<pre>${Handlebars.Utils.escapeExpression(code)}</pre>`; }; renderer.image = function(src, title, text) { return `<img src="${src}" alt="${text}" title="${title}">`; }; // Nuke wrapper <p> tag that gets generated. renderer.paragraph = function(str) { return str; }; try { str = marked(str, {renderer, sanitize: true}); } catch (e) { // Ignore fatal errors from marked js. } // The input str has been sanitized and transformed. Mark it as safe so // handlebars renders the text as HTML. return new Handlebars.SafeString(str); }
n/a
value => (getItemRating(value) !== RATINGS.GOOD.label)
n/a
function doExist(manifest) { if (!manifest || !manifest.icons) { return false; } if (manifest.icons.value.length === 0) { return false; } return true; }
n/a
function sizeAtLeast(sizeRequirement, manifest) { // An icon can be provided for a single size, or for multiple sizes. // To handle both, we flatten all found sizes into a single array. const iconValues = manifest.icons.value; const nestedSizes = iconValues.map(icon => icon.value.sizes.value); const flattenedSizes = [].concat(...nestedSizes); return flattenedSizes // First, filter out any undefined values, in case an icon was defined without a size .filter(size => typeof size === 'string') // discard sizes that are not AAxBB (eg. "any") .filter(size => /\d+x\d+/.test(size)) .filter(size => { // Split the '24x24' strings into ['24','24'] arrays const sizeStrs = size.split(/x/i); // Cast the ['24','24'] strings into [24,24] numbers const sizeNums = [parseFloat(sizeStrs[0]), parseFloat(sizeStrs[1])]; // Only keep sizes that are as big as our required size const areIconsBigEnough = sizeNums[0] >= sizeRequirement && sizeNums[1] >= sizeRequirement; // Square is required: https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/manifest/manifest_icon_selector .cc&q=ManifestIconSelector::IconSizesContainsBiggerThanMinimumSize&sq=package:chromium const areIconsSquare = sizeNums[0] === sizeNums[1]; return areIconsBigEnough && areIconsSquare; }); }
n/a
function ListAudits() { const audits = lighthouse.getAuditList().map((i) => i.replace(/\.js$/, '')); process.stdout.write(JSON.stringify({ audits }, null, 2)); process.exit(0); }
...
'select-chrome',
'verbose',
'quiet',
'help',
'interactive'
])
.choices('output', Printer.GetValidOutputOptions())
.default('chrome-flags', '')
.default('disable-cpu-throttling', true)
.default('output', Printer.GetValidOutputOptions()[Printer.OutputMode.none])
.default('output-path', 'stdout')
.default('port', 9222)
.default('max-wait-for-load', Driver.MAX_WAIT_FOR_FULLY_LOADED)
.check((argv) => {
// Make sure lighthouse has been passed a url, or at least one of --list-all-audits
...
function listTraceCategories() { const traceCategories = lighthouse.traceCategories; process.stdout.write(JSON.stringify({ traceCategories })); process.exit(0); }
...
'select-chrome',
'verbose',
'quiet',
'help',
'interactive'
])
.choices('output', Printer.GetValidOutputOptions())
.default('chrome-flags', '')
.default('disable-cpu-throttling', true)
.default('output', Printer.GetValidOutputOptions()[Printer.OutputMode.none])
.default('output-path', 'stdout')
.default('port', 9222)
.default('max-wait-for-load', Driver.MAX_WAIT_FOR_FULLY_LOADED)
.check((argv) => {
// Make sure lighthouse has been passed a url, or at least one of --list-all-audits
...
function GetValidOutputOptions() { return [OutputMode[OutputMode.pretty], OutputMode[OutputMode.json], OutputMode[OutputMode.html], OutputMode[OutputMode.none]]; }
...
'skip-autolaunch',
'select-chrome',
'verbose',
'quiet',
'help',
'interactive'
])
.choices('output', Printer.GetValidOutputOptions())
.default('chrome-flags', '')
.default('disable-cpu-throttling', true)
.default('output', Printer.GetValidOutputOptions()[Printer.OutputMode.none])
.default('output-path', 'stdout')
.default('port', 9222)
.default('max-wait-for-load', Driver.MAX_WAIT_FOR_FULLY_LOADED)
.check((argv) => {
...
function checkOutputPath(path) { if (!path) { log.warn('Printer', 'No output path set; using stdout'); return 'stdout'; } return path; }
n/a
function createOutput(results, outputMode) { const reportGenerator = new ReportGenerator(); // HTML report. if (outputMode === OutputMode.html) { return reportGenerator.generateHTML(results, 'cli'); } // JSON report. if (outputMode === OutputMode.json) { return JSON.stringify(results, null, 2); } // No report (the new default) if (outputMode === OutputMode.none) return ''; // Pretty printed CLI report. const version = results.lighthouseVersion; let output = `\n\n${log.bold}Lighthouse (${version}) results:${log.reset} ${results.url}\n\n`; results.aggregations.forEach(aggregation => { const total = aggregation.total ? ': ' + formatAggregationResultItem(Math.round(aggregation.total * 100), '%') : ''; output += `${log.whiteSmallSquare} ${log.bold}${aggregation.name}${log.reset}${total}\n\n`; aggregation.score.forEach(item => { const score = (item.overall * 100).toFixed(0); if (item.name) { output += `${log.bold}${item.name}${log.reset}: ${item.scored ? formatAggregationResultItem(score, '%') : ''}\n`; } item.subItems.forEach(subitem => { let auditResult; if (typeof subitem === 'string') { auditResult = results.audits[subitem]; } else { auditResult = subitem; } const formattedScore = auditResult.error ? `${log.redify('‽')}` : `${formatAggregationResultItem(auditResult.score)}`; let lineItem = ` ${log.doubleLightHorizontal} ${formattedScore} ${auditResult.description}`; if (auditResult.displayValue) { lineItem += ` (${log.bold}${auditResult.displayValue}${log.reset})`; } output += `${lineItem}\n`; if (auditResult.debugString) { output += ` ${auditResult.debugString}\n`; } if (auditResult.extendedInfo && auditResult.extendedInfo.value) { const formatter = Formatter.getByName(auditResult.extendedInfo.formatter).getFormatter('pretty'); output += `${formatter(auditResult.extendedInfo.value)}`; } }); output += '\n'; }); }); return output; }
n/a
function write(results, mode, path) {
return new Promise((resolve, reject) => {
const outputPath = checkOutputPath(path);
const output = createOutput(results, OutputMode[mode]);
// Testing stdout is out of scope, and doesn't really achieve much besides testing Node,
// so we will skip this chunk of the code.
/* istanbul ignore if */
if (outputPath === 'stdout') {
return writeToStdout(output).then(_ => resolve(results));
}
return writeFile(outputPath, output, OutputMode[mode]).then(_ => {
resolve(results);
}).catch(err => reject(err));
});
}
...
// Use an existing config or create a custom one.
const config = require('lighthouse/lighthouse-core/config/perf.json');
const url = 'https://example.com';
const flags = {output: 'html'};
launchChromeAndRunLighthouse(url, flags, config).then(lighthouseResults => {
lighthouseResults.artifacts = undefined; // You can save the artifacts separately if so desired
return Printer.write(lighthouseResults, flags.output);
}).catch(err => console.error(err));
```
**Example** - extracting an overall score from all scored audits
```javascript
function getOverallScore(lighthouseResults) {
...
function getRandomPort() { return new Promise((resolve, reject) => { const server = http_1.createServer(); server.listen(0); server.once('listening', () => { const port = server.address().port; server.close(() => resolve(port)); }); server.once('error', reject); }); }
...
function initPort(flags) {
return Promise.resolve().then(() => {
if (flags.port !== 0) {
log.verbose('Lighthouse CLI', `Using supplied port ${flags.port}`);
return;
}
log.verbose('Lighthouse CLI', 'Generating random port.');
return randomPort.getRandomPort().then(portNumber => {
flags.port = portNumber;
log.verbose('Lighthouse CLI', `Using generated port ${flags.port}.`);
});
});
}
/**
* Attempts to connect to an instance of Chrome with an open remote-debugging
...
function hostExperiment(params, results) { return new Promise(resolve => { url = params.url; config = params.config; database = new ExperimentDatabase(); const id = database.saveData(params.flags, results); fallbackReportId = id; const server = http.createServer(requestHandler); server.listen(0); server.on('listening', () => opn(`http://localhost:${server.address().port}/?id=${id}`)); server.on('error', err => log.error('PerformanceXServer', err.code, err)); server.on('close', resolve); process.on('SIGINT', () => { database.clear(); server.close(); }); }); }
...
yield initPort(flags);
const chromeLauncher = yield getDebuggableChrome(flags);
const results = yield lighthouse(url, flags, config);
const artifacts = results.artifacts;
delete results.artifacts;
yield saveResults(results, artifacts, flags);
if (flags.interactive) {
yield performanceXServer.hostExperiment({ url, flags, config }, results);
}
return yield chromeLauncher.kill();
}
catch (err) {
if (typeof chromeLauncher !== 'undefined') {
yield chromeLauncher.kill();
}
...
function addVendorPrefixes(propsNames) { const vendorPrefixes = ['-o-', '-ms-', '-moz-', '-webkit-']; propsNames = Array.isArray(propsNames) ? propsNames : [propsNames]; let propsNamesWithPrefixes = propsNames; // Map vendorPrefixes to propsNames for (const prefix of vendorPrefixes) { const temp = propsNames.map(propName => `${prefix}${propName}`); propsNamesWithPrefixes = propsNamesWithPrefixes.concat(temp); } // Add original propNames return propsNamesWithPrefixes; }
n/a
function filterStylesheetsByUsage(stylesheets, propName, propVal) { if (!propName && !propVal) { return []; } // Create deep clone of arrays so multiple calls to filterStylesheetsByUsage // don't alter the original artifacts in stylesheets arg. const deepClone = stylesheets.map(sheet => Object.assign({}, sheet)); return deepClone.filter(s => { if (s.isDuplicate) { return false; } s.parsedContent = s.parsedContent.filter(item => { let usedName = ''; let usedVal = ''; // Prevent includes call on null value if (propName) { propName = Array.isArray(propName) ? propName : [propName]; usedName = propName.includes(item.property.name); } if (propVal) { propVal = Array.isArray(propVal) ? propVal : [propVal]; usedVal = propVal.includes(item.property.val); } // Allow search by css property name, a value, or name/value pair. if (propName && !propVal) { return usedName; } else if (!propName && propVal) { return usedVal; } else if (propName && propVal) { return usedName && usedVal; } return false; }); return s.parsedContent.length > 0; }); }
n/a
function getFormattedStyleRule(content, parsedContent) { const lines = content.split('\n'); const declarationRange = parsedContent.declarationRange; const startLine = declarationRange.startLine; const endLine = declarationRange.endLine; const start = declarationRange.startColumn; const end = declarationRange.endColumn; let rule; if (startLine === endLine) { rule = lines[startLine].substring(start, end); } else { // If css property value spans multiple lines, include all of them so it's // obvious where the value was used. rule = lines.slice(startLine, endLine + 1).reduce((prev, line) => { prev.push(line); return prev; }, []).join('\n'); } const block = parsedContent.selector + ' {\n' + ` ${rule.trim()}\n` + '}'; return { styleRule: block.trim(), startLine, location: `${start}:${end}` }; }
n/a
function URL(url) { if (!this || this[impl] || !(this instanceof URL)) { throw new TypeError("Failed to construct 'URL': Please use the 'new' operator, this DOM object constructor cannot be called as a function."); } if (arguments.length < 1) { throw new TypeError("Failed to construct 'URL': 1 argument required, but only " + arguments.length + " present."); } const args = []; for (let i = 0; i < arguments.length && i < 2; ++i) { args[i] = arguments[i]; } args[0] = conversions["USVString"](args[0]); if (args[1] !== undefined) { args[1] = conversions["USVString"](args[1]); } module.exports.setup(this, args); }
n/a
equalWithExcludedFragments = function (url1, url2) { [url1, url2] = [url1, url2].map(rewriteChromeInternalUrl); url1 = new URL(url1); url1.hash = ''; url2 = new URL(url2); url2.hash = ''; return url1.href === url2.href; }
...
* @param {string} url The URL of the original requested page.
* @param {{online: boolean}} driver
* @param {!Array<WebInspector.NetworkRequest>} networkRecords
*/
static assertPageLoaded(url, driver, networkRecords) {
const mainRecord = networkRecords.find(record => {
// record.url is actual request url, so needs to be compared without any URL fragment.
return URL.equalWithExcludedFragments(record.url, url);
});
if (driver.online && (!mainRecord || mainRecord.failed)) {
const message = mainRecord ? mainRecord.localizedFailDescription : 'timeout reached';
log.error('GatherRunner', message);
const error = new Error(`Unable to load the page: ${message}`);
error.code = 'PAGE_LOAD_ERROR';
throw error;
...
function getDisplayName(url, options) { options = Object.assign({ numPathParts: 2, preserveQuery: false, preserveHost: false, }, options); const parsed = new URL(url); let name; if (parsed.protocol === 'about:' || parsed.protocol === 'data:') { // Handle 'about:*' and 'data:*' URLs specially since they have no path. name = parsed.href; } else { name = parsed.pathname; const parts = name.split('/'); if (options.numPathParts && parts.length > options.numPathParts) { name = ELLIPSIS + parts.slice(-1 * options.numPathParts).join('/'); } if (options.preserveHost) { name = `${parsed.host}/${name.replace(/^\//, '')}`; } if (options.preserveQuery) { name = `${name}${parsed.search}`; } } const MAX_LENGTH = 64; // Always elide hash name = name.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g, `$1${ELLIPSIS}`); // Elide query params first if (name.length > MAX_LENGTH && name.includes('?')) { // Try to leave the first query parameter intact name = name.replace(/\?([^=]*)(=)?.*/, `?$1$2${ELLIPSIS}`); // Remove it all if it's still too long if (name.length > MAX_LENGTH) { name = name.replace(/\?.*/, `?${ELLIPSIS}`); } } // Elide too long names next if (name.length > MAX_LENGTH) { const dotIndex = name.lastIndexOf('.'); if (dotIndex >= 0) { name = name.slice(0, MAX_LENGTH - 1 - (name.length - dotIndex)) + // Show file extension `${ELLIPSIS}${name.slice(dotIndex)}`; } else { name = name.slice(0, MAX_LENGTH - 1) + ELLIPSIS; } } return name; }
n/a
function hostsMatch(urlA, urlB) { try { return new URL(urlA).host === new URL(urlB).host; } catch (e) { return false; } }
n/a
function isValid(url) { try { new URL(url); return true; } catch (e) { return false; } }
n/a
toString = function () { if (!this || !module.exports.is(this)) { throw new TypeError("Illegal invocation"); } return this.href; }
...
];
const LSREGISTER = '/System/Library/Frameworks/CoreServices.framework' +
'/Versions/A/Frameworks/LaunchServices.framework' +
'/Versions/A/Support/lsregister';
const installations = [];
execSync(`${LSREGISTER} -dump` +
' | grep -i \'google chrome\\( canary\\)\\?.app$\'' +
' | awk \'{$1=""; print $0}\'').toString()
.split(newLineRegex)
.forEach((inst) => {
suffixes.forEach(suffix => {
const execPath = path.join(inst.trim(), suffix);
if (canAccess(execPath)) {
installations.push(execPath);
}
...
AgentLayer = function (target, layerPayload) { this._target = target; this._reset(layerPayload); }
n/a
AgentLayerTree = function (target) { WebInspector.LayerTreeBase.call(this, target); }
n/a
AggregatedTimelineTreeView = function (model, filters) { this._groupBySetting = WebInspector.settings.createSetting("timelineTreeGroupBy", WebInspector.TimelineAggregator.GroupBy.Category ); WebInspector.TimelineTreeView.call(this, model, filters); var nonessentialEvents = [ WebInspector.TimelineModel.RecordType.EventDispatch, WebInspector.TimelineModel.RecordType.FunctionCall, WebInspector.TimelineModel.RecordType.TimerFire ]; this._filters.push(new WebInspector.ExclusiveNameFilter(nonessentialEvents)); this._stackView = new WebInspector.TimelineStackView(this); this._stackView.addEventListener(WebInspector.TimelineStackView.Events.SelectionChanged, this._onStackViewSelectionChanged, this); }
n/a
BackingStorage = function () { }
n/a
BottomUpTimelineTreeView = function (model, filters) { WebInspector.AggregatedTimelineTreeView.call(this, model, filters); this._dataGrid.markColumnAsSortedBy("self", WebInspector.DataGrid.Order.Descending); }
n/a
CPUProfileDataModel = function (profile) { var isLegacyFormat = !!profile["head"]; if (isLegacyFormat) { // Legacy format contains raw timestamps and start/stop times are in seconds. this.profileStartTime = profile.startTime * 1000; this.profileEndTime = profile.endTime * 1000; this.timestamps = profile.timestamps; this._compatibilityConversionHeadToNodes(profile); } else { // Current format encodes timestamps as deltas. Start/stop times are in microseconds. this.profileStartTime = profile.startTime / 1000; this.profileEndTime = profile.endTime / 1000; this.timestamps = this._convertTimeDeltas(profile); } this.samples = profile.samples; this.totalHitCount = 0; this.profileHead = this._translateProfileTree(profile.nodes); WebInspector.ProfileTreeModel.call(this, this.profileHead); this._extractMetaNodes(); if (this.samples) { this._buildIdToNodeMap(); this._sortSamples(); this._normalizeTimestamps(); } }
n/a
CPUProfileNode = function (node, sampleTime) { var callFrame = node.callFrame || /** @type {!RuntimeAgent.CallFrame} */ ({ // Backward compatibility for old SamplingHeapProfileNode format. functionName: node["functionName"], scriptId: node["scriptId"], url: node["url"], lineNumber: node["lineNumber"] - 1, columnNumber: node["columnNumber"] - 1 }); WebInspector.ProfileNode.call(this, callFrame); this.id = node.id; this.self = node.hitCount * sampleTime; this.positionTicks = node.positionTicks; // Compatibility: legacy backends could provide "no reason" for optimized functions. this.deoptReason = node.deoptReason && node.deoptReason !== "no reason" ? node.deoptReason : null; }
n/a
CallTreeTimelineTreeView = function (model, filters) { WebInspector.AggregatedTimelineTreeView.call(this, model, filters); this._dataGrid.markColumnAsSortedBy("total", WebInspector.DataGrid.Order.Descending); }
n/a
Color = function (rgba, format, originalText) { this._rgba = rgba; this._originalText = originalText || null; this._originalTextIsValid = !!this._originalText; this._format = format; if (typeof this._rgba[3] === "undefined") this._rgba[3] = 1; for (var i = 0; i < 4; ++i) { if (this._rgba[i] < 0) { this._rgba[i] = 0; this._originalTextIsValid = false; } if (this._rgba[i] > 1) { this._rgba[i] = 1; this._originalTextIsValid = false; } } }
n/a
ConsoleMessage = function () {}
n/a
DeferredLayerTree = function (target) { this._target = target; }
n/a
DeferredTempFile = function () {}
n/a
DeferredTracingLayerTree = function (snapshot, target) { WebInspector.DeferredLayerTree.call(this, target); this._snapshot = snapshot; }
n/a
Event = function (target, type, data) { this.target = target; this.type = type; this.data = data; this.defaultPrevented = false; this._stoppedPropagation = false; }
n/a
EventTarget = function () { }
n/a
EventsTimelineTreeView = function (model, filters, delegate) { this._filtersControl = new WebInspector.TimelineFilters(); this._filtersControl.addEventListener(WebInspector.TimelineFilters.Events.FilterChanged, this._onFilterChanged, this); WebInspector.TimelineTreeView.call(this, model, filters); this._delegate = delegate; this._filters.push.apply(this._filters, this._filtersControl.filters()); this._dataGrid.markColumnAsSortedBy("startTime", WebInspector.DataGrid.Order.Ascending); }
n/a
ExcludeTopLevelFilter = function () { WebInspector.TimelineModel.Filter.call(this); }
n/a
ExclusiveNameFilter = function (excludeNames) { WebInspector.TimelineModel.Filter.call(this); this._excludeNames = new Set(excludeNames); }
n/a
FilmStripModel = function (tracingModel, zeroTime) { this.reset(tracingModel, zeroTime); }
n/a
HBox = function () {}
n/a
InvalidationTracker = function () { this._initializePerFrameState(); }
n/a
InvalidationTrackingEvent = function (event)
{
/** @type {string} */
this.type = event.name;
/** @type {number} */
this.startTime = event.startTime;
/** @type {!WebInspector.TracingModel.Event} */
this._tracingEvent = event;
var eventData = event.args["data"];
/** @type {number} */
this.frame = eventData["frame"];
/** @type {?number} */
this.nodeId = eventData["nodeId"];
/** @type {?string} */
this.nodeName = eventData["nodeName"];
/** @type {?number} */
this.paintId = eventData["paintId"];
/** @type {?number} */
this.invalidationSet = eventData["invalidationSet"];
/** @type {?string} */
this.invalidatedSelectorId = eventData["invalidatedSelectorId"];
/** @type {?string} */
this.changedId = eventData["changedId"];
/** @type {?string} */
this.changedClass = eventData["changedClass"];
/** @type {?string} */
this.changedAttribute = eventData["changedAttribute"];
/** @type {?string} */
this.changedPseudo = eventData["changedPseudo"];
/** @type {?string} */
this.selectorPart = eventData["selectorPart"];
/** @type {?string} */
this.extraData = eventData["extraData"];
/** @type {?Array.<!Object.<string, number>>} */
this.invalidationList = eventData["invalidationList"];
/** @type {!WebInspector.InvalidationCause} */
this.cause = {reason: eventData["reason"], stackTrace: eventData["stackTrace"]};
// FIXME: Move this to TimelineUIUtils.js.
if (!this.cause.reason && this.cause.stackTrace && this.type === WebInspector.TimelineModel.RecordType.LayoutInvalidationTracking
)
this.cause.reason = "Layout forced";
}
n/a
Layer = function () { }
n/a
LayerPaintEvent = function (event, target) { this._event = event; this._target = target; }
n/a
LayerTreeBase = function (target)
{
this._target = target;
this._domModel = target ? WebInspector.DOMModel.fromTarget(target) : null;
this._layersById = {};
/** @type Map<number, ?WebInspector.DOMNode> */
this._backendNodeIdToNode = new Map();
this._reset();
}
n/a
LayerTreeDispatcher = function (layerTreeModel) { this._layerTreeModel = layerTreeModel; }
n/a
LayerTreeModel = function (target)
{
WebInspector.SDKModel.call(this, WebInspector.LayerTreeModel, target);
target.registerLayerTreeDispatcher(new WebInspector.LayerTreeDispatcher(this));
WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.MainFrameNavigated, this._onMainFrameNavigated
, this);
/** @type {?WebInspector.LayerTreeBase} */
this._layerTree = null;
}
n/a
MultitargetNetworkManager = function ()
{
WebInspector.Object.call(this);
WebInspector.targetManager.observeTargets(this);
/** @type {!Set<string>} */
this._blockedURLs = new Set();
this._blockedSetting = WebInspector.moduleSetting("blockedURLs");
this._blockedSetting.addChangeListener(this._updateBlockedURLs, this);
this._blockedSetting.set([]);
this._updateBlockedURLs();
this._userAgentOverride = "";
/** @type {!Set<!Protocol.NetworkAgent>} */
this._agents = new Set();
/** @type {!WebInspector.NetworkManager.Conditions} */
this._networkConditions = WebInspector.NetworkManager.NoThrottlingConditions;
}
n/a
NetworkDispatcher = function (manager) { this._manager = manager; this._inflightRequestsById = {}; this._inflightRequestsByURL = {}; }
n/a
NetworkLog = function (target) { this._requests = new Map(); target.networkManager.addEventListener( WebInspector.NetworkManager.Events.RequestStarted, this._onRequestStarted, this); }
...
return fakeNetworkAgent;
},
registerNetworkDispatcher() { },
model() { }
};
fakeTarget.networkManager = new WebInspector.NetworkManager(fakeTarget);
fakeTarget.networkLog = new WebInspector.NetworkLog(fakeTarget);
WebInspector.NetworkLog.fromTarget = () => {
return fakeTarget.networkLog;
};
return fakeTarget.networkManager;
};
...
NetworkManager = function (target) { WebInspector.SDKModel.call(this, WebInspector.NetworkManager, target); this._dispatcher = new WebInspector.NetworkDispatcher(this); this._target = target; this._networkAgent = target.networkAgent(); target.registerNetworkDispatcher(this._dispatcher); if (WebInspector.moduleSetting("cacheDisabled").get()) this._networkAgent.setCacheDisabled(true); if (WebInspector.moduleSetting("monitoringXHREnabled").get()) this._networkAgent.setMonitoringXHREnabled(true); // Limit buffer when talking to a remote device. if (Runtime.queryParam("remoteFrontend") || Runtime.queryParam("ws")) this._networkAgent.enable(10000000, 5000000); else this._networkAgent.enable(); this._bypassServiceWorkerSetting = WebInspector.settings.createSetting("bypassServiceWorker", false); if (this._bypassServiceWorkerSetting.get()) this._bypassServiceWorkerChanged(); this._bypassServiceWorkerSetting.addChangeListener(this._bypassServiceWorkerChanged, this); WebInspector.moduleSetting("cacheDisabled").addChangeListener(this._cacheDisabledSettingChanged, this); }
...
networkAgent() {
return fakeNetworkAgent;
},
registerNetworkDispatcher() { },
model() { }
};
fakeTarget.networkManager = new WebInspector.NetworkManager(fakeTarget);
fakeTarget.networkLog = new WebInspector.NetworkLog(fakeTarget);
WebInspector.NetworkLog.fromTarget = () => {
return fakeTarget.networkLog;
};
return fakeTarget.networkManager;
...
NetworkRequest = function (target, requestId, url, documentURL, frameId, loaderId, initiator)
{
WebInspector.SDKObject.call(this, target);
this._networkLog = /** @type {!WebInspector.NetworkLog} */ (WebInspector.NetworkLog.fromTarget(target));
this._networkManager = /** @type {!WebInspector.NetworkManager} */ (WebInspector.NetworkManager.fromTarget(target));
this._requestId = requestId;
this.url = url;
this._documentURL = documentURL;
this._frameId = frameId;
this._loaderId = loaderId;
/** @type {?NetworkAgent.Initiator} */
this._initiator = initiator;
this._issueTime = -1;
this._startTime = -1;
this._endTime = -1;
/** @type {!NetworkAgent.BlockedReason|undefined} */
this._blockedReason = undefined;
this.statusCode = 0;
this.statusText = "";
this.requestMethod = "";
this.requestTime = 0;
this.protocol = "";
/** @type {!NetworkAgent.RequestMixedContentType} */
this.mixedContentType = NetworkAgent.RequestMixedContentType.None;
/** @type {?NetworkAgent.ResourcePriority} */
this._initialPriority = null;
/** @type {?NetworkAgent.ResourcePriority} */
this._currentPriority = null;
/** @type {!WebInspector.ResourceType} */
this._resourceType = WebInspector.resourceTypes.Other;
this._contentEncoded = false;
this._pendingContentCallbacks = [];
/** @type {!Array.<!WebInspector.NetworkRequest.WebSocketFrame>} */
this._frames = [];
/** @type {!Array.<!WebInspector.NetworkRequest.EventSourceMessage>} */
this._eventSourceMessages = [];
this._responseHeaderValues = {};
this._remoteAddress = "";
/** @type {!SecurityAgent.SecurityState} */
this._securityState = SecurityAgent.SecurityState.Unknown;
/** @type {?NetworkAgent.SecurityDetails} */
this._securityDetails = null;
/** @type {string} */
this.connectionId = "0";
}
n/a
Object = function () { }
n/a
ParsedURL = function (url) { this.isValid = false; this.url = url; this.scheme = ""; this.host = ""; this.port = ""; this.path = ""; this.queryParams = ""; this.fragment = ""; this.folderPathComponents = ""; this.lastPathComponent = ""; // RegExp groups: // 1 - scheme (using the RFC3986 grammar) // 2 - hostname // 3 - ?port // 4 - ?path // 5 - ?fragment var match = url.match(/^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\s\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i); if (match) { this.isValid = true; this.scheme = match[1].toLowerCase(); this.host = match[2]; this.port = match[3]; this.path = match[4] || "/"; this.fragment = match[5]; } else { if (this.url.startsWith("data:")) { this.scheme = "data"; return; } if (this.url === "about:blank") { this.scheme = "about"; return; } this.path = this.url; } // First cut the query params. var path = this.path; var indexOfQuery = path.indexOf("?"); if (indexOfQuery !== -1) { this.queryParams = path.substring(indexOfQuery + 1); path = path.substring(0, indexOfQuery); } // Then take last path component. var lastSlashIndex = path.lastIndexOf("/"); if (lastSlashIndex !== -1) { this.folderPathComponents = path.substring(0, lastSlashIndex); this.lastPathComponent = path.substring(lastSlashIndex + 1); } else this.lastPathComponent = path; }
n/a
PendingFrame = function (triggerTime, timeByCategory)
{
/** @type {!Object.<string, number>} */
this.timeByCategory = timeByCategory;
/** @type {!Array.<!WebInspector.LayerPaintEvent>} */
this.paints = [];
/** @type {number|undefined} */
this.mainFrameId = undefined;
this.triggerTime = triggerTime;
}
n/a
ProfileNode = function (callFrame)
{
/** @type {!RuntimeAgent.CallFrame} */
this.callFrame = callFrame;
/** @type {string} */
this.callUID = `${this.callFrame.functionName}@${this.callFrame.scriptId}:${this.callFrame.lineNumber}`;
/** @type {number} */
this.self = 0;
/** @type {number} */
this.total = 0;
/** @type {number} */
this.id = 0;
/** @type {?WebInspector.ProfileNode} */
this.parent = null;
/** @type {!Array<!WebInspector.ProfileNode>} */
this.children = [];
}
n/a
ProfileTreeModel = function (root) { this.root = root; this._assignDepthsAndParents(); this.total = this._calculateTotals(this.root); }
n/a
ResourceCategory = function (title, shortTitle) { this.title = title; this.shortTitle = shortTitle; }
n/a
ResourceType = function (name, title, category, isTextType) { this._name = name; this._title = title; this._category = category; this._isTextType = isTextType; }
n/a
SCSSParser = function () { }
n/a
SDKModel = function (modelClass, target) { WebInspector.SDKObject.call(this, target); target._modelByConstructor.set(modelClass, this); WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.TargetDisposed, this._targetDisposed, this); }
n/a
SDKObject = function (target) { WebInspector.Object.call(this); this._target = target; }
n/a
Segment = function (begin, end, data) { if (begin > end) console.assert(false, "Invalid segment"); this.begin = begin; this.end = end; this.data = data; }
n/a
SegmentedRange = function (mergeCallback)
{
/** @type {!Array<!WebInspector.Segment>} */
this._segments = [];
this._mergeCallback = mergeCallback;
}
n/a
SortableDataGrid = function (columnsArray, editCallback, deleteCallback, refreshCallback, contextMenuCallback)
{
WebInspector.ViewportDataGrid.call(this, columnsArray, editCallback, deleteCallback, refreshCallback, contextMenuCallback);
/** @type {!WebInspector.SortableDataGrid.NodeComparator} */
this._sortingFunction = WebInspector.SortableDataGrid.TrivialComparator;
this.setRootNode(new WebInspector.SortableDataGridNode());
}
n/a
SortableDataGridNode = function (data, hasChildren) { WebInspector.ViewportDataGridNode.call(this, data, hasChildren); }
n/a
SourceEdit = function (sourceURL, oldRange, newText) { this.sourceURL = sourceURL; this.oldRange = oldRange; this.newText = newText; }
n/a
SourceRange = function (offset, length) { this.offset = offset; this.length = length; }
n/a
Target = function (targetManager, name, capabilitiesMask, connection, parentTarget)
{
Protocol.Agents.call(this, connection.agentsMap());
this._targetManager = targetManager;
this._name = name;
this._inspectedURL = "";
this._capabilitiesMask = capabilitiesMask;
this._connection = connection;
this._parentTarget = parentTarget;
connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, this._onDisconnect, this);
this._id = WebInspector.Target._nextId++;
/** @type {!Map.<!Function, !WebInspector.SDKModel>} */
this._modelByConstructor = new Map();
}
n/a
TargetManager = function ()
{
WebInspector.Object.call(this);
/** @type {!Array.<!WebInspector.Target>} */
this._targets = [];
/** @type {!Array.<!WebInspector.TargetManager.Observer>} */
this._observers = [];
this._observerCapabiliesMaskSymbol = Symbol("observerCapabilitiesMask");
/** @type {!Map<symbol, !Array<{modelClass: !Function, thisObject: (!Object|undefined), listener: function(!WebInspector.Event
)}>>} */
this._modelListeners = new Map();
this._isSuspended = false;
}
n/a
TempFile = function () { this._fileEntry = null; this._writer = null; }
n/a
TempFileBackingStorage = function (dirName) { this._dirName = dirName; this.reset(); }
n/a
TextRange = function (startLine, startColumn, endLine, endColumn) { this.startLine = startLine; this.startColumn = startColumn; this.endLine = endLine; this.endColumn = endColumn; }
n/a
TimelineAggregator = function (titleMapper, categoryMapper)
{
this._titleMapper = titleMapper;
this._categoryMapper = categoryMapper;
/** @type {!Map<string, !WebInspector.TimelineProfileTree.Node>} */
this._groupNodes = new Map();
}
n/a
TimelineAsyncEventTracker = function ()
{
WebInspector.TimelineAsyncEventTracker._initialize();
/** @type {!Map<!WebInspector.TimelineModel.RecordType, !Map<string, !WebInspector.TracingModel.Event>>} */
this._initiatorByType = new Map();
for (var initiator of WebInspector.TimelineAsyncEventTracker._asyncEvents.keys())
this._initiatorByType.set(initiator, new Map());
}
n/a
TimelineCategory = function (name, title, visible, childColor, color) { this.name = name; this.title = title; this.visible = visible; this.childColor = childColor; this.color = color; this.hidden = false; }
n/a
TimelineDetailsContentHelper = function (target, linkifier) { this.fragment = createDocumentFragment(); this._linkifier = linkifier; this._target = target; this.element = createElementWithClass("div", "timeline-details-view-block"); this._tableElement = this.element.createChild("div", "vbox timeline-details-chip-body"); this.fragment.appendChild(this.element); }
n/a
TimelineFrame = function (startTime, startTimeOffset)
{
this.startTime = startTime;
this.startTimeOffset = startTimeOffset;
this.endTime = this.startTime;
this.duration = 0;
this.timeByCategory = {};
this.cpuTime = 0;
this.idle = false;
/** @type {?WebInspector.DeferredLayerTree} */
this.layerTree = null;
/** @type {!Array.<!WebInspector.LayerPaintEvent>} */
this.paints = [];
/** @type {number|undefined} */
this._mainFrameId = undefined;
}
n/a
TimelineFrameModel = function (categoryMapper) { this._categoryMapper = categoryMapper; this.reset(); }
n/a
TimelineIRModel = function () { this.reset(); }
n/a
TimelineModel = function (eventFilter) { this._eventFilter = eventFilter; this.reset(); }
n/a
TimelinePopupContentHelper = function (title) { this._contentTable = createElement("table"); var titleCell = this._createCell(WebInspector.UIString("%s - Details", title), "timeline-details-title"); titleCell.colSpan = 2; var titleRow = createElement("tr"); titleRow.appendChild(titleCell); this._contentTable.appendChild(titleRow); }
n/a
TimelineRecordStyle = function (title, category, hidden) { this.title = title; this.category = category; this.hidden = !!hidden; }
n/a
TimelineStackView = function (treeView) { WebInspector.VBox.call(this); var header = this.element.createChild("div", "timeline-stack-view-header"); header.textContent = WebInspector.UIString("Heaviest stack"); this._treeView = treeView; var columns = [ {id: "total", title: WebInspector.UIString("Total Time"), fixedWidth: true, width: "110px"}, {id: "activity", title: WebInspector.UIString("Activity")} ]; this._dataGrid = new WebInspector.ViewportDataGrid(columns); this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last); this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectionChanged, this); this._dataGrid.asWidget().show(this.element); }
n/a
TimelineTreeView = function (model, filters)
{
WebInspector.VBox.call(this);
this.element.classList.add("timeline-tree-view");
this._model = model;
this._linkifier = new WebInspector.Linkifier();
this._filters = filters.slice();
var columns = [];
this._populateColumns(columns);
var mainView = new WebInspector.VBox();
this._populateToolbar(mainView.element);
this._dataGrid = new WebInspector.SortableDataGrid(columns);
this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortingChanged, this);
this._dataGrid.element.addEventListener("mousemove", this._onMouseMove.bind(this), true)
this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last);
this._dataGrid.asWidget().show(mainView.element);
this._splitWidget = new WebInspector.SplitWidget(true, true, "timelineTreeViewDetailsSplitWidget");
this._splitWidget.show(this.element);
this._splitWidget.setMainWidget(mainView);
this._detailsView = new WebInspector.VBox();
this._detailsView.element.classList.add("timeline-details-view", "timeline-details-view-body");
this._splitWidget.setSidebarWidget(this._detailsView);
this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._updateDetailsForSelection, this);
/** @type {?WebInspector.TimelineProfileTree.Node|undefined} */
this._lastSelectedNode;
}
n/a
TimelineUIUtils = function () { }
n/a
TimelineVisibleEventsFilter = function (visibleTypes) { WebInspector.TimelineModel.Filter.call(this); this._visibleTypes = new Set(visibleTypes); }
n/a
TracingLayer = function (payload) { this._reset(payload); }
n/a
TracingLayerTree = function (target)
{
WebInspector.LayerTreeBase.call(this, target);
/** @type {!Map.<string, !WebInspector.TracingLayerTile>} */
this._tileById = new Map();
}
n/a
TracingModel = function (backingStorage) { this._backingStorage = backingStorage; // Avoid extra reset of the storage as it's expensive. this._firstWritePending = true; this.reset(); }
n/a
UIString = function (string, vararg) { return String.vsprintf(WebInspector.localize(string), Array.prototype.slice.call(arguments, 1)); }
n/a
UIStringFormat = function (format)
{
/** @type {string} */
this._localizedFormat = WebInspector.localize(format);
/** @type {!Array.<!Object>} */
this._tokenizedFormat = String.tokenizeFormatString(this._localizedFormat, String.standardFormatters);
}
n/a
VBox = function () {}
n/a
ViewportDataGrid = function () {}
n/a
ViewportDataGridNode = function () {}
n/a
localize = function (string) { return string; }
n/a
moduleSetting = function (settingName) { return this._moduleSettings[settingName]; }
n/a
setLocalizationPlatform = function (platform) { WebInspector._useLowerCaseMenuTitles = platform === "windows"; }
n/a