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