cdp-tunnel 1.0.9 → 1.0.11
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/console-test.js +52 -0
- package/extension-new/background.js +25 -12
- package/extension-new/cdp/handler/special.js +40 -22
- package/extension-new/cdp/index.js +6 -5
- package/extension-new/core/debugger.js +12 -5
- package/extension-new/core/state.js +13 -2
- package/extension-new/core/websocket.js +4 -2
- package/final-console-test.js +105 -0
- package/package.json +1 -1
- package/server/modules/config.js +1 -1
- package/server/modules/logger.js +1 -1
- package/server/proxy-server.js +13 -9
- package/simple-tab-group-test.js +56 -0
- package/test-cdp-connection.js +85 -0
- package/test-cdp-groups.js +71 -0
- package/test-check-newtab.js +144 -0
- package/test-chrome-native.js +140 -0
- package/test-client-connected.js +99 -0
- package/test-compare-formats.js +88 -0
- package/test-context-features.js +113 -0
- package/test-create-tab.js +113 -0
- package/test-debug-broadcast.js +52 -0
- package/test-debug-targets.js +127 -0
- package/test-expose-newtab.js +164 -0
- package/test-expose-shared.js +189 -0
- package/test-final-logs.js +110 -0
- package/test-fresh-chromium.js +153 -0
- package/test-init-script.js +128 -0
- package/test-keepalive.js +89 -0
- package/test-launch-chromium.js +140 -0
- package/test-launch-vs-connect.js +149 -0
- package/test-listen-events.js +102 -0
- package/test-monitor.js +83 -0
- package/test-multiple-cdp-groups.js +78 -0
- package/test-native.js +96 -0
- package/test-page-connection.js +74 -0
- package/test-playwright-connection.js +45 -0
- package/test-playwright-groups.js +47 -0
- package/test-playwright-pages.js +47 -0
- package/test-playwright-sequence.js +81 -0
- package/test-proper-context.js +129 -0
- package/test-real-final.js +251 -0
- package/test-real-scenario-v2.js +166 -0
- package/test-real-scenario-v3.js +231 -0
- package/test-real-scenario.js +104 -0
- package/test-server-logs.js +98 -0
- package/test-session-id.js +91 -0
- package/test-simple-cdp-groups.js +44 -0
- package/test-simple-context.js +137 -0
- package/test-tab-group-simple.js +58 -0
- package/test-tab-grouping.js +48 -0
- package/test-wait-for-page.js +95 -0
- package/test-with-logs.js +118 -0
- package/test-ws-groups.js +59 -0
- package/tests/e2e-auto-test.js +304 -0
- package/tests/iframe-test-page.html +89 -0
- package/tests/test-douyin-iframe.js +171 -0
- package/tests/test-iframe-debug.js +204 -0
package/console-test.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// 测试标签分组功能的脚本
|
|
2
|
+
// 复制到Chrome扩展的控制台中运行
|
|
3
|
+
|
|
4
|
+
// 1. 测试查询现有标签组
|
|
5
|
+
console.log('Testing tab group functionality...');
|
|
6
|
+
chrome.tabGroups.query({ title: 'lo' }, function(groups) {
|
|
7
|
+
console.log('Found tab groups:', groups);
|
|
8
|
+
|
|
9
|
+
// 2. 测试创建新标签
|
|
10
|
+
chrome.tabs.create({ url: 'https://www.baidu.com/', active: false }, function(tab) {
|
|
11
|
+
console.log('Created tab:', tab);
|
|
12
|
+
|
|
13
|
+
if (groups.length > 0) {
|
|
14
|
+
// 添加到现有组
|
|
15
|
+
console.log('Adding tab to existing group...');
|
|
16
|
+
chrome.tabs.group({ tabIds: tab.id, groupId: groups[0].id }, function(groupId) {
|
|
17
|
+
console.log('Added tab to group:', groupId);
|
|
18
|
+
});
|
|
19
|
+
} else {
|
|
20
|
+
// 创建新组并添加标签页
|
|
21
|
+
console.log('Creating new group and adding tab...');
|
|
22
|
+
chrome.tabs.group({ tabIds: tab.id }, function(groupId) {
|
|
23
|
+
console.log('Created group:', groupId);
|
|
24
|
+
// 更新组的标题和颜色
|
|
25
|
+
if (groupId) {
|
|
26
|
+
chrome.tabGroups.update(groupId, {
|
|
27
|
+
title: 'lo',
|
|
28
|
+
color: 'blue'
|
|
29
|
+
}, function(group) {
|
|
30
|
+
console.log('Updated group:', group);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 3. 测试现有标签的分组
|
|
39
|
+
chrome.tabs.query({ active: true }, function(tabs) {
|
|
40
|
+
if (tabs.length > 0) {
|
|
41
|
+
console.log('Current active tab:', tabs[0]);
|
|
42
|
+
|
|
43
|
+
chrome.tabGroups.query({ title: 'lo' }, function(groups) {
|
|
44
|
+
if (groups.length > 0) {
|
|
45
|
+
console.log('Adding current tab to group...');
|
|
46
|
+
chrome.tabs.group({ tabIds: tabs[0].id, groupId: groups[0].id }, function(groupId) {
|
|
47
|
+
console.log('Added current tab to group:', groupId);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
@@ -167,14 +167,19 @@ String.prototype.hashCode = function() {
|
|
|
167
167
|
var openerTabId = tab.openerTabId;
|
|
168
168
|
var isOpenerControlled = openerTabId && State.isTabAttached(openerTabId);
|
|
169
169
|
|
|
170
|
-
//
|
|
171
|
-
//
|
|
170
|
+
// 只有当 opener 被 CDP 控制时才跟踪新页面
|
|
171
|
+
// 这样可以避免跟踪用户手动点击链接打开的页面
|
|
172
172
|
if (!openerTabId) {
|
|
173
173
|
Logger.info('[Tabs] Tab has no opener, skipping. tabId:', tabId);
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
if (!isOpenerControlled) {
|
|
178
|
+
Logger.info('[Tabs] Opener not controlled by CDP, skipping. tabId:', tabId, 'openerTabId:', openerTabId);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
Logger.info('[Tabs] Tab has controlled opener, will attach. tabId:', tabId, 'openerTabId:', openerTabId);
|
|
178
183
|
|
|
179
184
|
LocalHandler.getTargetInfoById(String(tabId)).then(function(targetInfo) {
|
|
180
185
|
Logger.info('[Tabs] getTargetInfoById result:', targetInfo ? targetInfo.targetId : 'null');
|
|
@@ -209,18 +214,26 @@ String.prototype.hashCode = function() {
|
|
|
209
214
|
|
|
210
215
|
// 将标签页添加到CDP组(添加延迟等待)
|
|
211
216
|
setTimeout(function() {
|
|
212
|
-
//
|
|
213
|
-
var
|
|
217
|
+
// 获取openerTabId对应的clientId
|
|
218
|
+
var openerClientId = openerTabId ? State.getClientIdByTabId(openerTabId) : null;
|
|
214
219
|
var groupName;
|
|
215
|
-
|
|
216
|
-
//
|
|
217
|
-
if (
|
|
218
|
-
groupName = 'CDP-' +
|
|
220
|
+
|
|
221
|
+
// 如果有指定的clientId,使用该clientId作为组名
|
|
222
|
+
if (openerClientId) {
|
|
223
|
+
groupName = 'CDP-' + openerClientId.substring(0, 8);
|
|
224
|
+
Logger.info('[TabGroup] Using opener clientId for group name:', groupName, 'openerTabId:', openerTabId);
|
|
219
225
|
} else {
|
|
220
|
-
//
|
|
221
|
-
|
|
226
|
+
// 回退到使用第一个CDP客户端的ID
|
|
227
|
+
var cdpClients = State.getCDPClients() || [];
|
|
228
|
+
if (cdpClients.length > 0 && cdpClients[0] && cdpClients[0].id) {
|
|
229
|
+
groupName = 'CDP-' + cdpClients[0].id.substring(0, 8);
|
|
230
|
+
} else {
|
|
231
|
+
// 如果没有CDP客户端,使用时间戳作为组名
|
|
232
|
+
groupName = 'CDP-' + Date.now().toString(36);
|
|
233
|
+
}
|
|
234
|
+
Logger.info('[TabGroup] Using fallback clientId for group name:', groupName);
|
|
222
235
|
}
|
|
223
|
-
|
|
236
|
+
|
|
224
237
|
chrome.tabGroups.query({ title: groupName }, function(groups) {
|
|
225
238
|
if (groups.length > 0) {
|
|
226
239
|
// 找到现有的组,将标签页添加到组
|
|
@@ -18,6 +18,7 @@ var SpecialHandler = (function() {
|
|
|
18
18
|
function targetAttachToTarget(context) {
|
|
19
19
|
var params = context.params;
|
|
20
20
|
var targetId = params && params.targetId;
|
|
21
|
+
var clientId = context.clientId;
|
|
21
22
|
if (!targetId) {
|
|
22
23
|
return Promise.resolve({});
|
|
23
24
|
}
|
|
@@ -28,7 +29,7 @@ var SpecialHandler = (function() {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
var isAlreadyAttached = State.isTabAttached(tabId);
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
if (isAlreadyAttached) {
|
|
33
34
|
var newSessionId = CDPUtils.generateSessionId();
|
|
34
35
|
State.mapSession(newSessionId, tabId, targetId);
|
|
@@ -43,9 +44,14 @@ var SpecialHandler = (function() {
|
|
|
43
44
|
|
|
44
45
|
var sessionId = CDPUtils.generateSessionId();
|
|
45
46
|
State.mapSession(sessionId, tabId, targetId);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
|
|
48
|
+
// 保存tabId到clientId的映射
|
|
49
|
+
if (clientId) {
|
|
50
|
+
State.setTabIdToClientId(tabId, clientId);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
addTabToAutomationGroup(tabId, clientId);
|
|
54
|
+
|
|
49
55
|
return { sessionId: sessionId };
|
|
50
56
|
});
|
|
51
57
|
});
|
|
@@ -75,6 +81,7 @@ var SpecialHandler = (function() {
|
|
|
75
81
|
var params = context.params;
|
|
76
82
|
var url = (params && params.url) || 'about:blank';
|
|
77
83
|
var browserContextId = (params && params.browserContextId) || 'default';
|
|
84
|
+
var clientId = context.clientId;
|
|
78
85
|
|
|
79
86
|
return new Promise(function(resolve, reject) {
|
|
80
87
|
State.addPendingCreatedTabUrl(url);
|
|
@@ -85,10 +92,16 @@ var SpecialHandler = (function() {
|
|
|
85
92
|
reject(new Error('Failed to create tab'));
|
|
86
93
|
return;
|
|
87
94
|
}
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
|
|
96
|
+
// 保存tabId到clientId的映射,用于后续分组
|
|
97
|
+
if (clientId) {
|
|
98
|
+
State.setTabIdToClientId(tab.id, clientId);
|
|
99
|
+
Logger.info('[TabGroup] Mapped tabId:', tab.id, '-> clientId:', clientId);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 将标签页添加到CDP Automation组,使用对应的clientId
|
|
103
|
+
addTabToAutomationGroup(tab.id, clientId);
|
|
104
|
+
|
|
92
105
|
var targetId = String(tab.id);
|
|
93
106
|
State.addEmittedTarget(targetId);
|
|
94
107
|
getTargetIdByTabId(tab.id).then(function(targetId) {
|
|
@@ -99,22 +112,27 @@ var SpecialHandler = (function() {
|
|
|
99
112
|
});
|
|
100
113
|
});
|
|
101
114
|
}
|
|
102
|
-
|
|
103
|
-
function addTabToAutomationGroup(tabId) {
|
|
104
|
-
Logger.info('[TabGroup] Starting addTabToAutomationGroup for tabId:', tabId);
|
|
105
|
-
|
|
106
|
-
// 获取当前CDP客户端列表
|
|
107
|
-
var cdpClients = State.getCDPClients() || [];
|
|
115
|
+
|
|
116
|
+
function addTabToAutomationGroup(tabId, clientId) {
|
|
117
|
+
Logger.info('[TabGroup] Starting addTabToAutomationGroup for tabId:', tabId, 'clientId:', clientId);
|
|
118
|
+
|
|
108
119
|
var groupName;
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
if (
|
|
112
|
-
groupName = 'CDP-' +
|
|
113
|
-
Logger.info('[TabGroup] Using
|
|
120
|
+
|
|
121
|
+
// 如果有指定的clientId,使用该clientId作为组名
|
|
122
|
+
if (clientId) {
|
|
123
|
+
groupName = 'CDP-' + clientId.substring(0, 8);
|
|
124
|
+
Logger.info('[TabGroup] Using specific clientId for group name:', groupName);
|
|
114
125
|
} else {
|
|
115
|
-
// 如果没有CDP
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
// 如果没有clientId,回退到使用第一个CDP客户端的ID
|
|
127
|
+
var cdpClients = State.getCDPClients() || [];
|
|
128
|
+
if (cdpClients.length > 0 && cdpClients[0] && cdpClients[0].id) {
|
|
129
|
+
groupName = 'CDP-' + cdpClients[0].id.substring(0, 8);
|
|
130
|
+
Logger.info('[TabGroup] Using first CDP client ID for group name:', groupName);
|
|
131
|
+
} else {
|
|
132
|
+
// 如果没有CDP客户端,使用固定的组名
|
|
133
|
+
groupName = 'CDP-Automation';
|
|
134
|
+
Logger.info('[TabGroup] No CDP client, using default group name:', groupName);
|
|
135
|
+
}
|
|
118
136
|
}
|
|
119
137
|
|
|
120
138
|
// 添加延迟等待,确保标签页完全加载后再分组
|
|
@@ -57,20 +57,21 @@ function routeCDPCommand(message) {
|
|
|
57
57
|
var method = message.method;
|
|
58
58
|
var params = message.params;
|
|
59
59
|
var sessionId = message.sessionId;
|
|
60
|
+
var clientId = message.clientId;
|
|
60
61
|
|
|
61
|
-
console.log('[CDP] routeCDPCommand id=' + id + ' (type: ' + typeof id + ') method=' + method);
|
|
62
|
+
console.log('[CDP] routeCDPCommand id=' + id + ' (type: ' + typeof id + ') method=' + method + ' clientId=' + (clientId || 'none'));
|
|
62
63
|
|
|
63
64
|
var route = CDP_HANDLERS[method];
|
|
64
65
|
var logType = route ? route.type : 'FORWARD';
|
|
65
|
-
Logger.info('[CDP] RECV id=' + id + ' method=' + method + ' type=' + logType + ' sessionId=' + (sessionId || 'null'));
|
|
66
|
+
Logger.info('[CDP] RECV id=' + id + ' method=' + method + ' type=' + logType + ' sessionId=' + (sessionId || 'null') + ' clientId=' + (clientId || 'null'));
|
|
66
67
|
|
|
67
68
|
return new Promise(function(resolve) {
|
|
68
69
|
if (route) {
|
|
69
|
-
Promise.resolve(route.handler({ id: id, method: method, params: params, sessionId: sessionId }))
|
|
70
|
+
Promise.resolve(route.handler({ id: id, method: method, params: params, sessionId: sessionId, clientId: clientId }))
|
|
70
71
|
.then(function(result) {
|
|
71
72
|
if (result === null && route.type === 'SPECIAL') {
|
|
72
73
|
Logger.info('[CDP] SPECIAL null -> FORWARD id=' + id + ' method=' + method);
|
|
73
|
-
return ForwardHandler.execute({ id: id, method: method, params: params, sessionId: sessionId });
|
|
74
|
+
return ForwardHandler.execute({ id: id, method: method, params: params, sessionId: sessionId, clientId: clientId });
|
|
74
75
|
}
|
|
75
76
|
return result;
|
|
76
77
|
})
|
|
@@ -83,7 +84,7 @@ function routeCDPCommand(message) {
|
|
|
83
84
|
resolve({ error: { message: error.message } });
|
|
84
85
|
});
|
|
85
86
|
} else {
|
|
86
|
-
ForwardHandler.execute({ id: id, method: method, params: params, sessionId: sessionId })
|
|
87
|
+
ForwardHandler.execute({ id: id, method: method, params: params, sessionId: sessionId, clientId: clientId })
|
|
87
88
|
.then(function(result) {
|
|
88
89
|
Logger.info('[CDP] SEND id=' + id + ' method=' + method + ' hasError=false (forwarded)');
|
|
89
90
|
resolve({ result: result });
|
|
@@ -4,15 +4,25 @@ var DebuggerManager = (function() {
|
|
|
4
4
|
if (window.__internalUrlBlockInjected) return;
|
|
5
5
|
window.__internalUrlBlockInjected = true;
|
|
6
6
|
|
|
7
|
-
var blockedProtocols = ['bitbrowser:', 'chrome:', 'edge:', 'chrome-extension:'];
|
|
7
|
+
var blockedProtocols = ['bitbrowser:', 'chrome:', 'edge:', 'chrome-extension:', 'bytedance:', 'sslocal:', 'alipays:', 'weixin:', 'mqq:', 'taobao:', 'tmall:'];
|
|
8
8
|
|
|
9
9
|
function isInternalUrl(url) {
|
|
10
10
|
if (!url) return false;
|
|
11
|
+
// Whitelist: only allow http/https/about/data/blob/file
|
|
12
|
+
if (url.startsWith('http:') || url.startsWith('https:') || url.startsWith('about:') || url.startsWith('data:') || url.startsWith('blob:') || url.startsWith('file:')) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
// Also check explicit blocked list for common custom protocols
|
|
11
16
|
for (var i = 0; i < blockedProtocols.length; i++) {
|
|
12
17
|
if (url.startsWith(blockedProtocols[i])) {
|
|
13
18
|
return true;
|
|
14
19
|
}
|
|
15
20
|
}
|
|
21
|
+
// Block any other custom protocol (xxx://)
|
|
22
|
+
var colonIdx = url.indexOf(':');
|
|
23
|
+
if (colonIdx > 0 && colonIdx < 20 && url.substring(colonIdx, colonIdx + 3) === '://') {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
16
26
|
return false;
|
|
17
27
|
}
|
|
18
28
|
|
|
@@ -232,10 +242,7 @@ var DebuggerManager = (function() {
|
|
|
232
242
|
Logger.warn(' Disposition:', disposition);
|
|
233
243
|
Logger.warn(' FrameId:', frameId);
|
|
234
244
|
|
|
235
|
-
if (url.startsWith('
|
|
236
|
-
url.startsWith('bitbrowser://') ||
|
|
237
|
-
url.startsWith('edge://') ||
|
|
238
|
-
url.startsWith('chrome-extension://')) {
|
|
245
|
+
if (url && !url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('about:') && !url.startsWith('data:') && !url.startsWith('blob:') && !url.startsWith('file://')) {
|
|
239
246
|
Logger.error('[NAVIGATION] ⚠️ 检测到导航到内部页面,尝试阻止!');
|
|
240
247
|
Logger.error('[NAVIGATION] 目标URL:', url);
|
|
241
248
|
|
|
@@ -22,7 +22,8 @@ var State = (function() {
|
|
|
22
22
|
isAttached: false,
|
|
23
23
|
pendingCreatedTabUrls: new Set(),
|
|
24
24
|
clientIdToTabId: new Map(),
|
|
25
|
-
clientIdToSessionId: new Map()
|
|
25
|
+
clientIdToSessionId: new Map(),
|
|
26
|
+
tabIdToClientId: new Map()
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
function mapSession(sessionId, tabId, targetId) {
|
|
@@ -282,6 +283,14 @@ var State = (function() {
|
|
|
282
283
|
}
|
|
283
284
|
}
|
|
284
285
|
|
|
286
|
+
function setTabIdToClientId(tabId, clientId) {
|
|
287
|
+
_state.tabIdToClientId.set(tabId, clientId);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function getClientIdByTabId(tabId) {
|
|
291
|
+
return _state.tabIdToClientId.get(tabId);
|
|
292
|
+
}
|
|
293
|
+
|
|
285
294
|
function getTabIdByClientId(clientId) {
|
|
286
295
|
return _state.clientIdToTabId.get(clientId);
|
|
287
296
|
}
|
|
@@ -379,6 +388,8 @@ var State = (function() {
|
|
|
379
388
|
getAutomatedTabs: getAutomatedTabs,
|
|
380
389
|
clearSessionState: clearSessionState,
|
|
381
390
|
clearAllState: clearAllState,
|
|
382
|
-
cleanupAllTabs: cleanupAllTabs
|
|
391
|
+
cleanupAllTabs: cleanupAllTabs,
|
|
392
|
+
setTabIdToClientId: setTabIdToClientId,
|
|
393
|
+
getClientIdByTabId: getClientIdByTabId
|
|
383
394
|
};
|
|
384
395
|
})();
|
|
@@ -223,7 +223,8 @@ var WebSocketManager = (function() {
|
|
|
223
223
|
method: method,
|
|
224
224
|
params: params,
|
|
225
225
|
tabId: tabId,
|
|
226
|
-
sessionId: sessionId
|
|
226
|
+
sessionId: sessionId,
|
|
227
|
+
clientId: message.__clientId
|
|
227
228
|
});
|
|
228
229
|
}
|
|
229
230
|
break;
|
|
@@ -235,7 +236,8 @@ var WebSocketManager = (function() {
|
|
|
235
236
|
method: method,
|
|
236
237
|
params: params,
|
|
237
238
|
tabId: tabId,
|
|
238
|
-
sessionId: sessionId
|
|
239
|
+
sessionId: sessionId,
|
|
240
|
+
clientId: message.__clientId
|
|
239
241
|
});
|
|
240
242
|
}
|
|
241
243
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// 完整的标签分组测试脚本
|
|
2
|
+
// 复制到Chrome扩展的控制台中运行
|
|
3
|
+
|
|
4
|
+
console.log('=== 开始测试标签分组功能 ===');
|
|
5
|
+
|
|
6
|
+
// 1. 测试1:创建新标签并添加到lo组
|
|
7
|
+
function test1_CreateTabAndAddToGroup() {
|
|
8
|
+
console.log('\n=== 测试1:创建新标签并添加到lo组 ===');
|
|
9
|
+
|
|
10
|
+
// 先查询是否存在lo组
|
|
11
|
+
chrome.tabGroups.query({ title: 'lo' }, function(groups) {
|
|
12
|
+
console.log('找到的标签组:', groups);
|
|
13
|
+
|
|
14
|
+
// 创建新标签页
|
|
15
|
+
chrome.tabs.create({ url: 'https://www.baidu.com/', active: false }, function(tab) {
|
|
16
|
+
console.log('创建的标签页:', tab);
|
|
17
|
+
|
|
18
|
+
if (groups.length > 0) {
|
|
19
|
+
// 如果存在lo组,直接添加
|
|
20
|
+
console.log('将标签页添加到现有lo组');
|
|
21
|
+
chrome.tabs.group({ tabIds: tab.id, groupId: groups[0].id }, function(groupId) {
|
|
22
|
+
console.log('标签页已添加到组:', groupId);
|
|
23
|
+
test2_AddExistingTabToGroup();
|
|
24
|
+
});
|
|
25
|
+
} else {
|
|
26
|
+
// 如果不存在lo组,创建新组
|
|
27
|
+
console.log('创建新的lo组并添加标签页');
|
|
28
|
+
chrome.tabs.group({ tabIds: tab.id }, function(groupId) {
|
|
29
|
+
console.log('创建的组ID:', groupId);
|
|
30
|
+
// 更新组的标题和颜色
|
|
31
|
+
chrome.tabGroups.update(groupId, {
|
|
32
|
+
title: 'lo',
|
|
33
|
+
color: 'blue'
|
|
34
|
+
}, function(group) {
|
|
35
|
+
console.log('更新后的组:', group);
|
|
36
|
+
test2_AddExistingTabToGroup();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. 测试2:将现有标签添加到lo组
|
|
45
|
+
function test2_AddExistingTabToGroup() {
|
|
46
|
+
console.log('\n=== 测试2:将现有标签添加到lo组 ===');
|
|
47
|
+
|
|
48
|
+
// 获取当前活动标签
|
|
49
|
+
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
|
|
50
|
+
if (tabs.length > 0) {
|
|
51
|
+
var activeTab = tabs[0];
|
|
52
|
+
console.log('当前活动标签:', activeTab);
|
|
53
|
+
|
|
54
|
+
// 查询lo组
|
|
55
|
+
chrome.tabGroups.query({ title: 'lo' }, function(groups) {
|
|
56
|
+
if (groups.length > 0) {
|
|
57
|
+
console.log('将当前标签添加到lo组');
|
|
58
|
+
chrome.tabs.group({ tabIds: activeTab.id, groupId: groups[0].id }, function(groupId) {
|
|
59
|
+
console.log('当前标签已添加到组:', groupId);
|
|
60
|
+
test3_VerifyGroupMembers();
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
console.log('未找到lo组');
|
|
64
|
+
test3_VerifyGroupMembers();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
console.log('未找到活动标签');
|
|
69
|
+
test3_VerifyGroupMembers();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 3. 测试3:验证lo组的成员
|
|
75
|
+
function test3_VerifyGroupMembers() {
|
|
76
|
+
console.log('\n=== 测试3:验证lo组的成员 ===');
|
|
77
|
+
|
|
78
|
+
// 查询lo组
|
|
79
|
+
chrome.tabGroups.query({ title: 'lo' }, function(groups) {
|
|
80
|
+
if (groups.length > 0) {
|
|
81
|
+
var loGroup = groups[0];
|
|
82
|
+
console.log('lo组信息:', loGroup);
|
|
83
|
+
|
|
84
|
+
// 查询组内的标签
|
|
85
|
+
chrome.tabs.query({ groupId: loGroup.id }, function(tabs) {
|
|
86
|
+
console.log('lo组内的标签:', tabs);
|
|
87
|
+
console.log('lo组内标签数量:', tabs.length);
|
|
88
|
+
|
|
89
|
+
// 打印每个标签的信息
|
|
90
|
+
tabs.forEach(function(tab, index) {
|
|
91
|
+
console.log('标签', index + 1, ':', tab.title, '-', tab.url);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
console.log('\n=== 测试完成 ===');
|
|
95
|
+
console.log('请检查Chrome浏览器中的标签组是否正确创建,并且标签页是否被正确添加到组中。');
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
console.log('未找到lo组');
|
|
99
|
+
console.log('\n=== 测试完成 ===');
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 开始执行测试
|
|
105
|
+
test1_CreateTabAndAddToGroup();
|
package/package.json
CHANGED
package/server/modules/config.js
CHANGED
package/server/modules/logger.js
CHANGED
|
@@ -137,7 +137,7 @@ function logCDP(direction, message, sessionId = null, pluginType = null) {
|
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
139
139
|
const timestamp = new Date().toISOString();
|
|
140
|
-
const sessionPrefix = sessionId ? `[session:${sessionId.substring(0, 8)}]` : '';
|
|
140
|
+
const sessionPrefix = sessionId && typeof sessionId === 'string' ? `[session:${sessionId.substring(0, 8)}]` : '';
|
|
141
141
|
const typePrefix = pluginType ? `[${pluginType}]` : '';
|
|
142
142
|
const logLine = `[${timestamp}] ${typePrefix}${sessionPrefix}[${direction}] ${truncatedMessage}\n`;
|
|
143
143
|
|
package/server/proxy-server.js
CHANGED
|
@@ -362,7 +362,7 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
362
362
|
const openerClientId = targetIdToClientId.get(openerId);
|
|
363
363
|
if (openerClientId) {
|
|
364
364
|
targetIdToClientId.set(targetId, openerClientId);
|
|
365
|
-
console.log(`[TARGET CREATED with opener] targetId=${targetId
|
|
365
|
+
console.log(`[TARGET CREATED with opener] targetId=${targetId?.substring(0,8) || 'none'} openerId=${openerId?.substring(0,8) || 'none'} -> clientId=${openerClientId}`);
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
}
|
|
@@ -381,7 +381,7 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
381
381
|
|
|
382
382
|
if (targetId && sessionId) {
|
|
383
383
|
sessionToClientId.set(sessionId, targetId);
|
|
384
|
-
console.log(`[SESSION MAPPED] sessionId=${sessionId
|
|
384
|
+
console.log(`[SESSION MAPPED] sessionId=${sessionId?.substring(0,8) || 'none'} -> targetId=${targetId?.substring(0,8) || 'none'}`);
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
|
|
@@ -443,8 +443,10 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
443
443
|
// 检查是否有缓存的 Target.attachedToTarget 事件
|
|
444
444
|
const cachedEvent = pendingAttachedEvents.get(targetId);
|
|
445
445
|
if (cachedEvent) {
|
|
446
|
-
|
|
447
|
-
|
|
446
|
+
if (cachedEvent.sessionId) {
|
|
447
|
+
sessionToClientId.set(cachedEvent.sessionId, mapping.clientId);
|
|
448
|
+
}
|
|
449
|
+
console.log(`[SESSION MAPPED from cached] sessionId=${cachedEvent.sessionId?.substring(0,8) || 'none'} -> clientId=${mapping.clientId} (targetId=${targetId})`);
|
|
448
450
|
pendingAttachedEvents.delete(targetId);
|
|
449
451
|
|
|
450
452
|
// 先发送缓存的事件给客户端
|
|
@@ -483,7 +485,7 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
483
485
|
// 2. sessionId 路由:消息属于特定 session(事件,没有 id)
|
|
484
486
|
if (parsed && parsed.sessionId) {
|
|
485
487
|
const targetClientId = sessionToClientId.get(parsed.sessionId);
|
|
486
|
-
console.log(`[SESSION ROUTE] sessionId=${parsed.sessionId?.substring(0,8)} -> clientId=${targetClientId || 'not found'}`);
|
|
488
|
+
console.log(`[SESSION ROUTE] sessionId=${parsed.sessionId?.substring(0,8) || 'none'} -> clientId=${targetClientId || 'not found'}`);
|
|
487
489
|
if (targetClientId) {
|
|
488
490
|
const clientWs = clientById.get(targetClientId);
|
|
489
491
|
if (clientWs && clientWs.readyState === WebSocket.OPEN) {
|
|
@@ -491,7 +493,7 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
491
493
|
logCDP('DEBUG', `FORWARDED to client: ${targetClientId} (sessionId route)`, parsed?.sessionId);
|
|
492
494
|
}
|
|
493
495
|
} else {
|
|
494
|
-
console.log(`[WARN] No clientId for sessionId: ${parsed.sessionId?.substring(0, 8)}`);
|
|
496
|
+
console.log(`[WARN] No clientId for sessionId: ${parsed.sessionId?.substring(0, 8) || 'none'}`);
|
|
495
497
|
}
|
|
496
498
|
return;
|
|
497
499
|
}
|
|
@@ -787,8 +789,10 @@ function handleClientConnection(ws, clientInfo, customClientId = null) {
|
|
|
787
789
|
|
|
788
790
|
// 发送给配对的 plugin (或广播)
|
|
789
791
|
if (ws.pairedPlugin && ws.pairedPlugin.readyState === WebSocket.OPEN) {
|
|
790
|
-
console.log(`[SEND TO PLUGIN] id=${parsed?.id} method=${parsed?.method} sessionId=${parsed?.sessionId?.substring(0,8) || 'none'}`);
|
|
791
|
-
|
|
792
|
+
console.log(`[SEND TO PLUGIN] id=${parsed?.id} method=${parsed?.method} sessionId=${parsed?.sessionId?.substring(0,8) || 'none'} clientId=${id}`);
|
|
793
|
+
// 在消息中附加 clientId 信息
|
|
794
|
+
const pluginMsg = { ...parsed, __clientId: id };
|
|
795
|
+
ws.pairedPlugin.send(JSON.stringify(pluginMsg));
|
|
792
796
|
} else {
|
|
793
797
|
broadcastToPlugins(modifiedData, ws);
|
|
794
798
|
}
|
|
@@ -1045,7 +1049,7 @@ function rewriteBrowserContextId(cdpMsg) {
|
|
|
1045
1049
|
if (clientId) {
|
|
1046
1050
|
const contextId = clientIdToBrowserContext.get(clientId);
|
|
1047
1051
|
if (contextId) {
|
|
1048
|
-
console.log(`[CONTEXT REWRITE] targetId=${targetInfo.targetId?.substring(0,8)} browserContextId: 'default' -> '${contextId}' (via openerId=${targetInfo.openerId?.substring(0,8) || 'none'}, clientId=${clientId})`);
|
|
1052
|
+
console.log(`[CONTEXT REWRITE] targetId=${targetInfo.targetId?.substring(0,8) || 'none'} browserContextId: 'default' -> '${contextId}' (via openerId=${targetInfo.openerId?.substring(0,8) || 'none'}, clientId=${clientId})`);
|
|
1049
1053
|
targetInfo.browserContextId = contextId;
|
|
1050
1054
|
}
|
|
1051
1055
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// 最简单的标签组测试脚本
|
|
2
|
+
// 复制到Chrome扩展的控制台中运行
|
|
3
|
+
|
|
4
|
+
console.log('=== 最简单的标签组测试 ===');
|
|
5
|
+
|
|
6
|
+
// 步骤1: 检查API是否可用
|
|
7
|
+
console.log('步骤1: 检查API是否可用');
|
|
8
|
+
console.log('chrome.tabs.group:', typeof chrome.tabs.group);
|
|
9
|
+
console.log('chrome.tabGroups:', chrome.tabGroups);
|
|
10
|
+
console.log('chrome.tabGroups.query:', typeof chrome.tabGroups.query);
|
|
11
|
+
console.log('chrome.tabGroups.update:', typeof chrome.tabGroups.update);
|
|
12
|
+
|
|
13
|
+
// 步骤2: 创建一个标签页
|
|
14
|
+
console.log('\n步骤2: 创建一个标签页');
|
|
15
|
+
chrome.tabs.create({ url: 'https://www.baidu.com/', active: false }, function(tab) {
|
|
16
|
+
console.log('标签页创建成功:', tab.id);
|
|
17
|
+
|
|
18
|
+
// 步骤3: 等待5秒
|
|
19
|
+
console.log('\n步骤3: 等待5秒...');
|
|
20
|
+
setTimeout(function() {
|
|
21
|
+
console.log('等待完成,开始创建标签组...');
|
|
22
|
+
|
|
23
|
+
// 步骤4: 创建标签组
|
|
24
|
+
console.log('\n步骤4: 创建标签组');
|
|
25
|
+
chrome.tabs.group({ tabIds: tab.id }, function(groupId) {
|
|
26
|
+
if (chrome.runtime.lastError) {
|
|
27
|
+
console.error('创建标签组失败:', chrome.runtime.lastError.message);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log('标签组创建成功,组ID:', groupId);
|
|
32
|
+
|
|
33
|
+
// 步骤5: 更新标签组
|
|
34
|
+
console.log('\n步骤5: 更新标签组');
|
|
35
|
+
chrome.tabGroups.update(groupId, {
|
|
36
|
+
title: 'Test',
|
|
37
|
+
color: 'blue'
|
|
38
|
+
}, function(group) {
|
|
39
|
+
if (chrome.runtime.lastError) {
|
|
40
|
+
console.error('更新标签组失败:', chrome.runtime.lastError.message);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('标签组更新成功:', group);
|
|
45
|
+
|
|
46
|
+
// 步骤6: 查询所有标签组
|
|
47
|
+
console.log('\n步骤6: 查询所有标签组');
|
|
48
|
+
chrome.tabGroups.query({}, function(groups) {
|
|
49
|
+
console.log('所有标签组:', groups);
|
|
50
|
+
console.log('\n=== 测试完成 ===');
|
|
51
|
+
console.log('请检查Chrome浏览器中是否有一个名为"Test"的蓝色标签组');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}, 5000);
|
|
56
|
+
});
|