nsresult nsIncrementalDownload::ProcessTimeout() { NS_ASSERTION(!mChannel, "how can we have a channel?"); // Handle existing error conditions if (NS_FAILED(mStatus)) { CallOnStopRequest(); return NS_OK; } // Fetch next chunk nsCOMPtr<nsIChannel> channel; nsresult rv = NS_NewChannel(getter_AddRefs(channel), mFinalURI, nsnull, nsnull, this, mLoadFlags); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(channel, &rv); if (NS_FAILED(rv)) return rv; NS_ASSERTION(mCurrentSize != nsInt64(-1), "we should know the current file size by now"); rv = ClearRequestHeader(http); if (NS_FAILED(rv)) return rv; // Don't bother making a range request if we are just going to fetch the // entire document. if (mInterval || mCurrentSize != nsInt64(0)) { nsCAutoString range; MakeRangeSpec(mCurrentSize, mTotalSize, mChunkSize, mInterval == 0, range); rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Range"), range, PR_FALSE); if (NS_FAILED(rv)) return rv; } rv = channel->AsyncOpen(this, nsnull); if (NS_FAILED(rv)) return rv; // Wait to assign mChannel when we know we are going to succeed. This is // important because we don't want to introduce a reference cycle between // mChannel and this until we know for a fact that AsyncOpen has succeeded, // thus ensuring that our stream listener methods will be invoked. mChannel = channel; return NS_OK; }
nsresult nsMailboxProtocol::OpenMultipleMsgTransport(PRUint32 offset, PRInt32 size) { nsresult rv; nsCOMPtr<nsIStreamTransportService> serv = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // XXX 64-bit rv = serv->CreateInputTransport(m_multipleMsgMoveCopyStream, nsInt64(offset), nsInt64(size), PR_FALSE, getter_AddRefs(m_transport)); return rv; }
void nsTopProgressManager::UpdateProgressBar(AggregateTransferInfo& info) { if (info.MSecRemaining == 0) return; if (info.SuspendedCount > 0) { // turn on the strobe... FE_SetProgressBarPercent(fContext, 0); return; } nsInt64 dt = nsTime(PR_Now()) - fProgressBarStart; PRUint32 elapsed = dt / nsInt64((PRUint32) PR_USEC_PER_MSEC); // Compute the percent complete, that is, elapsed / (elapsed + remaining) double p = ((double) elapsed) / ((double) (elapsed + info.MSecRemaining)); PRUint32 pctComplete = (PRUint32) (100.0 * p); #define MONOTONIC_PROGRESS_BAR #if defined(MONOTONIC_PROGRESS_BAR) // This hackery is a kludge to make the progress bar // monotonically increase rather than slipping backwards as we // discover that there's more content to download. It works by // adjusting the progress manager's start time backwards to // make the elapsed time (time we've waited so far) seem // larger in proportion to the amount of time that appears to // be left. if (pctComplete < fProgress) { PRUint32 newElapsed = (PRUint32) ((p * ((double) info.MSecRemaining)) / (1.0 - p)); PRInt32 dMSec = newElapsed - elapsed; if (dMSec > 0) fProgressBarStart -= nsInt64(dMSec * ((PRUint32) PR_USEC_PER_MSEC)); // Progress bar hasn't changed -- don't bother updating it. return; } #endif fProgress = pctComplete; FE_SetProgressBarPercent(fContext, fProgress); }
// maxSize may be -1 if unknown static void MakeRangeSpec(const nsInt64 &size, const nsInt64 &maxSize, PRInt32 chunkSize, PRBool fetchRemaining, nsCString &rangeSpec) { rangeSpec.AssignLiteral("bytes="); rangeSpec.AppendInt(PRInt64(size)); rangeSpec.Append('-'); if (fetchRemaining) return; nsInt64 end = size + nsInt64(chunkSize); if (maxSize != nsInt64(-1) && end > maxSize) end = maxSize; end -= 1; rangeSpec.AppendInt(PRInt64(end)); }
NS_IMETHODIMP nsInputStreamPump::Init(nsIInputStream *stream, PRInt64 streamPos, PRInt64 streamLen, PRUint32 segsize, PRUint32 segcount, PRBool closeWhenDone) { NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS); mStreamOffset = PRUint64(streamPos); if (nsInt64(streamLen) >= nsInt64(0)) mStreamLength = PRUint64(streamLen); mStream = stream; mSegSize = segsize; mSegCount = segcount; mCloseWhenDone = closeWhenDone; return NS_OK; }
/** * asynchronously copy file. */ static nsresult RunTest(nsIFile *srcFile, nsIFile *destFile) { nsresult rv; LOG(("RunTest\n")); nsCOMPtr<nsIStreamTransportService> sts = do_GetService(kStreamTransportServiceCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIInputStream> srcStr; rv = NS_NewLocalFileInputStream(getter_AddRefs(srcStr), srcFile); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIOutputStream> destStr; rv = NS_NewLocalFileOutputStream(getter_AddRefs(destStr), destFile); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsITransport> srcTransport; rv = sts->CreateInputTransport(srcStr, nsInt64(-1), nsInt64(-1), PR_TRUE, getter_AddRefs(srcTransport)); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsITransport> destTransport; rv = sts->CreateOutputTransport(destStr, nsInt64(-1), nsInt64(-1), PR_TRUE, getter_AddRefs(destTransport)); if (NS_FAILED(rv)) return rv; MyCopier *copier = new MyCopier(); if (copier == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(copier); rv = copier->AsyncCopy(srcTransport, destTransport); if (NS_FAILED(rv)) return rv; PumpEvents(); NS_RELEASE(copier); return NS_OK; }
NS_IMETHODIMP nsMsgFileStream::Seek(PRInt32 whence, PRInt64 offset) { if (mFileDesc == nsnull) return NS_BASE_STREAM_CLOSED; nsInt64 cnt = PR_Seek64(mFileDesc, offset, (PRSeekWhence)whence); if (cnt == nsInt64(-1)) { return NS_ErrorAccordingToNSPR(); } return NS_OK; }
nsresult nsIncrementalDownload::FlushChunk() { NS_ASSERTION(mTotalSize != nsInt64(-1), "total size should be known"); if (mChunkLen == 0) return NS_OK; nsresult rv = AppendToFile(mDest, mChunk, mChunkLen); if (NS_FAILED(rv)) return rv; mCurrentSize += nsInt64(mChunkLen); mChunkLen = 0; if (mProgressSink) mProgressSink->OnProgress(this, mObserverContext, PRUint64(PRInt64(mCurrentSize)), PRUint64(PRInt64(mTotalSize))); return NS_OK; }
static nsresult RunBlockingTest(nsIFile *srcFile, nsIFile *destFile) { nsresult rv; LOG(("RunBlockingTest\n")); nsCOMPtr<nsIStreamTransportService> sts = do_GetService(kStreamTransportServiceCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIInputStream> srcIn; rv = NS_NewLocalFileInputStream(getter_AddRefs(srcIn), srcFile); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIOutputStream> fileOut; rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOut), destFile); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsITransport> destTransport; rv = sts->CreateOutputTransport(fileOut, nsInt64(-1), nsInt64(-1), PR_TRUE, getter_AddRefs(destTransport)); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIOutputStream> destOut; rv = destTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING, 100, 10, getter_AddRefs(destOut)); if (NS_FAILED(rv)) return rv; char buf[120]; PRUint32 n; for (;;) { rv = srcIn->Read(buf, sizeof(buf), &n); if (NS_FAILED(rv) || (n == 0)) return rv; rv = destOut->Write(buf, n, &n); if (NS_FAILED(rv)) return rv; } return NS_OK; }
NS_IMETHODIMP nsMsgFileStream::Tell(PRInt64 *result) { if (mFileDesc == nsnull) return NS_BASE_STREAM_CLOSED; nsInt64 cnt = PR_Seek64(mFileDesc, 0, PR_SEEK_CUR); if (cnt == nsInt64(-1)) { return NS_ErrorAccordingToNSPR(); } *result = cnt; return NS_OK; }
NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt) { nsCOMPtr<nsIInputStream> inStream; nsresult rv = MakeInputStream(getter_AddRefs(inStream), PR_TRUE); if (NS_FAILED(rv)) return rv; // Init our streampump rv = mPump->Init(inStream, nsInt64(-1), nsInt64(-1), 0, 0, PR_FALSE); if (NS_FAILED(rv)) return rv; rv = mPump->AsyncRead(this, ctxt); if (NS_SUCCEEDED(rv)) { // Store our real listener mListener = aListener; // Add ourself to the load group, if available if (mLoadGroup) mLoadGroup->AddRequest(this, nsnull); } return rv; }
static char* ReadManifestIntoMemory(xptiInterfaceInfoManager* aMgr, PRUint32* pLength) { PRFileDesc* fd = nsnull; PRInt32 flen; PRInt64 fileSize; char* whole = nsnull; PRBool success = PR_FALSE; nsCOMPtr<nsILocalFile> aFile; if(!aMgr->GetCloneOfManifestLocation(getter_AddRefs(aFile)) || !aFile) return nsnull; if(NS_FAILED(aFile->GetFileSize(&fileSize)) || !(flen = nsInt64(fileSize))) return nsnull; whole = new char[flen]; if (!whole) return nsnull; // All exits from on here should be via 'goto out' if(NS_FAILED(aFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd)) || !fd) goto out; if(flen > PR_Read(fd, whole, flen)) goto out; success = PR_TRUE; out: if(fd) PR_Close(fd); if(!success) { delete [] whole; return nsnull; } *pLength = flen; return whole; }
void nsTopProgressManager::UpdateStatusMessage(AggregateTransferInfo& info) { // Compute how much time has elapsed nsInt64 dt = nsTime(PR_Now()) - fActualStart; PRUint32 elapsed = dt / nsInt64((PRUint32) PR_USEC_PER_SEC); char buf[256]; *buf = 0; if (info.ObjectCount == 1 || info.CompleteCount == 0) { // If we only have one object that we're transferring, or if // nothing has completed yet, show the default status message PL_strncpy(buf, fDefaultStatus, sizeof(buf)); } if (elapsed > TIME_UNTIL_DETAILS) { char details[256]; *details = 0; if (!info.UnknownLengthCount && info.ContentLength > 0) { formatKnownContentLength(details, sizeof(details), info.BytesReceived, info.ContentLength, elapsed); } else if (info.BytesReceived > 0) { formatUnknownContentLength(details, sizeof(details), info.BytesReceived, elapsed); } if (*details) { // XXX needs to go to allxpstr.h if (*buf) PL_strcatn(buf, sizeof(buf), ", "); PL_strcatn(buf, sizeof(buf), details); } } FE_Progress(fContext, buf); }
// called on the socket thread nsresult nsHttpTransaction::HandleContent(char *buf, PRUint32 count, PRUint32 *contentRead, PRUint32 *contentRemaining) { nsresult rv; LOG(("nsHttpTransaction::HandleContent [this=%x count=%u]\n", this, count)); *contentRead = 0; *contentRemaining = 0; NS_ASSERTION(mConnection, "no connection"); if (!mDidContentStart) { rv = HandleContentStart(); if (NS_FAILED(rv)) return rv; // Do not write content to the pipe if we haven't started streaming yet if (!mDidContentStart) return NS_OK; } if (mChunkedDecoder) { // give the buf over to the chunked decoder so it can reformat the // data and tell us how much is really there. rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining); if (NS_FAILED(rv)) return rv; } else if (mContentLength >= nsInt64(0)) { // HTTP/1.0 servers have been known to send erroneous Content-Length // headers. So, unless the connection is persistent, we must make // allowances for a possibly invalid Content-Length header. Thus, if // NOT persistent, we simply accept everything in |buf|. if (mConnection->IsPersistent()) { nsInt64 remaining = mContentLength - mContentRead; nsInt64 count64 = count; *contentRead = PR_MIN(count64, remaining); *contentRemaining = count - *contentRead; } else { *contentRead = count; // mContentLength might need to be increased... nsInt64 position = mContentRead + nsInt64(count); if (position > mContentLength) { mContentLength = position; //mResponseHead->SetContentLength(mContentLength); } } } else { // when we are just waiting for the server to close the connection... // (no explicit content-length given) *contentRead = count; } if (*contentRead) { // update count of content bytes read and report progress... mContentRead += *contentRead; /* when uncommenting, take care of 64-bit integers w/ PR_MAX... if (mProgressSink) mProgressSink->OnProgress(nsnull, nsnull, mContentRead, PR_MAX(0, mContentLength)); */ } LOG(("nsHttpTransaction::HandleContent [this=%x count=%u read=%u mContentRead=%lld mContentLength=%lld]\n", this, count, *contentRead, mContentRead.mValue, mContentLength.mValue)); // check for end-of-file if ((mContentRead == mContentLength) || (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) { // the transaction is done with a complete response. mTransactionDone = PR_TRUE; mResponseIsComplete = PR_TRUE; // report the entire response has arrived if (mActivityDistributor) mActivityDistributor->ObserveActivity( mChannel, NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION, NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE, PR_Now(), static_cast<PRUint64>(mContentRead.mValue), EmptyCString()); } return NS_OK; }
// called on the socket thread nsresult nsHttpTransaction::HandleContentStart() { LOG(("nsHttpTransaction::HandleContentStart [this=%x]\n", this)); if (mResponseHead) { #if defined(PR_LOGGING) if (LOG3_ENABLED()) { LOG3(("http response [\n")); nsCAutoString headers; mResponseHead->Flatten(headers, PR_FALSE); LogHeaders(headers.get()); LOG3(("]\n")); } #endif // notify the connection, give it a chance to cause a reset. PRBool reset = PR_FALSE; mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset); // looks like we should ignore this response, resetting... if (reset) { LOG(("resetting transaction's response head\n")); mHaveAllHeaders = PR_FALSE; mHaveStatusLine = PR_FALSE; mReceivedData = PR_FALSE; mSentData = PR_FALSE; mResponseHead->Reset(); // wait to be called again... return NS_OK; } // check if this is a no-content response switch (mResponseHead->Status()) { case 204: case 205: case 304: mNoContent = PR_TRUE; LOG(("this response should not contain a body.\n")); break; } if (mNoContent) mContentLength = 0; else { // grab the content-length from the response headers mContentLength = mResponseHead->ContentLength(); // handle chunked encoding here, so we'll know immediately when // we're done with the socket. please note that _all_ other // decoding is done when the channel receives the content data // so as not to block the socket transport thread too much. // ignore chunked responses from HTTP/1.0 servers and proxies. if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 && mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) { // we only support the "chunked" transfer encoding right now. mChunkedDecoder = new nsHttpChunkedDecoder(); if (!mChunkedDecoder) return NS_ERROR_OUT_OF_MEMORY; LOG(("chunked decoder created\n")); // Ignore server specified Content-Length. mContentLength = -1; } #if defined(PR_LOGGING) else if (mContentLength == nsInt64(-1)) LOG(("waiting for the server to close the connection.\n")); #endif } } mDidContentStart = PR_TRUE; return NS_OK; }
NS_IMETHODIMP nsCookiePermission::CanSetCookie(nsIURI *aURI, nsIChannel *aChannel, nsICookie2 *aCookie, PRBool *aIsSession, PRInt64 *aExpiry, PRBool *aResult) { NS_ASSERTION(aURI, "null uri"); *aResult = kDefaultPolicy; PRUint32 perm; mPermMgr->TestPermission(aURI, kPermissionType, &perm); switch (perm) { case nsICookiePermission::ACCESS_SESSION: *aIsSession = PR_TRUE; case nsIPermissionManager::ALLOW_ACTION: // ACCESS_ALLOW *aResult = PR_TRUE; break; case nsIPermissionManager::DENY_ACTION: // ACCESS_DENY *aResult = PR_FALSE; break; default: // the permission manager has nothing to say about this cookie - // so, we apply the default prefs to it. NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission"); // now we need to figure out what type of accept policy we're dealing with // if we accept cookies normally, just bail and return if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) { *aResult = PR_TRUE; return NS_OK; } // declare this here since it'll be used in all of the remaining cases nsInt64 currentTime = NOW_IN_SECONDS; nsInt64 delta = nsInt64(*aExpiry) - currentTime; // check whether the user wants to be prompted if (mCookiesLifetimePolicy == ASK_BEFORE_ACCEPT) { // if it's a session cookie and the user wants to accept these // without asking, just accept the cookie and return if (*aIsSession && mCookiesAlwaysAcceptSession) { *aResult = PR_TRUE; return NS_OK; } // default to rejecting, in case the prompting process fails *aResult = PR_FALSE; nsCAutoString hostPort; aURI->GetHostPort(hostPort); if (!aCookie) { return NS_ERROR_UNEXPECTED; } // If there is no host, use the scheme, and append "://", // to make sure it isn't a host or something. // This is done to make the dialog appear for javascript cookies from // file:// urls, and make the text on it not too weird. (bug 209689) if (hostPort.IsEmpty()) { aURI->GetScheme(hostPort); if (hostPort.IsEmpty()) { // still empty. Just return the default. return NS_OK; } hostPort = hostPort + NS_LITERAL_CSTRING("://"); } // we don't cache the cookiePromptService - it's not used often, so not // worth the memory. nsresult rv; nsCOMPtr<nsICookiePromptService> cookiePromptService = do_GetService(NS_COOKIEPROMPTSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; // try to get a nsIDOMWindow from the channel... nsCOMPtr<nsIDOMWindow> parent; if (aChannel) NS_QueryNotificationCallbacks(aChannel, parent); // get some useful information to present to the user: // whether a previous cookie already exists, and how many cookies this host // has set PRBool foundCookie; PRUint32 countFromHost; nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) rv = cookieManager->FindMatchingCookie(aCookie, &countFromHost, &foundCookie); if (NS_FAILED(rv)) return rv; // check if the cookie we're trying to set is already expired, and return; // but only if there's no previous cookie, because then we need to delete the previous // cookie. we need this check to avoid prompting the user for already-expired cookies. if (!foundCookie && !*aIsSession && delta <= nsInt64(0)) { // the cookie has already expired. accept it, and let the backend figure // out it's expired, so that we get correct logging & notifications. *aResult = PR_TRUE; return rv; } PRBool rememberDecision = PR_FALSE; rv = cookiePromptService->CookieDialog(parent, aCookie, hostPort, countFromHost, foundCookie, &rememberDecision, aResult); if (NS_FAILED(rv)) return rv; if (*aResult == nsICookiePromptService::ACCEPT_SESSION_COOKIE) *aIsSession = PR_TRUE; if (rememberDecision) { switch (*aResult) { case nsICookiePromptService::DENY_COOKIE: mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::DENY_ACTION); break; case nsICookiePromptService::ACCEPT_COOKIE: mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::ALLOW_ACTION); break; case nsICookiePromptService::ACCEPT_SESSION_COOKIE: mPermMgr->Add(aURI, kPermissionType, nsICookiePermission::ACCESS_SESSION); break; default: break; } } } else { // we're not prompting, so we must be limiting the lifetime somehow // if it's a session cookie, we do nothing if (!*aIsSession && delta > nsInt64(0)) { if (mCookiesLifetimePolicy == ACCEPT_SESSION) { // limit lifetime to session *aIsSession = PR_TRUE; } else if (delta > mCookiesLifetimeSec) { // limit lifetime to specified time *aExpiry = currentTime + mCookiesLifetimeSec; } } } } return NS_OK; }
NS_IMETHODIMP nsIncrementalDownload::OnStartRequest(nsIRequest *request, nsISupports *context) { nsresult rv; nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(request, &rv); if (NS_FAILED(rv)) return rv; // Ensure that we are receiving a 206 response. PRUint32 code; rv = http->GetResponseStatus(&code); if (NS_FAILED(rv)) return rv; if (code != 206) { // We may already have the entire file downloaded, in which case // our request for a range beyond the end of the file would have // been met with an error response code. if (code == 416 && mTotalSize == nsInt64(-1)) { mTotalSize = mCurrentSize; // Return an error code here to suppress OnDataAvailable. return NS_ERROR_DOWNLOAD_COMPLETE; } // The server may have decided to give us all of the data in one chunk. If // we requested a partial range, then we don't want to download all of the // data at once. So, we'll just try again, but if this keeps happening then // we'll eventually give up. if (code == 200) { if (mInterval) { mChannel = nsnull; if (++mNonPartialCount > MAX_RETRY_COUNT) { NS_WARNING("unable to fetch a byte range; giving up"); return NS_ERROR_FAILURE; } // Increase delay with each failure. StartTimer(mInterval * mNonPartialCount); return NS_ERROR_DOWNLOAD_NOT_PARTIAL; } // Since we have been asked to download the rest of the file, we can deal // with a 200 response. This may result in downloading the beginning of // the file again, but that can't really be helped. } else { NS_WARNING("server response was unexpected"); return NS_ERROR_UNEXPECTED; } } else { // We got a partial response, so clear this counter in case the next chunk // results in a 200 response. mNonPartialCount = 0; } // Do special processing after the first response. if (mTotalSize == nsInt64(-1)) { // Update knowledge of mFinalURI rv = http->GetURI(getter_AddRefs(mFinalURI)); if (NS_FAILED(rv)) return rv; if (code == 206) { // OK, read the Content-Range header to determine the total size of this // download file. nsCAutoString buf; rv = http->GetResponseHeader(NS_LITERAL_CSTRING("Content-Range"), buf); if (NS_FAILED(rv)) return rv; PRInt32 slash = buf.FindChar('/'); if (slash == kNotFound) { NS_WARNING("server returned invalid Content-Range header!"); return NS_ERROR_UNEXPECTED; } if (PR_sscanf(buf.get() + slash + 1, "%lld", (PRInt64 *) &mTotalSize) != 1) return NS_ERROR_UNEXPECTED; } else { // Use nsIPropertyBag2 to fetch the content length as it exposes the // value as a 64-bit number. nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(request, &rv); if (NS_FAILED(rv)) return rv; rv = props->GetPropertyAsInt64(NS_CHANNEL_PROP_CONTENT_LENGTH, &mTotalSize.mValue); // We need to know the total size of the thing we're trying to download. if (mTotalSize == nsInt64(-1)) { NS_WARNING("server returned no content-length header!"); return NS_ERROR_UNEXPECTED; } // Need to truncate (or create, if it doesn't exist) the file since we // are downloading the whole thing. WriteToFile(mDest, nsnull, 0, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE); mCurrentSize = 0; } // Notify observer that we are starting... rv = CallOnStartRequest(); if (NS_FAILED(rv)) return rv; } // Adjust mChunkSize accordingly if mCurrentSize is close to mTotalSize. nsInt64 diff = mTotalSize - mCurrentSize; if (diff <= nsInt64(0)) { NS_WARNING("about to set a bogus chunk size; giving up"); return NS_ERROR_UNEXPECTED; } if (diff < nsInt64(mChunkSize)) mChunkSize = PRUint32(diff); mChunk = new char[mChunkSize]; if (!mChunk) rv = NS_ERROR_OUT_OF_MEMORY; return rv; }
// static PRBool xptiManifest::Read(xptiInterfaceInfoManager* aMgr, xptiWorkingSet* aWorkingSet) { int i; char* whole = nsnull; PRBool succeeded = PR_FALSE; PRUint32 flen; nsManifestLineReader reader; xptiHashEntry* hashEntry; int headerCount = 0; int dirCount = 0; int fileCount = 0; int zipItemCount = -1; int interfaceCount = 0; int dir; int flags; char* values[6]; // 6 is currently the max items we need to parse int lengths[6]; PRUint32 size32; PRInt64 size; PRInt64 date; whole = ReadManifestIntoMemory(aMgr, &flen); if(!whole) return PR_FALSE; reader.Init(whole, flen); // All exits from here on should be via 'goto out' // Look for "Header" section // This version accepts only version 1,0. We also freak if the header // has more than one entry. The rationale is that we want to force an // autoreg if the xpti.dat file was written by *any* other version of // the software. Future versions may wish to support updating older // manifests in some interesting way. if(!ReadSectionHeader(reader, g_TOKEN_Header, 2, &headerCount)) goto out; if(headerCount != 2) goto out; // Verify the version number if(!reader.NextLine()) goto out; // index,VersionLiteral,major,minor if(4 != reader.ParseLine(values, lengths, 4)) goto out; // index if(0 != atoi(values[0])) goto out; // VersionLiteral if(0 != PL_strcmp(values[1], g_TOKEN_Version)) goto out; // major if(g_VERSION_MAJOR != atoi(values[2])) goto out; // minor if(g_VERSION_MINOR != atoi(values[3])) goto out; // Verify the application directory if(!reader.NextLine()) goto out; // index,AppDirLiteral,directoryname if(3 != reader.ParseLine(values, lengths, 3)) goto out; // index if(1 != atoi(values[0])) goto out; // AppDirLiteral if(0 != PL_strcmp(values[1], g_TOKEN_AppDir)) goto out; if(!CurrentAppDirMatchesPersistentDescriptor(aMgr, values[2])) goto out; // Look for "Directories" section if(!ReadSectionHeader(reader, g_TOKEN_Directories, 1, &dirCount)) goto out; else { // To validate that the directory list matches the current search path // we first confirm that the list lengths match. nsCOMPtr<nsISupportsArray> searchPath; aMgr->GetSearchPath(getter_AddRefs(searchPath)); PRUint32 searchPathCount; searchPath->Count(&searchPathCount); if(dirCount != (int) searchPathCount) goto out; } // Read the directory records for(i = 0; i < dirCount; ++i) { if(!reader.NextLine()) goto out; // index,directoryname if(2 != reader.ParseLine(values, lengths, 2)) goto out; // index if(i != atoi(values[0])) goto out; // directoryname if(!aWorkingSet->DirectoryAtMatchesPersistentDescriptor(i, values[1])) goto out; } // Look for "Files" section if(!ReadSectionHeader(reader, g_TOKEN_Files, 1, &fileCount)) goto out; // Alloc room in the WorkingSet for the filearray. if(!aWorkingSet->NewFileArray(fileCount)) goto out; // Read the file records for(i = 0; i < fileCount; ++i) { if(!reader.NextLine()) goto out; // index,filename,dirIndex,dilesSize,filesDate if(5 != reader.ParseLine(values, lengths, 5)) goto out; // index if(i != atoi(values[0])) goto out; // filename if(!*values[1]) goto out; // dirIndex dir = atoi(values[2]); if(dir < 0 || dir > dirCount) goto out; // fileSize size32 = atoi(values[3]); if(size32 <= 0) goto out; LL_UI2L(size, size32); // fileDate date = nsCRT::atoll(values[4]); if(LL_IS_ZERO(date)) goto out; // Append a new file record to the array. aWorkingSet->AppendFile( xptiFile(nsInt64(size), nsInt64(date), dir, values[1], aWorkingSet)); } // Look for "ZipItems" section if(!ReadSectionHeader(reader, g_TOKEN_ArchiveItems, 0, &zipItemCount)) goto out; // Alloc room in the WorkingSet for the zipItemarray. if(zipItemCount) if(!aWorkingSet->NewZipItemArray(zipItemCount)) goto out; // Read the zipItem records for(i = 0; i < zipItemCount; ++i) { if(!reader.NextLine()) goto out; // index,filename if(2 != reader.ParseLine(values, lengths, 2)) goto out; // index if(i != atoi(values[0])) goto out; // filename if(!*values[1]) goto out; // Append a new zipItem record to the array. aWorkingSet->AppendZipItem(xptiZipItem(values[1], aWorkingSet)); } // Look for "Interfaces" section if(!ReadSectionHeader(reader, g_TOKEN_Interfaces, 1, &interfaceCount)) goto out; // Read the interface records for(i = 0; i < interfaceCount; ++i) { int fileIndex; int zipItemIndex; nsIID iid; xptiInterfaceEntry* entry; xptiTypelib typelibRecord; if(!reader.NextLine()) goto out; // index,interfaceName,iid,fileIndex,zipIndex,flags if(6 != reader.ParseLine(values, lengths, 6)) goto out; // index if(i != atoi(values[0])) goto out; // interfaceName if(!*values[1]) goto out; // iid if(!iid.Parse(values[2])) goto out; // fileIndex fileIndex = atoi(values[3]); if(fileIndex < 0 || fileIndex >= fileCount) goto out; // zipIndex (NOTE: -1 is a valid value) zipItemIndex = atoi(values[4]); if(zipItemIndex < -1 || zipItemIndex >= zipItemCount) goto out; // flags flags = atoi(values[5]); if(flags != 0 && flags != 1) goto out; // Build an InterfaceInfo and hook it in. if(zipItemIndex == -1) typelibRecord.Init(fileIndex); else typelibRecord.Init(fileIndex, zipItemIndex); entry = xptiInterfaceEntry::NewEntry(values[1], lengths[1], iid, typelibRecord, aWorkingSet); if(!entry) goto out; entry->SetScriptableFlag(flags==1); // Add our entry to the iid hashtable. hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mNameTable, entry->GetTheName(), PL_DHASH_ADD); if(hashEntry) hashEntry->value = entry; // Add our entry to the name hashtable. hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mIIDTable, entry->GetTheIID(), PL_DHASH_ADD); if(hashEntry) hashEntry->value = entry; } // success! succeeded = PR_TRUE; out: if(whole) delete [] whole; if(!succeeded) { // Cleanup the WorkingSet on failure. aWorkingSet->InvalidateInterfaceInfos(); aWorkingSet->ClearHashTables(); aWorkingSet->ClearFiles(); } return succeeded; }
NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request, nsISupports* aContext, nsresult aStatus) { nsresult rv = NS_OK; nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(request); if (!mp) { PRBool found = mRequests.RemoveObject(request); if (!found) { NS_ERROR("Received OnStopRequest for untracked request."); } } PLUGIN_LOG(PLUGIN_LOG_NOISY, ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n", this, aStatus, request)); // for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return. nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request); if (brr) { PRInt64 absoluteOffset64 = LL_ZERO; brr->GetStartRange(&absoluteOffset64); // XXX support 64-bit offsets PRInt32 absoluteOffset = (PRInt32)nsInt64(absoluteOffset64); nsPRUintKey key(absoluteOffset); // remove the request from our data forwarding count hash. mDataForwardToRequest->Remove(&key); PLUGIN_LOG(PLUGIN_LOG_NOISY, (" ::OnStopRequest for ByteRangeRequest Started=%d\n", absoluteOffset)); } else { // if this is not byte range request and // if we are writting the stream to disk ourselves, // close & tear it down here mFileCacheOutputStream = nsnull; } // if we still have pending stuff to do, lets not close the plugin socket. if (--mPendingRequests > 0) return NS_OK; // we keep our connections around... nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext); if (container) { PRUint32 magicNumber = 0; // set it to something that is not the magic number. container->GetData(&magicNumber); if (magicNumber == MAGIC_REQUEST_CONTEXT) { // this is one of our range requests return NS_OK; } } if (!mPStreamListener) return NS_ERROR_FAILURE; nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); if (!channel) return NS_ERROR_FAILURE; // Set the content type to ensure we don't pass null to the plugin nsCAutoString aContentType; rv = channel->GetContentType(aContentType); if (NS_FAILED(rv) && !mRequestFailed) return rv; if (!aContentType.IsEmpty()) mContentType = aContentType; // set error status if stream failed so we notify the plugin if (mRequestFailed) aStatus = NS_ERROR_FAILURE; if (NS_FAILED(aStatus)) { // on error status cleanup the stream // and return w/o OnFileAvailable() mPStreamListener->OnStopBinding(this, aStatus); return NS_OK; } // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly if (mStreamType >= NP_ASFILE) { nsCOMPtr<nsIFile> localFile; if (mLocalCachedFileHolder) localFile = mLocalCachedFileHolder->file(); else { nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request); if (cacheChannel) { cacheChannel->GetCacheFile(getter_AddRefs(localFile)); } else { // see if it is a file channel. nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request); if (fileChannel) { fileChannel->GetFile(getter_AddRefs(localFile)); } } } if (localFile) { OnFileAvailable(localFile); } } if (mStartBinding) { // On start binding has been called mPStreamListener->OnStopBinding(this, aStatus); } else { // OnStartBinding hasn't been called, so complete the action. mPStreamListener->OnStartBinding(this); mPStreamListener->OnStopBinding(this, aStatus); } if (NS_SUCCEEDED(aStatus)) { mStreamComplete = PR_TRUE; } return NS_OK; }
NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request, nsISupports* aContext, nsIInputStream *aIStream, PRUint32 sourceOffset, PRUint32 aLength) { NS_ASSERTION(mRequests.IndexOfObject(GetBaseRequest(request)) != -1, "Received OnDataAvailable for untracked request."); if (mRequestFailed) return NS_ERROR_FAILURE; if (mAbort) { PRUint32 magicNumber = 0; // set it to something that is not the magic number. nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext); if (container) container->GetData(&magicNumber); if (magicNumber != MAGIC_REQUEST_CONTEXT) { // this is not one of our range requests mAbort = PR_FALSE; return NS_BINDING_ABORTED; } } nsresult rv = NS_OK; if (!mPStreamListener) return NS_ERROR_FAILURE; const char * url = nsnull; GetURL(&url); PLUGIN_LOG(PLUGIN_LOG_NOISY, ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%d, length=%d, url=%s\n", this, request, sourceOffset, aLength, url ? url : "no url set")); // if the plugin has requested an AsFileOnly stream, then don't // call OnDataAvailable if (mStreamType != NP_ASFILEONLY) { // get the absolute offset of the request, if one exists. nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request); if (brr) { if (!mDataForwardToRequest) return NS_ERROR_FAILURE; PRInt64 absoluteOffset64 = LL_ZERO; brr->GetStartRange(&absoluteOffset64); // XXX handle 64-bit for real PRInt32 absoluteOffset = (PRInt32)nsInt64(absoluteOffset64); // we need to track how much data we have forwarded to the // plugin. // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130 // // Why couldn't this be tracked on the plugin info, and not in a // *hash table*? nsPRUintKey key(absoluteOffset); PRInt32 amtForwardToPlugin = NS_PTR_TO_INT32(mDataForwardToRequest->Get(&key)); mDataForwardToRequest->Put(&key, NS_INT32_TO_PTR(amtForwardToPlugin + aLength)); SetStreamOffset(absoluteOffset + amtForwardToPlugin); } nsCOMPtr<nsIInputStream> stream = aIStream; // if we are caching the file ourselves to disk, we want to 'tee' off // the data as the plugin read from the stream. We do this by the magic // of an input stream tee. if (mFileCacheOutputStream) { rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream); if (NS_FAILED(rv)) return rv; } rv = mPStreamListener->OnDataAvailable(this, stream, aLength); // if a plugin returns an error, the peer must kill the stream // else the stream and PluginStreamListener leak if (NS_FAILED(rv)) request->Cancel(rv); } else { // if we don't read from the stream, OnStopRequest will never be called char* buffer = new char[aLength]; PRUint32 amountRead, amountWrote = 0; rv = aIStream->Read(buffer, aLength, &amountRead); // if we are caching this to disk ourselves, lets write the bytes out. if (mFileCacheOutputStream) { while (amountWrote < amountRead && NS_SUCCEEDED(rv)) { rv = mFileCacheOutputStream->Write(buffer, amountRead, &amountWrote); } } delete [] buffer; } return rv; }