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