|
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/TabChild.h" |
|
12 #include "nsFtpProtocolHandler.h" |
|
13 #include "nsITabChild.h" |
|
14 #include "nsStringStream.h" |
|
15 #include "nsNetUtil.h" |
|
16 #include "base/compiler_specific.h" |
|
17 #include "mozilla/ipc/InputStreamUtils.h" |
|
18 #include "mozilla/ipc/URIUtils.h" |
|
19 #include "SerializedLoadContext.h" |
|
20 |
|
21 using namespace mozilla::ipc; |
|
22 |
|
23 #undef LOG |
|
24 #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args) |
|
25 |
|
26 namespace mozilla { |
|
27 namespace net { |
|
28 |
|
29 FTPChannelChild::FTPChannelChild(nsIURI* uri) |
|
30 : mIPCOpen(false) |
|
31 , mCanceled(false) |
|
32 , mSuspendCount(0) |
|
33 , mIsPending(false) |
|
34 , mWasOpened(false) |
|
35 , mLastModifiedTime(0) |
|
36 , mStartPos(0) |
|
37 , mDivertingToParent(false) |
|
38 , mFlushedForDiversion(false) |
|
39 , mSuspendSent(false) |
|
40 { |
|
41 LOG(("Creating FTPChannelChild @%x\n", this)); |
|
42 // grab a reference to the handler to ensure that it doesn't go away. |
|
43 NS_ADDREF(gFtpHandler); |
|
44 SetURI(uri); |
|
45 mEventQ = new ChannelEventQueue(static_cast<nsIFTPChannel*>(this)); |
|
46 } |
|
47 |
|
48 FTPChannelChild::~FTPChannelChild() |
|
49 { |
|
50 LOG(("Destroying FTPChannelChild @%x\n", this)); |
|
51 gFtpHandler->Release(); |
|
52 } |
|
53 |
|
54 void |
|
55 FTPChannelChild::AddIPDLReference() |
|
56 { |
|
57 NS_ABORT_IF_FALSE(!mIPCOpen, "Attempt to retain more than one IPDL reference"); |
|
58 mIPCOpen = true; |
|
59 AddRef(); |
|
60 } |
|
61 |
|
62 void |
|
63 FTPChannelChild::ReleaseIPDLReference() |
|
64 { |
|
65 NS_ABORT_IF_FALSE(mIPCOpen, "Attempt to release nonexistent IPDL reference"); |
|
66 mIPCOpen = false; |
|
67 Release(); |
|
68 } |
|
69 |
|
70 //----------------------------------------------------------------------------- |
|
71 // FTPChannelChild::nsISupports |
|
72 //----------------------------------------------------------------------------- |
|
73 |
|
74 NS_IMPL_ISUPPORTS_INHERITED(FTPChannelChild, |
|
75 nsBaseChannel, |
|
76 nsIFTPChannel, |
|
77 nsIUploadChannel, |
|
78 nsIResumableChannel, |
|
79 nsIProxiedChannel, |
|
80 nsIChildChannel, |
|
81 nsIDivertableChannel) |
|
82 |
|
83 //----------------------------------------------------------------------------- |
|
84 |
|
85 NS_IMETHODIMP |
|
86 FTPChannelChild::GetLastModifiedTime(PRTime* lastModifiedTime) |
|
87 { |
|
88 *lastModifiedTime = mLastModifiedTime; |
|
89 return NS_OK; |
|
90 } |
|
91 |
|
92 NS_IMETHODIMP |
|
93 FTPChannelChild::SetLastModifiedTime(PRTime lastModifiedTime) |
|
94 { |
|
95 return NS_ERROR_NOT_AVAILABLE; |
|
96 } |
|
97 |
|
98 NS_IMETHODIMP |
|
99 FTPChannelChild::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) |
|
100 { |
|
101 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); |
|
102 mStartPos = aStartPos; |
|
103 mEntityID = aEntityID; |
|
104 return NS_OK; |
|
105 } |
|
106 |
|
107 NS_IMETHODIMP |
|
108 FTPChannelChild::GetEntityID(nsACString& entityID) |
|
109 { |
|
110 entityID = mEntityID; |
|
111 return NS_OK; |
|
112 } |
|
113 |
|
114 NS_IMETHODIMP |
|
115 FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo) |
|
116 { |
|
117 DROP_DEAD(); |
|
118 } |
|
119 |
|
120 NS_IMETHODIMP |
|
121 FTPChannelChild::SetUploadStream(nsIInputStream* stream, |
|
122 const nsACString& contentType, |
|
123 int64_t contentLength) |
|
124 { |
|
125 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); |
|
126 mUploadStream = stream; |
|
127 // NOTE: contentLength is intentionally ignored here. |
|
128 return NS_OK; |
|
129 } |
|
130 |
|
131 NS_IMETHODIMP |
|
132 FTPChannelChild::GetUploadStream(nsIInputStream** stream) |
|
133 { |
|
134 NS_ENSURE_ARG_POINTER(stream); |
|
135 *stream = mUploadStream; |
|
136 NS_IF_ADDREF(*stream); |
|
137 return NS_OK; |
|
138 } |
|
139 |
|
140 //----------------------------------------------------------------------------- |
|
141 |
|
142 NS_IMETHODIMP |
|
143 FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext) |
|
144 { |
|
145 LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this)); |
|
146 |
|
147 NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE); |
|
148 NS_ENSURE_ARG_POINTER(listener); |
|
149 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); |
|
150 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); |
|
151 |
|
152 // Port checked in parent, but duplicate here so we can return with error |
|
153 // immediately, as we've done since before e10s. |
|
154 nsresult rv; |
|
155 rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate, |
|
156 // because in the child ipdl, |
|
157 // a typedef URI is defined... |
|
158 if (NS_FAILED(rv)) |
|
159 return rv; |
|
160 |
|
161 mozilla::dom::TabChild* tabChild = nullptr; |
|
162 nsCOMPtr<nsITabChild> iTabChild; |
|
163 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, |
|
164 NS_GET_IID(nsITabChild), |
|
165 getter_AddRefs(iTabChild)); |
|
166 GetCallback(iTabChild); |
|
167 if (iTabChild) { |
|
168 tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); |
|
169 } |
|
170 if (MissingRequiredTabChild(tabChild, "ftp")) { |
|
171 return NS_ERROR_ILLEGAL_VALUE; |
|
172 } |
|
173 |
|
174 mListener = listener; |
|
175 mListenerContext = aContext; |
|
176 |
|
177 // add ourselves to the load group. |
|
178 if (mLoadGroup) |
|
179 mLoadGroup->AddRequest(this, nullptr); |
|
180 |
|
181 OptionalInputStreamParams uploadStream; |
|
182 nsTArray<mozilla::ipc::FileDescriptor> fds; |
|
183 SerializeInputStream(mUploadStream, uploadStream, fds); |
|
184 |
|
185 MOZ_ASSERT(fds.IsEmpty()); |
|
186 |
|
187 FTPChannelOpenArgs openArgs; |
|
188 SerializeURI(nsBaseChannel::URI(), openArgs.uri()); |
|
189 openArgs.startPos() = mStartPos; |
|
190 openArgs.entityID() = mEntityID; |
|
191 openArgs.uploadStream() = uploadStream; |
|
192 |
|
193 gNeckoChild-> |
|
194 SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this), |
|
195 openArgs); |
|
196 |
|
197 // The socket transport layer in the chrome process now has a logical ref to |
|
198 // us until OnStopRequest is called. |
|
199 AddIPDLReference(); |
|
200 |
|
201 mIsPending = true; |
|
202 mWasOpened = true; |
|
203 |
|
204 return rv; |
|
205 } |
|
206 |
|
207 NS_IMETHODIMP |
|
208 FTPChannelChild::IsPending(bool* result) |
|
209 { |
|
210 *result = mIsPending; |
|
211 return NS_OK; |
|
212 } |
|
213 |
|
214 nsresult |
|
215 FTPChannelChild::OpenContentStream(bool async, |
|
216 nsIInputStream** stream, |
|
217 nsIChannel** channel) |
|
218 { |
|
219 NS_RUNTIMEABORT("FTPChannel*Child* should never have OpenContentStream called!"); |
|
220 return NS_OK; |
|
221 } |
|
222 |
|
223 //----------------------------------------------------------------------------- |
|
224 // FTPChannelChild::PFTPChannelChild |
|
225 //----------------------------------------------------------------------------- |
|
226 |
|
227 class FTPStartRequestEvent : public ChannelEvent |
|
228 { |
|
229 public: |
|
230 FTPStartRequestEvent(FTPChannelChild* aChild, |
|
231 const nsresult& aChannelStatus, |
|
232 const int64_t& aContentLength, |
|
233 const nsCString& aContentType, |
|
234 const PRTime& aLastModified, |
|
235 const nsCString& aEntityID, |
|
236 const URIParams& aURI) |
|
237 : mChild(aChild) |
|
238 , mChannelStatus(aChannelStatus) |
|
239 , mContentLength(aContentLength) |
|
240 , mContentType(aContentType) |
|
241 , mLastModified(aLastModified) |
|
242 , mEntityID(aEntityID) |
|
243 , mURI(aURI) |
|
244 { |
|
245 } |
|
246 void Run() |
|
247 { |
|
248 mChild->DoOnStartRequest(mChannelStatus, mContentLength, mContentType, |
|
249 mLastModified, mEntityID, mURI); |
|
250 } |
|
251 |
|
252 private: |
|
253 FTPChannelChild* mChild; |
|
254 nsresult mChannelStatus; |
|
255 int64_t mContentLength; |
|
256 nsCString mContentType; |
|
257 PRTime mLastModified; |
|
258 nsCString mEntityID; |
|
259 URIParams mURI; |
|
260 }; |
|
261 |
|
262 bool |
|
263 FTPChannelChild::RecvOnStartRequest(const nsresult& aChannelStatus, |
|
264 const int64_t& aContentLength, |
|
265 const nsCString& aContentType, |
|
266 const PRTime& aLastModified, |
|
267 const nsCString& aEntityID, |
|
268 const URIParams& aURI) |
|
269 { |
|
270 // mFlushedForDiversion and mDivertingToParent should NEVER be set at this |
|
271 // stage, as they are set in the listener's OnStartRequest. |
|
272 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
273 "mFlushedForDiversion should be unset before OnStartRequest!"); |
|
274 MOZ_RELEASE_ASSERT(!mDivertingToParent, |
|
275 "mDivertingToParent should be unset before OnStartRequest!"); |
|
276 |
|
277 if (mEventQ->ShouldEnqueue()) { |
|
278 mEventQ->Enqueue(new FTPStartRequestEvent(this, aChannelStatus, |
|
279 aContentLength, aContentType, |
|
280 aLastModified, aEntityID, aURI)); |
|
281 } else { |
|
282 DoOnStartRequest(aChannelStatus, aContentLength, aContentType, |
|
283 aLastModified, aEntityID, aURI); |
|
284 } |
|
285 return true; |
|
286 } |
|
287 |
|
288 void |
|
289 FTPChannelChild::DoOnStartRequest(const nsresult& aChannelStatus, |
|
290 const int64_t& aContentLength, |
|
291 const nsCString& aContentType, |
|
292 const PRTime& aLastModified, |
|
293 const nsCString& aEntityID, |
|
294 const URIParams& aURI) |
|
295 { |
|
296 LOG(("FTPChannelChild::RecvOnStartRequest [this=%p]\n", this)); |
|
297 |
|
298 // mFlushedForDiversion and mDivertingToParent should NEVER be set at this |
|
299 // stage, as they are set in the listener's OnStartRequest. |
|
300 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
301 "mFlushedForDiversion should be unset before OnStartRequest!"); |
|
302 MOZ_RELEASE_ASSERT(!mDivertingToParent, |
|
303 "mDivertingToParent should be unset before OnStartRequest!"); |
|
304 |
|
305 if (!mCanceled && NS_SUCCEEDED(mStatus)) { |
|
306 mStatus = aChannelStatus; |
|
307 } |
|
308 |
|
309 mContentLength = aContentLength; |
|
310 SetContentType(aContentType); |
|
311 mLastModifiedTime = aLastModified; |
|
312 mEntityID = aEntityID; |
|
313 |
|
314 nsCString spec; |
|
315 nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); |
|
316 uri->GetSpec(spec); |
|
317 nsBaseChannel::URI()->SetSpec(spec); |
|
318 |
|
319 AutoEventEnqueuer ensureSerialDispatch(mEventQ); |
|
320 nsresult rv = mListener->OnStartRequest(this, mListenerContext); |
|
321 if (NS_FAILED(rv)) |
|
322 Cancel(rv); |
|
323 |
|
324 if (mDivertingToParent) { |
|
325 mListener = nullptr; |
|
326 mListenerContext = nullptr; |
|
327 if (mLoadGroup) { |
|
328 mLoadGroup->RemoveRequest(this, nullptr, mStatus); |
|
329 } |
|
330 } |
|
331 } |
|
332 |
|
333 class FTPDataAvailableEvent : public ChannelEvent |
|
334 { |
|
335 public: |
|
336 FTPDataAvailableEvent(FTPChannelChild* aChild, |
|
337 const nsresult& aChannelStatus, |
|
338 const nsCString& aData, |
|
339 const uint64_t& aOffset, |
|
340 const uint32_t& aCount) |
|
341 : mChild(aChild) |
|
342 , mChannelStatus(aChannelStatus) |
|
343 , mData(aData) |
|
344 , mOffset(aOffset) |
|
345 , mCount(aCount) |
|
346 { |
|
347 } |
|
348 void Run() |
|
349 { |
|
350 mChild->DoOnDataAvailable(mChannelStatus, mData, mOffset, mCount); |
|
351 } |
|
352 |
|
353 private: |
|
354 FTPChannelChild* mChild; |
|
355 nsresult mChannelStatus; |
|
356 nsCString mData; |
|
357 uint64_t mOffset; |
|
358 uint32_t mCount; |
|
359 }; |
|
360 |
|
361 bool |
|
362 FTPChannelChild::RecvOnDataAvailable(const nsresult& channelStatus, |
|
363 const nsCString& data, |
|
364 const uint64_t& offset, |
|
365 const uint32_t& count) |
|
366 { |
|
367 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
368 "Should not be receiving any more callbacks from parent!"); |
|
369 |
|
370 if (mEventQ->ShouldEnqueue()) { |
|
371 mEventQ->Enqueue( |
|
372 new FTPDataAvailableEvent(this, channelStatus, data, offset, count)); |
|
373 } else { |
|
374 MOZ_RELEASE_ASSERT(!mDivertingToParent, |
|
375 "ShouldEnqueue when diverting to parent!"); |
|
376 |
|
377 DoOnDataAvailable(channelStatus, data, offset, count); |
|
378 } |
|
379 return true; |
|
380 } |
|
381 |
|
382 void |
|
383 FTPChannelChild::DoOnDataAvailable(const nsresult& channelStatus, |
|
384 const nsCString& data, |
|
385 const uint64_t& offset, |
|
386 const uint32_t& count) |
|
387 { |
|
388 LOG(("FTPChannelChild::RecvOnDataAvailable [this=%p]\n", this)); |
|
389 |
|
390 if (!mCanceled && NS_SUCCEEDED(mStatus)) { |
|
391 mStatus = channelStatus; |
|
392 } |
|
393 |
|
394 if (mDivertingToParent) { |
|
395 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
396 "Should not be processing any more callbacks from parent!"); |
|
397 |
|
398 SendDivertOnDataAvailable(data, offset, count); |
|
399 return; |
|
400 } |
|
401 |
|
402 if (mCanceled) |
|
403 return; |
|
404 |
|
405 // NOTE: the OnDataAvailable contract requires the client to read all the data |
|
406 // in the inputstream. This code relies on that ('data' will go away after |
|
407 // this function). Apparently the previous, non-e10s behavior was to actually |
|
408 // support only reading part of the data, allowing later calls to read the |
|
409 // rest. |
|
410 nsCOMPtr<nsIInputStream> stringStream; |
|
411 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), |
|
412 data.get(), |
|
413 count, |
|
414 NS_ASSIGNMENT_DEPEND); |
|
415 if (NS_FAILED(rv)) { |
|
416 Cancel(rv); |
|
417 return; |
|
418 } |
|
419 |
|
420 AutoEventEnqueuer ensureSerialDispatch(mEventQ); |
|
421 rv = mListener->OnDataAvailable(this, mListenerContext, |
|
422 stringStream, offset, count); |
|
423 if (NS_FAILED(rv)) |
|
424 Cancel(rv); |
|
425 stringStream->Close(); |
|
426 } |
|
427 |
|
428 class FTPStopRequestEvent : public ChannelEvent |
|
429 { |
|
430 public: |
|
431 FTPStopRequestEvent(FTPChannelChild* aChild, |
|
432 const nsresult& aChannelStatus) |
|
433 : mChild(aChild) |
|
434 , mChannelStatus(aChannelStatus) |
|
435 { |
|
436 } |
|
437 void Run() |
|
438 { |
|
439 mChild->DoOnStopRequest(mChannelStatus); |
|
440 } |
|
441 |
|
442 private: |
|
443 FTPChannelChild* mChild; |
|
444 nsresult mChannelStatus; |
|
445 }; |
|
446 |
|
447 bool |
|
448 FTPChannelChild::RecvOnStopRequest(const nsresult& aChannelStatus) |
|
449 { |
|
450 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
451 "Should not be receiving any more callbacks from parent!"); |
|
452 |
|
453 if (mEventQ->ShouldEnqueue()) { |
|
454 mEventQ->Enqueue(new FTPStopRequestEvent(this, aChannelStatus)); |
|
455 } else { |
|
456 DoOnStopRequest(aChannelStatus); |
|
457 } |
|
458 return true; |
|
459 } |
|
460 |
|
461 void |
|
462 FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus) |
|
463 { |
|
464 LOG(("FTPChannelChild::RecvOnStopRequest [this=%p status=%u]\n", |
|
465 this, aChannelStatus)); |
|
466 |
|
467 if (mDivertingToParent) { |
|
468 MOZ_RELEASE_ASSERT(!mFlushedForDiversion, |
|
469 "Should not be processing any more callbacks from parent!"); |
|
470 |
|
471 SendDivertOnStopRequest(aChannelStatus); |
|
472 return; |
|
473 } |
|
474 |
|
475 if (!mCanceled) |
|
476 mStatus = aChannelStatus; |
|
477 |
|
478 { // Ensure that all queued ipdl events are dispatched before |
|
479 // we initiate protocol deletion below. |
|
480 mIsPending = false; |
|
481 AutoEventEnqueuer ensureSerialDispatch(mEventQ); |
|
482 (void)mListener->OnStopRequest(this, mListenerContext, aChannelStatus); |
|
483 mListener = nullptr; |
|
484 mListenerContext = nullptr; |
|
485 |
|
486 if (mLoadGroup) |
|
487 mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus); |
|
488 } |
|
489 |
|
490 // This calls NeckoChild::DeallocPFTPChannelChild(), which deletes |this| if IPDL |
|
491 // holds the last reference. Don't rely on |this| existing after here! |
|
492 Send__delete__(this); |
|
493 } |
|
494 |
|
495 class FTPFailedAsyncOpenEvent : public ChannelEvent |
|
496 { |
|
497 public: |
|
498 FTPFailedAsyncOpenEvent(FTPChannelChild* aChild, nsresult aStatus) |
|
499 : mChild(aChild), mStatus(aStatus) {} |
|
500 void Run() { mChild->DoFailedAsyncOpen(mStatus); } |
|
501 private: |
|
502 FTPChannelChild* mChild; |
|
503 nsresult mStatus; |
|
504 }; |
|
505 |
|
506 bool |
|
507 FTPChannelChild::RecvFailedAsyncOpen(const nsresult& statusCode) |
|
508 { |
|
509 if (mEventQ->ShouldEnqueue()) { |
|
510 mEventQ->Enqueue(new FTPFailedAsyncOpenEvent(this, statusCode)); |
|
511 } else { |
|
512 DoFailedAsyncOpen(statusCode); |
|
513 } |
|
514 return true; |
|
515 } |
|
516 |
|
517 void |
|
518 FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode) |
|
519 { |
|
520 mStatus = statusCode; |
|
521 |
|
522 if (mLoadGroup) |
|
523 mLoadGroup->RemoveRequest(this, nullptr, statusCode); |
|
524 |
|
525 if (mListener) { |
|
526 mListener->OnStartRequest(this, mListenerContext); |
|
527 mIsPending = false; |
|
528 mListener->OnStopRequest(this, mListenerContext, statusCode); |
|
529 } else { |
|
530 mIsPending = false; |
|
531 } |
|
532 |
|
533 mListener = nullptr; |
|
534 mListenerContext = nullptr; |
|
535 |
|
536 if (mIPCOpen) |
|
537 Send__delete__(this); |
|
538 } |
|
539 |
|
540 class FTPFlushedForDiversionEvent : public ChannelEvent |
|
541 { |
|
542 public: |
|
543 FTPFlushedForDiversionEvent(FTPChannelChild* aChild) |
|
544 : mChild(aChild) |
|
545 { |
|
546 MOZ_RELEASE_ASSERT(aChild); |
|
547 } |
|
548 |
|
549 void Run() |
|
550 { |
|
551 mChild->FlushedForDiversion(); |
|
552 } |
|
553 private: |
|
554 FTPChannelChild* mChild; |
|
555 }; |
|
556 |
|
557 bool |
|
558 FTPChannelChild::RecvFlushedForDiversion() |
|
559 { |
|
560 MOZ_ASSERT(mDivertingToParent); |
|
561 |
|
562 if (mEventQ->ShouldEnqueue()) { |
|
563 mEventQ->Enqueue(new FTPFlushedForDiversionEvent(this)); |
|
564 } else { |
|
565 MOZ_CRASH(); |
|
566 } |
|
567 return true; |
|
568 } |
|
569 |
|
570 void |
|
571 FTPChannelChild::FlushedForDiversion() |
|
572 { |
|
573 MOZ_RELEASE_ASSERT(mDivertingToParent); |
|
574 |
|
575 // Once this is set, it should not be unset before FTPChannelChild is taken |
|
576 // down. After it is set, no OnStart/OnData/OnStop callbacks should be |
|
577 // received from the parent channel, nor dequeued from the ChannelEventQueue. |
|
578 mFlushedForDiversion = true; |
|
579 |
|
580 SendDivertComplete(); |
|
581 } |
|
582 |
|
583 bool |
|
584 FTPChannelChild::RecvDivertMessages() |
|
585 { |
|
586 MOZ_RELEASE_ASSERT(mDivertingToParent); |
|
587 MOZ_RELEASE_ASSERT(mSuspendCount > 0); |
|
588 |
|
589 // DivertTo() has been called on parent, so we can now start sending queued |
|
590 // IPDL messages back to parent listener. |
|
591 if (NS_WARN_IF(NS_FAILED(Resume()))) { |
|
592 return false; |
|
593 } |
|
594 return true; |
|
595 } |
|
596 |
|
597 class FTPDeleteSelfEvent : public ChannelEvent |
|
598 { |
|
599 public: |
|
600 FTPDeleteSelfEvent(FTPChannelChild* aChild) |
|
601 : mChild(aChild) {} |
|
602 void Run() { mChild->DoDeleteSelf(); } |
|
603 private: |
|
604 FTPChannelChild* mChild; |
|
605 }; |
|
606 |
|
607 bool |
|
608 FTPChannelChild::RecvDeleteSelf() |
|
609 { |
|
610 if (mEventQ->ShouldEnqueue()) { |
|
611 mEventQ->Enqueue(new FTPDeleteSelfEvent(this)); |
|
612 } else { |
|
613 DoDeleteSelf(); |
|
614 } |
|
615 return true; |
|
616 } |
|
617 |
|
618 void |
|
619 FTPChannelChild::DoDeleteSelf() |
|
620 { |
|
621 if (mIPCOpen) |
|
622 Send__delete__(this); |
|
623 } |
|
624 |
|
625 NS_IMETHODIMP |
|
626 FTPChannelChild::Cancel(nsresult status) |
|
627 { |
|
628 if (mCanceled) |
|
629 return NS_OK; |
|
630 |
|
631 mCanceled = true; |
|
632 mStatus = status; |
|
633 if (mIPCOpen) |
|
634 SendCancel(status); |
|
635 return NS_OK; |
|
636 } |
|
637 |
|
638 NS_IMETHODIMP |
|
639 FTPChannelChild::Suspend() |
|
640 { |
|
641 NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); |
|
642 |
|
643 // SendSuspend only once, when suspend goes from 0 to 1. |
|
644 // Don't SendSuspend at all if we're diverting callbacks to the parent; |
|
645 // suspend will be called at the correct time in the parent itself. |
|
646 if (!mSuspendCount++ && !mDivertingToParent) { |
|
647 SendSuspend(); |
|
648 mSuspendSent = true; |
|
649 } |
|
650 mEventQ->Suspend(); |
|
651 |
|
652 return NS_OK; |
|
653 } |
|
654 |
|
655 NS_IMETHODIMP |
|
656 FTPChannelChild::Resume() |
|
657 { |
|
658 NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); |
|
659 |
|
660 // SendResume only once, when suspend count drops to 0. |
|
661 // Don't SendResume at all if we're diverting callbacks to the parent (unless |
|
662 // suspend was sent earlier); otherwise, resume will be called at the correct |
|
663 // time in the parent itself. |
|
664 if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) { |
|
665 SendResume(); |
|
666 } |
|
667 mEventQ->Resume(); |
|
668 |
|
669 return NS_OK; |
|
670 } |
|
671 |
|
672 //----------------------------------------------------------------------------- |
|
673 // FTPChannelChild::nsIChildChannel |
|
674 //----------------------------------------------------------------------------- |
|
675 |
|
676 NS_IMETHODIMP |
|
677 FTPChannelChild::ConnectParent(uint32_t id) |
|
678 { |
|
679 mozilla::dom::TabChild* tabChild = nullptr; |
|
680 nsCOMPtr<nsITabChild> iTabChild; |
|
681 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, |
|
682 NS_GET_IID(nsITabChild), |
|
683 getter_AddRefs(iTabChild)); |
|
684 GetCallback(iTabChild); |
|
685 if (iTabChild) { |
|
686 tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); |
|
687 } |
|
688 |
|
689 // The socket transport in the chrome process now holds a logical ref to us |
|
690 // until OnStopRequest, or we do a redirect, or we hit an IPDL error. |
|
691 AddIPDLReference(); |
|
692 |
|
693 FTPChannelConnectArgs connectArgs(id); |
|
694 |
|
695 if (!gNeckoChild->SendPFTPChannelConstructor(this, tabChild, |
|
696 IPC::SerializedLoadContext(this), |
|
697 connectArgs)) { |
|
698 return NS_ERROR_FAILURE; |
|
699 } |
|
700 |
|
701 return NS_OK; |
|
702 } |
|
703 |
|
704 NS_IMETHODIMP |
|
705 FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, |
|
706 nsISupports *aContext) |
|
707 { |
|
708 LOG(("FTPChannelChild::CompleteRedirectSetup [this=%p]\n", this)); |
|
709 |
|
710 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); |
|
711 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); |
|
712 |
|
713 mIsPending = true; |
|
714 mWasOpened = true; |
|
715 mListener = listener; |
|
716 mListenerContext = aContext; |
|
717 |
|
718 // add ourselves to the load group. |
|
719 if (mLoadGroup) |
|
720 mLoadGroup->AddRequest(this, nullptr); |
|
721 |
|
722 // We already have an open IPDL connection to the parent. If on-modify-request |
|
723 // listeners or load group observers canceled us, let the parent handle it |
|
724 // and send it back to us naturally. |
|
725 return NS_OK; |
|
726 } |
|
727 |
|
728 //----------------------------------------------------------------------------- |
|
729 // FTPChannelChild::nsIDivertableChannel |
|
730 //----------------------------------------------------------------------------- |
|
731 NS_IMETHODIMP |
|
732 FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild) |
|
733 { |
|
734 MOZ_RELEASE_ASSERT(aChild); |
|
735 MOZ_RELEASE_ASSERT(gNeckoChild); |
|
736 MOZ_RELEASE_ASSERT(!mDivertingToParent); |
|
737 |
|
738 // We must fail DivertToParent() if there's no parent end of the channel (and |
|
739 // won't be!) due to early failure. |
|
740 if (NS_FAILED(mStatus) && !mIPCOpen) { |
|
741 return mStatus; |
|
742 } |
|
743 |
|
744 nsresult rv = Suspend(); |
|
745 if (NS_WARN_IF(NS_FAILED(rv))) { |
|
746 return rv; |
|
747 } |
|
748 |
|
749 // Once this is set, it should not be unset before the child is taken down. |
|
750 mDivertingToParent = true; |
|
751 |
|
752 PChannelDiverterChild* diverter = |
|
753 gNeckoChild->SendPChannelDiverterConstructor(this); |
|
754 MOZ_RELEASE_ASSERT(diverter); |
|
755 |
|
756 *aChild = static_cast<ChannelDiverterChild*>(diverter); |
|
757 |
|
758 return NS_OK; |
|
759 } |
|
760 |
|
761 } // namespace net |
|
762 } // namespace mozilla |
|
763 |