chrome-devtools-frontend 1.0.1661063 → 1.0.1662289

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +19 -9
  2. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +44 -16
  3. package/.agents/skills/migrate-chromium-test/SKILL.md +7 -2
  4. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +7 -1
  5. package/docs/ui_engineering.md +75 -42
  6. package/front_end/core/common/SettingRegistration.ts +0 -5
  7. package/front_end/core/platform/ArrayUtilities.ts +10 -0
  8. package/front_end/foundation/README.md +86 -0
  9. package/front_end/generated/InspectorBackendCommands.ts +4 -5
  10. package/front_end/generated/protocol.ts +20 -38
  11. package/front_end/models/bindings/CompilerScriptMapping.ts +5 -4
  12. package/front_end/models/bindings/ContentProviderBasedProject.ts +2 -3
  13. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +4 -3
  14. package/front_end/models/bindings/DefaultScriptMapping.ts +3 -3
  15. package/front_end/models/bindings/NetworkProject.ts +10 -1
  16. package/front_end/models/bindings/ResourceMapping.ts +4 -3
  17. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -1
  18. package/front_end/models/bindings/SASSSourceMapping.ts +4 -3
  19. package/front_end/models/bindings/StylesSourceMapping.ts +3 -3
  20. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +7 -5
  21. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +14 -5
  22. package/front_end/models/issues_manager/CheckFormsIssuesTrigger.ts +8 -9
  23. package/front_end/models/issues_manager/RelatedIssue.ts +10 -8
  24. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  25. package/front_end/models/logs/RequestResolver.ts +2 -2
  26. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +0 -5
  27. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +1 -1
  28. package/front_end/models/trace/handlers/FramesHandler.ts +4 -7
  29. package/front_end/models/trace/handlers/ModelHandlers.ts +0 -1
  30. package/front_end/models/trace/handlers/RendererHandler.ts +2 -15
  31. package/front_end/models/trace/handlers/Threads.ts +4 -11
  32. package/front_end/models/trace/insights/DOMSize.ts +1 -1
  33. package/front_end/models/workspace/WorkspaceImpl.ts +1 -10
  34. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -44
  35. package/front_end/panels/application/CookieItemsView.ts +1 -1
  36. package/front_end/panels/application/FrameDetailsView.ts +1 -1
  37. package/front_end/panels/application/ServiceWorkersView.ts +494 -411
  38. package/front_end/panels/application/application.ts +0 -6
  39. package/front_end/panels/application/components/components.ts +0 -2
  40. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +1 -1
  41. package/front_end/panels/application/serviceWorkersView.css +40 -4
  42. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +147 -123
  43. package/front_end/panels/common/ExtensionServer.ts +3 -3
  44. package/front_end/panels/console/ConsoleView.ts +1 -1
  45. package/front_end/panels/emulation/DeviceModeView.ts +139 -70
  46. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  47. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +53 -12
  48. package/front_end/panels/network/RequestPayloadView.ts +77 -39
  49. package/front_end/panels/network/components/ResponseHeaderSection.ts +5 -3
  50. package/front_end/panels/profiler/HeapSnapshotView.ts +2 -2
  51. package/front_end/panels/recorder/RecorderController.ts +213 -49
  52. package/front_end/panels/recorder/RecorderPanel.ts +1 -1
  53. package/front_end/panels/sources/SourcesNavigator.ts +3 -2
  54. package/front_end/panels/sources/SourcesView.ts +0 -17
  55. package/front_end/panels/sources/UISourceCodeFrame.ts +42 -0
  56. package/front_end/panels/sources/sources.ts +0 -2
  57. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -17
  58. package/front_end/panels/timeline/ThreadAppender.ts +0 -106
  59. package/front_end/third_party/chromium/README.chromium +1 -1
  60. package/front_end/ui/components/report_view/ReportView.ts +11 -5
  61. package/front_end/ui/components/report_view/report.css +4 -1
  62. package/front_end/ui/legacy/TextPrompt.ts +49 -1
  63. package/front_end/ui/legacy/UIUtils.ts +11 -1
  64. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +2 -2
  65. package/front_end/ui/legacy/textPrompt.css +10 -0
  66. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  67. package/package.json +1 -1
  68. package/front_end/models/trace/handlers/AuctionWorkletsHandler.ts +0 -183
  69. package/front_end/panels/application/InterestGroupStorageModel.ts +0 -81
  70. package/front_end/panels/application/InterestGroupStorageView.ts +0 -151
  71. package/front_end/panels/application/InterestGroupTreeElement.ts +0 -65
  72. package/front_end/panels/application/components/InterestGroupAccessGrid.ts +0 -135
  73. package/front_end/panels/application/components/interestGroupAccessGrid.css +0 -27
  74. package/front_end/panels/application/interestGroupStorageView.css +0 -9
  75. package/front_end/panels/sources/InplaceFormatterEditorAction.ts +0 -149
@@ -113,9 +113,17 @@ export class DeviceModeView extends UI.Widget.VBox {
113
113
  this.showRulersSetting = Common.Settings.Settings.instance().moduleSetting('emulation.show-rulers');
114
114
  this.showRulersSetting.addChangeListener(this.updateUI, this);
115
115
 
116
- this.topRuler = new Ruler(true, this.model.setWidthAndScaleToFit.bind(this.model));
116
+ this.topRuler = new Ruler();
117
+ this.topRuler.horizontal = true;
118
+ this.topRuler.addEventListener(RulerEvents.MARKER_SELECTED, event => {
119
+ this.model.setWidthAndScaleToFit(event.data);
120
+ });
117
121
  this.topRuler.element.classList.add('device-mode-ruler-top');
118
- this.leftRuler = new Ruler(false, this.model.setHeightAndScaleToFit.bind(this.model));
122
+ this.leftRuler = new Ruler();
123
+ this.leftRuler.horizontal = false;
124
+ this.leftRuler.addEventListener(RulerEvents.MARKER_SELECTED, event => {
125
+ this.model.setHeightAndScaleToFit(event.data);
126
+ });
119
127
  this.leftRuler.element.classList.add('device-mode-ruler-left');
120
128
  this.createUI();
121
129
  UI.ZoomManager.ZoomManager.instance().addEventListener(UI.ZoomManager.Events.ZOOM_CHANGED, this.zoomChanged, this);
@@ -566,86 +574,147 @@ export class DeviceModeView extends UI.Widget.VBox {
566
574
  }
567
575
  }
568
576
 
