blob: 4c5660490b37c28b34413357d3730eac0a9dc99a [file] [log] [blame]
Wolfgang Beyer40530b682022-05-17 13:02:011// Copyright 2022 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
5import * as Common from '../../../core/common/common.js';
Wolfgang Beyer7f495ee2022-07-08 13:45:136import * as Host from '../../../core/host/host.js';
Wolfgang Beyer40530b682022-05-17 13:02:017import * as i18n from '../../../core/i18n/i18n.js';
Wolfgang Beyer7f495ee2022-07-08 13:45:138import * as Platform from '../../../core/platform/platform.js';
Wolfgang Beyer40530b682022-05-17 13:02:019import * as SDK from '../../../core/sdk/sdk.js';
Wolfgang Beyer7f495ee2022-07-08 13:45:1310import * as Protocol from '../../../generated/protocol.js';
11import * as IssuesManager from '../../../models/issues_manager/issues_manager.js';
Wolfgang Beyerbcf4a832022-07-25 12:34:4712import * as ClientVariations from '../../../third_party/chromium/client-variations/client-variations.js';
Wolfgang Beyerd1dd7962022-05-24 15:48:4813import * as Buttons from '../../../ui/components/buttons/buttons.js';
Wolfgang Beyer40530b682022-05-17 13:02:0114import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
Wolfgang Beyer7f495ee2022-07-08 13:45:1315import * as IconButton from '../../../ui/components/icon_button/icon_button.js';
Jack Franklin1f19d522022-07-06 14:00:1816import * as Input from '../../../ui/components/input/input.js';
Wolfgang Beyer40530b682022-05-17 13:02:0117import * as UI from '../../../ui/legacy/legacy.js';
18import * as LitHtml from '../../../ui/lit-html/lit-html.js';
19
20import requestHeadersViewStyles from './RequestHeadersView.css.js';
21
Wolfgang Beyerd1dd7962022-05-24 15:48:4822const RAW_HEADER_CUTOFF = 3000;
Wolfgang Beyer40530b682022-05-17 13:02:0123const {render, html} = LitHtml;
24
25const UIStrings = {
26 /**
Wolfgang Beyerbcf4a832022-07-25 12:34:4727 *@description Comment used in decoded X-Client-Data HTTP header output in Headers View of the Network panel
28 */
29 activeClientExperimentVariation: 'Active `client experiment variation IDs`.',
30 /**
31 *@description Comment used in decoded X-Client-Data HTTP header output in Headers View of the Network panel
32 */
33 activeClientExperimentVariationIds: 'Active `client experiment variation IDs` that trigger server-side behavior.',
34 /**
Wolfgang Beyer7f495ee2022-07-08 13:45:1335 *@description Text in Headers View of the Network panel
36 */
37 chooseThisOptionIfTheResourceAnd:
38 'Choose this option if the resource and the document are served from the same site.',
39 /**
Wolfgang Beyerbcf4a832022-07-25 12:34:4740 *@description Text in Headers View of the Network panel for X-Client-Data HTTP headers
41 */
42 decoded: 'Decoded:',
43 /**
Wolfgang Beyer40530b682022-05-17 13:02:0144 *@description Text in Request Headers View of the Network panel
45 */
Wolfgang Beyer37fcd282022-06-29 09:34:1746 fromDiskCache: '(from disk cache)',
47 /**
48 *@description Text in Request Headers View of the Network panel
49 */
Wolfgang Beyer40530b682022-05-17 13:02:0150 fromMemoryCache: '(from memory cache)',
51 /**
52 *@description Text in Request Headers View of the Network panel
53 */
Wolfgang Beyer37fcd282022-06-29 09:34:1754 fromPrefetchCache: '(from prefetch cache)',
55 /**
56 *@description Text in Request Headers View of the Network panel
57 */
Wolfgang Beyer40530b682022-05-17 13:02:0158 fromServiceWorker: '(from `service worker`)',
59 /**
60 *@description Text in Request Headers View of the Network panel
61 */
62 fromSignedexchange: '(from signed-exchange)',
63 /**
64 *@description Text in Request Headers View of the Network panel
65 */
Wolfgang Beyer40530b682022-05-17 13:02:0166 fromWebBundle: '(from Web Bundle)',
67 /**
68 *@description Section header for a list of the main aspects of a http request
69 */
70 general: 'General',
71 /**
Wolfgang Beyer7f495ee2022-07-08 13:45:1372 *@description Text that is usually a hyperlink to more documentation
73 */
74 learnMore: 'Learn more',
75 /**
76 *@description Text for a link to the issues panel
77 */
78 learnMoreInTheIssuesTab: 'Learn more in the issues tab',
79 /**
Wolfgang Beyer235544c2022-05-24 08:07:4580 *@description Label for a checkbox to switch between raw and parsed headers
81 */
82 raw: 'Raw',
83 /**
Wolfgang Beyer7f495ee2022-07-08 13:45:1384 *@description Text in Headers View of the Network panel
85 */
86 onlyChooseThisOptionIfAn:
87 'Only choose this option if an arbitrary website including this resource does not impose a security risk.',
88 /**
Wolfgang Beyerd0ab3a62022-07-12 12:59:0289 *@description Message to explain lack of raw headers for a particular network request
90 */
91 provisionalHeadersAreShownDisableCache: 'Provisional headers are shown. Disable cache to see full headers.',
92 /**
93 *@description Tooltip to explain lack of raw headers for a particular network request
94 */
95 onlyProvisionalHeadersAre:
96 'Only provisional headers are available because this request was not sent over the network and instead was served from a local cache, which doesn’t store the original request headers. Disable cache to see full request headers.',
97 /**
98 *@description Message to explain lack of raw headers for a particular network request
99 */
100 provisionalHeadersAreShown: 'Provisional headers are shown.',
101 /**
Wolfgang Beyer235544c2022-05-24 08:07:45102 *@description Text in Request Headers View of the Network panel
103 */
Wolfgang Beyer37fcd282022-06-29 09:34:17104 referrerPolicy: 'Referrer Policy',
Wolfgang Beyer235544c2022-05-24 08:07:45105 /**
Wolfgang Beyer37fcd282022-06-29 09:34:17106 *@description Text in Network Log View Columns of the Network panel
Wolfgang Beyer40530b682022-05-17 13:02:01107 */
Wolfgang Beyer37fcd282022-06-29 09:34:17108 remoteAddress: 'Remote Address',
109 /**
110 *@description Text in Request Headers View of the Network panel
111 */
112 requestHeaders: 'Request Headers',
Wolfgang Beyer40530b682022-05-17 13:02:01113 /**
114 *@description The HTTP method of a request
115 */
116 requestMethod: 'Request Method',
117 /**
Wolfgang Beyer37fcd282022-06-29 09:34:17118 *@description The URL of a request
119 */
120 requestUrl: 'Request URL',
121 /**
Wolfgang Beyer235544c2022-05-24 08:07:45122 *@description A context menu item in the Network Log View Columns of the Network panel
123 */
124 responseHeaders: 'Response Headers',
125 /**
Wolfgang Beyerd1dd7962022-05-24 15:48:48126 *@description Text to show more content
127 */
128 showMore: 'Show more',
129 /**
Wolfgang Beyer40530b682022-05-17 13:02:01130 *@description HTTP response code
131 */
132 statusCode: 'Status Code',
Wolfgang Beyer7f495ee2022-07-08 13:45:13133 /**
134 *@description Text in Headers View of the Network panel
135 */
136 thisDocumentWasBlockedFrom:
137 'This document was blocked from loading in an `iframe` with a `sandbox` attribute because this document specified a cross-origin opener policy.',
138 /**
139 *@description Text in Headers View of the Network panel
140 */
141 toEmbedThisFrameInYourDocument:
142 'To embed this frame in your document, the response needs to enable the cross-origin embedder policy by specifying the following response header:',
143 /**
144 *@description Text in Headers View of the Network panel
145 */
146 toUseThisResourceFromADifferent:
147 'To use this resource from a different origin, the server needs to specify a cross-origin resource policy in the response headers:',
148 /**
149 *@description Text in Headers View of the Network panel
150 */
151 toUseThisResourceFromADifferentOrigin:
152 'To use this resource from a different origin, the server may relax the cross-origin resource policy response header:',
153 /**
154 *@description Text in Headers View of the Network panel
155 */
156 toUseThisResourceFromADifferentSite:
157 'To use this resource from a different site, the server may relax the cross-origin resource policy response header:',
Wolfgang Beyer40530b682022-05-17 13:02:01158};
159const str_ = i18n.i18n.registerUIStrings('panels/network/components/RequestHeadersView.ts', UIStrings);
160const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Wolfgang Beyer7f495ee2022-07-08 13:45:13161const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
Wolfgang Beyer40530b682022-05-17 13:02:01162
163export class RequestHeadersView extends UI.Widget.VBox {
164 readonly #headersView = new RequestHeadersComponent();
165 readonly #request: SDK.NetworkRequest.NetworkRequest;
166
167 constructor(request: SDK.NetworkRequest.NetworkRequest) {
168 super();
169 this.#request = request;
170 this.contentElement.appendChild(this.#headersView);
171 }
172
173 wasShown(): void {
174 this.#request.addEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this.#refreshHeadersView, this);
175 this.#request.addEventListener(SDK.NetworkRequest.Events.FinishedLoading, this.#refreshHeadersView, this);
Wolfgang Beyer87150062022-07-29 10:45:47176 this.#request.addEventListener(SDK.NetworkRequest.Events.RequestHeadersChanged, this.#refreshHeadersView, this);
177 this.#request.addEventListener(SDK.NetworkRequest.Events.ResponseHeadersChanged, this.#refreshHeadersView, this);
Wolfgang Beyer40530b682022-05-17 13:02:01178 this.#refreshHeadersView();
179 }
180
181 willHide(): void {
182 this.#request.removeEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this.#refreshHeadersView, this);
183 this.#request.removeEventListener(SDK.NetworkRequest.Events.FinishedLoading, this.#refreshHeadersView, this);
Wolfgang Beyer87150062022-07-29 10:45:47184 this.#request.removeEventListener(SDK.NetworkRequest.Events.RequestHeadersChanged, this.#refreshHeadersView, this);
185 this.#request.removeEventListener(SDK.NetworkRequest.Events.ResponseHeadersChanged, this.#refreshHeadersView, this);
Wolfgang Beyer40530b682022-05-17 13:02:01186 }
187
188 #refreshHeadersView(): void {
189 this.#headersView.data = {
190 request: this.#request,
191 };
192 }
193}
194
195export interface RequestHeadersComponentData {
196 request: SDK.NetworkRequest.NetworkRequest;
197}
198
199export class RequestHeadersComponent extends HTMLElement {
200 static readonly litTagName = LitHtml.literal`devtools-request-headers`;
201 readonly #shadow = this.attachShadow({mode: 'open'});
202 #request?: Readonly<SDK.NetworkRequest.NetworkRequest>;
Wolfgang Beyer235544c2022-05-24 08:07:45203 #showResponseHeadersText = false;
204 #showRequestHeadersText = false;
Wolfgang Beyerd1dd7962022-05-24 15:48:48205 #showResponseHeadersTextFull = false;
206 #showRequestHeadersTextFull = false;
Wolfgang Beyer40530b682022-05-17 13:02:01207
208 set data(data: RequestHeadersComponentData) {
209 this.#request = data.request;
210 this.#render();
211 }
212
213 connectedCallback(): void {
214 this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles];
215 }
216
217 #render(): void {
Wolfgang Beyer3e62bd72022-07-28 14:26:48218 if (!this.#request) {
219 return;
220 }
Wolfgang Beyer40530b682022-05-17 13:02:01221
222 // Disabled until https://crbug.com/1079231 is fixed.
223 // clang-format off
224 render(html`
225 ${this.#renderGeneralSection()}
Wolfgang Beyer235544c2022-05-24 08:07:45226 ${this.#renderResponseHeaders()}
227 ${this.#renderRequestHeaders()}
Wolfgang Beyer40530b682022-05-17 13:02:01228 `, this.#shadow, {host: this});
229 // clang-format on
230 }
231
Wolfgang Beyer3e62bd72022-07-28 14:26:48232 #renderResponseHeaders(): LitHtml.LitTemplate {
233 if (!this.#request) {
234 return LitHtml.nothing;
235 }
Wolfgang Beyer235544c2022-05-24 08:07:45236
Wolfgang Beyer7f495ee2022-07-08 13:45:13237 const headersWithIssues = [];
238 if (this.#request.wasBlocked()) {
239 const headerWithIssues =
240 BlockedReasonDetails.get((this.#request.blockedReason() as Protocol.Network.BlockedReason));
241 if (headerWithIssues) {
242 headersWithIssues.push(headerWithIssues);
243 }
244 }
245
246 function mergeHeadersWithIssues(
247 headers: SDK.NetworkRequest.NameValue[], headersWithIssues: HeaderDescriptor[]): HeaderDescriptor[] {
248 let i = 0, j = 0;
249 const result: HeaderDescriptor[] = [];
250 while (i < headers.length && j < headersWithIssues.length) {
251 if (headers[i].name < headersWithIssues[j].name) {
252 result.push({...headers[i++], headerNotSet: false});
253 } else if (headers[i].name > headersWithIssues[j].name) {
254 result.push({...headersWithIssues[j++], headerNotSet: true});
255 } else {
256 result.push({...headersWithIssues[j++], ...headers[i++], headerNotSet: false});
257 }
258 }
259 while (i < headers.length) {
260 result.push({...headers[i++], headerNotSet: false});
261 }
262 while (j < headersWithIssues.length) {
263 result.push({...headersWithIssues[j++], headerNotSet: true});
264 }
265 return result;
266 }
267
268 const mergedHeaders = mergeHeadersWithIssues(this.#request.sortedResponseHeaders.slice(), headersWithIssues);
269
Wolfgang Beyer21894d52022-07-26 12:12:44270 const blockedResponseCookies = this.#request.blockedResponseCookies();
271 const blockedCookieLineToReasons = new Map<string, Protocol.Network.SetCookieBlockedReason[]>(
272 blockedResponseCookies?.map(c => [c.cookieLine, c.blockedReasons]));
273 for (const header of mergedHeaders) {
274 if (header.name.toLowerCase() === 'set-cookie' && header.value) {
275 const matchingBlockedReasons = blockedCookieLineToReasons.get(header.value.toString());
276 if (matchingBlockedReasons) {
277 header.setCookieBlockedReasons = matchingBlockedReasons;
278 }
279 }
280 }
281
Wolfgang Beyer235544c2022-05-24 08:07:45282 const toggleShowRaw = (): void => {
283 this.#showResponseHeadersText = !this.#showResponseHeadersText;
284 this.#render();
285 };
286
287 // Disabled until https://crbug.com/1079231 is fixed.
288 // clang-format off
289 return html`
290 <${Category.litTagName}
291 @togglerawevent=${toggleShowRaw}
292 .data=${{
293 name: 'responseHeaders',
294 title: i18nString(UIStrings.responseHeaders),
295 headerCount: this.#request.sortedResponseHeaders.length,
296 checked: this.#request.responseHeadersText ? this.#showResponseHeadersText : undefined,
297 } as CategoryData}
298 aria-label=${i18nString(UIStrings.responseHeaders)}
299 >
Wolfgang Beyerd1dd7962022-05-24 15:48:48300 ${this.#showResponseHeadersText ?
301 this.#renderRawHeaders(this.#request.responseHeadersText, true) : html`
Wolfgang Beyer7f495ee2022-07-08 13:45:13302 ${mergedHeaders.map(header => this.#renderHeader(header))}
Wolfgang Beyer235544c2022-05-24 08:07:45303 `}
304 </${Category.litTagName}>
305 `;
Wolfgang Beyerbcf4a832022-07-25 12:34:47306 // clang-format on
Wolfgang Beyer235544c2022-05-24 08:07:45307 }
308
Wolfgang Beyer3e62bd72022-07-28 14:26:48309 #renderRequestHeaders(): LitHtml.LitTemplate {
310 if (!this.#request) {
311 return LitHtml.nothing;
312 }
Wolfgang Beyer235544c2022-05-24 08:07:45313
Wolfgang Beyer7f495ee2022-07-08 13:45:13314 const headers = this.#request.requestHeaders().slice();
315 headers.sort(function(a, b) {
316 return Platform.StringUtilities.compare(a.name.toLowerCase(), b.name.toLowerCase());
317 });
318 const requestHeadersText = this.#request.requestHeadersText();
319
Wolfgang Beyer235544c2022-05-24 08:07:45320 const toggleShowRaw = (): void => {
321 this.#showRequestHeadersText = !this.#showRequestHeadersText;
322 this.#render();
323 };
324
Wolfgang Beyer235544c2022-05-24 08:07:45325 // Disabled until https://crbug.com/1079231 is fixed.
326 // clang-format off
327 return html`
328 <${Category.litTagName}
329 @togglerawevent=${toggleShowRaw}
330 .data=${{
331 name: 'requestHeaders',
332 title: i18nString(UIStrings.requestHeaders),
333 headerCount: this.#request.requestHeaders().length,
334 checked: requestHeadersText? this.#showRequestHeadersText : undefined,
335 } as CategoryData}
336 aria-label=${i18nString(UIStrings.requestHeaders)}
337 >
Wolfgang Beyerd1dd7962022-05-24 15:48:48338 ${(this.#showRequestHeadersText && requestHeadersText) ?
339 this.#renderRawHeaders(requestHeadersText, false) : html`
Wolfgang Beyerd0ab3a62022-07-12 12:59:02340 ${this.#maybeRenderProvisionalHeadersWarning()}
Wolfgang Beyer7f495ee2022-07-08 13:45:13341 ${headers.map(header => this.#renderHeader({...header, headerNotSet: false}))}
Wolfgang Beyer235544c2022-05-24 08:07:45342 `}
343 </${Category.litTagName}>
344 `;
Wolfgang Beyerbcf4a832022-07-25 12:34:47345 // clang-format on
Wolfgang Beyer235544c2022-05-24 08:07:45346 }
347
Wolfgang Beyerd0ab3a62022-07-12 12:59:02348 #maybeRenderProvisionalHeadersWarning(): LitHtml.LitTemplate {
Wolfgang Beyer3e62bd72022-07-28 14:26:48349 if (!this.#request || this.#request.requestHeadersText() !== undefined) {
Wolfgang Beyerd0ab3a62022-07-12 12:59:02350 return LitHtml.nothing;
351 }
352
353 let cautionText;
354 let cautionTitle = '';
355 if (this.#request.cachedInMemory() || this.#request.cached()) {
356 cautionText = i18nString(UIStrings.provisionalHeadersAreShownDisableCache);
357 cautionTitle = i18nString(UIStrings.onlyProvisionalHeadersAre);
358 } else {
359 cautionText = i18nString(UIStrings.provisionalHeadersAreShown);
360 }
Wolfgang Beyerb7dadd32022-07-27 12:34:52361 // Disabled until https://crbug.com/1079231 is fixed.
362 // clang-format off
Wolfgang Beyerd0ab3a62022-07-12 12:59:02363 return html`
364 <div class="call-to-action">
365 <div class="call-to-action-body">
366 <div class="explanation" title=${cautionTitle}>
367 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
Wolfgang Beyerb7dadd32022-07-27 12:34:52368 iconName: 'clear-warning_icon',
369 width: '12px',
370 height: '12px',
Wolfgang Beyerd0ab3a62022-07-12 12:59:02371 } as IconButton.Icon.IconData}>
372 </${IconButton.Icon.Icon.litTagName}>
373 ${cautionText} <x-link href="https://developer.chrome.com/docs/devtools/network/reference/#provisional-headers" class="link">${i18nString(UIStrings.learnMore)}</x-link>
374 </div>
375 </div>
376 </div>
377 `;
Wolfgang Beyerb7dadd32022-07-27 12:34:52378 // clang-format on
Wolfgang Beyerd0ab3a62022-07-12 12:59:02379 }
380
Wolfgang Beyer7f495ee2022-07-08 13:45:13381 #renderHeader(header: HeaderDescriptor): LitHtml.TemplateResult {
Wolfgang Beyerbcf4a832022-07-25 12:34:47382 // Disabled until https://crbug.com/1079231 is fixed.
383 // clang-format off
Wolfgang Beyer7f495ee2022-07-08 13:45:13384 return html`
385 <div class="row">
Wolfgang Beyerbcf4a832022-07-25 12:34:47386 <div class="header-name">
Wolfgang Beyerfccc2762022-07-27 12:03:09387 ${header.headerNotSet ?
388 html`<div class="header-badge header-badge-text">${i18n.i18n.lockedString('not-set')}</div>` :
389 LitHtml.nothing
390 }${header.name}:
Wolfgang Beyerbcf4a832022-07-25 12:34:47391 </div>
Wolfgang Beyerd815e1f2022-07-28 16:01:29392 <div
393 class="header-value ${header.headerValueIncorrect ? 'header-warning' : ''}"
394 @copy=${():void => Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue)}
395 >
Wolfgang Beyer21894d52022-07-26 12:12:44396 ${header.value?.toString() || ''}
397 ${this.#maybeRenderHeaderValueSuffix(header)}
398 </div>
Wolfgang Beyer7f495ee2022-07-08 13:45:13399 </div>
Wolfgang Beyer21894d52022-07-26 12:12:44400 ${this.#maybeRenderBlockedDetails(header.blockedDetails)}
Wolfgang Beyer7f495ee2022-07-08 13:45:13401 `;
Wolfgang Beyerbcf4a832022-07-25 12:34:47402 // clang-format on
403 }
404
Wolfgang Beyer21894d52022-07-26 12:12:44405 #maybeRenderHeaderValueSuffix(header: HeaderDescriptor): LitHtml.LitTemplate {
Wolfgang Beyerbcf4a832022-07-25 12:34:47406 const headerId = header.name.toLowerCase();
Wolfgang Beyer21894d52022-07-26 12:12:44407
408 if (headerId === 'set-cookie' && header.setCookieBlockedReasons) {
409 const titleText =
410 header.setCookieBlockedReasons.map(SDK.NetworkRequest.setCookieBlockedReasonToUiString).join('\n');
411 // Disabled until https://crbug.com/1079231 is fixed.
412 // clang-format off
413 return html`
414 <${IconButton.Icon.Icon.litTagName} class="inline-icon" title=${titleText} .data=${{
Wolfgang Beyerb7dadd32022-07-27 12:34:52415 iconName: 'clear-warning_icon',
416 width: '12px',
417 height: '12px',
418 } as IconButton.Icon.IconData}>
Wolfgang Beyer21894d52022-07-26 12:12:44419 </${IconButton.Icon.Icon.litTagName}>
420 `;
421 // clang-format on
422 }
423
Wolfgang Beyerbcf4a832022-07-25 12:34:47424 if (headerId === 'x-client-data') {
425 const data = ClientVariations.parseClientVariations(header.value?.toString() || '');
426 const output = ClientVariations.formatClientVariations(
427 data, i18nString(UIStrings.activeClientExperimentVariation),
428 i18nString(UIStrings.activeClientExperimentVariationIds));
429 return html`
Wolfgang Beyer21894d52022-07-26 12:12:44430 <div>${i18nString(UIStrings.decoded)}</div>
431 <code>${output}</code>
Wolfgang Beyerbcf4a832022-07-25 12:34:47432 `;
433 }
434
Wolfgang Beyer21894d52022-07-26 12:12:44435 return LitHtml.nothing;
Wolfgang Beyer7f495ee2022-07-08 13:45:13436 }
437
Wolfgang Beyer21894d52022-07-26 12:12:44438 #maybeRenderBlockedDetails(blockedDetails?: BlockedDetailsDescriptor): LitHtml.LitTemplate {
439 if (!blockedDetails) {
Wolfgang Beyer7f495ee2022-07-08 13:45:13440 return LitHtml.nothing;
441 }
Wolfgang Beyer21894d52022-07-26 12:12:44442 // Disabled until https://crbug.com/1079231 is fixed.
443 // clang-format off
Wolfgang Beyer7f495ee2022-07-08 13:45:13444 return html`
Wolfgang Beyerd0ab3a62022-07-12 12:59:02445 <div class="call-to-action">
446 <div class="call-to-action-body">
Wolfgang Beyer21894d52022-07-26 12:12:44447 <div class="explanation">${blockedDetails.explanation()}</div>
448 ${blockedDetails.examples.map(example => html`
Wolfgang Beyerd0ab3a62022-07-12 12:59:02449 <div class="example">
450 <code>${example.codeSnippet}</code>
451 ${example.comment ? html`
452 <span class="comment">${example.comment()}</span>
453 ` : ''}
454 </div>
455 `)}
Wolfgang Beyer21894d52022-07-26 12:12:44456 ${this.#maybeRenderBlockedDetailsLink(blockedDetails)}
Wolfgang Beyer7f495ee2022-07-08 13:45:13457 </div>
458 </div>
459 `;
Wolfgang Beyer21894d52022-07-26 12:12:44460 // clang-format on
Wolfgang Beyer7f495ee2022-07-08 13:45:13461 }
462
Wolfgang Beyer21894d52022-07-26 12:12:44463 #maybeRenderBlockedDetailsLink(blockedDetails?: BlockedDetailsDescriptor): LitHtml.LitTemplate {
Wolfgang Beyer7f495ee2022-07-08 13:45:13464 if (this.#request && IssuesManager.RelatedIssue.hasIssueOfCategory(this.#request, IssuesManager.Issue.IssueCategory.CrossOriginEmbedderPolicy)) {
465 const followLink = (): void => {
466 Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.LearnMoreLinkCOEP);
467 if (this.#request) {
468 void IssuesManager.RelatedIssue.reveal(
469 this.#request, IssuesManager.Issue.IssueCategory.CrossOriginEmbedderPolicy);
470 }
471 };
472 return html`
473 <div class="devtools-link" @click=${followLink}>
474 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
475 iconName: 'issue-exclamation-icon',
476 color: 'var(--issue-color-yellow)',
477 width: '16px',
478 height: '16px',
479 } as IconButton.Icon.IconData}>
480 </${IconButton.Icon.Icon.litTagName}>
481 ${i18nString(UIStrings.learnMoreInTheIssuesTab)}
482 </div>
483 `;
484 }
Wolfgang Beyer21894d52022-07-26 12:12:44485 if (blockedDetails?.link) {
486 // Disabled until https://crbug.com/1079231 is fixed.
487 // clang-format off
Wolfgang Beyer7f495ee2022-07-08 13:45:13488 return html`
Wolfgang Beyer21894d52022-07-26 12:12:44489 <x-link href=${blockedDetails.link.url} class="link">
Wolfgang Beyer7f495ee2022-07-08 13:45:13490 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
491 iconName: 'link_icon',
492 color: 'var(--color-link)',
493 width: '16px',
494 height: '16px',
495 } as IconButton.Icon.IconData}>
496 </${IconButton.Icon.Icon.litTagName}
497 >${i18nString(UIStrings.learnMore)}
498 </x-link>
499 `;
Wolfgang Beyer21894d52022-07-26 12:12:44500 // clang-format on
Wolfgang Beyer7f495ee2022-07-08 13:45:13501 }
502 return LitHtml.nothing;
503 }
504
Wolfgang Beyerd1dd7962022-05-24 15:48:48505 #renderRawHeaders(rawHeadersText: string, forResponseHeaders: boolean): LitHtml.TemplateResult {
506 const trimmed = rawHeadersText.trim();
507 const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
508 const isShortened = !showFull && trimmed.length > RAW_HEADER_CUTOFF;
509
510 const showMore = ():void => {
511 if (forResponseHeaders) {
512 this.#showResponseHeadersTextFull = true;
513 } else {
514 this.#showRequestHeadersTextFull = true;
515 }
516 this.#render();
517 };
518
519 const onContextMenuOpen = (event: Event): void => {
520 const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
521 if (!showFull) {
522 const contextMenu = new UI.ContextMenu.ContextMenu(event);
523 const section = contextMenu.newSection();
524 section.appendItem(i18nString(UIStrings.showMore), showMore);
525 void contextMenu.show();
526 }
527 };
528
529 const addContextMenuListener = (el: Element):void => {
530 if (isShortened) {
531 el.addEventListener('contextmenu', onContextMenuOpen);
532 }
533 };
534
535 return html`
536 <div class="row raw-headers-row" on-render=${ComponentHelpers.Directives.nodeRenderedCallback(addContextMenuListener)}>
537 <div class="raw-headers">${isShortened ? trimmed.substring(0, RAW_HEADER_CUTOFF) : trimmed}</div>
538 ${isShortened ? html`
539 <${Buttons.Button.Button.litTagName}
540 .size=${Buttons.Button.Size.SMALL}
541 .variant=${Buttons.Button.Variant.SECONDARY}
542 @click=${showMore}
543 >${i18nString(UIStrings.showMore)}</${Buttons.Button.Button.litTagName}>
544 ` : LitHtml.nothing}
545 </div>
546 `;
547 }
548
Wolfgang Beyer3e62bd72022-07-28 14:26:48549 #renderGeneralSection(): LitHtml.LitTemplate {
550 if (!this.#request) {
551 return LitHtml.nothing;
552 }
Wolfgang Beyer40530b682022-05-17 13:02:01553
554 let coloredCircleClassName = 'red-circle';
555 if (this.#request.statusCode < 300 || this.#request.statusCode === 304) {
556 coloredCircleClassName = 'green-circle';
557 } else if (this.#request.statusCode < 400) {
558 coloredCircleClassName = 'yellow-circle';
559 }
560
561 let statusText = this.#request.statusCode + ' ' + this.#request.statusText;
562 let statusTextHasComment = false;
563 if (this.#request.cachedInMemory()) {
564 statusText += ' ' + i18nString(UIStrings.fromMemoryCache);
565 statusTextHasComment = true;
566 } else if (this.#request.fetchedViaServiceWorker) {
567 statusText += ' ' + i18nString(UIStrings.fromServiceWorker);
568 statusTextHasComment = true;
569 } else if (this.#request.redirectSourceSignedExchangeInfoHasNoErrors()) {
570 statusText += ' ' + i18nString(UIStrings.fromSignedexchange);
571 statusTextHasComment = true;
572 } else if (this.#request.webBundleInnerRequestInfo()) {
573 statusText += ' ' + i18nString(UIStrings.fromWebBundle);
574 statusTextHasComment = true;
575 } else if (this.#request.fromPrefetchCache()) {
576 statusText += ' ' + i18nString(UIStrings.fromPrefetchCache);
577 statusTextHasComment = true;
578 } else if (this.#request.cached()) {
579 statusText += ' ' + i18nString(UIStrings.fromDiskCache);
580 statusTextHasComment = true;
581 }
582
583 // Disabled until https://crbug.com/1079231 is fixed.
584 // clang-format off
585 return html`
Wolfgang Beyer235544c2022-05-24 08:07:45586 <${Category.litTagName}
587 .data=${{name: 'general', title: i18nString(UIStrings.general)} as CategoryData}
588 aria-label=${i18nString(UIStrings.general)}
589 >
Wolfgang Beyer40530b682022-05-17 13:02:01590 <div class="row">
591 <div class="header-name">${i18nString(UIStrings.requestUrl)}:</div>
Wolfgang Beyerd815e1f2022-07-28 16:01:29592 <div
593 class="header-value"
594 @copy=${():void => Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue)}
595 >${this.#request.url()}</div>
Wolfgang Beyer40530b682022-05-17 13:02:01596 </div>
597 ${this.#request.statusCode? html`
598 <div class="row">
599 <div class="header-name">${i18nString(UIStrings.requestMethod)}:</div>
Wolfgang Beyerd815e1f2022-07-28 16:01:29600 <div
601 class="header-value"
602 @copy=${():void => Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue)}
603 >${this.#request.requestMethod}</div>
Wolfgang Beyer40530b682022-05-17 13:02:01604 </div>
605 <div class="row">
606 <div class="header-name">${i18nString(UIStrings.statusCode)}:</div>
Wolfgang Beyerd815e1f2022-07-28 16:01:29607 <div
608 class="header-value ${coloredCircleClassName} ${statusTextHasComment ? 'status-with-comment' : ''}"
609 @copy=${():void => Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue)}
610 >${statusText}</div>
Wolfgang Beyer40530b682022-05-17 13:02:01611 </div>
612 ` : ''}
613 ${this.#request.remoteAddress()? html`
614 <div class="row">
615 <div class="header-name">${i18nString(UIStrings.remoteAddress)}:</div>
Wolfgang Beyerd815e1f2022-07-28 16:01:29616 <div
617 class="header-value"
618 @copy=${():void => Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue)}
619 >${this.#request.remoteAddress()}</div>
Wolfgang Beyer40530b682022-05-17 13:02:01620 </div>
621 ` : ''}
622 ${this.#request.referrerPolicy()? html`
623 <div class="row">
624 <div class="header-name">${i18nString(UIStrings.referrerPolicy)}:</div>
Wolfgang Beyerd815e1f2022-07-28 16:01:29625 <div
626 class="header-value"
627 @copy=${():void => Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue)}
628 >${this.#request.referrerPolicy()}</div>
Wolfgang Beyer40530b682022-05-17 13:02:01629 </div>
630 ` : ''}
631 </${Category.litTagName}>
632 `;
633 // clang-format on
634 }
635}
636
Wolfgang Beyer235544c2022-05-24 08:07:45637export class ToggleRawHeadersEvent extends Event {
638 static readonly eventName = 'togglerawevent';
639
640 constructor() {
641 super(ToggleRawHeadersEvent.eventName, {});
642 }
643}
644
Wolfgang Beyer40530b682022-05-17 13:02:01645export interface CategoryData {
646 name: string;
647 title: Common.UIString.LocalizedString;
Wolfgang Beyer235544c2022-05-24 08:07:45648 headerCount?: number;
649 checked?: boolean;
Wolfgang Beyer40530b682022-05-17 13:02:01650}
651
652export class Category extends HTMLElement {
653 static readonly litTagName = LitHtml.literal`devtools-request-headers-category`;
654 readonly #shadow = this.attachShadow({mode: 'open'});
655 #expandedSetting?: Common.Settings.Setting<boolean>;
656 #title: Common.UIString.LocalizedString = Common.UIString.LocalizedEmptyString;
Wolfgang Beyer235544c2022-05-24 08:07:45657 #headerCount?: number = undefined;
658 #checked: boolean|undefined = undefined;
Wolfgang Beyer40530b682022-05-17 13:02:01659
660 connectedCallback(): void {
Jack Franklin1f19d522022-07-06 14:00:18661 this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles, Input.checkboxStyles];
Wolfgang Beyer40530b682022-05-17 13:02:01662 }
663
664 set data(data: CategoryData) {
665 this.#title = data.title;
666 this.#expandedSetting =
667 Common.Settings.Settings.instance().createSetting('request-info-' + data.name + '-category-expanded', true);
Wolfgang Beyer235544c2022-05-24 08:07:45668 this.#headerCount = data.headerCount;
669 this.#checked = data.checked;
Wolfgang Beyer40530b682022-05-17 13:02:01670 this.#render();
671 }
672
Wolfgang Beyer235544c2022-05-24 08:07:45673 #onCheckboxToggle(): void {
674 this.dispatchEvent(new ToggleRawHeadersEvent());
675 }
676
Wolfgang Beyer40530b682022-05-17 13:02:01677 #render(): void {
Wolfgang Beyer235544c2022-05-24 08:07:45678 const isOpen = this.#expandedSetting ? this.#expandedSetting.get() : true;
Wolfgang Beyer40530b682022-05-17 13:02:01679 // Disabled until https://crbug.com/1079231 is fixed.
680 // clang-format off
681 render(html`
Wolfgang Beyer235544c2022-05-24 08:07:45682 <details ?open=${isOpen} @toggle=${this.#onToggle}>
683 <summary class="header" @keydown=${this.#onSummaryKeyDown}>
684 ${this.#title}${this.#headerCount ?
685 html`<span class="header-count"> (${this.#headerCount})</span>` :
686 LitHtml.nothing
687 }
688 ${this.#checked !== undefined ? html`
689 <span class="raw-checkbox-container">
690 <label>
691 <input type="checkbox" .checked=${this.#checked} @change=${this.#onCheckboxToggle} />
692 ${i18nString(UIStrings.raw)}
693 </label>
694 </span>
695 ` : LitHtml.nothing}
696 </summary>
Wolfgang Beyer40530b682022-05-17 13:02:01697 <slot></slot>
698 </details>
699 `, this.#shadow, {host: this});
700 // clang-format on
701 }
702
703 #onSummaryKeyDown(event: KeyboardEvent): void {
704 if (!event.target) {
705 return;
706 }
707 const summaryElement = event.target as HTMLElement;
708 const detailsElement = summaryElement.parentElement as HTMLDetailsElement;
709 if (!detailsElement) {
710 throw new Error('<details> element is not found for a <summary> element');
711 }
712 switch (event.key) {
713 case 'ArrowLeft':
714 detailsElement.open = false;
715 break;
716 case 'ArrowRight':
717 detailsElement.open = true;
718 break;
719 }
720 }
721
722 #onToggle(event: Event): void {
723 this.#expandedSetting?.set((event.target as HTMLDetailsElement).open);
724 }
725}
726
727ComponentHelpers.CustomElements.defineComponent('devtools-request-headers', RequestHeadersComponent);
728ComponentHelpers.CustomElements.defineComponent('devtools-request-headers-category', Category);
729
730declare global {
731 // eslint-disable-next-line @typescript-eslint/no-unused-vars
732 interface HTMLElementTagNameMap {
733 'devtools-request-headers': RequestHeadersComponent;
734 'devtools-request-headers-category': Category;
735 }
736}
Wolfgang Beyer7f495ee2022-07-08 13:45:13737
Wolfgang Beyer21894d52022-07-26 12:12:44738interface BlockedDetailsDescriptor {
Wolfgang Beyer7f495ee2022-07-08 13:45:13739 explanation: () => string;
740 examples: Array<{
741 codeSnippet: string,
742 comment?: (() => string),
743 }>;
744 link: {
745 url: string,
746 }|null;
747}
748
749interface HeaderDescriptor {
750 name: string;
751 value: Object|null;
752 headerValueIncorrect?: boolean|null;
Wolfgang Beyer21894d52022-07-26 12:12:44753 blockedDetails?: BlockedDetailsDescriptor;
Wolfgang Beyer7f495ee2022-07-08 13:45:13754 headerNotSet: boolean|null;
Wolfgang Beyer21894d52022-07-26 12:12:44755 setCookieBlockedReasons?: Protocol.Network.SetCookieBlockedReason[];
Wolfgang Beyer7f495ee2022-07-08 13:45:13756}
757
758const BlockedReasonDetails = new Map<Protocol.Network.BlockedReason, HeaderDescriptor>([
759 [
760 Protocol.Network.BlockedReason.CoepFrameResourceNeedsCoepHeader,
761 {
762 name: 'cross-origin-embedder-policy',
763 value: null,
764 headerValueIncorrect: null,
Wolfgang Beyer21894d52022-07-26 12:12:44765 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13766 explanation: i18nLazyString(UIStrings.toEmbedThisFrameInYourDocument),
767 examples: [{codeSnippet: 'Cross-Origin-Embedder-Policy: require-corp', comment: undefined}],
768 link: {url: 'https://web.dev/coop-coep/'},
769 },
770 headerNotSet: null,
771 },
772 ],
773 [
774 Protocol.Network.BlockedReason.CorpNotSameOriginAfterDefaultedToSameOriginByCoep,
775 {
776 name: 'cross-origin-resource-policy',
777 value: null,
778 headerValueIncorrect: null,
Wolfgang Beyer21894d52022-07-26 12:12:44779 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13780 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferent),
781 examples: [
782 {
783 codeSnippet: 'Cross-Origin-Resource-Policy: same-site',
784 comment: i18nLazyString(UIStrings.chooseThisOptionIfTheResourceAnd),
785 },
786 {
787 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
788 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
789 },
790 ],
791 link: {url: 'https://web.dev/coop-coep/'},
792 },
793 headerNotSet: null,
794 },
795 ],
796 [
797 Protocol.Network.BlockedReason.CoopSandboxedIframeCannotNavigateToCoopPage,
798 {
799 name: 'cross-origin-opener-policy',
800 value: null,
801 headerValueIncorrect: false,
Wolfgang Beyer21894d52022-07-26 12:12:44802 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13803 explanation: i18nLazyString(UIStrings.thisDocumentWasBlockedFrom),
804 examples: [],
805 link: {url: 'https://web.dev/coop-coep/'},
806 },
807 headerNotSet: null,
808 },
809 ],
810 [
811 Protocol.Network.BlockedReason.CorpNotSameSite,
812 {
813 name: 'cross-origin-resource-policy',
814 value: null,
815 headerValueIncorrect: true,
Wolfgang Beyer21894d52022-07-26 12:12:44816 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13817 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferentSite),
818 examples: [
819 {
820 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
821 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
822 },
823 ],
824 link: null,
825 },
826 headerNotSet: null,
827 },
828 ],
829 [
830 Protocol.Network.BlockedReason.CorpNotSameOrigin,
831 {
832 name: 'cross-origin-resource-policy',
833 value: null,
834 headerValueIncorrect: true,
Wolfgang Beyer21894d52022-07-26 12:12:44835 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13836 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferentOrigin),
837 examples: [
838 {
839 codeSnippet: 'Cross-Origin-Resource-Policy: same-site',
840 comment: i18nLazyString(UIStrings.chooseThisOptionIfTheResourceAnd),
841 },
842 {
843 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
844 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
845 },
846 ],
847 link: null,
848 },
849 headerNotSet: null,
850 },
851 ],
852]);