Skip to content

Commit fb25863

Browse files
lpasAtkinsSJ
authored andcommitted
LibWeb: CSS selector read-write honor is_allowed_to_be_readonly
1 parent cd4ac4f commit fb25863

File tree

5 files changed

+181
-19
lines changed

5 files changed

+181
-19
lines changed

Libraries/LibWeb/CSS/SelectorEngine.cpp

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -461,23 +461,12 @@ static bool matches_read_write_pseudo_class(DOM::Element const& element)
461461
// which for the purposes of Selectors are thus considered user-alterable: [SELECTORS]
462462
// - input elements to which the readonly attribute applies, and that are mutable
463463
// (i.e. that do not have the readonly attribute specified and that are not disabled)
464-
if (is<HTML::HTMLInputElement>(element)) {
465-
auto& input_element = static_cast<HTML::HTMLInputElement const&>(element);
466-
if (input_element.has_attribute(HTML::AttributeNames::readonly))
467-
return false;
468-
if (!input_element.enabled())
469-
return false;
470-
return true;
471-
}
464+
if (auto const* input_element = as_if<HTML::HTMLInputElement>(element))
465+
return input_element->is_allowed_to_be_readonly()
466+
&& !input_element->has_attribute(HTML::AttributeNames::readonly) && input_element->enabled();
472467
// - textarea elements that do not have a readonly attribute, and that are not disabled
473-
if (is<HTML::HTMLTextAreaElement>(element)) {
474-
auto& input_element = static_cast<HTML::HTMLTextAreaElement const&>(element);
475-
if (input_element.has_attribute(HTML::AttributeNames::readonly))
476-
return false;
477-
if (!input_element.enabled())
478-
return false;
479-
return true;
480-
}
468+
if (auto const* input_element = as_if<HTML::HTMLTextAreaElement>(element))
469+
return !input_element->has_attribute(HTML::AttributeNames::readonly) && input_element->enabled();
481470
// - elements that are editing hosts or editable and are neither input elements nor textarea elements
482471
return element.is_editable_or_editing_host();
483472
}

Libraries/LibWeb/HTML/HTMLInputElement.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -873,9 +873,9 @@ void HTMLInputElement::update_text_input_shadow_tree()
873873
}
874874

