NS_IMETHODIMP
nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest,
                                         nsISupports* aContext)
{
  mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest, PR_FALSE));
  if (!mRequestApproved) {
    if (nsXMLHttpRequest::sAccessControlCache) {
      nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
      if (channel) {
      nsCOMPtr<nsIURI> uri;
        NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
        if (uri) {
          nsXMLHttpRequest::sAccessControlCache->
            RemoveEntries(uri, mRequestingPrincipal);
        }
      }
    }

    aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
    mOuterListener->OnStartRequest(aRequest, aContext);

    return NS_ERROR_DOM_BAD_URI;
  }

  return mOuterListener->OnStartRequest(aRequest, aContext);
}
Example #2
0
NS_IMETHODIMP
FetchDriver::OnRedirectVerifyCallback(nsresult aResult)
{
  // On a successful redirect we perform the following substeps of Section 4.2,
  // step 4.10.
  if (NS_SUCCEEDED(aResult)) {
    // Step 4.10.3 "Set request's url to locationURL." so that when we set the
    // Response's URL from the Request's URL in Section 4, step 6, we get the
    // final value.
    nsCOMPtr<nsIURI> newURI;
    nsresult rv = NS_GetFinalChannelURI(mNewRedirectChannel, getter_AddRefs(newURI));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      aResult = rv;
    } else {
      // We need to update our request's URL.
      nsAutoCString newUrl;
      newURI->GetSpec(newUrl);
      mRequest->SetURL(newUrl);
    }
  }

  if (NS_FAILED(aResult)) {
    mOldRedirectChannel->Cancel(aResult);
  }

  mOldRedirectChannel = nullptr;
  mNewRedirectChannel = nullptr;
  mRedirectCallback->OnRedirectVerifyCallback(aResult);
  mRedirectCallback = nullptr;
  return NS_OK;
}
/*
 * Check that this channel passes all security checks. Returns an error code
 * if this requesst should not be permitted.
 */
nsresult
nsContentSecurityManager::CheckChannel(nsIChannel* aChannel)
{
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
  MOZ_ASSERT(loadInfo);

  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
  NS_ENSURE_SUCCESS(rv, rv);

  // Handle cookie policies
  uint32_t cookiePolicy = loadInfo->GetCookiePolicy();
  if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) {
    nsIPrincipal* loadingPrincipal = loadInfo->LoadingPrincipal();

    // It doesn't matter what we pass for the third, data-inherits, argument.
    // Any protocol which inherits won't pay attention to cookies anyway.
    rv = loadingPrincipal->CheckMayLoad(uri, false, false);
    if (NS_FAILED(rv)) {
      AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
    }
  }
  else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) {
    AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
  }

  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();

  // CORS mode is handled by nsCORSListenerProxy
  if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
    if (NS_HasBeenCrossOrigin(aChannel)) {
      loadInfo->MaybeIncreaseTainting(LoadTainting::CORS);
    }
    return NS_OK;
  }

  // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
  if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) ||
      (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
    rv = DoSOPChecks(uri, loadInfo, aChannel);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
      (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
    if (NS_HasBeenCrossOrigin(aChannel)) {
      loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque);
    }
    // Please note that DoCheckLoadURIChecks should only be enforced for
    // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
    // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
    // within nsCorsListenerProxy
    rv = DoCheckLoadURIChecks(uri, loadInfo);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  return NS_OK;
}
Example #4
0
NS_IMETHODIMP
EventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
                                    nsIChannel *aNewChannel,
                                    uint32_t    aFlags,
                                    nsIAsyncVerifyRedirectCallback *aCallback)
{
  nsCOMPtr<nsIRequest> aOldRequest = do_QueryInterface(aOldChannel);
  NS_PRECONDITION(aOldRequest, "Redirect from a null request?");

  nsresult rv = CheckHealthOfRequestCallback(aOldRequest);
  NS_ENSURE_SUCCESS(rv, rv);

  NS_PRECONDITION(aNewChannel, "Redirect without a channel?");

  nsCOMPtr<nsIURI> newURI;
  rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
  NS_ENSURE_SUCCESS(rv, rv);

  bool isValidScheme =
    (NS_SUCCEEDED(newURI->SchemeIs("http", &isValidScheme)) && isValidScheme) ||
    (NS_SUCCEEDED(newURI->SchemeIs("https", &isValidScheme)) && isValidScheme);

  rv = CheckInnerWindowCorrectness();
  if (NS_FAILED(rv) || !isValidScheme) {
     DispatchFailConnection();
     return NS_ERROR_DOM_SECURITY_ERR;
  }

  // update our channel

  mHttpChannel = do_QueryInterface(aNewChannel);
  NS_ENSURE_STATE(mHttpChannel);

  rv = SetupHttpChannel();
  NS_ENSURE_SUCCESS(rv, rv);

  if ((aFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
    rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc));
    NS_ENSURE_SUCCESS(rv, rv);
  }

  aCallback->OnRedirectVerifyCallback(NS_OK);

  return NS_OK;
}
Example #5
0
/**
 * Returns whether or not the sub-resource about to be loaded is eligible
 * for integrity checks. If it's not, the checks will be skipped and the
 * sub-resource will be loaded.
 */
