chrome-devtools-mcp 0.0.2 → 0.2.0

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 (69) hide show
  1. package/README.md +6 -3
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +3 -32
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/i18n/i18n.js +35 -8
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/root/Runtime.js +4 -1
  5. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +4 -4
  6. package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +12 -0
  7. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +366 -0
  8. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +366 -0
  9. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +64 -0
  10. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIQueries.js +105 -0
  11. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CSSWorkspaceBinding.js +243 -0
  12. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +407 -0
  13. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ContentProviderBasedProject.js +128 -0
  14. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerLanguagePlugins.js +992 -0
  15. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +574 -0
  16. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DefaultScriptMapping.js +112 -0
  17. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/FileUtils.js +186 -0
  18. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/LiveLocation.js +60 -0
  19. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/NetworkProject.js +107 -0
  20. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/PresentationConsoleMessageHelper.js +244 -0
  21. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceMapping.js +473 -0
  22. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceScriptMapping.js +399 -0
  23. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceUtils.js +87 -0
  24. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/SASSSourceMapping.js +181 -0
  25. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/StylesSourceMapping.js +268 -0
  26. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/TempFile.js +55 -0
  27. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/bindings.js +20 -0
  28. package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/CrUXManager.js +283 -0
  29. package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/crux-manager.js +4 -0
  30. package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +775 -0
  31. package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +1706 -0
  32. package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/emulation.js +6 -0
  33. package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +131 -0
  34. package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/ScriptFormatter.js +77 -0
  35. package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/formatter.js +6 -0
  36. package/build/node_modules/chrome-devtools-frontend/front_end/models/geometry/GeometryImpl.js +347 -0
  37. package/build/node_modules/chrome-devtools-frontend/front_end/models/geometry/geometry.js +4 -0
  38. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +626 -0
  39. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/ScopeChainModel.js +59 -0
  40. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/ScopeTreeCache.js +32 -0
  41. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/source_map_scopes.js +7 -0
  42. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTrace.js +4 -0
  43. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +67 -0
  44. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +97 -0
  45. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/Trie.js +113 -0
  46. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/stack_trace.js +5 -0
  47. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/stack_trace_impl.js +7 -0
  48. package/build/node_modules/chrome-devtools-frontend/front_end/models/text_utils/TextUtils.js +23 -0
  49. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +1 -1
  50. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Trace.js +1 -1
  51. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +5 -4
  52. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +199 -0
  53. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/trace_source_maps_resolver.js +4 -0
  54. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/FileManager.js +64 -0
  55. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/IgnoreListManager.js +511 -0
  56. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/SearchConfig.js +113 -0
  57. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/UISourceCode.js +563 -0
  58. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/WorkspaceImpl.js +204 -0
  59. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/workspace.js +9 -0
  60. package/build/src/McpContext.js +24 -9
  61. package/build/src/McpResponse.js +3 -3
  62. package/build/src/browser.js +3 -1
  63. package/build/src/index.js +1 -1
  64. package/build/src/tools/input.js +7 -7
  65. package/build/src/tools/performance.js +29 -2
  66. package/build/src/tools/screenshot.js +1 -1
  67. package/build/src/tools/script.js +40 -14
  68. package/build/src/trace-processing/parse.js +26 -22
  69. package/package.json +9 -7
