Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/network/TCPSocket.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 ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/ErrorResult.h"
8
#include "TCPSocket.h"
9
#include "TCPServerSocket.h"
10
#include "TCPSocketChild.h"
11
#include "mozilla/dom/TCPSocketBinding.h"
12
#include "mozilla/dom/TCPSocketErrorEvent.h"
13
#include "mozilla/dom/TCPSocketErrorEventBinding.h"
14
#include "mozilla/dom/TCPSocketEvent.h"
15
#include "mozilla/dom/TCPSocketEventBinding.h"
16
#include "mozilla/dom/ToJSValue.h"
17
#include "nsContentUtils.h"
18
#include "nsIArrayBufferInputStream.h"
19
#include "nsISocketTransportService.h"
20
#include "nsISocketTransport.h"
21
#include "nsIMultiplexInputStream.h"
22
#include "nsIAsyncStreamCopier.h"
23
#include "nsIInputStream.h"
24
#include "nsIBinaryInputStream.h"
25
#include "nsIScriptableInputStream.h"
26
#include "nsIInputStreamPump.h"
27
#include "nsIAsyncInputStream.h"
28
#include "nsISupportsPrimitives.h"
29
#include "nsITransport.h"
30
#include "nsIOutputStream.h"
31
#include "nsINSSErrorsService.h"
32
#include "nsISSLSocketControl.h"
33
#include "nsStringStream.h"
34
#include "secerr.h"
35
#include "sslerr.h"
36
37
0
#define BUFFER_SIZE 65536
38
#define NETWORK_STATS_THRESHOLD 65536
39
40
using namespace mozilla::dom;
41
42
NS_IMPL_CYCLE_COLLECTION(LegacyMozTCPSocket, mGlobal)
43
44
NS_IMPL_CYCLE_COLLECTING_ADDREF(LegacyMozTCPSocket)
45
NS_IMPL_CYCLE_COLLECTING_RELEASE(LegacyMozTCPSocket)
46
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LegacyMozTCPSocket)
47
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
48
0
NS_INTERFACE_MAP_END
49
50
LegacyMozTCPSocket::LegacyMozTCPSocket(nsPIDOMWindowInner* aWindow)
51
: mGlobal(do_QueryInterface(aWindow))
52
0
{
53
0
}
54
55
LegacyMozTCPSocket::~LegacyMozTCPSocket()
56
0
{
57
0
}
58
59
already_AddRefed<TCPSocket>
60
LegacyMozTCPSocket::Open(const nsAString& aHost,
61
                         uint16_t aPort,
62
                         const SocketOptions& aOptions,
63
                         mozilla::ErrorResult& aRv)
64
0
{
65
0
  AutoJSAPI api;
66
0
  if (NS_WARN_IF(!api.Init(mGlobal))) {
67
0
    aRv.Throw(NS_ERROR_FAILURE);
68
0
    return nullptr;
69
0
  }
70
0
  GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject());
71
0
  return TCPSocket::Constructor(globalObj, aHost, aPort, aOptions, aRv);
72
0
}
73
74
already_AddRefed<TCPServerSocket>
75
LegacyMozTCPSocket::Listen(uint16_t aPort,
76
                           const ServerSocketOptions& aOptions,
77
                           uint16_t aBacklog,
78
                           mozilla::ErrorResult& aRv)
79
0
{
80
0
  AutoJSAPI api;
81
0
  if (NS_WARN_IF(!api.Init(mGlobal))) {
82
0
    return nullptr;
83
0
  }
84
0
  GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject());
85
0
  return TCPServerSocket::Constructor(globalObj, aPort, aOptions, aBacklog, aRv);
86
0
}
87
88
bool
89
LegacyMozTCPSocket::WrapObject(JSContext* aCx,
90
                               JS::Handle<JSObject*> aGivenProto,
91
                               JS::MutableHandle<JSObject*> aReflector)
92
0
{
93
0
  return LegacyMozTCPSocket_Binding::Wrap(aCx, this, aGivenProto, aReflector);
94
0
}
95
96
NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocket)
97
98
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPSocket,
99
0
                                               DOMEventTargetHelper)
100
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
101
102
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPSocket,
103
0
                                                  DOMEventTargetHelper)
104
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransport)
105
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketInputStream)
106
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketOutputStream)
107
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamPump)
108
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamScriptable)
109
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamBinary)
110
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingDataAfterStartTLS)
111
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeChild)
112
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeParent)
113
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
114
115
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPSocket,
116
0
                                                DOMEventTargetHelper)
117
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransport)
118
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketInputStream)
119
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketOutputStream)
120
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamPump)
121
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamScriptable)
122
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamBinary)
123
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingDataAfterStartTLS)
124
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeChild)
125
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeParent)
126
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
127
128
NS_IMPL_ADDREF_INHERITED(TCPSocket, DOMEventTargetHelper)
129
NS_IMPL_RELEASE_INHERITED(TCPSocket, DOMEventTargetHelper)
130
131
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocket)
132
0
  NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
133
0
  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
134
0
  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
135
0
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
136
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
137
0
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
138
0
  NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback)
139
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
140
141
TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
142
                     bool aSsl, bool aUseArrayBuffers)
143
  : DOMEventTargetHelper(aGlobal)
144
  , mReadyState(TCPReadyState::Closed)
145
  , mUseArrayBuffers(aUseArrayBuffers)
146
  , mHost(aHost)
147
  , mPort(aPort)
148
  , mSsl(aSsl)
149
  , mAsyncCopierActive(false)
150
  , mWaitingForDrain(false)
151
  , mInnerWindowID(0)
