Reviewed by Eric.
authorap <ap@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Sep 2006 18:44:00 +0000 (18:44 +0000)
committerap <ap@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Sep 2006 18:44:00 +0000 (18:44 +0000)
        http://bugzilla.opendarwin.org/show_bug.cgi?id=4872
        XMLHttpRequest fails to throw an exception when there is a security violation
        (mismatching domains)

        Raise an exception if there is a security violation, and also in cases required by
        the current draft of XHR specification.

WebCore:
        * bindings/js/JSXMLHttpRequest.cpp:
        (KJS::JSXMLHttpRequest::getValueProperty): Raise an exception if a DOM method reports one.
        (KJS::JSXMLHttpRequestProtoFunc::callAsFunction): Raise an exception if a DOM method
        reports one, and also if there were too few arguments passed.

        * bindings/js/kjs_binding.cpp:
        (KJS::setDOMException): Added support for custom XHR exceptions.

        * xml/xmlhttprequest.h: Changed state names to match the current spec. Defined an
        exception code range for XHR exceptions.

        * xml/xmlhttprequest.cpp:
        (WebCore::XMLHttpRequest::open): Removed a check for m_aborted that could never succeed.
        (WebCore::XMLHttpRequest::send):
        (WebCore::XMLHttpRequest::setRequestHeader):
        (WebCore::XMLHttpRequest::getStatus):
        (WebCore::XMLHttpRequest::getStatusText):
        (WebCore::XMLHttpRequest::processSyncLoadResults):
        (WebCore::XMLHttpRequest::receivedAllData):
        (WebCore::XMLHttpRequest::receivedData):

LayoutTests:
        * http/tests/xmlhttprequest/exceptions-expected.txt: Added.
        * http/tests/xmlhttprequest/exceptions.html: Added.

        * http/tests/xmlhttprequest/extra-parameters-expected.txt: Added.
        * http/tests/xmlhttprequest/extra-parameters.html: Added.
        * http/tests/xmlhttprequest/resources/post-echo.cgi: Added.
        Test that passing too many parameters is OK (Firefox behavior; WinIE raises an exception).

        * fast/dom/xmlhttprequest-get-expected.txt: Updated results.

        * http/tests/xmlhttprequest/zero-length-response-expected.txt:
        * http/tests/xmlhttprequest/zero-length-response-sync-expected.txt:
        * http/tests/xmlhttprequest/zero-length-response-sync.html:
        * http/tests/xmlhttprequest/zero-length-response.html:
        Updated results and changed state names to match the current spec.

        * http/tests/xmlhttprequest/resources/zero-length.xml: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@16543 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/xmlhttprequest-get-expected.txt
LayoutTests/http/tests/xmlhttprequest/exceptions-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/xmlhttprequest/exceptions.html [new file with mode: 0644]
LayoutTests/http/tests/xmlhttprequest/extra-parameters-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/xmlhttprequest/extra-parameters.html [new file with mode: 0644]
LayoutTests/http/tests/xmlhttprequest/resources/post-echo.cgi [new file with mode: 0755]
LayoutTests/http/tests/xmlhttprequest/resources/zero-length.xml [new file with mode: 0644]
LayoutTests/http/tests/xmlhttprequest/zero-length-response-expected.txt
LayoutTests/http/tests/xmlhttprequest/zero-length-response-sync-expected.txt
LayoutTests/http/tests/xmlhttprequest/zero-length-response-sync.html
LayoutTests/http/tests/xmlhttprequest/zero-length-response.html
WebCore/ChangeLog
WebCore/bindings/js/JSXMLHttpRequest.cpp
WebCore/bindings/js/kjs_binding.cpp
WebCore/xml/xmlhttprequest.cpp
WebCore/xml/xmlhttprequest.h

index 974ebe115435f958092fb44547a9b2c845566330..9b69a7d15f10238a644705c910c3979fe6212468 100644 (file)
@@ -1,3 +1,29 @@
+2006-09-23  Alexey Proskuryakov  <ap@nypop.com>
+
+        Reviewed by Eric.
+
+        http://bugzilla.opendarwin.org/show_bug.cgi?id=4872
+        XMLHttpRequest fails to throw an exception when there is a security violation
+        (mismatching domains)
+
+        * http/tests/xmlhttprequest/exceptions-expected.txt: Added.
+        * http/tests/xmlhttprequest/exceptions.html: Added.
+
+        * http/tests/xmlhttprequest/extra-parameters-expected.txt: Added.
+        * http/tests/xmlhttprequest/extra-parameters.html: Added.
+        * http/tests/xmlhttprequest/resources/post-echo.cgi: Added.
+        Test that passing too many parameters is OK (Firefox behavior; WinIE raises an exception).
+
+        * fast/dom/xmlhttprequest-get-expected.txt: Updated results. 
+
+        * http/tests/xmlhttprequest/zero-length-response-expected.txt:
+        * http/tests/xmlhttprequest/zero-length-response-sync-expected.txt:
+        * http/tests/xmlhttprequest/zero-length-response-sync.html:
+        * http/tests/xmlhttprequest/zero-length-response.html:
+        Updated results and changed state names to match the current spec.
+
+        * http/tests/xmlhttprequest/resources/zero-length.xml: Added.
+
 2006-09-21  Nikolas Zimmermann  <zimmermann@kde.org>
 
         Reviewed by eseidel.
