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 }