cdp-tunnel 2.7.9 → 2.8.0
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/background.js +234 -130
- package/extension-new/cdp/handler/forward.js +9 -7
- package/extension-new/cdp/handler/local.js +60 -39
- package/extension-new/cdp/handler/special.js +145 -119
- package/extension-new/cdp/index.js +16 -7
- package/extension-new/cdp/response.js +12 -4
- package/extension-new/config-page-preview.html +174 -57
- package/extension-new/config-page.js +169 -70
- package/extension-new/core/connection-manager.js +120 -0
- package/extension-new/core/connection-state.js +355 -0
- package/extension-new/core/debugger.js +65 -52
- package/extension-new/core/state.js +87 -438
- package/extension-new/core/websocket.js +345 -279
- package/extension-new/features/screencast.js +42 -20
- package/extension-new/manifest.json +3 -2
- package/extension-new/utils/config.js +83 -2
- package/extension-new/utils/helpers.js +5 -4
- package/package.json +1 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
function ConnectionState(connectionId) {
|
|
2
|
+
this.connectionId = connectionId;
|
|
3
|
+
this.ws = null;
|
|
4
|
+
this.reconnectTimer = null;
|
|
5
|
+
this._hasConnectedClient = false;
|
|
6
|
+
this.cdpClients = [];
|
|
7
|
+
this.currentTabId = null;
|
|
8
|
+
this.isAttached = false;
|
|
9
|
+
|
|
10
|
+
this.sessionIdToTabId = new Map();
|
|
11
|
+
this.sessionIdToTargetId = new Map();
|
|
12
|
+
this.tabIdToClientId = new Map();
|
|
13
|
+
this.clientIdToGroupId = new Map();
|
|
14
|
+
this.attachedTabIds = new Set();
|
|
15
|
+
this.cdpCreatedTabIds = new Set();
|
|
16
|
+
this.emittedTargets = new Set();
|
|
17
|
+
this.pendingCreatedTabUrls = new Set();
|
|
18
|
+
this.preExistingTabIds = new Set();
|
|
19
|
+
this.pendingDebuggerTabs = new Set();
|
|
20
|
+
this.autoAttachConfig = {
|
|
21
|
+
autoAttach: false,
|
|
22
|
+
waitForDebuggerOnStart: false,
|
|
23
|
+
flatten: true
|
|
24
|
+
};
|
|
25
|
+
this.discoverTargetsEnabled = false;
|
|
26
|
+
this.browserContextIds = new Set(['default']);
|
|
27
|
+
this.screencastPollingSessions = new Map();
|
|
28
|
+
this.automatedTabs = new Set();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ConnectionState.prototype.mapSession = function(sessionId, tabId, targetId) {
|
|
32
|
+
this.sessionIdToTabId.set(sessionId, tabId);
|
|
33
|
+
this.sessionIdToTargetId.set(sessionId, targetId);
|
|
34
|
+
this.attachedTabIds.add(tabId);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
ConnectionState.prototype.unmapSession = function(sessionId) {
|
|
38
|
+
var tabId = this.sessionIdToTabId.get(sessionId);
|
|
39
|
+
this.sessionIdToTabId.delete(sessionId);
|
|
40
|
+
this.sessionIdToTargetId.delete(sessionId);
|
|
41
|
+
if (tabId && !this.hasOtherSessionForTab(tabId)) {
|
|
42
|
+
this.attachedTabIds.delete(tabId);
|
|
43
|
+
}
|
|
44
|
+
return tabId;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
ConnectionState.prototype.getTabIdBySession = function(sessionId) {
|
|
48
|
+
return this.sessionIdToTabId.get(sessionId);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
ConnectionState.prototype.getTargetIdBySession = function(sessionId) {
|
|
52
|
+
return this.sessionIdToTargetId.get(sessionId);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
ConnectionState.prototype.findSessionByTabId = function(tabId) {
|
|
56
|
+
var lastMatch = null;
|
|
57
|
+
this.sessionIdToTabId.forEach(function(mappedTabId, sessionId) {
|
|
58
|
+
if (mappedTabId === tabId) lastMatch = sessionId;
|
|
59
|
+
});
|
|
60
|
+
return lastMatch;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
ConnectionState.prototype.findSessionsByTabId = function(tabId) {
|
|
64
|
+
var sessions = [];
|
|
65
|
+
this.sessionIdToTabId.forEach(function(mappedTabId, sessionId) {
|
|
66
|
+
if (mappedTabId === tabId) sessions.push(sessionId);
|
|
67
|
+
});
|
|
68
|
+
return sessions;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
ConnectionState.prototype.findSessionByTargetId = function(targetId) {
|
|
72
|
+
var entries = this.sessionIdToTargetId.entries();
|
|
73
|
+
var entry = entries.next();
|
|
74
|
+
while (!entry.done) {
|
|
75
|
+
if (entry.value[1] === targetId) return entry.value[0];
|
|
76
|
+
entry = entries.next();
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
ConnectionState.prototype.getTabIdByTargetId = function(targetId) {
|
|
82
|
+
var sessionId = this.findSessionByTargetId(targetId);
|
|
83
|
+
if (sessionId) return this.sessionIdToTabId.get(sessionId);
|
|
84
|
+
return null;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
ConnectionState.prototype.hasOtherSessionForTab = function(tabId) {
|
|
88
|
+
var count = 0;
|
|
89
|
+
this.sessionIdToTabId.forEach(function(mappedTabId) {
|
|
90
|
+
if (mappedTabId === tabId) count++;
|
|
91
|
+
});
|
|
92
|
+
return count > 0;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
ConnectionState.prototype.addAttachedTab = function(tabId) {
|
|
96
|
+
this.attachedTabIds.add(tabId);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
ConnectionState.prototype.removeAttachedTab = function(tabId) {
|
|
100
|
+
this.attachedTabIds.delete(tabId);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
ConnectionState.prototype.isTabAttached = function(tabId) {
|
|
104
|
+
return this.attachedTabIds.has(tabId);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
ConnectionState.prototype.getAttachedTabIds = function() {
|
|
108
|
+
return Array.from(this.attachedTabIds);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
ConnectionState.prototype.addEmittedTarget = function(targetId) {
|
|
112
|
+
this.emittedTargets.add(targetId);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
ConnectionState.prototype.hasEmittedTarget = function(targetId) {
|
|
116
|
+
return this.emittedTargets.has(targetId);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
ConnectionState.prototype.setAutoAttachConfig = function(config) {
|
|
120
|
+
Object.assign(this.autoAttachConfig, config);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
ConnectionState.prototype.getAutoAttachConfig = function() {
|
|
124
|
+
return Object.assign({}, this.autoAttachConfig);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
ConnectionState.prototype.setDiscoverTargets = function(enabled) {
|
|
128
|
+
this.discoverTargetsEnabled = enabled;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
ConnectionState.prototype.addPendingDebuggerTab = function(tabId) {
|
|
132
|
+
this.pendingDebuggerTabs.add(tabId);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
ConnectionState.prototype.removePendingDebuggerTab = function(tabId) {
|
|
136
|
+
this.pendingDebuggerTabs.delete(tabId);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
ConnectionState.prototype.isPendingDebuggerTab = function(tabId) {
|
|
140
|
+
return this.pendingDebuggerTabs.has(tabId);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
ConnectionState.prototype.addBrowserContext = function(id) {
|
|
144
|
+
this.browserContextIds.add(id);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
ConnectionState.prototype.removeBrowserContext = function(id) {
|
|
148
|
+
this.browserContextIds.delete(id);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
ConnectionState.prototype.getBrowserContexts = function() {
|
|
152
|
+
return Array.from(this.browserContextIds);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
ConnectionState.prototype.getCurrentTabId = function() {
|
|
156
|
+
return this.currentTabId;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
ConnectionState.prototype.setCurrentTabId = function(tabId) {
|
|
160
|
+
this.currentTabId = tabId;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
ConnectionState.prototype.getWs = function() {
|
|
164
|
+
return this.ws;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
ConnectionState.prototype.setWs = function(ws) {
|
|
168
|
+
this.ws = ws;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
ConnectionState.prototype.clearReconnectTimer = function() {
|
|
172
|
+
if (this.reconnectTimer) {
|
|
173
|
+
clearTimeout(this.reconnectTimer);
|
|
174
|
+
this.reconnectTimer = null;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
ConnectionState.prototype.setReconnectTimer = function(timer) {
|
|
179
|
+
this.reconnectTimer = timer;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
ConnectionState.prototype.hasConnectedClient = function() {
|
|
183
|
+
return this._hasConnectedClient;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
ConnectionState.prototype.setHasConnectedClient = function(value) {
|
|
187
|
+
this._hasConnectedClient = value;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
ConnectionState.prototype.addPendingCreatedTabUrl = function(url) {
|
|
191
|
+
this.pendingCreatedTabUrls.add(url);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
ConnectionState.prototype.removePendingCreatedTabUrl = function(url) {
|
|
195
|
+
this.pendingCreatedTabUrls.delete(url);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
ConnectionState.prototype.hasPendingCreatedTabUrl = function(url) {
|
|
199
|
+
return this.pendingCreatedTabUrls.has(url);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
ConnectionState.prototype.addCDPCreatedTab = function(tabId) {
|
|
203
|
+
this.cdpCreatedTabIds.add(tabId);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
ConnectionState.prototype.isCDPCreatedTab = function(tabId) {
|
|
207
|
+
return this.cdpCreatedTabIds.has(tabId);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
ConnectionState.prototype.getCDPCreatedTabIds = function() {
|
|
211
|
+
return Array.from(this.cdpCreatedTabIds);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
ConnectionState.prototype.getScreencastSession = function(tabId) {
|
|
215
|
+
return this.screencastPollingSessions.get(tabId);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
ConnectionState.prototype.setScreencastSession = function(tabId, session) {
|
|
219
|
+
this.screencastPollingSessions.set(tabId, session);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
ConnectionState.prototype.deleteScreencastSession = function(tabId) {
|
|
223
|
+
this.screencastPollingSessions.delete(tabId);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
ConnectionState.prototype.addAutomatedTab = function(tabId) {
|
|
227
|
+
this.automatedTabs.add(tabId);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
ConnectionState.prototype.removeAutomatedTab = function(tabId) {
|
|
231
|
+
this.automatedTabs.delete(tabId);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
ConnectionState.prototype.getAutomatedTabs = function() {
|
|
235
|
+
return Array.from(this.automatedTabs);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
ConnectionState.prototype.setTabIdToClientId = function(tabId, clientId) {
|
|
239
|
+
this.tabIdToClientId.set(tabId, clientId);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
ConnectionState.prototype.removeTabIdToClientId = function(tabId) {
|
|
243
|
+
this.tabIdToClientId.delete(tabId);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
ConnectionState.prototype.getClientIdByTabId = function(tabId) {
|
|
247
|
+
return this.tabIdToClientId.get(tabId);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
ConnectionState.prototype.setGroupIdForClient = function(clientId, groupId) {
|
|
251
|
+
this.clientIdToGroupId.set(clientId, groupId);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
ConnectionState.prototype.getGroupIdForClient = function(clientId) {
|
|
255
|
+
return this.clientIdToGroupId.get(clientId);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
ConnectionState.prototype.removeGroupForClient = function(clientId) {
|
|
259
|
+
this.clientIdToGroupId.delete(clientId);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
ConnectionState.prototype.addPreExistingTab = function(tabId) {
|
|
263
|
+
this.preExistingTabIds.add(tabId);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
ConnectionState.prototype.isPreExistingTab = function(tabId) {
|
|
267
|
+
return this.preExistingTabIds.has(tabId);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
ConnectionState.prototype.getPreExistingTabs = function() {
|
|
271
|
+
return Array.from(this.preExistingTabIds);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
ConnectionState.prototype.removePreExistingTab = function(tabId) {
|
|
275
|
+
this.preExistingTabIds.delete(tabId);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
ConnectionState.prototype.clearPreExistingTabsForClient = function(clientId) {
|
|
279
|
+
var self = this;
|
|
280
|
+
this.preExistingTabIds.forEach(function(tabId) {
|
|
281
|
+
if (self.getClientIdByTabId(tabId) === clientId) {
|
|
282
|
+
self.preExistingTabIds.delete(tabId);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
ConnectionState.prototype.addCDPClient = function(clientId, info) {
|
|
288
|
+
var exists = false;
|
|
289
|
+
for (var i = 0; i < this.cdpClients.length; i++) {
|
|
290
|
+
if (this.cdpClients[i].id === clientId) { exists = true; break; }
|
|
291
|
+
}
|
|
292
|
+
if (!exists) {
|
|
293
|
+
this.cdpClients.push({ id: clientId, connectedAt: Date.now() });
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
ConnectionState.prototype.removeCDPClient = function(clientId) {
|
|
298
|
+
var newClients = [];
|
|
299
|
+
for (var i = 0; i < this.cdpClients.length; i++) {
|
|
300
|
+
if (this.cdpClients[i].id !== clientId) newClients.push(this.cdpClients[i]);
|
|
301
|
+
}
|
|
302
|
+
this.cdpClients = newClients;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
ConnectionState.prototype.getCDPClients = function() {
|
|
306
|
+
return this.cdpClients;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
ConnectionState.prototype.setCDPClients = function(clients) {
|
|
310
|
+
this.cdpClients = clients || [];
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
ConnectionState.prototype.clearSessionState = function() {
|
|
314
|
+
this.sessionIdToTabId.clear();
|
|
315
|
+
this.sessionIdToTargetId.clear();
|
|
316
|
+
this.pendingDebuggerTabs.clear();
|
|
317
|
+
this.emittedTargets.clear();
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
ConnectionState.prototype.clearAllState = function() {
|
|
321
|
+
this.clearSessionState();
|
|
322
|
+
this.attachedTabIds.clear();
|
|
323
|
+
this.emittedTargets.clear();
|
|
324
|
+
this.screencastPollingSessions.clear();
|
|
325
|
+
this.browserContextIds = new Set(['default']);
|
|
326
|
+
this.autoAttachConfig = { autoAttach: false, waitForDebuggerOnStart: false, flatten: true };
|
|
327
|
+
this.discoverTargetsEnabled = false;
|
|
328
|
+
this._hasConnectedClient = false;
|
|
329
|
+
this.tabIdToClientId.clear();
|
|
330
|
+
this.clientIdToGroupId.clear();
|
|
331
|
+
this.preExistingTabIds.clear();
|
|
332
|
+
this.pendingDebuggerTabs.clear();
|
|
333
|
+
this.automatedTabs.clear();
|
|
334
|
+
this.pendingCreatedTabUrls.clear();
|
|
335
|
+
this.cdpCreatedTabIds.clear();
|
|
336
|
+
this.cdpClients = [];
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
ConnectionState.prototype.persist = function(tabId, attached) {
|
|
340
|
+
this.currentTabId = tabId;
|
|
341
|
+
this.isAttached = attached;
|
|
342
|
+
return new Promise(function(resolve) {
|
|
343
|
+
chrome.storage.local.set({ currentTabId: tabId, isAttached: attached }, resolve);
|
|
344
|
+
}.bind(this));
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
ConnectionState.prototype.loadPersisted = function() {
|
|
348
|
+
return new Promise(function(resolve) {
|
|
349
|
+
chrome.storage.local.get(['currentTabId', 'isAttached'], function(result) {
|
|
350
|
+
this.currentTabId = result.currentTabId || null;
|
|
351
|
+
this.isAttached = result.isAttached || false;
|
|
352
|
+
resolve(result);
|
|
353
|
+
}.bind(this));
|
|
354
|
+
}.bind(this));
|
|
355
|
+
};
|
|
@@ -8,17 +8,14 @@ var DebuggerManager = (function() {
|
|
|
8
8
|
|
|
9
9
|
function isInternalUrl(url) {
|
|
10
10
|
if (!url) return false;
|
|
11
|
-
// Whitelist: only allow http/https/about/data/blob/file
|
|
12
11
|
if (url.startsWith('http:') || url.startsWith('https:') || url.startsWith('about:') || url.startsWith('data:') || url.startsWith('blob:') || url.startsWith('file:')) {
|
|
13
12
|
return false;
|
|
14
13
|
}
|
|
15
|
-
// Also check explicit blocked list for common custom protocols
|
|
16
14
|
for (var i = 0; i < blockedProtocols.length; i++) {
|
|
17
15
|
if (url.startsWith(blockedProtocols[i])) {
|
|
18
16
|
return true;
|
|
19
17
|
}
|
|
20
18
|
}
|
|
21
|
-
// Block any other custom protocol (xxx://)
|
|
22
19
|
var colonIdx = url.indexOf(':');
|
|
23
20
|
if (colonIdx > 0 && colonIdx < 20 && url.substring(colonIdx, colonIdx + 3) === '://') {
|
|
24
21
|
return true;
|
|
@@ -26,7 +23,6 @@ var DebuggerManager = (function() {
|
|
|
26
23
|
return false;
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
// 拦截 iframe 创建
|
|
30
26
|
var originalCreateElement = document.createElement;
|
|
31
27
|
document.createElement = function(tagName) {
|
|
32
28
|
var element = originalCreateElement.call(document, tagName);
|
|
@@ -43,7 +39,6 @@ var DebuggerManager = (function() {
|
|
|
43
39
|
return element;
|
|
44
40
|
};
|
|
45
41
|
|
|
46
|
-
// 拦截 window.open
|
|
47
42
|
var originalWindowOpen = window.open;
|
|
48
43
|
window.open = function(url) {
|
|
49
44
|
if (isInternalUrl(url)) {
|
|
@@ -53,7 +48,6 @@ var DebuggerManager = (function() {
|
|
|
53
48
|
return originalWindowOpen.apply(this, arguments);
|
|
54
49
|
};
|
|
55
50
|
|
|
56
|
-
// 拦截 location 修改
|
|
57
51
|
var locationDescriptor = Object.getOwnPropertyDescriptor(window, 'location');
|
|
58
52
|
if (locationDescriptor && typeof locationDescriptor.set === 'function') {
|
|
59
53
|
try {
|
|
@@ -71,7 +65,6 @@ var DebuggerManager = (function() {
|
|
|
71
65
|
configurable: true
|
|
72
66
|
});
|
|
73
67
|
} catch(e) {
|
|
74
|
-
// location property cannot be redefined on some contexts, skip silently
|
|
75
68
|
}
|
|
76
69
|
}
|
|
77
70
|
|
|
@@ -79,7 +72,8 @@ var DebuggerManager = (function() {
|
|
|
79
72
|
})();
|
|
80
73
|
`;
|
|
81
74
|
|
|
82
|
-
function attach(tabId) {
|
|
75
|
+
function attach(tabId, connState) {
|
|
76
|
+
var state = connState || _getAnyStateForTab(tabId);
|
|
83
77
|
if (tabId == null) {
|
|
84
78
|
return Promise.resolve(false);
|
|
85
79
|
}
|
|
@@ -87,7 +81,7 @@ var DebuggerManager = (function() {
|
|
|
87
81
|
return ensureTabExists(tabId).then(function(exists) {
|
|
88
82
|
if (!exists) {
|
|
89
83
|
Logger.warn('[Debugger] Tab', tabId, 'does not exist');
|
|
90
|
-
|
|
84
|
+
if (state) state.persist(null, false);
|
|
91
85
|
return false;
|
|
92
86
|
}
|
|
93
87
|
|
|
@@ -95,23 +89,28 @@ var DebuggerManager = (function() {
|
|
|
95
89
|
if (isAttached) {
|
|
96
90
|
Logger.info('[Debugger] Tab', tabId, 'already attached, detaching first...');
|
|
97
91
|
return chrome.debugger.detach({ tabId: tabId }).catch(function() {}).then(function() {
|
|
98
|
-
return doAttach(tabId);
|
|
92
|
+
return doAttach(tabId, state);
|
|
99
93
|
});
|
|
100
94
|
}
|
|
101
|
-
return doAttach(tabId);
|
|
95
|
+
return doAttach(tabId, state);
|
|
102
96
|
});
|
|
103
97
|
});
|
|
104
98
|
}
|
|
105
99
|
|
|
106
|
-
function
|
|
100
|
+
function _getAnyStateForTab(tabId) {
|
|
101
|
+
var entry = ConnectionManager.getConnectionByTabId(tabId);
|
|
102
|
+
return entry ? entry.state : null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function doAttach(tabId, state) {
|
|
107
106
|
return chrome.debugger.attach({ tabId: tabId }, Config.DEBUGGER_VERSION)
|
|
108
107
|
.then(function() {
|
|
109
108
|
Logger.info('[Debugger] Attached to tab', tabId);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
if (state) {
|
|
110
|
+
state.addAttachedTab(tabId);
|
|
111
|
+
state.setCurrentTabId(tabId);
|
|
112
|
+
state.persist(tabId, true);
|
|
113
|
+
}
|
|
115
114
|
return injectInternalUrlBlocker(tabId);
|
|
116
115
|
})
|
|
117
116
|
.then(function() {
|
|
@@ -142,7 +141,8 @@ var DebuggerManager = (function() {
|
|
|
142
141
|
});
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
function detach(tabId) {
|
|
144
|
+
function detach(tabId, connState) {
|
|
145
|
+
var state = connState || _getAnyStateForTab(tabId);
|
|
146
146
|
if (tabId == null) {
|
|
147
147
|
return Promise.resolve();
|
|
148
148
|
}
|
|
@@ -152,16 +152,18 @@ var DebuggerManager = (function() {
|
|
|
152
152
|
return chrome.debugger.detach({ tabId: tabId })
|
|
153
153
|
.then(function() {
|
|
154
154
|
Logger.info('[Debugger] Detached from tab', tabId);
|
|
155
|
-
|
|
155
|
+
if (state) {
|
|
156
|
+
state.removeAttachedTab(tabId);
|
|
157
|
+
if (state.getCurrentTabId() === tabId) {
|
|
158
|
+
state.persist(null, false);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
156
161
|
AutomationBadge.remove(tabId);
|
|
157
162
|
Screencast.stopPolling(tabId);
|
|
158
|
-
if (State.getCurrentTabId() === tabId) {
|
|
159
|
-
State.persist(null, false);
|
|
160
|
-
}
|
|
161
163
|
})
|
|
162
164
|
.catch(function(error) {
|
|
163
165
|
Logger.error('[Debugger] Failed to detach from tab', tabId, ':', error.message);
|
|
164
|
-
|
|
166
|
+
if (state) state.removeAttachedTab(tabId);
|
|
165
167
|
AutomationBadge.remove(tabId);
|
|
166
168
|
Screencast.stopPolling(tabId);
|
|
167
169
|
});
|
|
@@ -191,11 +193,17 @@ var DebuggerManager = (function() {
|
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
function handleDebuggerEvent(source, method, params) {
|
|
194
|
-
|
|
196
|
+
var entry = ConnectionManager.getConnectionByTabId(source.tabId);
|
|
197
|
+
if (!entry) return;
|
|
198
|
+
|
|
199
|
+
var state = entry.state;
|
|
200
|
+
var wsManager = entry.wsManager;
|
|
201
|
+
|
|
202
|
+
if (!state.isTabAttached(source.tabId)) {
|
|
195
203
|
return;
|
|
196
204
|
}
|
|
197
205
|
|
|
198
|
-
if (!
|
|
206
|
+
if (!state.hasConnectedClient()) {
|
|
199
207
|
return;
|
|
200
208
|
}
|
|
201
209
|
|
|
@@ -203,20 +211,19 @@ var DebuggerManager = (function() {
|
|
|
203
211
|
return;
|
|
204
212
|
}
|
|
205
213
|
|
|
206
|
-
var sessionIds =
|
|
214
|
+
var sessionIds = state.findSessionsByTabId(source.tabId);
|
|
207
215
|
Logger.info('[Event] method=' + method + ' tabId=' + source.tabId + ' sessions=' + sessionIds.length);
|
|
208
|
-
|
|
216
|
+
|
|
209
217
|
if (method === 'Runtime.executionContextCreated' && params && params.context) {
|
|
210
218
|
var context = params.context;
|
|
211
219
|
var isPlaywrightContext = context.name && context.name.indexOf('__playwright') === 0;
|
|
212
220
|
var isDefaultContext = context.auxData && context.auxData.isDefault;
|
|
213
|
-
|
|
221
|
+
|
|
214
222
|
if (!isPlaywrightContext && !isDefaultContext) {
|
|
215
223
|
Logger.info('[Event] Filtering non-Playwright Runtime.executionContextCreated:', context.name);
|
|
216
224
|
return;
|
|
217
225
|
}
|
|
218
|
-
|
|
219
|
-
// 在新的执行上下文中也注入拦截脚本(data: URL 上下文跳过,避免干扰导航)
|
|
226
|
+
|
|
220
227
|
if (isDefaultContext) {
|
|
221
228
|
chrome.tabs.get(source.tabId, function(tab) {
|
|
222
229
|
if (chrome.runtime.lastError) return;
|
|
@@ -234,7 +241,7 @@ var DebuggerManager = (function() {
|
|
|
234
241
|
});
|
|
235
242
|
}
|
|
236
243
|
}
|
|
237
|
-
|
|
244
|
+
|
|
238
245
|
if (method === 'Page.frameRequestedNavigation' && params) {
|
|
239
246
|
var url = params.url || '';
|
|
240
247
|
var reason = params.reason || '';
|
|
@@ -245,12 +252,11 @@ var DebuggerManager = (function() {
|
|
|
245
252
|
Logger.warn(' Reason:', reason);
|
|
246
253
|
Logger.warn(' Disposition:', disposition);
|
|
247
254
|
Logger.warn(' FrameId:', frameId);
|
|
248
|
-
|
|
255
|
+
|
|
249
256
|
if (url && !url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('about:') && !url.startsWith('data:') && !url.startsWith('blob:') && !url.startsWith('file://')) {
|
|
250
257
|
Logger.error('[NAVIGATION] ⚠️ 检测到导航到内部页面,尝试阻止!');
|
|
251
258
|
Logger.error('[NAVIGATION] 目标URL:', url);
|
|
252
|
-
|
|
253
|
-
// 尝试方法1: 停止页面加载
|
|
259
|
+
|
|
254
260
|
chrome.debugger.sendCommand(
|
|
255
261
|
{ tabId: source.tabId },
|
|
256
262
|
'Page.stopLoading',
|
|
@@ -260,8 +266,7 @@ var DebuggerManager = (function() {
|
|
|
260
266
|
}).catch(function(e) {
|
|
261
267
|
Logger.error('[NAVIGATION] Page.stopLoading 失败:', e.message);
|
|
262
268
|
});
|
|
263
|
-
|
|
264
|
-
// 尝试方法2: 获取当前页面URL并导航回去
|
|
269
|
+
|
|
265
270
|
chrome.tabs.get(source.tabId, function(tab) {
|
|
266
271
|
if (tab && tab.url && !tab.url.startsWith('bitbrowser://')) {
|
|
267
272
|
Logger.info('[NAVIGATION] 尝试导航回原页面:', tab.url);
|
|
@@ -278,36 +283,44 @@ var DebuggerManager = (function() {
|
|
|
278
283
|
}, 100);
|
|
279
284
|
}
|
|
280
285
|
});
|
|
281
|
-
|
|
282
|
-
// 不转发这个导航事件给客户端
|
|
286
|
+
|
|
283
287
|
Logger.warn('[NAVIGATION] 阻止转发导航事件到客户端');
|
|
284
288
|
return;
|
|
285
289
|
}
|
|
286
290
|
}
|
|
287
|
-
|
|
291
|
+
|
|
288
292
|
for (var i = 0; i < sessionIds.length; i++) {
|
|
289
|
-
EventBuilder.send(method, params, sessionIds[i]);
|
|
293
|
+
EventBuilder.send(method, params, sessionIds[i], wsManager);
|
|
290
294
|
}
|
|
291
295
|
}
|
|
292
296
|
|
|
293
297
|
function handleDetach(source, reason) {
|
|
294
298
|
Logger.info('[Debugger] Detached from tab', source.tabId, ', reason:', reason);
|
|
295
|
-
|
|
299
|
+
|
|
300
|
+
var entry = ConnectionManager.getConnectionByTabId(source.tabId);
|
|
301
|
+
var state = entry ? entry.state : null;
|
|
302
|
+
var wsManager = entry ? entry.wsManager : null;
|
|
303
|
+
|
|
304
|
+
if (state) {
|
|
305
|
+
state.removeAttachedTab(source.tabId);
|
|
306
|
+
}
|
|
296
307
|
Screencast.stopPolling(source.tabId);
|
|
297
308
|
AutomationBadge.remove(source.tabId);
|
|
298
309
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
310
|
+
if (state) {
|
|
311
|
+
var sessionId = state.findSessionByTabId(source.tabId);
|
|
312
|
+
if (sessionId) {
|
|
313
|
+
var targetId = state.getTargetIdBySession(sessionId);
|
|
314
|
+
EventBuilder.send('Target.detachedFromTarget', {
|
|
315
|
+
sessionId: sessionId,
|
|
316
|
+
targetId: targetId
|
|
317
|
+
}, null, wsManager);
|
|
318
|
+
state.unmapSession(sessionId);
|
|
319
|
+
}
|
|
308
320
|
|
|
309
|
-
|
|
310
|
-
|
|
321
|
+
if (state.getCurrentTabId() === source.tabId) {
|
|
322
|
+
state.persist(null, false);
|
|
323
|
+
}
|
|
311
324
|
}
|
|
312
325
|
}
|
|
313
326
|
|