@@ -0,0 +1,64 @@
1
+ // Copyright 2012 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+ import * as Common from '../../core/common/common.js';
5
+ import * as Host from '../../core/host/host.js';
6
+ let fileManagerInstance;
7
+ export class FileManager extends Common.ObjectWrapper.ObjectWrapper {
8
+ #saveCallbacks = new Map();
9
+ constructor() {
10
+ super();
11
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(Host.InspectorFrontendHostAPI.Events.SavedURL, this.savedURL, this);
12
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(Host.InspectorFrontendHostAPI.Events.CanceledSaveURL, this.#canceledSavedURL, this);
13
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(Host.InspectorFrontendHostAPI.Events.AppendedToURL, this.appendedToURL, this);
14
+ }
15
+ static instance(opts = { forceNew: null }) {
16
+ const { forceNew } = opts;
17
+ if (!fileManagerInstance || forceNew) {
18
+ fileManagerInstance = new FileManager();
19
+ }
20
+ return fileManagerInstance;
21
+ }
22
+ /**
23
+ * {@link FileManager.close | close} *must* be called, for the InspectorFrontendHostStub case, to complete the saving.
24
+ * @param url The url of the file to save. **NOTE:** The backend truncates this filename to 64 characters.
25
+ */
26
+ save(url, contentData, forceSaveAs) {
27
+ // Remove this url from the saved URLs while it is being saved.
28
+ const result = new Promise(resolve => this.#saveCallbacks.set(url, resolve));
29
+ const { isTextContent } = contentData;
30
+ const content = isTextContent ? contentData.text : contentData.base64;
31
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.save(url, content, forceSaveAs, !isTextContent);
32
+ return result;
33
+ }
34
+ /**
35
+ * Used in web tests
36
+ */
37
+ savedURL(event) {
38
+ const { url, fileSystemPath } = event.data;
39
+ const callback = this.#saveCallbacks.get(url);
40
+ this.#saveCallbacks.delete(url);
41
+ if (callback) {
42
+ callback({ fileSystemPath });
43
+ }
44
+ }
45
+ #canceledSavedURL({ data: url }) {
46
+ const callback = this.#saveCallbacks.get(url);
47
+ this.#saveCallbacks.delete(url);
48
+ if (callback) {
49
+ callback(null);
50
+ }
51
+ }
52
+ append(url, content) {
53
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.append(url, content);
54
+ }
55
+ close(url) {
56
+ Host.InspectorFrontendHost.InspectorFrontendHostInstance.close(url);
57
+ }
58
+ /**
59
+ * Used in web tests
60
+ */
61
+ appendedToURL({ data: url }) {
62
+ this.dispatchEventToListeners("AppendedToURL" /* Events.APPENDED_TO_URL */, url);
63
+ }
64
+ }
@@ -0,0 +1,511 @@
1
+ // Copyright 2014 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+ import * as Common from '../../core/common/common.js';
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 { projectTypes } from './WorkspaceImpl.js';
9
+ const UIStrings = {
10
+ /**
11
+ * @description Text to stop preventing the debugger from stepping into library code
12
+ */
13
+ removeFromIgnoreList: 'Remove from ignore list',
14
+ /**
15
+ * @description Text for scripts that should not be stepped into when debugging
16
+ */
17
+ addScriptToIgnoreList: 'Add script to ignore list',
18
+ /**
19
+ * @description Text for directories whose scripts should not be stepped into when debugging
20
+ */
21
+ addDirectoryToIgnoreList: 'Add directory to ignore list',
22
+ /**
23
+ * @description A context menu item in the Call Stack Sidebar Pane of the Sources panel
24
+ */
25
+ addAllContentScriptsToIgnoreList: 'Add all extension scripts to ignore list',
26
+ /**
27
+ * @description A context menu item in the Call Stack Sidebar Pane of the Sources panel
28
+ */
29
+ addAllThirdPartyScriptsToIgnoreList: 'Add all third-party scripts to ignore list',
30
+ /**
31
+ * @description A context menu item in the Call Stack Sidebar Pane of the Sources panel
32
+ */
33
+ addAllAnonymousScriptsToIgnoreList: 'Add all anonymous scripts to ignore list',
34
+ };
35
+ const str_ = i18n.i18n.registerUIStrings('models/workspace/IgnoreListManager.ts', UIStrings);
36
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
37
+ let ignoreListManagerInstance;
38
+ export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper {
39
+ #listeners;
40
+ #isIgnoreListedURLCache;
41
+ #contentScriptExecutionContexts;
42
+ constructor() {
43
+ super();
44
+ SDK.TargetManager.TargetManager.instance().addModelListener(SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, this.clearCacheIfNeeded.bind(this), this);
45
+ SDK.TargetManager.TargetManager.instance().addModelListener(SDK.RuntimeModel.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextCreated, this.onExecutionContextCreated, this, { scoped: true });
46
+ SDK.TargetManager.TargetManager.instance().addModelListener(SDK.RuntimeModel.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextDestroyed, this.onExecutionContextDestroyed, this, { scoped: true });
47
+ Common.Settings.Settings.instance()
48
+ .moduleSetting('skip-stack-frames-pattern')
49
+ .addChangeListener(this.patternChanged.bind(this));
50
+ Common.Settings.Settings.instance()
51
+ .moduleSetting('skip-content-scripts')
52
+ .addChangeListener(this.patternChanged.bind(this));
53
+ Common.Settings.Settings.instance()
54
+ .moduleSetting('automatically-ignore-list-known-third-party-scripts')
55
+ .addChangeListener(this.patternChanged.bind(this));
56
+ Common.Settings.Settings.instance()
57
+ .moduleSetting('enable-ignore-listing')
58
+ .addChangeListener(this.patternChanged.bind(this));
59
+ Common.Settings.Settings.instance()
60
+ .moduleSetting('skip-anonymous-scripts')
61
+ .addChangeListener(this.patternChanged.bind(this));
62
+ this.#listeners = new Set();
63
+ this.#isIgnoreListedURLCache = new Map();
64
+ this.#contentScriptExecutionContexts = new Set();
65
+ SDK.TargetManager.TargetManager.instance().observeModels(SDK.DebuggerModel.DebuggerModel, this);
66
+ }
67
+ static instance(opts = { forceNew: null }) {
68
+ const { forceNew } = opts;
69
+ if (!ignoreListManagerInstance || forceNew) {
70
+ ignoreListManagerInstance = new IgnoreListManager();
71
+ }
72
+ return ignoreListManagerInstance;
73
+ }
74
+ static removeInstance() {
75
+ ignoreListManagerInstance = undefined;
76
+ }
77
+ addChangeListener(listener) {
78
+ this.#listeners.add(listener);
79
+ }
80
+ removeChangeListener(listener) {
81
+ this.#listeners.delete(listener);
82
+ }
83
+ modelAdded(debuggerModel) {
84
+ void this.setIgnoreListPatterns(debuggerModel);
85
+ const sourceMapManager = debuggerModel.sourceMapManager();
86
+ sourceMapManager.addEventListener(SDK.SourceMapManager.Events.SourceMapAttached, this.sourceMapAttached, this);
87
+ sourceMapManager.addEventListener(SDK.SourceMapManager.Events.SourceMapDetached, this.sourceMapDetached, this);
88
+ }
89
+ modelRemoved(debuggerModel) {
90
+ this.clearCacheIfNeeded();
91
+ const sourceMapManager = debuggerModel.sourceMapManager();
92
+ sourceMapManager.removeEventListener(SDK.SourceMapManager.Events.SourceMapAttached, this.sourceMapAttached, this);
93
+ sourceMapManager.removeEventListener(SDK.SourceMapManager.Events.SourceMapDetached, this.sourceMapDetached, this);
94
+ }
95
+ isContentScript(executionContext) {
96
+ return !executionContext.isDefault;
97
+ }
98
+ onExecutionContextCreated(event) {
99
+ if (this.isContentScript(event.data)) {
100
+ this.#contentScriptExecutionContexts.add(event.data.uniqueId);
101
+ if (this.skipContentScripts) {
102
+ for (const debuggerModel of SDK.TargetManager.TargetManager.instance().models(SDK.DebuggerModel.DebuggerModel)) {
103
+ void this.updateIgnoredExecutionContexts(debuggerModel);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ onExecutionContextDestroyed(event) {
109
+ if (this.isContentScript(event.data)) {
110
+ this.#contentScriptExecutionContexts.delete(event.data.uniqueId);
111
+ if (this.skipContentScripts) {
112
+ for (const debuggerModel of SDK.TargetManager.TargetManager.instance().models(SDK.DebuggerModel.DebuggerModel)) {
113
+ void this.updateIgnoredExecutionContexts(debuggerModel);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ clearCacheIfNeeded() {
119
+ if (this.#isIgnoreListedURLCache.size > 1024) {
120
+ this.#isIgnoreListedURLCache.clear();
121
+ }
122
+ }
123
+ getSkipStackFramesPatternSetting() {
124
+ return Common.Settings.Settings.instance().moduleSetting('skip-stack-frames-pattern');
125
+ }
126
+ setIgnoreListPatterns(debuggerModel) {
127
+ const regexPatterns = this.enableIgnoreListing ? this.getSkipStackFramesPatternSetting().getAsArray() : [];
128
+ const patterns = [];
129
+ for (const item of regexPatterns) {
130
+ if (!item.disabled && item.pattern) {
131
+ patterns.push(item.pattern);
132
+ }
133
+ }
134
+ return debuggerModel.setBlackboxPatterns(patterns, this.skipAnonymousScripts);
135
+ }
136
+ updateIgnoredExecutionContexts(debuggerModel) {
137
+ return debuggerModel.setBlackboxExecutionContexts(this.skipContentScripts ? Array.from(this.#contentScriptExecutionContexts) : []);
138
+ }
139
+ getGeneralRulesForUISourceCode(uiSourceCode) {
140
+ const projectType = uiSourceCode.project().type();
141
+ const isContentScript = projectType === projectTypes.ContentScripts;
142
+ const isKnownThirdParty = uiSourceCode.isKnownThirdParty();
143
+ return { isContentScript, isKnownThirdParty };
144
+ }
145
+ isUserOrSourceMapIgnoreListedUISourceCode(uiSourceCode) {
146
+ if (uiSourceCode.isUnconditionallyIgnoreListed()) {
147
+ return true;
148
+ }
149
+ const url = this.uiSourceCodeURL(uiSourceCode);
150
+ return this.isUserIgnoreListedURL(url, this.getGeneralRulesForUISourceCode(uiSourceCode));
151
+ }
152
+ isUserIgnoreListedURL(url, options) {
153
+ if (!this.enableIgnoreListing) {
154
+ return false;
155
+ }
156
+ if (options?.isContentScript && this.skipContentScripts) {
157
+ return true;
158
+ }
159
+ if (options?.isKnownThirdParty && this.automaticallyIgnoreListKnownThirdPartyScripts) {
160
+ return true;
161
+ }
162
+ if (!url) {
163
+ return this.skipAnonymousScripts;
164
+ }
165
+ if (this.#isIgnoreListedURLCache.has(url)) {
166
+ return Boolean(this.#isIgnoreListedURLCache.get(url));
167
+ }
168
+ const isIgnoreListed = this.getFirstMatchedRegex(url) !== null;
169
+ this.#isIgnoreListedURLCache.set(url, isIgnoreListed);
170
+ return isIgnoreListed;
171
+ }
172
+ getFirstMatchedRegex(url) {
173
+ if (!url) {
174
+ return null;
175
+ }
176
+ const regexPatterns = this.getSkipStackFramesPatternSetting().getAsArray();
177
+ const regexValue = this.urlToRegExpString(url);
178
+ if (!regexValue) {
179
+ return null;
180
+ }
181
+ for (let i = 0; i < regexPatterns.length; ++i) {
182
+ const item = regexPatterns[i];
183
+ if (item.disabled || item.disabledForUrl === url) {
184
+ continue;
185
+ }
186
+ const regex = new RegExp(item.pattern);
187
+ if (regex.test(url)) {
188
+ return regex;
189
+ }
190
+ }
191
+ return null;
192
+ }
193
+ sourceMapAttached(event) {
194
+ const script = event.data.client;
195
+ const sourceMap = event.data.sourceMap;
196
+ void this.updateScriptRanges(script, sourceMap);
197
+ }
198
+ sourceMapDetached(event) {
199
+ const script = event.data.client;
200
+ void this.updateScriptRanges(script, undefined);
201
+ }
202
+ async updateScriptRanges(script, sourceMap) {
203
+ let hasIgnoreListedMappings = false;
204
+ if (!IgnoreListManager.instance().isUserIgnoreListedURL(script.sourceURL, { isContentScript: script.isContentScript() })) {
205
+ hasIgnoreListedMappings =
206
+ sourceMap?.sourceURLs().some(url => this.isUserIgnoreListedURL(url, { isKnownThirdParty: sourceMap.hasIgnoreListHint(url) })) ??
207
+ false;
208
+ }
209
+ if (!hasIgnoreListedMappings) {
210
+ if (scriptToRange.get(script) && await script.setBlackboxedRanges([])) {
211
+ scriptToRange.delete(script);
212
+ }
213
+ this.dispatchEventToListeners("IGNORED_SCRIPT_RANGES_UPDATED" /* Events.IGNORED_SCRIPT_RANGES_UPDATED */, script);
214
+ return;
215
+ }
216
+ if (!sourceMap) {
217
+ return;
218
+ }
219
+ const newRanges = sourceMap
220
+ .findRanges(srcURL => this.isUserIgnoreListedURL(srcURL, { isKnownThirdParty: sourceMap.hasIgnoreListHint(srcURL) }), { isStartMatching: true })
221
+ .flatMap(range => [range.start, range.end]);
222
+ const oldRanges = scriptToRange.get(script) || [];
223
+ if (!isEqual(oldRanges, newRanges) && await script.setBlackboxedRanges(newRanges)) {
224
+ scriptToRange.set(script, newRanges);
225
+ }
226
+ this.dispatchEventToListeners("IGNORED_SCRIPT_RANGES_UPDATED" /* Events.IGNORED_SCRIPT_RANGES_UPDATED */, script);
227
+ function isEqual(rangesA, rangesB) {
228
+ if (rangesA.length !== rangesB.length) {
229
+ return false;
230
+ }
231
+ for (let i = 0; i < rangesA.length; ++i) {
232
+ if (rangesA[i].lineNumber !== rangesB[i].lineNumber || rangesA[i].columnNumber !== rangesB[i].columnNumber) {
233
+ return false;
234
+ }
235
+ }
236
+ return true;
237
+ }
238
+ }
239
+ uiSourceCodeURL(uiSourceCode) {
240
+ return uiSourceCode.project().type() === projectTypes.Debugger ? null : uiSourceCode.url();
241
+ }
242
+ canIgnoreListUISourceCode(uiSourceCode) {
243
+ const url = this.uiSourceCodeURL(uiSourceCode);
244
+ return url ? Boolean(this.urlToRegExpString(url)) : false;
245
+ }
246
+ ignoreListUISourceCode(uiSourceCode) {
247
+ const url = this.uiSourceCodeURL(uiSourceCode);
248
+ if (url) {
249
+ this.ignoreListURL(url);
250
+ }
251
+ }
252
+ unIgnoreListUISourceCode(uiSourceCode) {
253
+ this.unIgnoreListURL(this.uiSourceCodeURL(uiSourceCode), this.getGeneralRulesForUISourceCode(uiSourceCode));
254
+ }
255
+ get enableIgnoreListing() {
256
+ return Common.Settings.Settings.instance().moduleSetting('enable-ignore-listing').get();
257
+ }
258
+ set enableIgnoreListing(value) {
259
+ Common.Settings.Settings.instance().moduleSetting('enable-ignore-listing').set(value);
260
+ }
261
+ get skipContentScripts() {
262
+ return this.enableIgnoreListing && Common.Settings.Settings.instance().moduleSetting('skip-content-scripts').get();
263
+ }
264
+ get skipAnonymousScripts() {
265
+ return this.enableIgnoreListing &&
266
+ Common.Settings.Settings.instance().moduleSetting('skip-anonymous-scripts').get();
267
+ }
268
+ get automaticallyIgnoreListKnownThirdPartyScripts() {
269
+ return this.enableIgnoreListing &&
270
+ Common.Settings.Settings.instance().moduleSetting('automatically-ignore-list-known-third-party-scripts').get();
271
+ }
272
+ ignoreListContentScripts() {
273
+ if (!this.enableIgnoreListing) {
274
+ this.enableIgnoreListing = true;
275
+ }
276
+ Common.Settings.Settings.instance().moduleSetting('skip-content-scripts').set(true);
277
+ }
278
+ unIgnoreListContentScripts() {
279
+ Common.Settings.Settings.instance().moduleSetting('skip-content-scripts').set(false);
280
+ }
281
+ ignoreListAnonymousScripts() {
282
+ if (!this.enableIgnoreListing) {
283
+ this.enableIgnoreListing = true;
284
+ }
285
+ Common.Settings.Settings.instance().moduleSetting('skip-anonymous-scripts').set(true);
286
+ }
287
+ unIgnoreListAnonymousScripts() {
288
+ Common.Settings.Settings.instance().moduleSetting('skip-anonymous-scripts').set(false);
289
+ }
290
+ ignoreListThirdParty() {
291
+ if (!this.enableIgnoreListing) {
292
+ this.enableIgnoreListing = true;
293
+ }
294
+ Common.Settings.Settings.instance().moduleSetting('automatically-ignore-list-known-third-party-scripts').set(true);
295
+ }
296
+ unIgnoreListThirdParty() {
297
+ Common.Settings.Settings.instance().moduleSetting('automatically-ignore-list-known-third-party-scripts').set(false);
298
+ }
299
+ ignoreListURL(url) {
300
+ const regexValue = this.urlToRegExpString(url);
301
+ if (!regexValue) {
302
+ return;
303
+ }
304
+ this.addRegexToIgnoreList(regexValue, url);
305
+ }
306
+ addRegexToIgnoreList(regexValue, disabledForUrl) {
307
+ const regexPatterns = this.getSkipStackFramesPatternSetting().getAsArray();
308
+ let found = false;
309
+ for (let i = 0; i < regexPatterns.length; ++i) {
310
+ const item = regexPatterns[i];
311
+ if (item.pattern === regexValue || (disabledForUrl && item.disabledForUrl === disabledForUrl)) {
312
+ item.disabled = false;
313
+ item.disabledForUrl = undefined;
314
+ found = true;
315
+ }
316
+ }
317
+ if (!found) {
318
+ regexPatterns.push({ pattern: regexValue, disabled: false });
319
+ }
320
+ if (!this.enableIgnoreListing) {
321
+ this.enableIgnoreListing = true;
322
+ }
323
+ this.getSkipStackFramesPatternSetting().setAsArray(regexPatterns);
324
+ }
325
+ unIgnoreListURL(url, options) {
326
+ if (options?.isContentScript) {
327
+ this.unIgnoreListContentScripts();
328
+ }
329
+ if (options?.isKnownThirdParty) {
330
+ this.unIgnoreListThirdParty();
331
+ }
332
+ if (!url) {
333
+ this.unIgnoreListAnonymousScripts();
334
+ return;
335
+ }
336
+ let regexPatterns = this.getSkipStackFramesPatternSetting().getAsArray();
337
+ const regexValue = IgnoreListManager.instance().urlToRegExpString(url);
338
+ if (!regexValue) {
339
+ return;
340
+ }
341
+ regexPatterns = regexPatterns.filter(function (item) {
342
+ return item.pattern !== regexValue;
343
+ });
344
+ for (let i = 0; i < regexPatterns.length; ++i) {
345
+ const item = regexPatterns[i];
346
+ if (item.disabled) {
347
+ continue;
348
+ }
349
+ try {
350
+ const regex = new RegExp(item.pattern);
351
+ if (regex.test(url)) {
352
+ item.disabled = true;
353
+ item.disabledForUrl = url;
354
+ }
355
+ }
356
+ catch {
357
+ }
358
+ }
359
+ this.getSkipStackFramesPatternSetting().setAsArray(regexPatterns);
360
+ }
361
+ removeIgnoreListPattern(regexValue) {
362
+ let regexPatterns = this.getSkipStackFramesPatternSetting().getAsArray();
363
+ regexPatterns = regexPatterns.filter(function (item) {
364
+ return item.pattern !== regexValue;
365
+ });
366
+ this.getSkipStackFramesPatternSetting().setAsArray(regexPatterns);
367
+ }
368
+ ignoreListHasPattern(regexValue, enabledOnly) {
369
+ const regexPatterns = this.getSkipStackFramesPatternSetting().getAsArray();
370
+ return regexPatterns.some(item => !(enabledOnly && item.disabled) && item.pattern === regexValue);
371
+ }
372
+ async patternChanged() {
373
+ this.#isIgnoreListedURLCache.clear();
374
+ const promises = [];
375
+ for (const debuggerModel of SDK.TargetManager.TargetManager.instance().models(SDK.DebuggerModel.DebuggerModel)) {
376
+ promises.push(this.setIgnoreListPatterns(debuggerModel));
377
+ const sourceMapManager = debuggerModel.sourceMapManager();
378
+ for (const script of debuggerModel.scripts()) {
379
+ promises.push(this.updateScriptRanges(script, sourceMapManager.sourceMapForClient(script)));
380
+ }
381
+ promises.push(this.updateIgnoredExecutionContexts(debuggerModel));
382
+ }
383
+ await Promise.all(promises);
384
+ const listeners = Array.from(this.#listeners);
385
+ for (const listener of listeners) {
386
+ listener();
387
+ }
388
+ this.patternChangeFinishedForTests();
389
+ }
390
+ patternChangeFinishedForTests() {
391
+ // This method is sniffed in tests.
392
+ }
393
+ urlToRegExpString(url) {
394
+ const parsedURL = new Common.ParsedURL.ParsedURL(url);
395
+ if (parsedURL.isAboutBlank() || parsedURL.isDataURL()) {
396
+ return '';
397
+ }
398
+ if (!parsedURL.isValid) {
399
+ return '^' + Platform.StringUtilities.escapeForRegExp(url) + '$';
400
+ }
401
+ let name = parsedURL.lastPathComponent;
402
+ if (name) {
403
+ name = '/' + name;
404
+ }
405
+ else if (parsedURL.folderPathComponents) {
406
+ name = parsedURL.folderPathComponents + '/';
407
+ }
408
+ if (!name) {
409
+ name = parsedURL.host;
410
+ }
411
+ if (!name) {
412
+ return '';
413
+ }
414
+ const scheme = parsedURL.scheme;
415
+ let prefix = '';
416
+ if (scheme && scheme !== 'http' && scheme !== 'https') {
417
+ prefix = '^' + scheme + '://';
418
+ if (scheme === 'chrome-extension') {
419
+ prefix += parsedURL.host + '\\b';
420
+ }
421
+ prefix += '.*';
422
+ }
423
+ return prefix + Platform.StringUtilities.escapeForRegExp(name) + (url.endsWith(name) ? '$' : '\\b');
424
+ }
425
+ getIgnoreListURLContextMenuItems(uiSourceCode) {
426
+ if (uiSourceCode.project().type() === projectTypes.FileSystem) {
427
+ return [];
428
+ }
429
+ const menuItems = [];
430
+ const canIgnoreList = this.canIgnoreListUISourceCode(uiSourceCode);
431
+ const isIgnoreListed = this.isUserOrSourceMapIgnoreListedUISourceCode(uiSourceCode);
432
+ const isAnonymous = !this.uiSourceCodeURL(uiSourceCode);
433
+ const { isContentScript, isKnownThirdParty } = this.getGeneralRulesForUISourceCode(uiSourceCode);
434
+ if (isIgnoreListed) {
435
+ if (canIgnoreList || isContentScript || isKnownThirdParty || isAnonymous) {
436
+ menuItems.push({
437
+ text: i18nString(UIStrings.removeFromIgnoreList),
438
+ callback: this.unIgnoreListUISourceCode.bind(this, uiSourceCode),
439
+ jslogContext: 'remove-script-from-ignorelist',
440
+ });
441
+ }
442
+ }
443
+ else {
444
+ if (canIgnoreList) {
445
+ menuItems.push({
446
+ text: i18nString(UIStrings.addScriptToIgnoreList),
447
+ callback: this.ignoreListUISourceCode.bind(this, uiSourceCode),
448
+ jslogContext: 'add-script-to-ignorelist',
449
+ });
450
+ }
451
+ else if (isAnonymous) {
452
+ menuItems.push({
453
+ text: i18nString(UIStrings.addAllAnonymousScriptsToIgnoreList),
454
+ callback: this.ignoreListAnonymousScripts.bind(this),
455
+ jslogContext: 'add-anonymous-scripts-to-ignorelist',
456
+ });
457
+ }
458
+ menuItems.push(...this.getIgnoreListGeneralContextMenuItems({ isContentScript, isKnownThirdParty }));
459
+ }
460
+ return menuItems;
461
+ }
462
+ getIgnoreListGeneralContextMenuItems(options) {
463
+ const menuItems = [];
464
+ if (options?.isContentScript) {
465
+ menuItems.push({
466
+ text: i18nString(UIStrings.addAllContentScriptsToIgnoreList),
467
+ callback: this.ignoreListContentScripts.bind(this),
468
+ jslogContext: 'add-content-scripts-to-ignorelist',
469
+ });
470
+ }
471
+ if (options?.isKnownThirdParty) {
472
+ menuItems.push({
473
+ text: i18nString(UIStrings.addAllThirdPartyScriptsToIgnoreList),
474
+ callback: this.ignoreListThirdParty.bind(this),
475
+ jslogContext: 'add-3p-scripts-to-ignorelist',
476
+ });
477
+ }
478
+ return menuItems;
479
+ }
480
+ getIgnoreListFolderContextMenuItems(url, options) {
481
+ const menuItems = [];
482
+ const regexValue = '^' + Platform.StringUtilities.escapeForRegExp(url) + '/';
483
+ if (this.ignoreListHasPattern(regexValue, true)) {
484
+ menuItems.push({
485
+ text: i18nString(UIStrings.removeFromIgnoreList),
486
+ callback: this.removeIgnoreListPattern.bind(this, regexValue),
487
+ jslogContext: 'remove-from-ignore-list',
488
+ });
489
+ }
490
+ else if (this.isUserIgnoreListedURL(url, options)) {
491
+ // This specific url isn't on the ignore list, but there are rules that match it.
492
+ menuItems.push({
493
+ text: i18nString(UIStrings.removeFromIgnoreList),
494
+ callback: this.unIgnoreListURL.bind(this, url, options),
495
+ jslogContext: 'remove-from-ignore-list',
496
+ });
497
+ }
498
+ else if (!options?.isCurrentlyIgnoreListed) {
499
+ // Provide options to add to ignore list, unless folder currently displays
500
+ // as entirely ignored.
501
+ menuItems.push({
502
+ text: i18nString(UIStrings.addDirectoryToIgnoreList),
503
+ callback: this.addRegexToIgnoreList.bind(this, regexValue),
504
+ jslogContext: 'add-directory-to-ignore-list',
505
+ });
506
+ menuItems.push(...this.getIgnoreListGeneralContextMenuItems(options));
507
+ }
508
+ return menuItems;
509
+ }
510
+ }
511
+ const scriptToRange = new WeakMap();
@@ -0,0 +1,113 @@
1
+ // Copyright 2023 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+ import * as Platform from '../../core/platform/platform.js';
5
+ export class SearchConfig {
6
+ #query;
7
+ #ignoreCase;
8
+ #isRegex;
9
+ #queries;
10
+ #fileRegexQueries;
11
+ constructor(query, ignoreCase, isRegex) {
12
+ this.#query = query;
13
+ this.#ignoreCase = ignoreCase;
14
+ this.#isRegex = isRegex;
15
+ const { queries, fileRegexQueries } = SearchConfig.#parse(query, ignoreCase, isRegex);
16
+ this.#queries = queries;
17
+ this.#fileRegexQueries = fileRegexQueries;
18
+ }
19
+ static fromPlainObject(object) {
20
+ return new SearchConfig(object.query, object.ignoreCase, object.isRegex);
21
+ }
22
+ filePathMatchesFileQuery(filePath) {
23
+ return this.#fileRegexQueries.every(({ regex, shouldMatch }) => (Boolean(filePath.match(regex)) === shouldMatch));
24
+ }
25
+ queries() {
26
+ return this.#queries;
27
+ }
28
+ query() {
29
+ return this.#query;
30
+ }
31
+ ignoreCase() {
32
+ return this.#ignoreCase;
33
+ }
34
+ isRegex() {
35
+ return this.#isRegex;
36
+ }
37
+ toPlainObject() {
38
+ return { query: this.query(), ignoreCase: this.ignoreCase(), isRegex: this.isRegex() };
39
+ }
40
+ static #parse(query, ignoreCase, isRegex) {
41
+ // Inside double quotes: any symbol except double quote and backslash or any symbol escaped with a backslash.
42
+ const quotedPattern = /"([^\\"]|\\.)+"/;
43
+ // A word is a sequence of any symbols except space and backslash or any symbols escaped with a backslash, that does not start with file:.
44
+ const unquotedWordPattern = /(\s*(?!-?f(ile)?:)[^\\ ]|\\.)+/;
45
+ const unquotedPattern = unquotedWordPattern.source + '(\\s+' + unquotedWordPattern.source + ')*';
46
+ const pattern = [
47
+ '(\\s*' + FilePatternRegex.source + '\\s*)',
48
+ '(' + quotedPattern.source + ')',
49
+ '(' + unquotedPattern + ')',
50
+ ].join('|');
51
+ const regexp = new RegExp(pattern, 'g');
52
+ const queryParts = query.match(regexp) || [];
53
+ const queries = [];
54
+ const fileRegexQueries = [];
55
+ for (const queryPart of queryParts) {
56
+ if (!queryPart) {
57
+ continue;
58
+ }
59
+ const fileQuery = SearchConfig.#parseFileQuery(queryPart);
60
+ if (fileQuery) {
61
+ const regex = new RegExp(fileQuery.text, ignoreCase ? 'i' : '');
62
+ fileRegexQueries.push({ regex, shouldMatch: fileQuery.shouldMatch });
63
+ }
64
+ else if (isRegex) {
65
+ queries.push(queryPart);
66
+ }
67
+ else if (queryPart.startsWith('"') && queryPart.endsWith('"')) {
68
+ queries.push(SearchConfig.#parseQuotedQuery(queryPart));
69
+ }
70
+ else {
71
+ queries.push(SearchConfig.#parseUnquotedQuery(queryPart));
72
+ }
73
+ }
74
+ return { queries, fileRegexQueries };
75
+ }
76
+ static #parseUnquotedQuery(query) {
77
+ return query.replace(/\\(.)/g, '$1');
78
+ }
79
+ static #parseQuotedQuery(query) {
80
+ return query.substring(1, query.length - 1).replace(/\\(.)/g, '$1');
81
+ }
82
+ static #parseFileQuery(query) {
83
+ const match = query.match(FilePatternRegex);
84
+ if (!match) {
85
+ return null;
86
+ }
87
+ query = match[3];
88
+ let result = '';
89
+ for (let i = 0; i < query.length; ++i) {
90
+ const char = query[i];
91
+ if (char === '*') {
92
+ result += '.*';
93
+ }
94
+ else if (char === '\\') {
95
+ ++i;
96
+ const nextChar = query[i];
97
+ if (nextChar === ' ') {
98
+ result += ' ';
99
+ }
100
+ }
101
+ else {
102
+ if (Platform.StringUtilities.regexSpecialCharacters().indexOf(query.charAt(i)) !== -1) {
103
+ result += '\\';
104
+ }
105
+ result += query.charAt(i);
106
+ }
107
+ }
108
+ const shouldMatch = !Boolean(match[1]);
109
+ return { text: result, shouldMatch };
110
+ }
111
+ }
112
+ // After file: prefix: any symbol except space and backslash or any symbol escaped with a backslash.
113
+ const FilePatternRegex = /(-)?f(ile)?:((?:[^\\ ]|\\.)+)/;