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.SUCCEEDED; 29 30 import java.util.concurrent.atomic.AtomicBoolean; 31 import java.util.concurrent.CountDownLatch; 32 import java.util.List; 33 import java.io.File; 34 import java.net.MalformedURLException; 35 import javafx.beans.value.ObservableValue; 36 import javafx.scene.web.WebEngine; 37 import javafx.scene.web.WebHistory; 38 import javafx.beans.value.ChangeListener; 39 import javafx.beans.value.ObservableValue; 40 import javafx.collections.ListChangeListener; 41 import java.util.Date; 42 43 import java.util.concurrent.Callable; 44 import javafx.scene.web.WebHistory; 45 import org.junit.Test; 46 import static org.junit.Assert.assertEquals; 47 import static org.junit.Assert.assertFalse; 48 import static org.junit.Assert.assertNull; 49 import static org.junit.Assert.assertTrue; 50 import static org.junit.Assert.fail; 51 52 public class HistoryTest extends TestBase { 53 WebHistory history = getEngine().getHistory(); 54 55 AtomicBoolean entriesChanged = new AtomicBoolean(false); 56 AtomicBoolean titleChanged = new AtomicBoolean(false); 57 AtomicBoolean dateChanged = new AtomicBoolean(false); 58 AtomicBoolean indexChanged = new AtomicBoolean(false); 59 60 @Test public void test() { 61 62 // 63 // before history is populated 64 // 65 submit(() -> { 66 try { 67 history.go(-1); 68 fail("go: IndexOutOfBoundsException is not thrown"); 69 } catch (IndexOutOfBoundsException ex) {} 70 try { 71 history.go(1); 72 fail("go: IndexOutOfBoundsException is not thrown"); 73 } catch (IndexOutOfBoundsException ex) {} 74 75 history.setMaxSize(99); 76 assertEquals("max size is wrong", history.getMaxSize(), 99); 77 }); 78 79 // [1*] 80 checkLoad(new File("src/test/resources/test/html/h1.html"), 1, 0, "1"); 81 82 // [1, 2*] 83 checkLoad(new File("src/test/resources/test/html/h2.html"), 2, 1, "2"); 84 85 // 86 // check the list update 87 // 88 history.getEntries().addListener(new ListChangeListener<WebHistory.Entry>() { 89 public void onChanged(ListChangeListener.Change<? extends WebHistory.Entry> c) { 90 c.next(); 91 assertTrue("entries: change is wrong", c.wasAdded()); 92 assertTrue("entries: size is wrong", c.getAddedSubList().size() == 1); 93 history.getEntries().removeListener(this); 94 entriesChanged.set(true); 95 } 96 }); 97 98 // [1, 2, 3*] 99 checkLoad(new File("src/test/resources/test/html/h3.html"), 3, 2, "3"); 100 101 ensureValueChanged(entriesChanged, "entries not changed after load"); 102 103 // 104 // check the title update 105 // 106 history.getEntries().get(history.getCurrentIndex()).titleProperty().addListener(new ChangeListener<String>() { 107 public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { 108 assertEquals("entries: old title is wrong", "3", oldValue); 109 assertEquals("entries: new title is wrong", "hello", newValue); 110 observable.removeListener(this); 111 titleChanged.set(true); 112 } 113 }); 114 executeScript("document.title='hello'"); 115 116 ensureValueChanged(titleChanged, "title not changed from JS"); 117 118 // 119 // check the date & index updates 120 // 121 history.getEntries().get(history.getCurrentIndex() - 1).lastVisitedDateProperty().addListener(newDateListener()); 122 123 try { Thread.sleep(150); } catch (Exception e) {} // ensure the next date doesn't fit into the same millisecond 124 125 history.currentIndexProperty().addListener(new ChangeListener<Number>() { 126 public void changed(ObservableValue<? extends java.lang.Number> observable, Number oldValue, Number newValue) { 127 assertEquals("currentIndexProperty: old index is wrong", 2, oldValue); 128 assertEquals("currentIndexProperty: new index is wrong", 1, newValue); 129 observable.removeListener(this); 130 indexChanged.set(true); 131 } 132 }); 133 134 submit(() -> { 135 // [1, 2*, 3] 136 history.go(-1); 137 }); 138 waitLoadFinished(); 139 check(new File("src/test/resources/test/html/h2.html"), 3, 1, "2"); 140 141 ensureValueChanged(dateChanged, "date not changed after go(-1)"); 142 ensureValueChanged(indexChanged, "index not changed after go(-1)"); 143 144 // 145 // more go() checks 146 // 147 148 submit(() -> { 149 // [1, 2, 3*] 150 history.go(1); 151 }); 152 waitLoadFinished(); 153 check(new File("src/test/resources/test/html/h3.html"), 3, 2, "3"); 154 155 submit(() -> { 156 // [1*, 2, 3] 157 history.go(-2); 158 }); 159 waitLoadFinished(); 160 check(new File("src/test/resources/test/html/h1.html"), 3, 0, "1"); 161 162 submit(() -> { 163 // [1*, 2, 3] 164 history.go(0); // no-op 165 }); 166 167 submit(() -> { 168 // [1*, 2, 3] 169 try { 170 history.go(-1); 171 fail("go: IndexOutOfBoundsException is not thrown"); 172 } catch (IndexOutOfBoundsException ex) {} 173 }); 174 175 submit(() -> { 176 // [1, 2, 3*] 177 history.go(2); 178 }); 179 waitLoadFinished(); 180 check(new File("src/test/resources/test/html/h3.html"), 3, 2, "3"); 181 182 submit(() -> { 183 // [1, 2, 3*] 184 try { 185 history.go(1); 186 fail("go: IndexOutOfBoundsException is not thrown"); 187 } catch (IndexOutOfBoundsException ex) {} 188 }); 189 190 // 191 // check the maxSize 192 // 193 194 submit(() -> { 195 // [1, 2, 3*] 196 history.setMaxSize(3); 197 }); 198 // [2, 3, 1*] 199 checkLoad(new File("src/test/resources/test/html/h1.html"), 3, 2, "1"); 200 201 submit(() -> { 202 // [2, 3*] 203 history.setMaxSize(2); 204 assertEquals("entries: size is wrong", 2, history.getEntries().size()); 205 assertEquals("entries: title is wrong", "2", history.getEntries().get(0).getTitle()); 206 }); 207 208 submit(() -> { 209 // [2, 3*] 210 history.setMaxSize(3); 211 // [2*, 3] 212 history.go(-1); 213 }); 214 waitLoadFinished(); 215 216 // [2, 1*] 217 checkLoad(new File("src/test/resources/test/html/h1.html"), 2, 1, "1"); 218 // [2, 1, 3*] 219 checkLoad(new File("src/test/resources/test/html/h3.html"), 3, 2, "3"); 220 221 submit(() -> { 222 // [2*, 1, 3] 223 history.go(-2); 224 }); 225 waitLoadFinished(); 226 227 // 228 // check for load in-beetwen 229 // 230 231 // [2, 3*] 232 checkLoad(new File("src/test/resources/test/html/h3.html"), 2, 1, "3"); 233 234 // 235 // check the date update on reload 236 // 237 238 // [2, 3, 4*] 239 load(new File("src/test/resources/test/html/h4.html")); 240 241 history.getEntries().get(history.getCurrentIndex()).lastVisitedDateProperty().addListener(newDateListener()); 242 243 try { Thread.sleep(150); } catch (Exception e) {} // ensure the next date doesn't fit into the same millisecond 244 245 reload(); 246 247 ensureValueChanged(dateChanged, "date not changed after reload"); 248 249 // 250 // finally, check zero and invalid maxSize 251 // 252 253 submit(() -> { 254 // [] 255 history.setMaxSize(0); 256 assertEquals("maxSizeProperty: wrong value", 0, history.getEntries().size()); 257 258 // [] 259 try { 260 history.maxSizeProperty().set(-1); 261 fail("maxSizeProperty: IllegalArgumentException is not thrown"); 262 } catch (IllegalArgumentException ex) {} 263 }); 264 } 265 266 /** 267 * @test 268 * @bug 8157686 269 * Summary : Html history.pushState, history.back and history.replace 270 * Ref : https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate 271 */ 272 @Test(timeout = 30000) public void historyOnPopStateTest() throws Exception { 273 final CountDownLatch latch = new CountDownLatch(1); 274 final String page0 = "archive-root0.html"; 275 final String page1 = "archive-root1.html"; 276 final String page2 = "archive-root2.html"; 277 final String page3 = "longselectorlist.html"; 278 279 submit(() -> { 280 WebEngine webEngine = new WebEngine(); 281 ClassLoader loader = LoadTest.class.getClassLoader(); 282 283 webEngine.locationProperty().addListener((observable, oldValue, newValue) -> { 284 if (oldValue == null) { 285 return; 286 } 287 288 if (oldValue.substring(oldValue.lastIndexOf('?') + 1).equalsIgnoreCase(page3) && 289 newValue.substring(newValue.lastIndexOf('?') + 1).equalsIgnoreCase(page1)) { 290 webEngine.executeScript("history.back();"); 291 } else if (oldValue.substring(oldValue.lastIndexOf('?') + 1).equalsIgnoreCase(page1) && 292 newValue.substring(newValue.lastIndexOf('/') + 1).equalsIgnoreCase(page0)) { 293 assertNull("history.state should be null ", webEngine.executeScript("history.state")); 294 webEngine.executeScript("history.go(2);"); 295 } else if(oldValue.substring(oldValue.lastIndexOf('/') + 1).equalsIgnoreCase(page0) && 296 newValue.substring(newValue.lastIndexOf('?') + 1).equalsIgnoreCase(page3)) { 297 latch.countDown(); 298 } 299 }); 300 301 webEngine.getLoadWorker().stateProperty().addListener(((observable, oldValue, newValue) -> { 302 if (newValue == SUCCEEDED) { 303 webEngine.executeScript("history.pushState({page: 1}, 'title 1', '?" + page1 + "');"); 304 assertEquals("history.pushState Failed", 305 page1, 306 webEngine.getLocation().substring(webEngine.getLocation().lastIndexOf('?') + 1) 307 ); 308 309 webEngine.executeScript("history.pushState({page: 2}, 'title 2', '?" + page2 + "');"); 310 assertEquals("history.pushState Failed", 311 page2, 312 webEngine.getLocation().substring(webEngine.getLocation().lastIndexOf('?') + 1) 313 ); 314 315 webEngine.executeScript("history.replaceState({page: 3}, 'title 3', '?" + page3 + "');"); 316 assertEquals("history.pushState Failed", 317 page3, 318 webEngine.getLocation().substring(webEngine.getLocation().lastIndexOf('?') + 1) 319 ); 320 321 webEngine.executeScript("history.back();"); 322 } 323 })); 324 325 webEngine.load(loader.getResource("test/html/archive-root0.html").toExternalForm()); 326 }); 327 try { 328 latch.await(); 329 } catch (InterruptedException ex) { 330 throw new AssertionError(ex); 331 } 332 } 333 334 void checkLoad(File file, int size, int index, String title) { 335 load(file); 336 check(file, size, index, title); 337 } 338 339 void check(File file, int size, int index, String title) { 340 assertEquals("entries: size is wrong", size, history.getEntries().size()); 341 assertEquals("currentIndex: index is wrong", index, history.getCurrentIndex()); 342 assertEquals("entries: url is wrong", file.toURI().toString(), history.getEntries().get(index).getUrl()); 343 assertEquals("entries: title is wrong", title, history.getEntries().get(index).getTitle()); 344 } 345 346 void ensureValueChanged(AtomicBoolean value, String errMsg) { 347 if (!value.compareAndSet(true, false)) { 348 fail(errMsg); 349 } 350 } 351 352 ChangeListener newDateListener() { 353 return new ChangeListener<Date>() { 354 long startTime = System.currentTimeMillis(); 355 356 public void changed(ObservableValue<? extends Date> observable, Date oldValue, Date newValue) { 357 long curTime = System.currentTimeMillis(); 358 359 if (newValue.before(oldValue) || 360 newValue.getTime() < startTime || 361 newValue.getTime() > curTime) 362 { 363 System.out.println("oldValue=" + oldValue.getTime() + 364 ", newValue=" + newValue.getTime() + 365 ", startTime=" + startTime + 366 ", curTime=" + curTime); 367 368 fail("entries: date is wrong"); 369 } 370 observable.removeListener(this); 371 dateChanged.set(true); 372 } 373 }; 374 375 } 376 }