mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 13:49:16 +00:00
LibWeb: Implement popovertarget buttons
This commit is contained in:
parent
a1cf5271c2
commit
108f3a9aac
Notes:
github-actions[bot]
2025-01-30 22:49:42 +00:00
Author: https://github.com/Gingeh
Commit: 108f3a9aac
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3285
Reviewed-by: https://github.com/ADKaster ✅
Reviewed-by: https://github.com/AtkinsSJ
14 changed files with 188 additions and 14 deletions
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <LibWeb/Bindings/HTMLButtonElementPrototype.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Event.h>
|
||||
#include <LibWeb/HTML/HTMLButtonElement.h>
|
||||
#include <LibWeb/HTML/HTMLFormElement.h>
|
||||
|
||||
|
@ -115,7 +116,9 @@ void HTMLButtonElement::activation_behavior(DOM::Event const& event)
|
|||
}
|
||||
}
|
||||
|
||||
// 4. FIXME: Run the popover target attribute activation behavior given element.
|
||||
// 4. Run the popover target attribute activation behavior given element and event's target.
|
||||
if (event.target() && event.target()->is_dom_node())
|
||||
PopoverInvokerElement::popover_target_activation_behaviour(*this, as<DOM::Node>(*event.target()));
|
||||
}
|
||||
|
||||
bool HTMLButtonElement::is_focusable() const
|
||||
|
|
|
@ -131,6 +131,7 @@ public:
|
|||
WebIDL::ExceptionOr<void> hide_popover_for_bindings();
|
||||
WebIDL::ExceptionOr<bool> toggle_popover(TogglePopoverOptionsOrForceBoolean const&);
|
||||
|
||||
WebIDL::ExceptionOr<bool> check_popover_validity(ExpectedToBeShowing expected_to_be_showing, ThrowExceptions throw_exceptions, GC::Ptr<DOM::Document>);
|
||||
WebIDL::ExceptionOr<void> show_popover(ThrowExceptions throw_exceptions, GC::Ptr<HTMLElement> invoker);
|
||||
WebIDL::ExceptionOr<void> hide_popover(FocusPreviousElement focus_previous_element, FireEvents fire_events, ThrowExceptions throw_exceptions);
|
||||
|
||||
|
@ -160,8 +161,6 @@ private:
|
|||
|
||||
GC::Ptr<DOM::NodeList> m_labels;
|
||||
|
||||
WebIDL::ExceptionOr<bool> check_popover_validity(ExpectedToBeShowing expected_to_be_showing, ThrowExceptions throw_exceptions, GC::Ptr<DOM::Document>);
|
||||
|
||||
void queue_a_popover_toggle_event_task(String old_state, String new_state);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/custom-elements.html#attached-internals
|
||||
|
|
|
@ -1261,8 +1261,10 @@ void HTMLInputElement::did_lose_focus()
|
|||
commit_pending_changes();
|
||||
}
|
||||
|
||||
void HTMLInputElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& value, Optional<FlyString> const&)
|
||||
void HTMLInputElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& value, Optional<FlyString> const& namespace_)
|
||||
{
|
||||
PopoverInvokerElement::associated_attribute_changed(name, value, namespace_);
|
||||
|
||||
if (name == HTML::AttributeNames::checked) {
|
||||
// https://html.spec.whatwg.org/multipage/input.html#the-input-element:concept-input-checked-dirty-2
|
||||
// When the checked content attribute is added, if the control does not have dirty checkedness, the user agent must set the checkedness of the element to true;
|
||||
|
@ -2538,6 +2540,7 @@ bool HTMLInputElement::has_activation_behavior() const
|
|||
return true;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/input.html#the-input-element:activation-behaviour
|
||||
void HTMLInputElement::activation_behavior(DOM::Event const& event)
|
||||
{
|
||||
// The activation behavior for input elements are these steps:
|
||||
|
@ -2546,6 +2549,10 @@ void HTMLInputElement::activation_behavior(DOM::Event const& event)
|
|||
|
||||
// 2. Run this element's input activation behavior, if any, and do nothing otherwise.
|
||||
run_input_activation_behavior(event).release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
// 3. Run the popover target attribute activation behavior given element and event's target.
|
||||
if (event.target() && event.target()->is_dom_node())
|
||||
PopoverInvokerElement::popover_target_activation_behaviour(*this, as<DOM::Node>(*event.target()));
|
||||
}
|
||||
|
||||
bool HTMLInputElement::has_input_activation_behavior() const
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <LibWeb/HTML/FileFilter.h>
|
||||
#include <LibWeb/HTML/FormAssociatedElement.h>
|
||||
#include <LibWeb/HTML/HTMLElement.h>
|
||||
#include <LibWeb/HTML/PopoverInvokerElement.h>
|
||||
#include <LibWeb/Layout/ImageProvider.h>
|
||||
#include <LibWeb/WebIDL/DOMException.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
@ -50,7 +51,8 @@ namespace Web::HTML {
|
|||
class HTMLInputElement final
|
||||
: public HTMLElement
|
||||
, public FormAssociatedTextControlElement
|
||||
, public Layout::ImageProvider {
|
||||
, public Layout::ImageProvider
|
||||
, public PopoverInvokerElement {
|
||||
WEB_PLATFORM_OBJECT(HTMLInputElement, HTMLElement);
|
||||
GC_DECLARE_ALLOCATOR(HTMLInputElement);
|
||||
FORM_ASSOCIATED_ELEMENT(HTMLElement, HTMLInputElement)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import <HTML/HTMLElement.idl>
|
||||
#import <HTML/HTMLFormElement.idl>
|
||||
#import <HTML/PopoverInvokerElement.idl>
|
||||
#import <HTML/ValidityState.idl>
|
||||
#import <FileAPI/FileList.idl>
|
||||
|
||||
|
@ -73,4 +74,4 @@ interface HTMLInputElement : HTMLElement {
|
|||
[CEReactions, Reflect] attribute DOMString align;
|
||||
[CEReactions, Reflect=usemap] attribute DOMString useMap;
|
||||
};
|
||||
// FIXME: HTMLInputElement includes PopoverInvokerElement;
|
||||
HTMLInputElement includes PopoverInvokerElement;
|
||||
|
|
|
@ -4,8 +4,12 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/HTML/AttributeNames.h>
|
||||
#include <LibWeb/HTML/FormAssociatedElement.h>
|
||||
#include <LibWeb/HTML/HTMLElement.h>
|
||||
#include <LibWeb/HTML/PopoverInvokerElement.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
@ -29,4 +33,92 @@ void PopoverInvokerElement::visit_edges(JS::Cell::Visitor& visitor)
|
|||
visitor.visit(m_popover_target_element);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/popover.html#popover-target-attribute-activation-behavior
|
||||
void PopoverInvokerElement::popover_target_activation_behaviour(GC::Ref<DOM::Node> node, GC::Ref<DOM::Node> event_target)
|
||||
{
|
||||
// To run the popover target attribute activation behavior given a Node node and a Node eventTarget:
|
||||
|
||||
// 1. Let popover be node's popover target element.
|
||||
auto popover = PopoverInvokerElement::get_the_popover_target_element(node);
|
||||
|
||||
// 2. If popover is null, then return.
|
||||
if (!popover)
|
||||
return;
|
||||
|
||||
// 3. If eventTarget is a shadow-including inclusive descendant of popover and popover is a shadow-including descendant of node, then return.
|
||||
if (event_target->is_shadow_including_inclusive_descendant_of(*popover)
|
||||
&& popover->is_shadow_including_descendant_of(node))
|
||||
return;
|
||||
|
||||
// 4. If node's popovertargetaction attribute is in the show state and popover's popover visibility state is showing, then return.
|
||||
if (as<DOM::Element>(*node).get_attribute_value(HTML::AttributeNames::popovertargetaction).equals_ignoring_ascii_case("show"sv)
|
||||
&& popover->popover_visibility_state() == HTMLElement::PopoverVisibilityState::Showing)
|
||||
return;
|
||||
|
||||
// 5. If node's popovertargetaction attribute is in the hide state and popover's popover visibility state is hidden, then return.
|
||||
if (as<DOM::Element>(*node).get_attribute_value(HTML::AttributeNames::popovertargetaction).equals_ignoring_ascii_case("hide"sv)
|
||||
&& popover->popover_visibility_state() == HTMLElement::PopoverVisibilityState::Hidden)
|
||||
return;
|
||||
|
||||
// 6. If popover's popover visibility state is showing, then run the hide popover algorithm given popover, true, true, and false.
|
||||
if (popover->popover_visibility_state() == HTMLElement::PopoverVisibilityState::Showing) {
|
||||
MUST(popover->hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No));
|
||||
}
|
||||
|
||||
// 7. Otherwise, if popover's popover visibility state is hidden and the result of running check popover validity given popover, false, false, and null is true, then run show popover given popover, false, and node.
|
||||
else if (popover->popover_visibility_state() == HTMLElement::PopoverVisibilityState::Hidden
|
||||
&& MUST(popover->check_popover_validity(ExpectedToBeShowing::No, ThrowExceptions::No, nullptr))) {
|
||||
MUST(popover->show_popover(ThrowExceptions::No, as<HTMLElement>(*node)));
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/popover.html#popover-target-element
|
||||
GC::Ptr<HTMLElement> PopoverInvokerElement::get_the_popover_target_element(GC::Ref<DOM::Node> node)
|
||||
{
|
||||
// To get the popover target element given a Node node, perform the following steps. They return an HTML element or null.
|
||||
|
||||
auto const* form_associated_element = dynamic_cast<FormAssociatedElement const*>(node.ptr());
|
||||
VERIFY(form_associated_element);
|
||||
|
||||
// 1. If node is not a button, then return null.
|
||||
if (!form_associated_element->is_button())
|
||||
return {};
|
||||
|
||||
// 2. If node is disabled, then return null.
|
||||
if (!form_associated_element->enabled())
|
||||
return {};
|
||||
|
||||
// 3. If node has a form owner and node is a submit button, then return null.
|
||||
if (form_associated_element->form() != nullptr && form_associated_element->is_submit_button())
|
||||
return {};
|
||||
|
||||
// 4. Let popoverElement be the result of running node's get the popovertarget-associated element.
|
||||
auto const* popover_invoker_element = dynamic_cast<PopoverInvokerElement const*>(node.ptr());
|
||||
VERIFY(popover_invoker_element);
|
||||
GC::Ptr<HTMLElement> popover_element = as<HTMLElement>(popover_invoker_element->m_popover_target_element.ptr());
|
||||
if (!popover_element) {
|
||||
auto target_id = as<HTMLElement>(*node).attribute("popovertarget"_fly_string);
|
||||
if (target_id.has_value()) {
|
||||
node->root().for_each_in_inclusive_subtree_of_type<HTMLElement>([&](auto& candidate) {
|
||||
if (candidate.attribute(HTML::AttributeNames::id) == target_id.value()) {
|
||||
popover_element = &candidate;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 5. If popoverElement is null, then return null.
|
||||
if (!popover_element)
|
||||
return {};
|
||||
|
||||
// 6. If popoverElement's popover attribute is in the no popover state, then return null.
|
||||
if (!popover_element->popover().has_value())
|
||||
return {};
|
||||
|
||||
// 7. Return popoverElement.
|
||||
return popover_element;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,12 +21,16 @@ public:
|
|||
|
||||
void set_popover_target_element(GC::Ptr<DOM::Element> value) { m_popover_target_element = value; }
|
||||
|
||||
static void popover_target_activation_behaviour(GC::Ref<DOM::Node> node, GC::Ref<DOM::Node> event_target);
|
||||
|
||||
protected:
|
||||
void visit_edges(JS::Cell::Visitor&);
|
||||
void associated_attribute_changed(FlyString const& name, Optional<String> const& value, Optional<FlyString> const& namespace_);
|
||||
|
||||
private:
|
||||
GC::Ptr<DOM::Element> m_popover_target_element;
|
||||
|
||||
static GC::Ptr<HTMLElement> get_the_popover_target_element(GC::Ref<DOM::Node> node);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue