chrome-devtools-frontend 1.0.1534717 → 1.0.1536371

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 (95) hide show
  1. package/docs/contributing/images/issues-nearestslo.png +0 -0
  2. package/docs/contributing/issues.md +32 -58
  3. package/eslint.config.mjs +1 -0
  4. package/front_end/core/common/Console.ts +1 -8
  5. package/front_end/core/common/ParsedURL.ts +10 -20
  6. package/front_end/core/common/SegmentedRange.ts +1 -2
  7. package/front_end/core/common/StringOutputStream.ts +1 -4
  8. package/front_end/core/host/InspectorFrontendHost.ts +6 -0
  9. package/front_end/core/host/UserMetrics.ts +5 -1
  10. package/front_end/core/i18n/i18nImpl.ts +0 -24
  11. package/front_end/core/protocol_client/CDPConnection.ts +53 -5
  12. package/front_end/core/protocol_client/protocol_client.ts +2 -0
  13. package/front_end/core/sdk/AnimationModel.ts +1 -2
  14. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -2
  15. package/front_end/core/sdk/CSSModel.ts +1 -1
  16. package/front_end/core/sdk/CSSProperty.ts +3 -6
  17. package/front_end/core/sdk/CSSStyleDeclaration.ts +4 -4
  18. package/front_end/core/sdk/DebuggerModel.ts +1 -2
  19. package/front_end/core/sdk/EnhancedTracesParser.ts +4 -0
  20. package/front_end/core/sdk/SourceMap.ts +2 -3
  21. package/front_end/devtools_compatibility.js +32 -24
  22. package/front_end/entrypoints/node_app/NodeConnectionsPanel.ts +2 -1
  23. package/front_end/generated/InspectorBackendCommands.js +1 -2
  24. package/front_end/generated/SupportedCSSProperties.js +57 -0
  25. package/front_end/generated/protocol.ts +0 -27
  26. package/front_end/panels/accessibility/AccessibilityNodeView.ts +18 -17
  27. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +9 -12
  28. package/front_end/panels/ai_assistance/PatchWidget.ts +39 -40
  29. package/front_end/panels/ai_assistance/components/ChatView.ts +5 -4
  30. package/front_end/panels/ai_assistance/components/ExploreWidget.ts +0 -2
  31. package/front_end/panels/application/AppManifestView.ts +7 -6
  32. package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -4
  33. package/front_end/panels/application/OpenedWindowDetailsView.ts +6 -6
  34. package/front_end/panels/application/StorageView.ts +9 -8
  35. package/front_end/panels/application/components/BackForwardCacheView.ts +333 -314
  36. package/front_end/panels/application/components/ProtocolHandlersView.ts +3 -2
  37. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +2 -1
  38. package/front_end/panels/autofill/AutofillView.ts +2 -3
  39. package/front_end/panels/browser_debugger/ObjectEventListenersSidebarPane.ts +8 -8
  40. package/front_end/panels/changes/CombinedDiffView.ts +13 -14
  41. package/front_end/panels/common/BadgeNotification.ts +2 -1
  42. package/front_end/panels/common/GdpSignUpDialog.ts +2 -1
  43. package/front_end/panels/console/ConsoleInsightTeaser.ts +13 -2
  44. package/front_end/panels/console/ConsolePinPane.ts +12 -7
  45. package/front_end/panels/console/ConsoleView.ts +1 -0
  46. package/front_end/panels/console/consoleView.css +0 -1
  47. package/front_end/panels/developer_resources/DeveloperResourcesView.ts +9 -9
  48. package/front_end/panels/elements/ComputedStyleWidget.ts +7 -7
  49. package/front_end/panels/elements/ElementsTreeOutline.ts +1 -1
  50. package/front_end/panels/elements/EventListenersWidget.ts +9 -9
  51. package/front_end/panels/elements/NodeStackTraceWidget.ts +6 -6
  52. package/front_end/panels/elements/PlatformFontsWidget.ts +5 -5
  53. package/front_end/panels/elements/PropertiesWidget.ts +8 -8
  54. package/front_end/panels/layer_viewer/Layers3DView.ts +2 -1
  55. package/front_end/panels/layer_viewer/PaintProfilerView.ts +3 -3
  56. package/front_end/panels/network/RequestCookiesView.ts +2 -1
  57. package/front_end/panels/network/RequestTimingView.ts +2 -1
  58. package/front_end/panels/network/components/DirectSocketConnectionView.ts +4 -6
  59. package/front_end/panels/recorder/RecorderController.ts +34 -23
  60. package/front_end/panels/recorder/components/CreateRecordingView.ts +249 -240
  61. package/front_end/panels/security/CookieControlsView.ts +74 -67
  62. package/front_end/panels/security/CookieReportView.ts +18 -16
  63. package/front_end/panels/security/IPProtectionView.ts +1 -2
  64. package/front_end/panels/security/SecurityPanel.ts +19 -19
  65. package/front_end/panels/settings/AISettingsTab.ts +2 -1
  66. package/front_end/panels/settings/KeybindsSettingsTab.ts +6 -0
  67. package/front_end/panels/settings/components/SyncSection.ts +2 -1
  68. package/front_end/panels/sources/DebuggerPausedMessage.ts +4 -3
  69. package/front_end/panels/sources/ResourceOriginPlugin.ts +3 -2
  70. package/front_end/panels/sources/SourcesNavigator.ts +2 -1
  71. package/front_end/panels/sources/TabbedEditorContainer.ts +3 -2
  72. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +9 -9
  73. package/front_end/panels/timeline/TimelineSelectorStatsView.ts +36 -36
  74. package/front_end/panels/timeline/TimelineUIUtils.ts +3 -2
  75. package/front_end/panels/timeline/components/DetailsView.ts +5 -4
  76. package/front_end/panels/timeline/components/FieldSettingsDialog.ts +2 -1
  77. package/front_end/panels/timeline/components/LiveMetricsView.ts +5 -4
  78. package/front_end/panels/timeline/components/MetricCompareStrings.ts +25 -24
  79. package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +1 -2
  80. package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +2 -1
  81. package/front_end/third_party/chromium/README.chromium +1 -1
  82. package/front_end/ui/components/docs/tooltip/basic.ts +1 -1
  83. package/front_end/ui/components/tooltips/Tooltip.ts +32 -17
  84. package/front_end/ui/i18n/i18n.ts +31 -0
  85. package/front_end/ui/legacy/SoftDropDown.ts +1 -12
  86. package/front_end/ui/legacy/ViewManager.ts +2 -4
  87. package/front_end/ui/legacy/Widget.ts +33 -17
  88. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +2 -1
  89. package/front_end/ui/legacy/legacy.ts +0 -2
  90. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  91. package/mcp/mcp.ts +1 -0
  92. package/package.json +1 -1
  93. package/front_end/ui/components/docs/recorder_create_recording_view/basic.html +0 -20
  94. package/front_end/ui/components/docs/recorder_create_recording_view/basic.ts +0 -27
  95. package/front_end/ui/legacy/ThrottledWidget.ts +0 -48
