From bd2381f764f8f7d31c95b522fe70779b0b887dc4 Mon Sep 17 00:00:00 2001 From: Peter Hedenskog Date: Tue, 19 May 2026 15:21:37 +0200 Subject: [PATCH] Fix the CDP field name for render-blocking lift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lift introduced in 1.3.0 silently never fires against real Chrome. Chrome emits the per-request render-blocking classification as Network.requestWillBeSent.params.renderBlockingBehavior, not renderBlockingStatus — _renderBlocking was therefore unset on every entry, and every downstream consumer (waterfall-tools' orange ⊗ marker, sitespeed.io's request detail panel, pagexray's render-blocking counts) silently rendered nothing. The synthetic unit tests passed against the typo because they authored the input next to the assertion; a regression test against a real perflog now guards that class of mistake. Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com --- index.js | 17 +++++++++-------- test/tests.js | 25 +++++++++++++++++++++---- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 6c47a2b..a4f4aeb 100644 --- a/index.js +++ b/index.js @@ -226,14 +226,15 @@ export function harFromMessages(messages, options) { _resourceType: params.type ? params.type.toLowerCase() : undefined }; - // CDP `renderBlockingStatus` (Chrome 108+) — values are PascalCase - // (`Blocking`, `NonBlocking`, `InBodyParserBlocking`, `Potentially…`). - // waterfall-tools' WPT-style renderer matches `_renderBlocking === - // 'blocking'` to draw the orange ⊗ marker, so lowercase the value - // here. Other variants ride along verbatim-lowercased for any - // consumer that wants to inspect them. - if (params.renderBlockingStatus) { - entry._renderBlocking = params.renderBlockingStatus.toLowerCase(); + // CDP per-request render-blocking classification (Chrome 108+). + // The field lives at `Network.requestWillBeSent.params.renderBlockingBehavior` + // and its values are PascalCase (`Blocking`, `NonBlocking`, + // `NonBlockingDynamic`, …). waterfall-tools' WPT-style renderer + // matches `_renderBlocking === 'blocking'` to draw the orange ⊗ + // marker, so lowercase the value here; other variants ride along + // verbatim-lowercased for any consumer that wants to inspect them. + if (params.renderBlockingBehavior) { + entry._renderBlocking = params.renderBlockingBehavior.toLowerCase(); } // The object initiator change according to its type diff --git a/test/tests.js b/test/tests.js index e61039d..04bf18f 100644 --- a/test/tests.js +++ b/test/tests.js @@ -130,13 +130,13 @@ test('Parses IPv6 address', t => { ); }); -test('Lifts CDP renderBlockingStatus onto entries as `_renderBlocking`', t => { +test('Lifts CDP renderBlockingBehavior onto entries as `_renderBlocking`', t => { // Minimal synthetic CDP stream: one render-blocking document + one // non-blocking subresource. The renderer in waterfall-tools matches // `_renderBlocking === 'blocking'` (lowercase) to draw the orange ⊗ // marker, so we assert the casing here too. const frameId = 'F1'; - const baseMessages = (requestId, url, renderBlockingStatus) => [ + const baseMessages = (requestId, url, renderBlockingBehavior) => [ { method: 'Network.requestWillBeSent', params: { @@ -154,7 +154,7 @@ test('Lifts CDP renderBlockingStatus onto entries as `_renderBlocking`', t => { wallTime: 1_700_000_000, initiator: { type: 'other' }, type: requestId === '1' ? 'Document' : 'Script', - ...(renderBlockingStatus ? { renderBlockingStatus } : {}) + ...(renderBlockingBehavior ? { renderBlockingBehavior } : {}) } }, { @@ -208,7 +208,7 @@ test('Lifts CDP renderBlockingStatus onto entries as `_renderBlocking`', t => { }); test('Omits `_renderBlocking` when CDP did not report it', t => { - // Older Chrome builds don't emit renderBlockingStatus at all — make sure + // Older Chrome builds don't emit renderBlockingBehavior at all — make sure // we don't materialise the field as `undefined` or an empty string in // that case. Absent input → absent output. const messages = [ @@ -270,6 +270,23 @@ test('Omits `_renderBlocking` when CDP did not report it', t => { t.false('_renderBlocking' in har.log.entries[0]); }); +test('Lifts `_renderBlocking` from a real Chrome perflog', t => { + // Regression guard against the field-name typo this test file was first + // shipped with (`renderBlockingStatus` vs. the real CDP field + // `renderBlockingBehavior`). The synthetic tests above can't catch that + // class of mistake — they'd silently pass against either spelling + // because they author the input alongside the assertion. A real Chrome + // perflog can. + const perflogPath = perflog('soft-navigation-github-issues.json'); + return parsePerflog(perflogPath).then(har => { + const values = new Set( + har.log.entries.map(e => e._renderBlocking).filter(v => v !== undefined) + ); + t.true(values.has('nonblocking')); + t.true(values.has('nonblockingdynamic')); + }); +}); + test('Forwards the resource type value', t => { const perflogPath = perflog('www.google.ru.json'); const expected = {