ladybird/Libraries/LibWeb/HTML/HTMLDialogElement.h
Andreas Kling 99b1780d57 LibWeb: Reject non-rendered elements as focus targets
Do not let elements inside display:none subtrees become focus targets.
The HTML focusable-area model only allows elements to be focusable when
they are rendered, delegate rendering to their children, or are relevant
canvas fallback content.

Preserve blur for the current focused area after script hides it, since
the unfocusing steps operate on the old focus target. Check display:none
through the flat-tree style parent chain, so slotted controls inside
hidden slot subtrees cannot become focused.

Cover hidden ancestors with materialized computed style, display:none
controls, display:contents, hidden focused controls, and slotted cases
inside hidden and visible shadow-tree subtrees.
2026-05-21 08:56:05 +02:00

91 lines
3.1 KiB
C++

/*
* Copyright (c) 2020, the SerenityOS developers.
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Forward.h>
#include <LibWeb/ARIA/Roles.h>
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/HTML/ToggleTaskTracker.h>
namespace Web::HTML {
class HTMLDialogElement final : public HTMLElement {
WEB_PLATFORM_OBJECT(HTMLDialogElement, HTMLElement);
GC_DECLARE_ALLOCATOR(HTMLDialogElement);
public:
virtual ~HTMLDialogElement() override;
virtual void removed_from(IsSubtreeRoot, Node* old_ancestor, Node& old_root) override;
// ^EventTarget
virtual bool is_focusable() const override
{
return meets_focusable_area_rendering_requirements();
}
String return_value() const;
void set_return_value(String);
static WebIDL::ExceptionOr<void> show_a_modal_dialog(HTMLDialogElement&, GC::Ptr<DOM::Element> source);
void close_the_dialog(Optional<String> result, GC::Ptr<DOM::Element> source);
void request_close_the_dialog(Optional<String> return_value, GC::Ptr<DOM::Element> source);
WebIDL::ExceptionOr<void> show();
WebIDL::ExceptionOr<void> show_modal();
void close(Optional<String> return_value);
void request_close(Optional<String> return_value);
static void light_dismiss_open_dialogs(UIEvents::PointerEvent const&, GC::Ptr<DOM::Node>);
// https://www.w3.org/TR/html-aria/#el-dialog
virtual Optional<ARIA::Role> default_role() const override { return ARIA::Role::dialog; }
bool is_modal() const { return m_is_modal; }
void set_is_modal(bool);
bool is_valid_command(String&) override;
void command_steps(DOM::Element&, String&) override;
private:
HTMLDialogElement(DOM::Document&, DOM::QualifiedName);
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
virtual void inserted() override;
virtual void attribute_changed(FlyString const& local_name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
void queue_a_dialog_toggle_event_task(String old_state, String new_state, GC::Ptr<DOM::Element> source);
void run_dialog_setup_steps();
void run_dialog_cleanup_steps();
void run_dialog_focusing_steps();
void set_close_watcher();
static GC::Ptr<HTMLDialogElement> nearest_clicked_dialog(UIEvents::PointerEvent const&, GC::Ptr<DOM::Node>);
String m_return_value;
bool m_is_modal { false };
Optional<String> m_request_close_return_value;
GC::Ptr<DOM::Element> m_request_close_source_element;
GC::Ptr<CloseWatcher> m_close_watcher;
// https://html.spec.whatwg.org/multipage/interactive-elements.html#enable-close-watcher-for-requestclose()
bool m_enable_close_watcher_for_request_close { false };
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-toggle-task-tracker
Optional<ToggleTaskTracker> m_dialog_toggle_task_tracker;
// https://html.spec.whatwg.org/multipage/interactive-elements.html#previously-focused-element
GC::Ptr<Node> m_previously_focused_element;
};
}