Coverage Report

Created: 2018-09-25 14:53

/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