chrome-devtools-frontend 1.0.1006344 → 1.0.1007778

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 (138) hide show
  1. package/config/gni/devtools_grd_files.gni +5 -0
  2. package/config/gni/devtools_image_files.gni +2 -0
  3. package/extension-api/ExtensionAPI.d.ts +10 -0
  4. package/front_end/Images/src/ic_sources_authored.svg +5 -0
  5. package/front_end/Images/src/ic_sources_deployed.svg +5 -0
  6. package/front_end/core/i18n/locales/af.json +202 -43
  7. package/front_end/core/i18n/locales/am.json +201 -42
  8. package/front_end/core/i18n/locales/ar.json +201 -42
  9. package/front_end/core/i18n/locales/as.json +201 -42
  10. package/front_end/core/i18n/locales/az.json +201 -42
  11. package/front_end/core/i18n/locales/be.json +201 -42
  12. package/front_end/core/i18n/locales/bg.json +201 -42
  13. package/front_end/core/i18n/locales/bn.json +201 -42
  14. package/front_end/core/i18n/locales/bs.json +201 -42
  15. package/front_end/core/i18n/locales/ca.json +204 -45
  16. package/front_end/core/i18n/locales/cs.json +201 -42
  17. package/front_end/core/i18n/locales/cy.json +201 -42
  18. package/front_end/core/i18n/locales/da.json +201 -42
  19. package/front_end/core/i18n/locales/de.json +201 -42
  20. package/front_end/core/i18n/locales/el.json +201 -42
  21. package/front_end/core/i18n/locales/en-GB.json +199 -40
  22. package/front_end/core/i18n/locales/en-US.json +57 -3
  23. package/front_end/core/i18n/locales/en-XL.json +57 -3
  24. package/front_end/core/i18n/locales/es-419.json +201 -42
  25. package/front_end/core/i18n/locales/es.json +201 -42
  26. package/front_end/core/i18n/locales/et.json +201 -42
  27. package/front_end/core/i18n/locales/eu.json +202 -43
  28. package/front_end/core/i18n/locales/fa.json +201 -42
  29. package/front_end/core/i18n/locales/fi.json +201 -42
  30. package/front_end/core/i18n/locales/fil.json +201 -42
  31. package/front_end/core/i18n/locales/fr-CA.json +201 -42
  32. package/front_end/core/i18n/locales/fr.json +203 -44
  33. package/front_end/core/i18n/locales/gl.json +203 -44
  34. package/front_end/core/i18n/locales/gu.json +201 -42
  35. package/front_end/core/i18n/locales/he.json +201 -42
  36. package/front_end/core/i18n/locales/hi.json +201 -42
  37. package/front_end/core/i18n/locales/hr.json +201 -42
  38. package/front_end/core/i18n/locales/hu.json +201 -42
  39. package/front_end/core/i18n/locales/hy.json +201 -42
  40. package/front_end/core/i18n/locales/id.json +201 -42
  41. package/front_end/core/i18n/locales/is.json +201 -42
  42. package/front_end/core/i18n/locales/it.json +203 -44
  43. package/front_end/core/i18n/locales/ja.json +203 -44
  44. package/front_end/core/i18n/locales/ka.json +201 -42
  45. package/front_end/core/i18n/locales/kk.json +201 -42
  46. package/front_end/core/i18n/locales/km.json +201 -42
  47. package/front_end/core/i18n/locales/kn.json +201 -42
  48. package/front_end/core/i18n/locales/ko.json +201 -42
  49. package/front_end/core/i18n/locales/ky.json +201 -42
  50. package/front_end/core/i18n/locales/lo.json +201 -42
  51. package/front_end/core/i18n/locales/lt.json +201 -42
  52. package/front_end/core/i18n/locales/lv.json +201 -42
  53. package/front_end/core/i18n/locales/mk.json +201 -42
  54. package/front_end/core/i18n/locales/ml.json +201 -42
  55. package/front_end/core/i18n/locales/mn.json +201 -42
  56. package/front_end/core/i18n/locales/mr.json +201 -42
  57. package/front_end/core/i18n/locales/ms.json +201 -42
  58. package/front_end/core/i18n/locales/my.json +201 -42
  59. package/front_end/core/i18n/locales/ne.json +201 -42
  60. package/front_end/core/i18n/locales/nl.json +201 -42
  61. package/front_end/core/i18n/locales/no.json +201 -42
  62. package/front_end/core/i18n/locales/or.json +201 -42
  63. package/front_end/core/i18n/locales/pa.json +201 -42
  64. package/front_end/core/i18n/locales/pl.json +201 -42
  65. package/front_end/core/i18n/locales/pt-PT.json +201 -42
  66. package/front_end/core/i18n/locales/pt.json +201 -42
  67. package/front_end/core/i18n/locales/ro.json +201 -42
  68. package/front_end/core/i18n/locales/ru.json +203 -44
  69. package/front_end/core/i18n/locales/si.json +201 -42
  70. package/front_end/core/i18n/locales/sk.json +201 -42
  71. package/front_end/core/i18n/locales/sl.json +201 -42
  72. package/front_end/core/i18n/locales/sq.json +201 -42
  73. package/front_end/core/i18n/locales/sr-Latn.json +201 -42
  74. package/front_end/core/i18n/locales/sr.json +201 -42
  75. package/front_end/core/i18n/locales/sv.json +201 -42
  76. package/front_end/core/i18n/locales/sw.json +201 -42
  77. package/front_end/core/i18n/locales/ta.json +201 -42
  78. package/front_end/core/i18n/locales/te.json +202 -43
  79. package/front_end/core/i18n/locales/th.json +201 -42
  80. package/front_end/core/i18n/locales/tr.json +201 -42
  81. package/front_end/core/i18n/locales/uk.json +201 -42
  82. package/front_end/core/i18n/locales/ur.json +201 -42
  83. package/front_end/core/i18n/locales/uz.json +201 -42
  84. package/front_end/core/i18n/locales/vi.json +201 -42
  85. package/front_end/core/i18n/locales/zh-HK.json +201 -42
  86. package/front_end/core/i18n/locales/zh-TW.json +201 -42
  87. package/front_end/core/i18n/locales/zh.json +201 -42
  88. package/front_end/core/i18n/locales/zu.json +201 -42
  89. package/front_end/core/sdk/CPUThrottlingManager.ts +54 -0
  90. package/front_end/core/sdk/DebuggerModel.ts +12 -3
  91. package/front_end/core/sdk/EmulationModel.ts +7 -0
  92. package/front_end/core/sdk/NetworkManager.ts +6 -2
  93. package/front_end/devtools_compatibility.js +1 -0
  94. package/front_end/entrypoints/formatter_worker/FormatterActions.ts +1 -0
  95. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +12 -10
  96. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +4 -0
  97. package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +1 -4
  98. package/front_end/generated/InspectorBackendCommands.js +3 -0
  99. package/front_end/generated/protocol-mapping.d.ts +2 -0
  100. package/front_end/generated/protocol-proxy-api.d.ts +3 -0
  101. package/front_end/generated/protocol.ts +7 -0
  102. package/front_end/legacy_test_runner/lighthouse_test_runner/lighthouse_test_runner.js +16 -0
  103. package/front_end/models/extensions/ExtensionAPI.ts +95 -12
  104. package/front_end/models/extensions/ExtensionEndpoint.ts +69 -0
  105. package/front_end/models/extensions/ExtensionServer.ts +21 -0
  106. package/front_end/models/extensions/LanguageExtensionEndpoint.ts +46 -78
  107. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +43 -0
  108. package/front_end/models/extensions/RecorderPluginManager.ts +30 -0
  109. package/front_end/models/extensions/extensions.ts +2 -0
  110. package/front_end/models/formatter/FormatterWorkerPool.ts +6 -0
  111. package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
  112. package/front_end/models/javascript_metadata/JavaScriptMetadata.ts +13 -20
  113. package/front_end/models/javascript_metadata/NativeFunctions.js +1237 -3962
  114. package/front_end/models/source_map_scopes/NamesResolver.ts +206 -73
  115. package/front_end/models/workspace/UISourceCode.ts +7 -0
  116. package/front_end/panels/accessibility/axBreadcrumbs.css +2 -2
  117. package/front_end/panels/application/AppManifestView.ts +2 -1
  118. package/front_end/panels/application/components/BackForwardCacheView.ts +16 -0
  119. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +15 -1
  120. package/front_end/panels/lighthouse/LighthouseController.ts +25 -10
  121. package/front_end/panels/lighthouse/LighthouseStartView.ts +32 -6
  122. package/front_end/panels/lighthouse/LighthouseStartViewFR.ts +70 -49
  123. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +113 -4
  124. package/front_end/panels/network/components/RequestHeadersView.css +36 -3
  125. package/front_end/panels/network/components/RequestHeadersView.ts +176 -3
  126. package/front_end/panels/sources/NavigatorView.ts +141 -40
  127. package/front_end/panels/sources/SourcesPanel.ts +8 -0
  128. package/front_end/panels/sources/TabbedEditorContainer.ts +2 -2
  129. package/front_end/panels/sources/sources-meta.ts +6 -0
  130. package/front_end/panels/timeline/TimelinePanel.ts +27 -4
  131. package/front_end/panels/timeline/timelinePanel.css +8 -0
  132. package/front_end/ui/components/text_editor/javascript.ts +12 -14
  133. package/front_end/ui/legacy/Treeoutline.ts +5 -2
  134. package/package.json +1 -1
  135. package/scripts/hosted_mode/server.js +14 -1
  136. package/scripts/javascript_natives/helpers.js +26 -7
  137. package/scripts/javascript_natives/index.js +4 -3
  138. package/scripts/javascript_natives/tests.js +2 -2
