cdp-tunnel 2.5.18 → 2.5.20
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/extension-new/cdp/handler/local.js +53 -1
- package/extension-new/cdp/handler/special.js +40 -10
- package/extension-new/cdp/index.js +2 -0
- package/extension-new/core/state.js +6 -1
- package/extension-new/core/websocket.js +21 -4
- package/extension-new/manifest.json +1 -1
- package/package.json +1 -1
- package/server/proxy-server.js +8 -5
|
@@ -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 },
|
|
@@ -263,6 +263,10 @@ var State = (function() {
|
|
|
263
263
|
return _state.cdpCreatedTabIds.has(tabId);
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
function getCDPCreatedTabIds() {
|
|
267
|
+
return Array.from(_state.cdpCreatedTabIds);
|
|
268
|
+
}
|
|
269
|
+
|
|
266
270
|
function clearAllState() {
|
|
267
271
|
clearSessionState();
|
|
268
272
|
_state.attachedTabIds.clear();
|
|
@@ -506,6 +510,7 @@ var State = (function() {
|
|
|
506
510
|
removePreExistingTab: removePreExistingTab,
|
|
507
511
|
clearPreExistingTabsForClient: clearPreExistingTabsForClient,
|
|
508
512
|
addCDPCreatedTab: addCDPCreatedTab,
|
|
509
|
-
isCDPCreatedTab: isCDPCreatedTab
|
|
513
|
+
isCDPCreatedTab: isCDPCreatedTab,
|
|
514
|
+
getCDPCreatedTabIds: getCDPCreatedTabIds
|
|
510
515
|
};
|
|
511
516
|
})();
|
|
@@ -339,23 +339,40 @@ var WebSocketManager = (function() {
|
|
|
339
339
|
|
|
340
340
|
function closeTabsByClientId(clientId, resolve) {
|
|
341
341
|
var attachedTabs = State.getAttachedTabIds();
|
|
342
|
+
var cdpCreatedTabs = State.getCDPCreatedTabIds();
|
|
342
343
|
var tabsToClose = [];
|
|
344
|
+
var tabsToCloseSet = new Set();
|
|
345
|
+
|
|
346
|
+
Logger.info('[WS] closeTabsByClientId: clientId=' + clientId + ' attachedTabs=' + JSON.stringify(attachedTabs) + ' cdpCreatedTabs=' + JSON.stringify(cdpCreatedTabs));
|
|
343
347
|
|
|
344
|
-
Logger.info('[WS] closeTabsByClientId: clientId=' + clientId + ' attachedTabs=' + JSON.stringify(attachedTabs));
|
|
345
348
|
attachedTabs.forEach(function(tabId) {
|
|
346
349
|
var tabClientId = State.getClientIdByTabId(tabId);
|
|
347
350
|
var isPre = State.isPreExistingTab(tabId);
|
|
348
351
|
var isCDP = State.isCDPCreatedTab(tabId);
|
|
349
|
-
Logger.info('[WS] tabId=' + tabId + ' clientId=' + tabClientId + ' isPre=' + isPre + ' isCDP=' + isCDP);
|
|
352
|
+
Logger.info('[WS] [attached] tabId=' + tabId + ' clientId=' + tabClientId + ' isPre=' + isPre + ' isCDP=' + isCDP);
|
|
350
353
|
if (tabClientId === clientId && !isPre) {
|
|
351
|
-
|
|
354
|
+
tabsToCloseSet.add(tabId);
|
|
352
355
|
}
|
|
353
356
|
});
|
|
354
357
|
|
|
358
|
+
cdpCreatedTabs.forEach(function(tabId) {
|
|
359
|
+
if (tabsToCloseSet.has(tabId)) return;
|
|
360
|
+
|
|
361
|
+
var tabClientId = State.getClientIdByTabId(tabId);
|
|
362
|
+
var isPre = State.isPreExistingTab(tabId);
|
|
363
|
+
Logger.info('[WS] [cdpCreated] tabId=' + tabId + ' clientId=' + tabClientId + ' isPre=' + isPre + ' isAttached=' + attachedTabs.includes(tabId));
|
|
364
|
+
if (tabClientId === clientId && !isPre && !attachedTabs.includes(tabId)) {
|
|
365
|
+
tabsToCloseSet.add(tabId);
|
|
366
|
+
Logger.info('[WS] -> Added to close list (not yet attached)');
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
tabsToClose = Array.from(tabsToCloseSet);
|
|
371
|
+
|
|
355
372
|
Logger.info('[WS] closeTabsByClientId: will close ' + tabsToClose.length + ' tabs');
|
|
356
373
|
|
|
357
374
|
if (tabsToClose.length === 0) {
|
|
358
|
-
Logger.info('[WS] No
|
|
375
|
+
Logger.info('[WS] No tabs found for clientId:', clientId);
|
|
359
376
|
resolve();
|
|
360
377
|
return;
|
|
361
378
|
}
|
package/package.json
CHANGED
package/server/proxy-server.js
CHANGED
|
@@ -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
|
}
|