diff --git a/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-tab.txt b/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-tab.txt
new file mode 100644
index 00000000000..46757bd93ec
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/UIEvents/KeyEvent-tab.txt
@@ -0,0 +1,4 @@
+input1 keydown Tab
+
+input2 keydown Tab
+
diff --git a/Tests/LibWeb/Text/input/UIEvents/KeyEvent-tab.html b/Tests/LibWeb/Text/input/UIEvents/KeyEvent-tab.html
new file mode 100644
index 00000000000..ee7b0478fc7
--- /dev/null
+++ b/Tests/LibWeb/Text/input/UIEvents/KeyEvent-tab.html
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp
index 4e3fc372822..67a7442e01a 100644
--- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp
+++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp
@@ -763,20 +763,31 @@ bool EventHandler::focus_next_element()
if (!m_navigable->active_document()->is_fully_active())
return false;
- auto* element = m_navigable->active_document()->focused_element();
- if (!element) {
- element = m_navigable->active_document()->first_child_of_type();
- if (element && element->is_focusable()) {
- m_navigable->active_document()->set_focused_element(element);
- return true;
+ auto set_focus_to_first_focusable_element = [&]() {
+ auto* element = m_navigable->active_document()->first_child_of_type();
+
+ for (; element; element = element->next_element_in_pre_order()) {
+ if (element->is_focusable()) {
+ m_navigable->active_document()->set_focused_element(element);
+ return true;
+ }
}
- }
+
+ return false;
+ };
+
+ auto* element = m_navigable->active_document()->focused_element();
+ if (!element)
+ return set_focus_to_first_focusable_element();
for (element = element->next_element_in_pre_order(); element && !element->is_focusable(); element = element->next_element_in_pre_order())
;
+ if (!element)
+ return set_focus_to_first_focusable_element();
+
m_navigable->active_document()->set_focused_element(element);
- return element;
+ return true;
}
bool EventHandler::focus_previous_element()
@@ -786,20 +797,32 @@ bool EventHandler::focus_previous_element()
if (!m_navigable->active_document()->is_fully_active())
return false;
- auto* element = m_navigable->active_document()->focused_element();
- if (!element) {
- element = m_navigable->active_document()->last_child_of_type();
- if (element && element->is_focusable()) {
- m_navigable->active_document()->set_focused_element(element);
- return true;
+ auto set_focus_to_last_focusable_element = [&]() {
+ // FIXME: This often returns the HTML element itself, which has no previous sibling.
+ auto* element = m_navigable->active_document()->last_child_of_type();
+
+ for (; element; element = element->previous_element_in_pre_order()) {
+ if (element->is_focusable()) {
+ m_navigable->active_document()->set_focused_element(element);
+ return true;
+ }
}
- }
+
+ return false;
+ };
+
+ auto* element = m_navigable->active_document()->focused_element();
+ if (!element)
+ return set_focus_to_last_focusable_element();
for (element = element->previous_element_in_pre_order(); element && !element->is_focusable(); element = element->previous_element_in_pre_order())
;
+ if (!element)
+ return set_focus_to_last_focusable_element();
+
m_navigable->active_document()->set_focused_element(element);
- return element;
+ return true;
}
constexpr bool should_ignore_keydown_event(u32 code_point, u32 modifiers)