LibWeb: Implement popover beforetoggle and toggle events

This commit is contained in:
Luke Warlow 2024-12-06 23:10:03 +00:00 committed by Andrew Kaster
commit fcf6cc27f2
Notes: github-actions[bot] 2024-12-12 22:11:32 +00:00
7 changed files with 371 additions and 11 deletions

View file

@ -28,6 +28,7 @@
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/HTML/HTMLLabelElement.h>
#include <LibWeb/HTML/HTMLParagraphElement.h>
#include <LibWeb/HTML/ToggleEvent.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/Infra/CharacterTypes.h>
#include <LibWeb/Infra/Strings.h>
@ -1016,9 +1017,21 @@ WebIDL::ExceptionOr<void> HTMLElement::show_popover(ThrowExceptions throw_except
m_popover_showing_or_hiding = false;
};
// FIXME: 8. If the result of firing an event named beforetoggle, using ToggleEvent, with the cancelable attribute initialized to true, the oldState attribute initialized to "closed", and the newState attribute initialized to "open" at element is false, then run cleanupShowingFlag and return.
// 8. If the result of firing an event named beforetoggle, using ToggleEvent, with the cancelable attribute initialized to true, the oldState attribute initialized to "closed", and the newState attribute initialized to "open" at element is false, then run cleanupShowingFlag and return.
ToggleEventInit event_init {};
event_init.old_state = "closed"_string;
event_init.new_state = "open"_string;
event_init.cancelable = true;
if (!dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::beforetoggle, move(event_init)))) {
cleanup_showing_flag->function()();
return {};
}
// FIXME: 9. If the result of running check popover validity given element, false, throwExceptions, and document is false, then run cleanupShowingFlag and return.
// 9. If the result of running check popover validity given element, false, throwExceptions, and document is false, then run cleanupShowingFlag and return.
if (!TRY(check_popover_validity(ExpectedToBeShowing::No, throw_exceptions, nullptr))) {
cleanup_showing_flag->function()();
return {};
}
// 10. Let shouldRestoreFocus be false.
bool should_restore_focus = false;
@ -1056,7 +1069,9 @@ WebIDL::ExceptionOr<void> HTMLElement::show_popover(ThrowExceptions throw_except
// FIXME: then set element's previously focused element to originallyFocusedElement.
}
// FIXME: 20. Queue a popover toggle event task given element, "closed", and "open".
// 20. Queue a popover toggle event task given element, "closed", and "open".
queue_a_popover_toggle_event_task("closed"_string, "open"_string);
// 21. Run cleanupShowingFlag.
cleanup_showing_flag();
@ -1112,9 +1127,19 @@ WebIDL::ExceptionOr<void> HTMLElement::hide_popover(FocusPreviousElement, FireEv
// 10. If fireEvents is true:
if (fire_events == FireEvents::Yes) {
// FIXME: 10.1. Fire an event named beforetoggle, using ToggleEvent, with the oldState attribute initialized to "open" and the newState attribute initialized to "closed" at element.
// 10.1. Fire an event named beforetoggle, using ToggleEvent, with the oldState attribute initialized to "open" and the newState attribute initialized to "closed" at element.
ToggleEventInit event_init {};
event_init.old_state = "open"_string;
event_init.new_state = "closed"_string;
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::beforetoggle, move(event_init)));
// FIXME: 10.2. If autoPopoverListContainsElement is true and document's showing auto popover list's last item is not element, then run hide all popovers until given element, focusPreviousElement, and false.
// FIXME: 10.3. If the result of running check popover validity given element, true, throwExceptions, and null is false, then run cleanupSteps and return.
// 10.3. If the result of running check popover validity given element, true, throwExceptions, and null is false, then run cleanupSteps and return.
if (!TRY(check_popover_validity(ExpectedToBeShowing::Yes, throw_exceptions, nullptr))) {
cleanup_steps->function()();
return {};
}
// 10.4. Request an element to be removed from the top layer given element.
document.request_an_element_to_be_remove_from_the_top_layer(*this);
} else {
@ -1125,7 +1150,9 @@ WebIDL::ExceptionOr<void> HTMLElement::hide_popover(FocusPreviousElement, FireEv
// 12. Set element's popover visibility state to hidden.
m_popover_visibility_state = PopoverVisibilityState::Hidden;
// FIXME: 13. If fireEvents is true, then queue a popover toggle event task given element, "open", and "closed".
// 13. If fireEvents is true, then queue a popover toggle event task given element, "open", and "closed".
if (fire_events == FireEvents::Yes)
queue_a_popover_toggle_event_task("open"_string, "closed"_string);
// FIXME: 14. Let previouslyFocusedElement be element's previously focused element.
@ -1175,6 +1202,44 @@ WebIDL::ExceptionOr<bool> HTMLElement::toggle_popover(TogglePopoverOptionsOrForc
return popover_visibility_state() == PopoverVisibilityState::Showing;
}
// https://html.spec.whatwg.org/multipage/popover.html#queue-a-popover-toggle-event-task
void HTMLElement::queue_a_popover_toggle_event_task(String old_state, String new_state)
{
// 1. If element's popover toggle task tracker is not null, then:
if (m_popover_toggle_task_tracker.has_value()) {
// 1. Set oldState to element's popover toggle task tracker's old state.
old_state = move(m_popover_toggle_task_tracker->old_state);
// 2. Remove element's popover toggle task tracker's task from its task queue.
HTML::main_thread_event_loop().task_queue().remove_tasks_matching([&](auto const& task) {
return task.id() == m_popover_toggle_task_tracker->task_id;
});
// 3. Set element's popover toggle task tracker to null.
m_popover_toggle_task_tracker->task_id = {};
}
// 2. Queue an element task given the DOM manipulation task source and element to run the following steps:
auto task_id = queue_an_element_task(HTML::Task::Source::DOMManipulation, [this, old_state, new_state = move(new_state)]() mutable {
// 1. Fire an event named toggle at element, using ToggleEvent, with the oldState attribute initialized to
// oldState and the newState attribute initialized to newState.
ToggleEventInit event_init {};
event_init.old_state = move(old_state);
event_init.new_state = move(new_state);
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::toggle, move(event_init)));
// 2. Set element's popover toggle task tracker to null.
m_popover_toggle_task_tracker = {};
});
// 3. Set element's popover toggle task tracker to a struct with task set to the just-queued task and old state set to oldState.
m_popover_toggle_task_tracker = ToggleTaskTracker {
.task_id = task_id,
.old_state = move(old_state),
};
}
void HTMLElement::did_receive_focus()
{
if (!first_is_one_of(m_content_editable_state, ContentEditableState::True, ContentEditableState::PlaintextOnly))

View file

@ -6,10 +6,12 @@
#pragma once
#include <AK/Optional.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/HTML/EventNames.h>
#include <LibWeb/HTML/GlobalEventHandlers.h>
#include <LibWeb/HTML/HTMLOrSVGElement.h>
#include <LibWeb/HTML/ToggleTaskTracker.h>
#include <LibWeb/HTML/TokenizedFeatures.h>
namespace Web::HTML {
@ -127,8 +129,6 @@ public:
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);
WebIDL::ExceptionOr<bool> check_popover_validity(ExpectedToBeShowing expected_to_be_showing, ThrowExceptions throw_exceptions, GC::Ptr<DOM::Document>);
protected:
HTMLElement(DOM::Document&, DOM::QualifiedName);
@ -155,6 +155,10 @@ 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
GC::Ptr<ElementInternals> m_attached_internals;
@ -174,6 +178,9 @@ private:
// https://html.spec.whatwg.org/multipage/popover.html#popover-showing-or-hiding
bool m_popover_showing_or_hiding { false };
// https://html.spec.whatwg.org/multipage/popover.html#the-popover-attribute:toggle-task-tracker
Optional<ToggleTaskTracker> m_popover_toggle_task_tracker;
};
}