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
@@ -51,8 +51,7 @@ import {
51
51
  type StepChanged
52
52
  } from './StepView.js';
53
53
 
54
- const {html, Decorators, Directives: {ref}, LitElement} = Lit;
55
- const {customElement, state} = Decorators;
54
+ const {html, Directives: {ref}} = Lit;
56
55
 
57
56
  const UIStrings = {
58
57
  /**
@@ -177,12 +176,6 @@ const GET_EXTENSIONS_URL = 'https://goo.gle/recorder-extension-list' as Platform
177
176
  const RECORDER_EXPLANATION_URL = 'https://developer.chrome.com/docs/devtools/recorder';
178
177
  const FEEDBACK_URL = 'https://goo.gle/recorder-feedback' as Platform.DevToolsPath.UrlString;
179
178
 
180
- declare global {
181
- interface HTMLElementTagNameMap {
182
- 'devtools-recorder-controller': RecorderController;
183
- }
184
- }
185
-
186
179
  interface StoredRecording {
187
180
  storageName: string;
188
181
  flow: Models.Schema.UserFlow;
@@ -225,45 +218,209 @@ function verifyFlowSize(flow: Models.Schema.UserFlow): void {
225
218
  }
226
219
  }
227
220
 
228
- @customElement('devtools-recorder-controller')
229
- export class RecorderController extends LitElement {
230
- @state() declare private currentRecordingSession?: Models.RecordingSession.RecordingSession;
231
- @state() declare private currentRecording: StoredRecording|undefined;
232
- @state() declare private currentStep?: Models.Schema.Step;
233
- @state() declare private recordingError?: Error;
221
+ export class RecorderController extends UI.Widget.VBox<DocumentFragment> {
222
+ #currentRecordingSession?: Models.RecordingSession.RecordingSession;
223
+ get currentRecordingSession(): Models.RecordingSession.RecordingSession|undefined {
224
+ return this.#currentRecordingSession;
225
+ }
226
+ set currentRecordingSession(value: Models.RecordingSession.RecordingSession|undefined) {
227
+ if (this.#currentRecordingSession !== value) {
228
+ this.#currentRecordingSession = value;
229
+ this.requestUpdate();
230
+ }
231
+ }
232
+
233
+ #currentRecording: StoredRecording|undefined;
234
+ get currentRecording(): StoredRecording|undefined {
235
+ return this.#currentRecording;
236
+ }
237
+ set currentRecording(value: StoredRecording|undefined) {
238
+ if (this.#currentRecording !== value) {
239
+ this.#currentRecording = value;
240
+ this.requestUpdate();
241
+ }
242
+ }
243
+
244
+ #currentStep?: Models.Schema.Step;
245
+ get currentStep(): Models.Schema.Step|undefined {
246
+ return this.#currentStep;
247
+ }
248
+ set currentStep(value: Models.Schema.Step|undefined) {
249
+ if (this.#currentStep !== value) {
250
+ this.#currentStep = value;
251
+ this.requestUpdate();
252
+ }
253
+ }
254
+
255
+ #recordingError?: Error;
256
+ get recordingError(): Error|undefined {
257
+ return this.#recordingError;
258
+ }
259
+ set recordingError(value: Error|undefined) {
260
+ if (this.#recordingError !== value) {
261
+ this.#recordingError = value;
262
+ this.requestUpdate();
263
+ }
264
+ }
234
265
 
235
266
  #storage = Models.RecordingStorage.RecordingStorage.instance();
236
267
  #screenshotStorage = Models.ScreenshotStorage.ScreenshotStorage.instance();
237
268
 
238
- @state() declare private isRecording: boolean;
239
- @state() declare private isToggling: boolean;
269
+ #isRecording = false;
270
+ get isRecording(): boolean {
271
+ return this.#isRecording;
272
+ }
273
+ set isRecording(value: boolean) {
274
+ if (this.#isRecording !== value) {
275
+ this.#isRecording = value;
276
+ this.requestUpdate();
277
+ }
278
+ }
279
+
280
+ #isToggling = false;
281
+ get isToggling(): boolean {
282
+ return this.#isToggling;
283
+ }
284
+ set isToggling(value: boolean) {
285
+ if (this.#isToggling !== value) {
286
+ this.#isToggling = value;
287
+ this.requestUpdate();
288
+ }
289
+ }
240
290
 
241
291
  // TODO: we keep the functionality to allow/disallow replay but right now it's not used.
242
292
  // It can be used to decide if we allow replay on a certain target for example.
243
293
  #replayAllowed = true;
244
- @state() declare private recordingPlayer?: Models.RecordingPlayer.RecordingPlayer;
245
- @state() declare private lastReplayResult?: Models.RecordingPlayer.ReplayResult;
294
+
295
+ #recordingPlayer?: Models.RecordingPlayer.RecordingPlayer;
296
+ get recordingPlayer(): Models.RecordingPlayer.RecordingPlayer|undefined {
297
+ return this.#recordingPlayer;
298
+ }
299
+ set recordingPlayer(value: Models.RecordingPlayer.RecordingPlayer|undefined) {
300
+ if (this.#recordingPlayer !== value) {
301
+ this.#recordingPlayer = value;
302
+ this.requestUpdate();
303
+ }
304
+ }
305
+
306
+ #lastReplayResult?: Models.RecordingPlayer.ReplayResult;
307
+ get lastReplayResult(): Models.RecordingPlayer.ReplayResult|undefined {
308
+ return this.#lastReplayResult;
309
+ }
310
+ set lastReplayResult(value: Models.RecordingPlayer.ReplayResult|undefined) {
311
+ if (this.#lastReplayResult !== value) {
312
+ this.#lastReplayResult = value;
313
+ this.requestUpdate();
314
+ }
315
+ }
316
+
246
317
  readonly #replayState: ReplayState = {isPlaying: false, isPausedOnBreakpoint: false};
247
318
 
248
- @state() declare private currentPage: Pages;
249
- @state() declare private previousPage?: Pages;
319
+ #currentPage: Pages = Pages.START_PAGE;
320
+ get currentPage(): Pages {
321
+ return this.#currentPage;
322
+ }
323
+ set currentPage(value: Pages) {
324
+ if (this.#currentPage !== value) {
325
+ this.#currentPage = value;
326
+ this.requestUpdate();
327
+ }
328
+ }
329
+
330
+ #previousPage?: Pages;
331
+ get previousPage(): Pages|undefined {
332
+ return this.#previousPage;
333
+ }
334
+ set previousPage(value: Pages|undefined) {
335
+ if (this.#previousPage !== value) {
336
+ this.#previousPage = value;
337
+ this.requestUpdate();
338
+ }
339
+ }
340
+
250
341
  #fileSelector?: HTMLInputElement;
251
342
 
252
- @state() declare private sections?: Models.Section.Section[];
253
- @state() declare private settings?: Models.RecordingSettings.RecordingSettings;
343
+ #sections?: Models.Section.Section[];
344
+ get sections(): Models.Section.Section[]|undefined {
345
+ return this.#sections;
346
+ }
347
+ set sections(value: Models.Section.Section[]|undefined) {
348
+ if (this.#sections !== value) {
349
+ this.#sections = value;
350
+ this.requestUpdate();
351
+ }
352
+ }
353
+
354
+ #settings?: Models.RecordingSettings.RecordingSettings;
355
+ get settings(): Models.RecordingSettings.RecordingSettings|undefined {
356
+ return this.#settings;
357
+ }
358
+ set settings(value: Models.RecordingSettings.RecordingSettings|undefined) {
359
+ if (this.#settings !== value) {
360
+ this.#settings = value;
361
+ this.requestUpdate();
362
+ }
363
+ }
364
+
365
+ #importError?: Error;
366
+ get importError(): Error|undefined {
367
+ return this.#importError;
368
+ }
369
+ set importError(value: Error|undefined) {
370
+ if (this.#importError !== value) {
371
+ this.#importError = value;
372
+ this.requestUpdate();
373
+ }
374
+ }
254
375
 
255
- @state() declare private importError?: Error;
376
+ #exportMenuExpanded = false;
377
+ get exportMenuExpanded(): boolean {
378
+ return this.#exportMenuExpanded;
379
+ }
380
+ set exportMenuExpanded(value: boolean) {
381
+ if (this.#exportMenuExpanded !== value) {
382
+ this.#exportMenuExpanded = value;
383
+ this.requestUpdate();
384
+ }
385
+ }
256
386
 
257
- @state() declare private exportMenuExpanded: boolean;
258
387
  #exportMenuButton: Buttons.Button.Button|undefined;
259
388
 
260
389
  #stepBreakpointIndexes = new Set<number>();
261
390
 
262
391
  #builtInConverters: readonly Converters.Converter.Converter[];
263
- @state() declare private extensionConverters: Converters.Converter.Converter[];
264
- @state() declare private replayExtensions: Extensions.ExtensionManager.Extension[];
392
+ #extensionConverters: Converters.Converter.Converter[] = [];
393
+ get extensionConverters(): Converters.Converter.Converter[] {
394
+ return this.#extensionConverters;
395
+ }
396
+ set extensionConverters(value: Converters.Converter.Converter[]) {
397
+ if (this.#extensionConverters !== value) {
398
+ this.#extensionConverters = value;
399
+ this.requestUpdate();
400
+ }
401
+ }
265
402
 
266
- @state() declare private viewDescriptor?: PublicExtensions.RecorderPluginManager.ViewDescriptor;
403
+ #replayExtensions: Extensions.ExtensionManager.Extension[] = [];
404
+ get replayExtensions(): Extensions.ExtensionManager.Extension[] {
405
+ return this.#replayExtensions;
406
+ }
407
+ set replayExtensions(value: Extensions.ExtensionManager.Extension[]) {
408
+ if (this.#replayExtensions !== value) {
409
+ this.#replayExtensions = value;
410
+ this.requestUpdate();
411
+ }
412
+ }
413
+
414
+ #viewDescriptor?: PublicExtensions.RecorderPluginManager.ViewDescriptor;
415
+ get viewDescriptor(): PublicExtensions.RecorderPluginManager.ViewDescriptor|undefined {
416
+ return this.#viewDescriptor;
417
+ }
418
+ set viewDescriptor(value: PublicExtensions.RecorderPluginManager.ViewDescriptor|undefined) {
419
+ if (this.#viewDescriptor !== value) {
420
+ this.#viewDescriptor = value;
421
+ this.requestUpdate();
422
+ }
423
+ }
267
424
  #extensionViewShowRequestedListener?:
268
425
  (event: Common.EventTarget.EventTargetEvent<PublicExtensions.RecorderPluginManager.ViewDescriptor>) => void;
269
426
 
@@ -279,7 +436,8 @@ export class RecorderController extends LitElement {
279
436
  #createRecordingView?: CreateRecordingView;
280
437
 
281
438
  constructor() {
282
- super();
439
+ const element = document.createElement('devtools-recorder-controller');
440
+ super(element, {useShadowDom: 'pure'});
283
441
 
284
442
  this.isRecording = false;
285
443
  this.isToggling = false;
@@ -306,11 +464,12 @@ export class RecorderController extends LitElement {
306
464
  });
307
465
 
308
466
  // used in e2e tests only.
309
- this.addEventListener('setrecording', (event: Event) => this.#onSetRecording(event));
467
+ /* eslint-disable-next-line @devtools/no-imperative-dom-api */
468
+ this.element.addEventListener('setrecording', (event: Event) => this.#onSetRecording(event));
310
469
  }
311
470
 
312
- override disconnectedCallback(): void {
313
- super.disconnectedCallback();
471
+ override onDetach(): void {
472
+ super.onDetach();
314
473
 
315
474
  if (this.currentRecordingSession) {
316
475
  void this.currentRecordingSession.stop();
@@ -623,7 +782,7 @@ export class RecorderController extends LitElement {
623
782
  Host.userMetrics.recordingReplayFinished(Host.UserMetrics.RecordingReplayFinished.OTHER_ERROR);
624
783
  }
625
784
  // Dispatch an event for e2e testing.
626
- this.dispatchEvent(new Events.ReplayFinishedEvent());
785
+ this.element.dispatchEvent(new Events.ReplayFinishedEvent());
627
786
  });
628
787
 
629
788
  this.recordingPlayer.addEventListener(Models.RecordingPlayer.Events.DONE, () => {
@@ -633,7 +792,7 @@ export class RecorderController extends LitElement {
633
792
  }
634
793
  this.lastReplayResult = Models.RecordingPlayer.ReplayResult.SUCCESS;
635
794
  // Dispatch an event for e2e testing.
636
- this.dispatchEvent(new Events.ReplayFinishedEvent());
795
+ this.element.dispatchEvent(new Events.ReplayFinishedEvent());
637
796
  Host.userMetrics.recordingReplayFinished(Host.UserMetrics.RecordingReplayFinished.SUCCESS);
638
797
  });
639
798
 
@@ -708,7 +867,7 @@ export class RecorderController extends LitElement {
708
867
  this.#setCurrentRecording(await this.#storage.upsertRecording(Models.SchemaUtils.parse(json)));
709
868
  this.#setCurrentPage(Pages.RECORDING_PAGE);
710
869
  this.#clearError();
711
- this.dispatchEvent(new Events.SetRecordingFinishedEvent());
870
+ this.element.dispatchEvent(new Events.SetRecordingFinishedEvent());
712
871
  }
713
872
 
714
873
  // Used by e2e tests to inspect the current recording.
@@ -988,7 +1147,7 @@ export class RecorderController extends LitElement {
988
1147
  this.#setCurrentPage(Pages.RECORDING_PAGE);
989
1148
 
990
1149
  // Dispatch an event for e2e testing.
991
- this.dispatchEvent(new Events.RecordingStateChangedEvent((this.currentRecording as StoredRecording).flow));
1150
+ this.element.dispatchEvent(new Events.RecordingStateChangedEvent((this.currentRecording as StoredRecording).flow));
992
1151
  }
993
1152
 
994
1153
  async #onRecordingFinished(): Promise<void> {
@@ -1011,7 +1170,7 @@ export class RecorderController extends LitElement {
1011
1170
  this.isRecording = false;
1012
1171
 
1013
1172
  // Dispatch an event for e2e testing.
1014
- this.dispatchEvent(new Events.RecordingStateChangedEvent(this.currentRecording.flow));
1173
+ this.element.dispatchEvent(new Events.RecordingStateChangedEvent(this.currentRecording.flow));
1015
1174
  }
1016
1175
 
1017
1176
  async onRecordingCancelled(): Promise<void> {
@@ -1271,10 +1430,10 @@ export class RecorderController extends LitElement {
1271
1430
  })),
1272
1431
  replayAllowed: this.#replayAllowed,
1273
1432
  })}
