chrome-devtools-frontend 1.0.969882 → 1.0.971140

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 (70) hide show
  1. package/config/gni/devtools_grd_files.gni +3 -0
  2. package/config/gni/devtools_image_files.gni +1 -0
  3. package/front_end/Images/src/ic_changes.svg +5 -0
  4. package/front_end/core/common/ParsedURL.ts +27 -4
  5. package/front_end/core/host/UserMetrics.ts +3 -1
  6. package/front_end/core/i18n/locales/en-US.json +21 -3
  7. package/front_end/core/i18n/locales/en-XL.json +21 -3
  8. package/front_end/core/platform/UserVisibleError.ts +28 -0
  9. package/front_end/core/platform/platform.ts +2 -0
  10. package/front_end/core/sdk/ChildTargetManager.ts +0 -1
  11. package/front_end/core/sdk/DebuggerModel.ts +4 -0
  12. package/front_end/core/sdk/NetworkManager.ts +15 -1
  13. package/front_end/core/sdk/NetworkRequest.ts +11 -0
  14. package/front_end/entrypoints/lighthouse_worker/LighthouseService.ts +15 -6
  15. package/front_end/entrypoints/main/MainImpl.ts +3 -0
  16. package/front_end/models/bindings/CSSWorkspaceBinding.ts +21 -0
  17. package/front_end/models/persistence/Automapping.ts +3 -33
  18. package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +11 -9
  19. package/front_end/models/persistence/IsolatedFileSystem.ts +20 -14
  20. package/front_end/models/persistence/NetworkPersistenceManager.ts +8 -4
  21. package/front_end/models/persistence/PlatformFileSystem.ts +3 -2
  22. package/front_end/models/workspace/UISourceCode.ts +11 -14
  23. package/front_end/models/workspace/WorkspaceImpl.ts +5 -1
  24. package/front_end/panels/animation/animationTimeline.css +0 -3
  25. package/front_end/panels/application/components/trustTokensViewDeleteButton.css +0 -1
  26. package/front_end/panels/browser_debugger/categorizedBreakpointsSidebarPane.css +0 -1
  27. package/front_end/panels/console/consolePinPane.css +0 -17
  28. package/front_end/panels/css_overview/cssOverviewCompletedView.css +0 -1
  29. package/front_end/panels/elements/ElementsPanel.ts +25 -11
  30. package/front_end/panels/elements/StylesSidebarPane.ts +198 -23
  31. package/front_end/panels/elements/components/adornerSettingsPane.css +0 -1
  32. package/front_end/panels/elements/components/computedStyleTrace.css +1 -1
  33. package/front_end/panels/elements/components/elementsBreadcrumbs.css +0 -1
  34. package/front_end/panels/elements/computedStyleWidgetTree.css +2 -2
  35. package/front_end/panels/elements/elementsTreeOutline.css +0 -3
  36. package/front_end/panels/emulation/deviceModeView.css +0 -1
  37. package/front_end/panels/event_listeners/eventListenersView.css +0 -1
  38. package/front_end/panels/issues/components/hideIssuesMenu.css +0 -1
  39. package/front_end/panels/lighthouse/LighthouseController.ts +30 -0
  40. package/front_end/panels/lighthouse/LighthousePanel.ts +7 -1
  41. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +6 -1
  42. package/front_end/panels/lighthouse/LighthouseStartView.ts +2 -2
  43. package/front_end/panels/lighthouse/LighthouseStartViewFR.ts +39 -0
  44. package/front_end/panels/media/playerListView.css +0 -1
  45. package/front_end/panels/network/networkLogView.css +0 -4
  46. package/front_end/panels/network/requestPayloadTree.css +0 -2
  47. package/front_end/panels/network/signedExchangeInfoTree.css +0 -1
  48. package/front_end/panels/sensors/sensors.css +0 -1
  49. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +0 -4
  50. package/front_end/panels/settings/emulation/devicesSettingsTab.css +0 -1
  51. package/front_end/panels/snippets/ScriptSnippetFileSystem.ts +4 -4
  52. package/front_end/panels/snippets/SnippetsQuickOpen.ts +1 -1
  53. package/front_end/panels/sources/NavigatorView.ts +9 -5
  54. package/front_end/panels/sources/TabbedEditorContainer.ts +9 -0
  55. package/front_end/panels/sources/sources-legacy.ts +0 -13
  56. package/front_end/panels/sources/sourcesView.css +0 -4
  57. package/front_end/panels/sources/watchExpressionsSidebarPane.css +0 -1
  58. package/front_end/panels/webauthn/webauthnPane.css +0 -12
  59. package/front_end/third_party/codemirror/package/addon/fold/foldgutter.css +1 -5
  60. package/front_end/ui/components/adorners/adorner.css +0 -4
  61. package/front_end/ui/components/buttons/button.css +0 -4
  62. package/front_end/ui/components/data_grid/dataGrid.css +0 -4
  63. package/front_end/ui/components/icon_button/iconButton.css +0 -1
  64. package/front_end/ui/legacy/TabbedPane.ts +1 -1
  65. package/front_end/ui/legacy/closeButton.css +0 -1
  66. package/front_end/ui/legacy/tabbedPane.css +0 -7
  67. package/front_end/ui/legacy/textButton.css +0 -1
  68. package/front_end/ui/legacy/toolbar.css +28 -4
  69. package/package.json +1 -1
  70. package/scripts/npm_test.js +1 -1
