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.javascript.host.html;
16  
17  import static org.htmlunit.BrowserVersionFeatures.JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE;
18  import static org.htmlunit.javascript.configuration.SupportedBrowser.FF;
19  import static org.htmlunit.javascript.configuration.SupportedBrowser.FF_ESR;
20  
21  import java.io.IOException;
22  
23  import org.apache.commons.lang3.StringUtils;
24  import org.apache.commons.lang3.math.NumberUtils;
25  import org.htmlunit.html.DomElement;
26  import org.htmlunit.html.DomNode;
27  import org.htmlunit.html.HtmlCheckBoxInput;
28  import org.htmlunit.html.HtmlFileInput;
29  import org.htmlunit.html.HtmlInput;
30  import org.htmlunit.html.HtmlNumberInput;
31  import org.htmlunit.html.HtmlRadioButtonInput;
32  import org.htmlunit.html.HtmlTextInput;
33  import org.htmlunit.html.impl.SelectableTextInput;
34  import org.htmlunit.javascript.JavaScriptEngine;
35  import org.htmlunit.javascript.configuration.JsxClass;
36  import org.htmlunit.javascript.configuration.JsxConstructor;
37  import org.htmlunit.javascript.configuration.JsxFunction;
38  import org.htmlunit.javascript.configuration.JsxGetter;
39  import org.htmlunit.javascript.configuration.JsxSetter;
40  import org.htmlunit.javascript.host.DOMRectList;
41  import org.htmlunit.javascript.host.Window;
42  import org.htmlunit.javascript.host.dom.DOMException;
43  import org.htmlunit.javascript.host.dom.NodeList;
44  import org.htmlunit.javascript.host.event.Event;
45  import org.htmlunit.javascript.host.file.FileList;
46  
47  /**
48   * The JavaScript object for {@link HtmlInput}.
49   *
50   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
51   * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
52   * @author Marc Guillemot
53   * @author Chris Erskine
54   * @author Ahmed Ashour
55   * @author Daniel Gredler
56   * @author Ronald Brill
57   * @author Frank Danek
58   * @author Anton Demydenko
59   */
60  @JsxClass(domClass = HtmlInput.class)
61  public class HTMLInputElement extends HTMLElement {
62  
63      /** "Live" labels collection; has to be a member to have equality (==) working. */
64      private NodeList labels_;
65  
66      /**
67       * JavaScript constructor.
68       */
69      @Override
70      @JsxConstructor
71      public void jsConstructor() {
72          super.jsConstructor();
73      }
74  
75      /**
76       * Returns the {@code type} property.
77       * @return the {@code type} property
78       */
79      @JsxGetter
80      public String getType() {
81          return getDomNodeOrDie().getType();
82      }
83  
84      /**
85       * Sets the value of the attribute {@code type}.
86       * Note: this replace the DOM node with a new one.
87       * @param newType the new type to set
88       */
89      @JsxSetter
90      public void setType(final String newType) {
91          getDomNodeOrDie().changeType(newType, false);
92      }
93  
94      /**
95       * Sets the value of the JavaScript attribute {@code value}.
96       *
97       * @param newValue the new value
98       */
99      @JsxSetter
100     @Override
101     public void setValue(final Object newValue) {
102         if (null == newValue) {
103             getDomNodeOrDie().setValue("");
104             getDomNodeOrDie().valueModifiedByJavascript();
105             return;
106         }
107 
108         final String val = JavaScriptEngine.toString(newValue);
109         if ("file".equals(getType())) {
110             if (StringUtils.isNotEmpty(val)) {
111                 throw JavaScriptEngine.asJavaScriptException(
112                         getWindow(),
113                         "Failed to set the 'value' property on 'HTMLInputElement'.",
114                         DOMException.INVALID_STATE_ERR);
115             }
116             return;
117         }
118 
119         getDomNodeOrDie().setValue(val);
120         getDomNodeOrDie().valueModifiedByJavascript();
121     }
122 
123     /**
124      * Sets the checked property. Although this property is defined in Input it
125      * doesn't make any sense for input's other than checkbox and radio. This
126      * implementation does nothing. The implementations in Checkbox and Radio
127      * actually do the work.
128      *
129      * @param checked True if this input should have the {@code checked} attribute set
130      */
131     @JsxSetter
132     public void setChecked(final boolean checked) {
133         getDomNodeOrDie().setChecked(checked);
134     }
135 
136     /**
137      * {@inheritDoc}
138      */
139     @Override
140     public HtmlInput getDomNodeOrDie() {
141         return (HtmlInput) super.getDomNodeOrDie();
142     }
143 
144     /**
145      * Returns the value of the checked property. Although this property is
146      * defined in Input it doesn't make any sense for input's other than
147      * checkbox and radio. This implementation does nothing. The
148      * implementations in Checkbox and Radio actually do the work.
149      *
150      * @return the checked property
151      */
152     @JsxGetter
153     public boolean isChecked() {
154         return getDomNodeOrDie().isChecked();
155     }
156 
157     /**
158      * Select this element.
159      */
160     @JsxFunction
161     public void select() {
162         final HtmlInput input = getDomNodeOrDie();
163         if (input instanceof HtmlTextInput) {
164             ((HtmlTextInput) input).select();
165         }
166         // currently nothing for other input types
167     }
168 
169     /**
170      * Returns the input's default value, used if the containing form gets reset.
171      * @return the input's default value, used if the containing form gets reset
172      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533718.aspx">MSDN Documentation</a>
173      */
174     @JsxGetter
175     public String getDefaultValue() {
176         return getDomNodeOrDie().getDefaultValue();
177     }
178 
179     /**
180      * Sets the input's default value, used if the containing form gets reset.
181      * @param defaultValue the input's default value, used if the containing form gets reset
182      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533718.aspx">MSDN Documentation</a>
183      */
184     @JsxSetter
185     public void setDefaultValue(final String defaultValue) {
186         getDomNodeOrDie().setDefaultValue(defaultValue);
187     }
188 
189     /**
190      * Returns the input's default checked value, used if the containing form gets reset.
191      * @return the input's default checked value, used if the containing form gets reset
192      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533715.aspx">MSDN Documentation</a>
193      */
194     @JsxGetter
195     public boolean isDefaultChecked() {
196         return getDomNodeOrDie().isDefaultChecked();
197     }
198 
199     /**
200      * Sets the input's default checked value, used if the containing form gets reset.
201      * @param defaultChecked the input's default checked value, used if the containing form gets reset
202      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533715.aspx">MSDN Documentation</a>
203      */
204     @JsxSetter
205     public void setDefaultChecked(final boolean defaultChecked) {
206         getDomNodeOrDie().setDefaultChecked(defaultChecked);
207     }
208 
209     /**
210      * Gets the value of {@code textLength} attribute.
211      * @return the text length
212      */
213     @JsxGetter({FF, FF_ESR})
214     public int getTextLength() {
215         return getValue().length();
216     }
217 
218     /**
219      * Gets the value of {@code selectionStart} attribute.
220      * @return the selection start
221      */
222     @JsxGetter
223     public Integer getSelectionStart() {
224         final DomNode dom = getDomNodeOrDie();
225         if (dom instanceof SelectableTextInput) {
226             if ("number".equals(getType())) {
227                 return null;
228             }
229 
230             return ((SelectableTextInput) dom).getSelectionStart();
231         }
232 
233         return null;
234     }
235 
236     /**
237      * Sets the value of {@code selectionStart} attribute.
238      * @param start selection start
239      */
240     @JsxSetter
241     public void setSelectionStart(final int start) {
242         final DomNode dom = getDomNodeOrDie();
243         if (dom instanceof SelectableTextInput) {
244             if ("number".equals(getType())) {
245                 throw JavaScriptEngine.asJavaScriptException(
246                         getWindow(),
247                         "Failed to set the 'selectionStart' property"
248                                 + "from 'HTMLInputElement': "
249                                 + "The input element's type ('number') does not support selection.",
250                         DOMException.INVALID_STATE_ERR);
251             }
252 
253             ((SelectableTextInput) dom).setSelectionStart(start);
254             return;
255         }
256 
257         throw JavaScriptEngine.asJavaScriptException(
258                 getWindow(),
259                 "Failed to set the 'selectionStart' property from 'HTMLInputElement': "
260                         + "The input element's type (" + getType() + ") does not support selection.",
261                 DOMException.INVALID_STATE_ERR);
262     }
263 
264     /**
265      * Gets the value of {@code selectionEnd} attribute.
266      * @return the selection end
267      */
268     @JsxGetter
269     public Integer getSelectionEnd() {
270         final DomNode dom = getDomNodeOrDie();
271         if (dom instanceof SelectableTextInput) {
272             if ("number".equals(getType())) {
273                 return null;
274             }
275 
276             return ((SelectableTextInput) dom).getSelectionEnd();
277         }
278 
279         return null;
280     }
281 
282     /**
283      * Sets the value of {@code selectionEnd} attribute.
284      * @param end selection end
285      */
286     @JsxSetter
287     public void setSelectionEnd(final int end) {
288         final DomNode dom = getDomNodeOrDie();
289         if (dom instanceof SelectableTextInput) {
290             if ("number".equals(getType())) {
291                 throw JavaScriptEngine.asJavaScriptException(
292                         getWindow(),
293                         "Failed to set the 'selectionEnd' property"
294                                 + "from 'HTMLInputElement': "
295                                 + "The input element's type ('number') does not support selection.",
296                         DOMException.INVALID_STATE_ERR);
297             }
298 
299             ((SelectableTextInput) dom).setSelectionEnd(end);
300             return;
301         }
302 
303         throw JavaScriptEngine.asJavaScriptException(
304                 getWindow(),
305                 "Failed to set the 'selectionEnd' property from 'HTMLInputElement': "
306                         + "The input element's type (" + getType() + ") does not support selection.",
307                 DOMException.INVALID_STATE_ERR);
308     }
309 
310     /**
311      * Gets the max length.
312      * @return the max length
313      */
314     @JsxGetter
315     public int getMaxLength() {
316         final String attrValue = getDomNodeOrDie().getAttribute("maxLength");
317         return NumberUtils.toInt(attrValue, -1);
318     }
319 
320     /**
321      * Sets the value of {@code maxLength} attribute.
322      * @param length the new value
323      */
324     @JsxSetter
325     public void setMaxLength(final int length) {
326         getDomNodeOrDie().setMaxLength(length);
327     }
328 
329     /**
330      * Gets the {@code minLength}.
331      * @return the {@code minLength}
332      */
333     @JsxGetter
334     public int getMinLength() {
335         final String attrValue = getDomNodeOrDie().getAttribute("minLength");
336         return NumberUtils.toInt(attrValue, -1);
337     }
338 
339     /**
340      * Sets the value of {@code minLength} attribute.
341      * @param length the new value
342      */
343     @JsxSetter
344     public void setMinLength(final int length) {
345         getDomNodeOrDie().setMinLength(length);
346     }
347 
348     /**
349      * Gets the {@code min} property.
350      * @return the {@code min} property
351      */
352     @JsxGetter
353     public String getMin() {
354         return getDomNodeOrDie().getAttributeDirect("min");
355     }
356 
357     /**
358      * Sets the {@code min} property.
359      * @param min the {@code min} property
360      */
361     @JsxSetter
362     public void setMin(final String min) {
363         getDomNodeOrDie().setAttribute("min", min);
364     }
365 
366     /**
367      * Gets the {@code max} property.
368      * @return the {@code max} property
369      */
370     @JsxGetter
371     public String getMax() {
372         return getDomNodeOrDie().getAttributeDirect("max");
373     }
374 
375     /**
376      * Sets the {@code max} property.
377      * @param max the {@code max} property
378      */
379     @JsxSetter
380     public void setMax(final String max) {
381         getDomNodeOrDie().setAttribute("max", max);
382     }
383 
384     /**
385      * Gets the {@code step} property.
386      * @return the {@code step} property
387      */
388     @JsxGetter
389     public String getStep() {
390         return getDomNodeOrDie().getAttributeDirect("step");
391     }
392 
393     /**
394      * Sets the {@code step} property.
395      * @param step the {@code step} property
396      */
397     @JsxSetter
398     public void setStep(final String step) {
399         getDomNodeOrDie().setAttribute("step", step);
400     }
401 
402     /**
403      * Gets the value of {@code readOnly} attribute.
404      * @return the readOnly attribute
405      */
406     @JsxGetter
407     public boolean isReadOnly() {
408         return getDomNodeOrDie().isReadOnly();
409     }
410 
411     /**
412      * Sets the value of {@code readOnly} attribute.
413      * @param readOnly the new value
414      */
415     @JsxSetter
416     public void setReadOnly(final boolean readOnly) {
417         getDomNodeOrDie().setReadOnly(readOnly);
418     }
419 
420     /**
421      * Sets the selected portion of this input element.
422      * @param start the index of the first character to select
423      * @param end the index of the character after the selection
424      */
425     @JsxFunction
426     public void setSelectionRange(final int start, final int end) {
427         setSelectionStart(start);
428         setSelectionEnd(end);
429     }
430 
431     /**
432      * Returns the value of the {@code alt} property.
433      * @return the value of the {@code alt} property
434      */
435     @JsxGetter
436     public String getAlt() {
437         return getDomNodeOrDie().getAttributeDirect("alt");
438     }
439 
440     /**
441      * Returns the value of the {@code alt} property.
442      * @param alt the value
443      */
444     @JsxSetter
445     public void setAlt(final String alt) {
446         getDomNodeOrDie().setAttribute("alt", alt);
447     }
448 
449     /**
450      * Returns the value of the {@code align} property.
451      * @return the value of the {@code align} property
452      */
453     @JsxGetter
454     public String getAlign() {
455         return getAlign(true);
456     }
457 
458     /**
459      * Sets the value of the {@code align} property.
460      * @param align the value of the {@code align} property
461      */
462     @JsxSetter
463     public void setAlign(final String align) {
464         setAlign(align, false);
465     }
466 
467     /**
468      * Returns the value of the {@code src} attribute.
469      * @return the value of the {@code src} attribute
470      */
471     @JsxGetter
472     public String getSrc() {
473         return getDomNodeOrDie().getSrc();
474     }
475 
476     /**
477      * Sets the value of the {@code src} attribute.
478      * @param src the new value
479      */
480     @JsxSetter
481     public void setSrc(final String src) {
482         getDomNodeOrDie().setSrcAttribute(src);
483     }
484 
485     /**
486      * Returns the value of the JavaScript attribute {@code value}.
487      *
488      * @return the value of this attribute
489      */
490     @JsxGetter
491     @Override
492     public String getValue() {
493         final HtmlInput htmlInput = getDomNodeOrDie();
494 
495         if (htmlInput instanceof HtmlNumberInput) {
496             final String valueAttr = htmlInput.getValue();
497             if (!valueAttr.isEmpty()) {
498                 if (org.htmlunit.util.StringUtils.equalsChar('-', valueAttr)
499                         || org.htmlunit.util.StringUtils.equalsChar('+', valueAttr)) {
500                     return "";
501                 }
502 
503                 final int lastPos = valueAttr.length() - 1;
504                 if (lastPos >= 0 && valueAttr.charAt(lastPos) == '.') {
505                     if (htmlInput.hasFeature(JS_INPUT_NUMBER_DOT_AT_END_IS_DOUBLE)) {
506                         return "";
507                     }
508                 }
509                 try {
510                     Double.parseDouble(valueAttr);
511                 }
512                 catch (final NumberFormatException e) {
513                     return "";
514                 }
515             }
516         }
517 
518         return htmlInput.getValue();
519     }
520 
521     /**
522      * {@inheritDoc}
523      */
524     @Override
525     public String getAttribute(final String attributeName) {
526         final String superAttribute = super.getAttribute(attributeName);
527         if (DomElement.VALUE_ATTRIBUTE.equalsIgnoreCase(attributeName)) {
528             if ((superAttribute == null || !superAttribute.isEmpty())
529                     && getDefaultValue().isEmpty()) {
530                 return null;
531             }
532             if (!"file".equals(getType())) {
533                 return getDefaultValue();
534             }
535         }
536         return superAttribute;
537     }
538 
539     /**
540      * {@inheritDoc}
541      */
542     @Override
543     public void click() throws IOException {
544         final HtmlInput domNode = getDomNodeOrDie();
545         final boolean originalState = domNode.isChecked();
546 
547         domNode.click(false, false, false, false, false, true, false);
548 
549         final boolean newState = domNode.isChecked();
550 
551         if (originalState != newState
552                 && (domNode instanceof HtmlRadioButtonInput || domNode instanceof HtmlCheckBoxInput)) {
553             domNode.fireEvent(Event.TYPE_CHANGE);
554         }
555     }
556 
557     /**
558      * {@inheritDoc}
559      */
560     @Override
561     protected boolean isEndTagForbidden() {
562         return true;
563     }
564 
565     /**
566      * Returns the {@code required} property.
567      * @return the {@code required} property
568      */
569     @JsxGetter
570     public boolean isRequired() {
571         return getDomNodeOrDie().isRequired();
572     }
573 
574     /**
575      * Sets the {@code required} property.
576      * @param required the new value
577      */
578     @JsxSetter
579     public void setRequired(final boolean required) {
580         getDomNodeOrDie().setRequired(required);
581     }
582 
583     /**
584      * Returns the {@code size} attribute.
585      * @return the {@code size} attribute
586      */
587     @JsxGetter
588     public String getSize() {
589         return getDomNodeOrDie().getSize();
590     }
591 
592     /**
593      * Sets the {@code size} attribute.
594      * @param size the new {@code size} value
595      */
596     @JsxSetter
597     public void setSize(final String size) {
598         getDomNodeOrDie().setSize(size);
599     }
600 
601     /**
602      * Returns the {@code accept} attribute.
603      * @return the {@code accept} attribute
604      */
605     @JsxGetter
606     public String getAccept() {
607         return getDomNodeOrDie().getAccept();
608     }
609 
610     /**
611      * Sets the {@code accept} attribute.
612      * @param accept the new {@code accept} value
613      */
614     @JsxSetter
615     public void setAccept(final String accept) {
616         getDomNodeOrDie().setAccept(accept);
617     }
618 
619     /**
620      * Returns the {@code autocomplete} attribute.
621      * @return the {@code autocomplete} attribute
622      */
623     @JsxGetter
624     public String getAutocomplete() {
625         return getDomNodeOrDie().getAutocomplete();
626     }
627 
628     /**
629      * Sets the {@code autocomplete} attribute.
630      * @param autocomplete the new {@code autocomplete} value
631      */
632     @JsxSetter
633     public void setAutocomplete(final String autocomplete) {
634         getDomNodeOrDie().setAutocomplete(autocomplete);
635     }
636 
637     /**
638      * Returns the {@code files} property.
639      * @return the {@code files} property
640      */
641     @JsxGetter
642     public FileList getFiles() {
643         final HtmlInput htmlInput = getDomNodeOrDie();
644         if (htmlInput instanceof HtmlFileInput) {
645             final FileList list = new FileList(((HtmlFileInput) htmlInput).getFiles());
646             list.setParentScope(getParentScope());
647             list.setPrototype(getPrototype(list.getClass()));
648             return list;
649         }
650         return null;
651     }
652 
653     /**
654      * Returns the {@code placeholder} attribute.
655      * @return the {@code placeholder} attribute
656      */
657     @JsxGetter
658     public String getPlaceholder() {
659         return getDomNodeOrDie().getPlaceholder();
660     }
661 
662     /**
663      * Sets the {@code placeholder} attribute.
664      * @param placeholder the new {@code placeholder} value
665      */
666     @JsxSetter
667     public void setPlaceholder(final String placeholder) {
668         getDomNodeOrDie().setPlaceholder(placeholder);
669     }
670 
671     /**
672      * Returns the {@code width} property.
673      * @return the {@code width} property
674      */
675     @JsxGetter
676     public int getWidth() {
677         final String value = getDomNodeOrDie().getAttributeDirect("width");
678         final Integer intValue = HTMLCanvasElement.getValue(value);
679         if (intValue != null) {
680             return intValue;
681         }
682         return 0;
683     }
684 
685     /**
686      * Sets the {@code width} property.
687      * @param width the {@code width} property
688      */
689     @JsxSetter
690     public void setWidth(final int width) {
691         getDomNodeOrDie().setAttribute("width", Integer.toString(width));
692     }
693 
694     /**
695      * Returns the {@code height} property.
696      * @return the {@code height} property
697      */
698     @JsxGetter
699     public int getHeight() {
700         final String value = getDomNodeOrDie().getAttributeDirect("height");
701         final Integer intValue = HTMLCanvasElement.getValue(value);
702         if (intValue != null) {
703             return intValue;
704         }
705         return 0;
706     }
707 
708     /**
709      * Sets the {@code height} property.
710      * @param height the {@code height} property
711      */
712     @JsxSetter
713     public void setHeight(final int height) {
714         getDomNodeOrDie().setAttribute("height", Integer.toString(height));
715     }
716 
717     /**
718      * Returns the labels associated with the element.
719      * @return the labels associated with the element
720      */
721     @JsxGetter
722     public NodeList getLabels() {
723         if (labels_ == null) {
724             labels_ = new LabelsNodeList(getDomNodeOrDie());
725         }
726         return labels_;
727     }
728 
729     /**
730      * Checks whether the element has any constraints and whether it satisfies them.
731      * @return if the element is valid
732      */
733     @JsxFunction
734     public boolean checkValidity() {
735         return getDomNodeOrDie().isValid();
736     }
737 
738     /**
739      * {@inheritDoc}
740      */
741     @JsxGetter
742     @Override
743     public String getName() {
744         return super.getName();
745     }
746 
747     /**
748      * {@inheritDoc}
749      */
750     @JsxSetter
751     @Override
752     public void setName(final String newName) {
753         super.setName(newName);
754     }
755 
756     /**
757      * {@inheritDoc} Overridden to modify browser configurations.
758      */
759     @Override
760     @JsxGetter
761     public boolean isDisabled() {
762         return super.isDisabled();
763     }
764 
765     /**
766      * {@inheritDoc} Overridden to modify browser configurations.
767      */
768     @Override
769     @JsxSetter
770     public void setDisabled(final boolean disabled) {
771         super.setDisabled(disabled);
772     }
773 
774     /**
775      * {@inheritDoc}
776      */
777     @JsxGetter
778     @Override
779     public HTMLFormElement getForm() {
780         return super.getForm();
781     }
782 
783     /**
784      * @return a ValidityState with the validity states that this element is in.
785      */
786     @JsxGetter
787     public ValidityState getValidity() {
788         final ValidityState validityState = new ValidityState();
789         validityState.setPrototype(getPrototype(validityState.getClass()));
790         validityState.setParentScope(getParentScope());
791         validityState.setDomNode(getDomNodeOrDie());
792         return validityState;
793     }
794 
795     /**
796      * @return whether the element is a candidate for constraint validation
797      */
798     @JsxGetter
799     public boolean getWillValidate() {
800         return getDomNodeOrDie().willValidate();
801     }
802 
803     /**
804      * Sets the custom validity message for the element to the specified message.
805      * @param message the new message
806      */
807     @JsxFunction
808     public void setCustomValidity(final String message) {
809         getDomNodeOrDie().setCustomValidity(message);
810     }
811 
812     /**
813      * Returns the value of the property {@code formnovalidate}.
814      * @return the value of this property
815      */
816     @JsxGetter
817     public boolean isFormNoValidate() {
818         return getDomNodeOrDie().isFormNoValidate();
819     }
820 
821     /**
822      * Sets the value of the property {@code formnovalidate}.
823      * @param value the new value
824      */
825     @JsxSetter
826     public void setFormNoValidate(final boolean value) {
827         getDomNodeOrDie().setFormNoValidate(value);
828     }
829 
830     /**
831      * {@inheritDoc}
832      */
833     @Override
834     public DOMRectList getClientRects() {
835         if ("hidden".equals(getType())) {
836             final Window w = getWindow();
837             final DOMRectList rectList = new DOMRectList();
838             rectList.setParentScope(w);
839             rectList.setPrototype(getPrototype(rectList.getClass()));
840 
841             return rectList;
842         }
843 
844         return super.getClientRects();
845     }
846 }