1 /*
   2  * Copyright (c) 2011, 2016, 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 java.io.File;
  29 import static java.lang.String.format;
  30 import java.net.HttpURLConnection;
  31 import java.util.ArrayList;
  32 import java.util.HashMap;
  33 import java.util.Random;
  34 import java.util.concurrent.Callable;
  35 import java.util.concurrent.CountDownLatch;
  36 import javafx.application.Platform;
  37 import javafx.beans.value.ChangeListener;
  38 import javafx.beans.value.ObservableValue;
  39 import javafx.concurrent.Worker.State;
  40 import javafx.scene.web.WebEngine;
  41 import javafx.scene.web.WebView;
  42 import netscape.javascript.JSObject;
  43 import static org.junit.Assert.assertEquals;
  44 import static org.junit.Assert.assertNotNull;
  45 import static org.junit.Assert.assertTrue;
  46 import static org.junit.Assert.fail;
  47 import org.junit.Test;
  48 import org.w3c.dom.Document;
  49 
  50 public class MiscellaneousTest extends TestBase {
  51 
  52     @Test public void testNoEffectOnFollowRedirects() {
  53         assertEquals("Unexpected HttpURLConnection.getFollowRedirects() result",
  54                 true, HttpURLConnection.getFollowRedirects());
  55         load("test/html/ipsum.html");
  56         assertEquals("Unexpected HttpURLConnection.getFollowRedirects() result",
  57                 true, HttpURLConnection.getFollowRedirects());
  58     }
  59 
  60     @Test public void testRT22458() throws Exception {
  61         final WebEngine webEngine = createWebEngine();
  62         Platform.runLater(() -> {
  63             webEngine.load(format("file://%d.ajax.googleapis.com/ajax",
  64                                   new Random().nextInt()));
  65         });
  66         Thread.sleep(200);
  67         long startTime = System.currentTimeMillis();
  68         DummyClass.dummyField++;
  69         long time = System.currentTimeMillis() - startTime;
  70         if (time > 2000) {
  71             fail(format("DummyClass took %f seconds to load", time / 1000.));
  72         }
  73     }
  74 
  75     private static final class DummyClass {
  76         private static int dummyField;
  77     }
  78 
  79     @org.junit.Ignore
  80     @Test public void testRT30835() throws Exception {
  81         class Record {
  82             private final Document document;
  83             private final String location;
  84             public Record(Document document, String location) {
  85                 this.document = document;
  86                 this.location = location;
  87             }
  88         }
  89         final ArrayList<Record> records = new ArrayList<Record>();
  90         ChangeListener<State> listener = (ov, oldValue, newValue) -> {
  91             if (newValue == State.SUCCEEDED) {
  92                 records.add(new Record(
  93                         getEngine().getDocument(),
  94                         getEngine().getLocation()));
  95             }
  96         };
  97         submit(() -> {
  98             getEngine().getLoadWorker().stateProperty().addListener(listener);
  99         });
 100         String location = new File("src/test/resources/test/html/RT30835.html")
 101                 .toURI().toASCIIString().replaceAll("^file:/", "file:///");
 102         load(location);
 103         assertEquals(1, records.size());
 104         assertNotNull(records.get(0).document);
 105         assertEquals(location, records.get(0).location);
 106     }
 107 
 108     @Test public void testRT26306() {
 109         loadContent(
 110                 "<script language='javascript'>\n" +
 111                 "var s = '0123456789abcdef';\n" +
 112                 "while (true) {\n" +
 113                 "    alert(s.length);\n" +
 114                 "    s = s + s;\n" +
 115                 "}\n" +
 116                 "</script>");
 117     }
 118 
 119     @Test public void testWebViewWithoutSceneGraph() {
 120         submit(() -> {
 121              WebEngine engine = new WebView().getEngine();
 122              engine.getLoadWorker().stateProperty().addListener(
 123                     (observable, oldValue, newValue) -> {
 124                         if (State.SUCCEEDED == newValue) {
 125                             engine.executeScript(
 126                                 "window.scrollTo" +
 127                                 "(0, document.documentElement.scrollHeight)");
 128                         }
 129                     });
 130              engine.loadContent("<body> <a href=#>hello</a></body>");
 131         });
 132     }
 133 
 134     // JDK-8133775
 135     @Test(expected = IllegalStateException.class) public void testDOMObjectThreadOwnership() {
 136           class IllegalStateExceptionChecker {
 137               public Object resultObject;
 138               public void start() {
 139                  WebEngine engine = new WebEngine();
 140                  // Get DOM object from JavaFX Application Thread.
 141                  resultObject = engine.executeScript("document.createElement('span')");
 142               }
 143            }
 144            IllegalStateExceptionChecker obj = new IllegalStateExceptionChecker();
 145            submit(obj::start);
 146            // Try accessing the resultObject created in JavaFX Application Thread
 147            // from someother thread. It should throw an exception.
 148            obj.resultObject.toString();
 149      }
 150 
 151     // JDK-8162715
 152     public class TimerCallback {
 153         private static final int INTERVAL_COUNT = 20;
 154         private final CountDownLatch latch = new CountDownLatch(INTERVAL_COUNT);
 155         private class Stat {
 156             private long firedTime;
 157             private long createdTime;
 158             private long interval;
 159         }
 160         private Stat[] stats = new Stat[INTERVAL_COUNT];
 161 
 162         public void call(long createdTime, long interval, int index) {
 163             Stat stat = new Stat();
 164             stat.firedTime = System.currentTimeMillis();
 165             stat.createdTime = createdTime;
 166             stat.interval = interval;
 167             stats[index] = stat;
 168             latch.countDown();
 169         }
 170     }
 171 
 172     @Test(timeout = 30000) public void testDOMTimer() {
 173         final TimerCallback timer = new TimerCallback();
 174         final WebEngine webEngine = createWebEngine();
 175         submit(() -> {
 176             final JSObject window = (JSObject) webEngine.executeScript("window");
 177             assertNotNull(window);
 178             window.setMember("timer", timer);
 179             // Try various intervals
 180             for (int i = 0; i < timer.INTERVAL_COUNT; i++) {
 181                 int timeout = i * (1000 / timer.INTERVAL_COUNT);
 182                 webEngine.executeScript("window.setTimeout("
 183                                       + "timer.call.bind(timer, Date.now(),"
 184                                       // pass 'i' to call to test time
 185                                       + timeout +"," + i + "),"
 186                                       // set 'i' as a timeout interval
 187                                       + timeout + ")");
 188             }
 189 
 190         });
 191 
 192         try {
 193             timer.latch.await();
 194         } catch (InterruptedException e) {
 195             throw new AssertionError(e);
 196         }
 197         for (TimerCallback.Stat stat : timer.stats) {
 198             assertNotNull(stat);
 199             final String msg = String.format(
 200                     "expected delta:%d, actual delta:%d",
 201                     stat.interval,
 202                     stat.firedTime - stat.createdTime);
 203             // Timer should not fire too early. Added 20 ms offset to compensate
 204             // the floating point approximation issues while dealing with timer.
 205             assertTrue(msg,
 206                     ((stat.firedTime + 20) - stat.createdTime) >= stat.interval);
 207             // Timer should not be too late. Since it is not a real time system,
 208             // we can't expect the timer to be fire at exactly on the requested
 209             // time, give a 1000 ms extra time.
 210             assertTrue(msg,
 211                     (stat.firedTime - stat.createdTime) <= (stat.interval + 1000));
 212         }
 213     }
 214 
 215     /**
 216      * @test
 217      * @bug 8163582
 218      * summary svg.path.getTotalLength
 219      * Load a simple SVG, Replace its path and get its path's totalLength using pat.getTotalLength
 220      */
 221     @Test(timeout = 30000) public void testSvgGetTotalLength() throws Exception {
 222         final String svgStub = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>" +
 223                 " <path id='pathId' d='M150 0 L75 200 L225 200 Z' /> <svg>";
 224 
 225         // <Path, [Expected, Error Tolerance]>
 226         final HashMap<String, Double[]> svgPaths = new HashMap<>();
 227         svgPaths.put("'M 0 0 L 100 0 L 100 100 L 0 100 Z'",
 228                 new Double[] {400.0, 0.000001});
 229         svgPaths.put("'M 0 0 l 100 0 l 0 100 l -100 0 Z'",
 230                 new Double[] {400.0, 0.000001});
 231         svgPaths.put("'M 0 0 t 0 100'",
 232                 new Double[] {100.0, 0.1});
 233         svgPaths.put("'M 0 0 Q 55 50 100 100'",
 234                 new Double[] {141.4803314, 0.000001});
 235         svgPaths.put("'M 778.4191616766467 375.19086364081954 C 781.239563 " +
 236                         "375.1908569 786.8525244750526 346.60170830052556 786.8802395209582 346.87991373394766'",
 237                 new Double[] {29.86020, 0.0001});
 238         svgPaths.put("'M 0 0 C 0.00001 0.00001 0.00002 0.00001 0.00003 0'",
 239                 new Double[] {0.0000344338, 0.000000001});
 240 
 241         loadContent(svgStub);
 242 
 243         svgPaths.forEach((pathData, expected) -> {
 244             executeScript("document.getElementById('pathId').setAttribute('d' , " + pathData + ");");
 245             // Get svg path's total length
 246             Double totalLength = ((Number) executeScript("document.getElementById('pathId').getTotalLength();")).doubleValue();
 247             final String msg = String.format(
 248                     "svg.path.getTotalLength() for %s, expected : %f, actual : %f",
 249                     pathData, expected[0], totalLength);
 250             assertEquals(msg,
 251                     expected[0], totalLength, expected[1]);
 252         });
 253     }
 254 
 255     private WebEngine createWebEngine() {
 256         return submit(() -> new WebEngine());
 257     }
 258 }