Skip to content

Commit 7196081

Browse files
committed
Implemented the validationMessage property for HtmlButton, HtmlTextArea, HtmlInput, HtmlObject, HtmlOutput, and HtmlSelect.
1 parent bc80b9e commit 7196081

23 files changed

Lines changed: 1053 additions & 126 deletions

src/changes/changes.xml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
<body>
1010
<release version="5.4.0" date="August xx, 2026" description="Firefox 153, Bugfixes">
11+
<action type="add" dev="rbri">
12+
Implemented the validationMessage property for HtmlButton, HtmlTextArea, HtmlInput, HtmlObject, HtmlOutput, and HtmlSelect.
13+
</action>
1114
<action type="fix" dev="rbri">
1215
Fixed HTMLFormElement.checkValidity()/reportValidity(): the aggregate check over a form's controls
1316
now correctly fires on every failing control.
@@ -23,38 +26,38 @@
2326
neko: section no longer closes select.
2427
</action>
2528
<action type="add" dev="rbri">
26-
Property HTMLFieldSetElement.type added.
29+
Added HTMLFieldSetElement.type property.
2730
</action>
2831
<action type="add" dev="rbri">
29-
Method HTMLFieldSetElement.elements() added.
32+
Property HTMLFieldSetElement.elements added.
3033
</action>
3134
<action type="fix" dev="rbri">
3235
Fixed DisabledElement.isDisabled(): a disabled fieldset no longer disables descendants of its
3336
first legend element, matching the spec's exemption for controls placed inside a fieldset's legend.
3437
</action>
3538
<action type="fix" dev="rbri">
3639
Fixed checkValidity()/reportValidity() and the CSS :valid/:invalid pseudo-classes
37-
for HtmlButtons, HtmlTextarea, HtmlInput's, HtmlObject, HtmlOutput, HtmlSelect.
40+
for HtmlButton, HtmlTextarea, HtmlInput, HtmlObject, HtmlOutput, and HtmlSelect.
3841
</action>
3942
<action type="fix" dev="rbri">
4043
HtmlFileInput.reset(): a form reset no longer tries to reconstruct a fake File from the
4144
'value' attribute; it now simply clears the selected files, without firing a change event.
4245
</action>
4346
<action type="fix" dev="rbri">
4447
HtmlFileInput.isValid(): a disabled file input is no longer incorrectly reported as invalid
45-
by checkValidity() when 'required' is set and no file is selected
48+
by checkValidity() when 'required' is set and no file is selected.
4649
</action>
4750
<action type="add" dev="rbri">
4851
Method reportValidity() support to various form controls added.
4952
</action>
5053
<action type="fix" dev="rbri">
51-
Various HtmlButton validation processing related fixed.
54+
Various fixes to HtmlButton validation processing.
5255
</action>
5356
<action type="remove" dev="rbri">
5457
Document HtmlButton's no-op implementations for reset and default value handling, and remove extraneous debug logging.
5558
</action>
5659
<action type="remove" dev="rbri">
57-
Method isReadonly() removed from HtmlButton; because there is no readonly support for this control.
60+
Method isReadonly() removed from HtmlButton because there is no readonly support for this control.
5861
</action>
5962
<action type="update" dev="rbri">
6063
DisabledElement now implements Element and provides a default implementation of isDisabled().

src/main/java/org/htmlunit/html/HtmlButton.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,14 @@ public boolean willValidate() {
331331
return !isDisabled();
332332
}
333333

334+
/**
335+
* {@inheritDoc}
336+
*/
337+
@Override
338+
public String getCustomValidity() {
339+
return customValidity_;
340+
}
341+
334342
/**
335343
* {@inheritDoc}
336344
*/

src/main/java/org/htmlunit/html/HtmlEmailInput.java

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,6 @@ public String getValue() {
6868
return raw.trim();
6969
}
7070

71-
@Override
72-
public boolean isValid() {
73-
final boolean isValid = super.isValid();
74-
if (!isValid) {
75-
return false;
76-
}
77-
78-
final String val = getValue();
79-
if (StringUtils.isNotBlank(val)) {
80-
return DEFAULT_PATTERN.matcher(val).matches();
81-
}
82-
return true;
83-
}
84-
8571
/**
8672
* {@inheritDoc}
8773
*/
@@ -105,4 +91,28 @@ protected boolean isBlankPatternValidated() {
10591
protected boolean isMinMaxLengthSupported() {
10692
return true;
10793
}
94+
95+
/**
96+
* {@inheritDoc}
97+
* Per spec, a non-empty value must match the (deliberately simplified,
98+
* RFC-5322-approximating) email pattern above. An empty value is never
99+
* a type mismatch on its own -- that's valueMissing's concern, if
100+
* 'required' is set.
101+
*/
102+
@Override
103+
public boolean hasTypeMismatchValidityState() {
104+
final String val = getValue();
105+
if (StringUtils.isBlank(val)) {
106+
return false;
107+
}
108+
return !DEFAULT_PATTERN.matcher(val).matches();
109+
}
110+
111+
/**
112+
* {@inheritDoc}
113+
*/
114+
@Override
115+
public String getTypeMismatchMessage() {
116+
return "Please enter an email address.";
117+
}
108118
}

