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;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.javascript.host.xml.XMLDocumentTest;
19  import org.htmlunit.junit.annotation.Alerts;
20  import org.htmlunit.util.MimeType;
21  import org.junit.jupiter.api.Test;
22  
23  /**
24   * Tests for {@link org.htmlunit.javascript.host.NamedNodeMap}.
25   *
26   * @author Marc Guillemot
27   * @author Daniel Gredler
28   * @author Ahmed Ashour
29   * @author Frank Danek
30   * @author Ronald Brill
31   */
32  public class NamedNodeMapTest extends WebDriverTestCase {
33  
34      /**
35       * @throws Exception if an error occurs
36       */
37      @Test
38      @Alerts({"name=f", "id=f", "foo=bar", "baz=blah"})
39      public void attributes() throws Exception {
40          final String html = DOCTYPE_HTML
41              + "<html>\n"
42              + "<head>\n"
43              + "<script>\n"
44              + LOG_TITLE_FUNCTION
45              + "  function test() {\n"
46              + "    var f = document.getElementById('f');\n"
47              + "    for(var i = 0; i < f.attributes.length; i++) {\n"
48              + "      if (f.attributes[i]) {\n"
49              + "        log(f.attributes[i].name + '=' + f.attributes[i].value);\n"
50              + "      } else {\n"
51              + "        log(i);\n"
52              + "      }\n"
53              + "    }\n"
54              + "  }\n"
55              + "</script>\n"
56              + "</head>\n"
57              + "<body onload='test()'>\n"
58              + "<form name='f' id='f' foo='bar' baz='blah'></form>\n"
59              + "</body>\n"
60              + "</html>";
61  
62          loadPageVerifyTitle2(html);
63      }
64  
65      /**
66       * @throws Exception if an error occurs
67       */
68      @Test
69      @Alerts({"name=f", "id=f", "foo=bar", "baz=blah"})
70      public void attributesForOf() throws Exception {
71          final String html = DOCTYPE_HTML
72              + "<html>\n"
73              + "<head>\n"
74              + "<script>\n"
75              + LOG_TITLE_FUNCTION
76              + "  function test() {\n"
77              + "    var f = document.getElementById('f');\n"
78              + "    for (var attr of f.attributes) {\n"
79              + "      if (attr) {\n"
80              + "        log(attr.name + '=' + attr.value);\n"
81              + "      } else {\n"
82              + "        log(i);\n"
83              + "      }\n"
84              + "    }\n"
85              + "  }\n"
86              + "</script>\n"
87              + "</head>\n"
88              + "<body onload='test()'>\n"
89              + "<form name='f' id='f' foo='bar' baz='blah'></form>\n"
90              + "</body>\n"
91              + "</html>";
92  
93          loadPageVerifyTitle2(html);
94      }
95  
96      /**
97       * @throws Exception if an error occurs
98       */
99      @Test
100     @Alerts({"name", "f", "name", "f", "name", "f", "name", "f", "null"})
101     public void getNamedItem_HTML() throws Exception {
102         final String html = DOCTYPE_HTML
103             + "<html>\n"
104             + "<head>\n"
105             + "<script>\n"
106             + LOG_TITLE_FUNCTION
107             + "  function test() {\n"
108             + "    var f = document.getElementById('f');\n"
109             + "    log(f.attributes.getNamedItem('name').nodeName);\n"
110             + "    log(f.attributes.getNamedItem('name').nodeValue);\n"
111             + "    log(f.attributes.getNamedItem('NaMe').nodeName);\n"
112             + "    log(f.attributes.getNamedItem('nAmE').nodeValue);\n"
113             + "    try {\n"
114             + "      log(f.attributes.name.nodeName);\n"
115             + "      log(f.attributes.name.nodeValue);\n"
116             + "    } catch(e) { logEx(e); }\n"
117             + "    try {\n"
118             + "      log(f.attributes.NaMe.nodeName);\n"
119             + "      log(f.attributes.nAmE.nodeValue);\n"
120             + "    } catch(e) { logEx(e); }\n"
121             + "    log(f.attributes.getNamedItem('notExisting'));\n"
122             + "  }\n"
123             + "</script>\n"
124             + "</head>\n"
125             + "<body onload='test()'>\n"
126             + "<form name='f' id='f' foo='bar' baz='blah'></form>\n"
127             + "</body>\n"
128             + "</html>";
129 
130         loadPageVerifyTitle2(html);
131     }
132 
133     /**
134      * @throws Exception if the test fails
135      */
136     @Test
137     @Alerts({"myattr", "myattr2", "myattr", "myattr2", "myattr2"})
138     public void getNamedItem_HTML_Case() throws Exception {
139         final String html = DOCTYPE_HTML
140             + "<html><head>\n"
141             + "<script>\n"
142             + LOG_TITLE_FUNCTION
143             + "  function test() {\n"
144             + "    var elem = document.getElementById('tester');\n"
145 
146             + "    var node = document.createAttribute('myAttr');\n"
147             + "    elem.attributes.setNamedItem(node);\n"
148 
149             + "    var node = document.createAttribute('myattr2');\n"
150             + "    elem.attributes.setNamedItem(node);\n"
151 
152             + "    for(var i = 0; i < elem.attributes.length; i++) {\n"
153             + "      var name = elem.attributes[i].name;\n"
154             + "      if (name.indexOf('my') === 0) { log(name); }\n"
155             + "    }\n"
156 
157             + "    var item = elem.attributes.getNamedItem('myAttr');\n"
158             + "    if (item) {\n"
159             + "      log(item.nodeName);\n"
160             + "    } else {\n"
161             + "      log('not found');\n"
162             + "    }\n"
163 
164             + "    log(elem.attributes.getNamedItem('myattr2').name);\n"
165             + "    log(elem.attributes.getNamedItem('MYaTTr2').name);\n"
166             + "  }\n"
167             + "</script></head><body onload='test()'>\n"
168             + "  <div id='tester'></div>\n"
169             + "</body></html>";
170 
171         loadPageVerifyTitle2(html);
172     }
173 
174     /**
175      * @throws Exception if an error occurs
176      */
177     @Test
178     @Alerts({"name", "y", "name", "y", "null", "undefined", "null"})
179     public void getNamedItem_XML() throws Exception {
180         final String html = DOCTYPE_HTML
181             + "<html><head>\n"
182             + "<script>\n"
183             + LOG_TITLE_FUNCTION
184             + "  function test() {\n"
185             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'second.xml'") + ";\n"
186             + "    log(doc.documentElement.attributes.getNamedItem('name').nodeName);\n"
187             + "    log(doc.documentElement.attributes.getNamedItem('name').nodeValue);\n"
188             + "    try {\n"
189             + "      log(doc.documentElement.attributes.name.nodeName);\n"
190             + "      log(doc.documentElement.attributes.name.nodeValue);\n"
191             + "    } catch(e) { logEx(e); }\n"
192             + "    log(doc.documentElement.attributes.getNamedItem('NaMe'));\n"
193             + "    log(doc.documentElement.attributes.NaMe);\n"
194             + "    log(doc.documentElement.attributes.getNamedItem('nonExistent'));\n"
195             + "  }\n"
196             + XMLDocumentTest.LOAD_XML_DOCUMENT_FROM_FILE_FUNCTION
197             + "</script></head><body onload='test()'>\n"
198             + "</body></html>";
199 
200         final String xml = "<blah name='y'></blah>";
201 
202         getMockWebConnection().setDefaultResponse(xml, MimeType.TEXT_XML);
203         loadPageVerifyTitle2(html);
204     }
205 
206     /**
207      * @throws Exception if the test fails
208      */
209     @Test
210     @Alerts("myattr")
211     public void setNamedItem_HTML() throws Exception {
212         final String html = DOCTYPE_HTML
213             + "<html><head>\n"
214             + "<script>\n"
215             + LOG_TITLE_FUNCTION
216             + "  function test() {\n"
217             + "    var node = document.createAttribute('myattr');\n"
218             + "    var elem = document.getElementById('tester');\n"
219             + "    elem.attributes.setNamedItem(node);\n"
220             + "    var item = elem.attributes.getNamedItem('myAttr');\n"
221             + "    if (item) {\n"
222             + "      log(item.nodeName);\n"
223             + "    } else {\n"
224             + "      log('not found');\n"
225             + "    }\n"
226             + "  }\n"
227             + "</script></head><body onload='test()'>\n"
228             + "  <div id='tester'></div\n"
229             + "</body></html>";
230 
231         loadPageVerifyTitle2(html);
232     }
233 
234     /**
235      * @throws Exception if the test fails
236      */
237     @Test
238     @Alerts("myAttr")
239     public void setNamedItem_XML() throws Exception {
240         final String html = DOCTYPE_HTML
241             + "<html><head>\n"
242             + "<script>\n"
243             + LOG_TITLE_FUNCTION
244             + "  function test() {\n"
245             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'" + URL_SECOND + "'") + ";\n"
246             + "    var node = doc.createAttribute('myAttr');\n"
247             + "    doc.documentElement.attributes.setNamedItem(node);\n"
248             + "    log(doc.documentElement.attributes.getNamedItem('myAttr').nodeName);\n"
249             + "  }\n"
250             + XMLDocumentTest.LOAD_XML_DOCUMENT_FROM_FILE_FUNCTION
251             + "</script></head><body onload='test()'>\n"
252             + "  <div id='tester'></div\n"
253             + "</body></html>";
254 
255         final String xml = "<test></test>";
256 
257         getMockWebConnection().setResponse(URL_SECOND, xml, MimeType.TEXT_XML);
258         loadPageVerifyTitle2(html);
259     }
260 
261     /**
262      * @throws Exception on test failure
263      */
264     @Test
265     @Alerts({"true", "[object Attr]", "true", "[object Attr]"})
266     public void has() throws Exception {
267         final String html = DOCTYPE_HTML
268             + "<html ng-app><body>\n"
269             + "<script>\n"
270             + LOG_TITLE_FUNCTION
271             + "var attributes = document.documentElement.attributes;\n"
272             + "log(0 in attributes);\n"
273             + "log(attributes[0]);\n"
274             + "log('0' in attributes);\n"
275             + "log(attributes['0']);\n"
276             + "</script>\n"
277             + "</body></html>";
278 
279         loadPageVerifyTitle2(html);
280     }
281 
282     /**
283      * @throws Exception if an error occurs
284      */
285     @Test
286     @Alerts({"div1", ""})
287     public void removeNamedItem() throws Exception {
288         final String html = DOCTYPE_HTML
289             + "<html>\n"
290             + "<body>\n"
291             + "<div id='div1' style='background-color:#FFFFC1;'>div1</div>\n"
292             + "<script>\n"
293             + LOG_TITLE_FUNCTION
294             + "  var el = document.getElementById('div1');\n"
295             + "  log(el.id);\n"
296             + "  el.attributes.removeNamedItem('id');\n"
297             + "  log(el.id);\n"
298             + "</script>\n"
299             + "</body>\n"
300             + "</html>";
301 
302         loadPageVerifyTitle2(html);
303     }
304 
305     /**
306      * @throws Exception if an error occurs
307      */
308     @Test
309     @Alerts({"undefined", "undefined", "undefined"})
310     public void unspecifiedAttributes() throws Exception {
311         final String html = DOCTYPE_HTML
312             + "<html>\n"
313             + "<head>\n"
314             + "<script>\n"
315             + LOG_TITLE_FUNCTION
316             + "  function test() {\n"
317             + "    log(document.body.attributes.language);\n"
318             + "    log(document.body.attributes.id);\n"
319             + "    log(document.body.attributes.dir);\n"
320             + "  }\n"
321             + "</script>\n"
322             + "</head>\n"
323             + "<body onload='test()'>\n"
324             + "</body>\n"
325             + "</html>";
326 
327         loadPageVerifyTitle2(html);
328     }
329 
330     /**
331      * @throws Exception if the test fails
332      */
333     @Test
334     @Alerts("media=\"screen\"")
335     public void changedAttribute() throws Exception {
336         final String html = DOCTYPE_HTML
337             + "<html><head>\n"
338 
339             + "<style id='myStyle'>my { }</style>\n"
340 
341             + "<script>\n"
342             + LOG_TITLE_FUNCTION
343             + "function doTest() {\n"
344             + "  style = document.getElementById('myStyle');\n"
345             + "  style.media = 'screen';\n"
346 
347             + "  var attributes = style.attributes;\n"
348             + "  for (var i = 0; i < attributes.length; i++) {\n"
349             + "    if (attributes[i].name === 'media') {\n"
350             + "      log(attributes[i].name + '=\"' + attributes[i].value + '\"');\n"
351             + "    }\n"
352             + "  }\n"
353             + "}\n"
354             + "</script>\n"
355             + "</head><body onload='doTest()'>\n"
356             + "</body></html>";
357 
358         loadPageVerifyTitle2(html);
359     }
360 
361     /**
362      * See issue #1716.
363      * @throws Exception if the test fails
364      */
365     @Test
366     @Alerts("<input id=\"myinput\" name=\"test_input\">")
367     public void readAccessOnlyDefinesNewAttribs() throws Exception {
368         final String html = DOCTYPE_HTML
369               + "<html>\n"
370               + "<head>\n"
371               + "</head>\n"
372               + "<body>\n"
373               + "  <input id='myinput' name='test_input' />\n"
374               + "  <script type='text/javascript'>\n"
375               + LOG_TITLE_FUNCTION
376               + "    var input = document.getElementById('myinput');\n"
377               + "    var attrs = input.attributes;\n"
378               + "    for(var i = 0; i < attrs.length; i++) {\n"
379               + "      attrs[i];\n"
380               + "    }\n"
381               + "    log(input.outerHTML);\n"
382               + "  </script>\n"
383               + "</body>\n"
384               + "</html>";
385         loadPageVerifyTitle2(html);
386     }
387 }