152
  , mBufferedAmount(0)
153
  , mSuspendCount(0)
154
  , mTrackingNumber(0)
155
  , mWaitingForStartTLS(false)
156
  , mObserversActive(false)
157
0
{
158
0
  if (aGlobal) {
159
0
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
160
0
    if (window) {
161
0
      mInnerWindowID = window->WindowID();
162
0
    }
163
0
  }
164
0
}
165
166
TCPSocket::~TCPSocket()
167
0
{
168
0
  if (mObserversActive) {
169
0
    nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
170
0
    if (obs) {
171
0
      obs->RemoveObserver(this, "inner-window-destroyed");
172
0
      obs->RemoveObserver(this, "profile-change-net-teardown");
173
0
    }
174
0
  }
175
0
}
176
177
nsresult
178
TCPSocket::CreateStream()
179
0
{
180
0
  nsresult rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream));
181
0
  NS_ENSURE_SUCCESS(rv, rv);
182
0
  rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(mSocketOutputStream));
183
0
  NS_ENSURE_SUCCESS(rv, rv);
184
0
185
0
  // If the other side is not listening, we will
186
0
  // get an onInputStreamReady callback where available
187
0
  // raises to indicate the connection was refused.
188
0
  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mSocketInputStream);
189
0
  NS_ENSURE_TRUE(asyncStream, NS_ERROR_NOT_AVAILABLE);
190
0
191
0
  nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
192
0
  rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, mainTarget);
193
0
  NS_ENSURE_SUCCESS(rv, rv);
194
0
195
0
  if (mUseArrayBuffers) {
196
0
    mInputStreamBinary = do_CreateInstance("@mozilla.org/binaryinputstream;1", &rv);
197
0
    NS_ENSURE_SUCCESS(rv, rv);
198
0
    rv = mInputStreamBinary->SetInputStream(mSocketInputStream);
199
0
    NS_ENSURE_SUCCESS(rv, rv);
200
0
  } else {
201
0
    mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
202
0
    NS_ENSURE_SUCCESS(rv, rv);
203
0
    rv = mInputStreamScriptable->Init(mSocketInputStream);
204
0
    NS_ENSURE_SUCCESS(rv, rv);
205
0
  }
206
0
207
0
  return NS_OK;
208
0
}
209
210
nsresult
211
TCPSocket::InitWithUnconnectedTransport(nsISocketTransport* aTransport)
212
0
{
213
0
  mReadyState = TCPReadyState::Connecting;
214
0
  mTransport = aTransport;
215
0
216
0
  MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content);
217
0
218
0
  nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
219
0
  mTransport->SetEventSink(this, mainTarget);
220
0
221
0
  nsresult rv = CreateStream();
222
0
  NS_ENSURE_SUCCESS(rv, rv);
223
0
224
0
  return NS_OK;
225
0
}
226
227
nsresult
228
TCPSocket::Init()
229
0
{
230
0
  nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
231
0
  if (obs) {
232
0
    mObserversActive = true;
233
0
    obs->AddObserver(this, "inner-window-destroyed", true); // weak reference
234
0
    obs->AddObserver(this, "profile-change-net-teardown", true); // weak ref
235
0
  }
236
0
237
0
  if (XRE_GetProcessType() == GeckoProcessType_Content) {
238
0
    mReadyState = TCPReadyState::Connecting;
239
0
240
0
    nsCOMPtr<nsIEventTarget> target;
241
0
    if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
242
0
      target = global->EventTargetFor(TaskCategory::Other);
243
0
    }
244
0
    mSocketBridgeChild = new TCPSocketChild(mHost, mPort, target);
245
0
    mSocketBridgeChild->SendOpen(this, mSsl, mUseArrayBuffers);
246
0
    return NS_OK;
247
0
  }
248
0
249
0
  nsCOMPtr<nsISocketTransportService> sts =
250
0
    do_GetService("@mozilla.org/network/socket-transport-service;1");
251
0
252
0
  const char* socketTypes[1];
253
0
  if (mSsl) {
254
0
    socketTypes[0] = "ssl";
255
0
  } else {
256
0
    socketTypes[0] = "starttls";
257
0
  }
258
0
  nsCOMPtr<nsISocketTransport> transport;
259
0
  nsresult rv = sts->CreateTransport(socketTypes, 1, NS_ConvertUTF16toUTF8(mHost), mPort,
260
0
                                     nullptr, getter_AddRefs(transport));
261
0
  NS_ENSURE_SUCCESS(rv, rv);
262
0
263
0
  return InitWithUnconnectedTransport(transport);