index a84a11910c8dd411bfea35d7bc0d6b2633865a7e..88fc695501e6acf5d64dd73ce0788de0f98fd399 100644 (file)
@@ -17,9 +17,8 @@ responseXML serialized
 getAllResponseHeaders()
 undefined
 status
-undefined
+0
 statusText
-undefined
 readyState
 4
 Event information
diff --git a/LayoutTests/http/tests/xmlhttprequest/exceptions-expected.txt b/LayoutTests/http/tests/xmlhttprequest/exceptions-expected.txt
new file mode 100644 (file)
index 0000000..ad64cb2
--- /dev/null
@@ -0,0 +1,16 @@
+Test that XMLHttpRequest raises exceptions when it should.
+
+new XMLHttpRequest()
+PASS: req.setRequestHeader("Foo", "bar") threw exception Error: INVALID_STATE_ERR: DOM Exception 11.
+PASS: req.send(null) threw exception Error: INVALID_STATE_ERR: DOM Exception 11.
+PASS: req.open("GET", "http://www.apple.com/", true) threw exception Error: Permission denied.
+open()
+PASS: req.setRequestHeader() threw exception SyntaxError: Not enough arguments.
+PASS: req.setRequestHeader("Foo") threw exception SyntaxError: Not enough arguments.
+PASS: req.status() threw exception Error: INVALID_STATE_ERR: DOM Exception 11.
+PASS: req.statusText() threw exception Error: INVALID_STATE_ERR: DOM Exception 11.
+send()
+PASS: req.send(null) threw exception Error: INVALID_STATE_ERR: DOM Exception 11.
+PASS: req.setRequestHeader("Foo", "bar") threw exception Error: INVALID_STATE_ERR: DOM Exception 11.
+PASS: req.getResponseHeader() threw exception SyntaxError: Not enough arguments.
+
diff --git a/LayoutTests/http/tests/xmlhttprequest/exceptions.html b/LayoutTests/http/tests/xmlhttprequest/exceptions.html
new file mode 100644 (file)
index 0000000..686c018
--- /dev/null
@@ -0,0 +1,83 @@
+<html>
+<head>
+<title>Test XMLHttpRequest exceptions</title>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<body>
+<p>Test that XMLHttpRequest raises exceptions when it should.</p>
+<script>
+
+    if (window.layoutTestController)
+        layoutTestController.dumpAsText();
+
+    var console_messages = document.createElement("ul");
+    document.body.appendChild(console_messages);
+    
+    function log(message)
+    {
+        var item = document.createElement("li");
+        item.appendChild(document.createTextNode(message));
+        console_messages.appendChild(item);
+    }
+    
+    function shouldThrow(_a, _e) {
+        var exception;
+        var _av;
+        try {
+            _av = eval(_a);
+        } catch (e) {
+            exception = e.description ? e.description : e;
+        }
+        
+        var _ev;
+        if (_e)
+            _ev =  eval(_e);
+        
+        if (exception) {
+            if (typeof _e == "undefined" || exception == _ev)
+                log("PASS: " + _a + " threw exception " + exception + ".");
+            else
+                log("FAIL: " + _a + " should throw exception " + _ev + ". Threw exception " + exception + ".");
+        } else if (typeof _av == "undefined")
+            log("FAIL: " + _a + " should throw exception " + _e + ". Was undefined.");
+        else
+            log("FAIL: " + _a + " should throw exception " + _e + ". Was " + _av + ".");
+    }
+
+// -------------------------
+    
+    if (window.XMLHttpRequest) {
+        req = new XMLHttpRequest();
+    } else {
+        try {
+            req = new ActiveXObject("Msxml2.XMLHTTP");
+        } catch (ex) {
+            req = new ActiveXObject("Microsoft.XMLHTTP");
+        }
+    }
+    log("new XMLHttpRequest()");
+
+    shouldThrow('req.setRequestHeader("Foo", "bar")');
+    shouldThrow('req.send(null)');
+
+    shouldThrow('req.open("GET", "http://www.apple.com/", true)');
+    
+    req.open('GET', 'resources/zero-length.txt', false);
+    log("open()");
+    
+    shouldThrow('req.setRequestHeader()');
+    shouldThrow('req.setRequestHeader("Foo")');
+    shouldThrow('req.status()');
+    shouldThrow('req.statusText()');
+    
+    req.send(null);
+    log("send()");
+
+    shouldThrow('req.send(null)');
+
+    shouldThrow('req.setRequestHeader("Foo", "bar")');
+
+    shouldThrow('req.getResponseHeader()');
+   
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/xmlhttprequest/extra-parameters-expected.txt b/LayoutTests/http/tests/xmlhttprequest/extra-parameters-expected.txt
new file mode 100644 (file)
index 0000000..0e361b1
--- /dev/null
@@ -0,0 +1,6 @@
+Test that XMLHttpRequest tolerates extra parameters to its methods.
+
+  Status: 200
+  StatusText: "OK"
+  ResponseText: "SUCCESS"
+
diff --git a/LayoutTests/http/tests/xmlhttprequest/extra-parameters.html b/LayoutTests/http/tests/xmlhttprequest/extra-parameters.html
new file mode 100644 (file)
index 0000000..8c5cf06
--- /dev/null
@@ -0,0 +1,64 @@
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<body>
+<p>Test that XMLHttpRequest tolerates extra parameters to its methods.</p>
+<script>
+
+    if (window.layoutTestController)
+        layoutTestController.dumpAsText();
+
+    var console_messages = document.createElement("ul");
+    document.body.appendChild(console_messages);
+    
+    function log(message)
+    {
+        var item = document.createElement("li");
+        item.appendChild(document.createTextNode(message));
+        console_messages.appendChild(item);
+    }
+    
+    function prettyPrintText(text) {
+        if (text == null)
+            return text;
+        return '"' + text + '"';
+    }
+    
+    function dumpResponse() {
+
+       try { log ("  Status: " + req.status); } catch (ex) { log("  Exception getting status: " + ex.message); }
+       try { log ("  StatusText: " + prettyPrintText(req.statusText)); } catch (ex) { log("  Exception getting StatusText: " + ex.message); }
+       try { log ("  ResponseText: " + prettyPrintText(req.responseText)); } catch (ex) { log("  Exception getting ResponseText: " + ex.message); }
+    }
+
+    try {
+
+        if (window.XMLHttpRequest) {
+            req = new XMLHttpRequest();
+        } else {
+            try {
+                req = new ActiveXObject("Msxml2.XMLHTTP");
+            } catch (ex) {
+                req = new ActiveXObject("Microsoft.XMLHTTP");
+            }
+        }
+        
+        req.open('POST', 'resources/post-echo.cgi', false, null, null, null);
+        
+        req.setRequestHeader('foo', 'bar', null);
+        req.overrideMimeType('text/xml', null);
+
+        req.send('SUCCESS', null);
+        
+        req.getResponseHeader('Date', null);
+        req.getAllResponseHeaders(null);
+        
+        dumpResponse();
+        
+    } catch (ex) {
+        log("FAILURE: " + ex);
+    }
+    
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/xmlhttprequest/resources/post-echo.cgi b/LayoutTests/http/tests/xmlhttprequest/resources/post-echo.cgi
new file mode 100755 (executable)
index 0000000..fea9e12
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/perl -w
+
+print "Content-type: text/plain\n\n"; 
+
+if ($ENV{'REQUEST_METHOD'} eq "POST") {
+    read(STDIN, $request, $ENV{'CONTENT_LENGTH'})
+                || die "Could not get query\n";
+    print $request;
+} else {
+    print "Wrong method: " . $ENV{'REQUEST_METHOD'} . "\n";
+} 
diff --git a/LayoutTests/http/tests/xmlhttprequest/resources/zero-length.xml b/LayoutTests/http/tests/xmlhttprequest/resources/zero-length.xml
new file mode 100644 (file)
index 0000000..e69de29
index 3675012ae95cb70b7c50075195c5d1899ce933d8..b9ba6784693bb78d0a5df09ae58138f7e916a920 100644 (file)
@@ -1,24 +1,33 @@
 Test for bug 5924 - zero-length responses to XMLHTTPRequest mishandled.
 
 after creation: Uninitialized
