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