@@ -2,8 +2,6 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
6
-
7
5
  import '../../ui/components/linkifier/linkifier.js';
8
6
  import '../../ui/legacy/components/data_grid/data_grid.js';
9
7
 
@@ -125,24 +123,9 @@ interface ViewInput {
125
123
  }
126
124
  type View = (input: ViewInput, output: object, target: HTMLElement) => void;
127
125
 
128
- export class TimelineSelectorStatsView extends UI.Widget.VBox {
129
- #selectorLocations: Map<string, Protocol.CSS.SourceRange[]>;
130
- #parsedTrace: Trace.TraceModel.ParsedTrace|null = null;
131
- /**
132
- * We store the last event (or array of events) that we renderered. We do
133
- * this because as the user zooms around the panel this view is updated,
134
- * however if the set of events that are populating the view is the same as it
135
- * was the last time, we can bail without doing any re-rendering work.
136
- * If the user views a single event, this will be set to that single event, but if they are viewing a range of events, this will be set to an array.
137
- * If it's null, that means we have not rendered yet.
138
- */
139
- #lastStatsSourceEventOrEvents: Trace.Types.Events.RecalcStyle|Trace.Types.Events.RecalcStyle[]|null = null;
140
- #view: View;
141
- #timings: SelectorTiming[] = [];
142
-
143
- constructor(parsedTrace: Trace.TraceModel.ParsedTrace|null, view: View = (input, _, target) => {
144
- render(
145
- html`
126
+ const DEFAULT_VIEW: View = (input, _output, target) => {
127
+ render(
128
+ html`
146
129
  <devtools-data-grid striped name=${i18nString(UIStrings.selectorStats)}
147
130
  @contextmenu=${input.onContextMenu.bind(input)}>
148
131
  <table>
@@ -153,7 +136,7 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
153
136
  </th>
154
137
  <th id=${SelectorTimingsKey.InvalidationCount} weight="1.5" sortable hideable>
155
138
  <span title=${i18nString(UIStrings.invalidationCountExplanation)}>${
156
- i18nString(UIStrings.invalidationCount)}</span>
139
+ i18nString(UIStrings.invalidationCount)}</span>
157
140
  </th>
158
141
  <th id=${SelectorTimingsKey.MatchAttempts} weight="1" sortable hideable align="right">
159
142
  <span title=${i18nString(UIStrings.matchAttemptsExplanation)}>
@@ -165,7 +148,7 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
165
148
  </th>
166
149
  <th id=${SelectorTimingsKey.RejectPercentage} weight="1" sortable hideable align="right">
167
150
  <span title=${i18nString(UIStrings.slowPathNonMatchesExplanation)}>${
168
- i18nString(UIStrings.slowPathNonMatches)}</span>
151
+ i18nString(UIStrings.slowPathNonMatches)}</span>
169
152
  </th>
170
153
  <th id=${SelectorTimingsKey.Selector} weight="3" sortable hideable>
171
154
  <span title=${i18nString(UIStrings.selectorExplanation)}>
@@ -177,15 +160,15 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
177
160
  </th>
178
161
  </tr>
179
162
  ${input.timings.map(timing => {
180
- const nonMatches = timing[SelectorTimingsKey.MatchAttempts] - timing[SelectorTimingsKey.MatchCount];
181
- const slowPathNonMatches =
182
- (nonMatches ? 1.0 - timing[SelectorTimingsKey.FastRejectCount] / nonMatches : 0) * 100;
183
- const styleSheetId = timing[SelectorTimingsKey.StyleSheetId];
184
- const locations = timing.locations;
185
- const locationMessage = locations ? null :
186
- locations === null ? '' :
187
- i18nString(UIStrings.unableToLinkViaStyleSheetId, {PH1: styleSheetId});
188
- return html`<tr>
163
+ const nonMatches = timing[SelectorTimingsKey.MatchAttempts] - timing[SelectorTimingsKey.MatchCount];
164
+ const slowPathNonMatches =
165
+ (nonMatches ? 1.0 - timing[SelectorTimingsKey.FastRejectCount] / nonMatches : 0) * 100;
166
+ const styleSheetId = timing[SelectorTimingsKey.StyleSheetId];
167
+ const locations = timing.locations;
168
+ const locationMessage = locations ? null :
169
+ locations === null ? '' :
170
+ i18nString(UIStrings.unableToLinkViaStyleSheetId, {PH1: styleSheetId});
171
+ return html`<tr>
189
172
  <td data-value=${timing[SelectorTimingsKey.Elapsed]}>
190
173
  ${(timing[SelectorTimingsKey.Elapsed] / 1000.0).toFixed(3)}
191
174
  </td>
@@ -201,17 +184,34 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
201
184
  ${timing[SelectorTimingsKey.Selector]}
202
185
  </td>
203
186
  <td data-value=${styleSheetId}>${
204
- locations ? html`${locations.map((location, itemIndex) => html`
187
+ locations ? html`${locations.map((location, itemIndex) => html`
205
188
  <devtools-linkifier .data=${location}></devtools-linkifier
206
189
  >${itemIndex !== locations.length - 1 ? ',' : ''}`)}` :
207
- locationMessage}
190
+ locationMessage}
208
191
  </td>
209
192
  </tr>`;
210
- })}
193
+ })}
211
194
  </table>
212
195
  </devtools-data-grid>`,
213
- target, {host: this});
214
- }) {
196
+ target);
197
+ };
198
+
199
+ export class TimelineSelectorStatsView extends UI.Widget.VBox {
200
+ #selectorLocations: Map<string, Protocol.CSS.SourceRange[]>;
201
+ #parsedTrace: Trace.TraceModel.ParsedTrace|null = null;
202
+ /**
203
+ * We store the last event (or array of events) that we renderered. We do
204
+ * this because as the user zooms around the panel this view is updated,
205
+ * however if the set of events that are populating the view is the same as it
206
+ * was the last time, we can bail without doing any re-rendering work.
207
+ * If the user views a single event, this will be set to that single event, but if they are viewing a range of events, this will be set to an array.
208
+ * If it's null, that means we have not rendered yet.
209
+ */
210
+ #lastStatsSourceEventOrEvents: Trace.Types.Events.RecalcStyle|Trace.Types.Events.RecalcStyle[]|null = null;
211
+ #view: View;
212
+ #timings: SelectorTiming[] = [];
213
+
214
+ constructor(parsedTrace: Trace.TraceModel.ParsedTrace|null, view: View = DEFAULT_VIEW) {
215
215
  super({jslog: `${VisualLogging.pane('selector-stats').track({resize: true})}`});
216
216
  this.registerRequiredCSS(timelineSelectorStatsViewStyles);
217
217
 
@@ -48,6 +48,7 @@ import * as Tracing from '../../services/tracing/tracing.js';
48
48
  import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
49
49
  // eslint-disable-next-line @devtools/es-modules-import
50
50
  import codeHighlighterStyles from '../../ui/components/code_highlighter/codeHighlighter.css.js';
51
+ import * as uiI18n from '../../ui/i18n/i18n.js';
51
52
  import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
52
53
  // eslint-disable-next-line @devtools/es-modules-import
53
54
  import imagePreviewStyles from '../../ui/legacy/components/utils/imagePreview.css.js';
@@ -1899,7 +1900,7 @@ export class TimelineUIUtils {
1899
1900
  const niceNodeLink = createLinkForInvalidationNode(invalidation);
1900
1901
 
1901
1902
  const text = scriptLink ?
1902
- i18n.i18n.getFormatLocalizedString(
1903
+ uiI18n.getFormatLocalizedString(
1903
1904
  str_, UIStrings.invalidationWithCallFrame, {PH1: niceNodeLink, PH2: scriptLink}) as HTMLElement :
1904
1905
  niceNodeLink;
1905
1906
 
@@ -2207,7 +2208,7 @@ export class TimelineUIUtils {
2207
2208
  PH1: i18n.TimeUtilities.millisToString(durationMilli, true),
2208
2209
  PH2: i18n.TimeUtilities.millisToString(offsetMilli, true),
2209
2210
  });
2210
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.emptyPlaceholder, {PH1: durationText});
2211
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.emptyPlaceholder, {PH1: durationText});
2211
2212
  }
