Files
palemoon27/netwerk/protocol/http/HttpChannelParent.cpp
T
roytam1 86a3aa0b54 import changes from `dev' branch of rmottola/Arctic-Fox:
- missing part of  Bug 1165772: P1. (7311039be4)
- missing gstreamer stuff (54a80d69b2)
- Bug 1214208: Do not use MP3Decoder on B2G. r=alfredo (0a19e7946e)
- Bug 1194014 - Remove redundant includes. r=jya (ccc3753113)
- Bug 1039639 - Add support for Flac on Firefox OS. r=cajbir (7d76197e07)
- Bug 875573 - Add video/x-m4v mime type. r=kentuckyfriedtakahe (6ec8af93e6)
- cleanup (6fb3d5dd26)
- Bug 1180621 - [FxOS] Enable VP9 codec for the Android version after KK. r=sotaro (58f7c2b657)
- Bug 1187247: [MSE] P2. Enable WebM in MediaSource. r=jya (2df0ee1f7a)
- Bug 1187247: [MSE] P1. Continue parsing MediaSegment if buffer starts with SimpleBlock/Block. r=kinetik (574475ed6f)
- Bug 1217170: P1. Rename functions to explicitly reflect what they are doing. r=kentuckyfriedtakahe (70c81a8179)
- Bug 1070216 - Split DOMMediaStream::InitStreamCommon into three. r=roc (1bda71cc88)
- Bug 1215582 - Rename Blacklist to Block list in GStreamerFormatReader. r=gerald (4f08077f5e)
- Bug 1170958 - Destroy track-locked MediaInputPorts when the track ends. r=roc (ff3922a2d6)
- Bug 1070216 - constify DOMMediaStream::Get[Audio/Video]Tracks(). r=roc (ba09f6f191)
- Bug 1070216 - Guard against adding a track owned by one MSG to a stream owned by another. r=padenot (a80deb8b30)
- Bug 1070216 - Implement MediaStream constructors. r=smaug,jib,padenot (3403ef2599)
- Bug 1070216 - Guard against a null MediaInputPort in DOMMediaStream::FindPlaybackDOMTrack(). r=roc (453a9ffbc1)
- Bug 1212783 - Expose TrackPort in DOMMediaStream.h r=roc (fb61c79ae7)
- Bug 1219711 - Ensure MediaStreamTrack.enabled propagates across peer connections. r=jesup (d9d1e54dae)
- Bug 1129051 - Fix double free in Camera Control Listener. Fix webrtc memory leak. r=aosmond (3e9b3bccfd)
- Bug 1152260 - Generate focused event for drivers that do not notify us when using continuous auto focus. r=mikeh (6c7bd42fdc)
- Bug 1175656 - Implement generation of recording posters in Gecko. r=dhylands,bz (51b2c66dc7)
- Bug 1187364 - Part 1. Add ability for camera to pause/resume recording. r=dhylands,bz (c54c735e37)
- Bug 1187364 - Part 2. Ensure that recording is resumed with a key frame. r=mchiang (c1c6048982)
- Bug 1187364 - Part 3. Fix missing end comment in WebIDL. r=me,bz (7faf106cc1)
- Bug 1212783 - Add a MediaStreamTrack to DOMCameraControl. r=aosmond (91e11efd3a)
- Bug 1124338 - Fix possible camera cached parameters invalidation from underlying driver modification. r=aosmond (dea67dc155)
- Bug 1196330 - Do not restart preview if configuration is unchanged. r=dhylands (097644f5d9)
- Bug 1215372 - Filter empty camera face detected events at gonk layer. r=dhylands (733efe50eb)
- Bug 1179726 - Prefer lower resolutions than 4kuhd as the default video recording profile. r=dhylands (27c71273dc)
- Bug 1222122 - Add picture size to verified parameters when reconfiguring the camera. r=dhylands (8c1fac6a4a)
- Bug 1141267 - register CameraThread with profiler, r=aosmond (299592a024)
- Bug 1008483 - removes the RW lock in CameraControlImpl and replaces it with a standard mutex. r=aosmond (45936cb90d)
- Bug 1008483 - Part 2. Readd missing nsPrintfCString.h include which has broken some local builds. r=me (9dd84b0f19)
- Bug 1191731 - Update poster API to allow application control over when poster is saved. r=bz, r=dhylands (73f9e7e0f4)
- Bug 1155648 - Fix documentation for DOMMediaStream::OnTracksAvailable. r=jesup (702828c304)
- Bug 1217170: [MSE] P2. Enable WebM/MSE on systems with no MP4/H264 support. r=kentuckyfriedtakahe (0b814b0708)
- Bug 1213177: Enable WebM on machines where H264 HW decoding is disabled. r=kentuckyfriedtakahe (e64da2ea24)
- add back some sps telemetry (52c2c64f5b)
- missing bit of Bug 1195073: [MSE/webm] P1 (9c45e82c3d)
- Bug 1150305 - sourcebuffer.buffered returns the same object if not changed. r=roc, r=bz, r=jya (6005d56c0c)
- Bug 1215447 - move flag setting from SeekStarted() to Seek(). r=roc. (a646b744c1)
- Bug 1119936 - Audio from FM Radio or Music app ceases to play when switching between front/back camera. r=roc (1a60aa7d69)
- Bug 1186806 - Part 1: Replace nsBaseHashtable::EnumerateRead() with iterators in HTMLFormControlsCollection. r=khuey (ccb8cb180a)
- Bug 1186806 - Part 2: Use NS_IMPL_CYCLE_COLLECTION_TRAVERSE instead of manual traversal in HTMLFormElement. r=khuey (57e6eabf1b)
- Bug 1186806 - Part 3: Replace nsBaseHashtable::EnumerateRead() with iterators in HTMLMediaElement. r=khuey (243ef6e83b)
- Bug 1186806 - Part 4: Replace nsBaseHashtable::EnumerateRead() with iterators in HTMLPropertiesCollection. r=khuey (499bdef85f)
- Bug 1163958 - Reduce the allocation in MediaStreamGraph - patch 3 CLOSED TREE (a557661df1)
- Bug 1219330 - Prevent the creation of TextureClient after shutdown. r=mattwoodrow (a6c047d54f)
- Bug 1205559: Make TextureChild/TextureClient thread-safe. r=nical (307c089631)
- missing bit of 1219330 (0e351ea419)
- nsRefPtr -> RefPtr (07ba248e69)
- Bug 1215023. Part 1 - make MediaDecoder::mOwner a const member. We will check mShuttingDown before calling functions of mOwner. r=kinetik. (da7f201815)
- Bug 1215023. Part 2 - remove null check of mOwner. We check mShuttingDown to know whether it is valid to call functions of mOwner. r=kinetik. (8d28a04bbe)
- Bug 1220558. Part 2 - remove unused members. r=jya. (d3a9ed8c68)
- Bug 1223599 - Remove the throttling argument from AbstractMediaDecoder::NotifyDataArrived(). r=jya. (320323ff1d)
- Bug 1194606 - Make MediaDecoderStateMachine capable of requesting different kind (decoded/raw) of media data. r=jya (1e2b6a5c44)
- Bug 1197075: P3. Decode frames ahead of MDSM requesting them. r=edwin This makes the media.*-decode-ahead pref performs more according to its name. We decode audio and video in advance so a MediaDataPromise can be resolved almost instantly. Default is 2. (b3f56447c4)
- Bug 1189964 - Fix bustage. r=bustage CLOSED TREE (afaa49b4b5)
- Bug 1212149 - e10s support for opening notification settings. r=wchen (f0e7778fb6)
- Bug 1215644 - Use child process volume service cache for available and storage status requests. r=dhylands (dfd49f2ef3)
- bug 1215552 - nsHttpConnectionMgr::PostEvent shouldnt manually ref count r=hurley (5e2f1886e6)
- Bug 1219392 - Capitalize mozilla::unused to avoid conflicts. r=froydnj (0c8bb7f15a)
- bug 1217834 - buzzfeed packet loss r=dragana (e9a60b605f)
- Bug 1168033 - Add a comment to nsHttpConnectionMgr.cpp explaining the assignment of attemptedOptimisticPipeline. r=mcmanus (2451996350)
- bug 1189645 - remove spdy telem r=hurley (cda90abbdb)
- Bug 1148268 - fixed misspelling attribute mActorDestoryed. r=dhyland. (3615d68765)
- Bug 1216031 - Make MediaDecoder::mVideoFrameContainer const. r=kinetik. (a3feb9d6bc)
- missing bits of  Bug 1165515 - Part 13-2 (009e32281f)
- Bug 1131473 - crash in -[NativeMenuItemTarget menuItemHit:]. r=spohl (ea2da6441c)
- Bug 1216416 - Fix -Wimplicit-fallthrough warnings in widget/cocoa. r=spohl (faaa390b20)
- Bug 1181977 - Firefox app menu contains only "Quit" in certain edgecases. r=spohl (0b9d912961)
2022-12-06 13:48:22 +08:00

1623 lines
50 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* 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/. */
// HttpLog.h should generally be included first
#include "HttpLog.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
#include "mozilla/net/HttpChannelParent.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/unused.h"
#include "HttpChannelParentListener.h"
#include "nsHttpHandler.h"
#include "nsNetUtil.h"
#include "nsISupportsPriority.h"
#include "nsIAuthPromptProvider.h"
#include "nsSerializationHelper.h"
#include "nsISerializable.h"
#include "nsIAssociatedContentSecurity.h"
#include "nsIApplicationCacheService.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "SerializedLoadContext.h"
#include "nsIAuthInformation.h"
#include "nsIAuthPromptCallback.h"
#include "nsIContentPolicy.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "nsIOService.h"
#include "nsICachingChannel.h"
#include "mozilla/LoadInfo.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsQueryObject.h"
#include "mozilla/BasePrincipal.h"
#include "nsCORSListenerProxy.h"
using mozilla::BasePrincipal;
using mozilla::OriginAttributes;
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus)
: mIPCClosed(false)
, mStoredStatus(NS_OK)
, mStoredProgress(0)
, mStoredProgressMax(0)
, mSentRedirect1Begin(false)
, mSentRedirect1BeginFailed(false)
, mReceivedRedirect2Verify(false)
, mPBOverride(aOverrideStatus)
, mLoadContext(aLoadContext)
, mStatus(NS_OK)
, mDivertingFromChild(false)
, mDivertedOnStartRequest(false)
, mSuspendedForDiversion(false)
, mShouldIntercept(false)
, mShouldSuspendIntercept(false)
, mNestedFrameId(0)
{
LOG(("Creating HttpChannelParent [this=%p]\n", this));
// Ensure gHttpHandler is initialized: we need the atom table up and running.
nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
MOZ_ASSERT(gHttpHandler);
mHttpHandler = gHttpHandler;
if (iframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
mTabParent = static_cast<dom::TabParent*>(iframeEmbedding.get_PBrowserParent());
} else {
mNestedFrameId = iframeEmbedding.get_TabId();
}
mObserver = new OfflineObserver(this);
mEventQ = new ChannelEventQueue(static_cast<nsIParentRedirectingChannel*>(this));
}
HttpChannelParent::~HttpChannelParent()
{
LOG(("Destroying HttpChannelParent [this=%p]\n", this));
if (mObserver) {
mObserver->RemoveObserver();
}
}
void
HttpChannelParent::ActorDestroy(ActorDestroyReason why)
{
// We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
// yet, but child process has crashed. We must not try to send any more msgs
// to child, or IPDL will kill chrome process, too.
mIPCClosed = true;
// If this is an intercepted channel, we need to make sure that any resources are
// cleaned up to avoid leaks.
if (mInterceptedChannel) {
mInterceptedChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
mInterceptedChannel = nullptr;
}
}
bool
HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
{
LOG(("HttpChannelParent::Init [this=%p]\n", this));
switch (aArgs.type()) {
case HttpChannelCreationArgs::THttpChannelOpenArgs:
{
const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
return DoAsyncOpen(a.uri(), a.original(), a.doc(), a.referrer(),
a.referrerPolicy(), a.apiRedirectTo(), a.topWindowURI(),
a.loadFlags(), a.requestHeaders(),
a.requestMethod(), a.uploadStream(),
a.uploadStreamHasHeaders(), a.priority(), a.classOfService(),
a.redirectionLimit(), a.allowPipelining(), a.allowSTS(),
a.thirdPartyFlags(), a.resumeAt(), a.startPos(),
a.entityID(), a.chooseApplicationCache(),
a.appCacheClientID(), a.allowSpdy(), a.allowAltSvc(), a.fds(),
a.loadInfo(), a.synthesizedResponseHead(),
a.synthesizedSecurityInfoSerialization(),
a.cacheKey(), a.schedulingContextID(), a.preflightArgs(),
a.initialRwin(), a.allowStaleCacheContent());
}
case HttpChannelCreationArgs::THttpChannelConnectArgs:
{
const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
return ConnectChannel(cArgs.channelId(), cArgs.shouldIntercept());
}
default:
NS_NOTREACHED("unknown open type");
return false;
}
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(HttpChannelParent,
nsIInterfaceRequestor,
nsIProgressEventSink,
nsIRequestObserver,
nsIStreamListener,
nsIPackagedAppChannelListener,
nsIParentChannel,
nsIAuthPromptProvider,
nsIParentRedirectingChannel,
nsINetworkInterceptController,
nsIDeprecationWarner)
NS_IMETHODIMP
HttpChannelParent::ShouldPrepareForIntercept(nsIURI* aURI,
bool aIsNonSubresourceRequest,
bool* aShouldIntercept)
{
*aShouldIntercept = mShouldIntercept;
return NS_OK;
}
class HeaderVisitor final : public nsIHttpHeaderVisitor
{
nsCOMPtr<nsIInterceptedChannel> mChannel;
~HeaderVisitor()
{
}
public:
explicit HeaderVisitor(nsIInterceptedChannel* aChannel) : mChannel(aChannel)
{
}
NS_DECL_ISUPPORTS
NS_IMETHOD VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
{
mChannel->SynthesizeHeader(aHeader, aValue);
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(HeaderVisitor, nsIHttpHeaderVisitor)
class FinishSynthesizedResponse : public nsRunnable
{
nsCOMPtr<nsIInterceptedChannel> mChannel;
public:
explicit FinishSynthesizedResponse(nsIInterceptedChannel* aChannel)
: mChannel(aChannel)
{
}
NS_IMETHOD Run()
{
// The URL passed as an argument here doesn't matter, since the child will
// receive a redirection notification as a result of this synthesized response.
mChannel->FinishSynthesizedResponse(EmptyCString());
return NS_OK;
}
};
class ResponseSynthesizer final : public nsIFetchEventDispatcher
{
public:
ResponseSynthesizer(nsIInterceptedChannel* aChannel,
HttpChannelParent* aParentChannel)
: mChannel(aChannel)
, mParentChannel(aParentChannel)
{
}
NS_DECL_ISUPPORTS
NS_DECL_NSIFETCHEVENTDISPATCHER
private:
~ResponseSynthesizer()
{
}
nsCOMPtr<nsIInterceptedChannel> mChannel;
RefPtr<HttpChannelParent> mParentChannel;
};
NS_IMPL_ISUPPORTS(ResponseSynthesizer, nsIFetchEventDispatcher)
NS_IMETHODIMP
ResponseSynthesizer::Dispatch()
{
mParentChannel->SynthesizeResponse(mChannel);
return NS_OK;
}
NS_IMETHODIMP
HttpChannelParent::ChannelIntercepted(nsIInterceptedChannel* aChannel,
nsIFetchEventDispatcher** aDispatcher)
{
RefPtr<ResponseSynthesizer> dispatcher =
new ResponseSynthesizer(aChannel, this);
dispatcher.forget(aDispatcher);
return NS_OK;
}
void
HttpChannelParent::SynthesizeResponse(nsIInterceptedChannel* aChannel)
{
if (mShouldSuspendIntercept) {
mInterceptedChannel = aChannel;
return;
}
aChannel->SynthesizeStatus(mSynthesizedResponseHead->Status(),
mSynthesizedResponseHead->StatusText());
nsCOMPtr<nsIHttpHeaderVisitor> visitor = new HeaderVisitor(aChannel);
mSynthesizedResponseHead->Headers().VisitHeaders(visitor);
nsCOMPtr<nsIRunnable> event = new FinishSynthesizedResponse(aChannel);
NS_DispatchToCurrentThread(event);
mSynthesizedResponseHead = nullptr;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIInterfaceRequestor
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParent::GetInterface(const nsIID& aIID, void **result)
{
if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider)) ||
aIID.Equals(NS_GET_IID(nsISecureBrowserUI))) {
if (mTabParent) {
return mTabParent->QueryInterface(aIID, result);
}
}
// Only support nsIAuthPromptProvider in Content process
if (XRE_IsParentProcess() &&
aIID.Equals(NS_GET_IID(nsIAuthPromptProvider))) {
*result = nullptr;
return NS_OK;
}
// Only support nsILoadContext if child channel's callbacks did too
if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
nsCOMPtr<nsILoadContext> copy = mLoadContext;
copy.forget(result);
return NS_OK;
}
return QueryInterface(aIID, result);
}
//-----------------------------------------------------------------------------
// HttpChannelParent::PHttpChannelParent
//-----------------------------------------------------------------------------
bool
HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const OptionalURIParams& aOriginalURI,
const OptionalURIParams& aDocURI,
const OptionalURIParams& aReferrerURI,
const uint32_t& aReferrerPolicy,
const OptionalURIParams& aAPIRedirectToURI,
const OptionalURIParams& aTopWindowURI,
const uint32_t& aLoadFlags,
const RequestHeaderTuples& requestHeaders,
const nsCString& requestMethod,
const OptionalInputStreamParams& uploadStream,
const bool& uploadStreamHasHeaders,
const uint16_t& priority,
const uint32_t& classOfService,
const uint8_t& redirectionLimit,
const bool& allowPipelining,
const bool& allowSTS,
const uint32_t& thirdPartyFlags,
const bool& doResumeAt,
const uint64_t& startPos,
const nsCString& entityID,
const bool& chooseApplicationCache,
const nsCString& appCacheClientID,
const bool& allowSpdy,
const bool& allowAltSvc,
const OptionalFileDescriptorSet& aFds,
const OptionalLoadInfoArgs& aLoadInfoArgs,
const OptionalHttpResponseHead& aSynthesizedResponseHead,
const nsCString& aSecurityInfoSerialization,
const uint32_t& aCacheKey,
const nsCString& aSchedulingContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin,
const bool& aAllowStaleCacheContent)
{
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri) {
// URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
// null deref here.
return false;
}
nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI);
nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aReferrerURI);
nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI);
nsCOMPtr<nsIURI> topWindowUri = DeserializeURI(aTopWindowURI);
nsCString uriSpec;
uri->GetSpec(uriSpec);
LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s]\n",
this, uriSpec.get()));
nsresult rv;
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
bool appOffline = false;
uint32_t appId = GetAppId();
if (appId != NECKO_UNKNOWN_APP_ID &&
appId != NECKO_NO_APP_ID) {
gIOService->IsAppOffline(appId, &appOffline);
}
uint32_t loadFlags = aLoadFlags;
if (appOffline) {
loadFlags |= nsICachingChannel::LOAD_ONLY_FROM_CACHE;
loadFlags |= nsIRequest::LOAD_FROM_CACHE;
loadFlags |= nsICachingChannel::LOAD_NO_NETWORK_IO;
}
nsCOMPtr<nsILoadInfo> loadInfo;
rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs,
getter_AddRefs(loadInfo));
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannelInternal(getter_AddRefs(channel), uri, loadInfo,
nullptr, nullptr, loadFlags, ios);
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
mChannel = static_cast<nsHttpChannel *>(channel.get());
mChannel->SetWarningReporter(this);
mChannel->SetTimingEnabled(true);
if (mPBOverride != kPBOverride_Unset) {
mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
}
if (doResumeAt)
mChannel->ResumeAt(startPos, entityID);
if (originalUri)
mChannel->SetOriginalURI(originalUri);
if (docUri)
mChannel->SetDocumentURI(docUri);
if (referrerUri)
mChannel->SetReferrerWithPolicyInternal(referrerUri, aReferrerPolicy);
if (apiRedirectToUri)
mChannel->RedirectTo(apiRedirectToUri);
if (topWindowUri)
mChannel->SetTopWindowURI(topWindowUri);
if (loadFlags != nsIRequest::LOAD_NORMAL)
mChannel->SetLoadFlags(loadFlags);
for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
if (requestHeaders[i].mEmpty) {
mChannel->SetEmptyRequestHeader(requestHeaders[i].mHeader);
} else {
mChannel->SetRequestHeader(requestHeaders[i].mHeader,
requestHeaders[i].mValue,
requestHeaders[i].mMerge);
}
}
mParentListener = new HttpChannelParentListener(this);
mChannel->SetNotificationCallbacks(mParentListener);
mChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
nsTArray<mozilla::ipc::FileDescriptor> fds;
if (aFds.type() == OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
FileDescriptorSetParent* fdSetActor =
static_cast<FileDescriptorSetParent*>(aFds.get_PFileDescriptorSetParent());
MOZ_ASSERT(fdSetActor);
fdSetActor->ForgetFileDescriptors(fds);
MOZ_ASSERT(!fds.IsEmpty());
Unused << fdSetActor->Send__delete__(fdSetActor);
} else if (aFds.type() == OptionalFileDescriptorSet::TArrayOfFileDescriptor) {
const_cast<OptionalFileDescriptorSet&>(aFds).
get_ArrayOfFileDescriptor().SwapElements(fds);
}
if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
nsCOMPtr<nsIPrincipal> preflightPrincipal =
PrincipalInfoToPrincipal(args.preflightPrincipal());
rv = mChannel->SetCorsPreflightParameters(args.unsafeHeaders(),
args.withCredentials(),
preflightPrincipal);
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
}
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream, fds);
if (stream) {
mChannel->InternalSetUploadStream(stream);
mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
}
mChannel->SetAllowStaleCacheContent(aAllowStaleCacheContent);
if (aSynthesizedResponseHead.type() == OptionalHttpResponseHead::TnsHttpResponseHead) {
mSynthesizedResponseHead = new nsHttpResponseHead(aSynthesizedResponseHead.get_nsHttpResponseHead());
mShouldIntercept = true;
mChannel->SetCouldBeSynthesized();
if (!aSecurityInfoSerialization.IsEmpty()) {
nsCOMPtr<nsISupports> secInfo;
NS_DeserializeObject(aSecurityInfoSerialization, getter_AddRefs(secInfo));
mChannel->OverrideSecurityInfo(secInfo);
}
} else {
nsLoadFlags loadFlags;
mChannel->GetLoadFlags(&loadFlags);
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
mChannel->SetLoadFlags(loadFlags);
}
nsCOMPtr<nsISupportsPRUint32> cacheKey =
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
rv = cacheKey->SetData(aCacheKey);
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
mChannel->SetCacheKey(cacheKey);
if (priority != nsISupportsPriority::PRIORITY_NORMAL) {
mChannel->SetPriority(priority);
}
if (classOfService) {
mChannel->SetClassFlags(classOfService);
}
mChannel->SetRedirectionLimit(redirectionLimit);
mChannel->SetAllowPipelining(allowPipelining);
mChannel->SetAllowSTS(allowSTS);
mChannel->SetThirdPartyFlags(thirdPartyFlags);
mChannel->SetAllowSpdy(allowSpdy);
mChannel->SetAllowAltSvc(allowAltSvc);
mChannel->SetInitialRwin(aInitialRwin);
nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
do_QueryObject(mChannel);
nsCOMPtr<nsIApplicationCacheService> appCacheService =
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
bool setChooseApplicationCache = chooseApplicationCache;
if (appCacheChan && appCacheService) {
// We might potentially want to drop this flag (that is TRUE by default)
// after we successfully associate the channel with an application cache
// reported by the channel child. Dropping it here may be too early.
appCacheChan->SetInheritApplicationCache(false);
if (!appCacheClientID.IsEmpty()) {
nsCOMPtr<nsIApplicationCache> appCache;
rv = appCacheService->GetApplicationCache(appCacheClientID,
getter_AddRefs(appCache));
if (NS_SUCCEEDED(rv)) {
appCacheChan->SetApplicationCache(appCache);
setChooseApplicationCache = false;
}
}
if (setChooseApplicationCache) {
OriginAttributes attrs;
if (mLoadContext) {
attrs.CopyFromLoadContext(mLoadContext);
}
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateCodebasePrincipal(uri, attrs);
bool chooseAppCache = false;
// This works because we've already called SetNotificationCallbacks and
// done mPBOverride logic by this point.
chooseAppCache = NS_ShouldCheckAppCache(principal, NS_UsePrivateBrowsing(mChannel));
appCacheChan->SetChooseApplicationCache(chooseAppCache);
}
}
nsID schedulingContextID;
schedulingContextID.Parse(aSchedulingContextID.BeginReading());
mChannel->SetSchedulingContextID(schedulingContextID);
if (loadInfo && loadInfo->GetEnforceSecurity()) {
rv = mChannel->AsyncOpen2(mParentListener);
}
else {
rv = mChannel->AsyncOpen(mParentListener, nullptr);
}
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
return true;
}
bool
HttpChannelParent::ConnectChannel(const uint32_t& channelId, const bool& shouldIntercept)
{
nsresult rv;
LOG(("HttpChannelParent::ConnectChannel: Looking for a registered channel "
"[this=%p, id=%lu]\n", this, channelId));
nsCOMPtr<nsIChannel> channel;
rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
mChannel = static_cast<nsHttpChannel*>(channel.get());
LOG((" found channel %p, rv=%08x", mChannel.get(), rv));
mShouldIntercept = shouldIntercept;
if (mShouldIntercept) {
// When an interception occurs, this channel should suspend all further activity.
// It will be torn down and recreated if necessary.
mShouldSuspendIntercept = true;
}
if (mPBOverride != kPBOverride_Unset) {
// redirected-to channel may not support PB
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
if (pbChannel) {
pbChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
}
}
bool appOffline = false;
uint32_t appId = GetAppId();
if (appId != NECKO_UNKNOWN_APP_ID &&
appId != NECKO_NO_APP_ID) {
gIOService->IsAppOffline(appId, &appOffline);
}
if (appOffline) {
uint32_t loadFlags;
mChannel->GetLoadFlags(&loadFlags);
loadFlags |= nsICachingChannel::LOAD_ONLY_FROM_CACHE;
loadFlags |= nsIRequest::LOAD_FROM_CACHE;
loadFlags |= nsICachingChannel::LOAD_NO_NETWORK_IO;
mChannel->SetLoadFlags(loadFlags);
}
return true;
}
bool
HttpChannelParent::RecvSetPriority(const uint16_t& priority)
{
LOG(("HttpChannelParent::RecvSetPriority [this=%p, priority=%u]\n",
this, priority));
if (mChannel) {
mChannel->SetPriority(priority);
}
nsCOMPtr<nsISupportsPriority> priorityRedirectChannel =
do_QueryInterface(mRedirectChannel);
if (priorityRedirectChannel)
priorityRedirectChannel->SetPriority(priority);
return true;
}
bool
HttpChannelParent::RecvSetClassOfService(const uint32_t& cos)
{
if (mChannel) {
mChannel->SetClassFlags(cos);
}
return true;
}
bool
HttpChannelParent::RecvSuspend()
{
LOG(("HttpChannelParent::RecvSuspend [this=%p]\n", this));
if (mChannel) {
mChannel->Suspend();
}
return true;
}
bool
HttpChannelParent::RecvResume()
{
LOG(("HttpChannelParent::RecvResume [this=%p]\n", this));
if (mChannel) {
mChannel->Resume();
}
return true;
}
bool
HttpChannelParent::RecvCancel(const nsresult& status)
{
LOG(("HttpChannelParent::RecvCancel [this=%p]\n", this));
// May receive cancel before channel has been constructed!
if (mChannel) {
mChannel->Cancel(status);
}
return true;
}
bool
HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset)
{
if (mCacheEntry)
mCacheEntry->SetMetaDataElement("charset", charset.get());
return true;
}
bool
HttpChannelParent::RecvUpdateAssociatedContentSecurity(const int32_t& broken,
const int32_t& no)
{
if (mAssociatedContentSecurity) {
mAssociatedContentSecurity->SetCountSubRequestsBrokenSecurity(broken);
mAssociatedContentSecurity->SetCountSubRequestsNoSecurity(no);
}
return true;
}
bool
HttpChannelParent::RecvRedirect2Verify(const nsresult& result,
const RequestHeaderTuples& changedHeaders,
const uint32_t& loadFlags,
const OptionalURIParams& aAPIRedirectURI)
{
LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%x]\n",
this, result));
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIHttpChannel> newHttpChannel =
do_QueryInterface(mRedirectChannel);
if (newHttpChannel) {
nsCOMPtr<nsIURI> apiRedirectUri = DeserializeURI(aAPIRedirectURI);
if (apiRedirectUri)
newHttpChannel->RedirectTo(apiRedirectUri);
for (uint32_t i = 0; i < changedHeaders.Length(); i++) {
if (changedHeaders[i].mEmpty) {
newHttpChannel->SetEmptyRequestHeader(changedHeaders[i].mHeader);
} else {
newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
changedHeaders[i].mValue,
changedHeaders[i].mMerge);
}
}
// A successfully redirected channel must have the LOAD_REPLACE flag.
MOZ_ASSERT(loadFlags & nsIChannel::LOAD_REPLACE);
if (loadFlags & nsIChannel::LOAD_REPLACE) {
newHttpChannel->SetLoadFlags(loadFlags);
}
}
}
if (!mRedirectCallback) {
// This should according the logic never happen, log the situation.
if (mReceivedRedirect2Verify)
LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this));
if (mSentRedirect1BeginFailed)
LOG(("RecvRedirect2Verify[%p]: Send to child failed", this));
if (mSentRedirect1Begin && NS_FAILED(result))
LOG(("RecvRedirect2Verify[%p]: Redirect failed", this));
if (mSentRedirect1Begin && NS_SUCCEEDED(result))
LOG(("RecvRedirect2Verify[%p]: Redirect succeeded", this));
if (!mRedirectChannel)
LOG(("RecvRedirect2Verify[%p]: Missing redirect channel", this));
NS_ERROR("Unexpcted call to HttpChannelParent::RecvRedirect2Verify, "
"mRedirectCallback null");
}
mReceivedRedirect2Verify = true;
if (mRedirectCallback) {
LOG(("HttpChannelParent::RecvRedirect2Verify call OnRedirectVerifyCallback"
" [this=%p result=%x, mRedirectCallback=%p]\n",
this, result, mRedirectCallback.get()));
mRedirectCallback->OnRedirectVerifyCallback(result);
mRedirectCallback = nullptr;
}
return true;
}
bool
HttpChannelParent::RecvDocumentChannelCleanup()
{
// From now on only using mAssociatedContentSecurity. Free everything else.
mChannel = 0; // Reclaim some memory sooner.
mCacheEntry = 0; // Else we'll block other channels reading same URI
return true;
}
bool
HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign()
{
if (mOfflineForeignMarker) {
mOfflineForeignMarker->MarkAsForeign();
mOfflineForeignMarker = 0;
}
return true;
}
class DivertDataAvailableEvent : public ChannelEvent
{
public:
DivertDataAvailableEvent(HttpChannelParent* aParent,
const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
: mParent(aParent)
, mData(data)
, mOffset(offset)
, mCount(count)
{
}
void Run()
{
mParent->DivertOnDataAvailable(mData, mOffset, mCount);
}
private:
HttpChannelParent* mParent;
nsCString mData;
uint64_t mOffset;
uint32_t mCount;
};
bool
HttpChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
{
LOG(("HttpChannelParent::RecvDivertOnDataAvailable [this=%p]\n", this));
MOZ_ASSERT(mParentListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot RecvDivertOnDataAvailable if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return false;
}
// Drop OnDataAvailables if the parent was canceled already.
if (NS_FAILED(mStatus)) {
return true;
}
if (mEventQ->ShouldEnqueue()) {
mEventQ->Enqueue(new DivertDataAvailableEvent(this, data, offset, count));
return true;
}
DivertOnDataAvailable(data, offset, count);
return true;
}
void
HttpChannelParent::DivertOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
{
LOG(("HttpChannelParent::DivertOnDataAvailable [this=%p]\n", this));
MOZ_ASSERT(mParentListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertOnDataAvailable if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
// Drop OnDataAvailables if the parent was canceled already.
if (NS_FAILED(mStatus)) {
return;
}
nsCOMPtr<nsIInputStream> stringStream;
nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(),
count, NS_ASSIGNMENT_DEPEND);
if (NS_FAILED(rv)) {
if (mChannel) {
mChannel->Cancel(rv);
}
mStatus = rv;
return;
}
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
rv = mParentListener->OnDataAvailable(mChannel, nullptr, stringStream,
offset, count);
stringStream->Close();
if (NS_FAILED(rv)) {
if (mChannel) {
mChannel->Cancel(rv);
}
mStatus = rv;
}
}
class DivertStopRequestEvent : public ChannelEvent
{
public:
DivertStopRequestEvent(HttpChannelParent* aParent,
const nsresult& statusCode)
: mParent(aParent)
, mStatusCode(statusCode)
{
}
void Run() {
mParent->DivertOnStopRequest(mStatusCode);
}
private:
HttpChannelParent* mParent;
nsresult mStatusCode;
};
bool
HttpChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
{
LOG(("HttpChannelParent::RecvDivertOnStopRequest [this=%p]\n", this));
MOZ_ASSERT(mParentListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot RecvDivertOnStopRequest if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return false;
}
if (mEventQ->ShouldEnqueue()) {
mEventQ->Enqueue(new DivertStopRequestEvent(this, statusCode));
return true;
}
DivertOnStopRequest(statusCode);
return true;
}
void
HttpChannelParent::DivertOnStopRequest(const nsresult& statusCode)
{
LOG(("HttpChannelParent::DivertOnStopRequest [this=%p]\n", this));
MOZ_ASSERT(mParentListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertOnStopRequest if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
// Honor the channel's status even if the underlying transaction completed.
nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
// Reset fake pending status in case OnStopRequest has already been called.
if (mChannel) {
mChannel->ForcePending(false);
}
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
mParentListener->OnStopRequest(mChannel, nullptr, status);
}
class DivertCompleteEvent : public ChannelEvent
{
public:
explicit DivertCompleteEvent(HttpChannelParent* aParent)
: mParent(aParent)
{
}
void Run() {
mParent->DivertComplete();
}
private:
HttpChannelParent* mParent;
};
bool
HttpChannelParent::RecvDivertComplete()
{
LOG(("HttpChannelParent::RecvDivertComplete [this=%p]\n", this));
MOZ_ASSERT(mParentListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot RecvDivertComplete if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return false;
}
if (mEventQ->ShouldEnqueue()) {
mEventQ->Enqueue(new DivertCompleteEvent(this));
return true;
}
DivertComplete();
return true;
}
void
HttpChannelParent::DivertComplete()
{
LOG(("HttpChannelParent::DivertComplete [this=%p]\n", this));
MOZ_ASSERT(mParentListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertComplete if diverting is not set!");
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
nsresult rv = ResumeForDiversion();
if (NS_WARN_IF(NS_FAILED(rv))) {
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
mParentListener = nullptr;
}
bool
HttpChannelParent::RecvRemoveCorsPreflightCacheEntry(const URIParams& uri,
const mozilla::ipc::PrincipalInfo& requestingPrincipal)
{
nsCOMPtr<nsIURI> deserializedURI = DeserializeURI(uri);
if (!deserializedURI) {
return false;
}
nsCOMPtr<nsIPrincipal> principal =
PrincipalInfoToPrincipal(requestingPrincipal);
if (!principal) {
return false;
}
nsCORSListenerProxy::RemoveFromCorsPreflightCache(deserializedURI,
principal);
return true;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIPackagedAppChannelListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParent::OnStartSignedPackageRequest(const nsACString& aPackageId)
{
if (mTabParent) {
mTabParent->OnStartSignedPackageRequest(mChannel, aPackageId);
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIRequestObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
{
LOG(("HttpChannelParent::OnStartRequest [this=%p, aRequest=%p]\n",
this, aRequest));
MOZ_RELEASE_ASSERT(!mDivertingFromChild,
"Cannot call OnStartRequest if diverting is set!");
nsHttpChannel *chan = static_cast<nsHttpChannel *>(aRequest);
nsHttpResponseHead *responseHead = chan->GetResponseHead();
nsHttpRequestHead *requestHead = chan->GetRequestHead();
bool isFromCache = false;
chan->IsFromCache(&isFromCache);
uint32_t expirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
chan->GetCacheTokenExpirationTime(&expirationTime);
nsCString cachedCharset;
chan->GetCacheTokenCachedCharset(cachedCharset);
bool loadedFromApplicationCache;
chan->GetLoadedFromApplicationCache(&loadedFromApplicationCache);
if (loadedFromApplicationCache) {
mOfflineForeignMarker = chan->GetOfflineCacheEntryAsForeignMarker();
nsCOMPtr<nsIApplicationCache> appCache;
chan->GetApplicationCache(getter_AddRefs(appCache));
nsCString appCacheGroupId;
nsCString appCacheClientId;
appCache->GetGroupID(appCacheGroupId);
appCache->GetClientID(appCacheClientId);
if (mIPCClosed ||
!SendAssociateApplicationCache(appCacheGroupId, appCacheClientId))
{
return NS_ERROR_UNEXPECTED;
}
}
nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(aRequest);
if (encodedChannel)
encodedChannel->SetApplyConversion(false);
// Keep the cache entry for future use in RecvSetCacheTokenCachedCharset().
// It could be already released by nsHttpChannel at that time.
nsCOMPtr<nsISupports> cacheEntry;
chan->GetCacheToken(getter_AddRefs(cacheEntry));
mCacheEntry = do_QueryInterface(cacheEntry);
nsresult channelStatus = NS_OK;
chan->GetStatus(&channelStatus);
nsCString secInfoSerialization;
UpdateAndSerializeSecurityInfo(secInfoSerialization);
uint16_t redirectCount = 0;
mChannel->GetRedirectCount(&redirectCount);
nsCOMPtr<nsISupports> cacheKey;
mChannel->GetCacheKey(getter_AddRefs(cacheKey));
uint32_t cacheKeyValue = 0;
if (cacheKey) {
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(cacheKey);
if (!container) {
return NS_ERROR_ILLEGAL_VALUE;
}
nsresult rv = container->GetData(&cacheKeyValue);
if (NS_FAILED(rv)) {
return rv;
}
}
if (mIPCClosed ||
!SendOnStartRequest(channelStatus,
responseHead ? *responseHead : nsHttpResponseHead(),
!!responseHead,
requestHead->Headers(),
isFromCache,
mCacheEntry ? true : false,
expirationTime, cachedCharset, secInfoSerialization,
mChannel->GetSelfAddr(), mChannel->GetPeerAddr(),
redirectCount,
cacheKeyValue))
{
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
NS_IMETHODIMP
HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatusCode)
{
LOG(("HttpChannelParent::OnStopRequest: [this=%p aRequest=%p status=%x]\n",
this, aRequest, aStatusCode));
MOZ_RELEASE_ASSERT(!mDivertingFromChild,
"Cannot call OnStopRequest if diverting is set!");
ResourceTimingStruct timing;
mChannel->GetDomainLookupStart(&timing.domainLookupStart);
mChannel->GetDomainLookupEnd(&timing.domainLookupEnd);
mChannel->GetConnectStart(&timing.connectStart);
mChannel->GetConnectEnd(&timing.connectEnd);
mChannel->GetRequestStart(&timing.requestStart);
mChannel->GetResponseStart(&timing.responseStart);
mChannel->GetResponseEnd(&timing.responseEnd);
mChannel->GetAsyncOpen(&timing.fetchStart);
mChannel->GetRedirectStart(&timing.redirectStart);
mChannel->GetRedirectEnd(&timing.redirectEnd);
mChannel->GetTransferSize(&timing.transferSize);
mChannel->GetEncodedBodySize(&timing.encodedBodySize);
// decodedBodySize can be computed in the child process so it doesn't need
// to be passed down.
if (mIPCClosed || !SendOnStopRequest(aStatusCode, timing))
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParent::OnDataAvailable(nsIRequest *aRequest,
nsISupports *aContext,
nsIInputStream *aInputStream,
uint64_t aOffset,
uint32_t aCount)
{
LOG(("HttpChannelParent::OnDataAvailable [this=%p aRequest=%p]\n",
this, aRequest));
MOZ_RELEASE_ASSERT(!mDivertingFromChild,
"Cannot call OnDataAvailable if diverting is set!");
nsCString data;
nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
if (NS_FAILED(rv))
return rv;
nsresult channelStatus = NS_OK;
mChannel->GetStatus(&channelStatus);
// OnDataAvailable is always preceded by OnStatus/OnProgress calls that set
// mStoredStatus/mStoredProgress(Max) to appropriate values, unless
// LOAD_BACKGROUND set. In that case, they'll have garbage values, but
// child doesn't use them.
if (mIPCClosed || !SendOnTransportAndData(channelStatus, mStoredStatus,
mStoredProgress, mStoredProgressMax,
data, aOffset, aCount)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIProgressEventSink
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParent::OnProgress(nsIRequest *aRequest,
nsISupports *aContext,
int64_t aProgress,
int64_t aProgressMax)
{
// OnStatus has always just set mStoredStatus. If it indicates this precedes
// OnDataAvailable, store and ODA will send to child.
if (mStoredStatus == NS_NET_STATUS_RECEIVING_FROM ||
mStoredStatus == NS_NET_STATUS_READING)
{
mStoredProgress = aProgress;
mStoredProgressMax = aProgressMax;
} else {
// Send OnProgress events to the child for data upload progress notifications
// (i.e. status == NS_NET_STATUS_SENDING_TO) or if the channel has
// LOAD_BACKGROUND set.
if (mIPCClosed || !SendOnProgress(aProgress, aProgressMax))
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
NS_IMETHODIMP
HttpChannelParent::OnStatus(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatus,
const char16_t *aStatusArg)
{
// If this precedes OnDataAvailable, store and ODA will send to child.
if (aStatus == NS_NET_STATUS_RECEIVING_FROM ||
aStatus == NS_NET_STATUS_READING)
{
mStoredStatus = aStatus;
return NS_OK;
}
// Otherwise, send to child now
if (mIPCClosed || !SendOnStatus(aStatus))
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIParentChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParent::SetParentListener(HttpChannelParentListener* aListener)
{
LOG(("HttpChannelParent::SetParentListener [this=%p aListener=%p]\n",
this, aListener));
MOZ_ASSERT(aListener);
MOZ_ASSERT(!mParentListener, "SetParentListener should only be called for "
"new HttpChannelParents after a redirect, when "
"mParentListener is null.");
mParentListener = aListener;
return NS_OK;
}
NS_IMETHODIMP
HttpChannelParent::NotifyTrackingProtectionDisabled()
{
if (!mIPCClosed)
Unused << SendNotifyTrackingProtectionDisabled();
return NS_OK;
}
NS_IMETHODIMP
HttpChannelParent::Delete()
{
if (!mIPCClosed)
Unused << SendDeleteSelf();
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIParentRedirectingChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParent::StartRedirect(uint32_t newChannelId,
nsIChannel* newChannel,
uint32_t redirectFlags,
nsIAsyncVerifyRedirectCallback* callback)
{
LOG(("HttpChannelParent::StartRedirect [this=%p, newChannelId=%lu "
"newChannel=%p callback=%p]\n", this, newChannelId, newChannel,
callback));
if (mIPCClosed)
return NS_BINDING_ABORTED;
nsCOMPtr<nsIURI> newURI;
newChannel->GetURI(getter_AddRefs(newURI));
URIParams uriParams;
SerializeURI(newURI, uriParams);
nsCString secInfoSerialization;
UpdateAndSerializeSecurityInfo(secInfoSerialization);
nsHttpResponseHead *responseHead = mChannel->GetResponseHead();
bool result = SendRedirect1Begin(newChannelId, uriParams, redirectFlags,
responseHead ? *responseHead
: nsHttpResponseHead(),
secInfoSerialization);
if (!result) {
// Bug 621446 investigation
mSentRedirect1BeginFailed = true;
return NS_BINDING_ABORTED;
}
// Bug 621446 investigation
mSentRedirect1Begin = true;
// Result is handled in RecvRedirect2Verify above
mRedirectChannel = newChannel;
mRedirectCallback = callback;
return NS_OK;
}
NS_IMETHODIMP
HttpChannelParent::CompleteRedirect(bool succeeded)
{
LOG(("HttpChannelParent::CompleteRedirect [this=%p succeeded=%d]\n",
this, succeeded));
if (succeeded && !mIPCClosed) {
// TODO: check return value: assume child dead if failed
Unused << SendRedirect3Complete();
}
mRedirectChannel = nullptr;
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::ADivertableParentChannel
//-----------------------------------------------------------------------------
nsresult
HttpChannelParent::SuspendForDiversion()
{
LOG(("HttpChannelParent::SuspendForDiversion [this=%p]\n", this));
MOZ_ASSERT(mChannel);
MOZ_ASSERT(mParentListener);
if (NS_WARN_IF(mDivertingFromChild)) {
MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
return NS_ERROR_UNEXPECTED;
}
// Try suspending the channel. Allow it to fail, since OnStopRequest may have
// been called and thus the channel may not be pending.
nsresult rv = mChannel->Suspend();
MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
mSuspendedForDiversion = NS_SUCCEEDED(rv);
rv = mParentListener->SuspendForDiversion();
MOZ_ASSERT(NS_SUCCEEDED(rv));
// Once this is set, no more OnStart/OnData/OnStop callbacks should be sent
// to the child.
mDivertingFromChild = true;
return NS_OK;
}
/* private, supporting function for ADivertableParentChannel */
nsresult
HttpChannelParent::ResumeForDiversion()
{
LOG(("HttpChannelParent::ResumeForDiversion [this=%p]\n", this));
MOZ_ASSERT(mChannel);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot ResumeForDiversion if not diverting!");
return NS_ERROR_UNEXPECTED;
}
if (mSuspendedForDiversion) {
// The nsHttpChannel will deliver remaining OnData/OnStop for the transfer.
nsresult rv = mChannel->Resume();
if (NS_WARN_IF(NS_FAILED(rv))) {
FailDiversion(NS_ERROR_UNEXPECTED, true);
return rv;
}
mSuspendedForDiversion = false;
}
if (NS_WARN_IF(mIPCClosed || !SendDeleteSelf())) {
FailDiversion(NS_ERROR_UNEXPECTED);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
void
HttpChannelParent::DivertTo(nsIStreamListener *aListener)
{
LOG(("HttpChannelParent::DivertTo [this=%p aListener=%p]\n",
this, aListener));
MOZ_ASSERT(mParentListener);
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertTo new listener if diverting is not set!");
return;
}
mDivertListener = aListener;
// Call OnStartRequest and SendDivertMessages asynchronously to avoid
// reentering client context.
NS_DispatchToCurrentThread(
NS_NewRunnableMethod(this, &HttpChannelParent::StartDiversion));
return;
}
void
HttpChannelParent::StartDiversion()
{
LOG(("HttpChannelParent::StartDiversion [this=%p]\n", this));
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot StartDiversion if diverting is not set!");
return;
}
// Fake pending status in case OnStopRequest has already been called.
if (mChannel) {
mChannel->ForcePending(true);
}
{
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
// Call OnStartRequest for the "DivertTo" listener.
nsresult rv = mDivertListener->OnStartRequest(mChannel, nullptr);
if (NS_FAILED(rv)) {
if (mChannel) {
mChannel->Cancel(rv);
}
mStatus = rv;
}
}
mDivertedOnStartRequest = true;
// After OnStartRequest has been called, setup content decoders if needed.
//
// Create a content conversion chain based on mDivertListener and update
// mDivertListener.
nsCOMPtr<nsIStreamListener> converterListener;
mChannel->DoApplyContentConversions(mDivertListener,
getter_AddRefs(converterListener));
if (converterListener) {
mDivertListener = converterListener.forget();
}
// Now mParentListener can be diverted to mDivertListener.
DebugOnly<nsresult> rvdbg = mParentListener->DivertTo(mDivertListener);
MOZ_ASSERT(NS_SUCCEEDED(rvdbg));
mDivertListener = nullptr;
if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
// The listener chain should now be setup; tell HttpChannelChild to divert
// the OnDataAvailables and OnStopRequest to this HttpChannelParent.
if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
FailDiversion(NS_ERROR_UNEXPECTED);
return;
}
}
class HTTPFailDiversionEvent : public nsRunnable
{
public:
HTTPFailDiversionEvent(HttpChannelParent *aChannelParent,
nsresult aErrorCode,
bool aSkipResume)
: mChannelParent(aChannelParent)
, mErrorCode(aErrorCode)
, mSkipResume(aSkipResume)
{
MOZ_RELEASE_ASSERT(aChannelParent);
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
}
NS_IMETHOD Run()
{
mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
return NS_OK;
}
private:
RefPtr<HttpChannelParent> mChannelParent;
nsresult mErrorCode;
bool mSkipResume;
};
void
HttpChannelParent::FailDiversion(nsresult aErrorCode,
bool aSkipResume)
{
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
MOZ_RELEASE_ASSERT(mDivertingFromChild);
MOZ_RELEASE_ASSERT(mParentListener);
MOZ_RELEASE_ASSERT(mChannel);
NS_DispatchToCurrentThread(
new HTTPFailDiversionEvent(this, aErrorCode, aSkipResume));
}
void
HttpChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
bool aSkipResume)
{
LOG(("HttpChannelParent::NotifyDiversionFailed [this=%p aErrorCode=%x]\n",
this, aErrorCode));
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
MOZ_RELEASE_ASSERT(mDivertingFromChild);
MOZ_RELEASE_ASSERT(mParentListener);
MOZ_RELEASE_ASSERT(mChannel);
mChannel->Cancel(aErrorCode);
mChannel->ForcePending(false);
bool isPending = false;
nsresult rv = mChannel->IsPending(&isPending);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
// Resume only if we suspended earlier.
if (mSuspendedForDiversion) {
mChannel->Resume();
}
// Channel has already sent OnStartRequest to the child, so ensure that we
// call it here if it hasn't already been called.
if (!mDivertedOnStartRequest) {
mChannel->ForcePending(true);
mParentListener->OnStartRequest(mChannel, nullptr);
mChannel->ForcePending(false);
}
// If the channel is pending, it will call OnStopRequest itself; otherwise, do
// it here.
if (!isPending) {
mParentListener->OnStopRequest(mChannel, nullptr, aErrorCode);
}
mParentListener = nullptr;
mChannel = nullptr;
if (!mIPCClosed) {
Unused << SendDeleteSelf();
}
}
void
HttpChannelParent::OfflineDisconnect()
{
if (mChannel) {
mChannel->Cancel(NS_ERROR_OFFLINE);
}
mStatus = NS_ERROR_OFFLINE;
}
uint32_t
HttpChannelParent::GetAppId()
{
uint32_t appId = NECKO_UNKNOWN_APP_ID;
if (mLoadContext) {
mLoadContext->GetAppId(&appId);
}
return appId;
}
NS_IMETHODIMP
HttpChannelParent::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid,
void** aResult)
{
nsCOMPtr<nsIAuthPrompt2> prompt =
new NeckoParent::NestedFrameAuthPrompt(Manager(), mNestedFrameId);
prompt.forget(aResult);
return NS_OK;
}
void
HttpChannelParent::UpdateAndSerializeSecurityInfo(nsACString& aSerializedSecurityInfoOut)
{
nsCOMPtr<nsISupports> secInfoSupp;
mChannel->GetSecurityInfo(getter_AddRefs(secInfoSupp));
if (secInfoSupp) {
mAssociatedContentSecurity = do_QueryInterface(secInfoSupp);
nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfoSupp);
if (secInfoSer) {
NS_SerializeToString(secInfoSer, aSerializedSecurityInfoOut);
}
}
}
//-----------------------------------------------------------------------------
// HttpChannelSecurityWarningReporter
//-----------------------------------------------------------------------------
nsresult
HttpChannelParent::ReportSecurityMessage(const nsAString& aMessageTag,
const nsAString& aMessageCategory)
{
if (mIPCClosed ||
NS_WARN_IF(!SendReportSecurityMessage(nsString(aMessageTag),
nsString(aMessageCategory)))) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
NS_IMETHODIMP
HttpChannelParent::IssueWarning(uint32_t aWarning, bool aAsError)
{
Unused << SendIssueDeprecationWarning(aWarning, aAsError);
return NS_OK;
}
} // namespace net
} // namespace mozilla