1274
- @createrecording=${this.#onCreateNewRecording}
1275
- @deleterecording=${this.#onDeleteRecording}
1276
- @openrecording=${this.#onRecordingSelected}
1277
- @playrecording=${this.#onPlayRecordingByName}
1433
+ @createrecording=${this.#onCreateNewRecording.bind(this)}
1434
+ @deleterecording=${this.#onDeleteRecording.bind(this)}
1435
+ @openrecording=${this.#onRecordingSelected.bind(this)}
1436
+ @playrecording=${this.#onPlayRecordingByName.bind(this)}
1278
1437
  >
1279
1438
  </devtools-widget>
1280
1439
  `;
@@ -1294,7 +1453,7 @@ export class RecorderController extends LitElement {
1294
1453
  jslogcontext="learn-more"
1295
1454
  >${i18nString(UIStrings.learnMore)}</devtools-link>
1296
1455
  </div>
1297
- <devtools-button .variant=${Buttons.Button.Variant.TONAL} jslogContext=${Actions.RecorderActions.CREATE_RECORDING} @click=${this.#onCreateNewRecording}>${i18nString(UIStrings.createRecording)}</devtools-button>
1456
+ <devtools-button .variant=${Buttons.Button.Variant.TONAL} jslogContext=${Actions.RecorderActions.CREATE_RECORDING} @click=${this.#onCreateNewRecording.bind(this)}>${i18nString(UIStrings.createRecording)}</devtools-button>
1298
1457
  </div>
1299
1458
  `;
1300
1459
  // clang-format on
@@ -1385,7 +1544,12 @@ export class RecorderController extends LitElement {
1385
1544
  this.exportMenuExpanded = false;
1386
1545
  }
1387
1546
 
1388
- protected override render(): Lit.TemplateResult {
1547
+ override performUpdate(): void {
1548
+ // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
1549
+ Lit.render(this.render(), this.contentElement);
1550
+ }
1551
+
1552
+ protected render(): Lit.TemplateResult {
1389
1553
  const recordings = this.#storage.getRecordings();
1390
1554
  const selectValue: string = this.currentRecording ? this.currentRecording.storageName : this.currentPage;
1391
1555
  // clang-format off
@@ -1414,7 +1578,7 @@ export class RecorderController extends LitElement {
1414
1578
  <div class="wrapper">
1415
1579
  <div class="header" jslog=${VisualLogging.toolbar()}>
1416
1580
  <devtools-button
1417
- @click=${this.#onCreateNewRecording}
1581
+ @click=${this.#onCreateNewRecording.bind(this)}
1418
1582
  .data=${
1419
1583
  {
1420
1584
  variant: Buttons.Button.Variant.TOOLBAR,
@@ -1440,7 +1604,7 @@ export class RecorderController extends LitElement {
1440
1604
  this.isToggling
1441
1605
  }
1442
1606
  @click=${(e: Event) => e.stopPropagation()}
1443
- @change=${this.#onRecordingSelected}
1607
+ @change=${this.#onRecordingSelected.bind(this)}
1444
1608
  jslog=${VisualLogging.dropDown('recordings').track({change: true})}
1445
1609
  >
1446
1610
  ${Lit.Directives.repeat(
@@ -1453,7 +1617,7 @@ export class RecorderController extends LitElement {
1453
1617
  </select>
1454
1618
  <div class="separator"></div>
1455
1619
  <devtools-button
1456
- @click=${this.#onImportRecording}
1620
+ @click=${this.#onImportRecording.bind(this)}
1457
1621
  .data=${
1458
1622
  {
1459
1623
  variant: Buttons.Button.Variant.TOOLBAR,
@@ -1465,7 +1629,7 @@ export class RecorderController extends LitElement {
1465
1629
  ></devtools-button>
1466
1630
  <devtools-button
1467
1631
  id='origin'
1468
- @click=${this.#onExportRecording}
1632
+ @click=${this.#onExportRecording.bind(this)}
1469
1633
  ${ref(el => {
1470
1634
  if (el instanceof HTMLElement) {
1471
1635
  this.#exportMenuButton = el as Buttons.Button.Button;
@@ -1482,8 +1646,8 @@ export class RecorderController extends LitElement {
1482
1646
  jslog=${VisualLogging.dropDown('export-recording').track({click: true})}
1483
1647
  ></devtools-button>
1484
1648
  <devtools-menu
1485
- @menucloserequest=${this.#onExportMenuClosed}
1486
- @menuitemselected=${this.#onExportOptionSelected}
1649
+ @menucloserequest=${this.#onExportMenuClosed.bind(this)}
1650
+ @menuitemselected=${this.#onExportOptionSelected.bind(this)}
1487
1651
  .origin=${this.#getExportMenuButton}
1488
1652
  .showDivider=${false}
1489
1653
  .showSelectedItem=${false}
@@ -1522,7 +1686,7 @@ export class RecorderController extends LitElement {
1522
1686
  </devtools-menu-group>
1523
1687
  </devtools-menu>
1524
1688
  <devtools-button
1525
- @click=${this.#onDeleteRecording}
1689
+ @click=${this.#onDeleteRecording.bind(this)}
1526
1690
  .data=${
1527
1691
  {
1528
1692
  variant: Buttons.Button.Variant.TOOLBAR,
@@ -20,7 +20,7 @@ export class RecorderPanel extends UI.Panel.Panel {
20
20
  super(RecorderPanel.panelName);
21
21
  this.element.setAttribute('jslog', `${VisualLogging.panel('chrome-recorder').track({resize: true})}`);
22
22
  this.#controller = new RecorderController();
23
- this.contentElement.append(this.#controller);
23
+ this.#controller.show(this.contentElement);
24
24
  this.setHideOnDetach();
25
25
  }
26
26
 
@@ -11,7 +11,7 @@ import * as Host from '../../core/host/host.js';
11
11
  import * as i18n from '../../core/i18n/i18n.js';
12
12
  import * as Platform from '../../core/platform/platform.js';
13
13
  import * as SDK from '../../core/sdk/sdk.js';
14
- import type * as Bindings from '../../models/bindings/bindings.js';
14
+ import * as Bindings from '../../models/bindings/bindings.js';
15
15
  import * as Persistence from '../../models/persistence/persistence.js';
16
16
  import * as TextUtils from '../../models/text_utils/text_utils.js';
17
17
  import * as Workspace from '../../models/workspace/workspace.js';
@@ -135,7 +135,8 @@ export class NetworkNavigatorView extends NavigatorView {
135
135
 
136
136
  override acceptProject(project: Workspace.Workspace.Project): boolean {
137
137
  return project.type() === Workspace.Workspace.projectTypes.Network &&
138
- SDK.TargetManager.TargetManager.instance().isInScope(project.target());
138
+ SDK.TargetManager.TargetManager.instance().isInScope(
139
+ Bindings.NetworkProject.NetworkProject.getTargetForProject(project));
139
140
  }
140
141
 
141
142
  onScopeChange(): void {
@@ -345,9 +345,6 @@ export class SourcesView extends Common.ObjectWrapper.eventMixin<EventTypes, typ
345
345
  if (view instanceof UI.View.SimpleView) {
346
346
  void view.toolbarItems().then(items => {
347
347
  this.#scriptViewToolbar.removeToolbarItems();
348
- for (const action of getRegisteredEditorActions()) {
349
- this.#scriptViewToolbar.appendToolbarItem(action.getOrCreateButton(this));
350
- }
351
348
  if (Array.isArray(items)) {
352
349
  items.map(item => this.#scriptViewToolbar.appendToolbarItem(item));
353
350
  } else {
@@ -657,20 +654,6 @@ export interface EventTypes {
657
654
  [Events.EDITOR_SELECTED]: Workspace.UISourceCode.UISourceCode;
658
655
  }
659
656
 
660
- export interface EditorAction {
661
- getOrCreateButton(sourcesView: SourcesView): UI.Toolbar.ToolbarButton;
662
- }
663
-
664
- const registeredEditorActions: Array<() => EditorAction> = [];
665
-
666
- export function registerEditorAction(editorAction: () => EditorAction): void {
667
- registeredEditorActions.push(editorAction);
668
- }
669
-
670
- export function getRegisteredEditorActions(): EditorAction[] {
671
- return registeredEditorActions.map(editorAction => editorAction());
672
- }
673
-
674
657
  export class SwitchFileActionDelegate implements UI.ActionRegistration.ActionDelegate {
675
658
  private static nextFile(currentUISourceCode: Workspace.UISourceCode.UISourceCode): Workspace.UISourceCode.UISourceCode
676
659
  |null {
@@ -6,8 +6,10 @@
6
6
 
7
7
  import * as Common from '../../core/common/common.js';
8
8
  import * as Host from '../../core/host/host.js';
9
+ import * as i18n from '../../core/i18n/i18n.js';
9
10
  import * as FormatterActions from '../../entrypoints/formatter_worker/FormatterActions.js'; // eslint-disable-line @devtools/es-modules-import
10
11
  import * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
12
+ import * as Formatter from '../../models/formatter/formatter.js';
11
13
  import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
12
14
  import * as Persistence from '../../models/persistence/persistence.js';
13
15
  import * as TextUtils from '../../models/text_utils/text_utils.js';
@@ -30,6 +32,15 @@ import {ResourceOriginPlugin} from './ResourceOriginPlugin.js';
30
32
  import {SnippetsPlugin} from './SnippetsPlugin.js';
31
33
  import {SourcesPanel} from './SourcesPanel.js';
32
34
 
35
+ const UIStrings = {
36
+ /**
37
+ * @description Title of the format button
38
+ */
39
+ format: 'Format',
40
+ } as const;
41
+ const str_ = i18n.i18n.registerUIStrings('panels/sources/UISourceCodeFrame.ts', UIStrings);
42
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
43
+
33
44
  export class UISourceCodeFrame extends Common.ObjectWrapper
34
45
  .eventMixin<EventTypes, typeof SourceFrame.SourceFrame.SourceFrameImpl>(
35
46
  SourceFrame.SourceFrame.SourceFrameImpl) {
@@ -290,6 +301,24 @@ export class UISourceCodeFrame extends Common.ObjectWrapper
290
301
  this.maybeSetContent(this.#uiSourceCode.workingCopyContentData());
291
302
  }
292
303
 
304
+ async #formatSourceInPlace(): Promise<void> {
305
+ const contentDataOrError = await this.workingCopy();
306
+ if (TextUtils.ContentData.ContentData.isError(contentDataOrError)) {
307
+ return;
308
+ }
309
+ const content = TextUtils.ContentData.ContentData.textOr(contentDataOrError, '');
310
+ const {formattedContent, formattedMapping} = await Formatter.ScriptFormatter.format(
311
+ Common.Settings.Settings.instance(), this.#uiSourceCode.contentType(), this.contentType, content);
312
+ if (this.#uiSourceCode.workingCopy() === formattedContent) {
313
+ return;
314
+ }
315
+ const selection = this.textEditor.toLineColumn(this.textEditor.state.selection.main.head);
316
+ const [lineNumber, columnNumber] =
317
+ formattedMapping.originalToFormatted(selection.lineNumber, selection.columnNumber);
318
+ this.#uiSourceCode.setWorkingCopy(formattedContent);
319
+ this.revealPosition({lineNumber, columnNumber});
320
+ }
321
+
293
322
  private onWorkingCopyCommitted(): void {
294
323
  if (!this.#muteSourceCodeEvents) {
295
324
  this.maybeSetContent(this.uiSourceCode().workingCopyContentData());
@@ -433,6 +462,19 @@ export class UISourceCodeFrame extends Common.ObjectWrapper
433
462
 
434
463
  override async toolbarItems(): Promise<UI.Toolbar.ToolbarItem[]> {
435
464
  const leftToolbarItems = await super.toolbarItems();
465
+
466
+ const isEditable = Persistence.Persistence.PersistenceImpl.instance().hasEditableContent(this.#uiSourceCode);
467
+ const isJavaScript = Common.ResourceType.ResourceType.isJavaScriptMimeType(this.contentType);
468
+ const isInplaceFormattable = isEditable && isJavaScript;
469
+
470
+ if (isInplaceFormattable) {
471
+ const formatButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.format), 'brackets');
472
+ formatButton.addEventListener(UI.Toolbar.ToolbarButton.Events.CLICK, () => {
473
+ void this.#formatSourceInPlace();
474
+ });
475
+ leftToolbarItems.unshift(formatButton);
476
+ }
477
+
436
478
  const rightToolbarItems = [];
437
479
  for (const plugin of this.plugins) {
438
480
  leftToolbarItems.push(...plugin.leftToolbarItems());
@@ -18,7 +18,6 @@ import * as EditingLocationHistoryManager from './EditingLocationHistoryManager.
18
18
  import * as FilePathScoreFunction from './FilePathScoreFunction.js';
19
19
  import * as FilteredUISourceCodeListProvider from './FilteredUISourceCodeListProvider.js';
20
20
  import * as GoToLineQuickOpen from './GoToLineQuickOpen.js';
21
- import * as InplaceFormatterEditorAction from './InplaceFormatterEditorAction.js';
22
21
  import * as NavigatorView from './NavigatorView.js';
23
22
  import * as OpenFileQuickOpen from './OpenFileQuickOpen.js';
24
23
  import * as OutlineQuickOpen from './OutlineQuickOpen.js';
@@ -54,7 +53,6 @@ export {
54
53
  FilePathScoreFunction,
55
54
  FilteredUISourceCodeListProvider,
56
55
  GoToLineQuickOpen,
57
- InplaceFormatterEditorAction,
58
56
  NavigatorView,
59
57
  OpenFileQuickOpen,
60
58
  OutlineQuickOpen,
@@ -168,7 +168,6 @@ export const enum VisualLoggingTrackName {
168
168
  THREAD_MAIN = 'thread.main',
169
169
  THREAD_FRAME = 'thread.frame',
170
170
  THREAD_WORKER = 'thread.worker',
171
- THREAD_AUCTION_WORKLET = 'thread.auction-worklet',
172
171
  THREAD_RASTERIZER = 'thread.rasterizer',
173
172
  THREAD_POOL = 'thread.pool',
174
173
  THREAD_OTHER = 'thread.other',
@@ -301,8 +300,6 @@ export class CompatibilityTracksAppender {
301
300
  }
302
301
  case Trace.Handlers.Threads.ThreadType.WORKER:
303
302
  return 3;
304
- case Trace.Handlers.Threads.ThreadType.AUCTION_WORKLET:
305
- return 3;
306
303
  case Trace.Handlers.Threads.ThreadType.RASTERIZER:
307
304
  return 4;
308
305
  case Trace.Handlers.Threads.ThreadType.THREAD_POOL:
@@ -328,20 +325,6 @@ export class CompatibilityTracksAppender {
328
325
  continue;
329
326
  }
330
327
 
331
- const matchingWorklet = this.#parsedTrace.data.AuctionWorklets.worklets.get(pid);
332
- if (matchingWorklet) {
333
- // Each AuctionWorklet has two key threads:
334
- // 1. the Utility Thread
335
- // 2. the V8 Helper Thread - either a bidder or seller. see buildNameForAuctionWorklet()
336
- // There are other threads in a worklet process, but we don't render them.
337
- const tids = [matchingWorklet.args.data.utilityThread.tid, matchingWorklet.args.data.v8HelperThread.tid];
338
- if (tids.includes(tid)) {
339
- this.#threadAppenders.push(new ThreadAppender(
340
- this, this.#parsedTrace, pid, tid, '', Trace.Handlers.Threads.ThreadType.AUCTION_WORKLET, entries, tree));
341
- }
342
- continue;
343
- }
344
-
345
328
  // The Common case… Add the main thread, or iframe, or thread pool, etc.
346
329
  this.#threadAppenders.push(new ThreadAppender(this, this.#parsedTrace, pid, tid, name, type, entries, tree));
347
330
  }