NS_IMETHODIMP nsIncrementalDownload::OnStopRequest(nsIRequest *request, nsISupports *context, nsresult status) { // Not a real error; just a trick to kill off the channel without our // listener having to care. if (status == NS_ERROR_DOWNLOAD_NOT_PARTIAL) return NS_OK; // Not a real error; just a trick used to suppress OnDataAvailable calls. if (status == NS_ERROR_DOWNLOAD_COMPLETE) status = NS_OK; if (NS_SUCCEEDED(mStatus)) mStatus = status; if (mChunk) { if (NS_SUCCEEDED(mStatus)) mStatus = FlushChunk(); mChunk = nsnull; // deletes memory mChunkLen = 0; } mChannel = nsnull; // Notify listener if we hit an error or finished if (NS_FAILED(mStatus) || mCurrentSize == mTotalSize) { CallOnStopRequest(); return NS_OK; } return StartTimer(mInterval); // Do next chunk }
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 != PRInt64(-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 != PRInt64(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; }
NS_IMETHODIMP nsIncrementalDownload::Observe(nsISupports *subject, const char *topic, const char16_t *data) { if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { Cancel(NS_ERROR_ABORT); // Since the app is shutting down, we need to go ahead and notify our // observer here. Otherwise, we would notify them after XPCOM has been // shutdown or not at all. CallOnStopRequest(); } else if (strcmp(topic, NS_TIMER_CALLBACK_TOPIC) == 0) { mTimer = nullptr; nsresult rv = ProcessTimeout(); if (NS_FAILED(rv)) Cancel(rv); } return NS_OK; }
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, nsContentUtils::GetSystemPrincipal(), nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, nsIContentPolicy::TYPE_OTHER, nullptr, // loadGroup this, // aCallbacks mLoadFlags); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(channel, &rv); if (NS_FAILED(rv)) return rv; NS_ASSERTION(mCurrentSize != int64_t(-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 != int64_t(0)) { nsAutoCString range; MakeRangeSpec(mCurrentSize, mTotalSize, mChunkSize, mInterval == 0, range); rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Range"), range, false); if (NS_FAILED(rv)) return rv; if (!mPartialValidator.IsEmpty()) http->SetRequestHeader(NS_LITERAL_CSTRING("If-Range"), mPartialValidator, false); if (mCacheBust) { http->SetRequestHeader(NS_LITERAL_CSTRING("Cache-Control"), NS_LITERAL_CSTRING("no-cache"), false); http->SetRequestHeader(NS_LITERAL_CSTRING("Pragma"), NS_LITERAL_CSTRING("no-cache"), false); } } rv = channel->AsyncOpen2(this); 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; }