tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TCPSocket.cpp (35655B)


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