264
0
}
265
266
void
267
TCPSocket::InitWithSocketChild(TCPSocketChild* aSocketBridge)
268
0
{
269
0
  mSocketBridgeChild = aSocketBridge;
270
0
  mReadyState = TCPReadyState::Open;
271
0
  mSocketBridgeChild->SetSocket(this);
272
0
  mSocketBridgeChild->GetHost(mHost);
273
0
  mSocketBridgeChild->GetPort(&mPort);
274
0
}
275
276
nsresult
277
TCPSocket::InitWithTransport(nsISocketTransport* aTransport)
278
0
{
279
0
  mTransport = aTransport;
280
0
  nsresult rv = CreateStream();
281
0
  NS_ENSURE_SUCCESS(rv, rv);
282
0
283
0
  mReadyState = TCPReadyState::Open;
284
0
  rv = CreateInputStreamPump();
285
0
  NS_ENSURE_SUCCESS(rv, rv);
286
0
287
0
  nsAutoCString host;
288
0
  mTransport->GetHost(host);
289
0
  mHost = NS_ConvertUTF8toUTF16(host);
290
0
  int32_t port;
291
0
  mTransport->GetPort(&port);
292
0
  mPort = port;
293
0
294
0
  return NS_OK;
295
0
}
296
297
void
298
TCPSocket::UpgradeToSecure(mozilla::ErrorResult& aRv)
299
0
{
300
0
  if (mReadyState != TCPReadyState::Open) {
301
0
    aRv.Throw(NS_ERROR_FAILURE);
302
0
    return;
303
0
  }
304
0
305
0
  if (mSsl) {
306
0
    return;
307
0
  }
308
0
309
0
  mSsl = true;
310
0
311
0
  if (mSocketBridgeChild) {
312
0
    mSocketBridgeChild->SendStartTLS();
313
0
    return;
314
0
  }
315
0
316
0
  if (!mAsyncCopierActive) {
317
0
    ActivateTLS();
318
0
  } else {
319
0
    mWaitingForStartTLS = true;
320
0
  }
321
0
}
322
323
namespace {
324
class CopierCallbacks final : public nsIRequestObserver
325
{
326
  RefPtr<TCPSocket> mOwner;
327
public:
328
0
  explicit CopierCallbacks(TCPSocket* aSocket) : mOwner(aSocket) {}
329
330
  NS_DECL_ISUPPORTS
331
  NS_DECL_NSIREQUESTOBSERVER
332
private:
333
0
  ~CopierCallbacks() {}
334
};
335
336
NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver)
337
338
NS_IMETHODIMP
339
CopierCallbacks::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
340
0
{
341
0
  return NS_OK;
342
0
}
343
344
NS_IMETHODIMP
345
CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
346
0
{
347
0
  mOwner->NotifyCopyComplete(aStatus);
348
0
  mOwner = nullptr;
349
0
  return NS_OK;
350
0
}
351
} // unnamed namespace
352
353
nsresult
354
TCPSocket::EnsureCopying()
355
0
{
356
0
  if (mAsyncCopierActive) {
357
0
    return NS_OK;
358
0
  }
359
0
360
0
  mAsyncCopierActive = true;
361
0
362
0
  nsresult rv;
363
0
364
0
  nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
365
0
    do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
366
0
  NS_ENSURE_SUCCESS(rv, rv);
367
0
368
0
  nsCOMPtr<nsIInputStream> stream = do_QueryInterface(multiplexStream);
369
0
370
0
  while (!mPendingData.IsEmpty()) {
371
0
    nsCOMPtr<nsIInputStream> stream = mPendingData[0];
372
0
    multiplexStream->AppendStream(stream);
373
0
    mPendingData.RemoveElementAt(0);
374
0
  }
375
0
376
0
  nsCOMPtr<nsIAsyncStreamCopier> copier =
377
0
    do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
378
0
  NS_ENSURE_SUCCESS(rv, rv);
379
0
380
0
  nsCOMPtr<nsISocketTransportService> sts =
381
0
      do_GetService("@mozilla.org/network/socket-transport-service;1");
382
0
383
0
  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
384
0
  rv = copier->Init(stream,
385
0
                    mSocketOutputStream,
386
0
                    target,
387
0
                    true, /* source buffered */
388
0
                    false, /* sink buffered */
389
0
                    BUFFER_SIZE,
390
0
                    false, /* close source */
391
0
                    false); /* close sink */
392
0
  NS_ENSURE_SUCCESS(rv, rv);
393
0
394
0
  RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
395
0
  rv = copier->AsyncCopy(callbacks, nullptr);
396
0
  NS_ENSURE_SUCCESS(rv, rv);
397
0
398
0
  return NS_OK;
399
0
}
400
401
void
402
TCPSocket::NotifyCopyComplete(nsresult aStatus)
403
0
{
404
0
  mAsyncCopierActive = false;
405
0
406
0
  // Let's update the buffered amount of data.
407
0
  uint64_t bufferedAmount = 0;
408
0
  for (uint32_t i = 0, len = mPendingData.Length(); i < len; ++i) {
409
0
    nsCOMPtr<nsIInputStream> stream = mPendingData[i];
410
0
    uint64_t available = 0;
411
0
    if (NS_SUCCEEDED(stream->Available(&available))) {
412
0
      bufferedAmount += available;
413
0
    }
414
0
  }
415
0
  mBufferedAmount = bufferedAmount;
416
0
417
0
  if (mSocketBridgeParent) {
418
0
    mozilla::Unused << mSocketBridgeParent->SendUpdateBufferedAmount(BufferedAmount(),
419
0
                                                                     mTrackingNumber);
420
0
  }
421
0
422
0
  if (NS_FAILED(aStatus)) {
423
0
    MaybeReportErrorAndCloseIfOpen(aStatus);
424
0
    return;
425
0
  }
426
0
427
0
  if (bufferedAmount != 0) {
428
0
    EnsureCopying();
429
0
    return;
430
0
  }
431
0
432
0
  // Maybe we have some empty stream. We want to have an empty queue now.
433
0
  mPendingData.Clear();
434
0
435
0
  // If we are waiting for initiating starttls, we can begin to
436
0
  // activate tls now.
437
0
  if (mWaitingForStartTLS && mReadyState == TCPReadyState::Open) {
438
0
    ActivateTLS();
439
0
    mWaitingForStartTLS = false;
440
0
    // If we have pending data, we should send them, or fire
441
0
    // a drain event if we are waiting for it.
442
0
    if (!mPendingDataAfterStartTLS.IsEmpty()) {
443
0
      mPendingData.SwapElements(mPendingDataAfterStartTLS);
444
0
      EnsureCopying();
445
0
      return;
446
0
    }
447
0
  }
448
0
449
0
  // If we have a connected child, we let the child decide whether
450
0
  // ondrain should be dispatched.
451
0
  if (mWaitingForDrain && !mSocketBridgeParent) {
452
0
    mWaitingForDrain = false;
453
0
    FireEvent(NS_LITERAL_STRING("drain"));
454
0
  }
455
0
456
0
  if (mReadyState == TCPReadyState::Closing) {
457
0
    if (mSocketOutputStream) {
458
0
      mSocketOutputStream->Close();
459
0
      mSocketOutputStream = nullptr;
460
0
    }
461
0
    mReadyState = TCPReadyState::Closed;
462
0
    FireEvent(NS_LITERAL_STRING("close"));
463
0
  }
464
0
}
465
466
void
467
TCPSocket::ActivateTLS()
468
0
{
469
0
  nsCOMPtr<nsISupports> securityInfo;
470
0
  mTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
471
0
  nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(securityInfo);
472
0
  if (socketControl) {
473
0
    socketControl->StartTLS();
474
0
  }
475
0
}
476
477
NS_IMETHODIMP
478
TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType)
479
0
{
480
0
  if (mSocketBridgeParent) {
481
0
    mSocketBridgeParent->FireErrorEvent(aName, aType, mReadyState);
482
0
    return NS_OK;
483
0
  }
484
0
485
0
  TCPSocketErrorEventInit init;
486
0
  init.mBubbles = false;
487
0
  init.mCancelable = false;
488
0
  init.mName = aName;
489
0
  init.mMessage = aType;
490
0
491
0
  RefPtr<TCPSocketErrorEvent> event =
492
0
    TCPSocketErrorEvent::Constructor(this, NS_LITERAL_STRING("error"), init);
493
0
  MOZ_ASSERT(event);
494
0
  event->SetTrusted(true);
495
0
  DispatchEvent(*event);
496
0
  return NS_OK;
497
0
}
498
499
NS_IMETHODIMP
500
TCPSocket::FireEvent(const nsAString& aType)
501
0
{
502
0
  if (mSocketBridgeParent) {
503
0
    mSocketBridgeParent->FireEvent(aType, mReadyState);
504
0
    return NS_OK;
505
0
  }
506
0
507
0
  AutoJSAPI api;
508
0
  if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
509
0
    return NS_ERROR_FAILURE;
510
0
  }