src/main/java/org/htmlunit/html/HtmlFieldSet.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ public boolean willValidate() {
5757
return false;
5858
}
5959

60+
/**
61+
* {@inheritDoc}
62+
*/
63+
@Override
64+
public String getCustomValidity() {
65+
return customValidity_;
66+
}
67+
6068
/**
6169
* {@inheritDoc}
6270
*/

src/main/java/org/htmlunit/html/HtmlInput.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,12 @@ public boolean isValid() {
859859
return !isValueMissingValidityState()
860860
&& isCustomValidityValid()
861861
&& isMaxLengthValid() && isMinLengthValid()
862-
&& !hasPatternMismatchValidityState();
862+
&& !hasPatternMismatchValidityState()
863+
&& !hasTypeMismatchValidityState()
864+
&& !hasRangeOverflowValidityState()
865+
&& !hasRangeUnderflowValidityState()
866+
&& !isStepMismatchValidityState()
867+
&& !hasBadInputValidityState();
863868
}
864869

865870
protected boolean isCustomValidityValid() {
@@ -987,6 +992,14 @@ public boolean willValidate() {
987992
return !isDisabled() && !isReadOnly();
988993
}
989994

995+
/**
996+
* {@inheritDoc}
997+
*/
998+
@Override
999+
public String getCustomValidity() {
1000+
return customValidity_;
1001+
}
1002+
9901003
/**
9911004
* {@inheritDoc}
9921005
*/
@@ -1058,7 +1071,12 @@ public boolean isValidValidityState() {
10581071
&& !isValueMissingValidityState()
10591072
&& !isTooLongValidityState()
10601073
&& !isTooShortValidityState()
1061-
&& !hasPatternMismatchValidityState();
1074+
&& !hasPatternMismatchValidityState()
1075+
&& !hasTypeMismatchValidityState()
1076+
&& !hasRangeOverflowValidityState()
1077+
&& !hasRangeUnderflowValidityState()
1078+
&& !isStepMismatchValidityState()
1079+
&& !hasBadInputValidityState();
10621080
}
10631081

10641082
@Override

src/main/java/org/htmlunit/html/HtmlNumberInput.java

Lines changed: 131 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -129,23 +129,145 @@ public String getValue() {
129129
* {@inheritDoc}
130130
*/
131131
@Override
132-
public boolean isValid() {
133-
if (!super.isValid()) {
132+
public boolean hasRangeOverflowValidityState() {
133+
if (super.hasRangeOverflowValidityState()) {
134+
return true;
135+
}
136+
137+
String rawValue = getRawValue();
138+
if (org.htmlunit.util.StringUtils.isBlank(rawValue)) {
134139
return false;
135140
}
136141

142+
if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL)) {
143+
rawValue = rawValue.replaceAll("\\s", "");
144+
}
145+
if (!rawValue.isEmpty()) {
146+
if (org.htmlunit.util.StringUtils.equalsChar('-', rawValue)
147+
|| org.htmlunit.util.StringUtils.equalsChar('+', rawValue)) {
148+
return true;
149+
}
150+
151+
// if we have no step, the value has to be an integer
152+
if (getStep().isEmpty()) {
153+
String val = rawValue;
154+
final int lastPos = val.length() - 1;
155+
if (lastPos >= 0 && val.charAt(lastPos) == '.') {
156+
if (hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE)) {
157+
return true;
158+
}
159+
val = val.substring(0, lastPos);
160+
}
161+
if (!StringUtils.containsOnly(val, VALID_INT_CHARS)) {
162+
return true;
163+
}
164+
}
165+
166+
final BigDecimal value;
167+
try {
168+
value = new BigDecimal(rawValue);
169+
}
170+
catch (final NumberFormatException e) {
171+
return true;
172+
}
173+
174+
if (!getMax().isEmpty()) {
175+
try {
176+
final BigDecimal max = new BigDecimal(getMax());
177+
if (value.compareTo(max) > 0) {
178+
return true;
179+
}
180+
}
181+
catch (final NumberFormatException ignored) {
182+
// ignore
183+
}
184+
}
185+
}
186+
return false;
187+
}
188+
189+
/**
190+
* {@inheritDoc}
191+
*/
192+
@Override
193+
public boolean hasRangeUnderflowValidityState() {
194+
if (super.hasRangeUnderflowValidityState()) {
195+
return true;
196+
}
197+
137198
String rawValue = getRawValue();
138199
if (org.htmlunit.util.StringUtils.isBlank(rawValue)) {
200+
return false;
201+
}
202+
203+
if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL)) {
204+
rawValue = rawValue.replaceAll("\\s", "");
205+
}
206+
if (!rawValue.isEmpty()) {
207+
if (org.htmlunit.util.StringUtils.equalsChar('-', rawValue)
208+
|| org.htmlunit.util.StringUtils.equalsChar('+', rawValue)) {
209+
return true;
210+
}
211+
212+
// if we have no step, the value has to be an integer
213+
if (getStep().isEmpty()) {
214+
String val = rawValue;
215+
final int lastPos = val.length() - 1;
216+
if (lastPos >= 0 && val.charAt(lastPos) == '.') {
217+
if (hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE)) {
218+
return true;
219+
}
220+
val = val.substring(0, lastPos);
221+
}
222+
if (!StringUtils.containsOnly(val, VALID_INT_CHARS)) {
223+
return true;
224+
}
225+
}
226+
227+
final BigDecimal value;
228+
try {
229+
value = new BigDecimal(rawValue);
230+
}
231+
catch (final NumberFormatException e) {
232+
return true;
233+
}
234+
235+
if (!getMin().isEmpty()) {
236+
try {
237+
final BigDecimal min = new BigDecimal(getMin());
238+
if (value.compareTo(min) < 0) {
239+
return true;
240+
}
241+
}
242+
catch (final NumberFormatException ignored) {
243+
// ignore
244+
}
245+
}
246+
}
247+
return false;
248+
}
249+
250+
/**
251+
* {@inheritDoc}
252+
*/
253+
@Override
254+
public boolean isStepMismatchValidityState() {
255+
if (super.isStepMismatchValidityState()) {
139256
return true;
140257
}
141258

259+
String rawValue = getRawValue();
260+
if (org.htmlunit.util.StringUtils.isBlank(rawValue)) {
261+
return false;
262+
}
263+
142264
if (!hasFeature(JS_INPUT_NUMBER_ACCEPT_ALL)) {
143265
rawValue = rawValue.replaceAll("\\s", "");
144266
}
145267
if (!rawValue.isEmpty()) {
146268
if (org.htmlunit.util.StringUtils.equalsChar('-', rawValue)
147269
|| org.htmlunit.util.StringUtils.equalsChar('+', rawValue)) {
148-
return false;
270+
return true;
149271
}
150272

151273
// if we have no step, the value has to be an integer
@@ -154,12 +276,12 @@ public boolean isValid() {
154276
final int lastPos = val.length() - 1;
155277
if (lastPos >= 0 && val.charAt(lastPos) == '.') {
156278
if (hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE)) {
157-
return false;
279+
return true;
158280
}
159281
val = val.substring(0, lastPos);
160282
}
161283
if (!StringUtils.containsOnly(val, VALID_INT_CHARS)) {
162-
return false;
284+
return true;
163285
}
164286
}
165287

@@ -168,21 +290,21 @@ public boolean isValid() {
168290
value = new BigDecimal(rawValue);
169291
}
170292
catch (final NumberFormatException e) {
171-
return false;
293+
return true;
172294
}
173295

174296
if (!getMin().isEmpty()) {
175297
try {
176298
final BigDecimal min = new BigDecimal(getMin());
177299
if (value.compareTo(min) < 0) {
178-
return false;
300+
return true;
179301
}
180302

181303
if (!getStep().isEmpty()) {
182304
try {
183305
final BigDecimal step = new BigDecimal(getStep());
184306
if (value.subtract(min).abs().remainder(step).doubleValue() > 0.0) {
185-
return false;
307+
return true;
186308
}
187309
}
188310
catch (final NumberFormatException ignored) {
@@ -194,18 +316,7 @@ public boolean isValid() {
194316
// ignore
195317
}
196318
}
197-
if (!getMax().isEmpty()) {
198-
try {
199-
final BigDecimal max = new BigDecimal(getMax());
200-
if (value.compareTo(max) > 0) {
201-
return false;
202-
}
203-
}
204-
catch (final NumberFormatException ignored) {
205-
// ignore
206-
}
207-
}
208319
}
209-
return true;
320+
return false;
210321
}
211322
}

0 commit comments

Comments
 (0)