chrome-devtools-frontend 1.0.945579 → 1.0.946920

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 (100) hide show
  1. package/config/gni/all_devtools_files.gni +0 -7
  2. package/config/gni/devtools_grd_files.gni +3 -39
  3. package/front_end/core/host/InspectorFrontendHostAPI.ts +0 -1
  4. package/front_end/core/host/UserMetrics.ts +0 -22
  5. package/front_end/core/i18n/locales/en-US.json +33 -27
  6. package/front_end/core/i18n/locales/en-XL.json +33 -27
  7. package/front_end/core/root/Runtime.ts +0 -1
  8. package/front_end/core/sdk/CSSProperty.ts +16 -9
  9. package/front_end/core/sdk/sdk-meta.ts +20 -8
  10. package/front_end/devtools_compatibility.js +1 -8
  11. package/front_end/entrypoints/devtools_app/devtools_app.js +3 -0
  12. package/front_end/entrypoints/devtools_app/devtools_app.json +1 -2
  13. package/front_end/entrypoints/formatter_worker/CSSFormatter.ts +1 -3
  14. package/front_end/entrypoints/formatter_worker/FormatterActions.ts +0 -2
  15. package/front_end/entrypoints/formatter_worker/FormatterWorker.ts +0 -120
  16. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +1 -11
  17. package/front_end/entrypoints/formatter_worker/formatter_worker.ts +5 -0
  18. package/front_end/entrypoints/js_app/js_app.js +3 -0
  19. package/front_end/entrypoints/js_app/js_app.json +1 -2
  20. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  21. package/front_end/entrypoints/ndb_app/ndb_app.js +3 -0
  22. package/front_end/entrypoints/ndb_app/ndb_app.json +1 -2
  23. package/front_end/entrypoints/{node_main → node_app}/NodeConnectionsPanel.ts +1 -1
  24. package/front_end/entrypoints/{node_main → node_app}/NodeMain.ts +2 -4
  25. package/front_end/entrypoints/{node_main → node_app}/nodeConnectionsPanel.css +0 -0
  26. package/front_end/entrypoints/node_app/node_app.ts +81 -0
  27. package/front_end/entrypoints/shell/shell.js +18 -1
  28. package/front_end/entrypoints/worker_app/worker_app.js +3 -0
  29. package/front_end/entrypoints/worker_app/worker_app.json +1 -2
  30. package/front_end/legacy_test_runner/sources_test_runner/sources_test_runner.js +0 -1
  31. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +1 -1
  32. package/front_end/models/bindings/ResourceMapping.ts +1 -1
  33. package/front_end/models/formatter/FormatterWorkerPool.ts +0 -18
  34. package/front_end/models/text_utils/CodeMirrorUtils.ts +6 -51
  35. package/front_end/models/text_utils/TextUtils.ts +1 -2
  36. package/front_end/models/text_utils/text_utils-legacy.ts +0 -5
  37. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  38. package/front_end/panels/application/ApplicationPanelSidebar.ts +2 -4
  39. package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +0 -11
  40. package/front_end/panels/elements/StylesSidebarPane.ts +0 -1
  41. package/front_end/panels/elements/elementsTreeOutline.css +0 -13
  42. package/front_end/panels/emulation/DeviceModeToolbar.ts +0 -16
  43. package/front_end/panels/network/NetworkConfigView.ts +10 -0
  44. package/front_end/panels/network/NetworkItemView.ts +10 -1
  45. package/front_end/panels/network/networkConfigView.css +5 -0
  46. package/front_end/panels/profiler/heapProfiler.css +2 -5
  47. package/front_end/panels/profiler/profilesPanel.css +1 -1
  48. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +6 -4
  49. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +0 -12
  50. package/front_end/panels/sources/DebuggerPlugin.ts +7 -3
  51. package/front_end/panels/sources/sourcesView.css +0 -130
  52. package/front_end/panels/timeline/TimelineTreeView.ts +1 -0
  53. package/front_end/panels/webauthn/WebauthnPane.ts +31 -32
  54. package/front_end/third_party/codemirror/codemirror-tsconfig.json +1 -25
  55. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  56. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +1486 -1423
  57. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  58. package/front_end/third_party/codemirror.next/package.json +9 -9
  59. package/front_end/ui/components/buttons/Button.ts +17 -0
  60. package/front_end/ui/components/buttons/button.css +31 -0
  61. package/front_end/ui/components/data_grid/DataGrid.ts +9 -0
  62. package/front_end/ui/components/docs/button/basic.ts +42 -0
  63. package/front_end/ui/components/linear_memory_inspector/LinearMemoryValueInterpreter.ts +0 -8
  64. package/front_end/ui/components/text_editor/TextEditor.ts +5 -2
  65. package/front_end/ui/components/text_editor/config.ts +3 -3
  66. package/front_end/ui/components/text_editor/javascript.ts +28 -10
  67. package/front_end/ui/components/text_editor/theme.ts +1 -0
  68. package/front_end/ui/legacy/InspectorView.ts +10 -0
  69. package/front_end/ui/legacy/ListWidget.ts +2 -2
  70. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +2 -3
  71. package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +0 -1
  72. package/front_end/ui/legacy/components/object_ui/object_ui-legacy.ts +0 -8
  73. package/front_end/ui/legacy/components/object_ui/object_ui.ts +0 -4
  74. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +3 -0
  75. package/front_end/ui/legacy/inspectorSyntaxHighlight.css +0 -211
  76. package/front_end/ui/legacy/legacy-legacy.ts +0 -6
  77. package/front_end/ui/legacy/legacy.ts +0 -2
  78. package/front_end/ui/legacy/tabbedPane.css +1 -1
  79. package/package.json +1 -1
  80. package/scripts/check_gn.js +1 -1
  81. package/scripts/eslint_rules/lib/l10n_filename_matches.js +17 -4
  82. package/scripts/eslint_rules/tests/l10n_filename_matches_test.js +21 -0
  83. package/scripts/hosted_mode/server.js +17 -2
  84. package/front_end/entrypoints/node_app/node_app-meta.ts +0 -43
  85. package/front_end/entrypoints/node_app/node_app.js +0 -13
  86. package/front_end/entrypoints/node_app/node_app.json +0 -4
  87. package/front_end/entrypoints/node_main/node_main-meta.ts +0 -48
  88. package/front_end/entrypoints/node_main/node_main.ts +0 -11
  89. package/front_end/entrypoints/shell/shell-meta-files.ts +0 -22
  90. package/front_end/entrypoints/shell/shell.json +0 -5
  91. package/front_end/ui/legacy/TextEditor.ts +0 -82
  92. package/front_end/ui/legacy/components/object_ui/JavaScriptAutocomplete.ts +0 -836
  93. package/front_end/ui/legacy/components/text_editor/CodeMirrorTextEditor.ts +0 -1676
  94. package/front_end/ui/legacy/components/text_editor/TextEditorAutocompleteController.ts +0 -586
  95. package/front_end/ui/legacy/components/text_editor/autocompleteTooltip.css +0 -20
  96. package/front_end/ui/legacy/components/text_editor/cm_modes.ts +0 -23
  97. package/front_end/ui/legacy/components/text_editor/cmdevtools.css +0 -995
  98. package/front_end/ui/legacy/components/text_editor/module.json +0 -7
  99. package/front_end/ui/legacy/components/text_editor/text_editor-legacy.ts +0 -33
  100. package/front_end/ui/legacy/components/text_editor/text_editor.ts +0 -13