511
0
  JS::Rooted<JS::Value> val(api.cx());
512
0
  return FireDataEvent(api.cx(), aType, val);
513
0
}
514
515
NS_IMETHODIMP
516
TCPSocket::FireDataArrayEvent(const nsAString& aType,
517
                              const InfallibleTArray<uint8_t>& buffer)
518
0
{
519
0
  AutoJSAPI api;
520
0
  if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
521
0
    return NS_ERROR_FAILURE;
522
0
  }
523
0
  JSContext* cx = api.cx();
524
0
  JS::Rooted<JS::Value> val(cx);
525
0
526
0
  bool ok = IPC::DeserializeArrayBuffer(cx, buffer, &val);
527
0
  if (ok) {
528
0
    return FireDataEvent(cx, aType, val);
529
0
  }
530
0
  return NS_ERROR_FAILURE;
531
0
}
532
533
NS_IMETHODIMP
534
TCPSocket::FireDataStringEvent(const nsAString& aType,
535
                               const nsACString& aString)
536
0
{
537
0
  AutoJSAPI api;
538
0
  if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
539
0
    return NS_ERROR_FAILURE;
540
0
  }
541
0
  JSContext* cx = api.cx();
542
0
  JS::Rooted<JS::Value> val(cx);
543
0
544
0
  bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(aString), &val);
545
0
  if (ok) {
546
0
    return FireDataEvent(cx, aType, val);
547
0
  }
548
0
  return NS_ERROR_FAILURE;
549
0
}
550
551
nsresult
552
TCPSocket::FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::Value> aData)
553
0
{
554
0
  MOZ_ASSERT(!mSocketBridgeParent);
555
0
556
0
  RootedDictionary<TCPSocketEventInit> init(aCx);
557
0
  init.mBubbles = false;
558
0
  init.mCancelable = false;
559
0
  init.mData = aData;
560
0
561
0
  RefPtr<TCPSocketEvent> event =
562
0
    TCPSocketEvent::Constructor(this, aType, init);
563
0
  event->SetTrusted(true);
564
0
  DispatchEvent(*event);
565
0
  return NS_OK;
566
0
}
567
568
JSObject*
569
TCPSocket::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
570
0
{
571
0
  return TCPSocket_Binding::Wrap(aCx, this, aGivenProto);
572
0
}
573
574
void
575
TCPSocket::GetHost(nsAString& aHost)
576
0
{
577
0
  aHost.Assign(mHost);
578
0
}
579
580
uint32_t
581
TCPSocket::Port()
582
0
{
583
0
  return mPort;
584
0
}
585
586
bool
587
TCPSocket::Ssl()
588
0
{
589
0
  return mSsl;
590
0
}
591
592
void
593
TCPSocket::Suspend()
594
0
{
595
0
  if (mSocketBridgeChild) {
596
0
    mSocketBridgeChild->SendSuspend();
597
0
    return;
598
0
  }
599
0
  if (mInputStreamPump) {
600
0
    mInputStreamPump->Suspend();
601
0
  }
602
0
  mSuspendCount++;
603
0
}
604
605
void
606
TCPSocket::Resume(mozilla::ErrorResult& aRv)
607
0
{
608
0
  if (mSocketBridgeChild) {
609
0
    mSocketBridgeChild->SendResume();
610
0
    return;
611
0
  }
612
0
  if (!mSuspendCount) {
613
0
    aRv.Throw(NS_ERROR_FAILURE);
614
0
    return;
615
0
  }
616
0
617
0
  if (mInputStreamPump) {
618
0
    mInputStreamPump->Resume();
619
0
  }
620
0
  mSuspendCount--;
621
0
}
622
623
nsresult
624
0
TCPSocket::MaybeReportErrorAndCloseIfOpen(nsresult status) {
625
0
  // If we're closed, we've already reported the error or just don't need to
626
0
  // report the error.
627
0
  if (mReadyState == TCPReadyState::Closed) {
628
0
    return NS_OK;
629
0
  }
630
0
631
0
  // go through ::Closing state and then mark ::Closed
632
0
  Close();
633
0
  mReadyState = TCPReadyState::Closed;
634
0
635
0
  if (NS_FAILED(status)) {
636
0
    // Convert the status code to an appropriate error message.
637
0
638
0
    nsString errorType, errName;
639
0
640
0
    // security module? (and this is an error)
641
0
    if ((static_cast<uint32_t>(status) & 0xFF0000) == 0x5a0000) {
642
0
      nsCOMPtr<nsINSSErrorsService> errSvc = do_GetService("@mozilla.org/nss_errors_service;1");
643
0
      // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is
644
0
      // somehow not in the set of covered errors.
645
0
      uint32_t errorClass;
646
0
      nsresult rv = errSvc->GetErrorClass(status, &errorClass);
647
0
      if (NS_FAILED(rv)) {
648
0
        errorType.AssignLiteral("SecurityProtocol");
649
0
      } else {
650
0
        switch (errorClass) {
651
0
          case nsINSSErrorsService::ERROR_CLASS_BAD_CERT:
652
0
            errorType.AssignLiteral("SecurityCertificate");
653
0
            break;
654
0
          default:
655
0
            errorType.AssignLiteral("SecurityProtocol");
656
0
            break;
657
0
        }
658
0
      }
659
0
660
0
      // NSS_SEC errors (happen below the base value because of negative vals)
661
0
      if ((static_cast<int32_t>(status) & 0xFFFF) < abs(nsINSSErrorsService::NSS_SEC_ERROR_BASE)) {
662
0
        switch (static_cast<SECErrorCodes>(status)) {
663
0
          case SEC_ERROR_EXPIRED_CERTIFICATE:
664
0
            errName.AssignLiteral("SecurityExpiredCertificateError");
665
0
            break;
666
0
          case SEC_ERROR_REVOKED_CERTIFICATE:
667
0
            errName.AssignLiteral("SecurityRevokedCertificateError");
668
0
            break;
669
0
            // per bsmith, we will be unable to tell these errors apart very soon,
670
0
            // so it makes sense to just folder them all together already.
671
0
          case SEC_ERROR_UNKNOWN_ISSUER:
672
0
          case SEC_ERROR_UNTRUSTED_ISSUER:
673
0
          case SEC_ERROR_UNTRUSTED_CERT:
674
0
          case SEC_ERROR_CA_CERT_INVALID:
675
0
            errName.AssignLiteral("SecurityUntrustedCertificateIssuerError");
676
0
            break;
677
0
          case SEC_ERROR_INADEQUATE_KEY_USAGE:
678
0
            errName.AssignLiteral("SecurityInadequateKeyUsageError");
679
0
            break;
680
0
          case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
681
0
            errName.AssignLiteral("SecurityCertificateSignatureAlgorithmDisabledError");
682
0
            break;
683
0
          default:
684
0
            errName.AssignLiteral("SecurityError");
685
0
            break;
686
0
        }
687
0
      } else {
688
0
        // NSS_SSL errors
689
0
        switch (static_cast<SSLErrorCodes>(status)) {
690
0
          case SSL_ERROR_NO_CERTIFICATE:
691
0
            errName.AssignLiteral("SecurityNoCertificateError");
692
0
            break;
693
0
          case SSL_ERROR_BAD_CERTIFICATE:
694
0
            errName.AssignLiteral("SecurityBadCertificateError");
695
0
            break;
696
0
          case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:
697
0
            errName.AssignLiteral("SecurityUnsupportedCertificateTypeError");
698
0
            break;
699
0
          case SSL_ERROR_UNSUPPORTED_VERSION:
700
0
            errName.AssignLiteral("SecurityUnsupportedTLSVersionError");
701
0
            break;
702
0
          case SSL_ERROR_BAD_CERT_DOMAIN:
703
0
            errName.AssignLiteral("SecurityCertificateDomainMismatchError");
704
0
            break;
705
0
          default:
706
0
            errName.AssignLiteral("SecurityError");
707
0
            break;
708
0
        }
709
0
      }
710
0
    } else {
711
0
      // must be network
712
0
      errorType.AssignLiteral("Network");
713
0
714
0
      switch (status) {
715
0
        // connect to host:port failed
716
0
        case NS_ERROR_CONNECTION_REFUSED:
717
0
          errName.AssignLiteral("ConnectionRefusedError");
718
0
          break;
719
0
          // network timeout error
720
0
        case NS_ERROR_NET_TIMEOUT:
721
0
          errName.AssignLiteral("NetworkTimeoutError");
722
0
          break;
723
0
          // hostname lookup failed
724
0
        case NS_ERROR_UNKNOWN_HOST:
725
0
          errName.AssignLiteral("DomainNotFoundError");
726
0
          break;
727
0
        case NS_ERROR_NET_INTERRUPT:
728
0
          errName.AssignLiteral("NetworkInterruptError");
729
0
          break;
730
0
        default:
731
0
          errName.AssignLiteral("NetworkError");
732
0
          break;
733
0
      }
734
0
    }
735
0
736
0
    Unused << NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType)));
