1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package test.javafx.scene.web;
  27 
  28 import static javafx.concurrent.Worker.State.READY;
  29 import static javafx.concurrent.Worker.State.RUNNING;
  30 import static javafx.concurrent.Worker.State.FAILED;
  31 import static javafx.concurrent.Worker.State.SUCCEEDED;
  32 import static org.junit.Assert.assertEquals;
  33 import static org.junit.Assert.assertNotNull;
  34 import static org.junit.Assert.assertNull;
  35 import static org.junit.Assert.assertTrue;
  36 import java.io.File;
  37 import java.util.concurrent.Callable;
  38 import java.util.concurrent.CountDownLatch;
  39 import javafx.concurrent.Worker.State;
  40 import javafx.event.EventHandler;
  41 import javafx.scene.web.WebEngine;
  42 import org.junit.Test;
  43 import org.w3c.dom.Document;
  44 import org.w3c.dom.Element;
  45 import org.w3c.dom.Node;
  46 import org.w3c.dom.NodeList;
  47 import org.w3c.dom.Text;
  48 
  49 
  50 public class LoadTest extends TestBase {
  51 
  52     private State getLoadState() {
  53         return submit(() -> getEngine().getLoadWorker().getState());
  54     }
  55 
  56     @Test public void testLoadGoodUrl() {
  57         final String FILE = "src/test/resources/test/html/ipsum.html";
  58         load(new File(FILE));
  59         WebEngine web = getEngine();
  60 
  61         assertTrue("Load task completed successfully", getLoadState() == SUCCEEDED);
  62         assertTrue("Location.endsWith(FILE)", web.getLocation().endsWith(FILE));
  63         assertEquals("Title", "Lorem Ipsum", web.getTitle());
  64         assertNotNull("Document should not be null", web.getDocument());
  65     }
  66 
  67     @Test public void testLoadBadUrl() {
  68         final String URL = "bad://url";
  69         load(URL);
  70         WebEngine web = getEngine();
  71 
  72         assertTrue("Load task failed", getLoadState() == FAILED);
  73         assertEquals("Location", URL, web.getLocation());
  74         assertNull("Title should be null", web.getTitle());
  75         assertNull("Document should be null", web.getDocument());
  76     }
  77 
  78     @Test public void testLoadHtmlContent() {
  79         final String TITLE = "In a Silent Way";
  80         loadContent("<html><head><title>" + TITLE + "</title></head></html>");
  81         WebEngine web = getEngine();
  82 
  83         assertTrue("Load task completed successfully", getLoadState() == SUCCEEDED);
  84         assertEquals("Location", "", web.getLocation());
  85         assertEquals("Title", TITLE, web.getTitle());
  86         assertNotNull("Document should not be null", web.getDocument());
  87     }
  88 
  89     @Test public void testLoadPlainContent() {
  90         final String TEXT =
  91                 "<html><head><title>Hidden Really Well</title></head></html>";
  92         loadContent(TEXT, "text/plain");
  93         final WebEngine web = getEngine();
  94 
  95         assertTrue("Load task completed successfully", getLoadState() == SUCCEEDED);
  96         assertEquals("Location", "", web.getLocation());
  97         assertNull("Title should be null", web.getTitle());
  98 
  99         // DOM access should happen on FX thread
 100         submit(() -> {
 101             Document doc = web.getDocument();
 102             assertNotNull("Document should not be null", doc);
 103             Node el = // html -> body -> pre -> text
 104                     doc.getDocumentElement().getLastChild().getFirstChild().getFirstChild();
 105             String text = ((Text)el).getNodeValue();
 106             assertEquals("Plain text should not be interpreted as HTML",
 107                     TEXT, text);
 108         });
 109     }
 110 
 111     @Test public void testLoadEmpty() {
 112         testLoadEmpty(null);
 113         testLoadEmpty("");
 114         testLoadEmpty("about:blank");
 115     }
 116 
 117     private void testLoadEmpty(String url) {
 118         load(url);
 119         final WebEngine web = getEngine();
 120 
 121         assertTrue("Load task completed successfully", getLoadState() == SUCCEEDED);
 122         assertEquals("Location", "about:blank", web.getLocation());
 123         assertNull("Title should be null", web.getTitle());
 124 
 125         submit(() -> {
 126             Document doc = web.getDocument();
 127             assertNotNull("Document should not be null", doc);
 128 
 129             Element html = doc.getDocumentElement();
 130             assertNotNull("There should be an HTML element", html);
 131             assertEquals("HTML element should have tag HTML", "HTML", html.getTagName());
 132 
 133             NodeList htmlNodes = html.getChildNodes();
 134             assertNotNull("HTML element should have two children", htmlNodes);
 135             assertEquals("HTML element should have two children", 2, htmlNodes.getLength());
 136 
 137             Element head = (Element) htmlNodes.item(0);
 138             NodeList headNodes = head.getChildNodes();
 139             assertNotNull("There should be a HEAD element", head);
 140             assertEquals("HEAD element should have tag HEAD", "HEAD", head.getTagName());
 141             assertTrue("HEAD element should have no children",
 142                     headNodes == null || headNodes.getLength() == 0);
 143 
 144             Element body = (Element) htmlNodes.item(1);
 145             NodeList bodyNodes = body.getChildNodes();
 146             assertNotNull("There should be a BODY element", body);
 147             assertEquals("BODY element should have tag BODY", "BODY", body.getTagName());
 148             assertTrue("BODY element should have no children",
 149                     bodyNodes == null || bodyNodes.getLength() == 0);
 150         });
 151     }
 152 
 153     @Test public void testLoadUrlWithEncodedSpaces() {
 154         final String URL = "http://localhost/test.php?param=a%20b%20c";
 155         load(URL);
 156         WebEngine web = getEngine();
 157 
 158         assertEquals("Unexpected location", URL, web.getLocation());
 159     }
 160 
 161     @Test public void testLoadUrlWithUnencodedSpaces() {
 162         final String URL = "http://localhost/test.php?param=a b c";
 163         load(URL);
 164         WebEngine web = getEngine();
 165 
 166         assertEquals("Unexpected location",
 167                 URL.replace(" ", "%20"), web.getLocation());
 168     }
 169 
 170     @Test public void testLoadContentWithLocalScript() {
 171         WebEngine webEngine = getEngine();
 172 
 173         final StringBuilder result = new StringBuilder();
 174         webEngine.setOnAlert(event -> {
 175             result.append("ALERT: ").append(event.getData());
 176         });
 177 
 178         String scriptUrl =
 179                 new File("src/test/resources/test/html/invoke-alert.js").toURI().toASCIIString();
 180         String html =
 181                 "<html>\n" +
 182                 "<head><script src=\"" + scriptUrl + "\"></script></head>\n" +
 183                 "<body><script>invokeAlert('foo');</script></body>\n" +
 184                 "</html>";
 185         loadContent(html);
 186 
 187         assertEquals("Unexpected result", "ALERT: foo", result.toString());
 188         assertEquals("Unexpected load state", SUCCEEDED, getLoadState());
 189         assertEquals("Unexpected location", "", webEngine.getLocation());
 190         assertNotNull("Document is null", webEngine.getDocument());
 191     }
 192 
 193     /**
 194      * @test
 195      * @bug 8140501
 196      * summary loadContent on location changed
 197      */
 198     @Test public void loadContentOnLocationChange() throws Exception {
 199         final CountDownLatch latch = new CountDownLatch(2);
 200 
 201         submit(() -> {
 202             WebEngine webEngine = new WebEngine();
 203             webEngine.locationProperty().addListener((observable, oldValue, newValue) -> {
 204                 // NOTE: blank url == about:blank
 205                 // loading a empty or null url to WebKit
 206                 // will be treated as blank url
 207                 // ref : https://html.spec.whatwg.org
 208                 if (newValue.equalsIgnoreCase("about:blank")) {
 209                     webEngine.loadContent("");
 210                     assertTrue("loadContent in READY State", webEngine.getLoadWorker().getState() == READY);
 211                 }
 212             });
 213 
 214             webEngine.getLoadWorker().stateProperty().addListener(((observable, oldValue, newValue) -> {
 215                 if (newValue == SUCCEEDED) {
 216                     latch.countDown();
 217                 }
 218             }));
 219 
 220             webEngine.load("");
 221             assertTrue("load task completed successfully", webEngine.getLoadWorker().getState() == SUCCEEDED);
 222         });
 223         try {
 224             latch.await();
 225         } catch (InterruptedException ex) {
 226             throw new AssertionError(ex);
 227         }
 228     }
 229 
 230     /**
 231      * @test
 232      * @bug 8140501
 233      * summary load url on location changed
 234      */
 235     @Test public void loadUrlOnLocationChange() throws Exception {
 236         // Cancelling loadContent is synchronous,
 237         // there wont be 2 SUCCEEDED event
 238         final CountDownLatch latch = new CountDownLatch(1);
 239 
 240         submit(() -> {
 241             WebEngine webEngine = new WebEngine();
 242             webEngine.locationProperty().addListener((observable, oldValue, newValue) -> {
 243                 if (newValue.equalsIgnoreCase("")) {
 244                     webEngine.load("");
 245                     assertTrue("Load in READY State", webEngine.getLoadWorker().getState() == READY);
 246                 }
 247             });
 248 
 249             webEngine.getLoadWorker().stateProperty().addListener(((observable, oldValue, newValue) -> {
 250                 if (newValue == SUCCEEDED) {
 251                     latch.countDown();
 252                 }
 253             }));
 254 
 255             webEngine.loadContent("");
 256             assertTrue("loadContent task running", webEngine.getLoadWorker().getState() == RUNNING);
 257         });
 258         try {
 259             latch.await();
 260         } catch (InterruptedException ex) {
 261             throw new AssertionError(ex);
 262         }
 263     }
 264 
 265    /**
 266      * @test
 267      * @bug 8152420
 268      * summary loading relative sub-resource from jar
 269      */
 270     @Test public void loadJarFile() throws Exception {
 271 
 272         // archive-root0.html -- src archive-r0.js, c/archive-c0.js
 273         load("jar:" + new File(System.getProperties().get("WEB_ARCHIVE_JAR_TEST_DIR").toString()
 274                 + "/webArchiveJar.jar").toURI().toASCIIString() + "!/archive-root0.html");
 275         assertEquals("archive-root0.html failed to load src='archive-r0.js'",
 276                 "loaded", executeScript("jsr0()").toString());
 277 
 278         assertEquals("archive-root0.html failed to load src='c/archive-c0.js'",
 279                 "loaded", executeScript("jsc0()").toString());
 280 
 281         // archive-root1.html -- src ./archive-r0.js, ./c/archive-c0.js
 282         load("jar:" + new File(System.getProperties().get("WEB_ARCHIVE_JAR_TEST_DIR").toString()
 283                 + "/webArchiveJar.jar").toURI().toASCIIString() + "!/archive-root1.html");
 284         assertEquals("archive-root1.html failed to load src='./archive-r0.js'",
 285                 "loaded", executeScript("jsr0()").toString());
 286 
 287         assertEquals("archive-root1.html failed to load src='./c/archive-c0.js'",
 288                 "loaded", executeScript("jsc0()").toString());
 289 
 290         // archive-root2.html -- src ./c/../archive-r0.js, ./c/./././archive-c0.js
 291         load("jar:" + new File(System.getProperties().get("WEB_ARCHIVE_JAR_TEST_DIR").toString()
 292                 + "/webArchiveJar.jar").toURI().toASCIIString() + "!/archive-root2.html");
 293         assertEquals("archive-root2.html failed to load src='./c/../archive-r0.js'",
 294                 "loaded", executeScript("jsr0()").toString());
 295 
 296         assertEquals("archive-root2.html failed to load src='./c/./././archive-c0.js'",
 297                 "loaded", executeScript("jsc0()").toString());
 298     }
 299 }