-ResponseText: ""
+  Status: 0
+  StatusText: ""
+  ResponseText: ""
+  ResponseXML: null
+  ResponseXML serialized: n/a
 after setting onreadystatechange: Uninitialized
-onreadystatechange: Loading
-  Status: undefined
-  StatusText: undefined
+onreadystatechange: Open
+  Exception getting status: INVALID_STATE_ERR: DOM Exception 11
+  Exception getting StatusText: INVALID_STATE_ERR: DOM Exception 11
   ResponseText: ""
   ResponseXML: null
   ResponseXML serialized: n/a
-after open(): Loading
-after overrideMimeType(): Loading
-after send(): Loading
-onreadystatechange: Loaded
+after open(): Open
+  Exception getting status: INVALID_STATE_ERR: DOM Exception 11
+  Exception getting StatusText: INVALID_STATE_ERR: DOM Exception 11
+  ResponseText: ""
+  ResponseXML: null
+  ResponseXML serialized: n/a
+after overrideMimeType(): Open
+after send(): Open
+onreadystatechange: Sent
   Status: 200
   StatusText: "OK"
   ResponseText: ""
   ResponseXML: null
   ResponseXML serialized: n/a
-onreadystatechange: Completed
+onreadystatechange: Loaded
   Status: 200
   StatusText: "OK"
   ResponseText: ""
