LibWeb: Implement the HTMLInputElement.list attribute

This returns the `HTMLDataListElement` pointed to by the `list`
content attribute.
This commit is contained in:
Tim Ledbetter 2025-03-03 15:23:48 +00:00 committed by Tim Ledbetter
parent a37315da87
commit 6178557a07
Notes: github-actions[bot] 2025-03-09 15:11:49 +00:00
7 changed files with 164 additions and 1 deletions

View file

@ -32,6 +32,7 @@
#include <LibWeb/HTML/Dates.h>
#include <LibWeb/HTML/DecodedImageData.h>
#include <LibWeb/HTML/EventNames.h>
#include <LibWeb/HTML/HTMLDataListElement.h>
#include <LibWeb/HTML/HTMLDivElement.h>
#include <LibWeb/HTML/HTMLFormElement.h>
#include <LibWeb/HTML/HTMLInputElement.h>
@ -190,6 +191,39 @@ void HTMLInputElement::set_indeterminate(bool value)
m_indeterminate = value;
}
// https://html.spec.whatwg.org/multipage/input.html#dom-input-list
GC::Ptr<HTMLDataListElement const> HTMLInputElement::list() const
{
// The list IDL attribute must return the current suggestions source element, if any, or null otherwise.
if (auto data_list_element = suggestions_source_element(); data_list_element.has_value())
return *data_list_element;
return nullptr;
}
// https://html.spec.whatwg.org/multipage/input.html#concept-input-list
Optional<GC::Ref<HTMLDataListElement const>> HTMLInputElement::suggestions_source_element() const
{
// The suggestions source element is the first element in the tree in tree order to have an ID equal to the value of the list attribute,
// if that element is a datalist element. If there is no list attribute, or if there is no element with that ID,
// or if the first element with that ID is not a datalist element, then there is no suggestions source element.
Optional<GC::Ref<HTMLDataListElement const>> result;
if (auto list_attribute_value = get_attribute(HTML::AttributeNames::list); list_attribute_value.has_value()) {
root().for_each_in_inclusive_subtree_of_type<DOM::Element>([&](auto& element) {
if (element.id() == *list_attribute_value) {
if (auto data_list_element = as_if<HTMLDataListElement>(element))
result = *data_list_element;
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
});
}
return result;
}
// https://html.spec.whatwg.org/multipage/input.html#compiled-pattern-regular-expression
Optional<Regex<ECMA262>> HTMLInputElement::compiled_pattern_regular_expression() const
{

View file

@ -106,6 +106,8 @@ public:
bool indeterminate() const { return m_indeterminate; }
void set_indeterminate(bool);
GC::Ptr<HTMLDataListElement const> list() const;
void did_pick_color(Optional<Color> picked_color, ColorPickerUpdateState state);
enum class MultipleHandling {
@ -356,6 +358,8 @@ private:
Optional<Regex<ECMA262>> compiled_pattern_regular_expression() const;
Optional<GC::Ref<HTMLDataListElement const>> suggestions_source_element() const;
Optional<DOM::DocumentLoadEventDelayer> m_load_event_delayer;
// https://html.spec.whatwg.org/multipage/input.html#dom-input-indeterminate

View file

@ -1,3 +1,4 @@
#import <HTML/HTMLDataListElement.idl>
#import <HTML/HTMLElement.idl>
#import <HTML/HTMLFormElement.idl>
#import <HTML/PopoverInvokerElement.idl>
@ -25,7 +26,7 @@ interface HTMLInputElement : HTMLElement {
[CEReactions, Reflect=formtarget] attribute DOMString formTarget;
[CEReactions] attribute unsigned long height;
attribute boolean indeterminate;
[FIXME] readonly attribute HTMLDataListElement? list;
readonly attribute HTMLDataListElement? list;
[CEReactions, Reflect] attribute DOMString max;
[CEReactions] attribute long maxLength;
[CEReactions, Reflect] attribute DOMString min;

View file

@ -0,0 +1,11 @@
Harness status: OK
Found 6 tests
6 Pass
Pass getting .list of input must return the datalist with that id
Pass getting .list of input must return null if it has no list attribute
Pass getting .list of input must return null if the list attribute is a non-datalist's id
Pass getting .list of input must return null if the list attribute is no element's id
Pass getting .list of input must return null if the list attribute is used in a non-datalist earlier than a datalist
Pass getting .list of input must return the datalist with that id even if a later non-datalist also has the id

View file

@ -0,0 +1,7 @@
Harness status: OK
Found 2 tests
2 Pass
Pass Input element's list attribute should point to the datalist element.
Pass Input element's list attribute should point to the datalist element in Shadow DOM.

View file

@ -0,0 +1,67 @@
<!DOCTYPE HTML>
<html>
<head>
<title>input list attribute</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
</head>
<body>
<p>
<h3>input_list</h3>
</p>
<hr>
<div id="log"></div>
<form method="post"
enctype="application/x-www-form-urlencoded"
action=""
name="input_form">
<datalist id="thelist">
<option value="one">one</option>
<option value="two">two</option>
</datalist>
<p id="non_datalist_first">
<datalist id="non_datalist_first">
<option value="one">one</option>
<option value="two">two</option>
</datalist>
<datalist id="datalist_first">
<option value="one">one</option>
<option value="two">two</option>
</datalist>
<p id="datalist_first">
<p><input list="thelist" id='input_with_list'></p>
<p><input id='input_without_list'></p>
<p><input list="input_with_list" id='input_with_nondatalist_list'></p>
<p><input list="not_an_id" id='input_with_missing_list'></p>
<p><input list="non_datalist_first" id='input_with_non_datalist_first'></p>
<p><input list="datalist_first" id='input_with_datalist_first'></p>
</form>
<script>
test(function() {
assert_equals(document.getElementById("input_with_list").list, document.getElementById("thelist"));
}, "getting .list of input must return the datalist with that id");
test(function() {
assert_equals(document.getElementById("input_without_list").list, null);
}, "getting .list of input must return null if it has no list attribute");
test(function() {
assert_equals(document.getElementById("input_with_nondatalist_list").list, null);
}, "getting .list of input must return null if the list attribute is a non-datalist's id");
test(function() {
assert_equals(document.getElementById("input_with_missing_list").list, null);
}, "getting .list of input must return null if the list attribute is no element's id");
test(function() {
assert_equals(document.getElementById("input_with_non_datalist_first").list, null);
}, "getting .list of input must return null if the list attribute is used in a non-datalist earlier than a datalist");
test(function() {
assert_equals(document.getElementById("input_with_datalist_first").list, document.querySelector("datalist#datalist_first"));
}, "getting .list of input must return the datalist with that id even if a later non-datalist also has the id");
</script>
</body>
</html>

View file

@ -0,0 +1,39 @@
<!doctype html>
<meta charset=utf-8>
<title>Input.list</title>
<script src=../resources/testharness.js></script>
<script src=../resources/testharnessreport.js></script>
<div id="testcontent">
<input id="input" list="datalist">
</div>
<script>
test(() => {
assert_equals(document.getElementById('input').list, null);
var dl = document.createElement("datalist");
dl.id = "datalist";
document.getElementById("testcontent").appendChild(dl);
assert_equals(document.getElementById('input').list, dl);
}, "Input element's list attribute should point to the datalist element.");
test(() => {
var host = document.createElement("div");
document.getElementById("testcontent").appendChild(host);
var sr = host.attachShadow({mode: "open"});
var input = document.createElement("input");
input.setAttribute("list", "datalist");
sr.appendChild(input);
assert_equals(input.list, null);
var dl = document.createElement("datalist");
dl.id = "datalist";
sr.appendChild(dl);
assert_equals(input.list, dl);
dl.remove();
assert_equals(input.list, null);
}, "Input element's list attribute should point to the datalist element in Shadow DOM.");
</script>