do not try to detach tab twice
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Protocol } from 'playwriter/src/cdp-types';
|
import type { CDPEvent, Protocol } from 'playwriter/src/cdp-types';
|
||||||
import type { ExtensionCommandMessage, ExtensionResponseMessage } from 'playwriter/src/extension/protocol';
|
import type { ExtensionCommandMessage, ExtensionResponseMessage } from 'playwriter/src/extension/protocol';
|
||||||
|
|
||||||
export function debugLog(...args: unknown[]): void {
|
export function debugLog(...args: unknown[]): void {
|
||||||
@@ -151,13 +151,17 @@ export class RelayConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
detachTab(tabId: number): void {
|
detachTab(tabId: number): void {
|
||||||
|
this._cleanupTab(tabId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cleanupTab(tabId: number, shouldDetachDebugger: boolean): void {
|
||||||
const tab = this._attachedTabs.get(tabId);
|
const tab = this._attachedTabs.get(tabId);
|
||||||
if (!tab) {
|
if (!tab) {
|
||||||
debugLog('detachTab: tab not found in map:', tabId);
|
debugLog('cleanupTab: tab not found in map:', tabId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog('Detaching tab:', tabId, 'sessionId:', tab.sessionId);
|
debugLog('Cleaning up tab:', tabId, 'sessionId:', tab.sessionId, 'shouldDetach:', shouldDetachDebugger);
|
||||||
|
|
||||||
this._sendMessage({
|
this._sendMessage({
|
||||||
method: 'forwardCDPEvent',
|
method: 'forwardCDPEvent',
|
||||||
@@ -173,13 +177,15 @@ export class RelayConnection {
|
|||||||
this._attachedTabs.delete(tabId);
|
this._attachedTabs.delete(tabId);
|
||||||
debugLog('Removed tab from _attachedTabs map. Remaining tabs:', this._attachedTabs.size);
|
debugLog('Removed tab from _attachedTabs map. Remaining tabs:', this._attachedTabs.size);
|
||||||
|
|
||||||
chrome.debugger.detach(tab.debuggee)
|
if (shouldDetachDebugger) {
|
||||||
.then(() => {
|
chrome.debugger.detach(tab.debuggee)
|
||||||
debugLog('Successfully detached debugger from tab:', tabId);
|
.then(() => {
|
||||||
})
|
debugLog('Successfully detached debugger from tab:', tabId);
|
||||||
.catch((err) => {
|
})
|
||||||
debugLog('Error detaching debugger from tab:', tabId, err.message);
|
.catch((err) => {
|
||||||
});
|
debugLog('Error detaching debugger from tab:', tabId, err.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(message: string): void {
|
close(message: string): void {
|
||||||
@@ -222,7 +228,7 @@ export class RelayConnection {
|
|||||||
this._onCloseCallback?.();
|
this._onCloseCallback?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onDebuggerEvent = (source: chrome.debugger.DebuggerSession, method: CDPEvent['method'], params: any): void => {
|
private _onDebuggerEvent = (source: chrome.debugger.DebuggerSession, method: string, params: any): void => {
|
||||||
const tab = this._attachedTabs.get(source.tabId!);
|
const tab = this._attachedTabs.get(source.tabId!);
|
||||||
if (!tab) return;
|
if (!tab) return;
|
||||||
|
|
||||||
@@ -267,19 +273,19 @@ export class RelayConnection {
|
|||||||
debugLog('User closed debugger via Chrome automation bar, calling onTabDetached callback');
|
debugLog('User closed debugger via Chrome automation bar, calling onTabDetached callback');
|
||||||
this._onTabDetachedCallback?.(tabId, reason);
|
this._onTabDetachedCallback?.(tabId, reason);
|
||||||
|
|
||||||
this.detachTab(tabId);
|
this._cleanupTab(tabId, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
private async _handleCommand(message: ExtensionCommandMessage): Promise<any> {
|
private async _handleCommand(msg: ExtensionCommandMessage): Promise<any> {
|
||||||
if (message.method === 'attachToTab') {
|
if (msg.method === 'attachToTab') {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.method === 'forwardCDPCommand') {
|
if (msg.method === 'forwardCDPCommand') {
|
||||||
const { sessionId, method, params } = message.params;
|
|
||||||
|
|
||||||
if (method === 'Target.createTarget') {
|
|
||||||
const url = params?.url || 'about:blank';
|
if (msg.params.method === 'Target.createTarget') {
|
||||||
|
const url = msg.params.params?.url || 'about:blank';
|
||||||
debugLog('Creating new tab with URL:', url);
|
debugLog('Creating new tab with URL:', url);
|
||||||
|
|
||||||
const tab = await chrome.tabs.create({ url, active: false });
|
const tab = await chrome.tabs.create({ url, active: false });
|
||||||
@@ -298,58 +304,58 @@ export class RelayConnection {
|
|||||||
return { targetId: targetInfo.targetId };
|
return { targetId: targetInfo.targetId };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method === 'Target.closeTarget' && params?.targetId) {
|
if (msg.params.method === 'Target.closeTarget' && msg.params.params?.targetId) {
|
||||||
debugLog('Closing target:', params.targetId);
|
debugLog('Closing target:', msg.params.params.targetId);
|
||||||
|
|
||||||
for (const [tabId, tab] of this._attachedTabs) {
|
for (const [tabId, tab] of this._attachedTabs) {
|
||||||
if (tab.targetId === params.targetId) {
|
if (tab.targetId === msg.params.params.targetId) {
|
||||||
debugLog('Found tab to close:', tabId);
|
debugLog('Found tab to close:', tabId);
|
||||||
await chrome.tabs.remove(tabId);
|
await chrome.tabs.remove(tabId);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog('Target not found:', params.targetId);
|
debugLog('Target not found:', msg.params.params.targetId);
|
||||||
throw new Error(`Target not found: ${params.targetId}`);
|
throw new Error(`Target not found: ${msg.params.params.targetId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetTab: AttachedTab | undefined;
|
let targetTab: AttachedTab | undefined;
|
||||||
|
|
||||||
for (const [tabId, tab] of this._attachedTabs) {
|
for (const [tabId, tab] of this._attachedTabs) {
|
||||||
if (tab.sessionId === sessionId) {
|
if (tab.sessionId === msg.params.sessionId) {
|
||||||
targetTab = tab;
|
targetTab = tab;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetTab) {
|
if (!targetTab) {
|
||||||
if (method === 'Browser.getVersion' || method === 'Target.getTargets') {
|
if (msg.params.method === 'Browser.getVersion' || msg.params.method === 'Target.getTargets') {
|
||||||
targetTab = this._attachedTabs.values().next().value;
|
targetTab = this._attachedTabs.values().next().value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetTab) {
|
if (!targetTab) {
|
||||||
throw new Error(`No tab found for method ${method} sessionId: ${sessionId}`);
|
throw new Error(`No tab found for method ${msg.params.method} sessionId: ${msg.params.sessionId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog('CDP command:', method, 'for tab:', targetTab.debuggee.tabId);
|
debugLog('CDP command:', msg.params.method, 'for tab:', targetTab.debuggee.tabId);
|
||||||
|
|
||||||
const debuggerSession: chrome.debugger.DebuggerSession = {
|
const debuggerSession: chrome.debugger.DebuggerSession = {
|
||||||
...targetTab.debuggee,
|
...targetTab.debuggee,
|
||||||
sessionId: sessionId !== targetTab.sessionId ? sessionId : undefined,
|
sessionId: msg.params.sessionId !== targetTab.sessionId ? msg.params.sessionId : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await chrome.debugger.sendCommand(
|
const result = await chrome.debugger.sendCommand(
|
||||||
debuggerSession,
|
debuggerSession,
|
||||||
method,
|
msg.params.method,
|
||||||
params
|
msg.params.params
|
||||||
);
|
);
|
||||||
|
|
||||||
// When Playwright reconnects and calls Runtime.enable, Chrome does NOT automatically
|
// When Playwright reconnects and calls Runtime.enable, Chrome does NOT automatically
|
||||||
// re-send Runtime.executionContextCreated events for contexts that already exist.
|
// re-send Runtime.executionContextCreated events for contexts that already exist.
|
||||||
// This causes page.evaluate() to hang because Playwright has no execution context IDs.
|
// This causes page.evaluate() to hang because Playwright has no execution context IDs.
|
||||||
// Solution: manually replay all cached contexts so Playwright gets fresh, valid IDs.
|
// Solution: manually replay all cached contexts so Playwright gets fresh, valid IDs.
|
||||||
if (method === 'Runtime.enable' && targetTab.executionContexts.size > 0) {
|
if (msg.params.method === 'Runtime.enable' && targetTab.executionContexts.size > 0) {
|
||||||
debugLog('Runtime.enable called, replaying', targetTab.executionContexts.size, 'cached execution contexts for tab:', targetTab.debuggee.tabId);
|
debugLog('Runtime.enable called, replaying', targetTab.executionContexts.size, 'cached execution contexts for tab:', targetTab.debuggee.tabId);
|
||||||
|
|
||||||
for (const contextEvent of targetTab.executionContexts.values()) {
|
for (const contextEvent of targetTab.executionContexts.values()) {
|
||||||
@@ -357,7 +363,7 @@ export class RelayConnection {
|
|||||||
this._sendMessage({
|
this._sendMessage({
|
||||||
method: 'forwardCDPEvent',
|
method: 'forwardCDPEvent',
|
||||||
params: {
|
params: {
|
||||||
sessionId: sessionId,
|
sessionId: msg.params.sessionId,
|
||||||
method: 'Runtime.executionContextCreated',
|
method: 'Runtime.executionContextCreated',
|
||||||
params: contextEvent,
|
params: contextEvent,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user