chrome-devtools-mcp 1.3.0 → 1.5.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 +42 -5
- package/build/src/HeapSnapshotManager.js +60 -3
- package/build/src/McpContext.js +47 -5
- package/build/src/McpResponse.js +66 -7
- package/build/src/bin/check-latest-version.js +1 -0
- package/build/src/bin/chrome-devtools-cli-options.js +51 -3
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +3 -3
- package/build/src/bin/chrome-devtools.js +25 -1
- package/build/src/daemon/daemon.js +1 -1
- package/build/src/formatters/HeapSnapshotFormatter.js +43 -0
- package/build/src/formatters/NetworkFormatter.js +3 -1
- package/build/src/index.js +2 -2
- package/build/src/issue-descriptions.js +6 -4
- package/build/src/third_party/THIRD_PARTY_NOTICES +3 -33
- package/build/src/third_party/bundled-packages.json +3 -4
- package/build/src/third_party/devtools-heap-snapshot-worker.js +94 -0
- package/build/src/third_party/index.js +2396 -896
- package/build/src/tools/memory.js +60 -4
- package/build/src/tools/screencast.js +1 -2
- package/build/src/tools/script.js +3 -10
- package/build/src/tools/thirdPartyDeveloper.js +6 -6
- package/build/src/utils/check-for-updates.js +1 -0
- package/build/src/utils/files.js +0 -4
- package/build/src/version.js +1 -1
- package/package.json +15 -6
- package/skills/a11y-debugging/SKILL.md +89 -0
- package/skills/a11y-debugging/references/a11y-snippets.md +92 -0
- package/skills/chrome-devtools/SKILL.md +72 -0
- package/skills/chrome-devtools-cli/SKILL.md +153 -0
- package/skills/chrome-devtools-cli/references/installation.md +14 -0
- package/skills/debug-optimize-lcp/SKILL.md +121 -0
- package/skills/debug-optimize-lcp/references/elements-and-size.md +27 -0
- package/skills/debug-optimize-lcp/references/lcp-breakdown.md +23 -0
- package/skills/debug-optimize-lcp/references/lcp-snippets.md +79 -0
- package/skills/debug-optimize-lcp/references/optimization-strategies.md +38 -0
- package/skills/memory-leak-debugging/SKILL.md +50 -0
- package/skills/memory-leak-debugging/references/common-leaks.md +33 -0
- package/skills/memory-leak-debugging/references/compare_snapshots.js +109 -0
- package/skills/memory-leak-debugging/references/memlab.md +29 -0
- package/skills/troubleshooting/SKILL.md +98 -0
|
@@ -157,7 +157,7 @@ export const cliOptions = {
|
|
|
157
157
|
},
|
|
158
158
|
experimentalToonFormat: {
|
|
159
159
|
type: 'boolean',
|
|
160
|
-
describe: 'Whether to format structured data in text response using Token-Oriented Object Notation.
|
|
160
|
+
describe: 'Whether to format structured data in text response using Token-Oriented Object Notation (requires @toon-format/toon). If running via npx, use: npx --package chrome-devtools-mcp@latest --package @toon-format/toon@latest chrome-devtools-mcp --experimentalToonFormat',
|
|
161
161
|
hidden: true,
|
|
162
162
|
},
|
|
163
163
|
experimentalIncludeAllPages: {
|
|
@@ -193,12 +193,12 @@ export const cliOptions = {
|
|
|
193
193
|
},
|
|
194
194
|
blockedUrlPattern: {
|
|
195
195
|
type: 'array',
|
|
196
|
-
describe:
|
|
196
|
+
describe: "Restricts browser's network access by blocking specified URL patterns (uses https://urlpattern.spec.whatwg.org/). Silently detaches from targets with blocked URLs upon connection, and blocks runtime requests (including navigations and subresources). Accepts an array of patterns.",
|
|
197
197
|
conflicts: ['allowedUrlPattern'],
|
|
198
198
|
},
|
|
199
199
|
allowedUrlPattern: {
|
|
200
200
|
type: 'array',
|
|
201
|
-
describe:
|
|
201
|
+
describe: "Restricts browser's network access by allowing only specified URL patterns (uses https://urlpattern.spec.whatwg.org/). Requires Chrome 149+. Silently detaches from targets with unallowed URLs upon connection, and blocks runtime requests (including navigations and subresources). Accepts an array of patterns.",
|
|
202
202
|
conflicts: ['blockedUrlPattern'],
|
|
203
203
|
},
|
|
204
204
|
ignoreDefaultChromeArg: {
|
|
@@ -41,6 +41,7 @@ startCliOptions.isolated.description =
|
|
|
41
41
|
'If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed. Defaults to true unless userDataDir is provided.';
|
|
42
42
|
startCliOptions.categoryExtensions.default = true;
|
|
43
43
|
const y = yargs(hideBin(process.argv))
|
|
44
|
+
.locale('en') // Force English to ensure error string matching works in .fail, all custom messages we output are in English anyways
|
|
44
45
|
.scriptName('chrome-devtools')
|
|
45
46
|
.showHelpOnFail(true)
|
|
46
47
|
.usage('chrome-devtools <command> [...args] --flags')
|
|
@@ -55,7 +56,30 @@ const y = yargs(hideBin(process.argv))
|
|
|
55
56
|
.version(VERSION)
|
|
56
57
|
.strict()
|
|
57
58
|
.help(true)
|
|
58
|
-
.wrap(120)
|
|
59
|
+
.wrap(120)
|
|
60
|
+
.fail((msg, err) => {
|
|
61
|
+
if (msg) {
|
|
62
|
+
console.error('Error:', msg);
|
|
63
|
+
if (msg.includes('Not enough non-option arguments') ||
|
|
64
|
+
msg.includes('Unknown argument') ||
|
|
65
|
+
msg.includes('Unknown arguments')) {
|
|
66
|
+
console.error('\n=========================================');
|
|
67
|
+
console.error('💡 TIP FOR AI AGENT / DEVELOPER:');
|
|
68
|
+
console.error('In the `chrome-devtools` CLI:');
|
|
69
|
+
console.error('1. Required parameters MUST be passed as positional arguments (without flags).');
|
|
70
|
+
console.error(' - INCORRECT: chrome-devtools evaluate_script --expression "() => document.title"');
|
|
71
|
+
console.error(' - CORRECT: chrome-devtools evaluate_script "() => document.title"');
|
|
72
|
+
console.error('2. Optional parameters are passed as double-dash options/flags (e.g. --pageId 1).');
|
|
73
|
+
console.error('3. Make sure to escape quotes properly for your shell environment.');
|
|
74
|
+
console.error('Run `chrome-devtools <command> --help` to see exact positional and optional parameters.');
|
|
75
|
+
console.error('=========================================');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (err) {
|
|
79
|
+
console.error(err);
|
|
80
|
+
}
|
|
81
|
+
process.exit(1);
|
|
82
|
+
});
|
|
59
83
|
y.command('start', 'Start or restart chrome-devtools-mcp', y => y
|
|
60
84
|
.options(startCliOptions)
|
|
61
85
|
.example('$0 start --browserUrl http://localhost:9222', 'Start the server connecting to an existing browser')
|
|
@@ -23,7 +23,7 @@ const pidFilePath = getPidFilePath(sessionId);
|
|
|
23
23
|
const pidDir = path.dirname(pidFilePath);
|
|
24
24
|
const currentUserUid = os.userInfo().uid;
|
|
25
25
|
try {
|
|
26
|
-
fs.mkdirSync(pidDir, { recursive: true });
|
|
26
|
+
fs.mkdirSync(pidDir, { recursive: true, mode: 0o700 });
|
|
27
27
|
if (os.platform() !== 'win32') {
|
|
28
28
|
// POSIX specific checks
|
|
29
29
|
try {
|
|
@@ -67,6 +67,16 @@ export class HeapSnapshotFormatter {
|
|
|
67
67
|
}
|
|
68
68
|
return lines.join('\n');
|
|
69
69
|
}
|
|
70
|
+
static formatDuplicateStrings(groups) {
|
|
71
|
+
const lines = [];
|
|
72
|
+
lines.push('value,count,totalSelfSize,totalRetainedSize,truncated,nodeIds');
|
|
73
|
+
for (const group of groups) {
|
|
74
|
+
const nodeIds = group.nodes.map(n => `@${n.id}`).join(' ');
|
|
75
|
+
const truncated = group.truncated ?? false;
|
|
76
|
+
lines.push(`${JSON.stringify(group.value)},${group.count},${DevTools.I18n.ByteUtilities.formatBytesToKb(group.totalSelfSize)},${DevTools.I18n.ByteUtilities.formatBytesToKb(group.totalRetainedSize)},${truncated},${nodeIds}`);
|
|
77
|
+
}
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
}
|
|
70
80
|
#getSortedAggregates() {
|
|
71
81
|
return Object.values(this.#aggregates).sort((a, b) => b.maxRet - a.maxRet);
|
|
72
82
|
}
|
|
@@ -93,5 +103,38 @@ export class HeapSnapshotFormatter {
|
|
|
93
103
|
static sort(aggregates) {
|
|
94
104
|
return Object.entries(aggregates).sort((a, b) => b[1].maxRet - a[1].maxRet);
|
|
95
105
|
}
|
|
106
|
+
static formatDiffSummary(diffs) {
|
|
107
|
+
const lines = [];
|
|
108
|
+
lines.push('index,className,addedCount,removedCount,countDelta,addedSize,removedSize,sizeDelta');
|
|
109
|
+
let index = 0;
|
|
110
|
+
for (const diff of diffs) {
|
|
111
|
+
lines.push(`${index},${diff.className},${diff.addedCount},${diff.removedCount},${diff.countDelta},${DevTools.I18n.ByteUtilities.formatBytesToKb(diff.addedSize)},${DevTools.I18n.ByteUtilities.formatBytesToKb(diff.removedSize)},${DevTools.I18n.ByteUtilities.formatBytesToKb(diff.sizeDelta)}`);
|
|
112
|
+
index++;
|
|
113
|
+
}
|
|
114
|
+
return lines.join('\n');
|
|
115
|
+
}
|
|
116
|
+
static formatDiffDetails(diff) {
|
|
117
|
+
const lines = [];
|
|
118
|
+
lines.push(`${diff.className}: # new: ${diff.addedCount}, # deleted: ${diff.removedCount}, # delta: ${formatSignedCount(diff.countDelta)}, alloc size: ${formatSignedSize(diff.addedSize)}, freed size: ${formatSignedSize(diff.removedSize)}, size delta: ${formatSignedSize(diff.sizeDelta)}`);
|
|
119
|
+
const addedIds = diff.addedIds;
|
|
120
|
+
const addedSelfSizes = diff.addedSelfSizes;
|
|
121
|
+
const deletedIds = diff.deletedIds;
|
|
122
|
+
const deletedSelfSizes = diff.deletedSelfSizes;
|
|
123
|
+
lines.push(`Objects:`);
|
|
124
|
+
for (let i = 0; i < addedIds.length; i++) {
|
|
125
|
+
lines.push(` + @${addedIds[i]} (self_size: ${DevTools.I18n.ByteUtilities.formatBytesToKb(addedSelfSizes[i])})`);
|
|
126
|
+
}
|
|
127
|
+
for (let i = 0; i < deletedIds.length; i++) {
|
|
128
|
+
lines.push(` - @${deletedIds[i]} (self_size: ${DevTools.I18n.ByteUtilities.formatBytesToKb(deletedSelfSizes[i])})`);
|
|
129
|
+
}
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function formatSignedCount(n) {
|
|
134
|
+
return n > 0 ? `+${n}` : `${n}`;
|
|
135
|
+
}
|
|
136
|
+
function formatSignedSize(bytes) {
|
|
137
|
+
const formatted = DevTools.I18n.ByteUtilities.formatBytesToKb(bytes);
|
|
138
|
+
return bytes > 0 ? `+${formatted}` : formatted;
|
|
96
139
|
}
|
|
97
140
|
//# sourceMappingURL=HeapSnapshotFormatter.js.map
|
|
@@ -226,7 +226,9 @@ function converNetworkRequestDetailedToStringDetailed(data) {
|
|
|
226
226
|
if (redirectChain?.length) {
|
|
227
227
|
response.push(`### Redirect chain`);
|
|
228
228
|
let indent = 0;
|
|
229
|
-
|
|
229
|
+
// `redirectChain` is already ordered by toJSONDetailed(); don't reverse it
|
|
230
|
+
// again here or the text output contradicts structuredContent (the JSON).
|
|
231
|
+
for (const request of redirectChain) {
|
|
230
232
|
response.push(`${' '.repeat(indent)}${convertNetworkRequestConciseToString(request)}`);
|
|
231
233
|
indent++;
|
|
232
234
|
}
|
package/build/src/index.js
CHANGED
|
@@ -108,8 +108,8 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
108
108
|
experimentalDevToolsDebugging: devtools,
|
|
109
109
|
experimentalIncludeAllPages: serverArgs.experimentalIncludeAllPages,
|
|
110
110
|
performanceCrux: serverArgs.performanceCrux,
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
allowList: allowlist,
|
|
112
|
+
blocklist: blocklist,
|
|
113
113
|
});
|
|
114
114
|
await updateRoots();
|
|
115
115
|
}
|
|
@@ -16,11 +16,13 @@ export async function loadIssueDescriptions() {
|
|
|
16
16
|
}
|
|
17
17
|
const files = await fs.promises.readdir(DESCRIPTIONS_PATH);
|
|
18
18
|
const descriptions = {};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
19
|
+
const results = await Promise.all(files
|
|
20
|
+
.filter(file => file.endsWith('.md'))
|
|
21
|
+
.map(async (file) => {
|
|
23
22
|
const content = await fs.promises.readFile(path.join(DESCRIPTIONS_PATH, file), 'utf-8');
|
|
23
|
+
return { file, content };
|
|
24
|
+
}));
|
|
25
|
+
for (const { file, content } of results) {
|
|
24
26
|
descriptions[file] = content;
|
|
25
27
|
}
|
|
26
28
|
issueDescriptions = descriptions;
|
|
@@ -324,7 +324,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE
|
|
|
324
324
|
|
|
325
325
|
Name: semver
|
|
326
326
|
URL: git+https://github.com/npm/node-semver.git
|
|
327
|
-
Version: 7.8.
|
|
327
|
+
Version: 7.8.5
|
|
328
328
|
License: ISC
|
|
329
329
|
|
|
330
330
|
The ISC License
|
|
@@ -867,14 +867,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
867
867
|
|
|
868
868
|
Name: puppeteer-core
|
|
869
869
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer-core
|
|
870
|
-
Version: 25.1
|
|
870
|
+
Version: 25.2.1
|
|
871
871
|
License: Apache-2.0
|
|
872
872
|
|
|
873
873
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
874
874
|
|
|
875
875
|
Name: @puppeteer/browsers
|
|
876
876
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/browsers
|
|
877
|
-
Version: 3.0.
|
|
877
|
+
Version: 3.0.5
|
|
878
878
|
License: Apache-2.0
|
|
879
879
|
|
|
880
880
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
@@ -906,36 +906,6 @@ 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.3.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
|
-
|
|
939
909
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
940
910
|
|
|
941
911
|
Name: chrome-devtools-frontend
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
3
|
-
"
|
|
4
|
-
"chrome-devtools-frontend": "1.0.1646286",
|
|
3
|
+
"chrome-devtools-frontend": "1.0.1652307",
|
|
5
4
|
"core-js": "3.49.0",
|
|
6
5
|
"debug": "4.4.3",
|
|
7
|
-
"lighthouse": "13.
|
|
6
|
+
"lighthouse": "13.4.0",
|
|
8
7
|
"semver": "^7.7.4",
|
|
9
8
|
"urlpattern-polyfill": "^10.1.0",
|
|
10
9
|
"yargs": "18.0.0",
|
|
11
|
-
"puppeteer-core": "25.1
|
|
10
|
+
"puppeteer-core": "25.2.1"
|
|
12
11
|
}
|
|
@@ -5930,6 +5930,9 @@ class HeapSnapshotProxy extends HeapSnapshotProxyObject {
|
|
|
5930
5930
|
aggregatesWithFilter(filter) {
|
|
5931
5931
|
return this.callMethodPromise('aggregatesWithFilter', filter);
|
|
5932
5932
|
}
|
|
5933
|
+
getDuplicateStrings() {
|
|
5934
|
+
return this.callMethodPromise('getDuplicateStrings');
|
|
5935
|
+
}
|
|
5933
5936
|
aggregatesForDiff(interfaceDefinitions) {
|
|
5934
5937
|
return this.callMethodPromise('aggregatesForDiff', interfaceDefinitions);
|
|
5935
5938
|
}
|
|
@@ -6712,6 +6715,45 @@ class HeapSnapshotNode {
|
|
|
6712
6715
|
value |= detachedness;
|
|
6713
6716
|
this.#setDetachednessAndClassIndex(value);
|
|
6714
6717
|
}
|
|
6718
|
+
findInternalEdgeTarget(name) {
|
|
6719
|
+
for (let iter = this.edges(); iter.hasNext(); iter.next()) {
|
|
6720
|
+
const edge = iter.edge;
|
|
6721
|
+
if (!edge.isInternal()) {
|
|
6722
|
+
continue;
|
|
6723
|
+
}
|
|
6724
|
+
if (edge.name() === name) {
|
|
6725
|
+
return edge.node();
|
|
6726
|
+
}
|
|
6727
|
+
}
|
|
6728
|
+
return undefined;
|
|
6729
|
+
}
|
|
6730
|
+
nodeValueAsBool() {
|
|
6731
|
+
if (this.rawType() !== this.snapshot.nodeNumberType) {
|
|
6732
|
+
return undefined;
|
|
6733
|
+
}
|
|
6734
|
+
if (this.rawName() !== 'bool') {
|
|
6735
|
+
return undefined;
|
|
6736
|
+
}
|
|
6737
|
+
const valNode = this.findInternalEdgeTarget('value');
|
|
6738
|
+
if (!valNode) {
|
|
6739
|
+
return undefined;
|
|
6740
|
+
}
|
|
6741
|
+
const rawName = valNode.rawName();
|
|
6742
|
+
if (rawName === 'true') {
|
|
6743
|
+
return true;
|
|
6744
|
+
}
|
|
6745
|
+
if (rawName === 'false') {
|
|
6746
|
+
return false;
|
|
6747
|
+
}
|
|
6748
|
+
return undefined;
|
|
6749
|
+
}
|
|
6750
|
+
nodeIsTruncatedString() {
|
|
6751
|
+
const truncNode = this.findInternalEdgeTarget('truncated');
|
|
6752
|
+
if (!truncNode) {
|
|
6753
|
+
return false;
|
|
6754
|
+
}
|
|
6755
|
+
return truncNode.nodeValueAsBool() === true;
|
|
6756
|
+
}
|
|
6715
6757
|
}
|
|
6716
6758
|
class HeapSnapshotNodeIterator {
|
|
6717
6759
|
node;
|
|
@@ -6927,6 +6969,7 @@ class HeapSnapshot {
|
|
|
6927
6969
|
nodeSyntheticType;
|
|
6928
6970
|
nodeClosureType;
|
|
6929
6971
|
nodeRegExpType;
|
|
6972
|
+
nodeNumberType;
|
|
6930
6973
|
edgeFieldsCount;
|
|
6931
6974
|
edgeTypeOffset;
|
|
6932
6975
|
edgeNameOffset;
|
|
@@ -7001,6 +7044,7 @@ class HeapSnapshot {
|
|
|
7001
7044
|
this.nodeSyntheticType = this.nodeTypes.indexOf('synthetic');
|
|
7002
7045
|
this.nodeClosureType = this.nodeTypes.indexOf('closure');
|
|
7003
7046
|
this.nodeRegExpType = this.nodeTypes.indexOf('regexp');
|
|
7047
|
+
this.nodeNumberType = this.nodeTypes.indexOf('number');
|
|
7004
7048
|
this.edgeFieldsCount = meta.edge_fields.length;
|
|
7005
7049
|
this.edgeTypeOffset = meta.edge_fields.indexOf('type');
|
|
7006
7050
|
this.edgeNameOffset = meta.edge_fields.indexOf('name_or_index');
|
|
@@ -7280,6 +7324,43 @@ class HeapSnapshot {
|
|
|
7280
7324
|
const key = filter ? filter.key : 'allObjects';
|
|
7281
7325
|
return this.getAggregatesByClassKey(false, key, filter);
|
|
7282
7326
|
}
|
|
7327
|
+
getDuplicateStrings() {
|
|
7328
|
+
const filter = this.createNamedFilter('duplicatedStrings');
|
|
7329
|
+
const groupsMap = new Map();
|
|
7330
|
+
const node = this.createNode(0);
|
|
7331
|
+
for (let i = 0; i < this.nodeCount; ++i) {
|
|
7332
|
+
node.nodeIndex = i * this.nodeFieldCount;
|
|
7333
|
+
if (filter(node)) {
|
|
7334
|
+
const name = node.name();
|
|
7335
|
+
const truncated = node.nodeIsTruncatedString();
|
|
7336
|
+
let group = groupsMap.get(name);
|
|
7337
|
+
if (!group) {
|
|
7338
|
+
group = {
|
|
7339
|
+
value: name,
|
|
7340
|
+
count: 0,
|
|
7341
|
+
totalSelfSize: 0,
|
|
7342
|
+
totalRetainedSize: 0,
|
|
7343
|
+
nodes: [],
|
|
7344
|
+
truncated,
|
|
7345
|
+
};
|
|
7346
|
+
groupsMap.set(name, group);
|
|
7347
|
+
}
|
|
7348
|
+
else if (truncated) {
|
|
7349
|
+
group.truncated = true;
|
|
7350
|
+
}
|
|
7351
|
+
group.count++;
|
|
7352
|
+
group.totalSelfSize += node.selfSize();
|
|
7353
|
+
group.totalRetainedSize += node.retainedSize();
|
|
7354
|
+
group.nodes.push({
|
|
7355
|
+
id: node.id(),
|
|
7356
|
+
selfSize: node.selfSize(),
|
|
7357
|
+
retainedSize: node.retainedSize(),
|
|
7358
|
+
distance: node.distance(),
|
|
7359
|
+
});
|
|
7360
|
+
}
|
|
7361
|
+
}
|
|
7362
|
+
return Array.from(groupsMap.values()).sort((a, b) => b.totalRetainedSize - a.totalRetainedSize);
|
|
7363
|
+
}
|
|
7283
7364
|
createNodeIdFilter(minNodeId, maxNodeId) {
|
|
7284
7365
|
function nodeIdFilter(node) {
|
|
7285
7366
|
const id = node.id();
|
|
@@ -7334,6 +7415,12 @@ class HeapSnapshot {
|
|
|
7334
7415
|
}
|
|
7335
7416
|
};
|
|
7336
7417
|
switch (filterName) {
|
|
7418
|
+
case 'objectsRetainedByContexts':
|
|
7419
|
+
traverse((_node, edge) => {
|
|
7420
|
+
return !this.isContextObject(edge.node());
|
|
7421
|
+
});
|
|
7422
|
+
markUnreachableNodes();
|
|
7423
|
+
return (node) => !getBit(node);
|
|
7337
7424
|
case 'objectsRetainedByDetachedDomNodes':
|
|
7338
7425
|
traverse((_node, edge) => {
|
|
7339
7426
|
return edge.node().detachedness() !== 2 ;
|
|
@@ -7477,6 +7564,9 @@ class HeapSnapshot {
|
|
|
7477
7564
|
isUserRoot(_node) {
|
|
7478
7565
|
return true;
|
|
7479
7566
|
}
|
|
7567
|
+
isContextObject(_node) {
|
|
7568
|
+
return false;
|
|
7569
|
+
}
|
|
7480
7570
|
calculateShallowSizes() {
|
|
7481
7571
|
}
|
|
7482
7572
|
calculateDistances(isForRetainersView, filter) {
|
|
@@ -9011,6 +9101,10 @@ class JSHeapSnapshot extends HeapSnapshot {
|
|
|
9011
9101
|
isUserRoot(node) {
|
|
9012
9102
|
return node.isUserRoot() || node.isDocumentDOMTreesRoot();
|
|
9013
9103
|
}
|
|
9104
|
+
isContextObject(node) {
|
|
9105
|
+
const name = node.rawName();
|
|
9106
|
+
return name === 'system / Context' || name.startsWith('system / Context / ');
|
|
9107
|
+
}
|
|
9014
9108
|
userObjectsMapAndFlag() {
|
|
9015
9109
|
return { map: this.flags, flag: this.nodeFlags.pageObject };
|
|
9016
9110
|
}
|