Browse Source

LibWeb: Add PopOverInvokerElement and use it in HTMLButtonElement

The popoverTargetElement seems to be one of the only cases of a
reflected Element? attribute in the HTML spec, the behaviour of which
is specified in section 2.6.1.

Buttons can't actually toggle popovers yet because showing/hiding
popovers is not implemented yet.
Nathan van der Kamp 8 months ago
parent
commit
a276cf2d5e

+ 1 - 0
Libraries/LibWeb/Forward.h

@@ -513,6 +513,7 @@ class PageTransitionEvent;
 class Path2D;
 class Plugin;
 class PluginArray;
+class PopoverInvokerElement;
 class PromiseRejectionEvent;
 class RadioNodeList;
 class SelectedFile;

+ 2 - 0
Libraries/LibWeb/HTML/AttributeNames.h

@@ -238,6 +238,8 @@ namespace AttributeNames {
     __ENUMERATE_HTML_ATTRIBUTE(placeholder)                \
     __ENUMERATE_HTML_ATTRIBUTE(playsinline)                \
     __ENUMERATE_HTML_ATTRIBUTE(popover)                    \
+    __ENUMERATE_HTML_ATTRIBUTE(popovertarget)              \
+    __ENUMERATE_HTML_ATTRIBUTE(popovertargetaction)        \
     __ENUMERATE_HTML_ATTRIBUTE(poster)                     \
     __ENUMERATE_HTML_ATTRIBUTE(preload)                    \
     __ENUMERATE_HTML_ATTRIBUTE(readonly)                   \

+ 6 - 0
Libraries/LibWeb/HTML/HTMLButtonElement.cpp

@@ -45,6 +45,12 @@ WebIDL::ExceptionOr<void> HTMLButtonElement::set_type(String const& type)
     return set_attribute(HTML::AttributeNames::type, type);
 }
 
+void HTMLButtonElement::visit_edges(Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    PopoverInvokerElement::visit_edges(visitor);
+}
+
 // https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex
 i32 HTMLButtonElement::default_tab_index_value() const
 {

+ 5 - 1
Libraries/LibWeb/HTML/HTMLButtonElement.h

@@ -9,6 +9,7 @@
 #include <LibWeb/ARIA/Roles.h>
 #include <LibWeb/HTML/FormAssociatedElement.h>
 #include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/HTML/PopoverInvokerElement.h>
 
 namespace Web::HTML {
 
@@ -19,7 +20,8 @@ namespace Web::HTML {
 
 class HTMLButtonElement final
     : public HTMLElement
-    , public FormAssociatedElement {
+    , public FormAssociatedElement
+    , public PopoverInvokerElement {
     WEB_PLATFORM_OBJECT(HTMLButtonElement, HTMLElement);
     GC_DECLARE_ALLOCATOR(HTMLButtonElement);
     FORM_ASSOCIATED_ELEMENT(HTMLElement, HTMLButtonElement)
@@ -73,6 +75,8 @@ public:
     virtual void activation_behavior(DOM::Event const&) override;
 
 private:
+    virtual void visit_edges(Visitor&) override;
+
     virtual bool is_html_button_element() const override { return true; }
 
     HTMLButtonElement(DOM::Document&, DOM::QualifiedName);

+ 2 - 1
Libraries/LibWeb/HTML/HTMLButtonElement.idl

@@ -1,5 +1,6 @@
 #import <HTML/HTMLElement.idl>
 #import <HTML/HTMLFormElement.idl>
+#import <HTML/PopoverInvokerElement.idl>
 
 [MissingValueDefault=submit, InvalidValueDefault=submit]
 enum ButtonTypeState {
@@ -33,4 +34,4 @@ interface HTMLButtonElement : HTMLElement {
 
     readonly attribute NodeList labels;
 };
-// FIXME: HTMLButtonElement includes PopoverInvokerElement;
+HTMLButtonElement includes PopoverInvokerElement;

+ 34 - 0
Libraries/LibWeb/HTML/PopoverInvokerElement.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024, Nathan van der Kamp <nbvdkamp@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibGC/Ptr.h>
+#include <LibJS/Heap/Cell.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::HTML {
+
+class PopoverInvokerElement {
+
+public:
+    PopoverInvokerElement() { }
+
+    GC::Ptr<DOM::Element> get_popover_target_element() { return m_popover_target_element; }
+
+    void set_popover_target_element(GC::Ptr<DOM::Element> value) { m_popover_target_element = value; }
+
+protected:
+    void visit_edges(JS::Cell::Visitor& visitor)
+    {
+        visitor.visit(m_popover_target_element);
+    }
+
+private:
+    GC::Ptr<DOM::Element> m_popover_target_element;
+};
+
+}

+ 13 - 0
Libraries/LibWeb/HTML/PopoverInvokerElement.idl

@@ -0,0 +1,13 @@
+// https://html.spec.whatwg.org/multipage/popover.html#attr-popovertargetaction
+[MissingValueDefault=toggle, InvalidValueDefault=toggle]
+enum PopoverTargetActionAttribute {
+    "toggle",
+    "show",
+    "hide"
+};
+
+// https://html.spec.whatwg.org/multipage/popover.html#popoverinvokerelement
+interface mixin PopoverInvokerElement {
+    [Reflect=popovertarget, CEReactions] attribute Element? popoverTargetElement;
+    [Reflect=popovertargetaction, Enumerated=PopoverTargetActionAttribute, CEReactions] attribute DOMString popoverTargetAction;
+};

+ 5 - 0
Tests/LibWeb/Text/expected/HTML/popover-invoker-attributes.txt

@@ -0,0 +1,5 @@
+PASS
+PASS
+PASS
+PASS
+PASS

+ 29 - 0
Tests/LibWeb/Text/input/HTML/popover-invoker-attributes.html

@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<button id="toggleButton" popovertarget="mypopover" popovertargetaction="toggle">Toggle the popover</button>
+<div id="mypopover" popover>Popover content</div>
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        const button = document.getElementById("toggleButton");
+        const div = document.getElementById("mypopover");
+
+        if (button.popoverTargetElement === div)
+            println("PASS");
+
+        button.popoverTargetElement = null;
+        if (button.popoverTargetElement === null)
+            println("PASS");
+        button.popoverTargetElement = div;
+        if (button.popoverTargetElement === div)
+            println("PASS");
+
+
+        if (button.popoverTargetAction === "toggle")
+            println("PASS");
+
+        button.popoverTargetAction = "invalid_value";
+
+        if (button.popoverTargetAction === "toggle")
+            println("PASS");
+    });
+</script>