michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsPACMan.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIAuthPrompt.h" michael@0: #include "nsIPromptFactory.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIAsyncVerifyRedirectCallback.h" michael@0: #include "nsISystemProxySettings.h" michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: #include "ipc/Nuwa.h" michael@0: #endif michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: using namespace mozilla; michael@0: using namespace mozilla::net; michael@0: michael@0: #if defined(PR_LOGGING) michael@0: #endif michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(GetProxyLog(), PR_LOG_DEBUG, args) michael@0: michael@0: // The PAC thread does evaluations of both PAC files and michael@0: // nsISystemProxySettings because they can both block the calling thread and we michael@0: // don't want that on the main thread michael@0: michael@0: // Check to see if the underlying request was not an error page in the case of michael@0: // a HTTP request. For other types of channels, just return true. michael@0: static bool michael@0: HttpRequestSucceeded(nsIStreamLoader *loader) michael@0: { michael@0: nsCOMPtr request; michael@0: loader->GetRequest(getter_AddRefs(request)); michael@0: michael@0: bool result = true; // default to assuming success michael@0: michael@0: nsCOMPtr httpChannel = do_QueryInterface(request); michael@0: if (httpChannel) michael@0: httpChannel->GetRequestSucceeded(&result); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: // The ExecuteCallback runnable is triggered by michael@0: // nsPACManCallback::OnQueryComplete on the Main thread when its completion is michael@0: // discovered on the pac thread michael@0: michael@0: class ExecuteCallback MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: ExecuteCallback(nsPACManCallback *aCallback, michael@0: nsresult status) michael@0: : mCallback(aCallback) michael@0: , mStatus(status) michael@0: { michael@0: } michael@0: michael@0: void SetPACString(const nsCString &pacString) michael@0: { michael@0: mPACString = pacString; michael@0: } michael@0: michael@0: void SetPACURL(const nsCString &pacURL) michael@0: { michael@0: mPACURL = pacURL; michael@0: } michael@0: michael@0: NS_IMETHODIMP Run() michael@0: { michael@0: mCallback->OnQueryComplete(mStatus, mPACString, mPACURL); michael@0: mCallback = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mCallback; michael@0: nsresult mStatus; michael@0: nsCString mPACString; michael@0: nsCString mPACURL; michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: // The PAC thread must be deleted from the main thread, this class michael@0: // acts as a proxy to do that, as the PACMan is reference counted michael@0: // and might be destroyed on either thread michael@0: michael@0: class ShutdownThread MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: ShutdownThread(nsIThread *thread) michael@0: : mThread(thread) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP Run() michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: mThread->Shutdown(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mThread; michael@0: }; michael@0: michael@0: // Dispatch this to wait until the PAC thread shuts down. michael@0: michael@0: class WaitForThreadShutdown MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: explicit WaitForThreadShutdown(nsPACMan *aPACMan) michael@0: : mPACMan(aPACMan) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP Run() michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: if (mPACMan->mPACThread) { michael@0: mPACMan->mPACThread->Shutdown(); michael@0: mPACMan->mPACThread = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mPACMan; michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: // PACLoadComplete allows the PAC thread to tell the main thread that michael@0: // the javascript PAC file has been installed (perhaps unsuccessfully) michael@0: // and that there is no reason to queue executions anymore michael@0: michael@0: class PACLoadComplete MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: PACLoadComplete(nsPACMan *aPACMan) michael@0: : mPACMan(aPACMan) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP Run() michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: mPACMan->mLoader = nullptr; michael@0: mPACMan->PostProcessPendingQ(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mPACMan; michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: // ExecutePACThreadAction is used to proxy actions from the main michael@0: // thread onto the PAC thread. There are 3 options: process the queue, michael@0: // cancel the queue, and setup the javascript context with a new PAC file michael@0: michael@0: class ExecutePACThreadAction MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: // by default we just process the queue michael@0: ExecutePACThreadAction(nsPACMan *aPACMan) michael@0: : mPACMan(aPACMan) michael@0: , mCancel(false) michael@0: , mSetupPAC(false) michael@0: { } michael@0: michael@0: void CancelQueue (nsresult status) michael@0: { michael@0: mCancel = true; michael@0: mCancelStatus = status; michael@0: } michael@0: michael@0: void SetupPAC (const char *text, uint32_t datalen, nsCString &pacURI) michael@0: { michael@0: mSetupPAC = true; michael@0: mSetupPACData.Assign(text, datalen); michael@0: mSetupPACURI = pacURI; michael@0: } michael@0: michael@0: NS_IMETHODIMP Run() michael@0: { michael@0: NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); michael@0: if (mCancel) { michael@0: mPACMan->CancelPendingQ(mCancelStatus); michael@0: mCancel = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mSetupPAC) { michael@0: mSetupPAC = false; michael@0: michael@0: mPACMan->mPAC.Init(mSetupPACURI, michael@0: mSetupPACData); michael@0: michael@0: nsRefPtr runnable = new PACLoadComplete(mPACMan); michael@0: NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: mPACMan->ProcessPendingQ(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mPACMan; michael@0: michael@0: bool mCancel; michael@0: nsresult mCancelStatus; michael@0: michael@0: bool mSetupPAC; michael@0: nsCString mSetupPACData; michael@0: nsCString mSetupPACURI; michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: PendingPACQuery::PendingPACQuery(nsPACMan *pacMan, nsIURI *uri, michael@0: nsPACManCallback *callback, michael@0: bool mainThreadResponse) michael@0: : mPACMan(pacMan) michael@0: , mCallback(callback) michael@0: , mOnMainThreadOnly(mainThreadResponse) michael@0: { michael@0: uri->GetAsciiSpec(mSpec); michael@0: uri->GetAsciiHost(mHost); michael@0: uri->GetScheme(mScheme); michael@0: uri->GetPort(&mPort); michael@0: } michael@0: michael@0: void michael@0: PendingPACQuery::Complete(nsresult status, const nsCString &pacString) michael@0: { michael@0: if (!mCallback) michael@0: return; michael@0: nsRefPtr runnable = new ExecuteCallback(mCallback, status); michael@0: runnable->SetPACString(pacString); michael@0: if (mOnMainThreadOnly) michael@0: NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); michael@0: else michael@0: runnable->Run(); michael@0: } michael@0: michael@0: void michael@0: PendingPACQuery::UseAlternatePACFile(const nsCString &pacURL) michael@0: { michael@0: if (!mCallback) michael@0: return; michael@0: michael@0: nsRefPtr runnable = new ExecuteCallback(mCallback, NS_OK); michael@0: runnable->SetPACURL(pacURL); michael@0: if (mOnMainThreadOnly) michael@0: NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); michael@0: else michael@0: runnable->Run(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PendingPACQuery::Run() michael@0: { michael@0: NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); michael@0: mPACMan->PostQuery(this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsPACMan::nsPACMan() michael@0: : mLoadPending(false) michael@0: , mShutdown(false) michael@0: , mLoadFailureCount(0) michael@0: , mInProgress(false) michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "pacman must be created on main thread"); michael@0: } michael@0: michael@0: nsPACMan::~nsPACMan() michael@0: { michael@0: if (mPACThread) { michael@0: if (NS_IsMainThread()) { michael@0: mPACThread->Shutdown(); michael@0: } michael@0: else { michael@0: nsRefPtr runnable = new ShutdownThread(mPACThread); michael@0: NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); michael@0: } michael@0: } michael@0: michael@0: NS_ASSERTION(mLoader == nullptr, "pac man not shutdown properly"); michael@0: NS_ASSERTION(mPendingQ.isEmpty(), "pac man not shutdown properly"); michael@0: } michael@0: michael@0: void michael@0: nsPACMan::Shutdown() michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "pacman must be shutdown on main thread"); michael@0: if (mShutdown) { michael@0: return; michael@0: } michael@0: mShutdown = true; michael@0: CancelExistingLoad(); michael@0: PostCancelPendingQ(NS_ERROR_ABORT); michael@0: michael@0: nsRefPtr runnable = new WaitForThreadShutdown(this); michael@0: NS_DispatchToMainThread(runnable); michael@0: } michael@0: michael@0: nsresult michael@0: nsPACMan::AsyncGetProxyForChannel(nsIChannel *channel, nsPACManCallback *callback, michael@0: bool mainThreadResponse) michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: if (mShutdown) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: // Maybe Reload PAC michael@0: if (!mPACURISpec.IsEmpty() && !mScheduledReload.IsNull() && michael@0: TimeStamp::Now() > mScheduledReload) michael@0: LoadPACFromURI(EmptyCString()); michael@0: michael@0: nsCOMPtr uri; michael@0: nsresult rv = channel->GetURI(getter_AddRefs(uri)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsRefPtr query = michael@0: new PendingPACQuery(this, uri, callback, mainThreadResponse); michael@0: michael@0: if (IsPACURI(uri)) { michael@0: // deal with this directly instead of queueing it michael@0: query->Complete(NS_OK, EmptyCString()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return mPACThread->Dispatch(query, nsIEventTarget::DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: nsPACMan::PostQuery(PendingPACQuery *query) michael@0: { michael@0: NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); michael@0: michael@0: if (mShutdown) { michael@0: query->Complete(NS_ERROR_NOT_AVAILABLE, EmptyCString()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // add a reference to the query while it is in the pending list michael@0: nsRefPtr addref(query); michael@0: mPendingQ.insertBack(addref.forget().take()); michael@0: ProcessPendingQ(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsPACMan::LoadPACFromURI(const nsCString &spec) michael@0: { michael@0: NS_ENSURE_STATE(!mShutdown); michael@0: NS_ENSURE_ARG(!spec.IsEmpty() || !mPACURISpec.IsEmpty()); michael@0: michael@0: nsCOMPtr loader = michael@0: do_CreateInstance(NS_STREAMLOADER_CONTRACTID); michael@0: NS_ENSURE_STATE(loader); michael@0: michael@0: // Since we might get called from nsProtocolProxyService::Init, we need to michael@0: // post an event back to the main thread before we try to use the IO service. michael@0: // michael@0: // But, we need to flag ourselves as loading, so that we queue up any PAC michael@0: // queries the enter between now and when we actually load the PAC file. michael@0: michael@0: if (!mLoadPending) { michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethod(this, &nsPACMan::StartLoading); michael@0: nsresult rv; michael@0: if (NS_FAILED(rv = NS_DispatchToCurrentThread(event))) michael@0: return rv; michael@0: mLoadPending = true; michael@0: } michael@0: michael@0: CancelExistingLoad(); michael@0: michael@0: mLoader = loader; michael@0: if (!spec.IsEmpty()) { michael@0: mPACURISpec = spec; michael@0: mPACURIRedirectSpec.Truncate(); michael@0: mNormalPACURISpec.Truncate(); // set at load time michael@0: mLoadFailureCount = 0; // reset michael@0: } michael@0: michael@0: // reset to Null michael@0: mScheduledReload = TimeStamp(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsPACMan::StartLoading() michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: mLoadPending = false; michael@0: michael@0: // CancelExistingLoad was called... michael@0: if (!mLoader) { michael@0: PostCancelPendingQ(NS_ERROR_ABORT); michael@0: return; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(mLoader->Init(this))) { michael@0: // Always hit the origin server when loading PAC. michael@0: nsCOMPtr ios = do_GetIOService(); michael@0: if (ios) { michael@0: nsCOMPtr channel; michael@0: nsCOMPtr pacURI; michael@0: NS_NewURI(getter_AddRefs(pacURI), mPACURISpec); michael@0: michael@0: // NOTE: This results in GetProxyForURI being called michael@0: if (pacURI) { michael@0: pacURI->GetSpec(mNormalPACURISpec); michael@0: ios->NewChannelFromURI(pacURI, getter_AddRefs(channel)); michael@0: } michael@0: else { michael@0: LOG(("nsPACMan::StartLoading Failed pacspec uri conversion %s\n", michael@0: mPACURISpec.get())); michael@0: } michael@0: michael@0: if (channel) { michael@0: channel->SetLoadFlags(nsIRequest::LOAD_BYPASS_CACHE); michael@0: channel->SetNotificationCallbacks(this); michael@0: if (NS_SUCCEEDED(channel->AsyncOpen(mLoader, nullptr))) michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: CancelExistingLoad(); michael@0: PostCancelPendingQ(NS_ERROR_UNEXPECTED); michael@0: } michael@0: michael@0: michael@0: void michael@0: nsPACMan::OnLoadFailure() michael@0: { michael@0: int32_t minInterval = 5; // 5 seconds michael@0: int32_t maxInterval = 300; // 5 minutes michael@0: michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (prefs) { michael@0: prefs->GetIntPref("network.proxy.autoconfig_retry_interval_min", michael@0: &minInterval); michael@0: prefs->GetIntPref("network.proxy.autoconfig_retry_interval_max", michael@0: &maxInterval); michael@0: } michael@0: michael@0: int32_t interval = minInterval << mLoadFailureCount++; // seconds michael@0: if (!interval || interval > maxInterval) michael@0: interval = maxInterval; michael@0: michael@0: mScheduledReload = TimeStamp::Now() + TimeDuration::FromSeconds(interval); michael@0: michael@0: // while we wait for the retry queued members should try direct michael@0: // even if that means fast failure. michael@0: PostCancelPendingQ(NS_ERROR_NOT_AVAILABLE); michael@0: } michael@0: michael@0: void michael@0: nsPACMan::CancelExistingLoad() michael@0: { michael@0: if (mLoader) { michael@0: nsCOMPtr request; michael@0: mLoader->GetRequest(getter_AddRefs(request)); michael@0: if (request) michael@0: request->Cancel(NS_ERROR_ABORT); michael@0: mLoader = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPACMan::PostProcessPendingQ() michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: nsRefPtr pending = michael@0: new ExecutePACThreadAction(this); michael@0: if (mPACThread) michael@0: mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); michael@0: } michael@0: michael@0: void michael@0: nsPACMan::PostCancelPendingQ(nsresult status) michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: nsRefPtr pending = michael@0: new ExecutePACThreadAction(this); michael@0: pending->CancelQueue(status); michael@0: if (mPACThread) michael@0: mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); michael@0: } michael@0: michael@0: void michael@0: nsPACMan::CancelPendingQ(nsresult status) michael@0: { michael@0: NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); michael@0: nsRefPtr query; michael@0: michael@0: while (!mPendingQ.isEmpty()) { michael@0: query = dont_AddRef(mPendingQ.popLast()); michael@0: query->Complete(status, EmptyCString()); michael@0: } michael@0: michael@0: if (mShutdown) michael@0: mPAC.Shutdown(); michael@0: } michael@0: michael@0: void michael@0: nsPACMan::ProcessPendingQ() michael@0: { michael@0: NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); michael@0: while (ProcessPending()); michael@0: michael@0: if (mShutdown) { michael@0: mPAC.Shutdown(); michael@0: } else { michael@0: // do GC while the thread has nothing pending michael@0: mPAC.GC(); michael@0: } michael@0: } michael@0: michael@0: // returns true if progress was made by shortening the queue michael@0: bool michael@0: nsPACMan::ProcessPending() michael@0: { michael@0: if (mPendingQ.isEmpty()) michael@0: return false; michael@0: michael@0: // queue during normal load, but if we are retrying a failed load then michael@0: // fast fail the queries michael@0: if (mInProgress || (IsLoading() && !mLoadFailureCount)) michael@0: return false; michael@0: michael@0: nsRefPtr query(dont_AddRef(mPendingQ.popFirst())); michael@0: michael@0: if (mShutdown || IsLoading()) { michael@0: query->Complete(NS_ERROR_NOT_AVAILABLE, EmptyCString()); michael@0: return true; michael@0: } michael@0: michael@0: nsAutoCString pacString; michael@0: bool completed = false; michael@0: mInProgress = true; michael@0: nsAutoCString PACURI; michael@0: michael@0: // first we need to consider the system proxy changing the pac url michael@0: if (mSystemProxySettings && michael@0: NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) && michael@0: !PACURI.IsEmpty() && michael@0: !PACURI.Equals(mPACURISpec)) { michael@0: query->UseAlternatePACFile(PACURI); michael@0: completed = true; michael@0: } michael@0: michael@0: // now try the system proxy settings for this particular url if michael@0: // PAC was not specified michael@0: if (!completed && mSystemProxySettings && PACURI.IsEmpty() && michael@0: NS_SUCCEEDED(mSystemProxySettings-> michael@0: GetProxyForURI(query->mSpec, query->mScheme, michael@0: query->mHost, query->mPort, michael@0: pacString))) { michael@0: query->Complete(NS_OK, pacString); michael@0: completed = true; michael@0: } michael@0: michael@0: // the systemproxysettings didn't complete the resolution. try via PAC michael@0: if (!completed) { michael@0: nsresult status = mPAC.GetProxyForURI(query->mSpec, query->mHost, pacString); michael@0: query->Complete(status, pacString); michael@0: } michael@0: michael@0: mInProgress = false; michael@0: return true; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsPACMan, nsIStreamLoaderObserver, michael@0: nsIInterfaceRequestor, nsIChannelEventSink) michael@0: michael@0: NS_IMETHODIMP michael@0: nsPACMan::OnStreamComplete(nsIStreamLoader *loader, michael@0: nsISupports *context, michael@0: nsresult status, michael@0: uint32_t dataLen, michael@0: const uint8_t *data) michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: if (mLoader != loader) { michael@0: // If this happens, then it means that LoadPACFromURI was called more michael@0: // than once before the initial call completed. In this case, status michael@0: // should be NS_ERROR_ABORT, and if so, then we know that we can and michael@0: // should delay any processing. michael@0: if (status == NS_ERROR_ABORT) michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(status) && HttpRequestSucceeded(loader)) { michael@0: // Get the URI spec used to load this PAC script. michael@0: nsAutoCString pacURI; michael@0: { michael@0: nsCOMPtr request; michael@0: loader->GetRequest(getter_AddRefs(request)); michael@0: nsCOMPtr channel = do_QueryInterface(request); michael@0: if (channel) { michael@0: nsCOMPtr uri; michael@0: channel->GetURI(getter_AddRefs(uri)); michael@0: if (uri) michael@0: uri->GetAsciiSpec(pacURI); michael@0: } michael@0: } michael@0: michael@0: // We assume that the PAC text is ASCII (or ISO-Latin-1). We've had this michael@0: // assumption forever, and some real-world PAC scripts actually have some michael@0: // non-ASCII text in comment blocks (see bug 296163). michael@0: const char *text = (const char *) data; michael@0: michael@0: // we have succeeded in loading the pac file using a bunch of interfaces that michael@0: // are main thread only, unfortunately we have to initialize the instance of michael@0: // the PAC evaluator (NS_PROXYAUTOCONFIG_CONTRACTID) on the pac thread, because michael@0: // that is where it will be used. michael@0: michael@0: nsRefPtr pending = michael@0: new ExecutePACThreadAction(this); michael@0: pending->SetupPAC(text, dataLen, pacURI); michael@0: if (mPACThread) michael@0: mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); michael@0: michael@0: // Even if the PAC file could not be parsed, we did succeed in loading the michael@0: // data for it. michael@0: mLoadFailureCount = 0; michael@0: } else { michael@0: // We were unable to load the PAC file (presumably because of a network michael@0: // failure). Try again a little later. michael@0: OnLoadFailure(); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(status)) michael@0: PostProcessPendingQ(); michael@0: else michael@0: PostCancelPendingQ(status); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPACMan::GetInterface(const nsIID &iid, void **result) michael@0: { michael@0: // In case loading the PAC file requires authentication. michael@0: if (iid.Equals(NS_GET_IID(nsIAuthPrompt))) { michael@0: nsCOMPtr promptFac = do_GetService("@mozilla.org/prompter;1"); michael@0: NS_ENSURE_TRUE(promptFac, NS_ERROR_FAILURE); michael@0: return promptFac->GetPrompt(nullptr, iid, reinterpret_cast(result)); michael@0: } michael@0: michael@0: // In case loading the PAC file results in a redirect. michael@0: if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) { michael@0: NS_ADDREF_THIS(); michael@0: *result = static_cast(this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_NO_INTERFACE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPACMan::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, michael@0: uint32_t flags, michael@0: nsIAsyncVerifyRedirectCallback *callback) michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr pacURI; michael@0: if (NS_FAILED((rv = newChannel->GetURI(getter_AddRefs(pacURI))))) michael@0: return rv; michael@0: michael@0: rv = pacURI->GetSpec(mPACURIRedirectSpec); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: LOG(("nsPACMan redirect from original %s to redirected %s\n", michael@0: mPACURISpec.get(), mPACURIRedirectSpec.get())); michael@0: michael@0: // do not update mPACURISpec - that needs to stay as the michael@0: // configured URI so that we can determine when the config changes. michael@0: // However do track the most recent URI in the redirect change michael@0: // as mPACURIRedirectSpec so that URI can be allowed to bypass michael@0: // the proxy and actually fetch the pac file. michael@0: michael@0: callback->OnRedirectVerifyCallback(NS_OK); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsPACMan::NamePACThread() michael@0: { michael@0: NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); michael@0: PR_SetCurrentThreadName("Proxy Resolution"); michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: if (IsNuwaProcess()) { michael@0: NS_ASSERTION(NuwaMarkCurrentThread != nullptr, michael@0: "NuwaMarkCurrentThread is undefined!"); michael@0: NuwaMarkCurrentThread(nullptr, nullptr); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: nsPACMan::Init(nsISystemProxySettings *systemProxySettings) michael@0: { michael@0: mSystemProxySettings = systemProxySettings; michael@0: michael@0: nsresult rv = NS_NewThread(getter_AddRefs(mPACThread), nullptr); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr event = NS_NewRunnableMethod(this, &nsPACMan::NamePACThread); michael@0: // don't check return value as it is not a big deal for this to fail. michael@0: mPACThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: PRLogModuleInfo* michael@0: GetProxyLog() michael@0: { michael@0: static PRLogModuleInfo *sLog; michael@0: if (!sLog) michael@0: sLog = PR_NewLogModule("proxy"); michael@0: return sLog; michael@0: } michael@0: michael@0: } michael@0: }