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,399 @@
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 i18n from '../../core/i18n/i18n.js';
6
+ import * as Platform from '../../core/platform/platform.js';
7
+ import * as Root from '../../core/root/root.js';
8
+ import * as SDK from '../../core/sdk/sdk.js';
9
+ import * as TextUtils from '../text_utils/text_utils.js';
10
+ import * as Workspace from '../workspace/workspace.js';
11
+ import { ContentProviderBasedProject } from './ContentProviderBasedProject.js';
12
+ import { DebuggerWorkspaceBinding } from './DebuggerWorkspaceBinding.js';
13
+ import { NetworkProject } from './NetworkProject.js';
14
+ import { metadataForURL } from './ResourceUtils.js';
15
+ const UIStrings = {
16
+ /**
17
+ * @description Error text displayed in the console when editing a live script fails. LiveEdit is
18
+ *the name of the feature for editing code that is already running.
19
+ * @example {warning} PH1
20
+ */
21
+ liveEditFailed: '`LiveEdit` failed: {PH1}',
22
+ /**
23
+ * @description Error text displayed in the console when compiling a live-edited script fails. LiveEdit is
24
+ *the name of the feature for editing code that is already running.
25
+ * @example {connection lost} PH1
26
+ */
27
+ liveEditCompileFailed: '`LiveEdit` compile failed: {PH1}',
28
+ };
29
+ const str_ = i18n.i18n.registerUIStrings('models/bindings/ResourceScriptMapping.ts', UIStrings);
30
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
31
+ export class ResourceScriptMapping {
32
+ debuggerModel;
33
+ #workspace;
34
+ debuggerWorkspaceBinding;
35
+ #uiSourceCodeToScriptFile;
36
+ #projects;
37
+ #scriptToUISourceCode;
38
+ #eventListeners;
39
+ constructor(debuggerModel, workspace, debuggerWorkspaceBinding) {
40
+ this.debuggerModel = debuggerModel;
41
+ this.#workspace = workspace;
42
+ this.debuggerWorkspaceBinding = debuggerWorkspaceBinding;
43
+ this.#uiSourceCodeToScriptFile = new Map();
44
+ this.#projects = new Map();
45
+ this.#scriptToUISourceCode = new Map();
46
+ const runtimeModel = debuggerModel.runtimeModel();
47
+ this.#eventListeners = [
48
+ this.debuggerModel.addEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, event => this.addScript(event.data), this),
49
+ this.debuggerModel.addEventListener(SDK.DebuggerModel.Events.GlobalObjectCleared, this.globalObjectCleared, this),
50
+ runtimeModel.addEventListener(SDK.RuntimeModel.Events.ExecutionContextDestroyed, this.executionContextDestroyed, this),
51
+ runtimeModel.target().targetManager().addEventListener("InspectedURLChanged" /* SDK.TargetManager.Events.INSPECTED_URL_CHANGED */, this.inspectedURLChanged, this),
52
+ ];
53
+ }
54
+ project(script) {
55
+ const prefix = script.isContentScript() ? 'js:extensions:' : 'js::';
56
+ const projectId = prefix + this.debuggerModel.target().id() + ':' + script.frameId;
57
+ let project = this.#projects.get(projectId);
58
+ if (!project) {
59
+ const projectType = script.isContentScript() ? Workspace.Workspace.projectTypes.ContentScripts :
60
+ Workspace.Workspace.projectTypes.Network;
61
+ project = new ContentProviderBasedProject(this.#workspace, projectId, projectType, '' /* displayName */, false /* isServiceProject */);
62
+ NetworkProject.setTargetForProject(project, this.debuggerModel.target());
63
+ this.#projects.set(projectId, project);
64
+ }
65
+ return project;
66
+ }
67
+ uiSourceCodeForScript(script) {
68
+ return this.#scriptToUISourceCode.get(script) ?? null;
69
+ }
70
+ rawLocationToUILocation(rawLocation) {
71
+ const script = rawLocation.script();
72
+ if (!script) {
73
+ return null;
74
+ }
75
+ const uiSourceCode = this.#scriptToUISourceCode.get(script);
76
+ if (!uiSourceCode) {
77
+ return null;
78
+ }
79
+ const scriptFile = this.#uiSourceCodeToScriptFile.get(uiSourceCode);
80
+ if (!scriptFile) {
81
+ return null;
82
+ }
83
+ if ((scriptFile.hasDivergedFromVM() && !scriptFile.isMergingToVM()) || scriptFile.isDivergingFromVM()) {
84
+ return null;
85
+ }
86
+ if (scriptFile.script !== script) {
87
+ return null;
88
+ }
89
+ const { lineNumber, columnNumber = 0 } = rawLocation;
90
+ return uiSourceCode.uiLocation(lineNumber, columnNumber);
91
+ }
92
+ uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber) {
93
+ const scriptFile = this.#uiSourceCodeToScriptFile.get(uiSourceCode);
94
+ if (!scriptFile) {
95
+ return [];
96
+ }
97
+ const { script } = scriptFile;
98
+ if (!script) {
99
+ return [];
100
+ }
101
+ return [this.debuggerModel.createRawLocation(script, lineNumber, columnNumber)];
102
+ }
103
+ uiLocationRangeToRawLocationRanges(uiSourceCode, { startLine, startColumn, endLine, endColumn }) {
104
+ const scriptFile = this.#uiSourceCodeToScriptFile.get(uiSourceCode);
105
+ if (!scriptFile) {
106
+ return null;
107
+ }
108
+ const { script } = scriptFile;
109
+ if (!script) {
110
+ return null;
111
+ }
112
+ const start = this.debuggerModel.createRawLocation(script, startLine, startColumn);
113
+ const end = this.debuggerModel.createRawLocation(script, endLine, endColumn);
114
+ return [{ start, end }];
115
+ }
116
+ inspectedURLChanged(event) {
117
+ for (let target = this.debuggerModel.target(); target !== event.data; target = target.parentTarget()) {
118
+ if (target === null) {
119
+ return;
120
+ }
121
+ }
122
+ // Just remove and readd all scripts to ensure their URLs are reflected correctly.
123
+ for (const script of Array.from(this.#scriptToUISourceCode.keys())) {
124
+ this.removeScripts([script]);
125
+ this.addScript(script);
126
+ }
127
+ }
128
+ addScript(script) {
129
+ // Ignore live edit scripts here.
130
+ if (script.isLiveEdit() || script.isBreakpointCondition) {
131
+ return;
132
+ }
133
+ let url = script.sourceURL;
134
+ if (!url) {
135
+ return;
136
+ }
137
+ if (script.hasSourceURL) {
138
+ // Try to resolve `//# sourceURL=` annotations relative to
139
+ // the base URL, according to the sourcemap specification.
140
+ url = SDK.SourceMapManager.SourceMapManager.resolveRelativeSourceURL(script.debuggerModel.target(), url);
141
+ }
142
+ else {
143
+ // Ignore inline <script>s without `//# sourceURL` annotation here.
144
+ if (script.isInlineScript()) {
145
+ return;
146
+ }
147
+ // Filter out embedder injected content scripts.
148
+ if (script.isContentScript()) {
149
+ const parsedURL = new Common.ParsedURL.ParsedURL(url);
150
+ if (!parsedURL.isValid) {
151
+ return;
152
+ }
153
+ }
154
+ }
155
+ // Remove previous UISourceCode, if any
156
+ const project = this.project(script);
157
+ const oldUISourceCode = project.uiSourceCodeForURL(url);
158
+ if (oldUISourceCode) {
159
+ const oldScriptFile = this.#uiSourceCodeToScriptFile.get(oldUISourceCode);
160
+ if (oldScriptFile?.script) {
161
+ this.removeScripts([oldScriptFile.script]);
162
+ }
163
+ }
164
+ // Create UISourceCode.
165
+ const originalContentProvider = script.originalContentProvider();
166
+ const uiSourceCode = project.createUISourceCode(url, originalContentProvider.contentType());
167
+ NetworkProject.setInitialFrameAttribution(uiSourceCode, script.frameId);
168
+ const metadata = metadataForURL(this.debuggerModel.target(), script.frameId, url);
169
+ // Bind UISourceCode to scripts.
170
+ const scriptFile = new ResourceScriptFile(this, uiSourceCode, script);
171
+ this.#uiSourceCodeToScriptFile.set(uiSourceCode, scriptFile);
172
+ this.#scriptToUISourceCode.set(script, uiSourceCode);
173
+ const mimeType = script.isWasm() ? 'application/wasm' : 'text/javascript';
174
+ project.addUISourceCodeWithProvider(uiSourceCode, originalContentProvider, metadata, mimeType);
175
+ void this.debuggerWorkspaceBinding.updateLocations(script);
176
+ }
177
+ scriptFile(uiSourceCode) {
178
+ return this.#uiSourceCodeToScriptFile.get(uiSourceCode) || null;
179
+ }
180
+ removeScripts(scripts) {
181
+ const uiSourceCodesByProject = new Platform.MapUtilities.Multimap();
182
+ for (const script of scripts) {
183
+ const uiSourceCode = this.#scriptToUISourceCode.get(script);
184
+ if (!uiSourceCode) {
185
+ continue;
186
+ }
187
+ const scriptFile = this.#uiSourceCodeToScriptFile.get(uiSourceCode);
188
+ if (scriptFile) {
189
+ scriptFile.dispose();
190
+ }
191
+ this.#uiSourceCodeToScriptFile.delete(uiSourceCode);
192
+ this.#scriptToUISourceCode.delete(script);
193
+ uiSourceCodesByProject.set(uiSourceCode.project(), uiSourceCode);
194
+ void this.debuggerWorkspaceBinding.updateLocations(script);
195
+ }
196
+ for (const project of uiSourceCodesByProject.keysArray()) {
197
+ const uiSourceCodes = uiSourceCodesByProject.get(project);
198
+ // Check if all the ui source codes in the project are in |uiSourceCodes|.
199
+ let allInProjectRemoved = true;
200
+ for (const projectSourceCode of project.uiSourceCodes()) {
201
+ if (!uiSourceCodes.has(projectSourceCode)) {
202
+ allInProjectRemoved = false;
203
+ break;
204
+ }
205
+ }
206
+ // Drop the whole project if no source codes are left in it.
207
+ if (allInProjectRemoved) {
208
+ this.#projects.delete(project.id());
209
+ project.removeProject();
210
+ }
211
+ else {
212
+ // Otherwise, announce the removal of each UI source code individually.
213
+ uiSourceCodes.forEach(c => project.removeUISourceCode(c.url()));
214
+ }
215
+ }
216
+ }
217
+ executionContextDestroyed(event) {
218
+ const executionContext = event.data;
219
+ this.removeScripts(this.debuggerModel.scriptsForExecutionContext(executionContext));
220
+ }
221
+ globalObjectCleared() {
222
+ const scripts = Array.from(this.#scriptToUISourceCode.keys());
223
+ this.removeScripts(scripts);
224
+ }
225
+ resetForTest() {
226
+ this.globalObjectCleared();
227
+ }
228
+ dispose() {
229
+ Common.EventTarget.removeEventListeners(this.#eventListeners);
230
+ this.globalObjectCleared();
231
+ }
232
+ }
233
+ export class ResourceScriptFile extends Common.ObjectWrapper.ObjectWrapper {
234
+ #resourceScriptMapping;
235
+ uiSourceCode;
236
+ script;
237
+ #scriptSource;
238
+ #isDivergingFromVM;
239
+ #hasDivergedFromVM;
240
+ #isMergingToVM;
241
+ #updateMutex = new Common.Mutex.Mutex();
242
+ constructor(resourceScriptMapping, uiSourceCode, script) {
243
+ super();
244
+ this.#resourceScriptMapping = resourceScriptMapping;
245
+ this.uiSourceCode = uiSourceCode;
246
+ this.script = this.uiSourceCode.contentType().isScript() ? script : null;
247
+ this.uiSourceCode.addEventListener(Workspace.UISourceCode.Events.WorkingCopyChanged, this.workingCopyChanged, this);
248
+ this.uiSourceCode.addEventListener(Workspace.UISourceCode.Events.WorkingCopyCommitted, this.workingCopyCommitted, this);
249
+ }
250
+ isDiverged() {
251
+ if (this.uiSourceCode.isDirty()) {
252
+ return true;
253
+ }
254
+ if (!this.script) {
255
+ return false;
256
+ }
257
+ if (typeof this.#scriptSource === 'undefined' || this.#scriptSource === null) {
258
+ return false;
259
+ }
260
+ const workingCopy = this.uiSourceCode.workingCopy();
261
+ if (!workingCopy) {
262
+ return false;
263
+ }
264
+ // Match ignoring sourceURL.
265
+ if (!workingCopy.startsWith(this.#scriptSource.trimEnd())) {
266
+ return true;
267
+ }
268
+ const suffix = this.uiSourceCode.workingCopy().substr(this.#scriptSource.length);
269
+ return Boolean(suffix.length) && !suffix.match(SDK.Script.sourceURLRegex);
270
+ }
271
+ workingCopyChanged() {
272
+ void this.update();
273
+ }
274
+ workingCopyCommitted() {
275
+ // This feature flag is for turning down live edit. If it's not present, we keep the feature enabled.
276
+ if (Root.Runtime.hostConfig.devToolsLiveEdit?.enabled === false) {
277
+ return;
278
+ }
279
+ if (this.uiSourceCode.project().canSetFileContent()) {
280
+ return;
281
+ }
282
+ if (!this.script) {
283
+ return;
284
+ }
285
+ const source = this.uiSourceCode.workingCopy();
286
+ void this.script.editSource(source).then(({ status, exceptionDetails }) => {
287
+ void this.scriptSourceWasSet(source, status, exceptionDetails);
288
+ });
289
+ }
290
+ async scriptSourceWasSet(source, status, exceptionDetails) {
291
+ if (status === "Ok" /* Protocol.Debugger.SetScriptSourceResponseStatus.Ok */) {
292
+ this.#scriptSource = source;
293
+ }
294
+ await this.update();
295
+ if (status === "Ok" /* Protocol.Debugger.SetScriptSourceResponseStatus.Ok */) {
296
+ return;
297
+ }
298
+ if (!exceptionDetails) {
299
+ // TODO(crbug.com/1334484): Instead of to the console, report these errors in an "info bar" at the bottom
300
+ // of the text editor, similar to e.g. source mapping errors.
301
+ Common.Console.Console.instance().addMessage(i18nString(UIStrings.liveEditFailed, { PH1: getErrorText(status) }), "warning" /* Common.Console.MessageLevel.WARNING */);
302
+ return;
303
+ }
304
+ const messageText = i18nString(UIStrings.liveEditCompileFailed, { PH1: exceptionDetails.text });
305
+ this.uiSourceCode.addLineMessage("Error" /* Workspace.UISourceCode.Message.Level.ERROR */, messageText, exceptionDetails.lineNumber, exceptionDetails.columnNumber);
306
+ function getErrorText(status) {
307
+ switch (status) {
308
+ case "BlockedByActiveFunction" /* Protocol.Debugger.SetScriptSourceResponseStatus.BlockedByActiveFunction */:
309
+ return 'Functions that are on the stack (currently being executed) can not be edited';
310
+ case "BlockedByActiveGenerator" /* Protocol.Debugger.SetScriptSourceResponseStatus.BlockedByActiveGenerator */:
311
+ return 'Async functions/generators that are active can not be edited';
312
+ case "BlockedByTopLevelEsModuleChange" /* Protocol.Debugger.SetScriptSourceResponseStatus.BlockedByTopLevelEsModuleChange */:
313
+ return 'The top-level of ES modules can not be edited';
314
+ case "CompileError" /* Protocol.Debugger.SetScriptSourceResponseStatus.CompileError */:
315
+ case "Ok" /* Protocol.Debugger.SetScriptSourceResponseStatus.Ok */:
316
+ throw new Error('Compile errors and Ok status must not be reported on the console');
317
+ }
318
+ }
319
+ }
320
+ async update() {
321
+ // Do not interleave "divergeFromVM" with "mergeToVM" calls.
322
+ const release = await this.#updateMutex.acquire();
323
+ const diverged = this.isDiverged();
324
+ if (diverged && !this.#hasDivergedFromVM) {
325
+ await this.divergeFromVM();
326
+ }
327
+ else if (!diverged && this.#hasDivergedFromVM) {
328
+ await this.mergeToVM();
329
+ }
330
+ release();
331
+ }
332
+ async divergeFromVM() {
333
+ if (this.script) {
334
+ this.#isDivergingFromVM = true;
335
+ await this.#resourceScriptMapping.debuggerWorkspaceBinding.updateLocations(this.script);
336
+ this.#isDivergingFromVM = undefined;
337
+ this.#hasDivergedFromVM = true;
338
+ this.dispatchEventToListeners("DidDivergeFromVM" /* ResourceScriptFile.Events.DID_DIVERGE_FROM_VM */);
339
+ }
340
+ }
341
+ async mergeToVM() {
342
+ if (this.script) {
343
+ this.#hasDivergedFromVM = undefined;
344
+ this.#isMergingToVM = true;
345
+ await this.#resourceScriptMapping.debuggerWorkspaceBinding.updateLocations(this.script);
346
+ this.#isMergingToVM = undefined;
347
+ this.dispatchEventToListeners("DidMergeToVM" /* ResourceScriptFile.Events.DID_MERGE_TO_VM */);
348
+ }
349
+ }
350
+ hasDivergedFromVM() {
351
+ return Boolean(this.#hasDivergedFromVM);
352
+ }
353
+ isDivergingFromVM() {
354
+ return Boolean(this.#isDivergingFromVM);
355
+ }
356
+ isMergingToVM() {
357
+ return Boolean(this.#isMergingToVM);
358
+ }
359
+ checkMapping() {
360
+ if (!this.script || typeof this.#scriptSource !== 'undefined') {
361
+ this.mappingCheckedForTest();
362
+ return;
363
+ }
364
+ void this.script.requestContentData().then(content => {
365
+ this.#scriptSource = TextUtils.ContentData.ContentData.textOr(content, null);
366
+ void this.update().then(() => this.mappingCheckedForTest());
367
+ });
368
+ }
369
+ mappingCheckedForTest() {
370
+ }
371
+ dispose() {
372
+ this.uiSourceCode.removeEventListener(Workspace.UISourceCode.Events.WorkingCopyChanged, this.workingCopyChanged, this);
373
+ this.uiSourceCode.removeEventListener(Workspace.UISourceCode.Events.WorkingCopyCommitted, this.workingCopyCommitted, this);
374
+ }
375
+ addSourceMapURL(sourceMapURL) {
376
+ if (!this.script) {
377
+ return;
378
+ }
379
+ this.script.debuggerModel.setSourceMapURL(this.script, sourceMapURL);
380
+ }
381
+ addDebugInfoURL(debugInfoURL) {
382
+ if (!this.script) {
383
+ return;
384
+ }
385
+ const { pluginManager } = DebuggerWorkspaceBinding.instance();
386
+ pluginManager.setDebugInfoURL(this.script, debugInfoURL);
387
+ }
388
+ hasSourceMapURL() {
389
+ return Boolean(this.script?.sourceMapURL);
390
+ }
391
+ async missingSymbolFiles() {
392
+ if (!this.script) {
393
+ return null;
394
+ }
395
+ const { pluginManager } = this.#resourceScriptMapping.debuggerWorkspaceBinding;
396
+ const sources = await pluginManager.getSourcesForScript(this.script);
397
+ return sources && 'missingSymbolFiles' in sources ? sources.missingSymbolFiles : null;
398
+ }
399
+ }
@@ -0,0 +1,87 @@
1
+ // Copyright 2021 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+ /*
5
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
6
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
7
+ * Copyright (C) 2009 Joseph Pecoraro
8
+ *
9
+ * Redistribution and use in source and binary forms, with or without
10
+ * modification, are permitted provided that the following conditions are
11
+ * met:
12
+ *
13
+ * 1. Redistributions of source code must retain the above copyright
14
+ * notice, this list of conditions and the following disclaimer.
15
+ * 2. Redistributions in binary form must reproduce the above copyright
16
+ * notice, this list of conditions and the following disclaimer in the
17
+ * documentation and/or other materials provided with the distribution.
18
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
19
+ * its contributors may be used to endorse or promote products derived
20
+ * from this software without specific prior written permission.
21
+ *
22
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
23
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
26
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+ */
33
+ import * as Common from '../../core/common/common.js';
34
+ import * as Platform from '../../core/platform/platform.js';
35
+ import * as SDK from '../../core/sdk/sdk.js';
36
+ import * as Workspace from '../workspace/workspace.js';
37
+ export function resourceForURL(url) {
38
+ return SDK.ResourceTreeModel.ResourceTreeModel.resourceForURL(url);
39
+ }
40
+ export function displayNameForURL(url) {
41
+ if (!url) {
42
+ return '';
43
+ }
44
+ const uiSourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(url);
45
+ if (uiSourceCode) {
46
+ return uiSourceCode.displayName();
47
+ }
48
+ const resource = resourceForURL(url);
49
+ if (resource) {
50
+ return resource.displayName;
51
+ }
52
+ const inspectedURL = SDK.TargetManager.TargetManager.instance().inspectedURL();
53
+ if (!inspectedURL) {
54
+ return Platform.StringUtilities.trimURL(url, '');
55
+ }
56
+ const parsedURL = Common.ParsedURL.ParsedURL.fromString(inspectedURL);
57
+ if (!parsedURL) {
58
+ return url;
59
+ }
60
+ const lastPathComponent = parsedURL.lastPathComponent;
61
+ const index = inspectedURL.indexOf(lastPathComponent);
62
+ if (index !== -1 && index + lastPathComponent.length === inspectedURL.length) {
63
+ const baseURL = inspectedURL.substring(0, index);
64
+ if (url.startsWith(baseURL) && url.length > index) {
65
+ return url.substring(index);
66
+ }
67
+ }
68
+ const displayName = Platform.StringUtilities.trimURL(url, parsedURL.host);
69
+ return displayName === '/' ? parsedURL.host + '/' : displayName;
70
+ }
71
+ export function metadataForURL(target, frameId, url) {
72
+ const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
73
+ if (!resourceTreeModel) {
74
+ return null;
75
+ }
76
+ const frame = resourceTreeModel.frameForId(frameId);
77
+ if (!frame) {
78
+ return null;
79
+ }
80
+ return resourceMetadata(frame.resourceForURL(url));
81
+ }
82
+ export function resourceMetadata(resource) {
83
+ if (!resource || (typeof resource.contentSize() !== 'number' && !resource.lastModified())) {
84
+ return null;
85
+ }
86
+ return new Workspace.UISourceCode.UISourceCodeMetadata(resource.lastModified(), resource.contentSize());
87
+ }
@@ -0,0 +1,181 @@
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 SDK from '../../core/sdk/sdk.js';
6
+ import * as TextUtils from '../text_utils/text_utils.js';
7
+ import * as Workspace from '../workspace/workspace.js';
8
+ import { ContentProviderBasedProject } from './ContentProviderBasedProject.js';
9
+ import { CSSWorkspaceBinding } from './CSSWorkspaceBinding.js';
10
+ import { NetworkProject } from './NetworkProject.js';
11
+ export class SASSSourceMapping {
12
+ #sourceMapManager;
13
+ #project;
14
+ #eventListeners;
15
+ #bindings;
16
+ constructor(target, sourceMapManager, workspace) {
17
+ this.#sourceMapManager = sourceMapManager;
18
+ this.#project = new ContentProviderBasedProject(workspace, 'cssSourceMaps:' + target.id(), Workspace.Workspace.projectTypes.Network, '', false /* isServiceProject */);
19
+ NetworkProject.setTargetForProject(this.#project, target);
20
+ this.#bindings = new Map();
21
+ this.#eventListeners = [
22
+ this.#sourceMapManager.addEventListener(SDK.SourceMapManager.Events.SourceMapAttached, this.sourceMapAttached, this),
23
+ this.#sourceMapManager.addEventListener(SDK.SourceMapManager.Events.SourceMapDetached, this.sourceMapDetached, this),
24
+ ];
25
+ }
26
+ sourceMapAttachedForTest(_sourceMap) {
27
+ }
28
+ async sourceMapAttached(event) {
29
+ const header = event.data.client;
30
+ const sourceMap = event.data.sourceMap;
31
+ const project = this.#project;
32
+ const bindings = this.#bindings;
33
+ for (const sourceURL of sourceMap.sourceURLs()) {
34
+ let binding = bindings.get(sourceURL);
35
+ if (!binding) {
36
+ binding = new Binding(project, sourceURL, header.createPageResourceLoadInitiator());
37
+ bindings.set(sourceURL, binding);
38
+ }
39
+ binding.addSourceMap(sourceMap, header.frameId);
40
+ }
41
+ await CSSWorkspaceBinding.instance().updateLocations(header);
42
+ this.sourceMapAttachedForTest(sourceMap);
43
+ }
44
+ async sourceMapDetached(event) {
45
+ const header = event.data.client;
46
+ const sourceMap = event.data.sourceMap;
47
+ const bindings = this.#bindings;
48
+ for (const sourceURL of sourceMap.sourceURLs()) {
49
+ const binding = bindings.get(sourceURL);
50
+ if (binding) {
51
+ binding.removeSourceMap(sourceMap, header.frameId);
52
+ if (!binding.getUiSourceCode()) {
53
+ bindings.delete(sourceURL);
54
+ }
55
+ }
56
+ }
57
+ await CSSWorkspaceBinding.instance().updateLocations(header);
58
+ }
59
+ rawLocationToUILocation(rawLocation) {
60
+ const header = rawLocation.header();
61
+ if (!header) {
62
+ return null;
63
+ }
64
+ const sourceMap = this.#sourceMapManager.sourceMapForClient(header);
65
+ if (!sourceMap) {
66
+ return null;
67
+ }
68
+ let { lineNumber, columnNumber } = rawLocation;
69
+ // If the source map maps the origin (line:0, column:0) but the CSS header is inline (in a HTML doc),
70
+ // then adjust the line and column numbers.
71
+ if (sourceMap.mapsOrigin() && header.isInline) {
72
+ lineNumber -= header.startLine;
73
+ if (lineNumber === 0) {
74
+ columnNumber -= header.startColumn;
75
+ }
76
+ }
77
+ const entry = sourceMap.findEntry(lineNumber, columnNumber);
78
+ if (!entry?.sourceURL) {
79
+ return null;
80
+ }
81
+ const uiSourceCode = this.#project.uiSourceCodeForURL(entry.sourceURL);
82
+ if (!uiSourceCode) {
83
+ return null;
84
+ }
85
+ return uiSourceCode.uiLocation(entry.sourceLineNumber, entry.sourceColumnNumber);
86
+ }
87
+ uiLocationToRawLocations(uiLocation) {
88
+ // TODO(crbug.com/1153123): Revisit the `#columnNumber || 0` and also preserve `undefined` for source maps?
89
+ const { uiSourceCode, lineNumber, columnNumber = 0 } = uiLocation;
90
+ const binding = uiSourceCodeToBinding.get(uiSourceCode);
91
+ if (!binding) {
92
+ return [];
93
+ }
94
+ const locations = [];
95
+ for (const sourceMap of binding.getReferringSourceMaps()) {
96
+ const entries = sourceMap.findReverseEntries(uiSourceCode.url(), lineNumber, columnNumber);
97
+ const header = this.#sourceMapManager.clientForSourceMap(sourceMap);
98
+ if (header) {
99
+ locations.push(...entries.map(entry => new SDK.CSSModel.CSSLocation(header, entry.lineNumber, entry.columnNumber)));
100
+ }
101
+ }
102
+ return locations;
103
+ }
104
+ static uiSourceOrigin(uiSourceCode) {
105
+ const binding = uiSourceCodeToBinding.get(uiSourceCode);
106
+ if (binding) {
107
+ return binding.getReferringSourceMaps().map(sourceMap => sourceMap.compiledURL());
108
+ }
109
+ return [];
110
+ }
111
+ dispose() {
112
+ Common.EventTarget.removeEventListeners(this.#eventListeners);
113
+ this.#project.dispose();
114
+ }
115
+ }
116
+ const uiSourceCodeToBinding = new WeakMap();
117
+ class Binding {
118
+ #project;
119
+ #url;
120
+ #initiator;
121
+ referringSourceMaps;
122
+ uiSourceCode;
123
+ constructor(project, url, initiator) {
124
+ this.#project = project;
125
+ this.#url = url;
126
+ this.#initiator = initiator;
127
+ this.referringSourceMaps = [];
128
+ this.uiSourceCode = null;
129
+ }
130
+ recreateUISourceCodeIfNeeded(frameId) {
131
+ const sourceMap = this.referringSourceMaps[this.referringSourceMaps.length - 1];
132
+ const contentType = Common.ResourceType.resourceTypes.SourceMapStyleSheet;
133
+ const embeddedContent = sourceMap.embeddedContentByURL(this.#url);
134
+ const contentProvider = embeddedContent !== null ?
135
+ TextUtils.StaticContentProvider.StaticContentProvider.fromString(this.#url, contentType, embeddedContent) :
136
+ new SDK.CompilerSourceMappingContentProvider.CompilerSourceMappingContentProvider(this.#url, contentType, this.#initiator);
137
+ const newUISourceCode = this.#project.createUISourceCode(this.#url, contentType);
138
+ uiSourceCodeToBinding.set(newUISourceCode, this);
139
+ const mimeType = Common.ResourceType.ResourceType.mimeFromURL(this.#url) || contentType.canonicalMimeType();
140
+ const metadata = typeof embeddedContent === 'string' ?
141
+ new Workspace.UISourceCode.UISourceCodeMetadata(null, embeddedContent.length) :
142
+ null;
143
+ if (this.uiSourceCode) {
144
+ NetworkProject.cloneInitialFrameAttribution(this.uiSourceCode, newUISourceCode);
145
+ this.#project.removeUISourceCode(this.uiSourceCode.url());
146
+ }
147
+ else {
148
+ NetworkProject.setInitialFrameAttribution(newUISourceCode, frameId);
149
+ }
150
+ this.uiSourceCode = newUISourceCode;
151
+ this.#project.addUISourceCodeWithProvider(this.uiSourceCode, contentProvider, metadata, mimeType);
152
+ }
153
+ addSourceMap(sourceMap, frameId) {
154
+ if (this.uiSourceCode) {
155
+ NetworkProject.addFrameAttribution(this.uiSourceCode, frameId);
156
+ }
157
+ this.referringSourceMaps.push(sourceMap);
158
+ this.recreateUISourceCodeIfNeeded(frameId);
159
+ }
160
+ removeSourceMap(sourceMap, frameId) {
161
+ const uiSourceCode = this.uiSourceCode;
162
+ NetworkProject.removeFrameAttribution(uiSourceCode, frameId);
163
+ const lastIndex = this.referringSourceMaps.lastIndexOf(sourceMap);
164
+ if (lastIndex !== -1) {
165
+ this.referringSourceMaps.splice(lastIndex, 1);
166
+ }
167
+ if (!this.referringSourceMaps.length) {
168
+ this.#project.removeUISourceCode(uiSourceCode.url());
169
+ this.uiSourceCode = null;
170
+ }
171
+ else {
172
+ this.recreateUISourceCodeIfNeeded(frameId);
173
+ }
174
+ }
175
+ getReferringSourceMaps() {
176
+ return this.referringSourceMaps;
177
+ }
178
+ getUiSourceCode() {
179
+ return this.uiSourceCode;
180
+ }
181
+ }