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