2212
2213
 
2213
2214
  static quadWidth(quad: number[]): number {
@@ -7,6 +7,7 @@ import * as i18n from '../../../core/i18n/i18n.js';
7
7
  import * as Platform from '../../../core/platform/platform.js';
8
8
  import type * as Protocol from '../../../generated/protocol.js';
9
9
  import * as Trace from '../../../models/trace/trace.js';
10
+ import * as uiI18n from '../../../ui/i18n/i18n.js';
10
11
  import * as UI from '../../../ui/legacy/legacy.js';
11
12
 
12
13
  // *********************************************************************
@@ -86,8 +87,8 @@ export function buildWarningElementsForEvent(
86
87
  const forcedReflowLink = UI.XLink.XLink.create(
87
88
  'https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing#avoid-forced-synchronous-layouts',
88
89
  i18nString(UIStrings.forcedReflow), undefined, undefined, 'forced-reflow');
89
- span.appendChild(i18n.i18n.getFormatLocalizedString(
90
- str_, UIStrings.sIsALikelyPerformanceBottleneck, {PH1: forcedReflowLink}));
90
+ span.appendChild(
91
+ uiI18n.getFormatLocalizedString(str_, UIStrings.sIsALikelyPerformanceBottleneck, {PH1: forcedReflowLink}));
91
92
  break;
92
93
  }
93
94
  case 'IDLE_CALLBACK_OVER_TIME': {
@@ -103,7 +104,7 @@ export function buildWarningElementsForEvent(
103
104
  case 'LONG_TASK': {
104
105
  const longTaskLink = UI.XLink.XLink.create(
105
106
  'https://web.dev/optimize-long-tasks/', i18nString(UIStrings.longTask), undefined, undefined, 'long-tasks');
106
- span.appendChild(i18n.i18n.getFormatLocalizedString(
107
+ span.appendChild(uiI18n.getFormatLocalizedString(
107
108
  str_, UIStrings.sTookS,
108
109
  {PH1: longTaskLink, PH2: i18n.TimeUtilities.millisToString((duration || 0), true)}));
109
110
  break;
@@ -111,7 +112,7 @@ export function buildWarningElementsForEvent(
111
112
  case 'LONG_INTERACTION': {
112
113
  const longInteractionINPLink = UI.XLink.XLink.create(
113
114
  'https://web.dev/inp', i18nString(UIStrings.longInteractionINP), undefined, undefined, 'long-interaction');
114
- span.appendChild(i18n.i18n.getFormatLocalizedString(
115
+ span.appendChild(uiI18n.getFormatLocalizedString(
115
116
  str_, UIStrings.sIsLikelyPoorPageResponsiveness, {PH1: longInteractionINPLink}));
116
117
  break;
117
118
  }
@@ -11,6 +11,7 @@ import * as Buttons from '../../../ui/components/buttons/buttons.js';
11
11
  import * as Dialogs from '../../../ui/components/dialogs/dialogs.js';
12
12
  import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
13
13
  import * as Input from '../../../ui/components/input/input.js';
14
+ import * as uiI18n from '../../../ui/i18n/i18n.js';
14
15
  import * as UI from '../../../ui/legacy/legacy.js';
15
16
  import * as Lit from '../../../ui/lit/lit.js';
16
17
  import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
@@ -331,7 +332,7 @@ export class FieldSettingsDialog extends HTMLElement {
331
332
  #render = (): void => {
332
333
  const linkEl =
333
334
  UI.XLink.XLink.create('https://developer.chrome.com/docs/crux', i18n.i18n.lockedString('Chrome UX Report'));
334
- const descriptionEl = i18n.i18n.getFormatLocalizedString(str_, UIStrings.fetchAggregated, {PH1: linkEl});
335
+ const descriptionEl = uiI18n.getFormatLocalizedString(str_, UIStrings.fetchAggregated, {PH1: linkEl});
335
336
 
336
337
  // clang-format off
337
338
  const output = html`
@@ -25,6 +25,7 @@ import * as LegacyWrapper from '../../../ui/components/legacy_wrapper/legacy_wra
25
25
  import type * as Menus from '../../../ui/components/menus/menus.js';
26
26
  import * as RenderCoordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
27
27
  import type * as Settings from '../../../ui/components/settings/settings.js';
28
+ import * as uiI18n from '../../../ui/i18n/i18n.js';
28
29
  import * as UI from '../../../ui/legacy/legacy.js';
29
30
  import * as Lit from '../../../ui/lit/lit.js';
30
31
  import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
@@ -629,8 +630,8 @@ export class LiveMetricsView extends LegacyWrapper.LegacyWrapper.WrappableCompon
629
630
  <div class="device-toolbar-description">${md(i18nString(UIStrings.useDeviceToolbar))}</div>
630
631
  ${fieldEnabled ? html`
631
632
  <ul class="environment-recs-list">
632
- <li>${i18n.i18n.getFormatLocalizedString(str_, UIStrings.device, {PH1: deviceRecEl})}</li>
633
- <li>${i18n.i18n.getFormatLocalizedString(str_, UIStrings.network, {PH1: networkRecEl})}</li>
633
+ <li>${uiI18n.getFormatLocalizedString(str_, UIStrings.device, {PH1: deviceRecEl})}</li>
634
+ <li>${uiI18n.getFormatLocalizedString(str_, UIStrings.network, {PH1: networkRecEl})}</li>
634
635
  </ul>
635
636
  ` : nothing}
636
637
  <div class="environment-option">
@@ -839,7 +840,7 @@ export class LiveMetricsView extends LegacyWrapper.LegacyWrapper.WrappableCompon
839
840
  dateEl.classList.add('collection-period-range');
840
841
  dateEl.textContent = range || i18nString(UIStrings.notEnoughData);
841
842
 
842
- const message = i18n.i18n.getFormatLocalizedString(str_, UIStrings.collectionPeriod, {
843
+ const message = uiI18n.getFormatLocalizedString(str_, UIStrings.collectionPeriod, {
843
844
  PH1: dateEl,
844
845
  });
845
846
 
@@ -862,7 +863,7 @@ export class LiveMetricsView extends LegacyWrapper.LegacyWrapper.WrappableCompon
862
863
 
863
864
  const linkEl =
864
865
  UI.XLink.XLink.create('https://developer.chrome.com/docs/crux', i18n.i18n.lockedString('Chrome UX Report'));
865
- const messageEl = i18n.i18n.getFormatLocalizedString(str_, UIStrings.seeHowYourLocalMetricsCompare, {PH1: linkEl});
866
+ const messageEl = uiI18n.getFormatLocalizedString(str_, UIStrings.seeHowYourLocalMetricsCompare, {PH1: linkEl});
866
867
 
867
868
  return html`
868
869
  <div class="field-data-message">${messageEl}</div>
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as i18n from '../../../core/i18n/i18n.js';
6
+ import * as uiI18n from '../../../ui/i18n/i18n.js';
6
7
 
7
8
  import type {MetricRating} from './Utils.js';
8
9
 
@@ -185,40 +186,40 @@ export function renderCompareText(
185
186
  };
186
187
 
187
188
  if (rating === 'good' && compare === 'better') {
188
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.goodBetterCompare, values);
189
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.goodBetterCompare, values);
189
190
  }
190
191
  if (rating === 'good' && compare === 'worse') {
191
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.goodWorseCompare, values);
192
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.goodWorseCompare, values);
192
193
  }
193
194
  if (rating === 'good' && compare === 'similar') {
194
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.goodSimilarCompare, values);
195
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.goodSimilarCompare, values);
195
196
  }
196
197
  if (rating === 'good' && !compare) {
197
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.goodSummarized, values);
198
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.goodSummarized, values);
198
199
  }
199
200
  if (rating === 'needs-improvement' && compare === 'better') {
200
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.needsImprovementBetterCompare, values);
201
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.needsImprovementBetterCompare, values);
201
202
  }