737
0
  }
738
0
739
0
  return FireEvent(NS_LITERAL_STRING("close"));
740
0
}
741
742
void
743
TCPSocket::Close()
744
0
{
745
0
  CloseHelper(true);
746
0
}
747
748
void
749
TCPSocket::CloseImmediately()
750
0
{
751
0
  CloseHelper(false);
752
0
}
753
754
void
755
TCPSocket::CloseHelper(bool waitForUnsentData)
756
0
{
757
0
  if (mReadyState == TCPReadyState::Closed || mReadyState == TCPReadyState::Closing) {
758
0
    return;
759
0
  }
760
0
761
0
  mReadyState = TCPReadyState::Closing;
762
0
763
0
  if (mSocketBridgeChild) {
764
0
    mSocketBridgeChild->SendClose();
765
0
    return;
766
0
  }
767
0
768
0
  if (!mAsyncCopierActive || !waitForUnsentData) {
769
0
770
0
    mPendingData.Clear();
771
0
    mPendingDataAfterStartTLS.Clear();
772
0
773
0
    if (mSocketOutputStream) {
774
0
      mSocketOutputStream->Close();
775
0
      mSocketOutputStream = nullptr;
776
0
    }
777
0
  }
778
0
779
0
  if (mSocketInputStream) {
780
0
    mSocketInputStream->Close();
781
0
    mSocketInputStream = nullptr;
782
0
  }
783
0
}
784
785
void
786
TCPSocket::SendWithTrackingNumber(const nsACString& aData,
787
                                  const uint32_t& aTrackingNumber,
788
                                  mozilla::ErrorResult& aRv)
789
0
{
790
0
  MOZ_ASSERT(mSocketBridgeParent);
791
0
  mTrackingNumber = aTrackingNumber;
792
0
  // The JSContext isn't necessary for string values; it's a codegen limitation.
793
0
  Send(nullptr, aData, aRv);
794
0
}
795
796
bool
797
TCPSocket::Send(JSContext* aCx, const nsACString& aData, mozilla::ErrorResult& aRv)
798
0
{
799
0
  if (mReadyState != TCPReadyState::Open) {
800
0
    aRv.Throw(NS_ERROR_FAILURE);
801
0
    return false;
802
0
  }
803
0
804
0
  uint64_t byteLength;
805
0
  nsCOMPtr<nsIInputStream> stream;
806
0
  if (mSocketBridgeChild) {
807
0
    mSocketBridgeChild->SendSend(aData, ++mTrackingNumber);
808
0
    byteLength = aData.Length();
809
0
  } else {
810
0
    nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), aData);
811
0
    if (NS_FAILED(rv)) {
812
0
      aRv.Throw(rv);
813
0
      return false;
814
0
    }
815
0
    rv = stream->Available(&byteLength);
816
0
    if (NS_FAILED(rv)) {
817
0
      aRv.Throw(rv);
818
0
      return false;
819
0
    }
820
0
  }
821
0
  return Send(stream, byteLength);
822
0
}
823
824
void
825
TCPSocket::SendWithTrackingNumber(JSContext* aCx,
826
                                  const ArrayBuffer& aData,
827
                                  uint32_t aByteOffset,
828
                                  const Optional<uint32_t>& aByteLength,
829
                                  const uint32_t& aTrackingNumber,
830
                                  mozilla::ErrorResult& aRv)
831
0
{
832
0
  MOZ_ASSERT(mSocketBridgeParent);
833
0
  mTrackingNumber = aTrackingNumber;
834
0
  Send(aCx, aData, aByteOffset, aByteLength, aRv);
835
0
}
836
837
bool
838
TCPSocket::Send(JSContext* aCx,
839
                const ArrayBuffer& aData,
840
                uint32_t aByteOffset,
841
                const Optional<uint32_t>& aByteLength,
842
                mozilla::ErrorResult& aRv)
843
0
{
844
0
  if (mReadyState != TCPReadyState::Open) {
845
0
    aRv.Throw(NS_ERROR_FAILURE);
846
0
    return false;
847
0
  }
848
0
849
0
  nsCOMPtr<nsIArrayBufferInputStream> stream;
850
0
851
0
  aData.ComputeLengthAndData();
852
0
  uint32_t byteLength = aByteLength.WasPassed() ? aByteLength.Value() : aData.Length();
853
0
854
0
  if (mSocketBridgeChild) {
855
0
    nsresult rv = mSocketBridgeChild->SendSend(aData, aByteOffset, byteLength, ++mTrackingNumber);
856
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
857
0
      aRv.Throw(rv);
858
0
      return false;
859
0
    }
860
0
  } else {
861
0
    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aData.Obj()));
862
0
863
0
    stream = do_CreateInstance("@mozilla.org/io/arraybuffer-input-stream;1");
864
0
    nsresult rv = stream->SetData(value, aByteOffset, byteLength);
865
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
866
0
      aRv.Throw(rv);
867
0
      return false;
868
0
    }
869
0
  }
870
0
  return Send(stream, byteLength);
