diff --git a/Tests/LibWeb/Text/expected/HTML/HTMLElement-labels.txt b/Tests/LibWeb/Text/expected/HTML/HTMLElement-labels.txt new file mode 100644 index 00000000000..8138db450b5 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/HTMLElement-labels.txt @@ -0,0 +1,21 @@ +input.labels.length: 1 +input.labels[0] === label: true +input.labels always returns the same object: true +meter.labels.length: 1 +meter.labels[0] === label: true +meter.labels always returns the same object: true +output.labels.length: 1 +output.labels[0] === label: true +output.labels always returns the same object: true +progress.labels.length: 1 +progress.labels[0] === label: true +progress.labels always returns the same object: true +select.labels.length: 1 +select.labels[0] === label: true +select.labels always returns the same object: true +textarea.labels.length: 1 +textarea.labels[0] === label: true +textarea.labels always returns the same object: true +input.labels returns null if input type is hidden: true +input.labels.length after input type is changed from hidden: 1 +input.labels[0] === label after input type is changed from hidden: true diff --git a/Tests/LibWeb/Text/input/HTML/HTMLElement-labels.html b/Tests/LibWeb/Text/input/HTML/HTMLElement-labels.html new file mode 100644 index 00000000000..0c9ea9d69ae --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/HTMLElement-labels.html @@ -0,0 +1,36 @@ + + + diff --git a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl index 6a7abc6bbd6..e7b8dae094e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl @@ -31,6 +31,6 @@ interface HTMLButtonElement : HTMLElement { // FIXME: boolean reportValidity(); // FIXME: undefined setCustomValidity(DOMString error); - // FIXME: readonly attribute NodeList labels; + readonly attribute NodeList labels; }; // FIXME: HTMLButtonElement includes PopoverInvokerElement; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp index 781f1c6768f..5b50352bfe2 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -436,6 +438,25 @@ bool HTMLElement::fire_a_synthetic_pointer_event(FlyString const& type, DOM::Ele return target.dispatch_event(event); } +// https://html.spec.whatwg.org/multipage/forms.html#dom-lfe-labels-dev +JS::GCPtr HTMLElement::labels() +{ + // Labelable elements and all input elements have a live NodeList object associated with them that represents the list of label elements, in tree order, + // whose labeled control is the element in question. The labels IDL attribute of labelable elements that are not form-associated custom elements, + // and the labels IDL attribute of input elements, on getting, must return that NodeList object, and that same value must always be returned, + // unless this element is an input element whose type attribute is in the Hidden state, in which case it must instead return null. + if (!is_labelable()) + return {}; + + if (!m_labels) { + m_labels = DOM::LiveNodeList::create(realm(), root(), DOM::LiveNodeList::Scope::Descendants, [&](auto& node) { + return is(node) && verify_cast(node).control() == this; + }); + } + + return m_labels; +} + // https://html.spec.whatwg.org/multipage/interaction.html#dom-click void HTMLElement::click() { diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.h b/Userland/Libraries/LibWeb/HTML/HTMLElement.h index e68bd7717ff..0b047bf0b85 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.h @@ -68,6 +68,8 @@ public: // https://html.spec.whatwg.org/multipage/forms.html#category-label virtual bool is_labelable() const { return false; } + JS::GCPtr labels(); + virtual Optional default_role() const override; String get_an_elements_target() const; @@ -93,6 +95,8 @@ private: JS::GCPtr m_dataset; + JS::GCPtr m_labels; + enum class ContentEditableState { True, False, diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl index a41d19732f7..9c103c8c6cb 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl @@ -54,7 +54,7 @@ interface HTMLInputElement : HTMLElement { boolean reportValidity(); undefined setCustomValidity(DOMString error); - // FIXME: readonly attribute NodeList? labels; + readonly attribute NodeList? labels; undefined select(); // FIXME: attribute unsigned long? selectionStart; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl index c8ad0c262f8..0b95f310c29 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl @@ -11,5 +11,5 @@ interface HTMLMeterElement : HTMLElement { [CEReactions] attribute double low; [CEReactions] attribute double high; [CEReactions] attribute double optimum; - // FIXME: readonly attribute NodeList labels; + readonly attribute NodeList labels; }; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl index 7bb6cba0107..00f8a4efed0 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl @@ -21,5 +21,5 @@ interface HTMLOutputElement : HTMLElement { // FIXME: boolean reportValidity(); // FIXME: undefined setCustomValidity(DOMString error); - // FIXME: readonly attribute NodeList labels; + readonly attribute NodeList labels; }; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl index a2d483f0c8a..07b5ca018ef 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl @@ -8,5 +8,5 @@ interface HTMLProgressElement : HTMLElement { [CEReactions] attribute double value; [CEReactions] attribute double max; readonly attribute double position; - // FIXME: readonly attribute NodeList labels; + readonly attribute NodeList labels; }; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl index 35b06dcd91a..92cde398156 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl @@ -37,5 +37,5 @@ interface HTMLSelectElement : HTMLElement { // FIXME: boolean reportValidity(); // FIXME: undefined setCustomValidity(DOMString error); - // FIXME: readonly attribute NodeList labels; + readonly attribute NodeList labels; }; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl index 52a2276f71f..be322031bfd 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl @@ -32,7 +32,7 @@ interface HTMLTextAreaElement : HTMLElement { boolean reportValidity(); undefined setCustomValidity(DOMString error); - // FIXME: readonly attribute NodeList labels; + readonly attribute NodeList labels; // FIXME: undefined select(); attribute unsigned long selectionStart;