1 38 package com.gargoylesoftware.htmlunit.javascript.host; 39 40 import java.io.IOException ; 41 import java.net.URL ; 42 import java.util.Collections ; 43 import java.util.Date ; 44 import java.util.HashMap ; 45 import java.util.List ; 46 import java.util.Map ; 47 import java.util.StringTokenizer ; 48 49 import org.apache.commons.collections.CollectionUtils; 50 import org.org.apache.commons.httpclient.Cookie; 51 import org.org.apache.commons.httpclient.HttpState; 52 import org.apache.commons.lang.StringUtils; 53 import org.jaxen.JaxenException; 54 import org.mozilla.javascript.Context; 55 import org.mozilla.javascript.Function; 56 import org.mozilla.javascript.NativeArray; 57 import org.mozilla.javascript.Scriptable; 58 import org.mozilla.javascript.UniqueTag; 59 60 import com.gargoylesoftware.htmlunit.BrowserVersion; 61 import com.gargoylesoftware.htmlunit.ElementNotFoundException; 62 import com.gargoylesoftware.htmlunit.StringWebResponse; 63 import com.gargoylesoftware.htmlunit.WebClient; 64 import com.gargoylesoftware.htmlunit.WebConnection; 65 import com.gargoylesoftware.htmlunit.WebResponse; 66 import com.gargoylesoftware.htmlunit.WebWindow; 67 import com.gargoylesoftware.htmlunit.html.DomNode; 68 import com.gargoylesoftware.htmlunit.html.DomText; 69 import com.gargoylesoftware.htmlunit.html.HtmlElement; 70 import com.gargoylesoftware.htmlunit.html.HtmlInlineFrame; 71 import com.gargoylesoftware.htmlunit.html.HtmlPage; 72 import com.gargoylesoftware.htmlunit.html.HtmlScript; 73 import com.gargoylesoftware.htmlunit.html.xpath.HtmlUnitXPath; 74 import com.gargoylesoftware.htmlunit.javascript.ElementArray; 75 76 94 public final class Document extends NodeImpl { 95 96 private static final long serialVersionUID = -7646789903352066465L; 97 private ElementArray all_; private ElementArray forms_; private ElementArray links_; private ElementArray images_; private ElementArray scripts_; 103 104 private final StringBuffer writeBuffer_ = new StringBuffer (); 105 private boolean writeInCurrentDocument_ = true; 106 private String domain_; 107 108 111 public Document() { 112 } 113 114 115 119 public void jsConstructor() { 120 } 121 122 123 127 public HtmlPage getHtmlPage() { 128 return (HtmlPage)getDomNodeOrDie(); 129 } 130 131 132 136 public Object jsxGet_forms() { 137 if (forms_ == null) { 138 forms_ = (ElementArray) makeJavaScriptObject(ElementArray.JS_OBJECT_NAME); 139 try { 140 forms_.init(getHtmlPage(), new HtmlUnitXPath("//form")); 141 } 142 catch (final JaxenException e) { 143 throw Context.reportRuntimeError("Failed to initialize collection document.forms: " + e.getMessage()); 144 } 145 } 146 return forms_; 147 } 148 149 150 156 public Object jsxGet_links() { 157 if (links_ == null) { 158 links_ = (ElementArray) makeJavaScriptObject(ElementArray.JS_OBJECT_NAME); 159 try { 160 links_.init(getHtmlPage(), 161 new HtmlUnitXPath("//a[@href] | //area[@href]")); 162 } 163 catch (final JaxenException e) { 164 throw Context.reportRuntimeError("Failed to initialize collection document.links: " + e.getMessage()); 165 } 166 } 167 return links_; 168 } 169 170 180 public static void jsxFunction_write( 181 final Context context, final Scriptable scriptable, final Object [] args, final Function function ) { 182 183 ((Document) scriptable).write(concatArgsAsString(args)); 184 } 185 186 187 192 private static String concatArgsAsString(final Object [] args) { 193 final StringBuffer buffer = new StringBuffer (); 194 for (int i=0; i<args.length; ++i) { 195 buffer.append(Context.toString(args[i])); 196 } 197 return buffer.toString(); 198 } 199 200 210 public static void jsxFunction_writeln( 211 final Context context, final Scriptable scriptable, final Object [] args, final Function function ) { 212 213 ((Document) scriptable).write(concatArgsAsString(args) + "\n"); 214 } 215 216 220 protected void write(final String content) { 221 getLog().debug("write: " + content); 222 223 writeBuffer_.append(content); 224 225 if (!writeInCurrentDocument_) { 226 getLog().debug("written content added to buffer"); 227 } 228 else { 229 final String bufferedContent = writeBuffer_.toString(); 231 if (canAlreadyBeParsed(bufferedContent)) { 232 writeBuffer_.setLength(0); 233 getLog().debug("parsing buffered content: " + bufferedContent); 234 235 final HtmlPage page = (HtmlPage) getDomNodeOrDie(); 236 HtmlElement current = getLastHtmlElement(page.getDocumentElement()); 238 getLog().debug("current: " + current); 239 240 if (current instanceof HtmlInlineFrame) { 242 current = (HtmlElement) current.getParentNode(); 243 } 244 ((HTMLElement) getJavaScriptNode(current)) 245 .jsxFunction_insertAdjacentHTML(HTMLElement.POSITION_BEFORE_END, bufferedContent); 246 } 247 else { 248 getLog().debug("write: not enough content to parsed it now"); 249 } 250 } 251 } 252 253 254 260 private boolean canAlreadyBeParsed(final String content) { 261 final String contentLowerCase = content.toLowerCase(); 263 if (count(contentLowerCase, "<script") != count(contentLowerCase, "</script>")) { 264 return false; 265 } 266 267 return true; 268 } 269 270 271 277 private int count(final String in, final String what) { 278 int index = in.indexOf(what); 279 int nbOccurences = 0; 280 while (index != -1) { 281 ++nbOccurences; 282 index = in.indexOf(what, index + 1); 283 } 284 285 return nbOccurences; 286 } 287 288 289 294 HtmlElement getLastHtmlElement(final HtmlElement node) { 295 final DomNode lastChild = node.getLastChild(); 296 if (lastChild == null 297 || !(lastChild instanceof HtmlElement) 298 || lastChild instanceof HtmlScript) { 299 return node; 300 } 301 302 return getLastHtmlElement((HtmlElement) lastChild); 303 } 304 305 306 private HttpState getHttpState() { 307 final HtmlPage htmlPage = getHtmlPage(); 308 final WebConnection connection = htmlPage.getWebClient().getWebConnection(); 309 final URL url = htmlPage.getWebResponse().getUrl(); 310 311 return connection.getStateForUrl( url ); 312 } 313 314 318 public String jsxGet_cookie() { 319 final HttpState state = getHttpState(); 320 final Cookie[] cookies = state.getCookies(); 321 if( cookies == null ) { 322 return ""; 323 } 324 325 final StringBuffer buffer = new StringBuffer (); 326 327 for( int i=0; i<cookies.length; i++ ) { 328 if( i != 0 ) { 329 buffer.append(";"); 330 } 331 buffer.append( cookies[i].getName() ); 332 buffer.append( "=" ); 333 buffer.append( cookies[i].getValue() ); 334 } 335 return buffer.toString(); 336 } 337 338 344 public void jsxSet_cookie(final String newCookie) { 345 final HttpState state = getHttpState(); 346 347 final Cookie cookie = buildCookie(newCookie, getHtmlPage().getWebResponse().getUrl()); 348 state.addCookie(cookie); 349 getLog().info("Added cookie: " + cookie); 350 } 351 352 353 359 static Cookie buildCookie(final String newCookie, final URL currentURL) { 360 final StringTokenizer st = new StringTokenizer (newCookie, ";"); 361 final String nameValue = st.nextToken(); 362 363 final String name = StringUtils.substringBefore(nameValue, "="); 364 final String value = StringUtils.substringAfter(nameValue, "="); 365 366 final Map attributes = new HashMap (); 367 attributes.put("domain", currentURL.getHost()); 369 attributes.put("path", ""); 372 373 while (st.hasMoreTokens()) { 374 final String token = st.nextToken(); 375 final int indexEqual = token.indexOf("="); 376 if (indexEqual > -1) { 377 attributes.put(token.substring(0, indexEqual), token.substring(indexEqual+1)); 378 } 379 else { 380 attributes.put(token, Boolean.TRUE); 381 } 382 } 383 384 final String domain = (String ) attributes.get("domain"); 385 final String path = (String ) attributes.get("path"); 386 final Date expires = null; 387 final boolean secure = (attributes.get("secure") != null); 388 final Cookie cookie = new Cookie(domain, name, value, path, expires, secure); 389 390 return cookie; 391 } 392 393 394 398 public Location jsxGet_location() { 399 final WebWindow webWindow = ((HtmlPage)getDomNodeOrDie()).getEnclosingWindow(); 400 return ((Window)webWindow.getScriptObject()).jsxGet_location(); 401 } 402 403 404 413 public void jsxSet_location(final String location) throws IOException { 414 final WebWindow webWindow = getHtmlPage().getEnclosingWindow(); 415 ((Window)webWindow.getScriptObject()).jsxSet_location(location); 416 } 417 418 419 423 public Object jsxGet_images() { 424 if (images_ == null) { 425 images_ = (ElementArray) makeJavaScriptObject(ElementArray.JS_OBJECT_NAME); 426 try { 427 images_.init(getHtmlPage(), new HtmlUnitXPath("//img")); 428 } 429 catch (final JaxenException e) { 430 throw Context.reportRuntimeError("Failed to initialize collection document.images: " + e.getMessage()); 431 } 432 } 433 return images_; 434 } 435 436 437 441 public String jsxGet_referrer() { 442 final String referrer = getHtmlPage().getWebResponse().getResponseHeaderValue("referrer"); 443 if( referrer == null ) { 444 return ""; 445 } 446 else { 447 return referrer; 448 } 449 } 450 451 452 456 public String jsxGet_URL() { 457 return getHtmlPage().getWebResponse().getUrl().toExternalForm(); 458 } 459 460 461 465 public ElementArray jsxGet_all() { 466 if (all_ == null) { 467 all_ = (ElementArray) makeJavaScriptObject(ElementArray.JS_OBJECT_NAME); 468 try { 469 all_.init(getHtmlPage(), new HtmlUnitXPath("//*")); 470 } 471 catch (final JaxenException e) { 472 throw Context.reportRuntimeError("Failed to initialize collection document.all: " + e.getMessage()); 473 } 474 } 475 return all_; 476 } 477 478 479 489 public static Object jsxFunction_open( 490 final Context context, final Scriptable scriptable, final Object [] args, final Function function ) { 491 492 final Document document = (Document) scriptable; 493 if (!document.writeInCurrentDocument_) { 494 document.getLog().warn("open() called when document is already open."); 495 } 496 document.writeInCurrentDocument_ = false; 497 return null; 498 } 499 500 501 505 public void jsxFunction_close() 506 throws 507 IOException { 508 509 if (writeInCurrentDocument_) { 510 getLog().warn("close() called when document is not open."); 511 } 512 else { 513 final WebResponse webResponse 514 = new StringWebResponse(writeBuffer_.toString()); 515 final HtmlPage page = getDomNodeOrDie().getPage(); 516 final WebClient webClient = page.getWebClient(); 517 final WebWindow window = page.getEnclosingWindow(); 518 519 webClient.loadWebResponseInto(webResponse, window); 520 521 writeInCurrentDocument_ = true; 522 writeBuffer_.setLength(0); 523 } 524 } 525 526 530 public Object jsxGet_documentElement() { 531 return getScriptableFor(((HtmlPage)getDomNodeOrDie()).getDocumentElement()); 532 } 533 534 535 541 public Object jsxFunction_createElement( final String tagName ) { 542 Object result = NOT_FOUND; 543 try { 544 final HtmlElement htmlElement = getDomNodeOrDie().getPage().createElement(tagName); 545 final Object jsElement = getScriptableFor(htmlElement); 546 547 if( jsElement == NOT_FOUND ) { 548 getLog().debug("createElement("+tagName 549 +") cannot return a result as there isn't a javascript object for the html element " 550 + htmlElement.getClass().getName()); 551 } 552 else { 553 result = jsElement; 554 } 555 } 556 catch( final ElementNotFoundException e ) { 557 } 559 return result; 560 } 561 562 563 569 public Attribute jsxFunction_createAttribute( final String attributeName ) { 570 final Attribute att = (Attribute) makeJavaScriptObject(Attribute.JS_OBJECT_NAME); 571 att.init(attributeName, null); 572 return att; 573 } 574 575 576 582 public Object jsxFunction_createTextNode( final String newData ) { 583 Object result = NOT_FOUND; 584 try { 585 final DomNode domNode = new DomText(getDomNodeOrDie().getPage(), newData); 586 final Object jsElement = getScriptableFor(domNode); 587 588 if( jsElement == NOT_FOUND ) { 589 getLog().debug("createTextNode("+newData 590 +") cannot return a result as there isn't a javascript object for the DOM node " 591 + domNode.getClass().getName()); 592 } 593 else { 594 result = jsElement; 595 } 596 } 597 catch( final ElementNotFoundException e ) { 598 } 600 return result; 601 } 602 603 604 610 public Object jsxFunction_getElementById( final String id ) { 611 Object result = null; 612 try { 613 final HtmlElement htmlElement = ((HtmlPage)getDomNodeOrDie()).getDocumentElement().getHtmlElementById(id); 614 final Object jsElement = getScriptableFor(htmlElement); 615 616 if( jsElement == NOT_FOUND ) { 617 getLog().debug("getElementById("+id 618 +") cannot return a result as there isn't a javascript object for the html element " 619 + htmlElement.getClass().getName()); 620 } 621 else { 622 result = jsElement; 623 } 624 } 625 catch (final ElementNotFoundException e) { 626 628 final BrowserVersion browser = getHtmlPage().getWebClient().getBrowserVersion(); 629 if (browser.isIE()) { 630 final NativeArray elements = (NativeArray) jsxFunction_getElementsByName(id); 631 result = elements.get(0, this); 632 if (result instanceof UniqueTag) { 633 return null; 634 } 635 getLog().warn("getElementById(" + id + ") did a getElementByName for Internet Explorer"); 636 return result; 637 } 638 getLog().warn("getElementById(" + id 639 + ") cannot return a result as there isn't a javascript object for the html element "); 640 } 641 return result; 642 } 643 644 645 650 public Object jsxFunction_getElementsByTagName( final String tagName ) { 651 final HtmlPage page = (HtmlPage)getDomNodeOrDie(); 652 final List list = page.getDocumentElement().getHtmlElementsByTagNames( 653 Collections.singletonList(tagName.toLowerCase())); 654 CollectionUtils.transform(list, getTransformerScriptableFor()); 655 656 return new NativeArray( list.toArray() ); 657 } 658 659 668 public Object jsxFunction_getElementsByName( final String elementName ) { 669 final HtmlPage page = (HtmlPage)getDomNodeOrDie(); 670 final String exp = "//*[@name='" + elementName + "']"; 671 final List list; 672 try { 673 final HtmlUnitXPath xpath = new HtmlUnitXPath(exp); 674 list = xpath.selectNodes(page); 675 } 676 catch (final JaxenException e) { 677 return new NativeArray(0); 678 } 679 CollectionUtils.transform(list, getTransformerScriptableFor()); 680 return new NativeArray( list.toArray() ); 681 } 682 683 684 691 public Object get( final String name, final Scriptable start ) { 692 final HtmlPage htmlPage = (HtmlPage)getDomNodeOrNull(); 696 if( htmlPage == null ) { 697 return super.get(name, start); 698 } 699 700 final ElementArray collection = (ElementArray) makeJavaScriptObject(ElementArray.JS_OBJECT_NAME); 703 try { 704 collection.init(htmlPage, new HtmlUnitXPath("//*[(@name = '" + name 705 + "' and (name() = 'img' or name() = 'form'))")); 706 } 707 catch (final JaxenException e) { 708 throw Context.reportRuntimeError("Failed to initialize collection: " + e.getMessage()); 709 } 710 711 final int size = collection.jsGet_length(); 712 if (size == 1) { 713 return collection.get(0, collection); 714 } 715 else if (size > 1) { 716 return collection; 717 } 718 719 return super.get(name, start); 720 } 721 722 726 public Object jsxGet_body() { 727 final List list = getHtmlPage().getDocumentElement().getHtmlElementsByTagName("body"); 728 if( list.size() == 0 ) { 729 return NOT_FOUND; 730 } 731 else { 732 final DomNode bodyElement = (DomNode)list.get(0); 733 return getScriptableFor(bodyElement); 734 } 735 } 736 737 738 742 public String jsxGet_title() { 743 return getHtmlPage().getTitleText(); 744 } 745 746 750 public void jsxSet_title(final String message ) { 751 getHtmlPage().setTitleText(message); 752 } 753 754 759 public String jsxGet_readyState() { 760 final DomNode node = getDomNodeOrDie(); 761 if (node instanceof HtmlPage) { 762 return ((HtmlPage) node).getDocumentElement().getReadyState(); 763 } 764 return getDomNodeOrDie().getReadyState(); 765 } 766 767 774 public String jsxGet_domain() { 775 if (domain_ == null) { 776 domain_ = getHtmlPage().getWebResponse().getUrl().getHost(); 777 final BrowserVersion browser = getHtmlPage().getWebClient().getBrowserVersion(); 778 if (browser.isNetscape()) { 779 domain_ = domain_.toLowerCase(); 780 } 781 } 782 783 return domain_; 784 } 785 786 815 public void jsxSet_domain( final String newDomain ) { 816 final String currentDomain = jsxGet_domain(); 817 if (currentDomain.equalsIgnoreCase(newDomain)) { 818 return; 819 } 820 821 if (newDomain.indexOf(".") == -1 822 || !currentDomain.toLowerCase().endsWith("." + newDomain.toLowerCase())) { 823 throw Context.reportRuntimeError("Illegal domain value, can not set domain from: \"" 824 + currentDomain + "\" to: \"" + newDomain +"\""); 825 } 826 827 if (getHtmlPage().getWebClient().getBrowserVersion().isNetscape()) { 829 domain_ = newDomain.toLowerCase(); 830 } 831 else { 832 domain_ = newDomain; 833 } 834 } 835 836 840 public Object jsxGet_scripts() { 841 if (scripts_ == null) { 842 scripts_ = (ElementArray) makeJavaScriptObject(ElementArray.JS_OBJECT_NAME); 843 try { 844 scripts_.init(getHtmlPage(), new HtmlUnitXPath("//script")); 845 } 846 catch (final JaxenException e) { 847 throw Context.reportRuntimeError("Failed to initialize collection document.scripts: " + e.getMessage()); 848 } 849 } 850 return scripts_; 851 } 852 } 853 854 | Popular Tags |