static nsresult
IsEligible(nsIChannel* aChannel, const CORSMode aCORSMode,
           const nsIDocument* aDocument)
{
  NS_ENSURE_ARG_POINTER(aDocument);

  if (!aChannel) {
    SRILOG(("SRICheck::IsEligible, null channel"));
    return NS_ERROR_SRI_NOT_ELIGIBLE;
  }

  // Was the sub-resource loaded via CORS?
  if (aCORSMode != CORS_NONE) {
    SRILOG(("SRICheck::IsEligible, CORS mode"));
    return NS_OK;
  }

  nsCOMPtr<nsIURI> finalURI;
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIURI> originalURI;
  rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
  NS_ENSURE_SUCCESS(rv, rv);
  nsAutoCString requestSpec;
  rv = originalURI->GetSpec(requestSpec);
  NS_ENSURE_SUCCESS(rv, rv);

  if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) {
    nsAutoCString documentSpec, finalSpec;
    aDocument->GetDocumentURI()->GetAsciiSpec(documentSpec);
    if (finalURI) {
      finalURI->GetSpec(finalSpec);
    }
    SRILOG(("SRICheck::IsEligible, documentURI=%s; requestURI=%s; finalURI=%s",
            documentSpec.get(), requestSpec.get(), finalSpec.get()));
  }

  // Is the sub-resource same-origin?
  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
  if (NS_SUCCEEDED(ssm->CheckSameOriginURI(aDocument->GetDocumentURI(),
                                           finalURI, false))) {
    SRILOG(("SRICheck::IsEligible, same-origin"));
    return NS_OK;
  }
  SRILOG(("SRICheck::IsEligible, NOT same origin"));

  NS_ConvertUTF8toUTF16 requestSpecUTF16(requestSpec);
  const char16_t* params[] = { requestSpecUTF16.get() };
  nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
                                  NS_LITERAL_CSTRING("Sub-resource Integrity"),
                                  aDocument,
                                  nsContentUtils::eSECURITY_PROPERTIES,
                                  "IneligibleResource",
                                  params, ArrayLength(params));
  return NS_ERROR_SRI_NOT_ELIGIBLE;
}
/*
 * Based on the security flags provided in the loadInfo of the channel,
 * doContentSecurityCheck() performs the following content security checks
 * before opening the channel:
 *
 * (1) Same Origin Policy Check (if applicable)
 * (2) Allow Cross Origin but perform sanity checks whether a principal
 *     is allowed to access the following URL.
 * (3) Perform CORS check (if applicable)
 * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...)
 *
 * @param aChannel
 *    The channel to perform the security checks on.
 * @param aInAndOutListener
 *    The streamListener that is passed to channel->AsyncOpen2() that is now potentially
 *    wrappend within nsCORSListenerProxy() and becomes the corsListener that now needs
 *    to be set as new streamListener on the channel.
 */
