Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v9.6.1 #13990

Merged
merged 6 commits into from
May 11, 2022
Merged

v9.6.1 #13990

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
<a name="9.6.1"></a>
# 9.6.1 (2022-05-11)
[Full Changelog](https://github.com/compare/v9.6.0...v9.6.1)

We expect this release to ship in the DevTools of [Chrome 103](https://chromiumdash.appspot.com/schedule), and to PageSpeed Insights within 2 weeks.

## Core

* fps: run at the end of timespan/snapshot ([#13989](https://github.com/GoogleChrome/lighthouse/pull/13989))
* responsiveness: add element screenshot to INP diagnostic ([#13984](https://github.com/GoogleChrome/lighthouse/pull/13984))
* responsiveness: add better INP fallback for old Chrome versions ([#13985](https://github.com/GoogleChrome/lighthouse/pull/13985))

## Report

* devtools: use absolute positioning for overlay ([#13988](https://github.com/GoogleChrome/lighthouse/pull/13988))

## Tests

* use origin-agent-cluster to actually test oopifs ([#13777](https://github.com/GoogleChrome/lighthouse/pull/13777))

<a name="9.6.0"></a>
# 9.6.0 (2022-05-09)
[Full Changelog](https://github.com/compare/v9.5.0...v9.6.0)
Expand Down
4 changes: 2 additions & 2 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ A Lighthouse plugin is just a node module with a name that starts with `lighthou
"name": "lighthouse-plugin-cats",
"main": "plugin.js",
"peerDependencies": {
"lighthouse": "^9.6.0"
"lighthouse": "^9.6.1"
},
"devDependencies": {
"lighthouse": "^9.6.0"
"lighthouse": "^9.6.1"
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/custom-audit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"private": true,
"scripts": {},
"devDependencies": {
"lighthouse": "^9.6.0"
"lighthouse": "^9.6.1"
}
}
2 changes: 1 addition & 1 deletion docs/recipes/gulp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"devDependencies": {
"gulp": "^3.9.1",
"gulp-connect": "^5.0.0",
"lighthouse": "^9.6.0"
"lighthouse": "^9.6.1"
}
}
2 changes: 1 addition & 1 deletion docs/recipes/lighthouse-plugin-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"main": "./plugin.js",
"peerDependencies": {
"lighthouse": "^9.6.0"
"lighthouse": "^9.6.1"
},
"devDependencies": {
"lighthouse": "^8.6.0"
Expand Down
5 changes: 4 additions & 1 deletion lighthouse-cli/test/fixtures/static-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ class Server {
// Used by Smokerider.
if (this._dataTransformer) data = this._dataTransformer(data);

const headers = {'Access-Control-Allow-Origin': '*'};
const headers = {
'Access-Control-Allow-Origin': '*',
'Origin-Agent-Cluster': '?1',
};

const contentType = mime.lookup(filePath);
const charset = mime.lookup(contentType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const expectations = {
isPositionFixed: true,
},
],
// Only `oopif-simple-page.html`'s inclusion of `simple-script.js` should show up here.
// Only `:10200/oopif-simple-page.html`'s inclusion of `simple-script.js` shows here.
// All other scripts are filtered out because of our "OOPIF" filter (including anything
// that is just in another process, like a worker).
ScriptElements: [
Expand All @@ -107,11 +107,6 @@ const expectations = {
source: 'network',
content: /🪁/,
},
{
src: 'http://localhost:10503/simple-script.js',
source: 'network',
content: /🪁/,
},
],
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const expectations = {
audits: {
'resource-summary': {
score: null,
displayValue: '10 requests • 164 KiB',
displayValue: '10 requests • 165 KiB',
details: {
items: [
{resourceType: 'total', requestCount: 10, transferSize: '168000±1000'},
Expand All @@ -86,7 +86,7 @@ const expectations = {
{
resourceType: 'total',
countOverBudget: '2 requests',
sizeOverBudget: '65000±1000',
sizeOverBudget: '66000±1000',
},
{
resourceType: 'script',
Expand All @@ -101,7 +101,7 @@ const expectations = {
{
resourceType: 'document',
countOverBudget: '1 request',
sizeOverBudget: '1200±50',
sizeOverBudget: '1250±50',
},
{
resourceType: 'stylesheet',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ class ExperimentalInteractionToNextPaint extends Audit {
return {score: null, notApplicable: true};
}

const timing = interactionEvent.args.data.duration;
// TODO: remove workaround once 103.0.5052.0 is sufficiently released.
const timing = interactionEvent.name === 'FallbackTiming' ?
interactionEvent.duration : interactionEvent.args.data.duration;

return {
score: Audit.computeLogNormalScore({p10: context.options.p10, median: context.options.median},
Expand Down
58 changes: 46 additions & 12 deletions lighthouse-core/audits/work-during-interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {taskGroups} = require('../lib/tracehouse/task-groups.js');
const TraceProcessor = require('../lib/tracehouse/trace-processor.js');
const {getExecutionTimingsByURL} = require('../lib/tracehouse/task-summary.js');
const inpThresholds = require('./metrics/experimental-interaction-to-next-paint.js').defaultOptions;
const LHError = require('../lib/lh-error.js');

/** @typedef {import('../computed/metrics/responsiveness.js').EventTimingEvent} EventTimingEvent */
/** @typedef {import('../lib/tracehouse/main-thread-tasks.js').TaskNode} TaskNode */
Expand All @@ -39,6 +40,8 @@ const UIStrings = {
* @example {mousedown} interactionType
*/
displayValue: `{timeInMs, number, milliseconds}\xa0ms spent on event '{interactionType}'`,
/** Label for a column in a data table; entries will the UI element that was the target of a user interaction (for example, a button that was clicked on). Ideally fits within a ~40 character limit. */
eventTarget: 'Event target',
};

const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
Expand All @@ -58,7 +61,7 @@ class WorkDuringInteraction extends Audit {
description: str_(UIStrings.description),
scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
supportedModes: ['timespan'],
requiredArtifacts: ['traces', 'devtoolsLogs'],
requiredArtifacts: ['traces', 'devtoolsLogs', 'TraceElements'],
};
}

Expand Down Expand Up @@ -125,7 +128,7 @@ class WorkDuringInteraction extends Audit {
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
* @return {{table: LH.Audit.Details.Table, phases: Record<string, {startTs: number, endTs: number}>}}
*/
static eventThreadBreakdown(interactionEvent, trace, processedTrace, networkRecords) {
static getThreadBreakdownTable(interactionEvent, trace, processedTrace, networkRecords) {
// Limit to interactionEvent's thread.
// TODO(bckenny): limit to interactionEvent's navigation.
const threadEvents = TraceProcessor.filteredTraceSort(trace.traceEvents, evt => {
Expand Down Expand Up @@ -196,6 +199,23 @@ class WorkDuringInteraction extends Audit {
};
}

/**
* @param {LH.Artifacts['TraceElements']} traceElements
* @return {LH.Audit.Details.Table | undefined}
*/
static getTraceElementTable(traceElements) {
const responsivenessElement = traceElements.find(el => el.traceEventType === 'responsiveness');
if (!responsivenessElement) return;

/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
{key: 'node', itemType: 'node', text: str_(UIStrings.eventTarget)},
];
const elementItems = [{node: Audit.makeNodeItem(responsivenessElement.node)}];

return Audit.makeTableDetails(headings, elementItems);
}

/**
* @param {LH.Artifacts} artifacts
* @param {LH.Audit.Context} context
Expand All @@ -215,28 +235,42 @@ class WorkDuringInteraction extends Audit {
if (interactionEvent === null) {
return {score: null, notApplicable: true};
}
// TODO: remove workaround once 103.0.5052.0 is sufficiently released.
if (interactionEvent.name === 'FallbackTiming') {
throw new LHError(
LHError.errors.UNSUPPORTED_OLD_CHROME,
{featureName: 'detailed EventTiming trace events'}
);
}

const auditDetailsItems = [];

const traceElementItem = WorkDuringInteraction.getTraceElementTable(artifacts.TraceElements);
if (traceElementItem) auditDetailsItems.push(traceElementItem);

const devtoolsLog = artifacts.devtoolsLogs[WorkDuringInteraction.DEFAULT_PASS];
// Network records will usually be empty for timespans.
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
const processedTrace = await ProcessedTrace.request(trace, context);
const {table, phases} = WorkDuringInteraction.eventThreadBreakdown(
interactionEvent, trace, processedTrace, networkRecords);
const {table: breakdownTable, phases} = WorkDuringInteraction.getThreadBreakdownTable(
interactionEvent, trace, processedTrace, networkRecords);
auditDetailsItems.push(breakdownTable);

const duration = interactionEvent.args.data.duration;
const interactionType = interactionEvent.args.data.type;
const displayValue = str_(UIStrings.displayValue, {timeInMs: duration, interactionType});
auditDetailsItems.push({
type: /** @type {const} */ ('debugdata'),
interactionType,
phases,
});

const duration = interactionEvent.args.data.duration;
const displayValue = str_(UIStrings.displayValue, {timeInMs: duration, interactionType});
return {
score: duration < inpThresholds.p10 ? 1 : 0,
displayValue,
details: {
...table,
debugData: {
type: 'debugdata',
interactionType,
phases,
},
type: 'list',
items: auditDetailsItems,
},
};
}
Expand Down
21 changes: 12 additions & 9 deletions lighthouse-core/computed/metrics/responsiveness.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@
* @property {number} interactionId
*/
/** @typedef {LH.Trace.AsyncEvent & {name: 'EventTiming', args: {data: EventTimingData}}} EventTimingEvent */

/**
* A fallback EventTiming placeholder, used if updated EventTiming events are not available.
* TODO: Remove once 103.0.5052.0 is sufficiently released.
* @typedef {{name: 'FallbackTiming', duration: number}} FallbackTimingEvent
*/

const makeComputedArtifact = require('../computed-artifact.js');
const ProcessedTrace = require('../processed-trace.js');
const LHError = require('../../lib/lh-error.js');

const KEYBOARD_EVENTS = new Set(['keydown', 'keypress', 'keyup']);
const CLICK_TAP_DRAG_EVENTS = new Set([
Expand Down Expand Up @@ -77,20 +80,20 @@ class Responsiveness {
* one interaction had this duration by returning the first found.
* @param {ResponsivenessEvent} responsivenessEvent
* @param {LH.Trace} trace
* @return {EventTimingEvent}
* @return {EventTimingEvent|FallbackTimingEvent}
*/
static findInteractionEvent(responsivenessEvent, {traceEvents}) {
const candidates = traceEvents.filter(/** @return {evt is EventTimingEvent} */ evt => {
// Examine only beginning/instant EventTiming events.
return evt.name === 'EventTiming' && evt.ph !== 'e';
});

if (candidates.length && !candidates[0].args.data.frame) {
if (candidates.length && !candidates.some(candidate => candidate.args.data?.frame)) {
// Full EventTiming data added in https://crrev.com/c/3632661
throw new LHError(
LHError.errors.UNSUPPORTED_OLD_CHROME,
{featureName: 'detailed EventTiming trace events'}
);
return {
name: 'FallbackTiming',
duration: responsivenessEvent.args.data.maxDuration,
};
}

const {maxDuration, interactionType} = responsivenessEvent.args.data;
Expand Down Expand Up @@ -131,7 +134,7 @@ class Responsiveness {
/**
* @param {{trace: LH.Trace, settings: Immutable<LH.Config.Settings>}} data
* @param {LH.Artifacts.ComputedContext} context
* @return {Promise<EventTimingEvent|null>}
* @return {Promise<EventTimingEvent|FallbackTimingEvent|null>}
*/
static async compute_(data, context) {
const {settings, trace} = data;
Expand Down
4 changes: 3 additions & 1 deletion lighthouse-core/fraggle-rock/config/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ const defaultConfig = {
{id: artifacts.EmbeddedContent, gatherer: 'seo/embedded-content'},
{id: artifacts.FontSize, gatherer: 'seo/font-size'},
{id: artifacts.Inputs, gatherer: 'inputs'},
{id: artifacts.FullPageScreenshot, gatherer: 'full-page-screenshot'},
{id: artifacts.GlobalListeners, gatherer: 'global-listeners'},
{id: artifacts.IFrameElements, gatherer: 'iframe-elements'},
{id: artifacts.ImageElements, gatherer: 'image-elements'},
Expand Down Expand Up @@ -132,6 +131,9 @@ const defaultConfig = {
// Artifact copies are renamed for compatibility with legacy artifacts.
{id: artifacts.devtoolsLogs, gatherer: 'devtools-log-compat'},
{id: artifacts.traces, gatherer: 'trace-compat'},

// FullPageScreenshot comes at the very end so all other node analysis is captured.
{id: artifacts.FullPageScreenshot, gatherer: 'full-page-screenshot'},
],
navigations: [
{
Expand Down
23 changes: 21 additions & 2 deletions lighthouse-core/gather/gatherers/trace-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const Trace = require('./trace.js');
const ProcessedTrace = require('../../computed/processed-trace.js');
const ProcessedNavigation = require('../../computed/processed-navigation.js');
const LighthouseError = require('../../lib/lh-error.js');
const ComputedResponsivenes = require('../../computed/metrics/responsiveness.js');

/** @typedef {{nodeId: number, score?: number, animations?: {name?: string, failureReasonsMask?: number, unsupportedProperties?: string[]}[]}} TraceElementData */

Expand Down Expand Up @@ -142,6 +143,23 @@ class TraceElements extends FRGatherer {
return topFive;
}

/**
* @param {LH.Trace} trace
* @param {LH.Gatherer.FRTransitionalContext} context
* @return {Promise<TraceElementData|undefined>}
*/
static async getResponsivenessElement(trace, context) {
const {settings} = context;
try {
const responsivenessEvent = await ComputedResponsivenes.request({trace, settings}, context);
if (!responsivenessEvent || responsivenessEvent.name === 'FallbackTiming') return;
return {nodeId: responsivenessEvent.args.data.nodeId};
} catch {
// Don't let responsiveness errors sink the rest of the gatherer.
return;
}
}

/**
* Find the node ids of elements which are animated using the Animation trace events.
* @param {Array<LH.TraceEvent>} mainThreadEvents
Expand Down Expand Up @@ -237,14 +255,15 @@ class TraceElements extends FRGatherer {

const lcpNodeId = largestContentfulPaintEvt?.args?.data?.nodeId;
const clsNodeData = TraceElements.getTopLayoutShiftElements(mainThreadEvents);
const animatedElementData =
await this.getAnimatedElements(mainThreadEvents);
const animatedElementData = await this.getAnimatedElements(mainThreadEvents);
const responsivenessElementData = await TraceElements.getResponsivenessElement(trace, context);

/** @type {Map<string, TraceElementData[]>} */
const backendNodeDataMap = new Map([
['largest-contentful-paint', lcpNodeId ? [{nodeId: lcpNodeId}] : []],
['layout-shift', clsNodeData],
['animation', animatedElementData],
['responsiveness', responsivenessElementData ? [responsivenessElementData] : []],
]);

const traceElements = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ describe('Interaction to Next Paint', () => {
});
});

it('falls back Responsiveness timing if no m103 EventTiming events', async () => {
const {artifacts, context} = getTestData();
const clonedTrace = JSON.parse(JSON.stringify(artifacts.traces.defaultPass));
for (let i = 0; i < clonedTrace.traceEvents.length; i++) {
if (clonedTrace.traceEvents[i].name !== 'EventTiming') continue;
clonedTrace.traceEvents[i].args = {};
}
artifacts.traces.defaultPass = clonedTrace;

const result = await ExperimentalInteractionToNextPaint.audit(artifacts, context);
// Conveniently, the matching responsiveness event has slightly different
// duration than the matching interaction event so can be tested against.
expect(result).toEqual({
score: 0.67,
numericValue: 364,
numericUnit: 'millisecond',
displayValue: expect.toBeDisplayString('360 ms'),
});
});

it('is not applicable if using simulated throttling', async () => {
const {artifacts, context} = getTestData();
context.settings.throttlingMethod = 'simulate';
Expand Down
Loading