Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et tw=80 : */
3 :
4 : /* This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "mozilla/net/NeckoChild.h"
9 : #include "mozilla/net/ChannelDiverterChild.h"
10 : #include "mozilla/net/FTPChannelChild.h"
11 : #include "mozilla/dom/ContentChild.h"
12 : #include "mozilla/dom/DocGroup.h"
13 : #include "mozilla/dom/TabChild.h"
14 : #include "nsContentUtils.h"
15 : #include "nsFtpProtocolHandler.h"
16 : #include "nsITabChild.h"
17 : #include "nsStringStream.h"
18 : #include "nsNetUtil.h"
19 : #include "base/compiler_specific.h"
20 : #include "mozilla/ipc/IPCStreamUtils.h"
21 : #include "mozilla/ipc/URIUtils.h"
22 : #include "SerializedLoadContext.h"
23 : #include "mozilla/ipc/BackgroundUtils.h"
24 : #include "nsIPrompt.h"
25 :
26 : using mozilla::dom::ContentChild;
27 : using namespace mozilla::ipc;
28 :
29 : #undef LOG
30 : #define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
31 :
32 : namespace mozilla {
33 : namespace net {
34 :
35 0 : FTPChannelChild::FTPChannelChild(nsIURI* uri)
36 : : mIPCOpen(false)
37 : , mUnknownDecoderInvolved(false)
38 : , mCanceled(false)
39 : , mSuspendCount(0)
40 : , mIsPending(false)
41 : , mLastModifiedTime(0)
42 : , mStartPos(0)
43 : , mDivertingToParent(false)
44 : , mFlushedForDiversion(false)
45 0 : , mSuspendSent(false)
46 : {
47 0 : LOG(("Creating FTPChannelChild @%p\n", this));
48 : // grab a reference to the handler to ensure that it doesn't go away.
49 0 : NS_ADDREF(gFtpHandler);
50 0 : SetURI(uri);
51 0 : mEventQ = new ChannelEventQueue(static_cast<nsIFTPChannel*>(this));
52 :
53 : // We could support thread retargeting, but as long as we're being driven by
54 : // IPDL on the main thread it doesn't buy us anything.
55 0 : DisallowThreadRetargeting();
56 0 : }
57 :
58 0 : FTPChannelChild::~FTPChannelChild()
59 : {
60 0 : LOG(("Destroying FTPChannelChild @%p\n", this));
61 0 : gFtpHandler->Release();
62 0 : }
63 :
64 : void
65 0 : FTPChannelChild::AddIPDLReference()
66 : {
67 0 : MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
68 0 : mIPCOpen = true;
69 0 : AddRef();
70 0 : }
71 :
72 : void
73 0 : FTPChannelChild::ReleaseIPDLReference()
74 : {
75 0 : MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
76 0 : mIPCOpen = false;
77 0 : Release();
78 0 : }
79 :
80 : //-----------------------------------------------------------------------------
81 : // FTPChannelChild::nsISupports
82 : //-----------------------------------------------------------------------------
83 :
84 0 : NS_IMPL_ISUPPORTS_INHERITED(FTPChannelChild,
85 : nsBaseChannel,
86 : nsIFTPChannel,
87 : nsIUploadChannel,
88 : nsIResumableChannel,
89 : nsIProxiedChannel,
90 : nsIChildChannel,
91 : nsIDivertableChannel)
92 :
93 : //-----------------------------------------------------------------------------
94 :
95 : NS_IMETHODIMP
96 0 : FTPChannelChild::GetLastModifiedTime(PRTime* lastModifiedTime)
97 : {
98 0 : *lastModifiedTime = mLastModifiedTime;
99 0 : return NS_OK;
100 : }
101 :
102 : NS_IMETHODIMP
103 0 : FTPChannelChild::SetLastModifiedTime(PRTime lastModifiedTime)
104 : {
105 0 : return NS_ERROR_NOT_AVAILABLE;
106 : }
107 :
108 : NS_IMETHODIMP
109 0 : FTPChannelChild::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID)
110 : {
111 0 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
112 0 : mStartPos = aStartPos;
113 0 : mEntityID = aEntityID;
114 0 : return NS_OK;
115 : }
116 :
117 : NS_IMETHODIMP
118 0 : FTPChannelChild::GetEntityID(nsACString& entityID)
119 : {
120 0 : entityID = mEntityID;
121 0 : return NS_OK;
122 : }
123 :
124 : NS_IMETHODIMP
125 0 : FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo)
126 : {
127 0 : DROP_DEAD();
128 : }
129 :
130 : NS_IMETHODIMP
131 0 : FTPChannelChild::SetUploadStream(nsIInputStream* stream,
132 : const nsACString& contentType,
133 : int64_t contentLength)
134 : {
135 0 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
136 0 : mUploadStream = stream;
137 : // NOTE: contentLength is intentionally ignored here.
138 0 : return NS_OK;
139 : }
140 :
141 : NS_IMETHODIMP
142 0 : FTPChannelChild::GetUploadStream(nsIInputStream** stream)
143 : {
144 0 : NS_ENSURE_ARG_POINTER(stream);
145 0 : *stream = mUploadStream;
146 0 : NS_IF_ADDREF(*stream);
147 0 : return NS_OK;
148 : }
149 :
150 : NS_IMETHODIMP
151 0 : FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext)
152 : {
153 0 : LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this));
154 :
155 0 : NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
156 0 : NS_ENSURE_TRUE(!static_cast<ContentChild*>(gNeckoChild->Manager())->
157 : IsShuttingDown(), NS_ERROR_FAILURE);
158 0 : NS_ENSURE_ARG_POINTER(listener);
159 0 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
160 0 : NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
161 :
162 : // Port checked in parent, but duplicate here so we can return with error
163 : // immediately, as we've done since before e10s.
164 : nsresult rv;
165 0 : rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
166 : // because in the child ipdl,
167 : // a typedef URI is defined...
168 0 : if (NS_FAILED(rv))
169 0 : return rv;
170 :
171 0 : mozilla::dom::TabChild* tabChild = nullptr;
172 0 : nsCOMPtr<nsITabChild> iTabChild;
173 0 : NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
174 : NS_GET_IID(nsITabChild),
175 0 : getter_AddRefs(iTabChild));
176 0 : GetCallback(iTabChild);
177 0 : if (iTabChild) {
178 0 : tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
179 : }
180 0 : if (MissingRequiredTabChild(tabChild, "ftp")) {
181 0 : return NS_ERROR_ILLEGAL_VALUE;
182 : }
183 :
184 0 : mListener = listener;
185 0 : mListenerContext = aContext;
186 :
187 : // add ourselves to the load group.
188 0 : if (mLoadGroup)
189 0 : mLoadGroup->AddRequest(this, nullptr);
190 :
191 0 : mozilla::ipc::AutoIPCStream autoStream;
192 0 : autoStream.Serialize(mUploadStream,
193 0 : static_cast<ContentChild*>(gNeckoChild->Manager()));
194 :
195 0 : FTPChannelOpenArgs openArgs;
196 0 : SerializeURI(nsBaseChannel::URI(), openArgs.uri());
197 0 : openArgs.startPos() = mStartPos;
198 0 : openArgs.entityID() = mEntityID;
199 0 : openArgs.uploadStream() = autoStream.TakeOptionalValue();
200 :
201 0 : nsCOMPtr<nsILoadInfo> loadInfo;
202 0 : GetLoadInfo(getter_AddRefs(loadInfo));
203 0 : rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo());
204 0 : NS_ENSURE_SUCCESS(rv, rv);
205 :
206 : // This must happen before the constructor message is sent.
207 0 : SetupNeckoTarget();
208 :
209 : gNeckoChild->
210 0 : SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this),
211 0 : openArgs);
212 :
213 : // The socket transport layer in the chrome process now has a logical ref to
214 : // us until OnStopRequest is called.
215 0 : AddIPDLReference();
216 :
217 0 : mIsPending = true;
218 0 : mWasOpened = true;
219 :
220 0 : return rv;
221 : }
222 :
223 : NS_IMETHODIMP
224 0 : FTPChannelChild::IsPending(bool* result)
225 : {
226 0 : *result = mIsPending;
227 0 : return NS_OK;
228 : }
229 :
230 : nsresult
231 0 : FTPChannelChild::OpenContentStream(bool async,
232 : nsIInputStream** stream,
233 : nsIChannel** channel)
234 : {
235 0 : MOZ_CRASH("FTPChannel*Child* should never have OpenContentStream called!");
236 : return NS_OK;
237 : }
238 :
239 : //-----------------------------------------------------------------------------
240 : // FTPChannelChild::PFTPChannelChild
241 : //-----------------------------------------------------------------------------
242 :
243 0 : class FTPStartRequestEvent : public NeckoTargetChannelEvent<FTPChannelChild>
244 : {
245 : public:
246 0 : FTPStartRequestEvent(FTPChannelChild* aChild,
247 : const nsresult& aChannelStatus,
248 : const int64_t& aContentLength,
249 : const nsCString& aContentType,
250 : const PRTime& aLastModified,
251 : const nsCString& aEntityID,
252 : const URIParams& aURI)
253 0 : : NeckoTargetChannelEvent<FTPChannelChild>(aChild)
254 0 : , mChannelStatus(aChannelStatus)
255 0 : , mContentLength(aContentLength)
256 : , mContentType(aContentType)
257 0 : , mLastModified(aLastModified)
258 : , mEntityID(aEntityID)
259 0 : , mURI(aURI)
260 : {
261 0 : }
262 0 : void Run()
263 : {
264 0 : mChild->DoOnStartRequest(mChannelStatus, mContentLength, mContentType,
265 0 : mLastModified, mEntityID, mURI);
266 0 : }
267 :
268 : private:
269 : nsresult mChannelStatus;
270 : int64_t mContentLength;
271 : nsCString mContentType;
272 : PRTime mLastModified;
273 : nsCString mEntityID;
274 : URIParams mURI;
275 : };
276 :
277 : mozilla::ipc::IPCResult
278 0 : FTPChannelChild::RecvOnStartRequest(const nsresult& aChannelStatus,
279 : const int64_t& aContentLength,
280 : const nsCString& aContentType,
281 : const PRTime& aLastModified,
282 : const nsCString& aEntityID,
283 : const URIParams& aURI)
284 : {
285 : // mFlushedForDiversion and mDivertingToParent should NEVER be set at this
286 : // stage, as they are set in the listener's OnStartRequest.
287 0 : MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
288 : "mFlushedForDiversion should be unset before OnStartRequest!");
289 0 : MOZ_RELEASE_ASSERT(!mDivertingToParent,
290 : "mDivertingToParent should be unset before OnStartRequest!");
291 :
292 0 : LOG(("FTPChannelChild::RecvOnStartRequest [this=%p]\n", this));
293 :
294 0 : mEventQ->RunOrEnqueue(new FTPStartRequestEvent(this, aChannelStatus,
295 : aContentLength, aContentType,
296 : aLastModified, aEntityID,
297 0 : aURI));
298 0 : return IPC_OK();
299 : }
300 :
301 : void
302 0 : FTPChannelChild::DoOnStartRequest(const nsresult& aChannelStatus,
303 : const int64_t& aContentLength,
304 : const nsCString& aContentType,
305 : const PRTime& aLastModified,
306 : const nsCString& aEntityID,
307 : const URIParams& aURI)
308 : {
309 0 : LOG(("FTPChannelChild::DoOnStartRequest [this=%p]\n", this));
310 :
311 : // mFlushedForDiversion and mDivertingToParent should NEVER be set at this
312 : // stage, as they are set in the listener's OnStartRequest.
313 0 : MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
314 : "mFlushedForDiversion should be unset before OnStartRequest!");
315 0 : MOZ_RELEASE_ASSERT(!mDivertingToParent,
316 : "mDivertingToParent should be unset before OnStartRequest!");
317 :
318 0 : if (!mCanceled && NS_SUCCEEDED(mStatus)) {
319 0 : mStatus = aChannelStatus;
320 : }
321 :
322 0 : mContentLength = aContentLength;
323 0 : SetContentType(aContentType);
324 0 : mLastModifiedTime = aLastModified;
325 0 : mEntityID = aEntityID;
326 :
327 0 : nsCString spec;
328 0 : nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
329 0 : nsresult rv = uri->GetSpec(spec);
330 0 : if (NS_SUCCEEDED(rv)) {
331 0 : rv = nsBaseChannel::URI()->SetSpec(spec);
332 0 : if (NS_FAILED(rv)) {
333 0 : Cancel(rv);
334 : }
335 : } else {
336 0 : Cancel(rv);
337 : }
338 :
339 0 : AutoEventEnqueuer ensureSerialDispatch(mEventQ);
340 0 : rv = mListener->OnStartRequest(this, mListenerContext);
341 0 : if (NS_FAILED(rv))
342 0 : Cancel(rv);
343 :
344 0 : if (mDivertingToParent) {
345 0 : mListener = nullptr;
346 0 : mListenerContext = nullptr;
347 0 : if (mLoadGroup) {
348 0 : mLoadGroup->RemoveRequest(this, nullptr, mStatus);
349 : }
350 : }
351 0 : }
352 :
353 0 : class FTPDataAvailableEvent : public NeckoTargetChannelEvent<FTPChannelChild>
354 : {
355 : public:
356 0 : FTPDataAvailableEvent(FTPChannelChild* aChild,
357 : const nsresult& aChannelStatus,
358 : const nsCString& aData,
359 : const uint64_t& aOffset,
360 : const uint32_t& aCount)
361 0 : : NeckoTargetChannelEvent<FTPChannelChild>(aChild)
362 0 : , mChannelStatus(aChannelStatus)
363 : , mData(aData)
364 0 : , mOffset(aOffset)
365 0 : , mCount(aCount)
366 : {
367 0 : }
368 0 : void Run()
369 : {
370 0 : mChild->DoOnDataAvailable(mChannelStatus, mData, mOffset, mCount);
371 0 : }
372 :
373 : private:
374 : nsresult mChannelStatus;
375 : nsCString mData;
376 : uint64_t mOffset;
377 : uint32_t mCount;
378 : };
379 :
380 : mozilla::ipc::IPCResult
381 0 : FTPChannelChild::RecvOnDataAvailable(const nsresult& channelStatus,
382 : const nsCString& data,
383 : const uint64_t& offset,
384 : const uint32_t& count)
385 : {
386 0 : MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
387 : "Should not be receiving any more callbacks from parent!");
388 :
389 0 : LOG(("FTPChannelChild::RecvOnDataAvailable [this=%p]\n", this));
390 :
391 0 : mEventQ->RunOrEnqueue(new FTPDataAvailableEvent(this, channelStatus, data,
392 0 : offset, count),
393 0 : mDivertingToParent);
394 :
395 0 : return IPC_OK();
396 : }
397 :
398 0 : class MaybeDivertOnDataFTPEvent : public NeckoTargetChannelEvent<FTPChannelChild>
399 : {
400 : public:
401 0 : MaybeDivertOnDataFTPEvent(FTPChannelChild* child,
402 : const nsCString& data,
403 : const uint64_t& offset,
404 : const uint32_t& count)
405 0 : : NeckoTargetChannelEvent<FTPChannelChild>(child)
406 : , mData(data)
407 0 : , mOffset(offset)
408 0 : , mCount(count) {}
409 :
410 0 : void Run()
411 : {
412 0 : mChild->MaybeDivertOnData(mData, mOffset, mCount);
413 0 : }
414 :
415 : private:
416 : nsCString mData;
417 : uint64_t mOffset;
418 : uint32_t mCount;
419 : };
420 :
421 : void
422 0 : FTPChannelChild::MaybeDivertOnData(const nsCString& data,
423 : const uint64_t& offset,
424 : const uint32_t& count)
425 : {
426 0 : if (mDivertingToParent) {
427 0 : SendDivertOnDataAvailable(data, offset, count);
428 : }
429 0 : }
430 :
431 : void
432 0 : FTPChannelChild::DoOnDataAvailable(const nsresult& channelStatus,
433 : const nsCString& data,
434 : const uint64_t& offset,
435 : const uint32_t& count)
436 : {
437 0 : LOG(("FTPChannelChild::DoOnDataAvailable [this=%p]\n", this));
438 :
439 0 : if (!mCanceled && NS_SUCCEEDED(mStatus)) {
440 0 : mStatus = channelStatus;
441 : }
442 :
443 0 : if (mDivertingToParent) {
444 0 : MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
445 : "Should not be processing any more callbacks from parent!");
446 :
447 0 : SendDivertOnDataAvailable(data, offset, count);
448 0 : return;
449 : }
450 :
451 0 : if (mCanceled)
452 0 : return;
453 :
454 0 : if (mUnknownDecoderInvolved) {
455 0 : mUnknownDecoderEventQ.AppendElement(
456 0 : MakeUnique<MaybeDivertOnDataFTPEvent>(this, data, offset, count));
457 : }
458 :
459 : // NOTE: the OnDataAvailable contract requires the client to read all the data
460 : // in the inputstream. This code relies on that ('data' will go away after
461 : // this function). Apparently the previous, non-e10s behavior was to actually
462 : // support only reading part of the data, allowing later calls to read the
463 : // rest.
464 0 : nsCOMPtr<nsIInputStream> stringStream;
465 0 : nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream),
466 : data.get(),
467 0 : count,
468 0 : NS_ASSIGNMENT_DEPEND);
469 0 : if (NS_FAILED(rv)) {
470 0 : Cancel(rv);
471 0 : return;
472 : }
473 :
474 0 : AutoEventEnqueuer ensureSerialDispatch(mEventQ);
475 0 : rv = mListener->OnDataAvailable(this, mListenerContext,
476 0 : stringStream, offset, count);
477 0 : if (NS_FAILED(rv))
478 0 : Cancel(rv);
479 0 : stringStream->Close();
480 : }
481 :
482 0 : class FTPStopRequestEvent : public NeckoTargetChannelEvent<FTPChannelChild>
483 : {
484 : public:
485 0 : FTPStopRequestEvent(FTPChannelChild* aChild,
486 : const nsresult& aChannelStatus,
487 : const nsCString &aErrorMsg,
488 : bool aUseUTF8)
489 0 : : NeckoTargetChannelEvent<FTPChannelChild>(aChild)
490 0 : , mChannelStatus(aChannelStatus)
491 : , mErrorMsg(aErrorMsg)
492 0 : , mUseUTF8(aUseUTF8)
493 : {
494 0 : }
495 0 : void Run()
496 : {
497 0 : mChild->DoOnStopRequest(mChannelStatus, mErrorMsg, mUseUTF8);
498 0 : }
499 :
500 : private:
501 : nsresult mChannelStatus;
502 : nsCString mErrorMsg;
503 : bool mUseUTF8;
504 : };
505 :
506 : mozilla::ipc::IPCResult
507 0 : FTPChannelChild::RecvOnStopRequest(const nsresult& aChannelStatus,
508 : const nsCString &aErrorMsg,
509 : const bool &aUseUTF8)
510 : {
511 0 : MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
512 : "Should not be receiving any more callbacks from parent!");
513 :
514 0 : LOG(("FTPChannelChild::RecvOnStopRequest [this=%p status=%" PRIx32"]\n",
515 : this, static_cast<uint32_t>(aChannelStatus)));
516 :
517 0 : mEventQ->RunOrEnqueue(new FTPStopRequestEvent(this, aChannelStatus, aErrorMsg,
518 0 : aUseUTF8));
519 0 : return IPC_OK();
520 : }
521 :
522 : class nsFtpChildAsyncAlert : public Runnable
523 : {
524 : public:
525 0 : nsFtpChildAsyncAlert(nsIPrompt *aPrompter, nsString aResponseMsg)
526 0 : : Runnable("nsFtpChildAsyncAlert")
527 : , mPrompter(aPrompter)
528 0 : , mResponseMsg(aResponseMsg)
529 : {
530 0 : }
531 : protected:
532 0 : virtual ~nsFtpChildAsyncAlert()
533 0 : {
534 0 : }
535 : public:
536 0 : NS_IMETHOD Run() override
537 : {
538 0 : if (mPrompter) {
539 0 : mPrompter->Alert(nullptr, mResponseMsg.get());
540 : }
541 0 : return NS_OK;
542 : }
543 : private:
544 : nsCOMPtr<nsIPrompt> mPrompter;
545 : nsString mResponseMsg;
546 : };
547 :
548 0 : class MaybeDivertOnStopFTPEvent : public NeckoTargetChannelEvent<FTPChannelChild>
549 : {
550 : public:
551 0 : MaybeDivertOnStopFTPEvent(FTPChannelChild* child,
552 : const nsresult& aChannelStatus)
553 0 : : NeckoTargetChannelEvent<FTPChannelChild>(child)
554 0 : , mChannelStatus(aChannelStatus) {}
555 :
556 0 : void Run()
557 : {
558 0 : mChild->MaybeDivertOnStop(mChannelStatus);
559 0 : }
560 :
561 : private:
562 : nsresult mChannelStatus;
563 : };
564 :
565 : void
566 0 : FTPChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus)
567 : {
568 0 : if (mDivertingToParent) {
569 0 : SendDivertOnStopRequest(aChannelStatus);
570 : }
571 0 : }
572 :
573 : void
574 0 : FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus,
575 : const nsCString &aErrorMsg,
576 : bool aUseUTF8)
577 : {
578 0 : LOG(("FTPChannelChild::DoOnStopRequest [this=%p status=%" PRIx32 "]\n",
579 : this, static_cast<uint32_t>(aChannelStatus)));
580 :
581 0 : if (mDivertingToParent) {
582 0 : MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
583 : "Should not be processing any more callbacks from parent!");
584 :
585 0 : SendDivertOnStopRequest(aChannelStatus);
586 0 : return;
587 : }
588 :
589 0 : if (!mCanceled)
590 0 : mStatus = aChannelStatus;
591 :
592 0 : if (mUnknownDecoderInvolved) {
593 0 : mUnknownDecoderEventQ.AppendElement(
594 0 : MakeUnique<MaybeDivertOnStopFTPEvent>(this, aChannelStatus));
595 : }
596 :
597 : { // Ensure that all queued ipdl events are dispatched before
598 : // we initiate protocol deletion below.
599 0 : mIsPending = false;
600 0 : AutoEventEnqueuer ensureSerialDispatch(mEventQ);
601 0 : (void)mListener->OnStopRequest(this, mListenerContext, aChannelStatus);
602 :
603 0 : if (NS_FAILED(aChannelStatus) && !aErrorMsg.IsEmpty()) {
604 0 : nsCOMPtr<nsIPrompt> prompter;
605 0 : GetCallback(prompter);
606 0 : if (prompter) {
607 0 : nsCOMPtr<nsIRunnable> alertEvent;
608 0 : if (aUseUTF8) {
609 : alertEvent = new nsFtpChildAsyncAlert(prompter,
610 0 : NS_ConvertUTF8toUTF16(aErrorMsg));
611 : } else {
612 : alertEvent = new nsFtpChildAsyncAlert(prompter,
613 0 : NS_ConvertASCIItoUTF16(aErrorMsg));
614 : }
615 :
616 0 : Dispatch(alertEvent.forget());
617 : }
618 : }
619 :
620 0 : mListener = nullptr;
621 0 : mListenerContext = nullptr;
622 :
623 0 : if (mLoadGroup)
624 0 : mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus);
625 : }
626 :
627 : // This calls NeckoChild::DeallocPFTPChannelChild(), which deletes |this| if IPDL
628 : // holds the last reference. Don't rely on |this| existing after here!
629 0 : Send__delete__(this);
630 : }
631 :
632 0 : class FTPFailedAsyncOpenEvent : public NeckoTargetChannelEvent<FTPChannelChild>
633 : {
634 : public:
635 0 : FTPFailedAsyncOpenEvent(FTPChannelChild* aChild, nsresult aStatus)
636 0 : : NeckoTargetChannelEvent<FTPChannelChild>(aChild)
637 0 : , mStatus(aStatus) {}
638 0 : void Run() { mChild->DoFailedAsyncOpen(mStatus); }
639 :
640 : private:
641 : nsresult mStatus;
642 : };
643 :
644 : mozilla::ipc::IPCResult
645 0 : FTPChannelChild::RecvFailedAsyncOpen(const nsresult& statusCode)
646 : {
647 0 : LOG(("FTPChannelChild::RecvFailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
648 : this, static_cast<uint32_t>(statusCode)));
649 0 : mEventQ->RunOrEnqueue(new FTPFailedAsyncOpenEvent(this, statusCode));
650 0 : return IPC_OK();
651 : }
652 :
653 : void
654 0 : FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode)
655 : {
656 0 : LOG(("FTPChannelChild::DoFailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
657 : this, static_cast<uint32_t>(statusCode)));
658 0 : mStatus = statusCode;
659 :
660 0 : if (mLoadGroup)
661 0 : mLoadGroup->RemoveRequest(this, nullptr, statusCode);
662 :
663 0 : if (mListener) {
664 0 : mListener->OnStartRequest(this, mListenerContext);
665 0 : mIsPending = false;
666 0 : mListener->OnStopRequest(this, mListenerContext, statusCode);
667 : } else {
668 0 : mIsPending = false;
669 : }
670 :
671 0 : mListener = nullptr;
672 0 : mListenerContext = nullptr;
673 :
674 0 : if (mIPCOpen)
675 0 : Send__delete__(this);
676 0 : }
677 :
678 0 : class FTPFlushedForDiversionEvent : public NeckoTargetChannelEvent<FTPChannelChild>
679 : {
680 : public:
681 0 : explicit FTPFlushedForDiversionEvent(FTPChannelChild* aChild)
682 0 : : NeckoTargetChannelEvent<FTPChannelChild>(aChild)
683 : {
684 0 : MOZ_RELEASE_ASSERT(aChild);
685 0 : }
686 :
687 0 : void Run()
688 : {
689 0 : mChild->FlushedForDiversion();
690 0 : }
691 : };
692 :
693 : mozilla::ipc::IPCResult
694 0 : FTPChannelChild::RecvFlushedForDiversion()
695 : {
696 0 : LOG(("FTPChannelChild::RecvFlushedForDiversion [this=%p]\n", this));
697 0 : MOZ_ASSERT(mDivertingToParent);
698 :
699 0 : mEventQ->RunOrEnqueue(new FTPFlushedForDiversionEvent(this), true);
700 0 : return IPC_OK();
701 : }
702 :
703 : void
704 0 : FTPChannelChild::FlushedForDiversion()
705 : {
706 0 : LOG(("FTPChannelChild::FlushedForDiversion [this=%p]\n", this));
707 0 : MOZ_RELEASE_ASSERT(mDivertingToParent);
708 :
709 : // Once this is set, it should not be unset before FTPChannelChild is taken
710 : // down. After it is set, no OnStart/OnData/OnStop callbacks should be
711 : // received from the parent channel, nor dequeued from the ChannelEventQueue.
712 0 : mFlushedForDiversion = true;
713 :
714 0 : SendDivertComplete();
715 0 : }
716 :
717 : mozilla::ipc::IPCResult
718 0 : FTPChannelChild::RecvDivertMessages()
719 : {
720 0 : LOG(("FTPChannelChild::RecvDivertMessages [this=%p]\n", this));
721 0 : MOZ_RELEASE_ASSERT(mDivertingToParent);
722 0 : MOZ_RELEASE_ASSERT(mSuspendCount > 0);
723 :
724 : // DivertTo() has been called on parent, so we can now start sending queued
725 : // IPDL messages back to parent listener.
726 0 : if (NS_WARN_IF(NS_FAILED(Resume()))) {
727 0 : return IPC_FAIL_NO_REASON(this);
728 : }
729 0 : return IPC_OK();
730 : }
731 :
732 0 : class FTPDeleteSelfEvent : public NeckoTargetChannelEvent<FTPChannelChild>
733 : {
734 : public:
735 0 : explicit FTPDeleteSelfEvent(FTPChannelChild* aChild)
736 0 : : NeckoTargetChannelEvent<FTPChannelChild>(aChild) {}
737 0 : void Run() { mChild->DoDeleteSelf(); }
738 : };
739 :
740 : mozilla::ipc::IPCResult
741 0 : FTPChannelChild::RecvDeleteSelf()
742 : {
743 0 : mEventQ->RunOrEnqueue(new FTPDeleteSelfEvent(this));
744 0 : return IPC_OK();
745 : }
746 :
747 : void
748 0 : FTPChannelChild::DoDeleteSelf()
749 : {
750 0 : if (mIPCOpen)
751 0 : Send__delete__(this);
752 0 : }
753 :
754 : NS_IMETHODIMP
755 0 : FTPChannelChild::Cancel(nsresult status)
756 : {
757 0 : LOG(("FTPChannelChild::Cancel [this=%p]\n", this));
758 0 : if (mCanceled)
759 0 : return NS_OK;
760 :
761 0 : mCanceled = true;
762 0 : mStatus = status;
763 0 : if (mIPCOpen)
764 0 : SendCancel(status);
765 0 : return NS_OK;
766 : }
767 :
768 : NS_IMETHODIMP
769 0 : FTPChannelChild::Suspend()
770 : {
771 0 : NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
772 :
773 0 : LOG(("FTPChannelChild::Suspend [this=%p]\n", this));
774 :
775 : // SendSuspend only once, when suspend goes from 0 to 1.
776 : // Don't SendSuspend at all if we're diverting callbacks to the parent;
777 : // suspend will be called at the correct time in the parent itself.
778 0 : if (!mSuspendCount++ && !mDivertingToParent) {
779 0 : SendSuspend();
780 0 : mSuspendSent = true;
781 : }
782 0 : mEventQ->Suspend();
783 :
784 0 : return NS_OK;
785 : }
786 :
787 : NS_IMETHODIMP
788 0 : FTPChannelChild::Resume()
789 : {
790 0 : NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
791 :
792 0 : LOG(("FTPChannelChild::Resume [this=%p]\n", this));
793 :
794 : // SendResume only once, when suspend count drops to 0.
795 : // Don't SendResume at all if we're diverting callbacks to the parent (unless
796 : // suspend was sent earlier); otherwise, resume will be called at the correct
797 : // time in the parent itself.
798 0 : if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) {
799 0 : SendResume();
800 : }
801 0 : mEventQ->Resume();
802 :
803 0 : return NS_OK;
804 : }
805 :
806 : //-----------------------------------------------------------------------------
807 : // FTPChannelChild::nsIChildChannel
808 : //-----------------------------------------------------------------------------
809 :
810 : NS_IMETHODIMP
811 0 : FTPChannelChild::ConnectParent(uint32_t id)
812 : {
813 0 : NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
814 0 : NS_ENSURE_TRUE(!static_cast<ContentChild*>(gNeckoChild->Manager())->
815 : IsShuttingDown(), NS_ERROR_FAILURE);
816 :
817 0 : LOG(("FTPChannelChild::ConnectParent [this=%p]\n", this));
818 :
819 0 : mozilla::dom::TabChild* tabChild = nullptr;
820 0 : nsCOMPtr<nsITabChild> iTabChild;
821 0 : NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
822 : NS_GET_IID(nsITabChild),
823 0 : getter_AddRefs(iTabChild));
824 0 : GetCallback(iTabChild);
825 0 : if (iTabChild) {
826 0 : tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
827 : }
828 :
829 : // This must happen before the constructor message is sent.
830 0 : SetupNeckoTarget();
831 :
832 : // The socket transport in the chrome process now holds a logical ref to us
833 : // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
834 0 : AddIPDLReference();
835 :
836 0 : FTPChannelConnectArgs connectArgs(id);
837 :
838 0 : if (!gNeckoChild->SendPFTPChannelConstructor(this, tabChild,
839 0 : IPC::SerializedLoadContext(this),
840 : connectArgs)) {
841 0 : return NS_ERROR_FAILURE;
842 : }
843 :
844 0 : return NS_OK;
845 : }
846 :
847 : NS_IMETHODIMP
848 0 : FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
849 : nsISupports *aContext)
850 : {
851 0 : LOG(("FTPChannelChild::CompleteRedirectSetup [this=%p]\n", this));
852 :
853 0 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
854 0 : NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
855 :
856 0 : mIsPending = true;
857 0 : mWasOpened = true;
858 0 : mListener = listener;
859 0 : mListenerContext = aContext;
860 :
861 : // add ourselves to the load group.
862 0 : if (mLoadGroup)
863 0 : mLoadGroup->AddRequest(this, nullptr);
864 :
865 : // We already have an open IPDL connection to the parent. If on-modify-request
866 : // listeners or load group observers canceled us, let the parent handle it
867 : // and send it back to us naturally.
868 0 : return NS_OK;
869 : }
870 :
871 : //-----------------------------------------------------------------------------
872 : // FTPChannelChild::nsIDivertableChannel
873 : //-----------------------------------------------------------------------------
874 : NS_IMETHODIMP
875 0 : FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild)
876 : {
877 0 : MOZ_RELEASE_ASSERT(aChild);
878 0 : MOZ_RELEASE_ASSERT(gNeckoChild);
879 0 : MOZ_RELEASE_ASSERT(!mDivertingToParent);
880 0 : NS_ENSURE_TRUE(!static_cast<ContentChild*>(gNeckoChild->Manager())->
881 : IsShuttingDown(), NS_ERROR_FAILURE);
882 :
883 0 : LOG(("FTPChannelChild::DivertToParent [this=%p]\n", this));
884 :
885 : // We must fail DivertToParent() if there's no parent end of the channel (and
886 : // won't be!) due to early failure.
887 0 : if (NS_FAILED(mStatus) && !mIPCOpen) {
888 0 : return mStatus;
889 : }
890 :
891 0 : nsresult rv = Suspend();
892 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
893 0 : return rv;
894 : }
895 :
896 : // Once this is set, it should not be unset before the child is taken down.
897 0 : mDivertingToParent = true;
898 :
899 : PChannelDiverterChild* diverter =
900 0 : gNeckoChild->SendPChannelDiverterConstructor(this);
901 0 : MOZ_RELEASE_ASSERT(diverter);
902 :
903 0 : *aChild = static_cast<ChannelDiverterChild*>(diverter);
904 :
905 0 : return NS_OK;
906 : }
907 :
908 : NS_IMETHODIMP
909 0 : FTPChannelChild::UnknownDecoderInvolvedKeepData()
910 : {
911 0 : mUnknownDecoderInvolved = true;
912 0 : return NS_OK;
913 : }
914 :
915 : NS_IMETHODIMP
916 0 : FTPChannelChild::UnknownDecoderInvolvedOnStartRequestCalled()
917 : {
918 0 : mUnknownDecoderInvolved = false;
919 :
920 0 : nsresult rv = NS_OK;
921 :
922 0 : if (mDivertingToParent) {
923 0 : rv = mEventQ->PrependEvents(mUnknownDecoderEventQ);
924 : }
925 0 : mUnknownDecoderEventQ.Clear();
926 :
927 0 : return rv;
928 : }
929 :
930 : NS_IMETHODIMP
931 0 : FTPChannelChild::GetDivertingToParent(bool* aDiverting)
932 : {
933 0 : NS_ENSURE_ARG_POINTER(aDiverting);
934 0 : *aDiverting = mDivertingToParent;
935 0 : return NS_OK;
936 : }
937 :
938 : void
939 0 : FTPChannelChild::SetupNeckoTarget()
940 : {
941 0 : if (mNeckoTarget) {
942 0 : return;
943 : }
944 :
945 0 : nsCOMPtr<nsILoadInfo> loadInfo;
946 0 : GetLoadInfo(getter_AddRefs(loadInfo));
947 :
948 0 : mNeckoTarget = nsContentUtils::GetEventTargetByLoadInfo(loadInfo, TaskCategory::Network);
949 0 : if (!mNeckoTarget) {
950 0 : return;
951 : }
952 :
953 0 : gNeckoChild->SetEventTargetForActor(this, mNeckoTarget);
954 : }
955 :
956 : } // namespace net
957 : } // namespace mozilla
958 :
|