From: <rb...@us...> - 2014-02-01 11:57:42
|
Revision: 9096 http://sourceforge.net/p/htmlunit/code/9096 Author: rbri Date: 2014-02-01 11:57:37 +0000 (Sat, 01 Feb 2014) Log Message: ----------- Dom manipulation fixed provided by Frank Danek. Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/BaseFrameElement.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlElement.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Node.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/NodeList.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/Window3Test.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocumentTest.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2014-01-31 21:55:16 UTC (rev 9095) +++ trunk/htmlunit/src/changes/changes.xml 2014-02-01 11:57:37 UTC (rev 9096) @@ -8,6 +8,11 @@ <body> <release version="2.14" date="???" description="FF24, Bugfixes, initial work on IE11"> + <action type="fix" dev="rbri" due-to="Frank Danek"> + JavaScript: Internal cleanup and some fixes for the innerHTML/outerHTML/insertAdjacentHTML. + Fix appendChild/insertBefore/replaceChild also. + Additionally trigger the correct notifications to inform about dom changes. + </action> <action type="fix" dev="rbri"> HtmlParser fixed for pages with scripts outside the html tag. </action> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/BaseFrameElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/BaseFrameElement.java 2014-01-31 21:55:16 UTC (rev 9095) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/BaseFrameElement.java 2014-02-01 11:57:37 UTC (rev 9096) @@ -19,9 +19,9 @@ import java.net.URL; import java.util.Map; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Attr; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.Page; @@ -69,6 +69,15 @@ super(qualifiedName, page, attributes); init(); + + if (page instanceof HtmlPage && ((HtmlPage) page).isParsingHtmlSnippet()) { + // if created by the HTMLParser the src attribute is not set via setAttribute() or some other method but is + // part of the given attributes already. + final String src = getAttribute("src"); + if (src != ATTRIBUTE_NOT_DEFINED && !WebClient.ABOUT_BLANK.equals(src)) { + loadSrcWhenAddedToPage_ = true; + } + } } private void init() { @@ -357,6 +366,31 @@ } } + /** + * {@inheritDoc} + */ + @Override + public Attr setAttributeNode(final Attr attribute) { + final String qualifiedName = attribute.getName(); + String attributeValue = null; + if ("src".equals(qualifiedName)) { + attributeValue = attribute.getValue().trim(); + } + + final Attr result = super.setAttributeNode(attribute); + + if ("src".equals(qualifiedName) && !WebClient.ABOUT_BLANK.equals(attributeValue)) { + if (isDirectlyAttachedToPage()) { + loadSrc(); + } + else { + loadSrcWhenAddedToPage_ = true; + } + } + + return result; + } + private void loadSrc() { loadSrcWhenAddedToPage_ = false; final String src = getSrcAttribute(); @@ -393,18 +427,6 @@ /** * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> * - * Marks this frame to load the source when added to page. - */ - public void markLoadSrcWhenAddedToPage() { - final String src = getSrcAttribute(); - if (StringUtils.isNoneEmpty(src) && !WebClient.ABOUT_BLANK.equals(src)) { - loadSrcWhenAddedToPage_ = true; - } - } - - /** - * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> - * * Marks this frame as created by javascript. This is needed to handle * some special IE behavior. */ Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java 2014-01-31 21:55:16 UTC (rev 9095) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java 2014-02-01 11:57:37 UTC (rev 9096) @@ -89,6 +89,7 @@ * @author <a href="mailto:tom...@un...">Tom Anderson</a> * @author Ronald Brill * @author Chuck Dumont + * @author Frank Danek */ public abstract class DomNode implements Cloneable, Serializable, Node { @@ -671,6 +672,13 @@ } /** + * {@inheritDoc} + */ + public NamedNodeMap getAttributes() { + return NamedAttrNodeMapImpl.EMPTY_MAP; + } + + /** * Returns a flag indicating whether or not this node should have any leading and trailing * whitespace removed when {@link #asText()} is called. This method should usually return * <tt>true</tt>, but must return <tt>false</tt> for such things as text formatting tags. @@ -923,7 +931,7 @@ if (domNode != this && domNode.getParentNode() != null) { domNode.detach(); } - // move the node + basicAppend(domNode); fireAddition(domNode); @@ -932,63 +940,9 @@ return domNode; } - private void fireAddition(final DomNode domNode) { - final boolean wasAlreadyAttached = domNode.isDirectlyAttachedToPage(); - domNode.directlyAttachedToPage_ = isDirectlyAttachedToPage(); - - if (isDirectlyAttachedToPage()) { - // trigger events - final Page page = getPage(); - if (page instanceof HtmlPage) { - ((HtmlPage) page).notifyNodeAdded(domNode); - } - - // a node that is already "complete" (ie not being parsed) and not yet attached - if (!domNode.isBodyParsed() && !wasAlreadyAttached) { - for (final DomNode child : domNode.getDescendants()) { - child.directlyAttachedToPage_ = true; - child.onAllChildrenAddedToPage(true); - } - domNode.onAllChildrenAddedToPage(true); - } - } - - if (this instanceof DomDocumentFragment) { - onAddedToDocumentFragment(); - } - - fireNodeAdded(this, domNode); - } - /** - * Indicates if the current node is being parsed. This means that the opening tag has already been - * parsed but not the body and end tag. - */ - private boolean isBodyParsed() { - return getStartLineNumber() != -1 && getEndLineNumber() == -1; - } - - /** - * Quietly removes this node and moves its children to the specified destination. "Quietly" means - * that no node events are fired. This method is not appropriate for most use cases. It should - * only be used in specific cases for HTML parsing hackery. - * - * @param destination the node to which this node's children should be moved before this node is removed - */ - void quietlyRemoveAndMoveChildrenTo(final DomNode destination) { - if (destination.getPage() != getPage()) { - throw new RuntimeException("Cannot perform quiet move on nodes from different pages."); - } - for (final DomNode child : getChildren()) { - child.basicRemove(); - destination.basicAppend(child); - } - basicRemove(); - } - - /** * Appends the specified node to the end of this node's children, assuming the specified - * node is clean (doesn't have preexisting relationships to other nodes. + * node is clean (doesn't have preexisting relationships to other nodes). * * @param node the node to append to this node's children */ @@ -1009,51 +963,31 @@ } /** - * Check for insertion errors for a new child node. This is overridden by derived - * classes to enforce which types of children are allowed. - * - * @param newChild the new child node that is being inserted below this node - * @throws DOMException HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does - * not allow children of the type of the newChild node, or if the node to insert is one of - * this node's ancestors or this node itself, or if this node is of type Document and the - * DOM application attempts to insert a second DocumentType or Element node. - * WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the - * one that created this node. - */ - protected void checkChildHierarchy(final Node newChild) throws DOMException { - Node parentNode = this; - while (parentNode != null) { - if (parentNode == newChild) { - throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "Child node is already a parent."); - } - parentNode = parentNode.getParentNode(); - } - final Document thisDocument = getOwnerDocument(); - final Document childDocument = newChild.getOwnerDocument(); - if (childDocument != thisDocument && childDocument != null) { - throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "Child node " + newChild.getNodeName() - + " is not in the same Document as this " + getNodeName() + "."); - } - } - - /** * {@inheritDoc} */ public Node insertBefore(final Node newChild, final Node refChild) { - if (refChild == null) { - appendChild(newChild); + if (newChild instanceof DomDocumentFragment) { + final DomDocumentFragment fragment = (DomDocumentFragment) newChild; + for (final DomNode child : fragment.getChildren()) { + insertBefore(child, refChild); + } } else { - if (refChild.getParentNode() != this) { - throw new DOMException(DOMException.NOT_FOUND_ERR, "Reference node is not a child of this node."); + if (refChild == null) { + appendChild(newChild); } - ((DomNode) refChild).insertBefore((DomNode) newChild); + else { + if (refChild.getParentNode() != this) { + throw new DOMException(DOMException.NOT_FOUND_ERR, "Reference node is not a child of this node."); + } + ((DomNode) refChild).insertBefore((DomNode) newChild); + } } - return null; + return newChild; } /** - * Inserts a new child node before this node into the child relationship this node is a + * Inserts the specified node as a new child node before this node into the child relationship this node is a * part of. If the specified node is this node, this method is a no-op. * * @param newNode the new node to insert @@ -1068,31 +1002,73 @@ return; } - //clean up the new node, in case it is being moved - final DomNode exParent = newNode.getParentNode(); - newNode.basicRemove(); + // clean up the new node, in case it is being moved + if (newNode.getParentNode() != null) { + newNode.detach(); + } + basicInsertBefore(newNode); + + fireAddition(newNode); + } + + /** + * Inserts the specified node into this node's parent's children right before this node, assuming the specified + * node is clean (doesn't have preexisting relationships to other nodes). + * + * @param node the node to insert before this node + */ + private void basicInsertBefore(final DomNode node) { + node.setPage(page_); if (parent_.firstChild_ == this) { - parent_.firstChild_ = newNode; + parent_.firstChild_ = node; } else { - previousSibling_.nextSibling_ = newNode; + previousSibling_.nextSibling_ = node; } - newNode.previousSibling_ = previousSibling_; - newNode.nextSibling_ = this; - previousSibling_ = newNode; - newNode.parent_ = parent_; - newNode.setPage(page_); + node.previousSibling_ = previousSibling_; + node.nextSibling_ = this; + previousSibling_ = node; + node.parent_ = parent_; + } - fireAddition(newNode); + private void fireAddition(final DomNode domNode) { + final boolean wasAlreadyAttached = domNode.isDirectlyAttachedToPage(); + domNode.directlyAttachedToPage_ = isDirectlyAttachedToPage(); - if (exParent != null) { - fireNodeDeleted(exParent, newNode); - exParent.fireNodeDeleted(exParent, this); + if (isDirectlyAttachedToPage()) { + // trigger events + final Page page = getPage(); + if (page instanceof HtmlPage) { + ((HtmlPage) page).notifyNodeAdded(domNode); + } + + // a node that is already "complete" (ie not being parsed) and not yet attached + if (!domNode.isBodyParsed() && !wasAlreadyAttached) { + for (final DomNode child : domNode.getDescendants()) { + child.directlyAttachedToPage_ = true; + child.onAllChildrenAddedToPage(true); + } + domNode.onAllChildrenAddedToPage(true); + } } + + if (this instanceof DomDocumentFragment) { + onAddedToDocumentFragment(); + } + + fireNodeAdded(this, domNode); } /** + * Indicates if the current node is being parsed. This means that the opening tag has already been + * parsed but not the body and end tag. + */ + private boolean isBodyParsed() { + return getStartLineNumber() != -1 && getEndLineNumber() == -1; + } + + /** * Recursively sets the new page on the node and its children * @param newPage the new owning page */ @@ -1110,13 +1086,6 @@ /** * {@inheritDoc} */ - public NamedNodeMap getAttributes() { - return NamedAttrNodeMapImpl.EMPTY_MAP; - } - - /** - * {@inheritDoc} - */ public Node removeChild(final Node child) { if (child.getParentNode() != this) { throw new DOMException(DOMException.NOT_FOUND_ERR, "Node is not a child of this node."); @@ -1126,25 +1095,12 @@ } /** - * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> - * - * Detach this node from all relationships with other nodes. - * This is the first step of an move. + * Removes all of this node's children. */ - protected void detach() { - final DomNode exParent = parent_; - basicRemove(); - - final HtmlPage htmlPage = getHtmlPageOrNull(); - if (htmlPage != null) { - htmlPage.notifyNodeRemoved(this); + public void removeAllChildren() { + while (getFirstChild() != null) { + getFirstChild().remove(); } - - if (exParent != null) { - fireNodeDeleted(exParent, this); - //ask ex-parent to fire event (because we don't have parent now) - exParent.fireNodeDeleted(exParent, this); - } } /** @@ -1156,6 +1112,20 @@ } /** + * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> + * + * Detach this node from all relationships with other nodes. + * This is the first step of an move. + */ + protected void detach() { + final DomNode exParent = parent_; + + basicRemove(); + + fireRemoval(exParent); + } + + /** * Cuts off all relationships this node has with siblings and parents. */ private void basicRemove() { @@ -1177,6 +1147,23 @@ parent_ = null; } + private void fireRemoval(final DomNode exParent) { + final HtmlPage htmlPage = getHtmlPageOrNull(); + if (htmlPage != null) { + // some of the actions executed on removal need an intact parent relationship (e.g. for the + // DocumentPositionComparator) so we have to restore it temporarily + parent_ = exParent; + htmlPage.notifyNodeRemoved(this); + parent_ = null; + } + + if (exParent != null) { + fireNodeDeleted(exParent, this); + // ask ex-parent to fire event (because we don't have parent now) + exParent.fireNodeDeleted(exParent, this); + } + } + /** * {@inheritDoc} */ @@ -1196,13 +1183,62 @@ */ public void replace(final DomNode newNode) throws IllegalStateException { if (newNode != this) { - newNode.remove(); - insertBefore(newNode); + final DomNode exParent = parent_; + final DomNode exNextSibling = nextSibling_; + remove(); + + exParent.insertBefore(newNode, exNextSibling); } } /** + * Quietly removes this node and moves its children to the specified destination. "Quietly" means + * that no node events are fired. This method is not appropriate for most use cases. It should + * only be used in specific cases for HTML parsing hackery. + * + * @param destination the node to which this node's children should be moved before this node is removed + */ + void quietlyRemoveAndMoveChildrenTo(final DomNode destination) { + if (destination.getPage() != getPage()) { + throw new RuntimeException("Cannot perform quiet move on nodes from different pages."); + } + for (final DomNode child : getChildren()) { + child.basicRemove(); + destination.basicAppend(child); + } + basicRemove(); + } + + /** + * Check for insertion errors for a new child node. This is overridden by derived + * classes to enforce which types of children are allowed. + * + * @param newChild the new child node that is being inserted below this node + * @throws DOMException HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does + * not allow children of the type of the newChild node, or if the node to insert is one of + * this node's ancestors or this node itself, or if this node is of type Document and the + * DOM application attempts to insert a second DocumentType or Element node. + * WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the + * one that created this node. + */ + protected void checkChildHierarchy(final Node newChild) throws DOMException { + Node parentNode = this; + while (parentNode != null) { + if (parentNode == newChild) { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "Child node is already a parent."); + } + parentNode = parentNode.getParentNode(); + } + final Document thisDocument = getOwnerDocument(); + final Document childDocument = newChild.getOwnerDocument(); + if (childDocument != thisDocument && childDocument != null) { + throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "Child node " + newChild.getNodeName() + + " is not in the same Document as this " + getNodeName() + "."); + } + } + + /** * Lifecycle method invoked whenever a node is added to a page. Intended to * be overridden by nodes which need to perform custom logic when they are * added to a page. This method is recursive, so if you override it, please @@ -1435,15 +1471,6 @@ } /** - * Removes all of this node's children. - */ - public void removeAllChildren() { - while (getFirstChild() != null) { - getFirstChild().remove(); - } - } - - /** * Parses the SelectionNamespaces property into a map of prefix/namespace pairs. * The default namespace (specified by xmlns=) is placed in the map using the * empty string ("") key. Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlElement.java 2014-01-31 21:55:16 UTC (rev 9095) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlElement.java 2014-02-01 11:57:37 UTC (rev 9096) @@ -37,6 +37,7 @@ import org.apache.commons.lang3.ClassUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; import org.w3c.dom.DOMException; @@ -216,6 +217,52 @@ } /** + * Sets the specified attribute. This method may be overridden by subclasses + * which are interested in specific attribute value changes, but such methods <b>must</b> + * invoke <tt>super.setAttributeNode()</tt>, and <b>should</b> consider the value of the + * <tt>cloning</tt> parameter when deciding whether or not to execute custom logic. + * + * @param attribute the attribute to set + * @return {@inheritDoc} + */ + @Override + public Attr setAttributeNode(final Attr attribute) { + final String qualifiedName = attribute.getName(); + final String oldAttributeValue = getAttribute(qualifiedName); + final HtmlPage htmlPage = (HtmlPage) getPage(); + final boolean mappedElement = isDirectlyAttachedToPage() + && HtmlPage.isMappedElement(htmlPage, qualifiedName); + if (mappedElement) { + // cast is save here because isMappedElement checks for HtmlPage + htmlPage.removeMappedElement(this); + } + + final Attr result = super.setAttributeNode(attribute); + + if (mappedElement) { + htmlPage.addMappedElement(this); + } + + final HtmlAttributeChangeEvent htmlEvent; + if (oldAttributeValue == ATTRIBUTE_NOT_DEFINED) { + htmlEvent = new HtmlAttributeChangeEvent(this, qualifiedName, attribute.getValue()); + fireHtmlAttributeAdded(htmlEvent); + htmlPage.fireHtmlAttributeAdded(htmlEvent); + } + else { + htmlEvent = new HtmlAttributeChangeEvent(this, qualifiedName, oldAttributeValue); + fireHtmlAttributeReplaced(htmlEvent); + htmlPage.fireHtmlAttributeReplaced(htmlEvent); + } + + if (hasFeature(EVENT_PROPERTY_CHANGE)) { + fireEvent(Event.createPropertyChangeEvent(this, qualifiedName)); + } + + return result; + } + + /** * Returns the HTML elements that are descendants of this element and that have one of the specified tag names. * @param tagNames the tag names to match (case-insensitive) * @return the HTML elements that are descendants of this element and that have one of the specified tag name Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java 2014-01-31 21:55:16 UTC (rev 9095) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java 2014-02-01 11:57:37 UTC (rev 9096) @@ -46,6 +46,7 @@ import net.sourceforge.htmlunit.corejs.javascript.Function; import net.sourceforge.htmlunit.corejs.javascript.Script; import net.sourceforge.htmlunit.corejs.javascript.Scriptable; +import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; @@ -376,35 +377,8 @@ /** * {@inheritDoc} - */ - public DomNodeList<DomElement> getElementsByTagName(final String tagName) { - return new XPathDomNodeList<DomElement>(this, "//*[local-name()='" + tagName + "']"); - } - - /** - * {@inheritDoc} * Not yet implemented. */ - public DomNodeList<DomElement> getElementsByTagNameNS(final String namespaceURI, final String localName) { - throw new UnsupportedOperationException("HtmlPage.getElementsByTagNameNS is not yet implemented."); - } - - /** - * {@inheritDoc} - */ - public DomElement getElementById(final String elementId) { - try { - return getElementById(elementId, true); - } - catch (final ElementNotFoundException e) { - return null; - } - } - - /** - * {@inheritDoc} - * Not yet implemented. - */ public String getInputEncoding() { throw new UnsupportedOperationException("HtmlPage.getInputEncoding is not yet implemented."); } @@ -519,6 +493,14 @@ /** * {@inheritDoc} + * Not yet implemented. + */ + public DOMImplementation getImplementation() { + throw new UnsupportedOperationException("HtmlPage.getImplementation is not yet implemented."); + } + + /** + * {@inheritDoc} * @param tagName the tag name, preferably in lowercase */ @Override @@ -578,27 +560,46 @@ * {@inheritDoc} * Not yet implemented. */ - public DOMImplementation getImplementation() { - throw new UnsupportedOperationException("HtmlPage.getImplementation is not yet implemented."); + public EntityReference createEntityReference(final String id) { + throw new UnsupportedOperationException("HtmlPage.createEntityReference is not yet implemented."); } /** * {@inheritDoc} * Not yet implemented. */ - public EntityReference createEntityReference(final String id) { - throw new UnsupportedOperationException("HtmlPage.createEntityReference is not yet implemented."); + public ProcessingInstruction createProcessingInstruction(final String namespaceURI, final String qualifiedName) { + throw new UnsupportedOperationException("HtmlPage.createProcessingInstruction is not yet implemented."); } /** * {@inheritDoc} + */ + public DomNodeList<DomElement> getElementsByTagName(final String tagName) { + return new XPathDomNodeList<DomElement>(this, "//*[local-name()='" + tagName + "']"); + } + + /** + * {@inheritDoc} * Not yet implemented. */ - public ProcessingInstruction createProcessingInstruction(final String namespaceURI, final String qualifiedName) { - throw new UnsupportedOperationException("HtmlPage.createProcessingInstruction is not yet implemented."); + public DomNodeList<DomElement> getElementsByTagNameNS(final String namespaceURI, final String localName) { + throw new UnsupportedOperationException("HtmlPage.getElementsByTagNameNS is not yet implemented."); } /** + * {@inheritDoc} + */ + public DomElement getElementById(final String elementId) { + try { + return getElementById(elementId, true); + } + catch (final ElementNotFoundException e) { + return null; + } + } + + /** * Returns the {@link HtmlAnchor} with the specified name. * * @param name the name to search by @@ -1333,6 +1334,15 @@ return true; } + /** + * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> + * + * @return true if the OnbeforeunloadHandler has accepted to change the page + */ + public boolean isOnbeforeunloadAccepted() { + return executeEventHandlersIfNeeded(Event.TYPE_BEFORE_UNLOAD); + } + private boolean isOnbeforeunloadAccepted(final HtmlPage page, final Event event) { if (event.getType().equals(Event.TYPE_BEFORE_UNLOAD) && event.getReturnValue() != null) { final OnbeforeunloadHandler handler = getWebClient().getOnbeforeunloadHandler(); @@ -1767,6 +1777,55 @@ } /** + * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> + * + * @param node the node that has just been added to the document + */ + void notifyNodeAdded(final DomNode node) { + if (node instanceof DomElement) { + addMappedElement((DomElement) node, true); + + if (node instanceof BaseFrameElement) { + frameElements_.add((BaseFrameElement) node); + } + for (final HtmlElement child : node.getHtmlElementDescendants()) { + if (child instanceof BaseFrameElement) { + frameElements_.add((BaseFrameElement) child); + } + } + + if ("base".equals(node.getNodeName())) { + calculateBase(); + } + } + node.onAddedToPage(); + } + + /** + * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> + * + * @param node the node that has just been removed from the tree + */ + void notifyNodeRemoved(final DomNode node) { + if (node instanceof HtmlElement) { + removeMappedElement((HtmlElement) node, true, true); + + if (node instanceof BaseFrameElement) { + frameElements_.remove(node); + } + for (final HtmlElement child : node.getHtmlElementDescendants()) { + if (child instanceof BaseFrameElement) { + frameElements_.remove(child); + } + } + + if ("base".equals(node.getNodeName())) { + calculateBase(); + } + } + } + + /** * Adds an element to the ID and name maps, if necessary. * @param element the element to be added to the ID and name maps */ @@ -1788,7 +1847,23 @@ private void addElement(final Map<String, SortedSet<DomElement>> map, final DomElement element, final String attribute, final boolean recurse) { - final String value = element.getAttribute(attribute); + // first try real attributes + String value = element.getAttribute(attribute); + + if (DomElement.ATTRIBUTE_NOT_DEFINED == value && !(element instanceof HtmlApplet)) { + // second try are JavaScript attributes + // ...but applets are a bit special so ignore them + final ScriptableObject scriptObject = element.getScriptObject(); + // we have to make sure the scriptObject has a slot for the given attribute. + // just using get() may use e.g. getWithPreemption(). + if (scriptObject.has(attribute, scriptObject)) { + final Object jsValue = scriptObject.get(attribute, scriptObject); + if (jsValue != null && jsValue != Scriptable.NOT_FOUND && jsValue instanceof String) { + value = (String) jsValue; + } + } + } + if (DomElement.ATTRIBUTE_NOT_DEFINED != value) { SortedSet<DomElement> elements = map.get(value); if (elements == null) { @@ -1829,8 +1904,24 @@ } private void removeElement(final Map<String, SortedSet<DomElement>> map, final DomElement element, - final String att, final boolean recurse) { - final String value = element.getAttribute(att); + final String attribute, final boolean recurse) { + // first try real attributes + String value = element.getAttribute(attribute); + + if (DomElement.ATTRIBUTE_NOT_DEFINED == value && !(element instanceof HtmlApplet)) { + // second try are JavaScript attributes + // ...but applets are a bit special so ignore them + final ScriptableObject scriptObject = element.getScriptObject(); + // we have to make sure the scriptObject has a slot for the given attribute. + // just using get() may use e.g. getWithPreemption(). + if (scriptObject.has(attribute, scriptObject)) { + final Object jsValue = scriptObject.get(attribute, scriptObject); + if (jsValue != null && jsValue != Scriptable.NOT_FOUND && jsValue instanceof String) { + value = (String) jsValue; + } + } + } + if (!StringUtils.isEmpty(value)) { final SortedSet<DomElement> elements = map.remove(value); if (elements != null && (elements.size() != 1 || !elements.contains(element))) { @@ -1840,29 +1931,20 @@ } if (recurse) { for (final DomElement child : element.getChildElements()) { - removeElement(map, child, att, true); + removeElement(map, child, attribute, true); } } } /** - * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> - * - * @param node the node that has just been added to the document + * Indicates if the attribute name indicates that the owning element is mapped. + * @param document the owning document + * @param attributeName the name of the attribute to consider + * @return <code>true</code> if the owning element should be mapped in its owning page */ - void notifyNodeAdded(final DomNode node) { - if (node instanceof DomElement) { - addMappedElement((DomElement) node, true); - - if (node instanceof BaseFrameElement) { - frameElements_.add((BaseFrameElement) node); - } - - if ("base".equals(node.getNodeName())) { - calculateBase(); - } - } - node.onAddedToPage(); + static boolean isMappedElement(final Document document, final String attributeName) { + return (document instanceof HtmlPage) + && ("name".equals(attributeName) || "id".equals(attributeName)); } private void calculateBase() { @@ -1883,25 +1965,6 @@ } /** - * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> - * - * @param node the node that has just been removed from the tree - */ - void notifyNodeRemoved(final DomNode node) { - if (node instanceof HtmlElement) { - removeMappedElement((HtmlElement) node, true, true); - - if (node instanceof BaseFrameElement) { - frameElements_.remove(node); - } - - if ("base".equals(node.getNodeName())) { - calculateBase(); - } - } - } - - /** * Loads the content of the contained frames. This is done after the page is completely loaded, to allow script * contained in the frames to reference elements from the page located after the closing </frame> tag. * @throws FailingHttpStatusCodeException if the server returns a failing status code AND the property @@ -2059,9 +2122,17 @@ */ @Override public HtmlPage cloneNode(final boolean deep) { - final HtmlPage result = (HtmlPage) super.cloneNode(deep); + // we need the ScriptObject clone before cloning the kids. + final HtmlPage result = (HtmlPage) super.cloneNode(false); final SimpleScriptable jsObjClone = ((SimpleScriptable) getScriptObject()).clone(); jsObjClone.setDomNode(result); + + // if deep, clone the kids too. + if (deep) { + for (DomNode child = getFirstChild(); child != null; child = child.getNextSibling()) { + result.appendChild(child.cloneNode(true)); + } + } return result; } @@ -2174,15 +2245,6 @@ } /** - * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> - * - * @return true if the OnbeforeunloadHandler has accepted to change the page - */ - public boolean isOnbeforeunloadAccepted() { - return executeEventHandlersIfNeeded(Event.TYPE_BEFORE_UNLOAD); - } - - /** * Returns <tt>true</tt> if an HTML parser is operating on this page, adding content to it. * @return <tt>true</tt> if an HTML parser is operating on this page, adding content to it */ @@ -2208,7 +2270,7 @@ * Returns <tt>true</tt> if an HTML parser is parsing a non-inline HTML snippet to add content * to this page. Non-inline content is content that is parsed for the page, but not in the * same stream as the page itself -- basically anything other than <tt>document.write()</tt> - * or <tt>document.writeln()</tt>: <tt>innerHTML</tt>, <tt>otherHTML</tt>, + * or <tt>document.writeln()</tt>: <tt>innerHTML</tt>, <tt>outerHTML</tt>, * <tt>document.createElement()</tt>, etc. * * @return <tt>true</tt> if an HTML parser is parsing a non-inline HTML snippet to add content @@ -2230,11 +2292,6 @@ */ void registerSnippetParsingEnd() { snippetParserCount_--; - - // maybe the stream has added a iframe tag - if (0 == snippetParserCount_) { - loadFrames(); - } } /** @@ -2367,17 +2424,6 @@ } /** - * Indicates if the attribute name indicates that the owning element is mapped. - * @param document the owning document - * @param attributeName the name of the attribute to consider - * @return <code>true</code> if the owning element should be mapped in its owning page - */ - static boolean isMappedElement(final Document document, final String attributeName) { - return (document instanceof HtmlPage) - && ("name".equals(attributeName) || "id".equals(attributeName)); - } - - /** * Returns whether the current page mode is in quirks mode or in standards mode. * @return true for quirks mode, false for standards mode */ Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Node.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Node.java 2014-01-31 21:55:16 UTC (rev 9095) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Node.java 2014-02-01 11:57:37 UTC (rev 9096) @@ -217,15 +217,26 @@ // is the node allowed here? if (!isNodeInsertable(childNode)) { - // IE silently ignores it if (getBrowserVersion().hasFeature(JS_APPEND_CHILD_THROWS_NO_EXCEPTION_FOR_WRONG_NODE)) { - return childObject; + return childNode; } - throw asJavaScriptException( new DOMException("Node cannot be inserted at the specified point in the hierarchy", DOMException.HIERARCHY_REQUEST_ERR)); } + if (childObject instanceof DomDocumentFragment) { + final DomDocumentFragment fragment = (DomDocumentFragment) childObject; + for (final DomNode child : fragment.getChildren()) { + if (!isNodeInsertable((Node) child.getScriptObject())) { + if (getBrowserVersion().hasFeature(JS_APPEND_CHILD_THROWS_NO_EXCEPTION_FOR_WRONG_NODE)) { + return childNode; + } + throw asJavaScriptException( + new DOMException("Node cannot be inserted at the specified point in the hierarchy", + DOMException.HIERARCHY_REQUEST_ERR)); + } + } + } // Get XML node for the DOM node passed in final DomNode childDomNode = childNode.getDomNodeOrDie(); @@ -248,7 +259,7 @@ } initInlineFrameIfNeeded(childDomNode); - for (final DomNode domNode : childDomNode.getChildren()) { + for (final DomNode domNode : childDomNode.getDescendants()) { initInlineFrameIfNeeded(domNode); } } @@ -302,50 +313,6 @@ } /** - * Indicates if the node can be inserted. - * @param childObject the node - * @return <code>false</code> if it is not allowed here - */ - private boolean isNodeInsertable(final Node childObject) { - return !(childObject instanceof HTMLHtmlElement); - } - - /** - * Clones this node. - * @param deep if <tt>true</tt>, recursively clones all descendants - * @return the newly cloned node - */ - @JsxFunction - public Object cloneNode(final boolean deep) { - final DomNode domNode = getDomNodeOrDie(); - final DomNode clonedNode = domNode.cloneNode(deep); - - final Node jsClonedNode = getJavaScriptNode(clonedNode); - if (getBrowserVersion().hasFeature(JS_CLONE_NODE_COPIES_EVENT_LISTENERS)) { - // need to copy the event listener when they exist - copyEventListenersWhenNeeded(domNode, clonedNode); - } - return jsClonedNode; - } - - private void copyEventListenersWhenNeeded(final DomNode domNode, final DomNode clonedNode) { - final Node jsNode = (Node) domNode.getScriptObject(); - if (jsNode != null) { - final Node jsClonedNode = getJavaScriptNode(clonedNode); - jsClonedNode.getEventListenersContainer().copyFrom(jsNode.getEventListenersContainer()); - } - - // look through the children - DomNode child = domNode.getFirstChild(); - DomNode clonedChild = clonedNode.getFirstChild(); - while (child != null && clonedChild != null) { - copyEventListenersWhenNeeded(child, clonedChild); - child = child.getNextSibling(); - clonedChild = clonedChild.getNextSibling(); - } - } - - /** * Add a DOM node as a child to this node before the referenced node. * If the referenced node is null, append to the end. * @param context the JavaScript context @@ -375,7 +342,7 @@ else { refChildObject = Undefined.instance; } - Object appendedChild = null; + Object insertedChild = null; if (newChildObject instanceof Node) { final Node newChild = (Node) newChildObject; @@ -383,22 +350,29 @@ // is the node allowed here? if (!isNodeInsertable(newChild)) { - // IE silently ignores it if (getBrowserVersion().hasFeature(JS_APPEND_CHILD_THROWS_NO_EXCEPTION_FOR_WRONG_NODE)) { - return newChildNode; + return newChild; } - throw Context.reportRuntimeError("Node cannot be inserted at the specified point in the hierarchy"); + throw asJavaScriptException( + new DOMException("Node cannot be inserted at the specified point in the hierarchy", + DOMException.HIERARCHY_REQUEST_ERR)); } - if (newChildNode instanceof DomDocumentFragment) { final DomDocumentFragment fragment = (DomDocumentFragment) newChildNode; for (final DomNode child : fragment.getChildren()) { - insertBeforeImpl(new Object[] {child.getScriptObject(), refChildObject}); + if (!isNodeInsertable((Node) child.getScriptObject())) { + if (getBrowserVersion().hasFeature(JS_APPEND_CHILD_THROWS_NO_EXCEPTION_FOR_WRONG_NODE)) { + return newChild; + } + throw asJavaScriptException( + new DOMException("Node cannot be inserted at the specified point in the hierarchy", + DOMException.HIERARCHY_REQUEST_ERR)); + } } - return newChildObject; } + + // extract refChild final DomNode refChildNode; - // IE accepts non standard calls with only one argument if (refChildObject == Undefined.instance) { if (getBrowserVersion().hasFeature(JS_NODE_INSERT_BEFORE_THROW_EXCEPTION_FOR_EXTRA_ARGUMENT)) { if (args.length > 1) { @@ -423,40 +397,27 @@ } final DomNode domNode = getDomNodeOrDie(); - // Append the child to the parent node - if (refChildNode != null) { - refChildNode.insertBefore(newChildNode); - appendedChild = newChildObject; - } - else { - domNode.appendChild(newChildNode); - appendedChild = newChildObject; - } - // if parentNode is null in IE, create a DocumentFragment to be the parentNode + domNode.insertBefore(newChildNode, refChildNode); + insertedChild = newChild; + + // if parentNode is null, create a DocumentFragment to be the parentNode if (domNode.getParentNode() == null && getBrowserVersion() .hasFeature(JS_APPEND_CHILD_CREATE_DOCUMENT_FRAGMENT_PARENT)) { final DomDocumentFragment fragment = domNode.getPage().createDomDocumentFragment(); fragment.appendChild(domNode); } } - return appendedChild; + return insertedChild; } /** - * This method provides a way to determine whether two Node references returned by - * the implementation reference the same object. - * When two Node references are references to the same object, even if through a proxy, - * the references may be used completely interchangeably, such that all attributes - * have the same values and calling the same DOM method on either reference always has exactly the same effect. - * - * @param other the node to test against - * - * @return whether this node is the same node as the given one + * Indicates if the node can be inserted. + * @param childObject the node + * @return <code>false</code> if it is not allowed here */ - @JsxFunction({ @WebBrowser(CHROME), @WebBrowser(value = IE, minVersion = 9) }) - public boolean isSameNode(final Object other) { - return other == this; + private boolean isNodeInsertable(final Node childObject) { + return !(childObject instanceof HTMLHtmlElement); } /** @@ -480,6 +441,105 @@ } /** + * Replaces a child DOM node with another DOM node. + * @param newChildObject the node to add as a child of this node + * @param oldChildObject the node to remove as a child of this node + * @return the removed child node + */ + @JsxFunction + public Object replaceChild(final Object newChildObject, final Object oldChildObject) { + Object removedChild = null; + + if (newChildObject instanceof DocumentFragment) { + final DocumentFragment fragment = (DocumentFragment) newChildObject; + Node firstNode = null; + final Node refChildObject = ((Node) oldChildObject).getNextSibling(); + for (final DomNode node : fragment.getDomNodeOrDie().getChildren()) { + if (firstNode == null) { + replaceChild(node.getScriptObject(), oldChildObject); + firstNode = (Node) node.getScriptObject(); + } + else { + insertBeforeImpl(new Object[] {node.getScriptObject(), refChildObject}); + } + } + if (firstNode == null) { + removeChild(oldChildObject); + } + removedChild = oldChildObject; + } + else if (newChildObject instanceof Node && oldChildObject instanceof Node) { + final Node newChild = (Node) newChildObject; + + // is the node allowed here? + if (!isNodeInsertable(newChild)) { + throw Context.reportRuntimeError("Node cannot be inserted at the specified point in the hierarchy"); + } + + // Get XML nodes for the DOM nodes passed in + final DomNode newChildNode = newChild.getDomNodeOrDie(); + final DomNode oldChildNode = ((Node) oldChildObject).getDomNodeOrDie(); + + // Replace the old child with the new child. + oldChildNode.replace(newChildNode); + removedChild = oldChildObject; + } + + return removedChild; + } + + /** + * Clones this node. + * @param deep if <tt>true</tt>, recursively clones all descendants + * @return the newly cloned node + */ + @JsxFunction + public Object cloneNode(final boolean deep) { + final DomNode domNode = getDomNodeOrDie(); + final DomNode clonedNode = domNode.cloneNode(deep); + + final Node jsClonedNode = getJavaScriptNode(clonedNode); + if (getBrowserVersion().hasFeature(JS_CLONE_NODE_COPIES_EVENT_LISTENERS)) { + // need to copy the event listener when they exist + copyEventListenersWhenNeeded(domNode, clonedNode); + } + return jsClonedNode; + } + + private void copyEventListenersWhenNeeded(final DomNode domNode, final DomNode clonedNode) { + final Node jsNode = (Node) domNode.getScriptObject(); + if (jsNode != null) { + final Node jsClonedNode = getJavaScriptNode(clonedNode); + jsClonedNode.getEventListenersContainer().copyFrom(jsNode.getEventListenersContainer()); + } + + // look through the children + DomNode child = domNode.getFirstChild(); + DomNode clonedChild = clonedNode.getFirstChild(); + while (child != null && clonedChild != null) { + copyEventListenersWhenNeeded(child, clonedChild); + child = child.getNextSibling(); + clonedChild = clonedChild.getNextSibling(); + } + } + + /** + * This method provides a way to determine whether two Node references returned by + * the implementation reference the same object. + * When two Node references are references to the same object, even if through a proxy, + * the references may be used completely interchangeably, such that all attributes + * have the same values and calling the same DOM method on either reference always has exactly the same effect. + * + * @param other the node to test against + * + * @return whether this node is the same node as the given one + */ + @JsxFunction({ @WebBrowser(CHROME), @WebBrowser(value = IE, minVersion = 9) }) + public boolean isSameNode(final Object other) { + return other == this; + } + + /** * Returns whether this node has any children. * @return boolean true if this node has any children, false otherwise */ @@ -543,54 +603,6 @@ } /** - * Replaces a child DOM node with another DOM node. - * @param newChildObject the node to add as a child of this node - * @param oldChildObject the node to remove as a child of this node - * @return the removed child node - */ - @JsxFunction - public Object replaceChild(final Object newChildObject, final Object oldChildObject) { - Object removedChild = null; - - if (newChildObject instanceof DocumentFragment) { - final DocumentFragment fragment = (DocumentFragment) newChildObject; - Node firstNode = null; - final Node refChildObject = ((Node) oldChildObject).getNextSibling(); - for (final DomNode node : fragment.getDomNodeOrDie().getChildren()) { - if (firstNode == null) { - replaceChild(node.getScriptObject(), oldChildObject); - firstNode = (Node) node.getScriptObject(); - } - else { - insertBeforeImpl(new Object[] {node.getScriptObject(), refChildObject}); - } - } - if (firstNode == null) { - removeChild(oldChildObject); - } - removedChild = oldChildObject; - } - else if (newChildObject instanceof Node && oldChildObject instanceof Node) { - final Node newChild = (Node) newChildObject; - - // is the node allowed here? - if (!isNodeInsertable(newChild)) { - throw Context.reportRuntimeError("Node cannot be inserted at the specified point in the hierarchy"); - } - - // Get XML nodes for the DOM nodes passed in - final DomNode newChildNode = newChild.getDomNodeOrDie(); - final DomNode oldChildNode = ((Node) oldChildObject).getDomNodeOrDie(); - - // Replace the old child with the new child. - oldChildNode.replace(newChildNode); - removedChild = oldChildObject; - } - - return removedChild; - } - - /** * Returns this node's parent node. * @return this node's parent node */ Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/NodeList.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/NodeList.java 2014-01-31 21:55:16 UTC (rev 9095) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/NodeList.java 2014-02-01 11:57:37 UTC (rev 9096) @@ -39,6 +39,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlAttributeChangeEvent; import com.gargoylesoftware.htmlunit.html.HtmlAttributeChangeListener; import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable; import com.gargoylesoftware.htmlunit.javascript.configuration.JavaScriptConfiguration; import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass; @@ -220,8 +221,13 @@ final DomNode domNode = getDomNodeOrNull(); if (domNode != null) { domNode.addDomChangeListener(listener); - if (attributeChangeSensitive_ && domNode instanceof HtmlElement) { - ((HtmlElement) domNode).addHtmlAttributeChangeListener(listener); + if (attributeChangeSensitive_) { + if (domNode instanceof HtmlElement) { + ((HtmlElement) domNode).addHtmlAttributeChangeListener(listener); + } + else if (domNode instanceof HtmlPage) { + ((HtmlPage) domNode).addHtmlAttributeChangeListener(listener); + } } } listenerRegistered_ = true; Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement.java 2014-01-31 21:55:16 UTC (rev 9095) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement.java 2014-02-01 11:57:37 UTC (rev 9096) @@ -76,7 +76,6 @@ import com.gargoylesoftware.htmlunit.ScriptResult; import com.gargoylesoftware.htmlunit.SgmlPage; import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.BaseFrameElement; import com.gargoylesoftware.htmlunit.html.DomAttr; import com.gargoylesoftware.htmlunit.html.DomCharacterData; import com.gargoylesoftware.htmlunit.html.DomComment; @@ -713,6 +712,22 @@ } /** + * Gets the attributes of the element in the form of a {@link org.xml.sax.Attributes}. + * @param element the element to read the attributes from + * @return the attributes + */ + protected AttributesImpl readAttributes(final HtmlElement element) { + final AttributesImpl attributes = new AttributesImpl(); + for (final DomAttr entry : element.getAttributesMap().values()) { + final String name = entry.getName(); + final String value = entry.getValue(); + attributes.addAttribute(null, name, name, null, value); + } + + return attributes; + } + + /** * Removes this object from the document hierarchy. * @param removeChildren whether to remove children or no * @return a reference to the object that is removed @@ -943,8 +958,8 @@ } /** - * Replace all children elements of this element with the supplied value. - * @param value the new value for the contents of this node + * Replaces all child elements of this element with the supplied value. + * @param value the new value for the contents of this element */ @JsxSetter public void setInnerHTML(final Object value) { @@ -952,21 +967,12 @@ domNode.removeAllChildren(); - // null && IE -> add child - // null && non-IE -> Don't add - // '' -> Don't add final boolean addChildForNull = getBrowserVersion().hasFeature(JS_INNER_HTML_ADD_CHILD_FOR_NULL_VALUE); if ((value == null && addChildForNull) || (value != null && !"".equals(value))) { final String valueAsString = Context.toString(value); - parseHtmlSnippet(domNode, true, valueAsString); + parseHtmlSnippet(domNode, valueAsString); - for (final DomNode node : domNode.getDescendants()) { - if (node instanceof BaseFrameElement) { - ((BaseFrameElement) node).markLoadSrcWhenAddedToPage(); - } - } - final boolean createFragment = getBrowserVersion().hasFeature(JS_INNER_HTML_CREATES_DOC_FRAGMENT_AS_PARENT); // if the parentNode has null parentNode in IE, // create a DocumentFragment to be the parentNode's parentNode. @@ -978,8 +984,8 @@ } /** - * Replace all children elements of this element with the supplied value. - * @param value the new value for the contents of this node + * Replaces all child elements of this element with the supplied text value. + * @param value the new value for the contents of this element */ @JsxSetter({ @WebBrowser(IE), @WebBrowser(CHROME) }) public void setInnerText(final String value) { @@ -1009,8 +1015,8 @@ } /** - * Replace all children elements of this element with the supplied value. - * @param value the new value for the contents of this node + * Replaces all child elements of this element with the supplied text value. + * @param value the new value for the contents of this element */ @Override public void setTextContent(final Object value) { @@ -1018,52 +1024,57 @@ } /** - * Replace all children elements of this element with the supplied value. - * Sets the outerHTML of the node. - * @param value the new value for replacing this node - * @see <a href="http://msdn.microsoft.com/en-us/library/ms534310.aspx">MSDN documentation</a> + * Replaces this element (including all child elements) with the supplied value. + * @param value the new value for replacing this element */ @JsxSetter public void setOuterHTML(final String value) { final DomNode domNode = getDomNodeOrDie(); + final DomNode parent = do... [truncated message content] |