mirror of
https://github.com/browser-use/browser-use.git
synced 2026-10-02 04:04:36 +08:00
Merge branch 'main' into fix/session-handler-registration
This commit is contained in:
@@ -2504,31 +2504,34 @@ class DefaultActionWatchdog(BaseWatchdog):
|
||||
'end': 'End',
|
||||
}
|
||||
|
||||
# Parse and normalize the key string
|
||||
keys = event.keys
|
||||
if '+' in keys:
|
||||
# Handle key combinations like "ctrl+a"
|
||||
parts = keys.split('+')
|
||||
normalized_parts = []
|
||||
for part in parts:
|
||||
part_lower = part.strip().lower()
|
||||
normalized = key_aliases.get(part_lower, part)
|
||||
normalized_parts.append(normalized)
|
||||
normalized_keys = '+'.join(normalized_parts)
|
||||
else:
|
||||
# Single key
|
||||
keys_lower = keys.strip().lower()
|
||||
normalized_keys = key_aliases.get(keys_lower, keys)
|
||||
|
||||
# Handle key combinations like "Control+A"
|
||||
if '+' in normalized_keys:
|
||||
parts = normalized_keys.split('+')
|
||||
modifiers = parts[:-1]
|
||||
main_key = parts[-1]
|
||||
modifier_map = {'Alt': 1, 'Control': 2, 'Meta': 4, 'Shift': 8}
|
||||
is_combination = False
|
||||
modifiers = []
|
||||
main_key = None
|
||||
if '+' in keys and keys != '+':
|
||||
if keys.endswith('++'):
|
||||
prefix = keys[:-2]
|
||||
raw_modifiers = prefix.split('+')
|
||||
if all(part.strip() for part in raw_modifiers):
|
||||
normalized_modifiers = [key_aliases.get(part.strip().lower(), part) for part in raw_modifiers]
|
||||
if all(modifier in modifier_map for modifier in normalized_modifiers):
|
||||
is_combination = True
|
||||
modifiers = normalized_modifiers
|
||||
main_key = '+'
|
||||
else:
|
||||
prefix, suffix = keys.rsplit('+', 1)
|
||||
raw_modifiers = prefix.split('+')
|
||||
if suffix.strip() and all(part.strip() for part in raw_modifiers):
|
||||
normalized_modifiers = [key_aliases.get(part.strip().lower(), part) for part in raw_modifiers]
|
||||
if all(modifier in modifier_map for modifier in normalized_modifiers):
|
||||
is_combination = True
|
||||
modifiers = normalized_modifiers
|
||||
main_key = key_aliases.get(suffix.strip().lower(), suffix)
|
||||
|
||||
if is_combination and main_key is not None:
|
||||
# Calculate modifier bitmask
|
||||
modifier_value = 0
|
||||
modifier_map = {'Alt': 1, 'Control': 2, 'Meta': 4, 'Shift': 8}
|
||||
for mod in modifiers:
|
||||
modifier_value |= modifier_map.get(mod, 0)
|
||||
|
||||
@@ -2545,6 +2548,9 @@ class DefaultActionWatchdog(BaseWatchdog):
|
||||
for mod in reversed(modifiers):
|
||||
await self._dispatch_key_event(cdp_session, 'keyUp', mod)
|
||||
else:
|
||||
keys_lower = keys.strip().lower()
|
||||
normalized_keys = key_aliases.get(keys_lower, keys)
|
||||
|
||||
# Check if this is a text string or special key
|
||||
special_keys = {
|
||||
'Enter',
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import asyncio
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
|
||||
from browser_use.browser.events import SendKeysEvent
|
||||
from browser_use.browser.watchdogs.default_action_watchdog import DefaultActionWatchdog
|
||||
|
||||
|
||||
def make_watchdog(recorded_params, dispatched_keys) -> DefaultActionWatchdog:
|
||||
class Input:
|
||||
async def dispatchKeyEvent(self, params=None, session_id=None):
|
||||
recorded_params.append(params or {})
|
||||
|
||||
cdp_session = SimpleNamespace(
|
||||
cdp_client=SimpleNamespace(send=SimpleNamespace(Input=Input())),
|
||||
session_id='session-1',
|
||||
)
|
||||
|
||||
class BrowserSession:
|
||||
async def get_or_create_cdp_session(self, focus=False):
|
||||
return cdp_session
|
||||
|
||||
async def dispatch_key_event(_session, event_type, key, modifiers=0):
|
||||
dispatched_keys.append((event_type, key, modifiers))
|
||||
|
||||
watchdog = SimpleNamespace(
|
||||
browser_session=BrowserSession(),
|
||||
logger=SimpleNamespace(info=lambda *args, **kwargs: None),
|
||||
_dispatch_key_event=dispatch_key_event,
|
||||
)
|
||||
watchdog._get_char_modifiers_and_vk = DefaultActionWatchdog._get_char_modifiers_and_vk.__get__(watchdog)
|
||||
watchdog._get_key_code_for_char = DefaultActionWatchdog._get_key_code_for_char.__get__(watchdog)
|
||||
return cast(DefaultActionWatchdog, watchdog)
|
||||
|
||||
|
||||
def test_send_keys_literal_plus_dispatches_char_event():
|
||||
recorded_params = []
|
||||
dispatched_keys = []
|
||||
watchdog = make_watchdog(recorded_params, dispatched_keys)
|
||||
|
||||
asyncio.run(DefaultActionWatchdog.on_SendKeysEvent(watchdog, SendKeysEvent(keys='+')))
|
||||
|
||||
char_events = [params for params in recorded_params if params.get('type') == 'char']
|
||||
assert [(params.get('text'), params.get('key')) for params in char_events] == [('+', '+')]
|
||||
assert all(params.get('key') for params in recorded_params)
|
||||
|
||||
|
||||
def test_send_keys_text_with_plus_dispatches_all_characters():
|
||||
recorded_params = []
|
||||
dispatched_keys = []
|
||||
watchdog = make_watchdog(recorded_params, dispatched_keys)
|
||||
|
||||
asyncio.run(DefaultActionWatchdog.on_SendKeysEvent(watchdog, SendKeysEvent(keys='C++')))
|
||||
|
||||
assert [params['text'] for params in recorded_params if params.get('type') == 'char'] == ['C', '+', '+']
|
||||
|
||||
|
||||
def test_send_keys_control_plus_keeps_plus_as_main_key():
|
||||
recorded_params = []
|
||||
dispatched_keys = []
|
||||
watchdog = make_watchdog(recorded_params, dispatched_keys)
|
||||
|
||||
asyncio.run(DefaultActionWatchdog.on_SendKeysEvent(watchdog, SendKeysEvent(keys='Control++')))
|
||||
|
||||
assert dispatched_keys == [
|
||||
('keyDown', 'Control', 0),
|
||||
('keyDown', '+', 2),
|
||||
('keyUp', '+', 2),
|
||||
('keyUp', 'Control', 0),
|
||||
]
|
||||
|
||||
|
||||
def test_send_keys_existing_shortcut_and_special_key_still_work():
|
||||
recorded_params = []
|
||||
dispatched_keys = []
|
||||
watchdog = make_watchdog(recorded_params, dispatched_keys)
|
||||
|
||||
asyncio.run(DefaultActionWatchdog.on_SendKeysEvent(watchdog, SendKeysEvent(keys='Control+a')))
|
||||
assert dispatched_keys == [
|
||||
('keyDown', 'Control', 0),
|
||||
('keyDown', 'a', 2),
|
||||
('keyUp', 'a', 2),
|
||||
('keyUp', 'Control', 0),
|
||||
]
|
||||
|
||||
recorded_params.clear()
|
||||
dispatched_keys.clear()
|
||||
asyncio.run(DefaultActionWatchdog.on_SendKeysEvent(watchdog, SendKeysEvent(keys='Enter')))
|
||||
assert dispatched_keys == [('keyDown', 'Enter', 0), ('keyUp', 'Enter', 0)]
|
||||
Reference in New Issue
Block a user