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 static java.nio.charset.StandardCharsets.ISO_8859_1;
18  import static java.nio.charset.StandardCharsets.UTF_8;
19  
20  import org.htmlunit.MockWebConnection;
21  import org.htmlunit.SimpleWebTestCase;
22  import org.htmlunit.WebClient;
23  import org.htmlunit.javascript.background.JavaScriptJobManager;
24  import org.htmlunit.junit.BrowserRunner;
25  import org.junit.Test;
26  import org.junit.runner.RunWith;
27  
28  /**
29   * Unit tests for {@link HtmlInlineFrame}.
30   *
31   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
32   * @author Ahmed Ashour
33   * @author Marc Guillemot
34   * @author Daniel Gredler
35   * @author Ronald Brill
36   */
37  @RunWith(BrowserRunner.class)
38  public class HtmlInlineFrameTest extends SimpleWebTestCase {
39  
40      /**
41       * @throws Exception if the test fails
42       */
43      @Test
44      public void setSrcAttribute() throws Exception {
45          final String firstContent = DOCTYPE_HTML
46              + "<html><head><title>First</title></head><body>\n"
47              + "<iframe id='iframe1' src='" + URL_SECOND + "'>\n"
48              + "</body></html>";
49          final String secondContent = DOCTYPE_HTML + "<html><head><title>Second</title></head><body></body></html>";
50          final String thirdContent = DOCTYPE_HTML + "<html><head><title>Third</title></head><body></body></html>";
51          final WebClient client = getWebClientWithMockWebConnection();
52  
53          final MockWebConnection webConnection = getMockWebConnection();
54          webConnection.setResponse(URL_FIRST, firstContent);
55          webConnection.setResponse(URL_SECOND, secondContent);
56          webConnection.setResponse(URL_THIRD, thirdContent);
57  
58          final HtmlPage page = client.getPage(URL_FIRST);
59          assertEquals("First", page.getTitleText());
60  
61          final HtmlInlineFrame iframe = page.getHtmlElementById("iframe1");
62          assertEquals(URL_SECOND.toExternalForm(), iframe.getSrcAttribute());
63          assertEquals("Second", ((HtmlPage) iframe.getEnclosedPage()).getTitleText());
64  
65          iframe.setSrcAttribute(URL_THIRD.toExternalForm());
66          assertEquals(URL_THIRD.toExternalForm(), iframe.getSrcAttribute());
67          assertEquals("Third", ((HtmlPage) iframe.getEnclosedPage()).getTitleText());
68      }
69  
70      /**
71       * @throws Exception if the test fails
72       */
73      @Test
74      public void setSrcAttributeWithWhiteSpaces() throws Exception {
75          final String firstContent = DOCTYPE_HTML
76              + "<html><head><title>First</title></head><body>\n"
77              + "<iframe id='iframe1' src='\n" + URL_SECOND + "\n'>\n"
78              + "</body></html>";
79          final String secondContent = DOCTYPE_HTML + "<html><head><title>Second</title></head><body></body></html>";
80          final String thirdContent = DOCTYPE_HTML + "<html><head><title>Third</title></head><body></body></html>";
81          final WebClient client = getWebClientWithMockWebConnection();
82  
83          final MockWebConnection webConnection = getMockWebConnection();
84          webConnection.setResponse(URL_FIRST, firstContent);
85          webConnection.setResponse(URL_SECOND, secondContent);
86          webConnection.setResponse(URL_THIRD, thirdContent);
87  
88          final HtmlPage page = client.getPage(URL_FIRST);
89          assertEquals("First", page.getTitleText());
90  
91          final HtmlInlineFrame iframe = page.getHtmlElementById("iframe1");
92          assertEquals(URL_SECOND.toExternalForm(), iframe.getSrcAttribute());
93          assertEquals("Second", ((HtmlPage) iframe.getEnclosedPage()).getTitleText());
94  
95          iframe.setSrcAttribute(URL_THIRD.toExternalForm());
96          assertEquals(URL_THIRD.toExternalForm(), iframe.getSrcAttribute());
97          assertEquals("Third", ((HtmlPage) iframe.getEnclosedPage()).getTitleText());
98      }
99  
100     /**
101      * Tests that a recursive src attribute (i.e. src="#xyz") doesn't result in an
102      * infinite loop (bug #459).
103      *
104      * @throws Exception if an error occurs
105      */
106     @Test
107     public void recursiveSrcAttribute() throws Exception {
108         final String html = DOCTYPE_HTML + "<html><body><iframe id='a' src='#abc'></body></html>";
109         final HtmlPage page = loadPage(html);
110         final HtmlInlineFrame iframe = page.getHtmlElementById("a");
111         assertNotNull(iframe.getEnclosedPage());
112     }
113 
114     /**
115      * Tests that a recursive src is prevented.
116      * @throws Exception if an error occurs
117      */
118     @Test
119     public void recursiveNestedFrames() throws Exception {
120         final String firstContent = DOCTYPE_HTML
121             + "<html><head><title>First</title></head><body>\n"
122             + "<iframe id='iframe1' src='" + URL_SECOND + "'>\n"
123             + "</body></html>";
124         final String secondContent = DOCTYPE_HTML
125             + "<html><head><title>Second</title></head>\n"
126             + "<body><iframe id='iframe2_1' src='" + URL_FIRST + "'></iframe></body></html>";
127         final WebClient client = getWebClientWithMockWebConnection();
128 
129         final MockWebConnection webConnection = getMockWebConnection();
130         webConnection.setResponse(URL_FIRST, firstContent);
131         webConnection.setResponse(URL_SECOND, secondContent);
132 
133         final HtmlPage page = client.getPage(URL_FIRST);
134         assertEquals("First", page.getTitleText());
135 
136         final HtmlInlineFrame iframe = page.getHtmlElementById("iframe1");
137         assertEquals(URL_SECOND.toExternalForm(), iframe.getSrcAttribute());
138         final HtmlPage iframePage = (HtmlPage) iframe.getEnclosedPage();
139         assertEquals("Second", iframePage.getTitleText());
140 
141         // the nested frame should not have been loaded
142         final HtmlInlineFrame iframeIn2 = iframePage.getHtmlElementById("iframe2_1");
143         assertEquals(URL_FIRST.toExternalForm(), iframeIn2.getSrcAttribute());
144         final HtmlPage iframePage2 = (HtmlPage) iframeIn2.getEnclosedPage();
145         assertEquals(URL_FIRST.toExternalForm(), iframeIn2.getEnclosedPage().getUrl());
146 
147         // the nested nested frame should not have been loaded
148         final HtmlInlineFrame iframeIn3 = iframePage2.getHtmlElementById("iframe1");
149         assertEquals(URL_SECOND.toExternalForm(), iframeIn3.getSrcAttribute());
150         assertEquals("about:blank", iframeIn3.getEnclosedPage().getUrl());
151     }
152 
153     /**
154      * Tests that an invalid src attribute (i.e. src="foo://bar") doesn't result
155      * in a NPE (bug #458).
156      *
157      * @throws Exception if an error occurs
158      */
159     @Test
160     public void invalidSrcAttribute() throws Exception {
161         final String html = DOCTYPE_HTML + "<html><body><iframe id='a' src='foo://bar'></body></html>";
162         final HtmlPage page = loadPage(html);
163         final HtmlInlineFrame iframe = page.getHtmlElementById("a");
164         assertNotNull(iframe.getEnclosedPage());
165     }
166 
167     /**
168      * @throws Exception if the test fails
169      */
170     @Test
171     public void setSrcAttribute_ViaJavaScript() throws Exception {
172         final String firstContent = DOCTYPE_HTML
173             + "<html><head><title>First</title></head><body>\n"
174             + "<iframe id='iframe1' src='" + URL_SECOND + "'></iframe>\n"
175             + "<script type='text/javascript'>document.getElementById('iframe1').src = '" + URL_THIRD + "';\n"
176             + "</script></body></html>";
177         final String secondContent = DOCTYPE_HTML + "<html><head><title>Second</title></head><body></body></html>";
178         final String thirdContent = DOCTYPE_HTML + "<html><head><title>Third</title></head><body></body></html>";
179         final WebClient client = getWebClientWithMockWebConnection();
180 
181         final MockWebConnection webConnection = getMockWebConnection();
182         webConnection.setResponse(URL_FIRST, firstContent);
183         webConnection.setResponse(URL_SECOND, secondContent);
184         webConnection.setResponse(URL_THIRD, thirdContent);
185 
186         final HtmlPage page = client.getPage(URL_FIRST);
187         assertEquals("First", page.getTitleText());
188 
189         final HtmlInlineFrame iframe = page.getHtmlElementById("iframe1");
190         assertEquals(URL_THIRD.toExternalForm(), iframe.getSrcAttribute());
191         assertEquals("Third", ((HtmlPage) iframe.getEnclosedPage()).getTitleText());
192     }
193 
194     /**
195      * Verifies that cloned frames do no reload their content (bug #613).
196      * @throws Exception if an error occurs
197      */
198     @Test
199     public void frameCloneDoesNotReloadFrame() throws Exception {
200         final String html1 = DOCTYPE_HTML + "<html><body><iframe src='" + URL_SECOND + "'></iframe></body></html>";
201         final String html2 = DOCTYPE_HTML + "<html><body>abc</body></html>";
202 
203         final WebClient client = getWebClientWithMockWebConnection();
204 
205         final MockWebConnection conn = getMockWebConnection();
206         conn.setResponse(URL_FIRST, html1);
207         conn.setResponse(URL_SECOND, html2);
208 
209         final HtmlPage page = client.getPage(URL_FIRST);
210         assertEquals(2, conn.getRequestCount());
211 
212         page.cloneNode(true);
213         assertEquals(2, conn.getRequestCount());
214     }
215 
216     /**
217      * Verifies that frames added via document.write() don't get their contents loaded twice (bug #238).
218      * @throws Exception if an error occurs
219      */
220     @Test
221     public void frameWriteDoesNotReloadFrame() throws Exception {
222         final String html1 = DOCTYPE_HTML
223             + "<html><body>\n"
224             + "<script>document.write('<iframe id=\"f\" src=\"" + URL_SECOND + "\"></iframe>')</script>\n"
225             + "</body></html>";
226         final String html2 = DOCTYPE_HTML + "<html><body>iframe content</body></html>";
227 
228         final WebClient client = getWebClientWithMockWebConnection();
229 
230         final MockWebConnection conn = getMockWebConnection();
231         conn.setResponse(URL_FIRST, html1);
232         conn.setResponse(URL_SECOND, html2);
233 
234         final HtmlPage page = client.getPage(URL_FIRST);
235         final HtmlElement iFrame = page.getHtmlElementById("f");
236 
237         assertEquals("iframe", iFrame.getTagName());
238 
239         final HtmlPage enclosedPage = (HtmlPage) ((HtmlInlineFrame) iFrame).getEnclosedPage();
240         assertEquals("iframe content", enclosedPage.asNormalizedText());
241 
242         assertEquals(2, conn.getRequestCount());
243     }
244 
245     /**
246      * Verifies that frames added via element.innerHtml() are resolved.
247      * @throws Exception if an error occurs
248      */
249     @Test
250     public void frameSetInnerHtmlDoesLoadFrame() throws Exception {
251         final String html1 = DOCTYPE_HTML
252             + "<html><body>\n"
253             + "<iframe id='myFrame' src='" + URL_THIRD + "'></iframe>';\n"
254             + "<span id='A'></span>\n"
255             + "<script>\n"
256             + "  var frame='<iframe id=\"f\" src=\"" + URL_SECOND + "\"></iframe>';\n"
257             + "  document.getElementById('A').innerHTML = frame;\n"
258             + "</script>\n"
259             + "</body></html>";
260         final String html2 = DOCTYPE_HTML + "<html><body>iframe content</body></html>";
261         final String html3 = DOCTYPE_HTML + "<html><head></head><body>Third content</body></html>";
262 
263         final WebClient client = getWebClientWithMockWebConnection();
264 
265         final MockWebConnection conn = getMockWebConnection();
266         conn.setResponse(URL_FIRST, html1);
267         conn.setResponse(URL_SECOND, html2);
268         conn.setResponse(URL_THIRD, html3);
269 
270         final HtmlPage page = client.getPage(URL_FIRST);
271 
272         final HtmlElement myFrame = page.getHtmlElementById("myFrame");
273         assertEquals("iframe", myFrame.getTagName());
274 
275         HtmlPage enclosedPage = (HtmlPage) ((HtmlInlineFrame) myFrame).getEnclosedPage();
276         assertEquals("Third content", enclosedPage.asNormalizedText());
277 
278         final HtmlElement iFrame = page.getHtmlElementById("f");
279         assertEquals("iframe", iFrame.getTagName());
280 
281         enclosedPage = (HtmlPage) ((HtmlInlineFrame) iFrame).getEnclosedPage();
282         assertEquals("iframe content", enclosedPage.asNormalizedText());
283 
284         assertEquals(3, conn.getRequestCount());
285     }
286 
287     /**
288      * Verifies that frames added via element.innerHtml() are resolved.
289      * @throws Exception if an error occurs
290      */
291     @Test
292     public void frameSetInnerHtmlDoesLoadFrameContentTimeout() throws Exception {
293         final String html1 = DOCTYPE_HTML
294             + "<html><body>\n"
295             + "<iframe id='myFrame' src='" + URL_THIRD + "'></iframe>';\n"
296             + "<span id='A'></span>\n"
297             + "<script>\n"
298             + "  function createIframe() {\n"
299             + "    var frame='<iframe id=\"f\" src=\"" + URL_SECOND + "\"></iframe>';\n"
300             + "    document.getElementById('A').innerHTML = frame;\n"
301             + "  }\n"
302             + "  setTimeout('createIframe()', 100);\n"
303             + "</script>\n"
304             + "</body></html>";
305         final String html2 = DOCTYPE_HTML + "<html><body>iframe content</body></html>";
306         final String html3 = DOCTYPE_HTML + "<html><head></head><body>Third content</body></html>";
307 
308         final WebClient client = getWebClientWithMockWebConnection();
309 
310         final MockWebConnection conn = getMockWebConnection();
311         conn.setResponse(URL_FIRST, html1);
312         conn.setResponse(URL_SECOND, html2);
313         conn.setResponse(URL_THIRD, html3);
314 
315         final HtmlPage page = client.getPage(URL_FIRST);
316 
317         final HtmlElement myFrame = page.getHtmlElementById("myFrame");
318         assertEquals("iframe", myFrame.getTagName());
319 
320         HtmlPage enclosedPage = (HtmlPage) ((HtmlInlineFrame) myFrame).getEnclosedPage();
321         assertEquals("Third content", enclosedPage.asNormalizedText());
322 
323         // wait for the timer
324         final JavaScriptJobManager jobManager = page.getEnclosingWindow().getJobManager();
325         jobManager.waitForJobs(1000);
326 
327         final HtmlElement iFrame = page.getHtmlElementById("f");
328         assertEquals("iframe", iFrame.getTagName());
329 
330         enclosedPage = (HtmlPage) ((HtmlInlineFrame) iFrame).getEnclosedPage();
331         assertEquals("iframe content", enclosedPage.asNormalizedText());
332 
333         assertEquals(3, conn.getRequestCount());
334     }
335 
336     /**
337      * Issue 3046109.
338      * The iframe has no source and is filled from javascript.
339      * @throws Exception if an error occurs
340      */
341     @Test
342     public void frameContentCreationViaJavascript() throws Exception {
343         final String html = DOCTYPE_HTML
344             + "<html><head><title>frames</title></head>\n"
345             + "<body>\n"
346             + "<iframe name='foo'></iframe>\n"
347             + "<script type='text/javascript'>\n"
348             + "var doc = window.frames['foo'].document;\n"
349             + "doc.open();\n"
350             + "doc.write('<html><body><div id=\"myContent\">Hi Folks!</div></body></html>');\n"
351             + "doc.close();\n"
352             + "</script>\n"
353             + "<body>\n"
354             + "</html>";
355 
356         final WebClient webClient = getWebClientWithMockWebConnection();
357         final MockWebConnection conn = getMockWebConnection();
358 
359         conn.setDefaultResponse(html);
360         final HtmlPage page = webClient.getPage(URL_FIRST);
361 
362         final HtmlPage enclosedPage = (HtmlPage) page.getFrames().get(0).getEnclosedPage();
363         final HtmlElement element = enclosedPage.getHtmlElementById("myContent");
364         final String content = element.asNormalizedText();
365         assertEquals("Hi Folks!", content);
366     }
367 
368     /**
369      * The iframe has no source and is filled from javascript.
370      * The javascript writes unicode charactes into the iframe content.
371      * @throws Exception if an error occurs
372      */
373     @Test
374     public void frameContentCreationViaJavascriptUnicode() throws Exception {
375         final String html = DOCTYPE_HTML
376             + "<html><head><title>frames</title></head>\n"
377             + "<body>\n"
378             + "<iframe name='foo'></iframe>\n"
379             + "<script type='text/javascript'>\n"
380             + "var doc = window.frames['foo'].document;\n"
381             + "doc.open();\n"
382             + "doc.write('<html><body><div id=\"myContent\">Hello worl\u0414</div></body></html>');\n"
383             + "doc.close();\n"
384             + "</script>\n"
385             + "<body>\n"
386             + "</html>";
387 
388         final WebClient webClient = getWebClientWithMockWebConnection();
389         final MockWebConnection conn = getMockWebConnection();
390 
391         conn.setResponse(URL_FIRST, html, "text/html; charset=UTF-8", UTF_8);
392         final HtmlPage page = webClient.getPage(URL_FIRST);
393 
394         final HtmlPage enclosedPage = (HtmlPage) page.getFrames().get(0).getEnclosedPage();
395         final HtmlElement element = enclosedPage.getHtmlElementById("myContent");
396         final String content = element.asNormalizedText();
397         assertEquals("Hello worl\u0414", content);
398     }
399 
400     /**
401      * The iframe has no source and is filled from javascript.
402      * The javascript writes windows characters into the iframe content.
403      * @throws Exception if an error occurs
404      */
405     @Test
406     public void frameContentCreationViaJavascriptISO_8859_1() throws Exception {
407         final String html = DOCTYPE_HTML
408             + "<html><head><title>frames</title></head>\n"
409             + "<body>\n"
410             + "<iframe name='foo'></iframe>\n"
411             + "<script type='text/javascript'>\n"
412             + "var doc = window.frames['foo'].document;\n"
413             + "doc.open();\n"
414             + "doc.write('<html><body><div id=\"myContent\">\u00e4\u00f6\u00fc</div></body></html>');\n"
415             + "doc.close();\n"
416             + "</script>\n"
417             + "<body>\n"
418             + "</html>";
419 
420         final WebClient webClient = getWebClientWithMockWebConnection();
421         final MockWebConnection conn = getMockWebConnection();
422         webClient.setWebConnection(conn);
423 
424         conn.setResponse(URL_FIRST, html, "text/html; charset=ISO-8859-1", ISO_8859_1);
425         final HtmlPage page = webClient.getPage(URL_FIRST);
426 
427         final HtmlPage enclosedPage = (HtmlPage) page.getFrames().get(0).getEnclosedPage();
428         final HtmlElement element = enclosedPage.getHtmlElementById("myContent");
429         final String content = element.asNormalizedText();
430         assertEquals("\u00e4\u00f6\u00fc", content);
431     }
432 
433     /**
434      * Issue 3046109.
435      * The iframe has a source but is filled from javascript before.
436      * @throws Exception if an error occurs
437      */
438     @Test
439     public void frameContentCreationViaJavascriptBeforeFrameResolved() throws Exception {
440         final String html = DOCTYPE_HTML
441             + "<html><head><title>frames</title></head>\n"
442             + "<body>\n"
443             + "<iframe name='foo' src='" + URL_SECOND + "'></iframe>\n"
444             + "<script type='text/javascript'>\n"
445             + "var doc = window.frames['foo'].document;\n"
446             + "doc.open();\n"
447             + "doc.write('<html><body><div id=\"myContent\">Hi Folks!</div></body></html>');\n"
448             + "doc.close();\n"
449             + "</script>\n"
450             + "<body>\n"
451             + "</html>";
452 
453         final String frameHtml = DOCTYPE_HTML
454             + "<html><head><title>inside frame</title></head>\n"
455             + "<body>\n"
456             + "<div id=\"myContent\">inside frame</div>\n"
457             + "<body>\n"
458             + "</html>";
459 
460         final WebClient webClient = getWebClient();
461         final MockWebConnection conn = new MockWebConnection();
462         webClient.setWebConnection(conn);
463 
464         conn.setResponse(URL_FIRST, html);
465         conn.setResponse(URL_SECOND, frameHtml);
466         final HtmlPage page = webClient.getPage(URL_FIRST);
467 
468         final HtmlPage enclosedPage = (HtmlPage) page.getFrames().get(0).getEnclosedPage();
469         final HtmlElement element = enclosedPage.getHtmlElementById("myContent");
470         final String content = element.asNormalizedText();
471         assertEquals("Hi Folks!", content);
472     }
473 
474     /**
475      * @throws Exception if the test fails
476      */
477     @Test
478     public void asNormalizedText() throws Exception {
479         final String firstContent = DOCTYPE_HTML
480             + "<html><head><title>First</title></head><body>\n"
481             + "before<iframe id='iframe1' src='" + URL_SECOND + "'></iframe>after\n"
482             + "</body></html>";
483         final String secondContent = DOCTYPE_HTML + "<html><head></head><body>Second content</body></html>";
484         final WebClient client = getWebClientWithMockWebConnection();
485 
486         final MockWebConnection webConnection = getMockWebConnection();
487         webConnection.setResponse(URL_FIRST, firstContent);
488         webConnection.setResponse(URL_SECOND, secondContent);
489 
490         final HtmlPage page = client.getPage(URL_FIRST);
491         assertEquals("First\nbefore\nSecond content\nafter", page.asNormalizedText());
492     }
493 
494     /**
495      * See issue #1825.
496      * @exception Exception If the test fails
497      */
498     @Test
499     public void brokenIframe() throws Exception {
500         final String html = DOCTYPE_HTML
501                 + "<html>\n"
502                 + "<head></head>\n"
503                 + "<body>"
504                 + "1<div>2<iframe/>3</div>4"
505                 + "</body>\n"
506                 + "</html>";
507 
508         final HtmlPage page = loadPage(html);
509         assertEquals("1\n2", page.asNormalizedText());
510     }
511 }