202
203
  if (rating === 'needs-improvement' && compare === 'worse') {
203
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.needsImprovementWorseCompare, values);
204
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.needsImprovementWorseCompare, values);
204
205
  }
205
206
  if (rating === 'needs-improvement' && compare === 'similar') {
206
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.needsImprovementSimilarCompare, values);
207
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.needsImprovementSimilarCompare, values);
207
208
  }
208
209
  if (rating === 'needs-improvement' && !compare) {
209
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.needsImprovementSummarized, values);
210
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.needsImprovementSummarized, values);
210
211
  }
211
212
  if (rating === 'poor' && compare === 'better') {
212
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.poorBetterCompare, values);
213
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.poorBetterCompare, values);
213
214
  }
214
215
  if (rating === 'poor' && compare === 'worse') {
215
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.poorWorseCompare, values);
216
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.poorWorseCompare, values);
216
217
  }
217
218
  if (rating === 'poor' && compare === 'similar') {
218
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.poorSimilarCompare, values);
219
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.poorSimilarCompare, values);
219
220
  }
220
221
  if (rating === 'poor' && !compare) {
221
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.poorSummarized, values);
222
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.poorSummarized, values);
222
223
  }
223
224
 
224
225
  throw new Error('Compare string not found');
@@ -241,40 +242,40 @@ export function renderDetailedCompareText(options: {
241
242
  };
242
243
 
243
244
  if (localRating === 'good' && fieldRating === 'good') {
244
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.goodGoodDetailedCompare, values);
245
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.goodGoodDetailedCompare, values);
245
246
  }