@@ -39,9 +39,11 @@ import * as Root from '../../core/root/root.js';
39
39
  import * as SDK from '../../core/sdk/sdk.js';
40
40
  import * as Protocol from '../../generated/protocol.js';
41
41
  import * as Bindings from '../../models/bindings/bindings.js';
42
+ import * as Formatter from '../../models/formatter/formatter.js';
42
43
  import * as TextUtils from '../../models/text_utils/text_utils.js';
43
44
  import * as Workspace from '../../models/workspace/workspace.js';
44
45
  import * as WorkspaceDiff from '../../models/workspace_diff/workspace_diff.js';
46
+ import type * as Diff from '../../third_party/diff/diff.js';
45
47
  import * as DiffView from '../../ui/components/diff_view/diff_view.js';
46
48
  import * as IconButton from '../../ui/components/icon_button/icon_button.js';
47
49
  import * as InlineEditor from '../../ui/legacy/components/inline_editor/inline_editor.js';
@@ -177,6 +179,14 @@ const UIStrings = {
177
179
  *@description Text that is announced by the screen reader when the user focuses on an input field for editing the name of a CSS selector in the Styles panel
178
180
  */
179
181
  cssSelector: '`CSS` selector',
182
+ /**
183
+ *@description Tooltip text that appears when hovering over the css changes button in the Styles Sidebar Pane of the Elements panel
184
+ */
185
+ copyAllCSSChanges: 'Copy all the CSS changes',
186
+ /**
187
+ *@description Tooltip text that appears after clicking on the copy CSS changes button
188
+ */
189
+ copiedToClipboard: 'Copied to clipboard',
180
190
  };
181
191
 
182
192
  const str_ = i18n.i18n.registerUIStrings('panels/elements/StylesSidebarPane.ts', UIStrings);
@@ -236,8 +246,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
236
246
  private readonly resizeThrottler: Common.Throttler.Throttler;
237
247
  private readonly imagePreviewPopover: ImagePreviewPopover;
238
248
  activeCSSAngle: InlineEditor.CSSAngle.CSSAngle|null;
239
- #changedLinesByURLs: Map<string, Set<number>> = new Map();
240
- #uiSourceCodeToDiffCallbacks: Map<Workspace.UISourceCode.UISourceCode, () => void> = new Map();
249
+ #urlToChangeTracker: Map<string, ChangeTracker> = new Map();
241
250
 
242
251
  static instance(): StylesSidebarPane {
243
252
  if (!_stylesSidebarPaneInstance) {
@@ -609,6 +618,9 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
609
618
  await this.innerRebuildUpdate(matchedStyles);
610
619
  if (!this.initialUpdateCompleted) {
611
620
  this.initialUpdateCompleted = true;
621
+ if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.STYLES_PANE_CSS_CHANGES)) {
622
+ this.appendToolbarItem(this.createCopyAllChangesButton());
623
+ }
612
624
  this.dispatchEventToListeners(Events.InitialUpdateCompleted);
613
625
  }
614
626
 
@@ -841,12 +853,14 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
841
853
  const blocks = [new SectionBlock(null)];
842
854
  let sectionIdx = 0;
843
855
  let lastParentNode: SDK.DOMModel.DOMNode|null = null;
