From: <rb...@us...> - 2013-11-13 19:50:37
|
Revision: 8775 http://sourceforge.net/p/htmlunit/code/8775 Author: rbri Date: 2013-11-13 19:50:32 +0000 (Wed, 13 Nov 2013) Log Message: ----------- the rest of the XMLHttpRequest fixes; all NYI annotations are gone, FF24 and IE10 are manly done also Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/WebDriverTestCase.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestCORSTest.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestTest.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2013-11-13 17:17:39 UTC (rev 8774) +++ trunk/htmlunit/src/changes/changes.xml 2013-11-13 19:50:32 UTC (rev 8775) @@ -8,6 +8,22 @@ <body> <release version="2.14" date="???" description="Bugfixes"> + <action type="fix" dev="rbri"> + JavaScript: XMLHttpRequest CORS handling fixed, some new tests added and implementation fixed. + </action> + <action type="fix" dev="rbri"> + JavaScript: Siplified/fixed implementation of the onReadyStateChange handler call. + </action> + <action type="fix" dev="rbri"> + JavaScript: XMLHttpRequest status and statusText throws an exception in IE8 depending + on the state of the request. + </action> + <action type="fix" dev="rbri"> + JavaScript: XMLHttpRequest property 'withCredentials' is not setable in sync mode (browsers specific). + </action> + <action type="fix" dev="rbri"> + JavaScript: XMLHttpRequest property 'withCredentials' is not available in IE8. + </action> <action type="fix" dev="asashour" issue="1553"> JavaScript: element.setAttributeNode() applies to XML as well. </action> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java 2013-11-13 17:17:39 UTC (rev 8774) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java 2013-11-13 19:50:32 UTC (rev 8775) @@ -1514,6 +1514,19 @@ @BrowserFeature(@WebBrowser(FF)) XHR_TRIGGER_ONLOAD_ON_COMPLETED, + /** Indicates that the "*" pattern is allowed when withCredential is enabled. */ + @BrowserFeature(@WebBrowser(IE)) + XHR_WITHCREDENTIALS_ALLOW_ORIGIN_ALL, + + /** Indicates that the propery 'withCredentials is not writable for sync requests. */ + @BrowserFeature(@WebBrowser(value = FF, maxVersion = 23)) + XHR_WITHCREDENTIALS_SYNC_NOT_WRITEABLE, + + /** Indicates that the propery 'withCredentials is not writable for sync requests. + * Setting the property throws an exception. */ + @BrowserFeature(@WebBrowser(value = FF, minVersion = 24)) + XHR_WITHCREDENTIALS_SYNC_NOT_WRITEABLE_EXCEPTION, + /** Indicates that the 'SelectionNamespaces' property is supported by XPath expressions. */ @BrowserFeature({ @WebBrowser(IE), @WebBrowser(CHROME) }) XPATH_SELECTION_NAMESPACES, Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java 2013-11-13 17:17:39 UTC (rev 8774) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java 2013-11-13 19:50:32 UTC (rev 8775) @@ -26,6 +26,9 @@ import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.XHR_ORIGIN_HEADER; import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.XHR_STATUS_THROWS_EXCEPTION_WHEN_UNSET; import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.XHR_TRIGGER_ONLOAD_ON_COMPLETED; +import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.XHR_WITHCREDENTIALS_SYNC_NOT_WRITEABLE; +import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.XHR_WITHCREDENTIALS_SYNC_NOT_WRITEABLE_EXCEPTION; +import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.XHR_WITHCREDENTIALS_ALLOW_ORIGIN_ALL; import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.CHROME; import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.FF; import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.IE; @@ -113,6 +116,7 @@ private static final String HEADER_ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; private static final String HEADER_ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; private static final String HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + private static final String HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; private static final String HEADER_ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; private static final String ALLOW_ORIGIN_ALL = "*"; @@ -480,6 +484,11 @@ throw Context.reportRuntimeError("URL for XHR.open can't be empty!"); } + if (!async && getWithCredentials()) { + throw Context.reportRuntimeError( + "open() in sync mode is not possible because 'withCredentials' is set to true"); + } + final String url = Context.toString(urlParam); // (URL + Method + User + Password) become a WebRequest instance. @@ -690,8 +699,20 @@ } boolean allowOriginResponse = true; if (crossOriginResourceSharing) { - final String value = webResponse.getResponseHeaderValue(HEADER_ACCESS_CONTROL_ALLOW_ORIGIN); - allowOriginResponse = ALLOW_ORIGIN_ALL.equals(value) || originHeaderValue.equals(value); + String value = webResponse.getResponseHeaderValue(HEADER_ACCESS_CONTROL_ALLOW_ORIGIN); + allowOriginResponse = originHeaderValue.equals(value); + if (getWithCredentials()) { + allowOriginResponse = allowOriginResponse + || (getBrowserVersion().hasFeature(XHR_WITHCREDENTIALS_ALLOW_ORIGIN_ALL) + && ALLOW_ORIGIN_ALL.equals(value)); + + // second step: check the allow-credentials header for true + value = webResponse.getResponseHeaderValue(HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS); + allowOriginResponse = allowOriginResponse && Boolean.parseBoolean(value); + } + else { + allowOriginResponse = allowOriginResponse || ALLOW_ORIGIN_ALL.equals(value); + } } if (allowOriginResponse) { if (overriddenMimeType_ == null) { @@ -706,10 +727,12 @@ }; } } - setState(STATE_HEADERS_RECEIVED, context); - setState(STATE_LOADING, context); - setState(STATE_DONE, context); - if (!allowOriginResponse) { + if (allowOriginResponse) { + setState(STATE_HEADERS_RECEIVED, context); + setState(STATE_LOADING, context); + setState(STATE_DONE, context); + } + else { if (LOG.isDebugEnabled()) { LOG.debug("No permitted \"Access-Control-Allow-Origin\" header for URL " + webRequest_.getUrl()); } @@ -857,6 +880,14 @@ */ @JsxSetter public void setWithCredentials(final boolean withCredentials) { + if (!async_ && state_ != STATE_UNSENT) { + if (getBrowserVersion().hasFeature(XHR_WITHCREDENTIALS_SYNC_NOT_WRITEABLE_EXCEPTION)) { + throw Context.reportRuntimeError("Property 'withCredentials' not writable in sync mode."); + } + if (getBrowserVersion().hasFeature(XHR_WITHCREDENTIALS_SYNC_NOT_WRITEABLE)) { + return; + } + } withCredentials_ = withCredentials; } Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/WebDriverTestCase.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/WebDriverTestCase.java 2013-11-13 17:17:39 UTC (rev 8774) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/WebDriverTestCase.java 2013-11-13 19:50:32 UTC (rev 8775) @@ -605,7 +605,7 @@ * @throws Exception if something goes wrong */ protected final WebDriver loadPageWithAlerts2(final String html, final URL url) throws Exception { - return loadPageWithAlerts2(html, url, DEFAULT_WAIT_TIME); + return loadPageWithAlerts2(html, url, DEFAULT_WAIT_TIME * 1000); } /** Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestCORSTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestCORSTest.java 2013-11-13 17:17:39 UTC (rev 8774) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestCORSTest.java 2013-11-13 19:50:32 UTC (rev 8775) @@ -14,9 +14,6 @@ */ package com.gargoylesoftware.htmlunit.javascript.host.xml; -import static com.gargoylesoftware.htmlunit.BrowserRunner.Browser.FF17; -import static com.gargoylesoftware.htmlunit.BrowserRunner.Browser.IE8; - import java.io.IOException; import java.io.Writer; import java.net.URL; @@ -33,7 +30,6 @@ import com.gargoylesoftware.htmlunit.BrowserRunner; import com.gargoylesoftware.htmlunit.BrowserRunner.Alerts; -import com.gargoylesoftware.htmlunit.BrowserRunner.NotYetImplemented; import com.gargoylesoftware.htmlunit.WebDriverTestCase; /** @@ -399,22 +395,53 @@ * @throws Exception if the test fails. */ @Test - @Alerts(DEFAULT = { "false", "ex: withCredentials=false", "ex: withCredentials=true" }, - FF17 = { "false", "false", "false" }, - IE8 = { "undefined", "false", "true" }, - IE10 = { "false", "false", "true" }) - @NotYetImplemented(FF17) + @Alerts(DEFAULT = { "false", "false", "ex: withCredentials=true", "ex: withCredentials=false" }, + FF17 = { "false", "false", "false", "false" }, + IE8 = { "undefined", "undefined", "true", "false" }, + IE10 = { "false", "false", "true", "false" }) public void withCredentials_notSetableInSyncMode() throws Exception { - expandExpectedAlertsVariables(new URL("http://localhost:" + PORT)); + final String html = "<html><head>\n" + + "<script>\n" + + "var xhr = " + XHRInstantiation_ + ";\n" + + "function test() {\n" + + " try {\n" + + " alert(xhr.withCredentials);\n" + + " xhr.open('GET', '/foo.xml', false);\n" + + " alert(xhr.withCredentials);\n" + + " try {\n" + + " xhr.withCredentials = true;\n" + + " alert(xhr.withCredentials);\n" + + " } catch(e) { alert('ex: withCredentials=true') }\n" + + + " try {\n" + + " xhr.withCredentials = false;\n" + + " alert(xhr.withCredentials);\n" + + " } catch(e) { alert('ex: withCredentials=false') }\n" + + " } catch(ex) { alert(ex) }\n" + + "}\n" + + "</script>\n" + + "</head>\n" + + "<body onload='test()'></body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails. + */ + @Test + @Alerts(DEFAULT = { "false", "false", "true", "ex: open" }, + IE8 = { "undefined", "false", "true", "open true" }, + IE10 = { "false", "ex: withCredentials=false", "ex: withCredentials=true", "open false" }) + public void withCredentials_openFailesInSyncMode() throws Exception { final String html = "<html><head>\n" + "<script>\n" + "var xhr = " + XHRInstantiation_ + ";\n" + "function test() {\n" + " try {\n" - + " var url = 'http://' + window.location.hostname + ':" + PORT2 + "/withCredentials2';\n" - + " xhr.open('GET', url, false);\n" + " alert(xhr.withCredentials);\n" + + " try {\n" + " xhr.withCredentials = false;\n" + " alert(xhr.withCredentials);\n" @@ -424,13 +451,18 @@ + " xhr.withCredentials = true;\n" + " alert(xhr.withCredentials);\n" + " } catch(e) { alert('ex: withCredentials=true') }\n" + + + " try {\n" + + " xhr.open('GET', '/foo.xml', false);\n" + + " alert('open ' + xhr.withCredentials);\n" + + " } catch(e) { alert('ex: open') }\n" + " } catch(ex) { alert(ex) }\n" + "}\n" + "</script>\n" + "</head>\n" + "<body onload='test()'></body></html>"; - loadPageWithAlerts2(html, new URL(getDefaultUrl(), "/withCredentials1")); + loadPageWithAlerts2(html); } /** @@ -440,7 +472,6 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented(FF17) public void withCredentials() throws Exception { testWithCredentials("*", "true"); } @@ -462,7 +493,6 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented(FF17) public void withCredentialsServerSlashAtEnd() throws Exception { testWithCredentials("http://localhost:" + PORT + "/", "true"); } @@ -474,7 +504,6 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented(FF17) public void withCredentials_no_header() throws Exception { testWithCredentials("*", null); } @@ -486,7 +515,6 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented(FF17) public void withCredentials_no_header_Server() throws Exception { testWithCredentials("http://localhost:" + PORT, null); } @@ -498,7 +526,6 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented(FF17) public void withCredentials_no_header_ServerSlashAtEnd() throws Exception { testWithCredentials("http://localhost:" + PORT + "/", null); } Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestTest.java 2013-11-13 17:17:39 UTC (rev 8774) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestTest.java 2013-11-13 19:50:32 UTC (rev 8775) @@ -257,7 +257,7 @@ + " xhr.onreadystatechange = function() { alert('orsc' + xhr.readyState); };\n" + " xhr.onload = function() { alert(xhr.readyState); alert(xhr.responseText); alert(this); }\n" - + " xhr.open('GET', '" + URL_SECOND + "', true);\n" + + " xhr.open('GET', '/foo.xml', true);\n" + " alert('open-done');\n" + " xhr.send('');\n" |