@@ -44,60 +44,22 @@ const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseStartViewF
44
44
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
45
45
 
46
46
  export class StartViewFR extends StartView {
47
- protected render(): void {
48
- super.render();
49
- this.refresh();
50
- }
51
-
52
- private createStartButton(mode: string): HTMLButtonElement {
53
- let buttonLabel: Platform.UIString.LocalizedString;
54
- let callback: () => void;
47
+ changeFormMode?: (mode: string) => void;
55
48
 
56
- if (mode === 'timespan') {
57
- buttonLabel = i18nString(UIStrings.startTimespan);
58
- callback = (): void => {
59
- this.controller.dispatchEventToListeners(
60
- Events.RequestLighthouseTimespanStart,
61
- /* keyboardInitiated */ this.startButton.matches(':focus-visible'),
62
- );
63
- };
64
- } else if (mode === 'snapshot') {
65
- buttonLabel = i18nString(UIStrings.analyzeSnapshot);
66
- callback = (): void => {
67
- this.controller.dispatchEventToListeners(
68
- Events.RequestLighthouseStart,
69
- /* keyboardInitiated */ this.startButton.matches(':focus-visible'),
70
- );
71
- };
72
- } else {
73
- buttonLabel = i18nString(UIStrings.analyzeNavigation);
74
- callback = (): void => {
75
- this.controller.dispatchEventToListeners(
76
- Events.RequestLighthouseStart,
77
- /* keyboardInitiated */ this.startButton.matches(':focus-visible'),
78
- );
79
- };
80
- }
81
-
82
- return UI.UIUtils.createTextButton(
83
- buttonLabel,
84
- callback,
85
- /* className */ '',
86
- /* primary */ true,
87
- );
88
- }
49
+ protected render(): void {
50
+ this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.legacy_navigation', this.settingsToolbarInternal);
51
+ this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.clear_storage', this.settingsToolbarInternal);
52
+ this.populateRuntimeSettingAsToolbarDropdown('lighthouse.throttling', this.settingsToolbarInternal);
89
53
 
90
- refresh(): void {
91
54
  const {mode} = this.controller.getFlags();
92
-
93
- this.startButton = this.createStartButton(mode);
55
+ this.populateStartButton(mode);
94
56
 
95
57
  const fragment = UI.Fragment.Fragment.build`
96
58
  <form class="lighthouse-start-view-fr">
97
59
  <header class="hbox">
98
60
  <div class="lighthouse-logo"></div>
99
61
  <div class="lighthouse-title">${i18nString(UIStrings.generateLighthouseReport)}</div>
100
- <div class="lighthouse-start-button-container">${this.startButton}</div>
62
+ <div class="lighthouse-start-button-container" $="start-button-container">${this.startButton}</div>
101
63
  </header>
102
64
  <div $="help-text" class="lighthouse-help-text hidden"></div>
103
65
  <div class="lighthouse-options hbox">
@@ -127,16 +89,75 @@ export class StartViewFR extends StartView {
127
89
  this.helpText = fragment.$('help-text');
128
90
  this.warningText = fragment.$('warning-text');
129
91
 
130
- // The previous radios are removed later and don't exist on the new fragment yet.
131
- this.populateFormControls(fragment, mode);
132
-
133
- // Populate the Lighthouse mode
134
92
  const modeFormElements = fragment.$('mode-form-elements');
135
93
  this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
136
94
 
95
+ // The previous radios are removed later and don't exist on the new fragment yet.
96
+ this.populateFormControls(fragment, mode);
97
+
137
98
  this.contentElement.textContent = '';
138
99
  this.contentElement.append(fragment.element());
139
100
 
101
+ this.refresh();
102
+ }
103
+
104
+ private populateStartButton(mode: string): void {
105
+ let buttonLabel: Platform.UIString.LocalizedString;
106
+ let callback: () => void;
107
+
108
+ if (mode === 'timespan') {
109
+ buttonLabel = i18nString(UIStrings.startTimespan);
110
+ callback = (): void => {
111
+ this.controller.dispatchEventToListeners(
112
+ Events.RequestLighthouseTimespanStart,
113
+ /* keyboardInitiated */ this.startButton.matches(':focus-visible'),
114
+ );
115
+ };
116
+ } else if (mode === 'snapshot') {
117
+ buttonLabel = i18nString(UIStrings.analyzeSnapshot);
118
+ callback = (): void => {
119
+ this.controller.dispatchEventToListeners(
120
+ Events.RequestLighthouseStart,
121
+ /* keyboardInitiated */ this.startButton.matches(':focus-visible'),
122
+ );
123
+ };
124
+ } else {
125
+ buttonLabel = i18nString(UIStrings.analyzeNavigation);
126
+ callback = (): void => {
127
+ this.controller.dispatchEventToListeners(
128
+ Events.RequestLighthouseStart,
129
+ /* keyboardInitiated */ this.startButton.matches(':focus-visible'),
130
+ );
131
+ };
132
+ }
133
+
134
+ const startButtonContainer = this.contentElement.querySelector('.lighthouse-start-button-container');
135
+ if (startButtonContainer) {
136
+ startButtonContainer.textContent = '';
137
+ this.startButton = UI.UIUtils.createTextButton(
138
+ buttonLabel,
139
+ callback,
140
+ /* className */ '',
141
+ /* primary */ true,
142
+ );
143
+ startButtonContainer.append(this.startButton);
144
+ }
145
+ }
146
+
147
+ refresh(): void {
148
+ const {mode} = this.controller.getFlags();
149
+ this.populateStartButton(mode);
150
+
151
+ for (const {checkbox, preset} of this.checkboxes) {
152
+ if (preset.supportedModes.includes(mode)) {
153
+ checkbox.setEnabled(true);
154
+ checkbox.setIndeterminate(false);
155
+ } else {
156
+ checkbox.setEnabled(false);
157
+ checkbox.setIndeterminate(true);
158
+ }
159
+ }
160
+
140
161
  // Ensure the correct layout is used after refresh.
141
162
  this.onResize();
142
163
  }
@@ -61,6 +61,26 @@ const UIStrings = {
61
61
  *@example {2} PH1
62
62
  */
63
63
  dSlowdown: '{PH1}× slowdown',
64
+ /**
65
+ *@description Tooltip text in Throttling Manager of the Performance panel
66
+ */
67
+ excessConcurrency: 'Exceeding the default value may degrade system performance.',
68
+ /**
69
+ *@description Tooltip text in Throttling Manager of the Performance panel
70
+ */
71
+ resetConcurrency: 'Reset to the default value',
72
+ /**
73
+ *@description Screen reader label for an check box that neables overriding navigator.hardwareConcurrency
74
+ */
75
+ hardwareConcurrency: 'Hardware concurrency',
76
+ /**
77
+ *@description Screen reader label for an input box that overrides navigator.hardwareConcurrency
78
+ */
79
+ hardwareConcurrencyValue: 'Value of navigator.hardwareConcurrency',
80
+ /**
81
+ *@description Icon title in Throttling Manager of the Performance panel
82
+ */
83
+ hardwareConcurrencyIsEnabled: 'Hardware concurrency override is enabled',
64
84
  };
65
85
  const str_ = i18n.i18n.registerUIStrings('panels/mobile_throttling/ThrottlingManager.ts', UIStrings);
66
86
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -73,6 +93,11 @@ export class ThrottlingManager {
73
93
  private readonly currentNetworkThrottlingConditionsSetting: Common.Settings.Setting<SDK.NetworkManager.Conditions>;
74
94
  private lastNetworkThrottlingConditions!: SDK.NetworkManager.Conditions;
75
95
  private readonly cpuThrottlingManager: SDK.CPUThrottlingManager.CPUThrottlingManager;
96
+ #hardwareConcurrencyOverrideEnabled = false;
97
+
98
+ get hardwareConcurrencyOverrideEnabled(): boolean {
99
+ return this.#hardwareConcurrencyOverrideEnabled;
100
+ }
76
101
 
77
102
  private constructor() {
78
103
  this.cpuThrottlingManager = SDK.CPUThrottlingManager.CPUThrottlingManager.instance();
@@ -232,19 +257,36 @@ export class ThrottlingManager {
232
257
  }
233
258
  }
234
259
 
260
+ private updatePanelIcon(): void {
261
+ const cpuRate = this.cpuThrottlingManager.cpuThrottlingRate();
262
+
263
+ if (cpuRate === SDK.CPUThrottlingManager.CPUThrottlingRates.NoThrottling &&
264
+ !this.hardwareConcurrencyOverrideEnabled) {
265
+ UI.InspectorView.InspectorView.instance().setPanelIcon('timeline', null);
266
+ return;
267
+ }
268
+ const icon = UI.Icon.Icon.create('smallicon-warning');
269
+ const tooltips: string[] = [];
270
+ if (cpuRate !== SDK.CPUThrottlingManager.CPUThrottlingRates.NoThrottling) {
271
+ tooltips.push(i18nString(UIStrings.cpuThrottlingIsEnabled));
272
+ }
273
+ if (this.hardwareConcurrencyOverrideEnabled) {
274
+ tooltips.push(i18nString(UIStrings.hardwareConcurrencyIsEnabled));
275
+ }
276
+ icon.title = tooltips.join('\n');
277
+ UI.InspectorView.InspectorView.instance().setPanelIcon('timeline', icon);
278
+ }
279
+
235
280
  setCPUThrottlingRate(rate: number): void {
236
281
  this.cpuThrottlingManager.setCPUThrottlingRate(rate);
237
- let icon: UI.Icon.Icon|null = null;
238
282
  if (rate !== SDK.CPUThrottlingManager.CPUThrottlingRates.NoThrottling) {
239
283
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.CpuThrottlingEnabled);
240
- icon = UI.Icon.Icon.create('smallicon-warning');
241
- UI.Tooltip.Tooltip.install(icon, i18nString(UIStrings.cpuThrottlingIsEnabled));
242
284
  }
243
285
  const index = this.cpuThrottlingRates.indexOf(rate);
244
286
  for (const control of this.cpuThrottlingControls) {
245
287
  control.setSelectedIndex(index);
246
288
  }
247
- UI.InspectorView.InspectorView.instance().setPanelIcon('timeline', icon);
289
+ this.updatePanelIcon();
248
290
  }
249
291
 
250
292
  createCPUThrottlingSelector(): UI.Toolbar.ToolbarComboBox {
@@ -266,6 +308,73 @@ export class ThrottlingManager {
266
308
  return control;
267
309
  }
268
310
 
311
+ createHardwareConcurrencySelector(): {
312
+ input: UI.Toolbar.ToolbarItem,
313
+ reset: UI.Toolbar.ToolbarButton,
314
+ warning: UI.Toolbar.ToolbarItem,
315
+ toggle: UI.Toolbar.ToolbarItem,
316
+ } {
317
+ const input = new UI.Toolbar.ToolbarItem(UI.UIUtils.createInput('devtools-text-input', 'number'));
318
+ input.setTitle(i18nString(UIStrings.hardwareConcurrencyValue));
319
+ const inputElement = input.element as HTMLInputElement;
320
+ inputElement.min = '1';
321
+ input.setEnabled(false);
322
+
323
+ const toggle = new UI.Toolbar.ToolbarCheckbox(i18nString(UIStrings.hardwareConcurrency));
324
+ const reset = new UI.Toolbar.ToolbarButton('Reset concurrency', 'largeicon-undo');
325
+ reset.setTitle(i18nString(UIStrings.resetConcurrency));
326
+ const warning = new UI.Toolbar.ToolbarItem(UI.Icon.Icon.create('smallicon-warning'));
327
+ warning.setTitle(i18nString(UIStrings.excessConcurrency));
328
+
329
+ toggle.inputElement.disabled = true; // Prevent modification while still wiring things up asynchronously below
330
+ reset.element.classList.add('timeline-concurrency-hidden');
331
+ warning.element.classList.add('timeline-concurrency-hidden');
332
+
333
+ void this.cpuThrottlingManager.getHardwareConcurrency().then(defaultValue => {
334
+ if (defaultValue === undefined) {
335
+ return;
336
+ }
337
+
338
+ const setHardwareConcurrency = (value: number): void => {
339
+ if (value >= 1) {
340
+ this.cpuThrottlingManager.setHardwareConcurrency(value);
341
+ }
342
+ if (value > defaultValue) {
343
+ warning.element.classList.remove('timeline-concurrency-hidden');
344
+ } else {
345
+ warning.element.classList.add('timeline-concurrency-hidden');
346
+ }
347
+ if (value === defaultValue) {
348
+ reset.element.classList.add('timeline-concurrency-hidden');
349
+ } else {
350
+ reset.element.classList.remove('timeline-concurrency-hidden');
351
+ }
352
+ };
353
+
354
+ inputElement.value = `${defaultValue}`;
355
+ inputElement.oninput = (): void => setHardwareConcurrency(Number(inputElement.value));
356
+ toggle.inputElement.disabled = false;
357
+ toggle.inputElement.addEventListener('change', () => {
358
+ this.#hardwareConcurrencyOverrideEnabled = toggle.checked();
359
+ this.updatePanelIcon();
360
+
361
+ input.setEnabled(this.hardwareConcurrencyOverrideEnabled);
362
+ setHardwareConcurrency(this.hardwareConcurrencyOverrideEnabled ? Number(inputElement.value) : defaultValue);
363
+ });
364
+
365
+ reset.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => {
366
+ inputElement.value = `${defaultValue}`;
367
+ setHardwareConcurrency(defaultValue);
368
+ });
369
+ });
370
+
371
+ return {input, reset, warning, toggle};
372
+ }
373
+
374
+ setHardwareConcurrency(concurrency: number): void {
375
+ this.cpuThrottlingManager.setHardwareConcurrency(concurrency);
376
+ }
377
+
269
378
  private isDirty(): boolean {
270
379
  const networkConditions = SDK.NetworkManager.MultitargetNetworkManager.instance().networkConditions();
271
380
  const knownCurrentConditions = this.currentNetworkThrottlingConditionsSetting.get();
@@ -23,10 +23,37 @@
23
23
  background-color: var(--legacy-focus-bg-color);
24
24
  }
25
25
 
26
+ details[open] .header-count {
27
+ display: none;
28
+ }
29
+
30
+ details summary label {
31
+ display: none;
32
+ }
33
+
34
+ details[open] summary label {
35
+ display: inline-flex;
36
+ gap: 2px;
37
+ }
38
+
39
+ .raw-checkbox-container {
40
+ float: right;
41
+ }
42
+
43
+ details summary input {
44
+ vertical-align: middle;
45
+ }
46
+
26
47
  .row {
27
48
  display: flex;
28
49
  line-height: 20px;
29
- padding-left: 25px;
50
+ padding-left: 8px;
51
+ gap: 12px;
52
+ user-select: text;
53
+ }
54
+
55
+ div.raw-headers-row {
56
+ display: block;
30
57
  }
31
58
 
32
59
  .row:first-of-type {
@@ -39,14 +66,13 @@
39
66
 
40
67
  .header-name {
41
68
  color: var(--color-primary);
42
- font-weight: bold;
69
+ font-weight: 500;
43
70
  width: 160px;
44
71
  flex-shrink: 0;
45
72
  }
46
73
 
47
74
  .header-value {
48
75
  word-break: break-all;
49
- user-select: text;
50
76
  }
51
77
 
52
78
  .green-circle::before,
@@ -76,3 +102,10 @@
76
102
  .status-with-comment {
77
103
  color: var(--color-text-secondary);
78
104
  }
105
+
106
+ .raw-headers {
107
+ font-family: var(--source-code-font-family);
108
+ font-size: var(--source-code-font-size);
109
+ white-space: pre-wrap;
110
+ word-break: break-all;
111
+ }
@@ -6,12 +6,14 @@ import * as Common from '../../../core/common/common.js';
6
6
  import * as i18n from '../../../core/i18n/i18n.js';
7
7
  import {assertNotNullOrUndefined} from '../../../core/platform/platform.js';
8
8
  import * as SDK from '../../../core/sdk/sdk.js';
9
+ import * as Buttons from '../../../ui/components/buttons/buttons.js';
9
10
  import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
10
11
  import * as UI from '../../../ui/legacy/legacy.js';
11
12
  import * as LitHtml from '../../../ui/lit-html/lit-html.js';
12
13
 
13
14
  import requestHeadersViewStyles from './RequestHeadersView.css.js';
14
15
 
16
+ const RAW_HEADER_CUTOFF = 3000;
15
17
  const {render, html} = LitHtml;
16
18
 
17
19
  const UIStrings = {
@@ -44,6 +46,14 @@ const UIStrings = {
44
46
  */
45
47
  general: 'General',
46
48
  /**
49
+ *@description Label for a checkbox to switch between raw and parsed headers
50
+ */
51
+ raw: 'Raw',
52
+ /**
53
+ *@description Text in Request Headers View of the Network panel
54
+ */
55
+ requestHeaders: 'Request Headers',
56
+ /**
47
57
  *@description The URL of a request
48
58
  */
49
59
  requestUrl: 'Request URL',
@@ -52,6 +62,14 @@ const UIStrings = {
52
62
  */
53
63
  requestMethod: 'Request Method',
54
64
  /**
65
+ *@description A context menu item in the Network Log View Columns of the Network panel
66
+ */
67
+ responseHeaders: 'Response Headers',
68
+ /**
69
+ *@description Text to show more content
70
+ */
71
+ showMore: 'Show more',
72
+ /**
55
73
  *@description HTTP response code
56
74
  */
57
75
  statusCode: 'Status Code',
@@ -103,6 +121,10 @@ export class RequestHeadersComponent extends HTMLElement {
103
121
  static readonly litTagName = LitHtml.literal`devtools-request-headers`;
104
122
  readonly #shadow = this.attachShadow({mode: 'open'});
105
123
  #request?: Readonly<SDK.NetworkRequest.NetworkRequest>;
124
+ #showResponseHeadersText = false;
125
+ #showRequestHeadersText = false;
126
+ #showResponseHeadersTextFull = false;
127
+ #showRequestHeadersTextFull = false;
106
128
 
107
129
  set data(data: RequestHeadersComponentData) {
108
130
  this.#request = data.request;
@@ -120,10 +142,126 @@ export class RequestHeadersComponent extends HTMLElement {
120
142
  // clang-format off
121
143
  render(html`
122
144
  ${this.#renderGeneralSection()}
145
+ ${this.#renderResponseHeaders()}
146
+ ${this.#renderRequestHeaders()}
123
147
  `, this.#shadow, {host: this});
124
148
  // clang-format on
125
149
  }
126
150
 
151
+ #renderResponseHeaders(): LitHtml.TemplateResult {
152
+ assertNotNullOrUndefined(this.#request);
153
+
154
+ const toggleShowRaw = (): void => {
155
+ this.#showResponseHeadersText = !this.#showResponseHeadersText;
156
+ this.#render();
157
+ };
158
+
159
+ // Disabled until https://crbug.com/1079231 is fixed.
160
+ // clang-format off
161
+ return html`
162
+ <${Category.litTagName}
163
+ @togglerawevent=${toggleShowRaw}
164
+ .data=${{
165
+ name: 'responseHeaders',
166
+ title: i18nString(UIStrings.responseHeaders),
167
+ headerCount: this.#request.sortedResponseHeaders.length,
168
+ checked: this.#request.responseHeadersText ? this.#showResponseHeadersText : undefined,
169
+ } as CategoryData}
170
+ aria-label=${i18nString(UIStrings.responseHeaders)}
171
+ >
172
+ ${this.#showResponseHeadersText ?
173
+ this.#renderRawHeaders(this.#request.responseHeadersText, true) : html`
174
+ ${this.#request.sortedResponseHeaders.map(header => html`
175
+ <div class="row">
176
+ <div class="header-name">${header.name}:</div>
177
+ <div class="header-value">${header.value}</div>
178
+ </div>
179
+ `)}
180
+ `}
181
+ </${Category.litTagName}>
182
+ `;
183
+ }
184
+
185
+ #renderRequestHeaders(): LitHtml.TemplateResult {
186
+ assertNotNullOrUndefined(this.#request);
187
+
188
+ const toggleShowRaw = (): void => {
189
+ this.#showRequestHeadersText = !this.#showRequestHeadersText;
190
+ this.#render();
191
+ };
192
+
193
+ const requestHeadersText = this.#request.requestHeadersText();
194
+
195
+ // Disabled until https://crbug.com/1079231 is fixed.
196
+ // clang-format off
197
+ return html`
198
+ <${Category.litTagName}
199
+ @togglerawevent=${toggleShowRaw}
200
+ .data=${{
201
+ name: 'requestHeaders',
202
+ title: i18nString(UIStrings.requestHeaders),
203
+ headerCount: this.#request.requestHeaders().length,
204
+ checked: requestHeadersText? this.#showRequestHeadersText : undefined,
205
+ } as CategoryData}
206
+ aria-label=${i18nString(UIStrings.requestHeaders)}
207
+ >
208
+ ${(this.#showRequestHeadersText && requestHeadersText) ?
209
+ this.#renderRawHeaders(requestHeadersText, false) : html`
210
+ ${this.#request.requestHeaders().map(header => html`
211
+ <div class="row">
212
+ <div class="header-name">${header.name}:</div>
213
+ <div class="header-value">${header.value}</div>
214
+ </div>
215
+ `)}
216
+ `}
217
+ </${Category.litTagName}>
218
+ `;
219
+ }
220
+
221
+ #renderRawHeaders(rawHeadersText: string, forResponseHeaders: boolean): LitHtml.TemplateResult {
222
+ const trimmed = rawHeadersText.trim();
223
+ const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
224
+ const isShortened = !showFull && trimmed.length > RAW_HEADER_CUTOFF;
225
+
226
+ const showMore = ():void => {
227
+ if (forResponseHeaders) {
228
+ this.#showResponseHeadersTextFull = true;
229
+ } else {
230
+ this.#showRequestHeadersTextFull = true;
231
+ }
232
+ this.#render();
233
+ };
234
+
235
+ const onContextMenuOpen = (event: Event): void => {
236
+ const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
237
+ if (!showFull) {
238
+ const contextMenu = new UI.ContextMenu.ContextMenu(event);
239
+ const section = contextMenu.newSection();
240
+ section.appendItem(i18nString(UIStrings.showMore), showMore);
241
+ void contextMenu.show();
242
+ }
243
+ };
244
+
245
+ const addContextMenuListener = (el: Element):void => {
246
+ if (isShortened) {
247
+ el.addEventListener('contextmenu', onContextMenuOpen);
248
+ }
249
+ };
250
+
251
+ return html`
252
+ <div class="row raw-headers-row" on-render=${ComponentHelpers.Directives.nodeRenderedCallback(addContextMenuListener)}>
253
+ <div class="raw-headers">${isShortened ? trimmed.substring(0, RAW_HEADER_CUTOFF) : trimmed}</div>
254
+ ${isShortened ? html`
255
+ <${Buttons.Button.Button.litTagName}
256
+ .size=${Buttons.Button.Size.SMALL}
257
+ .variant=${Buttons.Button.Variant.SECONDARY}
258
+ @click=${showMore}
259
+ >${i18nString(UIStrings.showMore)}</${Buttons.Button.Button.litTagName}>
260
+ ` : LitHtml.nothing}
261
+ </div>
262
+ `;
263
+ }
264
+
127
265
  #renderGeneralSection(): LitHtml.TemplateResult {
128
266
  assertNotNullOrUndefined(this.#request);
129
267
 
@@ -159,7 +297,10 @@ export class RequestHeadersComponent extends HTMLElement {
159
297
  // Disabled until https://crbug.com/1079231 is fixed.
160
298
  // clang-format off
161
299
  return html`
162
- <${Category.litTagName} .data=${{name: 'general', title: i18nString(UIStrings.general)} as CategoryData}>
300
+ <${Category.litTagName}
301
+ .data=${{name: 'general', title: i18nString(UIStrings.general)} as CategoryData}
302
+ aria-label=${i18nString(UIStrings.general)}
303
+ >
163
304
  <div class="row">
164
305
  <div class="header-name">${i18nString(UIStrings.requestUrl)}:</div>
165
306
  <div class="header-value">${this.#request.url()}</div>
@@ -192,9 +333,19 @@ export class RequestHeadersComponent extends HTMLElement {
192
333
  }
193
334
  }
194
335
 
336
+ export class ToggleRawHeadersEvent extends Event {
337
+ static readonly eventName = 'togglerawevent';
338
+
339
+ constructor() {
340
+ super(ToggleRawHeadersEvent.eventName, {});
341
+ }
342
+ }
343
+
195
344
  export interface CategoryData {
196
345
  name: string;
197
346
  title: Common.UIString.LocalizedString;
347
+ headerCount?: number;
348
+ checked?: boolean;
198
349
  }
199
350
 
200
351
  export class Category extends HTMLElement {
@@ -202,6 +353,8 @@ export class Category extends HTMLElement {
202
353
  readonly #shadow = this.attachShadow({mode: 'open'});
203
354
  #expandedSetting?: Common.Settings.Setting<boolean>;
204
355
  #title: Common.UIString.LocalizedString = Common.UIString.LocalizedEmptyString;
356
+ #headerCount?: number = undefined;
357
+ #checked: boolean|undefined = undefined;
205
358
 
206
359
  connectedCallback(): void {
207
360
  this.#shadow.adoptedStyleSheets = [requestHeadersViewStyles];
@@ -211,15 +364,35 @@ export class Category extends HTMLElement {
211
364
  this.#title = data.title;
212
365
  this.#expandedSetting =
213
366
  Common.Settings.Settings.instance().createSetting('request-info-' + data.name + '-category-expanded', true);
367
+ this.#headerCount = data.headerCount;
368
+ this.#checked = data.checked;
214
369
  this.#render();
215
370
  }
216
371
 
372
+ #onCheckboxToggle(): void {
373
+ this.dispatchEvent(new ToggleRawHeadersEvent());
374
+ }
375
+
217
376
  #render(): void {
377
+ const isOpen = this.#expandedSetting ? this.#expandedSetting.get() : true;
218
378
  // Disabled until https://crbug.com/1079231 is fixed.
219
379
  // clang-format off
220
380
  render(html`
221
- <details ?open=${this.#expandedSetting ? this.#expandedSetting.get() : true} @toggle=${this.#onToggle}>
222
- <summary class="header" @keydown=${this.#onSummaryKeyDown}>${this.#title}</summary>
381
+ <details ?open=${isOpen} @toggle=${this.#onToggle}>
382
+ <summary class="header" @keydown=${this.#onSummaryKeyDown}>
383
+ ${this.#title}${this.#headerCount ?
384
+ html`<span class="header-count"> (${this.#headerCount})</span>` :
385
+ LitHtml.nothing
386
+ }
387
+ ${this.#checked !== undefined ? html`
388
+ <span class="raw-checkbox-container">
389
+ <label>
390
+ <input type="checkbox" .checked=${this.#checked} @change=${this.#onCheckboxToggle} />
391
+ ${i18nString(UIStrings.raw)}
392
+ </label>
393
+ </span>
394
+ ` : LitHtml.nothing}
395
+ </summary>
223
396
  <slot></slot>
224
397
  </details>
225
398
  `, this.#shadow, {host: this});