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.util.StringUtils.toRootLowerCase;
18  
19  import java.io.IOException;
20  import java.util.Map;
21  
22  import org.htmlunit.Page;
23  import org.htmlunit.SgmlPage;
24  
25  /**
26   * Wrapper for the HTML element "input".
27   *
28   * @author Mike Bowler
29   * @author David K. Taylor
30   * @author Jun Chen
31   * @author Christian Sell
32   * @author Marc Guillemot
33   * @author Mike Bresnahan
34   * @author Daniel Gredler
35   * @author Ahmed Ashour
36   * @author Ronald Brill
37   * @author Frank Danek
38   */
39  public class HtmlCheckBoxInput extends HtmlInput implements LabelableElement {
40  
41      /**
42       * Value to use if no specified <code>value</code> attribute.
43       */
44      private static final String DEFAULT_VALUE = "on";
45  
46      private boolean defaultCheckedState_;
47      private boolean checkedState_;
48  
49      /**
50       * Creates an instance.
51       * If no value is specified, it is set to "on" as browsers do
52       * even if spec says that it is not allowed
53       * (<a href="http://www.w3.org/TR/REC-html40/interact/forms.html#adef-value-INPUT">W3C</a>).
54       *
55       * @param qualifiedName the qualified name of the element type to instantiate
56       * @param page the page that contains this element
57       * @param attributes the initial attributes
58       */
59      HtmlCheckBoxInput(final String qualifiedName, final SgmlPage page,
60              final Map<String, DomAttr> attributes) {
61          super(qualifiedName, page, attributes);
62  
63          if (getAttributeDirect(VALUE_ATTRIBUTE) == ATTRIBUTE_NOT_DEFINED) {
64              setRawValue(DEFAULT_VALUE);
65          }
66  
67          defaultCheckedState_ = hasAttribute(ATTRIBUTE_CHECKED);
68          checkedState_ = defaultCheckedState_;
69      }
70  
71      /**
72       * Returns {@code true} if this element is currently selected.
73       * @return {@code true} if this element is currently selected
74       */
75      @Override
76      public boolean isChecked() {
77          return checkedState_;
78      }
79  
80      /**
81       * {@inheritDoc}
82       * @see SubmittableElement#reset()
83       */
84      @Override
85      public void reset() {
86          setChecked(defaultCheckedState_);
87      }
88  
89      /**
90       * {@inheritDoc}
91       */
92      @Override
93      public Page setChecked(final boolean isChecked) {
94          checkedState_ = isChecked;
95          return executeOnChangeHandlerIfAppropriate(this);
96      }
97  
98      /**
99       * {@inheritDoc}
100      */
101     @Override
102     protected boolean doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey) throws IOException {
103         checkedState_ = !isChecked();
104         super.doClickStateUpdate(shiftKey, ctrlKey);
105         return true;
106     }
107 
108     /**
109      * {@inheritDoc}
110      */
111     @Override
112     protected void doClickFireChangeEvent() {
113         executeOnChangeHandlerIfAppropriate(this);
114     }
115 
116     /**
117      * First update the internal state of checkbox and then handle "onclick" event.
118      * {@inheritDoc}
119      */
120     @Override
121     protected boolean isStateUpdateFirst() {
122         return true;
123     }
124 
125     /**
126      * {@inheritDoc}
127      */
128     @Override
129     protected void preventDefault() {
130         checkedState_ = !checkedState_;
131     }
132 
133     /**
134      * {@inheritDoc}
135      * Also sets the value to the new default value.
136      * @see SubmittableElement#setDefaultValue(String)
137      */
138     @Override
139     public void setDefaultValue(final String defaultValue) {
140         super.setDefaultValue(defaultValue);
141         setValue(defaultValue);
142     }
143 
144     /**
145      * {@inheritDoc}
146      * Also sets the default value.
147      */
148     @Override
149     public void setValue(final String newValue) {
150         super.setValue(newValue);
151         super.setDefaultValue(newValue);
152     }
153 
154     /**
155      * {@inheritDoc}
156      * @see SubmittableElement#setDefaultChecked(boolean)
157      */
158     @Override
159     public void setDefaultChecked(final boolean defaultChecked) {
160         defaultCheckedState_ = defaultChecked;
161         setChecked(isDefaultChecked());
162     }
163 
164     /**
165      * {@inheritDoc}
166      * @see SubmittableElement#isDefaultChecked()
167      */
168     @Override
169     public boolean isDefaultChecked() {
170         return defaultCheckedState_;
171     }
172 
173     @Override
174     protected Object getInternalValue() {
175         return isChecked();
176     }
177 
178     @Override
179     void handleFocusLostValueChanged() {
180         // ignore
181     }
182 
183     /**
184      * {@inheritDoc}
185      */
186     @Override
187     protected void setAttributeNS(final String namespaceURI, final String qualifiedName, final String attributeValue,
188             final boolean notifyAttributeChangeListeners, final boolean notifyMutationObservers) {
189         final String qualifiedNameLC = toRootLowerCase(qualifiedName);
190 
191         if (VALUE_ATTRIBUTE.equals(qualifiedNameLC)) {
192             super.setAttributeNS(namespaceURI, qualifiedNameLC, attributeValue, notifyAttributeChangeListeners,
193                     notifyMutationObservers);
194             setRawValue(attributeValue);
195             return;
196         }
197 
198         if (ATTRIBUTE_CHECKED.equals(qualifiedNameLC)) {
199             checkedState_ = true;
200         }
201         super.setAttributeNS(namespaceURI, qualifiedNameLC, attributeValue, notifyAttributeChangeListeners,
202                 notifyMutationObservers);
203     }
204 
205     /**
206      * {@inheritDoc}
207      */
208     @Override
209     protected boolean propagateClickStateUpdateToParent() {
210         return false;
211     }
212 
213     @Override
214     public boolean isValueMissingValidityState() {
215         return hasAttribute(ATTRIBUTE_REQUIRED) && !isChecked();
216     }
217 }