123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsBaseChannel.h"
- #include "nsContentUtils.h"
- #include "nsURLHelper.h"
- #include "nsNetCID.h"
- #include "nsMimeTypes.h"
- #include "nsIContentSniffer.h"
- #include "nsIScriptSecurityManager.h"
- #include "nsMimeTypes.h"
- #include "nsIHttpEventSink.h"
- #include "nsIHttpChannel.h"
- #include "nsIChannelEventSink.h"
- #include "nsIStreamConverterService.h"
- #include "nsChannelClassifier.h"
- #include "nsAsyncRedirectVerifyHelper.h"
- #include "nsProxyRelease.h"
- #include "nsXULAppAPI.h"
- #include "nsContentSecurityManager.h"
- #include "mozilla/LoadInfo.h"
- #include "nsServiceManagerUtils.h"
- using namespace mozilla;
- using namespace mozilla::net;
- // This class is used to suspend a request across a function scope.
- class ScopedRequestSuspender {
- public:
- explicit ScopedRequestSuspender(nsIRequest *request)
- : mRequest(request) {
- if (mRequest && NS_FAILED(mRequest->Suspend())) {
- NS_WARNING("Couldn't suspend pump");
- mRequest = nullptr;
- }
- }
- ~ScopedRequestSuspender() {
- if (mRequest)
- mRequest->Resume();
- }
- private:
- nsIRequest *mRequest;
- };
- // Used to suspend data events from mPump within a function scope. This is
- // usually needed when a function makes callbacks that could process events.
- #define SUSPEND_PUMP_FOR_SCOPE() \
- ScopedRequestSuspender pump_suspender__(mPump)
- //-----------------------------------------------------------------------------
- // nsBaseChannel
- nsBaseChannel::nsBaseChannel()
- : mLoadFlags(LOAD_NORMAL)
- , mQueriedProgressSink(true)
- , mSynthProgressEvents(false)
- , mAllowThreadRetargeting(true)
- , mWaitingOnAsyncRedirect(false)
- , mStatus(NS_OK)
- , mContentDispositionHint(UINT32_MAX)
- , mContentLength(-1)
- , mWasOpened(false)
- {
- mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
- }
- nsBaseChannel::~nsBaseChannel()
- {
- NS_ReleaseOnMainThread(mLoadInfo.forget());
- }
- nsresult
- nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags,
- bool openNewChannel)
- {
- SUSPEND_PUMP_FOR_SCOPE();
- // Transfer properties
- newChannel->SetLoadGroup(mLoadGroup);
- newChannel->SetNotificationCallbacks(mCallbacks);
- newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE);
- // make a copy of the loadinfo, append to the redirectchain
- // and set it on the new channel
- if (mLoadInfo) {
- nsSecurityFlags secFlags = mLoadInfo->GetSecurityFlags() &
- ~nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
- nsCOMPtr<nsILoadInfo> newLoadInfo =
- static_cast<LoadInfo*>(mLoadInfo.get())->CloneWithNewSecFlags(secFlags);
- nsCOMPtr<nsIPrincipal> uriPrincipal;
- nsIScriptSecurityManager *sm = nsContentUtils::GetSecurityManager();
- sm->GetChannelURIPrincipal(this, getter_AddRefs(uriPrincipal));
- bool isInternalRedirect =
- (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
- nsIChannelEventSink::REDIRECT_STS_UPGRADE));
- newLoadInfo->AppendRedirectedPrincipal(uriPrincipal, isInternalRedirect);
- newChannel->SetLoadInfo(newLoadInfo);
- }
- else {
- // the newChannel was created with a dummy loadInfo, we should clear
- // it in case the original channel does not have a loadInfo
- newChannel->SetLoadInfo(nullptr);
- }
- // Preserve the privacy bit if it has been overridden
- if (mPrivateBrowsingOverriden) {
- nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel =
- do_QueryInterface(newChannel);
- if (newPBChannel) {
- newPBChannel->SetPrivate(mPrivateBrowsing);
- }
- }
- nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel);
- if (bag) {
- for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) {
- bag->SetProperty(iter.Key(), iter.UserData());
- }
- }
- // Notify consumer, giving chance to cancel redirect. For backwards compat,
- // we support nsIHttpEventSink if we are an HTTP channel and if this is not
- // an internal redirect.
- RefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
- new nsAsyncRedirectVerifyHelper();
- bool checkRedirectSynchronously = !openNewChannel;
- mRedirectChannel = newChannel;
- mRedirectFlags = redirectFlags;
- mOpenRedirectChannel = openNewChannel;
- nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags,
- checkRedirectSynchronously);
- if (NS_FAILED(rv))
- return rv;
- if (checkRedirectSynchronously && NS_FAILED(mStatus))
- return mStatus;
- return NS_OK;
- }
- nsresult
- nsBaseChannel::ContinueRedirect()
- {
- // Backwards compat for non-internal redirects from a HTTP channel.
- // XXX Is our http channel implementation going to derive from nsBaseChannel?
- // If not, this code can be removed.
- if (!(mRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
- nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface();
- if (httpChannel) {
- nsCOMPtr<nsIHttpEventSink> httpEventSink;
- GetCallback(httpEventSink);
- if (httpEventSink) {
- nsresult rv = httpEventSink->OnRedirect(httpChannel, mRedirectChannel);
- if (NS_FAILED(rv)) {
- return rv;
- }
- }
- }
- }
- // Make sure to do this _after_ making all the OnChannelRedirect calls
- mRedirectChannel->SetOriginalURI(OriginalURI());
- // If we fail to open the new channel, then we want to leave this channel
- // unaffected, so we defer tearing down our channel until we have succeeded
- // with the redirect.
- if (mOpenRedirectChannel) {
- nsresult rv = NS_OK;
- if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
- MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
- rv = mRedirectChannel->AsyncOpen2(mListener);
- }
- else {
- rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
- }
- NS_ENSURE_SUCCESS(rv, rv);
- }
- mRedirectChannel = nullptr;
- // close down this channel
- Cancel(NS_BINDING_REDIRECTED);
- ChannelDone();
- return NS_OK;
- }
- bool
- nsBaseChannel::HasContentTypeHint() const
- {
- NS_ASSERTION(!Pending(), "HasContentTypeHint called too late");
- return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE);
- }
- nsresult
- nsBaseChannel::PushStreamConverter(const char *fromType,
- const char *toType,
- bool invalidatesContentLength,
- nsIStreamListener **result)
- {
- NS_ASSERTION(mListener, "no listener");
- nsresult rv;
- nsCOMPtr<nsIStreamConverterService> scs =
- do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
- if (NS_FAILED(rv))
- return rv;
- nsCOMPtr<nsIStreamListener> converter;
- rv = scs->AsyncConvertData(fromType, toType, mListener, mListenerContext,
- getter_AddRefs(converter));
- if (NS_SUCCEEDED(rv)) {
- mListener = converter;
- if (invalidatesContentLength)
- mContentLength = -1;
- if (result) {
- *result = nullptr;
- converter.swap(*result);
- }
- }
- return rv;
- }
- nsresult
- nsBaseChannel::BeginPumpingData()
- {
- nsCOMPtr<nsIInputStream> stream;
- nsCOMPtr<nsIChannel> channel;
- nsresult rv = OpenContentStream(true, getter_AddRefs(stream),
- getter_AddRefs(channel));
- if (NS_FAILED(rv))
- return rv;
- NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?");
- if (channel) {
- rv = NS_DispatchToCurrentThread(new RedirectRunnable(this, channel));
- if (NS_SUCCEEDED(rv))
- mWaitingOnAsyncRedirect = true;
- return rv;
- }
- // By assigning mPump, we flag this channel as pending (see Pending). It's
- // important that the pending flag is set when we call into the stream (the
- // call to AsyncRead results in the stream's AsyncWait method being called)
- // and especially when we call into the loadgroup. Our caller takes care to
- // release mPump if we return an error.
-
- rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0,
- true);
- if (NS_SUCCEEDED(rv))
- rv = mPump->AsyncRead(this, nullptr);
- return rv;
- }
- void
- nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel)
- {
- NS_ASSERTION(!mPump, "Shouldn't have gotten here");
- nsresult rv = mStatus;
- if (NS_SUCCEEDED(mStatus)) {
- rv = Redirect(newChannel,
- nsIChannelEventSink::REDIRECT_TEMPORARY,
- true);
- if (NS_SUCCEEDED(rv)) {
- // OnRedirectVerifyCallback will be called asynchronously
- return;
- }
- }
- ContinueHandleAsyncRedirect(rv);
- }
- void
- nsBaseChannel::ContinueHandleAsyncRedirect(nsresult result)
- {
- mWaitingOnAsyncRedirect = false;
- if (NS_FAILED(result))
- Cancel(result);
- if (NS_FAILED(result) && mListener) {
- // Notify our consumer ourselves
- mListener->OnStartRequest(this, mListenerContext);
- mListener->OnStopRequest(this, mListenerContext, mStatus);
- ChannelDone();
- }
- if (mLoadGroup)
- mLoadGroup->RemoveRequest(this, nullptr, mStatus);
- // Drop notification callbacks to prevent cycles.
- mCallbacks = nullptr;
- CallbacksChanged();
- }
- void
- nsBaseChannel::ClassifyURI()
- {
- // For channels created in the child process, delegate to the parent to
- // classify URIs.
- if (!XRE_IsParentProcess()) {
- return;
- }
- if (mLoadFlags & LOAD_CLASSIFY_URI) {
- RefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
- if (classifier) {
- classifier->Start(this);
- } else {
- Cancel(NS_ERROR_OUT_OF_MEMORY);
- }
- }
- }
- //-----------------------------------------------------------------------------
- // nsBaseChannel::nsISupports
- NS_IMPL_ISUPPORTS_INHERITED(nsBaseChannel,
- nsHashPropertyBag,
- nsIRequest,
- nsIChannel,
- nsIThreadRetargetableRequest,
- nsIInterfaceRequestor,
- nsITransportEventSink,
- nsIRequestObserver,
- nsIStreamListener,
- nsIThreadRetargetableStreamListener,
- nsIAsyncVerifyRedirectCallback,
- nsIPrivateBrowsingChannel)
- //-----------------------------------------------------------------------------
- // nsBaseChannel::nsIRequest
- NS_IMETHODIMP
- nsBaseChannel::GetName(nsACString &result)
- {
- if (!mURI) {
- result.Truncate();
- return NS_OK;
- }
- return mURI->GetSpec(result);
- }
- NS_IMETHODIMP
- nsBaseChannel::IsPending(bool *result)
- {
- *result = Pending();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetStatus(nsresult *status)
- {
- if (mPump && NS_SUCCEEDED(mStatus)) {
- mPump->GetStatus(status);
- } else {
- *status = mStatus;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::Cancel(nsresult status)
- {
- // Ignore redundant cancelation
- if (NS_FAILED(mStatus))
- return NS_OK;
- mStatus = status;
- if (mPump)
- mPump->Cancel(status);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::Suspend()
- {
- NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
- return mPump->Suspend();
- }
- NS_IMETHODIMP
- nsBaseChannel::Resume()
- {
- NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
- return mPump->Resume();
- }
- NS_IMETHODIMP
- nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
- {
- *aLoadFlags = mLoadFlags;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
- {
- mLoadFlags = aLoadFlags;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
- {
- NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
- {
- if (!CanSetLoadGroup(aLoadGroup)) {
- return NS_ERROR_FAILURE;
- }
- mLoadGroup = aLoadGroup;
- CallbacksChanged();
- UpdatePrivateBrowsing();
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- // nsBaseChannel::nsIChannel
- NS_IMETHODIMP
- nsBaseChannel::GetOriginalURI(nsIURI **aURI)
- {
- *aURI = OriginalURI();
- NS_ADDREF(*aURI);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetOriginalURI(nsIURI *aURI)
- {
- NS_ENSURE_ARG_POINTER(aURI);
- mOriginalURI = aURI;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetURI(nsIURI **aURI)
- {
- NS_IF_ADDREF(*aURI = mURI);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetOwner(nsISupports **aOwner)
- {
- NS_IF_ADDREF(*aOwner = mOwner);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetOwner(nsISupports *aOwner)
- {
- mOwner = aOwner;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
- {
- mLoadInfo = aLoadInfo;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetLoadInfo(nsILoadInfo** aLoadInfo)
- {
- NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
- {
- NS_IF_ADDREF(*aCallbacks = mCallbacks);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
- {
- if (!CanSetCallbacks(aCallbacks)) {
- return NS_ERROR_FAILURE;
- }
- mCallbacks = aCallbacks;
- CallbacksChanged();
- UpdatePrivateBrowsing();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
- {
- NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetContentType(nsACString &aContentType)
- {
- aContentType = mContentType;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetContentType(const nsACString &aContentType)
- {
- // mContentCharset is unchanged if not parsed
- bool dummy;
- net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetContentCharset(nsACString &aContentCharset)
- {
- aContentCharset = mContentCharset;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetContentCharset(const nsACString &aContentCharset)
- {
- mContentCharset = aContentCharset;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetContentDisposition(uint32_t *aContentDisposition)
- {
- // preserve old behavior, fail unless explicitly set.
- if (mContentDispositionHint == UINT32_MAX) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- *aContentDisposition = mContentDispositionHint;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetContentDisposition(uint32_t aContentDisposition)
- {
- mContentDispositionHint = aContentDisposition;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
- {
- if (!mContentDispositionFilename) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- aContentDispositionFilename = *mContentDispositionFilename;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
- {
- mContentDispositionFilename = new nsString(aContentDispositionFilename);
- // For safety reasons ensure the filename doesn't contain null characters and
- // replace them with underscores. We may later pass the extension to system
- // MIME APIs that expect null terminated strings.
- mContentDispositionFilename->ReplaceChar(char16_t(0), '_');
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
- {
- return NS_ERROR_NOT_AVAILABLE;
- }
- NS_IMETHODIMP
- nsBaseChannel::GetContentLength(int64_t *aContentLength)
- {
- *aContentLength = mContentLength;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::SetContentLength(int64_t aContentLength)
- {
- mContentLength = aContentLength;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::Open(nsIInputStream **result)
- {
- NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
- NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
- nsCOMPtr<nsIChannel> chan;
- nsresult rv = OpenContentStream(false, result, getter_AddRefs(chan));
- NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?");
- if (NS_SUCCEEDED(rv) && chan) {
- rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, false);
- if (NS_FAILED(rv))
- return rv;
- rv = chan->Open(result);
- } else if (rv == NS_ERROR_NOT_IMPLEMENTED)
- return NS_ImplementChannelOpen(this, result);
- if (NS_SUCCEEDED(rv)) {
- mWasOpened = true;
- ClassifyURI();
- }
- return rv;
- }
- NS_IMETHODIMP
- nsBaseChannel::Open2(nsIInputStream** aStream)
- {
- nsCOMPtr<nsIStreamListener> listener;
- nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
- NS_ENSURE_SUCCESS(rv, rv);
- return Open(aStream);
- }
- NS_IMETHODIMP
- nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
- {
- MOZ_ASSERT(!mLoadInfo ||
- mLoadInfo->GetSecurityMode() == 0 ||
- mLoadInfo->GetInitialSecurityCheckDone() ||
- (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
- nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
- "security flags in loadInfo but asyncOpen2() not called");
- NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
- NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
- NS_ENSURE_ARG(listener);
- // Skip checking for chrome:// sub-resources.
- nsAutoCString scheme;
- mURI->GetScheme(scheme);
- if (!scheme.EqualsLiteral("file")) {
- NS_CompareLoadInfoAndLoadContext(this);
- }
- // Ensure that this is an allowed port before proceeding.
- nsresult rv = NS_CheckPortSafety(mURI);
- if (NS_FAILED(rv)) {
- mCallbacks = nullptr;
- return rv;
- }
- // Store the listener and context early so that OpenContentStream and the
- // stream's AsyncWait method (called by AsyncRead) can have access to them
- // via PushStreamConverter and the StreamListener methods. However, since
- // this typically introduces a reference cycle between this and the listener,
- // we need to be sure to break the reference if this method does not succeed.
- mListener = listener;
- mListenerContext = ctxt;
- // This method assigns mPump as a side-effect. We need to clear mPump if
- // this method fails.
- rv = BeginPumpingData();
- if (NS_FAILED(rv)) {
- mPump = nullptr;
- ChannelDone();
- mCallbacks = nullptr;
- return rv;
- }
- // At this point, we are going to return success no matter what.
- mWasOpened = true;
- SUSPEND_PUMP_FOR_SCOPE();
- if (mLoadGroup)
- mLoadGroup->AddRequest(this, nullptr);
- ClassifyURI();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::AsyncOpen2(nsIStreamListener *aListener)
- {
- nsCOMPtr<nsIStreamListener> listener = aListener;
- nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
- NS_ENSURE_SUCCESS(rv, rv);
- return AsyncOpen(listener, nullptr);
- }
- //-----------------------------------------------------------------------------
- // nsBaseChannel::nsITransportEventSink
- NS_IMETHODIMP
- nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status,
- int64_t progress, int64_t progressMax)
- {
- // In some cases, we may wish to suppress transport-layer status events.
- if (!mPump || NS_FAILED(mStatus)) {
- return NS_OK;
- }
- SUSPEND_PUMP_FOR_SCOPE();
- // Lazily fetch mProgressSink
- if (!mProgressSink) {
- if (mQueriedProgressSink) {
- return NS_OK;
- }
- GetCallback(mProgressSink);
- mQueriedProgressSink = true;
- if (!mProgressSink) {
- return NS_OK;
- }
- }
- if (!HasLoadFlag(LOAD_BACKGROUND)) {
- nsAutoString statusArg;
- if (GetStatusArg(status, statusArg)) {
- mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get());
- }
- }
- if (progress) {
- mProgressSink->OnProgress(this, mListenerContext, progress, progressMax);
- }
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- // nsBaseChannel::nsIInterfaceRequestor
- NS_IMETHODIMP
- nsBaseChannel::GetInterface(const nsIID &iid, void **result)
- {
- NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, iid, result);
- return *result ? NS_OK : NS_ERROR_NO_INTERFACE;
- }
- //-----------------------------------------------------------------------------
- // nsBaseChannel::nsIRequestObserver
- static void
- CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount)
- {
- nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
- nsAutoCString newType;
- NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType);
- if (!newType.IsEmpty()) {
- chan->SetContentType(newType);
- }
- }
- static void
- CallUnknownTypeSniffer(void *aClosure, const uint8_t *aData, uint32_t aCount)
- {
- nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
- nsCOMPtr<nsIContentSniffer> sniffer =
- do_CreateInstance(NS_GENERIC_CONTENT_SNIFFER);
- if (!sniffer)
- return;
- nsAutoCString detected;
- nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected);
- if (NS_SUCCEEDED(rv))
- chan->SetContentType(detected);
- }
- NS_IMETHODIMP
- nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
- {
- MOZ_ASSERT(request == mPump);
- // If our content type is unknown, use the content type
- // sniffer. If the sniffer is not available for some reason, then we just keep
- // going as-is.
- if (NS_SUCCEEDED(mStatus) &&
- mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
- mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this));
- }
- // Now, the general type sniffers. Skip this if we have none.
- if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS)
- mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
- SUSPEND_PUMP_FOR_SCOPE();
- if (mListener) // null in case of redirect
- return mListener->OnStartRequest(this, mListenerContext);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
- nsresult status)
- {
- // If both mStatus and status are failure codes, we keep mStatus as-is since
- // that is consistent with our GetStatus and Cancel methods.
- if (NS_SUCCEEDED(mStatus))
- mStatus = status;
- // Cause Pending to return false.
- mPump = nullptr;
- if (mListener) // null in case of redirect
- mListener->OnStopRequest(this, mListenerContext, mStatus);
- ChannelDone();
- // No need to suspend pump in this scope since we will not be receiving
- // any more events from it.
- if (mLoadGroup)
- mLoadGroup->RemoveRequest(this, nullptr, mStatus);
- // Drop notification callbacks to prevent cycles.
- mCallbacks = nullptr;
- CallbacksChanged();
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- // nsBaseChannel::nsIStreamListener
- NS_IMETHODIMP
- nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
- nsIInputStream *stream, uint64_t offset,
- uint32_t count)
- {
- SUSPEND_PUMP_FOR_SCOPE();
- nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream,
- offset, count);
- if (mSynthProgressEvents && NS_SUCCEEDED(rv)) {
- int64_t prog = offset + count;
- if (NS_IsMainThread()) {
- OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength);
- } else {
- class OnTransportStatusAsyncEvent : public mozilla::Runnable
- {
- RefPtr<nsBaseChannel> mChannel;
- int64_t mProgress;
- int64_t mContentLength;
- public:
- OnTransportStatusAsyncEvent(nsBaseChannel* aChannel,
- int64_t aProgress,
- int64_t aContentLength)
- : mChannel(aChannel),
- mProgress(aProgress),
- mContentLength(aContentLength)
- { }
- NS_IMETHOD Run() override
- {
- return mChannel->OnTransportStatus(nullptr, NS_NET_STATUS_READING,
- mProgress, mContentLength);
- }
- };
- nsCOMPtr<nsIRunnable> runnable =
- new OnTransportStatusAsyncEvent(this, prog, mContentLength);
- NS_DispatchToMainThread(runnable);
- }
- }
- return rv;
- }
- NS_IMETHODIMP
- nsBaseChannel::OnRedirectVerifyCallback(nsresult result)
- {
- if (NS_SUCCEEDED(result))
- result = ContinueRedirect();
- if (NS_FAILED(result) && !mWaitingOnAsyncRedirect) {
- if (NS_SUCCEEDED(mStatus))
- mStatus = result;
- return NS_OK;
- }
- if (mWaitingOnAsyncRedirect)
- ContinueHandleAsyncRedirect(result);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsBaseChannel::RetargetDeliveryTo(nsIEventTarget* aEventTarget)
- {
- MOZ_ASSERT(NS_IsMainThread());
- NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
- if (!mAllowThreadRetargeting) {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- return mPump->RetargetDeliveryTo(aEventTarget);
- }
- NS_IMETHODIMP
- nsBaseChannel::CheckListenerChain()
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!mAllowThreadRetargeting) {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
- do_QueryInterface(mListener);
- if (!listener) {
- return NS_ERROR_NO_INTERFACE;
- }
- return listener->CheckListenerChain();
- }
|