blob: 07df2340af1619dd2efbf5661c5079902364f202 [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 Lippe119690c2020-01-13 12:31:305export class SignedExchangeInfoView extends UI.VBox {
Tsuyoshi Horo625e92a2018-05-17 00:36:116 /**
Tsuyoshi Horo02266c32018-05-21 17:01:187 * @param {!SDK.NetworkRequest} request
Tsuyoshi Horo625e92a2018-05-17 00:36:118 */
Tsuyoshi Horo02266c32018-05-21 17:01:189 constructor(request) {
Tsuyoshi Horo625e92a2018-05-17 00:36:1110 super();
Tsuyoshi Horo02266c32018-05-21 17:01:1811 const signedExchangeInfo = request.signedExchangeInfo();
12 console.assert(signedExchangeInfo);
13
Tsuyoshi Horo625e92a2018-05-17 00:36:1114 this.registerRequiredCSS('network/signedExchangeInfoView.css');
15 this.element.classList.add('signed-exchange-info-view');
16
17 const root = new UI.TreeOutlineInShadow();
18 root.registerRequiredCSS('network/signedExchangeInfoTree.css');
19 root.element.classList.add('signed-exchange-info-tree');
20 root.setFocusable(false);
21 root.makeDense();
22 root.expandTreeElementsWhenArrowing = true;
23 this.element.appendChild(root.element);
24
Tsuyoshi Horo06db4472018-05-31 07:32:4525 /** @type {!Map<number|undefined, !Set<string>>} */
26 const errorFieldSetMap = new Map();
27
Tsuyoshi Horo625e92a2018-05-17 00:36:1128 if (signedExchangeInfo.errors && signedExchangeInfo.errors.length) {
Tim van der Lippe119690c2020-01-13 12:31:3029 const errorMessagesCategory = new Category(root, Common.UIString('Errors'));
Tsuyoshi Horo06db4472018-05-31 07:32:4530 for (const error of signedExchangeInfo.errors) {
Tsuyoshi Horo625e92a2018-05-17 00:36:1131 const fragment = createDocumentFragment();
32 fragment.appendChild(UI.Icon.create('smallicon-error', 'prompt-icon'));
Tsuyoshi Horo06db4472018-05-31 07:32:4533 fragment.createChild('div', 'error-log').textContent = error.message;
Tsuyoshi Horo625e92a2018-05-17 00:36:1134 errorMessagesCategory.createLeaf(fragment);
Tsuyoshi Horo06db4472018-05-31 07:32:4535 if (error.errorField) {
36 let errorFieldSet = errorFieldSetMap.get(error.signatureIndex);
37 if (!errorFieldSet) {
38 errorFieldSet = new Set();
39 errorFieldSetMap.set(error.signatureIndex, errorFieldSet);
40 }
41 errorFieldSet.add(error.errorField);
42 }
Tsuyoshi Horo625e92a2018-05-17 00:36:1143 }
44 }
Tsuyoshi Horo06db4472018-05-31 07:32:4545
46 const titleElement = createDocumentFragment();
47 titleElement.createChild('div', 'header-name').textContent = Common.UIString('Signed HTTP exchange');
48 const learnMoreNode =
49 UI.XLink.create('https://github.com/WICG/webpackage', Common.UIString('Learn\xa0more'), 'header-toggle');
50 titleElement.appendChild(learnMoreNode);
Tim van der Lippe119690c2020-01-13 12:31:3051 const headerCategory = new Category(root, titleElement);
Tsuyoshi Horo625e92a2018-05-17 00:36:1152 if (signedExchangeInfo.header) {
53 const header = signedExchangeInfo.header;
Tsuyoshi Horo02266c32018-05-21 17:01:1854 const redirectDestination = request.redirectDestination();
55 const requestURLElement = this._formatHeader(Common.UIString('Request URL'), header.requestUrl);
56 if (redirectDestination) {
57 const viewRequestLink = Components.Linkifier.linkifyRevealable(redirectDestination, 'View request');
58 viewRequestLink.classList.add('header-toggle');
59 requestURLElement.appendChild(viewRequestLink);
60 }
61 headerCategory.createLeaf(requestURLElement);
Tsuyoshi Horo625e92a2018-05-17 00:36:1162 headerCategory.createLeaf(this._formatHeader(Common.UIString('Response code'), header.responseCode + ''));
Tsuyoshi Horo720ff722019-07-01 04:54:3963 headerCategory.createLeaf(this._formatHeader(Common.UIString('Header integrity hash'), header.headerIntegrity));
Tsuyoshi Horo625e92a2018-05-17 00:36:1164
65 this._responseHeadersItem =
66 headerCategory.createLeaf(this._formatHeader(Common.UIString('Response headers'), ''));
67 const responseHeaders = header.responseHeaders;
68 for (const name in responseHeaders) {
69 const headerTreeElement = new UI.TreeElement(this._formatHeader(name, responseHeaders[name]));
70 headerTreeElement.selectable = false;
71 this._responseHeadersItem.appendChild(headerTreeElement);
72 }
73 this._responseHeadersItem.expand();
74
Tsuyoshi Horo06db4472018-05-31 07:32:4575 for (let i = 0; i < header.signatures.length; ++i) {
76 const errorFieldSet = errorFieldSetMap.get(i) || new Set();
77 const signature = header.signatures[i];
Tim van der Lippe119690c2020-01-13 12:31:3078 const signatureCategory = new Category(root, Common.UIString('Signature'));
Tsuyoshi Horo625e92a2018-05-17 00:36:1179 signatureCategory.createLeaf(this._formatHeader(Common.UIString('Label'), signature.label));
Tsuyoshi Horo06db4472018-05-31 07:32:4580 signatureCategory.createLeaf(this._formatHeaderForHexData(
81 Common.UIString('Signature'), signature.signature,
82 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureSig)));
Tsuyoshi Horo32b85e72018-05-25 03:54:1683
84 if (signature.certUrl) {
Tsuyoshi Horo06db4472018-05-31 07:32:4585 const certURLElement = this._formatHeader(
86 Common.UIString('Certificate URL'), signature.certUrl,
87 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureCertUrl));
Tsuyoshi Horo32b85e72018-05-25 03:54:1688 if (signature.certificates) {
89 const viewCertLink = certURLElement.createChild('span', 'devtools-link header-toggle');
90 viewCertLink.textContent = Common.UIString('View certificate');
91 viewCertLink.addEventListener(
Tim van der Lippe50cfa9b2019-10-01 10:40:5892 'click', Host.InspectorFrontendHost.showCertificateViewer.bind(null, signature.certificates), false);
Tsuyoshi Horo32b85e72018-05-25 03:54:1693 }
94 signatureCategory.createLeaf(certURLElement);
95 }
Tsuyoshi Horo06db4472018-05-31 07:32:4596 signatureCategory.createLeaf(this._formatHeader(
97 Common.UIString('Integrity'), signature.integrity,
98 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureIntegrity)));
Tsuyoshi Horo03fbafe2018-05-25 03:59:5899 if (signature.certSha256) {
Tsuyoshi Horo06db4472018-05-31 07:32:45100 signatureCategory.createLeaf(this._formatHeaderForHexData(
101 Common.UIString('Certificate SHA256'), signature.certSha256,
102 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureCertSha256)));
Tsuyoshi Horo03fbafe2018-05-25 03:59:58103 }
Tsuyoshi Horo06db4472018-05-31 07:32:45104 signatureCategory.createLeaf(this._formatHeader(
105 Common.UIString('Validity URL'), signature.validityUrl,
106 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureValidityUrl)));
107 signatureCategory.createLeaf().title = this._formatHeader(
108 Common.UIString('Date'), new Date(1000 * signature.date).toUTCString(),
109 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureTimestamps));
110 signatureCategory.createLeaf().title = this._formatHeader(
111 Common.UIString('Expires'), new Date(1000 * signature.expires).toUTCString(),
112 errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureTimestamps));
Tsuyoshi Horo625e92a2018-05-17 00:36:11113 }
114 }
115 if (signedExchangeInfo.securityDetails) {
116 const securityDetails = signedExchangeInfo.securityDetails;
Tim van der Lippe119690c2020-01-13 12:31:30117 const securityCategory = new Category(root, Common.UIString('Certificate'));
Tsuyoshi Horo625e92a2018-05-17 00:36:11118 securityCategory.createLeaf(this._formatHeader(Common.UIString('Subject'), securityDetails.subjectName));
119 securityCategory.createLeaf(
120 this._formatHeader(Common.UIString('Valid from'), new Date(1000 * securityDetails.validFrom).toUTCString()));
121 securityCategory.createLeaf(
122 this._formatHeader(Common.UIString('Valid until'), new Date(1000 * securityDetails.validTo).toUTCString()));
123 securityCategory.createLeaf(this._formatHeader(Common.UIString('Issuer'), securityDetails.issuer));
124 }
125 }
126
127 /**
128 * @param {string} name
129 * @param {string} value
Tsuyoshi Horo06db4472018-05-31 07:32:45130 * @param {boolean=} highlighted
Tsuyoshi Horo625e92a2018-05-17 00:36:11131 * @return {!DocumentFragment}
132 */
Tsuyoshi Horo06db4472018-05-31 07:32:45133 _formatHeader(name, value, highlighted) {
Tsuyoshi Horo625e92a2018-05-17 00:36:11134 const fragment = createDocumentFragment();
Tsuyoshi Horo06db4472018-05-31 07:32:45135 const nameElement = fragment.createChild('div', 'header-name');
136 nameElement.textContent = name + ': ';
Tsuyoshi Horo625e92a2018-05-17 00:36:11137 fragment.createChild('span', 'header-separator');
Tsuyoshi Horo06db4472018-05-31 07:32:45138 const valueElement = fragment.createChild('div', 'header-value source-code');
139 valueElement.textContent = value;
140 if (highlighted) {
141 nameElement.classList.add('error-field');
142 valueElement.classList.add('error-field');
143 }
Tsuyoshi Horo625e92a2018-05-17 00:36:11144 return fragment;
145 }
Tsuyoshi Horo03fbafe2018-05-25 03:59:58146
147 /**
148 * @param {string} name
149 * @param {string} value
Tsuyoshi Horo06db4472018-05-31 07:32:45150 * @param {boolean=} highlighted
Tsuyoshi Horo03fbafe2018-05-25 03:59:58151 * @return {!DocumentFragment}
152 */
Tsuyoshi Horo06db4472018-05-31 07:32:45153 _formatHeaderForHexData(name, value, highlighted) {
Tsuyoshi Horo03fbafe2018-05-25 03:59:58154 const fragment = createDocumentFragment();
Tsuyoshi Horo06db4472018-05-31 07:32:45155 const nameElement = fragment.createChild('div', 'header-name');
156 nameElement.textContent = name + ': ';
Tsuyoshi Horo03fbafe2018-05-25 03:59:58157 fragment.createChild('span', 'header-separator');
Tsuyoshi Horo06db4472018-05-31 07:32:45158 const valueElement = fragment.createChild('div', 'header-value source-code hex-data');
159 valueElement.textContent = value.replace(/(.{2})/g, '$1 ');
160 if (highlighted) {
161 nameElement.classList.add('error-field');
162 valueElement.classList.add('error-field');
163 }
Tsuyoshi Horo03fbafe2018-05-25 03:59:58164 return fragment;
165 }
Paul Lewis56509652019-12-06 12:51:58166}
Tsuyoshi Horo625e92a2018-05-17 00:36:11167
168/**
169 * @unrestricted
170 */
Paul Lewis56509652019-12-06 12:51:58171export class Category extends UI.TreeElement {
Tsuyoshi Horo625e92a2018-05-17 00:36:11172 /**
173 * @param {!UI.TreeOutline} root
Tsuyoshi Horo02266c32018-05-21 17:01:18174 * @param {(string|!Node)=} title
Tsuyoshi Horo625e92a2018-05-17 00:36:11175 */
176 constructor(root, title) {
177 super(title, true);
178 this.selectable = false;
179 this.toggleOnClick = true;
180 this.expanded = true;
181 root.appendChild(this);
182 }
183
184 /**
185 * @param {(string|!Node)=} title
186 */
187 createLeaf(title) {
188 const leaf = new UI.TreeElement(title);
189 leaf.selectable = false;
190 this.appendChild(leaf);
191 return leaf;
192 }
Paul Lewis56509652019-12-06 12:51:58193}