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 java.util.ArrayList;
18  import java.util.List;
19  
20  import org.htmlunit.corejs.javascript.Context;
21  import org.htmlunit.corejs.javascript.Scriptable;
22  import org.htmlunit.html.DomElement;
23  import org.htmlunit.html.DomNode;
24  import org.htmlunit.javascript.JavaScriptEngine;
25  import org.htmlunit.javascript.configuration.JsxClass;
26  import org.htmlunit.javascript.configuration.JsxConstructor;
27  
28  /**
29   * A special {@link HTMLCollection} for <code>document.all</code>.
30   *
31   * @author Ronald Brill
32   * @author Ahmed Ashour
33   */
34  @JsxClass
35  public class HTMLAllCollection extends HTMLCollection {
36  
37      /**
38       * Creates an instance.
39       */
40      public HTMLAllCollection() {
41          super();
42      }
43  
44      /**
45       * JavaScript constructor.
46       */
47      @Override
48      @JsxConstructor
49      public void jsConstructor() {
50          super.jsConstructor();
51      }
52  
53      /**
54       * Creates an instance.
55       * @param parentScope parent scope
56       */
57      public HTMLAllCollection(final DomNode parentScope) {
58          super(parentScope, false);
59      }
60  
61      /**
62       * Returns the item or items corresponding to the specified index or key.
63       * @param index the index or key corresponding to the element or elements to return
64       * @return the element or elements corresponding to the specified index or key
65       * @see <a href="http://msdn.microsoft.com/en-us/library/ms536460.aspx">MSDN doc</a>
66       */
67      @Override
68      public Object item(final Object index) {
69          final double numb;
70  
71          if (index instanceof String) {
72              final String name = (String) index;
73              final Object result = namedItem(name);
74              if (null != result && !JavaScriptEngine.isUndefined(result)) {
75                  return result;
76              }
77              numb = JavaScriptEngine.toNumber(index);
78              if (Double.isNaN(numb)) {
79                  return null;
80              }
81          }
82          else {
83              numb = JavaScriptEngine.toNumber(index);
84          }
85  
86          if (numb < 0) {
87              return null;
88          }
89  
90          if (Double.isInfinite(numb) || numb != Math.floor(numb)) {
91              return null;
92          }
93  
94          final Object object = get((int) numb, this);
95          if (object == NOT_FOUND) {
96              return null;
97          }
98          return object;
99      }
100 
101     /**
102      * {@inheritDoc}
103      */
104     @Override
105     public final Scriptable namedItem(final String name) {
106         final List<DomNode> elements = getElements();
107 
108         // See if there is an element in the element array with the specified id.
109         final List<DomElement> matching = new ArrayList<>();
110 
111         for (final DomNode next : elements) {
112             if (next instanceof DomElement) {
113                 final DomElement elem = (DomElement) next;
114                 if (name.equals(elem.getAttributeDirect(DomElement.NAME_ATTRIBUTE))
115                         || name.equals(elem.getId())) {
116                     matching.add(elem);
117                 }
118             }
119         }
120 
121         if (matching.size() == 1) {
122             return getScriptableForElement(matching.get(0));
123         }
124         if (matching.isEmpty()) {
125             return null;
126         }
127 
128         // many elements => build a sub collection
129         final DomNode domNode = getDomNodeOrNull();
130         final List<DomNode> nodes = new ArrayList<>(matching);
131         final HTMLCollection collection = new HTMLCollection(domNode, nodes);
132         collection.setAvoidObjectDetection(true);
133         return collection;
134     }
135 
136     /**
137      * {@inheritDoc}
138      */
139     @Override
140     public Object call(final Context cx, final Scriptable scope, final Scriptable thisObj, final Object[] args) {
141         boolean nullIfNotFound = false;
142         if (args[0] instanceof Number) {
143             final double val = ((Number) args[0]).doubleValue();
144             if (val != (int) val) {
145                 return null;
146             }
147             if (val >= 0) {
148                 nullIfNotFound = true;
149             }
150         }
151         else {
152             final String val = JavaScriptEngine.toString(args[0]);
153             try {
154                 args[0] = Integer.parseInt(val);
155             }
156             catch (final NumberFormatException ignored) {
157                 // ignore
158             }
159         }
160 
161         final Object value = super.call(cx, scope, thisObj, args);
162         if (nullIfNotFound && JavaScriptEngine.isUndefined(value)) {
163             return null;
164         }
165         return value;
166     }
167 
168     /**
169      * {@inheritDoc}
170      */
171     @Override
172     protected boolean supportsParentheses() {
173         return true;
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
179     @Override
180     protected Object equivalentValues(final Object value) {
181         if (value == null || JavaScriptEngine.isUndefined(value)) {
182             return Boolean.TRUE;
183         }
184 
185         return super.equivalentValues(value);
186     }
187 }