875875
// https://html.spec.whatwg.org/multipage/input.html#the-input-element:attr-input-readonly-3
876-
static bool is_allowed_to_be_readonly(HTML::HTMLInputElement::TypeAttributeState state)
876+
bool HTMLInputElement::is_allowed_to_be_readonly() const
877877
{
878-
switch (state) {
878+
switch (m_type) {
879879
case HTML::HTMLInputElement::TypeAttributeState::Text:
880880
case HTML::HTMLInputElement::TypeAttributeState::Search:
881881
case HTML::HTMLInputElement::TypeAttributeState::Telephone:
@@ -3676,7 +3676,7 @@ bool HTMLInputElement::is_mutable() const
36763676

36773677
// https://html.spec.whatwg.org/multipage/input.html#the-readonly-attribute:concept-fe-mutable
36783678
// The readonly attribute is a boolean attribute that controls whether or not the user can edit the form control. When specified, the element is not mutable.
3679-
&& !(has_attribute(AttributeNames::readonly) && is_allowed_to_be_readonly(m_type));
3679+
&& !(has_attribute(AttributeNames::readonly) && is_allowed_to_be_readonly());
36803680
}
36813681

36823682
// https://html.spec.whatwg.org/multipage/rendering.html#button-layout

Libraries/LibWeb/HTML/HTMLInputElement.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ class WEB_API HTMLInputElement final
251251

252252
virtual bool is_mutable() const override;
253253
virtual bool uses_button_layout() const override;
254+
bool is_allowed_to_be_readonly() const;
254255

255256
private:
256257
HTMLInputElement(DOM::Document&, DOM::QualifiedName);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Harness status: OK
2+
3+
Found 25 tests
4+
5+
25 Pass
6+
Pass The :read-write pseudo-class must not match input elements to which the readonly attribute does not apply
7+
Pass The :read-only pseudo-class must match input elements to which the readonly attribute does not apply
8+
Pass The :read-write pseudo-class must match input elements to which the readonly attribute applies, and that are mutable
9+
Pass The :read-only pseudo-class must not match input elements to which the readonly attribute applies, and that are mutable
10+
Pass The :read-write pseudo-class must not match input elements after the readonly attribute has been added
11+
Pass The :read-only pseudo-class must match input elements after the readonly attribute has been added
12+
Pass The :read-write pseudo-class must not match input elements after the readonly attribute has been removed
13+
Pass The :read-only pseudo-class must match input elements after the readonly attribute has been removed
14+
Pass The :read-write pseudo-class must not match input elements after the disabled attribute has been added
15+
Pass The :read-only pseudo-class must match input elements after the disabled attribute has been added
16+
Pass The :read-write pseudo-class must match input elements after the disabled attribute has been removed
17+
Pass The :read-only pseudo-class must not match input elements after the disabled attribute has been removed
18+
Pass The :read-write pseudo-class must match textarea elements that do not have a readonly attribute, and that are not disabled
19+
Pass The :read-only pseudo-class must match textarea elements that have a readonly attribute, or that are disabled
20+
Pass The :read-write pseudo-class must match textarea elements after the readonly attribute has been added
21+
Pass The :read-only pseudo-class must match textarea elements after the readonly attribute has been added
22+
Pass The :read-write pseudo-class must not match textarea elements that are disabled
23+
Pass The :read-only pseudo-class must match textarea elements that are disabled
24+
Pass The :read-write pseudo-class must match elements that are editable
25+
Pass The :read-only pseudo-class must not match elements that are editable
26+
Pass The :read-write pseudo-class must match elements that are editing hosts
27+
Pass The :read-only pseudo-class must not match elements that are editing hosts
28+
Pass The :read-write pseudo-class must match elements that are inside editing hosts, but not match inputs and textareas inside that aren't
29+
Pass The :read-only pseudo-class must match form-associated custom elements
30+
Pass The :read-write pseudo-class must match form-associated contenteditable custom elements
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<!DOCTYPE html>
2+
<meta charset=utf-8>
3+
<title>Selector: pseudo-classes (:read-write, :read-only)</title>
4+
<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
5+
<script src="../../../../resources/testharness.js"></script>
6+
<script src="../../../../resources/testharnessreport.js"></script>
7+
<script src="utils.js"></script>
8+
<script>
9+
class CustomElement extends HTMLElement {
10+
static formAssociated = true;
11+
12+
constructor() {
13+
super();
14+
}
15+
}
16+
17+
window.customElements.define("custom-element", CustomElement);
18+
</script>
19+
<div id="log"></div>
20+
21+
<div id=set0>
22+
<!-- The readonly attribute does not apply to the following input types -->
23+
<input id=checkbox1 type=checkbox>
24+
<input id=hidden1 type=hidden value=abc>
25+
<input id=range1 type=range>
26+
<input id=color1 type=color>
27+
<input id=radio1 type=radio>
28+
<input id=file1 type=file>
29+
<input id=submit1 type=submit>
30+
<input id=image1 type=image>
31+
<input id=button1 type=button value="Button">
32+
<input id=reset1 type=reset>
33+
</div>
34+
35+
<div id=set1>
36+
<input id=input1>
37+
<input id=input2 readonly>
38+
<input id=input3 disabled>
39+
<input id=input4 type=checkbox>
40+
<input id=input5 type=checkbox readonly>
41+
</div>
42+
43+
<div id=set2>
44+
<textarea id=textarea1>textarea1</textarea>
45+
<textarea readonly id=textarea2>textarea2</textarea>
46+
</div>
47+
48+
<div id=set3>
49+
<textarea id=textarea3>textarea3</textarea>
50+
<textarea disabled id=textarea4>textarea4</textarea>
51+
</div>
52+
53+
<div id=set4>
54+
<p id=p1>paragraph1.</p>
55+
<p id=p2 contenteditable>paragraph2.</p>
56+
</div>
57+
58+
<div id=set5>
59+
<div id=cd1 contenteditable>
60+
<p id=p3></p>
61+
<input id=ci1 readonly>
62+
<input id=ci2 disabled>
63+
<input id=ci3>
64+
<input id=ci4>
65+
<textarea id=ct1 readonly></textarea>
66+
<textarea id=ct2 disabled></textarea>
67+
<textarea id=ct3></textarea>
68+
<textarea id=ct4></textarea>
69+
</div>
70+
</div>
71+
72+
<div id=set6>
73+
<custom-element id=ce1>content</custom-element>
74+
<custom-element id=ce2 contenteditable>content</custom-element>
75+
<custom-element id=ce3 contenteditable readonly>content</custom-element>
76+
<custom-element id=ce4 contenteditable disabled>content</custom-element>
77+
<custom-element id=ce5 contenteditable readonly disabled>content</custom-element>
78+
</div>
79+
80+
<script>
81+
testSelectorIdsMatch("#set0 :read-write", [], "The :read-write pseudo-class must not match input elements to which the readonly attribute does not apply");
82+
83+
testSelectorIdsMatch("#set0 :read-only", ["checkbox1", "hidden1", "range1", "color1", "radio1", "file1", "submit1", "image1", "button1", "reset1"], "The :read-only pseudo-class must match input elements to which the readonly attribute does not apply");
84+
85+
testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must match input elements to which the readonly attribute applies, and that are mutable");
86+
87+
testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must not match input elements to which the readonly attribute applies, and that are mutable");
88+
89+
document.getElementById("input1").setAttribute("readonly", "readonly");
90+
testSelectorIdsMatch("#set1 :read-write", [], "The :read-write pseudo-class must not match input elements after the readonly attribute has been added");
91+
92+
testSelectorIdsMatch("#set1 :read-only", ["input1", "input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the readonly attribute has been added");
93+
94+
document.getElementById("input1").removeAttribute("readonly");
95+
testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must not match input elements after the readonly attribute has been removed");
96+
97+
testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the readonly attribute has been removed");
98+
99+
document.getElementById("input1").disabled = true;
100+
testSelectorIdsMatch("#set1 :read-write", [], "The :read-write pseudo-class must not match input elements after the disabled attribute has been added");
101+
102+
testSelectorIdsMatch("#set1 :read-only", ["input1", "input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the disabled attribute has been added");
103+
104+
document.getElementById("input1").disabled = false;
105+
106+
testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must match input elements after the disabled attribute has been removed");
107+
108+
testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must not match input elements after the disabled attribute has been removed");
109+
110+
testSelectorIdsMatch("#set2 :read-write", ["textarea1"], "The :read-write pseudo-class must match textarea elements that do not have a readonly attribute, and that are not disabled");
111+
112+
testSelectorIdsMatch("#set2 :read-only", ["textarea2"], "The :read-only pseudo-class must match textarea elements that have a readonly attribute, or that are disabled");
113+
114+
document.getElementById("textarea1").setAttribute("readonly", "readonly");
115+
testSelectorIdsMatch("#set2 :read-write", [], "The :read-write pseudo-class must match textarea elements after the readonly attribute has been added");
116+
117+
testSelectorIdsMatch("#set2 :read-only", ["textarea1", "textarea2"], "The :read-only pseudo-class must match textarea elements after the readonly attribute has been added");
118+
119+
testSelectorIdsMatch("#set3 :read-write", ["textarea3"], "The :read-write pseudo-class must not match textarea elements that are disabled");
120+
121+
testSelectorIdsMatch("#set3 :read-only", ["textarea4"], "The :read-only pseudo-class must match textarea elements that are disabled");
122+
123+
testSelectorIdsMatch("#set4 :read-write", ["p2"], "The :read-write pseudo-class must match elements that are editable");
124+
125+
testSelectorIdsMatch("#set4 :read-only", ["p1"], "The :read-only pseudo-class must not match elements that are editable");
126+
127+
document.designMode = "on";
128+
129+
testSelectorIdsMatch("#set4 :read-write", ["p1", "p2"], "The :read-write pseudo-class must match elements that are editing hosts");
130+
131+
testSelectorIdsMatch("#set4 :read-only", [], "The :read-only pseudo-class must not match elements that are editing hosts");
132+
133+
document.designMode = "off";
134+
135+
testSelectorIdsMatch("#set5 :read-write", ["cd1", "p3", "ci3", "ci4", "ct3", "ct4"], "The :read-write pseudo-class must match elements that are inside editing hosts, but not match inputs and textareas inside that aren't");
136+
137+
testSelectorIdsMatch("#set6 :read-only", ["ce1"], "The :read-only pseudo-class must match form-associated custom elements");
138+
139+
testSelectorIdsMatch("#set6 :read-write", ["ce2", "ce3", "ce4", "ce5"], "The :read-write pseudo-class must match form-associated contenteditable custom elements");
140+
141+
142+
</script>

0 commit comments

Comments
 (0)