Bug 727530 - XHR for data URIs should support content-type header field; r=jonas
authorMasatoshi Kimura <VYV03354@nifty.ne.jp>
Sun, 19 Feb 2012 11:58:24 +0000
changeset 90450 560cfb993c9d5def5c32f94e1e9ae27d46df6dc1
parent 90449 da1dae1b215d59c18db993b042c43e6decd1138e
child 90451 9a6fcf299f29136e446bf9bb4609f0aa1d5d564f
push id136
push userlsblakk@mozilla.com
push dateFri, 01 Jun 2012 02:39:32 +0000
treeherdermozilla-release@7ebf7352c959 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonas
bugs727530
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 727530 - XHR for data URIs should support content-type header field; r=jonas
content/base/src/nsXMLHttpRequest.cpp
content/base/test/test_XHR.html
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -1319,30 +1319,53 @@ nsXMLHttpRequest::Abort()
 
 /* string getAllResponseHeaders (); */
 NS_IMETHODIMP
 nsXMLHttpRequest::GetAllResponseHeaders(char **_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = nsnull;
 
+  // If the state is UNSENT or OPENED,
+  // return the empty string and terminate these steps.
+  if (mState & (XML_HTTP_REQUEST_UNSENT |
+                XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
+    *_retval = ToNewCString(EmptyString());
+    return NS_OK;
+  }
+
   if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
     *_retval = ToNewCString(EmptyString());
     return NS_OK;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
 
   if (httpChannel) {
     nsRefPtr<nsHeaderVisitor> visitor = new nsHeaderVisitor();
     nsresult rv = httpChannel->VisitResponseHeaders(visitor);
     if (NS_SUCCEEDED(rv))
       *_retval = ToNewCString(visitor->Headers());
+  } else if (mChannel) {
+    // Even non-http channels supply content type.
+    nsCString value;
+    if (NS_SUCCEEDED(mChannel->GetContentType(value))) {
+      nsCString headers;
+      headers.Append("Content-Type: ");
+      headers.Append(value);
+      if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
+          !value.IsEmpty()) {
+        headers.Append(";charset=");
+        headers.Append(value);
+      }
+      headers.Append('\n');
+      *_retval = ToNewCString(headers);
+    }
   }
- 
+
   if (!*_retval) {
     *_retval = ToNewCString(EmptyString());
   }
 
   return NS_OK;
 }
 
 /* ACString getResponseHeader (in AUTF8String header); */
@@ -1351,16 +1374,47 @@ nsXMLHttpRequest::GetResponseHeader(cons
                                     nsACString& _retval)
 {
   nsresult rv = NS_OK;
   _retval.SetIsVoid(true);
 
   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
 
   if (!httpChannel) {
+    // If the state is UNSENT or OPENED,
+    // return null and terminate these steps.
+    if (mState & (XML_HTTP_REQUEST_UNSENT |
+                  XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
+      return NS_OK;
+    }
+
+    // Even non-http channels supply content type.
+    // Remember we don't leak header information from denied cross-site
+    // requests.
+    nsresult status;
+    if (!mChannel ||
+        NS_FAILED(mChannel->GetStatus(&status)) ||
+        NS_FAILED(status) ||
+        !header.LowerCaseEqualsASCII("content-type")) {
+      return NS_OK;
+    }
+
+    if (NS_FAILED(mChannel->GetContentType(_retval))) {
+      // Means no content type
+      _retval.SetIsVoid(true);
+      return NS_OK;
+    }
+
+    nsCString value;
+    if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
+        !value.IsEmpty()) {
+      _retval.Append(";charset=");
+      _retval.Append(value);
+    }
+
     return NS_OK;
   }
 
   // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
   bool chrome = false; // default to false in case IsCapabilityEnabled fails
   IsCapabilityEnabled("UniversalXPConnect", &chrome);
   if (!chrome &&
        (header.LowerCaseEqualsASCII("set-cookie") ||
--- a/content/base/test/test_XHR.html
+++ b/content/base/test/test_XHR.html
@@ -17,43 +17,52 @@ SimpleTest.waitForExplicitFinish();
 
 var gen = runTests();
 function continueTest() { gen.next(); }
 
 function runTests() {
 
 var path = "/tests/content/base/test/";
 
-var passFiles = [['file_XHR_pass1.xml', 'GET', 200],
-                 ['file_XHR_pass2.txt', 'GET', 200],
-                 ['file_XHR_pass3.txt', 'GET', 200],
-                 ['data:text/xml,%3Cres%3Ehello%3C/res%3E%0A', 'GET', 0],
-                 ['data:text/plain,hello%20pass%0A', 'GET', 0],
+var passFiles = [['file_XHR_pass1.xml', 'GET', 200, 'text/xml'],
+                 ['file_XHR_pass2.txt', 'GET', 200, 'text/plain'],
+                 ['file_XHR_pass3.txt', 'GET', 200, 'text/plain'],
+                 ['data:text/xml,%3Cres%3Ehello%3C/res%3E%0A', 'GET', 0, 'text/xml'],
+                 ['data:text/plain,hello%20pass%0A', 'GET', 0, 'text/plain'],
+                 ['data:,foo', 'GET', 0, 'text/plain;charset=US-ASCII', 'foo'],
+                 ['data:text/plain;base64,Zm9v', 'GET', 0, 'text/plain', 'foo'],
+                 ['data:text/plain,foo#bar', 'GET', 0, 'text/plain', 'foo'],
+                 ['data:text/plain,foo%23bar', 'GET', 0, 'text/plain', 'foo#bar'],
                  ];
 
 var failFiles = [['//example.com' + path + 'file_XHR_pass1.xml', 'GET'],
                  ['ftp://localhost' + path + 'file_XHR_pass1.xml', 'GET'],
                  ['file_XHR_fail1.txt', 'GET'],
                  ];
 
 for (i = 0; i < passFiles.length; ++i) {
   xhr = new XMLHttpRequest();
+  is(xhr.getResponseHeader("Content-Type"), null, "should be null");
+  is(xhr.getAllResponseHeaders(), "", "should be empty string");
   is(xhr.responseType, "", "wrong initial responseType");
   xhr.open(passFiles[i][1], passFiles[i][0], false); 
   xhr.send(null);
   is(xhr.status, passFiles[i][2], "wrong status");
+  is(xhr.getResponseHeader("Content-Type"), passFiles[i][3], "wrong content type");
+  var headers = xhr.getAllResponseHeaders();
+  ok(/(?:^|\n)Content-Type:\s*([^\n]*)\n/i.test(headers) &&
+     RegExp.$1 === passFiles[i][3], "wrong response headers");
   if (xhr.responseXML) {
     is((new XMLSerializer()).serializeToString(xhr.responseXML.documentElement),
-       "<res>hello</res>",
-       "wrong responseXML");
-    is(xhr.response, "<res>hello</res>\n", "wrong response");
+       passFiles[i][4] || "<res>hello</res>", "wrong responseXML");
+    is(xhr.response, passFiles[i][4] || "<res>hello</res>\n", "wrong response");
   }
   else {
-    is(xhr.responseText, "hello pass\n", "wrong responseText");
-    is(xhr.response, "hello pass\n", "wrong response");
+    is(xhr.responseText, passFiles[i][4] || "hello pass\n", "wrong responseText");
+    is(xhr.response, passFiles[i][4] || "hello pass\n", "wrong response");
   }
 }
 
 for (i = 0; i < failFiles.length; ++i) {
   xhr = new XMLHttpRequest();
   var didthrow = false;
   try {
     xhr.open(failFiles[i][1], failFiles[i][0], false);