chrome-devtools-frontend 1.0.1519267 → 1.0.1520535

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 (94) hide show
  1. package/config/owner/COMMON_OWNERS +1 -2
  2. package/config/typescript/tsconfig.eslint.json +12 -1
  3. package/docs/ui_engineering.md +1011 -0
  4. package/front_end/core/host/GdpClient.ts +26 -5
  5. package/front_end/core/sdk/NetworkManager.ts +1 -0
  6. package/front_end/core/sdk/NetworkRequest.ts +10 -0
  7. package/front_end/entrypoints/main/MainImpl.ts +6 -1
  8. package/front_end/entrypoints/main/main-meta.ts +3 -3
  9. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +50 -48
  10. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
  11. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +128 -30
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +98 -63
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
  14. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +36 -21
  15. package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
  16. package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
  17. package/front_end/models/ai_assistance/performance/AIContext.ts +62 -7
  18. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -5
  19. package/front_end/models/badges/Badge.ts +6 -1
  20. package/front_end/models/badges/StarterBadge.ts +5 -0
  21. package/front_end/models/badges/UserBadges.ts +5 -4
  22. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
  23. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -7
  24. package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
  25. package/front_end/panels/ai_assistance/components/ChatView.ts +45 -69
  26. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +47 -1
  27. package/front_end/panels/ai_assistance/components/chatView.css +13 -1
  28. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  29. package/front_end/panels/animation/animationTimeline.css +4 -0
  30. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
  31. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
  32. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
  33. package/front_end/panels/console/ConsolePrompt.ts +6 -0
  34. package/front_end/panels/console/ConsoleView.ts +4 -2
  35. package/front_end/panels/coverage/CoverageListView.ts +146 -198
  36. package/front_end/panels/coverage/CoverageView.ts +48 -18
  37. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
  38. package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
  39. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  40. package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
  41. package/front_end/panels/search/SearchResultsPane.ts +48 -15
  42. package/front_end/panels/search/SearchView.ts +33 -30
  43. package/front_end/panels/search/searchView.css +0 -2
  44. package/front_end/panels/settings/components/SyncSection.ts +4 -4
  45. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +1 -4
  46. package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
  47. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
  48. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
  49. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
  50. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  51. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  52. package/front_end/panels/timeline/components/ExportTraceOptions.ts +56 -4
  53. package/front_end/panels/timeline/components/exportTraceOptions.css +5 -0
  54. package/front_end/third_party/chromium/README.chromium +1 -1
  55. package/front_end/third_party/lighthouse/README.chromium +8 -1
  56. package/front_end/third_party/puppeteer/README.chromium +2 -2
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  67. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  68. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  71. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  80. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  81. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  83. package/front_end/third_party/puppeteer/package/package.json +3 -2
  84. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  85. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
  86. package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
  87. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  88. package/front_end/third_party/puppeteer/package/src/util/Function.ts +1 -1
  89. package/front_end/tsconfig.json +12 -1
  90. package/front_end/ui/legacy/InspectorView.ts +86 -13
  91. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  92. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
  93. package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
  94. package/package.json +1 -1
@@ -212,3 +212,1014 @@ setMockConnectionResponseHandler('CSS.getHeaders', () => ({}));
212
212
  const presenter = new Presenter();
213
213
  presenter.doSomething();
