blob: e422f8c63d21b0c9b05ae994d6bfbdfe2e1441d0 [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 Beyer235544c2022-05-24 08:07:4577 *@description Text in Request Headers View of the Network panel
78 */
Wolfgang Beyer37fcd282022-06-29 09:34:1779 referrerPolicy: 'Referrer Policy',
Wolfgang Beyer235544c2022-05-24 08:07:4580 /**
Wolfgang Beyer37fcd282022-06-29 09:34:1781 *@description Text in Network Log View Columns of the Network panel
Wolfgang Beyer40530b682022-05-17 13:02:0182 */
Wolfgang Beyer37fcd282022-06-29 09:34:1783 remoteAddress: 'Remote Address',
84 /**
85 *@description Text in Request Headers View of the Network panel
86 */
87 requestHeaders: 'Request Headers',
Wolfgang Beyer40530b682022-05-17 13:02:0188 /**
89 *@description The HTTP method of a request
90 */
91 requestMethod: 'Request Method',
92 /**
Wolfgang Beyer37fcd282022-06-29 09:34:1793 *@description The URL of a request
94 */
95 requestUrl: 'Request URL',
96 /**
Wolfgang Beyer235544c2022-05-24 08:07:4597 *@description A context menu item in the Network Log View Columns of the Network panel
98 */
99 responseHeaders: 'Response Headers',
100 /**
Wolfgang Beyerd1dd7962022-05-24 15:48:48101 *@description Text to show more content
102 */
103 showMore: 'Show more',
104 /**
Wolfgang Beyer40530b682022-05-17 13:02:01105 *@description HTTP response code
106 */
107 statusCode: 'Status Code',
Wolfgang Beyer7f495ee2022-07-08 13:45:13108 /**
109 *@description Text in Headers View of the Network panel
110 */
111 thisDocumentWasBlockedFrom:
112 'This document was blocked from loading in an `iframe` with a `sandbox` attribute because this document specified a cross-origin opener policy.',
113 /**
114 *@description Text in Headers View of the Network panel
115 */
116 toEmbedThisFrameInYourDocument:
117 'To embed this frame in your document, the response needs to enable the cross-origin embedder policy by specifying the following response header:',
118 /**
119 *@description Text in Headers View of the Network panel
120 */
121 toUseThisResourceFromADifferent:
122 'To use this resource from a different origin, the server needs to specify a cross-origin resource policy in the response headers:',
123 /**
124 *@description Text in Headers View of the Network panel
125 */
126 toUseThisResourceFromADifferentOrigin:
127 'To use this resource from a different origin, the server may relax the cross-origin resource policy response header:',
128 /**
129 *@description Text in Headers View of the Network panel
130 */
131 toUseThisResourceFromADifferentSite:
132 '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:01133};
134const str_ = i18n.i18n.registerUIStrings('panels/network/components/RequestHeadersView.ts', UIStrings);
135const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Wolfgang Beyer7f495ee2022-07-08 13:45:13136const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
Wolfgang Beyer40530b682022-05-17 13:02:01137
138export class RequestHeadersView extends UI.Widget.VBox {
139 readonly #headersView = new RequestHeadersComponent();
140 readonly #request: SDK.NetworkRequest.NetworkRequest;
141
142 constructor(request: SDK.NetworkRequest.NetworkRequest) {
143 super();
144 this.#request = request;
145 this.contentElement.appendChild(this.#headersView);
146 }
147
148 wasShown(): void {
149 this.#request.addEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this.#refreshHeadersView, this);
150 this.#request.addEventListener(SDK.NetworkRequest.Events.FinishedLoading, this.#refreshHeadersView, this);
151 this.#refreshHeadersView();
152 }
153
154 willHide(): void {
155 this.#request.removeEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this.#refreshHeadersView, this);
156 this.#request.removeEventListener(SDK.NetworkRequest.Events.FinishedLoading, this.#refreshHeadersView, this);
157 }
158
159 #refreshHeadersView(): void {
160 this.#headersView.data = {
161 request: this.#request,
162 };
163 }
164}
165
166export interface RequestHeadersComponentData {
167 request: SDK.NetworkRequest.NetworkRequest;
168}
169
170export class RequestHeadersComponent extends HTMLElement {
171 static readonly litTagName = LitHtml.literal`devtools-request-headers`;
172 readonly #shadow = this.attachShadow({mode: 'open'});
173 #request?: Readonly<SDK.NetworkRequest.NetworkRequest>;
Wolfgang Beyer235544c2022-05-24 08:07:45174 #showResponseHeadersText = false;
175 #showRequestHeadersText = false;
Wolfgang Beyerd1dd7962022-05-24 15:48:48176 #showResponseHeadersTextFull = false;
177 #showRequestHeadersTextFull = false;
Wolfgang Beyer40530b682022-05-17 13:02:01178
179 set data(data: RequestHeadersComponentData) {
180 this.#request = data.request;
181 this.#render();
182 }
183
184 connectedCallback(): void {
185 this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles];
186 }
187
188 #render(): void {
189 assertNotNullOrUndefined(this.#request);
190
191 // Disabled until https://crbug.com/1079231 is fixed.
192 // clang-format off
193 render(html`
194 ${this.#renderGeneralSection()}
Wolfgang Beyer235544c2022-05-24 08:07:45195 ${this.#renderResponseHeaders()}
196 ${this.#renderRequestHeaders()}
Wolfgang Beyer40530b682022-05-17 13:02:01197 `, this.#shadow, {host: this});
198 // clang-format on
199 }
200
Wolfgang Beyer235544c2022-05-24 08:07:45201 #renderResponseHeaders(): LitHtml.TemplateResult {
202 assertNotNullOrUndefined(this.#request);
203
Wolfgang Beyer7f495ee2022-07-08 13:45:13204 const headersWithIssues = [];
205 if (this.#request.wasBlocked()) {
206 const headerWithIssues =
207 BlockedReasonDetails.get((this.#request.blockedReason() as Protocol.Network.BlockedReason));
208 if (headerWithIssues) {
209 headersWithIssues.push(headerWithIssues);
210 }
211 }
212
213 function mergeHeadersWithIssues(
214 headers: SDK.NetworkRequest.NameValue[], headersWithIssues: HeaderDescriptor[]): HeaderDescriptor[] {
215 let i = 0, j = 0;
216 const result: HeaderDescriptor[] = [];
217 while (i < headers.length && j < headersWithIssues.length) {
218 if (headers[i].name < headersWithIssues[j].name) {
219 result.push({...headers[i++], headerNotSet: false});
220 } else if (headers[i].name > headersWithIssues[j].name) {
221 result.push({...headersWithIssues[j++], headerNotSet: true});
222 } else {
223 result.push({...headersWithIssues[j++], ...headers[i++], headerNotSet: false});
224 }
225 }
226 while (i < headers.length) {
227 result.push({...headers[i++], headerNotSet: false});
228 }
229 while (j < headersWithIssues.length) {
230 result.push({...headersWithIssues[j++], headerNotSet: true});
231 }
232 return result;
233 }
234
235 const mergedHeaders = mergeHeadersWithIssues(this.#request.sortedResponseHeaders.slice(), headersWithIssues);
236
Wolfgang Beyer235544c2022-05-24 08:07:45237 const toggleShowRaw = (): void => {
238 this.#showResponseHeadersText = !this.#showResponseHeadersText;
239 this.#render();
240 };
241
242 // Disabled until https://crbug.com/1079231 is fixed.
243 // clang-format off
244 return html`
245 <${Category.litTagName}
246 @togglerawevent=${toggleShowRaw}
247 .data=${{
248 name: 'responseHeaders',
249 title: i18nString(UIStrings.responseHeaders),
250 headerCount: this.#request.sortedResponseHeaders.length,
251 checked: this.#request.responseHeadersText ? this.#showResponseHeadersText : undefined,
252 } as CategoryData}
253 aria-label=${i18nString(UIStrings.responseHeaders)}
254 >
Wolfgang Beyerd1dd7962022-05-24 15:48:48255 ${this.#showResponseHeadersText ?
256 this.#renderRawHeaders(this.#request.responseHeadersText, true) : html`
Wolfgang Beyer7f495ee2022-07-08 13:45:13257 ${mergedHeaders.map(header => this.#renderHeader(header))}
Wolfgang Beyer235544c2022-05-24 08:07:45258 `}
259 </${Category.litTagName}>
260 `;
261 }
262
263 #renderRequestHeaders(): LitHtml.TemplateResult {
264 assertNotNullOrUndefined(this.#request);
265
Wolfgang Beyer7f495ee2022-07-08 13:45:13266 const headers = this.#request.requestHeaders().slice();
267 headers.sort(function(a, b) {
268 return Platform.StringUtilities.compare(a.name.toLowerCase(), b.name.toLowerCase());
269 });
270 const requestHeadersText = this.#request.requestHeadersText();
271
Wolfgang Beyer235544c2022-05-24 08:07:45272 const toggleShowRaw = (): void => {
273 this.#showRequestHeadersText = !this.#showRequestHeadersText;
274 this.#render();
275 };
276
Wolfgang Beyer235544c2022-05-24 08:07:45277 // Disabled until https://crbug.com/1079231 is fixed.
278 // clang-format off
279 return html`
280 <${Category.litTagName}
281 @togglerawevent=${toggleShowRaw}
282 .data=${{
283 name: 'requestHeaders',
284 title: i18nString(UIStrings.requestHeaders),
285 headerCount: this.#request.requestHeaders().length,
286 checked: requestHeadersText? this.#showRequestHeadersText : undefined,
287 } as CategoryData}
288 aria-label=${i18nString(UIStrings.requestHeaders)}
289 >
Wolfgang Beyerd1dd7962022-05-24 15:48:48290 ${(this.#showRequestHeadersText && requestHeadersText) ?
291 this.#renderRawHeaders(requestHeadersText, false) : html`
Wolfgang Beyer7f495ee2022-07-08 13:45:13292 ${headers.map(header => this.#renderHeader({...header, headerNotSet: false}))}
Wolfgang Beyer235544c2022-05-24 08:07:45293 `}
294 </${Category.litTagName}>
295 `;
296 }
297
Wolfgang Beyer7f495ee2022-07-08 13:45:13298 #renderHeader(header: HeaderDescriptor): LitHtml.TemplateResult {
299 return html`
300 <div class="row">
301 <div class="header-name">${header.headerNotSet ? html`<div class="header-badge header-badge-text">not-set</div>` : ''}${header.name}:</div>
302 <div class="header-value ${header.headerValueIncorrect ? 'header-warning' : ''}">${header.value?.toString()||''}</div>
303 </div>
304 ${this.#maybeRenderHeaderDetails(header.details)}
305 `;
306 }
307
308 #maybeRenderHeaderDetails(headerDetails?: HeaderDetailsDescriptor): LitHtml.LitTemplate {
309 if (!headerDetails) {
310 return LitHtml.nothing;
311 }
312 return html`
313 <div class="header-details">
314 <div class="call-to-action">
315 <div class="call-to-action-body">
316 <div class="explanation">${headerDetails.explanation()}</div>
317 ${headerDetails.examples.map(example => html`
318 <div class="example">
319 <code>${example.codeSnippet}</code>
320 ${example.comment ? html`
321 <span class="comment">${example.comment()}</span>
322 ` : ''}
323 </div>
324 `)}
325 ${this.#maybeRenderHeaderDetailsLink(headerDetails)}
326 </div>
327 </div>
328 </div>
329 `;
330 }
331
332 #maybeRenderHeaderDetailsLink(headerDetails?: HeaderDetailsDescriptor): LitHtml.LitTemplate {
333 if (this.#request && IssuesManager.RelatedIssue.hasIssueOfCategory(this.#request, IssuesManager.Issue.IssueCategory.CrossOriginEmbedderPolicy)) {
334 const followLink = (): void => {
335 Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.LearnMoreLinkCOEP);
336 if (this.#request) {
337 void IssuesManager.RelatedIssue.reveal(
338 this.#request, IssuesManager.Issue.IssueCategory.CrossOriginEmbedderPolicy);
339 }
340 };
341 return html`
342 <div class="devtools-link" @click=${followLink}>
343 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
344 iconName: 'issue-exclamation-icon',
345 color: 'var(--issue-color-yellow)',
346 width: '16px',
347 height: '16px',
348 } as IconButton.Icon.IconData}>
349 </${IconButton.Icon.Icon.litTagName}>
350 ${i18nString(UIStrings.learnMoreInTheIssuesTab)}
351 </div>
352 `;
353 }
354 if (headerDetails?.link) {
355 return html`
356 <x-link href=${headerDetails.link.url} class="link">
357 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
358 iconName: 'link_icon',
359 color: 'var(--color-link)',
360 width: '16px',
361 height: '16px',
362 } as IconButton.Icon.IconData}>
363 </${IconButton.Icon.Icon.litTagName}
364 >${i18nString(UIStrings.learnMore)}
365 </x-link>
366 `;
367 }
368 return LitHtml.nothing;
369 }
370
Wolfgang Beyerd1dd7962022-05-24 15:48:48371 #renderRawHeaders(rawHeadersText: string, forResponseHeaders: boolean): LitHtml.TemplateResult {
372 const trimmed = rawHeadersText.trim();
373 const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
374 const isShortened = !showFull && trimmed.length > RAW_HEADER_CUTOFF;
375
376 const showMore = ():void => {
377 if (forResponseHeaders) {
378 this.#showResponseHeadersTextFull = true;
379 } else {
380 this.#showRequestHeadersTextFull = true;
381 }
382 this.#render();
383 };
384
385 const onContextMenuOpen = (event: Event): void => {
386 const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
387 if (!showFull) {
388 const contextMenu = new UI.ContextMenu.ContextMenu(event);
389 const section = contextMenu.newSection();
390 section.appendItem(i18nString(UIStrings.showMore), showMore);
391 void contextMenu.show();
392 }
393 };
394
395 const addContextMenuListener = (el: Element):void => {
396 if (isShortened) {
397 el.addEventListener('contextmenu', onContextMenuOpen);
398 }
399 };
400
401 return html`
402 <div class="row raw-headers-row" on-render=${ComponentHelpers.Directives.nodeRenderedCallback(addContextMenuListener)}>
403 <div class="raw-headers">${isShortened ? trimmed.substring(0, RAW_HEADER_CUTOFF) : trimmed}</div>
404 ${isShortened ? html`
405 <${Buttons.Button.Button.litTagName}
406 .size=${Buttons.Button.Size.SMALL}
407 .variant=${Buttons.Button.Variant.SECONDARY}
408 @click=${showMore}
409 >${i18nString(UIStrings.showMore)}</${Buttons.Button.Button.litTagName}>
410 ` : LitHtml.nothing}
411 </div>
412 `;
413 }
414
Wolfgang Beyer40530b682022-05-17 13:02:01415 #renderGeneralSection(): LitHtml.TemplateResult {
416 assertNotNullOrUndefined(this.#request);
417
418 let coloredCircleClassName = 'red-circle';
419 if (this.#request.statusCode < 300 || this.#request.statusCode === 304) {
420 coloredCircleClassName = 'green-circle';
421 } else if (this.#request.statusCode < 400) {
422 coloredCircleClassName = 'yellow-circle';
423 }
424
425 let statusText = this.#request.statusCode + ' ' + this.#request.statusText;
426 let statusTextHasComment = false;
427 if (this.#request.cachedInMemory()) {
428 statusText += ' ' + i18nString(UIStrings.fromMemoryCache);
429 statusTextHasComment = true;
430 } else if (this.#request.fetchedViaServiceWorker) {
431 statusText += ' ' + i18nString(UIStrings.fromServiceWorker);
432 statusTextHasComment = true;
433 } else if (this.#request.redirectSourceSignedExchangeInfoHasNoErrors()) {
434 statusText += ' ' + i18nString(UIStrings.fromSignedexchange);
435 statusTextHasComment = true;
436 } else if (this.#request.webBundleInnerRequestInfo()) {
437 statusText += ' ' + i18nString(UIStrings.fromWebBundle);
438 statusTextHasComment = true;
439 } else if (this.#request.fromPrefetchCache()) {
440 statusText += ' ' + i18nString(UIStrings.fromPrefetchCache);
441 statusTextHasComment = true;
442 } else if (this.#request.cached()) {
443 statusText += ' ' + i18nString(UIStrings.fromDiskCache);
444 statusTextHasComment = true;
445 }
446
447 // Disabled until https://crbug.com/1079231 is fixed.
448 // clang-format off
449 return html`
Wolfgang Beyer235544c2022-05-24 08:07:45450 <${Category.litTagName}
451 .data=${{name: 'general', title: i18nString(UIStrings.general)} as CategoryData}
452 aria-label=${i18nString(UIStrings.general)}
453 >
Wolfgang Beyer40530b682022-05-17 13:02:01454 <div class="row">
455 <div class="header-name">${i18nString(UIStrings.requestUrl)}:</div>
456 <div class="header-value">${this.#request.url()}</div>
457 </div>
458 ${this.#request.statusCode? html`
459 <div class="row">
460 <div class="header-name">${i18nString(UIStrings.requestMethod)}:</div>
461 <div class="header-value">${this.#request.requestMethod}</div>
462 </div>
463 <div class="row">
464 <div class="header-name">${i18nString(UIStrings.statusCode)}:</div>
465 <div class="header-value ${coloredCircleClassName} ${statusTextHasComment ? 'status-with-comment' : ''}">${statusText}</div>
466 </div>
467 ` : ''}
468 ${this.#request.remoteAddress()? html`
469 <div class="row">
470 <div class="header-name">${i18nString(UIStrings.remoteAddress)}:</div>
471 <div class="header-value">${this.#request.remoteAddress()}</div>
472 </div>
473 ` : ''}
474 ${this.#request.referrerPolicy()? html`
475 <div class="row">
476 <div class="header-name">${i18nString(UIStrings.referrerPolicy)}:</div>
477 <div class="header-value">${this.#request.referrerPolicy()}</div>
478 </div>
479 ` : ''}
480 </${Category.litTagName}>
481 `;
482 // clang-format on
483 }
484}
485
Wolfgang Beyer235544c2022-05-24 08:07:45486export class ToggleRawHeadersEvent extends Event {
487 static readonly eventName = 'togglerawevent';
488
489 constructor() {
490 super(ToggleRawHeadersEvent.eventName, {});
491 }
492}
493
Wolfgang Beyer40530b682022-05-17 13:02:01494export interface CategoryData {
495 name: string;
496 title: Common.UIString.LocalizedString;
Wolfgang Beyer235544c2022-05-24 08:07:45497 headerCount?: number;
498 checked?: boolean;
Wolfgang Beyer40530b682022-05-17 13:02:01499}
500
501export class Category extends HTMLElement {
502 static readonly litTagName = LitHtml.literal`devtools-request-headers-category`;
503 readonly #shadow = this.attachShadow({mode: 'open'});
504 #expandedSetting?: Common.Settings.Setting<boolean>;
505 #title: Common.UIString.LocalizedString = Common.UIString.LocalizedEmptyString;
Wolfgang Beyer235544c2022-05-24 08:07:45506 #headerCount?: number = undefined;
507 #checked: boolean|undefined = undefined;
Wolfgang Beyer40530b682022-05-17 13:02:01508
509 connectedCallback(): void {
Jack Franklin1f19d522022-07-06 14:00:18510 this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles, Input.checkboxStyles];
Wolfgang Beyer40530b682022-05-17 13:02:01511 }
512
513 set data(data: CategoryData) {
514 this.#title = data.title;
515 this.#expandedSetting =
516 Common.Settings.Settings.instance().createSetting('request-info-' + data.name + '-category-expanded', true);
Wolfgang Beyer235544c2022-05-24 08:07:45517 this.#headerCount = data.headerCount;
518 this.#checked = data.checked;
Wolfgang Beyer40530b682022-05-17 13:02:01519 this.#render();
520 }
521
Wolfgang Beyer235544c2022-05-24 08:07:45522 #onCheckboxToggle(): void {
523 this.dispatchEvent(new ToggleRawHeadersEvent());
524 }
525
Wolfgang Beyer40530b682022-05-17 13:02:01526 #render(): void {
Wolfgang Beyer235544c2022-05-24 08:07:45527 const isOpen = this.#expandedSetting ? this.#expandedSetting.get() : true;
Wolfgang Beyer40530b682022-05-17 13:02:01528 // Disabled until https://crbug.com/1079231 is fixed.
529 // clang-format off
530 render(html`
Wolfgang Beyer235544c2022-05-24 08:07:45531 <details ?open=${isOpen} @toggle=${this.#onToggle}>
532 <summary class="header" @keydown=${this.#onSummaryKeyDown}>
533 ${this.#title}${this.#headerCount ?
534 html`<span class="header-count"> (${this.#headerCount})</span>` :
535 LitHtml.nothing
536 }
537 ${this.#checked !== undefined ? html`
538 <span class="raw-checkbox-container">
539 <label>
540 <input type="checkbox" .checked=${this.#checked} @change=${this.#onCheckboxToggle} />
541 ${i18nString(UIStrings.raw)}
542 </label>
543 </span>
544 ` : LitHtml.nothing}
545 </summary>
Wolfgang Beyer40530b682022-05-17 13:02:01546 <slot></slot>
547 </details>
548 `, this.#shadow, {host: this});
549 // clang-format on
550 }
551
552 #onSummaryKeyDown(event: KeyboardEvent): void {
553 if (!event.target) {
554 return;
555 }
556 const summaryElement = event.target as HTMLElement;
557 const detailsElement = summaryElement.parentElement as HTMLDetailsElement;
558 if (!detailsElement) {
559 throw new Error('<details> element is not found for a <summary> element');
560 }
561 switch (event.key) {
562 case 'ArrowLeft':
563 detailsElement.open = false;
564 break;
565 case 'ArrowRight':
566 detailsElement.open = true;
567 break;
568 }
569 }
570
571 #onToggle(event: Event): void {
572 this.#expandedSetting?.set((event.target as HTMLDetailsElement).open);
573 }
574}
575
576ComponentHelpers.CustomElements.defineComponent('devtools-request-headers', RequestHeadersComponent);
577ComponentHelpers.CustomElements.defineComponent('devtools-request-headers-category', Category);
578
579declare global {
580 // eslint-disable-next-line @typescript-eslint/no-unused-vars
581 interface HTMLElementTagNameMap {
582 'devtools-request-headers': RequestHeadersComponent;
583 'devtools-request-headers-category': Category;
584 }
585}
Wolfgang Beyer7f495ee2022-07-08 13:45:13586
587interface HeaderDetailsDescriptor {
588 explanation: () => string;
589 examples: Array<{
590 codeSnippet: string,
591 comment?: (() => string),
592 }>;
593 link: {
594 url: string,
595 }|null;
596}
597
598interface HeaderDescriptor {
599 name: string;
600 value: Object|null;
601 headerValueIncorrect?: boolean|null;
602 details?: HeaderDetailsDescriptor;
603 headerNotSet: boolean|null;
604}
605
606const BlockedReasonDetails = new Map<Protocol.Network.BlockedReason, HeaderDescriptor>([
607 [
608 Protocol.Network.BlockedReason.CoepFrameResourceNeedsCoepHeader,
609 {
610 name: 'cross-origin-embedder-policy',
611 value: null,
612 headerValueIncorrect: null,
613 details: {
614 explanation: i18nLazyString(UIStrings.toEmbedThisFrameInYourDocument),
615 examples: [{codeSnippet: 'Cross-Origin-Embedder-Policy: require-corp', comment: undefined}],
616 link: {url: 'https://web.dev/coop-coep/'},
617 },
618 headerNotSet: null,
619 },
620 ],
621 [
622 Protocol.Network.BlockedReason.CorpNotSameOriginAfterDefaultedToSameOriginByCoep,
623 {
624 name: 'cross-origin-resource-policy',
625 value: null,
626 headerValueIncorrect: null,
627 details: {
628 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferent),
629 examples: [
630 {
631 codeSnippet: 'Cross-Origin-Resource-Policy: same-site',
632 comment: i18nLazyString(UIStrings.chooseThisOptionIfTheResourceAnd),
633 },
634 {
635 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
636 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
637 },
638 ],
639 link: {url: 'https://web.dev/coop-coep/'},
640 },
641 headerNotSet: null,
642 },
643 ],
644 [
645 Protocol.Network.BlockedReason.CoopSandboxedIframeCannotNavigateToCoopPage,
646 {
647 name: 'cross-origin-opener-policy',
648 value: null,
649 headerValueIncorrect: false,
650 details: {
651 explanation: i18nLazyString(UIStrings.thisDocumentWasBlockedFrom),
652 examples: [],
653 link: {url: 'https://web.dev/coop-coep/'},
654 },
655 headerNotSet: null,
656 },
657 ],
658 [
659 Protocol.Network.BlockedReason.CorpNotSameSite,
660 {
661 name: 'cross-origin-resource-policy',
662 value: null,
663 headerValueIncorrect: true,
664 details: {
665 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferentSite),
666 examples: [
667 {
668 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
669 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
670 },
671 ],
672 link: null,
673 },
674 headerNotSet: null,
675 },
676 ],
677 [
678 Protocol.Network.BlockedReason.CorpNotSameOrigin,
679 {
680 name: 'cross-origin-resource-policy',
681 value: null,
682 headerValueIncorrect: true,
683 details: {
684 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferentOrigin),
685 examples: [
686 {
687 codeSnippet: 'Cross-Origin-Resource-Policy: same-site',
688 comment: i18nLazyString(UIStrings.chooseThisOptionIfTheResourceAnd),
689 },
690 {
691 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
692 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
693 },
694 ],
695 link: null,
696 },
697 headerNotSet: null,
698 },
699 ],
700]);