TMWebDriver in GenericAgent: How It Controls Multiple Browser Tabs
TMWebDriver is the core browser-automation bridge in GenericAgent that manages multiple browser tabs by running a WebSocket server for Chrome extensions and an HTTP long-poll server for headless environments, tracking every open tab as a Session object in a centralized dictionary.
TMWebDriver enables GenericAgent to treat each browser tab as an independent, addressable session. According to the source code in TMWebDriver.py, the driver maintains a self.sessions dictionary that maps unique session IDs to Session objects, allowing the agent to execute JavaScript, navigate pages, and synchronize state across any number of concurrent tabs.
What Is TMWebDriver?
TMWebDriver serves as the automation layer between GenericAgent and the browser. It is implemented in TMWebDriver.py and functions as a dual-protocol server:
- WebSocket Server: Handles real-time connections from the Chrome extension (
ext_wstype) usingsimple_websocket_server.WebSocketServer - HTTP Long-Poll Server: Provides a
/api/longpollendpoint via Bottle for pure-HTTP environments where WebSockets are unavailable
When instantiated, the driver checks if a remote server is already listening via is_remote. If not, it automatically launches both servers on consecutive ports by calling start_ws_server() and start_http_server() (lines 44‑46 in TMWebDriver.py).
The Session Model for Multi-Tab Management
Every open browser tab is represented by a Session object that encapsulates the connection state, tab metadata, and communication channel.
Session Class Structure
The Session class definition appears at lines 8‑34 in TMWebDriver.py:
class Session:
def __init__(self, session_id, info, client=None):
self.id = session_id
self.info = info # url, title, type ('ws', 'ext_ws', 'http')
self.type = info.get('type', 'ws')
self.ws_client = client if self.type in ('ws','ext_ws') else None
self.http_queue = client if self.type == 'http' else None
Key attributes include:
session_id: A unique identifier (usually the Chrome tab ID for extension-based sessions)type: The transport protocol—'ws'for WebSocket,'ext_ws'for Chrome extension, or'http'for long-pollingws_clientorhttp_queue: The active communication channel for sending and receiving messages
Session Lifecycle and Cleanup
The driver tracks connection timestamps (connect_at and disconnect_at) and automatically cleans up stale sessions through the clean_sessions method. When a tab disconnects, the session is marked via mark_disconnected() but remains in self.sessions until the cleanup routine removes it.
Initializing the Browser Automation Bridge
Upon initialization, TMWebDriver prepares the communication infrastructure:
from TMWebDriver import TMWebDriver
# Automatically starts WS & HTTP servers if no remote server exists
driver = TMWebDriver() # Lines 38-44 in TMWebDriver.py
The initialization sequence:
- Checks for existing remote server (
is_remote) - Starts the WebSocket server on the configured port
- Starts the HTTP server on the next consecutive port
- Initializes empty dictionaries for
self.sessions,self.acks, andself.results
Registering and Synchronizing Browser Tabs
Tab Registration Workflow
When the Chrome extension connects, it sends a ready or ext_ready message. The JSExecutor.handle method extracts the sessionId and invokes _register_client (lines 31‑33):
driver._register_client(session_id, self, session_info)
Inside _register_client (lines 68‑70), the driver creates a new Session instance and updates the registry:
session = Session(session_id, session_info, client)
self.sessions[session_id] = session
The driver also updates self.default_session_id and self.latest_session_id to ensure subsequent commands can target the most recent or user-selected tab by default.
Keeping Tab Lists Synchronized
The extension periodically transmits a tabs_update payload containing all open tabs. JSExecutor.handle processes this to maintain synchronization (lines 33‑45):
tabs = data.get('tabs', [])
current_tab_ids = {str(tab['id']) for tab in tabs}
# Mark missing tabs as disconnected
for sid in list(driver.sessions.keys()):
sess = driver.sessions[sid]
if sess.type == 'ext_ws' and sid not in current_tab_ids:
sess.mark_disconnected()
# Register or refresh existing tabs
for tab in tabs:
session_id = str(tab['id'])
session_info = {'url': tab.get('url'), 'title': tab.get('title')}
driver._register_client(session_id, self, session_info)
This synchronization ensures that multiple tabs are tracked concurrently, each maintaining its own isolated Session object with current URL and title metadata.
Executing JavaScript Across Multiple Tabs
The execute_js Method
To run code in a specific tab, execute_js constructs a UUID-identified payload and transmits it through the appropriate channel (WebSocket, extension WebSocket, or HTTP queue):
payload = json.dumps({
'id': exec_id,
'code': code,
'tabId': int(session.id) if session.type == 'ext_ws' else None
})
session.ws_client.send_message(payload) # Lines 13-14 in TMWebDriver.py
The driver then waits for an ACK (stored in self.acks) and the final result (stored in self.results). If the target tab disconnects mid-execution, the driver can automatically fall back to the newest active session (lines 94‑104).
High-Level Tab Control Helpers
TMWebDriver provides convenience methods that abstract the underlying session management:
newtab(url): Opens a new Chrome tab by executingGM_openInTabin the current context (lines 81‑82)jump(url): Navigates the current default session to a new URLset_session(url_pattern): Selects a tab whose URL matches the given pattern and sets it asself.default_session_id(lines 68‑77)
All helper methods rely on the self.sessions dictionary to resolve tab targets, enabling parallel actions across unlimited browser tabs.
Practical Implementation Example
The following workflow demonstrates initializing the driver, opening multiple tabs, switching contexts, and executing JavaScript:
from TMWebDriver import TMWebDriver
# 1. Initialize the driver (starts WS & HTTP servers automatically)
driver = TMWebDriver() # Lines 38-44 in TMWebDriver.py
# 2. Open a new tab and load a page
driver.newtab('https://example.com') # Lines 81-82
# 3. Switch default session to a tab containing "example" in URL
driver.set_session('example') # Lines 68-77
# 4. Execute JavaScript in the active tab
result = driver.execute_js('document.title')
print(result['data']) # Output: "Example Domain"
# 5. Retrieve snapshot of all active sessions
all_tabs = driver.get_all_sessions() # Lines 48-52
for tab in all_tabs:
print(f"Tab {tab['id']}: {tab['url']}")
Summary
TMWebDriveracts as a dual-protocol automation bridge using WebSocket and HTTP long-polling to communicate with browser tabs.- Session objects in
self.sessionsprovide isolated state management for each tab, tracking connection type, metadata, and communication channels. - Automatic synchronization via
tabs_updatemessages from the Chrome extension keeps the session registry aligned with actual browser state. - JavaScript execution targets specific tabs through UUID-tracked payloads, with fallback mechanisms for disconnected sessions.
- High-level helpers like
newtab()andset_session()abstract the complexity of multi-tab coordination.
Frequently Asked Questions
How does TMWebDriver handle tab disconnections?
When the Chrome extension detects a closed tab, it stops including that tab ID in the tabs_update payload. The JSExecutor.handle method marks the corresponding session as disconnected via sess.mark_disconnected(). The driver also implements automatic cleanup routines that prune stale sessions from self.sessions based on disconnect timestamps.
Can TMWebDriver operate without the Chrome extension?
Yes. The driver supports an HTTP long-polling mode (http type) for environments where WebSocket connections are unavailable. The HTTP server (implemented in Bottle) exposes a /api/longpoll endpoint that allows headless or restricted browsers to poll for commands and return results, though this mode has higher latency than the WebSocket implementation.
How do I execute commands in a specific tab rather than the default?
Use the set_session(url_pattern) method to select a tab by URL pattern, which updates self.default_session_id. Alternatively, you can access the self.sessions dictionary directly using the Chrome tab ID as the key, then pass that specific session object to low-level execution methods. The execute_js method always targets the session specified in its internal lookup logic.
What is the difference between ext_ws and standard ws session types?
The ext_ws type indicates a connection from the Chrome extension (TMWebDriver CDP Bridge) located in assets/tmwd_cdp_bridge/, which provides full tab control including GM_openInTab functionality. The standard ws type represents direct WebSocket connections from other sources. Both use the same Session class but handle tab-specific commands like tabId injection differently during JavaScript execution.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →