1 /* 2 * Copyright (c) 2017, 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 org.junit.Test; 29 import org.junit.Before; 30 31 import java.util.concurrent.CountDownLatch; 32 import java.util.concurrent.TimeUnit; 33 import java.util.concurrent.atomic.AtomicInteger; 34 import javafx.collections.ObservableList; 35 import javafx.scene.web.WebHistory; 36 37 import static org.junit.Assert.assertEquals; 38 import static org.junit.Assert.assertNotNull; 39 import static org.junit.Assert.assertNull; 40 import static org.junit.Assert.assertTrue; 41 import static org.junit.Assert.fail; 42 43 public class HistoryStateTest extends TestBase { 44 private static final CountDownLatch historyStateLatch = new CountDownLatch(3); 45 final AtomicInteger historyListenerIndex = new AtomicInteger(-1); 46 47 private static final String resourcePath= "test/html/"; 48 private static final String initialLoadUrl = "archive-root0.html"; 49 private static final String firstLoadUrl = "archive-root1.html"; 50 private static final String secondLoadUrl = "archive-root2.html"; 51 private static final String replaceLoadUrl = "archive-root3.html"; 52 53 private static final String historyPushScript1 = "history.pushState({push1key : 1}, '', '?" + 54 firstLoadUrl + "');"; 55 private static final String historyPushScript2 = "history.pushState({push2key : 2}, '', '?" + 56 secondLoadUrl + "');"; 57 private static final String historyReplaceScript = "history.replaceState({replaceObject : 3}, '', '?" + 58 replaceLoadUrl + "');"; 59 private static final String historyStateScript = "history.state"; 60 private static final String historyLengthScript = "history.length"; 61 private static final String historyGoBackScript = "history.go(-1)"; 62 private static final String historyGoForwardScript = "history.go(1)"; 63 private static final String historyBackcript = "history.back()"; 64 65 private static final int TIMEOUT = 30; // seconds 66 67 @Before 68 public void before() { 69 load(HistoryStateTest.class.getClassLoader().getResource( 70 resourcePath + initialLoadUrl).toExternalForm()); 71 } 72 73 @Test 74 public void pushAndReplaceTest() throws Exception { 75 // Initial history.state should be null 76 assertNull(historyStateScript + " : Failed", 77 executeScript(historyStateScript)); 78 // Initial history.length will be 1 79 assertEquals(historyLengthScript + " : Failed", 80 1, executeScript(historyLengthScript)); 81 82 // history.pushState({push1Key : 1}, '', '?firstLoadUrl"'); 83 executeScript(historyPushScript1); 84 // Check if the history.state object for not null 85 assertNotNull(historyStateScript + " : Failed", 86 executeScript(historyStateScript)); 87 // {push1Key : 1} : {key = push1Key :value = (Integer) 1} 88 assertEquals("history.state.push1key Failed", 89 1, executeScript("history.state.push1key")); 90 91 // history.length expected to be 2 92 // Initial load + history.pushState(...) 93 assertEquals(historyLengthScript + " : Failed", 94 2, executeScript(historyLengthScript)); 95 96 // Check for WebEngine location is updated with new URL 97 assertTrue(historyPushScript1 + " : Failed", 98 getEngine().getLocation().endsWith(firstLoadUrl)); 99 100 101 executeScript(historyPushScript2); 102 // {push2Key : 2} : {key = push1Key :value = (Integer) 2} 103 assertEquals("history.state.push1key Failed", 104 2, executeScript("history.state.push2key")); 105 106 // history.length expected to be 2 107 // Initial load + history.pushState(...) 108 assertEquals(historyLengthScript + " : Failed", 109 3, executeScript(historyLengthScript)); 110 111 // Check for WebEngine location is updated with new URL 112 assertTrue(historyPushScript2 + " : Failed", 113 getEngine().getLocation().endsWith(secondLoadUrl)); 114 115 executeScript(historyReplaceScript); 116 // history.length remains same 117 assertEquals(historyLengthScript + " : Failed", 118 3, executeScript(historyLengthScript)); 119 120 assertEquals("history.state.replaceObject Failed", 121 3, executeScript("history.state.replaceObject")); 122 123 // Check for WebEngine location is updated with new URL 124 assertTrue(historyPushScript2 + " : Failed", 125 getEngine().getLocation().endsWith(replaceLoadUrl)); 126 127 submit(() -> { 128 getEngine().locationProperty().addListener((observable, previousUrl, newUrl) -> { 129 switch(historyListenerIndex.incrementAndGet()) { 130 case 0: 131 // call back to history.go(-1) --> newUrl = initialLoadURL 132 assertTrue(newUrl.endsWith(firstLoadUrl)); 133 // history.go(1), navigate forward 134 getEngine().executeScript(historyGoForwardScript); 135 break; 136 case 1: 137 // call back to history.go(1) --> newURL = firstLoad 138 assertTrue(newUrl.endsWith(replaceLoadUrl)); 139 // navigate back using history.back() 140 getEngine().executeScript(historyBackcript); 141 break; 142 case 2: 143 // call back to history.back() --> newURL = initialLoadUrl 144 assertTrue(newUrl.endsWith(firstLoadUrl)); 145 break; 146 default: 147 fail(); 148 } 149 historyStateLatch.countDown(); 150 }); 151 // history.go(-1), location will update in listener asynchronously 152 // expected to go back to firstLoadUrl based on previous states 153 // a. history.pushState(,,firstLoadUrl) 154 // b. history.pushState(,,secondUrl) 155 // c. history.replaceState(,,replaceLoadUrl) 156 getEngine().executeScript(historyGoBackScript); 157 }); 158 try { 159 historyStateLatch.await(TIMEOUT, TimeUnit.SECONDS); 160 } catch (InterruptedException ex) { 161 throw new AssertionError(ex); 162 } finally { 163 assertEquals("history navigation using javascript failed", 2, historyListenerIndex.get()); 164 } 165 } 166 167 // JDK-8204856 168 @Test 169 public void testDocumentExistenceAfterPushState() { 170 final ObservableList<WebHistory.Entry> history = getEngine().getHistory().getEntries(); 171 final int initialHistorySize = history.size(); 172 173 load(HistoryStateTest.class.getClassLoader().getResource( 174 resourcePath + initialLoadUrl).toExternalForm()); 175 assertNotNull(getEngine().getDocument()); 176 177 executeScript("history.pushState('push', 'title', 'pushState.html')"); 178 assertNotNull("Document shouldn't be null after history.pushState", getEngine().getDocument()); 179 assertTrue("location must end with pushState.html", getEngine().getLocation().endsWith("pushState.html")); 180 assertEquals("history count should be incremented", initialHistorySize + 1, history.size()); 181 } 182 183 // JDK-8204856 184 @Test 185 public void testDocumentExistenceAfterReplaceState() { 186 final ObservableList<WebHistory.Entry> history = getEngine().getHistory().getEntries(); 187 final int initialHistorySize = history.size(); 188 189 load(HistoryStateTest.class.getClassLoader().getResource( 190 resourcePath + initialLoadUrl).toExternalForm()); 191 assertNotNull(getEngine().getDocument()); 192 193 executeScript("history.replaceState('push', 'title', 'replaceState.html')"); 194 assertNotNull("Document shouldn't be null after history.replaceState", getEngine().getDocument()); 195 assertTrue("location must end with replaceState.html", getEngine().getLocation().endsWith("replaceState.html")); 196 assertEquals("history count shouldn't be incremented", initialHistorySize, history.size()); 197 } 198 } 199