nsresult
nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel,
                                                 nsCOMPtr<nsIStreamListener>& aInAndOutListener)
{
  NS_ENSURE_ARG(aChannel);
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();

  if (!loadInfo) {
    MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks");
    return NS_ERROR_UNEXPECTED;
  }

  // if dealing with a redirected channel then we have already installed
  // streamlistener and redirect proxies and so we are done.
  if (loadInfo->GetInitialSecurityCheckDone()) {
    return NS_OK;
  }

  // make sure that only one of the five security flags is set in the loadinfo
  // e.g. do not require same origin and allow cross origin at the same time
  nsresult rv = ValidateSecurityFlags(loadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // since aChannel was openend using asyncOpen2() we have to make sure
  // that redirects of that channel also get openend using asyncOpen2()
  // please note that some implementations of ::AsyncOpen2 might already
  // have set that flag to true (e.g. nsViewSourceChannel) in which case
  // we just set the flag again.
  rv = loadInfo->SetEnforceSecurity(true);
  NS_ENSURE_SUCCESS(rv, rv);

  if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
    rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  rv = CheckChannel(aChannel);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIURI> finalChannelURI;
  rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
  NS_ENSURE_SUCCESS(rv, rv);

  // Perform all ContentPolicy checks (MixedContent, CSP, ...)
  rv = DoContentSecurityChecks(finalChannelURI, loadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // now lets set the initalSecurityFlag for subsequent calls
  rv = loadInfo->SetInitialSecurityCheckDone(true);
  NS_ENSURE_SUCCESS(rv, rv);

  // all security checks passed - lets allow the load
  return NS_OK;
}
Example #7
0
/**
 * Returns whether or not the sub-resource about to be loaded is eligible
 * for integrity checks. If it's not, the checks will be skipped and the
 * sub-resource will be loaded.
 */
static nsresult
IsEligible(nsIChannel* aChannel, mozilla::LoadTainting aTainting,
           const nsACString& aSourceFileURI,
           nsIConsoleReportCollector* aReporter)
{
  NS_ENSURE_ARG_POINTER(aReporter);

  if (!aChannel) {
    SRILOG(("SRICheck::IsEligible, null channel"));
    return NS_ERROR_SRI_NOT_ELIGIBLE;
  }

  // Was the sub-resource loaded via CORS?
  if (aTainting == LoadTainting::CORS) {
    SRILOG(("SRICheck::IsEligible, CORS mode"));
    return NS_OK;
  }

  nsCOMPtr<nsIURI> finalURI;
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIURI> originalURI;
  rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
  NS_ENSURE_SUCCESS(rv, rv);
  nsAutoCString requestSpec;
  rv = originalURI->GetSpec(requestSpec);
  NS_ENSURE_SUCCESS(rv, rv);

  if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) {
    SRILOG(("SRICheck::IsEligible, requestURI=%s; finalURI=%s",
            requestSpec.get(),
            finalURI ? finalURI->GetSpecOrDefault().get() : ""));
  }

  // Is the sub-resource same-origin?
  if (aTainting == LoadTainting::Basic) {
    SRILOG(("SRICheck::IsEligible, same-origin"));
    return NS_OK;
  }
  SRILOG(("SRICheck::IsEligible, NOT same origin"));

  NS_ConvertUTF8toUTF16 requestSpecUTF16(requestSpec);
  nsTArray<nsString> params;
  params.AppendElement(requestSpecUTF16);
  aReporter->AddConsoleReport(nsIScriptError::errorFlag,
                              NS_LITERAL_CSTRING("Sub-resource Integrity"),
                              nsContentUtils::eSECURITY_PROPERTIES,
                              aSourceFileURI, 0, 0,
                              NS_LITERAL_CSTRING("IneligibleResource"),
                              const_cast<const nsTArray<nsString>&>(params));
  return NS_ERROR_SRI_NOT_ELIGIBLE;
}
NS_IMETHODIMP
nsCrossSiteListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
                                                 nsIChannel *aNewChannel,
                                                 PRUint32 aFlags,
                                                 nsIAsyncVerifyRedirectCallback *cb)
{
  nsresult rv;
  if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
    rv = CheckRequestApproved(aOldChannel, PR_TRUE);
    if (NS_FAILED(rv)) {
      if (nsXMLHttpRequest::sAccessControlCache) {
        nsCOMPtr<nsIURI> oldURI;
        NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
        if (oldURI) {
          nsXMLHttpRequest::sAccessControlCache->
            RemoveEntries(oldURI, mRequestingPrincipal);
        }
      }
      aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
      return NS_ERROR_DOM_BAD_URI;
    }
  }

  // Prepare to receive callback
  mRedirectCallback = cb;
  mOldRedirectChannel = aOldChannel;
  mNewRedirectChannel = aNewChannel;

  nsCOMPtr<nsIChannelEventSink> outer =
    do_GetInterface(mOuterNotificationCallbacks);
  if (outer) {
    rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this);
    if (NS_FAILED(rv)) {
        aOldChannel->Cancel(rv); // is this necessary...?
        mRedirectCallback = nsnull;
        mOldRedirectChannel = nsnull;
        mNewRedirectChannel = nsnull;
    }
    return rv;  
  }

  (void) OnRedirectVerifyCallback(NS_OK);
  return NS_OK;
}
void
nsCORSPreflightListener::AddResultToCache(nsIRequest *aRequest)
{
  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
  NS_ASSERTION(http, "Request was not http");

  // The "Access-Control-Max-Age" header should return an age in seconds.
  nsCAutoString headerVal;
  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
                          headerVal);
  if (headerVal.IsEmpty()) {
    return;
  }

  // Sanitize the string. We only allow 'delta-seconds' as specified by
  // http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
  // trailing non-whitespace characters).
  PRUint32 age = 0;
  nsCSubstring::const_char_iterator iter, end;
  headerVal.BeginReading(iter);
  headerVal.EndReading(end);
  while (iter != end) {
    if (*iter < '0' || *iter > '9') {
      return;
    }
    age = age * 10 + (*iter - '0');
    // Cap at 24 hours. This also avoids overflow
    age = NS_MIN(age, 86400U);
    ++iter;
  }

  if (!age || !EnsurePreflightCache()) {
    return;
  }


  // String seems fine, go ahead and cache.
  // Note that we have already checked that these headers follow the correct
  // syntax.

  nsCOMPtr<nsIURI> uri;
  NS_GetFinalChannelURI(http, getter_AddRefs(uri));

  // PR_Now gives microseconds
  PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;

  nsPreflightCache::CacheEntry* entry =
    sPreflightCache->GetEntry(uri, mReferrerPrincipal, mWithCredentials,
                              PR_TRUE);
  if (!entry) {
    return;
  }

  // The "Access-Control-Allow-Methods" header contains a comma separated
  // list of method names.
  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
                          headerVal);

  nsCCharSeparatedTokenizer methods(headerVal, ',');
  while(methods.hasMoreTokens()) {
    const nsDependentCSubstring& method = methods.nextToken();
    if (method.IsEmpty()) {
      continue;
    }
    PRUint32 i;
    for (i = 0; i < entry->mMethods.Length(); ++i) {
      if (entry->mMethods[i].token.Equals(method)) {
        entry->mMethods[i].expirationTime = expirationTime;
        break;
      }
    }
    if (i == entry->mMethods.Length()) {
      nsPreflightCache::TokenTime* newMethod =
        entry->mMethods.AppendElement();
      if (!newMethod) {
        return;
      }

      newMethod->token = method;
      newMethod->expirationTime = expirationTime;
    }
  }

  // The "Access-Control-Allow-Headers" header contains a comma separated
  // list of method names.
  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
                          headerVal);

  nsCCharSeparatedTokenizer headers(headerVal, ',');
  while(headers.hasMoreTokens()) {
    const nsDependentCSubstring& header = headers.nextToken();
    if (header.IsEmpty()) {
      continue;
    }
    PRUint32 i;
    for (i = 0; i < entry->mHeaders.Length(); ++i) {
      if (entry->mHeaders[i].token.Equals(header)) {
        entry->mHeaders[i].expirationTime = expirationTime;
        break;
      }
    }
    if (i == entry->mHeaders.Length()) {
      nsPreflightCache::TokenTime* newHeader =
        entry->mHeaders.AppendElement();
      if (!newHeader) {
        return;
      }

      newHeader->token = header;
      newHeader->expirationTime = expirationTime;
    }
  }
}
/*
 * Based on the security flags provided in the loadInfo of the channel,
 * doContentSecurityCheck() performs the following content security checks
 * before opening the channel:
 *
 * (1) Same Origin Policy Check (if applicable)
 * (2) Allow Cross Origin but perform sanity checks whether a principal
 *     is allowed to access the following URL.
 * (3) Perform CORS check (if applicable)
 * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...)
 *
 * @param aChannel
 *    The channel to perform the security checks on.
 * @param aInAndOutListener
 *    The streamListener that is passed to channel->AsyncOpen2() that is now potentially
 *    wrappend within nsCORSListenerProxy() and becomes the corsListener that now needs
 *    to be set as new streamListener on the channel.
 */
nsresult
nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel,
                                                 nsCOMPtr<nsIStreamListener>& aInAndOutListener)
{
  NS_ENSURE_ARG(aChannel);
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();

  if (!loadInfo) {
    MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks");
    return NS_ERROR_UNEXPECTED;
  }

  // make sure that only one of the five security flags is set in the loadinfo
  // e.g. do not require same origin and allow cross origin at the same time
  nsresult rv = ValidateSecurityFlags(loadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // lets store the initialSecurityCheckDone flag which indicates whether the channel
  // was initialy evaluated by the contentSecurityManager. Once the inital
  // asyncOpen() of the channel went through the contentSecurityManager then
  // redirects do not have perform all the security checks, e.g. no reason
  // to setup CORS again.
  bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone();

  // now lets set the initalSecurityFlag for subsequent calls
  rv = loadInfo->SetInitialSecurityCheckDone(true);
  NS_ENSURE_SUCCESS(rv, rv);

  // since aChannel was openend using asyncOpen2() we have to make sure
  // that redirects of that channel also get openend using asyncOpen2()
  // please note that some implementations of ::AsyncOpen2 might already
  // have set that flag to true (e.g. nsViewSourceChannel) in which case
  // we just set the flag again.
  rv = loadInfo->SetEnforceSecurity(true);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIURI> finalChannelURI;
  rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
  NS_ENSURE_SUCCESS(rv, rv);

  // Perform Same Origin Policy check
  rv = DoSOPChecks(finalChannelURI, loadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // if dealing with a redirected channel then we only enforce SOP
  // and can return at this point.
  if (initialSecurityCheckDone) {
    return NS_OK;
  }

  rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // Check if CORS needs to be set up
  rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
  NS_ENSURE_SUCCESS(rv, rv);

  // Perform all ContentPolicy checks (MixedContent, CSP, ...)
  rv = DoContentSecurityChecks(finalChannelURI, loadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // all security checks passed - lets allow the load
  return NS_OK;
}
nsresult
nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel)
{
  nsCOMPtr<nsIURI> uri, originalURI;
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
  NS_ENSURE_SUCCESS(rv, rv);
  rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
  NS_ENSURE_SUCCESS(rv, rv);  

  // Check that the uri is ok to load
  rv = nsContentUtils::GetSecurityManager()->
    CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
                              nsIScriptSecurityManager::STANDARD);
  NS_ENSURE_SUCCESS(rv, rv);

  if (originalURI != uri) {
    rv = nsContentUtils::GetSecurityManager()->
      CheckLoadURIWithPrincipal(mRequestingPrincipal, originalURI,
                                nsIScriptSecurityManager::STANDARD);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  if (!mHasBeenCrossSite &&
      NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri, PR_FALSE)) &&
      (originalURI == uri ||
       NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(originalURI,
                                                       PR_FALSE)))) {
    return NS_OK;
  }

  // It's a cross site load
  mHasBeenCrossSite = PR_TRUE;

  nsCString userpass;
  uri->GetUserPass(userpass);
  NS_ENSURE_TRUE(userpass.IsEmpty(), NS_ERROR_DOM_BAD_URI);

  // Add the Origin header
  nsCAutoString origin;
  rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
  NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);

  rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, PR_FALSE);
  NS_ENSURE_SUCCESS(rv, rv);

  // Add preflight headers if this is a preflight request
  if (mIsPreflight) {
    rv = http->
      SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
                       mPreflightMethod, PR_FALSE);
    NS_ENSURE_SUCCESS(rv, rv);

    if (!mPreflightHeaders.IsEmpty()) {
      nsCAutoString headers;
      for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
        if (i != 0) {
          headers += ',';
        }
        headers += mPreflightHeaders[i];
      }
      rv = http->
        SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
                         headers, PR_FALSE);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  // Make cookie-less if needed
  if (mIsPreflight || !mWithCredentials) {
    nsLoadFlags flags;
    rv = http->GetLoadFlags(&flags);
    NS_ENSURE_SUCCESS(rv, rv);

    flags |= nsIRequest::LOAD_ANONYMOUS;
    rv = http->SetLoadFlags(flags);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  return NS_OK;
}
/*
 * Based on the security flags provided in the loadInfo of the channel,
 * doContentSecurityCheck() performs the following content security checks
 * before opening the channel:
 *
 * (1) Same Origin Policy Check (if applicable)
 * (2) Allow Cross Origin but perform sanity checks whether a principal
 *     is allowed to access the following URL.
 * (3) Perform CORS check (if applicable)
 * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...)
 *
 * @param aChannel
 *    The channel to perform the security checks on.
 * @param aInAndOutListener
 *    The streamListener that is passed to channel->AsyncOpen2() that is now potentially
 *    wrappend within nsCORSListenerProxy() and becomes the corsListener that now needs
 *    to be set as new streamListener on the channel.
 */
