ladybird/Libraries/LibWeb/HTML/HTMLSummaryElement.cpp
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

82 lines
2.6 KiB
C++

/*
* Copyright (c) 2023, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/HTML/HTMLDetailsElement.h>
#include <LibWeb/HTML/HTMLSummaryElement.h>
namespace Web::HTML {
GC_DEFINE_ALLOCATOR(HTMLSummaryElement);
HTMLSummaryElement::HTMLSummaryElement(DOM::Document& document, DOM::QualifiedName qualified_name)
: HTMLElement(document, move(qualified_name))
{
}
bool HTMLSummaryElement::has_activation_behavior() const
{
return true;
}
void HTMLSummaryElement::activation_behavior(DOM::Event const&)
{
// The activation behavior of summary elements is to run the following steps:
// 1. If this summary element is not the summary for its parent details, then return.
if (!is_summary_for_its_parent_details())
return;
// 2. Let parent be this summary element's parent.
auto parent = this->parent_element();
// 3. If the open attribute is present on parent, then remove it. Otherwise, set parent's open attribute to the empty string.
if (parent->has_attribute(HTML::AttributeNames::open))
parent->remove_attribute(HTML::AttributeNames::open);
else
parent->set_attribute_value(HTML::AttributeNames::open, String {});
}
// https://html.spec.whatwg.org/multipage/interactive-elements.html#summary-for-its-parent-details
bool HTMLSummaryElement::is_summary_for_its_parent_details() const
{
// A summary element is a summary for its parent details if the following algorithm returns true:
// 1. If this summary element has no parent, then return false.
if (!parent_element())
return false;
// 2. Let parent be this summary element's parent.
auto parent = this->parent_element();
// 3. If parent is not a details element, then return false.
if (!is<HTMLDetailsElement>(*parent))
return false;
// 4. If parent's first summary element child is not this summary element, then return false.
if (parent->first_child_of_type<HTMLSummaryElement>() != this)
return false;
// 5. Return true.
return true;
}
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-summary-element
bool HTMLSummaryElement::is_focusable() const
{
// summary elements that are the first summary element child of a details element
return (Base::is_focusable() || is_summary_for_its_parent_details())
&& meets_focusable_area_rendering_requirements();
}
HTMLSummaryElement::~HTMLSummaryElement() = default;
void HTMLSummaryElement::initialize(JS::Realm& realm)
{
Base::initialize(realm);
}
}