Bug 782342 - blob: protocol no Content-Length header. r=sicking
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 27 Aug 2012 19:34:29 -0400
changeset 105645 3854823c96e22a1a14c0cc2ec2938f00fd40e3b8
parent 105644 4d7b9122a667b8830f000ae3dd4fc31b871fdbe7
child 105646 9c0e1448ff3be564613b1af42b8f31a7b1de8c59
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewerssicking
bugs782342
milestone18.0a1
Bug 782342 - blob: protocol no Content-Length header. r=sicking
content/base/src/nsBlobProtocolHandler.cpp
content/base/src/nsXMLHttpRequest.cpp
content/base/test/Makefile.in
content/base/test/file_bug782342.txt
content/base/test/test_bug782342.html
--- a/content/base/src/nsBlobProtocolHandler.cpp
+++ b/content/base/src/nsBlobProtocolHandler.cpp
@@ -51,37 +51,37 @@ nsBlobProtocolHandler::RemoveFileDataEnt
 }
 
 nsIPrincipal*
 nsBlobProtocolHandler::GetFileDataEntryPrincipal(nsACString& aUri)
 {
   if (!gFileDataTable) {
     return nullptr;
   }
-  
+
   FileDataInfo* res;
   gFileDataTable->Get(aUri, &res);
   if (!res) {
     return nullptr;
   }
 
   return res->mPrincipal;
 }
 
 static FileDataInfo*
 GetFileDataInfo(const nsACString& aUri)
 {
   NS_ASSERTION(StringBeginsWith(aUri,
                                 NS_LITERAL_CSTRING(BLOBURI_SCHEME ":")),
                "Bad URI");
-  
+
   if (!gFileDataTable) {
     return nullptr;
   }
-  
+
   FileDataInfo* res;
   gFileDataTable->Get(aUri, &res);
   return res;
 }
 
 // -----------------------------------------------------------------------
 // Protocol handler
 
@@ -168,24 +168,30 @@ nsBlobProtocolHandler::NewChannel(nsIURI
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsISupports> owner = do_QueryInterface(info->mPrincipal);
 
   nsAutoString type;
   rv = info->mFile->GetType(type);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  PRUint64 size;
+  rv = info->mFile->GetSize(&size);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   channel->SetOwner(owner);
   channel->SetOriginalURI(uri);
   channel->SetContentType(NS_ConvertUTF16toUTF8(type));
+  channel->SetContentLength(size);
+
   channel.forget(result);
-  
+
   return NS_OK;
 }
 
-NS_IMETHODIMP 
+NS_IMETHODIMP
 nsBlobProtocolHandler::AllowPort(int32_t port, const char *scheme,
                                      bool *_retval)
 {
-    // don't override anything.  
+    // don't override anything.
     *_retval = false;
     return NS_OK;
 }
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -1463,16 +1463,23 @@ nsXMLHttpRequest::GetAllResponseHeaders(
     aResponseHeaders.AppendLiteral("Content-Type: ");
     AppendASCIItoUTF16(value, aResponseHeaders);
     if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
       aResponseHeaders.AppendLiteral(";charset=");
       AppendASCIItoUTF16(value, aResponseHeaders);
     }
     aResponseHeaders.AppendLiteral("\r\n");
   }
+
+  PRInt32 length;
+  if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
+    aResponseHeaders.AppendLiteral("Content-Length: ");
+    aResponseHeaders.AppendInt(length);
+    aResponseHeaders.AppendLiteral("\r\n");
+  }
 }
 
 NS_IMETHODIMP
 nsXMLHttpRequest::GetResponseHeader(const nsACString& aHeader,
                                     nsACString& aResult)
 {
   ErrorResult rv;
   GetResponseHeader(aHeader, aResult, rv);
@@ -1490,38 +1497,48 @@ nsXMLHttpRequest::GetResponseHeader(cons
   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;
     }
 
-    // Even non-http channels supply content type.
+    // Even non-http channels supply content type and content length.
     // 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")) {
+        NS_FAILED(status)) {
       return;
     }
 
-    if (NS_FAILED(mChannel->GetContentType(_retval))) {
-      // Means no content type
-      _retval.SetIsVoid(true);
-      return;
+    // Content Type:
+    if (header.LowerCaseEqualsASCII("content-type")) {
+      if (NS_FAILED(mChannel->GetContentType(_retval))) {
+        // Means no content type
+        _retval.SetIsVoid(true);
+        return;
+      }
+
+      nsCString value;
+      if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
+          !value.IsEmpty()) {
+        _retval.Append(";charset=");
+        _retval.Append(value);
+      }
     }
 
-    nsCString value;
-    if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
-        !value.IsEmpty()) {
-      _retval.Append(";charset=");
-      _retval.Append(value);
+    // Content Length:
+    else if (header.LowerCaseEqualsASCII("content-length")) {
+      PRInt32 length;
+      if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
+        _retval.AppendInt(length);
+      }
     }
 
     return;
   }
 
   // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
   bool chrome = false; // default to false in case IsCapabilityEnabled fails
   IsCapabilityEnabled("UniversalXPConnect", &chrome);
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -248,16 +248,18 @@ MOCHITEST_FILES_A = \
 		file_XHRDocURI.xml \
 		file_XHRDocURI.xml^headers^ \
 		file_XHRDocURI.text \
 		file_XHRDocURI.text^headers^ \
 		test_DOMException.html \
 		test_mutationobservers.html \
 		mutationobserver_dialog.html \
 		test_bug744830.html \
+		file_bug782342.txt \
+		test_bug782342.html \
 		$(NULL)
 
 MOCHITEST_FILES_B = \
 		test_bug459424.html \
 		bug461735-redirect1.sjs \
 		bug461735-redirect2.sjs \
 		bug461735-post-redirect.js \
 		test_bug513194.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_bug782342.txt
@@ -0,0 +1,1 @@
+hello world
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug782342.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=782342
+-->
+<head>
+  <title>Test for bug 782342 - blob: protocol no Content-Length header</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=782342">Mozilla Bug 782342 - blob: protocol no Content-Length header</a>
+<p id="display">
+  <input id="fileList" type="file"></input>
+</p>
+
+<script type="text/javascript;version=1.7">
+SimpleTest.waitForExplicitFinish();
+
+var url = "file_bug782342.txt";
+var xhr = new XMLHttpRequest();
+xhr.open("GET", url, true);
+xhr.responseType = "blob";
+
+function checkHeaders(xhr) {
+  var headers = xhr.getAllResponseHeaders();
+  dump(headers + "\n");
+  var p = headers.split("\n");
+
+  var contentType = false;
+  var contentLength = false;
+
+  for (var i = 0; i < p.length; ++i) {
+    var header = p[i].split(':')[0];
+    if (header.toLowerCase() == 'content-type')
+      contentType = true;
+    else if (header.toLowerCase() == 'content-length')
+      contentLength = true;
+  }
+
+  ok(contentLength == true, "Content-length is part of the headers!");
+  ok(contentType == true, "Content-type is part of the headers!");
+
+  var ct = xhr.getResponseHeader('content-type');
+  ok(ct.length > 0, 'Get Response Header - content type: ' + ct);
+  var cl = xhr.getResponseHeader('content-length');
+  ok(cl.length > 0, 'Get Response Header - content length: ' + cl);
+}
+
+xhr.addEventListener("load", function () {
+  ok(xhr.status === 200, "Status 200!");
+  if (xhr.status === 200) {
+    var blob = xhr.response;
+    ok(blob, "Blob is: " + blob);
+    var blobUrl = window.URL.createObjectURL(blob);
+    ok(blobUrl, "Blob URL is: " + blobUrl);
+    checkHeaders(xhr);
+
+    var xhrBlob = new XMLHttpRequest();
+    xhrBlob.open("GET", blobUrl, true);
+    xhrBlob.responseType = "blob";
+
+    xhrBlob.addEventListener("load", function () {
+      var blob2 = xhr.response;
+      ok(blob2, "Blob is: " + blob2);
+      var blob2Url = window.URL.createObjectURL(blob);
+      ok(blob2Url, "Blob URL is: " + blob2Url);
+      checkHeaders(xhrBlob);
+
+      ok(blob2.size == blob.size, "Blob sizes are: " + blob2.size + " - " + blob.size);
+      ok(blob2.type == blob.type, "Blob types are: " + blob2.type + " - " + blob.type);
+
+      SimpleTest.finish();
+    }, false);
+    xhrBlob.send();
+  }
+}, false);
+xhr.send();
+</script>
+</body>
+
+</html>