index 766a01be384027387c97d74eab163ff7ead56e0c..bfc61b892604dfe2e4d3e100dcc633afe3d9af32 100644 (file)
@@ -3,9 +3,9 @@ Test for bug 5924 - zero-length responses to XMLHTTPRequest mishandled.
 after creation: Uninitialized
 ResponseText: ""
 after setting onreadystatechange: Uninitialized
-after open(): Loading
-after overrideMimeType(): Loading
-after send(): Completed
+after open(): Open
+after overrideMimeType(): Open
+after send(): Loaded
   Status: 200
   StatusText: "OK"
   ResponseText: ""
index fc743050b73eba3364d602dffce4134f57e47c3a..46ed544495702fdc56f6c0c49ea221cc6835400f 100644 (file)
@@ -1,6 +1,6 @@
 <html>
 <head>
-<title>Test XmlHttpRequest zero-length response handling (sync)</title>
+<title>Test XMLHttpRequest zero-length response handling (sync)</title>
 <meta http-equiv="content-type" content="text/html;charset=utf-8">
 <body>
 <p>Test for <a href="http://bugzilla.opendarwin.org/show_bug.cgi?id=5924">bug 5924</a>
 
         req.open('GET', url, async);
         log("after open(): " + stateName(req.readyState));
-        req.overrideMimeType('text/xml');
-        log("after overrideMimeType(): " + stateName(req.readyState));
+        try {
+            req.overrideMimeType('text/xml');
+            log("after overrideMimeType(): " + stateName(req.readyState));
+        } catch (ex) {
+            log("overrideMimeType(): " + ex);
+        }
+
         req.send(null);
         log("after send(): " + stateName(req.readyState));
 
       if (req.readyState == 0)
           return "Uninitialized";
       else if (req.readyState == 1)
-          return "Loading";
+          return "Open";
       else if (req.readyState == 2)
-          return "Loaded";
+          return "Sent";
       else if (req.readyState == 3)
-          return "Interactive";
+          return "Receiving";
       else if (req.readyState == 4)
-          return "Completed";
+          return "Loaded";
       else
         return "???";
     }
@@ -89,7 +94,7 @@
       dumpResponse();
     }
     
-    get('resources/zero-length.txt', false);
+    get('resources/zero-length.xml', false);
     dumpResponse();
 </script>
 </body>
index 6316773a0d3c5b20822217209d464fd02f353da3..e48ac1a940314581cb1181dd645892855d2062bf 100644 (file)
@@ -1,6 +1,6 @@
 <html>
 <head>
-<title>Test XmlHttpRequest zero-length response handling (async)</title>
+<title>Test XMLHttpRequest zero-length response handling (async)</title>
 <meta http-equiv="content-type" content="text/html;charset=utf-8">
 <body>
 <p>Test for <a href="http://bugzilla.opendarwin.org/show_bug.cgi?id=5924">bug 5924</a>
@@ -34,7 +34,7 @@
         
         log("after creation: " + stateName(req.readyState));
         
-        log("ResponseText: " + prettyPrintText(req.responseText));
+        dumpResponse();
 
         if (async)
             req.onreadystatechange = processStateChange;
 
         req.open('GET', url, async);
         log("after open(): " + stateName(req.readyState));
-        req.overrideMimeType('text/xml');
-        log("after overrideMimeType(): " + stateName(req.readyState));
+        dumpResponse();
+        
+        try {
+            req.overrideMimeType('text/xml');
+            log("after overrideMimeType(): " + stateName(req.readyState));
+        } catch (ex) {
+            log("overrideMimeType(): " + ex);
+        }
+
         req.send(null);
         log("after send(): " + stateName(req.readyState));
 
       if (req.readyState == 0)
           return "Uninitialized";
       else if (req.readyState == 1)
-          return "Loading";
+          return "Open";
       else if (req.readyState == 2)
-          return "Loaded";
+          return "Sent";
       else if (req.readyState == 3)
-          return "Interactive";
+          return "Receiving";
       else if (req.readyState == 4)
-          return "Completed";
+          return "Loaded";
       else
         return "???";
     }