246
247
  if (localRating === 'good' && fieldRating === 'needs-improvement') {
247
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.goodNeedsImprovementDetailedCompare, values);
248
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.goodNeedsImprovementDetailedCompare, values);
248
249
  }
249
250
  if (localRating === 'good' && fieldRating === 'poor') {
250
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.goodPoorDetailedCompare, values);
251
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.goodPoorDetailedCompare, values);
251
252
  }
252
253
  if (localRating === 'good' && !fieldRating) {
253
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.goodSummarized, values);
254
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.goodSummarized, values);
254
255
  }
255
256
  if (localRating === 'needs-improvement' && fieldRating === 'good') {
256
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.needsImprovementGoodDetailedCompare, values);
257
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.needsImprovementGoodDetailedCompare, values);
257
258
  }
258
259
  if (localRating === 'needs-improvement' && fieldRating === 'needs-improvement') {
259
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.needsImprovementNeedsImprovementDetailedCompare, values);
260
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.needsImprovementNeedsImprovementDetailedCompare, values);
260
261
  }
261
262
  if (localRating === 'needs-improvement' && fieldRating === 'poor') {
262
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.needsImprovementPoorDetailedCompare, values);
263
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.needsImprovementPoorDetailedCompare, values);
263
264
  }
264
265
  if (localRating === 'needs-improvement' && !fieldRating) {
265
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.needsImprovementSummarized, values);
266
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.needsImprovementSummarized, values);
266
267
  }
