chrome-devtools-frontend 1.0.1593518 → 1.0.1593959

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 (50) hide show
  1. package/docs/contributing/settings-experiments-features.md +75 -100
  2. package/front_end/core/host/UserMetrics.ts +1 -2
  3. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  4. package/front_end/core/root/ExperimentNames.ts +0 -1
  5. package/front_end/core/sdk/EmulationModel.ts +41 -1
  6. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  7. package/front_end/generated/InspectorBackendCommands.ts +6 -4
  8. package/front_end/generated/protocol-mapping.d.ts +7 -0
  9. package/front_end/generated/protocol-proxy-api.d.ts +5 -0
  10. package/front_end/generated/protocol.ts +47 -0
  11. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -0
  12. package/front_end/models/emulation/DeviceModeModel.ts +47 -0
  13. package/front_end/models/issues_manager/IssueAggregator.ts +0 -9
  14. package/front_end/models/issues_manager/IssuesManager.ts +0 -5
  15. package/front_end/models/issues_manager/issues_manager.ts +0 -4
  16. package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
  17. package/front_end/models/lighthouse/lighthouse.ts +9 -0
  18. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +3 -0
  19. package/front_end/panels/common/AiCodeGenerationTeaser.ts +3 -0
  20. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +1 -6
  21. package/front_end/panels/elements/StylesSidebarPane.ts +79 -0
  22. package/front_end/panels/emulation/DeviceModeToolbar.ts +206 -66
  23. package/front_end/panels/issues/HiddenIssuesRow.ts +1 -1
  24. package/front_end/panels/issues/IssueKindView.ts +2 -1
  25. package/front_end/panels/issues/IssueView.ts +2 -4
  26. package/front_end/panels/issues/IssuesPane.ts +2 -2
  27. package/front_end/panels/lighthouse/LighthouseController.ts +3 -3
  28. package/front_end/panels/lighthouse/LighthousePanel.ts +9 -5
  29. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +5 -5
  30. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +6 -7
  31. package/front_end/panels/lighthouse/LighthouseReportSelector.ts +4 -3
  32. package/front_end/panels/lighthouse/lighthouse.ts +0 -2
  33. package/front_end/third_party/chromium/README.chromium +1 -1
  34. package/front_end/third_party/source-map-scopes-codec/package/deno.json +1 -1
  35. package/front_end/third_party/source-map-scopes-codec/package/package.json +1 -1
  36. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +1 -1
  37. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js.map +1 -1
  38. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.ts +1 -1
  39. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
  40. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js.map +1 -1
  41. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.ts +2 -2
  42. package/front_end/ui/legacy/SuggestBox.ts +4 -0
  43. package/front_end/ui/legacy/TextPrompt.ts +1 -1
  44. package/front_end/ui/visual_logging/KnownContextValues.ts +15 -0
  45. package/inspector_overlay/highlight_grid_common.ts +11 -8
  46. package/package.json +1 -1
  47. package/front_end/models/issues_manager/ContrastCheckTrigger.ts +0 -78
  48. package/front_end/models/issues_manager/LowTextContrastIssue.ts +0 -63
  49. package/front_end/panels/issues/AffectedElementsWithLowContrastView.ts +0 -91
  50. /package/front_end/{panels → models}/lighthouse/LighthouseReporterTypes.ts +0 -0
@@ -10,6 +10,7 @@ import * as Host from '../../core/host/host.js';
10
10
  import * as i18n from '../../core/i18n/i18n.js';
11
11
  import * as Platform from '../../core/platform/platform.js';
12
12
  import * as EmulationModel from '../../models/emulation/emulation.js';
13
+ import * as uiI18n from '../../ui/i18n/i18n.js';
13
14
  import * as UI from '../../ui/legacy/legacy.js';
14
15
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
15
16
  import * as MobileThrottling from '../mobile_throttling/mobile_throttling.js';
