mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1373708 - Convert Jar channels to stop using main thread I/O, r=nwgh
This commit is contained in:
parent
f3e6656403
commit
9a06318245
@ -26,6 +26,7 @@
|
||||
#include "nsITabChild.h"
|
||||
#include "private/pprio.h"
|
||||
#include "nsInputStreamPump.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::net;
|
||||
@ -198,9 +199,12 @@ nsJARChannel::nsJARChannel()
|
||||
, mLoadFlags(LOAD_NORMAL)
|
||||
, mStatus(NS_OK)
|
||||
, mIsPending(false)
|
||||
, mEnableOMT(true)
|
||||
, mPendingEvent()
|
||||
, mIsUnsafe(true)
|
||||
, mBlockRemoteFiles(false)
|
||||
{
|
||||
LOG(("nsJARChannel::nsJARChannel [this=%p]\n", this));
|
||||
mBlockRemoteFiles = Preferences::GetBool("network.jar.block-remote-files", false);
|
||||
|
||||
// hold an owning reference to the jar handler
|
||||
@ -209,6 +213,7 @@ nsJARChannel::nsJARChannel()
|
||||
|
||||
nsJARChannel::~nsJARChannel()
|
||||
{
|
||||
LOG(("nsJARChannel::~nsJARChannel [this=%p]\n", this));
|
||||
NS_ReleaseOnMainThreadSystemGroup("nsJARChannel::mLoadInfo",
|
||||
mLoadInfo.forget());
|
||||
}
|
||||
@ -226,7 +231,14 @@ NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel,
|
||||
nsresult
|
||||
nsJARChannel::Init(nsIURI *uri)
|
||||
{
|
||||
LOG(("nsJARChannel::Init [this=%p]\n", this));
|
||||
nsresult rv;
|
||||
|
||||
mWorker = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mJarURI = do_QueryInterface(uri, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
@ -254,6 +266,7 @@ nsJARChannel::Init(nsIURI *uri)
|
||||
nsresult
|
||||
nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resultInput)
|
||||
{
|
||||
LOG(("nsJARChannel::CreateJarInput [this=%p]\n", this));
|
||||
MOZ_ASSERT(resultInput);
|
||||
MOZ_ASSERT(mJarFile || mTempMem);
|
||||
|
||||
@ -269,7 +282,7 @@ nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resu
|
||||
|
||||
nsCOMPtr<nsIZipReader> reader;
|
||||
if (mPreCachedJarReader) {
|
||||
reader = mPreCachedJarReader;
|
||||
reader = mPreCachedJarReader;
|
||||
} else if (jarCache) {
|
||||
MOZ_ASSERT(mJarFile);
|
||||
if (mInnerJarEntry.IsEmpty())
|
||||
@ -306,10 +319,9 @@ nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resu
|
||||
return rv;
|
||||
|
||||
RefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader,
|
||||
mJarURI,
|
||||
mJarEntry,
|
||||
jarCache != nullptr
|
||||
);
|
||||
mJarURI,
|
||||
mJarEntry,
|
||||
jarCache != nullptr);
|
||||
rv = input->Init();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
@ -322,7 +334,7 @@ nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resu
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJARChannel::LookupFile(bool aAllowAsync)
|
||||
nsJARChannel::LookupFile()
|
||||
{
|
||||
LOG(("nsJARChannel::LookupFile [this=%p %s]\n", this, mSpec.get()));
|
||||
|
||||
@ -377,25 +389,236 @@ nsJARChannel::LookupFile(bool aAllowAsync)
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateLocalJarInput(nsIZipReaderCache* aJarCache,
|
||||
nsIFile* aFile,
|
||||
const nsACString& aInnerJarEntry,
|
||||
nsIJARURI* aJarURI,
|
||||
const nsACString& aJarEntry,
|
||||
nsJARInputThunk** aResultInput)
|
||||
{
|
||||
LOG(("nsJARChannel::CreateLocalJarInput [aJarCache=%p, %s, %s]\n",
|
||||
aJarCache,
|
||||
PromiseFlatCString(aInnerJarEntry).get(),
|
||||
PromiseFlatCString(aJarEntry).get()));
|
||||
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aJarCache);
|
||||
MOZ_ASSERT(aResultInput);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIZipReader> reader;
|
||||
if (aInnerJarEntry.IsEmpty()) {
|
||||
rv = aJarCache->GetZip(aFile, getter_AddRefs(reader));
|
||||
} else {
|
||||
rv = aJarCache->GetInnerZip(aFile,
|
||||
aInnerJarEntry,
|
||||
getter_AddRefs(reader));
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
RefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader,
|
||||
aJarURI,
|
||||
aJarEntry,
|
||||
aJarCache != nullptr);
|
||||
rv = input->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
input.forget(aResultInput);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJARChannel::OpenLocalFile()
|
||||
{
|
||||
LOG(("nsJARChannel::OpenLocalFile [this=%p]\n", this));
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MOZ_ASSERT(mWorker);
|
||||
MOZ_ASSERT(mIsPending);
|
||||
MOZ_ASSERT(mJarFile);
|
||||
|
||||
// Local files are always considered safe.
|
||||
mIsUnsafe = false;
|
||||
|
||||
RefPtr<nsJARInputThunk> input;
|
||||
nsresult rv = CreateJarInput(gJarHandler->JarCache(),
|
||||
getter_AddRefs(input));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Create input stream pump and call AsyncRead as a block.
|
||||
rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = mPump->AsyncRead(this, nullptr);
|
||||
nsresult rv;
|
||||
|
||||
// Set mLoadGroup and mOpened before AsyncOpen return, and set back if
|
||||
// if failed when callback.
|
||||
if (mLoadGroup) {
|
||||
mLoadGroup->AddRequest(this, nullptr);
|
||||
}
|
||||
mOpened = true;
|
||||
|
||||
if (mPreCachedJarReader || !mEnableOMT) {
|
||||
RefPtr<nsJARInputThunk> input;
|
||||
rv = CreateJarInput(gJarHandler->JarCache(),
|
||||
getter_AddRefs(input));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return OnOpenLocalFileComplete(rv, true);
|
||||
}
|
||||
return ContinueOpenLocalFile(input, true);
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIZipReaderCache> jarCache = gJarHandler->JarCache();
|
||||
if (NS_WARN_IF(!jarCache)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> clonedFile;
|
||||
rv = mJarFile->Clone(getter_AddRefs(clonedFile));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// clone mJarURI
|
||||
nsCOMPtr<nsIURI> clonedURI;
|
||||
rv = mJarURI->Clone(getter_AddRefs(clonedURI));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsIJARURI> clonedJarURI = do_QueryInterface(clonedURI, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString jarEntry(mJarEntry);
|
||||
nsAutoCString innerJarEntry(mInnerJarEntry);
|
||||
|
||||
RefPtr<nsJARChannel> self = this;
|
||||
return mWorker->Dispatch(
|
||||
NS_NewRunnableFunction("nsJARChannel::OpenLocalFile",
|
||||
[self,
|
||||
jarCache,
|
||||
clonedFile,
|
||||
clonedJarURI,
|
||||
jarEntry,
|
||||
innerJarEntry] () mutable {
|
||||
|
||||
RefPtr<nsJARInputThunk> input;
|
||||
nsresult rv = CreateLocalJarInput(jarCache,
|
||||
clonedFile,
|
||||
innerJarEntry,
|
||||
clonedJarURI,
|
||||
jarEntry,
|
||||
getter_AddRefs(input));
|
||||
|
||||
NS_ReleaseOnMainThreadSystemGroup("nsJARChannel::clonedJarURI",
|
||||
clonedJarURI.forget());
|
||||
|
||||
nsCOMPtr<nsIRunnable> target;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
target = NewRunnableMethod<RefPtr<nsJARInputThunk>, bool>(
|
||||
"nsJARChannel::ContinueOpenLocalFile",
|
||||
self,
|
||||
&nsJARChannel::ContinueOpenLocalFile,
|
||||
input,
|
||||
false);
|
||||
} else {
|
||||
target = NewRunnableMethod<nsresult, bool>(
|
||||
"nsJARChannel::OnOpenLocalFileComplete",
|
||||
self,
|
||||
&nsJARChannel::OnOpenLocalFileComplete,
|
||||
rv,
|
||||
false);
|
||||
}
|
||||
|
||||
// nsJARChannel must be release on main thread, and sometimes
|
||||
// this still hold nsJARChannel after dispatched.
|
||||
self = nullptr;
|
||||
|
||||
NS_DispatchToMainThread(target.forget());
|
||||
}));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJARChannel::ContinueOpenLocalFile(nsJARInputThunk* aInput, bool aIsSyncCall)
|
||||
{
|
||||
LOG(("nsJARChannel::ContinueOpenLocalFile [this=%p %p]\n", this, aInput));
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mIsPending);
|
||||
|
||||
// Make GetContentLength meaningful
|
||||
mContentLength = aInput->GetContentLength();
|
||||
|
||||
nsresult rv;
|
||||
RefPtr<nsJARInputThunk> input = aInput;
|
||||
// Create input stream pump and call AsyncRead as a block.
|
||||
rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = mPump->AsyncRead(this, nullptr);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = CheckPendingEvents();
|
||||
}
|
||||
|
||||
return OnOpenLocalFileComplete(rv, aIsSyncCall);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJARChannel::OnOpenLocalFileComplete(nsresult aResult, bool aIsSyncCall)
|
||||
{
|
||||
LOG(("nsJARChannel::OnOpenLocalFileComplete [this=%p %08x]\n",
|
||||
this,
|
||||
static_cast<uint32_t>(aResult)));
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mIsPending);
|
||||
|
||||
if (NS_FAILED(aResult)) {
|
||||
if (!aIsSyncCall) {
|
||||
NotifyError(aResult);
|
||||
}
|
||||
|
||||
if (mLoadGroup) {
|
||||
mLoadGroup->RemoveRequest(this, nullptr, aResult);
|
||||
}
|
||||
|
||||
mOpened = false;
|
||||
mIsPending = false;
|
||||
mListenerContext = nullptr;
|
||||
mListener = nullptr;
|
||||
mCallbacks = nullptr;
|
||||
mProgressSink = nullptr;
|
||||
|
||||
return aResult;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsJARChannel::CheckPendingEvents()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mIsPending);
|
||||
MOZ_ASSERT(mPump);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
auto suspendCount = mPendingEvent.suspendCount;
|
||||
while (suspendCount--) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv = mPump->Suspend()))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (mPendingEvent.isCanceled) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv = mPump->Cancel(mStatus)))) {
|
||||
return rv;
|
||||
}
|
||||
mPendingEvent.isCanceled = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
@ -449,30 +672,41 @@ NS_IMETHODIMP
|
||||
nsJARChannel::Cancel(nsresult status)
|
||||
{
|
||||
mStatus = status;
|
||||
if (mPump)
|
||||
if (mPump) {
|
||||
return mPump->Cancel(status);
|
||||
}
|
||||
|
||||
if (mIsPending) {
|
||||
mPendingEvent.isCanceled = true;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::Suspend()
|
||||
{
|
||||
if (mPump)
|
||||
return mPump->Suspend();
|
||||
++mPendingEvent.suspendCount;
|
||||
|
||||
if (mPump) {
|
||||
return mPump->Suspend();
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::Resume()
|
||||
{
|
||||
if (mPump)
|
||||
return mPump->Resume();
|
||||
if (NS_WARN_IF(mPendingEvent.suspendCount == 0)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
--mPendingEvent.suspendCount;
|
||||
|
||||
if (mPump) {
|
||||
return mPump->Resume();
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -726,7 +960,7 @@ nsJARChannel::Open(nsIInputStream **stream)
|
||||
mJarFile = nullptr;
|
||||
mIsUnsafe = true;
|
||||
|
||||
nsresult rv = LookupFile(false);
|
||||
nsresult rv = LookupFile();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
@ -751,6 +985,7 @@ nsJARChannel::Open(nsIInputStream **stream)
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::Open2(nsIInputStream** aStream)
|
||||
{
|
||||
LOG(("nsJARChannel::Open2 [this=%p]\n", this));
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -760,6 +995,7 @@ nsJARChannel::Open2(nsIInputStream** aStream)
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
|
||||
{
|
||||
LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
|
||||
MOZ_ASSERT(!mLoadInfo ||
|
||||
mLoadInfo->GetSecurityMode() == 0 ||
|
||||
mLoadInfo->GetInitialSecurityCheckDone() ||
|
||||
@ -783,7 +1019,7 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
|
||||
mListenerContext = ctx;
|
||||
mIsPending = true;
|
||||
|
||||
nsresult rv = LookupFile(true);
|
||||
nsresult rv = LookupFile();
|
||||
if (NS_FAILED(rv)) {
|
||||
mIsPending = false;
|
||||
mListenerContext = nullptr;
|
||||
@ -834,9 +1070,13 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
|
||||
else {
|
||||
rv = channel->AsyncOpen(downloader, nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
rv = OpenLocalFile();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
@ -852,13 +1092,14 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
|
||||
mLoadGroup->AddRequest(this, nullptr);
|
||||
|
||||
mOpened = true;
|
||||
|
||||
LOG(("nsJARChannel::AsyncOpen [this=%p] 8\n", this));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::AsyncOpen2(nsIStreamListener *aListener)
|
||||
{
|
||||
LOG(("nsJARChannel::AsyncOpen2 [this=%p]\n", this));
|
||||
nsCOMPtr<nsIStreamListener> listener = aListener;
|
||||
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
|
||||
if (NS_FAILED(rv)) {
|
||||
@ -954,7 +1195,7 @@ nsJARChannel::EnsureCached(bool *aIsCached)
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::GetZipEntry(nsIZipEntry **aZipEntry)
|
||||
{
|
||||
nsresult rv = LookupFile(false);
|
||||
nsresult rv = LookupFile();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/net/MemoryDownloader.h"
|
||||
#include "nsIJARChannel.h"
|
||||
#include "nsIJARURI.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
@ -58,8 +59,11 @@ private:
|
||||
virtual ~nsJARChannel();
|
||||
|
||||
nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
|
||||
nsresult LookupFile(bool aAllowAsync);
|
||||
nsresult LookupFile();
|
||||
nsresult OpenLocalFile();
|
||||
nsresult ContinueOpenLocalFile(nsJARInputThunk* aInput, bool aIsSyncCall);
|
||||
nsresult OnOpenLocalFileComplete(nsresult aResult, bool aIsSyncCall);
|
||||
nsresult CheckPendingEvents();
|
||||
void NotifyError(nsresult aError);
|
||||
void FireOnProgress(uint64_t aProgress);
|
||||
virtual void OnDownloadComplete(mozilla::net::MemoryDownloader* aDownloader,
|
||||
@ -93,7 +97,15 @@ private:
|
||||
int64_t mContentLength;
|
||||
uint32_t mLoadFlags;
|
||||
nsresult mStatus;
|
||||
bool mIsPending;
|
||||
bool mIsPending; // the AsyncOpen is in progress.
|
||||
|
||||
bool mEnableOMT;
|
||||
// |Cancel()|, |Suspend()|, and |Resume()| might be called during AsyncOpen.
|
||||
struct {
|
||||
bool isCanceled;
|
||||
uint32_t suspendCount;
|
||||
} mPendingEvent;
|
||||
|
||||
bool mIsUnsafe;
|
||||
|
||||
mozilla::net::MemoryDownloader::Data mTempMem;
|
||||
@ -108,6 +120,9 @@ private:
|
||||
nsCString mJarEntry;
|
||||
nsCString mInnerJarEntry;
|
||||
|
||||
// use StreamTransportService as background thread
|
||||
nsCOMPtr<nsIEventTarget> mWorker;
|
||||
|
||||
// True if this channel should not download any remote files.
|
||||
bool mBlockRemoteFiles;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user