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.
- package/README.md +6 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +3 -32
- package/build/node_modules/chrome-devtools-frontend/front_end/core/i18n/i18n.js +35 -8
- package/build/node_modules/chrome-devtools-frontend/front_end/core/root/Runtime.js +4 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +4 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +12 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +366 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +366 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +64 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIQueries.js +105 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CSSWorkspaceBinding.js +243 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +407 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ContentProviderBasedProject.js +128 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerLanguagePlugins.js +992 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +574 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DefaultScriptMapping.js +112 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/FileUtils.js +186 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/LiveLocation.js +60 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/NetworkProject.js +107 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/PresentationConsoleMessageHelper.js +244 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceMapping.js +473 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceScriptMapping.js +399 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceUtils.js +87 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/SASSSourceMapping.js +181 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/StylesSourceMapping.js +268 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/TempFile.js +55 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/bindings.js +20 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/CrUXManager.js +283 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/crux-manager.js +4 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +775 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +1706 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/emulation.js +6 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +131 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/ScriptFormatter.js +77 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/formatter.js +6 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/geometry/GeometryImpl.js +347 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/geometry/geometry.js +4 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +626 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/ScopeChainModel.js +59 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/ScopeTreeCache.js +32 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/source_map_scopes.js +7 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTrace.js +4 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +67 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +97 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/Trie.js +113 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/stack_trace.js +5 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/stack_trace_impl.js +7 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/text_utils/TextUtils.js +23 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Trace.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +5 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +199 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/trace_source_maps_resolver.js +4 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/FileManager.js +64 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/IgnoreListManager.js +511 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/SearchConfig.js +113 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/UISourceCode.js +563 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/WorkspaceImpl.js +204 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/workspace.js +9 -0
- package/build/src/McpContext.js +24 -9
- package/build/src/McpResponse.js +3 -3
- package/build/src/browser.js +3 -1
- package/build/src/index.js +1 -1
- package/build/src/tools/input.js +7 -7
- package/build/src/tools/performance.js +29 -2
- package/build/src/tools/screenshot.js +1 -1
- package/build/src/tools/script.js +40 -14
- package/build/src/trace-processing/parse.js +26 -22
- package/package.json +9 -7
|
@@ -0,0 +1,186 @@
|
|
|
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 TextUtils from '../text_utils/text_utils.js';
|
|
6
|
+
import * as Workspace from '../workspace/workspace.js';
|
|
7
|
+
export class ChunkedFileReader {
|
|
8
|
+
#file;
|
|
9
|
+
#fileSize;
|
|
10
|
+
#loadedSize;
|
|
11
|
+
#streamReader;
|
|
12
|
+
#chunkSize;
|
|
13
|
+
#chunkTransferredCallback;
|
|
14
|
+
#decoder;
|
|
15
|
+
#isCanceled;
|
|
16
|
+
#error;
|
|
17
|
+
#transferFinished;
|
|
18
|
+
#output;
|
|
19
|
+
#reader;
|
|
20
|
+
constructor(file, chunkSize, chunkTransferredCallback) {
|
|
21
|
+
this.#file = file;
|
|
22
|
+
this.#fileSize = file.size;
|
|
23
|
+
this.#loadedSize = 0;
|
|
24
|
+
this.#chunkSize = (chunkSize) ? chunkSize : Number.MAX_VALUE;
|
|
25
|
+
this.#chunkTransferredCallback = chunkTransferredCallback;
|
|
26
|
+
this.#decoder = new TextDecoder();
|
|
27
|
+
this.#isCanceled = false;
|
|
28
|
+
this.#error = null;
|
|
29
|
+
this.#streamReader = null;
|
|
30
|
+
}
|
|
31
|
+
async read(output) {
|
|
32
|
+
if (this.#chunkTransferredCallback) {
|
|
33
|
+
this.#chunkTransferredCallback(this);
|
|
34
|
+
}
|
|
35
|
+
if (this.#file?.type.endsWith('gzip')) {
|
|
36
|
+
const fileStream = this.#file.stream();
|
|
37
|
+
const stream = Common.Gzip.decompressStream(fileStream);
|
|
38
|
+
this.#streamReader = stream.getReader();
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.#reader = new FileReader();
|
|
42
|
+
this.#reader.onload = this.onChunkLoaded.bind(this);
|
|
43
|
+
this.#reader.onerror = this.onError.bind(this);
|
|
44
|
+
}
|
|
45
|
+
this.#output = output;
|
|
46
|
+
void this.loadChunk();
|
|
47
|
+
return await new Promise(resolve => {
|
|
48
|
+
this.#transferFinished = resolve;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
cancel() {
|
|
52
|
+
this.#isCanceled = true;
|
|
53
|
+
}
|
|
54
|
+
loadedSize() {
|
|
55
|
+
return this.#loadedSize;
|
|
56
|
+
}
|
|
57
|
+
fileSize() {
|
|
58
|
+
return this.#fileSize;
|
|
59
|
+
}
|
|
60
|
+
fileName() {
|
|
61
|
+
if (!this.#file) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
return this.#file.name;
|
|
65
|
+
}
|
|
66
|
+
error() {
|
|
67
|
+
return this.#error;
|
|
68
|
+
}
|
|
69
|
+
onChunkLoaded(event) {
|
|
70
|
+
if (this.#isCanceled) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const eventTarget = event.target;
|
|
74
|
+
if (eventTarget.readyState !== FileReader.DONE) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!this.#reader) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const buffer = this.#reader.result;
|
|
81
|
+
this.#loadedSize += buffer.byteLength;
|
|
82
|
+
const endOfFile = this.#loadedSize === this.#fileSize;
|
|
83
|
+
void this.decodeChunkBuffer(buffer, endOfFile);
|
|
84
|
+
}
|
|
85
|
+
async decodeChunkBuffer(buffer, endOfFile) {
|
|
86
|
+
if (!this.#output) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const decodedString = this.#decoder.decode(buffer, { stream: !endOfFile });
|
|
90
|
+
await this.#output.write(decodedString, endOfFile);
|
|
91
|
+
if (this.#isCanceled) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (this.#chunkTransferredCallback) {
|
|
95
|
+
this.#chunkTransferredCallback(this);
|
|
96
|
+
}
|
|
97
|
+
if (endOfFile) {
|
|
98
|
+
void this.finishRead();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
void this.loadChunk();
|
|
102
|
+
}
|
|
103
|
+
async finishRead() {
|
|
104
|
+
if (!this.#output) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.#file = null;
|
|
108
|
+
this.#reader = null;
|
|
109
|
+
await this.#output.close();
|
|
110
|
+
this.#transferFinished(!this.#error);
|
|
111
|
+
}
|
|
112
|
+
async loadChunk() {
|
|
113
|
+
if (!this.#output || !this.#file) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (this.#streamReader) {
|
|
117
|
+
const { value, done } = await this.#streamReader.read();
|
|
118
|
+
if (done || !value) {
|
|
119
|
+
// Write empty string to inform of file end
|
|
120
|
+
await this.#output.write('', true);
|
|
121
|
+
return await this.finishRead();
|
|
122
|
+
}
|
|
123
|
+
void this.decodeChunkBuffer(value.buffer, false);
|
|
124
|
+
}
|
|
125
|
+
if (this.#reader) {
|
|
126
|
+
const chunkStart = this.#loadedSize;
|
|
127
|
+
const chunkEnd = Math.min(this.#fileSize, chunkStart + this.#chunkSize);
|
|
128
|
+
const nextPart = this.#file.slice(chunkStart, chunkEnd);
|
|
129
|
+
this.#reader.readAsArrayBuffer(nextPart);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
onError(event) {
|
|
133
|
+
const eventTarget = event.target;
|
|
134
|
+
this.#error = eventTarget.error;
|
|
135
|
+
this.#transferFinished(false);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export class FileOutputStream {
|
|
139
|
+
#writeCallbacks;
|
|
140
|
+
#fileName;
|
|
141
|
+
#closed;
|
|
142
|
+
constructor() {
|
|
143
|
+
this.#writeCallbacks = [];
|
|
144
|
+
}
|
|
145
|
+
async open(fileName) {
|
|
146
|
+
this.#closed = false;
|
|
147
|
+
this.#writeCallbacks = [];
|
|
148
|
+
this.#fileName = fileName;
|
|
149
|
+
const saveResponse = await Workspace.FileManager.FileManager.instance().save(this.#fileName, TextUtils.ContentData.EMPTY_TEXT_CONTENT_DATA, /* forceSaveAs=*/ true);
|
|
150
|
+
if (saveResponse) {
|
|
151
|
+
Workspace.FileManager.FileManager.instance().addEventListener("AppendedToURL" /* Workspace.FileManager.Events.APPENDED_TO_URL */, this.onAppendDone, this);
|
|
152
|
+
}
|
|
153
|
+
return Boolean(saveResponse);
|
|
154
|
+
}
|
|
155
|
+
write(data) {
|
|
156
|
+
return new Promise(resolve => {
|
|
157
|
+
this.#writeCallbacks.push(resolve);
|
|
158
|
+
Workspace.FileManager.FileManager.instance().append(this.#fileName, data);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async close() {
|
|
162
|
+
this.#closed = true;
|
|
163
|
+
if (this.#writeCallbacks.length) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
Workspace.FileManager.FileManager.instance().removeEventListener("AppendedToURL" /* Workspace.FileManager.Events.APPENDED_TO_URL */, this.onAppendDone, this);
|
|
167
|
+
Workspace.FileManager.FileManager.instance().close(this.#fileName);
|
|
168
|
+
}
|
|
169
|
+
onAppendDone(event) {
|
|
170
|
+
if (event.data !== this.#fileName) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const writeCallback = this.#writeCallbacks.shift();
|
|
174
|
+
if (writeCallback) {
|
|
175
|
+
writeCallback();
|
|
176
|
+
}
|
|
177
|
+
if (this.#writeCallbacks.length) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (!this.#closed) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
Workspace.FileManager.FileManager.instance().removeEventListener("AppendedToURL" /* Workspace.FileManager.Events.APPENDED_TO_URL */, this.onAppendDone, this);
|
|
184
|
+
Workspace.FileManager.FileManager.instance().close(this.#fileName);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
export class LiveLocationWithPool {
|
|
5
|
+
#updateDelegate;
|
|
6
|
+
#locationPool;
|
|
7
|
+
#updatePromise;
|
|
8
|
+
constructor(updateDelegate, locationPool) {
|
|
9
|
+
this.#updateDelegate = updateDelegate;
|
|
10
|
+
this.#locationPool = locationPool;
|
|
11
|
+
this.#locationPool.add(this);
|
|
12
|
+
this.#updatePromise = null;
|
|
13
|
+
}
|
|
14
|
+
async update() {
|
|
15
|
+
if (!this.#updateDelegate) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
// The following is a basic scheduling algorithm, guaranteeing that
|
|
19
|
+
// {#updateDelegate} is always run atomically. That is, we always
|
|
20
|
+
// wait for an update to finish before we trigger the next run.
|
|
21
|
+
if (this.#updatePromise) {
|
|
22
|
+
await this.#updatePromise.then(() => this.update());
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
this.#updatePromise = this.#updateDelegate(this);
|
|
26
|
+
await this.#updatePromise;
|
|
27
|
+
this.#updatePromise = null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async uiLocation() {
|
|
31
|
+
throw new Error('Not implemented');
|
|
32
|
+
}
|
|
33
|
+
dispose() {
|
|
34
|
+
this.#locationPool.delete(this);
|
|
35
|
+
this.#updateDelegate = null;
|
|
36
|
+
}
|
|
37
|
+
isDisposed() {
|
|
38
|
+
return !this.#locationPool.has(this);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class LiveLocationPool {
|
|
42
|
+
#locations;
|
|
43
|
+
constructor() {
|
|
44
|
+
this.#locations = new Set();
|
|
45
|
+
}
|
|
46
|
+
add(location) {
|
|
47
|
+
this.#locations.add(location);
|
|
48
|
+
}
|
|
49
|
+
delete(location) {
|
|
50
|
+
this.#locations.delete(location);
|
|
51
|
+
}
|
|
52
|
+
has(location) {
|
|
53
|
+
return this.#locations.has(location);
|
|
54
|
+
}
|
|
55
|
+
disposeAll() {
|
|
56
|
+
for (const location of this.#locations) {
|
|
57
|
+
location.dispose();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/NetworkProject.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
const uiSourceCodeToAttributionMap = new WeakMap();
|
|
7
|
+
const projectToTargetMap = new WeakMap();
|
|
8
|
+
let networkProjectManagerInstance;
|
|
9
|
+
export class NetworkProjectManager extends Common.ObjectWrapper.ObjectWrapper {
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
}
|
|
13
|
+
static instance({ forceNew } = { forceNew: false }) {
|
|
14
|
+
if (!networkProjectManagerInstance || forceNew) {
|
|
15
|
+
networkProjectManagerInstance = new NetworkProjectManager();
|
|
16
|
+
}
|
|
17
|
+
return networkProjectManagerInstance;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class NetworkProject {
|
|
21
|
+
static resolveFrame(uiSourceCode, frameId) {
|
|
22
|
+
const target = NetworkProject.targetForUISourceCode(uiSourceCode);
|
|
23
|
+
const resourceTreeModel = target?.model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
24
|
+
return resourceTreeModel ? resourceTreeModel.frameForId(frameId) : null;
|
|
25
|
+
}
|
|
26
|
+
static setInitialFrameAttribution(uiSourceCode, frameId) {
|
|
27
|
+
if (!frameId) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const frame = NetworkProject.resolveFrame(uiSourceCode, frameId);
|
|
31
|
+
if (!frame) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const attribution = new Map();
|
|
35
|
+
attribution.set(frameId, { frame, count: 1 });
|
|
36
|
+
uiSourceCodeToAttributionMap.set(uiSourceCode, attribution);
|
|
37
|
+
}
|
|
38
|
+
static cloneInitialFrameAttribution(fromUISourceCode, toUISourceCode) {
|
|
39
|
+
const fromAttribution = uiSourceCodeToAttributionMap.get(fromUISourceCode);
|
|
40
|
+
if (!fromAttribution) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const toAttribution = new Map();
|
|
44
|
+
for (const frameId of fromAttribution.keys()) {
|
|
45
|
+
const value = fromAttribution.get(frameId);
|
|
46
|
+
if (typeof value !== 'undefined') {
|
|
47
|
+
toAttribution.set(frameId, { frame: value.frame, count: value.count });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
uiSourceCodeToAttributionMap.set(toUISourceCode, toAttribution);
|
|
51
|
+
}
|
|
52
|
+
static addFrameAttribution(uiSourceCode, frameId) {
|
|
53
|
+
const frame = NetworkProject.resolveFrame(uiSourceCode, frameId);
|
|
54
|
+
if (!frame) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const frameAttribution = uiSourceCodeToAttributionMap.get(uiSourceCode);
|
|
58
|
+
if (!frameAttribution) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const attributionInfo = frameAttribution.get(frameId) || { frame, count: 0 };
|
|
62
|
+
attributionInfo.count += 1;
|
|
63
|
+
frameAttribution.set(frameId, attributionInfo);
|
|
64
|
+
if (attributionInfo.count !== 1) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const data = { uiSourceCode, frame };
|
|
68
|
+
NetworkProjectManager.instance().dispatchEventToListeners("FrameAttributionAdded" /* Events.FRAME_ATTRIBUTION_ADDED */, data);
|
|
69
|
+
}
|
|
70
|
+
static removeFrameAttribution(uiSourceCode, frameId) {
|
|
71
|
+
const frameAttribution = uiSourceCodeToAttributionMap.get(uiSourceCode);
|
|
72
|
+
if (!frameAttribution) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const attributionInfo = frameAttribution.get(frameId);
|
|
76
|
+
console.assert(Boolean(attributionInfo), 'Failed to remove frame attribution for url: ' + uiSourceCode.url());
|
|
77
|
+
if (!attributionInfo) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
attributionInfo.count -= 1;
|
|
81
|
+
if (attributionInfo.count > 0) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
frameAttribution.delete(frameId);
|
|
85
|
+
const data = { uiSourceCode, frame: attributionInfo.frame };
|
|
86
|
+
NetworkProjectManager.instance().dispatchEventToListeners("FrameAttributionRemoved" /* Events.FRAME_ATTRIBUTION_REMOVED */, data);
|
|
87
|
+
}
|
|
88
|
+
static targetForUISourceCode(uiSourceCode) {
|
|
89
|
+
return projectToTargetMap.get(uiSourceCode.project()) || null;
|
|
90
|
+
}
|
|
91
|
+
static setTargetForProject(project, target) {
|
|
92
|
+
projectToTargetMap.set(project, target);
|
|
93
|
+
}
|
|
94
|
+
static getTargetForProject(project) {
|
|
95
|
+
return projectToTargetMap.get(project) || null;
|
|
96
|
+
}
|
|
97
|
+
static framesForUISourceCode(uiSourceCode) {
|
|
98
|
+
const target = NetworkProject.targetForUISourceCode(uiSourceCode);
|
|
99
|
+
const resourceTreeModel = target?.model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
100
|
+
const attribution = uiSourceCodeToAttributionMap.get(uiSourceCode);
|
|
101
|
+
if (!resourceTreeModel || !attribution) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const frames = Array.from(attribution.keys()).map(frameId => resourceTreeModel.frameForId(frameId));
|
|
105
|
+
return frames.filter(frame => !!frame);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
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 SDK from '../../core/sdk/sdk.js';
|
|
5
|
+
import * as TextUtils from '../text_utils/text_utils.js';
|
|
6
|
+
import * as Workspace from '../workspace/workspace.js';
|
|
7
|
+
import { CSSWorkspaceBinding } from './CSSWorkspaceBinding.js';
|
|
8
|
+
import { DebuggerWorkspaceBinding } from './DebuggerWorkspaceBinding.js';
|
|
9
|
+
import { LiveLocationPool, LiveLocationWithPool } from './LiveLocation.js';
|
|
10
|
+
export class PresentationSourceFrameMessageManager {
|
|
11
|
+
#targetToMessageHelperMap = new WeakMap();
|
|
12
|
+
constructor() {
|
|
13
|
+
SDK.TargetManager.TargetManager.instance().observeModels(SDK.DebuggerModel.DebuggerModel, this);
|
|
14
|
+
SDK.TargetManager.TargetManager.instance().observeModels(SDK.CSSModel.CSSModel, this);
|
|
15
|
+
}
|
|
16
|
+
modelAdded(model) {
|
|
17
|
+
const target = model.target();
|
|
18
|
+
const helper = this.#targetToMessageHelperMap.get(target) ?? new PresentationSourceFrameMessageHelper();
|
|
19
|
+
if (model instanceof SDK.DebuggerModel.DebuggerModel) {
|
|
20
|
+
helper.setDebuggerModel(model);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
helper.setCSSModel(model);
|
|
24
|
+
}
|
|
25
|
+
this.#targetToMessageHelperMap.set(target, helper);
|
|
26
|
+
}
|
|
27
|
+
modelRemoved(model) {
|
|
28
|
+
const target = model.target();
|
|
29
|
+
const helper = this.#targetToMessageHelperMap.get(target);
|
|
30
|
+
helper?.clear();
|
|
31
|
+
}
|
|
32
|
+
addMessage(message, source, target) {
|
|
33
|
+
const helper = this.#targetToMessageHelperMap.get(target);
|
|
34
|
+
void helper?.addMessage(message, source);
|
|
35
|
+
}
|
|
36
|
+
clear() {
|
|
37
|
+
for (const target of SDK.TargetManager.TargetManager.instance().targets()) {
|
|
38
|
+
const helper = this.#targetToMessageHelperMap.get(target);
|
|
39
|
+
helper?.clear();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class PresentationConsoleMessageManager {
|
|
44
|
+
#sourceFrameMessageManager = new PresentationSourceFrameMessageManager();
|
|
45
|
+
constructor() {
|
|
46
|
+
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.ConsoleModel.ConsoleModel, SDK.ConsoleModel.Events.MessageAdded, event => this.consoleMessageAdded(event.data));
|
|
47
|
+
SDK.ConsoleModel.ConsoleModel.allMessagesUnordered().forEach(this.consoleMessageAdded, this);
|
|
48
|
+
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.ConsoleModel.ConsoleModel, SDK.ConsoleModel.Events.ConsoleCleared, () => this.#sourceFrameMessageManager.clear());
|
|
49
|
+
}
|
|
50
|
+
consoleMessageAdded(consoleMessage) {
|
|
51
|
+
const runtimeModel = consoleMessage.runtimeModel();
|
|
52
|
+
if (!consoleMessage.isErrorOrWarning() || !consoleMessage.runtimeModel() ||
|
|
53
|
+
consoleMessage.source === "violation" /* Protocol.Log.LogEntrySource.Violation */ || !runtimeModel) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const level = consoleMessage.level === "error" /* Protocol.Log.LogEntryLevel.Error */ ?
|
|
57
|
+
"Error" /* Workspace.UISourceCode.Message.Level.ERROR */ :
|
|
58
|
+
"Warning" /* Workspace.UISourceCode.Message.Level.WARNING */;
|
|
59
|
+
this.#sourceFrameMessageManager.addMessage(new Workspace.UISourceCode.Message(level, consoleMessage.messageText), consoleMessage, runtimeModel.target());
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export class PresentationSourceFrameMessageHelper {
|
|
63
|
+
#debuggerModel;
|
|
64
|
+
#cssModel;
|
|
65
|
+
#presentationMessages = new Map();
|
|
66
|
+
#locationPool;
|
|
67
|
+
constructor() {
|
|
68
|
+
this.#locationPool = new LiveLocationPool();
|
|
69
|
+
Workspace.Workspace.WorkspaceImpl.instance().addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, this.#uiSourceCodeAdded.bind(this));
|
|
70
|
+
}
|
|
71
|
+
setDebuggerModel(debuggerModel) {
|
|
72
|
+
if (this.#debuggerModel) {
|
|
73
|
+
throw new Error('Cannot set DebuggerModel twice');
|
|
74
|
+
}
|
|
75
|
+
this.#debuggerModel = debuggerModel;
|
|
76
|
+
// TODO(dgozman): queueMicrotask because we race with DebuggerWorkspaceBinding on ParsedScriptSource event delivery.
|
|
77
|
+
debuggerModel.addEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, event => {
|
|
78
|
+
queueMicrotask(() => {
|
|
79
|
+
this.#parsedScriptSource(event);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
debuggerModel.addEventListener(SDK.DebuggerModel.Events.GlobalObjectCleared, this.#debuggerReset, this);
|
|
83
|
+
}
|
|
84
|
+
setCSSModel(cssModel) {
|
|
85
|
+
if (this.#cssModel) {
|
|
86
|
+
throw new Error('Cannot set CSSModel twice');
|
|
87
|
+
}
|
|
88
|
+
this.#cssModel = cssModel;
|
|
89
|
+
cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, event => queueMicrotask(() => this.#styleSheetAdded(event)));
|
|
90
|
+
}
|
|
91
|
+
async addMessage(message, source) {
|
|
92
|
+
const presentation = new PresentationSourceFrameMessage(message, this.#locationPool);
|
|
93
|
+
const location = this.#rawLocation(source) ?? this.#cssLocation(source) ?? this.#uiLocation(source);
|
|
94
|
+
if (location) {
|
|
95
|
+
await presentation.updateLocationSource(location);
|
|
96
|
+
}
|
|
97
|
+
if (source.url) {
|
|
98
|
+
let messages = this.#presentationMessages.get(source.url);
|
|
99
|
+
if (!messages) {
|
|
100
|
+
messages = [];
|
|
101
|
+
this.#presentationMessages.set(source.url, messages);
|
|
102
|
+
}
|
|
103
|
+
messages.push({ source, presentation });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
#uiLocation(source) {
|
|
107
|
+
if (!source.url) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const uiSourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(source.url);
|
|
111
|
+
if (!uiSourceCode) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return new Workspace.UISourceCode.UILocation(uiSourceCode, source.line, source.column);
|
|
115
|
+
}
|
|
116
|
+
#cssLocation(source) {
|
|
117
|
+
if (!this.#cssModel || !source.url) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const locations = this.#cssModel.createRawLocationsByURL(source.url, source.line, source.column);
|
|
121
|
+
return locations[0] ?? null;
|
|
122
|
+
}
|
|
123
|
+
#rawLocation(source) {
|
|
124
|
+
if (!this.#debuggerModel) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
if (source.scriptId) {
|
|
128
|
+
return this.#debuggerModel.createRawLocationByScriptId(source.scriptId, source.line, source.column);
|
|
129
|
+
}
|
|
130
|
+
const callFrame = source.stackTrace?.callFrames ? source.stackTrace.callFrames[0] : null;
|
|
131
|
+
if (callFrame) {
|
|
132
|
+
return this.#debuggerModel.createRawLocationByScriptId(callFrame.scriptId, callFrame.lineNumber, callFrame.columnNumber);
|
|
133
|
+
}
|
|
134
|
+
if (source.url) {
|
|
135
|
+
return this.#debuggerModel.createRawLocationByURL(source.url, source.line, source.column);
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
#parsedScriptSource(event) {
|
|
140
|
+
const script = event.data;
|
|
141
|
+
const messages = this.#presentationMessages.get(script.sourceURL);
|
|
142
|
+
const promises = [];
|
|
143
|
+
for (const { presentation, source } of messages ?? []) {
|
|
144
|
+
const rawLocation = this.#rawLocation(source);
|
|
145
|
+
if (rawLocation && script.scriptId === rawLocation.scriptId) {
|
|
146
|
+
promises.push(presentation.updateLocationSource(rawLocation));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
void Promise.all(promises).then(this.parsedScriptSourceForTest.bind(this));
|
|
150
|
+
}
|
|
151
|
+
parsedScriptSourceForTest() {
|
|
152
|
+
}
|
|
153
|
+
#uiSourceCodeAdded(event) {
|
|
154
|
+
const uiSourceCode = event.data;
|
|
155
|
+
const messages = this.#presentationMessages.get(uiSourceCode.url());
|
|
156
|
+
const promises = [];
|
|
157
|
+
for (const { presentation, source } of messages ?? []) {
|
|
158
|
+
promises.push(presentation.updateLocationSource(new Workspace.UISourceCode.UILocation(uiSourceCode, source.line, source.column)));
|
|
159
|
+
}
|
|
160
|
+
void Promise.all(promises).then(this.uiSourceCodeAddedForTest.bind(this));
|
|
161
|
+
}
|
|
162
|
+
uiSourceCodeAddedForTest() {
|
|
163
|
+
}
|
|
164
|
+
#styleSheetAdded(event) {
|
|
165
|
+
const header = event.data;
|
|
166
|
+
const messages = this.#presentationMessages.get(header.sourceURL);
|
|
167
|
+
const promises = [];
|
|
168
|
+
for (const { source, presentation } of messages ?? []) {
|
|
169
|
+
if (header.containsLocation(source.line, source.column)) {
|
|
170
|
+
promises.push(presentation.updateLocationSource(new SDK.CSSModel.CSSLocation(header, source.line, source.column)));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
void Promise.all(promises).then(this.styleSheetAddedForTest.bind(this));
|
|
174
|
+
}
|
|
175
|
+
styleSheetAddedForTest() {
|
|
176
|
+
}
|
|
177
|
+
clear() {
|
|
178
|
+
this.#debuggerReset();
|
|
179
|
+
}
|
|
180
|
+
#debuggerReset() {
|
|
181
|
+
const presentations = Array.from(this.#presentationMessages.values()).flat();
|
|
182
|
+
for (const { presentation } of presentations) {
|
|
183
|
+
presentation.dispose();
|
|
184
|
+
}
|
|
185
|
+
this.#presentationMessages.clear();
|
|
186
|
+
this.#locationPool.disposeAll();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
class FrozenLiveLocation extends LiveLocationWithPool {
|
|
190
|
+
#uiLocation;
|
|
191
|
+
constructor(uiLocation, updateDelegate, locationPool) {
|
|
192
|
+
super(updateDelegate, locationPool);
|
|
193
|
+
this.#uiLocation = uiLocation;
|
|
194
|
+
}
|
|
195
|
+
async uiLocation() {
|
|
196
|
+
return this.#uiLocation;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export class PresentationSourceFrameMessage {
|
|
200
|
+
#uiSourceCode;
|
|
201
|
+
#liveLocation;
|
|
202
|
+
#locationPool;
|
|
203
|
+
#message;
|
|
204
|
+
constructor(message, locationPool) {
|
|
205
|
+
this.#message = message;
|
|
206
|
+
this.#locationPool = locationPool;
|
|
207
|
+
}
|
|
208
|
+
async updateLocationSource(source) {
|
|
209
|
+
if (source instanceof SDK.DebuggerModel.Location) {
|
|
210
|
+
await DebuggerWorkspaceBinding.instance().createLiveLocation(source, this.#updateLocation.bind(this), this.#locationPool);
|
|
211
|
+
}
|
|
212
|
+
else if (source instanceof SDK.CSSModel.CSSLocation) {
|
|
213
|
+
await CSSWorkspaceBinding.instance().createLiveLocation(source, this.#updateLocation.bind(this), this.#locationPool);
|
|
214
|
+
}
|
|
215
|
+
else if (source instanceof Workspace.UISourceCode.UILocation) {
|
|
216
|
+
if (!this.#liveLocation) { // Don't "downgrade" the location if a debugger or css mapping was already successful
|
|
217
|
+
this.#liveLocation = new FrozenLiveLocation(source, this.#updateLocation.bind(this), this.#locationPool);
|
|
218
|
+
await this.#liveLocation.update();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async #updateLocation(liveLocation) {
|
|
223
|
+
if (this.#uiSourceCode) {
|
|
224
|
+
this.#uiSourceCode.removeMessage(this.#message);
|
|
225
|
+
}
|
|
226
|
+
if (liveLocation !== this.#liveLocation) {
|
|
227
|
+
this.#uiSourceCode?.removeMessage(this.#message);
|
|
228
|
+
this.#liveLocation?.dispose();
|
|
229
|
+
this.#liveLocation = liveLocation;
|
|
230
|
+
}
|
|
231
|
+
const uiLocation = await liveLocation.uiLocation();
|
|
232
|
+
if (!uiLocation) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.#message.range =
|
|
236
|
+
TextUtils.TextRange.TextRange.createFromLocation(uiLocation.lineNumber, uiLocation.columnNumber || 0);
|
|
237
|
+
this.#uiSourceCode = uiLocation.uiSourceCode;
|
|
238
|
+
this.#uiSourceCode.addMessage(this.#message);
|
|
239
|
+
}
|
|
240
|
+
dispose() {
|
|
241
|
+
this.#uiSourceCode?.removeMessage(this.#message);
|
|
242
|
+
this.#liveLocation?.dispose();
|
|
243
|
+
}
|
|
244
|
+
}
|