@@ -19,8 +20,14 @@ const UIStrings = {
19
20
  /**
20
21
  * @description Title of the device dimensions selection item in the Device Mode Toolbar.
21
22
  * webpage in pixels.
23
+ * @example {Responsive} PH1
22
24
  */
23
- dimensions: 'Dimensions',
25
+ dimensions: 'Dimensions: {PH1}',
26
+ /**
27
+ * @description Title of the device pixel ratio selection item in the Device Mode Toolbar.
28
+ * @example {2.0} PH1
29
+ */
30
+ dpr: 'DPR: {PH1}',
24
31
  /**
25
32
  * @description Title of the width input textbox in the Device Mode Toolbar, for the width of the
26
33
  * webpage in pixels.
@@ -53,12 +60,12 @@ const UIStrings = {
53
60
  */
54
61
  moreOptions: 'More options',
55
62
  /**
56
- * @description A context menu item in the Device Mode Toolbar. This is a command to resize the
57
- * webpage preview to fit the current window. The placeholder is the percentage of full-size that
58
- * will be displayed after fitting.
63
+ * @description A menu item in the drop-down box that allows the user to select the zoom level.
64
+ * Labels the value which corresponds to the 'fit to window' zoom level, represented by the
65
+ * placeholder, which is a number. In the Device Mode Toolbar.
59
66
  * @example {30.0} PH1
60
67
  */
61
- fitToWindowF: 'Fit to window ({PH1}%)',
68
+ fitToWindowPercentage: '{PH1}% (fit to window)',
62
69
  /**
63
70
  * @description A checkbox setting that appears in the context menu for the zoom level, in the
64
71
  * Device Mode Toolbar.
@@ -70,7 +77,7 @@ const UIStrings = {
70
77
  * placeholder, which is a number. In the Device Mode Toolbar.
71
78
  * @example {4.3} PH1
72
79
  */
73
- defaultF: 'Default: {PH1}',
80
+ defaultF: '{PH1} (default)',
74
81
  /**
75
82
  * @description Command to hide the frame (like a picture frame) around the mobile device screen.
76
83
  */
@@ -163,6 +170,11 @@ const UIStrings = {
163
170
  * @description Tooltip of the rotate/screen orientation button.
164
171
  */
165
172
  screenOrientationOptions: 'Screen orientation options',
173
+ /**
174
+ * @description Tooltip shown on the rotate button when screen orientation is locked by the page
175
+ * via screen.orientation.lock().
176
+ */
177
+ screenOrientationLocked: 'Screen orientation is locked by the page',
166
178
  /**
167
179
  * @description Tooltip for a button which turns on/off dual-screen mode, which emulates devices
168
180
  * like tablets which have two screens.
@@ -284,6 +296,9 @@ export class DeviceModeToolbar {
284
296
  new UI.Toolbar.ToolbarMenuButton(this.appendDeviceMenuItems.bind(this), undefined, undefined, 'device');
285
297
  this.deviceSelectItem.turnShrinkable();
286
298
  this.deviceSelectItem.setDarkText();
299
+ const dimensionsSpan =
300
+ uiI18n.getFormatLocalizedString(str_, UIStrings.dimensions, {PH1: this.deviceSelectItem.element});
301
+ mainToolbar.append(...dimensionsSpan.childNodes);
287
302
  mainToolbar.appendToolbarItem(this.deviceSelectItem);
288
303
 
289
304
  this.widthInput.addEventListener('sizechanged', ({size: width}) => {
@@ -315,6 +330,11 @@ export class DeviceModeToolbar {
315
330
  this.scaleItem.turnShrinkable();
316
331
  this.scaleItem.setDarkText();
317
332
  mainToolbar.appendToolbarItem(this.scaleItem);
333
+
334
+ const autoAdjustScaleButton = new UI.Toolbar.ToolbarSettingToggle(
335
+ this.autoAdjustScaleSetting, 'center-focus-weak', i18nString(UIStrings.autoadjustZoom));
336
+ mainToolbar.appendToolbarItem(autoAdjustScaleButton);
337
+
318
338
  mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
319
339
 
320
340
  this.deviceScaleItem = new UI.Toolbar.ToolbarMenuButton(
@@ -323,6 +343,8 @@ export class DeviceModeToolbar {
323
343
  this.deviceScaleItem.setVisible(this.showDeviceScaleFactorSetting.get());
324
344
  setTitleForButton(this.deviceScaleItem, i18nString(UIStrings.devicePixelRatio));
325
345
  this.deviceScaleItem.setDarkText();
346
+ const deviceScaleSpan = uiI18n.getFormatLocalizedString(str_, UIStrings.dpr, {PH1: this.deviceScaleItem.element});
347
+ mainToolbar.append(...deviceScaleSpan.childNodes);
326
348
  mainToolbar.appendToolbarItem(this.deviceScaleItem);
327
349
  mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
328
350
  this.uaItem =
@@ -374,11 +396,16 @@ export class DeviceModeToolbar {
374
396
  return optionsToolbar;
375
397
  }
376
398
 
399
+ private getDevicePostureOptions(): Array<{title: string, value: string, selected: boolean}> {
400
+ const currentPosture = this.currentDevicePosture();
401
+ return ['Continuous', 'Folded'].map(title => ({title, value: title, selected: currentPosture === title}));
402
+ }
403
+
377
404
  private appendDevicePostureItems(contextMenu: UI.ContextMenu.ContextMenu): void {
378
- for (const title of ['Continuous', 'Folded']) {
405
+ for (const option of this.getDevicePostureOptions()) {
379
406
  contextMenu.defaultSection().appendCheckboxItem(
380
- title, this.spanClicked.bind(this),
381
- {checked: title === this.currentDevicePosture(), jslogContext: title.toLowerCase()});
407
+ option.title, this.spanClicked.bind(this),
408
+ {checked: option.selected, jslogContext: option.title.toLowerCase()});
382
409
  }
383
410
  }
384
411
 
@@ -392,27 +419,43 @@ export class DeviceModeToolbar {
392
419
  return 'Continuous';
393
420
  }
394
421
 
395
- private appendScaleMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
422
+ private getScaleOptions(): Array<{title: string, value: number, selected: boolean, jslogContext: string}> {
423
+ const values = [0.5, 0.75, 1, 1.25, 1.5, 2];
424
+ let fitValue: number|null = null;
396
425
  if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
397
- contextMenu.footerSection().appendItem(
398
- i18nString(UIStrings.fitToWindowF, {PH1: this.getPrettyFitZoomPercentage()}),
399
- this.onScaleMenuChanged.bind(this, this.model.fitScale()), {jslogContext: 'fit-to-window'});
400
- }
401
- contextMenu.footerSection().appendCheckboxItem(
402
- i18nString(UIStrings.autoadjustZoom), this.onAutoAdjustScaleChanged.bind(this),
403
- {checked: this.autoAdjustScaleSetting.get(), jslogContext: 'auto-adjust-zoom'});
404
- const boundAppendScaleItem = appendScaleItem.bind(this);
405
- boundAppendScaleItem('50%', 0.5);
406
- boundAppendScaleItem('75%', 0.75);
407
- boundAppendScaleItem('100%', 1);
408
- boundAppendScaleItem('125%', 1.25);
409
- boundAppendScaleItem('150%', 1.5);
410
- boundAppendScaleItem('200%', 2);
411
-
412
- function appendScaleItem(this: DeviceModeToolbar, title: string, value: number): void {
426
+ fitValue = this.model.fitScale();
427
+ const fitValuePct = (fitValue * 100).toFixed(0);
428
+ let found = false;
429
+ for (let i = 0; i < values.length; ++i) {
430
+ if ((values[i] * 100).toFixed(0) === fitValuePct) {
431
+ found = true;
432
+ values[i] = fitValue;
433
+ break;
434
+ }
435
+ }
436
+ if (!found) {
437
+ values.push(fitValue);
438
+ values.sort((a, b) => a - b);
439
+ }
440
+ }
441
+
442
+ const currentScale = this.model.scaleSetting().get();
443
+ return values.map(value => {
444
+ let title = (value * 100).toFixed(0) + '%';
445
+ let jslogContext = title;
446
+ if (value === fitValue) {
447
+ title = i18nString(UIStrings.fitToWindowPercentage, {PH1: (value * 100).toFixed(0)});
448
+ jslogContext = 'fit-to-window';
449
+ }
450
+ return {title, value, selected: currentScale === value, jslogContext};
451
+ });
452
+ }
453
+
454
+ private appendScaleMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
455
+ for (const option of this.getScaleOptions()) {
413
456
  contextMenu.defaultSection().appendCheckboxItem(
414
- title, this.onScaleMenuChanged.bind(this, value),
415
- {checked: this.model.scaleSetting().get() === value, jslogContext: title});
457
+ option.title, this.onScaleMenuChanged.bind(this, option.value),
458
+ {checked: option.selected, jslogContext: option.jslogContext});
416
459
  }
417
460
  }
418
461
 
@@ -420,41 +463,68 @@ export class DeviceModeToolbar {
420
463
  this.model.scaleSetting().set(value);
421
464
  }
422
465
 
423
- private onAutoAdjustScaleChanged(): void {
424
- this.autoAdjustScaleSetting.set(!this.autoAdjustScaleSetting.get());
425
- }
426
-
427
- private appendDeviceScaleMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
466
+ private getDeviceScaleFactorOptions():
467
+ Array<{title: string, value: number, selected: boolean, jslogContext: string}> {
428
468
  const deviceScaleFactorSetting = this.model.deviceScaleFactorSetting();
429
469
  const defaultValue = this.model.uaSetting().get() === EmulationModel.DeviceModeModel.UA.MOBILE ||
430
470
  this.model.uaSetting().get() === EmulationModel.DeviceModeModel.UA.MOBILE_NO_TOUCH ?
431
471
  EmulationModel.DeviceModeModel.defaultMobileScaleFactor :
432
472
  window.devicePixelRatio;
433
- appendDeviceScaleFactorItem(
434
- contextMenu.headerSection(), i18nString(UIStrings.defaultF, {PH1: defaultValue}), 0, 'dpr-default');
435
- appendDeviceScaleFactorItem(contextMenu.defaultSection(), '1', 1, 'dpr-1');
436
- appendDeviceScaleFactorItem(contextMenu.defaultSection(), '2', 2, 'dpr-2');
437
- appendDeviceScaleFactorItem(contextMenu.defaultSection(), '3', 3, 'dpr-3');
473
+ const values = [1, 2, 3];
474
+ if (!values.includes(defaultValue)) {
475
+ values.push(defaultValue);
476
+ values.sort((a, b) => a - b);
477
+ }
478
+
479
+ const currentDPR = deviceScaleFactorSetting.get();
480
+ return values.map(value => {
481
+ let title = String(value);
482
+ let jslogContext = `dpr-${value}`;
483
+ if (value === defaultValue) {
484
+ title = i18nString(UIStrings.defaultF, {PH1: value});
485
+ jslogContext = 'dpr-default';
486
+ }
487
+ return {
488
+ title,
489
+ value: value === defaultValue ? 0 : value,
490
+ selected: currentDPR === value || (value === defaultValue && currentDPR === 0),
491
+ jslogContext
492
+ };
493
+ });
494
+ }
438
495
 
439
- function appendDeviceScaleFactorItem(
440
- section: UI.ContextMenu.Section, title: string, value: number, jslogContext: string): void {
441
- section.appendCheckboxItem(
442
- title, deviceScaleFactorSetting.set.bind(deviceScaleFactorSetting, value),
443
- {checked: deviceScaleFactorSetting.get() === value, jslogContext});
496
+ private appendDeviceScaleMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
497
+ const deviceScaleFactorSetting = this.model.deviceScaleFactorSetting();
498
+ for (const option of this.getDeviceScaleFactorOptions()) {
499
+ contextMenu.defaultSection().appendCheckboxItem(
500
+ option.title, deviceScaleFactorSetting.set.bind(deviceScaleFactorSetting, option.value),
501
+ {checked: option.selected, jslogContext: option.jslogContext});
444
502
  }
445
503
  }
446
504
 
447
- private appendUserAgentMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
505
+ private getUserAgentOptions():
506
+ Array<{title: string, value: EmulationModel.DeviceModeModel.UA, selected: boolean, jslogContext: string}> {
448
507
  const uaSetting = this.model.uaSetting();
449
- appendUAItem(EmulationModel.DeviceModeModel.UA.MOBILE, EmulationModel.DeviceModeModel.UA.MOBILE);
450
- appendUAItem(EmulationModel.DeviceModeModel.UA.MOBILE_NO_TOUCH, EmulationModel.DeviceModeModel.UA.MOBILE_NO_TOUCH);
451
- appendUAItem(EmulationModel.DeviceModeModel.UA.DESKTOP, EmulationModel.DeviceModeModel.UA.DESKTOP);
452
- appendUAItem(EmulationModel.DeviceModeModel.UA.DESKTOP_TOUCH, EmulationModel.DeviceModeModel.UA.DESKTOP_TOUCH);
508
+ const currentUserAgent = uaSetting.get();
509
+ return [
510
+ EmulationModel.DeviceModeModel.UA.MOBILE,
511
+ EmulationModel.DeviceModeModel.UA.MOBILE_NO_TOUCH,
512
+ EmulationModel.DeviceModeModel.UA.DESKTOP,
513
+ EmulationModel.DeviceModeModel.UA.DESKTOP_TOUCH,
514
+ ].map(value => ({
515
+ title: value,
516
+ value,
517
+ selected: currentUserAgent === value,
518
+ jslogContext: Platform.StringUtilities.toKebabCase(value)
519
+ }));
520
+ }
453
521
 
454
- function appendUAItem(title: string, value: EmulationModel.DeviceModeModel.UA): void {
522
+ private appendUserAgentMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
523
+ const uaSetting = this.model.uaSetting();
524
+ for (const option of this.getUserAgentOptions()) {
455
525
  contextMenu.defaultSection().appendCheckboxItem(
456
- title, uaSetting.set.bind(uaSetting, value),
457
- {checked: uaSetting.get() === value, jslogContext: Platform.StringUtilities.toKebabCase(value)});
526
+ option.title, uaSetting.set.bind(uaSetting, option.value),
527
+ {checked: option.selected, jslogContext: option.jslogContext});
458
528
  }
459
529
  }
460
530
 
@@ -539,25 +609,75 @@ export class DeviceModeToolbar {
539
609
  return this.standardDevices().concat(this.customDevices());
540
610
  }
541
611
 
612
+ private getDeviceModeOptions(): {
613
+ responsive: {title: string, selected: boolean, jslogContext: string},
614
+ standard: Array<{
615
+ device: EmulationModel.EmulatedDevices.EmulatedDevice,
616
+ title: string,
617
+ selected: boolean,
618
+ jslogContext: string,
619
+ }>,
620
+ custom: Array<{
621
+ device: EmulationModel.EmulatedDevices.EmulatedDevice,
622
+ title: string,
623
+ selected: boolean,
624
+ jslogContext: string,
625
+ }>,
626
+ edit: {title: string, jslogContext: string},
627
+ } {
628
+ return {
629
+ responsive: {
630
+ title: i18nString(UIStrings.responsive),
631
+ selected: this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive,
632
+ jslogContext: 'responsive'
633
+ },
634
+ standard: this.standardDevices().map(device => ({
635
+ device,
636
+ title: device.title,
637
+ selected: this.model.device() === device,
638
+ jslogContext: Platform.StringUtilities.toKebabCase(device.title)
639
+ })),
640
+ custom: this.customDevices().map(device => ({
641
+ device,
642
+ title: device.title,
643
+ selected: this.model.device() === device,
644
+ jslogContext: Platform.StringUtilities.toKebabCase(device.title)
645
+ })),
646
+ edit: {
647
+ title: i18nString(UIStrings.edit),
648
+ jslogContext: 'edit',
649
+ }
650
+ };
651
+ }
652
+
542
653
  private appendDeviceMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
654
+ const options = this.getDeviceModeOptions();
655
+
543
656
  contextMenu.headerSection().appendCheckboxItem(
544
- i18nString(UIStrings.responsive), this.switchToResponsive.bind(this),
545
- {checked: this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive, jslogContext: 'responsive'});
546
- appendGroup.call(this, this.standardDevices());
547
- appendGroup.call(this, this.customDevices());
548
- contextMenu.footerSection().appendItem(
549
- i18nString(UIStrings.edit), this.emulatedDevicesList.revealCustomSetting.bind(this.emulatedDevicesList),
550
- {jslogContext: 'edit'});
657
+ options.responsive.title, this.switchToResponsive.bind(this),
658
+ {checked: options.responsive.selected, jslogContext: options.responsive.jslogContext});
659
+
660
+ appendGroup.call(this, options.standard);
661
+ appendGroup.call(this, options.custom);
551
662
 
552
- function appendGroup(this: DeviceModeToolbar, devices: EmulationModel.EmulatedDevices.EmulatedDevice[]): void {
553
- if (!devices.length) {
663
+ contextMenu.footerSection().appendItem(
664
+ options.edit.title, this.emulatedDevicesList.revealCustomSetting.bind(this.emulatedDevicesList),
665
+ {jslogContext: options.edit.jslogContext});
666
+
667
+ function appendGroup(this: DeviceModeToolbar, group: Array<{
668
+ device: EmulationModel.EmulatedDevices.EmulatedDevice,
669
+ title: string,
670
+ selected: boolean,
671
+ jslogContext: string,
672
+ }>): void {
673
+ if (!group.length) {
554
674
  return;
555
675
  }
556
676
  const section = contextMenu.section();
557
- for (const device of devices) {
558
- section.appendCheckboxItem(device.title, this.emulateDevice.bind(this, device), {
559
- checked: this.model.device() === device,
560
- jslogContext: Platform.StringUtilities.toKebabCase(device.title),
677
+ for (const item of group) {
678
+ section.appendCheckboxItem(item.title, this.emulateDevice.bind(this, item.device), {
679
+ checked: item.selected,
680
+ jslogContext: item.jslogContext,
561
681
  });
562
682
  }
563
683
  }
@@ -617,6 +737,9 @@ export class DeviceModeToolbar {
617
737
  private modeMenuClicked(event: {
618
738
  data: Event,
619
739
  }): void {
740
+ if (this.model.isScreenOrientationLocked()) {
741
+ return;
742
+ }
620
743
  const device = this.model.device();
621
744
  const model = this.model;
622
745
  const autoAdjustScaleSetting = this.autoAdjustScaleSetting;
@@ -737,7 +860,7 @@ export class DeviceModeToolbar {
737
860
 
738
861
  const deviceScale = this.model.appliedDeviceScaleFactor();
739
862
  if (deviceScale !== this.cachedDeviceScale) {
740
- this.deviceScaleItem.setText(`DPR: ${deviceScale.toFixed(1)}`);
863
+ this.deviceScaleItem.setText(`${deviceScale.toFixed(1)}`);
741
864
  this.cachedDeviceScale = deviceScale;
742
865
  }
743
866
 
@@ -755,7 +878,7 @@ export class DeviceModeToolbar {
755
878
  if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device && device) {
756
879
  deviceItemTitle = device.title;
757
880
  }
758
- this.deviceSelectItem.setText(`${i18nString(UIStrings.dimensions)}: ${deviceItemTitle}`);
881
+ this.deviceSelectItem.setText(deviceItemTitle);
759
882
 
760
883
  if (this.model.device() !== this.cachedModelDevice) {
761
884
  const device = this.model.device();
@@ -804,6 +927,23 @@ export class DeviceModeToolbar {
804
927
  }
805
928
  this.persistenceSetting.set(value);
806
929
  }
930
+
931
+ // When screen orientation is locked by the page (via screen.orientation.lock()),
932
+ // disable the rotate button to prevent user-initiated rotation.
933
+ // When unlocked, restore the button to its normal state.
934
+ if (this.model.isScreenOrientationLocked()) {
935
+ this.modeButton.setEnabled(false);
936
+ setTitleForButton(this.modeButton, i18nString(UIStrings.screenOrientationLocked));
937
+ } else if (this.cachedModelDevice) {
938
+ const modeCount = this.cachedModelDevice.modes.length;
939
+ this.modeButton.setEnabled(modeCount >= 2);
940
+ setTitleForButton(
941
+ this.modeButton,
942
+ modeCount === 2 ? i18nString(UIStrings.rotate) : i18nString(UIStrings.screenOrientationOptions));
943
+ } else if (this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive) {
944
+ this.modeButton.setEnabled(true);
945
+ setTitleForButton(this.modeButton, i18nString(UIStrings.rotate));
946
+ }
807
947
  }
808
948
 
809
949
  restore(): void {
@@ -66,7 +66,7 @@ export class HiddenIssuesRow extends UI.TreeOutline.TreeElement {
66
66
  #view: View;
67
67
 
68
68
  constructor(view: View = DEFAULT_VIEW) {
69
- super(undefined, true);
69
+ super(undefined, true, 'hidden-issues');
70
70
  this.#view = view;
71
71
  this.toggleOnClick = true;
72
72
  this.listItemElement.classList.add('issue-category', 'hidden-issues');
@@ -5,6 +5,7 @@
5
5
 
6
6
  import * as Common from '../../core/common/common.js';
7
7
  import * as i18n from '../../core/i18n/i18n.js';
8
+ import * as Platform from '../../core/platform/platform.js';
8
9
  import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
9
10
  import * as Adorners from '../../ui/components/adorners/adorners.js';
10
11
  import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
@@ -64,7 +65,7 @@ export class IssueKindView extends UI.TreeOutline.TreeElement {
64
65
  #issueCount: HTMLElement;
65
66
 
66
67
  constructor(kind: IssuesManager.Issue.IssueKind) {
67
- super(undefined, true);
68
+ super(undefined, true, Platform.StringUtilities.toKebabCase(kind));
68
69
  this.#kind = kind;
69
70
  this.#issueCount = document.createElement('span');
70
71
 
@@ -6,7 +6,7 @@
6
6
  import * as Common from '../../core/common/common.js';
7
7
  import * as Host from '../../core/host/host.js';
8
8
  import * as i18n from '../../core/i18n/i18n.js';
9
- import type * as Platform from '../../core/platform/platform.js';
9
+ import * as Platform from '../../core/platform/platform.js';
10
10
  import * as Protocol from '../../generated/protocol.js';
11
11
  import type {AggregatedIssue} from '../../models/issues_manager/IssueAggregator.js';
12
12
  import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
@@ -24,7 +24,6 @@ import {AffectedDescendantsWithinSelectElementView} from './AffectedDescendantsW
24
24
  import {AffectedDirectivesView} from './AffectedDirectivesView.js';
25
25
  import {AffectedDocumentsInQuirksModeView} from './AffectedDocumentsInQuirksModeView.js';
26
26
  import {AffectedElementsView} from './AffectedElementsView.js';
27
- import {AffectedElementsWithLowContrastView} from './AffectedElementsWithLowContrastView.js';
28
27
  import {AffectedHeavyAdView} from './AffectedHeavyAdView.js';
29
28
  import {AffectedMetadataAllowedSitesView} from './AffectedMetadataAllowedSitesView.js';
30
29
  import {AffectedPartitioningBlobURLView} from './AffectedPartitioningBlobURLView.js';
@@ -230,7 +229,7 @@ export class IssueView extends UI.TreeOutline.TreeElement {
230
229
  #contentCreated = false;
231
230
 
232
231
  constructor(issue: AggregatedIssue, description: IssuesManager.MarkdownIssueDescription.IssueDescription) {
233
- super();
232
+ super(undefined, undefined, Platform.StringUtilities.toKebabCase(issue.getCategory()));
234
233
  this.#issue = issue;
235
234
  this.#description = description;
236
235
  this.#throttle = new Common.Throttler.Throttler(250);
@@ -251,7 +250,6 @@ export class IssueView extends UI.TreeOutline.TreeElement {
251
250
  new AffectedDirectivesView(this, this.#issue, 'directives-details'),
252
251
  new AffectedBlockedByResponseView(this, this.#issue, 'blocked-by-response-details'),
253
252
  new AffectedSharedArrayBufferIssueDetailsView(this, this.#issue, 'sab-details'),
254
- new AffectedElementsWithLowContrastView(this, this.#issue, 'low-contrast-details'),
255
253
  new CorsIssueDetailsView(this, this.#issue, 'cors-details'),
256
254
  new GenericIssueDetailsView(this, this.#issue, 'generic-details'),
257
255
  new AffectedDocumentsInQuirksModeView(this, this.#issue, 'affected-documents'),
@@ -7,7 +7,7 @@ import '../../ui/legacy/legacy.js';
7
7
 
8
8
  import * as Common from '../../core/common/common.js';
9
9
  import * as i18n from '../../core/i18n/i18n.js';
10
- import type * as Platform from '../../core/platform/platform.js';
10
+ import * as Platform from '../../core/platform/platform.js';
11
11
  import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
12
12
  import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
13
13
  import * as UI from '../../ui/legacy/legacy.js';
@@ -121,7 +121,7 @@ class IssueCategoryView extends UI.TreeOutline.TreeElement {
121
121
  #category: IssuesManager.Issue.IssueCategory;
122
122
 
123
123
  constructor(category: IssuesManager.Issue.IssueCategory) {
124
- super();
124
+ super(undefined, undefined, Platform.StringUtilities.toKebabCase(category));
125
125
  this.#category = category;
126
126
 
127
127
  this.toggleOnClick = true;
@@ -9,10 +9,10 @@ import type * as Platform from '../../core/platform/platform.js';
9
9
  import * as SDK from '../../core/sdk/sdk.js';
10
10
  import * as Protocol from '../../generated/protocol.js';
11
11
  import * as EmulationModel from '../../models/emulation/emulation.js';
12
+ import type * as LighthouseModel from '../../models/lighthouse/lighthouse.js';
12
13
  import * as Emulation from '../emulation/emulation.js';
13
14
 
14
15
  import type {LighthouseRun as LighthouseRunType, ProtocolService} from './LighthouseProtocolService.js';
15
- import type {RunnerResult} from './LighthouseReporterTypes.js';
16
16
 
17
17
  const UIStrings = {
18
18
  /**
@@ -236,7 +236,7 @@ class LighthouseRun {
236
236
  }
237
237
  }
238
238
 
239
- async collect(): Promise<RunnerResult> {
239
+ async collect(): Promise<LighthouseModel.ReporterTypes.RunnerResult> {
240
240
  try {
241
241
  const lighthouseResponse = await this.protocolService.collectLighthouseResults(
242
242
  {inspectedURL: this.inspectedURL, categoryIDs: this.categoryIDs, flags: this.flags});
@@ -663,7 +663,7 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
663
663
  return await this.lastAction;
664
664
  }
665
665
 
666
- async collectLighthouseResults(): Promise<RunnerResult> {
666
+ async collectLighthouseResults(): Promise<LighthouseModel.ReporterTypes.RunnerResult> {
667
667
  if (!this.currentLighthouseRun) {
668
668
  throw new Error('Lighthouse is not started');
669
669
  }
@@ -7,6 +7,7 @@ import '../../ui/legacy/legacy.js';
7
7
 
8
8
  import * as Common from '../../core/common/common.js';
9
9
  import * as i18n from '../../core/i18n/i18n.js';
10
+ import type * as LighthouseModel from '../../models/lighthouse/lighthouse.js';
10
11
  import * as UI from '../../ui/legacy/legacy.js';
11
12
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
12
13
 
@@ -19,7 +20,6 @@ import {
19
20
  } from './LighthouseController.js';
20
21
  import lighthousePanelStyles from './lighthousePanel.css.js';
21
22
  import {ProtocolService} from './LighthouseProtocolService.js';
22
- import type {ReportJSON, RunnerResultArtifacts} from './LighthouseReporterTypes.js';
23
23
  import {LighthouseReportRenderer} from './LighthouseReportRenderer.js';
24
24
  import {Item, ReportSelector} from './LighthouseReportSelector.js';
25
25
  import {StartView} from './LighthouseStartView.js';
@@ -69,7 +69,7 @@ export class LighthousePanel extends UI.Panel.Panel {
69
69
  private readonly timespanView: TimespanView;
70
70
  private warningText: Nullable<string>;
71
71
  private unauditableExplanation: Nullable<string>;
72
- private readonly cachedRenderedReports: Map<ReportJSON, HTMLElement>;
72
+ private readonly cachedRenderedReports: Map<LighthouseModel.ReporterTypes.ReportJSON, HTMLElement>;
73
73
  private readonly dropTarget: UI.DropTarget.DropTarget;
74
74
  private readonly auditResultsElement: HTMLElement;
75
75
  private clearButton!: UI.Toolbar.ToolbarButton;
@@ -296,7 +296,9 @@ export class LighthousePanel extends UI.Panel.Panel {
296
296
  this.statusView.toggleCancelButton(true);
297
297
  }
298
298
 
299
- private renderReport(lighthouseResult: ReportJSON, artifacts?: RunnerResultArtifacts): void {
299
+ private renderReport(
300
+ lighthouseResult: LighthouseModel.ReporterTypes.ReportJSON,
301
+ artifacts?: LighthouseModel.ReporterTypes.RunnerResultArtifacts): void {
300
302
  this.toggleSettingsDisplay(false);
301
303
  this.contentElement.classList.toggle('in-progress', false);
302
304
  this.startView.hideWidget();
@@ -319,7 +321,9 @@ export class LighthousePanel extends UI.Panel.Panel {
319
321
  this.cachedRenderedReports.set(lighthouseResult, reportContainer);
320
322
  }
321
323
 
322
- private buildReportUI(lighthouseResult: ReportJSON, artifacts?: RunnerResultArtifacts): void {
324
+ private buildReportUI(
325
+ lighthouseResult: LighthouseModel.ReporterTypes.ReportJSON,
326
+ artifacts?: LighthouseModel.ReporterTypes.RunnerResultArtifacts): void {
323
327
  if (lighthouseResult === null) {
324
328
  return;
325
329
  }
@@ -355,7 +359,7 @@ export class LighthousePanel extends UI.Panel.Panel {
355
359
  if (!data['lighthouseVersion']) {
356
360
  return;
357
361
  }
358
- this.buildReportUI(data as ReportJSON);
362
+ this.buildReportUI(data as LighthouseModel.ReporterTypes.ReportJSON);
359
363
  }
360
364
 
361
365
  override elementsToRestoreScrollPositionsFor(): Element[] {
@@ -7,8 +7,7 @@ import type * as Platform from '../../core/platform/platform.js';
7
7
  import type * as ProtocolClient from '../../core/protocol_client/protocol_client.js';
8
8
  import * as SDK from '../../core/sdk/sdk.js';
9
9
  import type * as Protocol from '../../generated/protocol.js';
10
-
11
- import type * as ReportRenderer from './LighthouseReporterTypes.js';
10
+ import type * as LighthouseModel from '../../models/lighthouse/lighthouse.js';
12
11
 
13
12
  /**
14
13
  * @file
@@ -149,7 +148,8 @@ export class ProtocolService implements ProtocolClient.CDPConnection.CDPConnecti
149
148
  });
150
149
  }
151
150
 
152
- async collectLighthouseResults(currentLighthouseRun: LighthouseRun): Promise<ReportRenderer.RunnerResult> {
151
+ async collectLighthouseResults(currentLighthouseRun: LighthouseRun):
152
+ Promise<LighthouseModel.ReporterTypes.RunnerResult> {
153
153
  const {inspectedURL, categoryIDs, flags} = currentLighthouseRun;
154
154
 
155
155
  if (!this.mainSessionId || !this.rootTargetId) {
@@ -289,10 +289,10 @@ export class ProtocolService implements ProtocolClient.CDPConnection.CDPConnecti
289
289
 
290
290
  /** sendWithResponse currently only handles the original startLighthouse request and LHR-filled response. */
291
291
  private async sendWithResponse(action: string, args: Record<string, string|string[]|object|undefined> = {}):
292
- Promise<ReportRenderer.RunnerResult> {
292
+ Promise<LighthouseModel.ReporterTypes.RunnerResult> {
293
293
  const worker = await this.ensureWorkerExists();
294
294
  const messageId = lastId++;
295
- const messageResult = new Promise<ReportRenderer.RunnerResult>(resolve => {
295
+ const messageResult = new Promise<LighthouseModel.ReporterTypes.RunnerResult>(resolve => {
296
296
  const workerListener = (event: MessageEvent): void => {
297
297
  const lighthouseMessage = event.data;
298
298
 
@@ -6,6 +6,7 @@ import * as Common from '../../core/common/common.js';
6
6
  import * as Host from '../../core/host/host.js';
7
7
  import * as Platform from '../../core/platform/platform.js';
8
8
  import * as SDK from '../../core/sdk/sdk.js';
9
+ import type * as LighthouseModel from '../../models/lighthouse/lighthouse.js';
9
10
  import * as TextUtils from '../../models/text_utils/text_utils.js';
10
11
  import * as Workspace from '../../models/workspace/workspace.js';
11
12
  import * as LighthouseReport from '../../third_party/lighthouse/report/report.js';
@@ -15,9 +16,6 @@ import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
15
16
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
16
17
  import * as PanelsCommon from '../common/common.js';
17
18
 
18
- import type {
19
- NodeDetailsJSON, ReportJSON, RunnerResultArtifacts, SourceLocationDetailsJSON} from './LighthouseReporterTypes.js';
20
-
21
19
  const MaxLengthForLinks = 40;
22
20
 
23
21
  interface RenderReportOpts {
@@ -26,8 +24,9 @@ interface RenderReportOpts {
26
24
  }
27
25
 
28
26
  export class LighthouseReportRenderer {
29
- static renderLighthouseReport(lhr: ReportJSON, artifacts?: RunnerResultArtifacts, opts?: RenderReportOpts):
30
- HTMLElement {
27
+ static renderLighthouseReport(
28
+ lhr: LighthouseModel.ReporterTypes.ReportJSON, artifacts?: LighthouseModel.ReporterTypes.RunnerResultArtifacts,
29
+ opts?: RenderReportOpts): HTMLElement {
31
30
  let onViewTrace: (() => Promise<void>)|undefined = undefined;
32
31
  if (artifacts) {
33
32
  onViewTrace = async () => {
@@ -134,7 +133,7 @@ export class LighthouseReportRenderer {
134
133
 
135
134
  for (const origElement of el.getElementsByClassName('lh-node')) {
136
135
  const origHTMLElement = origElement as HTMLElement;
137
- const detailsItem = origHTMLElement.dataset as unknown as NodeDetailsJSON;
136
+ const detailsItem = origHTMLElement.dataset as unknown as LighthouseModel.ReporterTypes.NodeDetailsJSON;
138
137
  if (!detailsItem.path) {
139
138
  continue;
140
139
  }
@@ -165,7 +164,7 @@ export class LighthouseReportRenderer {
165
164
  static async linkifySourceLocationDetails(el: Element): Promise<void> {
166
165
  for (const origElement of el.getElementsByClassName('lh-source-location')) {
167
166
  const origHTMLElement = origElement as HTMLElement;
168
- const detailsItem = origHTMLElement.dataset as SourceLocationDetailsJSON;
167
+ const detailsItem = origHTMLElement.dataset as LighthouseModel.ReporterTypes.SourceLocationDetailsJSON;
169
168
  if (!detailsItem.sourceUrl || !detailsItem.sourceLine || !detailsItem.sourceColumn) {
170
169
  continue;
171
170
  }