214
214
  ```
215
+
216
+ # Migrating Widgets and other "legacy" components
217
+
218
+ This section provides a series of examples for migrating from imperative DOM manipulation to a declarative approach using lit-html templates.
219
+
220
+ ## Setting `className` on `this.element`
221
+
222
+ Instead of setting `className` directly on `this.element`, define the component's structure declaratively using a lit-html template.
223
+
224
+ **Before:**
225
+ ```typescript
226
+ class SomeWidget extends UI.Widget.Widget {
227
+ constructor() {
228
+ super();
229
+ this.element.className = 'some-class';
230
+ }
231
+ }
232
+ ```
233
+
234
+ **After:**
235
+ ```typescript
236
+
237
+ export const DEFAULT_VIEW = (input, _output, target) => {
238
+ render(html`
239
+ <div class="some-class"></div>`,
240
+ target, {host: input});
241
+ };
242
+ ```
243
+
244
+ ## Appending a new element
245
+
246
+ Instead of using `appendChild` with `document.createElement`, define the new element within a lit-html template.
247
+
248
+ **Before:**
249
+ ```typescript
250
+
251
+ class SomeWidget extends UI.Widget.Widget {
252
+ constructor() {
253
+ super();
254
+ this.contentElement.appendChild(document.createElement('div'));
255
+ }
256
+ }
257
+ ```
258
+
259
+ **After:**
260
+ ```typescript
261
+
262
+
263
+ export const DEFAULT_VIEW = (input, _output, target) => {
264
+ render(html`
265
+ <div>
266
+ <div></div>
267
+ </div>`,
268
+ target, {host: input});
269
+ };
270
+ ```
271
+
272
+ ## Setting multiple attributes and `textContent`
273
+
274
+ Combine setting `className`, attributes, and `textContent` into a single declarative lit-html template.
275
+
276
+ **Before:**
277
+ ```typescript
278
+
279
+ class SomeWidget extends UI.Widget.Widget {
280
+ constructor() {
281
+ super();
282
+ this.contentElement.className = 'some-class';
283
+ this.contentElement.setAttribute('aria-label', 'some-label');
284
+ this.contentElement.textContent = 'some-text';
285
+ }
286
+ }
287
+ ```
288
+
289
+ **After:**
290
+ ```typescript
291
+
292
+
293
+ export const DEFAULT_VIEW = (input, _output, target) => {
294
+ render(html`
295
+ <div class="some-class" aria-label="some-label">some-text</div>`,
296
+ target, {host: input});
297
+ };
298
+ ```
299
+
300
+ ## Creating a child, adding a class, and adding an event listener
301
+
302
+ Use a lit-html template to create the element, set multiple classes, and attach event listeners declaratively.
303
+
304
+ **Before:**
305
+ ```typescript
306
+
307
+ class SomeWidget extends UI.Widget.Widget {
308
+ constructor() {
309
+ super();
310
+ this.container = this.contentElement.createChild('div', 'some-class');
311
+ this.container.classList.add('container');
312
+ this.container.addEventListener('click', this.onClick.bind(this));
313
+ }
314
+ }
315
+ ```
316
+
317
+ **After:**
318
+ ```typescript
319
+
320
+
321
+ export const DEFAULT_VIEW = (input, _output, target) => {
322
+ render(html`
323
+ <div>
324
+ <div class="some-class container" @click=${this.onClick.bind(this)}></div>
325
+ </div>`,
326
+ target, {host: input});
327
+ };
328
+ ```
329
+
330
+ ## Setting inline styles
331
+
332
+ Set inline styles directly within the lit-html template using the `style` attribute.
333
+
334
+ **Before:**
335
+ ```typescript
336
+
337
+ class SomeWidget extends UI.Widget.Widget {
338
+ constructor() {
339
+ super();
340
+ this.contentElement.style.width = '100%';
341
+ this.contentElement.style.marginLeft = '10px';
342
+ }
343
+ }
344
+ ```
345
+
346
+ **After:**
347
+ ```typescript
348
+
349
+
350
+ export const DEFAULT_VIEW = (input, _output, target) => {
351
+ render(html`
352
+ <div style="width:100%; margin-left:10px"></div>`,
353
+ target, {host: input});
354
+ };
355
+ ```
356
+
357
+ ## Creating and appending a styled element
358
+
359
+ Replace `document.createElement`, setting `className`, and `appendChild` with a declarative lit-html template.
360
+
361
+ **Before:**
362
+ ```typescript
363
+
364
+ class SomeWidget extends UI.Widget.Widget {
365
+ constructor() {
366
+ super();
367
+ const div = document.createElement('div');
368
+ div.className = 'some-class';
369
+ this.contentElement.appendChild(div);
370
+ }
371
+ }
372
+ ```
373
+
374
+ **After:**
375
+ ```typescript
376
+
377
+
378
+ export const DEFAULT_VIEW = (input, _output, target) => {
379
+ render(html`
380
+ <div>
381
+ <div class="some-class"></div>
382
+ </div>`,
383
+ target, {host: input});
384
+ };
385
+ ```
386
+
387
+ ## Creating a child with text content
388
+
389
+ Define the element and its text content directly within a lit-html template.
390
+
391
+ **Before:**
392
+ ```typescript
393
+
394
+ class SomeWidget extends UI.Widget.Widget {
395
+ constructor() {
396
+ super();
397
+ this.contentElement.createChild('span', 'some-class').textContent = 'some-text';
398
+ }
399
+ }
400
+ ```
401
+
402
+ **After:**
403
+ ```typescript
404
+
405
+
406
+ export const DEFAULT_VIEW = (input, _output, target) => {
407
+ render(html`
408
+ <div>
409
+ <span class="some-class">some-text</span>
410
+ </div>`,
411
+ target, {host: input});
412
+ };
413
+ ```
414
+
415
+ ## Migrating `UI.Toolbar.ToolbarFilter`
416
+
417
+ Replace the imperative creation of a `ToolbarFilter` with the declarative `<devtools-toolbar-input>` component.
418
+
419
+ **Before:**
420
+ ```typescript
421
+
422
+ class SomeWidget extends UI.Widget.Widget {
423
+ constructor() {
424
+ super();
425
+ const toolbar = this.contentElement.createChild('devtools-toolbar');
426
+ const filterInput = new UI.Toolbar.ToolbarFilter('some-placeholder', 0.5, 1, undefined, this.complete.bind(this), false, 'some-filter');
427
+ filterInput.addEventListener(UI.Toolbar.ToolbarInput.Event.TEXT_CHANGED, this.onFilterChanged.bind(this));
428
+ filterInput.element.classList.add('completions');
429
+ filterInput.element.setAttribute('aria-hidden', 'true');
430
+ toolbar.appendToolbarItem(filterInput);
431
+ }
432
+ }
433
+ ```
434
+
435
+ **After:**
436
+ ```typescript
437
+
438
+
439
+ export const DEFAULT_VIEW = (input, _output, target) => {
440
+ render(html`
441
+ <div>
442
+ <devtools-toolbar>
443
+ <devtools-toolbar-input class="completions" type="filter" placeholder="some-placeholder"
444
+ list="completions" id="some-filter" aria-hidden="true"
445
+ @change=${this.onFilterChanged.bind(this)} style="flex-grow:0.5; flex-shrink:1">
446
+ <datalist id="completions">${this.complete.bind(this)}</datalist>
447
+ </devtools-toolbar-input>
448
+ </devtools-toolbar>
449
+ </div>`,
450
+ target, {host: input});
451
+ };
452
+ ```
453
+
454
+ ## Migrating `UI.Toolbar.ToolbarInput`
455
+
456
+ Replace the imperative creation of a `ToolbarInput` with the declarative `<devtools-toolbar-input>` component.
457
+
458
+ **Before:**
459
+ ```typescript
460
+
461
+ class SomeWidget extends UI.Widget.Widget {
462
+ constructor() {
463
+ super();
464
+ const toolbar = this.contentElement.createChild('devtools-toolbar');
465
+ const filterInput = new UI.Toolbar.ToolbarInput('some-placeholder', 'accessible-placeholder', 0.5, 1);
466
+ toolbar.appendToolbarItem(filterInput);
467
+ }
468
+ }
469
+ ```
470
+
471
+ **After:**
472
+ ```typescript
473
+
474
+
475
+ export const DEFAULT_VIEW = (input, _output, target) => {
476
+ render(html`
477
+ <div>
478
+ <devtools-toolbar>
479
+ <devtools-toolbar-input type="text" placeholder="some-placeholder"
480
+ aria-label="accessible-placeholder" style="flex-grow:0.5; flex-shrink:1"></devtools-toolbar-input>
481
+ </devtools-toolbar>
482
+ </div>`,
483
+ target, {host: input});
484
+ };
485
+ ```
486
+
487
+ ## Migrating `Adorners.Adorner.Adorner`
488
+
489
+ Replace the imperative creation of an `Adorner` with the declarative `<devtools-adorner>` component.
490
+
491
+ **Before:**
492
+ ```typescript
493
+
494
+ class SomeWidget extends UI.Widget.Widget {
495
+ constructor() {
496
+ super();
497
+ const adornerContent = document.createElement('span');
498
+ adornerContent.innerHTML = '<div style="font-size: 12px;">💫</div>';
499
+ const adorner = new Adorners.Adorner.Adorner();
500
+ adorner.classList.add('fix-perf-icon');
501
+ adorner.data = {
502
+ name: i18nString(UIStrings.fixMe),
503
+ content: adornerContent,
504
+ jslogContext: 'fix-perf',
505
+ };
506
+ this.contentElement.appendChild(adorner);
507
+ }
508
+ }
509
+ ```
510
+
511
+ **After:**
512
+ ```typescript
513
+
514
+
515
+ export const DEFAULT_VIEW = (input, _output, target) => {
516
+ render(html`
517
+ <div>
518
+ <devtools-adorner class="fix-perf-icon" aria-label=${i18nString(UIStrings.fixMe)}
519
+ jslog=${VisualLogging.adorner('fix-perf')}>
520
+ <span><div style="font-size: 12px;">💫</div></span>
521
+ </devtools-adorner>
522
+ </div>`,
523
+ target, {host: input});
524
+ };
525
+ ```
526
+
527
+ ## Migrating `UI.Toolbar.ToolbarButton`
528
+
529
+ Replace the imperative creation of a `ToolbarButton` with the declarative `<devtools-button>` component.
530
+
531
+ **Before:**
532
+ ```typescript
533
+
534
+ class SomeWidget extends UI.Widget.Widget {
535
+ constructor() {
536
+ super();
537
+ const toolbar = this.contentElement.createChild('devtools-toolbar');
538
+ const editButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.editName), 'edit', undefined, 'edit-name');
539
+ editButton.addEventListener(UI.Toolbar.ToolbarButton.Events.CLICK, this.onClick.bind(this));
540
+ toolbar.appendToolbarItem(editButton);
541
+ }
542
+ }
543
+ ```
544
+
545
+ **After:**
546
+ ```typescript
547
+
548
+
549
+ export const DEFAULT_VIEW = (input, _output, target) => {
550
+ render(html`
551
+ <div>
552
+ <devtools-toolbar>
553
+ <devtools-button title=${i18nString(UIStrings.editName)} @click=${this.onClick.bind(this)}
554
+ .variant=${Buttons.Button.Variant.TOOLBAR} .iconName=${'edit'}
555
+ .jslogContext=${'edit-name'}></devtools-button>
556
+ </devtools-toolbar>
557
+ </div>`,
558
+ target, {host: input});
559
+ };
560
+ ```
561
+
562
+ ## Migrating various HTML elements with multiple properties
563
+
564
+ Replace imperative creation of standard HTML elements like `<a>`, `<img>`, and `<input>` with their declarative equivalents in a lit-html template, setting their properties as attributes.
565
+
566
+ **Before:**
567
+ ```typescript
568
+
569
+ class SomeWidget extends UI.Widget.Widget {
570
+ constructor() {
571
+ super();
572
+ const input = document.createElement('input');
573
+ input.type = 'text';
574
+ input.placeholder = 'some-placeholder';
575
+ input.value = 'some-value';
576
+ input.disabled = !this.enabled;
577
+ input.checked = true
578
+ this.contentElement.append(input);
579
+
580
+ const anchor = document.createElement('a');
581
+ anchor.href = 'https://www.google.com';
582
+ anchor.innerText = 'some-text';
583
+ anchor.dataset.someKey = 'some-value';
584
+ anchor.role = 'some-role';
585
+ this.contentElement.insertBefore(anchor, input);
586
+
587
+ const img = document.createElement('img');
588
+ img.src = 'https://www.google.com/some-image.png';
589
+ img.alt = 'some-alt';
590
+ img.draggable = true;
591
+ img.height = 100;
592
+ img.hidden = 'hidden';
593
+ img.href = 'https://www.google.com';
594
+ img.id = 'some-id';
595
+ img.name = 'some-name';
596
+ img.rel = 'some-rel';
597
+ img.scope = 'some-scope';
598
+
599
+ input.insertAdjacentElement('beforebegin', img);
600
+ }
601
+ }
602
+ ```
603
+
604
+ **After:**
605
+ ```typescript
606
+
607
+
608
+ export const DEFAULT_VIEW = (input, _output, target) => {
609
+ render(html`
610
+ <div>
611
+ <a href="https://www.google.com" data-some-key="some-value" role="some-role">some-text</a>
612
+ <img src="https://www.google.com/some-image.png" alt="some-alt" draggable="true" height="100"
613
+ hidden="hidden" href="https://www.google.com" id="some-id" name="some-name" rel="some-rel"
614
+ scope="some-scope"></img>
615
+ <input type="text" placeholder="some-placeholder" value="some-value"
616
+ ?disabled=${!this.enabled} checked>
617
+ </div>`,
618
+ target, {host: input});
619
+ };
620
+ ```
621
+
622
+ ## Migrating `UI.UIUtils` helpers
623
+
624
+ Replace `UI.UIUtils` helper functions like `createLabel` and `createTextButton` with their declarative component counterparts like `<label>` and `<devtools-button>`.
625
+
626
+ **Before:**
627
+ ```typescript
628
+
629
+ class SomeWidget extends UI.Widget.Widget {
630
+ constructor() {
631
+ super();
632
+ const select = document.createElement('select');
633
+ select.add(UI.UIUtils.createOption('Option 1', '1', 'option-1'));
634
+ this.contentElement.appendChild(UI.UIUtils.createLabel('Some label:', 'some-label', select));
635
+ this.contentElement.appendChild(UI.UIUtils.createTextButton('Some button', onClick, {
636
+ className: 'some-class',
637
+ jslogContext: 'some-button',
638
+ variant: Buttons.Button.Variant.PRIMARY,
639
+ title: i18nString(UIStrings.someTitle),
640
+ iconName: 'some-icon'
641
+ }));
642
+ }
643
+ }
644
+ ```
645
+
646
+ **After:**
647
+ ```typescript
648
+
649
+
650
+ export const DEFAULT_VIEW = (input, _output, target) => {
651
+ render(html`
652
+ <div>
653
+ <label class="some-label">Some label:
654
+ <select>
655
+ <option value="1" jslog=${VisualLogging.dropDown('1').track({click: true})}>Option 1</option>
656
+ </select>
657
+ </label>
658
+ <devtools-button class="some-class" title=${i18nString(UIStrings.someTitle)} @click=${onClick}
659
+ .jslogContext=${'some-button'} .variant=${Buttons.Button.Variant.PRIMARY}>Some button</devtools-button>
660
+ </div>`,
661
+ target, {host: input});
662
+ };
663
+ ```
664
+
665
+ ## Migrating `UI.UIUtils.createTextChild`
666
+
667
+ Instead of using `createTextChild`, include the text directly inside the element in the lit-html template.
668
+
669
+ **Before:**
670
+ ```typescript
671
+
672
+ class SomeWidget extends UI.Widget.Widget {
673
+ constructor() {
674
+ super();
675
+ UI.UIUtils.createTextChild(this.contentElement.createChild('div', 'some-class'), 'some-text');
676
+ }
677
+ }
678
+ ```
679
+
680
+ **After:**
681
+ ```typescript
682
+
683
+
684
+ export const DEFAULT_VIEW = (input, _output, target) => {
685
+ render(html`
686
+ <div>
687
+ <div class="some-class">some-text</div>
688
+ </div>`,
689
+ target, {host: input});
690
+ };
691
+ ```
692
+
693
+ ## Migrating `Buttons.Button.Button` with ARIA and Tooltip helpers
694
+
695
+ Replace the imperative creation of a `Button` and subsequent modifications with ARIA and Tooltip helpers with a single declarative `<devtools-button>` component, setting properties like `role` and `title`.
696
+
697
+ **Before:**
698
+ ```typescript
699
+
700
+ class SomeWidget extends UI.Widget.Widget {
701
+ constructor() {
702
+ super();
703
+ this.button = new Buttons.Button.Button();
704
+ this.button.data = {
705
+ jslogContext: 'some-button',
706
+ variant: Buttons.Button.Variant.PRIMARY,
707
+ title: i18nString(UIStrings.someTitle),
708
+ };
709
+ UI.ARIAUtils.markAsPresentation(this.button);
710
+ UI.Tooltip.Tooltip.install(this.button, i18nString(UIStrings.someTooltip));
711
+ }
712
+ }
713
+ ```
714
+
715
+ **After:**
716
+ ```typescript
717
+
718
+ class SomeWidget extends UI.Widget.Widget {
719
+ constructor() {
720
+ super();
721
+ this.button = html`
722
+ <devtools-button role="presentation" title=${i18nString(UIStrings.someTooltip)}
723
+ .data=${{
724
+ jslogContext: 'some-button',
725
+ variant: Buttons.Button.Variant.PRIMARY,
726
+ title: i18nString(UIStrings.someTitle),
727
+ }}
728
+ ></devtools-button>`;
729
+ }
730
+ }
731
+ ```
732
+
733
+ ## Migrating `IconButton.Icon.Icon`
734
+
735
+ Replace the imperative `Icon` creation with the declarative `<devtools-icon>` component.
736
+
737
+ **Before:**
738
+ ```typescript
739
+
740
+ class SomeWidget extends UI.Widget.Widget {
741
+ constructor() {
742
+ super();
743
+ const icon = new IconButton.Icon.Icon();
744
+ icon.data = {iconName: 'checkmark', color: 'var(--icon-checkmark-green)', width: '14px', height: '14px'};
745
+ this.contentElement.appendChild(icon);
746
+ }
747
+ }
748
+ ```
749
+
750
+ **After:**
751
+ ```typescript
752
+
753
+
754
+ export const DEFAULT_VIEW = (input, _output, target) => {
755
+ render(html`
756
+ <div>
757
+ <devtools-icon name="checkmark"
758
+ style="color:var(--icon-checkmark-green); width:14px; height:14px"></devtools-icon>
759
+ </div>`,
760
+ target, {host: input});
761
+ };
762
+ ```
763
+
764
+ ## Migrating Checkboxes
765
+
766
+ Replace various imperative checkbox creation methods with the declarative `<devtools-checkbox>` component. For settings-backed checkboxes, use the `bindToSetting` directive.
767
+
768
+ **Before:**
769
+ ```typescript
770
+
771
+ class SomeWidget extends UI.Widget.Widget {
772
+ constructor() {
773
+ super();
774
+ this.contentElement.appendChild(UI.UIUtils.CheckboxLabel.create(
775
+ i18nString(UIStrings.someTitle), true, i18nString(UIStrings.someTooltip),
776
+ undefined, 'some-checkbox', true));
777
+ this.contentElement.appendChild(UI.UIUtils.CheckboxLabel.create());
778
+ this.contentElement.appendChild(UI.UIUtils.CheckboxLabel.createWithStringLiteral(
779
+ ':hover', undefined, undefined, 'some-other-checkbox'));
780
+
781
+ const toolbar = this.contentElement.createChild('devtools-toolbar');
782
+ toolbar.appendToolbarItem(new UI.Toolbar.ToolbarCheckbox(
783
+ i18nString(UIStrings.someToolbarTitle), i18nString(UIStrings.someToolbarTooltip),
784
+ this.someToolbarCheckboxClicked.bind(this), 'some-toolbar-checkbox'));
785
+ toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(
786
+ this.someSetting, i18nString(UIStrings.someToolbarTooltip), i18nString(UIStrings.alternateToolbarTitle)));
787
+ toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(this.someOtherSetting));
788
+ }
789
+ }
790
+ ```
791
+
792
+ **After:**
793
+ ```typescript
794
+
795
+
796
+ export const DEFAULT_VIEW = (input, _output, target) => {
797
+ render(html`
798
+ <div>
799
+ <devtools-checkbox class="small" checked>${i18nString(UIStrings.someTitle)}</devtools-checkbox>
800
+ <devtools-checkbox></devtools-checkbox>
801
+ <devtools-checkbox class="small">:hover</devtools-checkbox>
802
+ <devtools-toolbar>
803
+ <devtools-checkbox title=${i18nString(UIStrings.someToolbarTooltip)}
804
+ @click=${this.someToolbarCheckboxClicked.bind(this)}
805
+ .jslogContext=${'some-toolbar-checkbox'}>${i18nString(UIStrings.someToolbarTitle)}</devtools-checkbox>
806
+ <devtools-checkbox title=${i18nString(UIStrings.someToolbarTooltip)}
807
+ ${bindToSetting(this.someSetting)}>${i18nString(UIStrings.alternateToolbarTitle)}</devtools-checkbox>
808
+ <devtools-checkbox ${bindToSetting(this.someOtherSetting)}>${this.someOtherSetting.title()}</devtools-checkbox>
809
+ </devtools-toolbar>
810
+ </div>`,
811
+ target, {host: input});
812
+ };
813
+ ```
814
+
815
+ ## Migrating `iframe` creation
816
+
817
+ Replace `document.createElement('iframe')` and `setAttribute` calls with a declarative `<iframe>` tag in a lit-html template.
818
+
819
+ **Before:**
820
+ ```typescript
821
+
822
+ class SomeWidget extends UI.Widget.Widget {
823
+ constructor() {
824
+ super();
825
+ const iframe = document.createElement('iframe');
826
+ iframe.setAttribute('sandbox', '');
827
+ iframe.tabIndex = -1;
828
+ this.contentElement.appendChild(iframe);
829
+ }
830
+ }
831
+ ```
832
+
833
+ **After:**
834
+ ```typescript
835
+
836
+
837
+ export const DEFAULT_VIEW = (input, _output, target) => {
838
+ render(html`
839
+ <div>
840
+ <iframe sandbox tabindex="-1"></iframe>
841
+ </div>`,
842
+ target, {host: input});
843
+ };
844
+ ```
845
+
846
+ ## Migrating `UI.UIUtils.createInput`
847
+
848
+ Replace the `createInput` helper with a standard `<input>` element in a lit-html template.
849
+
850
+ **Before:**
851
+ ```typescript
852
+
853
+ class SomeWidget extends UI.Widget.Widget {
854
+ constructor() {
855
+ super();
856
+ this.contentElement.appendChild(UI.UIUtils.createInput('add-source-map', 'text', 'url'));
857
+ }
858
+ }
859
+ ```
860
+
861
+ **After:**
862
+ ```typescript
863
+
864
+
865
+ export const DEFAULT_VIEW = (input, _output, target) => {
866
+ render(html`
867
+ <div>
868
+ <input class="harmony-input add-source-map" spellcheck="false" type="text"
869
+ jslog=${VisualLogging.textField('url').track({keydown: 'Enter', change: true})}>
870
+ </div>`,
871
+ target, {host: input});
872
+ };
873
+ ```
874
+
875
+ ## Migrating `SortableDataGrid`, `ViewportDataGrid`, `DataGridImpl`
876
+
877
+ Replace the imperative `SortableDataGrid`, `ViewportDataGrid`, or `DataGridImpl` with the declarative `<devtools-data-grid>` component. Columns are defined using `<th>` elements inside a `<table>`.
878
+
879
+ **Before:**
880
+ ```typescript
881
+
882
+ class SomeWidget extends UI.Widget.Widget {
883
+ constructor() {
884
+ super();
885
+ this.#columns = [
886
+ {
887
+ id: 'node-id',
888
+ title: i18nString(UIStrings.element),
889
+ sortable: true,
890
+ weight: 50,
891
+ align: undefined,
892
+ },
893
+ {
894
+ id: 'declaration',
895
+ title: i18nString(UIStrings.declaration),
896
+ },
897
+ {
898
+ id: 'source-url',
899
+ title: i18nString(UIStrings.source),
900
+ sortable: false,
901
+ weight: 100,
902
+ align: DataGrid.DataGrid.Align.RIGHT,
903
+ },
904
+ ];
905
+
906
+ this.#dataGrid = new DataGrid.SortableDataGrid.SortableDataGrid({
907
+ displayName: i18nString(UIStrings.someTitle),
908
+ columns: this.#columns,
909
+ deleteCallback: undefined,
910
+ refreshCallback: undefined,
911
+ });
912
+ this.#dataGrid.setStriped(true);
913
+ this.#dataGrid.addEventListener(
914
+ DataGrid.DataGrid.Events.SORTING_CHANGED, this.#sortMediaQueryDataGrid.bind(this));
915
+
916
+ this.#dataGrid.asWidget().show(this.element);
917
+ }
918
+ }
919
+ ```
920
+
921
+ **After:**
922
+ ```typescript
923
+
924
+
925
+ export const DEFAULT_VIEW = (input, _output, target) => {
926
+ render(html`
927
+ <div>
928
+ <devtools-data-grid name=${i18nString(UIStrings.someTitle)} striped
929
+ @sort=${this.#sortMediaQueryDataGrid.bind(this)}>
930
+ <table>
931
+ <tr>
932
+ <th id="node-id" weight="50" sortable>${i18nString(UIStrings.element)}</th>
933
+ <th id="declaration">${i18nString(UIStrings.declaration)}</th>
934
+ <th id="source-url" weight="100" align="right">${i18nString(UIStrings.source)}</th>
935
+ </tr>
936
+ </table>
937
+ </devtools-data-grid>
938
+ </div>`,
939
+ target, {host: input});
940
+ };
941
+ ```
942
+
943
+ ## Migrating `DataGridNode.createCell`
944
+
945
+ The `createCell` method in a `DataGridNode` can be refactored to return a lit-html template for the cell's content, making it declarative.
946
+
947
+ **Before:**
948
+ ```typescript
949
+
950
+ class ElementNode extends DataGrid.SortableDataGrid.SortableDataGridNode<ElementNode> {
951
+ override createCell(columnId: string): HTMLElement {
952
+ const cell = this.createTD(columnId);
953
+ cell.classList.add('node-id');
954
+ cell.createChild('span', 'node-id-text').textContent = this.data.id;
955
+ return cell;
956
+ }
957
+ }
958
+ ```
959
+
960
+ **After:**
961
+ ```typescript
962
+
963
+ class ElementNode extends DataGrid.SortableDataGrid.SortableDataGridNode<ElementNode> {
964
+ override createCell(columnId: string): HTMLElement {
965
+ const cell = this.createTD(columnId);
966
+ render(html`
967
+ <td class="node-id">
968
+ <span class="node-id-text">${this.data.id}</span>
969
+ </td>`, cell);
970
+ return cell;
971
+ }
972
+ }
973
+ ```
974
+
975
+ ## Migrating `SplitWidget`
976
+
977
+ Replace the imperative `SplitWidget` with the declarative `<devtools-split-view>` component. Nested widgets can be rendered using `<devtools-widget>`.
978
+
979
+ **Before:**
980
+ ```typescript
981
+
982
+ class SomeWidget extends UI.Widget.Widget {
983
+ constructor() {
984
+ super();
985
+ this.#splitWidget = new UI.SplitWidget.SplitWidget(this.vertical, false, undefined, 200);
986
+ this.#splitWidget.show(this.element);
987
+
988
+ this.#mainContainer = new UI.SplitWidget.SplitWidget(true, true);
989
+ this.#resultsContainer = new UI.Widget.EmptyWidget();
990
+ this.#elementContainer = new DetailsView();
991
+
992
+ this.#mainContainer.setMainWidget(this.#resultsContainer);
993
+ this.#mainContainer.setSidebarWidget(this.#elementContainer);
994
+ this.#mainContainer.setVertical(false);
995
+ this.#mainContainer.setSecondIsSidebar(this.dockedLeft);
996
+
997
+ this.#sideBar = new SidebarPanel();
998
+ this.#sideBar.setMinimumSize(100, 25);
999
+ this.#splitWidget.setSidebarWidget(this.#sideBar);
1000
+ this.#splitWidget.setMainWidget(this.#mainContainer);
1001
+ }
1002
+ }
1003
+ ```
1004
+
1005
+ **After:**
1006
+ ```typescript
1007
+
1008
+
1009
+ export const DEFAULT_VIEW = (input, _output, target) => {
1010
+ render(html`
1011
+ <div>
1012
+ <devtools-split-view direction=${this.vertical ? 'column' : 'row'} sidebar-position="first"
1013
+ sidebar-initial-size="200">
1014
+ <devtools-widget slot="sidebar" .widgetConfig=${widgetConfig(SidebarPanel,
1015
+ {minimumSize: {width: 100, height: 25}})}></devtools-widget>
1016
+ <devtools-split-view direction="column" sidebar-position="second" slot="main"
1017
+ direction="row" sidebar-position="$this.dockedLeft ? 'second' : 'first'}">
1018
+ <devtools-widget slot="main" .widgetConfig=${widgetConfig(UI.Widget.EmptyWidget)}></devtools-widget>
1019
+ <devtools-widget slot="sidebar" .widgetConfig=${widgetConfig(DetailsView)}></devtools-widget>
1020
+ </devtools-split-view>
1021
+ </devtools-split-view>
1022
+ </div>`,
1023
+ target, {host: input});
1024
+ };
1025
+ ```
1026
+
1027
+ ## Migrating `UI.Fragment`
1028
+
1029
+ Replace `UI.Fragment.Fragment.build` with a standard lit-html template. To get a reference to an element, use the `ref` directive.
1030
+
1031
+ **Before:**
1032
+ ```typescript
1033
+
1034
+ class SomeWidget extends UI.Widget.Widget {
1035
+ constructor() {
1036
+ super();
1037
+ const contrastFragment = UI.Fragment.Fragment.build`
1038
+ <div class="contrast-container-in-grid" $="contrast-container-element">
1039
+ <span class="contrast-preview">Aa</span>
1040
+ <span>${contrastRatioString}</span>
1041
+ </div>`;
1042
+ this.contentElement.appendChild(contrastFragment.element());
1043
+ }
1044
+ }
1045
+ ```
1046
+
1047
+ **After:**
1048
+ ```typescript
1049
+
1050
+
1051
+ export const DEFAULT_VIEW = (input, output, target) => {
1052
+ render(html`
1053
+ <div>
1054
+ <div class="contrast-container-in-grid" ${ref(e => { output.contrastContainerElement = e; })}>
1055
+ <span class="contrast-preview">Aa</span>
1056
+ <span>${contrastRatioString}</span>
1057
+ </div>
1058
+ </div>`,
1059
+ target, {host: input});
1060
+ };
1061
+ ```
1062
+
1063
+ ## Migrating `UI.ARIAUtils` helpers
1064
+
1065
+ Replace calls to `UI.ARIAUtils` helper functions with the corresponding ARIA attributes directly in the lit-html template.
1066
+
1067
+ **Before:**
1068
+ ```typescript
1069
+
1070
+ class SomeWidget extends UI.Widget.Widget {
1071
+ constructor() {
1072
+ super();
1073
+ this.button = this.contentElement.createChild('button');
1074
+ UI.ARIAUtils.markAsMenuButton(this.button);
1075
+ const tree = this.contentElement.createChild('ul');
1076
+ UI.ARIAUtils.markAsTree(tree);
1077
+ UI.ARIAUtils.markAsTreeitem(tree.createChild('li'));
1078
+ const alert = this.contentElement.createChild('span');
1079
+ alert.textContent = 'Alert';
1080
+ UI.ARIAUtils.markAsAlert(alert);
1081
+ const slider = this.contentElement.createChild('input');
1082
+ UI.ARIAUtils.markAsSlider(slider, 10);
1083
+
1084
+ UI.ARIAUtils.setDescription(this.button, 'Some button');
1085
+ UI.ARIAUtils.setInvalid(slider, this.valid);
1086
+
1087
+ const progress = this.contentElement.createChild('div');
1088
+ UI.ARIAUtils.markAsProgressBar(progress);
1089
+ UI.ARIAUtils.setProgressBarValue(progress, 0.5, '50% done');
1090
+ }
1091
+ }
1092
+ ```
1093
+
1094
+ **After:**
1095
+ ```typescript
1096
+
1097
+
1098
+ export const DEFAULT_VIEW = (input, _output, target) => {
1099
+ render(html`
1100
+ <div>
1101
+ <button role="button" aria-haspopup="true" aria-description="Some button"></button>
1102
+ <ul role="tree">
1103
+ <li role="treeitem"></li>
1104
+ </ul>
1105
+ <span role="alert" aria-live="polite">Alert</span>
1106
+ <input role="slider" aria-valuemin="10" aria-valuemax="100" aria-invalid=${this.valid}>
1107
+ <div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0.5"
1108
+ aria-valuetext="50% done"></div>
1109
+ </div>`,
1110
+ target, {host: input});
1111
+ };
1112
+ ```
1113
+
1114
+ ## Migrating `EmptyWidget`
1115
+
1116
+ Replace the imperative `EmptyWidget` with a declarative `<devtools-widget>` and configure it with `widgetConfig` to render an `EmptyWidget`.
1117
+
1118
+ **Before:**
1119
+ ```typescript
1120
+
1121
+ class SomeWidget extends UI.Widget.Widget {
1122
+ constructor() {
1123
+ super();
1124
+ const widget = new UI.EmptyWidget.EmptyWidget(i18nString(UIStrings.nothingToSeeHere), this.explanation);
1125
+ widget.link = 'http://www.google.com';
1126
+ widget.show(this.element);
1127
+ }
1128
+ }
1129
+ ```
1130
+
1131
+ **After:**
1132
+ ```typescript
1133
+
1134
+
1135
+ export const DEFAULT_VIEW = (input, _output, target) => {
1136
+ render(html`
1137
+ <div>
1138
+ <devtools-widget .widgetConfig=${widgetConfig(UI.EmptyWidget.EmptyWidget,{
1139
+ header: i18nString(UIStrings.nothingToSeeHere), text: this.explanation,
1140
+ link: 'http://www.google.com',})}></devtools-widget>
1141
+ </div>`,
1142
+ target, {host: input});
1143
+ };
1144
+ ```
1145
+
1146
+ ## Migrating `TextPrompt`
1147
+
1148
+ Replace imperative `TextPrompt` creation with the declarative `<devtools-prompt>` component, providing completions via `<datalist>`.
1149
+
1150
+ **Before:**
1151
+ ```typescript
1152
+ class SomeWidget extends UI.Widget.Widget {
1153
+ constructor() {
1154
+ super();
1155
+ const prompt = new UI.TextPrompt.TextPrompt();
1156
+ prompt.initialize(async (expression, prefix) => {
1157
+ const options = ['completion1', 'completion2'];
1158
+ return options.filter(opt => opt.startsWith(prefix)).map(text => ({text}));
1159
+ });
1160
+ const promptElement = this.contentElement.createChild('span');
1161
+ prompt.attach(promptElement);
1162
+ }
1163
+ }
1164
+ ```
1165
+
1166
+ **After:**
1167
+ ```typescript
1168
+ export const DEFAULT_VIEW = (input, _output, target) => {
1169
+ render(html`
1170
+ <div>
1171
+ <datalist id="my-completions">
1172
+ <option>completion1</option>
1173
+ <option>completion2</option>
1174
+ </datalist>
1175
+ <devtools-prompt completions="my-completions" editing></devtools-prompt>
1176
+ </div>`,
1177
+ target, {host: input});
1178
+ };
1179
+ ```
1180
+
1181
+ ## Migrating `TreeOutline`
1182
+
1183
+ Replace imperative `TreeOutline` and `TreeElement` creation with the declarative `<devtools-tree>` component.
1184
+
1185
+ **Before:**
1186
+ ```typescript
1187
+ class SomeWidget extends UI.Widget.Widget {
1188
+ constructor() {
1189
+ super();
1190
+ const tree = new UI.TreeOutline.TreeOutlineInShadow();
1191
+ this.contentElement.appendChild(tree.element);
1192
+
1193
+ const root = tree.rootElement();
1194
+ const node1 = new UI.TreeOutline.TreeElement('Node 1');
1195
+ root.appendChild(node1);
1196
+
1197
+ const node2 = new UI.TreeOutline.TreeElement('Node 2', true);
1198
+ root.appendChild(node2);
1199
+
1200
+ const child1 = new UI.TreeOutline.TreeElement('Child 1');
1201
+ node2.appendChild(child1);
1202
+ }
1203
+ }
1204
+ ```
1205
+
1206
+ **After:**
1207
+ ```typescript
1208
+ export const DEFAULT_VIEW = (input, _output, target) => {
1209
+ render(html`
1210
+ <div>
1211
+ <devtools-tree .template=${html`
1212
+ <ul role="tree">
1213
+ <li role="treeitem">Node 1</li>
1214
+ <li role="treeitem">
1215
+ Node 2
1216
+ <ul role="group" hidden>
1217
+ <li role="treeitem">Child 1</li>
1218
+ </ul>
1219
+ </li>
1220
+ </ul>
1221
+ `}></devtools-tree>
1222
+ </div>`,
1223
+ target, {host: input});
1224
+ };
1225
+ ```