Bug 782342 - blob: protocol no Content-Length header. r=sicking
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 27 Aug 2012 19:34:29 -0400
changeset 103610 3854823c96e22a1a14c0cc2ec2938f00fd40e3b8
parent 103609 4d7b9122a667b8830f000ae3dd4fc31b871fdbe7
child 103611 9c0e1448ff3be564613b1af42b8f31a7b1de8c59
push id14074
push userryanvm@gmail.com
push dateMon, 27 Aug 2012 23:36:47 +0000
treeherdermozilla-inbound@1b3fa4309f31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking
bugs782342
milestone18.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 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>