@vforsh/argus 0.1.19 → 0.1.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/dist/.tsbuildinfo +1 -1
- package/dist/argus.js +257 -59
- package/dist/cli/register/extensionCommands.d.ts.map +1 -1
- package/dist/cli/register/extensionCommands.js +28 -3
- package/dist/cli/register/extensionCommands.js.map +1 -1
- package/dist/commands/extension/extensionId.d.ts +27 -0
- package/dist/commands/extension/extensionId.d.ts.map +1 -0
- package/dist/commands/extension/extensionId.js +37 -0
- package/dist/commands/extension/extensionId.js.map +1 -0
- package/dist/commands/extension/extensionPath.d.ts +15 -0
- package/dist/commands/extension/extensionPath.d.ts.map +1 -0
- package/dist/commands/extension/extensionPath.js +61 -0
- package/dist/commands/extension/extensionPath.js.map +1 -0
- package/dist/commands/extension/install.d.ts +16 -0
- package/dist/commands/extension/install.d.ts.map +1 -0
- package/dist/commands/extension/install.js +154 -0
- package/dist/commands/extension/install.js.map +1 -0
- package/dist/commands/extension/setup.d.ts +2 -1
- package/dist/commands/extension/setup.d.ts.map +1 -1
- package/dist/commands/extension/setup.js +5 -2
- package/dist/commands/extension/setup.js.map +1 -1
- package/dist/extension/dist/background/auth-header-monitor.js +77 -0
- package/dist/extension/dist/background/bridge-client.js +120 -0
- package/dist/extension/dist/background/cdp-proxy.js +201 -0
- package/dist/extension/dist/background/debugger-manager.js +296 -0
- package/dist/extension/dist/background/service-worker.js +2004 -0
- package/dist/extension/dist/background/service-worker.js.map +7 -0
- package/dist/extension/dist/popup/popup.js +730 -0
- package/dist/extension/dist/popup/popup.js.map +7 -0
- package/dist/extension/dist/popup/tabMarkup.js +104 -0
- package/dist/extension/dist/popup/viewState.js +60 -0
- package/dist/extension/dist/types/messages.js +5 -0
- package/dist/extension/icons/icon-128.png +0 -0
- package/dist/extension/icons/icon-16.png +0 -0
- package/dist/extension/icons/icon-48.png +0 -0
- package/dist/extension/manifest.json +26 -0
- package/dist/extension/src/popup/popup.html +756 -0
- package/dist/skill/argus/SKILL.md +3 -1
- package/dist/skill/argus/reference/EXTENSION.md +13 -8
- package/package.json +2 -2
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP Proxy - Routes CDP commands and events between the Native Messaging
|
|
3
|
+
* host (argus-watcher in extension mode) and the chrome.debugger API.
|
|
4
|
+
*/
|
|
5
|
+
export class CdpProxy {
|
|
6
|
+
debuggerManager;
|
|
7
|
+
bridgeClient;
|
|
8
|
+
constructor(debuggerManager, bridgeClient) {
|
|
9
|
+
this.debuggerManager = debuggerManager;
|
|
10
|
+
this.bridgeClient = bridgeClient;
|
|
11
|
+
this.setupEventForwarding();
|
|
12
|
+
this.setupMessageHandling();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Forward CDP events from debugger to bridge.
|
|
16
|
+
*/
|
|
17
|
+
setupEventForwarding() {
|
|
18
|
+
this.debuggerManager.onEvent((tabId, method, params, meta) => {
|
|
19
|
+
this.bridgeClient.send({
|
|
20
|
+
type: 'cdp_event',
|
|
21
|
+
tabId,
|
|
22
|
+
method,
|
|
23
|
+
params,
|
|
24
|
+
sessionId: meta?.sessionId ?? undefined,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
this.debuggerManager.onDetach((tabId, reason) => {
|
|
28
|
+
this.bridgeClient.send({
|
|
29
|
+
type: 'tab_detached',
|
|
30
|
+
tabId,
|
|
31
|
+
reason,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Handle messages from the bridge.
|
|
37
|
+
*/
|
|
38
|
+
setupMessageHandling() {
|
|
39
|
+
this.bridgeClient.onMessage((message) => {
|
|
40
|
+
this.handleMessage(message);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Process a message from the bridge.
|
|
45
|
+
*/
|
|
46
|
+
async handleMessage(message) {
|
|
47
|
+
switch (message.type) {
|
|
48
|
+
case 'attach_tab':
|
|
49
|
+
await this.handleAttachTab(message);
|
|
50
|
+
break;
|
|
51
|
+
case 'detach_tab':
|
|
52
|
+
await this.handleDetachTab(message);
|
|
53
|
+
break;
|
|
54
|
+
case 'cdp_command':
|
|
55
|
+
await this.handleCdpCommand(message);
|
|
56
|
+
break;
|
|
57
|
+
case 'list_tabs':
|
|
58
|
+
await this.handleListTabs(message);
|
|
59
|
+
break;
|
|
60
|
+
case 'enable_domain':
|
|
61
|
+
await this.handleEnableDomain(message);
|
|
62
|
+
break;
|
|
63
|
+
case 'host_info':
|
|
64
|
+
case 'target_info':
|
|
65
|
+
break;
|
|
66
|
+
default:
|
|
67
|
+
console.warn('[CdpProxy] Unknown message type:', message.type);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Attach to a tab.
|
|
72
|
+
*/
|
|
73
|
+
async handleAttachTab(message) {
|
|
74
|
+
try {
|
|
75
|
+
const target = await this.debuggerManager.attach(message.tabId);
|
|
76
|
+
this.bridgeClient.send({
|
|
77
|
+
type: 'tab_attached',
|
|
78
|
+
tabId: target.tabId,
|
|
79
|
+
url: target.url,
|
|
80
|
+
title: target.title,
|
|
81
|
+
faviconUrl: target.faviconUrl,
|
|
82
|
+
topFrameId: target.topFrameId,
|
|
83
|
+
frames: this.debuggerManager.getFrames(target.tabId),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.error('[CdpProxy] Failed to attach:', err);
|
|
88
|
+
this.bridgeClient.send({
|
|
89
|
+
type: 'tab_detached',
|
|
90
|
+
tabId: message.tabId,
|
|
91
|
+
reason: err instanceof Error ? err.message : 'attach_failed',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Detach from a tab.
|
|
97
|
+
*/
|
|
98
|
+
async handleDetachTab(message) {
|
|
99
|
+
await this.debuggerManager.detach(message.tabId);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Execute a CDP command.
|
|
103
|
+
*/
|
|
104
|
+
async handleCdpCommand(message) {
|
|
105
|
+
try {
|
|
106
|
+
const result = await this.debuggerManager.sendCommand(message.tabId, message.method, message.params, message.sessionId);
|
|
107
|
+
this.bridgeClient.send({
|
|
108
|
+
type: 'cdp_response',
|
|
109
|
+
requestId: message.requestId,
|
|
110
|
+
result,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
this.bridgeClient.send({
|
|
115
|
+
type: 'cdp_response',
|
|
116
|
+
requestId: message.requestId,
|
|
117
|
+
error: {
|
|
118
|
+
message: err instanceof Error ? err.message : 'Unknown error',
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* List available tabs.
|
|
125
|
+
*/
|
|
126
|
+
async handleListTabs(message) {
|
|
127
|
+
const chromeTabs = await chrome.tabs.query({});
|
|
128
|
+
const attachedTargets = this.debuggerManager.listAttached();
|
|
129
|
+
const attachedTabIds = new Set(attachedTargets.map((t) => t.tabId));
|
|
130
|
+
let tabs = chromeTabs
|
|
131
|
+
.filter((tab) => tab.id !== undefined && tab.url !== undefined)
|
|
132
|
+
.map((tab) => ({
|
|
133
|
+
tabId: tab.id,
|
|
134
|
+
url: tab.url,
|
|
135
|
+
title: tab.title ?? '',
|
|
136
|
+
faviconUrl: tab.favIconUrl,
|
|
137
|
+
attached: attachedTabIds.has(tab.id),
|
|
138
|
+
}));
|
|
139
|
+
// Apply filters if provided
|
|
140
|
+
if (message.filter) {
|
|
141
|
+
if (message.filter.url) {
|
|
142
|
+
const urlFilter = message.filter.url.toLowerCase();
|
|
143
|
+
tabs = tabs.filter((t) => t.url.toLowerCase().includes(urlFilter));
|
|
144
|
+
}
|
|
145
|
+
if (message.filter.title) {
|
|
146
|
+
const titleFilter = message.filter.title.toLowerCase();
|
|
147
|
+
tabs = tabs.filter((t) => t.title.toLowerCase().includes(titleFilter));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
this.bridgeClient.send({
|
|
151
|
+
type: 'list_tabs_response',
|
|
152
|
+
tabs,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Enable a CDP domain for a tab.
|
|
157
|
+
*/
|
|
158
|
+
async handleEnableDomain(message) {
|
|
159
|
+
try {
|
|
160
|
+
await this.debuggerManager.enableDomain(message.tabId, message.domain);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
console.error('[CdpProxy] Failed to enable domain:', err);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Manually attach to a tab (called from popup).
|
|
168
|
+
*/
|
|
169
|
+
async attachTab(tabId) {
|
|
170
|
+
await this.handleAttachTab({ type: 'attach_tab', tabId });
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Manually detach from a tab (called from popup).
|
|
174
|
+
*/
|
|
175
|
+
async detachTab(tabId) {
|
|
176
|
+
await this.debuggerManager.detach(tabId);
|
|
177
|
+
this.bridgeClient.send({
|
|
178
|
+
type: 'tab_detached',
|
|
179
|
+
tabId,
|
|
180
|
+
reason: 'user_requested',
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get list of tabs for popup UI.
|
|
185
|
+
*/
|
|
186
|
+
async getTabsForPopup() {
|
|
187
|
+
const chromeTabs = await chrome.tabs.query({});
|
|
188
|
+
const attachedTargets = this.debuggerManager.listAttached();
|
|
189
|
+
const attachedTabIds = new Set(attachedTargets.map((t) => t.tabId));
|
|
190
|
+
return chromeTabs
|
|
191
|
+
.filter((tab) => tab.id !== undefined && tab.url !== undefined)
|
|
192
|
+
.filter((tab) => !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://'))
|
|
193
|
+
.map((tab) => ({
|
|
194
|
+
tabId: tab.id,
|
|
195
|
+
url: tab.url,
|
|
196
|
+
title: tab.title ?? '',
|
|
197
|
+
faviconUrl: tab.favIconUrl,
|
|
198
|
+
attached: attachedTabIds.has(tab.id),
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages chrome.debugger attachment lifecycle.
|
|
3
|
+
* Handles root tab attachment, recursive child-target auto-attach, frame discovery,
|
|
4
|
+
* and session-aware CDP command routing.
|
|
5
|
+
*/
|
|
6
|
+
export class DebuggerManager {
|
|
7
|
+
attached = new Map();
|
|
8
|
+
globalEventHandler = null;
|
|
9
|
+
detachHandler = null;
|
|
10
|
+
constructor() {
|
|
11
|
+
chrome.debugger.onEvent.addListener((debuggee, method, params) => {
|
|
12
|
+
this.handleCdpEvent(debuggee, method, params);
|
|
13
|
+
});
|
|
14
|
+
chrome.debugger.onDetach.addListener((debuggee, reason) => {
|
|
15
|
+
if (debuggee.tabId) {
|
|
16
|
+
this.handleDetach(debuggee.tabId, debuggee.sessionId ?? null, reason);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
onEvent(handler) {
|
|
21
|
+
this.globalEventHandler = handler;
|
|
22
|
+
}
|
|
23
|
+
onDetach(handler) {
|
|
24
|
+
this.detachHandler = handler;
|
|
25
|
+
}
|
|
26
|
+
async attach(tabId) {
|
|
27
|
+
if (this.attached.has(tabId)) {
|
|
28
|
+
return this.attached.get(tabId);
|
|
29
|
+
}
|
|
30
|
+
const debuggee = { tabId };
|
|
31
|
+
await chrome.debugger.attach(debuggee, '1.3');
|
|
32
|
+
const tab = await chrome.tabs.get(tabId);
|
|
33
|
+
const target = {
|
|
34
|
+
tabId,
|
|
35
|
+
debuggeeId: debuggee,
|
|
36
|
+
url: tab.url ?? '',
|
|
37
|
+
title: tab.title ?? '',
|
|
38
|
+
faviconUrl: tab.favIconUrl,
|
|
39
|
+
attachedAt: Date.now(),
|
|
40
|
+
enabledDomains: new Set(),
|
|
41
|
+
childSessions: new Map(),
|
|
42
|
+
frames: new Map(),
|
|
43
|
+
topFrameId: null,
|
|
44
|
+
};
|
|
45
|
+
this.attached.set(tabId, target);
|
|
46
|
+
await this.configureAutoAttach(tabId);
|
|
47
|
+
await this.refreshFrameTree(tabId, null);
|
|
48
|
+
return target;
|
|
49
|
+
}
|
|
50
|
+
async detach(tabId) {
|
|
51
|
+
const target = this.attached.get(tabId);
|
|
52
|
+
if (!target)
|
|
53
|
+
return;
|
|
54
|
+
try {
|
|
55
|
+
await chrome.debugger.detach(target.debuggeeId);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Tab may already be closed
|
|
59
|
+
}
|
|
60
|
+
this.attached.delete(tabId);
|
|
61
|
+
}
|
|
62
|
+
async sendCommand(tabId, method, params, sessionId) {
|
|
63
|
+
const target = this.getRequiredTarget(tabId);
|
|
64
|
+
const debuggee = this.toDebuggee(target, sessionId);
|
|
65
|
+
const result = await chrome.debugger.sendCommand(debuggee, method, params);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
async enableDomain(tabId, domain, sessionId) {
|
|
69
|
+
const enabledDomains = this.getRequiredEnabledDomains(tabId, sessionId);
|
|
70
|
+
if (enabledDomains.has(domain)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
await this.sendCommand(tabId, `${domain}.enable`, undefined, sessionId);
|
|
74
|
+
enabledDomains.add(domain);
|
|
75
|
+
}
|
|
76
|
+
isAttached(tabId) {
|
|
77
|
+
return this.attached.has(tabId);
|
|
78
|
+
}
|
|
79
|
+
listAttached() {
|
|
80
|
+
return [...this.attached.values()];
|
|
81
|
+
}
|
|
82
|
+
getTarget(tabId) {
|
|
83
|
+
return this.attached.get(tabId);
|
|
84
|
+
}
|
|
85
|
+
getFrames(tabId) {
|
|
86
|
+
const target = this.attached.get(tabId);
|
|
87
|
+
if (!target) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
return [...target.frames.values()];
|
|
91
|
+
}
|
|
92
|
+
async configureAutoAttach(tabId, sessionId) {
|
|
93
|
+
await this.sendCommand(tabId, 'Target.setAutoAttach', {
|
|
94
|
+
autoAttach: true,
|
|
95
|
+
waitForDebuggerOnStart: false,
|
|
96
|
+
flatten: true,
|
|
97
|
+
filter: [{ type: 'iframe', exclude: false }],
|
|
98
|
+
}, sessionId);
|
|
99
|
+
}
|
|
100
|
+
getEnabledDomains(tabId, sessionId) {
|
|
101
|
+
const target = this.attached.get(tabId);
|
|
102
|
+
if (!target) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
if (!sessionId) {
|
|
106
|
+
return target.enabledDomains;
|
|
107
|
+
}
|
|
108
|
+
return target.childSessions.get(sessionId)?.enabledDomains ?? null;
|
|
109
|
+
}
|
|
110
|
+
getRequiredTarget(tabId) {
|
|
111
|
+
const target = this.attached.get(tabId);
|
|
112
|
+
if (!target) {
|
|
113
|
+
throw new Error(`Tab ${tabId} is not attached`);
|
|
114
|
+
}
|
|
115
|
+
return target;
|
|
116
|
+
}
|
|
117
|
+
getRequiredEnabledDomains(tabId, sessionId) {
|
|
118
|
+
const enabledDomains = this.getEnabledDomains(tabId, sessionId);
|
|
119
|
+
if (!enabledDomains) {
|
|
120
|
+
throw new Error(`Tab ${tabId} is not attached`);
|
|
121
|
+
}
|
|
122
|
+
return enabledDomains;
|
|
123
|
+
}
|
|
124
|
+
toDebuggee(target, sessionId) {
|
|
125
|
+
if (!sessionId) {
|
|
126
|
+
return target.debuggeeId;
|
|
127
|
+
}
|
|
128
|
+
return { tabId: target.tabId, sessionId };
|
|
129
|
+
}
|
|
130
|
+
handleCdpEvent(debuggee, method, params) {
|
|
131
|
+
const tabId = debuggee.tabId;
|
|
132
|
+
if (!tabId || !this.attached.has(tabId))
|
|
133
|
+
return;
|
|
134
|
+
const sessionId = debuggee.sessionId ?? null;
|
|
135
|
+
if (method === 'Target.attachedToTarget' && params) {
|
|
136
|
+
void this.handleAttachedToTarget(tabId, params);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (method === 'Target.detachedFromTarget' && params) {
|
|
140
|
+
this.handleDetachedFromTarget(tabId, params);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.updateStateFromEvent(tabId, sessionId, method, params);
|
|
144
|
+
if (this.globalEventHandler) {
|
|
145
|
+
this.globalEventHandler(tabId, method, params ?? {}, { sessionId });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async handleAttachedToTarget(tabId, params) {
|
|
149
|
+
const target = this.attached.get(tabId);
|
|
150
|
+
if (!target) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const record = params;
|
|
154
|
+
if (!record.sessionId) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
target.childSessions.set(record.sessionId, {
|
|
158
|
+
sessionId: record.sessionId,
|
|
159
|
+
targetId: record.targetInfo?.targetId ?? record.sessionId,
|
|
160
|
+
type: record.targetInfo?.type ?? 'unknown',
|
|
161
|
+
url: record.targetInfo?.url ?? '',
|
|
162
|
+
title: record.targetInfo?.title ?? '',
|
|
163
|
+
attachedAt: Date.now(),
|
|
164
|
+
enabledDomains: new Set(),
|
|
165
|
+
});
|
|
166
|
+
try {
|
|
167
|
+
await this.configureAutoAttach(tabId, record.sessionId);
|
|
168
|
+
await this.enableChildSessionDomains(tabId, record.sessionId);
|
|
169
|
+
await this.refreshFrameTree(tabId, record.sessionId);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.warn('[DebuggerManager] Failed to bootstrap child target:', error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async enableChildSessionDomains(tabId, sessionId) {
|
|
176
|
+
// Keep child sessions aligned with the root tab so watcher features can observe iframe traffic too.
|
|
177
|
+
for (const domain of ['Runtime', 'Page', 'Network']) {
|
|
178
|
+
await this.enableDomain(tabId, domain, sessionId);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
handleDetachedFromTarget(tabId, params) {
|
|
182
|
+
const record = params;
|
|
183
|
+
if (!record.sessionId) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
this.dropChildSession(tabId, record.sessionId);
|
|
187
|
+
}
|
|
188
|
+
async refreshFrameTree(tabId, sessionId) {
|
|
189
|
+
const frameTree = (await this.sendCommand(tabId, 'Page.getFrameTree', undefined, sessionId));
|
|
190
|
+
this.mergeFrameTree(tabId, sessionId, frameTree.frameTree);
|
|
191
|
+
}
|
|
192
|
+
mergeFrameTree(tabId, sessionId, node) {
|
|
193
|
+
if (!node?.frame?.id) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const target = this.attached.get(tabId);
|
|
197
|
+
if (!target) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const frameId = node.frame.id;
|
|
201
|
+
target.frames.set(frameId, this.toFrameRecord(node.frame, sessionId));
|
|
202
|
+
if (!node.frame.parentId && !sessionId) {
|
|
203
|
+
target.topFrameId = frameId;
|
|
204
|
+
}
|
|
205
|
+
if (this.globalEventHandler) {
|
|
206
|
+
this.globalEventHandler(tabId, 'Page.frameNavigated', {
|
|
207
|
+
frame: {
|
|
208
|
+
id: frameId,
|
|
209
|
+
parentId: node.frame.parentId ?? null,
|
|
210
|
+
url: node.frame.url ?? '',
|
|
211
|
+
name: node.frame.name ?? null,
|
|
212
|
+
},
|
|
213
|
+
}, { sessionId });
|
|
214
|
+
}
|
|
215
|
+
for (const child of node.childFrames ?? []) {
|
|
216
|
+
this.mergeFrameTree(tabId, sessionId, child);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
updateStateFromEvent(tabId, sessionId, method, params) {
|
|
220
|
+
const target = this.attached.get(tabId);
|
|
221
|
+
if (!target) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (method === 'Page.frameNavigated' && params) {
|
|
225
|
+
const frame = params.frame;
|
|
226
|
+
if (!frame?.id) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
target.frames.set(frame.id, this.toFrameRecord(frame, sessionId));
|
|
230
|
+
if (!frame.parentId && !sessionId && frame.url) {
|
|
231
|
+
target.url = frame.url;
|
|
232
|
+
target.topFrameId = frame.id;
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (method === 'Page.frameAttached' && params) {
|
|
237
|
+
const frame = params;
|
|
238
|
+
if (!frame.frameId) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const existing = target.frames.get(frame.frameId);
|
|
242
|
+
target.frames.set(frame.frameId, {
|
|
243
|
+
frameId: frame.frameId,
|
|
244
|
+
parentFrameId: frame.parentFrameId ?? target.topFrameId,
|
|
245
|
+
url: existing?.url ?? '',
|
|
246
|
+
title: existing?.title ?? null,
|
|
247
|
+
sessionId: existing?.sessionId ?? sessionId,
|
|
248
|
+
});
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (method === 'Page.frameDetached' && params) {
|
|
252
|
+
const frame = params;
|
|
253
|
+
if (!frame.frameId) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
target.frames.delete(frame.frameId);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
toFrameRecord(frame, sessionId) {
|
|
260
|
+
return {
|
|
261
|
+
frameId: frame.id,
|
|
262
|
+
parentFrameId: frame.parentId ?? null,
|
|
263
|
+
url: frame.url ?? '',
|
|
264
|
+
title: frame.name ?? null,
|
|
265
|
+
sessionId,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
handleDetach(tabId, sessionId, reason) {
|
|
269
|
+
const target = this.attached.get(tabId);
|
|
270
|
+
if (!target) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (sessionId) {
|
|
274
|
+
this.dropChildSession(tabId, sessionId);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.attached.delete(tabId);
|
|
278
|
+
if (this.detachHandler) {
|
|
279
|
+
this.detachHandler(tabId, reason);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
dropChildSession(tabId, sessionId) {
|
|
283
|
+
const target = this.attached.get(tabId);
|
|
284
|
+
if (!target) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
target.childSessions.delete(sessionId);
|
|
288
|
+
for (const [frameId, frame] of target.frames.entries()) {
|
|
289
|
+
if (frame.sessionId !== sessionId) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
target.frames.delete(frameId);
|
|
293
|
+
this.globalEventHandler?.(tabId, 'Page.frameDetached', { frameId }, { sessionId });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|