mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-28 11:49:44 +00:00
LibWeb: Implement the ToggleEvent.source attribute
See: https://github.com/whatwg/html/pull/11186
This commit is contained in:
parent
82f9b51da6
commit
fc35229dab
Notes:
github-actions[bot]
2025-06-07 03:07:15 +00:00
Author: https://github.com/Gingeh
Commit: fc35229dab
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5002
Reviewed-by: https://github.com/tcl3 ✅
Reviewed-by: https://github.com/trflynn89
13 changed files with 297 additions and 88 deletions
|
@ -223,9 +223,9 @@ void HTMLButtonElement::activation_behavior(DOM::Event const& event)
|
||||||
// 9. If command is in the Hide Popover state:
|
// 9. If command is in the Hide Popover state:
|
||||||
if (command == "hide-popover") {
|
if (command == "hide-popover") {
|
||||||
// 1. If the result of running check popover validity given target, true, false, and null is true,
|
// 1. If the result of running check popover validity given target, true, false, and null is true,
|
||||||
// then run the hide popover algorithm given target, true, true, and false.
|
// then run the hide popover algorithm given target, true, true, false, and element.
|
||||||
if (MUST(target->check_popover_validity(ExpectedToBeShowing::Yes, ThrowExceptions::No, nullptr, IgnoreDomState::No))) {
|
if (MUST(target->check_popover_validity(ExpectedToBeShowing::Yes, ThrowExceptions::No, nullptr, IgnoreDomState::No))) {
|
||||||
MUST(target->hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::No));
|
MUST(target->hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::No, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,9 +238,9 @@ void HTMLButtonElement::activation_behavior(DOM::Event const& event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Otheriwse, if the result of running check popover validity given target, true, false, and null is true,
|
// 2. Otheriwse, if the result of running check popover validity given target, true, false, and null is true,
|
||||||
// then run the hide popover algorithm given target, true, true, and false.
|
// then run the hide popover algorithm given target, true, true, false and element.
|
||||||
else if (MUST(target->check_popover_validity(ExpectedToBeShowing::Yes, ThrowExceptions::No, nullptr, IgnoreDomState::No))) {
|
else if (MUST(target->check_popover_validity(ExpectedToBeShowing::Yes, ThrowExceptions::No, nullptr, IgnoreDomState::No))) {
|
||||||
MUST(target->hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::No));
|
MUST(target->hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::No, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ void HTMLDialogElement::visit_edges(JS::Cell::Visitor& visitor)
|
||||||
Base::visit_edges(visitor);
|
Base::visit_edges(visitor);
|
||||||
|
|
||||||
visitor.visit(m_close_watcher);
|
visitor.visit(m_close_watcher);
|
||||||
|
visitor.visit(m_request_close_source_element);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLDialogElement::removed_from(Node* old_parent, Node& old_root)
|
void HTMLDialogElement::removed_from(Node* old_parent, Node& old_root)
|
||||||
|
@ -63,7 +64,7 @@ void HTMLDialogElement::removed_from(Node* old_parent, Node& old_root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/interactive-elements.html#queue-a-dialog-toggle-event-task
|
// https://html.spec.whatwg.org/multipage/interactive-elements.html#queue-a-dialog-toggle-event-task
|
||||||
void HTMLDialogElement::queue_a_dialog_toggle_event_task(AK::String old_state, AK::String new_state)
|
void HTMLDialogElement::queue_a_dialog_toggle_event_task(AK::String old_state, AK::String new_state, GC::Ptr<DOM::Element> source)
|
||||||
{
|
{
|
||||||
// 1. If element's dialog toggle task tracker is not null, then:
|
// 1. If element's dialog toggle task tracker is not null, then:
|
||||||
if (m_dialog_toggle_task_tracker.has_value()) {
|
if (m_dialog_toggle_task_tracker.has_value()) {
|
||||||
|
@ -80,14 +81,14 @@ void HTMLDialogElement::queue_a_dialog_toggle_event_task(AK::String old_state, A
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Queue an element task given the DOM manipulation task source and element to run the following steps:
|
// 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(Task::Source::DOMManipulation, [this, old_state, new_state = move(new_state)]() {
|
auto task_id = queue_an_element_task(Task::Source::DOMManipulation, [this, old_state, new_state = move(new_state), source]() {
|
||||||
// 1. Fire an event named toggle at element, using ToggleEvent, with the oldState attribute initialized to
|
// 1. Fire an event named toggle at element, using ToggleEvent, with the oldState attribute initialized to
|
||||||
// oldState and the newState attribute initialized to newState.
|
// oldState, the newState attribute initialized to newState, and the source attribute initialized to source.
|
||||||
ToggleEventInit event_init {};
|
ToggleEventInit event_init {};
|
||||||
event_init.old_state = move(old_state);
|
event_init.old_state = move(old_state);
|
||||||
event_init.new_state = move(new_state);
|
event_init.new_state = move(new_state);
|
||||||
|
|
||||||
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::toggle, move(event_init)));
|
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::toggle, move(event_init), source));
|
||||||
|
|
||||||
// 2. Set element's dialog toggle task tracker to null.
|
// 2. Set element's dialog toggle task tracker to null.
|
||||||
m_dialog_toggle_task_tracker = {};
|
m_dialog_toggle_task_tracker = {};
|
||||||
|
@ -127,8 +128,8 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show()
|
||||||
if (has_attribute(AttributeNames::open))
|
if (has_attribute(AttributeNames::open))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
// 5. Queue a dialog toggle event task given this, "closed", and "open".
|
// 5. Queue a dialog toggle event task given this, "closed", "open", and null.
|
||||||
queue_a_dialog_toggle_event_task("closed"_string, "open"_string);
|
queue_a_dialog_toggle_event_task("closed"_string, "open"_string, nullptr);
|
||||||
|
|
||||||
// 6. Add an open attribute to this, whose value is the empty string.
|
// 6. Add an open attribute to this, whose value is the empty string.
|
||||||
TRY(set_attribute(AttributeNames::open, {}));
|
TRY(set_attribute(AttributeNames::open, {}));
|
||||||
|
@ -169,11 +170,11 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show()
|
||||||
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-showmodal
|
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-showmodal
|
||||||
WebIDL::ExceptionOr<void> HTMLDialogElement::show_modal()
|
WebIDL::ExceptionOr<void> HTMLDialogElement::show_modal()
|
||||||
{
|
{
|
||||||
// The showModal() method steps are to show a modal dialog given this.
|
// The showModal() method steps are to show a modal dialog given this and null.
|
||||||
return show_a_modal_dialog(*this);
|
return show_a_modal_dialog(*this, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<void> HTMLDialogElement::show_a_modal_dialog(HTMLDialogElement& subject)
|
WebIDL::ExceptionOr<void> HTMLDialogElement::show_a_modal_dialog(HTMLDialogElement& subject, GC::Ptr<DOM::Element> source)
|
||||||
{
|
{
|
||||||
// To show a modal dialog given a dialog element subject:
|
// To show a modal dialog given a dialog element subject:
|
||||||
auto& realm = subject.realm();
|
auto& realm = subject.realm();
|
||||||
|
@ -200,13 +201,13 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show_a_modal_dialog(HTMLDialogEleme
|
||||||
|
|
||||||
// 6. If the result of firing an event named beforetoggle, using ToggleEvent,
|
// 6. If the result of firing an event named beforetoggle, using ToggleEvent,
|
||||||
// with the cancelable attribute initialized to true, the oldState attribute initialized to "closed",
|
// with the cancelable attribute initialized to true, the oldState attribute initialized to "closed",
|
||||||
// and the newState attribute initialized to "open" at subject is false, then return.
|
// the newState attribute initialized to "open", and the source attribute initialized to source at subject is false, then return.
|
||||||
ToggleEventInit event_init {};
|
ToggleEventInit event_init {};
|
||||||
event_init.cancelable = true;
|
event_init.cancelable = true;
|
||||||
event_init.old_state = "closed"_string;
|
event_init.old_state = "closed"_string;
|
||||||
event_init.new_state = "open"_string;
|
event_init.new_state = "open"_string;
|
||||||
|
|
||||||
auto beforetoggle_result = subject.dispatch_event(ToggleEvent::create(realm, EventNames::beforetoggle, move(event_init)));
|
auto beforetoggle_result = subject.dispatch_event(ToggleEvent::create(realm, EventNames::beforetoggle, move(event_init), source));
|
||||||
if (!beforetoggle_result)
|
if (!beforetoggle_result)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
@ -222,8 +223,8 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show_a_modal_dialog(HTMLDialogEleme
|
||||||
if (subject.popover_visibility_state() == PopoverVisibilityState::Showing)
|
if (subject.popover_visibility_state() == PopoverVisibilityState::Showing)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
// 10. Queue a dialog toggle event task given subject, "closed", and "open".
|
// 10. Queue a dialog toggle event task given subject, "closed", "open", and source.
|
||||||
subject.queue_a_dialog_toggle_event_task("closed"_string, "open"_string);
|
subject.queue_a_dialog_toggle_event_task("closed"_string, "open"_string, source);
|
||||||
|
|
||||||
// 11. Add an open attribute to subject, whose value is the empty string.
|
// 11. Add an open attribute to subject, whose value is the empty string.
|
||||||
TRY(subject.set_attribute(AttributeNames::open, {}));
|
TRY(subject.set_attribute(AttributeNames::open, {}));
|
||||||
|
@ -277,31 +278,38 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show_a_modal_dialog(HTMLDialogEleme
|
||||||
void HTMLDialogElement::close(Optional<String> return_value)
|
void HTMLDialogElement::close(Optional<String> return_value)
|
||||||
{
|
{
|
||||||
// 1. If returnValue is not given, then set it to null.
|
// 1. If returnValue is not given, then set it to null.
|
||||||
// 2. Close the dialog this with returnValue.
|
// 2. Close the dialog this with returnValue and null.
|
||||||
close_the_dialog(move(return_value));
|
close_the_dialog(move(return_value), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-requestclose
|
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-requestclose
|
||||||
void HTMLDialogElement::request_close(Optional<String> return_value)
|
void HTMLDialogElement::request_close(Optional<String> return_value)
|
||||||
{
|
{
|
||||||
|
// AD-HOC: This method is an amalgamation of the requestClose() and "request to close" algorithms from the spec.
|
||||||
|
// FIXME: Once the "request-close" command is implemented, this will need to be split into two methods.
|
||||||
|
// For now this implementation is only used for the requestClose() method, which sets source to null.
|
||||||
|
auto source = nullptr;
|
||||||
|
|
||||||
// 1. If this does not have an open attribute, then return.
|
// 1. If this does not have an open attribute, then return.
|
||||||
if (!has_attribute(AttributeNames::open))
|
if (!has_attribute(AttributeNames::open))
|
||||||
return;
|
return;
|
||||||
// ADHOC: 2. If this's close watcher is null, then close the dialog this with returnValue, and return. See https://github.com/whatwg/html/pull/10983
|
// AD-HOC: 2. If this's close watcher is null, then close the dialog this with returnValue and source, and return. See https://github.com/whatwg/html/pull/10983
|
||||||
if (!m_close_watcher) {
|
if (!m_close_watcher) {
|
||||||
close_the_dialog(move(return_value));
|
close_the_dialog(move(return_value), source);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 3. Set dialog's enable close watcher for requestClose() to true.
|
// 3. Set dialog's enable close watcher for requestClose() to true.
|
||||||
// ADHOC: Implemented slightly differently to the spec, as the spec is unnecessarily complex.
|
// AD-HOC: Implemented slightly differently to the spec, as the spec is unnecessarily complex.
|
||||||
m_close_watcher->set_enabled(true);
|
m_close_watcher->set_enabled(true);
|
||||||
// 4. If returnValue is not given, then set it to null.
|
// 4. If returnValue is not given, then set it to null.
|
||||||
// 5. Set this's request close return value to returnValue.
|
// 5. Set this's request close return value to returnValue.
|
||||||
m_request_close_return_value = return_value;
|
m_request_close_return_value = return_value;
|
||||||
|
// 6. Set subject's request close source element to source.
|
||||||
|
m_request_close_source_element = source;
|
||||||
// 6. Request to close dialog's close watcher with false.
|
// 6. Request to close dialog's close watcher with false.
|
||||||
m_close_watcher->request_close(false);
|
m_close_watcher->request_close(false);
|
||||||
// 7. Set dialog's enable close watcher for requestClose() to false.
|
// 7. Set dialog's enable close watcher for requestClose() to false.
|
||||||
// ADHOC: Implemented slightly differently to the spec, as the spec is unnecessarily complex.
|
// AD-HOC: Implemented slightly differently to the spec, as the spec is unnecessarily complex.
|
||||||
// FIXME: This should be set based on dialog closedby state, when implemented.
|
// FIXME: This should be set based on dialog closedby state, when implemented.
|
||||||
if (m_close_watcher)
|
if (m_close_watcher)
|
||||||
m_close_watcher->set_enabled(m_is_modal);
|
m_close_watcher->set_enabled(m_is_modal);
|
||||||
|
@ -320,25 +328,25 @@ void HTMLDialogElement::set_return_value(String return_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/interactive-elements.html#close-the-dialog
|
// https://html.spec.whatwg.org/multipage/interactive-elements.html#close-the-dialog
|
||||||
void HTMLDialogElement::close_the_dialog(Optional<String> result)
|
void HTMLDialogElement::close_the_dialog(Optional<String> result, GC::Ptr<DOM::Element> source)
|
||||||
{
|
{
|
||||||
// 1. If subject does not have an open attribute, then return.
|
// 1. If subject does not have an open attribute, then return.
|
||||||
if (!has_attribute(AttributeNames::open))
|
if (!has_attribute(AttributeNames::open))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 2. Fire an event named beforetoggle, using ToggleEvent, with the oldState attribute initialized to "open" and the newState attribute initialized to "closed" at subject.
|
// 2. Fire an event named beforetoggle, using ToggleEvent, with the oldState attribute initialized to "open", the newState attribute initialized to "closed", and the source attribute initialized to source at subject.
|
||||||
ToggleEventInit event_init {};
|
ToggleEventInit event_init {};
|
||||||
event_init.old_state = "open"_string;
|
event_init.old_state = "open"_string;
|
||||||
event_init.new_state = "closed"_string;
|
event_init.new_state = "closed"_string;
|
||||||
|
|
||||||
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::beforetoggle, move(event_init)));
|
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::beforetoggle, move(event_init), source));
|
||||||
|
|
||||||
// 3. If subject does not have an open attribute, then return.
|
// 3. If subject does not have an open attribute, then return.
|
||||||
if (!has_attribute(AttributeNames::open))
|
if (!has_attribute(AttributeNames::open))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 4. Queue a dialog toggle event task given subject, "open", and "closed".
|
// 4. Queue a dialog toggle event task given subject, "open", "closed", and source.
|
||||||
queue_a_dialog_toggle_event_task("open"_string, "closed"_string);
|
queue_a_dialog_toggle_event_task("open"_string, "closed"_string, source);
|
||||||
|
|
||||||
// 5. Remove subject's open attribute.
|
// 5. Remove subject's open attribute.
|
||||||
remove_attribute(AttributeNames::open);
|
remove_attribute(AttributeNames::open);
|
||||||
|
@ -355,30 +363,33 @@ void HTMLDialogElement::close_the_dialog(Optional<String> result)
|
||||||
// 9. Remove subject from subject's node document's open dialogs list.
|
// 9. Remove subject from subject's node document's open dialogs list.
|
||||||
document().open_dialogs_list().remove_first_matching([this](auto other) { return other == this; });
|
document().open_dialogs_list().remove_first_matching([this](auto other) { return other == this; });
|
||||||
|
|
||||||
// 10. If result is not null, then set the returnValue attribute to result.
|
// 10. If result is not null, then set subject's return value to result.
|
||||||
if (result.has_value())
|
if (result.has_value())
|
||||||
set_return_value(result.release_value());
|
set_return_value(result.release_value());
|
||||||
|
|
||||||
// 11. Set the request close return value to null.
|
// 11. Set subject's request close return value to null.
|
||||||
m_request_close_return_value = {};
|
m_request_close_return_value = {};
|
||||||
|
|
||||||
// FIXME: 12. If subject's previously focused element is not null, then:
|
// 12. Set subject's request close source element to null.
|
||||||
|
m_request_close_source_element = nullptr;
|
||||||
|
|
||||||
|
// FIXME: 13. If subject's previously focused element is not null, then:
|
||||||
// 1. Let element be subject's previously focused element.
|
// 1. Let element be subject's previously focused element.
|
||||||
// 2. Set subject's previously focused element to null.
|
// 2. Set subject's previously focused element to null.
|
||||||
// 3. If subject's node document's focused area of the document's DOM anchor is a shadow-including inclusive descendant of subject,
|
// 3. If subject's node document's focused area of the document's DOM anchor is a shadow-including inclusive descendant of subject,
|
||||||
// or wasModal is true, then run the focusing steps for element; the viewport should not be scrolled by doing this step.
|
// or wasModal is true, then run the focusing steps for element; the viewport should not be scrolled by doing this step.
|
||||||
|
|
||||||
// 13. Queue an element task on the user interaction task source given the subject element to fire an event named close at subject.
|
// 14. Queue an element task on the user interaction task source given the subject element to fire an event named close at subject.
|
||||||
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
|
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
|
||||||
auto close_event = DOM::Event::create(realm(), HTML::EventNames::close);
|
auto close_event = DOM::Event::create(realm(), HTML::EventNames::close);
|
||||||
dispatch_event(close_event);
|
dispatch_event(close_event);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 14. If subject's close watcher is not null, then:
|
// 15. If subject's close watcher is not null, then:
|
||||||
if (m_close_watcher) {
|
if (m_close_watcher) {
|
||||||
// 14.1 Destroy subject's close watcher.
|
// 1. Destroy subject's close watcher.
|
||||||
m_close_watcher->destroy();
|
m_close_watcher->destroy();
|
||||||
// 14.2 Set subject's close watcher to null.
|
// 2. Set subject's close watcher to null.
|
||||||
m_close_watcher = nullptr;
|
m_close_watcher = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -401,10 +412,10 @@ void HTMLDialogElement::set_close_watcher()
|
||||||
0, ""_fly_string, &realm());
|
0, ""_fly_string, &realm());
|
||||||
auto cancel_callback = realm().heap().allocate<WebIDL::CallbackType>(*cancel_callback_function, realm());
|
auto cancel_callback = realm().heap().allocate<WebIDL::CallbackType>(*cancel_callback_function, realm());
|
||||||
m_close_watcher->add_event_listener_without_options(HTML::EventNames::cancel, DOM::IDLEventListener::create(realm(), cancel_callback));
|
m_close_watcher->add_event_listener_without_options(HTML::EventNames::cancel, DOM::IDLEventListener::create(realm(), cancel_callback));
|
||||||
// - closeAction being to close the dialog given dialog and dialog's request close return value.
|
// - closeAction being to close the dialog given dialog, dialog's request close return value, and dialog's request close source element.
|
||||||
auto close_callback_function = JS::NativeFunction::create(
|
auto close_callback_function = JS::NativeFunction::create(
|
||||||
realm(), [this](JS::VM&) {
|
realm(), [this](JS::VM&) {
|
||||||
close_the_dialog(m_request_close_return_value);
|
close_the_dialog(m_request_close_return_value, m_request_close_source_element);
|
||||||
|
|
||||||
return JS::js_undefined();
|
return JS::js_undefined();
|
||||||
},
|
},
|
||||||
|
@ -475,19 +486,17 @@ void HTMLDialogElement::invoker_command_steps(DOM::Element& invoker, String& com
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. If command is in the Close state and element has an open attribute:
|
// 2. If command is in the Close state and element has an open attribute,
|
||||||
|
// then close the dialog given element with invoker's optional value and invoker.
|
||||||
if (command == "close" && has_attribute(AttributeNames::open)) {
|
if (command == "close" && has_attribute(AttributeNames::open)) {
|
||||||
// 1. Let value be invoker's value.
|
// FIXME: This assumes invoker is a button.
|
||||||
// FIXME: This assumes invoker is a button.
|
auto optional_value = invoker.get_attribute(AttributeNames::value);
|
||||||
auto value = invoker.get_attribute(AttributeNames::value);
|
close_the_dialog(optional_value, invoker);
|
||||||
|
|
||||||
// 2. Close the dialog element with value.
|
|
||||||
close_the_dialog(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. If command is the Show Modal state and element does not have an open attribute, then show a modal dialog given element.
|
// 3. If command is the Show Modal state and element does not have an open attribute, then show a modal dialog given element and invoker.
|
||||||
if (command == "show-modal" && !has_attribute(AttributeNames::open)) {
|
if (command == "show-modal" && !has_attribute(AttributeNames::open)) {
|
||||||
MUST(show_a_modal_dialog(*this));
|
MUST(show_a_modal_dialog(*this, invoker));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,9 @@ public:
|
||||||
String return_value() const;
|
String return_value() const;
|
||||||
void set_return_value(String);
|
void set_return_value(String);
|
||||||
|
|
||||||
static WebIDL::ExceptionOr<void> show_a_modal_dialog(HTMLDialogElement&);
|
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);
|
||||||
|
|
||||||
WebIDL::ExceptionOr<void> show();
|
WebIDL::ExceptionOr<void> show();
|
||||||
WebIDL::ExceptionOr<void> show_modal();
|
WebIDL::ExceptionOr<void> show_modal();
|
||||||
|
@ -50,9 +52,7 @@ private:
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
void queue_a_dialog_toggle_event_task(String old_state, String new_state);
|
void queue_a_dialog_toggle_event_task(String old_state, String new_state, GC::Ptr<DOM::Element> source);
|
||||||
|
|
||||||
void close_the_dialog(Optional<String> result);
|
|
||||||
|
|
||||||
void run_dialog_focusing_steps();
|
void run_dialog_focusing_steps();
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ private:
|
||||||
String m_return_value;
|
String m_return_value;
|
||||||
bool m_is_modal { false };
|
bool m_is_modal { false };
|
||||||
Optional<String> m_request_close_return_value;
|
Optional<String> m_request_close_return_value;
|
||||||
|
GC::Ptr<DOM::Element> m_request_close_source_element;
|
||||||
GC::Ptr<CloseWatcher> m_close_watcher;
|
GC::Ptr<CloseWatcher> m_close_watcher;
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-toggle-task-tracker
|
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-toggle-task-tracker
|
||||||
|
|
|
@ -693,10 +693,10 @@ void HTMLElement::attribute_changed(FlyString const& name, Optional<String> cons
|
||||||
|
|
||||||
// 3. If element's popover visibility state is in the showing state
|
// 3. If element's popover visibility state is in the showing state
|
||||||
// and oldValue and value are in different states,
|
// and oldValue and value are in different states,
|
||||||
// then run the hide popover algorithm given element, true, true, false, and true.
|
// then run the hide popover algorithm given element, true, true, false, true, and null.
|
||||||
if (m_popover_visibility_state == PopoverVisibilityState::Showing
|
if (m_popover_visibility_state == PopoverVisibilityState::Showing
|
||||||
&& popover_value_to_state(old_value) != popover_value_to_state(value))
|
&& popover_value_to_state(old_value) != popover_value_to_state(value))
|
||||||
MUST(hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::Yes));
|
MUST(hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::Yes, nullptr));
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1211,12 +1211,12 @@ WebIDL::ExceptionOr<void> HTMLElement::show_popover(ThrowExceptions throw_except
|
||||||
m_popover_showing_or_hiding = false;
|
m_popover_showing_or_hiding = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 9. 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.
|
// 9. If the result of firing an event named beforetoggle, using ToggleEvent, with the cancelable attribute initialized to true, the oldState attribute initialized to "closed", the newState attribute initialized to "open" at element, and the source attribute initialized to invoker is false, then run cleanupShowingFlag and return.
|
||||||
ToggleEventInit event_init {};
|
ToggleEventInit event_init {};
|
||||||
event_init.old_state = "closed"_string;
|
event_init.old_state = "closed"_string;
|
||||||
event_init.new_state = "open"_string;
|
event_init.new_state = "open"_string;
|
||||||
event_init.cancelable = true;
|
event_init.cancelable = true;
|
||||||
if (!dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::beforetoggle, move(event_init)))) {
|
if (!dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::beforetoggle, move(event_init), invoker))) {
|
||||||
cleanup_showing_flag();
|
cleanup_showing_flag();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1354,10 +1354,10 @@ WebIDL::ExceptionOr<void> HTMLElement::show_popover(ThrowExceptions throw_except
|
||||||
m_popover_close_watcher = CloseWatcher::establish(*document.window());
|
m_popover_close_watcher = CloseWatcher::establish(*document.window());
|
||||||
// - cancelAction being to return true.
|
// - cancelAction being to return true.
|
||||||
// We simply don't add an event listener for the cancel action.
|
// We simply don't add an event listener for the cancel action.
|
||||||
// - closeAction being to hide a popover given element, true, true, and false.
|
// - closeAction being to hide a popover given element, true, true, false, and null.
|
||||||
auto close_callback_function = JS::NativeFunction::create(
|
auto close_callback_function = JS::NativeFunction::create(
|
||||||
realm(), [this](JS::VM&) {
|
realm(), [this](JS::VM&) {
|
||||||
MUST(hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::No));
|
MUST(hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::No, nullptr));
|
||||||
|
|
||||||
return JS::js_undefined();
|
return JS::js_undefined();
|
||||||
},
|
},
|
||||||
|
@ -1378,8 +1378,8 @@ WebIDL::ExceptionOr<void> HTMLElement::show_popover(ThrowExceptions throw_except
|
||||||
// FIXME: 24. Set element's implicit anchor element to invoker.
|
// FIXME: 24. Set element's implicit anchor element to invoker.
|
||||||
// FIXME: 25. Run the popover focusing steps given element.
|
// FIXME: 25. Run the popover focusing steps given element.
|
||||||
// FIXME: 26. If shouldRestoreFocus is true and element's popover attribute is not in the no popover state, then set element's previously focused element to originallyFocusedElement.
|
// FIXME: 26. If shouldRestoreFocus is true and element's popover attribute is not in the no popover state, then set element's previously focused element to originallyFocusedElement.
|
||||||
// 27. Queue a popover toggle event task given element, "closed", and "open".
|
// 27. Queue a popover toggle event task given element, "closed", "open", and invoker.
|
||||||
queue_a_popover_toggle_event_task("closed"_string, "open"_string);
|
queue_a_popover_toggle_event_task("closed"_string, "open"_string, invoker);
|
||||||
// 28. Run cleanupShowingFlag.
|
// 28. Run cleanupShowingFlag.
|
||||||
cleanup_showing_flag();
|
cleanup_showing_flag();
|
||||||
|
|
||||||
|
@ -1390,13 +1390,13 @@ WebIDL::ExceptionOr<void> HTMLElement::show_popover(ThrowExceptions throw_except
|
||||||
// https://whatpr.org/html/9457/popover.html#dom-hidepopover
|
// https://whatpr.org/html/9457/popover.html#dom-hidepopover
|
||||||
WebIDL::ExceptionOr<void> HTMLElement::hide_popover_for_bindings()
|
WebIDL::ExceptionOr<void> HTMLElement::hide_popover_for_bindings()
|
||||||
{
|
{
|
||||||
// The hidePopover() method steps are to run the hide popover algorithm given this, true, true, true, and false.
|
// The hidePopover() method steps are to run the hide popover algorithm given this, true, true, true, false, and null.
|
||||||
return hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::Yes, IgnoreDomState::No);
|
return hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::Yes, IgnoreDomState::No, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/popover.html#hide-popover-algorithm
|
// https://html.spec.whatwg.org/multipage/popover.html#hide-popover-algorithm
|
||||||
// https://whatpr.org/html/9457/popover.html#hide-popover-algorithm
|
// https://whatpr.org/html/9457/popover.html#hide-popover-algorithm
|
||||||
WebIDL::ExceptionOr<void> HTMLElement::hide_popover(FocusPreviousElement focus_previous_element, FireEvents fire_events, ThrowExceptions throw_exceptions, IgnoreDomState ignore_dom_state)
|
WebIDL::ExceptionOr<void> HTMLElement::hide_popover(FocusPreviousElement focus_previous_element, FireEvents fire_events, ThrowExceptions throw_exceptions, IgnoreDomState ignore_dom_state, GC::Ptr<HTMLElement> source)
|
||||||
{
|
{
|
||||||
// 1. If the result of running check popover validity given element, true, throwExceptions, null and ignoreDomState is false, then return.
|
// 1. If the result of running check popover validity given element, true, throwExceptions, null and ignoreDomState is false, then return.
|
||||||
if (!TRY(check_popover_validity(ExpectedToBeShowing::Yes, throw_exceptions, nullptr, ignore_dom_state)))
|
if (!TRY(check_popover_validity(ExpectedToBeShowing::Yes, throw_exceptions, nullptr, ignore_dom_state)))
|
||||||
|
@ -1446,11 +1446,11 @@ WebIDL::ExceptionOr<void> HTMLElement::hide_popover(FocusPreviousElement focus_p
|
||||||
|
|
||||||
// 9. If fireEvents is true:
|
// 9. If fireEvents is true:
|
||||||
if (fire_events == FireEvents::Yes) {
|
if (fire_events == FireEvents::Yes) {
|
||||||
// 9.1. Fire an event named beforetoggle, using ToggleEvent, with the oldState attribute initialized to "open" and the newState attribute initialized to "closed" at element.
|
// 9.1. Fire an event named beforetoggle, using ToggleEvent, with the oldState attribute initialized to "open", the newState attribute initialized to "closed", and the source attribute set to source at element.
|
||||||
ToggleEventInit event_init {};
|
ToggleEventInit event_init {};
|
||||||
event_init.old_state = "open"_string;
|
event_init.old_state = "open"_string;
|
||||||
event_init.new_state = "closed"_string;
|
event_init.new_state = "closed"_string;
|
||||||
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::beforetoggle, move(event_init)));
|
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::beforetoggle, move(event_init), source));
|
||||||
|
|
||||||
// 9.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.
|
// 9.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.
|
||||||
if (auto_popover_list_contains_element && (showing_popovers.is_empty() || showing_popovers.last() != this))
|
if (auto_popover_list_contains_element && (showing_popovers.is_empty() || showing_popovers.last() != this))
|
||||||
|
@ -1502,9 +1502,9 @@ WebIDL::ExceptionOr<void> HTMLElement::hide_popover(FocusPreviousElement focus_p
|
||||||
// 13. Set element's popover visibility state to hidden.
|
// 13. Set element's popover visibility state to hidden.
|
||||||
m_popover_visibility_state = PopoverVisibilityState::Hidden;
|
m_popover_visibility_state = PopoverVisibilityState::Hidden;
|
||||||
|
|
||||||
// 14. If fireEvents is true, then queue a popover toggle event task given element, "open", and "closed".
|
// 14. If fireEvents is true, then queue a popover toggle event task given element, "open", "closed", and source.
|
||||||
if (fire_events == FireEvents::Yes)
|
if (fire_events == FireEvents::Yes)
|
||||||
queue_a_popover_toggle_event_task("open"_string, "closed"_string);
|
queue_a_popover_toggle_event_task("open"_string, "closed"_string, source);
|
||||||
|
|
||||||
// FIXME: 15. Let previouslyFocusedElement be element's previously focused element.
|
// FIXME: 15. Let previouslyFocusedElement be element's previously focused element.
|
||||||
|
|
||||||
|
@ -1538,9 +1538,9 @@ WebIDL::ExceptionOr<bool> HTMLElement::toggle_popover(TogglePopoverOptionsOrForc
|
||||||
invoker = options.source;
|
invoker = options.source;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5. If this's popover visibility state is showing, and force is null or false, then run the hide popover algorithm given this, true, true, true, and false.
|
// 5. If this's popover visibility state is showing, and force is null or false, then run the hide popover algorithm given this, true, true, true, false, and null.
|
||||||
if (popover_visibility_state() == PopoverVisibilityState::Showing && (!force.has_value() || !force.value()))
|
if (popover_visibility_state() == PopoverVisibilityState::Showing && (!force.has_value() || !force.value()))
|
||||||
TRY(hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::Yes, IgnoreDomState::No));
|
TRY(hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::Yes, IgnoreDomState::No, nullptr));
|
||||||
// 6. Otherwise, if force is not present or true, then run show popover given this true, and invoker.
|
// 6. Otherwise, if force is not present or true, then run show popover given this true, and invoker.
|
||||||
else if (!force.has_value() || force.value())
|
else if (!force.has_value() || force.value())
|
||||||
TRY(show_popover(ThrowExceptions::Yes, invoker));
|
TRY(show_popover(ThrowExceptions::Yes, invoker));
|
||||||
|
@ -1644,8 +1644,8 @@ void HTMLElement::hide_popover_stack_until(Vector<GC::Ref<HTMLElement>> const& p
|
||||||
// 1. Assert: popoverList is not empty.
|
// 1. Assert: popoverList is not empty.
|
||||||
VERIFY(!popover_list.is_empty());
|
VERIFY(!popover_list.is_empty());
|
||||||
|
|
||||||
// 2. Run the hide popover algorithm given the last item in popoverList, focusPreviousElement, fireEvents, and false.
|
// 2. Run the hide popover algorithm given the last item in popoverList, focusPreviousElement, fireEvents, false, and null.
|
||||||
MUST(popover_list.last()->hide_popover(focus_previous_element, fire_events, ThrowExceptions::No, IgnoreDomState::No));
|
MUST(popover_list.last()->hide_popover(focus_previous_element, fire_events, ThrowExceptions::No, IgnoreDomState::No, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Assert: repeatingHide is false or popoverList's last item is endpoint.
|
// 5. Assert: repeatingHide is false or popoverList's last item is endpoint.
|
||||||
|
@ -1670,8 +1670,8 @@ void HTMLElement::close_entire_popover_list(Vector<GC::Ref<HTMLElement>> const&
|
||||||
// FIXME: If an event handler opens a new popover then this could be an infinite loop.
|
// FIXME: If an event handler opens a new popover then this could be an infinite loop.
|
||||||
// 1. While popoverList is not empty:
|
// 1. While popoverList is not empty:
|
||||||
while (!popover_list.is_empty()) {
|
while (!popover_list.is_empty()) {
|
||||||
// 1. Run the hide popover algorithm given popoverList's last item, focusPreviousElement, fireEvents, and false.
|
// 1. Run the hide popover algorithm given popoverList's last item, focusPreviousElement, fireEvents, false, and null.
|
||||||
MUST(popover_list.last()->hide_popover(focus_previous_element, fire_events, ThrowExceptions::No, IgnoreDomState::No));
|
MUST(popover_list.last()->hide_popover(focus_previous_element, fire_events, ThrowExceptions::No, IgnoreDomState::No, nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1829,7 +1829,7 @@ GC::Ptr<HTMLElement> HTMLElement::nearest_inclusive_target_popover_for_invoker()
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/popover.html#queue-a-popover-toggle-event-task
|
// 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)
|
void HTMLElement::queue_a_popover_toggle_event_task(String old_state, String new_state, GC::Ptr<HTMLElement> source)
|
||||||
{
|
{
|
||||||
// 1. If element's popover toggle task tracker is not null, then:
|
// 1. If element's popover toggle task tracker is not null, then:
|
||||||
if (m_popover_toggle_task_tracker.has_value()) {
|
if (m_popover_toggle_task_tracker.has_value()) {
|
||||||
|
@ -1846,14 +1846,14 @@ void HTMLElement::queue_a_popover_toggle_event_task(String old_state, String new
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Queue an element task given the DOM manipulation task source and element to run the following steps:
|
// 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 {
|
auto task_id = queue_an_element_task(HTML::Task::Source::DOMManipulation, [this, old_state, new_state = move(new_state), source]() mutable {
|
||||||
// 1. Fire an event named toggle at element, using ToggleEvent, with the oldState attribute initialized to
|
// 1. Fire an event named toggle at element, using ToggleEvent, with the oldState attribute initialized to
|
||||||
// oldState and the newState attribute initialized to newState.
|
// oldState, the newState attribute initialized to newState, and the source attribute initialized to source.
|
||||||
ToggleEventInit event_init {};
|
ToggleEventInit event_init {};
|
||||||
event_init.old_state = move(old_state);
|
event_init.old_state = move(old_state);
|
||||||
event_init.new_state = move(new_state);
|
event_init.new_state = move(new_state);
|
||||||
|
|
||||||
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::toggle, move(event_init)));
|
dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::toggle, move(event_init), source));
|
||||||
|
|
||||||
// 2. Set element's popover toggle task tracker to null.
|
// 2. Set element's popover toggle task tracker to null.
|
||||||
m_popover_toggle_task_tracker = {};
|
m_popover_toggle_task_tracker = {};
|
||||||
|
@ -2002,9 +2002,9 @@ void HTMLElement::removed_from(Node* old_parent, Node& old_root)
|
||||||
Element::removed_from(old_parent, old_root);
|
Element::removed_from(old_parent, old_root);
|
||||||
|
|
||||||
// https://whatpr.org/html/9457/infrastructure.html#dom-trees:concept-node-remove-ext
|
// https://whatpr.org/html/9457/infrastructure.html#dom-trees:concept-node-remove-ext
|
||||||
// If removedNode's popover attribute is not in the no popover state, then run the hide popover algorithm given removedNode, false, false, false, and true.
|
// If removedNode's popover attribute is not in the no popover state, then run the hide popover algorithm given removedNode, false, false, false, true, and null.
|
||||||
if (popover().has_value())
|
if (popover().has_value())
|
||||||
MUST(hide_popover(FocusPreviousElement::No, FireEvents::No, ThrowExceptions::No, IgnoreDomState::Yes));
|
MUST(hide_popover(FocusPreviousElement::No, FireEvents::No, ThrowExceptions::No, IgnoreDomState::Yes, nullptr));
|
||||||
|
|
||||||
if (old_parent) {
|
if (old_parent) {
|
||||||
auto* parent_html_element = as_if<HTMLElement>(old_parent);
|
auto* parent_html_element = as_if<HTMLElement>(old_parent);
|
||||||
|
|
|
@ -147,7 +147,7 @@ public:
|
||||||
|
|
||||||
WebIDL::ExceptionOr<bool> check_popover_validity(ExpectedToBeShowing expected_to_be_showing, ThrowExceptions throw_exceptions, GC::Ptr<DOM::Document>, IgnoreDomState ignore_dom_state);
|
WebIDL::ExceptionOr<bool> check_popover_validity(ExpectedToBeShowing expected_to_be_showing, ThrowExceptions throw_exceptions, GC::Ptr<DOM::Document>, IgnoreDomState ignore_dom_state);
|
||||||
WebIDL::ExceptionOr<void> show_popover(ThrowExceptions throw_exceptions, GC::Ptr<HTMLElement> invoker);
|
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, IgnoreDomState ignore_dom_state);
|
WebIDL::ExceptionOr<void> hide_popover(FocusPreviousElement focus_previous_element, FireEvents fire_events, ThrowExceptions throw_exceptions, IgnoreDomState ignore_dom_state, GC::Ptr<HTMLElement> source);
|
||||||
|
|
||||||
static void hide_all_popovers_until(Variant<GC::Ptr<HTMLElement>, GC::Ptr<DOM::Document>> endpoint, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
static void hide_all_popovers_until(Variant<GC::Ptr<HTMLElement>, GC::Ptr<DOM::Document>> endpoint, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
||||||
static GC::Ptr<HTMLElement> topmost_popover_ancestor(GC::Ptr<DOM::Node> new_popover_or_top_layer_element, Vector<GC::Ref<HTMLElement>> const& popover_list, GC::Ptr<HTMLElement> invoker, IsPopover is_popover);
|
static GC::Ptr<HTMLElement> topmost_popover_ancestor(GC::Ptr<DOM::Node> new_popover_or_top_layer_element, Vector<GC::Ref<HTMLElement>> const& popover_list, GC::Ptr<HTMLElement> invoker, IsPopover is_popover);
|
||||||
|
@ -199,7 +199,7 @@ private:
|
||||||
|
|
||||||
GC::Ptr<DOM::NodeList> m_labels;
|
GC::Ptr<DOM::NodeList> m_labels;
|
||||||
|
|
||||||
void queue_a_popover_toggle_event_task(String old_state, String new_state);
|
void queue_a_popover_toggle_event_task(String old_state, String new_state, GC::Ptr<HTMLElement> source);
|
||||||
|
|
||||||
static Optional<String> popover_value_to_state(Optional<String> value);
|
static Optional<String> popover_value_to_state(Optional<String> value);
|
||||||
void hide_popover_stack_until(Vector<GC::Ref<HTMLElement>> const& popover_list, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
void hide_popover_stack_until(Vector<GC::Ref<HTMLElement>> const& popover_list, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
||||||
|
|
|
@ -220,8 +220,8 @@ WebIDL::ExceptionOr<void> HTMLFormElement::submit_form(GC::Ref<HTMLElement> subm
|
||||||
if (!result.has_value())
|
if (!result.has_value())
|
||||||
result = submitter->get_attribute_value(AttributeNames::value);
|
result = submitter->get_attribute_value(AttributeNames::value);
|
||||||
|
|
||||||
// 6. Close the dialog subject with result.
|
// 6. Close the dialog subject with result and null.
|
||||||
subject->close(move(result));
|
subject->close_the_dialog(move(result), nullptr);
|
||||||
|
|
||||||
// 7. Return.
|
// 7. Return.
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -61,9 +61,9 @@ void PopoverInvokerElement::popover_target_activation_behaviour(GC::Ref<DOM::Nod
|
||||||
&& popover->popover_visibility_state() == HTMLElement::PopoverVisibilityState::Hidden)
|
&& popover->popover_visibility_state() == HTMLElement::PopoverVisibilityState::Hidden)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 6. If popover's popover visibility state is showing, then run the hide popover algorithm given popover, true, true, false, and false.
|
// 6. If popover's popover visibility state is showing, then run the hide popover algorithm given popover, true, true, false, false, and node.
|
||||||
if (popover->popover_visibility_state() == HTMLElement::PopoverVisibilityState::Showing) {
|
if (popover->popover_visibility_state() == HTMLElement::PopoverVisibilityState::Showing) {
|
||||||
MUST(popover->hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::No));
|
MUST(popover->hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::No, as<HTMLElement>(*node)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Otherwise, if popover's popover visibility state is hidden and the result of running check popover validity given popover, false, false, null, and false is true, then run show popover given popover, false, and node.
|
// 7. Otherwise, if popover's popover visibility state is hidden and the result of running check popover validity given popover, false, false, null, and false is true, then run show popover given popover, false, and node.
|
||||||
|
|
|
@ -12,9 +12,9 @@ namespace Web::HTML {
|
||||||
|
|
||||||
GC_DEFINE_ALLOCATOR(ToggleEvent);
|
GC_DEFINE_ALLOCATOR(ToggleEvent);
|
||||||
|
|
||||||
GC::Ref<ToggleEvent> ToggleEvent::create(JS::Realm& realm, FlyString const& event_name, ToggleEventInit event_init)
|
GC::Ref<ToggleEvent> ToggleEvent::create(JS::Realm& realm, FlyString const& event_name, ToggleEventInit event_init, GC::Ptr<DOM::Element> source)
|
||||||
{
|
{
|
||||||
return realm.create<ToggleEvent>(realm, event_name, move(event_init));
|
return realm.create<ToggleEvent>(realm, event_name, move(event_init), source);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<GC::Ref<ToggleEvent>> ToggleEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, ToggleEventInit event_init)
|
WebIDL::ExceptionOr<GC::Ref<ToggleEvent>> ToggleEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, ToggleEventInit event_init)
|
||||||
|
@ -22,10 +22,11 @@ WebIDL::ExceptionOr<GC::Ref<ToggleEvent>> ToggleEvent::construct_impl(JS::Realm&
|
||||||
return create(realm, event_name, move(event_init));
|
return create(realm, event_name, move(event_init));
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleEvent::ToggleEvent(JS::Realm& realm, FlyString const& event_name, ToggleEventInit event_init)
|
ToggleEvent::ToggleEvent(JS::Realm& realm, FlyString const& event_name, ToggleEventInit event_init, GC::Ptr<DOM::Element> source)
|
||||||
: DOM::Event(realm, event_name, event_init)
|
: DOM::Event(realm, event_name, event_init)
|
||||||
, m_old_state(move(event_init.old_state))
|
, m_old_state(move(event_init.old_state))
|
||||||
, m_new_state(move(event_init.new_state))
|
, m_new_state(move(event_init.new_state))
|
||||||
|
, m_source(source)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,4 +36,10 @@ void ToggleEvent::initialize(JS::Realm& realm)
|
||||||
Base::initialize(realm);
|
Base::initialize(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ToggleEvent::visit_edges(Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_source);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
|
|
||||||
#include <AK/FlyString.h>
|
#include <AK/FlyString.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
#include <LibWeb/DOM/Element.h>
|
||||||
#include <LibWeb/DOM/Event.h>
|
#include <LibWeb/DOM/Event.h>
|
||||||
|
#include <LibWeb/DOM/Utils.h>
|
||||||
|
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ class ToggleEvent : public DOM::Event {
|
||||||
GC_DECLARE_ALLOCATOR(ToggleEvent);
|
GC_DECLARE_ALLOCATOR(ToggleEvent);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] static GC::Ref<ToggleEvent> create(JS::Realm&, FlyString const& event_name, ToggleEventInit = {});
|
[[nodiscard]] static GC::Ref<ToggleEvent> create(JS::Realm&, FlyString const& event_name, ToggleEventInit = {}, GC::Ptr<DOM::Element> source = {});
|
||||||
static WebIDL::ExceptionOr<GC::Ref<ToggleEvent>> construct_impl(JS::Realm&, FlyString const& event_name, ToggleEventInit);
|
static WebIDL::ExceptionOr<GC::Ref<ToggleEvent>> construct_impl(JS::Realm&, FlyString const& event_name, ToggleEventInit);
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/interaction.html#dom-toggleevent-oldstate
|
// https://html.spec.whatwg.org/multipage/interaction.html#dom-toggleevent-oldstate
|
||||||
|
@ -31,13 +33,23 @@ public:
|
||||||
// https://html.spec.whatwg.org/multipage/interaction.html#dom-toggleevent-newstate
|
// https://html.spec.whatwg.org/multipage/interaction.html#dom-toggleevent-newstate
|
||||||
String const& new_state() const { return m_new_state; }
|
String const& new_state() const { return m_new_state; }
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#dom-toggleevent-source
|
||||||
|
GC::Ptr<DOM::Element> source() const
|
||||||
|
{
|
||||||
|
// The source getter steps are to return the result of retargeting source against this's currentTarget.
|
||||||
|
return as<DOM::Element>(retarget(m_source, current_target()));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ToggleEvent(JS::Realm&, FlyString const& event_name, ToggleEventInit event_init);
|
ToggleEvent(JS::Realm&, FlyString const& event_name, ToggleEventInit event_init, GC::Ptr<DOM::Element> source);
|
||||||
|
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
|
|
||||||
String m_old_state;
|
String m_old_state;
|
||||||
String m_new_state;
|
String m_new_state;
|
||||||
|
GC::Ptr<DOM::Element> m_source;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ interface ToggleEvent : Event {
|
||||||
constructor(DOMString type, optional ToggleEventInit eventInitDict = {});
|
constructor(DOMString type, optional ToggleEventInit eventInitDict = {});
|
||||||
readonly attribute DOMString oldState;
|
readonly attribute DOMString oldState;
|
||||||
readonly attribute DOMString newState;
|
readonly attribute DOMString newState;
|
||||||
|
readonly attribute Element? source;
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary ToggleEventInit : EventInit {
|
dictionary ToggleEventInit : EventInit {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 7 tests
|
||||||
|
|
||||||
|
4 Pass
|
||||||
|
3 Fail
|
||||||
|
Pass ToggleEvent.source on popover elements: showPopover() without source.
|
||||||
|
Fail ToggleEvent.source on popover elements: showPopover() with source.
|
||||||
|
Pass ToggleEvent.source on popover elements: Calling click() on a popovertarget button.
|
||||||
|
Fail ToggleEvent.source on popover elements: Calling click() on a command button.
|
||||||
|
Pass ToggleEvent.source on popover elements: showPopover() then popovertarget button.
|
||||||
|
Fail ToggleEvent.source on popover elements: showPopover(invoker) then popovertarget button.
|
||||||
|
Pass ToggleEvent.source on popover elements: popovertarget button then hidePopover().
|
|
@ -0,0 +1,90 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<link rel=author href="mailto:jarhar@chromium.org">
|
||||||
|
<link rel=help href="https://github.com/whatwg/html/issues/9111">
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<script src="resources/toggle-event-source-test.js"></script>
|
||||||
|
|
||||||
|
<button id=popoversource popovertarget=popover>popovertarget source</button>
|
||||||
|
<button id=commandsource commandfor=popover command=show-popover>command source</button>
|
||||||
|
<div id=popover popover=auto>
|
||||||
|
popover
|
||||||
|
<button id=popoversourcehide popovertarget=popover>popovertarget source</button>
|
||||||
|
<button id=commandsourcehide commandfor=popover command=hide-popover>command source</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const popoversource = document.getElementById('popoversource');
|
||||||
|
const popoversourcehide = document.getElementById('popoversourcehide');
|
||||||
|
const commandsource = document.getElementById('commandsource');
|
||||||
|
const commandsourcehide = document.getElementById('commandsourcehide');
|
||||||
|
const popover = document.getElementById('popover');
|
||||||
|
|
||||||
|
let beforetoggleEvent = null;
|
||||||
|
let toggleEvent = null;
|
||||||
|
popover.addEventListener('beforetoggle', event => beforetoggleEvent = event);
|
||||||
|
popover.addEventListener('toggle', event => toggleEvent = event);
|
||||||
|
|
||||||
|
createToggleEventSourceTest({
|
||||||
|
description: 'ToggleEvent.source on popover elements: showPopover() without source.',
|
||||||
|
target: popover,
|
||||||
|
openFunc: async () => popover.showPopover(),
|
||||||
|
closeFunc: async () => popover.hidePopover(),
|
||||||
|
openSource: null,
|
||||||
|
closeSource: null
|
||||||
|
});
|
||||||
|
|
||||||
|
createToggleEventSourceTest({
|
||||||
|
description: 'ToggleEvent.source on popover elements: showPopover() with source.',
|
||||||
|
target: popover,
|
||||||
|
openFunc: async () => popover.showPopover({source: popoversource}),
|
||||||
|
closeFunc: async () => popover.hidePopover(),
|
||||||
|
openSource: popoversource,
|
||||||
|
closeSource: null
|
||||||
|
});
|
||||||
|
|
||||||
|
createToggleEventSourceTest({
|
||||||
|
description: 'ToggleEvent.source on popover elements: Calling click() on a popovertarget button.',
|
||||||
|
target: popover,
|
||||||
|
openFunc: async () => popoversource.click(),
|
||||||
|
closeFunc: async () => popoversourcehide.click(),
|
||||||
|
openSource: popoversource,
|
||||||
|
closeSource: popoversourcehide
|
||||||
|
});
|
||||||
|
|
||||||
|
createToggleEventSourceTest({
|
||||||
|
description: 'ToggleEvent.source on popover elements: Calling click() on a command button.',
|
||||||
|
target: popover,
|
||||||
|
openFunc: async () => commandsource.click(),
|
||||||
|
closeFunc: async () => commandsourcehide.click(),
|
||||||
|
openSource: commandsource,
|
||||||
|
closeSource: commandsourcehide
|
||||||
|
});
|
||||||
|
|
||||||
|
createToggleEventSourceTest({
|
||||||
|
description: 'ToggleEvent.source on popover elements: showPopover() then popovertarget button.',
|
||||||
|
target: popover,
|
||||||
|
openFunc: async () => popover.showPopover(),
|
||||||
|
closeFunc: async () => popoversourcehide.click(),
|
||||||
|
openSource: null,
|
||||||
|
closeSource: popoversourcehide
|
||||||
|
});
|
||||||
|
|
||||||
|
createToggleEventSourceTest({
|
||||||
|
description: 'ToggleEvent.source on popover elements: showPopover(invoker) then popovertarget button.',
|
||||||
|
target: popover,
|
||||||
|
openFunc: async () => popover.showPopover({source: popoversource}),
|
||||||
|
closeFunc: async () => popoversourcehide.click(),
|
||||||
|
openSource: popoversource,
|
||||||
|
closeSource: popoversourcehide
|
||||||
|
});
|
||||||
|
|
||||||
|
createToggleEventSourceTest({
|
||||||
|
description: 'ToggleEvent.source on popover elements: popovertarget button then hidePopover().',
|
||||||
|
target: popover,
|
||||||
|
openFunc: async () => popoversource.click(),
|
||||||
|
closeFunc: async () => popover.hidePopover(),
|
||||||
|
openSource: popoversource,
|
||||||
|
closeSource: null
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,76 @@
|
||||||
|
function createToggleEventSourceTest({
|
||||||
|
description,
|
||||||
|
target,
|
||||||
|
openFunc,
|
||||||
|
closeFunc,
|
||||||
|
openSource,
|
||||||
|
closeSource,
|
||||||
|
skipBeforetoggle}) {
|
||||||
|
promise_test(async () => {
|
||||||
|
let beforetoggleEvent = null;
|
||||||
|
let beforetoggleDuplicate = false;
|
||||||
|
let toggleEvent = null;
|
||||||
|
let toggleDuplicate = false;
|
||||||
|
target.addEventListener('beforetoggle', event => {
|
||||||
|
if (beforetoggleEvent) {
|
||||||
|
beforetoggleDuplicate = true;
|
||||||
|
}
|
||||||
|
beforetoggleEvent = event;
|
||||||
|
});
|
||||||
|
target.addEventListener('toggle', event => {
|
||||||
|
if (toggleEvent) {
|
||||||
|
toggleDuplicate = true;
|
||||||
|
}
|
||||||
|
toggleEvent = event;
|
||||||
|
});
|
||||||
|
|
||||||
|
await openFunc();
|
||||||
|
await new Promise(requestAnimationFrame);
|
||||||
|
await new Promise(requestAnimationFrame);
|
||||||
|
if (!skipBeforetoggle) {
|
||||||
|
assert_true(!!beforetoggleEvent,
|
||||||
|
'An opening beforetoggle event should have been fired.');
|
||||||
|
assert_false(beforetoggleDuplicate,
|
||||||
|
'Only one opening beforetoggle event should have been fired.');
|
||||||
|
assert_equals(beforetoggleEvent.newState, 'open',
|
||||||
|
'beforetoggle newState should be open.');
|
||||||
|
assert_equals(beforetoggleEvent.source, openSource,
|
||||||
|
'Opening beforetoggle.source.');
|
||||||
|
}
|
||||||
|
assert_true(!!toggleEvent,
|
||||||
|
'An opening toggle event should have been fired.');
|
||||||
|
assert_false(toggleDuplicate,
|
||||||
|
'Only one opening toggle event should have been fired.');
|
||||||
|
assert_equals(toggleEvent.newState, 'open',
|
||||||
|
'toggle newstate should be open.');
|
||||||
|
assert_equals(toggleEvent.source, openSource,
|
||||||
|
'Opening toggle.source.');
|
||||||
|
beforetoggleEvent = null;
|
||||||
|
beforetoggleDuplicate = false;
|
||||||
|
toggleEvent = null;
|
||||||
|
toggleDuplicate = false;
|
||||||
|
|
||||||
|
await closeFunc();
|
||||||
|
await new Promise(requestAnimationFrame);
|
||||||
|
await new Promise(requestAnimationFrame);
|
||||||
|
|
||||||
|
if (!skipBeforetoggle) {
|
||||||
|
assert_true(!!beforetoggleEvent,
|
||||||
|
'A closing beforetoggle event should have been fired.');
|
||||||
|
assert_false(beforetoggleDuplicate,
|
||||||
|
'Only one closing beforetoggle event should have been fired.');
|
||||||
|
assert_equals(beforetoggleEvent.newState, 'closed',
|
||||||
|
'beforetoggle newState should be closed.');
|
||||||
|
assert_equals(beforetoggleEvent.source, closeSource,
|
||||||
|
'Closing beforetoggle.source.');
|
||||||
|
}
|
||||||
|
assert_true(!!toggleEvent,
|
||||||
|
'A closing toggle event should have been fired.');
|
||||||
|
assert_false(toggleDuplicate,
|
||||||
|
'Only one closing toggle event should have been fired.');
|
||||||
|
assert_equals(toggleEvent.newState, 'closed',
|
||||||
|
'toggle newstate should be closed.');
|
||||||
|
assert_equals(toggleEvent.source, closeSource,
|
||||||
|
'Closing toggle.source.');
|
||||||
|
}, description);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue