Bug 605296 part 2. Make username/password stringify undefined to empty string. r=sicking, a=blocker
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 30 Nov 2010 13:18:15 -0500
changeset 58473 251cd89364a8e08eaa956de02fd279f5eca118bd
parent 58472 43437276f9f174878ac212b6c03eb4efa831ac20
child 58474 7ab0eb4c345b12838f45bfe19f3088be8025215a
push idunknown
push userunknown
push dateunknown
reviewerssicking, blocker
bugs605296
milestone2.0b8pre
Bug 605296 part 2. Make username/password stringify undefined to empty string. r=sicking, a=blocker
content/base/public/nsIXMLHttpRequest.idl
content/base/test/file_CrossSiteXHR_inner.html
content/base/test/test_CrossSiteXHR.html
js/src/xpconnect/src/dom_quickstubs.qsconf
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -237,18 +237,18 @@ interface nsIXMLHttpRequest : nsISupport
    *              be thrown.
    * @param user (optional) A username for authentication if necessary.
    *             The default value is the empty string
    * @param password (optional) A password for authentication if necessary.
    *                 The default value is the empty string
    */
   [optional_argc] void open(in AUTF8String method, in AUTF8String url,
                             [optional] in boolean async,
-                            [optional] in DOMString user,
-                            [optional] in DOMString password);
+                            [optional,Undefined(Empty)] in DOMString user,
+                            [optional,Undefined(Empty)] in DOMString password);
 
   /**
    * Sends the request. If the request is asynchronous, returns
    * immediately after sending the request. If it is synchronous
    * returns only after the response has been received.
    *
    * All event listeners must be set before calling send().
    *
--- a/content/base/test/file_CrossSiteXHR_inner.html
+++ b/content/base/test/file_CrossSiteXHR_inner.html
@@ -11,17 +11,22 @@
 window.addEventListener("message", function(e) {
 
   sendData = null;
 
   req = eval(e.data);
   var res = {
     didFail: false,
     events: [],
-    progressEvents: 0
+    progressEvents: 0,
+    status: 0,
+    responseText: "",
+    statusText: "",
+    responseXML: null,
+    sendThrew: false
   };
   
   var xhr = new XMLHttpRequest();
   for each(type in ["load", "abort", "error", "loadstart"]) {
     xhr.addEventListener(type, function(e) {
       res.events.push(e.type);
     }, false);
   }
@@ -46,16 +51,17 @@ window.addEventListener("message", funct
   xhr.onload = xhr.onerror = function (event) {
     if (event.type == "error") {
       res.didFail = true;
     }
     res.status = xhr.status;
     try {
       res.statusText = xhr.statusText;
     } catch (e) {
+      delete(res.statusText);
     }
     res.responseXML = xhr.responseXML ?
       (new XMLSerializer()).serializeToString(xhr.responseXML) :
       null;
     res.responseText = xhr.responseText;
 
     res.responseHeaders = {};
     for (responseHeader in req.responseHeaders) {
@@ -67,24 +73,38 @@ window.addEventListener("message", funct
   }
 
   if (req.withCred)
     xhr.withCredentials = true;
   if (req.body)
     sendData = req.body;
 
   res.events.push("opening");
-  xhr.open(req.method, req.url, true);
+  // Allow passign in falsy usernames/passwords so we can test them
+  try {
+    xhr.open(req.method, req.url, true,
+             ("username" in req) ? req.username : "",
+             ("password" in req) ? req.password : "aa");
+  } catch (ex) {
+    res.didFail = true;
+    post(e, res);
+  }
 
   for (header in req.headers) {
     xhr.setRequestHeader(header, req.headers[header]);
   }
 
   res.events.push("sending");
-  xhr.send(sendData);
+  try {
+    xhr.send(sendData);
+  } catch (ex) {
+    res.didFail = true;
+    res.sendThrew = true;
+    post(e, res);
+  }
 
 }, false);
 
 function post(e, res) {
   e.source.postMessage(res.toSource(), "http://mochi.test:8888");
 }
 
 </script>
--- a/content/base/test/test_CrossSiteXHR.html
+++ b/content/base/test/test_CrossSiteXHR.html
@@ -44,16 +44,47 @@ function runTest() {
   yield;
 
   tests =     [// Plain request
                { pass: 1,
                  method: "GET",
                  noAllowPreflight: 1,
                },
 
+               // undefined username
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 username: undefined
+               },
+
+               // undefined username and password
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 username: undefined,
+                 password: undefined
+               },
+
+               // nonempty username
+               { pass: 0,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 username: "user",
+               },
+
+               // nonempty password
+               // XXXbz this passes for now, because we ignore passwords
+               // without usernames in most cases.
+               { pass: 1,
+                 method: "GET",
+                 noAllowPreflight: 1,
+                 password: "password",
+               },
+
                // Default allowed headers
                { pass: 1,
                  method: "GET",
                  headers: { "Content-Type": "text/plain",
                             "Accept": "foo/bar",
                             "Accept-Language": "sv-SE" },
                  noAllowPreflight: 1,
                },
@@ -552,16 +583,24 @@ function runTest() {
       responseHeaders: test.responseHeaders,
     };
 
     if (test.pass) {
        req.url += "&origin=" + escape(origin) +
                   "&requestMethod=" + test.method;
     }
 
+    if ("username" in test) {
+      req.username = test.username;
+    }
+
+    if ("password" in test) {
+      req.password = test.password;
+    }
+
     if (test.noAllowPreflight)
       req.url += "&noAllowPreflight";
 
     if (test.pass && "headers" in test) {
       function isUnsafeHeader(name) {
         lName = name.toLowerCase();
         return lName != "accept" &&
                lName != "accept-language" &&
@@ -643,24 +682,26 @@ function runTest() {
           }
         }
       }
     }
     else {
       is(res.didFail, true,
         "should have failed in test for " + test.toSource());
       is(res.status, 0, "wrong status in test for " + test.toSource());
-      is(res.statusText, "", "wrong status in test for " + test.toSource());
+      is(res.statusText, "", "wrong status text for " + test.toSource());
       is(res.responseXML, null,
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "",
          "wrong responseText in test for " + test.toSource());
-      is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs4,error",
-         "wrong events in test for " + test.toSource());
+      if (!res.sendThrew) {
+        is(res.events.join(","),
+           "opening,rs1,sending,rs1,loadstart,rs2,rs4,error",
+           "wrong events in test for " + test.toSource());
+      }
       is(res.progressEvents, 0,
          "wrong events in test for " + test.toSource());
       if (test.responseHeaders) {
         for (header in test.responseHeaders) {
           is(res.responseHeaders[header], null,
              "wrong response header (" + header + ") in test for " +
              test.toSource());
         }
@@ -783,17 +824,17 @@ function runTest() {
       is(res.events.join(","),
          "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load",
          "wrong responseText in test for " + test.toSource());
     }
     else {
       is(res.didFail, true,
         "should have failed in test for " + test.toSource());
       is(res.status, 0, "wrong status in test for " + test.toSource());
-      is(res.statusText, "", "wrong status in test for " + test.toSource());
+      is(res.statusText, "", "wrong status text for " + test.toSource());
       is(res.responseXML, null,
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "",
          "wrong responseText in test for " + test.toSource());
       is(res.events.join(","),
          "opening,rs1,sending,rs1,loadstart,rs2,rs4,error",
          "wrong events in test for " + test.toSource());
       is(res.progressEvents, 0,
@@ -1052,17 +1093,17 @@ function runTest() {
       is(res.events.join(","),
          "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load",
          "wrong responseText in test for " + test.toSource());
     }
     else {
       is(res.didFail, true,
         "should have failed in test for " + test.toSource());
       is(res.status, 0, "wrong status in test for " + test.toSource());
-      is(res.statusText, "", "wrong status in test for " + test.toSource());
+      is(res.statusText, "", "wrong status text for " + test.toSource());
       is(res.responseXML, null,
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "",
          "wrong responseText in test for " + test.toSource());
       is(res.events.join(","),
          "opening,rs1,sending,rs1,loadstart,rs2,rs4,error",
          "wrong events in test for " + test.toSource());
       is(res.progressEvents, 0,
--- a/js/src/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/src/xpconnect/src/dom_quickstubs.qsconf
@@ -422,18 +422,16 @@ members = [
     'nsIBoxObject.width',
     'nsIBoxObject.height',
 
     # XHR
     'nsIXMLHttpRequest.*',
     # nsIXMLHttpRequest.channel is not used on the web, and more
     # importantly relies on the CAPS check that quickstubs don't make.
     '-nsIXMLHttpRequest.channel',
-    # nsIXMLHttpRequest.open uses the JS stack
-    '-nsIXMLHttpRequest.open',
     # various XHR things use ACString and AUTF8String and [cstring]
     # which quickstubs don't handle as return values (or at all in the
     # case of AUTF8String) yet.
     '-nsIXMLHttpRequest.statusText',
     '-nsIXMLHttpRequest.getAllResponseHeaders',
     '-nsIXMLHttpRequest.getResponseHeader',
     '-nsIXMLHttpRequest.setRequestHeader',
     '-nsIXMLHttpRequest.overrideMimeType',