Bug 1195167 part 1: Let necko handle all protocols. r=bkelly
authorJonas Sicking <jonas@sicking.cc>
Mon, 19 Oct 2015 18:24:36 -0700
changeset 303611 ede755bf408567d1ff919e5e5c9fc20e7bff7a73
parent 303610 35ceec16cee3195ba2c9c5545aeede8198a1d575
child 303612 d0e57a128d0d6953a426d7938089314c4d95927d
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1195167
milestone44.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 1195167 part 1: Let necko handle all protocols. r=bkelly
dom/base/nsHostObjectProtocolHandler.cpp
dom/fetch/FetchDriver.cpp
dom/fetch/FetchDriver.h
dom/security/nsContentSecurityManager.cpp
dom/tests/mochitest/fetch/test_fetch_basic.js
netwerk/protocol/http/nsCORSListenerProxy.cpp
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -566,21 +566,24 @@ nsHostObjectProtocolHandler::NewChannel2
 
   ErrorResult rv;
   nsCOMPtr<nsIInputStream> stream;
   blob->GetInternalStream(getter_AddRefs(stream), rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
+  nsAutoString contentType;
+  blob->GetType(contentType);
+
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
                                         uri,
                                         stream,
-                                        EmptyCString(), // aContentType
+                                        NS_ConvertUTF16toUTF8(contentType),
                                         EmptyCString(), // aContentCharset
                                         aLoadInfo);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   nsString type;
   blob->GetType(type);
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -155,21 +155,16 @@ FetchDriver::SetTaintingAndGetNextOp(boo
   }
 
   // request's mode is "no-cors"
   if (mRequest->Mode() == RequestMode::No_cors) {
     mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUE);
     return MainFetchOp(BASIC_FETCH);
   }
 
-  // request's current url's scheme is not one of "http" and "https"
-  if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
-    return MainFetchOp(NETWORK_ERROR);
-  }
-
   // request's mode is "cors-with-forced-preflight"
   // request's unsafe-request flag is set and either request's method is not
   // a simple method or a header in request's header list is not a simple header
   if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
       (mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() ||
                                      !mRequest->Headers()->HasOnlySimpleHeaders()))) {
     mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
     mRequest->SetRedirectMode(RequestRedirect::Error);
@@ -210,163 +205,17 @@ FetchDriver::ContinueFetch(bool aCORSFla
 
   MOZ_ASSERT_UNREACHABLE("Unexpected main fetch operation!");
   return FailWithNetworkError();
  }
 
 nsresult
 FetchDriver::BasicFetch()
 {
-  nsAutoCString url;
-  mRequest->GetURL(url);
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = NS_NewURI(getter_AddRefs(uri),
-                 url,
-                 nullptr,
-                 nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    FailWithNetworkError();
-    return rv;
-  }
-
-  nsAutoCString scheme;
-  rv = uri->GetScheme(scheme);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    FailWithNetworkError();
-    return rv;
-  }
-
-  if (scheme.LowerCaseEqualsLiteral("about")) {
-    if (url.EqualsLiteral("about:blank")) {
-      RefPtr<InternalResponse> response =
-        new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-      ErrorResult result;
-      response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
-                                  NS_LITERAL_CSTRING("text/html;charset=utf-8"),
-                                  result);
-      MOZ_ASSERT(!result.Failed());
-      nsCOMPtr<nsIInputStream> body;
-      rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString());
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        FailWithNetworkError();
-        return rv;
-      }
-
-      response->SetBody(body);
-      BeginResponse(response);
-      return SucceedWithResponse();
-    }
-    return FailWithNetworkError();
-  }
-
-  if (scheme.LowerCaseEqualsLiteral("blob")) {
-    RefPtr<BlobImpl> blobImpl;
-    rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl));
-    BlobImpl* blob = static_cast<BlobImpl*>(blobImpl.get());
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      FailWithNetworkError();
-      return rv;
-    }
-
-    RefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-    ErrorResult result;
-    uint64_t size = blob->GetSize(result);
-    if (NS_WARN_IF(result.Failed())) {
-      FailWithNetworkError();
-      return result.StealNSResult();
-    }
-
-    nsAutoString sizeStr;
-    sizeStr.AppendInt(size);
-    response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result);
-    if (NS_WARN_IF(result.Failed())) {
-      FailWithNetworkError();
-      return result.StealNSResult();
-    }
-
-    nsAutoString type;
-    blob->GetType(type);
-    response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result);
-    if (NS_WARN_IF(result.Failed())) {
-      FailWithNetworkError();
-      return result.StealNSResult();
-    }
-
-    nsCOMPtr<nsIInputStream> stream;
-    blob->GetInternalStream(getter_AddRefs(stream), result);
-    if (NS_WARN_IF(result.Failed())) {
-      FailWithNetworkError();
-      return result.StealNSResult();
-    }
-
-    response->SetBody(stream);
-    BeginResponse(response);
-    return SucceedWithResponse();
-  }
-
-  if (scheme.LowerCaseEqualsLiteral("data")) {
-    nsAutoCString method;
-    mRequest->GetMethod(method);
-    if (method.LowerCaseEqualsASCII("get")) {
-      nsresult rv;
-      nsCOMPtr<nsIProtocolHandler> dataHandler =
-        do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "data", &rv);
-
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return FailWithNetworkError();
-      }
-
-      nsCOMPtr<nsIChannel> channel;
-      rv = dataHandler->NewChannel(uri, getter_AddRefs(channel));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return FailWithNetworkError();
-      }
-
-      nsCOMPtr<nsIInputStream> stream;
-      rv = channel->Open(getter_AddRefs(stream));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return FailWithNetworkError();
-      }
-
-      // nsDataChannel will parse the data URI when it is Open()ed and set the
-      // correct content type and charset.
-      nsAutoCString contentType;
-      if (NS_SUCCEEDED(channel->GetContentType(contentType))) {
-        nsAutoCString charset;
-        if (NS_SUCCEEDED(channel->GetContentCharset(charset)) && !charset.IsEmpty()) {
-          contentType.AppendLiteral(";charset=");
-          contentType.Append(charset);
-        }
-      } else {
-        NS_WARNING("Could not get content type from data channel");
-      }
-
-      RefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-      ErrorResult result;
-      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result);
-      if (NS_WARN_IF(result.Failed())) {
-        FailWithNetworkError();
-        return result.StealNSResult();
-      }
-
-      response->SetBody(stream);
-      BeginResponse(response);
-      return SucceedWithResponse();
-    }
-
-    return FailWithNetworkError();
-  }
-
-  if (scheme.LowerCaseEqualsLiteral("http") ||
-      scheme.LowerCaseEqualsLiteral("https") ||
-      scheme.LowerCaseEqualsLiteral("app")) {
-    return HttpFetch();
-  }
-
-  return FailWithNetworkError();
+  return HttpFetch();
 }
 
 // This function implements the "HTTP Fetch" algorithm from the Fetch spec.
 // Functionality is often split between here, the CORS listener proxy and the
 // Necko HTTP implementation.
 nsresult
 FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthenticationFlag)
 {
@@ -441,17 +290,18 @@ FetchDriver::HttpFetch(bool aCORSFlag, b
 
   // From here on we create a channel and set its properties with the
   // information from the InternalRequest. This is an implementation detail.
   MOZ_ASSERT(mLoadGroup);
   nsCOMPtr<nsIChannel> chan;
   rv = NS_NewChannel(getter_AddRefs(chan),
                      uri,
                      mPrincipal,
-                     nsILoadInfo::SEC_NORMAL,
+                     nsILoadInfo::SEC_NORMAL |
+                       nsILoadInfo::SEC_ABOUT_BLANK_INHERITS,
                      mRequest->ContentPolicyType(),
                      mLoadGroup,
                      nullptr, /* aCallbacks */
                      nsIRequest::LOAD_NORMAL | credentialsFlag | bypassFlag,
                      ios);
   mLoadGroup = nullptr;
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
@@ -694,23 +544,16 @@ FetchDriver::BeginAndGetFilteredResponse
 
   MOZ_ASSERT(filteredResponse);
   MOZ_ASSERT(mObserver);
   mObserver->OnResponseAvailable(filteredResponse);
   mResponseAvailableCalled = true;
   return filteredResponse.forget();
 }
 
-void
-FetchDriver::BeginResponse(InternalResponse* aResponse)
-{
-  RefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse, nullptr);
-  // Release the ref.
-}
-
 nsresult
 FetchDriver::SucceedWithResponse()
 {
   workers::AssertIsOnMainThread();
   if (mObserver) {
     mObserver->OnResponseEnd();
     mObserver = nullptr;
   }
@@ -780,47 +623,75 @@ FetchDriver::OnStartRequest(nsIRequest* 
     return rv;
   }
 
   // We should only get to the following code once.
   MOZ_ASSERT(!mPipeOutputStream);
   MOZ_ASSERT(mObserver);
 
   RefPtr<InternalResponse> response;
+  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
+  nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aRequest);
+
   if (httpChannel) {
     uint32_t responseStatus;
     httpChannel->GetResponseStatus(&responseStatus);
 
     nsAutoCString statusText;
     httpChannel->GetResponseStatusText(statusText);
 
     response = new InternalResponse(responseStatus, statusText);
 
     RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
     rv = httpChannel->VisitResponseHeaders(visitor);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       NS_WARNING("Failed to visit all headers.");
     }
-  } else {
-    nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aRequest);
-    // If it is not an http channel, it has to be a jar one.
-    MOZ_ASSERT(jarChannel);
-
+  } else if (jarChannel) {
     // We simulate the http protocol for jar/app requests
     uint32_t responseStatus = 200;
     nsAutoCString statusText;
     response = new InternalResponse(responseStatus, NS_LITERAL_CSTRING("OK"));
     ErrorResult result;
     nsAutoCString contentType;
     jarChannel->GetContentType(contentType);
     response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
                                 contentType,
                                 result);
     MOZ_ASSERT(!result.Failed());
+  } else {
+    response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
+
+    ErrorResult result;
+    nsAutoCString contentType;
+    rv = channel->GetContentType(contentType);
+    if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
+      nsAutoCString contentCharset;
+      channel->GetContentCharset(contentCharset);
+      if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
+        contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
+      }
+
+      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
+                                  contentType,
+                                  result);
+      MOZ_ASSERT(!result.Failed());
+    }
+
+    int64_t contentLength;
+    rv = channel->GetContentLength(&contentLength);
+    if (NS_SUCCEEDED(rv) && contentLength) {
+      nsAutoCString contentLenStr;
+      contentLenStr.AppendInt(contentLength);
+      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
+                                  contentLenStr,
+                                  result);
+      MOZ_ASSERT(!result.Failed());
+    }
   }
 
   // We open a pipe so that we can immediately set the pipe's read end as the
   // response's body. Setting the segment size to UINT32_MAX means that the
   // pipe has infinite space. The nsIChannel will continue to buffer data in
   // xpcom events even if we block on a fixed size pipe.  It might be possible
   // to suspend the channel and then resume when there is space available, but
   // for now use an infinite pipe to avoid blocking.
@@ -833,17 +704,16 @@ FetchDriver::OnStartRequest(nsIRequest* 
                   false /* blocking output, since the pipe is 'in'finite */ );
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
     return rv;
   }
   response->SetBody(pipeInputStream);
 
-  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   response->InitChannelInfo(channel);
 
   nsCOMPtr<nsIURI> channelURI;
   rv = channel->GetURI(getter_AddRefs(channelURI));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
     return rv;
--- a/dom/fetch/FetchDriver.h
+++ b/dom/fetch/FetchDriver.h
@@ -120,17 +120,16 @@ private:
   nsresult HttpFetch(bool aCORSFlag = false, bool aCORSPreflightFlag = false, bool aAuthenticationFlag = false);
   nsresult ContinueHttpFetchAfterNetworkFetch();
   // Returns the filtered response sent to the observer.
   // Callers who don't have access to a channel can pass null for aFinalURI.
   already_AddRefed<InternalResponse>
   BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI);
   // Utility since not all cases need to do any post processing of the filtered
   // response.
-  void BeginResponse(InternalResponse* aResponse);
   nsresult FailWithNetworkError();
   nsresult SucceedWithResponse();
   nsresult DoesNotRequirePreflight(nsIChannel* aChannel);
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -79,16 +79,23 @@ DoSOPChecks(nsIURI* aURI, nsILoadInfo* a
   if (aLoadInfo->GetAllowChrome() && SchemeIs(aURI, "chrome")) {
     // Enforce same-origin policy, except to chrome.
     return DoCheckLoadURIChecks(aURI, aLoadInfo);
   }
 
   nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal();
   bool sameOriginDataInherits =
     aLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
+
+  if (sameOriginDataInherits &&
+      aLoadInfo->GetAboutBlankInherits() &&
+      NS_IsAboutBlank(aURI)) {
+    return NS_OK;
+  }
+
   return loadingPrincipal->CheckMayLoad(aURI,
                                         true, // report to console
                                         sameOriginDataInherits);
 }
 
 nsresult
 DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
              nsCOMPtr<nsIStreamListener>& aInAndOutListener)
--- a/dom/tests/mochitest/fetch/test_fetch_basic.js
+++ b/dom/tests/mochitest/fetch/test_fetch_basic.js
@@ -1,13 +1,15 @@
 function testAboutURL() {
   var p1 = fetch('about:blank').then(function(res) {
     is(res.status, 200, "about:blank should load a valid Response");
     is(res.headers.get('content-type'), 'text/html;charset=utf-8',
        "about:blank content-type should be text/html;charset=utf-8");
+    is(res.headers.get('content-length'), '0',
+       "about:blank content-type should be text/html;charset=utf-8");
     return res.text().then(function(v) {
       is(v, "", "about:blank body should be empty");
     });
   });
 
   var p2 = fetch('about:config').then(function(res) {
     ok(false, "about:config should fail");
   }, function(e) {
--- a/netwerk/protocol/http/nsCORSListenerProxy.cpp
+++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp
@@ -824,16 +824,22 @@ nsCORSListenerProxy::UpdateChannel(nsICh
   // exempt data URIs from the same origin check.
   if (aAllowDataURI == DataURIHandling::Allow && originalURI == uri) {
     bool dataScheme = false;
     rv = uri->SchemeIs("data", &dataScheme);
     NS_ENSURE_SUCCESS(rv, rv);
     if (dataScheme) {
       return NS_OK;
     }
+    nsCOMPtr<nsILoadInfo> loadInfo;
+    aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
+    if (loadInfo && loadInfo->GetAboutBlankInherits() &&
+        NS_IsAboutBlank(uri)) {
+      return NS_OK;
+    }
   }
 
   // Set CORS attributes on channel so that intercepted requests get correct
   // values. We have to do this here because the CheckMayLoad checks may lead
   // to early return. We can't be sure this is an http channel though, so we
   // can't return early on failure.
   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aChannel);
   if (internal) {