cdp-tunnel 2.5.18 → 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
- State.addPendingCreatedTabUrl(url);
106
- // 默认设置active: false,避免自动切换标签页
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
- addTabToAutomationGroup(tab.id, clientId);
122
-
123
- getTargetIdByTabId(tab.id).then(function(targetId) {
124
- return emitAutoAttachEvents(tab.id, targetId, browserContextId).then(function() {
125
- resolve({ targetId: targetId });
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 },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "CDP Bridge",
4
- "version": "2.5.18",
4
+ "version": "2.5.19",
5
5
  "description": "Chrome DevTools Protocol Bridge for Playwright/Puppeteer automation",
6
6
  "permissions": [
7
7
  "debugger",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-tunnel",
3
- "version": "2.5.18",
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",
@@ -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 = null;
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 = null;
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
  }