844
- if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.STYLES_PANE_CSS_CHANGES)) {
845
- this.resetChangedLinesTracking();
846
- }
856
+ const refreshedURLs = new Set<string>();
847
857
  for (const style of matchedStyles.nodeStyles()) {
848
858
  if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.STYLES_PANE_CSS_CHANGES) && style.parentRule) {
849
- await this.trackChangedLines(style.parentRule.resourceURL());
859
+ const url = style.parentRule.resourceURL();
860
+ if (url && !refreshedURLs.has(url)) {
861
+ await this.trackURLForChanges(url);
862
+ refreshedURLs.add(url);
863
+ }
850
864
  }
851
865
 
852
866
  const parentNode = matchedStyles.isInherited(style) ? matchedStyles.nodeForStyle(style) : null;
@@ -1001,25 +1015,27 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1001
1015
  return sections;
1002
1016
  }
1003
1017
 
1004
- resetChangedLinesTracking(): void {
1005
- this.#changedLinesByURLs.clear();
1006
- for (const [uiSourceCode, callback] of this.#uiSourceCodeToDiffCallbacks) {
1007
- WorkspaceDiff.WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(uiSourceCode, callback);
1018
+ async trackURLForChanges(url: string): Promise<void> {
1019
+ const currentTracker = this.#urlToChangeTracker.get(url);
1020
+ if (currentTracker) {
1021
+ WorkspaceDiff.WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(
1022
+ currentTracker.uiSourceCode, currentTracker.diffChangeCallback);
1008
1023
  }
1009
- this.#uiSourceCodeToDiffCallbacks.clear();
1010
- }
1011
1024
 
1012
- async trackChangedLines(url: string): Promise<void> {
1013
- if (!url || this.#changedLinesByURLs.has(url)) {
1014
- return;
1015
- }
1025
+ // We get a refreshed uiSourceCode each time because the underlying instance may be recreated.
1016
1026
  const uiSourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(url);
1017
- if (uiSourceCode) {
1018
- await this.refreshChangedLines(uiSourceCode);
1019
- const callback = this.refreshChangedLines.bind(this, uiSourceCode);
1020
- WorkspaceDiff.WorkspaceDiff.workspaceDiff().subscribeToDiffChange(uiSourceCode, callback);
1021
- this.#uiSourceCodeToDiffCallbacks.set(uiSourceCode, callback);
1027
+ if (!uiSourceCode) {
1028
+ return;
1022
1029
  }
1030
+ const diffChangeCallback = this.refreshChangedLines.bind(this, uiSourceCode);
1031
+ WorkspaceDiff.WorkspaceDiff.workspaceDiff().subscribeToDiffChange(uiSourceCode, diffChangeCallback);
1032
+ const newTracker = {
1033
+ uiSourceCode,
1034
+ changedLines: new Set<number>(),
1035
+ diffChangeCallback,
1036
+ };
1037
+ this.#urlToChangeTracker.set(url, newTracker);
1038
+ await this.refreshChangedLines(newTracker.uiSourceCode);
1023
1039
  }
1024
1040
 
1025
1041
  isPropertyChanged(property: SDK.CSSProperty.CSSProperty): boolean {
@@ -1027,7 +1043,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1027
1043
  if (!url) {
1028
1044
  return false;
1029
1045
  }
1030
- const changedLines = this.#changedLinesByURLs.get(url);
1046
+ const changedLines = this.#urlToChangeTracker.get(url)?.changedLines;
1031
1047
  if (!changedLines) {
1032
1048
  return false;
1033
1049
  }
@@ -1040,6 +1056,10 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1040
1056
  }
1041
1057
 
1042
1058
  private async refreshChangedLines(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
1059
+ const changeTracker = this.#urlToChangeTracker.get(uiSourceCode.url());
1060
+ if (!changeTracker) {
1061
+ return;
1062
+ }
1043
1063
  const diff = await WorkspaceDiff.WorkspaceDiff.workspaceDiff().requestDiff(uiSourceCode, {shouldFormatDiff: true});
1044
1064
  const changedLines = new Set<number>();
1045
1065
  if (diff && diff.length > 0) {
@@ -1050,7 +1070,24 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1050
1070
  }
1051
1071
  }
1052
1072
  }
1053
- this.#changedLinesByURLs.set(uiSourceCode.url(), changedLines);
1073
+ changeTracker.changedLines = changedLines;
1074
+ }
1075
+
1076
+ private async getFormattedChanges(): Promise<string> {
1077
+ let allChanges = '';
1078
+ for (const [url, {uiSourceCode}] of this.#urlToChangeTracker) {
1079
+ const diff =
1080
+ await WorkspaceDiff.WorkspaceDiff.workspaceDiff().requestDiff(uiSourceCode, {shouldFormatDiff: true});
1081
+ if (!diff || diff.length < 2) {
1082
+ continue;
1083
+ }
1084
+ const changes = await formatCSSChangesFromDiff(diff);
1085
+ if (changes.length > 0) {
1086
+ allChanges += `/* ${escapeUrlAsCssComment(url)} */\n\n${changes}\n\n`;
1087
+ }
1088
+ }
1089
+
1090
+ return allChanges;
1054
1091
  }
1055
1092
 
1056
1093
  private clipboardCopy(_event: Event): void {
@@ -1146,6 +1183,34 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1146
1183
  }
1147
1184
  }
