1 /*
2 * Copyright (c) 2002-2026 Gargoyle Software Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 * https://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package org.htmlunit.html;
16
17 import static org.htmlunit.html.HtmlForm.ATTRIBUTE_FORMNOVALIDATE;
18
19 import java.io.IOException;
20 import java.util.Map;
21
22 import org.htmlunit.SgmlPage;
23 import org.htmlunit.javascript.host.event.Event;
24 import org.htmlunit.javascript.host.event.MouseEvent;
25 import org.htmlunit.util.NameValuePair;
26 import org.htmlunit.util.StringUtils;
27
28 /**
29 * Wrapper for the HTML element "button".
30 *
31 * @author Mike Bowler
32 * @author David K. Taylor
33 * @author Christian Sell
34 * @author David D. Kilzer
35 * @author Daniel Gredler
36 * @author Ahmed Ashour
37 * @author Dmitri Zoubkov
38 * @author Ronald Brill
39 * @author Frank Danek
40 * @author Sven Strickroth
41 * @author Lai Quang Duong
42 */
43 public class HtmlButton extends HtmlElement implements DisabledElement, SubmittableElement,
44 LabelableElement, ValidatableHtmlElement {
45
46 // private static final Log LOG = LogFactory.getLog(HtmlButton.class);
47
48 /** The HTML tag represented by this element. */
49 public static final String TAG_NAME = "button";
50
51 private static final String TYPE_SUBMIT = "submit";
52 private static final String TYPE_RESET = "reset";
53 private static final String TYPE_BUTTON = "button";
54
55 private String customValidity_;
56
57 /**
58 * Creates a new instance.
59 *
60 * @param qualifiedName the qualified name of the element type to instantiate
61 * @param page the page that contains this element
62 * @param attributes the initial attributes
63 */
64 HtmlButton(final String qualifiedName, final SgmlPage page,
65 final Map<String, DomAttr> attributes) {
66 super(qualifiedName, page, attributes);
67 }
68
69 /**
70 * Sets the content of the {@code value} attribute.
71 *
72 * @param newValue the new content
73 */
74 public void setValueAttribute(final String newValue) {
75 setAttribute(VALUE_ATTRIBUTE, newValue);
76 }
77
78 /**
79 * {@inheritDoc}
80 */
81 @Override
82 protected boolean doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey) throws IOException {
83 if (!isDisabled()) {
84 final HtmlForm form = getEnclosingForm();
85 if (form != null) {
86 final String type = getType();
87 if (TYPE_BUTTON.equals(type)) {
88 return false;
89 }
90
91 if (TYPE_RESET.equals(type)) {
92 form.reset();
93 return false;
94 }
95
96 form.submit(this);
97 return false;
98 }
99 }
100
101 super.doClickStateUpdate(shiftKey, ctrlKey);
102 return false;
103 }
104
105 /**
106 * {@inheritDoc}
107 */
108 @Override
109 public NameValuePair[] getSubmitNameValuePairs() {
110 return new NameValuePair[]{new NameValuePair(getNameAttribute(), getValueAttribute())};
111 }
112
113 /**
114 * {@inheritDoc}
115 *
116 * This implementation is empty; buttons have no reset-specific behavior.
117 *
118 * @see SubmittableElement#reset()
119 */
120 @Override
121 public void reset() {
122 // Empty.
123 }
124
125 /**
126 * {@inheritDoc}
127 *
128 * This implementation is empty; buttons do not maintain a default value.
129 *
130 * @see SubmittableElement#setDefaultValue(String)
131 */
132 @Override
133 public void setDefaultValue(final String defaultValue) {
134 // Empty.
135 }
136
137 /**
138 * {@inheritDoc}
139 *
140 * This implementation returns an empty string; buttons do not maintain a default value.
141 *
142 * @see SubmittableElement#getDefaultValue()
143 */
144 @Override
145 public String getDefaultValue() {
146 return "";
147 }
148
149 /**
150 * {@inheritDoc}
151 *
152 * This implementation is empty; only checkboxes and radio buttons really care what the
153 * default checked value is.
154 *
155 * @see SubmittableElement#setDefaultChecked(boolean)
156 * @see HtmlRadioButtonInput#setDefaultChecked(boolean)
157 * @see HtmlCheckBoxInput#setDefaultChecked(boolean)
158 */
159 @Override
160 public void setDefaultChecked(final boolean defaultChecked) {
161 // Empty.
162 }
163
164 /**
165 * {@inheritDoc}
166 *
167 * This implementation returns {@code false}; only checkboxes and radio buttons really care what
168 * the default checked value is.
169 *
170 * @see SubmittableElement#isDefaultChecked()
171 * @see HtmlRadioButtonInput#isDefaultChecked()
172 * @see HtmlCheckBoxInput#isDefaultChecked()
173 */
174 @Override
175 public boolean isDefaultChecked() {
176 return false;
177 }
178
179 /**
180 * {@inheritDoc}
181 */
182 @Override
183 public boolean handles(final Event event) {
184 if (event instanceof MouseEvent) {
185 return true;
186 }
187
188 return super.handles(event);
189 }
190
191 /**
192 * Returns the value of the attribute {@code name}. Refer to the
193 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
194 * documentation for details on the use of this attribute.
195 *
196 * @return the value of the attribute {@code name} or an empty string if that attribute isn't defined
197 */
198 public final String getNameAttribute() {
199 return getAttributeDirect(NAME_ATTRIBUTE);
200 }
201
202 /**
203 * Returns the value of the attribute {@code value}. Refer to the
204 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
205 * documentation for details on the use of this attribute.
206 *
207 * @return the value of the attribute {@code value} or an empty string if that attribute isn't defined
208 */
209 public final String getValueAttribute() {
210 return getAttributeDirect(VALUE_ATTRIBUTE);
211 }
212
213 /**
214 * Returns the value of the attribute {@code type}. Refer to the
215 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
216 * documentation for details on the use of this attribute.
217 *
218 * @return the value of the attribute {@code type} or the default value if that attribute isn't defined
219 */
220 public final String getTypeAttribute() {
221 return getAttributeDirect(TYPE_ATTRIBUTE);
222 }
223
224 /**
225 * Returns the normalized button type.
226 *
227 * @return the normalized type value ({@code submit}, {@code reset}, or {@code button})
228 */
229 public String getType() {
230 final String type = getTypeAttribute();
231 if (TYPE_RESET.equalsIgnoreCase(type)) {
232 return TYPE_RESET;
233 }
234 if (TYPE_BUTTON.equalsIgnoreCase(type)) {
235 return TYPE_BUTTON;
236 }
237 return TYPE_SUBMIT;
238 }
239
240 /**
241 * Returns the value of the attribute {@code disabled}. Refer to the
242 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
243 * documentation for details on the use of this attribute.
244 *
245 * @return the value of the attribute {@code disabled} or an empty string if that attribute isn't defined
246 */
247 @Override
248 public final String getDisabledAttribute() {
249 return getAttributeDirect(ATTRIBUTE_DISABLED);
250 }
251
252 /**
253 * Returns the value of the attribute {@code tabindex}. Refer to the
254 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
255 * documentation for details on the use of this attribute.
256 *
257 * @return the value of the attribute {@code tabindex} or an empty string if that attribute isn't defined
258 */
259 public final String getTabIndexAttribute() {
260 return getAttributeDirect("tabindex");
261 }
262
263 /**
264 * Returns the value of the attribute {@code accesskey}. Refer to the
265 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
266 * documentation for details on the use of this attribute.
267 *
268 * @return the value of the attribute {@code accesskey} or an empty string if that attribute isn't defined
269 */
270 public final String getAccessKeyAttribute() {
271 return getAttributeDirect("accesskey");
272 }
273
274 /**
275 * Returns the value of the attribute {@code onfocus}. Refer to the
276 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
277 * documentation for details on the use of this attribute.
278 *
279 * @return the value of the attribute {@code onfocus} or an empty string if that attribute isn't defined
280 */
281 public final String getOnFocusAttribute() {
282 return getAttributeDirect("onfocus");
283 }
284
285 /**
286 * Returns the value of the attribute {@code onblur}. Refer to the
287 * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
288 * documentation for details on the use of this attribute.
289 *
290 * @return the value of the attribute {@code onblur} or an empty string if that attribute isn't defined
291 */
292 public final String getOnBlurAttribute() {
293 return getAttributeDirect("onblur");
294 }
295
296 /**
297 * {@inheritDoc}
298 */
299 @Override
300 public DisplayStyle getDefaultStyleDisplay() {
301 return DisplayStyle.INLINE_BLOCK;
302 }
303
304 /**
305 * {@inheritDoc}
306 *
307 * @return {@code true} to make generated XML readable as HTML.
308 */
309 @Override
310 protected boolean isEmptyXmlTagExpanded() {
311 return true;
312 }
313
314 /**
315 * {@inheritDoc}
316 */
317 @Override
318 public boolean isValid() {
319 return super.isValid() && !isCustomErrorValidityState();
320 }
321
322 /**
323 * {@inheritDoc}
324 */
325 @Override
326 public boolean willValidate() {
327 if (TYPE_RESET.equals(getType()) || TYPE_BUTTON.equals(getType())) {
328 return false;
329 }
330
331 return !isDisabled();
332 }
333
334 /**
335 * {@inheritDoc}
336 */
337 @Override
338 public String getCustomValidity() {
339 return customValidity_;
340 }
341
342 /**
343 * {@inheritDoc}
344 */
345 @Override
346 public void setCustomValidity(final String message) {
347 customValidity_ = message;
348 }
349
350 /**
351 * {@inheritDoc}
352 */
353 @Override
354 public boolean isCustomErrorValidityState() {
355 return !StringUtils.isEmptyOrNull(customValidity_);
356 }
357
358 @Override
359 public boolean isValidValidityState() {
360 return !isCustomErrorValidityState();
361 }
362
363 /**
364 * Returns whether the {@code formnovalidate} attribute is present.
365 *
366 * @return {@code true} if the {@code formnovalidate} attribute is present
367 */
368 public final boolean isFormNoValidate() {
369 return hasAttribute(ATTRIBUTE_FORMNOVALIDATE);
370 }
371
372 /**
373 * Sets the value of the attribute {@code formnovalidate}.
374 *
375 * @param noValidate the value of the attribute {@code formnovalidate}
376 */
377 public final void setFormNoValidate(final boolean noValidate) {
378 if (noValidate) {
379 setAttribute(ATTRIBUTE_FORMNOVALIDATE, ATTRIBUTE_FORMNOVALIDATE);
380 }
381 else {
382 removeAttribute(ATTRIBUTE_FORMNOVALIDATE);
383 }
384 }
385 }