blob: 3e99f4005ccc8cba29fce0e81f55c23ebde5fc5d [file] [log] [blame]
Tsuyoshi Horo625e92a2018-05-17 00:36:111// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Tim van der Lippe0ed1d2b2020-02-04 13:45:135import * as Common from '../common/common.js';
6import * as Components from '../components/components.js';
7import * as Host from '../host/host.js';
8import * as SDK from '../sdk/sdk.js'; // eslint-disable-line no-unused-vars
9import * as UI from '../ui/ui.js';
10
11export class SignedExchangeInfoView extends UI.Widget.VBox {
Tsuyoshi Horo625e92a2018-05-17 00:36:1112 /**
Tim van der Lippe0ed1d2b2020-02-04 13:45:1313 * @param {!SDK.NetworkRequest.NetworkRequest} request
Tsuyoshi Horo625e92a2018-05-17 00:36:1114 */
Tsuyoshi Horo02266c32018-05-21 17:01:1815 constructor(request) {
Tsuyoshi Horo625e92a2018-05-17 00:36:1116 super();
Simon Zünd9c1b6062020-10-07 08:05:1917 console.assert(request.signedExchangeInfo() !== null);
18 /** @type {!Protocol.Network.SignedExchangeInfo} */
19 const signedExchangeInfo = /** @type {!Protocol.Network.SignedExchangeInfo} */ (request.signedExchangeInfo());
Tsuyoshi Horo02266c32018-05-21 17:01:1820
Jack Franklin71519f82020-11-03 12:08:5921 this.registerRequiredCSS('network/signedExchangeInfoView.css', {enableLegacyPatching: true});
Tsuyoshi Horo625e92a2018-05-17 00:36:1122 this.element.classList.add('signed-exchange-info-view');
23
Tim van der Lippe0ed1d2b2020-02-04 13:45:1324 const root = new UI.TreeOutline.TreeOutlineInShadow();
Jack Franklin71519f82020-11-03 12:08:5925 root.registerRequiredCSS('network/signedExchangeInfoTree.css', {enableLegacyPatching: true});
Tsuyoshi Horo625e92a2018-05-17 00:36:1126 root.element.classList.add('signed-exchange-info-tree');
27 root.setFocusable(false);
28 root.makeDense();
29 root.expandTreeElementsWhenArrowing = true;
30 this.element.appendChild(root.element);
31
Tsuyoshi Horo06db4472018-05-31 07:32:4532 /** @type {!Map<number|undefined, !Set<string>>} */
33 const errorFieldSetMap = new Map();
34
Tsuyoshi Horo625e92a2018-05-17 00:36:1135 if (signedExchangeInfo.errors && signedExchangeInfo.errors.length) {
Tim van der Lippe0ed1d2b2020-02-04 13:45:1336 const errorMessagesCategory = new Category(root, Common.UIString.UIString('Errors'));
Tsuyoshi Horo06db4472018-05-31 07:32:4537 for (const error of signedExchangeInfo.errors) {
Simon Zünd9c1b6062020-10-07 08:05:1938 const fragment = document.createDocumentFragment();
Tim van der Lippe0ed1d2b2020-02-04 13:45:1339 fragment.appendChild(UI.Icon.Icon.create('smallicon-error', 'prompt-icon'));
Tsuyoshi Horo06db4472018-05-31 07:32:4540 fragment.createChild('div', 'error-log').textContent = error.message;
Tsuyoshi Horo625e92a2018-05-17 00:36:1141 errorMessagesCategory.createLeaf(fragment);
Tsuyoshi Horo06db4472018-05-31 07:32:4542 if (error.errorField) {
43 let errorFieldSet = errorFieldSetMap.get(error.signatureIndex);
44 if (!errorFieldSet) {
45 errorFieldSet = new Set();
46 errorFieldSetMap.set(error.signatureIndex, errorFieldSet);
47 }
48 errorFieldSet.add(error.errorField);
49 }
Tsuyoshi Horo625e92a2018-05-17 00:36:1150 }
51 }
Tsuyoshi Horo06db4472018-05-31 07:32:4552
Simon Zünd9c1b6062020-10-07 08:05:1953 const titleElement = document.createDocumentFragment();
Tim van der Lippe0ed1d2b2020-02-04 13:45:1354 titleElement.createChild('div', 'header-name').textContent = Common.UIString.UIString('Signed HTTP exchange');
55 const learnMoreNode = UI.XLink.XLink.create(
56 'https://github.com/WICG/webpackage', Common.UIString.UIString('Learn\xa0more'), 'header-toggle');
Tsuyoshi Horo06db4472018-05-31 07:32:4557 titleElement.appendChild(learnMoreNode);
Tim van der Lippe119690c2020-01-13 12:31:3058 const headerCategory = new Category(root, titleElement);
Tsuyoshi Horo625e92a2018-05-17 00:36:1159 if (signedExchangeInfo.header) {
60 const header = signedExchangeInfo.header;
Tsuyoshi Horo02266c32018-05-21 17:01:1861 const redirectDestination = request.redirectDestination();
Tim van der Lippe0ed1d2b2020-02-04 13:45:1362 const requestURLElement = this._formatHeader(Common.UIString.UIString('Request URL'), header.requestUrl);
Tsuyoshi Horo02266c32018-05-21 17:01:1863 if (redirectDestination) {
Tim van der Lippe0ed1d2b2020-02-04 13:45:1364 const viewRequestLink = Components.Linkifier.Linkifier.linkifyRevealable(redirectDestination, 'View request');
Tsuyoshi Horo02266c32018-05-21 17:01:1865 viewRequestLink.classList.add('header-toggle');
66 requestURLElement.appendChild(viewRequestLink);
67 }
68 headerCategory.createLeaf(requestURLElement);
Tim van der Lippe0ed1d2b2020-02-04 13:45:1369 headerCategory.createLeaf(
70 this._formatHeader(Common.UIString.UIString('Response code'), header.responseCode + ''));
71 headerCategory.createLeaf(
72 this._formatHeader(Common.UIString.UIString('Header integrity hash'), header.headerIntegrity));
Tsuyoshi Horo625e92a2018-05-17 00:36:1173
74 this._responseHeadersItem =
Tim van der Lippe0ed1d2b2020-02-04 13:45:1375 headerCategory.createLeaf(this._formatHeader(Common.UIString.UIString('Response headers'), ''));
Tsuyoshi Horo625e92a2018-05-17 00:36:1176 const responseHeaders = header.responseHeaders;
77 for (const name in responseHeaders) {
Tim van der Lippe0ed1d2b2020-02-04 13:45:1378 const headerTreeElement = new UI.TreeOutline.TreeElement(this._formatHeader(name, responseHeaders[name]));
Tsuyoshi Horo625e92a2018-05-17 00:36:1179 headerTreeElement.selectable = false;
80 this._responseHeadersItem.appendChild(headerTreeElement);
81 }
82 this._responseHeadersItem.expand();
83
Tsuyoshi Horo06db4472018-05-31 07:32:4584 for (let i = 0; i < header.signatures.length; ++i) {
85 const errorFieldSet = errorFieldSetMap.get(i) || new Set();
86 const signature = header.signatures[i];
Tim van der Lippe0ed1d2b2020-02-04 13:45:1387 const signatureCategory = new Category(root, Common.UIString.UIString('Signature'));
88 signatureCategory.createLeaf(this._formatHeader(Common.UIString.UIString('Label'), signature.label));
Tsuyoshi Horo06db4472018-05-31 07:32:4589 signatureCategory.createLeaf(this._formatHeaderForHexData(
Tim van der Lippe0ed1d2b2020-02-04 13:45:1390 Common.UIString.UIString('Signature'), signature.signature,
Tsuyoshi Horo06db4472018-05-31 07:32:4591 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureSig)));
Tsuyoshi Horo32b85e72018-05-25 03:54:1692
93 if (signature.certUrl) {
Tsuyoshi Horo06db4472018-05-31 07:32:4594 const certURLElement = this._formatHeader(
Tim van der Lippe0ed1d2b2020-02-04 13:45:1395 Common.UIString.UIString('Certificate URL'), signature.certUrl,
Tsuyoshi Horo06db4472018-05-31 07:32:4596 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureCertUrl));
Tsuyoshi Horo32b85e72018-05-25 03:54:1697 if (signature.certificates) {
98 const viewCertLink = certURLElement.createChild('span', 'devtools-link header-toggle');
Tim van der Lippe0ed1d2b2020-02-04 13:45:1399 viewCertLink.textContent = Common.UIString.UIString('View certificate');
Tsuyoshi Horo32b85e72018-05-25 03:54:16100 viewCertLink.addEventListener(
Tim van der Lippe0ed1d2b2020-02-04 13:45:13101 'click',
102 Host.InspectorFrontendHost.InspectorFrontendHostInstance.showCertificateViewer.bind(
103 null, signature.certificates),
104 false);
Tsuyoshi Horo32b85e72018-05-25 03:54:16105 }
106 signatureCategory.createLeaf(certURLElement);
107 }
Tsuyoshi Horo06db4472018-05-31 07:32:45108 signatureCategory.createLeaf(this._formatHeader(
Tim van der Lippe0ed1d2b2020-02-04 13:45:13109 Common.UIString.UIString('Integrity'), signature.integrity,
Tsuyoshi Horo06db4472018-05-31 07:32:45110 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureIntegrity)));
Tsuyoshi Horo03fbafe2018-05-25 03:59:58111 if (signature.certSha256) {
Tsuyoshi Horo06db4472018-05-31 07:32:45112 signatureCategory.createLeaf(this._formatHeaderForHexData(
Tim van der Lippe0ed1d2b2020-02-04 13:45:13113 Common.UIString.UIString('Certificate SHA256'), signature.certSha256,
Tsuyoshi Horo06db4472018-05-31 07:32:45114 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureCertSha256)));
Tsuyoshi Horo03fbafe2018-05-25 03:59:58115 }
Tsuyoshi Horo06db4472018-05-31 07:32:45116 signatureCategory.createLeaf(this._formatHeader(
Tim van der Lippe0ed1d2b2020-02-04 13:45:13117 Common.UIString.UIString('Validity URL'), signature.validityUrl,
Tsuyoshi Horo06db4472018-05-31 07:32:45118 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureValidityUrl)));
119 signatureCategory.createLeaf().title = this._formatHeader(
Tim van der Lippe0ed1d2b2020-02-04 13:45:13120 Common.UIString.UIString('Date'), new Date(1000 * signature.date).toUTCString(),
Tsuyoshi Horo06db4472018-05-31 07:32:45121 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureTimestamps));
122 signatureCategory.createLeaf().title = this._formatHeader(
Tim van der Lippe0ed1d2b2020-02-04 13:45:13123 Common.UIString.UIString('Expires'), new Date(1000 * signature.expires).toUTCString(),
Tsuyoshi Horo06db4472018-05-31 07:32:45124 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureTimestamps));
Tsuyoshi Horo625e92a2018-05-17 00:36:11125 }
126 }
127 if (signedExchangeInfo.securityDetails) {
128 const securityDetails = signedExchangeInfo.securityDetails;
Tim van der Lippe0ed1d2b2020-02-04 13:45:13129 const securityCategory = new Category(root, Common.UIString.UIString('Certificate'));
130 securityCategory.createLeaf(this._formatHeader(Common.UIString.UIString('Subject'), securityDetails.subjectName));
131 securityCategory.createLeaf(this._formatHeader(
132 Common.UIString.UIString('Valid from'), new Date(1000 * securityDetails.validFrom).toUTCString()));
133 securityCategory.createLeaf(this._formatHeader(
134 Common.UIString.UIString('Valid until'), new Date(1000 * securityDetails.validTo).toUTCString()));
135 securityCategory.createLeaf(this._formatHeader(Common.UIString.UIString('Issuer'), securityDetails.issuer));
Tsuyoshi Horo625e92a2018-05-17 00:36:11136 }
137 }
138
139 /**
140 * @param {string} name
141 * @param {string} value
Tsuyoshi Horo06db4472018-05-31 07:32:45142 * @param {boolean=} highlighted
Tsuyoshi Horo625e92a2018-05-17 00:36:11143 * @return {!DocumentFragment}
144 */
Tsuyoshi Horo06db4472018-05-31 07:32:45145 _formatHeader(name, value, highlighted) {
Simon Zünd9c1b6062020-10-07 08:05:19146 const fragment = document.createDocumentFragment();
Tsuyoshi Horo06db4472018-05-31 07:32:45147 const nameElement = fragment.createChild('div', 'header-name');
148 nameElement.textContent = name + ': ';
Tsuyoshi Horo625e92a2018-05-17 00:36:11149 fragment.createChild('span', 'header-separator');
Tsuyoshi Horo06db4472018-05-31 07:32:45150 const valueElement = fragment.createChild('div', 'header-value source-code');
151 valueElement.textContent = value;
152 if (highlighted) {
153 nameElement.classList.add('error-field');
154 valueElement.classList.add('error-field');
155 }
Tsuyoshi Horo625e92a2018-05-17 00:36:11156 return fragment;
157 }
Tsuyoshi Horo03fbafe2018-05-25 03:59:58158
159 /**
160 * @param {string} name
161 * @param {string} value
Tsuyoshi Horo06db4472018-05-31 07:32:45162 * @param {boolean=} highlighted
Tsuyoshi Horo03fbafe2018-05-25 03:59:58163 * @return {!DocumentFragment}
164 */
Tsuyoshi Horo06db4472018-05-31 07:32:45165 _formatHeaderForHexData(name, value, highlighted) {
Simon Zünd9c1b6062020-10-07 08:05:19166 const fragment = document.createDocumentFragment();
Tsuyoshi Horo06db4472018-05-31 07:32:45167 const nameElement = fragment.createChild('div', 'header-name');
168 nameElement.textContent = name + ': ';
Tsuyoshi Horo03fbafe2018-05-25 03:59:58169 fragment.createChild('span', 'header-separator');
Tsuyoshi Horo06db4472018-05-31 07:32:45170 const valueElement = fragment.createChild('div', 'header-value source-code hex-data');
171 valueElement.textContent = value.replace(/(.{2})/g, '$1 ');
172 if (highlighted) {
173 nameElement.classList.add('error-field');
174 valueElement.classList.add('error-field');
175 }
Tsuyoshi Horo03fbafe2018-05-25 03:59:58176 return fragment;
177 }
Paul Lewis56509652019-12-06 12:51:58178}
Tsuyoshi Horo625e92a2018-05-17 00:36:11179
180/**
181 * @unrestricted
182 */
Tim van der Lippe0ed1d2b2020-02-04 13:45:13183export class Category extends UI.TreeOutline.TreeElement {
Tsuyoshi Horo625e92a2018-05-17 00:36:11184 /**
Tim van der Lippe0ed1d2b2020-02-04 13:45:13185 * @param {!UI.TreeOutline.TreeOutline} root
Tsuyoshi Horo02266c32018-05-21 17:01:18186 * @param {(string|!Node)=} title
Tsuyoshi Horo625e92a2018-05-17 00:36:11187 */
188 constructor(root, title) {
189 super(title, true);
190 this.selectable = false;
191 this.toggleOnClick = true;
192 this.expanded = true;
193 root.appendChild(this);
194 }
195
196 /**
197 * @param {(string|!Node)=} title
198 */
199 createLeaf(title) {
Tim van der Lippe0ed1d2b2020-02-04 13:45:13200 const leaf = new UI.TreeOutline.TreeElement(title);
Tsuyoshi Horo625e92a2018-05-17 00:36:11201 leaf.selectable = false;
202 this.appendChild(leaf);
203 return leaf;
204 }
Paul Lewis56509652019-12-06 12:51:58205}