chrome-devtools-mcp 1.1.0 → 1.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 +12 -3
- package/build/src/DevtoolsUtils.js +57 -0
- package/build/src/HeapSnapshotManager.js +5 -0
- package/build/src/McpContext.js +28 -8
- package/build/src/McpPage.js +9 -9
- package/build/src/McpResponse.js +101 -53
- package/build/src/PageCollector.js +7 -7
- package/build/src/ServiceWorkerCollector.js +171 -0
- package/build/src/TextSnapshot.js +1 -1
- package/build/src/ToolHandler.js +11 -5
- package/build/src/WaitForHelper.js +2 -2
- package/build/src/bin/chrome-devtools-cli-options.js +22 -4
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +19 -4
- package/build/src/bin/chrome-devtools-mcp-main.js +5 -5
- package/build/src/browser.js +8 -4
- package/build/src/daemon/client.js +7 -7
- package/build/src/daemon/daemon.js +12 -12
- package/build/src/daemon/utils.js +2 -2
- package/build/src/formatters/IssueFormatter.js +4 -4
- package/build/src/index.js +13 -1
- package/build/src/telemetry/ClearcutLogger.js +1 -1
- package/build/src/telemetry/WatchdogClient.js +4 -4
- package/build/src/telemetry/persistence.js +2 -2
- package/build/src/telemetry/watchdog/ClearcutSender.js +10 -10
- package/build/src/telemetry/watchdog/main.js +5 -5
- package/build/src/third_party/THIRD_PARTY_NOTICES +30 -0
- package/build/src/third_party/bundled-packages.json +2 -1
- package/build/src/third_party/devtools-formatter-worker.js +1 -0
- package/build/src/third_party/devtools-heap-snapshot-worker.js +107 -0
- package/build/src/third_party/index.js +5906 -4913
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
- package/build/src/tools/console.js +7 -0
- package/build/src/tools/emulation.js +1 -0
- package/build/src/tools/extensions.js +5 -2
- package/build/src/tools/input.js +11 -3
- package/build/src/tools/lighthouse.js +11 -6
- package/build/src/tools/memory.js +33 -10
- package/build/src/tools/network.js +2 -2
- package/build/src/tools/pages.js +13 -5
- package/build/src/tools/performance.js +8 -7
- package/build/src/tools/screencast.js +2 -1
- package/build/src/tools/screenshot.js +1 -1
- package/build/src/tools/script.js +2 -2
- package/build/src/tools/slim/tools.js +3 -0
- package/build/src/tools/snapshot.js +3 -2
- package/build/src/tools/thirdPartyDeveloper.js +12 -2
- package/build/src/tools/webmcp.js +2 -0
- package/build/src/trace-processing/parse.js +5 -5
- package/build/src/version.js +1 -1
- package/package.json +14 -13
package/build/src/index.js
CHANGED
|
@@ -43,7 +43,7 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
43
43
|
context?.setRoots(roots.roots);
|
|
44
44
|
}
|
|
45
45
|
catch (e) {
|
|
46
|
-
logger('Failed to list roots', e);
|
|
46
|
+
logger?.('Failed to list roots', e);
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
server.server.oninitialized = () => {
|
|
@@ -66,6 +66,12 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
66
66
|
chromeArgs.push(`--proxy-server=${serverArgs.proxyServer}`);
|
|
67
67
|
}
|
|
68
68
|
const devtools = serverArgs.experimentalDevtools ?? false;
|
|
69
|
+
const blocklist = serverArgs.blockedUrlPattern
|
|
70
|
+
? serverArgs.blockedUrlPattern.map(String)
|
|
71
|
+
: undefined;
|
|
72
|
+
const allowlist = serverArgs.allowedUrlPattern
|
|
73
|
+
? serverArgs.allowedUrlPattern.map(String)
|
|
74
|
+
: undefined;
|
|
69
75
|
const browser = serverArgs.browserUrl || serverArgs.wsEndpoint || serverArgs.autoConnect
|
|
70
76
|
? await ensureBrowserConnected({
|
|
71
77
|
browserURL: serverArgs.browserUrl,
|
|
@@ -77,6 +83,8 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
77
83
|
: undefined,
|
|
78
84
|
userDataDir: serverArgs.userDataDir,
|
|
79
85
|
devtools,
|
|
86
|
+
blocklist,
|
|
87
|
+
allowlist,
|
|
80
88
|
})
|
|
81
89
|
: await ensureBrowserLaunched({
|
|
82
90
|
headless: serverArgs.headless,
|
|
@@ -92,12 +100,16 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
92
100
|
devtools,
|
|
93
101
|
enableExtensions: serverArgs.categoryExtensions,
|
|
94
102
|
viaCli: serverArgs.viaCli,
|
|
103
|
+
blocklist,
|
|
104
|
+
allowlist,
|
|
95
105
|
});
|
|
96
106
|
if (context?.browser !== browser) {
|
|
97
107
|
context = await McpContext.from(browser, logger, {
|
|
98
108
|
experimentalDevToolsDebugging: devtools,
|
|
99
109
|
experimentalIncludeAllPages: serverArgs.experimentalIncludeAllPages,
|
|
100
110
|
performanceCrux: serverArgs.performanceCrux,
|
|
111
|
+
hasNetworkBlockOrAllowlist: Boolean((blocklist && blocklist.length > 0) ||
|
|
112
|
+
(allowlist && allowlist.length > 0)),
|
|
101
113
|
});
|
|
102
114
|
await updateRoots();
|
|
103
115
|
}
|
|
@@ -35,10 +35,10 @@ export class WatchdogClient {
|
|
|
35
35
|
});
|
|
36
36
|
this.#childProcess.unref();
|
|
37
37
|
this.#childProcess.on('error', err => {
|
|
38
|
-
logger('Watchdog process error:', err);
|
|
38
|
+
logger?.('Watchdog process error:', err);
|
|
39
39
|
});
|
|
40
40
|
this.#childProcess.on('exit', (code, signal) => {
|
|
41
|
-
logger(`Watchdog exited with code ${code} and signal ${signal}`);
|
|
41
|
+
logger?.(`Watchdog exited with code ${code} and signal ${signal}`);
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
send(message) {
|
|
@@ -50,11 +50,11 @@ export class WatchdogClient {
|
|
|
50
50
|
this.#childProcess.stdin.write(line);
|
|
51
51
|
}
|
|
52
52
|
catch (err) {
|
|
53
|
-
logger('Failed to write to watchdog stdin', err);
|
|
53
|
+
logger?.('Failed to write to watchdog stdin', err);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
else {
|
|
57
|
-
logger('Watchdog stdin not available, dropping message');
|
|
57
|
+
logger?.('Watchdog stdin not available, dropping message');
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -45,7 +45,7 @@ export class FilePersistence {
|
|
|
45
45
|
return JSON.parse(content);
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
48
|
-
logger(`Failed to read telemetry state from ${filePath}:`, error);
|
|
48
|
+
logger?.(`Failed to read telemetry state from ${filePath}:`, error);
|
|
49
49
|
void ClearcutLogger.get()?.logServerError({
|
|
50
50
|
errorCode: ErrorCode.ERROR_CODE_PERSISTENCE_FILE_READ_FAILED,
|
|
51
51
|
});
|
|
@@ -62,7 +62,7 @@ export class FilePersistence {
|
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
64
|
// Ignore errors during state saving to avoid crashing the server
|
|
65
|
-
logger(`Failed to save telemetry state to ${filePath}:`, error);
|
|
65
|
+
logger?.(`Failed to save telemetry state to ${filePath}:`, error);
|
|
66
66
|
void ClearcutLogger.get()?.logServerError({
|
|
67
67
|
errorCode: ErrorCode.ERROR_CODE_PERSISTENCE_FILE_SAVE_FAILED,
|
|
68
68
|
});
|
|
@@ -48,7 +48,7 @@ export class ClearcutSender {
|
|
|
48
48
|
app_version: this.#appVersion,
|
|
49
49
|
os_type: this.#osType,
|
|
50
50
|
};
|
|
51
|
-
logger('Enqueing telemetry event', JSON.stringify(eventToSend, null, 2));
|
|
51
|
+
logger?.('Enqueing telemetry event', JSON.stringify(eventToSend, null, 2));
|
|
52
52
|
this.#addToBuffer(eventToSend);
|
|
53
53
|
if (!this.#timerStarted) {
|
|
54
54
|
this.#timerStarted = true;
|
|
@@ -69,10 +69,10 @@ export class ClearcutSender {
|
|
|
69
69
|
this.#finalFlush(),
|
|
70
70
|
new Promise(resolve => setTimeout(resolve, SHUTDOWN_TIMEOUT_MS)),
|
|
71
71
|
]);
|
|
72
|
-
logger('Final flush completed');
|
|
72
|
+
logger?.('Final flush completed');
|
|
73
73
|
}
|
|
74
74
|
catch (error) {
|
|
75
|
-
logger('Final flush failed:', error);
|
|
75
|
+
logger?.('Final flush failed:', error);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
async #flush() {
|
|
@@ -97,7 +97,7 @@ export class ClearcutSender {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
else if (result.isPermanentError) {
|
|
100
|
-
logger('Permanent error, dropped batch of', eventsToSend.length, 'events');
|
|
100
|
+
logger?.('Permanent error, dropped batch of', eventsToSend.length, 'events');
|
|
101
101
|
}
|
|
102
102
|
else {
|
|
103
103
|
// Transient error: Requeue events at the front of the buffer
|
|
@@ -108,7 +108,7 @@ export class ClearcutSender {
|
|
|
108
108
|
catch (error) {
|
|
109
109
|
// Safety catch for unexpected errors, requeue events
|
|
110
110
|
this.#buffer = [...eventsToSend, ...this.#buffer];
|
|
111
|
-
logger('Flush failed unexpectedly:', error);
|
|
111
|
+
logger?.('Flush failed unexpectedly:', error);
|
|
112
112
|
}
|
|
113
113
|
finally {
|
|
114
114
|
this.#isFlushing = false;
|
|
@@ -118,7 +118,7 @@ export class ClearcutSender {
|
|
|
118
118
|
#addToBuffer(event) {
|
|
119
119
|
if (this.#buffer.length >= MAX_BUFFER_SIZE) {
|
|
120
120
|
this.#buffer.shift();
|
|
121
|
-
logger('Telemetry buffer overflow: dropped oldest event');
|
|
121
|
+
logger?.('Telemetry buffer overflow: dropped oldest event');
|
|
122
122
|
}
|
|
123
123
|
this.#buffer.push({
|
|
124
124
|
event,
|
|
@@ -126,18 +126,18 @@ export class ClearcutSender {
|
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
128
|
#scheduleFlush(delayMs) {
|
|
129
|
-
logger(`Scheduling flush in ${delayMs}`);
|
|
129
|
+
logger?.(`Scheduling flush in ${delayMs}`);
|
|
130
130
|
if (this.#flushTimer) {
|
|
131
131
|
clearTimeout(this.#flushTimer);
|
|
132
132
|
}
|
|
133
133
|
this.#flushTimer = setTimeout(() => {
|
|
134
134
|
this.#flush().catch(err => {
|
|
135
|
-
logger('Flush error:', err);
|
|
135
|
+
logger?.('Flush error:', err);
|
|
136
136
|
});
|
|
137
137
|
}, delayMs);
|
|
138
138
|
}
|
|
139
139
|
async #sendBatch(events) {
|
|
140
|
-
logger(`Sending batch of ${events.length}`);
|
|
140
|
+
logger?.(`Sending batch of ${events.length}`);
|
|
141
141
|
const requestBody = {
|
|
142
142
|
log_source: LOG_SOURCE,
|
|
143
143
|
request_time_ms: Date.now().toString(),
|
|
@@ -176,7 +176,7 @@ export class ClearcutSender {
|
|
|
176
176
|
if (status >= 500 || status === 429) {
|
|
177
177
|
return { success: false };
|
|
178
178
|
}
|
|
179
|
-
logger('Telemetry permanent error:', status);
|
|
179
|
+
logger?.('Telemetry permanent error:', status);
|
|
180
180
|
return { success: false, isPermanentError: true };
|
|
181
181
|
}
|
|
182
182
|
catch {
|
|
@@ -65,7 +65,7 @@ function main() {
|
|
|
65
65
|
process.exit(code);
|
|
66
66
|
});
|
|
67
67
|
};
|
|
68
|
-
logger('Watchdog started', JSON.stringify({
|
|
68
|
+
logger?.('Watchdog started', JSON.stringify({
|
|
69
69
|
pid: process.pid,
|
|
70
70
|
parentPid,
|
|
71
71
|
version: appVersion,
|
|
@@ -84,15 +84,15 @@ function main() {
|
|
|
84
84
|
return;
|
|
85
85
|
}
|
|
86
86
|
isShuttingDown = true;
|
|
87
|
-
logger(`Parent death detected (${reason}). Sending shutdown event...`);
|
|
87
|
+
logger?.(`Parent death detected (${reason}). Sending shutdown event...`);
|
|
88
88
|
sender
|
|
89
89
|
.sendShutdownEvent()
|
|
90
90
|
.then(() => {
|
|
91
|
-
logger('Shutdown event sent. Exiting.');
|
|
91
|
+
logger?.('Shutdown event sent. Exiting.');
|
|
92
92
|
exit(0);
|
|
93
93
|
})
|
|
94
94
|
.catch(err => {
|
|
95
|
-
logger('Failed to send shutdown event', err);
|
|
95
|
+
logger?.('Failed to send shutdown event', err);
|
|
96
96
|
exit(1);
|
|
97
97
|
});
|
|
98
98
|
}
|
|
@@ -114,7 +114,7 @@ function main() {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
catch (err) {
|
|
117
|
-
logger('Failed to parse IPC message', err);
|
|
117
|
+
logger?.('Failed to parse IPC message', err);
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
120
|
}
|
|
@@ -906,6 +906,36 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
906
906
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
907
907
|
|
|
908
908
|
|
|
909
|
+
-------------------- DEPENDENCY DIVIDER --------------------
|
|
910
|
+
|
|
911
|
+
Name: @toon-format/toon
|
|
912
|
+
URL: https://toonformat.dev
|
|
913
|
+
Version: 2.2.0
|
|
914
|
+
License: MIT
|
|
915
|
+
|
|
916
|
+
MIT License
|
|
917
|
+
|
|
918
|
+
Copyright (c) 2025-PRESENT Johann Schopplich
|
|
919
|
+
|
|
920
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
921
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
922
|
+
in the Software without restriction, including without limitation the rights
|
|
923
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
924
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
925
|
+
furnished to do so, subject to the following conditions:
|
|
926
|
+
|
|
927
|
+
The above copyright notice and this permission notice shall be included in all
|
|
928
|
+
copies or substantial portions of the Software.
|
|
929
|
+
|
|
930
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
931
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
932
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
933
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
934
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
935
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
936
|
+
SOFTWARE.
|
|
937
|
+
|
|
938
|
+
|
|
909
939
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
910
940
|
|
|
911
941
|
Name: chrome-devtools-frontend
|
|
@@ -2648,6 +2648,7 @@ var ExperimentName;
|
|
|
2648
2648
|
ExperimentName["USE_SOURCE_MAP_SCOPES"] = "use-source-map-scopes";
|
|
2649
2649
|
ExperimentName["DURABLE_MESSAGES"] = "durable-messages";
|
|
2650
2650
|
ExperimentName["JPEG_XL"] = "jpeg-xl";
|
|
2651
|
+
ExperimentName["PLUS_BUTTON"] = "plus-button";
|
|
2651
2652
|
})(ExperimentName || (ExperimentName = {}));
|
|
2652
2653
|
|
|
2653
2654
|
// Copyright 2016 The Chromium Authors
|
|
@@ -2843,6 +2843,7 @@ var ExperimentName;
|
|
|
2843
2843
|
ExperimentName["USE_SOURCE_MAP_SCOPES"] = "use-source-map-scopes";
|
|
2844
2844
|
ExperimentName["DURABLE_MESSAGES"] = "durable-messages";
|
|
2845
2845
|
ExperimentName["JPEG_XL"] = "jpeg-xl";
|
|
2846
|
+
ExperimentName["PLUS_BUTTON"] = "plus-button";
|
|
2846
2847
|
})(ExperimentName || (ExperimentName = {}));
|
|
2847
2848
|
|
|
2848
2849
|
// Copyright 2021 The Chromium Authors
|
|
@@ -5992,6 +5993,9 @@ class HeapSnapshotProxy extends HeapSnapshotProxyObject {
|
|
|
5992
5993
|
ignoreNodeInRetainersView(nodeIndex) {
|
|
5993
5994
|
return this.callMethodPromise('ignoreNodeInRetainersView', nodeIndex);
|
|
5994
5995
|
}
|
|
5996
|
+
getRetainingPaths(nodeIndex, maxDepth, maxNodes, maxSiblings) {
|
|
5997
|
+
return this.callMethodPromise('getRetainingPaths', nodeIndex, maxDepth, maxNodes, maxSiblings);
|
|
5998
|
+
}
|
|
5995
5999
|
unignoreNodeInRetainersView(nodeIndex) {
|
|
5996
6000
|
return this.callMethodPromise('unignoreNodeInRetainersView', nodeIndex);
|
|
5997
6001
|
}
|
|
@@ -8359,6 +8363,109 @@ class HeapSnapshot {
|
|
|
8359
8363
|
const indexProvider = new HeapSnapshotRetainerEdgeIndexProvider(this);
|
|
8360
8364
|
return new HeapSnapshotEdgesProvider(this, filter, node.retainers(), indexProvider);
|
|
8361
8365
|
}
|
|
8366
|
+
getRetainingPaths(nodeIndex, maxDepth = 30, maxNodes = 5000, maxSiblings = 100) {
|
|
8367
|
+
const { nodeFieldCount, firstRetainerIndex, retainingNodes, retainingEdges, edgeTypeOffset, edgeWeakType, containmentEdges } = this;
|
|
8368
|
+
const distances = this.#nodeDistancesForRetainersView ?? this.nodeDistances;
|
|
8369
|
+
let traversedNodesCount = 0;
|
|
8370
|
+
const visiting = new Set();
|
|
8371
|
+
const visited = new Map();
|
|
8372
|
+
const rootDistance = 2;
|
|
8373
|
+
const limitsReached = {};
|
|
8374
|
+
const buildForest = (currentIndex, currentDepth) => {
|
|
8375
|
+
traversedNodesCount++;
|
|
8376
|
+
if (traversedNodesCount > maxNodes) {
|
|
8377
|
+
limitsReached.nodes = true;
|
|
8378
|
+
return [];
|
|
8379
|
+
}
|
|
8380
|
+
if (currentDepth >= maxDepth) {
|
|
8381
|
+
limitsReached.depth = true;
|
|
8382
|
+
return [];
|
|
8383
|
+
}
|
|
8384
|
+
const ordinal = currentIndex / nodeFieldCount;
|
|
8385
|
+
const currentDistance = distances[ordinal];
|
|
8386
|
+
if (currentDistance <= rootDistance) {
|
|
8387
|
+
return [];
|
|
8388
|
+
}
|
|
8389
|
+
if (visiting.has(currentIndex)) {
|
|
8390
|
+
return [];
|
|
8391
|
+
}
|
|
8392
|
+
const cachedDepth = visited.get(currentIndex);
|
|
8393
|
+
if (cachedDepth !== undefined) {
|
|
8394
|
+
if (currentDepth >= cachedDepth) {
|
|
8395
|
+
return [];
|
|
8396
|
+
}
|
|
8397
|
+
}
|
|
8398
|
+
visiting.add(currentIndex);
|
|
8399
|
+
const beginRetainerIndex = firstRetainerIndex[ordinal];
|
|
8400
|
+
const endRetainerIndex = firstRetainerIndex[ordinal + 1];
|
|
8401
|
+
const retainers = [];
|
|
8402
|
+
for (let retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) {
|
|
8403
|
+
const retainerNodeIndex = retainingNodes[retainerIndex];
|
|
8404
|
+
const retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount;
|
|
8405
|
+
const dist = distances[retainerNodeOrdinal];
|
|
8406
|
+
const globalEdgeIndex = retainingEdges[retainerIndex];
|
|
8407
|
+
if (this.isEdgeIgnoredInRetainersView(globalEdgeIndex)) {
|
|
8408
|
+
continue;
|
|
8409
|
+
}
|
|
8410
|
+
const edgeType = containmentEdges.getValue(globalEdgeIndex + edgeTypeOffset);
|
|
8411
|
+
if (edgeType === edgeWeakType) {
|
|
8412
|
+
continue;
|
|
8413
|
+
}
|
|
8414
|
+
if (dist >= 0) {
|
|
8415
|
+
const remainingDepth = maxDepth - currentDepth;
|
|
8416
|
+
const neededDepth = dist - rootDistance;
|
|
8417
|
+
if (neededDepth < remainingDepth) {
|
|
8418
|
+
retainers.push({ retainerIndex, dist, nodeIndex: retainerNodeIndex });
|
|
8419
|
+
}
|
|
8420
|
+
else {
|
|
8421
|
+
limitsReached.depth = true;
|
|
8422
|
+
}
|
|
8423
|
+
}
|
|
8424
|
+
}
|
|
8425
|
+
retainers.sort((a, b) => a.dist - b.dist);
|
|
8426
|
+
const length = Math.min(retainers.length, maxSiblings);
|
|
8427
|
+
if (retainers.length > maxSiblings) {
|
|
8428
|
+
limitsReached.siblings = true;
|
|
8429
|
+
}
|
|
8430
|
+
const forest = [];
|
|
8431
|
+
for (let i = 0; i < length; i++) {
|
|
8432
|
+
const retainer = retainers[i];
|
|
8433
|
+
const edge = this.createRetainingEdge(retainer.retainerIndex);
|
|
8434
|
+
const globalEdgeIndex = retainingEdges[retainer.retainerIndex];
|
|
8435
|
+
const isRoot = retainer.dist === rootDistance;
|
|
8436
|
+
let children = [];
|
|
8437
|
+
if (isRoot) {
|
|
8438
|
+
traversedNodesCount++;
|
|
8439
|
+
if (traversedNodesCount > maxNodes) {
|
|
8440
|
+
limitsReached.nodes = true;
|
|
8441
|
+
break;
|
|
8442
|
+
}
|
|
8443
|
+
}
|
|
8444
|
+
else {
|
|
8445
|
+
children = buildForest(retainer.nodeIndex, currentDepth + 1);
|
|
8446
|
+
if (children.length === 0) {
|
|
8447
|
+
continue;
|
|
8448
|
+
}
|
|
8449
|
+
}
|
|
8450
|
+
const retainerNode = this.createNode(retainer.nodeIndex);
|
|
8451
|
+
forest.push({
|
|
8452
|
+
edgeIndex: globalEdgeIndex,
|
|
8453
|
+
edgeName: edge.name(),
|
|
8454
|
+
edgeType: edge.type(),
|
|
8455
|
+
nodeId: retainerNode.id(),
|
|
8456
|
+
nodeIndex: retainer.nodeIndex,
|
|
8457
|
+
nodeName: retainerNode.name(),
|
|
8458
|
+
distance: retainer.dist,
|
|
8459
|
+
children,
|
|
8460
|
+
});
|
|
8461
|
+
}
|
|
8462
|
+
visiting.delete(currentIndex);
|
|
8463
|
+
visited.set(currentIndex, currentDepth);
|
|
8464
|
+
return forest;
|
|
8465
|
+
};
|
|
8466
|
+
const paths = buildForest(nodeIndex, 0);
|
|
8467
|
+
return { paths, limitsReached };
|
|
8468
|
+
}
|
|
8362
8469
|
createAddedNodesProvider(baseSnapshotId, classKey) {
|
|
8363
8470
|
const snapshotDiff = this.#snapshotDiffs[baseSnapshotId];
|
|
8364
8471
|
const diffForClass = snapshotDiff[classKey];
|