1148
1185
  }
1186
+
1187
+ private createCopyAllChangesButton(): UI.Toolbar.ToolbarButton {
1188
+ const copyAllIcon = new IconButton.Icon.Icon();
1189
+ copyAllIcon.data = {
1190
+ iconName: 'ic_changes',
1191
+ color: 'var(--color-text-secondary)',
1192
+ width: '18px',
1193
+ height: '18px',
1194
+ };
1195
+ const copyAllChangesButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.copyAllCSSChanges), copyAllIcon);
1196
+ // TODO(1296947): implement a dedicated component to share between all copy buttons
1197
+ copyAllChangesButton.element.setAttribute('data-content', i18nString(UIStrings.copiedToClipboard));
1198
+ let timeout: number|undefined;
1199
+ copyAllChangesButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, async () => {
1200
+ const allChanges = await this.getFormattedChanges();
1201
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(allChanges);
1202
+ if (timeout) {
1203
+ clearTimeout(timeout);
1204
+ timeout = undefined;
1205
+ }
1206
+ copyAllChangesButton.element.classList.add('copied-to-clipboard');
1207
+ timeout = window.setTimeout(() => {
1208
+ copyAllChangesButton.element.classList.remove('copied-to-clipboard');
1209
+ timeout = undefined;
1210
+ }, 2000);
1211
+ });
1212
+ return copyAllChangesButton;
1213
+ }
1149
1214
  }
1150
1215
 
