blob: c6d9852a8538b63699cba5c84ed99051022d74f8 [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 {assertNotNullOrUndefined} from '../../../core/platform/platform.js';
10import * as SDK from '../../../core/sdk/sdk.js';
Wolfgang Beyer7f495ee2022-07-08 13:45:1311import * as Protocol from '../../../generated/protocol.js';
12import * as IssuesManager from '../../../models/issues_manager/issues_manager.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 Beyer7f495ee2022-07-08 13:45:1327 *@description Text in Headers View of the Network panel
28 */
29 chooseThisOptionIfTheResourceAnd:
30 'Choose this option if the resource and the document are served from the same site.',
31 /**
Wolfgang Beyer40530b682022-05-17 13:02:0132 *@description Text in Request Headers View of the Network panel
33 */
Wolfgang Beyer37fcd282022-06-29 09:34:1734 fromDiskCache: '(from disk cache)',
35 /**
36 *@description Text in Request Headers View of the Network panel
37 */
Wolfgang Beyer40530b682022-05-17 13:02:0138 fromMemoryCache: '(from memory cache)',
39 /**
40 *@description Text in Request Headers View of the Network panel
41 */
Wolfgang Beyer37fcd282022-06-29 09:34:1742 fromPrefetchCache: '(from prefetch cache)',
43 /**
44 *@description Text in Request Headers View of the Network panel
45 */
Wolfgang Beyer40530b682022-05-17 13:02:0146 fromServiceWorker: '(from `service worker`)',
47 /**
48 *@description Text in Request Headers View of the Network panel
49 */
50 fromSignedexchange: '(from signed-exchange)',
51 /**
52 *@description Text in Request Headers View of the Network panel
53 */
Wolfgang Beyer40530b682022-05-17 13:02:0154 fromWebBundle: '(from Web Bundle)',
55 /**
56 *@description Section header for a list of the main aspects of a http request
57 */
58 general: 'General',
59 /**
Wolfgang Beyer7f495ee2022-07-08 13:45:1360 *@description Text that is usually a hyperlink to more documentation
61 */
62 learnMore: 'Learn more',
63 /**
64 *@description Text for a link to the issues panel
65 */
66 learnMoreInTheIssuesTab: 'Learn more in the issues tab',
67 /**
Wolfgang Beyer235544c2022-05-24 08:07:4568 *@description Label for a checkbox to switch between raw and parsed headers
69 */
70 raw: 'Raw',
71 /**
Wolfgang Beyer7f495ee2022-07-08 13:45:1372 *@description Text in Headers View of the Network panel
73 */
74 onlyChooseThisOptionIfAn:
75 'Only choose this option if an arbitrary website including this resource does not impose a security risk.',
76 /**
Wolfgang Beyerd0ab3a62022-07-12 12:59:0277 *@description Message to explain lack of raw headers for a particular network request
78 */
79 provisionalHeadersAreShownDisableCache: 'Provisional headers are shown. Disable cache to see full headers.',
80 /**
81 *@description Tooltip to explain lack of raw headers for a particular network request
82 */
83 onlyProvisionalHeadersAre:
84 '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.',
85 /**
86 *@description Message to explain lack of raw headers for a particular network request
87 */
88 provisionalHeadersAreShown: 'Provisional headers are shown.',
89 /**
Wolfgang Beyer235544c2022-05-24 08:07:4590 *@description Text in Request Headers View of the Network panel
91 */
Wolfgang Beyer37fcd282022-06-29 09:34:1792 referrerPolicy: 'Referrer Policy',
Wolfgang Beyer235544c2022-05-24 08:07:4593 /**
Wolfgang Beyer37fcd282022-06-29 09:34:1794 *@description Text in Network Log View Columns of the Network panel
Wolfgang Beyer40530b682022-05-17 13:02:0195 */
Wolfgang Beyer37fcd282022-06-29 09:34:1796 remoteAddress: 'Remote Address',
97 /**
98 *@description Text in Request Headers View of the Network panel
99 */
100 requestHeaders: 'Request Headers',
Wolfgang Beyer40530b682022-05-17 13:02:01101 /**
102 *@description The HTTP method of a request
103 */
104 requestMethod: 'Request Method',
105 /**
Wolfgang Beyer37fcd282022-06-29 09:34:17106 *@description The URL of a request
107 */
108 requestUrl: 'Request URL',
109 /**
Wolfgang Beyer235544c2022-05-24 08:07:45110 *@description A context menu item in the Network Log View Columns of the Network panel
111 */
112 responseHeaders: 'Response Headers',
113 /**
Wolfgang Beyerd1dd7962022-05-24 15:48:48114 *@description Text to show more content
115 */
116 showMore: 'Show more',
117 /**
Wolfgang Beyer40530b682022-05-17 13:02:01118 *@description HTTP response code
119 */
120 statusCode: 'Status Code',
Wolfgang Beyer7f495ee2022-07-08 13:45:13121 /**
122 *@description Text in Headers View of the Network panel
123 */
124 thisDocumentWasBlockedFrom:
125 'This document was blocked from loading in an `iframe` with a `sandbox` attribute because this document specified a cross-origin opener policy.',
126 /**
127 *@description Text in Headers View of the Network panel
128 */
129 toEmbedThisFrameInYourDocument:
130 'To embed this frame in your document, the response needs to enable the cross-origin embedder policy by specifying the following response header:',
131 /**
132 *@description Text in Headers View of the Network panel
133 */
134 toUseThisResourceFromADifferent:
135 'To use this resource from a different origin, the server needs to specify a cross-origin resource policy in the response headers:',
136 /**
137 *@description Text in Headers View of the Network panel
138 */
139 toUseThisResourceFromADifferentOrigin:
140 'To use this resource from a different origin, the server may relax the cross-origin resource policy response header:',
141 /**
142 *@description Text in Headers View of the Network panel
143 */
144 toUseThisResourceFromADifferentSite:
145 '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:01146};
147const str_ = i18n.i18n.registerUIStrings('panels/network/components/RequestHeadersView.ts', UIStrings);
148const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Wolfgang Beyer7f495ee2022-07-08 13:45:13149const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
Wolfgang Beyer40530b682022-05-17 13:02:01150
151export class RequestHeadersView extends UI.Widget.VBox {
152 readonly #headersView = new RequestHeadersComponent();
153 readonly #request: SDK.NetworkRequest.NetworkRequest;
154
155 constructor(request: SDK.NetworkRequest.NetworkRequest) {
156 super();
157 this.#request = request;
158 this.contentElement.appendChild(this.#headersView);
159 }
160
161 wasShown(): void {
162 this.#request.addEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this.#refreshHeadersView, this);
163 this.#request.addEventListener(SDK.NetworkRequest.Events.FinishedLoading, this.#refreshHeadersView, this);
164 this.#refreshHeadersView();
165 }
166
167 willHide(): void {
168 this.#request.removeEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this.#refreshHeadersView, this);
169 this.#request.removeEventListener(SDK.NetworkRequest.Events.FinishedLoading, this.#refreshHeadersView, this);
170 }
171
172 #refreshHeadersView(): void {
173 this.#headersView.data = {
174 request: this.#request,
175 };
176 }
177}
178
179export interface RequestHeadersComponentData {
180 request: SDK.NetworkRequest.NetworkRequest;
181}
182
183export class RequestHeadersComponent extends HTMLElement {
184 static readonly litTagName = LitHtml.literal`devtools-request-headers`;
185 readonly #shadow = this.attachShadow({mode: 'open'});
186 #request?: Readonly<SDK.NetworkRequest.NetworkRequest>;
Wolfgang Beyer235544c2022-05-24 08:07:45187 #showResponseHeadersText = false;
188 #showRequestHeadersText = false;
Wolfgang Beyerd1dd7962022-05-24 15:48:48189 #showResponseHeadersTextFull = false;
190 #showRequestHeadersTextFull = false;
Wolfgang Beyer40530b682022-05-17 13:02:01191
192 set data(data: RequestHeadersComponentData) {
193 this.#request = data.request;
194 this.#render();
195 }
196
197 connectedCallback(): void {
198 this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles];
199 }
200
201 #render(): void {
202 assertNotNullOrUndefined(this.#request);
203
204 // Disabled until https://crbug.com/1079231 is fixed.
205 // clang-format off
206 render(html`
207 ${this.#renderGeneralSection()}
Wolfgang Beyer235544c2022-05-24 08:07:45208 ${this.#renderResponseHeaders()}
209 ${this.#renderRequestHeaders()}
Wolfgang Beyer40530b682022-05-17 13:02:01210 `, this.#shadow, {host: this});
211 // clang-format on
212 }
213
Wolfgang Beyer235544c2022-05-24 08:07:45214 #renderResponseHeaders(): LitHtml.TemplateResult {
215 assertNotNullOrUndefined(this.#request);
216
Wolfgang Beyer7f495ee2022-07-08 13:45:13217 const headersWithIssues = [];
218 if (this.#request.wasBlocked()) {
219 const headerWithIssues =
220 BlockedReasonDetails.get((this.#request.blockedReason() as Protocol.Network.BlockedReason));
221 if (headerWithIssues) {
222 headersWithIssues.push(headerWithIssues);
223 }
224 }
225
226 function mergeHeadersWithIssues(
227 headers: SDK.NetworkRequest.NameValue[], headersWithIssues: HeaderDescriptor[]): HeaderDescriptor[] {
228 let i = 0, j = 0;
229 const result: HeaderDescriptor[] = [];
230 while (i < headers.length && j < headersWithIssues.length) {
231 if (headers[i].name < headersWithIssues[j].name) {
232 result.push({...headers[i++], headerNotSet: false});
233 } else if (headers[i].name > headersWithIssues[j].name) {
234 result.push({...headersWithIssues[j++], headerNotSet: true});
235 } else {
236 result.push({...headersWithIssues[j++], ...headers[i++], headerNotSet: false});
237 }
238 }
239 while (i < headers.length) {
240 result.push({...headers[i++], headerNotSet: false});
241 }
242 while (j < headersWithIssues.length) {
243 result.push({...headersWithIssues[j++], headerNotSet: true});
244 }
245 return result;
246 }
247
248 const mergedHeaders = mergeHeadersWithIssues(this.#request.sortedResponseHeaders.slice(), headersWithIssues);
249
Wolfgang Beyer235544c2022-05-24 08:07:45250 const toggleShowRaw = (): void => {
251 this.#showResponseHeadersText = !this.#showResponseHeadersText;
252 this.#render();
253 };
254
255 // Disabled until https://crbug.com/1079231 is fixed.
256 // clang-format off
257 return html`
258 <${Category.litTagName}
259 @togglerawevent=${toggleShowRaw}
260 .data=${{
261 name: 'responseHeaders',
262 title: i18nString(UIStrings.responseHeaders),
263 headerCount: this.#request.sortedResponseHeaders.length,
264 checked: this.#request.responseHeadersText ? this.#showResponseHeadersText : undefined,
265 } as CategoryData}
266 aria-label=${i18nString(UIStrings.responseHeaders)}
267 >
Wolfgang Beyerd1dd7962022-05-24 15:48:48268 ${this.#showResponseHeadersText ?
269 this.#renderRawHeaders(this.#request.responseHeadersText, true) : html`
Wolfgang Beyer7f495ee2022-07-08 13:45:13270 ${mergedHeaders.map(header => this.#renderHeader(header))}
Wolfgang Beyer235544c2022-05-24 08:07:45271 `}
272 </${Category.litTagName}>
273 `;
274 }
275
276 #renderRequestHeaders(): LitHtml.TemplateResult {
277 assertNotNullOrUndefined(this.#request);
278
Wolfgang Beyer7f495ee2022-07-08 13:45:13279 const headers = this.#request.requestHeaders().slice();
280 headers.sort(function(a, b) {
281 return Platform.StringUtilities.compare(a.name.toLowerCase(), b.name.toLowerCase());
282 });
283 const requestHeadersText = this.#request.requestHeadersText();
284
Wolfgang Beyer235544c2022-05-24 08:07:45285 const toggleShowRaw = (): void => {
286 this.#showRequestHeadersText = !this.#showRequestHeadersText;
287 this.#render();
288 };
289
Wolfgang Beyer235544c2022-05-24 08:07:45290 // Disabled until https://crbug.com/1079231 is fixed.
291 // clang-format off
292 return html`
293 <${Category.litTagName}
294 @togglerawevent=${toggleShowRaw}
295 .data=${{
296 name: 'requestHeaders',
297 title: i18nString(UIStrings.requestHeaders),
298 headerCount: this.#request.requestHeaders().length,
299 checked: requestHeadersText? this.#showRequestHeadersText : undefined,
300 } as CategoryData}
301 aria-label=${i18nString(UIStrings.requestHeaders)}
302 >
Wolfgang Beyerd1dd7962022-05-24 15:48:48303 ${(this.#showRequestHeadersText && requestHeadersText) ?
304 this.#renderRawHeaders(requestHeadersText, false) : html`
Wolfgang Beyerd0ab3a62022-07-12 12:59:02305 ${this.#maybeRenderProvisionalHeadersWarning()}
Wolfgang Beyer7f495ee2022-07-08 13:45:13306 ${headers.map(header => this.#renderHeader({...header, headerNotSet: false}))}
Wolfgang Beyer235544c2022-05-24 08:07:45307 `}
308 </${Category.litTagName}>
309 `;
310 }
311
Wolfgang Beyerd0ab3a62022-07-12 12:59:02312 #maybeRenderProvisionalHeadersWarning(): LitHtml.LitTemplate {
313 assertNotNullOrUndefined(this.#request);
314 if (this.#request.requestHeadersText() !== undefined) {
315 return LitHtml.nothing;
316 }
317
318 let cautionText;
319 let cautionTitle = '';
320 if (this.#request.cachedInMemory() || this.#request.cached()) {
321 cautionText = i18nString(UIStrings.provisionalHeadersAreShownDisableCache);
322 cautionTitle = i18nString(UIStrings.onlyProvisionalHeadersAre);
323 } else {
324 cautionText = i18nString(UIStrings.provisionalHeadersAreShown);
325 }
326 return html`
327 <div class="call-to-action">
328 <div class="call-to-action-body">
329 <div class="explanation" title=${cautionTitle}>
330 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
331 iconName: 'warning_icon',
332 width: '12px',
333 height: '12px',
334 } as IconButton.Icon.IconData}>
335 </${IconButton.Icon.Icon.litTagName}>
336 ${cautionText} <x-link href="https://developer.chrome.com/docs/devtools/network/reference/#provisional-headers" class="link">${i18nString(UIStrings.learnMore)}</x-link>
337 </div>
338 </div>
339 </div>
340 `;
341 }
342
Wolfgang Beyer7f495ee2022-07-08 13:45:13343 #renderHeader(header: HeaderDescriptor): LitHtml.TemplateResult {
344 return html`
345 <div class="row">
346 <div class="header-name">${header.headerNotSet ? html`<div class="header-badge header-badge-text">not-set</div>` : ''}${header.name}:</div>
347 <div class="header-value ${header.headerValueIncorrect ? 'header-warning' : ''}">${header.value?.toString()||''}</div>
348 </div>
349 ${this.#maybeRenderHeaderDetails(header.details)}
350 `;
351 }
352
353 #maybeRenderHeaderDetails(headerDetails?: HeaderDetailsDescriptor): LitHtml.LitTemplate {
354 if (!headerDetails) {
355 return LitHtml.nothing;
356 }
357 return html`
Wolfgang Beyerd0ab3a62022-07-12 12:59:02358 <div class="call-to-action">
359 <div class="call-to-action-body">
360 <div class="explanation">${headerDetails.explanation()}</div>
361 ${headerDetails.examples.map(example => html`
362 <div class="example">
363 <code>${example.codeSnippet}</code>
364 ${example.comment ? html`
365 <span class="comment">${example.comment()}</span>
366 ` : ''}
367 </div>
368 `)}
369 ${this.#maybeRenderHeaderDetailsLink(headerDetails)}
Wolfgang Beyer7f495ee2022-07-08 13:45:13370 </div>
371 </div>
372 `;
373 }
374
375 #maybeRenderHeaderDetailsLink(headerDetails?: HeaderDetailsDescriptor): LitHtml.LitTemplate {
376 if (this.#request && IssuesManager.RelatedIssue.hasIssueOfCategory(this.#request, IssuesManager.Issue.IssueCategory.CrossOriginEmbedderPolicy)) {
377 const followLink = (): void => {
378 Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.LearnMoreLinkCOEP);
379 if (this.#request) {
380 void IssuesManager.RelatedIssue.reveal(
381 this.#request, IssuesManager.Issue.IssueCategory.CrossOriginEmbedderPolicy);
382 }
383 };
384 return html`
385 <div class="devtools-link" @click=${followLink}>
386 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
387 iconName: 'issue-exclamation-icon',
388 color: 'var(--issue-color-yellow)',
389 width: '16px',
390 height: '16px',
391 } as IconButton.Icon.IconData}>
392 </${IconButton.Icon.Icon.litTagName}>
393 ${i18nString(UIStrings.learnMoreInTheIssuesTab)}
394 </div>
395 `;
396 }
397 if (headerDetails?.link) {
398 return html`
399 <x-link href=${headerDetails.link.url} class="link">
400 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
401 iconName: 'link_icon',
402 color: 'var(--color-link)',
403 width: '16px',
404 height: '16px',
405 } as IconButton.Icon.IconData}>
406 </${IconButton.Icon.Icon.litTagName}
407 >${i18nString(UIStrings.learnMore)}
408 </x-link>
409 `;
410 }
411 return LitHtml.nothing;
412 }
413
Wolfgang Beyerd1dd7962022-05-24 15:48:48414 #renderRawHeaders(rawHeadersText: string, forResponseHeaders: boolean): LitHtml.TemplateResult {
415 const trimmed = rawHeadersText.trim();
416 const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
417 const isShortened = !showFull && trimmed.length > RAW_HEADER_CUTOFF;
418
419 const showMore = ():void => {
420 if (forResponseHeaders) {
421 this.#showResponseHeadersTextFull = true;
422 } else {
423 this.#showRequestHeadersTextFull = true;
424 }
425 this.#render();
426 };
427
428 const onContextMenuOpen = (event: Event): void => {
429 const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
430 if (!showFull) {
431 const contextMenu = new UI.ContextMenu.ContextMenu(event);
432 const section = contextMenu.newSection();
433 section.appendItem(i18nString(UIStrings.showMore), showMore);
434 void contextMenu.show();
435 }
436 };
437
438 const addContextMenuListener = (el: Element):void => {
439 if (isShortened) {
440 el.addEventListener('contextmenu', onContextMenuOpen);
441 }
442 };
443
444 return html`
445 <div class="row raw-headers-row" on-render=${ComponentHelpers.Directives.nodeRenderedCallback(addContextMenuListener)}>
446 <div class="raw-headers">${isShortened ? trimmed.substring(0, RAW_HEADER_CUTOFF) : trimmed}</div>
447 ${isShortened ? html`
448 <${Buttons.Button.Button.litTagName}
449 .size=${Buttons.Button.Size.SMALL}
450 .variant=${Buttons.Button.Variant.SECONDARY}
451 @click=${showMore}
452 >${i18nString(UIStrings.showMore)}</${Buttons.Button.Button.litTagName}>
453 ` : LitHtml.nothing}
454 </div>
455 `;
456 }
457
Wolfgang Beyer40530b682022-05-17 13:02:01458 #renderGeneralSection(): LitHtml.TemplateResult {
459 assertNotNullOrUndefined(this.#request);
460
461 let coloredCircleClassName = 'red-circle';
462 if (this.#request.statusCode < 300 || this.#request.statusCode === 304) {
463 coloredCircleClassName = 'green-circle';
464 } else if (this.#request.statusCode < 400) {
465 coloredCircleClassName = 'yellow-circle';
466 }
467
468 let statusText = this.#request.statusCode + ' ' + this.#request.statusText;
469 let statusTextHasComment = false;
470 if (this.#request.cachedInMemory()) {
471 statusText += ' ' + i18nString(UIStrings.fromMemoryCache);
472 statusTextHasComment = true;
473 } else if (this.#request.fetchedViaServiceWorker) {
474 statusText += ' ' + i18nString(UIStrings.fromServiceWorker);
475 statusTextHasComment = true;
476 } else if (this.#request.redirectSourceSignedExchangeInfoHasNoErrors()) {
477 statusText += ' ' + i18nString(UIStrings.fromSignedexchange);
478 statusTextHasComment = true;
479 } else if (this.#request.webBundleInnerRequestInfo()) {
480 statusText += ' ' + i18nString(UIStrings.fromWebBundle);
481 statusTextHasComment = true;
482 } else if (this.#request.fromPrefetchCache()) {
483 statusText += ' ' + i18nString(UIStrings.fromPrefetchCache);
484 statusTextHasComment = true;
485 } else if (this.#request.cached()) {
486 statusText += ' ' + i18nString(UIStrings.fromDiskCache);
487 statusTextHasComment = true;
488 }
489
490 // Disabled until https://crbug.com/1079231 is fixed.
491 // clang-format off
492 return html`
Wolfgang Beyer235544c2022-05-24 08:07:45493 <${Category.litTagName}
494 .data=${{name: 'general', title: i18nString(UIStrings.general)} as CategoryData}
495 aria-label=${i18nString(UIStrings.general)}
496 >
Wolfgang Beyer40530b682022-05-17 13:02:01497 <div class="row">
498 <div class="header-name">${i18nString(UIStrings.requestUrl)}:</div>
499 <div class="header-value">${this.#request.url()}</div>
500 </div>
501 ${this.#request.statusCode? html`
502 <div class="row">
503 <div class="header-name">${i18nString(UIStrings.requestMethod)}:</div>
504 <div class="header-value">${this.#request.requestMethod}</div>
505 </div>
506 <div class="row">
507 <div class="header-name">${i18nString(UIStrings.statusCode)}:</div>
508 <div class="header-value ${coloredCircleClassName} ${statusTextHasComment ? 'status-with-comment' : ''}">${statusText}</div>
509 </div>
510 ` : ''}
511 ${this.#request.remoteAddress()? html`
512 <div class="row">
513 <div class="header-name">${i18nString(UIStrings.remoteAddress)}:</div>
514 <div class="header-value">${this.#request.remoteAddress()}</div>
515 </div>
516 ` : ''}
517 ${this.#request.referrerPolicy()? html`
518 <div class="row">
519 <div class="header-name">${i18nString(UIStrings.referrerPolicy)}:</div>
520 <div class="header-value">${this.#request.referrerPolicy()}</div>
521 </div>
522 ` : ''}
523 </${Category.litTagName}>
524 `;
525 // clang-format on
526 }
527}
528
Wolfgang Beyer235544c2022-05-24 08:07:45529export class ToggleRawHeadersEvent extends Event {
530 static readonly eventName = 'togglerawevent';
531
532 constructor() {
533 super(ToggleRawHeadersEvent.eventName, {});
534 }
535}
536
Wolfgang Beyer40530b682022-05-17 13:02:01537export interface CategoryData {
538 name: string;
539 title: Common.UIString.LocalizedString;
Wolfgang Beyer235544c2022-05-24 08:07:45540 headerCount?: number;
541 checked?: boolean;
Wolfgang Beyer40530b682022-05-17 13:02:01542}
543
544export class Category extends HTMLElement {
545 static readonly litTagName = LitHtml.literal`devtools-request-headers-category`;
546 readonly #shadow = this.attachShadow({mode: 'open'});
547 #expandedSetting?: Common.Settings.Setting<boolean>;
548 #title: Common.UIString.LocalizedString = Common.UIString.LocalizedEmptyString;
Wolfgang Beyer235544c2022-05-24 08:07:45549 #headerCount?: number = undefined;
550 #checked: boolean|undefined = undefined;
Wolfgang Beyer40530b682022-05-17 13:02:01551
552 connectedCallback(): void {
Jack Franklin1f19d522022-07-06 14:00:18553 this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles, Input.checkboxStyles];
Wolfgang Beyer40530b682022-05-17 13:02:01554 }
555
556 set data(data: CategoryData) {
557 this.#title = data.title;
558 this.#expandedSetting =
559 Common.Settings.Settings.instance().createSetting('request-info-' + data.name + '-category-expanded', true);
Wolfgang Beyer235544c2022-05-24 08:07:45560 this.#headerCount = data.headerCount;
561 this.#checked = data.checked;
Wolfgang Beyer40530b682022-05-17 13:02:01562 this.#render();
563 }
564
Wolfgang Beyer235544c2022-05-24 08:07:45565 #onCheckboxToggle(): void {
566 this.dispatchEvent(new ToggleRawHeadersEvent());
567 }
568
Wolfgang Beyer40530b682022-05-17 13:02:01569 #render(): void {
Wolfgang Beyer235544c2022-05-24 08:07:45570 const isOpen = this.#expandedSetting ? this.#expandedSetting.get() : true;
Wolfgang Beyer40530b682022-05-17 13:02:01571 // Disabled until https://crbug.com/1079231 is fixed.
572 // clang-format off
573 render(html`
Wolfgang Beyer235544c2022-05-24 08:07:45574 <details ?open=${isOpen} @toggle=${this.#onToggle}>
575 <summary class="header" @keydown=${this.#onSummaryKeyDown}>
576 ${this.#title}${this.#headerCount ?
577 html`<span class="header-count"> (${this.#headerCount})</span>` :
578 LitHtml.nothing
579 }
580 ${this.#checked !== undefined ? html`
581 <span class="raw-checkbox-container">
582 <label>
583 <input type="checkbox" .checked=${this.#checked} @change=${this.#onCheckboxToggle} />
584 ${i18nString(UIStrings.raw)}
585 </label>
586 </span>
587 ` : LitHtml.nothing}
588 </summary>
Wolfgang Beyer40530b682022-05-17 13:02:01589 <slot></slot>
590 </details>
591 `, this.#shadow, {host: this});
592 // clang-format on
593 }
594
595 #onSummaryKeyDown(event: KeyboardEvent): void {
596 if (!event.target) {
597 return;
598 }
599 const summaryElement = event.target as HTMLElement;
600 const detailsElement = summaryElement.parentElement as HTMLDetailsElement;
601 if (!detailsElement) {
602 throw new Error('<details> element is not found for a <summary> element');
603 }
604 switch (event.key) {
605 case 'ArrowLeft':
606 detailsElement.open = false;
607 break;
608 case 'ArrowRight':
609 detailsElement.open = true;
610 break;
611 }
612 }
613
614 #onToggle(event: Event): void {
615 this.#expandedSetting?.set((event.target as HTMLDetailsElement).open);
616 }
617}
618
619ComponentHelpers.CustomElements.defineComponent('devtools-request-headers', RequestHeadersComponent);
620ComponentHelpers.CustomElements.defineComponent('devtools-request-headers-category', Category);
621
622declare global {
623 // eslint-disable-next-line @typescript-eslint/no-unused-vars
624 interface HTMLElementTagNameMap {
625 'devtools-request-headers': RequestHeadersComponent;
626 'devtools-request-headers-category': Category;
627 }
628}
Wolfgang Beyer7f495ee2022-07-08 13:45:13629
630interface HeaderDetailsDescriptor {
631 explanation: () => string;
632 examples: Array<{
633 codeSnippet: string,
634 comment?: (() => string),
635 }>;
636 link: {
637 url: string,
638 }|null;
639}
640
641interface HeaderDescriptor {
642 name: string;
643 value: Object|null;
644 headerValueIncorrect?: boolean|null;
645 details?: HeaderDetailsDescriptor;
646 headerNotSet: boolean|null;
647}
648
649const BlockedReasonDetails = new Map<Protocol.Network.BlockedReason, HeaderDescriptor>([
650 [
651 Protocol.Network.BlockedReason.CoepFrameResourceNeedsCoepHeader,
652 {
653 name: 'cross-origin-embedder-policy',
654 value: null,
655 headerValueIncorrect: null,
656 details: {
657 explanation: i18nLazyString(UIStrings.toEmbedThisFrameInYourDocument),
658 examples: [{codeSnippet: 'Cross-Origin-Embedder-Policy: require-corp', comment: undefined}],
659 link: {url: 'https://web.dev/coop-coep/'},
660 },
661 headerNotSet: null,
662 },
663 ],
664 [
665 Protocol.Network.BlockedReason.CorpNotSameOriginAfterDefaultedToSameOriginByCoep,
666 {
667 name: 'cross-origin-resource-policy',
668 value: null,
669 headerValueIncorrect: null,
670 details: {
671 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferent),
672 examples: [
673 {
674 codeSnippet: 'Cross-Origin-Resource-Policy: same-site',
675 comment: i18nLazyString(UIStrings.chooseThisOptionIfTheResourceAnd),
676 },
677 {
678 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
679 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
680 },
681 ],
682 link: {url: 'https://web.dev/coop-coep/'},
683 },
684 headerNotSet: null,
685 },
686 ],
687 [
688 Protocol.Network.BlockedReason.CoopSandboxedIframeCannotNavigateToCoopPage,
689 {
690 name: 'cross-origin-opener-policy',
691 value: null,
692 headerValueIncorrect: false,
693 details: {
694 explanation: i18nLazyString(UIStrings.thisDocumentWasBlockedFrom),
695 examples: [],
696 link: {url: 'https://web.dev/coop-coep/'},
697 },
698 headerNotSet: null,
699 },
700 ],
701 [
702 Protocol.Network.BlockedReason.CorpNotSameSite,
703 {
704 name: 'cross-origin-resource-policy',
705 value: null,
706 headerValueIncorrect: true,
707 details: {
708 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferentSite),
709 examples: [
710 {
711 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
712 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
713 },
714 ],
715 link: null,
716 },
717 headerNotSet: null,
718 },
719 ],
720 [
721 Protocol.Network.BlockedReason.CorpNotSameOrigin,
722 {
723 name: 'cross-origin-resource-policy',
724 value: null,
725 headerValueIncorrect: true,
726 details: {
727 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferentOrigin),
728 examples: [
729 {
730 codeSnippet: 'Cross-Origin-Resource-Policy: same-site',
731 comment: i18nLazyString(UIStrings.chooseThisOptionIfTheResourceAnd),
732 },
733 {
734 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
735 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
736 },
737 ],
738 link: null,
739 },
740 headerNotSet: null,
741 },
742 ],
743]);