chrome-devtools-mcp-for-extension 0.9.6 → 0.9.8
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.
|
@@ -49,67 +49,86 @@ export async function waitForExtensionChildTarget(cdp, pattern, timeoutMs = 8000
|
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
export async function inspectIframe(cdp, urlPattern, waitMs = 8000) {
|
|
52
|
-
//
|
|
52
|
+
// Strategy: Use DOMSnapshot.captureSnapshot to get iframe-inclusive DOM structure
|
|
53
|
+
// Note: chrome-extension:// iframes may not be included due to SOP/extension isolation
|
|
54
|
+
await cdp.send('DOMSnapshot.enable');
|
|
53
55
|
try {
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Evaluate outerHTML in child session
|
|
60
|
-
const htmlResult = await sendToChildSession(cdp, child.sessionId, 'Runtime.evaluate', {
|
|
61
|
-
expression: 'document.documentElement.outerHTML',
|
|
62
|
-
returnByValue: true,
|
|
56
|
+
// Capture full DOM snapshot including iframes
|
|
57
|
+
const snapshot = await cdp.send('DOMSnapshot.captureSnapshot', {
|
|
58
|
+
computedStyles: [],
|
|
59
|
+
includeDOMRects: false,
|
|
60
|
+
includePaintOrder: false,
|
|
63
61
|
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
// Search for matching iframe in the snapshot
|
|
63
|
+
const result = findIframeInSnapshot(snapshot, urlPattern);
|
|
64
|
+
if (result) {
|
|
65
|
+
return {
|
|
66
|
+
frameId: null,
|
|
67
|
+
frameUrl: result.url,
|
|
68
|
+
html: result.html,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Extension iframe not found in snapshot - this is expected behavior
|
|
72
|
+
// due to Same-Origin Policy and Chrome extension isolation
|
|
73
|
+
throw new Error('EXTENSION_FRAME_UNREADABLE: Chrome extension iframes are isolated by ' +
|
|
74
|
+
'Same-Origin Policy and extension security model. The iframe exists but ' +
|
|
75
|
+
'cannot be read from the page context. This is expected Chrome behavior.');
|
|
70
76
|
}
|
|
71
|
-
catch (
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
77
|
+
catch (error) {
|
|
78
|
+
// If DOMSnapshot fails or iframe not found, provide clear explanation
|
|
79
|
+
if (error.message?.includes('EXTENSION_FRAME_UNREADABLE')) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Failed to capture DOM snapshot: ${error.message}. ` +
|
|
83
|
+
'Note: Chrome extension iframes are typically unreadable due to security policies.');
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
await cdp.send('DOMSnapshot.disable');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function findIframeInSnapshot(snapshot, urlPattern) {
|
|
90
|
+
// DOMSnapshot structure:
|
|
91
|
+
// - documents: array of document snapshots
|
|
92
|
+
// - strings: string table for deduplication
|
|
93
|
+
const documents = snapshot.documents || [];
|
|
94
|
+
for (const doc of documents) {
|
|
95
|
+
const baseURL = doc.baseURL;
|
|
96
|
+
// Check if this document matches the pattern
|
|
97
|
+
if (baseURL && urlPattern.test(baseURL)) {
|
|
98
|
+
// Reconstruct HTML from snapshot
|
|
99
|
+
const html = reconstructHTMLFromSnapshot(doc, snapshot.strings || []);
|
|
100
|
+
return { url: baseURL, html };
|
|
92
101
|
}
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function reconstructHTMLFromSnapshot(doc, strings) {
|
|
106
|
+
// Simple reconstruction - get text content from nodes
|
|
107
|
+
// Note: DOMSnapshot doesn't provide perfect HTML reconstruction
|
|
108
|
+
// but gives us the essential content
|
|
109
|
+
const nodes = doc.nodes || {};
|
|
110
|
+
const nodeNames = nodes.nodeName || [];
|
|
111
|
+
const nodeValues = nodes.nodeValue || [];
|
|
112
|
+
const textValues = nodes.textValue || {};
|
|
113
|
+
// Build a simple HTML representation
|
|
114
|
+
let html = '<html>';
|
|
115
|
+
// Try to find body content
|
|
116
|
+
for (let i = 0; i < nodeNames.length; i++) {
|
|
117
|
+
const nameIdx = nodeNames[i];
|
|
118
|
+
const name = strings[nameIdx] || '';
|
|
119
|
+
const valueIdx = nodeValues[i];
|
|
120
|
+
const value = valueIdx >= 0 ? strings[valueIdx] : '';
|
|
121
|
+
if (name === '#document' || name === 'HTML')
|
|
122
|
+
continue;
|
|
123
|
+
if (name === '#text' && value) {
|
|
124
|
+
html += value;
|
|
125
|
+
}
|
|
126
|
+
else if (name) {
|
|
127
|
+
html += `<${name}>`;
|
|
95
128
|
}
|
|
96
|
-
// Execute in the frame context using Page.createIsolatedWorld
|
|
97
|
-
const { executionContextId } = await cdp.send('Page.createIsolatedWorld', {
|
|
98
|
-
frameId: targetFrameId,
|
|
99
|
-
});
|
|
100
|
-
await cdp.send('Runtime.enable');
|
|
101
|
-
const htmlResult = await cdp.send('Runtime.evaluate', {
|
|
102
|
-
expression: 'document.documentElement.outerHTML',
|
|
103
|
-
returnByValue: true,
|
|
104
|
-
contextId: executionContextId,
|
|
105
|
-
});
|
|
106
|
-
const html = String(htmlResult?.result?.value ?? '');
|
|
107
|
-
return {
|
|
108
|
-
frameId: targetFrameId,
|
|
109
|
-
frameUrl: targetFrameUrl,
|
|
110
|
-
html,
|
|
111
|
-
};
|
|
112
129
|
}
|
|
130
|
+
html += '</html>';
|
|
131
|
+
return html;
|
|
113
132
|
}
|
|
114
133
|
async function sendToChildSession(cdp, sessionId, method, params) {
|
|
115
134
|
const id = Math.floor(Math.random() * 1000000);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-devtools-mcp-for-extension",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.8",
|
|
4
4
|
"description": "MCP server for Chrome extension development with Web Store automation. Fork of chrome-devtools-mcp with extension-specific tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./build/src/index.js",
|