1151
1216
  export const enum Events {
@@ -1162,6 +1227,108 @@ export type EventTypes = {
1162
1227
  [Events.StylesUpdateCompleted]: StylesUpdateCompletedEvent,
1163
1228
  };
1164
1229
 
1230
+ type ChangeTracker = {
1231
+ uiSourceCode: Workspace.UISourceCode.UISourceCode,
1232
+ changedLines: Set<number>,
1233
+ diffChangeCallback: () => Promise<void>,
1234
+ };
1235
+
1236
+ export async function formatCSSChangesFromDiff(diff: Diff.Diff.DiffArray): Promise<string> {
1237
+ const {originalLines, currentLines, rows} = DiffView.DiffView.buildDiffRows(diff);
1238
+
1239
+ const {propertyToSelector: originalPropertyToSelector, ruleToSelector: originalRuleToSelector} =
1240
+ await buildPropertyRuleMaps(originalLines.join('\n'));
1241
+ const {propertyToSelector: currentPropertyToSelector, ruleToSelector: currentRuleToSelector} =
1242
+ await buildPropertyRuleMaps(currentLines.join('\n'));
1243
+ let changes = '';
1244
+ let recordedOriginalSelector, recordedCurrentSelector;
1245
+ for (const {currentLineNumber, originalLineNumber, type} of rows) {
1246
+ // diff line arrays starts at 0, but line numbers start at 1.
1247
+ const currentLineIndex = currentLineNumber - 1;
1248
+ const originalLineIndex = originalLineNumber - 1;
1249
+ switch (type) {
1250
+ case DiffView.DiffView.RowType.Deletion: {
1251
+ const originalLine = originalLines[originalLineIndex].trim();
1252
+ if (originalRuleToSelector.has(originalLineIndex)) {
1253
+ changes += `/* ${originalLine} { */\n`;
1254
+ recordedOriginalSelector = originalLine;
1255
+ continue;
1256
+ }
1257
+
1258
+ const originalSelector = originalPropertyToSelector.get(originalLineIndex);
1259
+ if (!originalSelector) {
1260
+ continue;
1261
+ }
1262
+ if (originalSelector !== recordedOriginalSelector && originalSelector !== recordedCurrentSelector) {
1263
+ if (recordedOriginalSelector || recordedCurrentSelector) {
1264
+ changes += '}\n\n';
1265
+ }
1266
+ changes += `${originalSelector} {\n`;
1267
+ }
1268
+ recordedOriginalSelector = originalSelector;
1269
+ changes += ` /* ${originalLine} */\n`;
1270
+ break;
1271
+ }
1272
+ case DiffView.DiffView.RowType.Addition: {
1273
+ const currentLine = currentLines[currentLineIndex].trim();
1274
+ if (currentRuleToSelector.has(currentLineIndex)) {
1275
+ changes += `${currentLine} {\n`;
1276
+ recordedCurrentSelector = currentLine;
1277
+ continue;
1278
+ }
1279
+
1280
+ const currentSelector = currentPropertyToSelector.get(currentLineIndex);
1281
+ if (!currentSelector) {
1282
+ continue;
1283
+ }
1284
+ if (currentSelector !== recordedOriginalSelector && currentSelector !== recordedCurrentSelector) {
1285
+ if (recordedOriginalSelector || recordedCurrentSelector) {
1286
+ changes += '}\n\n';
1287
+ }
1288
+ changes += `${currentSelector} {\n`;
1289
+ }
1290
+ recordedCurrentSelector = currentSelector;
1291
+ changes += ` ${currentLine}\n`;
1292
+ break;
1293
+ }
1294
+ default:
1295
+ break;
1296
+ }
1297
+ }
1298
+ if (changes.length > 0) {
1299
+ changes += '}';
1300
+ }
1301
+ return changes;
1302
+ }
1303
+
1304
+ async function buildPropertyRuleMaps(content: string):
1305
+ Promise<{propertyToSelector: Map<number, string>, ruleToSelector: Map<number, string>}> {
1306
+ const rules = await new Promise<Formatter.FormatterWorkerPool.CSSRule[]>(res => {
1307
+ const rules: Formatter.FormatterWorkerPool.CSSRule[] = [];
1308
+ Formatter.FormatterWorkerPool.formatterWorkerPool().parseCSS(content, (isLastChunk, currentRules) => {
1309
+ rules.push(...currentRules);
1310
+ if (isLastChunk) {
1311
+ res(rules);
1312
+ }
1313
+ });
1314
+ });
1315
+ const propertyToSelector = new Map<number, string>();
1316
+ const ruleToSelector = new Map<number, string>();
1317
+ for (const rule of rules) {
1318
+ if ('styleRange' in rule) {
1319
+ const selector = rule.selectorText.split('\n').pop()?.trim();
1320
+ if (!selector) {
1321
+ continue;
1322
+ }
1323
+ ruleToSelector.set(rule.styleRange.startLine, selector);
1324
+ for (const property of rule.properties) {
1325
+ propertyToSelector.set(property.range.startLine, selector);
1326
+ }
1327
+ }
1328
+ }
1329
+ return {propertyToSelector, ruleToSelector};
1330
+ }
1331
+
1165
1332
  // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
1166
1333
  // eslint-disable-next-line @typescript-eslint/naming-convention
1167
1334
  export const _maxLinkLength = 23;
@@ -3052,6 +3219,14 @@ export function unescapeCssString(input: string): string {
3052
3219
  });
3053
3220
  }
3054
3221
 
3222
+ export function escapeUrlAsCssComment(urlText: string): string {
3223
+ const url = new URL(urlText);
3224
+ if (url.search) {
3225
+ return `${url.origin}${url.pathname}${url.search.replaceAll('*/', '*%2F')}${url.hash}`;
3226
+ }
3227
+ return url.toString();
3228
+ }
3229
+
3055
3230
  export class StylesSidebarPropertyRenderer {
3056
3231
  private rule: SDK.CSSRule.CSSRule|null;
3057
3232
  private node: SDK.DOMModel.DOMNode|null;
@@ -39,7 +39,6 @@
39
39
  border: none;
40
40
  border-radius: 50%;
41
41
  background-color: var(--color-background-elevation-1);
42
- cursor: pointer;
43
42
  }
44
43
 
45
44
  .close::before,
@@ -16,7 +16,7 @@
16
16
 
17
17
  .computed-style-trace:hover {
18
18
  background-color: var(--legacy-focus-bg-color);
19
- cursor: pointer;
19
+ cursor: text;
20
20
  }
21
21
 
