Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 1 | // 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 Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 5 | import * as Common from '../common/common.js'; |
| 6 | import * as Components from '../components/components.js'; |
| 7 | import * as Host from '../host/host.js'; |
| 8 | import * as SDK from '../sdk/sdk.js'; // eslint-disable-line no-unused-vars |
| 9 | import * as UI from '../ui/ui.js'; |
| 10 | |
| 11 | export class SignedExchangeInfoView extends UI.Widget.VBox { |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 12 | /** |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 13 | * @param {!SDK.NetworkRequest.NetworkRequest} request |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 14 | */ |
Tsuyoshi Horo | 02266c3 | 2018-05-21 17:01:18 | [diff] [blame] | 15 | constructor(request) { |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 16 | super(); |
Simon Zünd | 9c1b606 | 2020-10-07 08:05:19 | [diff] [blame] | 17 | console.assert(request.signedExchangeInfo() !== null); |
| 18 | /** @type {!Protocol.Network.SignedExchangeInfo} */ |
| 19 | const signedExchangeInfo = /** @type {!Protocol.Network.SignedExchangeInfo} */ (request.signedExchangeInfo()); |
Tsuyoshi Horo | 02266c3 | 2018-05-21 17:01:18 | [diff] [blame] | 20 | |
Jack Franklin | 71519f8 | 2020-11-03 12:08:59 | [diff] [blame^] | 21 | this.registerRequiredCSS('network/signedExchangeInfoView.css', {enableLegacyPatching: true}); |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 22 | this.element.classList.add('signed-exchange-info-view'); |
| 23 | |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 24 | const root = new UI.TreeOutline.TreeOutlineInShadow(); |
Jack Franklin | 71519f8 | 2020-11-03 12:08:59 | [diff] [blame^] | 25 | root.registerRequiredCSS('network/signedExchangeInfoTree.css', {enableLegacyPatching: true}); |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 26 | 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 Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 32 | /** @type {!Map<number|undefined, !Set<string>>} */ |
| 33 | const errorFieldSetMap = new Map(); |
| 34 | |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 35 | if (signedExchangeInfo.errors && signedExchangeInfo.errors.length) { |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 36 | const errorMessagesCategory = new Category(root, Common.UIString.UIString('Errors')); |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 37 | for (const error of signedExchangeInfo.errors) { |
Simon Zünd | 9c1b606 | 2020-10-07 08:05:19 | [diff] [blame] | 38 | const fragment = document.createDocumentFragment(); |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 39 | fragment.appendChild(UI.Icon.Icon.create('smallicon-error', 'prompt-icon')); |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 40 | fragment.createChild('div', 'error-log').textContent = error.message; |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 41 | errorMessagesCategory.createLeaf(fragment); |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 42 | 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 Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 50 | } |
| 51 | } |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 52 | |
Simon Zünd | 9c1b606 | 2020-10-07 08:05:19 | [diff] [blame] | 53 | const titleElement = document.createDocumentFragment(); |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 54 | 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 Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 57 | titleElement.appendChild(learnMoreNode); |
Tim van der Lippe | 119690c | 2020-01-13 12:31:30 | [diff] [blame] | 58 | const headerCategory = new Category(root, titleElement); |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 59 | if (signedExchangeInfo.header) { |
| 60 | const header = signedExchangeInfo.header; |
Tsuyoshi Horo | 02266c3 | 2018-05-21 17:01:18 | [diff] [blame] | 61 | const redirectDestination = request.redirectDestination(); |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 62 | const requestURLElement = this._formatHeader(Common.UIString.UIString('Request URL'), header.requestUrl); |
Tsuyoshi Horo | 02266c3 | 2018-05-21 17:01:18 | [diff] [blame] | 63 | if (redirectDestination) { |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 64 | const viewRequestLink = Components.Linkifier.Linkifier.linkifyRevealable(redirectDestination, 'View request'); |
Tsuyoshi Horo | 02266c3 | 2018-05-21 17:01:18 | [diff] [blame] | 65 | viewRequestLink.classList.add('header-toggle'); |
| 66 | requestURLElement.appendChild(viewRequestLink); |
| 67 | } |
| 68 | headerCategory.createLeaf(requestURLElement); |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 69 | 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 Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 73 | |
| 74 | this._responseHeadersItem = |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 75 | headerCategory.createLeaf(this._formatHeader(Common.UIString.UIString('Response headers'), '')); |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 76 | const responseHeaders = header.responseHeaders; |
| 77 | for (const name in responseHeaders) { |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 78 | const headerTreeElement = new UI.TreeOutline.TreeElement(this._formatHeader(name, responseHeaders[name])); |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 79 | headerTreeElement.selectable = false; |
| 80 | this._responseHeadersItem.appendChild(headerTreeElement); |
| 81 | } |
| 82 | this._responseHeadersItem.expand(); |
| 83 | |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 84 | 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 Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 87 | const signatureCategory = new Category(root, Common.UIString.UIString('Signature')); |
| 88 | signatureCategory.createLeaf(this._formatHeader(Common.UIString.UIString('Label'), signature.label)); |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 89 | signatureCategory.createLeaf(this._formatHeaderForHexData( |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 90 | Common.UIString.UIString('Signature'), signature.signature, |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 91 | errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureSig))); |
Tsuyoshi Horo | 32b85e7 | 2018-05-25 03:54:16 | [diff] [blame] | 92 | |
| 93 | if (signature.certUrl) { |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 94 | const certURLElement = this._formatHeader( |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 95 | Common.UIString.UIString('Certificate URL'), signature.certUrl, |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 96 | errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureCertUrl)); |
Tsuyoshi Horo | 32b85e7 | 2018-05-25 03:54:16 | [diff] [blame] | 97 | if (signature.certificates) { |
| 98 | const viewCertLink = certURLElement.createChild('span', 'devtools-link header-toggle'); |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 99 | viewCertLink.textContent = Common.UIString.UIString('View certificate'); |
Tsuyoshi Horo | 32b85e7 | 2018-05-25 03:54:16 | [diff] [blame] | 100 | viewCertLink.addEventListener( |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 101 | 'click', |
| 102 | Host.InspectorFrontendHost.InspectorFrontendHostInstance.showCertificateViewer.bind( |
| 103 | null, signature.certificates), |
| 104 | false); |
Tsuyoshi Horo | 32b85e7 | 2018-05-25 03:54:16 | [diff] [blame] | 105 | } |
| 106 | signatureCategory.createLeaf(certURLElement); |
| 107 | } |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 108 | signatureCategory.createLeaf(this._formatHeader( |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 109 | Common.UIString.UIString('Integrity'), signature.integrity, |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 110 | errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureIntegrity))); |
Tsuyoshi Horo | 03fbafe | 2018-05-25 03:59:58 | [diff] [blame] | 111 | if (signature.certSha256) { |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 112 | signatureCategory.createLeaf(this._formatHeaderForHexData( |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 113 | Common.UIString.UIString('Certificate SHA256'), signature.certSha256, |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 114 | errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureCertSha256))); |
Tsuyoshi Horo | 03fbafe | 2018-05-25 03:59:58 | [diff] [blame] | 115 | } |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 116 | signatureCategory.createLeaf(this._formatHeader( |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 117 | Common.UIString.UIString('Validity URL'), signature.validityUrl, |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 118 | errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureValidityUrl))); |
| 119 | signatureCategory.createLeaf().title = this._formatHeader( |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 120 | Common.UIString.UIString('Date'), new Date(1000 * signature.date).toUTCString(), |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 121 | errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureTimestamps)); |
| 122 | signatureCategory.createLeaf().title = this._formatHeader( |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 123 | Common.UIString.UIString('Expires'), new Date(1000 * signature.expires).toUTCString(), |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 124 | errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureTimestamps)); |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 125 | } |
| 126 | } |
| 127 | if (signedExchangeInfo.securityDetails) { |
| 128 | const securityDetails = signedExchangeInfo.securityDetails; |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 129 | 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 Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 136 | } |
| 137 | } |
| 138 | |
| 139 | /** |
| 140 | * @param {string} name |
| 141 | * @param {string} value |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 142 | * @param {boolean=} highlighted |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 143 | * @return {!DocumentFragment} |
| 144 | */ |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 145 | _formatHeader(name, value, highlighted) { |
Simon Zünd | 9c1b606 | 2020-10-07 08:05:19 | [diff] [blame] | 146 | const fragment = document.createDocumentFragment(); |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 147 | const nameElement = fragment.createChild('div', 'header-name'); |
| 148 | nameElement.textContent = name + ': '; |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 149 | fragment.createChild('span', 'header-separator'); |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 150 | 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 Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 156 | return fragment; |
| 157 | } |
Tsuyoshi Horo | 03fbafe | 2018-05-25 03:59:58 | [diff] [blame] | 158 | |
| 159 | /** |
| 160 | * @param {string} name |
| 161 | * @param {string} value |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 162 | * @param {boolean=} highlighted |
Tsuyoshi Horo | 03fbafe | 2018-05-25 03:59:58 | [diff] [blame] | 163 | * @return {!DocumentFragment} |
| 164 | */ |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 165 | _formatHeaderForHexData(name, value, highlighted) { |
Simon Zünd | 9c1b606 | 2020-10-07 08:05:19 | [diff] [blame] | 166 | const fragment = document.createDocumentFragment(); |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 167 | const nameElement = fragment.createChild('div', 'header-name'); |
| 168 | nameElement.textContent = name + ': '; |
Tsuyoshi Horo | 03fbafe | 2018-05-25 03:59:58 | [diff] [blame] | 169 | fragment.createChild('span', 'header-separator'); |
Tsuyoshi Horo | 06db447 | 2018-05-31 07:32:45 | [diff] [blame] | 170 | 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 Horo | 03fbafe | 2018-05-25 03:59:58 | [diff] [blame] | 176 | return fragment; |
| 177 | } |
Paul Lewis | 5650965 | 2019-12-06 12:51:58 | [diff] [blame] | 178 | } |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 179 | |
| 180 | /** |
| 181 | * @unrestricted |
| 182 | */ |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 183 | export class Category extends UI.TreeOutline.TreeElement { |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 184 | /** |
Tim van der Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 185 | * @param {!UI.TreeOutline.TreeOutline} root |
Tsuyoshi Horo | 02266c3 | 2018-05-21 17:01:18 | [diff] [blame] | 186 | * @param {(string|!Node)=} title |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 187 | */ |
| 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 Lippe | 0ed1d2b | 2020-02-04 13:45:13 | [diff] [blame] | 200 | const leaf = new UI.TreeOutline.TreeElement(title); |
Tsuyoshi Horo | 625e92a | 2018-05-17 00:36:11 | [diff] [blame] | 201 | leaf.selectable = false; |
| 202 | this.appendChild(leaf); |
| 203 | return leaf; |
| 204 | } |
Paul Lewis | 5650965 | 2019-12-06 12:51:58 | [diff] [blame] | 205 | } |