View Javadoc
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 }