267
268
  if (localRating === 'poor' && fieldRating === 'good') {
268
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.poorGoodDetailedCompare, values);
269
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.poorGoodDetailedCompare, values);
269
270
  }
270
271
  if (localRating === 'poor' && fieldRating === 'needs-improvement') {
271
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.poorNeedsImprovementDetailedCompare, values);
272
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.poorNeedsImprovementDetailedCompare, values);
272
273
  }
273
274
  if (localRating === 'poor' && fieldRating === 'poor') {
274
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.poorPoorDetailedCompare, values);
275
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.poorPoorDetailedCompare, values);
275
276
  }
276
277
  if (localRating === 'poor' && !fieldRating) {
277
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.poorSummarized, values);
278
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.poorSummarized, values);
278
279
  }
279
280
 
280
281
  throw new Error('Detailed compare string not found');
@@ -1,7 +1,6 @@
1
1
  // Copyright 2024 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
4
 
6
5
  import * as Common from '../../../core/common/common.js';
7
6
  import * as i18n from '../../../core/i18n/i18n.js';
@@ -448,5 +447,5 @@ export const DEFAULT_VIEW: (input: SidebarAnnotationsTabViewInput, output: objec
448
447
  </setting-checkbox>`
449
448
  }
450
449
  </span>`,
451
- target, {host: target});
450
+ target);
452
451
  };
@@ -8,6 +8,7 @@ import './Checklist.js';
8
8
  import * as i18n from '../../../../core/i18n/i18n.js';
9
9
  import type {LCPDiscoveryInsightModel} from '../../../../models/trace/insights/LCPDiscovery.js';
10
10
  import * as Trace from '../../../../models/trace/trace.js';
11
+ import * as uiI18n from '../../../../ui/i18n/i18n.js';
11
12
  import * as Lit from '../../../../ui/lit/lit.js';
12
13
 
13
14
  import {BaseInsightComponent} from './BaseInsightComponent.js';
@@ -63,7 +64,7 @@ export class LCPDiscovery extends BaseInsightComponent<LCPDiscoveryInsightModel>
63
64
  const timeWrapper = document.createElement('span');
64
65
  timeWrapper.classList.add('discovery-time-ms');
65
66
  timeWrapper.innerText = i18n.TimeUtilities.formatMicroSecondsAsMillisFixed(delay);
66
- return i18n.i18n.getFormatLocalizedString(str_, UIStrings.lcpLoadDelay, {PH1: timeWrapper});
67
+ return uiI18n.getFormatLocalizedString(str_, UIStrings.lcpLoadDelay, {PH1: timeWrapper});
67
68
  }
68
69
 
