mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-11 10:41:30 +00:00
LibWeb: Light dismiss popovers on click
This commit is contained in:
parent
6061da3382
commit
1f1884da54
Notes:
github-actions[bot]
2025-04-29 01:43:08 +00:00
Author: https://github.com/Gingeh
Commit: 1f1884da54
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4428
Reviewed-by: https://github.com/AtkinsSJ ✅
9 changed files with 308 additions and 6 deletions
|
@ -592,6 +592,7 @@ void Document::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_top_layer_pending_removals);
|
visitor.visit(m_top_layer_pending_removals);
|
||||||
visitor.visit(m_showing_auto_popover_list);
|
visitor.visit(m_showing_auto_popover_list);
|
||||||
visitor.visit(m_showing_hint_popover_list);
|
visitor.visit(m_showing_hint_popover_list);
|
||||||
|
visitor.visit(m_popover_pointerdown_target);
|
||||||
visitor.visit(m_console_client);
|
visitor.visit(m_console_client);
|
||||||
visitor.visit(m_editing_host_manager);
|
visitor.visit(m_editing_host_manager);
|
||||||
visitor.visit(m_local_storage_holder);
|
visitor.visit(m_local_storage_holder);
|
||||||
|
@ -6117,6 +6118,23 @@ void Document::process_top_layer_removals()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/popover.html#topmost-auto-popover
|
||||||
|
GC::Ptr<HTML::HTMLElement> Document::topmost_auto_or_hint_popover()
|
||||||
|
{
|
||||||
|
// To find the topmost auto or hint popover given a Document document, perform the following steps. They return an HTML element or null.
|
||||||
|
|
||||||
|
// 1. If document's showing hint popover list is not empty, then return document's showing hint popover list's last element.
|
||||||
|
if (!m_showing_hint_popover_list.is_empty())
|
||||||
|
return m_showing_hint_popover_list.last();
|
||||||
|
|
||||||
|
// 2. If document's showing auto popover list is not empty, then return document's showing auto popover list's last element.
|
||||||
|
if (!m_showing_auto_popover_list.is_empty())
|
||||||
|
return m_showing_auto_popover_list.last();
|
||||||
|
|
||||||
|
// 3. Return null.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void Document::set_needs_to_refresh_scroll_state(bool b)
|
void Document::set_needs_to_refresh_scroll_state(bool b)
|
||||||
{
|
{
|
||||||
if (auto* paintable = this->paintable())
|
if (auto* paintable = this->paintable())
|
||||||
|
|
|
@ -796,6 +796,11 @@ public:
|
||||||
Vector<GC::Ref<HTML::HTMLElement>> const& showing_auto_popover_list() const { return m_showing_auto_popover_list; }
|
Vector<GC::Ref<HTML::HTMLElement>> const& showing_auto_popover_list() const { return m_showing_auto_popover_list; }
|
||||||
Vector<GC::Ref<HTML::HTMLElement>> const& showing_hint_popover_list() const { return m_showing_hint_popover_list; }
|
Vector<GC::Ref<HTML::HTMLElement>> const& showing_hint_popover_list() const { return m_showing_hint_popover_list; }
|
||||||
|
|
||||||
|
GC::Ptr<HTML::HTMLElement> topmost_auto_or_hint_popover();
|
||||||
|
|
||||||
|
void set_popover_pointerdown_target(GC::Ptr<HTML::HTMLElement> target) { m_popover_pointerdown_target = target; }
|
||||||
|
GC::Ptr<HTML::HTMLElement> popover_pointerdown_target() { return m_popover_pointerdown_target; }
|
||||||
|
|
||||||
size_t transition_generation() const { return m_transition_generation; }
|
size_t transition_generation() const { return m_transition_generation; }
|
||||||
|
|
||||||
// Does document represent an embedded svg img
|
// Does document represent an embedded svg img
|
||||||
|
@ -1203,6 +1208,7 @@ private:
|
||||||
Vector<GC::Ref<HTML::HTMLElement>> m_showing_auto_popover_list;
|
Vector<GC::Ref<HTML::HTMLElement>> m_showing_auto_popover_list;
|
||||||
Vector<GC::Ref<HTML::HTMLElement>> m_showing_hint_popover_list;
|
Vector<GC::Ref<HTML::HTMLElement>> m_showing_hint_popover_list;
|
||||||
|
|
||||||
|
GC::Ptr<HTML::HTMLElement> m_popover_pointerdown_target;
|
||||||
// https://dom.spec.whatwg.org/#document-allow-declarative-shadow-roots
|
// https://dom.spec.whatwg.org/#document-allow-declarative-shadow-roots
|
||||||
bool m_allow_declarative_shadow_roots { false };
|
bool m_allow_declarative_shadow_roots { false };
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <LibWeb/HTML/HTMLElement.h>
|
#include <LibWeb/HTML/HTMLElement.h>
|
||||||
#include <LibWeb/HTML/HTMLLabelElement.h>
|
#include <LibWeb/HTML/HTMLLabelElement.h>
|
||||||
#include <LibWeb/HTML/HTMLParagraphElement.h>
|
#include <LibWeb/HTML/HTMLParagraphElement.h>
|
||||||
|
#include <LibWeb/HTML/PopoverInvokerElement.h>
|
||||||
#include <LibWeb/HTML/ToggleEvent.h>
|
#include <LibWeb/HTML/ToggleEvent.h>
|
||||||
#include <LibWeb/HTML/Window.h>
|
#include <LibWeb/HTML/Window.h>
|
||||||
#include <LibWeb/Infra/CharacterTypes.h>
|
#include <LibWeb/Infra/CharacterTypes.h>
|
||||||
|
@ -1771,6 +1772,34 @@ GC::Ptr<HTMLElement> HTMLElement::nearest_inclusive_open_popover()
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/popover.html#nearest-inclusive-target-popover-for-invoker
|
||||||
|
GC::Ptr<HTMLElement> HTMLElement::nearest_inclusive_target_popover_for_invoker()
|
||||||
|
{
|
||||||
|
// To find the nearest inclusive target popover for invoker given a Node node:
|
||||||
|
|
||||||
|
// 1. Let currentNode be node.
|
||||||
|
auto* current_node = this;
|
||||||
|
|
||||||
|
// 2. While currentNode is not null:
|
||||||
|
while (current_node) {
|
||||||
|
// 1. Let targetPopover be currentNode's popover target element.
|
||||||
|
auto target_popover = PopoverInvokerElement::get_the_popover_target_element(*current_node);
|
||||||
|
|
||||||
|
// AD-HOC: This also allows hint popovers.
|
||||||
|
// See nearest_inclusive_open_popover above.
|
||||||
|
// 2. If targetPopover is not null and targetPopover's popover attribute is in the auto state and targetPopover's popover visibility state is showing, then return targetPopover.
|
||||||
|
if (target_popover) {
|
||||||
|
if (target_popover->popover().has_value() && target_popover->popover().value().is_one_of("auto", "hint") && target_popover->popover_visibility_state() == PopoverVisibilityState::Showing)
|
||||||
|
return target_popover;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Set currentNode to currentNode's ancestor in the flat tree.
|
||||||
|
current_node = current_node->shadow_including_first_ancestor_of_type<HTMLElement>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
{
|
{
|
||||||
|
@ -1809,6 +1838,108 @@ void HTMLElement::queue_a_popover_toggle_event_task(String old_state, String new
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/popover.html#light-dismiss-open-popovers
|
||||||
|
void HTMLElement::light_dismiss_open_popovers(UIEvents::PointerEvent const& event, GC::Ptr<DOM::Node> const target)
|
||||||
|
{
|
||||||
|
// To light dismiss open popovers, given a PointerEvent event:
|
||||||
|
|
||||||
|
// 1. Assert: event's isTrusted attribute is true.
|
||||||
|
VERIFY(event.is_trusted());
|
||||||
|
|
||||||
|
// 2. Let target be event's target.
|
||||||
|
// FIXME: The event's target hasn't been initialized yet, so it's passed as an argument
|
||||||
|
|
||||||
|
// 3. Let document be target's node document.
|
||||||
|
auto& document = target->document();
|
||||||
|
|
||||||
|
// 4. Let topmostPopover be the result of running topmost auto popover given document.
|
||||||
|
auto topmost_popover = document.topmost_auto_or_hint_popover();
|
||||||
|
|
||||||
|
// 5. If topmostPopover is null, then return.
|
||||||
|
if (!topmost_popover)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 6. If event's type is "pointerdown", then: set document's popover pointerdown target to the result of running topmost clicked popover given target.
|
||||||
|
if (event.type() == UIEvents::EventNames::pointerdown)
|
||||||
|
document.set_popover_pointerdown_target(topmost_clicked_popover(target));
|
||||||
|
|
||||||
|
// 7. If event's type is "pointerup", then:
|
||||||
|
if (event.type() == UIEvents::EventNames::pointerup) {
|
||||||
|
// 1. Let ancestor be the result of running topmost clicked popover given target.
|
||||||
|
auto const ancestor = topmost_clicked_popover(target);
|
||||||
|
|
||||||
|
// 2. Let sameTarget be true if ancestor is document's popover pointerdown target.
|
||||||
|
bool const same_target = ancestor == document.popover_pointerdown_target();
|
||||||
|
|
||||||
|
// 3. Set document's popover pointerdown target to null.
|
||||||
|
document.set_popover_pointerdown_target({});
|
||||||
|
|
||||||
|
// 4. If ancestor is null, then set ancestor to document.
|
||||||
|
Variant<GC::Ptr<HTMLElement>, GC::Ptr<DOM::Document>> ancestor_or_document = ancestor;
|
||||||
|
if (!ancestor)
|
||||||
|
ancestor_or_document = GC::Ptr(document);
|
||||||
|
|
||||||
|
// 5. If sameTarget is true, then run hide all popovers until given ancestor, false, and true.
|
||||||
|
if (same_target)
|
||||||
|
hide_all_popovers_until(ancestor_or_document, FocusPreviousElement::No, FireEvents::Yes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/popover.html#get-the-popover-stack-position
|
||||||
|
size_t HTMLElement::popover_stack_position()
|
||||||
|
{
|
||||||
|
// To get the popover stack position, given an HTML element popover:
|
||||||
|
|
||||||
|
// 1. Let hintList be popover's node document's showing hint popover list.
|
||||||
|
auto const& hint_list = document().showing_hint_popover_list();
|
||||||
|
|
||||||
|
// 2. Let autoList be popover's node document's showing auto popover list.
|
||||||
|
auto const& auto_list = document().showing_auto_popover_list();
|
||||||
|
|
||||||
|
// 3. If popover is in hintList, then return the index of popover in hintList + the size of autoList + 1.
|
||||||
|
if (hint_list.contains_slow(GC::Ref(*this)))
|
||||||
|
return hint_list.find_first_index(GC::Ref(*this)).value() + auto_list.size() + 1;
|
||||||
|
|
||||||
|
// 4. If popover is in autoList, then return the index of popover in autoList + 1.
|
||||||
|
if (auto_list.contains_slow(GC::Ref(*this)))
|
||||||
|
return auto_list.find_first_index(GC::Ref(*this)).value() + 1;
|
||||||
|
|
||||||
|
// 5. Return 0.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/popover.html#topmost-clicked-popover
|
||||||
|
GC::Ptr<HTMLElement> HTMLElement::topmost_clicked_popover(GC::Ptr<DOM::Node> node)
|
||||||
|
{
|
||||||
|
// To find the topmost clicked popover, given a Node node:
|
||||||
|
|
||||||
|
GC::Ptr<HTMLElement> nearest_element = as_if<HTMLElement>(*node);
|
||||||
|
if (!nearest_element)
|
||||||
|
nearest_element = node->shadow_including_first_ancestor_of_type<HTMLElement>();
|
||||||
|
|
||||||
|
if (!nearest_element)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 1. Let clickedPopover be the result of running nearest inclusive open popover given node.
|
||||||
|
auto clicked_popover = nearest_element->nearest_inclusive_open_popover();
|
||||||
|
|
||||||
|
// 2. Let invokerPopover be the result of running nearest inclusive target popover for invoker given node.
|
||||||
|
auto invoker_popover = nearest_element->nearest_inclusive_target_popover_for_invoker();
|
||||||
|
|
||||||
|
if (!clicked_popover)
|
||||||
|
return invoker_popover;
|
||||||
|
|
||||||
|
if (!invoker_popover)
|
||||||
|
return clicked_popover;
|
||||||
|
|
||||||
|
// 3. If the result of getting the popover stack position given clickedPopover is greater than the result of getting the popover stack position given invokerPopover, then return clickedPopover.
|
||||||
|
if (clicked_popover->popover_stack_position() > invoker_popover->popover_stack_position())
|
||||||
|
return clicked_popover;
|
||||||
|
|
||||||
|
// 4. Return invokerPopover.
|
||||||
|
return invoker_popover;
|
||||||
|
}
|
||||||
|
|
||||||
void HTMLElement::did_receive_focus()
|
void HTMLElement::did_receive_focus()
|
||||||
{
|
{
|
||||||
if (!first_is_one_of(m_content_editable_state, ContentEditableState::True, ContentEditableState::PlaintextOnly))
|
if (!first_is_one_of(m_content_editable_state, ContentEditableState::True, ContentEditableState::PlaintextOnly))
|
||||||
|
|
|
@ -149,6 +149,8 @@ public:
|
||||||
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);
|
||||||
|
|
||||||
|
static void light_dismiss_open_popovers(UIEvents::PointerEvent const&, GC::Ptr<DOM::Node>);
|
||||||
|
|
||||||
bool is_inert() const { return m_inert; }
|
bool is_inert() const { return m_inert; }
|
||||||
|
|
||||||
virtual bool is_valid_invoker_command(String&) { return false; }
|
virtual bool is_valid_invoker_command(String&) { return false; }
|
||||||
|
@ -197,7 +199,10 @@ private:
|
||||||
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);
|
||||||
GC::Ptr<HTMLElement> nearest_inclusive_open_popover();
|
GC::Ptr<HTMLElement> nearest_inclusive_open_popover();
|
||||||
|
GC::Ptr<HTMLElement> nearest_inclusive_target_popover_for_invoker();
|
||||||
static void close_entire_popover_list(Vector<GC::Ref<HTMLElement>> const& popover_list, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
static void close_entire_popover_list(Vector<GC::Ref<HTMLElement>> const& popover_list, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
||||||
|
static GC::Ptr<HTMLElement> topmost_clicked_popover(GC::Ptr<DOM::Node> node);
|
||||||
|
size_t popover_stack_position();
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/custom-elements.html#attached-internals
|
// https://html.spec.whatwg.org/multipage/custom-elements.html#attached-internals
|
||||||
GC::Ptr<ElementInternals> m_attached_internals;
|
GC::Ptr<ElementInternals> m_attached_internals;
|
||||||
|
|
|
@ -78,8 +78,9 @@ GC::Ptr<HTMLElement> PopoverInvokerElement::get_the_popover_target_element(GC::R
|
||||||
{
|
{
|
||||||
// To get the popover target element given a Node node, perform the following steps. They return an HTML element or null.
|
// 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());
|
auto const* form_associated_element = as_if<FormAssociatedElement>(*node);
|
||||||
VERIFY(form_associated_element);
|
if (!form_associated_element)
|
||||||
|
return {};
|
||||||
|
|
||||||
// 1. If node is not a button, then return null.
|
// 1. If node is not a button, then return null.
|
||||||
if (!form_associated_element->is_button())
|
if (!form_associated_element->is_button())
|
||||||
|
|
|
@ -21,6 +21,8 @@ public:
|
||||||
|
|
||||||
void set_popover_target_element(GC::Ptr<DOM::Element> value) { m_popover_target_element = value; }
|
void set_popover_target_element(GC::Ptr<DOM::Element> value) { m_popover_target_element = value; }
|
||||||
|
|
||||||
|
static GC::Ptr<HTMLElement> get_the_popover_target_element(GC::Ref<DOM::Node> node);
|
||||||
|
|
||||||
static void popover_target_activation_behaviour(GC::Ref<DOM::Node> node, GC::Ref<DOM::Node> event_target);
|
static void popover_target_activation_behaviour(GC::Ref<DOM::Node> node, GC::Ref<DOM::Node> event_target);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -29,8 +31,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GC::Ptr<DOM::Element> m_popover_target_element;
|
GC::Ptr<DOM::Element> m_popover_target_element;
|
||||||
|
|
||||||
static GC::Ptr<HTMLElement> get_the_popover_target_element(GC::Ref<DOM::Node> node);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,6 +339,17 @@ static void set_user_selection(GC::Ptr<DOM::Node> anchor_node, unsigned anchor_o
|
||||||
(void)selection->set_base_and_extent(*anchor_node, anchor_offset, *focus_node, focus_offset);
|
(void)selection->set_base_and_extent(*anchor_node, anchor_offset, *focus_node, focus_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interactive-elements.html#run-light-dismiss-activities
|
||||||
|
static void light_dismiss_activities(UIEvents::PointerEvent const& event, const GC::Ptr<DOM::Node> target)
|
||||||
|
{
|
||||||
|
// To run light dismiss activities, given a PointerEvent event:
|
||||||
|
|
||||||
|
// 1. Run light dismiss open popovers with event.
|
||||||
|
HTML::HTMLElement::light_dismiss_open_popovers(event, target);
|
||||||
|
|
||||||
|
// FIXME: 2. Run light dismiss open dialogs with event.
|
||||||
|
}
|
||||||
|
|
||||||
EventHandler::EventHandler(Badge<HTML::Navigable>, HTML::Navigable& navigable)
|
EventHandler::EventHandler(Badge<HTML::Navigable>, HTML::Navigable& navigable)
|
||||||
: m_navigable(navigable)
|
: m_navigable(navigable)
|
||||||
, m_drag_and_drop_event_handler(make<DragAndDropEventHandler>())
|
, m_drag_and_drop_event_handler(make<DragAndDropEventHandler>())
|
||||||
|
@ -481,7 +492,9 @@ EventResult EventHandler::handle_mouseup(CSSPixelPoint viewport_position, CSSPix
|
||||||
|
|
||||||
auto page_offset = compute_mouse_event_page_offset(viewport_position);
|
auto page_offset = compute_mouse_event_page_offset(viewport_position);
|
||||||
auto offset = compute_mouse_event_offset(page_offset, *layout_node->first_paintable());
|
auto offset = compute_mouse_event_offset(page_offset, *layout_node->first_paintable());
|
||||||
node->dispatch_event(UIEvents::PointerEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::pointerup, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
|
auto pointer_event = UIEvents::PointerEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::pointerup, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors();
|
||||||
|
light_dismiss_activities(pointer_event, node);
|
||||||
|
node->dispatch_event(pointer_event);
|
||||||
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mouseup, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
|
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mouseup, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
|
||||||
handled_event = EventResult::Handled;
|
handled_event = EventResult::Handled;
|
||||||
|
|
||||||
|
@ -629,7 +642,9 @@ EventResult EventHandler::handle_mousedown(CSSPixelPoint viewport_position, CSSP
|
||||||
m_mousedown_target = node.ptr();
|
m_mousedown_target = node.ptr();
|
||||||
auto page_offset = compute_mouse_event_page_offset(viewport_position);
|
auto page_offset = compute_mouse_event_page_offset(viewport_position);
|
||||||
auto offset = compute_mouse_event_offset(page_offset, *layout_node->first_paintable());
|
auto offset = compute_mouse_event_offset(page_offset, *layout_node->first_paintable());
|
||||||
node->dispatch_event(UIEvents::PointerEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::pointerdown, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
|
auto pointer_event = UIEvents::PointerEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::pointerdown, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors();
|
||||||
|
light_dismiss_activities(pointer_event, node);
|
||||||
|
node->dispatch_event(pointer_event);
|
||||||
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mousedown, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
|
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mousedown, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
Tests/LibWeb/Text/expected/popover/popover-light-dismiss.txt
Normal file
26
Tests/LibWeb/Text/expected/popover/popover-light-dismiss.txt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
p1 open: false (should be false)
|
||||||
|
p2 open: false (should be false)
|
||||||
|
Opening p1
|
||||||
|
p1 open: true (should be true)
|
||||||
|
p2 open: false (should be false)
|
||||||
|
Opening p2
|
||||||
|
p1 open: true (should be true)
|
||||||
|
p2 open: true (should be true)
|
||||||
|
Clicking p2
|
||||||
|
p1 open: true (should be true)
|
||||||
|
p2 open: true (should be true)
|
||||||
|
Clicking p1
|
||||||
|
p1 open: true (should be true)
|
||||||
|
p2 open: false (should be false)
|
||||||
|
Clicking p1's show invoker
|
||||||
|
p1 open: true (should be true)
|
||||||
|
p2 open: false (should be false)
|
||||||
|
Clicking p1's toggle invoker
|
||||||
|
p1 open: false (should be false)
|
||||||
|
p2 open: false (should be false)
|
||||||
|
Re-opening both popovers
|
||||||
|
p1 open: true (should be true)
|
||||||
|
p2 open: true (should be true)
|
||||||
|
Clicking outside both popovers
|
||||||
|
p1 open: false (should be false)
|
||||||
|
p2 open: false (should be false)
|
100
Tests/LibWeb/Text/input/popover/popover-light-dismiss.html
Normal file
100
Tests/LibWeb/Text/input/popover/popover-light-dismiss.html
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<style>
|
||||||
|
[popover] {
|
||||||
|
/* Position most popovers at the bottom-right, out of the way */
|
||||||
|
inset: auto;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[popover]::backdrop {
|
||||||
|
/* This should *not* affect anything: */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p1 {
|
||||||
|
top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p2 {
|
||||||
|
top: 120px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<button id=b1t popovertarget='p1'>Popover 1</button>
|
||||||
|
<button id=b1s popovertarget='p1' popovertargetaction=show>Popover 1</button>
|
||||||
|
<span id=outside>Outside all popovers</span>
|
||||||
|
<div popover id=p1>
|
||||||
|
<span id=inside1>Inside popover 1</span>
|
||||||
|
<button id=b2 popovertarget='p2' popovertargetaction=show>Popover 2</button>
|
||||||
|
<span id=inside1after>Inside popover 1 after button</span>
|
||||||
|
<div popover id=p2>
|
||||||
|
<span id=inside2>Inside popover 2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
click = function (element) {
|
||||||
|
const boundingRect = element.getBoundingClientRect();
|
||||||
|
const centerPoint = {
|
||||||
|
x: boundingRect.left + boundingRect.width / 2,
|
||||||
|
y: boundingRect.top + boundingRect.height / 2
|
||||||
|
};
|
||||||
|
internals.click(centerPoint.x, centerPoint.y);
|
||||||
|
};
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be false)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be false)");
|
||||||
|
|
||||||
|
println("Opening p1");
|
||||||
|
click(b1t);
|
||||||
|
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be true)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be false)");
|
||||||
|
|
||||||
|
println("Opening p2");
|
||||||
|
click(b2);
|
||||||
|
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be true)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be true)");
|
||||||
|
|
||||||
|
println("Clicking p2");
|
||||||
|
click(p2);
|
||||||
|
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be true)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be true)");
|
||||||
|
|
||||||
|
println("Clicking p1");
|
||||||
|
click(p1);
|
||||||
|
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be true)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be false)");
|
||||||
|
|
||||||
|
println("Clicking p1's show invoker");
|
||||||
|
click(b1s);
|
||||||
|
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be true)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be false)");
|
||||||
|
|
||||||
|
println("Clicking p1's toggle invoker");
|
||||||
|
click(b1t);
|
||||||
|
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be false)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be false)");
|
||||||
|
|
||||||
|
println("Re-opening both popovers");
|
||||||
|
click(b1t);
|
||||||
|
click(b2);
|
||||||
|
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be true)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be true)");
|
||||||
|
|
||||||
|
println("Clicking outside both popovers");
|
||||||
|
click(outside);
|
||||||
|
|
||||||
|
println("p1 open: " + p1.matches(":popover-open") + " (should be false)");
|
||||||
|
println("p2 open: " + p2.matches(":popover-open") + " (should be false)");
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue