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