コード例 #1
0
NS_IMETHODIMP
nsOutputStreamTransport::OpenOutputStream(PRUint32 flags,
                                          PRUint32 segsize,
                                          PRUint32 segcount,
                                          nsIOutputStream **result)
{
    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);

    nsresult rv;
    nsCOMPtr<nsIEventTarget> target =
            do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
    if (NS_FAILED(rv)) return rv;

    // XXX if the caller requests an unbuffered stream, then perhaps
    //     we'd want to simply return mSink; however, then we would
    //     not be writing to mSink on a background thread.  is this ok?
 
    PRBool nonblocking = !(flags & OPEN_BLOCKING);

    net_ResolveSegmentParams(segsize, segcount);
    nsIMemory *segalloc = net_GetSegmentAlloc(segsize);

    nsCOMPtr<nsIAsyncInputStream> pipeIn;
    rv = NS_NewPipe2(getter_AddRefs(pipeIn),
                     getter_AddRefs(mPipeOut),
                     PR_TRUE, nonblocking,
                     segsize, segcount, segalloc);
    if (NS_FAILED(rv)) return rv;

    mInProgress = PR_TRUE;

    // startup async copy process...
    rv = NS_AsyncCopy(pipeIn, this, target,
                      NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
    if (NS_SUCCEEDED(rv))
        NS_ADDREF(*result = mPipeOut);

    return rv;
}
コード例 #2
0
NS_IMETHODIMP
nsInputStreamTransport::OpenInputStream(uint32_t flags,
                                        uint32_t segsize,
                                        uint32_t segcount,
                                        nsIInputStream **result)
{
    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);

    nsresult rv;
    nsCOMPtr<nsIEventTarget> target =
            do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
    if (NS_FAILED(rv)) return rv;

    // XXX if the caller requests an unbuffered stream, then perhaps
    //     we'd want to simply return mSource; however, then we would
    //     not be reading mSource on a background thread.  is this ok?
 
    bool nonblocking = !(flags & OPEN_BLOCKING);

    net_ResolveSegmentParams(segsize, segcount);

    nsCOMPtr<nsIAsyncOutputStream> pipeOut;
    rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
                     getter_AddRefs(pipeOut),
                     nonblocking, true,
                     segsize, segcount);
    if (NS_FAILED(rv)) return rv;

    mInProgress = true;

    // startup async copy process...
    rv = NS_AsyncCopy(this, pipeOut, target,
                      NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
    if (NS_SUCCEEDED(rv))
        NS_ADDREF(*result = mPipeIn);

    return rv;
}
コード例 #3
0
// Called on the worker thread.
nsresult
BackgroundFileSaver::ProcessStateChange()
{
  nsresult rv;

  // We might have been notified because the operation is complete, verify.
  if (CheckCompletion()) {
    return NS_OK;
  }

  // Get a copy of the current shared state for the worker thread.
  nsCOMPtr<nsIFile> initialTarget;
  bool initialTargetKeepPartial;
  nsCOMPtr<nsIFile> renamedTarget;
  bool renamedTargetKeepPartial;
  bool sha256Enabled;
  bool append;
  {
    MutexAutoLock lock(mLock);

    initialTarget = mInitialTarget;
    initialTargetKeepPartial = mInitialTargetKeepPartial;
    renamedTarget = mRenamedTarget;
    renamedTargetKeepPartial = mRenamedTargetKeepPartial;
    sha256Enabled = mSha256Enabled;
    append = mAppend;

    // From now on, another attention event needs to be posted if state changes.
    mWorkerThreadAttentionRequested = false;
  }

  // The initial target can only be null if it has never been assigned.  In this
  // case, there is nothing to do since we never created any output file.
  if (!initialTarget) {
    return NS_OK;
  }

  // Determine if we are processing the attention request for the first time.
  bool isContinuation = !!mActualTarget;
  if (!isContinuation) {
    // Assign the target file for the first time.
    mActualTarget = initialTarget;
    mActualTargetKeepPartial = initialTargetKeepPartial;
  }

  // Verify whether we have actually been instructed to use a different file.
  // This may happen the first time this function is executed, if SetTarget was
  // called two times before the worker thread processed the attention request.
  bool equalToCurrent = false;
  if (renamedTarget) {
    rv = mActualTarget->Equals(renamedTarget, &equalToCurrent);
    NS_ENSURE_SUCCESS(rv, rv);
    if (!equalToCurrent)
    {
      // If we were asked to rename the file but the initial file did not exist,
      // we simply create the file in the renamed location.  We avoid this check
      // if we have already started writing the output file ourselves.
      bool exists = true;
      if (!isContinuation) {
        rv = mActualTarget->Exists(&exists);
        NS_ENSURE_SUCCESS(rv, rv);
      }
      if (exists) {
        // We are moving the previous target file to a different location.
        nsCOMPtr<nsIFile> renamedTargetParentDir;
        rv = renamedTarget->GetParent(getter_AddRefs(renamedTargetParentDir));
        NS_ENSURE_SUCCESS(rv, rv);

        nsAutoString renamedTargetName;
        rv = renamedTarget->GetLeafName(renamedTargetName);
        NS_ENSURE_SUCCESS(rv, rv);

        // We must delete any existing target file before moving the current
        // one.
        rv = renamedTarget->Exists(&exists);
        NS_ENSURE_SUCCESS(rv, rv);
        if (exists) {
          rv = renamedTarget->Remove(false);
          NS_ENSURE_SUCCESS(rv, rv);
        }

        // Move the file.  If this fails, we still reference the original file
        // in mActualTarget, so that it is deleted if requested.  If this
        // succeeds, the nsIFile instance referenced by mActualTarget mutates
        // and starts pointing to the new file, but we'll discard the reference.
        rv = mActualTarget->MoveTo(renamedTargetParentDir, renamedTargetName);
        NS_ENSURE_SUCCESS(rv, rv);
      }

      // Now we can update the actual target file name.
      mActualTarget = renamedTarget;
      mActualTargetKeepPartial = renamedTargetKeepPartial;
    }
  }

  // Notify if the target file name actually changed.
  if (!equalToCurrent) {
    // We must clone the nsIFile instance because mActualTarget is not
    // immutable, it may change if the target is renamed later.
    nsCOMPtr<nsIFile> actualTargetToNotify;
    rv = mActualTarget->Clone(getter_AddRefs(actualTargetToNotify));
    NS_ENSURE_SUCCESS(rv, rv);

    RefPtr<NotifyTargetChangeRunnable> event =
      new NotifyTargetChangeRunnable(this, actualTargetToNotify);
    NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);

    rv = mControlThread->Dispatch(event, NS_DISPATCH_NORMAL);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  if (isContinuation) {
    // The pending rename operation might be the last task before finishing. We
    // may return here only if we have already created the target file.
    if (CheckCompletion()) {
      return NS_OK;
    }

    // Even if the operation did not complete, the pipe input stream may be
    // empty and may have been closed already.  We detect this case using the
    // Available property, because it never returns an error if there is more
    // data to be consumed.  If the pipe input stream is closed, we just exit
    // and wait for more calls like SetTarget or Finish to be invoked on the
    // control thread.  However, we still truncate the file or create the
    // initial digest context if we are expected to do that.
    uint64_t available;
    rv = mPipeInputStream->Available(&available);
    if (NS_FAILED(rv)) {
      return NS_OK;
    }
  }

  // Create the digest context if requested and NSS hasn't been shut down.
  if (sha256Enabled && !mDigestContext) {
    nsNSSShutDownPreventionLock lock;
    if (!isAlreadyShutDown()) {
      mDigestContext = UniquePK11Context(
        PK11_CreateDigestContext(SEC_OID_SHA256));
      NS_ENSURE_TRUE(mDigestContext, NS_ERROR_OUT_OF_MEMORY);
    }
  }

  // When we are requested to append to an existing file, we should read the
  // existing data and ensure we include it as part of the final hash.
  if (mDigestContext && append && !isContinuation) {
    nsCOMPtr<nsIInputStream> inputStream;
    rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
                                    mActualTarget,
                                    PR_RDONLY | nsIFile::OS_READAHEAD);
    if (rv != NS_ERROR_FILE_NOT_FOUND) {
      NS_ENSURE_SUCCESS(rv, rv);

      char buffer[BUFFERED_IO_SIZE];
      while (true) {
        uint32_t count;
        rv = inputStream->Read(buffer, BUFFERED_IO_SIZE, &count);
        NS_ENSURE_SUCCESS(rv, rv);

        if (count == 0) {
          // We reached the end of the file.
          break;
        }

        nsNSSShutDownPreventionLock lock;
        if (isAlreadyShutDown()) {
          return NS_ERROR_NOT_AVAILABLE;
        }

        nsresult rv = MapSECStatus(PK11_DigestOp(mDigestContext.get(),
                                                 uint8_t_ptr_cast(buffer),
                                                 count));
        NS_ENSURE_SUCCESS(rv, rv);
      }

      rv = inputStream->Close();
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  // We will append to the initial target file only if it was requested by the
  // caller, but we'll always append on subsequent accesses to the target file.
  int32_t creationIoFlags;
  if (isContinuation) {
    creationIoFlags = PR_APPEND;
  } else {
    creationIoFlags = (append ? PR_APPEND : PR_TRUNCATE) | PR_CREATE_FILE;
  }

  // Create the target file, or append to it if we already started writing it.
  // The 0600 permissions are used while the file is being downloaded, and for
  // interrupted downloads. Those may be located in the system temporary
  // directory, as well as the target directory, and generally have a ".part"
  // extension. Those part files should never be group or world-writable even
  // if the umask allows it.
  nsCOMPtr<nsIOutputStream> outputStream;
  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
                                   mActualTarget,
                                   PR_WRONLY | creationIoFlags, 0600);
  NS_ENSURE_SUCCESS(rv, rv);

  outputStream = NS_BufferOutputStream(outputStream, BUFFERED_IO_SIZE);
  if (!outputStream) {
    return NS_ERROR_FAILURE;
  }

  // Wrap the output stream so that it feeds the digest context if needed.
  if (mDigestContext) {
    // No need to acquire the NSS lock here, DigestOutputStream must acquire it
    // in any case before each asynchronous write. Constructing the
    // DigestOutputStream cannot fail. Passing mDigestContext to
    // DigestOutputStream is safe, because BackgroundFileSaver always outlives
    // the outputStream. BackgroundFileSaver is reference-counted before the
    // call to AsyncCopy, and mDigestContext is never destroyed before
    // AsyncCopyCallback.
    outputStream = new DigestOutputStream(outputStream, mDigestContext.get());
  }

  // Start copying our input to the target file.  No errors can be raised past
  // this point if the copy starts, since they should be handled by the thread.
  {
    MutexAutoLock lock(mLock);

    rv = NS_AsyncCopy(mPipeInputStream, outputStream, mWorkerThread,
                      NS_ASYNCCOPY_VIA_READSEGMENTS, 4096, AsyncCopyCallback,
                      this, false, true, getter_AddRefs(mAsyncCopyContext),
                      GetProgressCallback());
    if (NS_FAILED(rv)) {
      NS_WARNING("NS_AsyncCopy failed.");
      mAsyncCopyContext = nullptr;
      return rv;
    }
  }

  // If the operation succeeded, we must ensure that we keep this object alive
  // for the entire duration of the copy, since only the raw pointer will be
  // provided as the argument of the AsyncCopyCallback function.  We can add the
  // reference now, after NS_AsyncCopy returned, because it always starts
  // processing asynchronously, and there is no risk that the callback is
  // invoked before we reach this point.  If the operation failed instead, then
  // AsyncCopyCallback will never be called.
  NS_ADDREF_THIS();

  return NS_OK;
}