871
0
}
872
873
bool
874
TCPSocket::Send(nsIInputStream* aStream, uint32_t aByteLength)
875
0
{
876
0
  uint64_t newBufferedAmount = BufferedAmount() + aByteLength;
877
0
  bool bufferFull = newBufferedAmount > BUFFER_SIZE;
878
0
  if (bufferFull) {
879
0
    // If we buffered more than some arbitrary amount of data,
880
0
    // (65535 right now) we should tell the caller so they can
881
0
    // wait until ondrain is called if they so desire. Once all the
882
0
    // buffered data has been written to the socket, ondrain is
883
0
    // called.
884
0
    mWaitingForDrain = true;
885
0
  }
886
0
887
0
  if (mSocketBridgeChild) {
888
0
    // In the child, we just add the buffer length to our bufferedAmount and let
889
0
    // the parent update our bufferedAmount when the data have been sent.
890
0
    mBufferedAmount = newBufferedAmount;
891
0
    return !bufferFull;
892
0
  }
893
0
894
0
  if (mWaitingForStartTLS) {
895
0
    // When we are waiting for starttls, newStream is added to pendingData
896
0
    // and will be appended to multiplexStream after tls had been set up.
897
0
    mPendingDataAfterStartTLS.AppendElement(aStream);
898
0
  } else {
899
0
    mPendingData.AppendElement(aStream);
900
0
  }
901
0
902
0
  EnsureCopying();
903
0
904
0
  return !bufferFull;
905
0
}
906
907
TCPReadyState
908
TCPSocket::ReadyState()
909
0
{
910
0
  return mReadyState;
911
0
}
912
913
TCPSocketBinaryType
914
TCPSocket::BinaryType()
915
0
{
916
0
  if (mUseArrayBuffers) {
917
0
    return TCPSocketBinaryType::Arraybuffer;
918
0
  } else {
919
0
    return TCPSocketBinaryType::String;
920
0
  }
921
0
}
922
923
already_AddRefed<TCPSocket>
924
TCPSocket::CreateAcceptedSocket(nsIGlobalObject* aGlobal,
925
                                nsISocketTransport* aTransport,
926
                                bool aUseArrayBuffers)
927
0
{
928
0
  RefPtr<TCPSocket> socket = new TCPSocket(aGlobal, EmptyString(), 0, false, aUseArrayBuffers);
929
0
  nsresult rv = socket->InitWithTransport(aTransport);
930
0
  NS_ENSURE_SUCCESS(rv, nullptr);
931
0
  return socket.forget();
932
0
}
933
934
already_AddRefed<TCPSocket>
935
TCPSocket::CreateAcceptedSocket(nsIGlobalObject* aGlobal,
936
                                TCPSocketChild* aBridge,
937
                                bool aUseArrayBuffers)
938
0
{
939
0
  RefPtr<TCPSocket> socket = new TCPSocket(aGlobal, EmptyString(), 0, false, aUseArrayBuffers);
940
0
  socket->InitWithSocketChild(aBridge);
941
0
  return socket.forget();
942
0
}
943
944
already_AddRefed<TCPSocket>
945
TCPSocket::Constructor(const GlobalObject& aGlobal,
946
                       const nsAString& aHost,
947
                       uint16_t aPort,
948
                       const SocketOptions& aOptions,
949
                       mozilla::ErrorResult& aRv)
950
0
{
951
0
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
952
0
  RefPtr<TCPSocket> socket =
953
0
    new TCPSocket(global, aHost, aPort, aOptions.mUseSecureTransport,
954
0
                  aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer);
955
0
  nsresult rv = socket->Init();
956
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
957
0
    aRv.Throw(rv);
958
0
    return nullptr;
959
0
  }
960
0
961
0
  return socket.forget();
962
0
}
963
964
nsresult
965
TCPSocket::CreateInputStreamPump()
966
0
{
967
0
  if (!mSocketInputStream) {
968
0
    return NS_ERROR_NOT_AVAILABLE;
969
0
  }
970
0
  nsresult rv;
971
0
  mInputStreamPump = do_CreateInstance("@mozilla.org/network/input-stream-pump;1", &rv);
972
0
  NS_ENSURE_SUCCESS(rv, rv);
973
0
974
0
  rv = mInputStreamPump->Init(mSocketInputStream, 0, 0, false, nullptr);
975
0
  NS_ENSURE_SUCCESS(rv, rv);
976
0
977
0
  uint64_t suspendCount = mSuspendCount;
978
0
  while (suspendCount--) {
979
0
    mInputStreamPump->Suspend();
980
0
  }
981
0
982
0
  rv = mInputStreamPump->AsyncRead(this, nullptr);
983
0
  NS_ENSURE_SUCCESS(rv, rv);
984
0
  return NS_OK;
985
0
}
986
987
NS_IMETHODIMP
988
TCPSocket::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
989
                             int64_t aProgress, int64_t aProgressMax)
990
0
{
991
0
  if (static_cast<uint32_t>(aStatus) != nsISocketTransport::STATUS_CONNECTED_TO) {
992
0
    return NS_OK;
993
0
  }
994
0
995
0
  mReadyState = TCPReadyState::Open;
996
0
  nsresult rv = CreateInputStreamPump();
997
0
  NS_ENSURE_SUCCESS(rv, rv);
998
0
  FireEvent(NS_LITERAL_STRING("open"));
999
0
1000
0
  return NS_OK;
1001
0
}
1002
1003
NS_IMETHODIMP
1004
TCPSocket::OnInputStreamReady(nsIAsyncInputStream* aStream)
1005
0
{
1006
0
  // Only used for detecting if the connection was refused.
1007
0
1008
0
  uint64_t dummy;
1009
0
  nsresult rv = aStream->Available(&dummy);
1010
0
  if (NS_FAILED(rv)) {
1011
0
    MaybeReportErrorAndCloseIfOpen(NS_ERROR_CONNECTION_REFUSED);
1012
0
  }
1013
0
  return NS_OK;
1014
0
}
1015
1016
NS_IMETHODIMP
1017
TCPSocket::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
1018
0
{
1019
0
  return NS_OK;
1020
0
}
1021
1022
NS_IMETHODIMP
1023
TCPSocket::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
1024
                           uint64_t aOffset, uint32_t aCount)