@@ -1,836 +0,0 @@
1
- // Copyright 2016 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 Platform from '../../../../core/platform/platform.js';
7
- import * as SDK from '../../../../core/sdk/sdk.js';
8
- import * as Formatter from '../../../../models/formatter/formatter.js';
9
- import * as JavaScriptMetaData from '../../../../models/javascript_metadata/javascript_metadata.js';
10
- import * as TextUtils from '../../../../models/text_utils/text_utils.js';
11
- import * as UI from '../../legacy.js';
12
-
13
- const UIStrings = {
14
- /**
15
- *@description 0 of suggestions in Java Script Autocomplete
16
- */
17
- keys: 'Keys',
18
- /**
19
- *@description Text in Java Script Autocomplete
20
- */
21
- lexicalScopeVariables: 'Lexical scope variables',
22
- /**
23
- *@description Text in Java Script Autocomplete
24
- */
25
- keywords: 'keywords',
26
- };
27
- const str_ = i18n.i18n.registerUIStrings('ui/legacy/components/object_ui/JavaScriptAutocomplete.ts', UIStrings);
28
- const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
29
- const DEFAULT_TIMEOUT = 500;
30
-
31
- let javaScriptAutocompleteInstance: JavaScriptAutocomplete;
32
-
33
- export class JavaScriptAutocomplete {
34
- private readonly expressionCache: Map<string, {
35
- date: number,
36
- value: Promise<Array<CompletionGroup>>,
37
- }>;
38
- private constructor() {
39
- this.expressionCache = new Map();
40
- SDK.ConsoleModel.ConsoleModel.instance().addEventListener(
41
- SDK.ConsoleModel.Events.CommandEvaluated, this.clearCache, this);
42
- UI.Context.Context.instance().addFlavorChangeListener(SDK.RuntimeModel.ExecutionContext, this.clearCache, this);
43
- SDK.TargetManager.TargetManager.instance().addModelListener(
44
- SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, this.clearCache, this);
45
- SDK.TargetManager.TargetManager.instance().addModelListener(
46
- SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, this.clearCache, this);
47
- }
48
-
49
- static instance(): JavaScriptAutocomplete {
50
- if (!javaScriptAutocompleteInstance) {
51
- javaScriptAutocompleteInstance = new JavaScriptAutocomplete();
52
- }
53
- return javaScriptAutocompleteInstance;
54
- }
55
-
56
- private clearCache(): void {
57
- this.expressionCache.clear();
58
- }
59
-
60
- async completionsForTextInCurrentContext(fullText: string, query: string, force?: boolean):
61
- Promise<UI.SuggestBox.Suggestions> {
62
- const trimmedText = fullText.trim();
63
-
64
- const [mapCompletions, expressionCompletions] = await Promise.all(
65
- [this.mapCompletions(trimmedText, query), this.completionsForExpression(trimmedText, query, force)]);
66
- return mapCompletions.concat(expressionCompletions);
67
- }
68
-
69
- async argumentsHint(fullText: string): Promise<{
70
- args: Array<Array<string>>,
71
- argumentIndex: number,
72
- }|null|undefined> {
73
- const functionCall = await Formatter.FormatterWorkerPool.formatterWorkerPool().findLastFunctionCall(fullText);
74
- if (!functionCall) {
75
- return null;
76
- }
77
- const executionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
78
- if (!executionContext) {
79
- return null;
80
- }
81
- const result = await executionContext.evaluate(
82
- {
83
- expression: functionCall.baseExpression,
84
- objectGroup: 'argumentsHint',
85
- includeCommandLineAPI: true,
86
- silent: true,
87
- returnByValue: false,
88
- generatePreview: false,
89
- throwOnSideEffect: true,
90
- timeout: DEFAULT_TIMEOUT,
91
- allowUnsafeEvalBlockedByCSP: undefined,
92
- disableBreaks: undefined,
93
- replMode: undefined,
94
- },
95
- /* userGesture */ false, /* awaitPromise */ false);
96
- if (!result || 'error' in result || result.exceptionDetails ||
97
- ('object' in result && (!result.object || result.object.type !== 'function'))) {
98
- executionContext.runtimeModel.releaseObjectGroup('argumentsHint');
99
- return null;
100
- }
101
-
102
- const args = await this.argumentsForFunction(result.object, async () => {
103
- const result = await executionContext.evaluate(
104
- {
105
- expression: functionCall.receiver,
106
- objectGroup: 'argumentsHint',
107
- includeCommandLineAPI: true,
108
- silent: true,
109
- returnByValue: false,
110
- generatePreview: false,
111
- throwOnSideEffect: true,
112
- timeout: DEFAULT_TIMEOUT,
113
- allowUnsafeEvalBlockedByCSP: undefined,
114
- disableBreaks: undefined,
115
- replMode: undefined,
116
- },
117
- /* userGesture */ false, /* awaitPromise */ false);
118
- return (result && !('error' in result) && !result.exceptionDetails && result.object) ? result.object : null;
119
- }, functionCall.functionName);
120
- executionContext.runtimeModel.releaseObjectGroup('argumentsHint');
121
- if (!args.length || (args.length === 1 && (!args[0] || !args[0].length))) {
122
- return null;
123
- }
124
- return {args, argumentIndex: functionCall.argumentIndex};
125
- }
126
-
127
- private async argumentsForFunction(
128
- functionObject: SDK.RemoteObject.RemoteObject,
129
- receiverObjGetter: () => Promise<SDK.RemoteObject.RemoteObject|null>,
130
- parsedFunctionName?: string): Promise<string[][]> {
131
- const description = functionObject.description;
132
- if (!description) {
133
- return [];
134
- }
135
- if (!description.endsWith('{ [native code] }')) {
136
- return [await Formatter.FormatterWorkerPool.formatterWorkerPool().argumentsList(description)];
137
- }
138
-
139
- // Check if this is a bound function.
140
- if (description === 'function () { [native code] }') {
141
- const properties = await functionObject.getOwnProperties(false);
142
- const internalProperties = properties.internalProperties || [];
143
- const targetProperty = internalProperties.find(property => property.name === '[[TargetFunction]]');
144
- const argsProperty = internalProperties.find(property => property.name === '[[BoundArgs]]');
145
- const thisProperty = internalProperties.find(property => property.name === '[[BoundThis]]');
146
- if (thisProperty && targetProperty && argsProperty && targetProperty.value && thisProperty.value &&
147
- argsProperty.value) {
148
- const thisValue = thisProperty.value;
149
- const originalSignatures =
150
- await this.argumentsForFunction(targetProperty.value, () => Promise.resolve(thisValue));
151
- const boundArgsLength = SDK.RemoteObject.RemoteObject.arrayLength(argsProperty.value);
152
- const clippedArgs = [];
153
- for (const signature of originalSignatures) {
154
- const restIndex = signature.slice(0, boundArgsLength).findIndex(arg => arg.startsWith('...'));
155
- if (restIndex !== -1) {
156
- clippedArgs.push(signature.slice(restIndex));
157
- } else {
158
- clippedArgs.push(signature.slice(boundArgsLength));
159
- }
160
- }
161
- return clippedArgs;
162
- }
163
- }
164
-
165
- const javaScriptMetadata = JavaScriptMetaData.JavaScriptMetadata.JavaScriptMetadataImpl.instance();
166
-
167
- const descriptionRegexResult = /^function ([^(]*)\(/.exec(description);
168
- const name = descriptionRegexResult && descriptionRegexResult[1] || parsedFunctionName;
169
- if (!name) {
170
- return [];
171
- }
172
- const uniqueSignatures = javaScriptMetadata.signaturesForNativeFunction(name);
173
- if (uniqueSignatures) {
174
- return uniqueSignatures;
175
- }
176
- const receiverObj = await receiverObjGetter();
177
- if (!receiverObj) {
178
- return [];
179
- }
180
- const className = receiverObj.className;
181
- if (className) {
182
- const instanceMethods = javaScriptMetadata.signaturesForInstanceMethod(name, className);
183
- if (instanceMethods) {
184
- return instanceMethods;
185
- }
186
- }
187
-
188
- // Check for static methods on a constructor.
189
- if (receiverObj.description && receiverObj.type === 'function' &&
190
- receiverObj.description.endsWith('{ [native code] }')) {
191
- const receiverDescriptionRegexResult = /^function ([^(]*)\(/.exec(receiverObj.description);
192
- if (receiverDescriptionRegexResult) {
193
- const receiverName = receiverDescriptionRegexResult[1];
194
- const staticSignatures = javaScriptMetadata.signaturesForStaticMethod(name, receiverName);
195
- if (staticSignatures) {
196
- return staticSignatures;
197
- }
198
- }
199
- }
200
-
201
- let protoNames: string[];
202
- if (receiverObj.type === 'number') {
203
- protoNames = ['Number', 'Object'];
204
- } else if (receiverObj.type === 'string') {
205
- protoNames = ['String', 'Object'];
206
- } else if (receiverObj.type === 'symbol') {
207
- protoNames = ['Symbol', 'Object'];
208
- } else if (receiverObj.type === 'bigint') {
209
- protoNames = ['BigInt', 'Object'];
210
- } else if (receiverObj.type === 'boolean') {
211
- protoNames = ['Boolean', 'Object'];
212
- } else if (receiverObj.type === 'undefined' || receiverObj.subtype === 'null') {
213
- protoNames = [];
214
- } else {
215
- protoNames = await receiverObj.callFunctionJSON(function() {
216
- const result = [];
217
- for (let object: Object = this; object; object = Object.getPrototypeOf(object)) {
218
- if (typeof object === 'object' && object.constructor && object.constructor.name) {
219
- result[result.length] = object.constructor.name;
220
- }
221
- }
222
- return result;
223
- }, []);
224
- }
225
-
226
- if (!protoNames) {
227
- return [];
228
- }
229
-
230
- for (const proto of protoNames) {
231
- const instanceSignatures = javaScriptMetadata.signaturesForInstanceMethod(name, proto);
232
- if (instanceSignatures) {
233
- return instanceSignatures;
234
- }
235
- }
236
- return [];
237
- }
238
-
239
- private async mapCompletions(text: string, query: string): Promise<UI.SuggestBox.Suggestions> {
240
- const mapMatch = text.match(/\.\s*(get|set|delete)\s*\(\s*$/);
241
- const executionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
242
- if (!executionContext || !mapMatch) {
243
- return [];
244
- }
245
-
246
- const expression =
247
- await Formatter.FormatterWorkerPool.formatterWorkerPool().findLastExpression(text.substring(0, mapMatch.index));
248
- if (!expression) {
249
- return [];
250
- }
251
-
252
- const result = await executionContext.evaluate(
253
- {
254
- expression,
255
- objectGroup: 'mapCompletion',
256
- includeCommandLineAPI: true,
257
- silent: true,
258
- returnByValue: false,
259
- generatePreview: false,
260
- throwOnSideEffect: true,
261
- timeout: DEFAULT_TIMEOUT,
262
- allowUnsafeEvalBlockedByCSP: undefined,
263
- disableBreaks: undefined,
264
- replMode: undefined,
265
- },
266
- /* userGesture */ false, /* awaitPromise */ false);
267
- if ('error' in result || Boolean(result.exceptionDetails) || result.object.subtype !== 'map') {
268
- return [];
269
- }
270
- const properties = await result.object.getOwnProperties(false);
271
- const internalProperties = properties.internalProperties || [];
272
- const entriesProperty = internalProperties.find(property => property.name === '[[Entries]]');
273
- if (!entriesProperty || !entriesProperty.value) {
274
- return [];
275
- }
276
- const keysObj = await entriesProperty.value.callFunctionJSON(function() {
277
- const actualThis = (this as {
278
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
279
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
280
- key: any,
281
- }[]);
282
- const result: {
283
- [x: string]: boolean|null,
284
- } = {__proto__: null};
285
- for (let i = 0; i < actualThis.length; i++) {
286
- if (typeof actualThis[i].key === 'string') {
287
- result[actualThis[i].key] = true;
288
- }
289
- }
290
- return result;
291
- }, []);
292
- executionContext.runtimeModel.releaseObjectGroup('mapCompletion');
293
- const rawKeys = Object.keys(keysObj);
294
-
295
- const caseSensitivePrefix: UI.SuggestBox.Suggestions = [];
296
- const caseInsensitivePrefix: UI.SuggestBox.Suggestions = [];
297
- const caseSensitiveAnywhere: UI.SuggestBox.Suggestions = [];
298
- const caseInsensitiveAnywhere: UI.SuggestBox.Suggestions = [];
299
- let quoteChar = '"';
300
- if (query.startsWith('\'')) {
301
- quoteChar = '\'';
302
- }
303
- let endChar = ')';
304
- if (mapMatch[0].indexOf('set') !== -1) {
305
- endChar = ', ';
306
- }
307
-
308
- const sorter = rawKeys.length < 1000 ? Platform.StringUtilities.naturalOrderComparator : undefined;
309
- const keys = rawKeys.sort(sorter).map(key => quoteChar + key + quoteChar);
310
-
311
- for (const key of keys) {
312
- if (key.length < query.length) {
313
- continue;
314
- }
315
- if (query.length && key.toLowerCase().indexOf(query.toLowerCase()) === -1) {
316
- continue;
317
- }
318
- // Substitute actual newlines with newline characters. @see crbug.com/498421
319
- const title = key.split('\n').join('\\n');
320
- const text = title + endChar;
321
-
322
- if (key.startsWith(query)) {
323
- caseSensitivePrefix.push(({text: text, title: title, priority: 4} as UI.SuggestBox.Suggestion));
324
- } else if (key.toLowerCase().startsWith(query.toLowerCase())) {
325
- caseInsensitivePrefix.push(({text: text, title: title, priority: 3} as UI.SuggestBox.Suggestion));
326
- } else if (key.indexOf(query) !== -1) {
327
- caseSensitiveAnywhere.push(({text: text, title: title, priority: 2} as UI.SuggestBox.Suggestion));
328
- } else {
329
- caseInsensitiveAnywhere.push(({text: text, title: title, priority: 1} as UI.SuggestBox.Suggestion));
330
- }
331
- }
332
- const suggestions =
333
- caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere);
334
- if (suggestions.length) {
335
- suggestions[0].subtitle = i18nString(UIStrings.keys);
336
- }
337
- return suggestions;
338
- }
339
-
340
- private async completionsForExpression(fullText: string, query: string, force?: boolean):
341
- Promise<UI.SuggestBox.Suggestions> {
342
- const executionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
343
- if (!executionContext) {
344
- return [];
345
- }
346
- let expression;
347
- if (fullText.endsWith('?.')) {
348
- expression = await Formatter.FormatterWorkerPool.formatterWorkerPool().findLastExpression(
349
- fullText.substring(0, fullText.length - 2));
350
- } else if (fullText.endsWith('.') || fullText.endsWith('[')) {
351
- expression = await Formatter.FormatterWorkerPool.formatterWorkerPool().findLastExpression(
352
- fullText.substring(0, fullText.length - 1));
353
- }
354
- if (!expression) {
355
- if (fullText.endsWith('.')) {
356
- return [];
357
- }
358
- expression = '';
359
- }
360
-
361
- const expressionString = expression;
362
- const dotNotation = fullText.endsWith('.');
363
- const bracketNotation = Boolean(expressionString) && fullText.endsWith('[');
364
-
365
- // User is entering float value, do not suggest anything.
366
- if ((expressionString && !isNaN(Number(expressionString))) ||
367
- (!expressionString && query && !isNaN(Number(query)))) {
368
- return [];
369
- }
370
-
371
- if (!query && !expressionString && !force) {
372
- return [];
373
- }
374
- const selectedFrame = executionContext.debuggerModel.selectedCallFrame();
375
- let completionGroups: CompletionGroup[]|null;
376
- const TEN_SECONDS = 10000;
377
- let cache = this.expressionCache.get(expressionString);
378
- if (cache && cache.date + TEN_SECONDS > Date.now()) {
379
- completionGroups = await cache.value;
380
- } else if (!expressionString && selectedFrame) {
381
- const value: CompletionGroup[] = [{
382
- items: ['this'],
383
- title: undefined,
384
- }];
385
- const scopeChain = selectedFrame.scopeChain();
386
- const groupPromises = [];
387
- for (const scope of scopeChain) {
388
- groupPromises.push(scope.object()
389
- .getAllProperties(false /* accessorPropertiesOnly */, false /* generatePreview */)
390
- .then(result => ({properties: result.properties, name: scope.name()})));
391
- }
392
- const fullScopes = await Promise.all(groupPromises);
393
- executionContext.runtimeModel.releaseObjectGroup('completion');
394
- for (const scope of fullScopes) {
395
- if (scope.properties) {
396
- value.push({title: scope.name, items: scope.properties.map(property => property.name).sort()});
397
- }
398
- }
399
- cache = {date: Date.now(), value: Promise.resolve(value)};
400
- this.expressionCache.set(expressionString, cache);
401
- completionGroups = await cache.value;
402
- } else {
403
- const resultPromise = executionContext.evaluate(
404
- {
405
- expression: expressionString,
406
- objectGroup: 'completion',
407
- includeCommandLineAPI: true,
408
- silent: true,
409
- returnByValue: false,
410
- generatePreview: false,
411
- throwOnSideEffect: true,
412
- timeout: DEFAULT_TIMEOUT,
413
- allowUnsafeEvalBlockedByCSP: undefined,
414
- disableBreaks: undefined,
415
- replMode: undefined,
416
- },
417
- /* userGesture */ false, /* awaitPromise */ false);
418
- cache = {date: Date.now(), value: resultPromise.then(result => completionsOnGlobal.call(this, result))};
419
- this.expressionCache.set(expressionString, cache);
420
- completionGroups = await cache.value;
421
- }
422
- return this.receivedPropertyNames(completionGroups.slice(0), dotNotation, bracketNotation, expressionString, query);
423
-
424
- async function completionsOnGlobal(
425
- this: JavaScriptAutocomplete, result: SDK.RuntimeModel.EvaluationResult): Promise<CompletionGroup[]> {
426
- if ('error' in result || Boolean(result.exceptionDetails) || !result.object) {
427
- return [];
428
- }
429
-
430
- if (!executionContext) {
431
- return [];
432
- }
433
-
434
- let object: SDK.RemoteObject.RemoteObject = result.object;
435
- while (object && object.type === 'object' && object.subtype === 'proxy') {
436
- const propertiesObject: SDK.RemoteObject.GetPropertiesResult =
437
- await object.getOwnProperties(false /* generatePreview */);
438
- const internalProperties = propertiesObject.internalProperties || [];
439
- const target = internalProperties.find(property => property.name === '[[Target]]');
440
- if (target && target.value) {
441
- object = target.value;
442
- } else {
443
- break;
444
- }
445
- }
446
- if (!object) {
447
- return [];
448
- }
449
- let completions: CompletionGroup[] = [];
450
- if (object.type === 'object' || object.type === 'function') {
451
- completions = (await object.callFunctionJSON(
452
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
453
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
454
- (getCompletions as (this: Object, ...arg1: any[]) => Object),
455
- [SDK.RemoteObject.RemoteObject.toCallArgument(object.subtype)]) as CompletionGroup[]) ||
456
- [];
457
- } else if (
458
- object.type === 'string' || object.type === 'number' || object.type === 'boolean' ||
459
- object.type === 'bigint') {
460
- const evaluateResult = await executionContext.evaluate(
461
- {
462
- expression: '(' + getCompletions + ')("' + object.type + '")',
463
- objectGroup: 'completion',
464
- includeCommandLineAPI: false,
465
- silent: true,
466
- returnByValue: true,
467
- generatePreview: false,
468
- allowUnsafeEvalBlockedByCSP: undefined,
469
- disableBreaks: undefined,
470
- replMode: undefined,
471
- throwOnSideEffect: undefined,
472
- timeout: undefined,
473
- },
474
- /* userGesture */ false,
475
- /* awaitPromise */ false);
476
- if (!('error' in evaluateResult) && evaluateResult.object && !evaluateResult.exceptionDetails) {
477
- completions = (evaluateResult.object.value as CompletionGroup[]) || [];
478
- }
479
- }
480
- executionContext.runtimeModel.releaseObjectGroup('completion');
481
-
482
- if (!expressionString) {
483
- const globalNames = await executionContext.globalLexicalScopeNames();
484
- if (globalNames) {
485
- // Merge lexical scope names with first completion group on global object: let a and let b should be in the same group.
486
- if (completions.length) {
487
- completions[0].items = completions[0].items.concat(globalNames);
488
- } else {
489
- completions.push({items: globalNames.sort(), title: i18nString(UIStrings.lexicalScopeVariables)});
490
- }
491
- }
492
- }
493
-
494
- for (const group of completions) {
495
- for (let i = 0; i < group.items.length; i++) {
496
- group.items[i] = group.items[i].replace(/\n/g, '\\n');
497
- }
498
-
499
- group.items.sort(group.items.length < 1000 ? this.itemComparator : undefined);
500
- }
501
-
502
- return completions;
503
-
504
- function getCompletions(this: Object, type?: string): Object {
505
- let object;
506
- if (type === 'string') {
507
- object = new String('');
508
- } else if (type === 'number') {
509
- object = new Number(0);
510
- }
511
- // Object-wrapped BigInts cannot be constructed via `new BigInt`.
512
- else if (type === 'bigint') {
513
- object = Object(BigInt(0));
514
- } else if (type === 'boolean') {
515
- object = new Boolean(false);
516
- } else {
517
- object = this;
518
- }
519
-
520
- const result: CompletionGroup[] = [];
521
- try {
522
- for (let o = object; o; o = Object.getPrototypeOf(o)) {
523
- if ((type === 'array' || type === 'typedarray') && o === object && o.length > 9999) {
524
- continue;
525
- }
526
-
527
- const group = ({items: [], title: undefined, __proto__: null} as CompletionGroup);
528
- try {
529
- if (typeof o === 'object' && Object.prototype.hasOwnProperty.call(o, 'constructor') && o.constructor &&
530
- o.constructor.name) {
531
- group.title = o.constructor.name;
532
- }
533
- } catch (ee) {
534
- // we could break upon cross origin check.
535
- }
536
- result[result.length] = group;
537
- const names = Object.getOwnPropertyNames(o);
538
- const isArray = Array.isArray(o);
539
- for (let i = 0; i < names.length && group.items.length < 10000; ++i) {
540
- // Skip array elements indexes.
541
- if (isArray && /^[0-9]/.test(names[i])) {
542
- continue;
543
- }
544
- group.items[group.items.length] = names[i];
545
- }
546
- }
547
- } catch (e) {
548
- }
549
- return result;
550
- }
551
- }
552
- }
553
-
554
- private receivedPropertyNames(
555
- propertyGroups: CompletionGroup[]|null, dotNotation: boolean, bracketNotation: boolean, expressionString: string,
556
- query: string): UI.SuggestBox.Suggestions {
557
- if (!propertyGroups) {
558
- return [];
559
- }
560
- const includeCommandLineAPI = (!dotNotation && !bracketNotation);
561
- if (includeCommandLineAPI) {
562
- const commandLineAPI = [
563
- 'dir',
564
- 'dirxml',
565
- 'keys',
566
- 'values',
567
- 'profile',
568
- 'profileEnd',
569
- 'monitorEvents',
570
- 'unmonitorEvents',
571
- 'inspect',
572
- 'copy',
573
- 'clear',
574
- 'getEventListeners',
575
- 'debug',
576
- 'undebug',
577
- 'monitor',
578
- 'unmonitor',
579
- 'table',
580
- 'queryObjects',
581
- '$',
582
- '$$',
583
- '$x',
584
- '$0',
585
- '$_',
586
- ];
587
- propertyGroups.push({
588
- items: commandLineAPI,
589
- title: undefined,
590
- });
591
- }
592
- return this.completionsForQuery(dotNotation, bracketNotation, expressionString, query, propertyGroups);
593
- }
594
-
595
- private completionsForQuery(
596
- dotNotation: boolean, bracketNotation: boolean, expressionString: string, query: string,
597
- propertyGroups: CompletionGroup[]): UI.SuggestBox.Suggestions {
598
- const quoteUsed = (bracketNotation && query.startsWith('\'')) ? '\'' : '"';
599
-
600
- if (!expressionString) {
601
- // See ES2017 spec: https://www.ecma-international.org/ecma-262/8.0/index.html
602
- const keywords = [
603
- // Section 11.6.2.1 Reserved keywords.
604
- 'await',
605
- 'break',
606
- 'case',
607
- 'catch',
608
- 'class',
609
- 'const',
610
- 'continue',
611
- 'debugger',
612
- 'default',
613
- 'delete',
614
- 'do',
615
- 'else',
616
- 'exports',
617
- 'extends',
618
- 'finally',
619
- 'for',
620
- 'function',
621
- 'if',
622
- 'import',
623
- 'in',
624
- 'instanceof',
625
- 'new',
626
- 'return',
627
- 'super',
628
- 'switch',
629
- 'this',
630
- 'throw',
631
- 'try',
632
- 'typeof',
633
- 'var',
634
- 'void',
635
- 'while',
636
- 'with',
637
- 'yield',
638
-
639
- // Section 11.6.2.1's note mentions words treated as reserved in certain cases.
640
- 'let',
641
- 'static',
642
-
643
- // Other keywords not explicitly reserved by spec.
644
- 'async',
645
- 'of',
646
- ];
647
- propertyGroups.push({title: i18nString(UIStrings.keywords), items: keywords.sort()});
648
- }
649
-
650
- const allProperties = new Set<string>();
651
- let result: UI.SuggestBox.Suggestion[] = [];
652
- let lastGroupTitle;
653
- const regex = /^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF]*$/;
654
- const lowerCaseQuery = query.toLowerCase();
655
- for (const group of propertyGroups) {
656
- const caseSensitivePrefix: UI.SuggestBox.Suggestions = [];
657
- const caseInsensitivePrefix: UI.SuggestBox.Suggestions = [];
658
- const caseSensitiveAnywhere: UI.SuggestBox.Suggestions = [];
659
- const caseInsensitiveAnywhere: UI.SuggestBox.Suggestions = [];
660
-
661
- for (let i = 0; i < group.items.length; i++) {
662
- let property: string = group.items[i];
663
- // Assume that all non-ASCII characters are letters and thus can be used as part of identifier.
664
- if (!bracketNotation && !regex.test(property)) {
665
- continue;
666
- }
667
-
668
- if (bracketNotation) {
669
- if (!/^[0-9]+$/.test(property)) {
670
- property = quoteUsed + Platform.StringUtilities.escapeCharacters(property, quoteUsed + '\\') + quoteUsed;
671
- }
672
- property += ']';
673
- }
674
- if (allProperties.has(property)) {
675
- continue;
676
- }
677
-
678
- if (property.length < query.length) {
679
- continue;
680
- }
681
- const lowerCaseProperty = property.toLowerCase();
682
- if (query.length && lowerCaseProperty.indexOf(lowerCaseQuery) === -1) {
683
- continue;
684
- }
685
-
686
- allProperties.add(property);
687
- if (property.startsWith(query)) {
688
- caseSensitivePrefix.push(
689
- ({text: property, priority: property === query ? 5 : 4} as UI.SuggestBox.Suggestion));
690
- } else if (lowerCaseProperty.startsWith(lowerCaseQuery)) {
691
- caseInsensitivePrefix.push(({text: property, priority: 3} as UI.SuggestBox.Suggestion));
692
- } else if (property.indexOf(query) !== -1) {
693
- caseSensitiveAnywhere.push(({text: property, priority: 2} as UI.SuggestBox.Suggestion));
694
- } else {
695
- caseInsensitiveAnywhere.push(({text: property, priority: 1} as UI.SuggestBox.Suggestion));
696
- }
697
- }
698
- const structuredGroup =
699
- caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere);
700
- if (structuredGroup.length && group.title !== lastGroupTitle) {
701
- structuredGroup[0].subtitle = group.title;
702
- lastGroupTitle = group.title;
703
- }
704
- result = result.concat(structuredGroup);
705
- result.forEach(item => {
706
- if (item.text.endsWith(']')) {
707
- item.title = item.text.substring(0, item.text.length - 1);
708
- }
709
- });
710
- }
711
- return result;
712
- }
713
-
714
- private itemComparator(a: string, b: string): number {
715
- const aStartsWithUnderscore = a.startsWith('_');
716
- const bStartsWithUnderscore = b.startsWith('_');
717
- if (aStartsWithUnderscore && !bStartsWithUnderscore) {
718
- return 1;
719
- }
720
- if (bStartsWithUnderscore && !aStartsWithUnderscore) {
721
- return -1;
722
- }
723
- return Platform.StringUtilities.naturalOrderComparator(a, b);
724
- }
725
-
726
- static async isExpressionComplete(expression: string): Promise<boolean> {
727
- const currentExecutionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
728
- if (!currentExecutionContext) {
729
- return true;
730
- }
731
- const result =
732
- await currentExecutionContext.runtimeModel.compileScript(expression, '', false, currentExecutionContext.id);
733
- if (!result || !result.exceptionDetails || !result.exceptionDetails.exception) {
734
- return true;
735
- }
736
- const description = result.exceptionDetails.exception.description;
737
- if (description) {
738
- return !description.startsWith('SyntaxError: Unexpected end of input') &&
739
- !description.startsWith('SyntaxError: Unterminated template literal');
740
- }
741
- return false;
742
- }
743
- }
744
-
745
- export class JavaScriptAutocompleteConfig {
746
- private readonly editor: UI.TextEditor.TextEditor;
747
- constructor(editor: UI.TextEditor.TextEditor) {
748
- this.editor = editor;
749
- }
750
-
751
- static createConfigForEditor(editor: UI.TextEditor.TextEditor): UI.SuggestBox.AutocompleteConfig {
752
- const autocomplete = new JavaScriptAutocompleteConfig(editor);
753
- return {
754
- substituteRangeCallback: autocomplete.substituteRange.bind(autocomplete),
755
- suggestionsCallback: autocomplete.suggestionsCallback.bind(autocomplete),
756
- tooltipCallback: autocomplete.tooltipCallback.bind(autocomplete),
757
- anchorBehavior: undefined,
758
- isWordChar: undefined,
759
- };
760
- }
761
-
762
- private substituteRange(lineNumber: number, columnNumber: number): TextUtils.TextRange.TextRange|null {
763
- const token = this.editor.tokenAtTextPosition(lineNumber, columnNumber);
764
- if (token && token.type === 'js-string') {
765
- return new TextUtils.TextRange.TextRange(lineNumber, token.startColumn, lineNumber, columnNumber);
766
- }
767
-
768
- const lineText = this.editor.line(lineNumber);
769
- let index;
770
- for (index = columnNumber - 1; index >= 0; index--) {
771
- if (' =:[({;,!+-*/&|^<>.\t\r\n'.indexOf(lineText.charAt(index)) !== -1) {
772
- break;
773
- }
774
- }
775
- return new TextUtils.TextRange.TextRange(lineNumber, index + 1, lineNumber, columnNumber);
776
- }
777
-
778
- private async suggestionsCallback(
779
- queryRange: TextUtils.TextRange.TextRange, substituteRange: TextUtils.TextRange.TextRange,
780
- force?: boolean): Promise<UI.SuggestBox.Suggestions> {
781
- const query = this.editor.text(queryRange);
782
- const before =
783
- this.editor.text(new TextUtils.TextRange.TextRange(0, 0, queryRange.startLine, queryRange.startColumn));
784
- const token = this.editor.tokenAtTextPosition(substituteRange.startLine, substituteRange.startColumn);
785
- if (token) {
786
- const excludedTokens = new Set<string>(['js-comment', 'js-string-2', 'js-def']);
787
- const trimmedBefore = before.trim();
788
- if (!trimmedBefore.endsWith('[') && !trimmedBefore.match(/\.\s*(get|set|delete)\s*\(\s*$/)) {
789
- excludedTokens.add('js-string');
790
- }
791
- if (!trimmedBefore.endsWith('.')) {
792
- excludedTokens.add('js-property');
793
- }
794
- if (excludedTokens.has(token.type)) {
795
- return [];
796
- }
797
- }
798
- const queryAndAfter = this.editor.line(queryRange.startLine).substring(queryRange.startColumn);
799
-
800
- const words = await JavaScriptAutocomplete.instance().completionsForTextInCurrentContext(before, query, force);
801
- if (!force && queryAndAfter && queryAndAfter !== query &&
802
- words.some(word => queryAndAfter.startsWith(word.text) && query.length !== word.text.length)) {
803
- return [];
804
- }
805
- return words;
806
- }
807
-
808
- private async tooltipCallback(lineNumber: number, columnNumber: number): Promise<Element|null> {
809
- const before = this.editor.text(new TextUtils.TextRange.TextRange(0, 0, lineNumber, columnNumber));
810
- const result = await JavaScriptAutocomplete.instance().argumentsHint(before);
811
- if (!result) {
812
- return null;
813
- }
814
- const argumentIndex = result.argumentIndex;
815
- const tooltip = document.createElement('div');
816
- for (const args of result.args) {
817
- const argumentsElement = document.createElement('span');
818
- for (let i = 0; i < args.length; i++) {
819
- if (i === argumentIndex || (i < argumentIndex && args[i].startsWith('...'))) {
820
- argumentsElement.appendChild(UI.Fragment.html`<b>${args[i]}</b>`);
821
- } else {
822
- UI.UIUtils.createTextChild(argumentsElement, args[i]);
823
- }
824
- if (i < args.length - 1) {
825
- UI.UIUtils.createTextChild(argumentsElement, ', ');
826
- }
827
- }
828
- tooltip.appendChild(UI.Fragment.html`<div class='source-code'>\u0192(${argumentsElement})</div>`);
829
- }
830
- return tooltip;
831
- }
832
- }
833
- export interface CompletionGroup {
834
- title?: string;
835
- items: string[];
836
- }