22
22
  .goto {
@@ -73,7 +73,6 @@
73
73
  .overflow:not(:disabled):hover {
74
74
  background-color: var(--color-background-elevation-2);
75
75
  color: var(--color-text-primary);
76
- cursor: pointer;
77
76
  }
78
77
 
79
78
  .crumb-link {
@@ -15,7 +15,7 @@
15
15
 
16
16
  .tree-outline li:hover {
17
17
  background-color: var(--legacy-focus-bg-color);
18
- cursor: pointer;
18
+ cursor: text;
19
19
  }
20
20
 
21
21
  .tree-outline li::before {
@@ -35,7 +35,7 @@
35
35
  margin: 1px 0 0;
36
36
  padding: 1em 0;
37
37
  width: 100%;
38
- cursor: pointer;
38
+ cursor: text;
39
39
  color: var(--color-text-secondary);
40
40
  font-size: 11px;
41
41
  font-weight: 400;
@@ -144,7 +144,6 @@
144
144
  .elements-disclosure > ol {
145
145
  position: relative;
146
146
  margin: 0;
147
- cursor: default;
148
147
  min-width: 100%;
149
148
  min-height: 100%;
150
149
  padding-left: 2px;
@@ -203,7 +202,6 @@ select {
203
202
  border-radius: 5px;
204
203
  border: 1px solid var(--issue-color-yellow);
205
204
  background-color: var(--issue-color-yellow);
206
- cursor: pointer;
207
205
  }
208
206
 
209
207
  .elements-gutter-decoration.elements-has-decorated-children {
@@ -278,7 +276,6 @@ select {
278
276
  position: absolute;
279
277
  top: 0;
280
278
  left: 0;
281
- cursor: pointer;
282
279
  width: 15px;
283
280
  height: 15px;
284
281
  }
@@ -90,7 +90,6 @@
90
90
  pointer-events: auto;
91
91
  text-align: center;
92
92
  flex: none;
93
- cursor: pointer;
94
93
  color: var(--color-text-primary);
95
94
  display: flex;
96
95
  align-items: center;
@@ -56,7 +56,6 @@
56
56
  border: 1px solid var(--color-details-hairline);
57
57
  margin-left: 5px;
58
58
  display: block;
59
- cursor: pointer;
60
59
  opacity: 80%;
61
60
  flex-shrink: 0;
62
61
  }
@@ -14,7 +14,6 @@
14
14
  padding: 1px 4px;
15
15
  overflow: hidden;
16
16
  border-radius: 0;
17
- cursor: pointer;
18
17
  border: none;
19
18
 
20
19
  --icon-color: var(--color-text-primary);
@@ -104,6 +104,22 @@ const UIStrings = {
104
104
  *@description Tooltip text of checkbox to emulate mobile device behavior when running audits in Lighthouse
105
105
  */
106
106
  applyMobileEmulationDuring: 'Apply mobile emulation during auditing',
107
+ /**
108
+ * @description ARIA label for a radio button input to select the Lighthouse mode.
109
+ */
110
+ lighthouseMode: 'Lighthouse mode',
111
+ /**
112
+ * @description Tooltip text of a radio button to select the Lighthouse mode.
113
+ */
114
+ runLighthouseInMode: 'Run Lighthouse in navigation, timespan, or snapshot mode',
115
+ /**
116
+ * @description Text for Lighthouse navigation mode.
117
+ */
118
+ navigation: 'Navigation',
119
+ /**
120
+ * @description Text for Lighthouse snapshot mode.
121
+ */
122
+ snapshot: 'Snapshot',
107
123
  /**
108
124
  *@description Text for the mobile platform, as opposed to desktop
109
125
  */
@@ -421,6 +437,20 @@ export const RuntimeSettings: RuntimeSetting[] = [
421
437
  ],
422
438
  learnMore: undefined,
423
439
  },
440
+ {
441
+ setting: Common.Settings.Settings.instance().createSetting(
442
+ 'lighthouse.mode', 'navigation', Common.Settings.SettingStorageType.Synced),
443
+ title: i18nLazyString(UIStrings.lighthouseMode),
444
+ description: i18nLazyString(UIStrings.runLighthouseInMode),
445
+ setFlags: (flags: Flags, value: string|boolean): void => {
446
+ flags.mode = value;
447
+ },
448
+ options: [
449
+ {label: i18nLazyString(UIStrings.navigation), value: 'navigation'},
450
+ {label: i18nLazyString(UIStrings.snapshot), value: 'snapshot'},
451
+ ],
452
+ learnMore: undefined,
453
+ },
424
454
  {
425
455
  // This setting is disabled, but we keep it around to show in the UI.
426
456
  setting: Common.Settings.Settings.instance().createSetting(
@@ -5,6 +5,7 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as Host from '../../core/host/host.js';
7
7
  import * as i18n from '../../core/i18n/i18n.js';
8
+ import * as Root from '../../core/root/root.js';
8
9
  import * as SDK from '../../core/sdk/sdk.js';
9
10
  import * as EmulationModel from '../../models/emulation/emulation.js';
10
11
  import * as UI from '../../ui/legacy/legacy.js';
@@ -20,6 +21,7 @@ import * as LighthouseReport from '../../third_party/lighthouse/report/report.js
20
21
  import {LighthouseReportRenderer, LighthouseReportUIFeatures} from './LighthouseReportRenderer.js';
21
22
  import {Item, ReportSelector} from './LighthouseReportSelector.js';
22
23
  import {StartView} from './LighthouseStartView.js';
24
+ import {StartViewFR} from './LighthouseStartViewFR.js';
23
25
  import {StatusView} from './LighthouseStatusView.js';
24
26
 
25
27
  const UIStrings = {
@@ -85,7 +87,11 @@ export class LighthousePanel extends UI.Panel.Panel {
85
87
 
86
88
  this.protocolService = new ProtocolService();
87
89
  this.controller = new LighthouseController(this.protocolService);
88
- this.startView = new StartView(this.controller);
90
+ if (Root.Runtime.experiments.isEnabled('lighthousePanelFR')) {
91
+ this.startView = new StartViewFR(this.controller);
92
+ } else {
93
+ this.startView = new StartView(this.controller);
94
+ }
89
95
  this.statusView = new StatusView(this.controller);
90
96
 
91
97
  this.warningText = null;
@@ -63,7 +63,12 @@ export class ProtocolService {
63
63
  if (!this.targetInfo) {
64
64
  throw new Error('Unable to get target info required for Lighthouse');
65
65
  }
66
- const mode = flags.legacyNavigation ? 'start' : 'navigate';
66
+
67
+ let mode = flags.mode as string;
68
+ if (mode === 'navigation' && flags.legacyNavigation) {
69
+ mode = 'legacyNavigation';
70
+ }
71
+
67
72
  return this.sendWithResponse(mode, {
68
73
  url: auditURL,
69
74
  categoryIDs,
@@ -60,7 +60,7 @@ export class StartView extends UI.Widget.Widget {
60
60
  return this.settingsToolbarInternal;
61
61
  }
62
62
 
63
- private populateRuntimeSettingAsRadio(settingName: string, label: string, parentElement: Element): void {
63
+ protected populateRuntimeSettingAsRadio(settingName: string, label: string, parentElement: Element): void {
64
64
  const runtimeSetting = RuntimeSettings.find(item => item.setting.name === settingName);
65
65
  if (!runtimeSetting || !runtimeSetting.options) {
66
66
  throw new Error(`${settingName} is not a setting with options`);
@@ -112,7 +112,7 @@ export class StartView extends UI.Widget.Widget {
112
112
  UI.ARIAUtils.setAccessibleName(pluginFormElements, i18nString(UIStrings.communityPluginsBeta));
113
113
  }
114
114
 
115
- private render(): void {
115
+ protected render(): void {
116
116
  this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.legacy_navigation', this.settingsToolbarInternal);
117
117
  this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.clear_storage', this.settingsToolbarInternal);
118
118
  this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.throttling', this.settingsToolbarInternal);
@@ -0,0 +1,39 @@
1
+ // Copyright 2022 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as i18n from '../../core/i18n/i18n.js';
6
+ import * as UI from '../../ui/legacy/legacy.js';
7
+
8
+ import {StartView} from './LighthouseStartView.js';
9
+
10
+ const UIStrings = {
11
+ /**
12
+ * @description Text that refers to the Lighthouse mode
13
+ */
14
+ mode: 'Mode',
15
+ };
16
+
17
+ const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseStartViewFR.ts', UIStrings);
18
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
19
+
20
+ export class StartViewFR extends StartView {
21
+ protected render(): void {
22
+ super.render();
23
+ const fragment = UI.Fragment.Fragment.build`
24
+ <div class="lighthouse-form-section">
25
+ <div class="lighthouse-form-section-label">
26
+ ${i18nString(UIStrings.mode)}
27
+ </div>
28
+ <div class="lighthouse-form-elements" $="mode-form-elements"></div>
29
+ </div>
30
+ `;
31
+
32
+ // Populate the Lighthouse mode
33
+ const modeFormElements = fragment.$('mode-form-elements');
34
+ this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
35
+
36
+ const form = this.contentElement.querySelector('form');
37
+ form?.appendChild(fragment.element());
38
+ }
39
+ }
@@ -61,7 +61,6 @@ li.storage-group-list-item::before {
61
61
  }
62
62
 
63
63
  .player-entry-row:hover {
64
- cursor: pointer;
65
64
  background: var(--color-background-hover-overlay);
66
65
  }
67
66
 
@@ -124,10 +124,6 @@
124
124
  color: inherit;
125
125
  }
126
126
 
127
- .network-log-grid.data-grid .name-column {
128
- cursor: pointer;
129
- }
130
-
131
127
  .network-log-grid.data-grid .waterfall-column {
132
128
  padding: 1px 0;
133
129
  }
@@ -71,7 +71,6 @@
71
71
 
72
72
  .tree-outline li .header-toggle:hover {
73
73
  color: var(--color-text-secondary);
74
- cursor: pointer;
75
74
  }
76
75
 
77
76
  .tree-outline .payload-name {
@@ -105,7 +104,6 @@
105
104
  display: inline-block;
106
105
  font-size: 12px;
107
106
  font-family: sans-serif;
108
- cursor: pointer;
109
107
  margin: 0 4px;
110
108
  padding: 2px 4px;
111
109
  }
@@ -69,7 +69,6 @@
69
69
  --override-header-hover-color: rgb(20% 20% 45%);
70
70
 
71
71
  color: var(--override-header-hover-color);
72
- cursor: pointer;
73
72
  }
74
73
 
75
74
  .-theme-with-dark-background .tree-outline .header-toggle:hover,
@@ -107,7 +107,6 @@
107
107
  .orientation-stage.disabled {
108
108
  filter: grayscale();
109
109
  opacity: 50%;
110
- cursor: default !important; /* stylelint-disable-line declaration-no-important */
111
110
  }
112
111
 
113
112
  .orientation-element,
@@ -75,10 +75,6 @@
75
75
  margin-right: 5px;
76
76
  }
77
77
 
78
- .delete-icon {
79
- cursor: pointer;
80
- }
81
-
82
78
  .brand-row {
83
79
  display: flex;
84
80
  align-items: center;
@@ -34,7 +34,6 @@
34
34
  display: flex;
35
35
  align-items: center;
36
36
  flex: auto 1 1;
37
- cursor: pointer;
38
37
  overflow: hidden;
39
38
  color: var(--color-text-primary);
40
39
  user-select: none;
@@ -28,12 +28,12 @@ const UIStrings = {
28
28
  const str_ = i18n.i18n.registerUIStrings('panels/snippets/ScriptSnippetFileSystem.ts', UIStrings);
29
29
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
30
30
 
31
- function escapeSnippetName(name: string): string {
32
- return escape(name);
31
+ function escapeSnippetName(name: string): Platform.DevToolsPath.EncodedPathString {
32
+ return Common.ParsedURL.ParsedURL.rawPathToEncodedPathString(name as Platform.DevToolsPath.RawPathString);
33
33
  }
34
34
 
35
35
  function unescapeSnippetName(name: string): string {
36
- return unescape(name);
36
+ return Common.ParsedURL.ParsedURL.encodedPathToRawPathString(name as Platform.DevToolsPath.EncodedPathString);
37
37
  }
38
38
 
39
39
  export class SnippetFileSystem extends Persistence.PlatformFileSystem.PlatformFileSystem {
@@ -47,7 +47,7 @@ export class SnippetFileSystem extends Persistence.PlatformFileSystem.PlatformFi
47
47
  this.snippetsSetting = Common.Settings.Settings.instance().createSetting('scriptSnippets', []);
48
48
  }
49
49
 
50
- initialFilePaths(): string[] {
50
+ initialFilePaths(): Platform.DevToolsPath.EncodedPathString[] {
51
51
  const savedSnippets: Snippet[] = this.snippetsSetting.get();
52
52
  return savedSnippets.map(snippet => escapeSnippetName(snippet.name));
53
53
  }
@@ -77,7 +77,7 @@ export class SnippetsQuickOpen extends QuickOpen.FilteredListWidget.Provider {
77
77
  }
78
78
 
79
79
  renderItem(itemIndex: number, query: string, titleElement: Element, _subtitleElement: Element): void {
80
- titleElement.textContent = unescape(this.snippets[itemIndex].name());
80
+ titleElement.textContent = this.snippets[itemIndex].name();
81
81
  titleElement.classList.add('monospace');
82
82
  QuickOpen.FilteredListWidget.FilteredListWidget.highlightRanges(titleElement, query, true);
83
83
  }