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;

#ifdef DEBUG
  // Make sure we're seeing the channel that we expect.
  nsCOMPtr<nsIRequest> requestDebug;
  nsresult rvDebug = aLoader->GetRequest(getter_AddRefs(requestDebug));

  // When we cancel sometimes we get null here. That should be ok, but only if
  // we're canceled.
  NS_ASSERTION(NS_SUCCEEDED(rvDebug) || mCanceled, "GetRequest failed!");

  if (NS_SUCCEEDED(rvDebug)) {
    nsCOMPtr<nsIChannel> channel(do_QueryInterface(requestDebug));
    NS_ASSERTION(channel, "QI failed!");

    nsCOMPtr<nsISupports> thisChannel(do_QueryInterface(channel));
    NS_ASSERTION(thisChannel, "QI failed!");

    nsCOMPtr<nsISupports> ourChannel(do_QueryInterface(loadInfo.channel));
    NS_ASSERTION(ourChannel, "QI failed!");

    NS_ASSERTION(thisChannel == ourChannel, "Wrong channel!");
  }
#endif

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

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

  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;
    }
  }

  nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();
  if (!parentDoc) {
    NS_ASSERTION(mWorker->IsCanceled(),
                 "Null parent document when we're not canceled?!");
    return rv = NS_ERROR_FAILURE;
  }

  // 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;
  loadInfo.finalURI->GetSpec(filename);

  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));
  }

  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;
}
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;
}