1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/ftp/FTPChannelChild.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,763 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=8 et tw=80 : */ 1.6 + 1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "mozilla/net/NeckoChild.h" 1.12 +#include "mozilla/net/ChannelDiverterChild.h" 1.13 +#include "mozilla/net/FTPChannelChild.h" 1.14 +#include "mozilla/dom/TabChild.h" 1.15 +#include "nsFtpProtocolHandler.h" 1.16 +#include "nsITabChild.h" 1.17 +#include "nsStringStream.h" 1.18 +#include "nsNetUtil.h" 1.19 +#include "base/compiler_specific.h" 1.20 +#include "mozilla/ipc/InputStreamUtils.h" 1.21 +#include "mozilla/ipc/URIUtils.h" 1.22 +#include "SerializedLoadContext.h" 1.23 + 1.24 +using namespace mozilla::ipc; 1.25 + 1.26 +#undef LOG 1.27 +#define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args) 1.28 + 1.29 +namespace mozilla { 1.30 +namespace net { 1.31 + 1.32 +FTPChannelChild::FTPChannelChild(nsIURI* uri) 1.33 +: mIPCOpen(false) 1.34 +, mCanceled(false) 1.35 +, mSuspendCount(0) 1.36 +, mIsPending(false) 1.37 +, mWasOpened(false) 1.38 +, mLastModifiedTime(0) 1.39 +, mStartPos(0) 1.40 +, mDivertingToParent(false) 1.41 +, mFlushedForDiversion(false) 1.42 +, mSuspendSent(false) 1.43 +{ 1.44 + LOG(("Creating FTPChannelChild @%x\n", this)); 1.45 + // grab a reference to the handler to ensure that it doesn't go away. 1.46 + NS_ADDREF(gFtpHandler); 1.47 + SetURI(uri); 1.48 + mEventQ = new ChannelEventQueue(static_cast<nsIFTPChannel*>(this)); 1.49 +} 1.50 + 1.51 +FTPChannelChild::~FTPChannelChild() 1.52 +{ 1.53 + LOG(("Destroying FTPChannelChild @%x\n", this)); 1.54 + gFtpHandler->Release(); 1.55 +} 1.56 + 1.57 +void 1.58 +FTPChannelChild::AddIPDLReference() 1.59 +{ 1.60 + NS_ABORT_IF_FALSE(!mIPCOpen, "Attempt to retain more than one IPDL reference"); 1.61 + mIPCOpen = true; 1.62 + AddRef(); 1.63 +} 1.64 + 1.65 +void 1.66 +FTPChannelChild::ReleaseIPDLReference() 1.67 +{ 1.68 + NS_ABORT_IF_FALSE(mIPCOpen, "Attempt to release nonexistent IPDL reference"); 1.69 + mIPCOpen = false; 1.70 + Release(); 1.71 +} 1.72 + 1.73 +//----------------------------------------------------------------------------- 1.74 +// FTPChannelChild::nsISupports 1.75 +//----------------------------------------------------------------------------- 1.76 + 1.77 +NS_IMPL_ISUPPORTS_INHERITED(FTPChannelChild, 1.78 + nsBaseChannel, 1.79 + nsIFTPChannel, 1.80 + nsIUploadChannel, 1.81 + nsIResumableChannel, 1.82 + nsIProxiedChannel, 1.83 + nsIChildChannel, 1.84 + nsIDivertableChannel) 1.85 + 1.86 +//----------------------------------------------------------------------------- 1.87 + 1.88 +NS_IMETHODIMP 1.89 +FTPChannelChild::GetLastModifiedTime(PRTime* lastModifiedTime) 1.90 +{ 1.91 + *lastModifiedTime = mLastModifiedTime; 1.92 + return NS_OK; 1.93 +} 1.94 + 1.95 +NS_IMETHODIMP 1.96 +FTPChannelChild::SetLastModifiedTime(PRTime lastModifiedTime) 1.97 +{ 1.98 + return NS_ERROR_NOT_AVAILABLE; 1.99 +} 1.100 + 1.101 +NS_IMETHODIMP 1.102 +FTPChannelChild::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) 1.103 +{ 1.104 + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1.105 + mStartPos = aStartPos; 1.106 + mEntityID = aEntityID; 1.107 + return NS_OK; 1.108 +} 1.109 + 1.110 +NS_IMETHODIMP 1.111 +FTPChannelChild::GetEntityID(nsACString& entityID) 1.112 +{ 1.113 + entityID = mEntityID; 1.114 + return NS_OK; 1.115 +} 1.116 + 1.117 +NS_IMETHODIMP 1.118 +FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo) 1.119 +{ 1.120 + DROP_DEAD(); 1.121 +} 1.122 + 1.123 +NS_IMETHODIMP 1.124 +FTPChannelChild::SetUploadStream(nsIInputStream* stream, 1.125 + const nsACString& contentType, 1.126 + int64_t contentLength) 1.127 +{ 1.128 + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1.129 + mUploadStream = stream; 1.130 + // NOTE: contentLength is intentionally ignored here. 1.131 + return NS_OK; 1.132 +} 1.133 + 1.134 +NS_IMETHODIMP 1.135 +FTPChannelChild::GetUploadStream(nsIInputStream** stream) 1.136 +{ 1.137 + NS_ENSURE_ARG_POINTER(stream); 1.138 + *stream = mUploadStream; 1.139 + NS_IF_ADDREF(*stream); 1.140 + return NS_OK; 1.141 +} 1.142 + 1.143 +//----------------------------------------------------------------------------- 1.144 + 1.145 +NS_IMETHODIMP 1.146 +FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext) 1.147 +{ 1.148 + LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this)); 1.149 + 1.150 + NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE); 1.151 + NS_ENSURE_ARG_POINTER(listener); 1.152 + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1.153 + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); 1.154 + 1.155 + // Port checked in parent, but duplicate here so we can return with error 1.156 + // immediately, as we've done since before e10s. 1.157 + nsresult rv; 1.158 + rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate, 1.159 + // because in the child ipdl, 1.160 + // a typedef URI is defined... 1.161 + if (NS_FAILED(rv)) 1.162 + return rv; 1.163 + 1.164 + mozilla::dom::TabChild* tabChild = nullptr; 1.165 + nsCOMPtr<nsITabChild> iTabChild; 1.166 + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, 1.167 + NS_GET_IID(nsITabChild), 1.168 + getter_AddRefs(iTabChild)); 1.169 + GetCallback(iTabChild); 1.170 + if (iTabChild) { 1.171 + tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); 1.172 + } 1.173 + if (MissingRequiredTabChild(tabChild, "ftp")) { 1.174 + return NS_ERROR_ILLEGAL_VALUE; 1.175 + } 1.176 + 1.177 + mListener = listener; 1.178 + mListenerContext = aContext; 1.179 + 1.180 + // add ourselves to the load group. 1.181 + if (mLoadGroup) 1.182 + mLoadGroup->AddRequest(this, nullptr); 1.183 + 1.184 + OptionalInputStreamParams uploadStream; 1.185 + nsTArray<mozilla::ipc::FileDescriptor> fds; 1.186 + SerializeInputStream(mUploadStream, uploadStream, fds); 1.187 + 1.188 + MOZ_ASSERT(fds.IsEmpty()); 1.189 + 1.190 + FTPChannelOpenArgs openArgs; 1.191 + SerializeURI(nsBaseChannel::URI(), openArgs.uri()); 1.192 + openArgs.startPos() = mStartPos; 1.193 + openArgs.entityID() = mEntityID; 1.194 + openArgs.uploadStream() = uploadStream; 1.195 + 1.196 + gNeckoChild-> 1.197 + SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this), 1.198 + openArgs); 1.199 + 1.200 + // The socket transport layer in the chrome process now has a logical ref to 1.201 + // us until OnStopRequest is called. 1.202 + AddIPDLReference(); 1.203 + 1.204 + mIsPending = true; 1.205 + mWasOpened = true; 1.206 + 1.207 + return rv; 1.208 +} 1.209 + 1.210 +NS_IMETHODIMP 1.211 +FTPChannelChild::IsPending(bool* result) 1.212 +{ 1.213 + *result = mIsPending; 1.214 + return NS_OK; 1.215 +} 1.216 + 1.217 +nsresult 1.218 +FTPChannelChild::OpenContentStream(bool async, 1.219 + nsIInputStream** stream, 1.220 + nsIChannel** channel) 1.221 +{ 1.222 + NS_RUNTIMEABORT("FTPChannel*Child* should never have OpenContentStream called!"); 1.223 + return NS_OK; 1.224 +} 1.225 + 1.226 +//----------------------------------------------------------------------------- 1.227 +// FTPChannelChild::PFTPChannelChild 1.228 +//----------------------------------------------------------------------------- 1.229 + 1.230 +class FTPStartRequestEvent : public ChannelEvent 1.231 +{ 1.232 +public: 1.233 + FTPStartRequestEvent(FTPChannelChild* aChild, 1.234 + const nsresult& aChannelStatus, 1.235 + const int64_t& aContentLength, 1.236 + const nsCString& aContentType, 1.237 + const PRTime& aLastModified, 1.238 + const nsCString& aEntityID, 1.239 + const URIParams& aURI) 1.240 + : mChild(aChild) 1.241 + , mChannelStatus(aChannelStatus) 1.242 + , mContentLength(aContentLength) 1.243 + , mContentType(aContentType) 1.244 + , mLastModified(aLastModified) 1.245 + , mEntityID(aEntityID) 1.246 + , mURI(aURI) 1.247 + { 1.248 + } 1.249 + void Run() 1.250 + { 1.251 + mChild->DoOnStartRequest(mChannelStatus, mContentLength, mContentType, 1.252 + mLastModified, mEntityID, mURI); 1.253 + } 1.254 + 1.255 +private: 1.256 + FTPChannelChild* mChild; 1.257 + nsresult mChannelStatus; 1.258 + int64_t mContentLength; 1.259 + nsCString mContentType; 1.260 + PRTime mLastModified; 1.261 + nsCString mEntityID; 1.262 + URIParams mURI; 1.263 +}; 1.264 + 1.265 +bool 1.266 +FTPChannelChild::RecvOnStartRequest(const nsresult& aChannelStatus, 1.267 + const int64_t& aContentLength, 1.268 + const nsCString& aContentType, 1.269 + const PRTime& aLastModified, 1.270 + const nsCString& aEntityID, 1.271 + const URIParams& aURI) 1.272 +{ 1.273 + // mFlushedForDiversion and mDivertingToParent should NEVER be set at this 1.274 + // stage, as they are set in the listener's OnStartRequest. 1.275 + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, 1.276 + "mFlushedForDiversion should be unset before OnStartRequest!"); 1.277 + MOZ_RELEASE_ASSERT(!mDivertingToParent, 1.278 + "mDivertingToParent should be unset before OnStartRequest!"); 1.279 + 1.280 + if (mEventQ->ShouldEnqueue()) { 1.281 + mEventQ->Enqueue(new FTPStartRequestEvent(this, aChannelStatus, 1.282 + aContentLength, aContentType, 1.283 + aLastModified, aEntityID, aURI)); 1.284 + } else { 1.285 + DoOnStartRequest(aChannelStatus, aContentLength, aContentType, 1.286 + aLastModified, aEntityID, aURI); 1.287 + } 1.288 + return true; 1.289 +} 1.290 + 1.291 +void 1.292 +FTPChannelChild::DoOnStartRequest(const nsresult& aChannelStatus, 1.293 + const int64_t& aContentLength, 1.294 + const nsCString& aContentType, 1.295 + const PRTime& aLastModified, 1.296 + const nsCString& aEntityID, 1.297 + const URIParams& aURI) 1.298 +{ 1.299 + LOG(("FTPChannelChild::RecvOnStartRequest [this=%p]\n", this)); 1.300 + 1.301 + // mFlushedForDiversion and mDivertingToParent should NEVER be set at this 1.302 + // stage, as they are set in the listener's OnStartRequest. 1.303 + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, 1.304 + "mFlushedForDiversion should be unset before OnStartRequest!"); 1.305 + MOZ_RELEASE_ASSERT(!mDivertingToParent, 1.306 + "mDivertingToParent should be unset before OnStartRequest!"); 1.307 + 1.308 + if (!mCanceled && NS_SUCCEEDED(mStatus)) { 1.309 + mStatus = aChannelStatus; 1.310 + } 1.311 + 1.312 + mContentLength = aContentLength; 1.313 + SetContentType(aContentType); 1.314 + mLastModifiedTime = aLastModified; 1.315 + mEntityID = aEntityID; 1.316 + 1.317 + nsCString spec; 1.318 + nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); 1.319 + uri->GetSpec(spec); 1.320 + nsBaseChannel::URI()->SetSpec(spec); 1.321 + 1.322 + AutoEventEnqueuer ensureSerialDispatch(mEventQ); 1.323 + nsresult rv = mListener->OnStartRequest(this, mListenerContext); 1.324 + if (NS_FAILED(rv)) 1.325 + Cancel(rv); 1.326 + 1.327 + if (mDivertingToParent) { 1.328 + mListener = nullptr; 1.329 + mListenerContext = nullptr; 1.330 + if (mLoadGroup) { 1.331 + mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1.332 + } 1.333 + } 1.334 +} 1.335 + 1.336 +class FTPDataAvailableEvent : public ChannelEvent 1.337 +{ 1.338 +public: 1.339 + FTPDataAvailableEvent(FTPChannelChild* aChild, 1.340 + const nsresult& aChannelStatus, 1.341 + const nsCString& aData, 1.342 + const uint64_t& aOffset, 1.343 + const uint32_t& aCount) 1.344 + : mChild(aChild) 1.345 + , mChannelStatus(aChannelStatus) 1.346 + , mData(aData) 1.347 + , mOffset(aOffset) 1.348 + , mCount(aCount) 1.349 + { 1.350 + } 1.351 + void Run() 1.352 + { 1.353 + mChild->DoOnDataAvailable(mChannelStatus, mData, mOffset, mCount); 1.354 + } 1.355 + 1.356 +private: 1.357 + FTPChannelChild* mChild; 1.358 + nsresult mChannelStatus; 1.359 + nsCString mData; 1.360 + uint64_t mOffset; 1.361 + uint32_t mCount; 1.362 +}; 1.363 + 1.364 +bool 1.365 +FTPChannelChild::RecvOnDataAvailable(const nsresult& channelStatus, 1.366 + const nsCString& data, 1.367 + const uint64_t& offset, 1.368 + const uint32_t& count) 1.369 +{ 1.370 + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, 1.371 + "Should not be receiving any more callbacks from parent!"); 1.372 + 1.373 + if (mEventQ->ShouldEnqueue()) { 1.374 + mEventQ->Enqueue( 1.375 + new FTPDataAvailableEvent(this, channelStatus, data, offset, count)); 1.376 + } else { 1.377 + MOZ_RELEASE_ASSERT(!mDivertingToParent, 1.378 + "ShouldEnqueue when diverting to parent!"); 1.379 + 1.380 + DoOnDataAvailable(channelStatus, data, offset, count); 1.381 + } 1.382 + return true; 1.383 +} 1.384 + 1.385 +void 1.386 +FTPChannelChild::DoOnDataAvailable(const nsresult& channelStatus, 1.387 + const nsCString& data, 1.388 + const uint64_t& offset, 1.389 + const uint32_t& count) 1.390 +{ 1.391 + LOG(("FTPChannelChild::RecvOnDataAvailable [this=%p]\n", this)); 1.392 + 1.393 + if (!mCanceled && NS_SUCCEEDED(mStatus)) { 1.394 + mStatus = channelStatus; 1.395 + } 1.396 + 1.397 + if (mDivertingToParent) { 1.398 + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, 1.399 + "Should not be processing any more callbacks from parent!"); 1.400 + 1.401 + SendDivertOnDataAvailable(data, offset, count); 1.402 + return; 1.403 + } 1.404 + 1.405 + if (mCanceled) 1.406 + return; 1.407 + 1.408 + // NOTE: the OnDataAvailable contract requires the client to read all the data 1.409 + // in the inputstream. This code relies on that ('data' will go away after 1.410 + // this function). Apparently the previous, non-e10s behavior was to actually 1.411 + // support only reading part of the data, allowing later calls to read the 1.412 + // rest. 1.413 + nsCOMPtr<nsIInputStream> stringStream; 1.414 + nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), 1.415 + data.get(), 1.416 + count, 1.417 + NS_ASSIGNMENT_DEPEND); 1.418 + if (NS_FAILED(rv)) { 1.419 + Cancel(rv); 1.420 + return; 1.421 + } 1.422 + 1.423 + AutoEventEnqueuer ensureSerialDispatch(mEventQ); 1.424 + rv = mListener->OnDataAvailable(this, mListenerContext, 1.425 + stringStream, offset, count); 1.426 + if (NS_FAILED(rv)) 1.427 + Cancel(rv); 1.428 + stringStream->Close(); 1.429 +} 1.430 + 1.431 +class FTPStopRequestEvent : public ChannelEvent 1.432 +{ 1.433 +public: 1.434 + FTPStopRequestEvent(FTPChannelChild* aChild, 1.435 + const nsresult& aChannelStatus) 1.436 + : mChild(aChild) 1.437 + , mChannelStatus(aChannelStatus) 1.438 + { 1.439 + } 1.440 + void Run() 1.441 + { 1.442 + mChild->DoOnStopRequest(mChannelStatus); 1.443 + } 1.444 + 1.445 +private: 1.446 + FTPChannelChild* mChild; 1.447 + nsresult mChannelStatus; 1.448 +}; 1.449 + 1.450 +bool 1.451 +FTPChannelChild::RecvOnStopRequest(const nsresult& aChannelStatus) 1.452 +{ 1.453 + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, 1.454 + "Should not be receiving any more callbacks from parent!"); 1.455 + 1.456 + if (mEventQ->ShouldEnqueue()) { 1.457 + mEventQ->Enqueue(new FTPStopRequestEvent(this, aChannelStatus)); 1.458 + } else { 1.459 + DoOnStopRequest(aChannelStatus); 1.460 + } 1.461 + return true; 1.462 +} 1.463 + 1.464 +void 1.465 +FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus) 1.466 +{ 1.467 + LOG(("FTPChannelChild::RecvOnStopRequest [this=%p status=%u]\n", 1.468 + this, aChannelStatus)); 1.469 + 1.470 + if (mDivertingToParent) { 1.471 + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, 1.472 + "Should not be processing any more callbacks from parent!"); 1.473 + 1.474 + SendDivertOnStopRequest(aChannelStatus); 1.475 + return; 1.476 + } 1.477 + 1.478 + if (!mCanceled) 1.479 + mStatus = aChannelStatus; 1.480 + 1.481 + { // Ensure that all queued ipdl events are dispatched before 1.482 + // we initiate protocol deletion below. 1.483 + mIsPending = false; 1.484 + AutoEventEnqueuer ensureSerialDispatch(mEventQ); 1.485 + (void)mListener->OnStopRequest(this, mListenerContext, aChannelStatus); 1.486 + mListener = nullptr; 1.487 + mListenerContext = nullptr; 1.488 + 1.489 + if (mLoadGroup) 1.490 + mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus); 1.491 + } 1.492 + 1.493 + // This calls NeckoChild::DeallocPFTPChannelChild(), which deletes |this| if IPDL 1.494 + // holds the last reference. Don't rely on |this| existing after here! 1.495 + Send__delete__(this); 1.496 +} 1.497 + 1.498 +class FTPFailedAsyncOpenEvent : public ChannelEvent 1.499 +{ 1.500 + public: 1.501 + FTPFailedAsyncOpenEvent(FTPChannelChild* aChild, nsresult aStatus) 1.502 + : mChild(aChild), mStatus(aStatus) {} 1.503 + void Run() { mChild->DoFailedAsyncOpen(mStatus); } 1.504 + private: 1.505 + FTPChannelChild* mChild; 1.506 + nsresult mStatus; 1.507 +}; 1.508 + 1.509 +bool 1.510 +FTPChannelChild::RecvFailedAsyncOpen(const nsresult& statusCode) 1.511 +{ 1.512 + if (mEventQ->ShouldEnqueue()) { 1.513 + mEventQ->Enqueue(new FTPFailedAsyncOpenEvent(this, statusCode)); 1.514 + } else { 1.515 + DoFailedAsyncOpen(statusCode); 1.516 + } 1.517 + return true; 1.518 +} 1.519 + 1.520 +void 1.521 +FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode) 1.522 +{ 1.523 + mStatus = statusCode; 1.524 + 1.525 + if (mLoadGroup) 1.526 + mLoadGroup->RemoveRequest(this, nullptr, statusCode); 1.527 + 1.528 + if (mListener) { 1.529 + mListener->OnStartRequest(this, mListenerContext); 1.530 + mIsPending = false; 1.531 + mListener->OnStopRequest(this, mListenerContext, statusCode); 1.532 + } else { 1.533 + mIsPending = false; 1.534 + } 1.535 + 1.536 + mListener = nullptr; 1.537 + mListenerContext = nullptr; 1.538 + 1.539 + if (mIPCOpen) 1.540 + Send__delete__(this); 1.541 +} 1.542 + 1.543 +class FTPFlushedForDiversionEvent : public ChannelEvent 1.544 +{ 1.545 + public: 1.546 + FTPFlushedForDiversionEvent(FTPChannelChild* aChild) 1.547 + : mChild(aChild) 1.548 + { 1.549 + MOZ_RELEASE_ASSERT(aChild); 1.550 + } 1.551 + 1.552 + void Run() 1.553 + { 1.554 + mChild->FlushedForDiversion(); 1.555 + } 1.556 + private: 1.557 + FTPChannelChild* mChild; 1.558 +}; 1.559 + 1.560 +bool 1.561 +FTPChannelChild::RecvFlushedForDiversion() 1.562 +{ 1.563 + MOZ_ASSERT(mDivertingToParent); 1.564 + 1.565 + if (mEventQ->ShouldEnqueue()) { 1.566 + mEventQ->Enqueue(new FTPFlushedForDiversionEvent(this)); 1.567 + } else { 1.568 + MOZ_CRASH(); 1.569 + } 1.570 + return true; 1.571 +} 1.572 + 1.573 +void 1.574 +FTPChannelChild::FlushedForDiversion() 1.575 +{ 1.576 + MOZ_RELEASE_ASSERT(mDivertingToParent); 1.577 + 1.578 + // Once this is set, it should not be unset before FTPChannelChild is taken 1.579 + // down. After it is set, no OnStart/OnData/OnStop callbacks should be 1.580 + // received from the parent channel, nor dequeued from the ChannelEventQueue. 1.581 + mFlushedForDiversion = true; 1.582 + 1.583 + SendDivertComplete(); 1.584 +} 1.585 + 1.586 +bool 1.587 +FTPChannelChild::RecvDivertMessages() 1.588 +{ 1.589 + MOZ_RELEASE_ASSERT(mDivertingToParent); 1.590 + MOZ_RELEASE_ASSERT(mSuspendCount > 0); 1.591 + 1.592 + // DivertTo() has been called on parent, so we can now start sending queued 1.593 + // IPDL messages back to parent listener. 1.594 + if (NS_WARN_IF(NS_FAILED(Resume()))) { 1.595 + return false; 1.596 + } 1.597 + return true; 1.598 +} 1.599 + 1.600 +class FTPDeleteSelfEvent : public ChannelEvent 1.601 +{ 1.602 + public: 1.603 + FTPDeleteSelfEvent(FTPChannelChild* aChild) 1.604 + : mChild(aChild) {} 1.605 + void Run() { mChild->DoDeleteSelf(); } 1.606 + private: 1.607 + FTPChannelChild* mChild; 1.608 +}; 1.609 + 1.610 +bool 1.611 +FTPChannelChild::RecvDeleteSelf() 1.612 +{ 1.613 + if (mEventQ->ShouldEnqueue()) { 1.614 + mEventQ->Enqueue(new FTPDeleteSelfEvent(this)); 1.615 + } else { 1.616 + DoDeleteSelf(); 1.617 + } 1.618 + return true; 1.619 +} 1.620 + 1.621 +void 1.622 +FTPChannelChild::DoDeleteSelf() 1.623 +{ 1.624 + if (mIPCOpen) 1.625 + Send__delete__(this); 1.626 +} 1.627 + 1.628 +NS_IMETHODIMP 1.629 +FTPChannelChild::Cancel(nsresult status) 1.630 +{ 1.631 + if (mCanceled) 1.632 + return NS_OK; 1.633 + 1.634 + mCanceled = true; 1.635 + mStatus = status; 1.636 + if (mIPCOpen) 1.637 + SendCancel(status); 1.638 + return NS_OK; 1.639 +} 1.640 + 1.641 +NS_IMETHODIMP 1.642 +FTPChannelChild::Suspend() 1.643 +{ 1.644 + NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); 1.645 + 1.646 + // SendSuspend only once, when suspend goes from 0 to 1. 1.647 + // Don't SendSuspend at all if we're diverting callbacks to the parent; 1.648 + // suspend will be called at the correct time in the parent itself. 1.649 + if (!mSuspendCount++ && !mDivertingToParent) { 1.650 + SendSuspend(); 1.651 + mSuspendSent = true; 1.652 + } 1.653 + mEventQ->Suspend(); 1.654 + 1.655 + return NS_OK; 1.656 +} 1.657 + 1.658 +NS_IMETHODIMP 1.659 +FTPChannelChild::Resume() 1.660 +{ 1.661 + NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); 1.662 + 1.663 + // SendResume only once, when suspend count drops to 0. 1.664 + // Don't SendResume at all if we're diverting callbacks to the parent (unless 1.665 + // suspend was sent earlier); otherwise, resume will be called at the correct 1.666 + // time in the parent itself. 1.667 + if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) { 1.668 + SendResume(); 1.669 + } 1.670 + mEventQ->Resume(); 1.671 + 1.672 + return NS_OK; 1.673 +} 1.674 + 1.675 +//----------------------------------------------------------------------------- 1.676 +// FTPChannelChild::nsIChildChannel 1.677 +//----------------------------------------------------------------------------- 1.678 + 1.679 +NS_IMETHODIMP 1.680 +FTPChannelChild::ConnectParent(uint32_t id) 1.681 +{ 1.682 + mozilla::dom::TabChild* tabChild = nullptr; 1.683 + nsCOMPtr<nsITabChild> iTabChild; 1.684 + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, 1.685 + NS_GET_IID(nsITabChild), 1.686 + getter_AddRefs(iTabChild)); 1.687 + GetCallback(iTabChild); 1.688 + if (iTabChild) { 1.689 + tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); 1.690 + } 1.691 + 1.692 + // The socket transport in the chrome process now holds a logical ref to us 1.693 + // until OnStopRequest, or we do a redirect, or we hit an IPDL error. 1.694 + AddIPDLReference(); 1.695 + 1.696 + FTPChannelConnectArgs connectArgs(id); 1.697 + 1.698 + if (!gNeckoChild->SendPFTPChannelConstructor(this, tabChild, 1.699 + IPC::SerializedLoadContext(this), 1.700 + connectArgs)) { 1.701 + return NS_ERROR_FAILURE; 1.702 + } 1.703 + 1.704 + return NS_OK; 1.705 +} 1.706 + 1.707 +NS_IMETHODIMP 1.708 +FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, 1.709 + nsISupports *aContext) 1.710 +{ 1.711 + LOG(("FTPChannelChild::CompleteRedirectSetup [this=%p]\n", this)); 1.712 + 1.713 + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1.714 + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); 1.715 + 1.716 + mIsPending = true; 1.717 + mWasOpened = true; 1.718 + mListener = listener; 1.719 + mListenerContext = aContext; 1.720 + 1.721 + // add ourselves to the load group. 1.722 + if (mLoadGroup) 1.723 + mLoadGroup->AddRequest(this, nullptr); 1.724 + 1.725 + // We already have an open IPDL connection to the parent. If on-modify-request 1.726 + // listeners or load group observers canceled us, let the parent handle it 1.727 + // and send it back to us naturally. 1.728 + return NS_OK; 1.729 +} 1.730 + 1.731 +//----------------------------------------------------------------------------- 1.732 +// FTPChannelChild::nsIDivertableChannel 1.733 +//----------------------------------------------------------------------------- 1.734 +NS_IMETHODIMP 1.735 +FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild) 1.736 +{ 1.737 + MOZ_RELEASE_ASSERT(aChild); 1.738 + MOZ_RELEASE_ASSERT(gNeckoChild); 1.739 + MOZ_RELEASE_ASSERT(!mDivertingToParent); 1.740 + 1.741 + // We must fail DivertToParent() if there's no parent end of the channel (and 1.742 + // won't be!) due to early failure. 1.743 + if (NS_FAILED(mStatus) && !mIPCOpen) { 1.744 + return mStatus; 1.745 + } 1.746 + 1.747 + nsresult rv = Suspend(); 1.748 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.749 + return rv; 1.750 + } 1.751 + 1.752 + // Once this is set, it should not be unset before the child is taken down. 1.753 + mDivertingToParent = true; 1.754 + 1.755 + PChannelDiverterChild* diverter = 1.756 + gNeckoChild->SendPChannelDiverterConstructor(this); 1.757 + MOZ_RELEASE_ASSERT(diverter); 1.758 + 1.759 + *aChild = static_cast<ChannelDiverterChild*>(diverter); 1.760 + 1.761 + return NS_OK; 1.762 +} 1.763 + 1.764 +} // namespace net 1.765 +} // namespace mozilla 1.766 +