blob: b9a6d5880ea98038d3211a9538eb0b0f32a17364 [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 Beyerbcf4a832022-07-25 12:34:4713import * as ClientVariations from '../../../third_party/chromium/client-variations/client-variations.js';
Wolfgang Beyerd1dd7962022-05-24 15:48:4814import * as Buttons from '../../../ui/components/buttons/buttons.js';
Wolfgang Beyer40530b682022-05-17 13:02:0115import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
Wolfgang Beyer7f495ee2022-07-08 13:45:1316import * as IconButton from '../../../ui/components/icon_button/icon_button.js';
Jack Franklin1f19d522022-07-06 14:00:1817import * as Input from '../../../ui/components/input/input.js';
Wolfgang Beyer40530b682022-05-17 13:02:0118import * as UI from '../../../ui/legacy/legacy.js';
19import * as LitHtml from '../../../ui/lit-html/lit-html.js';
20
21import requestHeadersViewStyles from './RequestHeadersView.css.js';
22
Wolfgang Beyerd1dd7962022-05-24 15:48:4823const RAW_HEADER_CUTOFF = 3000;
Wolfgang Beyer40530b682022-05-17 13:02:0124const {render, html} = LitHtml;
25
26const UIStrings = {
27 /**
Wolfgang Beyerbcf4a832022-07-25 12:34:4728 *@description Comment used in decoded X-Client-Data HTTP header output in Headers View of the Network panel
29 */
30 activeClientExperimentVariation: 'Active `client experiment variation IDs`.',
31 /**
32 *@description Comment used in decoded X-Client-Data HTTP header output in Headers View of the Network panel
33 */
34 activeClientExperimentVariationIds: 'Active `client experiment variation IDs` that trigger server-side behavior.',
35 /**
Wolfgang Beyer7f495ee2022-07-08 13:45:1336 *@description Text in Headers View of the Network panel
37 */
38 chooseThisOptionIfTheResourceAnd:
39 'Choose this option if the resource and the document are served from the same site.',
40 /**
Wolfgang Beyerbcf4a832022-07-25 12:34:4741 *@description Text in Headers View of the Network panel for X-Client-Data HTTP headers
42 */
43 decoded: 'Decoded:',
44 /**
Wolfgang Beyer40530b682022-05-17 13:02:0145 *@description Text in Request Headers View of the Network panel
46 */
Wolfgang Beyer37fcd282022-06-29 09:34:1747 fromDiskCache: '(from disk cache)',
48 /**
49 *@description Text in Request Headers View of the Network panel
50 */
Wolfgang Beyer40530b682022-05-17 13:02:0151 fromMemoryCache: '(from memory cache)',
52 /**
53 *@description Text in Request Headers View of the Network panel
54 */
Wolfgang Beyer37fcd282022-06-29 09:34:1755 fromPrefetchCache: '(from prefetch cache)',
56 /**
57 *@description Text in Request Headers View of the Network panel
58 */
Wolfgang Beyer40530b682022-05-17 13:02:0159 fromServiceWorker: '(from `service worker`)',
60 /**
61 *@description Text in Request Headers View of the Network panel
62 */
63 fromSignedexchange: '(from signed-exchange)',
64 /**
65 *@description Text in Request Headers View of the Network panel
66 */
Wolfgang Beyer40530b682022-05-17 13:02:0167 fromWebBundle: '(from Web Bundle)',
68 /**
69 *@description Section header for a list of the main aspects of a http request
70 */
71 general: 'General',
72 /**
Wolfgang Beyer7f495ee2022-07-08 13:45:1373 *@description Text that is usually a hyperlink to more documentation
74 */
75 learnMore: 'Learn more',
76 /**
77 *@description Text for a link to the issues panel
78 */
79 learnMoreInTheIssuesTab: 'Learn more in the issues tab',
80 /**
Wolfgang Beyer235544c2022-05-24 08:07:4581 *@description Label for a checkbox to switch between raw and parsed headers
82 */
83 raw: 'Raw',
84 /**
Wolfgang Beyer7f495ee2022-07-08 13:45:1385 *@description Text in Headers View of the Network panel
86 */
87 onlyChooseThisOptionIfAn:
88 'Only choose this option if an arbitrary website including this resource does not impose a security risk.',
89 /**
Wolfgang Beyerd0ab3a62022-07-12 12:59:0290 *@description Message to explain lack of raw headers for a particular network request
91 */
92 provisionalHeadersAreShownDisableCache: 'Provisional headers are shown. Disable cache to see full headers.',
93 /**
94 *@description Tooltip to explain lack of raw headers for a particular network request
95 */
96 onlyProvisionalHeadersAre:
97 '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.',
98 /**
99 *@description Message to explain lack of raw headers for a particular network request
100 */
101 provisionalHeadersAreShown: 'Provisional headers are shown.',
102 /**
Wolfgang Beyer235544c2022-05-24 08:07:45103 *@description Text in Request Headers View of the Network panel
104 */
Wolfgang Beyer37fcd282022-06-29 09:34:17105 referrerPolicy: 'Referrer Policy',
Wolfgang Beyer235544c2022-05-24 08:07:45106 /**
Wolfgang Beyer37fcd282022-06-29 09:34:17107 *@description Text in Network Log View Columns of the Network panel
Wolfgang Beyer40530b682022-05-17 13:02:01108 */
Wolfgang Beyer37fcd282022-06-29 09:34:17109 remoteAddress: 'Remote Address',
110 /**
111 *@description Text in Request Headers View of the Network panel
112 */
113 requestHeaders: 'Request Headers',
Wolfgang Beyer40530b682022-05-17 13:02:01114 /**
115 *@description The HTTP method of a request
116 */
117 requestMethod: 'Request Method',
118 /**
Wolfgang Beyer37fcd282022-06-29 09:34:17119 *@description The URL of a request
120 */
121 requestUrl: 'Request URL',
122 /**
Wolfgang Beyer235544c2022-05-24 08:07:45123 *@description A context menu item in the Network Log View Columns of the Network panel
124 */
125 responseHeaders: 'Response Headers',
126 /**
Wolfgang Beyerd1dd7962022-05-24 15:48:48127 *@description Text to show more content
128 */
129 showMore: 'Show more',
130 /**
Wolfgang Beyer40530b682022-05-17 13:02:01131 *@description HTTP response code
132 */
133 statusCode: 'Status Code',
Wolfgang Beyer7f495ee2022-07-08 13:45:13134 /**
135 *@description Text in Headers View of the Network panel
136 */
137 thisDocumentWasBlockedFrom:
138 'This document was blocked from loading in an `iframe` with a `sandbox` attribute because this document specified a cross-origin opener policy.',
139 /**
140 *@description Text in Headers View of the Network panel
141 */
142 toEmbedThisFrameInYourDocument:
143 'To embed this frame in your document, the response needs to enable the cross-origin embedder policy by specifying the following response header:',
144 /**
145 *@description Text in Headers View of the Network panel
146 */
147 toUseThisResourceFromADifferent:
148 'To use this resource from a different origin, the server needs to specify a cross-origin resource policy in the response headers:',
149 /**
150 *@description Text in Headers View of the Network panel
151 */
152 toUseThisResourceFromADifferentOrigin:
153 'To use this resource from a different origin, the server may relax the cross-origin resource policy response header:',
154 /**
155 *@description Text in Headers View of the Network panel
156 */
157 toUseThisResourceFromADifferentSite:
158 '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:01159};
160const str_ = i18n.i18n.registerUIStrings('panels/network/components/RequestHeadersView.ts', UIStrings);
161const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Wolfgang Beyer7f495ee2022-07-08 13:45:13162const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
Wolfgang Beyer40530b682022-05-17 13:02:01163
164export class RequestHeadersView extends UI.Widget.VBox {
165 readonly #headersView = new RequestHeadersComponent();
166 readonly #request: SDK.NetworkRequest.NetworkRequest;
167
168 constructor(request: SDK.NetworkRequest.NetworkRequest) {
169 super();
170 this.#request = request;
171 this.contentElement.appendChild(this.#headersView);
172 }
173
174 wasShown(): void {
175 this.#request.addEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this.#refreshHeadersView, this);
176 this.#request.addEventListener(SDK.NetworkRequest.Events.FinishedLoading, this.#refreshHeadersView, this);
177 this.#refreshHeadersView();
178 }
179
180 willHide(): void {
181 this.#request.removeEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this.#refreshHeadersView, this);
182 this.#request.removeEventListener(SDK.NetworkRequest.Events.FinishedLoading, this.#refreshHeadersView, this);
183 }
184
185 #refreshHeadersView(): void {
186 this.#headersView.data = {
187 request: this.#request,
188 };
189 }
190}
191
192export interface RequestHeadersComponentData {
193 request: SDK.NetworkRequest.NetworkRequest;
194}
195
196export class RequestHeadersComponent extends HTMLElement {
197 static readonly litTagName = LitHtml.literal`devtools-request-headers`;
198 readonly #shadow = this.attachShadow({mode: 'open'});
199 #request?: Readonly<SDK.NetworkRequest.NetworkRequest>;
Wolfgang Beyer235544c2022-05-24 08:07:45200 #showResponseHeadersText = false;
201 #showRequestHeadersText = false;
Wolfgang Beyerd1dd7962022-05-24 15:48:48202 #showResponseHeadersTextFull = false;
203 #showRequestHeadersTextFull = false;
Wolfgang Beyer40530b682022-05-17 13:02:01204
205 set data(data: RequestHeadersComponentData) {
206 this.#request = data.request;
207 this.#render();
208 }
209
210 connectedCallback(): void {
211 this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles];
212 }
213
214 #render(): void {
215 assertNotNullOrUndefined(this.#request);
216
217 // Disabled until https://crbug.com/1079231 is fixed.
218 // clang-format off
219 render(html`
220 ${this.#renderGeneralSection()}
Wolfgang Beyer235544c2022-05-24 08:07:45221 ${this.#renderResponseHeaders()}
222 ${this.#renderRequestHeaders()}
Wolfgang Beyer40530b682022-05-17 13:02:01223 `, this.#shadow, {host: this});
224 // clang-format on
225 }
226
Wolfgang Beyer235544c2022-05-24 08:07:45227 #renderResponseHeaders(): LitHtml.TemplateResult {
228 assertNotNullOrUndefined(this.#request);
229
Wolfgang Beyer7f495ee2022-07-08 13:45:13230 const headersWithIssues = [];
231 if (this.#request.wasBlocked()) {
232 const headerWithIssues =
233 BlockedReasonDetails.get((this.#request.blockedReason() as Protocol.Network.BlockedReason));
234 if (headerWithIssues) {
235 headersWithIssues.push(headerWithIssues);
236 }
237 }
238
239 function mergeHeadersWithIssues(
240 headers: SDK.NetworkRequest.NameValue[], headersWithIssues: HeaderDescriptor[]): HeaderDescriptor[] {
241 let i = 0, j = 0;
242 const result: HeaderDescriptor[] = [];
243 while (i < headers.length && j < headersWithIssues.length) {
244 if (headers[i].name < headersWithIssues[j].name) {
245 result.push({...headers[i++], headerNotSet: false});
246 } else if (headers[i].name > headersWithIssues[j].name) {
247 result.push({...headersWithIssues[j++], headerNotSet: true});
248 } else {
249 result.push({...headersWithIssues[j++], ...headers[i++], headerNotSet: false});
250 }
251 }
252 while (i < headers.length) {
253 result.push({...headers[i++], headerNotSet: false});
254 }
255 while (j < headersWithIssues.length) {
256 result.push({...headersWithIssues[j++], headerNotSet: true});
257 }
258 return result;
259 }
260
261 const mergedHeaders = mergeHeadersWithIssues(this.#request.sortedResponseHeaders.slice(), headersWithIssues);
262
Wolfgang Beyer21894d52022-07-26 12:12:44263 const blockedResponseCookies = this.#request.blockedResponseCookies();
264 const blockedCookieLineToReasons = new Map<string, Protocol.Network.SetCookieBlockedReason[]>(
265 blockedResponseCookies?.map(c => [c.cookieLine, c.blockedReasons]));
266 for (const header of mergedHeaders) {
267 if (header.name.toLowerCase() === 'set-cookie' && header.value) {
268 const matchingBlockedReasons = blockedCookieLineToReasons.get(header.value.toString());
269 if (matchingBlockedReasons) {
270 header.setCookieBlockedReasons = matchingBlockedReasons;
271 }
272 }
273 }
274
Wolfgang Beyer235544c2022-05-24 08:07:45275 const toggleShowRaw = (): void => {
276 this.#showResponseHeadersText = !this.#showResponseHeadersText;
277 this.#render();
278 };
279
280 // Disabled until https://crbug.com/1079231 is fixed.
281 // clang-format off
282 return html`
283 <${Category.litTagName}
284 @togglerawevent=${toggleShowRaw}
285 .data=${{
286 name: 'responseHeaders',
287 title: i18nString(UIStrings.responseHeaders),
288 headerCount: this.#request.sortedResponseHeaders.length,
289 checked: this.#request.responseHeadersText ? this.#showResponseHeadersText : undefined,
290 } as CategoryData}
291 aria-label=${i18nString(UIStrings.responseHeaders)}
292 >
Wolfgang Beyerd1dd7962022-05-24 15:48:48293 ${this.#showResponseHeadersText ?
294 this.#renderRawHeaders(this.#request.responseHeadersText, true) : html`
Wolfgang Beyer7f495ee2022-07-08 13:45:13295 ${mergedHeaders.map(header => this.#renderHeader(header))}
Wolfgang Beyer235544c2022-05-24 08:07:45296 `}
297 </${Category.litTagName}>
298 `;
Wolfgang Beyerbcf4a832022-07-25 12:34:47299 // clang-format on
Wolfgang Beyer235544c2022-05-24 08:07:45300 }
301
302 #renderRequestHeaders(): LitHtml.TemplateResult {
303 assertNotNullOrUndefined(this.#request);
304
Wolfgang Beyer7f495ee2022-07-08 13:45:13305 const headers = this.#request.requestHeaders().slice();
306 headers.sort(function(a, b) {
307 return Platform.StringUtilities.compare(a.name.toLowerCase(), b.name.toLowerCase());
308 });
309 const requestHeadersText = this.#request.requestHeadersText();
310
Wolfgang Beyer235544c2022-05-24 08:07:45311 const toggleShowRaw = (): void => {
312 this.#showRequestHeadersText = !this.#showRequestHeadersText;
313 this.#render();
314 };
315
Wolfgang Beyer235544c2022-05-24 08:07:45316 // Disabled until https://crbug.com/1079231 is fixed.
317 // clang-format off
318 return html`
319 <${Category.litTagName}
320 @togglerawevent=${toggleShowRaw}
321 .data=${{
322 name: 'requestHeaders',
323 title: i18nString(UIStrings.requestHeaders),
324 headerCount: this.#request.requestHeaders().length,
325 checked: requestHeadersText? this.#showRequestHeadersText : undefined,
326 } as CategoryData}
327 aria-label=${i18nString(UIStrings.requestHeaders)}
328 >
Wolfgang Beyerd1dd7962022-05-24 15:48:48329 ${(this.#showRequestHeadersText && requestHeadersText) ?
330 this.#renderRawHeaders(requestHeadersText, false) : html`
Wolfgang Beyerd0ab3a62022-07-12 12:59:02331 ${this.#maybeRenderProvisionalHeadersWarning()}
Wolfgang Beyer7f495ee2022-07-08 13:45:13332 ${headers.map(header => this.#renderHeader({...header, headerNotSet: false}))}
Wolfgang Beyer235544c2022-05-24 08:07:45333 `}
334 </${Category.litTagName}>
335 `;
Wolfgang Beyerbcf4a832022-07-25 12:34:47336 // clang-format on
Wolfgang Beyer235544c2022-05-24 08:07:45337 }
338
Wolfgang Beyerd0ab3a62022-07-12 12:59:02339 #maybeRenderProvisionalHeadersWarning(): LitHtml.LitTemplate {
340 assertNotNullOrUndefined(this.#request);
341 if (this.#request.requestHeadersText() !== undefined) {
342 return LitHtml.nothing;
343 }
344
345 let cautionText;
346 let cautionTitle = '';
347 if (this.#request.cachedInMemory() || this.#request.cached()) {
348 cautionText = i18nString(UIStrings.provisionalHeadersAreShownDisableCache);
349 cautionTitle = i18nString(UIStrings.onlyProvisionalHeadersAre);
350 } else {
351 cautionText = i18nString(UIStrings.provisionalHeadersAreShown);
352 }
Wolfgang Beyerb7dadd32022-07-27 12:34:52353 // Disabled until https://crbug.com/1079231 is fixed.
354 // clang-format off
Wolfgang Beyerd0ab3a62022-07-12 12:59:02355 return html`
356 <div class="call-to-action">
357 <div class="call-to-action-body">
358 <div class="explanation" title=${cautionTitle}>
359 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
Wolfgang Beyerb7dadd32022-07-27 12:34:52360 iconName: 'clear-warning_icon',
361 width: '12px',
362 height: '12px',
Wolfgang Beyerd0ab3a62022-07-12 12:59:02363 } as IconButton.Icon.IconData}>
364 </${IconButton.Icon.Icon.litTagName}>
365 ${cautionText} <x-link href="https://developer.chrome.com/docs/devtools/network/reference/#provisional-headers" class="link">${i18nString(UIStrings.learnMore)}</x-link>
366 </div>
367 </div>
368 </div>
369 `;
Wolfgang Beyerb7dadd32022-07-27 12:34:52370 // clang-format on
Wolfgang Beyerd0ab3a62022-07-12 12:59:02371 }
372
Wolfgang Beyer7f495ee2022-07-08 13:45:13373 #renderHeader(header: HeaderDescriptor): LitHtml.TemplateResult {
Wolfgang Beyerbcf4a832022-07-25 12:34:47374 // Disabled until https://crbug.com/1079231 is fixed.
375 // clang-format off
Wolfgang Beyer7f495ee2022-07-08 13:45:13376 return html`
377 <div class="row">
Wolfgang Beyerbcf4a832022-07-25 12:34:47378 <div class="header-name">
Wolfgang Beyerfccc2762022-07-27 12:03:09379 ${header.headerNotSet ?
380 html`<div class="header-badge header-badge-text">${i18n.i18n.lockedString('not-set')}</div>` :
381 LitHtml.nothing
382 }${header.name}:
Wolfgang Beyerbcf4a832022-07-25 12:34:47383 </div>
Wolfgang Beyer21894d52022-07-26 12:12:44384 <div class="header-value ${header.headerValueIncorrect ? 'header-warning' : ''}">
385 ${header.value?.toString() || ''}
386 ${this.#maybeRenderHeaderValueSuffix(header)}
387 </div>
Wolfgang Beyer7f495ee2022-07-08 13:45:13388 </div>
Wolfgang Beyer21894d52022-07-26 12:12:44389 ${this.#maybeRenderBlockedDetails(header.blockedDetails)}
Wolfgang Beyer7f495ee2022-07-08 13:45:13390 `;
Wolfgang Beyerbcf4a832022-07-25 12:34:47391 // clang-format on
392 }
393
Wolfgang Beyer21894d52022-07-26 12:12:44394 #maybeRenderHeaderValueSuffix(header: HeaderDescriptor): LitHtml.LitTemplate {
Wolfgang Beyerbcf4a832022-07-25 12:34:47395 const headerId = header.name.toLowerCase();
Wolfgang Beyer21894d52022-07-26 12:12:44396
397 if (headerId === 'set-cookie' && header.setCookieBlockedReasons) {
398 const titleText =
399 header.setCookieBlockedReasons.map(SDK.NetworkRequest.setCookieBlockedReasonToUiString).join('\n');
400 // Disabled until https://crbug.com/1079231 is fixed.
401 // clang-format off
402 return html`
403 <${IconButton.Icon.Icon.litTagName} class="inline-icon" title=${titleText} .data=${{
Wolfgang Beyerb7dadd32022-07-27 12:34:52404 iconName: 'clear-warning_icon',
405 width: '12px',
406 height: '12px',
407 } as IconButton.Icon.IconData}>
Wolfgang Beyer21894d52022-07-26 12:12:44408 </${IconButton.Icon.Icon.litTagName}>
409 `;
410 // clang-format on
411 }
412
Wolfgang Beyerbcf4a832022-07-25 12:34:47413 if (headerId === 'x-client-data') {
414 const data = ClientVariations.parseClientVariations(header.value?.toString() || '');
415 const output = ClientVariations.formatClientVariations(
416 data, i18nString(UIStrings.activeClientExperimentVariation),
417 i18nString(UIStrings.activeClientExperimentVariationIds));
418 return html`
Wolfgang Beyer21894d52022-07-26 12:12:44419 <div>${i18nString(UIStrings.decoded)}</div>
420 <code>${output}</code>
Wolfgang Beyerbcf4a832022-07-25 12:34:47421 `;
422 }
423
Wolfgang Beyer21894d52022-07-26 12:12:44424 return LitHtml.nothing;
Wolfgang Beyer7f495ee2022-07-08 13:45:13425 }
426
Wolfgang Beyer21894d52022-07-26 12:12:44427 #maybeRenderBlockedDetails(blockedDetails?: BlockedDetailsDescriptor): LitHtml.LitTemplate {
428 if (!blockedDetails) {
Wolfgang Beyer7f495ee2022-07-08 13:45:13429 return LitHtml.nothing;
430 }
Wolfgang Beyer21894d52022-07-26 12:12:44431 // Disabled until https://crbug.com/1079231 is fixed.
432 // clang-format off
Wolfgang Beyer7f495ee2022-07-08 13:45:13433 return html`
Wolfgang Beyerd0ab3a62022-07-12 12:59:02434 <div class="call-to-action">
435 <div class="call-to-action-body">
Wolfgang Beyer21894d52022-07-26 12:12:44436 <div class="explanation">${blockedDetails.explanation()}</div>
437 ${blockedDetails.examples.map(example => html`
Wolfgang Beyerd0ab3a62022-07-12 12:59:02438 <div class="example">
439 <code>${example.codeSnippet}</code>
440 ${example.comment ? html`
441 <span class="comment">${example.comment()}</span>
442 ` : ''}
443 </div>
444 `)}
Wolfgang Beyer21894d52022-07-26 12:12:44445 ${this.#maybeRenderBlockedDetailsLink(blockedDetails)}
Wolfgang Beyer7f495ee2022-07-08 13:45:13446 </div>
447 </div>
448 `;
Wolfgang Beyer21894d52022-07-26 12:12:44449 // clang-format on
Wolfgang Beyer7f495ee2022-07-08 13:45:13450 }
451
Wolfgang Beyer21894d52022-07-26 12:12:44452 #maybeRenderBlockedDetailsLink(blockedDetails?: BlockedDetailsDescriptor): LitHtml.LitTemplate {
Wolfgang Beyer7f495ee2022-07-08 13:45:13453 if (this.#request && IssuesManager.RelatedIssue.hasIssueOfCategory(this.#request, IssuesManager.Issue.IssueCategory.CrossOriginEmbedderPolicy)) {
454 const followLink = (): void => {
455 Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.LearnMoreLinkCOEP);
456 if (this.#request) {
457 void IssuesManager.RelatedIssue.reveal(
458 this.#request, IssuesManager.Issue.IssueCategory.CrossOriginEmbedderPolicy);
459 }
460 };
461 return html`
462 <div class="devtools-link" @click=${followLink}>
463 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
464 iconName: 'issue-exclamation-icon',
465 color: 'var(--issue-color-yellow)',
466 width: '16px',
467 height: '16px',
468 } as IconButton.Icon.IconData}>
469 </${IconButton.Icon.Icon.litTagName}>
470 ${i18nString(UIStrings.learnMoreInTheIssuesTab)}
471 </div>
472 `;
473 }
Wolfgang Beyer21894d52022-07-26 12:12:44474 if (blockedDetails?.link) {
475 // Disabled until https://crbug.com/1079231 is fixed.
476 // clang-format off
Wolfgang Beyer7f495ee2022-07-08 13:45:13477 return html`
Wolfgang Beyer21894d52022-07-26 12:12:44478 <x-link href=${blockedDetails.link.url} class="link">
Wolfgang Beyer7f495ee2022-07-08 13:45:13479 <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
480 iconName: 'link_icon',
481 color: 'var(--color-link)',
482 width: '16px',
483 height: '16px',
484 } as IconButton.Icon.IconData}>
485 </${IconButton.Icon.Icon.litTagName}
486 >${i18nString(UIStrings.learnMore)}
487 </x-link>
488 `;
Wolfgang Beyer21894d52022-07-26 12:12:44489 // clang-format on
Wolfgang Beyer7f495ee2022-07-08 13:45:13490 }
491 return LitHtml.nothing;
492 }
493
Wolfgang Beyerd1dd7962022-05-24 15:48:48494 #renderRawHeaders(rawHeadersText: string, forResponseHeaders: boolean): LitHtml.TemplateResult {
495 const trimmed = rawHeadersText.trim();
496 const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
497 const isShortened = !showFull && trimmed.length > RAW_HEADER_CUTOFF;
498
499 const showMore = ():void => {
500 if (forResponseHeaders) {
501 this.#showResponseHeadersTextFull = true;
502 } else {
503 this.#showRequestHeadersTextFull = true;
504 }
505 this.#render();
506 };
507
508 const onContextMenuOpen = (event: Event): void => {
509 const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
510 if (!showFull) {
511 const contextMenu = new UI.ContextMenu.ContextMenu(event);
512 const section = contextMenu.newSection();
513 section.appendItem(i18nString(UIStrings.showMore), showMore);
514 void contextMenu.show();
515 }
516 };
517
518 const addContextMenuListener = (el: Element):void => {
519 if (isShortened) {
520 el.addEventListener('contextmenu', onContextMenuOpen);
521 }
522 };
523
524 return html`
525 <div class="row raw-headers-row" on-render=${ComponentHelpers.Directives.nodeRenderedCallback(addContextMenuListener)}>
526 <div class="raw-headers">${isShortened ? trimmed.substring(0, RAW_HEADER_CUTOFF) : trimmed}</div>
527 ${isShortened ? html`
528 <${Buttons.Button.Button.litTagName}
529 .size=${Buttons.Button.Size.SMALL}
530 .variant=${Buttons.Button.Variant.SECONDARY}
531 @click=${showMore}
532 >${i18nString(UIStrings.showMore)}</${Buttons.Button.Button.litTagName}>
533 ` : LitHtml.nothing}
534 </div>
535 `;
536 }
537
Wolfgang Beyer40530b682022-05-17 13:02:01538 #renderGeneralSection(): LitHtml.TemplateResult {
539 assertNotNullOrUndefined(this.#request);
540
541 let coloredCircleClassName = 'red-circle';
542 if (this.#request.statusCode < 300 || this.#request.statusCode === 304) {
543 coloredCircleClassName = 'green-circle';
544 } else if (this.#request.statusCode < 400) {
545 coloredCircleClassName = 'yellow-circle';
546 }
547
548 let statusText = this.#request.statusCode + ' ' + this.#request.statusText;
549 let statusTextHasComment = false;
550 if (this.#request.cachedInMemory()) {
551 statusText += ' ' + i18nString(UIStrings.fromMemoryCache);
552 statusTextHasComment = true;
553 } else if (this.#request.fetchedViaServiceWorker) {
554 statusText += ' ' + i18nString(UIStrings.fromServiceWorker);
555 statusTextHasComment = true;
556 } else if (this.#request.redirectSourceSignedExchangeInfoHasNoErrors()) {
557 statusText += ' ' + i18nString(UIStrings.fromSignedexchange);
558 statusTextHasComment = true;
559 } else if (this.#request.webBundleInnerRequestInfo()) {
560 statusText += ' ' + i18nString(UIStrings.fromWebBundle);
561 statusTextHasComment = true;
562 } else if (this.#request.fromPrefetchCache()) {
563 statusText += ' ' + i18nString(UIStrings.fromPrefetchCache);
564 statusTextHasComment = true;
565 } else if (this.#request.cached()) {
566 statusText += ' ' + i18nString(UIStrings.fromDiskCache);
567 statusTextHasComment = true;
568 }
569
570 // Disabled until https://crbug.com/1079231 is fixed.
571 // clang-format off
572 return html`
Wolfgang Beyer235544c2022-05-24 08:07:45573 <${Category.litTagName}
574 .data=${{name: 'general', title: i18nString(UIStrings.general)} as CategoryData}
575 aria-label=${i18nString(UIStrings.general)}
576 >
Wolfgang Beyer40530b682022-05-17 13:02:01577 <div class="row">
578 <div class="header-name">${i18nString(UIStrings.requestUrl)}:</div>
579 <div class="header-value">${this.#request.url()}</div>
580 </div>
581 ${this.#request.statusCode? html`
582 <div class="row">
583 <div class="header-name">${i18nString(UIStrings.requestMethod)}:</div>
584 <div class="header-value">${this.#request.requestMethod}</div>
585 </div>
586 <div class="row">
587 <div class="header-name">${i18nString(UIStrings.statusCode)}:</div>
588 <div class="header-value ${coloredCircleClassName} ${statusTextHasComment ? 'status-with-comment' : ''}">${statusText}</div>
589 </div>
590 ` : ''}
591 ${this.#request.remoteAddress()? html`
592 <div class="row">
593 <div class="header-name">${i18nString(UIStrings.remoteAddress)}:</div>
594 <div class="header-value">${this.#request.remoteAddress()}</div>
595 </div>
596 ` : ''}
597 ${this.#request.referrerPolicy()? html`
598 <div class="row">
599 <div class="header-name">${i18nString(UIStrings.referrerPolicy)}:</div>
600 <div class="header-value">${this.#request.referrerPolicy()}</div>
601 </div>
602 ` : ''}
603 </${Category.litTagName}>
604 `;
605 // clang-format on
606 }
607}
608
Wolfgang Beyer235544c2022-05-24 08:07:45609export class ToggleRawHeadersEvent extends Event {
610 static readonly eventName = 'togglerawevent';
611
612 constructor() {
613 super(ToggleRawHeadersEvent.eventName, {});
614 }
615}
616
Wolfgang Beyer40530b682022-05-17 13:02:01617export interface CategoryData {
618 name: string;
619 title: Common.UIString.LocalizedString;
Wolfgang Beyer235544c2022-05-24 08:07:45620 headerCount?: number;
621 checked?: boolean;
Wolfgang Beyer40530b682022-05-17 13:02:01622}
623
624export class Category extends HTMLElement {
625 static readonly litTagName = LitHtml.literal`devtools-request-headers-category`;
626 readonly #shadow = this.attachShadow({mode: 'open'});
627 #expandedSetting?: Common.Settings.Setting<boolean>;
628 #title: Common.UIString.LocalizedString = Common.UIString.LocalizedEmptyString;
Wolfgang Beyer235544c2022-05-24 08:07:45629 #headerCount?: number = undefined;
630 #checked: boolean|undefined = undefined;
Wolfgang Beyer40530b682022-05-17 13:02:01631
632 connectedCallback(): void {
Jack Franklin1f19d522022-07-06 14:00:18633 this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles, Input.checkboxStyles];
Wolfgang Beyer40530b682022-05-17 13:02:01634 }
635
636 set data(data: CategoryData) {
637 this.#title = data.title;
638 this.#expandedSetting =
639 Common.Settings.Settings.instance().createSetting('request-info-' + data.name + '-category-expanded', true);
Wolfgang Beyer235544c2022-05-24 08:07:45640 this.#headerCount = data.headerCount;
641 this.#checked = data.checked;
Wolfgang Beyer40530b682022-05-17 13:02:01642 this.#render();
643 }
644
Wolfgang Beyer235544c2022-05-24 08:07:45645 #onCheckboxToggle(): void {
646 this.dispatchEvent(new ToggleRawHeadersEvent());
647 }
648
Wolfgang Beyer40530b682022-05-17 13:02:01649 #render(): void {
Wolfgang Beyer235544c2022-05-24 08:07:45650 const isOpen = this.#expandedSetting ? this.#expandedSetting.get() : true;
Wolfgang Beyer40530b682022-05-17 13:02:01651 // Disabled until https://crbug.com/1079231 is fixed.
652 // clang-format off
653 render(html`
Wolfgang Beyer235544c2022-05-24 08:07:45654 <details ?open=${isOpen} @toggle=${this.#onToggle}>
655 <summary class="header" @keydown=${this.#onSummaryKeyDown}>
656 ${this.#title}${this.#headerCount ?
657 html`<span class="header-count"> (${this.#headerCount})</span>` :
658 LitHtml.nothing
659 }
660 ${this.#checked !== undefined ? html`
661 <span class="raw-checkbox-container">
662 <label>
663 <input type="checkbox" .checked=${this.#checked} @change=${this.#onCheckboxToggle} />
664 ${i18nString(UIStrings.raw)}
665 </label>
666 </span>
667 ` : LitHtml.nothing}
668 </summary>
Wolfgang Beyer40530b682022-05-17 13:02:01669 <slot></slot>
670 </details>
671 `, this.#shadow, {host: this});
672 // clang-format on
673 }
674
675 #onSummaryKeyDown(event: KeyboardEvent): void {
676 if (!event.target) {
677 return;
678 }
679 const summaryElement = event.target as HTMLElement;
680 const detailsElement = summaryElement.parentElement as HTMLDetailsElement;
681 if (!detailsElement) {
682 throw new Error('<details> element is not found for a <summary> element');
683 }
684 switch (event.key) {
685 case 'ArrowLeft':
686 detailsElement.open = false;
687 break;
688 case 'ArrowRight':
689 detailsElement.open = true;
690 break;
691 }
692 }
693
694 #onToggle(event: Event): void {
695 this.#expandedSetting?.set((event.target as HTMLDetailsElement).open);
696 }
697}
698
699ComponentHelpers.CustomElements.defineComponent('devtools-request-headers', RequestHeadersComponent);
700ComponentHelpers.CustomElements.defineComponent('devtools-request-headers-category', Category);
701
702declare global {
703 // eslint-disable-next-line @typescript-eslint/no-unused-vars
704 interface HTMLElementTagNameMap {
705 'devtools-request-headers': RequestHeadersComponent;
706 'devtools-request-headers-category': Category;
707 }
708}
Wolfgang Beyer7f495ee2022-07-08 13:45:13709
Wolfgang Beyer21894d52022-07-26 12:12:44710interface BlockedDetailsDescriptor {
Wolfgang Beyer7f495ee2022-07-08 13:45:13711 explanation: () => string;
712 examples: Array<{
713 codeSnippet: string,
714 comment?: (() => string),
715 }>;
716 link: {
717 url: string,
718 }|null;
719}
720
721interface HeaderDescriptor {
722 name: string;
723 value: Object|null;
724 headerValueIncorrect?: boolean|null;
Wolfgang Beyer21894d52022-07-26 12:12:44725 blockedDetails?: BlockedDetailsDescriptor;
Wolfgang Beyer7f495ee2022-07-08 13:45:13726 headerNotSet: boolean|null;
Wolfgang Beyer21894d52022-07-26 12:12:44727 setCookieBlockedReasons?: Protocol.Network.SetCookieBlockedReason[];
Wolfgang Beyer7f495ee2022-07-08 13:45:13728}
729
730const BlockedReasonDetails = new Map<Protocol.Network.BlockedReason, HeaderDescriptor>([
731 [
732 Protocol.Network.BlockedReason.CoepFrameResourceNeedsCoepHeader,
733 {
734 name: 'cross-origin-embedder-policy',
735 value: null,
736 headerValueIncorrect: null,
Wolfgang Beyer21894d52022-07-26 12:12:44737 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13738 explanation: i18nLazyString(UIStrings.toEmbedThisFrameInYourDocument),
739 examples: [{codeSnippet: 'Cross-Origin-Embedder-Policy: require-corp', comment: undefined}],
740 link: {url: 'https://web.dev/coop-coep/'},
741 },
742 headerNotSet: null,
743 },
744 ],
745 [
746 Protocol.Network.BlockedReason.CorpNotSameOriginAfterDefaultedToSameOriginByCoep,
747 {
748 name: 'cross-origin-resource-policy',
749 value: null,
750 headerValueIncorrect: null,
Wolfgang Beyer21894d52022-07-26 12:12:44751 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13752 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferent),
753 examples: [
754 {
755 codeSnippet: 'Cross-Origin-Resource-Policy: same-site',
756 comment: i18nLazyString(UIStrings.chooseThisOptionIfTheResourceAnd),
757 },
758 {
759 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
760 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
761 },
762 ],
763 link: {url: 'https://web.dev/coop-coep/'},
764 },
765 headerNotSet: null,
766 },
767 ],
768 [
769 Protocol.Network.BlockedReason.CoopSandboxedIframeCannotNavigateToCoopPage,
770 {
771 name: 'cross-origin-opener-policy',
772 value: null,
773 headerValueIncorrect: false,
Wolfgang Beyer21894d52022-07-26 12:12:44774 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13775 explanation: i18nLazyString(UIStrings.thisDocumentWasBlockedFrom),
776 examples: [],
777 link: {url: 'https://web.dev/coop-coep/'},
778 },
779 headerNotSet: null,
780 },
781 ],
782 [
783 Protocol.Network.BlockedReason.CorpNotSameSite,
784 {
785 name: 'cross-origin-resource-policy',
786 value: null,
787 headerValueIncorrect: true,
Wolfgang Beyer21894d52022-07-26 12:12:44788 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13789 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferentSite),
790 examples: [
791 {
792 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
793 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
794 },
795 ],
796 link: null,
797 },
798 headerNotSet: null,
799 },
800 ],
801 [
802 Protocol.Network.BlockedReason.CorpNotSameOrigin,
803 {
804 name: 'cross-origin-resource-policy',
805 value: null,
806 headerValueIncorrect: true,
Wolfgang Beyer21894d52022-07-26 12:12:44807 blockedDetails: {
Wolfgang Beyer7f495ee2022-07-08 13:45:13808 explanation: i18nLazyString(UIStrings.toUseThisResourceFromADifferentOrigin),
809 examples: [
810 {
811 codeSnippet: 'Cross-Origin-Resource-Policy: same-site',
812 comment: i18nLazyString(UIStrings.chooseThisOptionIfTheResourceAnd),
813 },
814 {
815 codeSnippet: 'Cross-Origin-Resource-Policy: cross-origin',
816 comment: i18nLazyString(UIStrings.onlyChooseThisOptionIfAn),
817 },
818 ],
819 link: null,
820 },
821 headerNotSet: null,
822 },
823 ],
824]);