1025
0
{
1026
0
  if (mUseArrayBuffers) {
1027
0
    nsTArray<uint8_t> buffer;
1028
0
    buffer.SetCapacity(aCount);
1029
0
    uint32_t actual;
1030
0
    nsresult rv = aStream->Read(reinterpret_cast<char*>(buffer.Elements()), aCount, &actual);
1031
0
    NS_ENSURE_SUCCESS(rv, rv);
1032
0
    MOZ_ASSERT(actual == aCount);
1033
0
    buffer.SetLength(actual);
1034
0
1035
0
    if (mSocketBridgeParent) {
1036
0
      mSocketBridgeParent->FireArrayBufferDataEvent(buffer, mReadyState);
1037
0
      return NS_OK;
1038
0
    }
1039
0
1040
0
    AutoJSAPI api;
1041
0
    if (!api.Init(GetOwnerGlobal())) {
1042
0
      return NS_ERROR_FAILURE;
1043
0
    }
1044
0
    JSContext* cx = api.cx();
1045
0
1046
0
    JS::Rooted<JS::Value> value(cx);
1047
0
    if (!ToJSValue(cx, TypedArrayCreator<ArrayBuffer>(buffer), &value)) {
1048
0
      return NS_ERROR_FAILURE;
1049
0
    }
1050
0
    FireDataEvent(cx, NS_LITERAL_STRING("data"), value);
1051
0
    return NS_OK;
1052
0
  }
1053
0
1054
0
  nsCString data;
1055
0
  nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
1056
0
  NS_ENSURE_SUCCESS(rv, rv);
1057
0
1058
0
  if (mSocketBridgeParent) {
1059
0
    mSocketBridgeParent->FireStringDataEvent(data, mReadyState);
1060
0
    return NS_OK;
1061
0
  }
1062
0
1063
0
  AutoJSAPI api;
1064
0
  if (!api.Init(GetOwnerGlobal())) {
1065
0
    return NS_ERROR_FAILURE;
1066
0
  }
1067
0
  JSContext* cx = api.cx();
1068
0
1069
0
  JS::Rooted<JS::Value> value(cx);
1070
0
  if (!ToJSValue(cx, NS_ConvertASCIItoUTF16(data), &value)) {
1071
0
    return NS_ERROR_FAILURE;
1072
0
  }
1073
0
  FireDataEvent(cx, NS_LITERAL_STRING("data"), value);
1074
0
1075
0
  return NS_OK;
1076
0
}
1077
1078
NS_IMETHODIMP
1079
TCPSocket::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
1080
0
{
1081
0
  mInputStreamPump = nullptr;
1082
0
1083
0
  if (mAsyncCopierActive && NS_SUCCEEDED(aStatus)) {
1084
0
    // If we have some buffered output still, and status is not an
1085
0
    // error, the other side has done a half-close, but we don't
1086
0
    // want to be in the close state until we are done sending
1087
0
    // everything that was buffered. We also don't want to call onclose
1088
0
    // yet.
1089
0
    return NS_OK;
1090
0
  }
1091
0
1092
0
  // We call this even if there is no error.
1093
0
  MaybeReportErrorAndCloseIfOpen(aStatus);
1094
0
  return NS_OK;
1095
0
}
1096
1097
void
1098
TCPSocket::SetSocketBridgeParent(TCPSocketParent* aBridgeParent)
1099
0
{
1100
0
  mSocketBridgeParent = aBridgeParent;
1101
0
}
1102
1103
NS_IMETHODIMP
1104
TCPSocket::UpdateReadyState(uint32_t aReadyState)
1105
0
{
1106
0
  MOZ_ASSERT(mSocketBridgeChild);
1107
0
  mReadyState = static_cast<TCPReadyState>(aReadyState);
1108
0
  return NS_OK;
1109
0
}
1110
1111
NS_IMETHODIMP
1112
TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount, uint32_t aTrackingNumber)
1113
0
{
1114
0
  if (aTrackingNumber != mTrackingNumber) {
1115
0
    return NS_OK;
1116
0
  }
1117
0
  mBufferedAmount = aBufferedAmount;
1118
0
  if (!mBufferedAmount) {
1119
0
    if (mWaitingForDrain) {
1120
0
      mWaitingForDrain = false;
1121
0
      return FireEvent(NS_LITERAL_STRING("drain"));
1122
0
    }
1123
0
  }
1124
0
  return NS_OK;
1125
0
}
1126
1127
NS_IMETHODIMP
1128
TCPSocket::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
1129
0
{
1130
0
  if (!strcmp(aTopic, "inner-window-destroyed")) {
1131
0
    nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
1132
0
    NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
1133
0
    uint64_t innerID;
1134
0
    nsresult rv = wrapper->GetData(&innerID);
1135
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1136
0
      return rv;
1137
0
    }
1138
0
1139
0
    if (innerID == mInnerWindowID) {
1140
0
      Close();
1141
0
    }
1142
0
  } else if (!strcmp(aTopic, "profile-change-net-teardown")) {
1143
0
    Close();
1144
0
  }
1145
0
1146
0
  return NS_OK;
1147
0
}
1148
1149
/* static */
1150
bool
1151
TCPSocket::ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal)
1152
0
{
1153
0
  JS::Rooted<JSObject*> global(aCx, aGlobal);
1154
0
  return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
1155
0
}