cdp-tunnel 2.10.12 → 2.10.14
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.
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
var ForwardHandler = (function() {
|
|
2
|
+
// 合成输入事件(keyboard/mouse)需要页面 visibility=visible 才能投递到 DOM。
|
|
3
|
+
// cdp-tunnel 的隔离 tab(active:false + 折叠分组)默认 visibility=hidden,
|
|
4
|
+
// 导致 Input.dispatchKeyEvent/dispatchMouseEvent 被 Chromium 丢弃。
|
|
5
|
+
// 这些命令发送前需要 Page.bringToFront 让页面变 visible + 恢复焦点。
|
|
6
|
+
var SYNTHETIC_INPUT_METHODS = [
|
|
7
|
+
'Input.dispatchKeyEvent',
|
|
8
|
+
'Input.dispatchMouseEvent'
|
|
9
|
+
];
|
|
10
|
+
|
|
2
11
|
function execute(context) {
|
|
3
12
|
var id = context.id;
|
|
4
13
|
var method = context.method;
|
|
@@ -19,11 +28,52 @@ var ForwardHandler = (function() {
|
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
Logger.debug('[Forward]', method, '-> tabId:', tabId);
|
|
31
|
+
|
|
32
|
+
// 合成输入事件需要页面 visible:先 ensureVisible 再发命令
|
|
33
|
+
if (SYNTHETIC_INPUT_METHODS.indexOf(method) >= 0) {
|
|
34
|
+
return ensureVisible(tabId).then(function() {
|
|
35
|
+
return chrome.debugger.sendCommand({ tabId: tabId }, method, params);
|
|
36
|
+
}).then(function(result) {
|
|
37
|
+
return result || {};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
22
41
|
return chrome.debugger.sendCommand({ tabId: tabId }, method, params).then(function(result) {
|
|
23
42
|
return result || {};
|
|
24
43
|
});
|
|
25
44
|
}
|
|
26
45
|
|
|
46
|
+
/**
|
|
47
|
+
* 让 tab 变 visible:Page.bringToFront + 等 visibilitychange + 恢复焦点。
|
|
48
|
+
* bringToFront 会重置页面元素焦点,需要保存/恢复。
|
|
49
|
+
*/
|
|
50
|
+
function ensureVisible(tabId) {
|
|
51
|
+
// 1. 保存焦点:给 activeElement 打标记
|
|
52
|
+
return chrome.debugger.sendCommand({ tabId: tabId }, 'Runtime.evaluate', {
|
|
53
|
+
expression: '(function(){var el=document.activeElement;if(el&&el!==document.body&&el.focus){el.setAttribute("data-cdp-saved-focus","1");return 1}return 0})()',
|
|
54
|
+
returnByValue: true
|
|
55
|
+
}).catch(function() { return { result: { value: 0 } }; }).then(function(res) {
|
|
56
|
+
var hadFocus = res && res.result && res.result.value;
|
|
57
|
+
|
|
58
|
+
// 2. bringToFront 让 visibility 从 hidden→visible
|
|
59
|
+
return chrome.debugger.sendCommand({ tabId: tabId }, 'Page.bringToFront', {}).then(function() {
|
|
60
|
+
// 3. 等 visibilitychange 事件 + 双 rAF(确保 renderer 完成切换)
|
|
61
|
+
return chrome.debugger.sendCommand({ tabId: tabId }, 'Runtime.evaluate', {
|
|
62
|
+
expression: 'new Promise(function(r){function ok(){requestAnimationFrame(function(){requestAnimationFrame(function(){r(1)})})}if(document.visibilityState==="visible"){ok()}else{var d=function(){if(document.visibilityState==="visible"){document.removeEventListener("visibilitychange",d);ok()}};document.addEventListener("visibilitychange",d);setTimeout(function(){document.removeEventListener("visibilitychange",d);ok()},3000)}})',
|
|
63
|
+
awaitPromise: true
|
|
64
|
+
});
|
|
65
|
+
}).then(function() {
|
|
66
|
+
// 4. 恢复焦点
|
|
67
|
+
if (hadFocus) {
|
|
68
|
+
return chrome.debugger.sendCommand({ tabId: tabId }, 'Runtime.evaluate', {
|
|
69
|
+
expression: '(function(){var el=document.querySelector("[data-cdp-saved-focus]");if(el){el.removeAttribute("data-cdp-saved-focus");el.focus();return 1}return 0})()',
|
|
70
|
+
returnByValue: true
|
|
71
|
+
}).catch(function() {});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
27
77
|
function resolveTabId(sessionId, state) {
|
|
28
78
|
if (!state) return null;
|
|
29
79
|
if (sessionId) {
|
package/package.json
CHANGED
package/server/proxy-server.js
CHANGED
|
@@ -394,7 +394,31 @@ async function handleHttpRequest(req, res) {
|
|
|
394
394
|
res.end(JSON.stringify(targetList));
|
|
395
395
|
return;
|
|
396
396
|
}
|
|
397
|
-
|
|
397
|
+
|
|
398
|
+
if (url.pathname === '/debug/maps') {
|
|
399
|
+
const stats = {};
|
|
400
|
+
for (const [pluginWs, ns] of pluginNamespaces) {
|
|
401
|
+
stats.targetIdToClientId = ns.targetIdToClientId.size;
|
|
402
|
+
stats.sessionToClientId = ns.sessionToClientId.size;
|
|
403
|
+
stats.browserContextToClientId = ns.browserContextToClientId.size;
|
|
404
|
+
stats.clientIdToBrowserContext = ns.clientIdToBrowserContext.size;
|
|
405
|
+
stats.pendingAttachedEvents = ns.pendingAttachedEvents.size;
|
|
406
|
+
stats.pendingTargetCreatedEvents = ns.pendingTargetCreatedEvents.size;
|
|
407
|
+
stats.pendingSessionToClientId = (ns.pendingSessionToClientId || new Map()).size;
|
|
408
|
+
stats.discoveringClientIds = ns.discoveringClientIds.size;
|
|
409
|
+
stats.cachedTargets = ns.cachedTargets.length;
|
|
410
|
+
}
|
|
411
|
+
stats.globalRequestIdMap = globalRequestIdMap.size;
|
|
412
|
+
stats.connectionPairs = connectionPairs.size;
|
|
413
|
+
stats.clientById = clientById.size;
|
|
414
|
+
stats.clientIdToPlugin = clientIdToPlugin.size;
|
|
415
|
+
stats.clientConnections = clientConnections.size;
|
|
416
|
+
stats.pluginConnections = pluginConnections.size;
|
|
417
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
418
|
+
res.end(JSON.stringify(stats, null, 2));
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
398
422
|
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
399
423
|
res.end('Not Found');
|
|
400
424
|
}
|
|
@@ -543,6 +567,33 @@ function cleanupClient(ws, id, reason) {
|
|
|
543
567
|
for (const [tId, cId] of ns.targetIdToClientId.entries()) {
|
|
544
568
|
if (cId === id) ns.targetIdToClientId.delete(tId);
|
|
545
569
|
}
|
|
570
|
+
// session 清理:value 可能是 clientId(正常)或 targetId(旧 bug 残留,兼容清理)
|
|
571
|
+
const clientTargetIds = new Set();
|
|
572
|
+
for (const [tId, cId] of ns.targetIdToClientId.entries()) {
|
|
573
|
+
if (cId === id) clientTargetIds.add(tId);
|
|
574
|
+
}
|
|
575
|
+
for (const [sId, val] of ns.sessionToClientId.entries()) {
|
|
576
|
+
if (val === id || clientTargetIds.has(val)) {
|
|
577
|
+
ns.sessionToClientId.delete(sId);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
// 清理 pending session(归属未定的暂存)
|
|
581
|
+
if (ns.pendingSessionToClientId) {
|
|
582
|
+
for (const [pSid, pTid] of ns.pendingSessionToClientId.entries()) {
|
|
583
|
+
if (clientTargetIds.has(pTid)) ns.pendingSessionToClientId.delete(pSid);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
// 清理 pending 事件(targetCreated/attachedToTarget 缓存,防止泄漏)
|
|
587
|
+
if (ns.pendingAttachedEvents) {
|
|
588
|
+
for (const [pTid] of ns.pendingAttachedEvents.entries()) {
|
|
589
|
+
if (clientTargetIds.has(pTid)) ns.pendingAttachedEvents.delete(pTid);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (ns.pendingTargetCreatedEvents) {
|
|
593
|
+
for (const [pTid] of ns.pendingTargetCreatedEvents.entries()) {
|
|
594
|
+
if (clientTargetIds.has(pTid)) ns.pendingTargetCreatedEvents.delete(pTid);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
546
597
|
for (const [bcId, cId] of ns.browserContextToClientId.entries()) {
|
|
547
598
|
if (cId === id) ns.browserContextToClientId.delete(bcId);
|
|
548
599
|
}
|
|
@@ -874,8 +925,11 @@ function handlePluginConnection(ws, clientInfo, request) {
|
|
|
874
925
|
ns.sessionToClientId.set(sessionId, clientId);
|
|
875
926
|
console.log(`[SESSION MAPPED] sessionId=${sessionId?.substring(0,8) || 'none'} -> clientId=${clientId?.substring(0,8) || 'none'}`);
|
|
876
927
|
} else {
|
|
877
|
-
|
|
878
|
-
|
|
928
|
+
// 以前这里存 targetId 作为 value,导致 cleanupClient 按 clientId 匹配时清不掉(泄漏)
|
|
929
|
+
// 改为:暂存到 pendingSessionToClientId,等 targetId 归属绑定时再补绑
|
|
930
|
+
if (!ns.pendingSessionToClientId) ns.pendingSessionToClientId = new Map();
|
|
931
|
+
ns.pendingSessionToClientId.set(sessionId, targetId);
|
|
932
|
+
console.log(`[SESSION MAPPED] sessionId=${sessionId?.substring(0,8) || 'none'} -> targetId=${targetId?.substring(0,8) || 'none'} (pending, no clientId yet)`);
|
|
879
933
|
}
|
|
880
934
|
}
|
|
881
935
|
}
|
|
@@ -940,7 +994,19 @@ function handlePluginConnection(ws, clientInfo, request) {
|
|
|
940
994
|
const targetId = parsed.result.targetId;
|
|
941
995
|
ns.targetIdToClientId.set(targetId, mapping.clientId);
|
|
942
996
|
console.log(`[TARGET MAPPED] targetId=${targetId} -> clientId=${mapping.clientId} mapSize=${ns.targetIdToClientId.size}`);
|
|
943
|
-
|
|
997
|
+
|
|
998
|
+
// 补绑 pending 的 session(之前 targetId 归属未定时暂存的)
|
|
999
|
+
if (ns.pendingSessionToClientId && ns.pendingSessionToClientId.size > 0) {
|
|
1000
|
+
const pendingSessionId = null;
|
|
1001
|
+
for (const [pSid, pTid] of ns.pendingSessionToClientId.entries()) {
|
|
1002
|
+
if (pTid === targetId) {
|
|
1003
|
+
ns.sessionToClientId.set(pSid, mapping.clientId);
|
|
1004
|
+
ns.pendingSessionToClientId.delete(pSid);
|
|
1005
|
+
console.log(`[SESSION MAPPED from pending] sessionId=${pSid?.substring(0,8)} -> clientId=${mapping.clientId?.substring(0,8)} (targetId=${targetId?.substring(0,8)})`);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
944
1010
|
const cachedCreated = ns.pendingTargetCreatedEvents.get(targetId);
|
|
945
1011
|
if (cachedCreated) {
|
|
946
1012
|
clientWs.send(cachedCreated.cdpData);
|