chrome-devtools-frontend 1.0.1002867 → 1.0.1003469

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.
@@ -372,6 +372,7 @@ export enum EnumeratedHistogram {
372
372
  RecordingEdited = 'DevTools.RecordingEdited',
373
373
  RecordingExported = 'DevTools.RecordingExported',
374
374
  RecordingReplayFinished = 'DevTools.RecordingReplayFinished',
375
+ RecordingReplaySpeed = 'DevTools.RecordingReplaySpeed',
375
376
  RecordingReplayStarted = 'DevTools.RecordingReplayStarted',
376
377
  RecordingToggled = 'DevTools.RecordingToggled',
377
378
  StyleTextCopied = 'DevTools.StyleTextCopied',
@@ -246,6 +246,11 @@ export class UserMetrics {
246
246
  EnumeratedHistogram.RecordingReplayFinished, value, RecordingReplayFinished.MaxValue);
247
247
  }
248
248
 
249
+ recordingReplaySpeed(value: RecordingReplaySpeed): void {
250
+ InspectorFrontendHostInstance.recordEnumeratedHistogram(
251
+ EnumeratedHistogram.RecordingReplaySpeed, value, RecordingReplaySpeed.MaxValue);
252
+ }
253
+
249
254
  recordingReplayStarted(value: RecordingReplayStarted): void {
250
255
  InspectorFrontendHostInstance.recordEnumeratedHistogram(
251
256
  EnumeratedHistogram.RecordingReplayStarted, value, RecordingReplayStarted.MaxValue);
@@ -895,6 +900,16 @@ export enum RecordingReplayFinished {
895
900
  MaxValue = 5,
896
901
  }
897
902
 
903
+ // TODO(crbug.com/1167717): Make this a const enum again
904
+ // eslint-disable-next-line rulesdir/const_enum
905
+ export enum RecordingReplaySpeed {
906
+ Normal = 1,
907
+ Slow = 2,
908
+ VerySlow = 3,
909
+ ExtremelySlow = 4,
910
+ MaxValue = 5,
911
+ }
912
+
898
913
  // TODO(crbug.com/1167717): Make this a const enum again
899
914
  // eslint-disable-next-line rulesdir/const_enum
900
915
  export enum RecordingReplayStarted {
@@ -7025,6 +7025,9 @@
7025
7025
  "panels/network/NetworkLogView.ts | copyStacktrace": {
7026
7026
  "message": "Copy stack trace"
7027
7027
  },
7028
+ "panels/network/NetworkLogView.ts | createResponseHeaderOverride": {
7029
+ "message": "Create response header override"
7030
+ },
7028
7031
  "panels/network/NetworkLogView.ts | domcontentloadedS": {
7029
7032
  "message": "DOMContentLoaded: {PH1}"
7030
7033
  },
@@ -10424,9 +10427,6 @@
10424
10427
  "panels/timeline/TimelineFlameChartDataProvider.ts | idleFrame": {
10425
10428
  "message": "Idle Frame"
10426
10429
  },
10427
- "panels/timeline/TimelineFlameChartDataProvider.ts | interactions": {
10428
- "message": "Interactions"
10429
- },
10430
10430
  "panels/timeline/TimelineFlameChartDataProvider.ts | longFrame": {
10431
10431
  "message": "Long frame"
10432
10432
  },
@@ -12497,6 +12497,12 @@
12497
12497
  "ui/legacy/InspectorView.ts | reloadDevtools": {
12498
12498
  "message": "Reload DevTools"
12499
12499
  },
12500
+ "ui/legacy/InspectorView.ts | selectFolder": {
12501
+ "message": "Select folder"
12502
+ },
12503
+ "ui/legacy/InspectorView.ts | selectOverrideFolder": {
12504
+ "message": "Select a folder to store override files in."
12505
+ },
12500
12506
  "ui/legacy/InspectorView.ts | setToBrowserLanguage": {
12501
12507
  "message": "Always match Chrome's language"
12502
12508
  },
@@ -7025,6 +7025,9 @@
7025
7025
  "panels/network/NetworkLogView.ts | copyStacktrace": {
7026
7026
  "message": "Ĉóp̂ý ŝt́âćk̂ t́r̂áĉé"
7027
7027
  },
7028
+ "panels/network/NetworkLogView.ts | createResponseHeaderOverride": {
7029
+ "message": "Ĉŕêát̂é r̂éŝṕôńŝé ĥéâd́êŕ ôv́êŕr̂íd̂é"
7030
+ },
7028
7031
  "panels/network/NetworkLogView.ts | domcontentloadedS": {
7029
7032
  "message": "D̂ÓM̂Ćôńt̂én̂t́L̂óâd́êd́: {PH1}"
7030
7033
  },
@@ -10424,9 +10427,6 @@
10424
10427
  "panels/timeline/TimelineFlameChartDataProvider.ts | idleFrame": {
10425
10428
  "message": "Îd́l̂é F̂ŕâḿê"
10426
10429
  },
10427
- "panels/timeline/TimelineFlameChartDataProvider.ts | interactions": {
10428
- "message": "Îńt̂ér̂áĉt́îón̂ś"
10429
- },
10430
10430
  "panels/timeline/TimelineFlameChartDataProvider.ts | longFrame": {
10431
10431
  "message": "L̂ón̂ǵ f̂ŕâḿê"
10432
10432
  },
@@ -12497,6 +12497,12 @@
12497
12497
  "ui/legacy/InspectorView.ts | reloadDevtools": {
12498
12498
  "message": "R̂él̂óâd́ D̂év̂T́ôól̂ś"
12499
12499
  },
12500
+ "ui/legacy/InspectorView.ts | selectFolder": {
12501
+ "message": "Ŝél̂éĉt́ f̂ól̂d́êŕ"
12502
+ },
12503
+ "ui/legacy/InspectorView.ts | selectOverrideFolder": {
12504
+ "message": "Ŝél̂éĉt́ â f́ôĺd̂ér̂ t́ô śt̂ór̂é ôv́êŕr̂íd̂é f̂íl̂éŝ ín̂."
12505
+ },
12500
12506
  "ui/legacy/InspectorView.ts | setToBrowserLanguage": {
12501
12507
  "message": "Âĺŵáŷś m̂át̂ćĥ Ćĥŕôḿê'ś l̂án̂ǵûáĝé"
12502
12508
  },
@@ -203,17 +203,18 @@ export class TracingModel {
203
203
  this.#minimumRecordTimeInternal = timestamp;
204
204
  }
205
205
 
206
- // Track only main thread navigation start items. This is done by tracking isLoadingMainFrame,
207
- // and whether documentLoaderURL is set.
206
+ // Track only main thread navigation start items. This is done by tracking
207
+ // isOutermostMainFrame, and whether documentLoaderURL is set.
208
208
  if (payload.name === 'navigationStart') {
209
209
  const data = (payload.args.data as {
210
210
  isLoadingMainFrame: boolean,
211
211
  documentLoaderURL: string,
212
212
  navigationId: string,
213
+ isOutermostMainFrame?: boolean,
213
214
  } | null);
214
215
  if (data) {
215
- const {isLoadingMainFrame, documentLoaderURL, navigationId} = data;
216
- if (isLoadingMainFrame && documentLoaderURL !== '') {
216
+ const {isLoadingMainFrame, documentLoaderURL, navigationId, isOutermostMainFrame} = data;
217
+ if ((isOutermostMainFrame ?? isLoadingMainFrame) && documentLoaderURL !== '') {
217
218
  const thread = process.threadById(payload.tid);
218
219
  const navStartEvent = Event.fromPayload(payload, thread);
219
220
  this.#mainFrameNavStartTimes.set(navigationId, navStartEvent);
@@ -416,6 +416,7 @@
416
416
  RecordingEdited: 'DevTools.RecordingEdited',
417
417
  RecordingExported: 'DevTools.RecordingExported',
418
418
  RecordingReplayFinished: 'DevTools.RecordingReplayFinished',
419
+ RecordingReplaySpeed: 'DevTools.RecordingReplaySpeed',
419
420
  RecordingReplayStarted: 'DevTools.RecordingReplayStarted',
420
421
  RecordingToggled: 'DevTools.RecordingToggled',
421
422
  SyncSetting: 'DevTools.SyncSetting',
@@ -272,6 +272,26 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
272
272
  (this.projectInternal as FileSystem).fileSystemPath(), '/', this.encodedPathFromUrl(url, ignoreInactive));
273
273
  }
274
274
 
275
+ private getHeadersUISourceCodeFromUrl(url: Platform.DevToolsPath.UrlString): Workspace.UISourceCode.UISourceCode
276
+ |null {
277
+ const fileUrlFromRequest = this.fileUrlFromNetworkUrl(url, /* ignoreNoActive */ true);
278
+ const folderUrlFromRequest =
279
+ Common.ParsedURL.ParsedURL.substring(fileUrlFromRequest, 0, fileUrlFromRequest.lastIndexOf('/'));
280
+ const headersFileUrl = Common.ParsedURL.ParsedURL.concatenate(folderUrlFromRequest, '/', HEADERS_FILENAME);
281
+ return Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(headersFileUrl);
282
+ }
283
+
284
+ async getOrCreateHeadersUISourceCodeFromUrl(url: Platform.DevToolsPath.UrlString):
285
+ Promise<Workspace.UISourceCode.UISourceCode|null> {
286
+ let uiSourceCode = this.getHeadersUISourceCodeFromUrl(url);
287
+ if (!uiSourceCode && this.projectInternal) {
288
+ const encodedFilePath = this.encodedPathFromUrl(url, /* ignoreNoActive */ true);
289
+ const encodedPath = Common.ParsedURL.ParsedURL.substring(encodedFilePath, 0, encodedFilePath.lastIndexOf('/'));
290
+ uiSourceCode = await this.projectInternal.createFile(encodedPath, HEADERS_FILENAME, '');
291
+ }
292
+ return uiSourceCode;
293
+ }
294
+
275
295
  private decodeLocalPathToUrlPath(path: string): string {
276
296
  try {
277
297
  return unescape(path);
@@ -225,7 +225,8 @@ export class TimelineModelImpl {
225
225
  }
226
226
 
227
227
  isMainFrameNavigationStartEvent(event: SDK.TracingModel.Event): boolean {
228
- return this.isNavigationStartEvent(event) && event.args['data']['isLoadingMainFrame'] &&
228
+ return this.isNavigationStartEvent(event) &&
229
+ (event.args['data']['isOutermostMainFrame'] ?? event.args['data']['isLoadingMainFrame']) &&
229
230
  event.args['data']['documentLoaderURL'];
230
231
  }
231
232
 
@@ -1354,6 +1354,10 @@ export interface StylesUpdateCompletedEvent {
1354
1354
  hasMatchedStyles: boolean;
1355
1355
  }
1356
1356
 
1357
+ interface CompletionResult extends UI.SuggestBox.Suggestion {
1358
+ isCSSVariableColor?: boolean;
1359
+ }
1360
+
1357
1361
  export type EventTypes = {
1358
1362
  [Events.InitialUpdateCompleted]: void,
1359
1363
  [Events.StylesUpdateCompleted]: StylesUpdateCompletedEvent,
@@ -1649,8 +1653,8 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1649
1653
  return Promise.resolve([]);
1650
1654
  }
1651
1655
 
1652
- const prefixResults: UI.SuggestBox.Suggestions = [];
1653
- const anywhereResults: UI.SuggestBox.Suggestions = [];
1656
+ const prefixResults: Array<CompletionResult> = [];
1657
+ const anywhereResults: Array<CompletionResult> = [];
1654
1658
  if (!editingVariable) {
1655
1659
  this.cssCompletions.forEach(completion => filterCompletions.call(this, completion, false /* variable */));
1656
1660
  }
@@ -1745,10 +1749,10 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1745
1749
 
1746
1750
  if (this.isColorAware && !this.isEditingName) {
1747
1751
  results.sort((a, b) => {
1748
- if (Boolean(a.subtitleRenderer) === Boolean(b.subtitleRenderer)) {
1752
+ if (a.isCSSVariableColor && b.isCSSVariableColor) {
1749
1753
  return 0;
1750
1754
  }
1751
- return a.subtitleRenderer ? -1 : 1;
1755
+ return a.isCSSVariableColor ? -1 : 1;
1752
1756
  });
1753
1757
  }
1754
1758
  return Promise.resolve(results);
@@ -1756,7 +1760,7 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1756
1760
  function filterCompletions(
1757
1761
  this: CSSPropertyPrompt, completion: string, variable: boolean, nameValue?: boolean): void {
1758
1762
  const index = completion.toLowerCase().indexOf(lowerQuery);
1759
- const result: UI.SuggestBox.Suggestion = {
1763
+ const result: CompletionResult = {
1760
1764
  text: completion,
1761
1765
  title: undefined,
1762
1766
  subtitle: undefined,
@@ -1767,6 +1771,7 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1767
1771
  selectionRange: undefined,
1768
1772
  hideGhostText: undefined,
1769
1773
  iconElement: undefined,
1774
+ isCSSVariableColor: false,
1770
1775
  };
1771
1776
  if (variable) {
1772
1777
  const computedValue =
@@ -1775,6 +1780,9 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1775
1780
  const color = Common.Color.Color.parse(computedValue);
1776
1781
  if (color) {
1777
1782
  result.subtitleRenderer = swatchRenderer.bind(null, color);
1783
+ result.isCSSVariableColor = true;
1784
+ } else {
1785
+ result.subtitleRenderer = computedValueSubtitleRenderer.bind(null, computedValue);
1778
1786
  }
1779
1787
  }
1780
1788
  }
@@ -1795,6 +1803,14 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1795
1803
  swatch.style.pointerEvents = 'none';
1796
1804
  return swatch;
1797
1805
  }
1806
+ function computedValueSubtitleRenderer(computedValue: string): Element {
1807
+ const subtitleElement = document.createElement('span');
1808
+ subtitleElement.className = 'suggestion-subtitle';
1809
+ subtitleElement.textContent = `${computedValue}`;
1810
+ subtitleElement.style.maxWidth = '100px';
1811
+ subtitleElement.title = `${computedValue}`;
1812
+ return subtitleElement;
1813
+ }
1798
1814
  }
1799
1815
  }
1800
1816
 
@@ -42,8 +42,10 @@ import * as Bindings from '../../models/bindings/bindings.js';
42
42
  import * as HAR from '../../models/har/har.js';
43
43
  import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
44
44
  import * as Logs from '../../models/logs/logs.js';
45
+ import * as Persistence from '../../models/persistence/persistence.js';
45
46
  import * as TextUtils from '../../models/text_utils/text_utils.js';
46
47
  import * as NetworkForward from '../../panels/network/forward/forward.js';
48
+ import * as Sources from '../../panels/sources/sources.js';
47
49
  import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
48
50
  import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
49
51
  import * as Components from '../../ui/legacy/components/utils/utils.js';
@@ -346,6 +348,11 @@ const UIStrings = {
346
348
  *@description Text in Network Log View of the Network panel
347
349
  */
348
350
  areYouSureYouWantToClearBrowserCookies: 'Are you sure you want to clear browser cookies?',
351
+ /**
352
+ *@description A context menu item in the Network Log View of the Network panel
353
+ * for creating a header override
354
+ */
355
+ createResponseHeaderOverride: 'Create response header override',
349
356
  };
350
357
  const str_ = i18n.i18n.registerUIStrings('panels/network/NetworkLogView.ts', UIStrings);
351
358
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -1576,6 +1583,10 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
1576
1583
 
1577
1584
  contextMenu.saveSection().appendItem(i18nString(UIStrings.saveAllAsHarWithContent), this.exportAll.bind(this));
1578
1585
 
1586
+ contextMenu.editSection().appendItem(
1587
+ i18nString(UIStrings.createResponseHeaderOverride),
1588
+ this.#handleCreateResponseHeaderOverrideClick.bind(this, request));
1589
+ contextMenu.editSection().appendSeparator();
1579
1590
  contextMenu.editSection().appendItem(i18nString(UIStrings.clearBrowserCache), this.clearBrowserCache.bind(this));
1580
1591
  contextMenu.editSection().appendItem(
1581
1592
  i18nString(UIStrings.clearBrowserCookies), this.clearBrowserCookies.bind(this));
@@ -1691,6 +1702,27 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
1691
1702
  void stream.close();
1692
1703
  }
1693
1704
 
1705
+ async #handleCreateResponseHeaderOverrideClick(request: SDK.NetworkRequest.NetworkRequest): Promise<void> {
1706
+ if (Persistence.NetworkPersistenceManager.NetworkPersistenceManager.instance().project()) {
1707
+ await this.#revealHeaderOverrideEditor(request);
1708
+ } else { // If folder for local overrides has not been provided yet
1709
+ UI.InspectorView.InspectorView.instance().displaySelectOverrideFolderInfobar(async(): Promise<void> => {
1710
+ await Sources.SourcesNavigator.OverridesNavigatorView.instance().setupNewWorkspace();
1711
+ await this.#revealHeaderOverrideEditor(request);
1712
+ });
1713
+ }
1714
+ }
1715
+
1716
+ async #revealHeaderOverrideEditor(request: SDK.NetworkRequest.NetworkRequest): Promise<void> {
1717
+ const networkPersistanceManager = Persistence.NetworkPersistenceManager.NetworkPersistenceManager.instance();
1718
+ const uiSourceCode = await networkPersistanceManager.getOrCreateHeadersUISourceCodeFromUrl(request.url());
1719
+ if (uiSourceCode) {
1720
+ const sourcesPanel = Sources.SourcesPanel.SourcesPanel.instance();
1721
+ sourcesPanel.showUISourceCode(uiSourceCode);
1722
+ sourcesPanel.revealInNavigator(uiSourceCode);
1723
+ }
1724
+ }
1725
+
1694
1726
  private clearBrowserCache(): void {
1695
1727
  if (confirm(i18nString(UIStrings.areYouSureYouWantToClearBrowser))) {
1696
1728
  SDK.NetworkManager.MultitargetNetworkManager.instance().clearBrowserCache();
@@ -278,7 +278,7 @@ export class OverridesNavigatorView extends NavigatorView {
278
278
  this.toolbar.appendToolbarItem(setupButton);
279
279
  }
280
280
 
281
- private async setupNewWorkspace(): Promise<void> {
281
+ async setupNewWorkspace(): Promise<void> {
282
282
  const fileSystem =
283
283
  await Persistence.IsolatedFileSystemManager.IsolatedFileSystemManager.instance().addFileSystem('overrides');
284
284
  if (!fileSystem) {
@@ -528,7 +528,7 @@ export class SourcesPanel extends UI.Panel.Panel implements UI.ContextMenu.Provi
528
528
  this.showUISourceCode(uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber, omitFocus);
529
529
  }
530
530
 
531
- private revealInNavigator(uiSourceCode: Workspace.UISourceCode.UISourceCode, skipReveal?: boolean): void {
531
+ revealInNavigator(uiSourceCode: Workspace.UISourceCode.UISourceCode, skipReveal?: boolean): void {
532
532
  for (const navigator of registeredNavigatorViews) {
533
533
  const navigatorView = navigator.navigatorView();
534
534
  const viewId = navigator.viewId;
@@ -105,10 +105,6 @@ const UIStrings = {
105
105
  */
106
106
  experience: 'Experience',
107
107
  /**
108
- *@description Text in Timeline Flame Chart Data Provider of the Performance panel
109
- */
110
- interactions: 'Interactions',
111
- /**
112
108
  *@description Text for rendering frames
113
109
  */
114
110
  frames: 'Frames',
@@ -178,8 +174,7 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
178
174
  private readonly collapsibleTimingsHeader: PerfUI.FlameChart.GroupStyle;
179
175
  private readonly timingsHeader: PerfUI.FlameChart.GroupStyle;
180
176
  private readonly screenshotsHeader: PerfUI.FlameChart.GroupStyle;
181
- private readonly interactionsHeaderLevel1: PerfUI.FlameChart.GroupStyle;
182
- private readonly interactionsHeaderLevel2: PerfUI.FlameChart.GroupStyle;
177
+ private readonly animationsHeader: PerfUI.FlameChart.GroupStyle;
183
178
  private readonly experienceHeader: PerfUI.FlameChart.GroupStyle;
184
179
  private readonly flowEventIndexById: Map<string, number>;
185
180
  private entryData!: TimelineFlameChartEntry[];
@@ -239,8 +234,7 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
239
234
  this.buildGroupStyle({shareHeaderLine: true, useFirstLineForOverview: true, collapsible: false});
240
235
  this.screenshotsHeader =
241
236
  this.buildGroupStyle({useFirstLineForOverview: true, nestingLevel: 1, collapsible: false, itemsHeight: 150});
242
- this.interactionsHeaderLevel1 = this.buildGroupStyle({useFirstLineForOverview: true});
243
- this.interactionsHeaderLevel2 = this.buildGroupStyle({padding: 2, nestingLevel: 1});
237
+ this.animationsHeader = this.buildGroupStyle({useFirstLineForOverview: false});
244
238
  this.experienceHeader = this.buildGroupStyle({collapsible: false});
245
239
 
246
240
  ThemeSupport.ThemeSupport.instance().addEventListener(ThemeSupport.ThemeChangeEvent.eventName, () => {
@@ -252,8 +246,7 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
252
246
  this.collapsibleTimingsHeader,
253
247
  this.timingsHeader,
254
248
  this.screenshotsHeader,
255
- this.interactionsHeaderLevel1,
256
- this.interactionsHeaderLevel2,
249
+ this.animationsHeader,
257
250
  this.experienceHeader,
258
251
  ];
259
252
  for (const header of headers) {
@@ -460,7 +453,7 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
460
453
  switch (track.type) {
461
454
  case TimelineModel.TimelineModel.TrackType.Animation: {
462
455
  this.appendAsyncEventsGroup(
463
- track, i18nString(UIStrings.animation), track.asyncEvents, this.interactionsHeaderLevel2, eventEntryType,
456
+ track, i18nString(UIStrings.animation), track.asyncEvents, this.animationsHeader, eventEntryType,
464
457
  false /* selectable */);
465
458
  break;
466
459
  }
@@ -752,7 +745,6 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
752
745
  if (!interactionRecords.length) {
753
746
  return;
754
747
  }
755
- this.appendHeader(i18nString(UIStrings.interactions), this.interactionsHeaderLevel1, false /* selectable */);
756
748
  for (const segment of interactionRecords) {
757
749
  const index = this.entryData.length;
758
750
  this.entryData.push((segment.data as TimelineModel.TimelineIRModel.Phases));
@@ -340,8 +340,6 @@ async function completeProperties(
340
340
  return result;
341
341
  }
342
342
 
343
- const prototypePropertyPenalty = -80;
344
-
345
343
  async function completePropertiesInner(
346
344
  expression: string,
347
345
  context: SDK.RuntimeModel.ExecutionContext,
@@ -382,17 +380,10 @@ async function completePropertiesInner(
382
380
  (!prop.private || expression === 'this') && (quoted || SPAN_IDENT.test(prop.name))) {
383
381
  const label =
384
382
  quoted ? quoted + prop.name.replaceAll('\\', '\\\\').replaceAll(quoted, '\\' + quoted) + quoted : prop.name;
385
- const completion: CodeMirror.Completion = {
386
- label,
387
- type: prop.value?.type === 'function' ? functionType : otherType,
388
- };
389
- if (quoted && !hasBracket) {
390
- completion.apply = label + ']';
391
- }
392
- if (!prop.isOwn) {
393
- completion.boost = prototypePropertyPenalty;
394
- }
395
- result.add(completion);
383
+ const apply = (quoted && !hasBracket) ? `${label}]` : undefined;
384
+ const boost = 2 * Number(prop.isOwn) + 1 * Number(prop.enumerable);
385
+ const type = prop.value?.type === 'function' ? functionType : otherType;
386
+ result.add({apply, label, type, boost});
396
387
  }
397
388
  }
398
389
  }
@@ -115,6 +115,15 @@ const UIStrings = {
115
115
  *@description The aria label for the drawer hidden.
116
116
  */
117
117
  drawerHidden: 'Drawer hidden',
118
+ /**
119
+ * @description Request for the user to select a local file system folder for DevTools
120
+ * to store local overrides in.
121
+ */
122
+ selectOverrideFolder: 'Select a folder to store override files in.',
123
+ /**
124
+ *@description Label for a button which opens a file picker.
125
+ */
126
+ selectFolder: 'Select folder',
118
127
  };
119
128
  const str_ = i18n.i18n.registerUIStrings('ui/legacy/InspectorView.ts', UIStrings);
120
129
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -133,6 +142,7 @@ export class InspectorView extends VBox implements ViewLocationResolver {
133
142
  private focusRestorer?: WidgetFocusRestorer|null;
134
143
  private ownerSplitWidget?: SplitWidget;
135
144
  private reloadRequiredInfobar?: Infobar;
145
+ #selectOverrideFolderInfobar?: Infobar;
136
146
 
137
147
  constructor() {
138
148
  super();
@@ -439,6 +449,25 @@ export class InspectorView extends VBox implements ViewLocationResolver {
439
449
  }
440
450
  }
441
451
 
452
+ displaySelectOverrideFolderInfobar(callback: () => void): void {
453
+ if (!this.#selectOverrideFolderInfobar) {
454
+ const infobar = new Infobar(InfobarType.Info, i18nString(UIStrings.selectOverrideFolder), [
455
+ {
456
+ text: i18nString(UIStrings.selectFolder),
457
+ highlight: true,
458
+ delegate: (): void => callback(),
459
+ dismiss: true,
460
+ },
461
+ ]);
462
+ infobar.setParentView(this);
463
+ this.attachInfobar(infobar);
464
+ this.#selectOverrideFolderInfobar = infobar;
465
+ infobar.setCloseCallback(() => {
466
+ this.#selectOverrideFolderInfobar = undefined;
467
+ });
468
+ }
469
+ }
470
+
442
471
  private createInfoBarDiv(): void {
443
472
  if (!this.infoBarDiv) {
444
473
  this.infoBarDiv = document.createElement('div');
package/package.json CHANGED
@@ -55,5 +55,5 @@
55
55
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
56
56
  "watch": "vpython third_party/node/node.py --output scripts/watch_build.js"
57
57
  },
58
- "version": "1.0.1002867"
58
+ "version": "1.0.1003469"
59
59
  }