page.on()-style event extension for CDP events | 能否添加page.on 功能
Author: wuge-1337Created May 15, 2026Updated Sep 8, 2026
类似playwright的page.on。
`
class PageEvent: def init(self, page): self.page = page self.driver = page._driver self.listeners = {} self._original_handlers = {} self._queue_hooked = False self._hook_event_queue()
def _hook_event_queue(self):
if self._queue_hooked: return
self._queue_hooked = True
original_put = self.driver.event_queue.put
def patched_put(msg):
sid = msg.get('sessionId')
method = msg.get('method', '')
if sid:
# print(f"[queue] listeners for {method}: {len(self.listeners.get(method, []))}")
params = msg.get('params', {})
for cb in list(self.listeners.get(method, [])):
try:
cb({"type": method, "sessionId": sid, "data": params})
except Exception as e:
print(f"[PageEvent] 子Session回调错误 ({method}): {e}")
original_put(msg)
self.driver.event_queue.put = patched_put
def run_cdp_session(self, method, session_id, **params):
msg = {
"sessionId": session_id, # <- Flat Session 关键字段
"method": method,
"params": params or {}
}
self.driver._send(msg) # _send 会自动加 id 并发出
def on(self, event_name, callback):
self.listeners.setdefault(event_name, []).append(callback)
# 如果还没 hook,就 hook
if event_name not in self._original_handlers:
self._hook(event_name)
return callback # 方便 off 用
def once(self, event_name, callback):
def wrapper(event):
try:
callback(event)
finally:
self.off(event_name, wrapper)
return self.on(event_name, wrapper)
def off(self, event_name, callback=None):
if event_name not in self.listeners: return
if callback is None:
self.listeners[event_name].clear()
else:
try:
self.listeners[event_name].remove(callback)
except ValueError: pass
# 如果已经没有 listener -> 恢复原 handler
if not self.listeners[event_name]: self._unhook(event_name)
def _hook(self, event_name):
# 保存原 handler
original = self.driver.event_handlers.get(event_name)
self._original_handlers[event_name] = original
def dispatcher(**params):
# 执行监听器(拷贝避免修改时崩)
for cb in list(self.listeners.get(event_name, [])):
try:
cb({"type": event_name, "data": params})
except Exception as e:
print(f"[PageEvent] 回调错误 ({event_name}): {e}")
# 动态获取 original(避免捕获旧引用)
current_original = self._original_handlers.get(event_name)
if current_original:
try:
current_original(**params)
except Exception as e:
print(f"[PageEvent] 原 handler 错误 ({event_name}): {e}")
# 替换 handler
self.driver.event_handlers[event_name] = dispatcher
def _unhook(self, event_name):
original = self._original_handlers.get(event_name)
if original:
self.driver.event_handlers[event_name] = original
else:
# 原本没有 handler -> 删除
self.driver.event_handlers.pop(event_name, None)
# 清理
self._original_handlers.pop(event_name, None)
self.listeners.pop(event_name, None)
def listener_count(self, event_name):
return len(self.listeners.get(event_name, []))
`
Source: g1879/DrissionPage