diff --git a/front_end/core/i18n/locales/en-US.json b/front_end/core/i18n/locales/en-US.json index 3a2858cd99..0200a2d8f9 100644 --- a/front_end/core/i18n/locales/en-US.json +++ b/front_end/core/i18n/locales/en-US.json @@ -6185,6 +6185,9 @@ "panels/network/NetworkDataGridNode.ts | servedFromSignedHttpExchange": { "message": "Served from Signed HTTP Exchange, resource size: {PH1}" }, + "panels/network/NetworkDataGridNode.ts | servedFromWebBundle": { + "message": "Served from Web Bundle, resource size: {PH1}" + }, "panels/network/NetworkDataGridNode.ts | serviceworker": { "message": "(ServiceWorker)" }, @@ -6194,6 +6197,12 @@ "panels/network/NetworkDataGridNode.ts | sPreflight": { "message": "{PH1} + Preflight" }, + "panels/network/NetworkDataGridNode.ts | webBundle": { + "message": "(Web Bundle)" + }, + "panels/network/NetworkDataGridNode.ts | webBundleError": { + "message": "Web Bundle error" + }, "panels/network/NetworkItemView.ts | cookies": { "message": "Cookies" }, diff --git a/front_end/core/i18n/locales/en-XL.json b/front_end/core/i18n/locales/en-XL.json index 6f7e0bf343..269a5ddfe3 100644 --- a/front_end/core/i18n/locales/en-XL.json +++ b/front_end/core/i18n/locales/en-XL.json @@ -6185,6 +6185,9 @@ "panels/network/NetworkDataGridNode.ts | servedFromSignedHttpExchange": { "message": "Ŝér̂v́êd́ f̂ŕôḿ Ŝíĝńêd́ ĤT́T̂Ṕ Êx́ĉh́âńĝé, r̂éŝóûŕĉé ŝíẑé: {PH1}" }, + "panels/network/NetworkDataGridNode.ts | servedFromWebBundle": { + "message": "Ŝér̂v́êd́ f̂ŕôḿ Ŵéb̂ B́ûńd̂ĺê, ŕêśôúr̂ćê śîźê: {PH1}" + }, "panels/network/NetworkDataGridNode.ts | serviceworker": { "message": "(ServiceWorker)" }, @@ -6194,6 +6197,12 @@ "panels/network/NetworkDataGridNode.ts | sPreflight": { "message": "{PH1} + P̂ŕêf́l̂íĝh́t̂" }, + "panels/network/NetworkDataGridNode.ts | webBundle": { + "message": "(Ŵéb̂ B́ûńd̂ĺê)" + }, + "panels/network/NetworkDataGridNode.ts | webBundleError": { + "message": "Ŵéb̂ B́ûńd̂ĺê ér̂ŕôŕ" + }, "panels/network/NetworkItemView.ts | cookies": { "message": "Ĉóôḱîéŝ" }, diff --git a/front_end/panels/network/NetworkDataGridNode.ts b/front_end/panels/network/NetworkDataGridNode.ts index 1656da4b4e..581820acf5 100644 --- a/front_end/panels/network/NetworkDataGridNode.ts +++ b/front_end/panels/network/NetworkDataGridNode.ts @@ -183,6 +183,11 @@ const UIStrings = { */ servedFromSignedHttpExchange: 'Served from Signed HTTP Exchange, resource size: {PH1}', /** + *@description Cell title in Network Data Grid Node of the Network panel + *@example {4 B} PH1 + */ + servedFromWebBundle: 'Served from Web Bundle, resource size: {PH1}', + /** *@description Text of a DOM element in Network Data Grid Node of the Network panel */ prefetchCache: '(prefetch cache)', @@ -208,6 +213,14 @@ const UIStrings = { *@description Text describing the depth of a top level node in the network datagrid */ level: 'level 1', + /** + *@description Text in Network Data Grid Node of the Network panel + */ + webBundleError: 'Web Bundle error', + /** + *@description Text in Network Data Grid Node of the Network panel + */ + webBundle: '(Web Bundle)', }; const str_ = i18n.i18n.registerUIStrings('panels/network/NetworkDataGridNode.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); @@ -1013,9 +1026,20 @@ export class NetworkRequestNode extends NetworkNode { } _isFailed(): boolean { + if (this._request.failed && !this._request.statusCode) { + return true; + } + if (this._request.statusCode >= 400) { + return true; + } const signedExchangeInfo = this._request.signedExchangeInfo(); - return (this._request.failed && !this._request.statusCode) || (this._request.statusCode >= 400) || - (signedExchangeInfo !== null && Boolean(signedExchangeInfo.errors)); + if (signedExchangeInfo !== null && Boolean(signedExchangeInfo.errors)) { + return true; + } + if (this._request.webBundleInfo()?.errorMessage || this._request.webBundleInnerRequestInfo()?.errorMessage) { + return true; + } + return false; } _renderPrimaryCell(cell: HTMLElement, columnId: string, text?: string): void { @@ -1068,7 +1092,11 @@ export class NetworkRequestNode extends NetworkNode { 'network-dim-cell', !this._isFailed() && (this._request.cached() || !this._request.statusCode)); const corsErrorStatus = this._request.corsErrorStatus(); - if (this._request.failed && !this._request.canceled && !this._request.wasBlocked() && !corsErrorStatus) { + const webBundleErrorMessage = + this._request.webBundleInfo()?.errorMessage || this._request.webBundleInnerRequestInfo()?.errorMessage; + if (webBundleErrorMessage) { + this._setTextAndTitle(cell, i18nString(UIStrings.webBundleError), webBundleErrorMessage); + } else if (this._request.failed && !this._request.canceled && !this._request.wasBlocked() && !corsErrorStatus) { const failText = i18nString(UIStrings.failed); if (this._request.localizedFailDescription) { UI.UIUtils.createTextChild(cell, failText); @@ -1269,6 +1297,10 @@ export class NetworkRequestNode extends NetworkNode { UI.UIUtils.createTextChild(cell, i18n.i18n.lockedString('(signed-exchange)')); UI.Tooltip.Tooltip.install(cell, i18nString(UIStrings.servedFromSignedHttpExchange, {PH1: resourceSize})); cell.classList.add('network-dim-cell'); + } else if (this._request.webBundleInnerRequestInfo()) { + UI.UIUtils.createTextChild(cell, i18nString(UIStrings.webBundle)); + UI.Tooltip.Tooltip.install(cell, i18nString(UIStrings.servedFromWebBundle, {PH1: resourceSize})); + cell.classList.add('network-dim-cell'); } else if (this._request.fromPrefetchCache()) { UI.UIUtils.createTextChild(cell, i18nString(UIStrings.prefetchCache)); UI.Tooltip.Tooltip.install(cell, i18nString(UIStrings.servedFromPrefetchCacheResource, {PH1: resourceSize})); diff --git a/scripts/hosted_mode/server.js b/scripts/hosted_mode/server.js index b466aa0894..2987b9f6c5 100644 --- a/scripts/hosted_mode/server.js +++ b/scripts/hosted_mode/server.js @@ -95,7 +95,7 @@ async function requestHandler(request, response) { let encoding = 'utf8'; if (absoluteFilePath.endsWith('.wasm') || absoluteFilePath.endsWith('.png') || absoluteFilePath.endsWith('.jpg') || - absoluteFilePath.endsWith('.avif')) { + absoluteFilePath.endsWith('.avif') || absoluteFilePath.endsWith('.wbn')) { encoding = 'binary'; } diff --git a/test/e2e/network/network-datagrid_test.ts b/test/e2e/network/network-datagrid_test.ts index b5002c96c2..655fe18c3d 100644 --- a/test/e2e/network/network-datagrid_test.ts +++ b/test/e2e/network/network-datagrid_test.ts @@ -1,4 +1,4 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. +// Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,6 +13,9 @@ describe('The Network Tab', async function() { // These tests take some time on slow windows machines. this.timeout(10000); } + const formatByteSize = (value: number) => { + return `${value}\xA0B`; + }; beforeEach(async () => { await navigateToNetworkTab('empty.html'); @@ -114,9 +117,6 @@ describe('The Network Tab', async function() { const getNetworkRequestMimeTypes = () => frontend.evaluate(() => { return Array.from(document.querySelectorAll('.type-column')).slice(1, 3).map(node => node.textContent); }); - const formatByteSize = (value: number) => { - return `${value}\xA0B`; - }; assert.deepEqual(await getNetworkRequestSize(), [ `${formatByteSize(361)}${formatByteSize(219)}`, @@ -205,4 +205,55 @@ describe('The Network Tab', async function() { }); }); }); + + it('indicates resources from the web bundle in the size column', async () => { + const {target, frontend} = getBrowserAndPages(); + + await navigateToNetworkTab('resources-from-webbundle.html'); + + await target.reload({waitUntil: 'networkidle0'}); + + await waitForSomeRequestsToAppear(3); + + const getNetworkRequestSize = () => frontend.evaluate(() => { + return Array.from(document.querySelectorAll('.size-column')).slice(2, 4).map(node => node.textContent); + }); + + assert.sameMembers(await getNetworkRequestSize(), [ + `${formatByteSize(645)}${formatByteSize(0)}`, + `(Web Bundle)${formatByteSize(27)}`, + ]); + }); + + it('shows web bundle metadata error in the status column', async () => { + const {target, frontend} = getBrowserAndPages(); + + await navigateToNetworkTab('resources-from-webbundle-with-bad-metadata.html'); + + await target.reload({waitUntil: 'networkidle0'}); + + await waitForSomeRequestsToAppear(3); + + const getNetworkRequestSize = () => frontend.evaluate(() => { + return Array.from(document.querySelectorAll('.status-column')).slice(2, 4).map(node => node.textContent); + }); + + assert.sameMembers(await getNetworkRequestSize(), ['Web Bundle error', '(failed)net::ERR_INVALID_WEB_BUNDLE']); + }); + + it('shows web bundle inner request error in the status column', async () => { + const {target, frontend} = getBrowserAndPages(); + + await navigateToNetworkTab('resources-from-webbundle-with-bad-inner-request.html'); + + await target.reload({waitUntil: 'networkidle0'}); + + await waitForSomeRequestsToAppear(3); + + const getNetworkRequestSize = () => frontend.evaluate(() => { + return Array.from(document.querySelectorAll('.status-column')).slice(2, 4).map(node => node.textContent); + }); + + assert.sameMembers(await getNetworkRequestSize(), ['200OK', 'Web Bundle error']); + }); }); diff --git a/test/e2e/resources/network/BUILD.gn b/test/e2e/resources/network/BUILD.gn index 955eefdbad..e5dd2694ae 100644 --- a/test/e2e/resources/network/BUILD.gn +++ b/test/e2e/resources/network/BUILD.gn @@ -14,11 +14,20 @@ copy_to_gen("network") { "last-modified.rawresponse", "requests.html", "resources-from-cache.html", + "resources-from-webbundle-with-bad-inner-request.html", + "resources-from-webbundle-with-bad-metadata.html", + "resources-from-webbundle.html", "trusted-type-policy-violation-report-only.rawresponse", "trusted-type-violations-enforced.rawresponse", "trusted-type-violations-report-only-in-html.rawresponse", "trusted-type-violations-report-only.rawresponse", "unreachable.rawresponse", "utf-8.rawresponse", + "webbundle.wbn", + "webbundle.wbn.headers", + "webbundle_bad_inner_request.wbn", + "webbundle_bad_inner_request.wbn.headers", + "webbundle_bad_metadata.wbn", + "webbundle_bad_metadata.wbn.headers", ] } diff --git a/test/e2e/resources/network/generate-test-wbns.sh b/test/e2e/resources/network/generate-test-wbns.sh new file mode 100755 index 0000000000..3d3ce4c785 --- /dev/null +++ b/test/e2e/resources/network/generate-test-wbns.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +set -e + +if ! command -v gen-bundle > /dev/null 2>&1; then + + echo "gen-bundle is not installed. Please run:" + echo " go get -u github.com/WICG/webpackage/go/bundle/cmd/..." + echo ' export PATH=$PATH:$(go env GOPATH)/bin' + exit 1 +fi + +gen-bundle \ + -version b1 \ + -har webbundle.har \ + -primaryURL urn:uuid:020111b3-437a-4c5c-ae07-adb6bbffb720 \ + -o webbundle.wbn + +cp webbundle.wbn webbundle_bad_metadata.wbn + +# corrupt magic bytes +echo 'XX' | dd of=webbundle_bad_metadata.wbn bs=1 count=2 conv=notrunc + +cp webbundle.wbn webbundle_bad_inner_request.wbn +# corrupt headers +echo 'XXXX' | dd of=webbundle_bad_inner_request.wbn bs=1 seek=205 count=4 conv=notrunc diff --git a/test/e2e/resources/network/resources-from-webbundle-with-bad-inner-request.html b/test/e2e/resources/network/resources-from-webbundle-with-bad-inner-request.html new file mode 100644 index 0000000000..34c75cb927 --- /dev/null +++ b/test/e2e/resources/network/resources-from-webbundle-with-bad-inner-request.html @@ -0,0 +1,9 @@ + +
+ + + + diff --git a/test/e2e/resources/network/resources-from-webbundle-with-bad-metadata.html b/test/e2e/resources/network/resources-from-webbundle-with-bad-metadata.html new file mode 100644 index 0000000000..1e11a0948b --- /dev/null +++ b/test/e2e/resources/network/resources-from-webbundle-with-bad-metadata.html @@ -0,0 +1,9 @@ + + + + + + diff --git a/test/e2e/resources/network/resources-from-webbundle.html b/test/e2e/resources/network/resources-from-webbundle.html new file mode 100644 index 0000000000..02c5bac877 --- /dev/null +++ b/test/e2e/resources/network/resources-from-webbundle.html @@ -0,0 +1,9 @@ + + + + + + diff --git a/test/e2e/resources/network/webbundle.har b/test/e2e/resources/network/webbundle.har new file mode 100644 index 0000000000..1708a8917b --- /dev/null +++ b/test/e2e/resources/network/webbundle.har @@ -0,0 +1,44 @@ +{ + "log": { + "entries": [ + { + "request": { + "method": "GET", + "url": "urn:uuid:020111b3-437a-4c5c-ae07-adb6bbffb720", + "headers": [] + }, + "response": { + "status": 200, + "headers": [ + { + "name": "Content-type", + "value": "application/javascript" + } + ], + "content": { + "text": "window.report_result('OK');" + } + } + }, + { + "request": { + "method": "GET", + "url": "urn:uuid:429fcc4e-0696-4bad-b099-ee9175f023ae", + "headers": [] + }, + "response": { + "status": 200, + "headers": [ + { + "name": "Content-type", + "value": "text/html" + } + ], + "content": { + "text": "" + } + } + } + ] + } +} diff --git a/test/e2e/resources/network/webbundle.wbn b/test/e2e/resources/network/webbundle.wbn new file mode 100644 index 0000000000..3255787c1c Binary files /dev/null and b/test/e2e/resources/network/webbundle.wbn differ diff --git a/test/e2e/resources/network/webbundle.wbn.headers b/test/e2e/resources/network/webbundle.wbn.headers new file mode 100644 index 0000000000..dc84ef50f6 --- /dev/null +++ b/test/e2e/resources/network/webbundle.wbn.headers @@ -0,0 +1,3 @@ +200 +Content-Type: application/webbundle +X-Content-Type-Options: nosniff diff --git a/test/e2e/resources/network/webbundle_bad_inner_request.wbn b/test/e2e/resources/network/webbundle_bad_inner_request.wbn new file mode 100644 index 0000000000..4facd5652f Binary files /dev/null and b/test/e2e/resources/network/webbundle_bad_inner_request.wbn differ diff --git a/test/e2e/resources/network/webbundle_bad_inner_request.wbn.headers b/test/e2e/resources/network/webbundle_bad_inner_request.wbn.headers new file mode 100644 index 0000000000..dc84ef50f6 --- /dev/null +++ b/test/e2e/resources/network/webbundle_bad_inner_request.wbn.headers @@ -0,0 +1,3 @@ +200 +Content-Type: application/webbundle +X-Content-Type-Options: nosniff diff --git a/test/e2e/resources/network/webbundle_bad_metadata.wbn b/test/e2e/resources/network/webbundle_bad_metadata.wbn new file mode 100644 index 0000000000..f320624548 Binary files /dev/null and b/test/e2e/resources/network/webbundle_bad_metadata.wbn differ diff --git a/test/e2e/resources/network/webbundle_bad_metadata.wbn.headers b/test/e2e/resources/network/webbundle_bad_metadata.wbn.headers new file mode 100644 index 0000000000..dc84ef50f6 --- /dev/null +++ b/test/e2e/resources/network/webbundle_bad_metadata.wbn.headers @@ -0,0 +1,3 @@ +200 +Content-Type: application/webbundle +X-Content-Type-Options: nosniff