diff --git a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
index bf31b58670..ad2885cca8 100644
--- a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
+++ b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
@@ -245,7 +245,7 @@ void EventLoop::process_input_events() const
[&](KeyEvent const& key_event) {
switch (key_event.type) {
case KeyEvent::Type::KeyDown:
- return page.handle_keydown(key_event.key, key_event.modifiers, key_event.code_point, key_event.repeat);
+ return page.handle_keydown(key_event.key, key_event.modifiers, key_event.code_point, key_event.repeat, key_event.should_insert_text);
case KeyEvent::Type::KeyUp:
return page.handle_keyup(key_event.key, key_event.modifiers, key_event.code_point, key_event.repeat);
}
diff --git a/Libraries/LibWeb/Internals/Internals.cpp b/Libraries/LibWeb/Internals/Internals.cpp
index 96c188bc0b..5216d72222 100644
--- a/Libraries/LibWeb/Internals/Internals.cpp
+++ b/Libraries/LibWeb/Internals/Internals.cpp
@@ -294,9 +294,9 @@ void Internals::send_text(HTML::HTMLElement& target, String const& text, WebIDL:
for (auto code_point : text.code_points()) {
if (auto data = webdriver_key_to_key_code(code_point); data.has_value())
- page.handle_keydown(data->key_code, modifiers | data->additional_modifiers, data->code_point_to_send, false);
+ page.handle_keydown(data->key_code, modifiers | data->additional_modifiers, data->code_point_to_send, false, data->code_point_to_send != 0);
else
- page.handle_keydown(UIEvents::code_point_to_key_code(code_point), modifiers, code_point, false);
+ page.handle_keydown(UIEvents::code_point_to_key_code(code_point), modifiers, code_point, false, true);
}
}
@@ -305,7 +305,7 @@ void Internals::send_key(HTML::HTMLElement& target, String const& key_name, WebI
auto key_code = UIEvents::key_code_from_string(key_name);
target.focus();
- page().handle_keydown(key_code, modifiers, 0, false);
+ page().handle_keydown(key_code, modifiers, 0, false, false);
}
void Internals::paste(HTML::HTMLElement& target, Utf16String const& text)
@@ -318,7 +318,7 @@ void Internals::paste(HTML::HTMLElement& target, Utf16String const& text)
void Internals::commit_text()
{
- page().handle_keydown(UIEvents::Key_Return, 0, 0x0d, false);
+ page().handle_keydown(UIEvents::Key_Return, 0, 0x0d, false, true);
}
UIEvents::MouseButton Internals::button_from_unsigned_short(WebIDL::UnsignedShort button)
diff --git a/Libraries/LibWeb/Page/EventHandler.cpp b/Libraries/LibWeb/Page/EventHandler.cpp
index ab29cbaffa..1a15f294bd 100644
--- a/Libraries/LibWeb/Page/EventHandler.cpp
+++ b/Libraries/LibWeb/Page/EventHandler.cpp
@@ -869,16 +869,24 @@ static constexpr bool is_enter_key_or_interoperable_enter_key_combo(UIEvents::Ke
return false;
}
-static constexpr bool should_ignore_keydown_event(u32 code_point, u32 modifiers)
+static bool should_ignore_keydown_event(u32 code_point, u32 modifiers, bool should_insert_text)
{
- if (modifiers & (UIEvents::KeyModifier::Mod_Ctrl | UIEvents::KeyModifier::Mod_Alt | UIEvents::KeyModifier::Mod_Super))
+ if (code_point == 0 || code_point == 27)
return true;
- // FIXME: There are probably also keys with non-zero code points that should be filtered out.
- return code_point == 0 || code_point == 27;
+ if (modifiers & UIEvents::KeyModifier::Mod_Super)
+ return true;
+
+ if ((modifiers & UIEvents::KeyModifier::Mod_Ctrl) && !(modifiers & UIEvents::KeyModifier::Mod_Alt))
+ return true;
+
+ if ((modifiers & UIEvents::KeyModifier::Mod_Alt) && !should_insert_text)
+ return true;
+
+ return false;
}
-EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code_point, bool repeat)
+EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code_point, bool repeat, bool should_insert_text)
{
if (!m_navigable->active_document())
return EventResult::Dropped;
@@ -1047,7 +1055,7 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u
}
// FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
- if (!should_ignore_keydown_event(code_point, modifiers)) {
+ if (!should_ignore_keydown_event(code_point, modifiers, should_insert_text)) {
FIRE(input_event(UIEvents::EventNames::beforeinput, UIEvents::InputTypes::insertText, m_navigable, code_point));
target->handle_insert(UIEvents::InputTypes::insertText, Utf16String::from_code_point(code_point));
return EventResult::Handled;
diff --git a/Libraries/LibWeb/Page/EventHandler.h b/Libraries/LibWeb/Page/EventHandler.h
index 29ad861369..639ec552e0 100644
--- a/Libraries/LibWeb/Page/EventHandler.h
+++ b/Libraries/LibWeb/Page/EventHandler.h
@@ -51,7 +51,7 @@ public:
void update_hover_after_scroll();
GC::Ptr target_node_for_mouse_position(CSSPixelPoint);
- EventResult handle_keydown(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat);
+ EventResult handle_keydown(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat, bool should_insert_text);
EventResult handle_keyup(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat);
EventResult handle_drag_and_drop_event(DragEvent::Type, CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, Vector files);
diff --git a/Libraries/LibWeb/Page/InputEvent.cpp b/Libraries/LibWeb/Page/InputEvent.cpp
index d315ca68a7..4a584d0cc5 100644
--- a/Libraries/LibWeb/Page/InputEvent.cpp
+++ b/Libraries/LibWeb/Page/InputEvent.cpp
@@ -13,7 +13,7 @@ namespace Web {
KeyEvent KeyEvent::clone_without_browser_data() const
{
- return { type, key, modifiers, code_point, repeat, nullptr };
+ return { type, key, modifiers, code_point, repeat, should_insert_text, nullptr };
}
MouseEvent MouseEvent::clone_without_browser_data() const
@@ -36,6 +36,7 @@ ErrorOr IPC::encode(Encoder& encoder, Web::KeyEvent const& event)
TRY(encoder.encode(event.modifiers));
TRY(encoder.encode(event.code_point));
TRY(encoder.encode(event.repeat));
+ TRY(encoder.encode(event.should_insert_text));
return {};
}
@@ -47,8 +48,9 @@ ErrorOr IPC::decode(Decoder& decoder)
auto modifiers = TRY(decoder.decode());
auto code_point = TRY(decoder.decode());
auto repeat = TRY(decoder.decode());
+ auto should_insert_text = TRY(decoder.decode());
- return Web::KeyEvent { type, key, modifiers, code_point, repeat, nullptr };
+ return Web::KeyEvent { type, key, modifiers, code_point, repeat, should_insert_text, nullptr };
}
template<>
diff --git a/Libraries/LibWeb/Page/InputEvent.h b/Libraries/LibWeb/Page/InputEvent.h
index 8a2f6c7fe8..28323d686e 100644
--- a/Libraries/LibWeb/Page/InputEvent.h
+++ b/Libraries/LibWeb/Page/InputEvent.h
@@ -36,6 +36,7 @@ struct WEB_API KeyEvent {
UIEvents::KeyModifier modifiers { UIEvents::KeyModifier::Mod_None };
u32 code_point { 0 };
bool repeat { false };
+ bool should_insert_text { false };
OwnPtr browser_data;
};
diff --git a/Libraries/LibWeb/Page/Page.cpp b/Libraries/LibWeb/Page/Page.cpp
index 6f538159f6..1f69b14731 100644
--- a/Libraries/LibWeb/Page/Page.cpp
+++ b/Libraries/LibWeb/Page/Page.cpp
@@ -351,9 +351,9 @@ EventResult Page::handle_pinch_event(DevicePixelPoint position, unsigned modifie
return top_level_traversable()->event_handler().handle_pinch_event(device_to_css_point(position), modifiers, scale);
}
-EventResult Page::handle_keydown(UIEvents::KeyCode key, unsigned modifiers, u32 code_point, bool repeat)
+EventResult Page::handle_keydown(UIEvents::KeyCode key, unsigned modifiers, u32 code_point, bool repeat, bool should_insert_text)
{
- return focused_navigable().event_handler().handle_keydown(key, modifiers, code_point, repeat);
+ return focused_navigable().event_handler().handle_keydown(key, modifiers, code_point, repeat, should_insert_text);
}
EventResult Page::handle_keyup(UIEvents::KeyCode key, unsigned modifiers, u32 code_point, bool repeat)
diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h
index c7725dfb62..a8556432d1 100644
--- a/Libraries/LibWeb/Page/Page.h
+++ b/Libraries/LibWeb/Page/Page.h
@@ -125,7 +125,7 @@ public:
EventResult handle_drag_and_drop_event(DragEvent::Type, DevicePixelPoint, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, Vector files);
EventResult handle_pinch_event(DevicePixelPoint point, unsigned modifiers, double scale);
- EventResult handle_keydown(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat);
+ EventResult handle_keydown(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat, bool should_insert_text);
EventResult handle_keyup(UIEvents::KeyCode, unsigned modifiers, u32 code_point, bool repeat);
void handle_sdl_input_events();
diff --git a/Libraries/LibWeb/WebDriver/Actions.cpp b/Libraries/LibWeb/WebDriver/Actions.cpp
index 4de4306cd4..1709c5c861 100644
--- a/Libraries/LibWeb/WebDriver/Actions.cpp
+++ b/Libraries/LibWeb/WebDriver/Actions.cpp
@@ -922,6 +922,7 @@ static bool is_shifted_character(u32 code_point)
struct KeyEvent {
u32 code_point { 0 };
UIEvents::KeyModifier modifiers { UIEvents::KeyModifier::Mod_None };
+ bool should_insert_text { false };
};
static KeyEvent key_code_to_page_event(u32 code_point, UIEvents::KeyModifier modifiers, KeyCodeData const& code)
{
@@ -962,7 +963,11 @@ static KeyEvent key_code_to_page_event(u32 code_point, UIEvents::KeyModifier mod
if (has_flag(modifiers, UIEvents::KeyModifier::Mod_Shift))
code_point = code.alternate_key.value_or(code_point);
- return { code_point, modifiers };
+ auto should_insert_text = code_point != 0
+ && code_point < 0xE000
+ && !(modifiers & (UIEvents::KeyModifier::Mod_Ctrl | UIEvents::KeyModifier::Mod_Alt | UIEvents::KeyModifier::Mod_Super));
+
+ return { code_point, modifiers, should_insert_text };
}
// https://w3c.github.io/webdriver/#dfn-dispatch-a-keydown-action
@@ -1018,7 +1023,7 @@ static ErrorOr dispatch_key_down_action(ActionObject::Ke
// keyboard in accordance with the requirements of [UI-EVENTS], and producing the following events, as appropriate,
// with the specified properties. This will always produce events including at least a keyDown event.
auto event = key_code_to_page_event(raw_key, modifiers, code);
- browsing_context.page().handle_keydown(code.code, event.modifiers, event.code_point, repeat);
+ browsing_context.page().handle_keydown(code.code, event.modifiers, event.code_point, repeat, event.should_insert_text);
// 13. Return success with data null.
return {};
diff --git a/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-alt-text-insertion.txt b/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-alt-text-insertion.txt
new file mode 100644
index 0000000000..847008da55
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-alt-text-insertion.txt
@@ -0,0 +1,16 @@
+keydown key=å keyCodePoints=e5 altKey=true ctrlKey=false
+beforeinput data=å dataCodePoints=e5 inputType=insertText
+value after Alt: å
+keydown key=@ keyCodePoints=40 altKey=true ctrlKey=true
+beforeinput data=@ dataCodePoints=40 inputType=insertText
+value after Ctrl+Alt: å@
+keydown key= keyCodePoints=a0 altKey=true ctrlKey=false
+beforeinput data= dataCodePoints=a0 inputType=insertText
+value after Alt+Space: å@
+value code points after Alt+Space: e5 40 a0
+keydown key=f keyCodePoints=66 altKey=true ctrlKey=false
+value after Alt+F shortcut: å@
+value code points after Alt+F shortcut: e5 40 a0
+keydown key=x keyCodePoints=78 altKey=false ctrlKey=true
+value after Ctrl: å@
+value code points after Ctrl: e5 40 a0
diff --git a/Tests/LibWeb/Text/input/UIEvents/KeyEvent-alt-text-insertion.html b/Tests/LibWeb/Text/input/UIEvents/KeyEvent-alt-text-insertion.html
new file mode 100644
index 0000000000..8416e087a2
--- /dev/null
+++ b/Tests/LibWeb/Text/input/UIEvents/KeyEvent-alt-text-insertion.html
@@ -0,0 +1,36 @@
+
+
+
+
+
diff --git a/UI/AppKit/Interface/Event.h b/UI/AppKit/Interface/Event.h
index 00ad8b2627..e4f791e2fe 100644
--- a/UI/AppKit/Interface/Event.h
+++ b/UI/AppKit/Interface/Event.h
@@ -20,7 +20,7 @@ Web::MouseEvent ns_event_to_mouse_event(Web::MouseEvent::Type, NSEvent*, NSView*
Web::DragEvent ns_event_to_drag_event(Web::DragEvent::Type, id, NSView*);
Vector drag_event_url_list(Web::DragEvent const&);
-Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type, NSEvent*);
+Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type, NSEvent*, bool should_insert_text = false);
NSEvent* key_event_to_ns_event(Web::KeyEvent const&);
NSEvent* create_context_menu_mouse_event(NSView*, Gfx::IntPoint);
diff --git a/UI/AppKit/Interface/Event.mm b/UI/AppKit/Interface/Event.mm
index 78613b6885..c0a4a5c56a 100644
--- a/UI/AppKit/Interface/Event.mm
+++ b/UI/AppKit/Interface/Event.mm
@@ -289,7 +289,7 @@ private:
CFTypeRef m_event { nullptr };
};
-Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type type, NSEvent* event)
+Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type type, NSEvent* event, bool should_insert_text)
{
auto modifiers = ns_modifiers_to_key_modifiers(event.modifierFlags);
auto key_code = ns_key_code_to_key_code(event.keyCode, modifiers);
@@ -311,7 +311,7 @@ Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type type, NSEvent* event)
if (code_point >= 0xE000 && code_point <= 0xF8FF)
code_point = 0;
- return { type, key_code, modifiers, code_point, repeat, make(event) };
+ return { type, key_code, modifiers, code_point, repeat, should_insert_text, make(event) };
}
NSEvent* key_event_to_ns_event(Web::KeyEvent const& event)
diff --git a/UI/AppKit/Interface/LadybirdWebView.mm b/UI/AppKit/Interface/LadybirdWebView.mm
index 31e383eb86..b70a53da7a 100644
--- a/UI/AppKit/Interface/LadybirdWebView.mm
+++ b/UI/AppKit/Interface/LadybirdWebView.mm
@@ -952,7 +952,7 @@ static Web::DevicePixelPoint node_picker_position_for(Ladybird::WebViewBridge co
};
}
-- (void)handleCurrentKeyDownEvent
+- (void)handleCurrentKeyDownEvent:(BOOL)shouldInsertText
{
if (!self.current_key_down_event)
return;
@@ -962,7 +962,7 @@ static Web::DevicePixelPoint node_picker_position_for(Ladybird::WebViewBridge co
return;
}
- auto key_event = Ladybird::ns_event_to_key_event(Web::KeyEvent::Type::KeyDown, self.current_key_down_event);
+ auto key_event = Ladybird::ns_event_to_key_event(Web::KeyEvent::Type::KeyDown, self.current_key_down_event, shouldInsertText);
m_web_view_bridge->enqueue_input_event(move(key_event));
self.current_key_down_event = nil;
@@ -1396,12 +1396,12 @@ static Web::DevicePixelPoint node_picker_position_for(Ladybird::WebViewBridge co
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
- [self handleCurrentKeyDownEvent];
+ [self handleCurrentKeyDownEvent:YES];
}
- (void)doCommandBySelector:(SEL)selector
{
- [self handleCurrentKeyDownEvent];
+ [self handleCurrentKeyDownEvent:NO];
}
- (BOOL)hasMarkedText
diff --git a/UI/Gtk/WebContentView.cpp b/UI/Gtk/WebContentView.cpp
index 365b2c359c..7c608fa753 100644
--- a/UI/Gtk/WebContentView.cpp
+++ b/UI/Gtk/WebContentView.cpp
@@ -106,12 +106,19 @@ void WebContentView::enqueue_native_event(Web::MouseEvent::Type type, double x,
void WebContentView::enqueue_native_event(Web::KeyEvent::Type type, guint keyval, GdkModifierType state)
{
+ auto modifiers = gdk_modifier_to_web(state);
+ auto code_point = gdk_keyval_to_unicode(keyval);
+ auto should_insert_text = type == Web::KeyEvent::Type::KeyDown
+ && code_point != 0
+ && !(modifiers & (Web::UIEvents::KeyModifier::Mod_Ctrl | Web::UIEvents::KeyModifier::Mod_Alt | Web::UIEvents::KeyModifier::Mod_Super));
+
Web::KeyEvent event {
.type = type,
.key = gdk_keyval_to_web(keyval),
- .modifiers = gdk_modifier_to_web(state),
- .code_point = gdk_keyval_to_unicode(keyval),
+ .modifiers = modifiers,
+ .code_point = code_point,
.repeat = false,
+ .should_insert_text = should_insert_text,
.browser_data = {},
};
enqueue_input_event(move(event));
diff --git a/UI/Qt/WebContentView.cpp b/UI/Qt/WebContentView.cpp
index 961103c7cd..bfc4724ffb 100644
--- a/UI/Qt/WebContentView.cpp
+++ b/UI/Qt/WebContentView.cpp
@@ -1116,19 +1116,20 @@ void WebContentView::enqueue_native_event(Web::KeyEvent::Type type, QKeyEvent co
auto text = event.text();
auto code_point = text.isEmpty() ? 0u : event.text()[0].unicode();
+ auto should_insert_text = type == Web::KeyEvent::Type::KeyDown && !text.isEmpty();
auto to_web_event = [&]() -> Web::KeyEvent {
if (event.key() == Qt::Key_Backtab) {
// Qt transforms Shift+Tab into a "Backtab", so we undo that transformation here.
- return { type, Web::UIEvents::KeyCode::Key_Tab, Web::UIEvents::Mod_Shift, '\t', event.isAutoRepeat(), make(event) };
+ return { type, Web::UIEvents::KeyCode::Key_Tab, Web::UIEvents::Mod_Shift, '\t', event.isAutoRepeat(), false, make(event) };
}
if (event.key() == Qt::Key_Enter || event.key() == Qt::Key_Return) {
// This ensures consistent behavior between systems that treat Enter as '\n' and '\r\n'
- return { type, Web::UIEvents::KeyCode::Key_Return, modifiers, '\n', event.isAutoRepeat(), make(event) };
+ return { type, Web::UIEvents::KeyCode::Key_Return, modifiers, '\n', event.isAutoRepeat(), should_insert_text, make(event) };
}
- return { type, keycode, modifiers, code_point, event.isAutoRepeat(), make(event) };
+ return { type, keycode, modifiers, code_point, event.isAutoRepeat(), should_insert_text, make(event) };
};
enqueue_input_event(to_web_event());