LibWeb: Remember last focus trigger in Document

We will need this to implement focus indication.
This commit is contained in:
Jelle Raaijmakers 2025-06-13 14:10:30 +02:00 committed by Alexander Kalenik
commit 7016921067
Notes: github-actions[bot] 2025-06-13 15:40:30 +00:00
5 changed files with 20 additions and 4 deletions

View file

@ -30,6 +30,7 @@
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/CrossOrigin/OpenerPolicy.h>
#include <LibWeb/HTML/DocumentReadyState.h>
#include <LibWeb/HTML/Focus.h>
#include <LibWeb/HTML/HTMLScriptElement.h>
#include <LibWeb/HTML/History.h>
#include <LibWeb/HTML/LazyLoadingElement.h>
@ -433,6 +434,9 @@ public:
void set_focused_element(GC::Ptr<Element>);
HTML::FocusTrigger last_focus_trigger() const { return m_last_focus_trigger; }
void set_last_focus_trigger(HTML::FocusTrigger trigger) { m_last_focus_trigger = trigger; }
Element const* active_element() const { return m_active_element.ptr(); }
void set_active_element(GC::Ptr<Element>);
@ -1013,6 +1017,8 @@ private:
bool m_editable { false };
GC::Ptr<Element> m_focused_element;
HTML::FocusTrigger m_last_focus_trigger { HTML::FocusTrigger::Other };
GC::Ptr<Element> m_active_element;
GC::Ptr<Element> m_target_element;

View file

@ -193,7 +193,7 @@ static Vector<GC::Root<DOM::Node>> focus_chain(DOM::Node* subject)
// https://html.spec.whatwg.org/multipage/interaction.html#focusing-steps
// FIXME: This should accept more types.
void run_focusing_steps(DOM::Node* new_focus_target, DOM::Node* fallback_target, [[maybe_unused]] Optional<ByteString> focus_trigger)
void run_focusing_steps(DOM::Node* new_focus_target, DOM::Node* fallback_target, FocusTrigger focus_trigger)
{
// FIXME: 1. If new focus target is not a focusable area, then set new focus target
// to the result of getting the focusable area for new focus target,
@ -234,6 +234,9 @@ void run_focusing_steps(DOM::Node* new_focus_target, DOM::Node* fallback_target,
// 7. Let new chain be the focus chain of new focus target.
auto new_chain = focus_chain(new_focus_target);
// AD-HOC: Remember last focus trigger for :focus-visible / focus indication.
new_focus_target->document().set_last_focus_trigger(focus_trigger);
// 8. Run the focus update steps with old chain, new chain, and new focus target respectively.
run_focus_update_steps(old_chain, new_chain, new_focus_target);
}

View file

@ -12,7 +12,14 @@
namespace Web::HTML {
void run_focusing_steps(DOM::Node* new_focus_target, DOM::Node* fallback_target = nullptr, Optional<ByteString> focus_trigger = {});
enum class FocusTrigger : u8 {
Click,
Key,
Script,
Other,
};
void run_focusing_steps(DOM::Node* new_focus_target, DOM::Node* fallback_target = nullptr, FocusTrigger focus_trigger = FocusTrigger::Other);
void run_unfocusing_steps(DOM::Node* old_focus_target);
}

View file

@ -31,7 +31,7 @@ void HTMLOrSVGElement<ElementBase>::focus()
return;
// 2. Run the focusing steps for this.
run_focusing_steps(static_cast<ElementBase*>(this));
run_focusing_steps(static_cast<ElementBase*>(this), nullptr, FocusTrigger::Script);
// FIXME: 3. If options["focusVisible"] is true, or does not exist but in an implementation-defined way the user agent determines it would be best to do so, then indicate focus.

View file

@ -679,7 +679,7 @@ EventResult EventHandler::handle_mousedown(CSSPixelPoint viewport_position, CSSP
// When a user activates a click focusable focusable area, the user agent must run the focusing steps on the focusable area with focus trigger set to "click".
// Spec Note: Note that focusing is not an activation behavior, i.e. calling the click() method on an element or dispatching a synthetic click event on it won't cause the element to get focused.
if (focus_candidate)
HTML::run_focusing_steps(focus_candidate, nullptr, "click"sv);
HTML::run_focusing_steps(focus_candidate, nullptr, HTML::FocusTrigger::Click);
else if (auto* focused_element = document->focused_element())
HTML::run_unfocusing_steps(focused_element);