69
70
  override renderContent(): Lit.LitTemplate {
@@ -1,7 +1,7 @@
1
1
  Name: Dependencies sourced from the upstream `chromium` repository
2
2
  URL: https://source.chromium.org/chromium/chromium/src/+/main:components/variations/proto/devtools/
3
3
  Version: N/A
4
- Revision: 2a28b0737c1e845353e9a2082e30ecd5043c0afe
4
+ Revision: 02e01d70b196feed2ae409aa37b1c99afb6c2ef3
5
5
  Update Mechanism: Manual (https://crbug.com/428069060)
6
6
  License: BSD-3-Clause
7
7
  License File: LICENSE
@@ -31,7 +31,7 @@ Lit.render(
31
31
  >
32
32
  Non-button click trigger
33
33
  </span>
34
- <devtools-tooltip id="rich-tooltip" variant="rich" use-click>
34
+ <devtools-tooltip id="rich-tooltip" variant="rich" trigger="click">
35
35
  <p>Rich tooltip</p>
36
36
  <button>Action</button>
37
37
  </devtools-tooltip>
@@ -167,6 +167,7 @@ const proposedRectForSimpleTooltip =
167
167
 
168
168
  export type TooltipVariant = 'simple'|'rich';
169
169
  export type PaddingMode = 'small'|'large';
170
+ export type TooltipTrigger = 'hover'|'click'|'both';
170
171
 
171
172
  export interface TooltipProperties {
172
173
  id: string;
@@ -174,6 +175,7 @@ export interface TooltipProperties {
174
175
  padding?: PaddingMode;
175
176
  anchor?: HTMLElement;
176
177
  jslogContext?: string;
178
+ trigger?: TooltipTrigger;
177
179
  }
178
180
 
179
181
  /**
@@ -182,7 +184,7 @@ export interface TooltipProperties {
182
184
  * @property hoverDelay - reflects the `"hover-delay"` attribute.
183
185
  * @property variant - reflects the `"variant"` attribute.
184
186
  * @property padding - reflects the `"padding"` attribute.
185
- * @property useClick - reflects the `"click"` attribute.
187
+ * @property trigger - reflects the `"trigger"` attribute.
186
188
  * @property verticalDistanceIncrease - reflects the `"vertical-distance-increase"` attribute.
187
189
  * @property preferSpanLeft - reflects the `"prefer-span-left"` attribute.
188
190
  * @attribute id - Id of the tooltip. Used for searching an anchor element with aria-describedby.
@@ -190,17 +192,19 @@ export interface TooltipProperties {
190
192
  * @attribute variant - Variant of the tooltip, `"simple"` for strings only, inverted background,
191
193
  * `"rich"` for interactive content, background according to theme's surface.
192
194
  * @attribute padding - Which padding to use, defaults to `"small"`. Use `"large"` for richer content.
193
- * @attribute use-click - If present, the tooltip will be shown on click instead of on hover.
195
+ * @attribute trigger - Specifies which action triggers the tooltip. `"hover"` is the default. `"click"` means the
196
+ * tooltip will be shown on click instead of hover. `"both"` means both hover and click trigger the
197
+ * tooltip.
194
198
  * @attribute vertical-distance-increase - The tooltip is moved vertically this many pixels further away from its anchor.
195
199
  * @attribute prefer-span-left - If present, the tooltip's preferred position is `"span-left"` (The right
196
200
  * side of the tooltip and its anchor are aligned. The tooltip expands to the left from
197
201
  * there.). Applies to rich tooltips only.
198
202
  * @attribute use-hotkey - If present, the tooltip will be shown on hover but not when receiving focus.
199
- * Requires a hotkey to open when fosed (Alt-down). When `"use-click"` is present
200
- * as well, use-click takes precedence.
203
+ * Requires a hotkey to open when fosed (Alt-down). When `"trigger"` is present
204
+ * as well, `"trigger"` takes precedence.
201
205
  */
202
206
  export class Tooltip extends HTMLElement {
203
- static readonly observedAttributes = ['id', 'variant', 'jslogcontext'];
207
+ static readonly observedAttributes = ['id', 'variant', 'jslogcontext', 'trigger'];
204
208
  static lastOpenedTooltipId: string|null = null;
205
209
 
206
210
  readonly #shadow = this.attachShadow({mode: 'open'});
@@ -231,16 +235,20 @@ export class Tooltip extends HTMLElement {
231
235
  }
232
236
  }
233
237
 
234
- get useClick(): boolean {
235
- return this.hasAttribute('use-click') ?? false;
236
- }
237
- set useClick(useClick: boolean) {
238
- if (useClick) {
239
- this.setAttribute('use-click', '');
240
- } else {
241
- this.removeAttribute('use-click');
238
+ get trigger(): TooltipTrigger {
239
+ switch (this.getAttribute('trigger')) {
240
+ case 'click':
241
+ return 'click';
242
+ case 'both':
243
+ return 'both';
244
+ case 'hover':
245
+ default:
246
+ return 'hover';
242
247
  }
243
248
  }
249
+ set trigger(trigger: TooltipTrigger) {
250
+ this.setAttribute('trigger', trigger);
251
+ }
244
252
 
245
253
  get hoverDelay(): number {
246
254
  return this.hasAttribute('hover-delay') ? Number(this.getAttribute('hover-delay')) : 300;
@@ -297,7 +305,7 @@ export class Tooltip extends HTMLElement {
297
305
 
298
306
  constructor(properties?: TooltipProperties) {
299
307
  super();
300
- const {id, variant, padding, jslogContext, anchor} = properties ?? {};
308
+ const {id, variant, padding, jslogContext, anchor, trigger} = properties ?? {};
301
309
  if (id) {
302
310
  this.id = id;
303
311
  }
@@ -317,6 +325,9 @@ export class Tooltip extends HTMLElement {
317
325
  }
318
326
  this.#anchor = anchor;
319
327
  }
328
+ if (trigger) {
329
+ this.trigger = trigger;
330
+ }
320
331
  }
321
332
 
322
333
  attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
@@ -463,7 +474,7 @@ export class Tooltip extends HTMLElement {
463
474
  if (!this.hasAttribute('role')) {
464
475
  this.setAttribute('role', 'tooltip');
465
476
  }
466
- this.setAttribute('popover', this.useClick ? 'auto' : 'manual');
477
+ this.setAttribute('popover', this.trigger === 'hover' ? 'manual' : 'auto');
467
478
  this.#updateJslog();
468
479
  }
469
480
 
@@ -474,6 +485,9 @@ export class Tooltip extends HTMLElement {
474
485
  #setClosing = (event: Event): void => {
475
486
  if ((event as ToggleEvent).newState === 'closed') {
476
487
  this.#closing = true;
488
+ if (this.#timeout) {
489
+ window.clearTimeout(this.#timeout);
490
+ }
477
491
  }
478
492
  };
479
493
 
@@ -521,9 +535,10 @@ export class Tooltip extends HTMLElement {
521
535
  // as we always want to support ESC to close.
522
536
  this.#anchor.addEventListener('keydown', this.#keyDown);
523
537
 
524
- if (this.useClick) {
538
+ if (this.trigger === 'click' || this.trigger === 'both') {
525
539
  this.#anchor.addEventListener('click', this.toggle);
526
- } else {
540
+ }
541
+ if (this.trigger === 'hover' || this.trigger === 'both') {
527
542
  this.#anchor.addEventListener('mouseenter', this.showTooltip);
528
543
  if (!this.useHotkey) {
529
544
  this.#anchor.addEventListener('focus', this.showTooltip);
@@ -0,0 +1,31 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ /* eslint-disable @devtools/no-imperative-dom-api */
6
+
7
+ import * as I18n from '../../core/i18n/i18n.js';
8
+ import type * as ThirdPartyI18n from '../../third_party/i18n/i18n.js';
9
+
10
+ /**
11
+ * Returns a span element that may contains other DOM element as placeholders
12
+ */
13
+ export function getFormatLocalizedString(
14
+ registeredStrings: ThirdPartyI18n.LocalizedStringSet.RegisteredFileStrings, stringId: string,
15
+ placeholders: Record<string, Object>): HTMLSpanElement {
16
+ const formatter = registeredStrings.getLocalizedStringSetFor(I18n.DevToolsLocale.DevToolsLocale.instance().locale)
17
+ .getMessageFormatterFor(stringId);
18
+
19
+ const element = document.createElement('span');
20
+ for (const icuElement of formatter.getAst()) {
21
+ if (icuElement.type === /* argumentElement */ 1) {
22
+ const placeholderValue = placeholders[icuElement.value];
23
+ if (placeholderValue) {
24
+ element.append(placeholderValue as Node | string);
25
+ }
26
+ } else if ('value' in icuElement) {
27
+ element.append(String(icuElement.value));
28
+ }
29
+ }
30
+ return element;
31
+ }
@@ -38,7 +38,6 @@ export class SoftDropDown<T> implements ListDelegate<T> {
38
38
  private list: ListControl<T>;
39
39
  private rowHeight: number;
40
40
  private width: number;
41
- private listWasShowing200msAgo: boolean;
42
41
 
43
42
  constructor(model: ListModel<T>, delegate: Delegate<T>, jslogContext?: string) {
44
43
  this.delegate = delegate;
@@ -79,9 +78,8 @@ export class SoftDropDown<T> implements ListDelegate<T> {
79
78
  'jslog',
80
79
  `${VisualLogging.menu().parent('mapped').track({resize: true, keydown: 'ArrowUp|ArrowDown|PageUp|PageDown'})}`);
81
80
 
82
- this.listWasShowing200msAgo = false;
83
81
  this.element.addEventListener('mousedown', event => {
84
- if (this.listWasShowing200msAgo) {
82
+ if (this.glassPane.isShowing()) {
85
83
  this.hide(event);
86
84
  } else if (!this.element.disabled) {
87
85
  this.show(event);
@@ -96,9 +94,6 @@ export class SoftDropDown<T> implements ListDelegate<T> {
96
94
  return;
97
95
  }
98
96
 
99
- if (!this.listWasShowing200msAgo) {
100
- return;
101
- }
102
97
  this.selectHighlightedItem();
103
98
  if (event.target instanceof Element && event.target?.parentElement) {
104
99
  // hide() will consume the mouseup event and click won't be triggered
@@ -122,9 +117,6 @@ export class SoftDropDown<T> implements ListDelegate<T> {
122
117
  this.list.selectItem(this.selectedItem);
123
118
  }
124
119
  event.consume(true);
125
- window.setTimeout(() => {
126
- this.listWasShowing200msAgo = true;
127
- }, 200);
128
120
  }
129
121
 
130
122
  private updateGlasspaneSize(): void {
@@ -134,9 +126,6 @@ export class SoftDropDown<T> implements ListDelegate<T> {
134
126
  }
135
127
 
136
128
  private hide(event: Event): void {
137
- window.setTimeout(() => {
138
- this.listWasShowing200msAgo = false;
139
- }, 200);
140
129
  this.glassPane.hide();
141
130
  this.list.selectItem(null);
142
131
  ARIAUtils.setExpanded(this.element, false);
@@ -164,14 +164,12 @@ export interface EventTypes {
164
164
  }
165
165
 
166
166
  export class ViewManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
167
- readonly views: Map<string, View>;
168
- private readonly locationNameByViewId: Map<string, string>;
167
+ readonly views = new Map<string, View>();
168
+ private readonly locationNameByViewId = new Map<string, string>();
169
169
  private readonly locationOverrideSetting: Common.Settings.Setting<Record<string, string>>;
170
170
 
171
171
  private constructor() {
172
172
  super();
173
- this.views = new Map();
174
- this.locationNameByViewId = new Map();
175
173
 
176
174
  // Read override setting for location
177
175
  this.locationOverrideSetting = Common.Settings.Settings.instance().createSetting('views-location-override', {});