569
- export class Ruler extends UI.Widget.VBox {
570
- #contentElement: HTMLElement;
571
- private readonly horizontal: boolean;
572
- private scale: number;
573
- private readonly throttler: Common.Throttler.Throttler;
574
- private readonly applyCallback: (arg0: number) => void;
575
- constructor(horizontal: boolean, applyCallback: (arg0: number) => void) {
576
- super({jslog: `${VisualLogging.deviceModeRuler().track({click: true})}`});
577
- this.element.classList.add('device-mode-ruler');
578
- this.#contentElement =
579
- this.element.createChild('div', 'device-mode-ruler-content').createChild('div', 'device-mode-ruler-inner');
580
- this.horizontal = horizontal;
581
- this.scale = 1;
582
- this.throttler = new Common.Throttler.Throttler(0);
583
- this.applyCallback = applyCallback;
577
+ export interface RulerViewInput {
578
+ horizontal: boolean;
579
+ scale: number;
580
+ onMarkerClick: (size: number) => void;
581
+ }
582
+
583
+ export type RulerView = (input: RulerViewInput, output: undefined, target: HTMLElement) => void;
584
+
585
+ export const DEFAULT_RULER_VIEW: RulerView = (input: RulerViewInput, output: undefined, target: HTMLElement): void => {
586
+ const zoomFactor = UI.ZoomManager.ZoomManager.instance().zoomFactor();
587
+ const size = input.horizontal ? target.offsetWidth : target.offsetHeight;
588
+
589
+ const dipSize = size * zoomFactor / input.scale;
590
+ const count = Math.ceil(dipSize / 5);
591
+ let step = 1;
592
+ if (input.scale < 0.8) {
593
+ step = 2;
594
+ }
595
+ if (input.scale < 0.6) {
596
+ step = 4;
597
+ }
598
+ if (input.scale < 0.4) {
599
+ step = 8;
600
+ }
601
+ if (input.scale < 0.2) {
602
+ step = 16;
603
+ }
604
+ if (input.scale < 0.1) {
605
+ step = 32;
584
606
  }
585
607
 
586
- render(scale: number): void {
587
- this.scale = scale;
588
- void this.throttler.schedule(this.update.bind(this));
608
+ const markers = [];
609
+ for (let i = 0; i < count; i++) {
610
+ if (i % step) {
611
+ continue;
612
+ }
613
+ const isLarge = !(i % 10);
614
+ const isMedium = !(i % 5);
615
+ const offset = i ? `${(5 * i) * input.scale / zoomFactor}px` : undefined;
616
+
617
+ // clang-format off
618
+ markers.push(html`
619
+ <div
620
+ class=${classMap({
621
+ 'device-mode-ruler-marker': true,
622
+ 'device-mode-ruler-marker-large': isLarge,
623
+ 'device-mode-ruler-marker-medium': isMedium && !isLarge,
624
+ })}
625
+ style=${styleMap(input.horizontal ? {left: offset} : {top: offset})}>
626
+ ${i && !(i % 20) ?
627
+ html`<div class="device-mode-ruler-text" @click=${() => input.onMarkerClick(i * 5)}>${i * 5}</div>` :
628
+ nothing}
629
+ </div>
630
+ `);
631
+ // clang-format on
589
632
  }
590
633
 
591
- override onResize(): void {
592
- void this.throttler.schedule(this.update.bind(this));
634
+ render(html`
635
+ <div class="device-mode-ruler-content">
636
+ <div class="device-mode-ruler-inner">
637
+ ${markers}
638
+ </div>
639
+ </div>
640
+ `,
641
+ target, {
642
+ container: {
643
+ classes: ['device-mode-ruler'],
644
+ attributes: {jslog: VisualLogging.deviceModeRuler().track({click: true})},
645
+ },
646
+ });
647
+ };
648
+
649
+ export const enum RulerEvents {
650
+ MARKER_SELECTED = 'MarkerSelected',
651
+ }
652
+
653
+ export interface RulerEventTypes {
654
+ [RulerEvents.MARKER_SELECTED]: number;
655
+ }
656
+
657
+ export class Ruler extends Common.ObjectWrapper.eventMixin<RulerEventTypes, typeof UI.Widget.Widget>(UI.Widget.Widget) {
658
+ #view: RulerView;
659
+ #horizontal = true;
660
+ #scale = 1;
661
+
662
+ constructor(element?: HTMLElement, view: RulerView = DEFAULT_RULER_VIEW) {
663
+ super(element);
664
+ this.#view = view;
593
665
  }
594
666
 
595
- update(): void {
596
- const zoomFactor = UI.ZoomManager.ZoomManager.instance().zoomFactor();
597
- const size = this.horizontal ? this.#contentElement.offsetWidth : this.#contentElement.offsetHeight;
667
+ get horizontal(): boolean {
668
+ return this.#horizontal;
669
+ }
598
670
 
599
- const dipSize = size * zoomFactor / this.scale;
600
- const count = Math.ceil(dipSize / 5);
601
- let step = 1;
602
- if (this.scale < 0.8) {
603
- step = 2;
604
- }
605
- if (this.scale < 0.6) {
606
- step = 4;
607
- }
608
- if (this.scale < 0.4) {
609
- step = 8;
610
- }
611
- if (this.scale < 0.2) {
612
- step = 16;
613
- }
614
- if (this.scale < 0.1) {
615
- step = 32;
671
+ set horizontal(horizontal: boolean) {
672
+ if (this.#horizontal === horizontal) {
673
+ return;
616
674
  }
675
+ this.#horizontal = horizontal;
676
+ this.requestUpdate();
677
+ }
617
678
 
618
- const markers = [];
619
- for (let i = 0; i < count; i++) {
620
- if (i % step) {
621
- continue;
622
- }
623
- const isLarge = !(i % 10);
624
- const isMedium = !(i % 5);
625
- const offset = i ? `${(5 * i) * this.scale / zoomFactor}px` : undefined;
626
-
627
- // clang-format off
628
- markers.push(html`
629
- <div
630
- class=${classMap({
631
- 'device-mode-ruler-marker': true,
632
- 'device-mode-ruler-marker-large': isLarge,
633
- 'device-mode-ruler-marker-medium': isMedium && !isLarge,
634
- })}
635
- style=${styleMap(this.horizontal ? {left: offset} : {top: offset})}>
636
- ${i && !(i % 20) ?
637
- html`<div class="device-mode-ruler-text" @click=${() => this.onMarkerClick(i * 5)}>${i * 5}</div>` :
638
- nothing}
639
- </div>
640
- `);
641
- // clang-format on
679
+ get scale(): number {
680
+ return this.#scale;
681
+ }
682
+
683
+ set scale(scale: number) {
684
+ if (this.#scale === scale) {
685
+ return;
642
686
  }
687
+ this.#scale = scale;
688
+ this.requestUpdate();
689
+ }
643
690
 
644
- // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
645
- render(html`${markers}`, this.#contentElement);
691
+ render(scale: number): void {
692
+ this.scale = scale;
646
693
  }
647
694
 
648
- private onMarkerClick(size: number): void {
649
- this.applyCallback.call(null, size);
695
+ override wasShown(): void {
696
+ super.wasShown();
697
+ this.requestUpdate();
698
+ }
699
+
700
+ override onResize(): void {
701
+ super.onResize();
702
+ this.requestUpdate();
703
+ }
704
+
705
+ #onMarkerClick = (size: number): void => {
706
+ this.dispatchEventToListeners(RulerEvents.MARKER_SELECTED, size);
707
+ };
708
+
709
+ override performUpdate(): void {
710
+ if (!this.isShowing()) {
711
+ return;
712
+ }
713
+ const viewInput: RulerViewInput = {
714
+ horizontal: this.#horizontal,
715
+ scale: this.#scale,
716
+ onMarkerClick: this.#onMarkerClick,
717
+ };
718
+ this.#view(viewInput, undefined, this.contentElement);
650
719
  }
651
720
  }
@@ -84,7 +84,7 @@ export abstract class AffectedResourcesView extends UI.TreeOutline.TreeElement {
84
84
 
85
85
  this.affectedResources = this.createAffectedResources();
86
86
  this.#affectedResourcesCount = 0;
87
- this.requestResolver = new Logs.RequestResolver.RequestResolver();
87
+ this.requestResolver = new Logs.RequestResolver.RequestResolver(Logs.NetworkLog.NetworkLog.instance());
88
88
  this.#frameListeners = [];
89
89
  this.#unresolvedFrameIds = new Set();
90
90
  }
@@ -5,7 +5,7 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import type * as Platform from '../../core/platform/platform.js';
8
- import type * as ProtocolClient from '../../core/protocol_client/protocol_client.js';
8
+ import * as ProtocolClient from '../../core/protocol_client/protocol_client.js';
9
9
  import * as SDK from '../../core/sdk/sdk.js';
10
10
  import type * as Protocol from '../../generated/protocol.js';
11
11
  import type * as LighthouseModel from '../../models/lighthouse/lighthouse.js';
@@ -78,6 +78,14 @@ export class ProtocolService implements ProtocolClient.CDPConnection.CDPConnecti
78
78
  private configForTesting?: object;
79
79
  private connection?: ProtocolClient.CDPConnection.CDPConnection;
80
80
 
81
+ /**
82
+ * Session ids that belong to this Lighthouse run. The proxy only relays
83
+ * worker commands and connection events that target one of these sessions
84
+ * so that traffic stays scoped to the parallel session created in
85
+ * `attach()` and its descendants.
86
+ */
87
+ readonly #knownSessionIds = new Set<string>();
88
+
81
89
  /**
82
90
  * Tracks pending requests to the Lighthouse worker.
83
91
  * Key: The message ID sent to the worker.
@@ -119,6 +127,8 @@ export class ProtocolService implements ProtocolClient.CDPConnection.CDPConnecti
119
127
 
120
128
  const rootTargetId = await rootChildTargetManager.getParentTargetId();
121
129
  const {sessionId} = await rootTarget.targetAgent().invoke_attachToTarget({targetId: rootTargetId, flatten: true});
130
+ this.#knownSessionIds.clear();
131
+ this.#knownSessionIds.add(sessionId);
122
132
  this.connection = connection;
123
133
  this.connection.observe(this);
124
134
 
@@ -217,6 +227,7 @@ export class ProtocolService implements ProtocolClient.CDPConnection.CDPConnecti
217
227
  this.rootTarget = undefined;
218
228
  this.connection?.unobserve(this);
219
229
  this.connection = undefined;
230
+ this.#knownSessionIds.clear();
220
231
 
221
232
  if (oldLighthouseWorker) {
222
233
  (await oldLighthouseWorker).terminate();
@@ -237,18 +248,29 @@ export class ProtocolService implements ProtocolClient.CDPConnection.CDPConnecti
237
248
  }
238
249
 
239
250
  private dispatchProtocolMessage(message: ProtocolClient.CDPConnection.CDPReceivableMessage): void {
240
- // A message without a sessionId is the main session of the main target (call it "Main session").
241
- // A parallel connection and session was made that connects to the same main target (call it "Lighthouse session").
242
- // Messages from the "Lighthouse session" have a sessionId.
243
- // Without some care, there is a risk of sending the same events for the same main frame to Lighthouse–the backend
244
- // will create events for the "Main session" and the "Lighthouse session".
245
- // The workaround–only send message to Lighthouse if:
246
- // * the message has a sessionId (is not for the "Main session")
247
- // * the message does not have a sessionId (is for the "Main session"), but only for the Target domain
248
- // (to kickstart autoAttach in LH).
249
- if (message.sessionId || ('method' in message && message.method?.startsWith('Target'))) {
250
- void this.send('dispatchProtocolMessage', {message});
251
+ // The shared CDPConnection carries traffic for every DevTools session. A
252
+ // parallel session is created for the Lighthouse run in `attach()`, and the
253
+ // worker only ever needs traffic that belongs to that session or one of the
254
+ // child sessions it subsequently attaches. Filtering on `#knownSessionIds`
255
+ // keeps the proxy scoped to the run instead of broadcasting unrelated
256
+ // session traffic into the worker.
257
+ if (!message.sessionId || !this.#knownSessionIds.has(message.sessionId)) {
258
+ return;
259
+ }
260
+ if ('method' in message) {
261
+ if (message.method === 'Target.attachedToTarget') {
262
+ const childSessionId = (message.params as Protocol.Target.AttachedToTargetEvent | undefined)?.sessionId;
263
+ if (childSessionId) {
264
+ this.#knownSessionIds.add(childSessionId);
265
+ }
266
+ } else if (message.method === 'Target.detachedFromTarget') {
267
+ const childSessionId = (message.params as Protocol.Target.DetachedFromTargetEvent | undefined)?.sessionId;
268
+ if (childSessionId) {
269
+ this.#knownSessionIds.delete(childSessionId);
270
+ }
271
+ }
251
272
  }
273
+ void this.send('dispatchProtocolMessage', {message});
252
274
  }
253
275
 
254
276
  onDisconnect(): void {
@@ -303,11 +325,30 @@ export class ProtocolService implements ProtocolClient.CDPConnection.CDPConnecti
303
325
 
304
326
  private sendProtocolMessage(message: string): void {
305
327
  const {id, method, params, sessionId} = JSON.parse(message);
328
+ // The worker is expected to only address the parallel session created in
329
+ // `attach()` or sessions discovered beneath it. Anything else, including
330
+ // the root session, is outside the scope of the run.
331
+ if (!sessionId || !this.#knownSessionIds.has(sessionId)) {
332
+ void this.send('dispatchProtocolMessage', {
333
+ message: {
334
+ id,
335
+ sessionId,
336
+ error: {
337
+ code: ProtocolClient.CDPConnection.CDPErrorStatus.SESSION_NOT_FOUND,
338
+ message: `Unknown session id: ${sessionId}`,
339
+ },
340
+ },
341
+ });
342
+ return;
343
+ }
306
344
  // CDPConnection manages it's own message IDs and it's important, otherwise we'd clash
307
345
  // with the rest of the DevTools traffic.
308
346
  // Instead, we ignore the ID coming from the worker when sending the command, but
309
347
  // patch it back in when sending the response back to the worker.
310
348
  void this.connection?.send(method, params, sessionId).then(response => {
349
+ if ('result' in response && method === 'Target.attachToTarget' && response.result?.sessionId) {
350
+ this.#knownSessionIds.add(response.result.sessionId);
351
+ }
311
352
  const message =
312
353
  'result' in response ? {id, sessionId, result: response.result} : {id, sessionId, error: response.error};
313
354
  this.dispatchProtocolMessage(message);
@@ -109,8 +109,10 @@ const str_ = i18n.i18n.registerUIStrings('panels/network/RequestPayloadView.ts',
109
109
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
110
110
 
111
111
  interface ViewInput {
112
- decodeRequestParameters: boolean;
113
- setURLDecoding(value: boolean): void;
112
+ decodeQueryParameters: boolean;
113
+ setDecodeQueryParameters(value: boolean): void;
114
+ decodeFormParameters: boolean;
115
+ setDecodeFormParameters(value: boolean): void;
114
116
  viewQueryParamSource: boolean;
115
117
  setViewQueryParamSource(value: boolean): void;
116
118
  viewFormParamSource: boolean;
@@ -157,15 +159,28 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
157
159
  </devtools-widget>
158
160
  </li>`;
159
161
 
160
- const createParsedParams = (params: SDK.NetworkRequest.NameValue[]): TemplateResult[] => params.map(
161
- param => html`<li role=treeitem @contextmenu=${
162
- copyValueContextmenu(i18nString(UIStrings.copyValue), () => decodeURIComponent(param.value), 'copy-value')}>${
163
- param.name !== '' ?
164
- html`${RequestPayloadView.formatParameter(param.name, 'payload-name', input.decodeRequestParameters)}${
165
- RequestPayloadView.formatParameter(
166
- param.value, 'payload-value source-code', input.decodeRequestParameters)}` :
167
- RequestPayloadView.formatParameter(
168
- i18nString(UIStrings.empty), 'empty-request-payload', input.decodeRequestParameters)}</li>`);
162
+ const createParsedParams = (params: SDK.NetworkRequest.NameValue[],
163
+ decodeParameters: boolean): TemplateResult[] => params.map(param => {
164
+ // clang-format off
165
+ return html`
166
+ <li role=treeitem
167
+ @contextmenu=${copyValueContextmenu(
168
+ i18nString(UIStrings.copyValue),
169
+ () => decodeURIComponent(param.value),
170
+ 'copy-value'
171
+ )}>
172
+ ${param.name !== '' ? html`
173
+ ${RequestPayloadView.formatParameter(param.name, 'payload-name', decodeParameters)}
174
+ ${RequestPayloadView.formatParameter(param.value, 'payload-value source-code', decodeParameters)}
175
+ ` : RequestPayloadView.formatParameter(
176
+ i18nString(UIStrings.empty),
177
+ 'empty-request-payload',
178
+ decodeParameters
179
+ )}
180
+ </li>
181
+ `;
182
+ // clang-format on
183
+ });
169
184
 
170
185
  const parsedFormData = (() => {
171
186
  if (input.formData && !input.formParameters) {
@@ -201,24 +216,23 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
201
216
  const requestPayloadExpandedSetting =
202
217
  Common.Settings.Settings.instance().createSetting('request-info-request-payload-category-expanded', true);
203
218
 
204
- const toggleURLDecoding = (e: Event): void => {
205
- e.consume();
206
- input.setURLDecoding(!input.decodeRequestParameters);
207
- };
208
-
209
- const onContextMenu = (viewSource: boolean, callback: (value: boolean) => void, includeURLDecodingOption = true) => (
210
- event: Event): void => {
219
+ const onContextMenu = (
220
+ viewSource: boolean,
221
+ setViewSource: (value: boolean) => void,
222
+ decoding?: {decode: boolean, toggleDecode: () => void},
223
+ ) => (event: Event): void => {
211
224
  const contextMenu = new UI.ContextMenu.ContextMenu(event);
212
225
  const section = contextMenu.newSection();
213
226
  if (viewSource) {
214
- section.appendItem(i18nString(UIStrings.viewParsed), () => callback(!viewSource), {jslogContext: 'view-parsed'});
227
+ section.appendItem(i18nString(UIStrings.viewParsed), () => setViewSource(!viewSource),
228
+ {jslogContext: 'view-parsed'});
215
229
  } else {
216
- section.appendItem(i18nString(UIStrings.viewSource), () => callback(!viewSource), {jslogContext: 'view-source'});
217
- if (includeURLDecodingOption) {
230
+ section.appendItem(i18nString(UIStrings.viewSource), () => setViewSource(!viewSource),
231
+ {jslogContext: 'view-source'});
232
+ if (decoding) {
218
233
  const viewURLEncodedText =
219
- input.decodeRequestParameters ? i18nString(UIStrings.viewUrlEncoded) : i18nString(UIStrings.viewDecoded);
220
- section.appendItem(
221
- viewURLEncodedText, toggleURLDecoding.bind(this, event), {jslogContext: 'toggle-url-decoding'});
234
+ decoding.decode ? i18nString(UIStrings.viewUrlEncoded) : i18nString(UIStrings.viewDecoded);
235
+ section.appendItem(viewURLEncodedText, () => decoding.toggleDecode(), {jslogContext: 'toggle-url-decoding'});
222
236
  }
223
237
  }
224
238
  void contextMenu.show();
@@ -235,7 +249,14 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
235
249
  role=treeitem
236
250
  ?hidden=${!input.queryParameters}
237
251
  jslog=${VisualLogging.section().context('query-string')}
238
- @contextmenu=${onContextMenu(input.viewQueryParamSource, input.setViewQueryParamSource)}
252
+ @contextmenu=${onContextMenu(
253
+ input.viewQueryParamSource,
254
+ input.setViewQueryParamSource,
255
+ {
256
+ decode: input.decodeQueryParameters,
257
+ toggleDecode: () => input.setDecodeQueryParameters(!input.decodeQueryParameters),
258
+ }
259
+ )}
239
260
  @expanded=${(e: UI.TreeOutline.TreeViewElement.ExpandEvent) =>
240
261
  queryStringExpandedSetting.set(e.detail.expanded)}
241
262
  ?open=${queryStringExpandedSetting.get()}
@@ -248,19 +269,26 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
248
269
  ?hidden=${input.viewQueryParamSource}
249
270
  jslog=${VisualLogging.action().track({click: true}).context('decode-encode')}
250
271
  .variant=${Buttons.Button.Variant.OUTLINED}
251
- @click=${toggleURLDecoding}>
252
- ${input.decodeRequestParameters ? i18nString(UIStrings.viewUrlEncoded) : i18nString(UIStrings.viewDecoded)}
272
+ @click=${(e: Event) => { e.consume(); input.setDecodeQueryParameters(!input.decodeQueryParameters); }}>
273
+ ${input.decodeQueryParameters ? i18nString(UIStrings.viewUrlEncoded) : i18nString(UIStrings.viewDecoded)}
253
274
  </devtools-button>
254
275
  <ul role=group>
255
276
  ${ifExpanded(input.viewQueryParamSource ? createSourceText(input.queryString ?? '')
256
- : createParsedParams(input.queryParameters ?? []))}
277
+ : createParsedParams(input.queryParameters ?? [], input.decodeQueryParameters))}
257
278
  </ul>
258
279
  </li>
259
280
  <li
260
281
  role=treeitem
261
282
  ?hidden=${!input.formData || !input.formParameters}
262
283
  jslog=${VisualLogging.section().context('form-data')}
263
- @contextmenu=${onContextMenu(input.viewFormParamSource, input.setViewFormParamSource)}
284
+ @contextmenu=${onContextMenu(
285
+ input.viewFormParamSource,
286
+ input.setViewFormParamSource,
287
+ {
288
+ decode: input.decodeFormParameters,
289
+ toggleDecode: () => input.setDecodeFormParameters(!input.decodeFormParameters),
290
+ }
291
+ )}
264
292
  @expanded=${(e: UI.TreeOutline.TreeViewElement.ExpandEvent) => formDataExpandedSetting.set(e.detail.expanded)}
265
293
  ?open=${formDataExpandedSetting.get()}
266
294
  >
@@ -272,20 +300,22 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
272
300
  ?hidden=${input.viewFormParamSource}
273
301
  jslog=${VisualLogging.action().track({click: true}).context('decode-encode')}
274
302
  .variant=${Buttons.Button.Variant.OUTLINED}
275
- @click=${toggleURLDecoding}>
276
- ${input.decodeRequestParameters ? i18nString(UIStrings.viewUrlEncoded) : i18nString(UIStrings.viewDecoded)}
303
+ @click=${(e: Event) => { e.consume(); input.setDecodeFormParameters(!input.decodeFormParameters); }}>
304
+ ${input.decodeFormParameters ? i18nString(UIStrings.viewUrlEncoded) : i18nString(UIStrings.viewDecoded)}
277
305
  </devtools-button>
278
306
  <ul role=group>
279
307
  ${ifExpanded(input.viewFormParamSource ? createSourceText(input.formData ?? '')
280
- : createParsedParams(input.formParameters ?? []))}
308
+ : createParsedParams(input.formParameters ?? [], input.decodeFormParameters))}
281
309
  </ul>
282
310
  </li>
283
311
  <li
284
312
  role=treeitem
285
313
  ?hidden=${!input.formData || Boolean(input.formParameters) || Boolean(input.binaryPayloadContentData)}
286
314
  jslog=${VisualLogging.section().context('request-payload')}
287
- @contextmenu=${onContextMenu(input.viewJSONPayloadSource, input.setViewJSONPayloadSource,
288
- /* includeURLDecodingOption*/ false)}
315
+ @contextmenu=${onContextMenu(
316
+ input.viewJSONPayloadSource,
317
+ input.setViewJSONPayloadSource,
318
+ )}
289
319
  @expanded=${(e: UI.TreeOutline.TreeViewElement.ExpandEvent) =>
290
320
  requestPayloadExpandedSetting.set(e.detail.expanded)}
291
321
  ?open=${requestPayloadExpandedSetting.get()}
@@ -324,7 +354,8 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
324
354
 
325
355
  export class RequestPayloadView extends UI.Widget.VBox {
326
356
  #request?: SDK.NetworkRequest.NetworkRequest;
327
- #decodeRequestParameters = true;
357
+ #decodeQueryParameters = true;
358
+ #decodeFormParameters = true;
328
359
  #formData?: string;
329
360
  #formParameters?: SDK.NetworkRequest.NameValue[];
330
361
  #binaryPayloadContentData: TextUtils.ContentData.ContentData|null = null;
@@ -345,9 +376,11 @@ export class RequestPayloadView extends UI.Widget.VBox {
345
376
  }
346
377
  this.#request = request;
347
378
 
379
+ this.#decodeQueryParameters = true;
380
+ this.#decodeFormParameters = true;
348
381
  const contentType = request.requestContentType();
349
382
  if (contentType) {
350
- this.#decodeRequestParameters = Boolean(contentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i));
383
+ this.#decodeFormParameters = Boolean(contentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i));
351
384
  }
352
385
 
353
386
  if (this.isShowing()) {
@@ -402,9 +435,14 @@ export class RequestPayloadView extends UI.Widget.VBox {
402
435
  queryParameters: this.request.queryParameters,
403
436
  formData: this.#formData,
404
437
  formParameters: this.#formParameters,
405
- decodeRequestParameters: this.#decodeRequestParameters,
406
- setURLDecoding: (value: boolean): void => {
407
- this.#decodeRequestParameters = value;
438
+ decodeQueryParameters: this.#decodeQueryParameters,
439
+ setDecodeQueryParameters: (value: boolean): void => {
440
+ this.#decodeQueryParameters = value;
441
+ this.requestUpdate();
442
+ },
443
+ decodeFormParameters: this.#decodeFormParameters,
444
+ setDecodeFormParameters: (value: boolean): void => {
445
+ this.#decodeFormParameters = value;
408
446
  this.requestUpdate();
409
447
  },
410
448
  viewQueryParamSource: this.#viewQueryParamSource,
@@ -170,12 +170,14 @@ export class ResponseHeaderSection extends ResponseHeaderSectionBase {
170
170
  BlockedReasonDetails.get((this.#request.blockedReason() as Protocol.Network.BlockedReason));
171
171
  if (headerWithIssues) {
172
172
  if (IssuesManager.RelatedIssue.hasIssueOfCategory(
173
- this.#request, IssuesManager.Issue.IssueCategory.CROSS_ORIGIN_EMBEDDER_POLICY)) {
173
+ this.#request, IssuesManager.Issue.IssueCategory.CROSS_ORIGIN_EMBEDDER_POLICY,
174
+ IssuesManager.IssuesManager.IssuesManager.instance())) {
174
175
  const followLink = (): void => {
175
176
  Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.LEARN_MORE_LINK_COEP);
176
177
  if (this.#request) {
177
- void IssuesManager.RelatedIssue.reveal(
178
- this.#request, IssuesManager.Issue.IssueCategory.CROSS_ORIGIN_EMBEDDER_POLICY);
178
+ void IssuesManager.RelatedIssue.reveal(this.#request,
179
+ IssuesManager.IssuesManager.IssuesManager.instance(),
180
+ IssuesManager.Issue.IssueCategory.CROSS_ORIGIN_EMBEDDER_POLICY);
179
181
  }
180
182
  };
181
183
  if (headerWithIssues.blockedDetails) {
@@ -1795,8 +1795,8 @@ export class HeapProfileHeader extends ProfileHeader {
1795
1795
 
1796
1796
  setupWorker(): void {
1797
1797
  console.assert(!this.workerProxy, 'HeapSnapshotWorkerProxy already exists');
1798
- this.workerProxy =
1799
- new HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy(this.handleWorkerEvent.bind(this));
1798
+ this.workerProxy = new HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy(
1799
+ this.handleWorkerEvent.bind(this), Common.Console.Console.instance());
1800
1800
  this.workerProxy.addEventListener(
1801
1801
  HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy.Events.WAIT, event => {
1802
1802
  this.updateStatus(null, event.data);