lighthouse 13.3.0 → 13.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/cli-flags.d.ts +8 -9
- package/cli/test/smokehouse/config/exclusions.js +2 -0
- package/cli/test/smokehouse/version-check.d.ts +1 -1
- package/core/audits/accessibility/accesskeys.js +1 -1
- package/core/audits/accessibility/aria-allowed-attr.js +1 -1
- package/core/audits/accessibility/aria-allowed-role.js +1 -1
- package/core/audits/accessibility/aria-command-name.js +1 -1
- package/core/audits/accessibility/aria-conditional-attr.js +1 -1
- package/core/audits/accessibility/aria-deprecated-role.js +1 -1
- package/core/audits/accessibility/aria-dialog-name.js +1 -1
- package/core/audits/accessibility/aria-hidden-body.js +1 -1
- package/core/audits/accessibility/aria-hidden-focus.js +1 -1
- package/core/audits/accessibility/aria-input-field-name.js +1 -1
- package/core/audits/accessibility/aria-meter-name.js +1 -1
- package/core/audits/accessibility/aria-progressbar-name.js +1 -1
- package/core/audits/accessibility/aria-prohibited-attr.js +1 -1
- package/core/audits/accessibility/aria-required-attr.js +1 -1
- package/core/audits/accessibility/aria-required-children.js +1 -1
- package/core/audits/accessibility/aria-required-parent.js +1 -1
- package/core/audits/accessibility/aria-roles.js +1 -1
- package/core/audits/accessibility/aria-text.js +1 -1
- package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
- package/core/audits/accessibility/aria-tooltip-name.js +1 -1
- package/core/audits/accessibility/aria-treeitem-name.js +1 -1
- package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
- package/core/audits/accessibility/aria-valid-attr.js +1 -1
- package/core/audits/accessibility/autocomplete-valid.js +1 -1
- package/core/audits/accessibility/button-name.js +1 -1
- package/core/audits/accessibility/bypass.js +1 -1
- package/core/audits/accessibility/color-contrast.js +1 -1
- package/core/audits/accessibility/definition-list.js +1 -1
- package/core/audits/accessibility/dlitem.js +1 -1
- package/core/audits/accessibility/document-title.js +1 -1
- package/core/audits/accessibility/duplicate-id-aria.js +1 -1
- package/core/audits/accessibility/empty-heading.js +1 -1
- package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
- package/core/audits/accessibility/frame-title.js +1 -1
- package/core/audits/accessibility/heading-order.js +1 -1
- package/core/audits/accessibility/html-has-lang.js +1 -1
- package/core/audits/accessibility/html-lang-valid.js +1 -1
- package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
- package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
- package/core/audits/accessibility/image-alt.js +1 -1
- package/core/audits/accessibility/image-redundant-alt.js +1 -1
- package/core/audits/accessibility/input-button-name.js +1 -1
- package/core/audits/accessibility/input-image-alt.js +1 -1
- package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
- package/core/audits/accessibility/label.js +1 -1
- package/core/audits/accessibility/landmark-one-main.js +1 -1
- package/core/audits/accessibility/link-in-text-block.js +1 -1
- package/core/audits/accessibility/link-name.js +1 -1
- package/core/audits/accessibility/list.js +1 -1
- package/core/audits/accessibility/listitem.js +1 -1
- package/core/audits/accessibility/meta-refresh.js +1 -1
- package/core/audits/accessibility/meta-viewport.js +1 -1
- package/core/audits/accessibility/object-alt.js +1 -1
- package/core/audits/accessibility/presentation-role-conflict.js +1 -1
- package/core/audits/accessibility/select-name.js +1 -1
- package/core/audits/accessibility/skip-link.js +1 -1
- package/core/audits/accessibility/svg-img-alt.js +1 -1
- package/core/audits/accessibility/tabindex.js +1 -1
- package/core/audits/accessibility/table-duplicate-name.js +1 -1
- package/core/audits/accessibility/table-fake-caption.js +1 -1
- package/core/audits/accessibility/target-size.js +1 -1
- package/core/audits/accessibility/td-has-header.js +1 -1
- package/core/audits/accessibility/td-headers-attr.js +1 -1
- package/core/audits/accessibility/th-has-data-cells.js +1 -1
- package/core/audits/accessibility/valid-lang.js +1 -1
- package/core/audits/accessibility/video-caption.js +1 -1
- package/core/audits/agentic/llms-txt.js +1 -1
- package/core/audits/baseline.js +10 -12
- package/core/audits/dobetterweb/geolocation-on-start.js +2 -1
- package/core/audits/network-requests.js +2 -0
- package/core/audits/seo/canonical.js +19 -7
- package/core/computed/js-bundles.d.ts +1 -1
- package/core/computed/load-simulator.d.ts +1 -1
- package/core/computed/metrics/first-contentful-paint-all-frames.d.ts +1 -1
- package/core/computed/metrics/first-contentful-paint.d.ts +1 -1
- package/core/computed/metrics/interactive.d.ts +1 -1
- package/core/computed/metrics/lantern-metric.d.ts +6 -6
- package/core/computed/metrics/largest-contentful-paint-all-frames.d.ts +1 -1
- package/core/computed/metrics/largest-contentful-paint.d.ts +1 -1
- package/core/computed/metrics/max-potential-fid.d.ts +1 -1
- package/core/computed/metrics/speed-index.d.ts +1 -1
- package/core/computed/metrics/time-to-first-byte.d.ts +1 -1
- package/core/computed/metrics/total-blocking-time.d.ts +1 -1
- package/core/computed/module-duplication.d.ts +1 -1
- package/core/computed/page-dependency-graph.d.ts +1 -1
- package/core/computed/unused-css.d.ts +1 -1
- package/core/config/agentic-browsing-config.d.ts +1 -0
- package/core/config/agentic-browsing-config.js +1 -0
- package/core/config/config-helpers.d.ts +1 -0
- package/core/config/config-helpers.js +1 -1
- package/core/config/config-plugin.d.ts +1 -0
- package/core/config/config-plugin.js +1 -0
- package/core/config/config.d.ts +1 -0
- package/core/config/config.js +1 -0
- package/core/config/constants.d.ts +1 -0
- package/core/config/constants.js +1 -0
- package/core/config/experimental-config.d.ts +1 -9
- package/core/config/experimental-config.js +2 -0
- package/core/config/filters.d.ts +1 -0
- package/core/config/filters.js +1 -0
- package/core/config/full-config.d.ts +1 -5
- package/core/config/full-config.js +2 -0
- package/core/config/lr-desktop-config.d.ts +1 -0
- package/core/config/lr-desktop-config.js +1 -0
- package/core/config/lr-mobile-config.d.ts +1 -5
- package/core/config/lr-mobile-config.js +2 -0
- package/core/config/perf-config.d.ts +1 -5
- package/core/config/perf-config.js +2 -0
- package/core/config/validation.d.ts +1 -0
- package/core/config/validation.js +1 -0
- package/core/gather/driver/execution-context.d.ts +23 -0
- package/core/gather/driver/execution-context.js +86 -1
- package/core/gather/driver/storage.js +13 -0
- package/core/gather/driver.d.ts +1 -1
- package/core/gather/gatherers/accessibility.js +1 -0
- package/core/gather/gatherers/trace-elements.d.ts +4 -1
- package/core/gather/gatherers/trace-elements.js +24 -28
- package/core/gather/gatherers/webmcp-schema.js +9 -16
- package/core/gather/gatherers/webmcp.d.ts +5 -0
- package/core/gather/gatherers/webmcp.js +34 -27
- package/core/lib/baseline/web-features-data.json +1177 -0
- package/core/lib/baseline/web-features-metadata.json +1 -1
- package/core/lib/deprecations-strings.d.ts +21 -5
- package/core/lib/deprecations-strings.js +16 -0
- package/core/lib/script-helpers.js +13 -1
- package/core/scoring.d.ts +58 -58
- package/dist/report/bundle.esm.js +4 -7
- package/dist/report/flow.js +6 -9
- package/dist/report/standalone.js +20 -12
- package/flow-report/types/flow-report.d.ts +2 -2
- package/package.json +20 -23
- package/report/assets/styles.css +1 -4
- package/report/renderer/components.js +1 -1
- package/report/renderer/details-renderer.d.ts +6 -1
- package/report/renderer/details-renderer.js +11 -3
- package/report/renderer/explodey-gauge.js +9 -7
- package/report/renderer/i18n-formatter.d.ts +1 -1
- package/report/renderer/logger.js +18 -4
- package/report/renderer/text-encoding.js +1 -1
- package/report/types/html-renderer.d.ts +2 -2
- package/shared/localization/locales/ar-XB.json +290 -65
- package/shared/localization/locales/ar.json +290 -65
- package/shared/localization/locales/bg.json +290 -65
- package/shared/localization/locales/ca.json +295 -70
- package/shared/localization/locales/cs.json +290 -65
- package/shared/localization/locales/da.json +294 -69
- package/shared/localization/locales/de.json +295 -70
- package/shared/localization/locales/el.json +290 -65
- package/shared/localization/locales/en-GB.json +290 -65
- package/shared/localization/locales/en-US.json +79 -67
- package/shared/localization/locales/en-XA.json +253 -64
- package/shared/localization/locales/en-XL.json +79 -67
- package/shared/localization/locales/es-419.json +290 -65
- package/shared/localization/locales/es.json +298 -73
- package/shared/localization/locales/fi.json +290 -65
- package/shared/localization/locales/fil.json +290 -65
- package/shared/localization/locales/fr.json +294 -69
- package/shared/localization/locales/he.json +293 -68
- package/shared/localization/locales/hi.json +291 -66
- package/shared/localization/locales/hr.json +290 -65
- package/shared/localization/locales/hu.json +290 -65
- package/shared/localization/locales/id.json +290 -65
- package/shared/localization/locales/it.json +294 -69
- package/shared/localization/locales/ja.json +290 -65
- package/shared/localization/locales/ko.json +290 -65
- package/shared/localization/locales/lt.json +290 -65
- package/shared/localization/locales/lv.json +290 -65
- package/shared/localization/locales/nl.json +290 -65
- package/shared/localization/locales/no.json +290 -65
- package/shared/localization/locales/pl.json +290 -65
- package/shared/localization/locales/pt-PT.json +291 -66
- package/shared/localization/locales/pt.json +290 -65
- package/shared/localization/locales/ro.json +290 -65
- package/shared/localization/locales/ru.json +301 -76
- package/shared/localization/locales/sk.json +291 -66
- package/shared/localization/locales/sl.json +290 -65
- package/shared/localization/locales/sr-Latn.json +290 -65
- package/shared/localization/locales/sr.json +290 -65
- package/shared/localization/locales/sv.json +297 -72
- package/shared/localization/locales/ta.json +291 -66
- package/shared/localization/locales/te.json +293 -68
- package/shared/localization/locales/th.json +291 -66
- package/shared/localization/locales/tr.json +290 -65
- package/shared/localization/locales/uk.json +290 -65
- package/shared/localization/locales/vi.json +291 -66
- package/shared/localization/locales/zh-HK.json +292 -67
- package/shared/localization/locales/zh-TW.json +291 -66
- package/shared/localization/locales/zh.json +291 -66
- package/shared/types/shared.d.ts +1 -1
- package/tsconfig-base.json +2 -1
- package/tsconfig.json +2 -0
- package/types/artifacts.d.ts +1 -1
- package/types/internal/rxjs.d.ts +1 -1
- package/types/internal/smokehouse.d.ts +1 -1
- package/types.d.ts +2 -0
- package/types.js +11 -0
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
export default config;
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright 2020 Google LLC
|
|
5
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* @fileoverview Config for new audits that aren't quite ready for
|
|
9
|
-
* being enabled by default.
|
|
10
|
-
*/
|
|
11
2
|
/** @type {LH.Config} */
|
|
12
3
|
declare const config: LH.Config;
|
|
4
|
+
import * as LH from '../../types/lh.js';
|
|
13
5
|
//# sourceMappingURL=experimental-config.d.ts.map
|
package/core/config/filters.d.ts
CHANGED
|
@@ -72,4 +72,5 @@ export function filterCategoriesByExplicitFilters(categories: LH.Config.Resolved
|
|
|
72
72
|
* @return {LH.Config.ResolvedConfig['categories']}
|
|
73
73
|
*/
|
|
74
74
|
export function filterCategoriesByGatherMode(categories: LH.Config.ResolvedConfig["categories"], mode: LH.Gatherer.GatherMode): LH.Config.ResolvedConfig["categories"];
|
|
75
|
+
import * as LH from '../../types/lh.js';
|
|
75
76
|
//# sourceMappingURL=filters.d.ts.map
|
package/core/config/filters.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
export default fullConfig;
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright 2017 Google LLC
|
|
5
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
-
*/
|
|
7
2
|
/** @type {LH.Config} */
|
|
8
3
|
declare const fullConfig: LH.Config;
|
|
4
|
+
import * as LH from '../../types/lh.js';
|
|
9
5
|
//# sourceMappingURL=full-config.d.ts.map
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
export default config;
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright 2018 Google LLC
|
|
5
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
-
*/
|
|
7
2
|
/** @type {LH.Config} */
|
|
8
3
|
declare const config: LH.Config;
|
|
4
|
+
import * as LH from '../../types/lh.js';
|
|
9
5
|
//# sourceMappingURL=lr-mobile-config.d.ts.map
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
export default perfConfig;
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright 2018 Google LLC
|
|
5
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
-
*/
|
|
7
2
|
/** @type {LH.Config} */
|
|
8
3
|
declare const perfConfig: LH.Config;
|
|
4
|
+
import * as LH from '../../types/lh.js';
|
|
9
5
|
//# sourceMappingURL=perf-config.d.ts.map
|
|
@@ -58,4 +58,5 @@ export function throwInvalidDependencyOrder(artifactId: string, dependencyKey: s
|
|
|
58
58
|
* @return {never}
|
|
59
59
|
*/
|
|
60
60
|
export function throwInvalidArtifactDependency(artifactId: string, dependencyKey: string): never;
|
|
61
|
+
import * as LH from '../../types/lh.js';
|
|
61
62
|
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -102,6 +102,29 @@ export class ExecutionContext {
|
|
|
102
102
|
args: T;
|
|
103
103
|
deps?: Array<Function | string>;
|
|
104
104
|
}): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Call a function on the given object.
|
|
107
|
+
* Returns a promise that resolves on a value of `mainFn`'s return type.
|
|
108
|
+
* @template {unknown[]} T, R
|
|
109
|
+
* @param {((thisArg: any, ...args: T) => R)} mainFn The main function to call.
|
|
110
|
+
* @param {{args: T, objectId: string, deps?: Array<Function|string>}} options `args` should
|
|
111
|
+
* match the args of `mainFn`, and can be any serializable value. `deps` are functions that must be
|
|
112
|
+
* defined for `mainFn` to work.
|
|
113
|
+
* @return {Promise<Awaited<R>>}
|
|
114
|
+
*/
|
|
115
|
+
evaluateOnObject<T extends unknown[], R>(mainFn: ((thisArg: any, ...args: T) => R), options: {
|
|
116
|
+
args: T;
|
|
117
|
+
objectId: string;
|
|
118
|
+
deps?: Array<Function | string>;
|
|
119
|
+
}): Promise<Awaited<R>>;
|
|
120
|
+
/**
|
|
121
|
+
* @param {string} functionDeclaration
|
|
122
|
+
* @param {{objectId: string}} options
|
|
123
|
+
* @return {Promise<*>}
|
|
124
|
+
*/
|
|
125
|
+
_callFunctionOn(functionDeclaration: string, options: {
|
|
126
|
+
objectId: string;
|
|
127
|
+
}): Promise<any>;
|
|
105
128
|
/**
|
|
106
129
|
* Cache native functions/objects inside window so we are sure polyfills do not overwrite the
|
|
107
130
|
* native implementations when the page loads.
|
|
@@ -223,6 +223,7 @@ class ExecutionContext {
|
|
|
223
223
|
|
|
224
224
|
const expression = `(() => {
|
|
225
225
|
${ExecutionContext._cachedNativesPreamble};
|
|
226
|
+
${pageFunctions.esbuildFunctionWrapperString}
|
|
226
227
|
${depsSerialized};
|
|
227
228
|
(${mainFn})(${argsSerialized});
|
|
228
229
|
})()
|
|
@@ -231,6 +232,87 @@ class ExecutionContext {
|
|
|
231
232
|
await this._session.sendCommand('Page.addScriptToEvaluateOnNewDocument', {source: expression});
|
|
232
233
|
}
|
|
233
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Call a function on the given object.
|
|
237
|
+
* Returns a promise that resolves on a value of `mainFn`'s return type.
|
|
238
|
+
* @template {unknown[]} T, R
|
|
239
|
+
* @param {((thisArg: any, ...args: T) => R)} mainFn The main function to call.
|
|
240
|
+
* @param {{args: T, objectId: string, deps?: Array<Function|string>}} options `args` should
|
|
241
|
+
* match the args of `mainFn`, and can be any serializable value. `deps` are functions that must be
|
|
242
|
+
* defined for `mainFn` to work.
|
|
243
|
+
* @return {Promise<Awaited<R>>}
|
|
244
|
+
*/
|
|
245
|
+
evaluateOnObject(mainFn, options) {
|
|
246
|
+
const argsSerialized = ExecutionContext.serializeArguments(options.args);
|
|
247
|
+
const depsSerialized = ExecutionContext.serializeDeps(options.deps);
|
|
248
|
+
|
|
249
|
+
const argsString = argsSerialized ? `this, ${argsSerialized}` : 'this';
|
|
250
|
+
const functionDeclaration = `function() {
|
|
251
|
+
${depsSerialized}
|
|
252
|
+
return (${mainFn})(${argsString});
|
|
253
|
+
}`;
|
|
254
|
+
return this._callFunctionOn(functionDeclaration, options);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @param {string} functionDeclaration
|
|
259
|
+
* @param {{objectId: string}} options
|
|
260
|
+
* @return {Promise<*>}
|
|
261
|
+
*/
|
|
262
|
+
async _callFunctionOn(functionDeclaration, options) {
|
|
263
|
+
const timeout = this._session.hasNextProtocolTimeout() ?
|
|
264
|
+
this._session.getNextProtocolTimeout() :
|
|
265
|
+
60000;
|
|
266
|
+
|
|
267
|
+
const evaluationParams = {
|
|
268
|
+
functionDeclaration: `function wrapInNativePromise() {
|
|
269
|
+
${ExecutionContext._cachedNativesPreamble};
|
|
270
|
+
${pageFunctions.esbuildFunctionWrapperString}
|
|
271
|
+
const self = this;
|
|
272
|
+
const args = arguments;
|
|
273
|
+
return new Promise(function (resolve) {
|
|
274
|
+
return Promise.resolve()
|
|
275
|
+
.then(_ => (${functionDeclaration}).apply(self, args))
|
|
276
|
+
.catch(${pageFunctions.wrapRuntimeEvalErrorInBrowser})
|
|
277
|
+
.then(resolve);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
//# sourceURL=_lighthouse-eval.js
|
|
281
|
+
`,
|
|
282
|
+
objectId: options.objectId,
|
|
283
|
+
returnByValue: true,
|
|
284
|
+
awaitPromise: true,
|
|
285
|
+
timeout,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
this._session.setNextProtocolTimeout(timeout);
|
|
289
|
+
const response = await this._session.sendCommand('Runtime.callFunctionOn', evaluationParams);
|
|
290
|
+
|
|
291
|
+
const ex = response.exceptionDetails;
|
|
292
|
+
if (ex) {
|
|
293
|
+
const elidedExpression = functionDeclaration.replace(/\s+/g, ' ').substring(0, 100);
|
|
294
|
+
const messageLines = [
|
|
295
|
+
'Runtime.callFunctionOn exception',
|
|
296
|
+
`Expression: ${elidedExpression}\n---- (elided)`,
|
|
297
|
+
!ex.stackTrace ? `Parse error at: ${ex.lineNumber + 1}:${ex.columnNumber + 1}` : null,
|
|
298
|
+
ex.exception?.description || ex.text,
|
|
299
|
+
].filter(Boolean);
|
|
300
|
+
const evaluationError = new Error(messageLines.join('\n'));
|
|
301
|
+
return Promise.reject(evaluationError);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (response.result === undefined) {
|
|
305
|
+
return Promise.reject(
|
|
306
|
+
new Error('Runtime.callFunctionOn response did not contain a "result" object'));
|
|
307
|
+
}
|
|
308
|
+
const value = response.result.value;
|
|
309
|
+
if (value?.__failedInBrowser) {
|
|
310
|
+
return Promise.reject(Object.assign(new Error(), value));
|
|
311
|
+
} else {
|
|
312
|
+
return value;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
234
316
|
/**
|
|
235
317
|
* Cache native functions/objects inside window so we are sure polyfills do not overwrite the
|
|
236
318
|
* native implementations when the page loads.
|
|
@@ -279,7 +361,10 @@ class ExecutionContext {
|
|
|
279
361
|
* @return {string}
|
|
280
362
|
*/
|
|
281
363
|
static serializeDeps(deps) {
|
|
282
|
-
|
|
364
|
+
if (!deps) {
|
|
365
|
+
return '';
|
|
366
|
+
}
|
|
367
|
+
|
|
283
368
|
return deps.map(dep => {
|
|
284
369
|
if (typeof dep === 'function') {
|
|
285
370
|
// esbuild will change the actual function name (ie. function actualName() {})
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import log from 'lighthouse-logger';
|
|
8
8
|
|
|
9
9
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
10
|
+
import {Sentry} from '../../lib/sentry.js';
|
|
10
11
|
|
|
11
12
|
/* eslint-disable max-len */
|
|
12
13
|
const UIStrings = {
|
|
@@ -81,6 +82,18 @@ async function getImportantStorageWarning(session, url) {
|
|
|
81
82
|
const usageData = await session.sendCommand('Storage.getUsageAndQuota', {
|
|
82
83
|
origin: url,
|
|
83
84
|
});
|
|
85
|
+
|
|
86
|
+
// According to the types, this should never happen. But we've gotten an error
|
|
87
|
+
// report that it does.
|
|
88
|
+
// https://github.com/GoogleChrome/lighthouse/issues/17011
|
|
89
|
+
if (!usageData || !usageData.usageBreakdown) {
|
|
90
|
+
const err = new Error(`missing usageData: ${JSON.stringify(usageData)}`);
|
|
91
|
+
Sentry.captureException(err, {
|
|
92
|
+
level: 'error',
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
84
97
|
/** @type {Record<string, string>} */
|
|
85
98
|
const storageTypeNames = {
|
|
86
99
|
local_storage: 'Local Storage',
|
package/core/gather/driver.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export class Driver implements LH.Gatherer.Driver {
|
|
|
13
13
|
_executionContext: ExecutionContext | undefined;
|
|
14
14
|
/** @type {Fetcher|undefined} */
|
|
15
15
|
_fetcher: Fetcher | undefined;
|
|
16
|
-
defaultSession: import("../../types
|
|
16
|
+
defaultSession: import("../../types.js").Gatherer.ProtocolSession;
|
|
17
17
|
/** @return {LH.Gatherer.Driver['executionContext']} */
|
|
18
18
|
get executionContext(): LH.Gatherer.Driver["executionContext"];
|
|
19
19
|
get fetcher(): any;
|
|
@@ -67,9 +67,12 @@ declare class TraceElements extends BaseGatherer {
|
|
|
67
67
|
stopInstrumentation(context: LH.Gatherer.Context): Promise<void>;
|
|
68
68
|
/**
|
|
69
69
|
* @param {LH.Gatherer.ProtocolSession} session
|
|
70
|
+
* @param {LH.Gatherer.Driver['executionContext']} executionContext
|
|
70
71
|
* @param {number} backendNodeId
|
|
71
72
|
*/
|
|
72
|
-
getNodeDetails(session: LH.Gatherer.ProtocolSession, backendNodeId: number): Promise<
|
|
73
|
+
getNodeDetails(session: LH.Gatherer.ProtocolSession, executionContext: LH.Gatherer.Driver["executionContext"], backendNodeId: number): Promise<{
|
|
74
|
+
node: any;
|
|
75
|
+
} | null | undefined>;
|
|
73
76
|
/**
|
|
74
77
|
* @param {LH.Gatherer.Context<'Trace'|'SourceMaps'>} context
|
|
75
78
|
* @return {Promise<LH.Artifacts.TraceElement[]>}
|
|
@@ -20,7 +20,6 @@ import Trace from './trace.js';
|
|
|
20
20
|
import {ProcessedTrace} from '../../computed/processed-trace.js';
|
|
21
21
|
import {Responsiveness} from '../../computed/metrics/responsiveness.js';
|
|
22
22
|
import {CumulativeLayoutShift} from '../../computed/metrics/cumulative-layout-shift.js';
|
|
23
|
-
import {ExecutionContext} from '../driver/execution-context.js';
|
|
24
23
|
import {TraceEngineResult} from '../../computed/trace-engine-result.js';
|
|
25
24
|
import SourceMaps from './source-maps.js';
|
|
26
25
|
|
|
@@ -29,14 +28,14 @@ import SourceMaps from './source-maps.js';
|
|
|
29
28
|
const MAX_LAYOUT_SHIFTS = 15;
|
|
30
29
|
|
|
31
30
|
/**
|
|
32
|
-
* @
|
|
31
|
+
* @param {Element | ShadowRoot | Text} node
|
|
33
32
|
*/
|
|
34
33
|
/* c8 ignore start */
|
|
35
|
-
function getNodeDetailsData() {
|
|
36
|
-
/** @type {Element|null} */
|
|
37
|
-
let elem =
|
|
38
|
-
if (!elem &&
|
|
39
|
-
elem =
|
|
34
|
+
function getNodeDetailsData(node) {
|
|
35
|
+
/** @type {Element | ShadowRoot | Text | null} */
|
|
36
|
+
let elem = node.nodeType === document.ELEMENT_NODE ? node : node.parentElement;
|
|
37
|
+
if (!elem && node instanceof ShadowRoot) {
|
|
38
|
+
elem = node.host;
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
let traceElement;
|
|
@@ -292,26 +291,22 @@ class TraceElements extends BaseGatherer {
|
|
|
292
291
|
|
|
293
292
|
/**
|
|
294
293
|
* @param {LH.Gatherer.ProtocolSession} session
|
|
294
|
+
* @param {LH.Gatherer.Driver['executionContext']} executionContext
|
|
295
295
|
* @param {number} backendNodeId
|
|
296
296
|
*/
|
|
297
|
-
async getNodeDetails(session, backendNodeId) {
|
|
297
|
+
async getNodeDetails(session, executionContext, backendNodeId) {
|
|
298
298
|
try {
|
|
299
299
|
const objectId = await resolveNodeIdToObjectId(session, backendNodeId);
|
|
300
300
|
if (!objectId) return null;
|
|
301
301
|
|
|
302
|
-
|
|
303
|
-
pageFunctions.getNodeDetails,
|
|
302
|
+
return await executionContext.evaluateOnObject(
|
|
304
303
|
getNodeDetailsData,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}`,
|
|
312
|
-
returnByValue: true,
|
|
313
|
-
awaitPromise: true,
|
|
314
|
-
});
|
|
304
|
+
{
|
|
305
|
+
objectId,
|
|
306
|
+
args: [],
|
|
307
|
+
deps: [pageFunctions.getNodeDetails],
|
|
308
|
+
}
|
|
309
|
+
);
|
|
315
310
|
} catch (err) {
|
|
316
311
|
Sentry.captureException(err, {
|
|
317
312
|
tags: {gatherer: 'TraceElements'},
|
|
@@ -346,29 +341,30 @@ class TraceElements extends BaseGatherer {
|
|
|
346
341
|
trace, traceEngineResult, context);
|
|
347
342
|
const animatedElementData = await this.getAnimatedElements(mainThreadEvents);
|
|
348
343
|
|
|
349
|
-
/** @type {Map<
|
|
344
|
+
/** @type {Map<LH.Artifacts.TraceElement['traceEventType'], TraceElementData[]>} */
|
|
350
345
|
const backendNodeDataMap = new Map([
|
|
351
346
|
['trace-engine', traceEngineData],
|
|
352
347
|
['layout-shift', shiftsData],
|
|
353
348
|
['animation', animatedElementData],
|
|
354
349
|
]);
|
|
355
350
|
|
|
356
|
-
/** @type {Map<number, LH.
|
|
357
|
-
const
|
|
351
|
+
/** @type {Map<number, {node: LH.Artifacts.NodeDetails} | null>} */
|
|
352
|
+
const evaluateOnObjectCache = new Map();
|
|
358
353
|
/** @type {LH.Artifacts.TraceElement[]} */
|
|
359
354
|
const traceElements = [];
|
|
360
355
|
for (const [traceEventType, backendNodeData] of backendNodeDataMap) {
|
|
361
356
|
for (let i = 0; i < backendNodeData.length; i++) {
|
|
362
357
|
const backendNodeId = backendNodeData[i].nodeId;
|
|
363
|
-
let response =
|
|
358
|
+
let response = evaluateOnObjectCache.get(backendNodeId);
|
|
364
359
|
if (response === undefined) {
|
|
365
|
-
response = await this.getNodeDetails(
|
|
366
|
-
|
|
360
|
+
response = await this.getNodeDetails(
|
|
361
|
+
session, context.driver.executionContext, backendNodeId) || null;
|
|
362
|
+
evaluateOnObjectCache.set(backendNodeId, response);
|
|
367
363
|
}
|
|
368
364
|
|
|
369
|
-
if (response?.
|
|
365
|
+
if (response?.node) {
|
|
370
366
|
traceElements.push({
|
|
371
|
-
...response
|
|
367
|
+
...response,
|
|
372
368
|
traceEventType,
|
|
373
369
|
animations: backendNodeData[i].animations,
|
|
374
370
|
nodeId: backendNodeId,
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import BaseGatherer from '../base-gatherer.js';
|
|
8
8
|
import {resolveNodeIdToObjectId} from '../driver/dom.js';
|
|
9
9
|
import {pageFunctions} from '../../lib/page-functions.js';
|
|
10
|
-
import {ExecutionContext} from '../driver/execution-context.js';
|
|
11
10
|
|
|
12
11
|
class WebMcpSchemaIssues extends BaseGatherer {
|
|
13
12
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
@@ -68,27 +67,21 @@ class WebMcpSchemaIssues extends BaseGatherer {
|
|
|
68
67
|
async getArtifact(context) {
|
|
69
68
|
const session = context.driver.defaultSession;
|
|
70
69
|
|
|
71
|
-
const deps = ExecutionContext.serializeDeps([
|
|
72
|
-
pageFunctions.getNodeDetails,
|
|
73
|
-
]);
|
|
74
|
-
|
|
75
70
|
const promises = this._issues.map(async (issue) => {
|
|
76
71
|
const processedIssue = {...issue};
|
|
77
72
|
if (issue.violatingNodeId) {
|
|
78
73
|
try {
|
|
79
74
|
const objectId = await resolveNodeIdToObjectId(session, issue.violatingNodeId);
|
|
80
75
|
if (objectId) {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (response && response.result && response.result.value) {
|
|
91
|
-
processedIssue.nodeDetails = response.result.value;
|
|
76
|
+
const nodeDetails = await context.driver.executionContext.evaluateOnObject(
|
|
77
|
+
pageFunctions.getNodeDetails,
|
|
78
|
+
{
|
|
79
|
+
objectId,
|
|
80
|
+
args: [],
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
if (nodeDetails) {
|
|
84
|
+
processedIssue.nodeDetails = nodeDetails;
|
|
92
85
|
}
|
|
93
86
|
}
|
|
94
87
|
} catch (err) {
|
|
@@ -48,6 +48,11 @@ declare class WebMCP extends BaseGatherer {
|
|
|
48
48
|
* @param {LH.Gatherer.Context} passContext
|
|
49
49
|
*/
|
|
50
50
|
stopInstrumentation(passContext: LH.Gatherer.Context): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* @param {LH.Gatherer.Context} context
|
|
53
|
+
* @param {WebMCPTool} tool
|
|
54
|
+
*/
|
|
55
|
+
_tryResolveToolNodeDetails(context: LH.Gatherer.Context, tool: WebMCPTool): Promise<void>;
|
|
51
56
|
/**
|
|
52
57
|
* @param {LH.Gatherer.Context} context
|
|
53
58
|
* @return {Promise<LH.Artifacts['WebMCP']>}
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import BaseGatherer from '../base-gatherer.js';
|
|
12
12
|
import {resolveNodeIdToObjectId} from '../driver/dom.js';
|
|
13
13
|
import {pageFunctions} from '../../lib/page-functions.js';
|
|
14
|
-
import {ExecutionContext} from '../driver/execution-context.js';
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* @typedef {Object} WebMCPTool
|
|
@@ -99,6 +98,37 @@ class WebMCP extends BaseGatherer {
|
|
|
99
98
|
}
|
|
100
99
|
}
|
|
101
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @param {LH.Gatherer.Context} context
|
|
103
|
+
* @param {WebMCPTool} tool
|
|
104
|
+
*/
|
|
105
|
+
async _tryResolveToolNodeDetails(context, tool) {
|
|
106
|
+
if (!tool.backendNodeId) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const session = context.driver.defaultSession;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const objectId = await resolveNodeIdToObjectId(session, tool.backendNodeId);
|
|
114
|
+
if (!objectId) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const nodeDetails = await context.driver.executionContext.evaluateOnObject(
|
|
119
|
+
pageFunctions.getNodeDetails, {
|
|
120
|
+
objectId,
|
|
121
|
+
args: [],
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
if (nodeDetails) {
|
|
125
|
+
tool.nodeDetails = nodeDetails;
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
// Ignore error
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
102
132
|
/**
|
|
103
133
|
* @param {LH.Gatherer.Context} context
|
|
104
134
|
* @return {Promise<LH.Artifacts['WebMCP']>}
|
|
@@ -113,9 +143,8 @@ class WebMCP extends BaseGatherer {
|
|
|
113
143
|
return {isSupported: false, tools: []};
|
|
114
144
|
}
|
|
115
145
|
|
|
116
|
-
const session = context.driver.defaultSession;
|
|
117
|
-
|
|
118
146
|
// Remove duplicates based on name, keeping the latest occurrence.
|
|
147
|
+
/** @type {Map<string, WebMCPTool>} */
|
|
119
148
|
const toolMap = new Map();
|
|
120
149
|
for (const tool of this._tools) {
|
|
121
150
|
toolMap.set(tool.name, tool);
|
|
@@ -123,32 +152,10 @@ class WebMCP extends BaseGatherer {
|
|
|
123
152
|
|
|
124
153
|
const resolvedTools = [];
|
|
125
154
|
for (const tool of toolMap.values()) {
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
const objectId = await resolveNodeIdToObjectId(session, tool.backendNodeId);
|
|
129
|
-
if (objectId) {
|
|
130
|
-
const deps = ExecutionContext.serializeDeps([
|
|
131
|
-
pageFunctions.getNodeDetails,
|
|
132
|
-
]);
|
|
133
|
-
const response = await session.sendCommand('Runtime.callFunctionOn', {
|
|
134
|
-
objectId,
|
|
135
|
-
functionDeclaration: `function () {
|
|
136
|
-
${deps}
|
|
137
|
-
return getNodeDetails(this);
|
|
138
|
-
}`,
|
|
139
|
-
returnByValue: true,
|
|
140
|
-
awaitPromise: true,
|
|
141
|
-
});
|
|
142
|
-
if (response && response.result && response.result.value) {
|
|
143
|
-
tool.nodeDetails = response.result.value;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
} catch (err) {
|
|
147
|
-
// Ignore error
|
|
148
|
-
}
|
|
149
|
-
}
|
|
155
|
+
await this._tryResolveToolNodeDetails(context, tool);
|
|
150
156
|
resolvedTools.push(tool);
|
|
151
157
|
}
|
|
158
|
+
|
|
152
159
|
return {
|
|
153
160
|
isSupported: true,
|
|
154
161
|
tools: resolvedTools,
|