Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions extension/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { Connection, debugLog } from './connection.js';
import { RelayConnection, debugLog } from './relayConnection.js';

/**
* Simple Chrome Extension that pumps CDP messages between chrome.debugger and WebSocket
Expand All @@ -29,7 +29,7 @@ type PopupMessage = {
type SendResponse = (response: any) => void;

class TabShareExtension {
private activeConnections: Map<number, Connection>;
private activeConnections: Map<number, RelayConnection>;

constructor() {
this.activeConnections = new Map(); // tabId -> connection
Expand Down Expand Up @@ -75,7 +75,7 @@ class TabShareExtension {
let activeTabId: number | null = null;

if (isConnected) {
const [tabId] = this.activeConnections.entries().next().value as [number, Connection];
const [tabId] = this.activeConnections.entries().next().value as [number, RelayConnection];
activeTabId = tabId;

// Get tab info
Expand Down Expand Up @@ -123,14 +123,14 @@ class TabShareExtension {
// Store connection
this.activeConnections.set(tabId, info);

void this._updateUI(tabId, { text: '●', color: '#4CAF50', title: 'Disconnect from Playwright MCP' });
await this._updateUI(tabId, { text: '●', color: '#4CAF50', title: 'Disconnect from Playwright MCP' });
debugLog(`Tab ${tabId} connected successfully`);
} catch (error: any) {
debugLog(`Failed to connect tab ${tabId}:`, error.message);
await this._cleanupConnection(tabId);

// Show error to user
void this._updateUI(tabId, { text: '!', color: '#F44336', title: `Connection failed: ${error.message}` });
await this._updateUI(tabId, { text: '!', color: '#F44336', title: `Connection failed: ${error.message}` });

throw error;
}
Expand All @@ -143,8 +143,8 @@ class TabShareExtension {
await chrome.action.setTitle({ tabId, title });
}

private _createConnection(tabId: number, socket: WebSocket): Connection {
const connection = new Connection(tabId, socket);
private _createConnection(tabId: number, socket: WebSocket): RelayConnection {
const connection = new RelayConnection(tabId, socket);
socket.onclose = () => {
debugLog(`WebSocket closed for tab ${tabId}`);
void this.disconnectTab(tabId);
Expand Down
89 changes: 44 additions & 45 deletions extension/src/connection.ts → extension/src/relayConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,21 @@ export function debugLog(...args: unknown[]): void {
}
}

export type ProtocolCommand = {
type ProtocolCommand = {
id: number;
sessionId?: string;
method: string;
params?: any;
};

export class Connection {
type ProtocolResponse = {
id?: number;
method?: string;
params?: any;
result?: any;
error?: string;
};

export class RelayConnection {
private _debuggee: chrome.debugger.Debuggee;
private _rootSessionId: string;
private _ws: WebSocket;
Expand Down Expand Up @@ -61,21 +68,23 @@ export class Connection {
private _onDebuggerEvent(source: chrome.debugger.DebuggerSession, method: string, params: any): void {
if (source.tabId !== this._debuggee.tabId)
return;
// If the sessionId is not provided, use the root sessionId.
const event = {
sessionId: source.sessionId || this._rootSessionId,
method,
params,
};
debugLog('Forwarding CDP event:', event);
this._ws.send(JSON.stringify(event));
debugLog('Forwarding CDP event:', method, params);
const sessionId = source.sessionId || this._rootSessionId;
this._sendMessage({
method: 'forwardCDPEvent',
params: {
sessionId,
method,
params,
},
});
}

private _onDebuggerDetach(source: chrome.debugger.Debuggee, reason: string): void {
if (source.tabId !== this._debuggee.tabId)
return;
this._sendMessage({
method: 'PWExtension.detachedFromTab',
method: 'detachedFromTab',
params: {
tabId: this._debuggee.tabId,
reason,
Expand All @@ -99,29 +108,21 @@ export class Connection {

debugLog('Received message:', message);

const sessionId = message.sessionId;
const response: { id: any; sessionId: any; result?: any; error?: { code: number; message: string } } = {
const response: ProtocolResponse = {
id: message.id,
sessionId,
};
try {
if (message.method.startsWith('PWExtension.'))
response.result = await this._handleExtensionCommand(message);
else
response.result = await this._handleCDPCommand(message);
response.result = await this._handleCommand(message);
} catch (error: any) {
debugLog('Error handling message:', error);
response.error = {
code: -32000,
message: error.message,
};
debugLog('Error handling command:', error);
response.error = error.message;
}
debugLog('Sending response:', response);
this._sendMessage(response);
}

private async _handleExtensionCommand(message: ProtocolCommand): Promise<any> {
if (message.method === 'PWExtension.attachToTab') {
private async _handleCommand(message: ProtocolCommand): Promise<any> {
if (message.method === 'attachToTab') {
debugLog('Attaching debugger to tab:', this._debuggee);
await chrome.debugger.attach(this._debuggee, '1.3');
const result: any = await chrome.debugger.sendCommand(this._debuggee, 'Target.getTargetInfo');
Expand All @@ -130,26 +131,24 @@ export class Connection {
targetInfo: result?.targetInfo,
};
}
if (message.method === 'PWExtension.detachFromTab') {
if (message.method === 'detachFromTab') {
debugLog('Detaching debugger from tab:', this._debuggee);
await this.detachDebugger();
return;
return await this.detachDebugger();
}
if (message.method === 'forwardCDPCommand') {
const { sessionId, method, params } = message.params;
debugLog('CDP command:', method, params);
const debuggerSession: chrome.debugger.DebuggerSession = { ...this._debuggee };
// Pass session id, unless it's the root session.
if (sessionId && sessionId !== this._rootSessionId)
debuggerSession.sessionId = sessionId;
// Forward CDP command to chrome.debugger
return await chrome.debugger.sendCommand(
debuggerSession,
method,
params
);
}
}

private async _handleCDPCommand(message: ProtocolCommand): Promise<any> {
const sessionId = message.sessionId;
const debuggerSession: chrome.debugger.DebuggerSession = { ...this._debuggee };
// Pass session id, unless it's the root session.
if (sessionId && sessionId !== this._rootSessionId)
debuggerSession.sessionId = sessionId;
// Forward CDP command to chrome.debugger
const result = await chrome.debugger.sendCommand(
debuggerSession,
message.method,
message.params
);
return result;
}

private _sendError(code: number, message: string): void {
Expand All @@ -161,7 +160,7 @@ export class Connection {
});
}

private _sendMessage(message: object): void {
private _sendMessage(message: any): void {
this._ws.send(JSON.stringify(message));
}
}
2 changes: 1 addition & 1 deletion extension/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"module": "ESNext",
"rootDir": "src",
"outDir": "./lib",
"resolveJsonModule": true
"resolveJsonModule": true,
},
"include": [
"src",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"wtest": "playwright test --project=webkit",
"etest": "playwright test --project=chromium-extension",
"run-server": "node lib/browserServer.js",
"clean": "rm -rf lib",
"clean": "rm -rf lib && rm -rf extension/lib",
"npm-publish": "npm run clean && npm run build && npm run test && npm publish"
},
"exports": {
Expand Down
Loading