nsresult
nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel,
                                                 nsCOMPtr<nsIStreamListener>& aInAndOutListener)
{
  NS_ENSURE_ARG(aChannel);
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();

  if (!loadInfo) {
    MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks");
    return NS_ERROR_UNEXPECTED;
  }

  // make sure that only one of the five security flags is set in the loadinfo
  // e.g. do not require same origin and allow cross origin at the same time
  nsresult rv = ValidateSecurityFlags(loadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // lets store the initialSecurityCheckDone flag which indicates whether the channel
  // was initialy evaluated by the contentSecurityManager. Once the inital
  // asyncOpen() of the channel went through the contentSecurityManager then
  // redirects do not have perform all the security checks, e.g. no reason
  // to setup CORS again.
  bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone();

  // now lets set the initalSecurityFlag for subsequent calls
  rv = loadInfo->SetInitialSecurityCheckDone(true);
  NS_ENSURE_SUCCESS(rv, rv);

  // since aChannel was openend using asyncOpen2() we have to make sure
  // that redirects of that channel also get openend using asyncOpen2()
  // please note that some implementations of ::AsyncOpen2 might already
  // have set that flag to true (e.g. nsViewSourceChannel) in which case
  // we just set the flag again.
  rv = loadInfo->SetEnforceSecurity(true);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIURI> finalChannelURI;
  rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
  NS_ENSURE_SUCCESS(rv, rv);

  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();

  // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
  if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) ||
      (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
    rv = DoSOPChecks(finalChannelURI, loadInfo);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // if dealing with a redirected channel then we only enforce SOP
  // and can return at this point.
  if (initialSecurityCheckDone) {
    return NS_OK;
  }

  if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
      (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
    // Please note that DoCheckLoadURIChecks should only be enforced for
    // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
    // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
    // within nsCorsListenerProxy
    rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
    rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // Perform all ContentPolicy checks (MixedContent, CSP, ...)
  rv = DoContentSecurityChecks(finalChannelURI, loadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // all security checks passed - lets allow the load
  return NS_OK;
}
Example #13
0
nsresult
nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
                                     nsIStreamLoader* aLoader,
                                     nsresult aStatus,
                                     PRUint32 aStringLen,
                                     const PRUint8* aString)
{
    if (NS_FAILED(aStatus)) {
        return aStatus;
    }

    // If we don't have a document, then we need to abort further
    // evaluation.
    if (!mDocument) {
        return NS_ERROR_NOT_AVAILABLE;
    }

    // If the load returned an error page, then we need to abort
    nsCOMPtr<nsIRequest> req;
    nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
    NS_ASSERTION(req, "StreamLoader's request went away prematurely");
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(req);
    if (httpChannel) {
        PRBool requestSucceeded;
        rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
        if (NS_SUCCEEDED(rv) && !requestSucceeded) {
            return NS_ERROR_NOT_AVAILABLE;
        }
    }

    nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
    NS_GetFinalChannelURI(channel, getter_AddRefs(aRequest->mFinalURI));
    if (aStringLen) {
        // Check the charset attribute to determine script charset.
        nsAutoString hintCharset;
        aRequest->mElement->GetScriptCharset(hintCharset);
        rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument,
                            aRequest->mScriptText);

        NS_ASSERTION(NS_SUCCEEDED(rv),
                     "Could not convert external JavaScript to Unicode!");
        NS_ENSURE_SUCCESS(rv, rv);

        if (!ShouldExecuteScript(mDocument, channel)) {
            return NS_ERROR_NOT_AVAILABLE;
        }
    }

    // This assertion could fire errorously if we ran out of memory when
    // inserting the request in the array. However it's an unlikely case
    // so if you see this assertion it is likely something else that is
    // wrong, especially if you see it more than once.
    NS_ASSERTION(mPendingRequests.IndexOf(aRequest) >= 0,
                 "aRequest should be pending!");

    // Mark this as loaded
    aRequest->mLoading = PR_FALSE;

    return NS_OK;
}
Example #14
0
NS_IMETHODIMP
FetchDriver::OnRedirectVerifyCallback(nsresult aResult)
{
  // On a successful redirect we perform the following substeps of HTTP Fetch,
  // step 5, "redirect status", step 11.
  if (NS_SUCCEEDED(aResult)) {
    // Step 11.5 "Append locationURL to request's url list." so that when we set the
    // Response's URL from the Request's URL in Main Fetch, step 15, we get the
    // final value. Note, we still use a single URL value instead of a list.
    nsCOMPtr<nsIURI> newURI;
    nsresult rv = NS_GetFinalChannelURI(mNewRedirectChannel, getter_AddRefs(newURI));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      aResult = rv;
    } else {
      // We need to update our request's URL.
      nsAutoCString newUrl;
      newURI->GetSpec(newUrl);
      mRequest->SetURL(newUrl);
    }
  }

  if (NS_FAILED(aResult)) {
    mOldRedirectChannel->Cancel(aResult);
  }

  // Implement Main Fetch step 8 again on redirect.
  MainFetchOp nextOp = SetTaintingAndGetNextOp(mCORSFlagEverSet);

  if (nextOp.mType == NETWORK_ERROR) {
    // Cancel the channel if Main Fetch blocks the redirect from continuing.
    aResult = NS_ERROR_DOM_BAD_URI;
    mOldRedirectChannel->Cancel(aResult);
  } else {
    // Otherwise, we rely on necko and the CORS proxy to do the right thing
    // as the redirect is followed.  In general this means basic or http
    // fetch.  If we've ever been CORS, we need to stay CORS.
    MOZ_ASSERT(nextOp.mType == BASIC_FETCH || nextOp.mType == HTTP_FETCH);
    MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mType == HTTP_FETCH);
    MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mCORSFlag);

    // Examine and possibly set the LOAD_ANONYMOUS flag on the channel.
    nsLoadFlags flags;
    aResult = mNewRedirectChannel->GetLoadFlags(&flags);
    if (NS_SUCCEEDED(aResult)) {
      if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
          mRequest->GetResponseTainting() == InternalRequest::RESPONSETAINT_OPAQUE) {
        // In the case of a "no-cors" mode request with "same-origin" credentials,
        // we have to set LOAD_ANONYMOUS manually here in order to avoid sending
        // credentials on a cross-origin redirect.
        flags |= nsIRequest::LOAD_ANONYMOUS;
        aResult = mNewRedirectChannel->SetLoadFlags(flags);

      } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
        // Make sure nothing in the redirect chain screws up our credentials
        // settings.  LOAD_ANONYMOUS must be set if we RequestCredentials is "omit".
        MOZ_ASSERT(flags & nsIRequest::LOAD_ANONYMOUS);

      } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
                 nextOp.mCORSFlag) {
        // We also want to verify the LOAD_ANONYMOUS flag is set when we are in
        // "same-origin" credentials mode and the CORS flag is set.  We can't
        // unconditionally assert here, however, because the nsCORSListenerProxy
        // will set the flag later in the redirect callback chain.  Instead,
        // perform a weaker assertion here by checking if CORS flag was set
        // before this redirect.  In that case LOAD_ANONYMOUS must still be set.
        MOZ_ASSERT_IF(mCORSFlagEverSet, flags & nsIRequest::LOAD_ANONYMOUS);

      } else {
        // Otherwise, we should be sending credentials
        MOZ_ASSERT(!(flags & nsIRequest::LOAD_ANONYMOUS));
      }
    }

    // Track the CORSFlag through redirects.
    mCORSFlagEverSet = mCORSFlagEverSet || nextOp.mCORSFlag;
  }

  mOldRedirectChannel = nullptr;
  mNewRedirectChannel = nullptr;
  mRedirectCallback->OnRedirectVerifyCallback(aResult);
  mRedirectCallback = nullptr;
  return NS_OK;
}
Example #15
0
NS_IMETHODIMP
FetchDriver::OnStartRequest(nsIRequest* aRequest,
                            nsISupports* aContext)
{
  workers::AssertIsOnMainThread();

  // Note, this can be called multiple times if we are doing an opaqueredirect.
  // In that case we will get a simulated OnStartRequest() and then the real
  // channel will call in with an errored OnStartRequest().

  nsresult rv;
  aRequest->GetStatus(&rv);
  if (NS_FAILED(rv)) {
    FailWithNetworkError();
    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);

  // On a successful redirect we perform the following substeps of HTTP Fetch,
  // step 5, "redirect status", step 11.

  // Step 11.5 "Append locationURL to request's url list." so that when we set the
  // Response's URL from the Request's URL in Main Fetch, step 15, we get the
  // final value. Note, we still use a single URL value instead of a list.
  // Because of that we only need to do this after the request finishes.
  nsCOMPtr<nsIURI> newURI;
  rv = NS_GetFinalChannelURI(channel, getter_AddRefs(newURI));
  if (NS_FAILED(rv)) {
    FailWithNetworkError();
    return rv;
  }
  nsAutoCString newUrl;
  newURI->GetSpec(newUrl);
  mRequest->SetURL(newUrl);

  bool foundOpaqueRedirect = false;

  if (httpChannel) {
    uint32_t responseStatus;
    httpChannel->GetResponseStatus(&responseStatus);

    if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
      if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
        FailWithNetworkError();
        return NS_BINDING_FAILED;
      }
      if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
        foundOpaqueRedirect = true;
      }
    }

    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 {
    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.
  nsCOMPtr<nsIInputStream> pipeInputStream;
  rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
                  getter_AddRefs(mPipeOutputStream),
                  0, /* default segment size */
                  UINT32_MAX /* infinite pipe */,
                  true /* non-blocking input, otherwise you deadlock */,
                  false /* blocking output, since the pipe is 'in'finite */ );
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }
  response->SetBody(pipeInputStream);

  response->InitChannelInfo(channel);

  nsCOMPtr<nsIURI> channelURI;
  rv = channel->GetURI(getter_AddRefs(channelURI));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  nsCOMPtr<nsILoadInfo> loadInfo;
  rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  // Propagate any tainting from the channel back to our response here.  This
  // step is not reflected in the spec because the spec is written such that
  // FetchEvent.respondWith() just passes the already-tainted Response back to
  // the outer fetch().  In gecko, however, we serialize the Response through
  // the channel and must regenerate the tainting from the channel in the
  // interception case.
  mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());

  // Resolves fetch() promise which may trigger code running in a worker.  Make
  // sure the Response is fully initialized before calling this.
  mResponse = BeginAndGetFilteredResponse(response, channelURI,
                                          foundOpaqueRedirect);

  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  // Try to retarget off main thread.
  if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
    NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
  }
  return NS_OK;
}
nsresult
NS_StartCORSPreflight(nsIChannel* aRequestChannel,
                      nsIStreamListener* aListener,
                      nsIPrincipal* aPrincipal,
                      PRBool aWithCredentials,
                      nsTArray<nsCString>& aUnsafeHeaders,
                      nsIChannel** aPreflightChannel)
{
  *aPreflightChannel = nsnull;

  nsCAutoString method;
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel));
  NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED);
  httpChannel->GetRequestMethod(method);

  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(uri));
  NS_ENSURE_SUCCESS(rv, rv);

  nsPreflightCache::CacheEntry* entry =
    sPreflightCache ?
    sPreflightCache->GetEntry(uri, aPrincipal, aWithCredentials, PR_FALSE) :
    nsnull;

  if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
    // We have a cached preflight result, just start the original channel
    return aRequestChannel->AsyncOpen(aListener, nsnull);
  }

  // Either it wasn't cached or the cached result has expired. Build a
  // channel for the OPTIONS request.

  nsCOMPtr<nsILoadGroup> loadGroup;
  rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
  NS_ENSURE_SUCCESS(rv, rv);

  nsLoadFlags loadFlags;
  rv = aRequestChannel->GetLoadFlags(&loadFlags);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIChannel> preflightChannel;
  rv = NS_NewChannel(getter_AddRefs(preflightChannel), uri, nsnull,
                     loadGroup, nsnull, loadFlags);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
  NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");

  rv = preHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
  NS_ENSURE_SUCCESS(rv, rv);
  
  // Set up listener which will start the original channel
  nsCOMPtr<nsIStreamListener> preflightListener =
    new nsCORSPreflightListener(aRequestChannel, aListener, nsnull, aPrincipal,
                                method, aWithCredentials);
  NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);

  preflightListener =
    new nsCORSListenerProxy(preflightListener, aPrincipal,
                            preflightChannel, aWithCredentials,
                            method, aUnsafeHeaders, &rv);
  NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
  NS_ENSURE_SUCCESS(rv, rv);

  // Start preflight
  rv = preflightChannel->AsyncOpen(preflightListener, nsnull);
  NS_ENSURE_SUCCESS(rv, rv);
  
  // Return newly created preflight channel
  preflightChannel.forget(aPreflightChannel);

  return NS_OK;
}
static nsresult
DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
{
    nsCOMPtr<nsIURI> uri;
    nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
    NS_ENSURE_SUCCESS(rv, rv);

    nsContentPolicyType contentPolicyType =
        aLoadInfo->GetExternalContentPolicyType();
    nsContentPolicyType internalContentPolicyType =
        aLoadInfo->InternalContentPolicyType();
    nsCString mimeTypeGuess;
    nsCOMPtr<nsINode> requestingContext = nullptr;

#ifdef DEBUG
    // Don't enforce TYPE_DOCUMENT assertions for loads
    // initiated by javascript tests.
    bool skipContentTypeCheck = false;
    skipContentTypeCheck = Preferences::GetBool("network.loadinfo.skip_type_assertion");
#endif

    switch(contentPolicyType) {
    case nsIContentPolicy::TYPE_OTHER: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_SCRIPT: {
        mimeTypeGuess = NS_LITERAL_CSTRING("application/javascript");
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_IMAGE: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_STYLESHEET: {
        mimeTypeGuess = NS_LITERAL_CSTRING("text/css");
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_OBJECT: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_DOCUMENT: {
        MOZ_ASSERT(skipContentTypeCheck || false, "contentPolicyType not supported yet");
        break;
    }

    case nsIContentPolicy::TYPE_SUBDOCUMENT: {
        mimeTypeGuess = NS_LITERAL_CSTRING("text/html");
        requestingContext = aLoadInfo->LoadingNode();
        MOZ_ASSERT(!requestingContext ||
                   requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
                   "type_subdocument requires requestingContext of type Document");
        break;
    }

    case nsIContentPolicy::TYPE_REFRESH: {
        MOZ_ASSERT(false, "contentPolicyType not supported yet");
        break;
    }

    case nsIContentPolicy::TYPE_XBL: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_PING: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_XMLHTTPREQUEST: {
        // alias nsIContentPolicy::TYPE_DATAREQUEST:
        requestingContext = aLoadInfo->LoadingNode();
        MOZ_ASSERT(!requestingContext ||
                   requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
                   "type_xml requires requestingContext of type Document");

        // We're checking for the external TYPE_XMLHTTPREQUEST here in case
        // an addon creates a request with that type.
        if (internalContentPolicyType ==
                nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST ||
                internalContentPolicyType ==
                nsIContentPolicy::TYPE_XMLHTTPREQUEST) {
            mimeTypeGuess = EmptyCString();
        }
        else {
            MOZ_ASSERT(internalContentPolicyType ==
                       nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE,
                       "can not set mime type guess for unexpected internal type");
            mimeTypeGuess = NS_LITERAL_CSTRING(TEXT_EVENT_STREAM);
        }
        break;
    }

    case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        MOZ_ASSERT(!requestingContext ||
                   requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE,
                   "type_subrequest requires requestingContext of type Element");
        break;
    }

    case nsIContentPolicy::TYPE_DTD: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        MOZ_ASSERT(!requestingContext ||
                   requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
                   "type_dtd requires requestingContext of type Document");
        break;
    }

    case nsIContentPolicy::TYPE_FONT: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_MEDIA: {
        if (internalContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_TRACK) {
            mimeTypeGuess = NS_LITERAL_CSTRING("text/vtt");
        }
        else {
            mimeTypeGuess = EmptyCString();
        }
        requestingContext = aLoadInfo->LoadingNode();
        MOZ_ASSERT(!requestingContext ||
                   requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE,
                   "type_media requires requestingContext of type Element");
        break;
    }

    case nsIContentPolicy::TYPE_WEBSOCKET: {
        // Websockets have to use the proxied URI:
        // ws:// instead of http:// for CSP checks
        nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal
            = do_QueryInterface(aChannel);
        MOZ_ASSERT(httpChannelInternal);
        if (httpChannelInternal) {
            httpChannelInternal->GetProxyURI(getter_AddRefs(uri));
        }
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_CSP_REPORT: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_XSLT: {
        mimeTypeGuess = NS_LITERAL_CSTRING("application/xml");
        requestingContext = aLoadInfo->LoadingNode();
        MOZ_ASSERT(!requestingContext ||
                   requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
                   "type_xslt requires requestingContext of type Document");
        break;
    }

    case nsIContentPolicy::TYPE_BEACON: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        MOZ_ASSERT(!requestingContext ||
                   requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
                   "type_beacon requires requestingContext of type Document");
        break;
    }

    case nsIContentPolicy::TYPE_FETCH: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_IMAGESET: {
        mimeTypeGuess = EmptyCString();
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    case nsIContentPolicy::TYPE_WEB_MANIFEST: {
        mimeTypeGuess = NS_LITERAL_CSTRING("application/manifest+json");
        requestingContext = aLoadInfo->LoadingNode();
        break;
    }

    default:
        // nsIContentPolicy::TYPE_INVALID
        MOZ_ASSERT(false, "can not perform security check without a valid contentType");
    }

    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
    rv = NS_CheckContentLoadPolicy(internalContentPolicyType,
                                   uri,
                                   aLoadInfo->LoadingPrincipal(),
                                   requestingContext,
                                   mimeTypeGuess,
                                   nullptr,        //extra,
                                   &shouldLoad,
                                   nsContentUtils::GetContentPolicy(),
                                   nsContentUtils::GetSecurityManager());
    NS_ENSURE_SUCCESS(rv, rv);
    if (NS_CP_REJECTED(shouldLoad)) {
        return NS_ERROR_CONTENT_BLOCKED;
    }

    if (nsMixedContentBlocker::sSendHSTSPriming) {
        rv = nsMixedContentBlocker::MarkLoadInfoForPriming(uri,
                requestingContext,
                aLoadInfo);
        return rv;
    }

    return NS_OK;
}
nsresult
nsDOMWorkerScriptLoader::OnStreamCompleteInternal(nsIStreamLoader* aLoader,
                                                  nsISupports* aContext,
                                                  nsresult aStatus,
                                                  PRUint32 aStringLen,
                                                  const PRUint8* aString)
{
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");

  nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext));
  NS_ENSURE_TRUE(indexSupports, NS_ERROR_NO_INTERFACE);

  PRUint32 index = PR_UINT32_MAX;
  indexSupports->GetData(&index);

  if (index >= mScriptCount) {
    NS_NOTREACHED("This really can't fail or we'll hang!");
    return NS_ERROR_FAILURE;
  }

  ScriptLoadInfo& loadInfo = mLoadInfos[index];

  NS_ASSERTION(!loadInfo.done, "Got complete on the same load twice!");
  loadInfo.done = PR_TRUE;

  // Use an alias to keep rv and loadInfo.result in sync.
  nsresult& rv = loadInfo.result;

  if (NS_FAILED(aStatus)) {
    return rv = aStatus;
  }

  if (!(aStringLen && aString)) {
    return rv = NS_ERROR_UNEXPECTED;
  }

  // Make sure we're not seeing the result of a 404 or something by checking the
  // 'requestSucceeded' attribute on the http channel.
  nsCOMPtr<nsIRequest> request;
  rv = aLoader->GetRequest(getter_AddRefs(request));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
  if (httpChannel) {
    PRBool requestSucceeded;
    rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
    NS_ENSURE_SUCCESS(rv, rv);

    if (!requestSucceeded) {
      return rv = NS_ERROR_NOT_AVAILABLE;
    }
  }

  // May be null.
  nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();

  // Use the regular nsScriptLoader for this grunt work! Should be just fine
  // because we're running on the main thread.
  rv = nsScriptLoader::ConvertToUTF16(loadInfo.channel, aString, aStringLen,
                                      EmptyString(), parentDoc,
                                      loadInfo.scriptText);
  if (NS_FAILED(rv)) {
    return rv;
  }

  if (loadInfo.scriptText.IsEmpty()) {
    return rv = NS_ERROR_FAILURE;
  }

  nsCString filename;
  rv = loadInfo.finalURI->GetSpec(filename);
  NS_ENSURE_SUCCESS(rv, rv);

  if (filename.IsEmpty()) {
    filename.Assign(NS_LossyConvertUTF16toASCII(loadInfo.url));
  }
  else {
    // This will help callers figure out what their script url resolved to in
    // case of errors.
    loadInfo.url.Assign(NS_ConvertUTF8toUTF16(filename));
  }

  // Update the principal of the worker and its base URI if we just loaded the
  // worker's primary script.
  if (mForWorker) {
    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
    NS_ASSERTION(channel, "This should never fail!");

    // Take care of the base URI first.
    nsCOMPtr<nsIURI> finalURI;
    rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
    NS_ENSURE_SUCCESS(rv, rv);

    mWorker->SetBaseURI(finalURI);

    // Now to figure out which principal to give this worker.
    nsRefPtr<nsDOMWorker> parent = mWorker->GetParent();
    NS_ASSERTION(mWorker->GetPrincipal() || parent, "Must have one of these!");

    nsCOMPtr<nsIPrincipal> loadPrincipal = mWorker->GetPrincipal() ?
                                           mWorker->GetPrincipal() :
                                           parent->GetPrincipal();

    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
    NS_ASSERTION(ssm, "Should never be null!");

    nsCOMPtr<nsIPrincipal> channelPrincipal;
    rv = ssm->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal));
    NS_ENSURE_SUCCESS(rv, rv);

    // See if this is a resource URI. Since JSMs usually come from resource://
    // URIs we're currently considering all URIs with the URI_IS_UI_RESOURCE
    // flag as valid for creating privileged workers.
    if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
      PRBool isResource;
      rv = NS_URIChainHasFlags(finalURI,
                               nsIProtocolHandler::URI_IS_UI_RESOURCE,
                               &isResource);
      NS_ENSURE_SUCCESS(rv, rv);

      if (isResource) {
        rv = ssm->GetSystemPrincipal(getter_AddRefs(channelPrincipal));
        NS_ENSURE_SUCCESS(rv, rv);
      }
    }

    // If the load principal is the system principal then the channel principal
    // must also be the system principal (we do not allow chrome code to create
    // workers with non-chrome scripts). Otherwise this channel principal must
    // be same origin with the load principal (we check again here in case
    // redirects changed the location of the script).
    if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) {
      if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
        return rv = NS_ERROR_DOM_BAD_URI;
      }
    }
    else if (NS_FAILED(loadPrincipal->CheckMayLoad(finalURI, PR_FALSE))) {
      return rv = NS_ERROR_DOM_BAD_URI;
    }

    mWorker->SetPrincipal(channelPrincipal);
  }

  nsRefPtr<ScriptCompiler> compiler =
    new ScriptCompiler(this, loadInfo.scriptText, filename, loadInfo.scriptObj);
  NS_ASSERTION(compiler, "Out of memory!");
  if (!compiler) {
    return rv = NS_ERROR_OUT_OF_MEMORY;
  }

  rv = mTarget->Dispatch(compiler, NS_DISPATCH_NORMAL);
  NS_ENSURE_SUCCESS(rv, rv);

  return rv;
}