From: <mgu...@us...> - 2013-04-11 14:29:06
|
Revision: 8218 http://sourceforge.net/p/htmlunit/code/8218 Author: mguillem Date: 2013-04-11 14:29:01 +0000 (Thu, 11 Apr 2013) Log Message: ----------- JavaScript: load the source of a dynamically created HTML(I)FrameElement first when it is added to the DOM. 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/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLIFrameElement2Test.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2013-04-10 16:07:28 UTC (rev 8217) +++ trunk/htmlunit/src/changes/changes.xml 2013-04-11 14:29:01 UTC (rev 8218) @@ -8,6 +8,9 @@ <body> <release version="2.13" date="???" description="Bugfixes"> + <action type="fix" dev="mguillem"> + JavaScript: load the source of a dynamically created HTML(I)FrameElement first when it is added to the DOM. + </action> <action type="fix" dev="rbri" issue="1501"> JavaScript: IE 9 supports String.trim(). </action> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/BaseFrameElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/BaseFrameElement.java 2013-04-10 16:07:28 UTC (rev 8217) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/BaseFrameElement.java 2013-04-11 14:29:01 UTC (rev 8218) @@ -50,9 +50,10 @@ public abstract class BaseFrameElement extends HtmlElement { private static final Log LOG = LogFactory.getLog(BaseFrameElement.class); - private WebWindow enclosedWindow_; + private FrameWindow enclosedWindow_; private boolean contentLoaded_ = false; private boolean createdByJavascript_ = false; + private boolean loadSrcWhenAddedToPage_ = false; /** * Creates an instance of BaseFrame. @@ -70,7 +71,7 @@ } private void init() { - WebWindow enclosedWindow = null; + FrameWindow enclosedWindow = null; try { if (getPage() instanceof HtmlPage) { // if loaded as part of XHR.responseXML, don't load content enclosedWindow = new FrameWindow(this); @@ -321,7 +322,7 @@ * Gets the window enclosed in this frame. * @return the window enclosed in this frame */ - public WebWindow getEnclosedWindow() { + public FrameWindow getEnclosedWindow() { return enclosedWindow_; } @@ -345,28 +346,46 @@ super.setAttributeNS(namespaceURI, qualifiedName, attributeValue); if ("src".equals(qualifiedName) && !WebClient.ABOUT_BLANK.equals(attributeValue)) { - final JavaScriptEngine jsEngine = getPage().getWebClient().getJavaScriptEngine(); - // When src is set from a script, loading is postponed until script finishes - // in fact this implementation is probably wrong: JavaScript URL should be - // first evaluated and only loading, when any, should be postponed. - if (!jsEngine.isScriptRunning() || attributeValue.startsWith(JavaScriptURLConnection.JAVASCRIPT_PREFIX)) { - loadInnerPageIfPossible(attributeValue); + if (isDirectlyAttachedToPage()) { + loadSrc(); } else { - final String src = attributeValue; - final PostponedAction action = new PostponedAction(getPage()) { - @Override - public void execute() throws Exception { - if (getSrcAttribute().equals(src)) { - loadInnerPage(); - } - } - }; - jsEngine.addPostponedAction(action); + loadSrcWhenAddedToPage_ = true; } } } + private void loadSrc() { + loadSrcWhenAddedToPage_ = false; + final String src = getSrcAttribute(); + + final JavaScriptEngine jsEngine = getPage().getWebClient().getJavaScriptEngine(); + // When src is set from a script, loading is postponed until script finishes + // in fact this implementation is probably wrong: JavaScript URL should be + // first evaluated and only loading, when any, should be postponed. + if (!jsEngine.isScriptRunning() || src.startsWith(JavaScriptURLConnection.JAVASCRIPT_PREFIX)) { + loadInnerPageIfPossible(src); + } + else { + final PostponedAction action = new PostponedAction(getPage()) { + @Override + public void execute() throws Exception { + if (getSrcAttribute().equals(src)) { + loadInnerPage(); + } + } + }; + jsEngine.addPostponedAction(action); + } + } + + @Override + protected void onAddedToPage() { + if (loadSrcWhenAddedToPage_) { + loadSrc(); + } + } + /** * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> * Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java 2013-04-10 16:07:28 UTC (rev 8217) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java 2013-04-11 14:29:01 UTC (rev 8218) @@ -830,6 +830,7 @@ newnode.previousSibling_ = null; newnode.firstChild_ = null; newnode.scriptObject_ = null; + newnode.directlyAttachedToPage_ = false; // if deep, clone the kids too. if (deep) { @@ -914,19 +915,20 @@ final boolean wasAlreadyAttached = domNode.isDirectlyAttachedToPage(); domNode.directlyAttachedToPage_ = isDirectlyAttachedToPage(); - // trigger events - if (!(this instanceof DomDocumentFragment) && (getPage() instanceof HtmlPage)) { - ((HtmlPage) getPage()).notifyNodeAdded(domNode); - } + if (isDirectlyAttachedToPage()) { + // trigger events + if (!(this instanceof DomDocumentFragment) && (getPage() instanceof HtmlPage)) { + ((HtmlPage) getPage()).notifyNodeAdded(domNode); + } - // a node that is already "complete" (ie not being parsed) and not yet attached - if (!domNode.isBodyParsed() && isDirectlyAttachedToPage() && !wasAlreadyAttached) { - domNode.onAddedToPage(); - for (final DomNode child : domNode.getDescendants()) { - child.directlyAttachedToPage_ = true; - child.onAllChildrenAddedToPage(true); + // 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); } - domNode.onAllChildrenAddedToPage(true); } fireNodeAdded(this, domNode); Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlElement.java 2013-04-10 16:07:28 UTC (rev 8217) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlElement.java 2013-04-11 14:29:01 UTC (rev 8218) @@ -133,7 +133,8 @@ final String oldAttributeValue = getAttribute(qualifiedName); - final boolean mappedElement = HtmlPage.isMappedElement(getOwnerDocument(), qualifiedName); + final boolean mappedElement = isDirectlyAttachedToPage() + && HtmlPage.isMappedElement(getOwnerDocument(), qualifiedName); if (mappedElement) { ((HtmlPage) getPage()).removeMappedElement(this); } Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java 2013-04-10 16:07:28 UTC (rev 8217) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java 2013-04-11 14:29:01 UTC (rev 8218) @@ -141,6 +141,7 @@ private String originalCharset_; private Map<String, SortedSet<DomElement>> idMap_ = new HashMap<String, SortedSet<DomElement>>(); private Map<String, SortedSet<DomElement>> nameMap_ = new HashMap<String, SortedSet<DomElement>>(); + private SortedSet<BaseFrameElement> frameElements_ = new TreeSet<BaseFrameElement>(documentPositionComparator); private HtmlElement elementWithFocus_; private int parserCount_; private int snippetParserCount_; @@ -1451,13 +1452,8 @@ */ public List<FrameWindow> getFrames() { final List<FrameWindow> list = new ArrayList<FrameWindow>(); - final WebWindow enclosingWindow = getEnclosingWindow(); - for (final WebWindow window : getWebClient().getWebWindows()) { - // quite strange but for a TopLevelWindow parent == self - if (enclosingWindow == window.getParentWindow() - && enclosingWindow != window) { - list.add((FrameWindow) window); - } + for (final BaseFrameElement frameElement : frameElements_) { + list.add(frameElement.getEnclosedWindow()); } return list; } @@ -1740,10 +1736,8 @@ * @param recurse indicates if children must be added too */ void addMappedElement(final DomElement element, final boolean recurse) { - if (isDescendant(element)) { - addElement(idMap_, element, "id", recurse); - addElement(nameMap_, element, "name", recurse); - } + addElement(idMap_, element, "id", recurse); + addElement(nameMap_, element, "name", recurse); } private void addElement(final Map<String, SortedSet<DomElement>> map, final DomElement element, @@ -1814,6 +1808,10 @@ if (node instanceof DomElement) { addMappedElement((DomElement) node, true); + if (node instanceof BaseFrameElement) { + frameElements_.add((BaseFrameElement) node); + } + if ("base".equals(node.getNodeName())) { calculateBase(); } @@ -1846,6 +1844,11 @@ 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(); } @@ -2056,13 +2059,6 @@ final HtmlPage result = (HtmlPage) super.cloneNode(deep); final SimpleScriptable jsObjClone = ((SimpleScriptable) getScriptObject()).clone(); jsObjClone.setDomNode(result); - if (deep) { - // fix up idMap_ and result's idMap_s - for (final HtmlElement child : result.getHtmlElementDescendants()) { - removeMappedElement(child); - result.addMappedElement(child); - } - } return result; } Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLIFrameElement2Test.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLIFrameElement2Test.java 2013-04-10 16:07:28 UTC (rev 8217) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLIFrameElement2Test.java 2013-04-11 14:29:01 UTC (rev 8218) @@ -238,9 +238,26 @@ } /** + * A frame element that is not appended to the document should not be loaded. * @throws Exception if an error occurs */ @Test + @Alerts("created") + public void documentCreateElement_noAppendNoLoad() throws Exception { + final String html = "<html><body><script>\n" + + "var myFrame = document.createElement('iframe');\n" + + "myFrame.src = 'notExisting.html';\n" + + "alert('created');\n" + + "</script></body></html>"; + + loadPageWithAlerts2(html); + assertEquals(1, getMockWebConnection().getRequestCount()); + } + + /** + * @throws Exception if an error occurs + */ + @Test @Alerts(DEFAULT = { "createIFrame", "loaded", "foo" }, IE = { "createIFrame" }) public void documentCreateElement_onLoad3() throws Exception { final String html = |