cdp-tunnel 2.5.17 → 2.5.19
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.
|
@@ -228,6 +228,56 @@ var LocalHandler = (function() {
|
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
function tabSimulateUserOpen(context) {
|
|
232
|
+
var attachedTabIds = State.getAttachedTabIds();
|
|
233
|
+
var openerTabId = null;
|
|
234
|
+
for (var i = 0; i < attachedTabIds.length; i++) {
|
|
235
|
+
if (State.isCDPCreatedTab(attachedTabIds[i])) {
|
|
236
|
+
openerTabId = attachedTabIds[i];
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (openerTabId == null) {
|
|
241
|
+
return Promise.resolve({ success: false, error: 'No CDP-created tab found to use as opener' });
|
|
242
|
+
}
|
|
243
|
+
return new Promise(function(resolve) {
|
|
244
|
+
chrome.tabs.create({ url: 'https://example.com', openerTabId: openerTabId }, function(tab) {
|
|
245
|
+
if (chrome.runtime.lastError) {
|
|
246
|
+
Logger.error('[TabSimulateUserOpen] Error: ' + chrome.runtime.lastError.message);
|
|
247
|
+
resolve({ success: false, error: chrome.runtime.lastError.message });
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
setTimeout(function() {
|
|
251
|
+
chrome.tabs.get(tab.id, function(t) {
|
|
252
|
+
resolve({ success: true, newTabId: tab.id, openerTabId: openerTabId, actualOpenerTabId: t.openerTabId, actualGroupId: t.groupId });
|
|
253
|
+
});
|
|
254
|
+
}, 1000);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function tabGetTabGroup(context) {
|
|
260
|
+
var tabId = context.params && context.params.tabId;
|
|
261
|
+
if (tabId == null) {
|
|
262
|
+
return Promise.resolve({ error: 'tabId is required' });
|
|
263
|
+
}
|
|
264
|
+
return new Promise(function(resolve) {
|
|
265
|
+
chrome.tabs.get(tabId, function(tab) {
|
|
266
|
+
if (chrome.runtime.lastError) {
|
|
267
|
+
Logger.error('[TabGetTabGroup] Error: ' + chrome.runtime.lastError.message);
|
|
268
|
+
resolve({ tabId: tabId, groupId: -1, error: chrome.runtime.lastError.message });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
resolve({
|
|
272
|
+
tabId: tab.id,
|
|
273
|
+
groupId: tab.groupId != null ? tab.groupId : -1,
|
|
274
|
+
url: tab.url || '',
|
|
275
|
+
openerTabId: tab.openerTabId || null
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
231
281
|
function tabGetMuteStatus(params) {
|
|
232
282
|
var cdpOnly = params && params.cdpOnly;
|
|
233
283
|
var attachedTabIds = State.getAttachedTabIds();
|
|
@@ -403,6 +453,8 @@ var LocalHandler = (function() {
|
|
|
403
453
|
mapToTargetInfo: mapToTargetInfo,
|
|
404
454
|
tabGetMuteStatus: tabGetMuteStatus,
|
|
405
455
|
tabGetGroupInfo: tabGetGroupInfo,
|
|
406
|
-
tabUngroup: tabUngroup
|
|
456
|
+
tabUngroup: tabUngroup,
|
|
457
|
+
tabSimulateUserOpen: tabSimulateUserOpen,
|
|
458
|
+
tabGetTabGroup: tabGetTabGroup
|
|
407
459
|
};
|
|
408
460
|
})();
|
|
@@ -100,35 +100,65 @@ var SpecialHandler = (function() {
|
|
|
100
100
|
var url = (params && params.url) || 'about:blank';
|
|
101
101
|
var browserContextId = (params && params.browserContextId) || 'default';
|
|
102
102
|
var clientId = context.clientId;
|
|
103
|
+
var needsNavigate = url !== 'about:blank' && url !== '';
|
|
103
104
|
|
|
104
105
|
return new Promise(function(resolve, reject) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
chrome.tabs.create({ url: url, active: false }, function(tab) {
|
|
106
|
+
// Step 1: 先创建 about:blank (不加载任何资源,tab bar 闪烁最小)
|
|
107
|
+
chrome.tabs.create({ url: 'about:blank', active: false }, function(tab) {
|
|
108
108
|
if (!tab || !tab.id) {
|
|
109
|
-
State.removePendingCreatedTabUrl(url);
|
|
110
109
|
reject(new Error('Failed to create tab'));
|
|
111
110
|
return;
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
// 保存tabId到clientId的映射,用于后续分组
|
|
115
113
|
if (clientId) {
|
|
116
114
|
State.setTabIdToClientId(tab.id, clientId);
|
|
117
115
|
}
|
|
118
116
|
|
|
119
117
|
State.addCDPCreatedTab(tab.id);
|
|
120
118
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return
|
|
125
|
-
|
|
119
|
+
// Step 2: 立刻分组折叠 (用户在 tab bar 上看不到)
|
|
120
|
+
groupTabSilently(tab.id, clientId).then(function() {
|
|
121
|
+
// Step 3: 获取 targetId 并 attach debugger
|
|
122
|
+
return getTargetIdByTabId(tab.id).then(function(targetId) {
|
|
123
|
+
return emitAutoAttachEvents(tab.id, targetId, browserContextId).then(function() {
|
|
124
|
+
// Step 4: 折叠+attach 完成后,再导航到真实 URL
|
|
125
|
+
if (needsNavigate) {
|
|
126
|
+
State.addPendingCreatedTabUrl(url);
|
|
127
|
+
return navigateTabQuietly(tab.id, url).then(function() {
|
|
128
|
+
resolve({ targetId: targetId });
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
resolve({ targetId: targetId });
|
|
132
|
+
});
|
|
126
133
|
});
|
|
134
|
+
}).catch(function(err) {
|
|
135
|
+
Logger.error('[CreateTarget] Error:', err.message || err);
|
|
136
|
+
reject(err);
|
|
127
137
|
});
|
|
128
138
|
});
|
|
129
139
|
});
|
|
130
140
|
}
|
|
131
141
|
|
|
142
|
+
function groupTabSilently(tabId, clientId) {
|
|
143
|
+
return new Promise(function(resolve) {
|
|
144
|
+
addTabToAutomationGroup(tabId, clientId);
|
|
145
|
+
// 分组操作是异步的,给一点时间让 tab 被收进折叠的 group
|
|
146
|
+
setTimeout(resolve, 100);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function navigateTabQuietly(tabId, url) {
|
|
151
|
+
return new Promise(function(resolve) {
|
|
152
|
+
chrome.tabs.update(tabId, { url: url }, function() {
|
|
153
|
+
if (chrome.runtime.lastError) {
|
|
154
|
+
Logger.warn('[NavigateQuietly] Failed:', chrome.runtime.lastError.message);
|
|
155
|
+
}
|
|
156
|
+
// 不管成功失败都 resolve,让调用者继续
|
|
157
|
+
resolve();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
132
162
|
function addTabToAutomationGroup(tabId, clientId) {
|
|
133
163
|
Logger.info('[TabGroup] Starting addTabToAutomationGroup for tabId:', tabId, 'clientId:', clientId);
|
|
134
164
|
|
|
@@ -25,6 +25,8 @@ var CDP_HANDLERS = {
|
|
|
25
25
|
'Tab.getMuteStatus': { type: 'LOCAL', handler: LocalHandler.tabGetMuteStatus },
|
|
26
26
|
'Tab.getGroupInfo': { type: 'LOCAL', handler: LocalHandler.tabGetGroupInfo },
|
|
27
27
|
'Tab.ungroup': { type: 'LOCAL', handler: LocalHandler.tabUngroup },
|
|
28
|
+
'Tab.simulateUserOpen': { type: 'LOCAL', handler: LocalHandler.tabSimulateUserOpen },
|
|
29
|
+
'Tab.getTabGroup': { type: 'LOCAL', handler: LocalHandler.tabGetTabGroup },
|
|
28
30
|
|
|
29
31
|
'SystemInfo.getInfo': { type: 'LOCAL', handler: LocalHandler.systemInfoGetInfo },
|
|
30
32
|
'SystemInfo.getProcessInfo': { type: 'LOCAL', handler: LocalHandler.systemInfoGetProcessInfo },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdp-tunnel",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.19",
|
|
4
4
|
"description": "Bridge Chrome's debugger API to WebSocket — control your existing browser with Playwright/Puppeteer via CDP",
|
|
5
5
|
"main": "server/proxy-server.js",
|
|
6
6
|
"bin": "./cli/index.js",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"LICENSE"
|
|
54
54
|
],
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"cdp-tunnel": "^2.5.
|
|
56
|
+
"cdp-tunnel": "^2.5.17",
|
|
57
57
|
"commander": "^12.0.0",
|
|
58
58
|
"ws": "^8.16.0"
|
|
59
59
|
},
|
package/server/proxy-server.js
CHANGED
|
@@ -269,7 +269,7 @@ async function handleHttpRequest(req, res) {
|
|
|
269
269
|
const userAgent = ver?.userAgent || 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36';
|
|
270
270
|
const product = ver?.product || 'Chrome/131.0.6778.86';
|
|
271
271
|
const payload = {
|
|
272
|
-
Browser: product
|
|
272
|
+
Browser: `${product} (cdp-tunnel/${PKG_VERSION})`,
|
|
273
273
|
'Protocol-Version': ver?.protocolVersion || '1.3',
|
|
274
274
|
'User-Agent': userAgent,
|
|
275
275
|
'V8-Version': ver?.jsVersion || '',
|
|
@@ -1169,7 +1169,6 @@ function handlePageConnection(ws, clientInfo, targetId) {
|
|
|
1169
1169
|
const plugin = pluginConnections.values().next().value;
|
|
1170
1170
|
if (plugin && plugin.readyState === WebSocket.OPEN) {
|
|
1171
1171
|
ws.pairedPlugin = plugin;
|
|
1172
|
-
plugin.pairedClientId = id;
|
|
1173
1172
|
if (shouldLog('info')) {
|
|
1174
1173
|
console.log(` - Paired with plugin: ${plugin.id}`);
|
|
1175
1174
|
}
|
|
@@ -1268,7 +1267,9 @@ function handlePageConnection(ws, clientInfo, targetId) {
|
|
|
1268
1267
|
|
|
1269
1268
|
if (ws.pairedPlugin && ws.pluginMessageHandler) {
|
|
1270
1269
|
ws.pairedPlugin.off('message', ws.pluginMessageHandler);
|
|
1271
|
-
ws.pairedPlugin.pairedClientId
|
|
1270
|
+
if (ws.pairedPlugin.pairedClientId === id) {
|
|
1271
|
+
ws.pairedPlugin.pairedClientId = null;
|
|
1272
|
+
}
|
|
1272
1273
|
|
|
1273
1274
|
safeSend(ws.pairedPlugin, JSON.stringify({
|
|
1274
1275
|
type: 'client-disconnected',
|
|
@@ -1291,7 +1292,9 @@ function handlePageConnection(ws, clientInfo, targetId) {
|
|
|
1291
1292
|
|
|
1292
1293
|
if (ws.pairedPlugin && ws.pluginMessageHandler) {
|
|
1293
1294
|
ws.pairedPlugin.off('message', ws.pluginMessageHandler);
|
|
1294
|
-
ws.pairedPlugin.pairedClientId
|
|
1295
|
+
if (ws.pairedPlugin.pairedClientId === id) {
|
|
1296
|
+
ws.pairedPlugin.pairedClientId = null;
|
|
1297
|
+
}
|
|
1295
1298
|
}
|
|
1296
1299
|
|
|
1297
1300
|
ws.pluginMessageHandler = null;
|
|
@@ -1336,8 +1339,8 @@ function broadcastToClients(data, excludeWs = null) {
|
|
|
1336
1339
|
let sent = 0;
|
|
1337
1340
|
logCDP('BROADCAST', `Starting broadcast to ${clientConnections.size} clients, data preview: ${data.substring(0, 200)}`);
|
|
1338
1341
|
clientConnections.forEach((client) => {
|
|
1339
|
-
logCDP('BROADCAST', `Checking client ${client.id}, state=${client.readyState}, excluded=${client === excludeWs}`);
|
|
1340
|
-
if (client !== excludeWs && safeSend(client, data, 'client')) {
|
|
1342
|
+
logCDP('BROADCAST', `Checking client ${client.id}, state=${client.readyState}, excluded=${client === excludeWs}, hasOwnHandler=${!!client.pluginMessageHandler}`);
|
|
1343
|
+
if (client !== excludeWs && !client.pluginMessageHandler && safeSend(client, data, 'client')) {
|
|
1341
1344
|
sent++;
|
|
1342
1345
|
logCDP('BROADCAST', `Sent to client ${client.id}`);
|
|
1343
1346
|
}
|