From: <rb...@us...> - 2013-11-13 16:56:43
|
Revision: 8773 http://sourceforge.net/p/htmlunit/code/8773 Author: rbri Date: 2013-11-13 16:56:39 +0000 (Wed, 13 Nov 2013) Log Message: ----------- next fix on our XMLHttpRequest implementation Modified Paths: -------------- 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/javascript/host/xml/XMLHttpRequest3Test.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/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java 2013-11-13 07:50:35 UTC (rev 8772) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java 2013-11-13 16:56:39 UTC (rev 8773) @@ -1505,6 +1505,11 @@ @BrowserFeature({ @WebBrowser(FF), @WebBrowser(CHROME) }) XHR_ORIGIN_HEADER, + /** Indicates that the impl throws an exception when accessing the status/statusText + * property in unset state. */ + @BrowserFeature(@WebBrowser(value = IE, maxVersion = 8)) + XHR_STATUS_THROWS_EXCEPTION_WHEN_UNSET, + /** Indicates that the onload handler is not triggered if completed (FF). */ @BrowserFeature(@WebBrowser(FF)) XHR_TRIGGER_ONLOAD_ON_COMPLETED, 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 07:50:35 UTC (rev 8772) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java 2013-11-13 16:56:39 UTC (rev 8773) @@ -24,6 +24,7 @@ import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.XHR_ONREADYSTATECHANGE_WITH_EVENT_PARAM; import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.XHR_OPEN_ALLOW_EMTPY_URL; 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.javascript.configuration.BrowserName.CHROME; import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.FF; @@ -199,16 +200,6 @@ final Scriptable scope = stateChangeHandler_.getParentScope(); final JavaScriptEngine jsEngine = containingPage_.getWebClient().getJavaScriptEngine(); - final int nbExecutions; - if (async_ && STATE_OPENED == state) { - // quite strange but IE and FF seem both to fire state loading twice - // in async mode (at least with HTML of the unit tests) - nbExecutions = 2; - } - else { - nbExecutions = 1; - } - final Scriptable thisValue; if (browser.hasFeature(XHR_HANDLER_THIS_IS_FUNCTION)) { thisValue = stateChangeHandler_; @@ -216,25 +207,23 @@ else { thisValue = this; } - for (int i = 0; i < nbExecutions; i++) { - if (LOG.isDebugEnabled()) { - LOG.debug("Calling onreadystatechange handler for state " + state); - } - Object[] params = ArrayUtils.EMPTY_OBJECT_ARRAY; - if (browser.hasFeature(XHR_ONREADYSTATECHANGE_WITH_EVENT_PARAM)) { - params = new Object[1]; - final Event event = new Event(this, Event.TYPE_READY_STATE_CHANGE); - params[0] = event; - } + if (LOG.isDebugEnabled()) { + LOG.debug("Calling onreadystatechange handler for state " + state); + } + Object[] params = ArrayUtils.EMPTY_OBJECT_ARRAY; + if (browser.hasFeature(XHR_ONREADYSTATECHANGE_WITH_EVENT_PARAM)) { + params = new Object[1]; + final Event event = new Event(this, Event.TYPE_READY_STATE_CHANGE); + params[0] = event; + } - jsEngine.callFunction(containingPage_, stateChangeHandler_, scope, thisValue, params); - if (LOG.isDebugEnabled()) { - if (context == null) { - context = Context.getCurrentContext(); - } - LOG.debug("onreadystatechange handler: " + context.decompileFunction(stateChangeHandler_, 4)); - LOG.debug("Calling onreadystatechange handler for state " + state + ". Done."); + jsEngine.callFunction(containingPage_, stateChangeHandler_, scope, thisValue, params); + if (LOG.isDebugEnabled()) { + if (context == null) { + context = Context.getCurrentContext(); } + LOG.debug("onreadystatechange handler: " + context.decompileFunction(stateChangeHandler_, 4)); + LOG.debug("Calling onreadystatechange handler for state " + state + ". Done."); } } @@ -390,6 +379,9 @@ @JsxGetter public int getStatus() { if (state_ == STATE_UNSENT || state_ == STATE_OPENED) { + if (getBrowserVersion().hasFeature(XHR_STATUS_THROWS_EXCEPTION_WHEN_UNSET)) { + throw Context.reportRuntimeError("status not set"); + } return 0; } if (webResponse_ != null) { @@ -408,6 +400,9 @@ @JsxGetter public String getStatusText() { if (state_ == STATE_UNSENT || state_ == STATE_OPENED) { + if (getBrowserVersion().hasFeature(XHR_STATUS_THROWS_EXCEPTION_WHEN_UNSET)) { + throw Context.reportRuntimeError("statusText not set"); + } return ""; } if (webResponse_ != null) { @@ -584,6 +579,10 @@ doSend(Context.getCurrentContext()); } else { + // quite strange but IE and FF seem both to fire state loading twice + // in async mode (at least with HTML of the unit tests) + setState(STATE_OPENED, Context.getCurrentContext()); + // Create and start a thread in which to execute the request. final Scriptable startingScope = getWindow(); final ContextFactory cf = client.getJavaScriptEngine().getContextFactory(); Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest3Test.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest3Test.java 2013-11-13 07:50:35 UTC (rev 8772) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest3Test.java 2013-11-13 16:56:39 UTC (rev 8773) @@ -37,7 +37,6 @@ import com.gargoylesoftware.htmlunit.CollectingAlertHandler; import com.gargoylesoftware.htmlunit.HttpMethod; import com.gargoylesoftware.htmlunit.MockWebConnection; -import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.WebResponse; @@ -450,44 +449,4 @@ assertEquals(0, client.waitForBackgroundJavaScriptStartingBefore(1000)); assertEquals(getExpectedAlerts(), collectedAlerts); } - - /** - * @throws Exception if the test fails - */ - @Test - @Browsers(FF) - @Alerts({ "orsc1", "orsc1", "orsc2", "orsc3", "orsc4", "4", "<a>b</a>", "[object XMLHttpRequest]" }) - public void onload() throws Exception { - final String html = - "<html>\n" - + " <head>\n" - + " <script>\n" - + " function test() {\n" - + " var xhr;\n" - + " if (window.XMLHttpRequest) xhr = new XMLHttpRequest();\n" - + " else xhr = new ActiveXObject('Microsoft.XMLHTTP');\n" - + " 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.send('');\n" - + " }\n" - + " </script>\n" - + " </head>\n" - + " <body onload='test()'></body>\n" - + "</html>"; - - final String xml = "<a>b</a>"; - - final WebClient client = getWebClient(); - client.setAjaxController(new NicelyResynchronizingAjaxController()); - final List<String> collectedAlerts = new ArrayList<String>(); - client.setAlertHandler(new CollectingAlertHandler(collectedAlerts)); - final MockWebConnection conn = new MockWebConnection(); - conn.setResponse(URL_FIRST, html); - conn.setResponse(URL_SECOND, xml, "text/xml"); - client.setWebConnection(conn); - client.getPage(URL_FIRST); - - assertEquals(getExpectedAlerts(), collectedAlerts); - } } 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 07:50:35 UTC (rev 8772) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestCORSTest.java 2013-11-13 16:56:39 UTC (rev 8773) @@ -440,7 +440,7 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented({ FF17, IE8 }) + @NotYetImplemented(FF17) public void withCredentials() throws Exception { testWithCredentials("*", "true"); } @@ -451,7 +451,6 @@ @Test @Alerts(DEFAULT = { "1", "0", "4", "200" }, IE8 = { "1", "ex: status not available", "4", "200" }) - @NotYetImplemented(IE8) public void withCredentialsServer() throws Exception { testWithCredentials("http://localhost:" + PORT, "true"); } @@ -463,7 +462,7 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented({ FF17, IE8 }) + @NotYetImplemented(FF17) public void withCredentialsServerSlashAtEnd() throws Exception { testWithCredentials("http://localhost:" + PORT + "/", "true"); } @@ -475,7 +474,7 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented({ FF17, IE8 }) + @NotYetImplemented(FF17) public void withCredentials_no_header() throws Exception { testWithCredentials("*", null); } @@ -487,7 +486,7 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented({ FF17, IE8 }) + @NotYetImplemented(FF17) public void withCredentials_no_header_Server() throws Exception { testWithCredentials("http://localhost:" + PORT, null); } @@ -499,7 +498,7 @@ @Alerts(DEFAULT = { "1", "0", "4", "0" }, IE8 = { "1", "ex: status not available", "4", "200" }, IE10 = { "1", "0", "4", "200" }) - @NotYetImplemented({ FF17, IE8 }) + @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 07:50:35 UTC (rev 8772) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequestTest.java 2013-11-13 16:56:39 UTC (rev 8773) @@ -135,26 +135,142 @@ * @throws Exception if the test fails */ @Test - @Alerts({"0-", "0-" }) - public void statusBeforeSend() throws Exception { + @Alerts(DEFAULT = { "1: 0-", "2: ", "3: 200-OK" }, + IE8 = {"1: ex: status-ex: statusText, 2: , 3: 200-OK" }) + public void statusSync() throws Exception { final String html = "<html>\n" + " <head>\n" + " <title>XMLHttpRequest Test</title>\n" + + "<script>\n" + + " var xhr;\n" + + " if (window.XMLHttpRequest)\n" + + " xhr = new XMLHttpRequest();\n" + + " else if (window.ActiveXObject)\n" + + " xhr = new ActiveXObject('Microsoft.XMLHTTP');\n" + + + " alertStatus('1: ');\n" + + " xhr.open('GET', '/foo.xml', false);\n" + + " alert('2: ');\n" + + + " xhr.send();\n" + + " alertStatus('3: ');\n" + + + " function alertStatus(prefix) {\n" + + " var msg = prefix;" + + " try {\n" + + " msg = msg + xhr.status + '-';\n" + + " } catch(e) { msg = msg + 'ex: status' + '-' }\n" + + " try {\n" + + " msg = msg + xhr.statusText;;\n" + + " } catch(e) { msg = msg + 'ex: statusText' }\n" + + " alert(msg);\n" + + " }\n" + + "</script>\n" + + " </head>\n" + + " <body></body>\n" + + "</html>"; + + getMockWebConnection().setDefaultResponse("<res></res>", "text/xml"); + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts(DEFAULT = { "1: 0-", "2: 0-", "#1: 0-", "3: 0-", "#1: 0-", "4: 0-", + "#2: 200-OK", "#3: 200-OK", "#4: 200-OK" }, + FF24 = { "1: 0-", "2: 0-", "#1: 0-", "3: 0-", "4: 0-", "#2: 200-OK", "#3: 200-OK", "#4: 200-OK" }, + IE8 = { "1: ex: status-ex: statusText", "2: ex: status-ex: statusText", "#1: ex: status-ex: statusText", + "3: ex: status-ex: statusText", "#1: ex: status-ex: statusText", "4: ex: status-ex: statusText", + "#2: 200-OK", "#3: 200-OK", "#4: 200-OK" }) + public void statusAsync() throws Exception { + final String html = + "<html>\n" + + " <head>\n" + + " <title>XMLHttpRequest Test</title>\n" + + "<script>\n" + + " var xhr;\n" + + " if (window.XMLHttpRequest)\n" + + " xhr = new XMLHttpRequest();\n" + + " else if (window.ActiveXObject)\n" + + " xhr = new ActiveXObject('Microsoft.XMLHTTP');\n" + + + " function test() {\n" + + " try {\n" + + " alertStatus('1: ');\n" + + + " xhr.onreadystatechange = onReadyStateChange;\n" + + " alertStatus('2: ');\n" + + + " xhr.open('GET', '/foo.xml', true);\n" + + " alertStatus('3: ');\n" + + + " xhr.send();\n" + + " alertStatus('4: ');\n" + + " } catch(e) { alert(e) }\n" + + " }\n" + + + " function onReadyStateChange() {\n" + + " alertStatus('#' + xhr.readyState + ': ');" + + " }\n" + + + " function alertStatus(prefix) {\n" + + " var msg = prefix;" + + " try {\n" + + " msg = msg + xhr.status + '-';\n" + + " } catch(e) { msg = msg + 'ex: status' + '-' }\n" + + " try {\n" + + " msg = msg + xhr.statusText;;\n" + + " } catch(e) { msg = msg + 'ex: statusText' }\n" + + " alert(msg);\n" + + " }\n" + + "</script>\n" + + " </head>\n" + + " <body onload='test()'></body>\n" + + "</html>"; + + getMockWebConnection().setDefaultResponse("<res></res>", "text/xml"); + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts(DEFAULT = { "orsc1", "open-done", "orsc1", "send-done", + "orsc2", "orsc3", "orsc4", "4", "<a>b</a>", "[object XMLHttpRequest]" }, + FF24 = { "orsc1", "open-done", "send-done", + "orsc2", "orsc3", "orsc4", "4", "<a>b</a>", "[object XMLHttpRequest]" }, + IE8 = { "orsc1", "open-done", "orsc1", "send-done", "orsc2", "orsc3", "orsc4" }) + public void onload() throws Exception { + final String html = + "<html>\n" + + " <head>\n" + " <script>\n" - + " var request;\n" - + " if (window.XMLHttpRequest)\n" - + " request = new XMLHttpRequest();\n" - + " else if (window.ActiveXObject)\n" - + " request = new ActiveXObject('Microsoft.XMLHTTP');\n" + + " function test() {\n" + + " var xhr;\n" + + " if (window.XMLHttpRequest) xhr = new XMLHttpRequest();\n" + + " else xhr = new ActiveXObject('Microsoft.XMLHTTP');\n" - + " alert(request.status + '-' + request.statusText);\n" - + " request.open('GET', '/foo.xml', false);\n" - + " alert(request.status + '-' + request.statusText);\n" + + " 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" + + " alert('open-done');\n" + + + " xhr.send('');\n" + + " alert('send-done');\n" + + " }\n" + " </script>\n" + " </head>\n" - + " <body></body>\n" + + " <body onload='test()'></body>\n" + "</html>"; + + final String xml = "<a>b</a>"; + + getMockWebConnection().setDefaultResponse(xml, "text/xml"); loadPageWithAlerts2(html); } |