@@ -90,7 +97,7 @@
     }
     
     // start async steps
-    get('resources/zero-length.txt', true);
+    get('resources/zero-length.xml', true);
     
 </script>
 </body>
index 3f5e3bb6de5f66a282391120b8435466d157cab0..7ccaea2010d181822996ffc6a44789d6b27cfc5a 100644 (file)
@@ -1,3 +1,35 @@
+2006-09-23  Alexey Proskuryakov  <ap@nypop.com>
+
+        Reviewed by Eric.
+
+        http://bugzilla.opendarwin.org/show_bug.cgi?id=4872
+        XMLHttpRequest fails to throw an exception when there is a security violation
+        (mismatching domains)
+
+        Raise an exception if there is a security violation, and also in cases required by
+        the current draft of XHR specification.
+
+        * bindings/js/JSXMLHttpRequest.cpp:
+        (KJS::JSXMLHttpRequest::getValueProperty): Raise an exception if a DOM method reports one.
+        (KJS::JSXMLHttpRequestProtoFunc::callAsFunction): Raise an exception if a DOM method
+        reports one, and also if there were too few arguments passed. 
+
+        * bindings/js/kjs_binding.cpp:
+        (KJS::setDOMException): Added support for custom XHR exceptions.
+
+        * xml/xmlhttprequest.h: Changed state names to match the current spec. Defined an
+        exception code range for XHR exceptions.
+
+        * xml/xmlhttprequest.cpp:
+        (WebCore::XMLHttpRequest::open): Removed a check for m_aborted that could never succeed.
+        (WebCore::XMLHttpRequest::send):
+        (WebCore::XMLHttpRequest::setRequestHeader):
+        (WebCore::XMLHttpRequest::getStatus):
+        (WebCore::XMLHttpRequest::getStatusText):
+        (WebCore::XMLHttpRequest::processSyncLoadResults):
+        (WebCore::XMLHttpRequest::receivedAllData):
+        (WebCore::XMLHttpRequest::receivedData):
+
 2006-09-22  Steve Falkenburg  <sfalken@apple.com>
 
         Reviewed by Jeff Jenkins.
