Skip to content
Closed
5 changes: 3 additions & 2 deletions lighthouse-cli/cli-flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function getFlags(manualArgv) {
'list-trace-categories': 'Prints a list of all required trace categories and exits',
'additional-trace-categories':
'Additional categories to capture with the trace (comma-delimited).',
'config-path': `The path to the config JSON.
'config-path': `The path to the config JSON.
An example config file: lighthouse-core/config/lr-desktop-config.js`,
'preset': `Use a built-in configuration.
WARNING: If the --config-path flag is provided, this preset will be ignored.`,
Expand All @@ -106,6 +106,7 @@ function getFlags(manualArgv) {
'skip-audits': 'Run everything except these audits',
'plugins': 'Run the specified plugins',
'print-config': 'Print the normalized config for the given config and options, then exit.',
'ignore-https-errors': 'Whether to ignore HTTPS errors during navigation. Defaults to false',
})
// set aliases
.alias({'gather-mode': 'G', 'audit-mode': 'A'})
Expand All @@ -124,7 +125,7 @@ function getFlags(manualArgv) {
// boolean values
.boolean([
'disable-storage-reset', 'disable-device-emulation', 'save-assets', 'list-all-audits',
'list-trace-categories', 'view', 'verbose', 'quiet', 'help', 'print-config',
'list-trace-categories', 'view', 'verbose', 'quiet', 'help', 'print-config', 'ignore-https-errors',
])
.choices('output', printer.getValidOutputOptions())
.choices('emulated-form-factor', ['mobile', 'desktop', 'none'])
Expand Down
2 changes: 2 additions & 0 deletions lighthouse-cli/test/cli/__snapshots__/index-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,7 @@ Object {
"emulatedFormFactor": "mobile",
"extraHeaders": null,
"gatherMode": false,
"ignoreHttpsErrors": false,
"locale": "en-US",
"maxWaitForFcp": 15000,
"maxWaitForLoad": 45000,
Expand Down Expand Up @@ -1335,6 +1336,7 @@ Object {
"emulatedFormFactor": "mobile",
"extraHeaders": null,
"gatherMode": false,
"ignoreHttpsErrors": false,
"locale": "en-US",
"maxWaitForFcp": 15000,
"maxWaitForLoad": 45000,
Expand Down
1 change: 1 addition & 0 deletions lighthouse-core/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const defaultSettings = {
onlyAudits: null,
onlyCategories: null,
skipAudits: null,
ignoreHttpsErrors: false,
};

/** @type {LH.Config.Pass} */
Expand Down
41 changes: 37 additions & 4 deletions lighthouse-core/gather/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,17 @@ class Driver {
return {promise: Promise.resolve(), cancel() {}};
}

/**
* Returns a promise that never resolves.
* Used for placeholder conditions that we don't want race with, but still want to satisfy the same
* interface.
* @return {{promise: Promise<void>, cancel: function(): void}}
*/
_waitForever() {
return {promise: new Promise(() => null), cancel() {}};
}


/**
* Returns a promise that resolve when a frame has been navigated.
* Used for detecting that our about:blank reset has been completed.
Expand Down Expand Up @@ -865,11 +876,12 @@ class Driver {
* @param {number} cpuQuietThresholdMs
* @param {number} maxWaitForLoadedMs
* @param {number=} maxWaitForFCPMs
* @param {boolean} ignoreHttpsErrors
* @return {Promise<void>}
* @private
*/
async _waitForFullyLoaded(pauseAfterLoadMs, networkQuietThresholdMs, cpuQuietThresholdMs,
maxWaitForLoadedMs, maxWaitForFCPMs) {
maxWaitForLoadedMs, maxWaitForFCPMs, ignoreHttpsErrors) {
/** @type {NodeJS.Timer|undefined} */
let maxTimeoutHandle;

Expand All @@ -882,7 +894,9 @@ class Driver {
// CPU listener. Resolves when the CPU has been idle for cpuQuietThresholdMs after network idle.
let waitForCPUIdle = this._waitForNothing();

const monitorForInsecureState = this._monitorForInsecureState();
const monitorForInsecureState = ignoreHttpsErrors
? this._waitForever()
: this._monitorForInsecureState();
const securityCheckPromise = monitorForInsecureState.promise.then(securityMessages => {
return function() {
throw new LHError(LHError.errors.INSECURE_DOCUMENT_REQUEST, {securityMessages});
Expand Down Expand Up @@ -1024,7 +1038,7 @@ class Driver {
* possible workaround.
* Resolves on the url of the loaded page, taking into account any redirects.
* @param {string} url
* @param {{waitForFCP?: boolean, waitForLoad?: boolean, waitForNavigated?: boolean, passContext?: LH.Gatherer.PassContext}} options
* @param {{waitForFCP?: boolean, waitForLoad?: boolean, waitForNavigated?: boolean, passContext?: LH.Gatherer.PassContext, ignoreHttpsErrors?: boolean}} options
* @return {Promise<string>}
*/
async gotoURL(url, options = {}) {
Expand All @@ -1033,6 +1047,7 @@ class Driver {
const waitForLoad = options.waitForLoad || false;
const passContext = /** @type {Partial<LH.Gatherer.PassContext>} */ (options.passContext || {});
const disableJS = passContext.disableJavaScript || false;
const ignoreHttpsErrors = options.ignoreHttpsErrors || false;

if (waitForNavigated && (waitForFCP || waitForLoad)) {
throw new Error('Cannot use both waitForNavigated and another event, pick just one');
Expand Down Expand Up @@ -1071,7 +1086,7 @@ class Driver {

if (!waitForFCP) maxFCPMs = undefined;
await this._waitForFullyLoaded(pauseAfterLoadMs, networkQuietThresholdMs, cpuQuietThresholdMs,
maxWaitMs, maxFCPMs);
maxWaitMs, maxFCPMs, ignoreHttpsErrors);
}

// Bring `Page.navigate` errors back into the promise chain. See https://github.com/GoogleChrome/lighthouse/pull/6739.
Expand Down Expand Up @@ -1454,6 +1469,24 @@ class Driver {

await this.sendCommand('Page.enable');
}

/**
* Set chrome security settings to ignore certificate errors.
* @return {Promise<void>}
*/
async setIgnoreHttpsErrors() {
// the devtools protocol also has an experimental Security.setIgnoreCertificateErrors
// https://chromedevtools.github.io/devtools-protocol/tot/Security#method-setIgnoreCertificateErrors
// It can be used instead of the following when it becomes stable.
this.on('Security.certificateError', event => {
this.sendCommand('Security.handleCertificateError', {
eventId: event.eventId,
action: 'continue',
}).catch(err => log.warn('Driver', err));
});
await this.sendCommand('Security.enable');
await this.sendCommand('Security.setOverrideCertificateErrors', {override: true});
}
}

module.exports = Driver;
3 changes: 3 additions & 0 deletions lighthouse-core/gather/gather-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class GatherRunner {
const finalUrl = await driver.gotoURL(passContext.url, {
waitForFCP: passContext.passConfig.recordTrace,
waitForLoad: true,
ignoreHttpsErrors: passContext.settings.ignoreHttpsErrors,
passContext,
});
passContext.url = finalUrl;
Expand All @@ -105,12 +106,14 @@ class GatherRunner {
const status = {msg: 'Initializing…', id: 'lh:gather:setupDriver'};
log.time(status);
const resetStorage = !options.settings.disableStorageReset;
const ignoreHttpsErrors = options.settings.ignoreHttpsErrors;
await driver.assertNoSameOriginServiceWorkerClients(options.requestedUrl);
await driver.beginEmulation(options.settings);
await driver.enableRuntimeEvents();
await driver.cacheNatives();
await driver.registerPerformanceObserver();
await driver.dismissJavaScriptDialogs();
if (ignoreHttpsErrors) await driver.setIgnoreHttpsErrors();
if (resetStorage) await driver.clearDataForOrigin(options.requestedUrl);
log.timeEnd(status);
}
Expand Down
2 changes: 2 additions & 0 deletions types/externs.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ declare global {
channel?: string
/** Precomputed lantern estimates to use instead of observed analysis. */
precomputedLanternData?: PrecomputedLanternData | null;
/** Ignore HTTPS errors during navigation */
ignoreHttpsErrors?: boolean;
}

/**
Expand Down