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 org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertNull; 30 import static org.junit.Assert.assertNotNull; 31 import static org.junit.Assert.assertSame; 32 import static org.junit.Assert.assertTrue; 33 import static org.junit.Assert.fail; 34 35 import javafx.scene.web.WebEngine; 36 37 import org.junit.Test; 38 import org.w3c.dom.*; 39 import org.w3c.dom.css.*; 40 import org.w3c.dom.events.*; 41 import org.w3c.dom.html.*; 42 import org.w3c.dom.stylesheets.*; 43 import org.w3c.dom.views.*; 44 import com.sun.webkit.dom.*; 45 46 47 /** 48 * Tests for various aspects of DOM access. 49 * 50 * <p><strong>DOM should be accessed from FX thread only, 51 * so please be sure to use submit(Callable).</strong> 52 */ 53 public class DOMTest extends TestBase { 54 55 @Test public void testGetSetId() { 56 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 57 submit(() -> { 58 NodeList ee = doc.getElementsByTagName("p"); 59 60 int numProcessed = 0; 61 for (int i = 0 ; i < ee.getLength() ; i++) { 62 Node n = ee.item(i); 63 String s = ((ElementImpl)n).getId(); 64 String newId = "new" + s; 65 ((ElementImpl)n).setId(newId); 66 assertEquals("New element id", newId, ((ElementImpl)n).getId()); 67 numProcessed++; 68 } 69 70 assertTrue("Number of processed Elements is equal to 0", numProcessed > 0); 71 }); 72 } 73 74 @Test public void testEmptyTextContent() { 75 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 76 submit(() -> { 77 Element emptyP = doc.getElementById("empty-paragraph"); 78 String textContent = emptyP.getTextContent(); 79 assertEquals("Text content of an empty paragraph", "", textContent); 80 }); 81 } 82 83 @Test public void testAppendChild() { 84 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 85 submit(() -> { 86 Node p1 = doc.getElementById("p1"); 87 NodeList c1 = p1.getChildNodes(); 88 Node left1 = c1.item(2); 89 int count1 = c1.getLength(); 90 91 Node p2 = doc.getElementById("p2"); 92 NodeList c2 = p2.getChildNodes(); 93 Node left2 = c2.item(0); 94 Node n = c2.item(1); 95 Node right2 = c2.item(2); 96 int count2 = c2.getLength(); 97 98 // Some sanity/identity checks 99 assertSame("Sibling expected", right2, n.getNextSibling()); 100 assertSame("Sibling expected", n, right2.getPreviousSibling()); 101 102 Node ret = p1.appendChild(n); 103 assertSame("Sibling expected", left2, right2.getPreviousSibling()); 104 assertSame("Parent check", p2, right2.getParentNode()); 105 106 verifyChildRemoved(p2, count2, left2, right2); 107 verifyChildAdded(n, p1, count1); 108 verifySiblings(n, left1, null); 109 assertSame("Returned node", n, ret); 110 }); 111 } 112 113 @Test public void testInsertBeforeEnd() { 114 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 115 submit(() -> { 116 Node p1 = doc.getElementById("p1"); 117 NodeList c1 = p1.getChildNodes(); 118 Node left1 = c1.item(2); 119 int count1 = c1.getLength(); 120 121 Node p2 = doc.getElementById("p2"); 122 NodeList c2 = p2.getChildNodes(); 123 Node left2 = c2.item(0); 124 Node n = c2.item(1); 125 Node right2 = c2.item(2); 126 int count2 = c2.getLength(); 127 128 // Some sanity/identity checks 129 assertSame("Sibling expected", right2, n.getNextSibling()); 130 assertSame("Sibling expected", n, right2.getPreviousSibling()); 131 132 try { 133 p1.insertBefore(null, null); 134 fail("DOMException expected but not thrown"); 135 } catch (DOMException ex) { 136 // Expected. 137 } catch (Throwable ex) { 138 fail("DOMException expected but instead threw "+ex.getClass().getName()); 139 } 140 141 Node ret = p1.insertBefore(n, null); 142 assertSame("Sibling expected", left2, right2.getPreviousSibling()); 143 assertSame("Parent check", p2, right2.getParentNode()); 144 145 verifyChildRemoved(p2, count2, left2, right2); 146 verifyChildAdded(n, p1, count1); 147 verifySiblings(n, left1, null); 148 assertSame("Returned node", n, ret); 149 }); 150 } 151 152 @Test public void testInsertBefore() { 153 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 154 submit(() -> { 155 Node p1 = doc.getElementById("p1"); 156 NodeList c1 = p1.getChildNodes(); 157 Node left1 = c1.item(0); 158 Node right1 = c1.item(1); 159 int count1 = c1.getLength(); 160 161 Node p2 = doc.getElementById("p2"); 162 NodeList c2 = p2.getChildNodes(); 163 Node left2 = c2.item(0); 164 Node n = c2.item(1); 165 Node right2 = c2.item(2); 166 int count2 = c2.getLength(); 167 168 Node ret = p1.insertBefore(n, right1); 169 170 verifyChildRemoved(p2, count2, left2, right2); 171 verifyChildAdded(n, p1, count1); 172 verifySiblings(n, left1, right1); 173 assertEquals("Returned node", n, ret); 174 }); 175 } 176 177 @Test public void testReplaceChild() { 178 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 179 submit(() -> { 180 Node p1 = doc.getElementById("p1"); 181 NodeList c1 = p1.getChildNodes(); 182 Node left1 = c1.item(0); 183 Node old = c1.item(1); 184 Node right1 = c1.item(2); 185 int count1 = c1.getLength(); 186 187 Node p2 = doc.getElementById("p2"); 188 NodeList c2 = p2.getChildNodes(); 189 Node left2 = c2.item(0); 190 Node n = c2.item(1); 191 Node right2 = c2.item(2); 192 int count2 = c2.getLength(); 193 194 Node ret = p1.replaceChild(n, old); 195 196 verifyChildRemoved(p2, count2, left2, right2); 197 verifyChildAdded(n, p1, count1 - 1); // child count stays the same 198 verifySiblings(n, left1, right1); 199 verifyNodeRemoved(old); 200 assertEquals("Returned node", old, ret); 201 }); 202 } 203 204 @Test public void testRemoveChild() { 205 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 206 submit(() -> { 207 Node p = doc.getElementById("p1"); 208 NodeList c = p.getChildNodes(); 209 Node left = c.item(0); 210 Node n = c.item(1); 211 Node right = c.item(2); 212 int count = c.getLength(); 213 214 Node ret = p.removeChild(n); 215 216 verifyChildRemoved(p, count, left, right); 217 verifyNodeRemoved(n); 218 assertEquals("Returned node", n, ret); 219 }); 220 } 221 222 @Test public void testRemoveChildWithEventHandler() { 223 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 224 submit(() -> { 225 Node p = doc.getElementById("p1"); 226 NodeList c = p.getChildNodes(); 227 Node left = c.item(0); 228 final Node n = c.item(1); 229 Node right = c.item(2); 230 int count = c.getLength(); 231 final EventTarget[] evtTarget = new EventTarget[1]; 232 233 EventListener listener = new EventListener() { 234 public void handleEvent(Event evt) { 235 evtTarget[0] = evt.getTarget(); 236 } 237 }; 238 ((EventTarget) p).addEventListener("DOMNodeRemoved", 239 listener, false); 240 241 Node ret = p.removeChild(n); 242 assertEquals("event target2", evtTarget[0], n); 243 verifyChildRemoved(p, count, left, right); 244 verifyNodeRemoved(n); 245 assertEquals("Returned node", n, ret); 246 }); 247 } 248 249 @Test public void testNodeTypes() { 250 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 251 submit(() -> { 252 Element p = doc.getElementById("showcase-paragraph"); 253 assertEquals("P element's node type", Node.ELEMENT_NODE, p.getNodeType()); 254 assertEquals("P element's tag name", "P", p.getTagName()); 255 256 NodeList children = p.getChildNodes(); 257 assertEquals("Paragraph child count", 3, children.getLength()); 258 Node text = children.item(0); 259 assertEquals("Text node type", Node.TEXT_NODE, text.getNodeType()); 260 Node comment = children.item(1); 261 assertEquals("Comment node type", Node.COMMENT_NODE, comment.getNodeType()); 262 Node element = children.item(2); 263 assertEquals("SPAN element's node type", Node.ELEMENT_NODE, element.getNodeType()); 264 265 Element span = (Element) element; 266 assertEquals("SPAN element's tag name", "SPAN", span.getTagName()); 267 assertTrue("SPAN has 'class' attribute", span.hasAttribute("class")); 268 assertTrue("SPAN has 'CLASS' attribute", span.hasAttribute("CLASS")); 269 assertEquals("SPAN attributes count", 1, span.getAttributes().getLength()); 270 271 Attr attr = span.getAttributeNode("class"); 272 assertEquals("Attr node type", Node.ATTRIBUTE_NODE, attr.getNodeType()); 273 children = span.getChildNodes(); 274 assertEquals("SPAN element child count", 1, children.getLength()); 275 text = children.item(0); 276 assertEquals("SPAN text node type", Node.TEXT_NODE, text.getNodeType()); 277 }); 278 } 279 280 @Test public void testNodeTypification() { 281 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 282 submit(() -> { 283 NodeList inputsp = doc.getElementsByTagName("p"); 284 HTMLParagraphElement elp = (HTMLParagraphElement) inputsp.item(0); 285 assertEquals("P element typification", "left", elp.getAlign()); 286 287 NodeList inputsi = doc.getElementsByTagName("img"); 288 HTMLImageElement eli = (HTMLImageElement) inputsi.item(0); 289 assertEquals("Image element typification", "file:///C:/test.png", eli.getSrc()); 290 }); 291 } 292 293 @Test public void testEventListenerCascade() { 294 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 295 submit(() -> { 296 HTMLDocument htmlDoc = (HTMLDocument)doc; 297 final HTMLBodyElement body = (HTMLBodyElement)htmlDoc.getBody(); 298 299 final EventListener listenerJS = ((HTMLBodyElementImpl)body).getOnclick(); 300 301 // typecast test 302 UIEvent evKeyUp = (UIEvent)((DocumentEvent)htmlDoc).createEvent("KeyboardEvent"); 303 ((KeyboardEventImpl)evKeyUp).initKeyboardEvent( 304 "keyup"//String type 305 , true//boolean canBubble 306 , true//boolean cancelable 307 , ((DocumentView)htmlDoc).getDefaultView()//AbstractView view 308 , "K"//String keyIdentifier 309 , KeyboardEventImpl.KEY_LOCATION_STANDARD//int keyLocation 310 , false //boolean ctrlKey 311 , false //boolean altKey 312 , false // boolean shiftKey 313 , false //boolean metaKey 314 , false //boolean altGraphKey 315 ); 316 WheelEventImpl evWheelUp = (WheelEventImpl)((DocumentEvent)htmlDoc).createEvent("WheelEvent"); 317 318 // dispatch test 319 MouseEvent evClick = (MouseEvent)((DocumentEvent)htmlDoc).createEvent("MouseEvent"); 320 evClick.initMouseEvent( 321 "click", 322 true, 323 true, 324 ((DocumentView)htmlDoc).getDefaultView(), 325 10, 326 0, 0, 0, 0, 327 true, true, true, true, 328 (short)1, (EventTarget)body); 329 330 //check start condition 331 assertEquals("Wrong body initial state", "bodyClass", body.getClassName()); 332 333 //FIXME: ineffective - there is not ScriptExecutionContext 334 listenerJS.handleEvent(evClick); 335 //OK! 336 ((EventTarget)body).dispatchEvent(evClick); 337 assertEquals("JS EventHandler does not work directly", "testClass", body.getClassName()); 338 339 EventListener listener1 = evt -> { 340 EventTarget src = ((MouseEvent) evt).getTarget(); 341 ((HTMLBodyElement) src).setClassName("newTestClass"); 342 }; 343 ((EventTarget)body).addEventListener("click", listener1, true); 344 ((EventTarget)body).dispatchEvent(evClick); 345 assertEquals("Java EventHandler does not work directly", "newTestClass", body.getClassName()); 346 347 EventListener listener2 = evt -> { 348 //OK: stacked ScriptExecutionContext 349 listenerJS.handleEvent(evt); 350 }; 351 ((EventTarget)body).addEventListener("click", listener2, true); 352 ((EventTarget)body).dispatchEvent(evClick); 353 assertEquals("JS EventHandler does not work from Java call", "testClass", body.getClassName()); 354 }); 355 } 356 357 @Test public void testDOMWindowAndStyleAccess() { 358 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 359 submit(() -> { 360 HTMLDocument htmlDoc = (HTMLDocument)doc; 361 final HTMLBodyElement body = (HTMLBodyElement)htmlDoc.getBody(); 362 363 //JS [window] access 364 DOMWindowImpl wnd = 365 (DOMWindowImpl)((DocumentView)htmlDoc).getDefaultView(); 366 wnd.resizeBy(1,1); 367 368 //Style access 369 CSSStyleDeclaration style = ((HTMLBodyElementImpl)body).getStyle(); 370 assertEquals("Style extraction", "blue", style.getPropertyValue("background-color")); 371 }); 372 } 373 374 @Test public void testDOMCSS() { 375 final Document doc = getDocumentFor("src/test/resources/test/html/dom.html"); 376 submit(() -> { 377 StyleSheetList shl = ((HTMLDocumentImpl)doc).getStyleSheets(); 378 for (int i = 0; i < shl.getLength(); ++i ) { 379 StyleSheet sh = shl.item(i); 380 String type = sh.getType(); 381 assertEquals("Style type", "text/css", type); 382 String media = sh.getMedia().getMediaText(); 383 if (i == 0) { 384 assertEquals("Style media", "screen", media); 385 } 386 CSSRuleList rl = ((CSSStyleSheet)sh).getCssRules(); 387 for (int k = 0; k < rl.getLength(); ++k ) { 388 CSSRule r = rl.item(k); 389 switch (r.getType()) { 390 case CSSRule.MEDIA_RULE: 391 CSSRuleList mediaRl = ((CSSMediaRule)r).getCssRules(); 392 break; 393 case CSSRule.IMPORT_RULE: 394 String url = ((CSSImportRule)r).getHref(); 395 break; 396 } 397 String cssText = r.getCssText(); 398 } 399 } 400 }); 401 } 402 403 // JDK-8179321 404 // Still we are supporting DOM3 interface, need to relook once we move to 405 // DOM4 spec. 406 @Test public void testDocumentURIForDOM3Compliance() { 407 // According to DOM3 spec, page loaded without base url(i.e as String) 408 // must have "document.documentURI" value as null. 409 loadContent("test"); 410 submit(() -> { 411 final WebEngine webEngine = getEngine(); 412 final Document document = webEngine.getDocument(); 413 assertNotNull(document); 414 assertNull(document.getDocumentURI()); 415 }); 416 } 417 418 // helper methods 419 420 private void verifyChildRemoved(Node parent, 421 int oldChildrenCount, Node leftSibling, Node rightSibling) { 422 assertSame("Children count", 423 oldChildrenCount - 1, parent.getChildNodes().getLength()); 424 assertSame("Left sibling's next sibling", 425 rightSibling, leftSibling.getNextSibling()); 426 assertSame("Right sibling's previous sibling", 427 leftSibling, rightSibling.getPreviousSibling()); 428 } 429 430 private void verifyChildAdded(Node n, Node parent, int oldChildrenCount) { 431 assertEquals("Children count", 432 oldChildrenCount + 1, parent.getChildNodes().getLength()); 433 assertEquals("Added node's parent", 434 parent, n.getParentNode()); 435 } 436 437 private void verifySiblings(Node n, Node leftSibling, Node rightSibling) { 438 assertSame("Added node's previous sibling", 439 leftSibling, n.getPreviousSibling()); 440 assertSame("Added node's next sibling", 441 rightSibling, n.getNextSibling()); 442 443 if (leftSibling != null) 444 assertSame("Previous sibling's next sibling", 445 n, leftSibling.getNextSibling()); 446 447 if (rightSibling != null) 448 assertSame("Next sibling's previous sibling", 449 n, rightSibling.getPreviousSibling()); 450 } 451 452 private void verifyNodeRemoved(Node n) { 453 assertNull("Removed node's parent", n.getParentNode()); 454 assertNull("Removed node's previous sibling", n.getPreviousSibling()); 455 assertNull("Removed node's next sibling", n.getNextSibling()); 456 } 457 }