index 3a75c13c8dfa67bcce6657c17241b34b853b0656..9e7ed7a75066aab9615873378f36348d1465d304 100644 (file)
@@ -88,6 +88,8 @@ bool JSXMLHttpRequest::getOwnPropertySlot(ExecState *exec, const Identifier& pro
 
 JSValue* JSXMLHttpRequest::getValueProperty(ExecState *exec, int token) const
 {
+  ExceptionCode ec = 0;
+  
   switch (token) {
   case ReadyState:
     return jsNumber(m_impl->getReadyState());
@@ -98,11 +100,15 @@ JSValue* JSXMLHttpRequest::getValueProperty(ExecState *exec, int token) const
       return toJS(exec, responseXML);
     return jsNull();
   case Status: {
-    int status = m_impl->getStatus();
-    return status > 0 ? jsNumber(status) : jsUndefined();
+    JSValue* result = jsNumber(m_impl->getStatus(ec));
+    setDOMException(exec, ec);
+    return result;
+  }
+  case StatusText: {
+    JSValue* result = jsString(m_impl->getStatusText(ec));
+    setDOMException(exec, ec);
+    return result;
   }
-  case StatusText:
-    return jsStringOrUndefined(m_impl->getStatusText());
   case Onreadystatechange:
    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onReadyStateChangeListener()))
       if (JSObject* listenerObj = listener->listenerObj())
@@ -171,6 +177,8 @@ JSValue* JSXMLHttpRequestProtoFunc::callAsFunction(ExecState *exec, JSObject* th
 
   JSXMLHttpRequest *request = static_cast<JSXMLHttpRequest *>(thisObj);
 
+  ExceptionCode ec = 0;
+
   switch (id) {
   case JSXMLHttpRequest::Abort:
     request->m_impl->abort();
@@ -178,13 +186,13 @@ JSValue* JSXMLHttpRequestProtoFunc::callAsFunction(ExecState *exec, JSObject* th
   case JSXMLHttpRequest::GetAllResponseHeaders:
     return jsStringOrUndefined(request->m_impl->getAllResponseHeaders());
   case JSXMLHttpRequest::GetResponseHeader:
-    if (args.size() != 1)
-      return jsUndefined();
+    if (args.size() < 1)
+        return throwError(exec, SyntaxError, "Not enough arguments");
     return jsStringOrUndefined(request->m_impl->getResponseHeader(args[0]->toString(exec)));
   case JSXMLHttpRequest::Open:
     {
-      if (args.size() < 2 || args.size() > 5)
-        return jsUndefined();
+      if (args.size() < 2)
+        return throwError(exec, SyntaxError, "Not enough arguments");
     
       String method = args[0]->toString(exec);
       KURL url = KURL(Window::retrieveActive(exec)->frame()->document()->completeURL(DeprecatedString(args[1]->toString(exec))));
@@ -201,15 +209,13 @@ JSValue* JSXMLHttpRequestProtoFunc::callAsFunction(ExecState *exec, JSObject* th
       if (args.size() >= 5)
         password = args[4]->toString(exec);
 
-      request->m_impl->open(method, url, async, user, password);
+      request->m_impl->open(method, url, async, user, password, ec);
+      setDOMException(exec, ec);
 
       return jsUndefined();
     }
   case JSXMLHttpRequest::Send:
     {
-      if (args.size() > 1)
-        return jsUndefined();
-
       String body;
 
       if (args.size() >= 1) {
@@ -225,18 +231,20 @@ JSValue* JSXMLHttpRequestProtoFunc::callAsFunction(ExecState *exec, JSObject* th
         }
       }
 
-      request->m_impl->send(body);
+      request->m_impl->send(body, ec);
+      setDOMException(exec, ec);
 
       return jsUndefined();
     }
   case JSXMLHttpRequest::SetRequestHeader:
-    if (args.size() != 2)
-      return jsUndefined();
-    request->m_impl->setRequestHeader(args[0]->toString(exec), args[1]->toString(exec));
+    if (args.size() < 2)
+      return throwError(exec, SyntaxError, "Not enough arguments");
+    request->m_impl->setRequestHeader(args[0]->toString(exec), args[1]->toString(exec), ec);
+    setDOMException(exec, ec);
     return jsUndefined();
   case JSXMLHttpRequest::OverrideMIMEType:
-    if (args.size() != 1)
-      return jsUndefined();
+    if (args.size() < 1)
+      return throwError(exec, SyntaxError, "Not enough arguments");
     request->m_impl->overrideMIMEType(args[0]->toString(exec));
     return jsUndefined();
   }
index 93e2b530f63912d34713d09c8d6fd3874fd5b417..525f97c4560bdc5b158eb2d3bac47cd270d333ed 100644 (file)
@@ -32,6 +32,7 @@
 #include "PlatformString.h"
 #include "Range.h"
 #include "RangeException.h"
+#include "xmlhttprequest.h"
 #include "XPathEvaluator.h"
 #include "kjs_dom.h"
 #include "kjs_window.h"
@@ -312,6 +313,10 @@ static const char * const eventExceptionNames[] = {
     "UNSPECIFIED_EVENT_TYPE_ERR"
 };
 
+static const char * const xmlHttpRequestExceptionNames[] = {
+    "Permission denied"
+};
+
 #ifdef XPATH_SUPPORT
 static const char * const xpathExceptionNames[] = {
     "INVALID_EXPRESSION_ERR",
@@ -343,6 +348,15 @@ void setDOMException(ExecState* exec, ExceptionCode ec)
     nameIndex = code;
     nameTable = eventExceptionNames;
     nameTableSize = sizeof(eventExceptionNames) / sizeof(eventExceptionNames[0]);
+  } else if (code >= XMLHttpRequestExceptionOffset && code <= XMLHttpRequestExceptionMax) {
+    // XMLHttpRequest case is special, because there is no exception code assigned (yet?).
+    code -= XMLHttpRequestExceptionOffset;
+    nameIndex = code;
+    nameTable = xmlHttpRequestExceptionNames;
+    nameTableSize = sizeof(xmlHttpRequestExceptionNames) / sizeof(xmlHttpRequestExceptionNames[0]);
+
+    throwError(exec, GeneralError, nameIndex < nameTableSize ? nameTable[nameIndex] : 0);
+    return;
 #ifdef XPATH_SUPPORT
   } else if (code >= XPathExceptionOffset && code <= XPathExceptionMax) {
     type = "DOM XPath";
index 94eb64b6355b7d31db824244415cf4fa0332812e..026868a988642c86122c63f9bb547daf8833ca51 100644 (file)
@@ -28,6 +28,7 @@
 #include "Event.h"
 #include "EventListener.h"
 #include "EventNames.h"
+#include "ExceptionCode.h"
 #include "FormData.h"
 #include "HTMLDocument.h"
 #include "LoaderFunctions.h"
@@ -148,7 +149,7 @@ String XMLHttpRequest::getResponseText() const
 
 Document* XMLHttpRequest::getResponseXML() const
 {
-    if (m_state != Completed)
+    if (m_state != Loaded)
         return 0;
 
     if (!m_createdDocument) {
@@ -217,7 +218,7 @@ void XMLHttpRequest::callReadyStateChangeListener()
     if (m_doc && m_doc->frame() && m_onReadyStateChangeListener)
         m_onReadyStateChangeListener->handleEvent(new Event(readystatechangeEvent, true, true), true);
     
-    if (m_doc && m_doc->frame() && m_state == Completed && m_onLoadListener)
+    if (m_doc && m_doc->frame() && m_state == Loaded && m_onLoadListener)
         m_onLoadListener->handleEvent(new Event(loadEvent, true, true), true);
 }
 
@@ -238,7 +239,7 @@ bool XMLHttpRequest::urlMatchesDocumentDomain(const KURL& url) const
     return false;
 }
 
-void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password)
+void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec)
 {
     abort();
     m_aborted = false;
@@ -252,11 +253,10 @@ void XMLHttpRequest::open(const String& method, const KURL& url, bool async, con
 
     changeState(Uninitialized);
 
-    if (m_aborted)
-        return;
-
-    if (!urlMatchesDocumentDomain(url))
+    if (!urlMatchesDocumentDomain(url)) {
+        ec = PERMISSION_DENIED;
         return;
+    }
 
     m_url = url;
 
@@ -278,18 +278,20 @@ void XMLHttpRequest::open(const String& method, const KURL& url, bool async, con
 
     m_async = async;
 
-    changeState(Loading);
+    changeState(Open);
 }
 
-void XMLHttpRequest::send(const String& body)
+void XMLHttpRequest::send(const String& body, ExceptionCode& ec)
 {
     if (!m_doc)
         return;
 
-    if (m_state != Loading)
+    if (m_state != Open) {
+        ec = INVALID_STATE_ERR;
         return;
+    }
   
-    // FIXME: Should this abort instead if we already have a m_job going?
+    // FIXME: Should this abort or raise an exception instead if we already have a m_job going?
     if (m_job)
         return;
 
@@ -298,9 +300,11 @@ void XMLHttpRequest::send(const String& body)
     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && (m_url.protocol().lower() == "http" || m_url.protocol().lower() == "https")) {
         String contentType = getRequestHeader("Content-Type");
         String charset;
-        if (contentType.isEmpty())
-            setRequestHeader("Content-Type", "application/xml");
-        else
+        if (contentType.isEmpty()) {
+            ExceptionCode ec = 0;
+            setRequestHeader("Content-Type", "application/xml", ec);
+            ASSERT(ec == 0);
+        } else
             charset = getCharset(contentType);
       
         if (charset.isEmpty())
@@ -378,8 +382,13 @@ void XMLHttpRequest::overrideMIMEType(const String& override)
     m_mimeTypeOverride = override;
 }
 
-void XMLHttpRequest::setRequestHeader(const String& name, const String& value)
+void XMLHttpRequest::setRequestHeader(const String& name, const String& value, ExceptionCode& ec)
 {
+    if (m_state != Open) {
+        ec = INVALID_STATE_ERR;
+        return;
+    }
+
     if (m_requestHeaders.length() > 0)
         m_requestHeaders += "\r\n";
     m_requestHeaders += name.deprecatedString();
@@ -440,37 +449,51 @@ bool XMLHttpRequest::responseIsXML() const
     return DOMImplementation::isXMLMIMEType(mimeType);
 }
 
-int XMLHttpRequest::getStatus() const
+int XMLHttpRequest::getStatus(ExceptionCode& ec) const
 {
-    if (m_responseHeaders.isEmpty())
-        return -1;
-  
+    if (m_state == Uninitialized)
+        return 0;
+    
+    if (m_responseHeaders.isEmpty()) {
+        if (m_state != Receiving && m_state != Loaded)
+            // status MUST be available in these states, but we don't get any headers from non-HTTP requests
+            ec = INVALID_STATE_ERR;
+        return 0;
+    }
+    
     int endOfLine = m_responseHeaders.find("\n");
     String firstLine = endOfLine == -1 ? m_responseHeaders : m_responseHeaders.substring(0, endOfLine);
     int codeStart = firstLine.find(" ");
     int codeEnd = firstLine.find(" ", codeStart + 1);
-    if (codeStart == -1 || codeEnd == -1)
-        return -1;
+    ASSERT(codeStart != -1);
+    ASSERT(codeEnd != -1);
   
     String number = firstLine.substring(codeStart + 1, codeEnd - (codeStart + 1));
     bool ok = false;
     int code = number.toInt(&ok);
-    if (!ok)
-        return -1;
+    ASSERT(ok);
+
     return code;
 }
 
-String XMLHttpRequest::getStatusText() const
+String XMLHttpRequest::getStatusText(ExceptionCode& ec) const
 {
-    if (m_responseHeaders.isEmpty())
+    if (m_state == Uninitialized)
+        return "";
+    
+    if (m_responseHeaders.isEmpty()) {
+        if (m_state != Receiving && m_state != Loaded)
+            // statusText MUST be available in these states, but we don't get any headers from non-HTTP requests
+            ec = INVALID_STATE_ERR;
         return String();
+    }
   
     int endOfLine = m_responseHeaders.find("\n");
     String firstLine = endOfLine == -1 ? m_responseHeaders : m_responseHeaders.substring(0, endOfLine);
     int codeStart = firstLine.find(" ");
     int codeEnd = firstLine.find(" ", codeStart + 1);
-    if (codeStart == -1 || codeEnd == -1)
-        return String();
+    ASSERT(codeStart != -1);
+    ASSERT(codeEnd != -1);
   
     return firstLine.substring(codeEnd + 1, endOfLine - (codeEnd + 1)).stripWhiteSpace();
 }
@@ -483,7 +506,7 @@ void XMLHttpRequest::processSyncLoadResults(const Vector<char>& data, const KURL
     }
 
     m_responseHeaders = headers;
-    changeState(Loaded);
+    changeState(Sent);
     if (m_aborted)
         return;
 
@@ -502,8 +525,8 @@ void XMLHttpRequest::receivedAllData(ResourceLoader*)
     if (m_responseHeaders.isEmpty() && m_job)
         m_responseHeaders = m_job->queryMetaData("HTTP-Headers");
 
-    if (m_state < Loaded)
-        changeState(Loaded);
+    if (m_state < Sent)
+        changeState(Sent);
 
     if (m_decoder)
         m_response += m_decoder->flush();
@@ -511,7 +534,7 @@ void XMLHttpRequest::receivedAllData(ResourceLoader*)
     bool hadJob = m_job;
     m_job = 0;
 
-    changeState(Completed);
+    changeState(Loaded);
     m_decoder = 0;
 
     if (hadJob) {
@@ -534,8 +557,8 @@ void XMLHttpRequest::receivedData(ResourceLoader*, const char *data, int len)
     if (m_responseHeaders.isEmpty() && m_job)
         m_responseHeaders = m_job->queryMetaData("HTTP-Headers");
 
-    if (m_state < Loaded)
-        changeState(Loaded);
+    if (m_state < Sent)
+        changeState(Sent);
   
     if (!m_decoder) {
         m_encoding = getCharset(m_mimeTypeOverride);
@@ -563,8 +586,8 @@ void XMLHttpRequest::receivedData(ResourceLoader*, const char *data, int len)
     m_response += decoded;
 
     if (!m_aborted) {
-        if (m_state != Interactive)
-            changeState(Interactive);
+        if (m_state != Receiving)
+            changeState(Receiving);
         else
             // Firefox calls readyStateChanged every time it receives data, 4449442
             callReadyStateChangeListener();
index 8fd118637b3bb1520c1cb67e5897db91e85b56c4..0011c6b9e6bf2a86feca4ffe7f846afc66d0240e 100644 (file)
@@ -34,13 +34,19 @@ namespace WebCore {
   class EventListener;
   class String;
 
+  typedef int ExceptionCode;
+
+  const int XMLHttpRequestExceptionOffset = 500;
+  const int XMLHttpRequestExceptionMax = 599;
+  enum XMLHttpRequestExceptionCode { PERMISSION_DENIED = XMLHttpRequestExceptionOffset };
+
   // these exact numeric values are important because JS expects them
   enum XMLHttpRequestState {
-    Uninitialized = 0,  // open() has not been called yet
-    Loading = 1,        // send() has not been called yet
-    Loaded = 2,         // send() has been called, headers and status are available
-    Interactive = 3,    // Downloading, responseText holds the partial data
-    Completed = 4       // Finished with all operations
+    Uninitialized = 0,  // The initial value.
+    Open = 1,           // The open() method has been successfully called.
+    Sent = 2,           // The user agent successfully completed the request, but no data has yet been received.
+    Receiving = 3,      // Immediately before receiving the message body (if any). All HTTP headers have been received.
+    Loaded = 4          // The data transfer has been completed.
   };
 
   class XMLHttpRequest : public Shared<XMLHttpRequest>, ResourceLoaderClient {
@@ -51,13 +57,13 @@ namespace WebCore {
     static void detachRequests(Document*);
     static void cancelRequests(Document*);
 
-    String getStatusText() const;
-    int getStatus() const;
+    String getStatusText(ExceptionCode&) const;
+    int getStatus(ExceptionCode&) const;
     XMLHttpRequestState getReadyState() const;
-    void open(const String& method, const KURL& url, bool async, const String& user, const String& password);
-    void send(const String& body);
+    void open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec);
+    void send(const String& body, ExceptionCode&);
     void abort();
-    void setRequestHeader(const String& name, const String &value);
+    void setRequestHeader(const String& name, const String &value, ExceptionCode&);
     void overrideMIMEType(const String& override);
     String getAllResponseHeaders() const;
     String getResponseHeader(const String& name) const;