From 940fe589e9353f7bae11a960d22f46acfa5b7111 Mon Sep 17 00:00:00 2001 From: Peter Hedenskog Date: Sat, 15 Jul 2023 11:39:33 +0200 Subject: [PATCH] Get elements that needs recalculate styles before FCP/LCP. (#1964) * Get elements that needs recalculate styles before FCP/LCP. First go at collecting number of elements that needs recalculate styles before FCP/LCP. I think this should work fine, the only thing that needs tweaking is the data structure. * Add the data in renderBlocking instead --- lib/chrome/webdriver/chromium.js | 28 ++++++++++- lib/chrome/webdriver/traceUtilities.js | 64 ++++++++++++++++++++++++++ lib/core/engine/collector.js | 6 +++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 lib/chrome/webdriver/traceUtilities.js diff --git a/lib/chrome/webdriver/chromium.js b/lib/chrome/webdriver/chromium.js index b9e5e15c7..74ac019c3 100644 --- a/lib/chrome/webdriver/chromium.js +++ b/lib/chrome/webdriver/chromium.js @@ -15,6 +15,11 @@ import { parse } from '../traceCategoriesParser.js'; import { pathToFolder } from '../../support/pathToFolder.js'; import { ChromeDevtoolsProtocol } from '../chromeDevtoolsProtocol.js'; import { Android, isAndroidConfigured } from '../../android/index.js'; +import { + getFirstContentFulPaintEvent, + getLargestContentfulPaintEvent, + getRecalculateStyleElementsAndTimeBefore +} from './traceUtilities.js'; const unlink = promisify(_unlink); const rm = promisify(_rm); @@ -353,6 +358,27 @@ export class Chromium { asset.args.data.renderBlocking; } + const fcpEvent = getFirstContentFulPaintEvent(trace.traceEvents); + const lcpEvent = getLargestContentfulPaintEvent(trace.traceEvents); + + result.renderBlocking = { recalculateStyle: {}, requests: {} }; + + if (fcpEvent) { + const beforeFCP = getRecalculateStyleElementsAndTimeBefore( + trace.traceEvents, + fcpEvent.ts + ); + result.renderBlocking.recalculateStyle.beforeFCP = beforeFCP; + } + + if (lcpEvent) { + const beforeLCP = getRecalculateStyleElementsAndTimeBefore( + trace.traceEvents, + lcpEvent.ts + ); + result.renderBlocking.recalculateStyle.beforeLCP = beforeLCP; + } + if (!this.options.skipHar) { for (let harRequest of this.hars[index - 1].log.entries) { if (renderBlockingInfo[harRequest.request.url]) { @@ -362,7 +388,7 @@ export class Chromium { } } - result.renderBlocking = renderBlockingInfo; + result.renderBlocking.requests = renderBlockingInfo; } // Google Web Vitals hacksery diff --git a/lib/chrome/webdriver/traceUtilities.js b/lib/chrome/webdriver/traceUtilities.js new file mode 100644 index 000000000..2ca1c8022 --- /dev/null +++ b/lib/chrome/webdriver/traceUtilities.js @@ -0,0 +1,64 @@ +import intel from 'intel'; +const log = intel.getLogger('browsertime.chrome'); + +export function getLargestContentfulPaintEvent(traceEvents) { + const lcpCandidates = traceEvents.filter( + task => task.name === 'largestContentfulPaint::Candidate' + ); + + if (lcpCandidates.length > 0) { + let lcpEvent = lcpCandidates[0]; + + for (const candidate of lcpCandidates) { + if (candidate.ts > lcpEvent.ts) { + lcpEvent = candidate; + } + } + return lcpEvent; + } else { + log.info('No LCP event found in the trace'); + } +} + +export function getFirstContentFulPaintEvent(traceEvents) { + // Get first contentful paint + const fcpEvent = traceEvents.find( + task => task.name === 'firstContentfulPaint' + ); + + if (fcpEvent) { + return fcpEvent; + } else { + log.info('Did not find the FCP event in the trace'); + } +} + +export function getRecalculateStyleElementsAndTimeBefore( + traceEvents, + timestamp +) { + const recalculatesBefore = traceEvents.filter( + task => + task.cat === 'disabled-by-default-devtools.timeline' && + task.name === 'ScheduleStyleRecalculation' && + task.ts < timestamp + ); + + const updateLayoutTree = traceEvents.filter( + task => + task.cat === 'blink,devtools.timeline' && + task.name === 'UpdateLayoutTree' && + task.ts < timestamp && + recalculatesBefore.some( + recalculate => task.args.beginData.frame === recalculate.args.data.frame + ) + ); + let elements = 0; + let duration = 0; + for (let a of updateLayoutTree) { + elements += a.args.elementCount; + duration += a.dur; + } + + return { elements, durationInMillis: duration / 1000 }; +} diff --git a/lib/core/engine/collector.js b/lib/core/engine/collector.js index 21f9dd9e0..29c301694 100644 --- a/lib/core/engine/collector.js +++ b/lib/core/engine/collector.js @@ -404,6 +404,12 @@ export class Collector { results.renderBlocking = []; } results.renderBlocking.push(data.renderBlocking); + + statistics.addDeep({ + renderBlocking: { + recalculateStyle: data.renderBlocking.recalculateStyle + } + }); } // Add power data (if we have it)