tor-browser

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

WebSocketChannelChild.cpp (21324B)


      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 /* 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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "WebSocketLog.h"
      8 #include "mozilla/dom/BrowserChild.h"
      9 #include "mozilla/net/NeckoChild.h"
     10 #include "WebSocketChannelChild.h"
     11 #include "nsContentUtils.h"
     12 #include "nsIBrowserChild.h"
     13 #include "nsNetUtil.h"
     14 #include "mozilla/ipc/IPCStreamUtils.h"
     15 #include "mozilla/ipc/URIUtils.h"
     16 #include "mozilla/ipc/BackgroundUtils.h"
     17 #include "mozilla/net/ChannelEventQueue.h"
     18 #include "SerializedLoadContext.h"
     19 #include "mozilla/dom/ContentChild.h"
     20 #include "nsITransportProvider.h"
     21 
     22 using namespace mozilla::ipc;
     23 using mozilla::dom::ContentChild;
     24 
     25 namespace mozilla {
     26 namespace net {
     27 
     28 NS_IMPL_ADDREF(WebSocketChannelChild)
     29 
     30 NS_IMETHODIMP_(MozExternalRefCountType) WebSocketChannelChild::Release() {
     31  MOZ_ASSERT(0 != mRefCnt, "dup release");
     32  --mRefCnt;
     33  NS_LOG_RELEASE(this, mRefCnt, "WebSocketChannelChild");
     34 
     35  if (mRefCnt == 1) {
     36    MaybeReleaseIPCObject();
     37    return mRefCnt;
     38  }
     39 
     40  if (mRefCnt == 0) {
     41    mRefCnt = 1; /* stabilize */
     42    delete this;
     43    return 0;
     44  }
     45  return mRefCnt;
     46 }
     47 
     48 NS_INTERFACE_MAP_BEGIN(WebSocketChannelChild)
     49  NS_INTERFACE_MAP_ENTRY(nsIWebSocketChannel)
     50  NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler)
     51  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketChannel)
     52  NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
     53 NS_INTERFACE_MAP_END
     54 
     55 WebSocketChannelChild::WebSocketChannelChild(bool aEncrypted)
     56    : NeckoTargetHolder(nullptr),
     57      mIPCState(Closed),
     58      mMutex("WebSocketChannelChild::mMutex") {
     59  MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     60 
     61  LOG(("WebSocketChannelChild::WebSocketChannelChild() %p\n", this));
     62  mEncrypted = aEncrypted;
     63  mEventQ = new ChannelEventQueue(static_cast<nsIWebSocketChannel*>(this));
     64 }
     65 
     66 WebSocketChannelChild::~WebSocketChannelChild() {
     67  LOG(("WebSocketChannelChild::~WebSocketChannelChild() %p\n", this));
     68  mEventQ->NotifyReleasingOwner();
     69 }
     70 
     71 void WebSocketChannelChild::AddIPDLReference() {
     72  MOZ_ASSERT(NS_IsMainThread());
     73 
     74  {
     75    MutexAutoLock lock(mMutex);
     76    MOZ_ASSERT(mIPCState == Closed,
     77               "Attempt to retain more than one IPDL reference");
     78    mIPCState = Opened;
     79  }
     80 
     81  AddRef();
     82 }
     83 
     84 void WebSocketChannelChild::ReleaseIPDLReference() {
     85  MOZ_ASSERT(NS_IsMainThread());
     86 
     87  {
     88    MutexAutoLock lock(mMutex);
     89    MOZ_ASSERT(mIPCState != Closed,
     90               "Attempt to release nonexistent IPDL reference");
     91    mIPCState = Closed;
     92  }
     93 
     94  Release();
     95 }
     96 
     97 void WebSocketChannelChild::MaybeReleaseIPCObject() {
     98  {
     99    MutexAutoLock lock(mMutex);
    100    if (mIPCState != Opened) {
    101      return;
    102    }
    103 
    104    mIPCState = Closing;
    105  }
    106 
    107  if (!NS_IsMainThread()) {
    108    nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
    109    MOZ_ALWAYS_SUCCEEDS(target->Dispatch(
    110        NewRunnableMethod("WebSocketChannelChild::MaybeReleaseIPCObject", this,
    111                          &WebSocketChannelChild::MaybeReleaseIPCObject),
    112        NS_DISPATCH_NORMAL));
    113    return;
    114  }
    115 
    116  SendDeleteSelf();
    117 }
    118 
    119 void WebSocketChannelChild::GetEffectiveURL(nsAString& aEffectiveURL) const {
    120  aEffectiveURL = mEffectiveURL;
    121 }
    122 
    123 bool WebSocketChannelChild::IsEncrypted() const { return mEncrypted; }
    124 
    125 class WebSocketEvent {
    126 public:
    127  MOZ_COUNTED_DEFAULT_CTOR(WebSocketEvent)
    128  MOZ_COUNTED_DTOR_VIRTUAL(WebSocketEvent)
    129  virtual void Run(WebSocketChannelChild* aChild) = 0;
    130 };
    131 
    132 class WrappedWebSocketEvent : public Runnable {
    133 public:
    134  WrappedWebSocketEvent(WebSocketChannelChild* aChild,
    135                        UniquePtr<WebSocketEvent>&& aWebSocketEvent)
    136      : Runnable("net::WrappedWebSocketEvent"),
    137        mChild(aChild),
    138        mWebSocketEvent(std::move(aWebSocketEvent)) {
    139    MOZ_RELEASE_ASSERT(!!mWebSocketEvent);
    140  }
    141  NS_IMETHOD Run() override {
    142    mWebSocketEvent->Run(mChild);
    143    return NS_OK;
    144  }
    145 
    146 private:
    147  RefPtr<WebSocketChannelChild> mChild;
    148  UniquePtr<WebSocketEvent> mWebSocketEvent;
    149 };
    150 
    151 class EventTargetDispatcher : public ChannelEvent {
    152 public:
    153  EventTargetDispatcher(WebSocketChannelChild* aChild,
    154                        WebSocketEvent* aWebSocketEvent)
    155      : mChild(aChild),
    156        mWebSocketEvent(aWebSocketEvent),
    157        mEventTarget(mChild->GetTargetThread()) {}
    158 
    159  void Run() override {
    160    if (mEventTarget) {
    161      mEventTarget->Dispatch(
    162          new WrappedWebSocketEvent(mChild, std::move(mWebSocketEvent)),
    163          NS_DISPATCH_NORMAL);
    164      return;
    165    }
    166  }
    167 
    168  already_AddRefed<nsIEventTarget> GetEventTarget() override {
    169    nsCOMPtr<nsIEventTarget> target = mEventTarget;
    170    if (!target) {
    171      target = GetMainThreadSerialEventTarget();
    172    }
    173    return target.forget();
    174  }
    175 
    176 private:
    177  // The lifetime of the child is ensured by ChannelEventQueue.
    178  WebSocketChannelChild* mChild;
    179  UniquePtr<WebSocketEvent> mWebSocketEvent;
    180  nsCOMPtr<nsIEventTarget> mEventTarget;
    181 };
    182 
    183 class StartEvent : public WebSocketEvent {
    184 public:
    185  StartEvent(const nsACString& aProtocol, const nsACString& aExtensions,
    186             const nsAString& aEffectiveURL, bool aEncrypted,
    187             uint64_t aHttpChannelId)
    188      : mProtocol(aProtocol),
    189        mExtensions(aExtensions),
    190        mEffectiveURL(aEffectiveURL),
    191        mEncrypted(aEncrypted),
    192        mHttpChannelId(aHttpChannelId) {}
    193 
    194  void Run(WebSocketChannelChild* aChild) override {
    195    aChild->OnStart(mProtocol, mExtensions, mEffectiveURL, mEncrypted,
    196                    mHttpChannelId);
    197  }
    198 
    199 private:
    200  nsCString mProtocol;
    201  nsCString mExtensions;
    202  nsString mEffectiveURL;
    203  bool mEncrypted;
    204  uint64_t mHttpChannelId;
    205 };
    206 
    207 mozilla::ipc::IPCResult WebSocketChannelChild::RecvOnStart(
    208    const nsACString& aProtocol, const nsACString& aExtensions,
    209    const nsAString& aEffectiveURL, const bool& aEncrypted,
    210    const uint64_t& aHttpChannelId) {
    211  mEventQ->RunOrEnqueue(new EventTargetDispatcher(
    212      this, new StartEvent(aProtocol, aExtensions, aEffectiveURL, aEncrypted,
    213                           aHttpChannelId)));
    214 
    215  return IPC_OK();
    216 }
    217 
    218 void WebSocketChannelChild::OnStart(const nsACString& aProtocol,
    219                                    const nsACString& aExtensions,
    220                                    const nsAString& aEffectiveURL,
    221                                    const bool& aEncrypted,
    222                                    const uint64_t& aHttpChannelId) {
    223  LOG(("WebSocketChannelChild::RecvOnStart() %p\n", this));
    224  SetProtocol(aProtocol);
    225  mNegotiatedExtensions = aExtensions;
    226  mEffectiveURL = aEffectiveURL;
    227  mEncrypted = aEncrypted;
    228  mHttpChannelId = aHttpChannelId;
    229 
    230  if (mListenerMT) {
    231    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    232    nsresult rv = mListenerMT->mListener->OnStart(mListenerMT->mContext);
    233    if (NS_FAILED(rv)) {
    234      LOG(
    235          ("WebSocketChannelChild::OnStart "
    236           "mListenerMT->mListener->OnStart() failed with error 0x%08" PRIx32,
    237           static_cast<uint32_t>(rv)));
    238    }
    239  }
    240 }
    241 
    242 class StopEvent : public WebSocketEvent {
    243 public:
    244  explicit StopEvent(const nsresult& aStatusCode) : mStatusCode(aStatusCode) {}
    245 
    246  void Run(WebSocketChannelChild* aChild) override {
    247    aChild->OnStop(mStatusCode);
    248  }
    249 
    250 private:
    251  nsresult mStatusCode;
    252 };
    253 
    254 mozilla::ipc::IPCResult WebSocketChannelChild::RecvOnStop(
    255    const nsresult& aStatusCode) {
    256  mEventQ->RunOrEnqueue(
    257      new EventTargetDispatcher(this, new StopEvent(aStatusCode)));
    258 
    259  return IPC_OK();
    260 }
    261 
    262 void WebSocketChannelChild::OnStop(const nsresult& aStatusCode) {
    263  LOG(("WebSocketChannelChild::RecvOnStop() %p\n", this));
    264  if (mListenerMT) {
    265    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    266    nsresult rv =
    267        mListenerMT->mListener->OnStop(mListenerMT->mContext, aStatusCode);
    268    if (NS_FAILED(rv)) {
    269      LOG(
    270          ("WebSocketChannel::OnStop "
    271           "mListenerMT->mListener->OnStop() failed with error 0x%08" PRIx32,
    272           static_cast<uint32_t>(rv)));
    273    }
    274  }
    275 }
    276 
    277 class MessageEvent : public WebSocketEvent {
    278 public:
    279  MessageEvent(const nsACString& aMessage, bool aBinary)
    280      : mMessage(aMessage), mBinary(aBinary) {}
    281 
    282  void Run(WebSocketChannelChild* aChild) override {
    283    if (!mBinary) {
    284      aChild->OnMessageAvailable(mMessage);
    285    } else {
    286      aChild->OnBinaryMessageAvailable(mMessage);
    287    }
    288  }
    289 
    290 private:
    291  nsCString mMessage;
    292  bool mBinary;
    293 };
    294 
    295 bool WebSocketChannelChild::RecvOnMessageAvailableInternal(
    296    const nsACString& aMsg, bool aMoreData, bool aBinary) {
    297  if (aMoreData) {
    298    return mReceivedMsgBuffer.Append(aMsg, fallible);
    299  }
    300 
    301  if (!mReceivedMsgBuffer.Append(aMsg, fallible)) {
    302    return false;
    303  }
    304 
    305  mEventQ->RunOrEnqueue(new EventTargetDispatcher(
    306      this, new MessageEvent(mReceivedMsgBuffer, aBinary)));
    307  mReceivedMsgBuffer.Truncate();
    308  return true;
    309 }
    310 
    311 class OnErrorEvent : public WebSocketEvent {
    312 public:
    313  OnErrorEvent() = default;
    314 
    315  void Run(WebSocketChannelChild* aChild) override { aChild->OnError(); }
    316 };
    317 
    318 void WebSocketChannelChild::OnError() {
    319  LOG(("WebSocketChannelChild::OnError() %p", this));
    320  if (mListenerMT) {
    321    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    322    (void)mListenerMT->mListener->OnError();
    323  }
    324 }
    325 
    326 mozilla::ipc::IPCResult WebSocketChannelChild::RecvOnMessageAvailable(
    327    const nsACString& aMsg, const bool& aMoreData) {
    328  if (!RecvOnMessageAvailableInternal(aMsg, aMoreData, false)) {
    329    LOG(("WebSocketChannelChild %p append message failed", this));
    330    mEventQ->RunOrEnqueue(new EventTargetDispatcher(this, new OnErrorEvent()));
    331  }
    332  return IPC_OK();
    333 }
    334 
    335 void WebSocketChannelChild::OnMessageAvailable(const nsACString& aMsg) {
    336  LOG(("WebSocketChannelChild::RecvOnMessageAvailable() %p\n", this));
    337  if (mListenerMT) {
    338    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    339    nsresult rv =
    340        mListenerMT->mListener->OnMessageAvailable(mListenerMT->mContext, aMsg);
    341    if (NS_FAILED(rv)) {
    342      LOG(
    343          ("WebSocketChannelChild::OnMessageAvailable "
    344           "mListenerMT->mListener->OnMessageAvailable() "
    345           "failed with error 0x%08" PRIx32,
    346           static_cast<uint32_t>(rv)));
    347    }
    348  }
    349 }
    350 
    351 mozilla::ipc::IPCResult WebSocketChannelChild::RecvOnBinaryMessageAvailable(
    352    const nsACString& aMsg, const bool& aMoreData) {
    353  if (!RecvOnMessageAvailableInternal(aMsg, aMoreData, true)) {
    354    LOG(("WebSocketChannelChild %p append message failed", this));
    355    mEventQ->RunOrEnqueue(new EventTargetDispatcher(this, new OnErrorEvent()));
    356  }
    357  return IPC_OK();
    358 }
    359 
    360 void WebSocketChannelChild::OnBinaryMessageAvailable(const nsACString& aMsg) {
    361  LOG(("WebSocketChannelChild::RecvOnBinaryMessageAvailable() %p\n", this));
    362  if (mListenerMT) {
    363    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    364    nsresult rv = mListenerMT->mListener->OnBinaryMessageAvailable(
    365        mListenerMT->mContext, aMsg);
    366    if (NS_FAILED(rv)) {
    367      LOG(
    368          ("WebSocketChannelChild::OnBinaryMessageAvailable "
    369           "mListenerMT->mListener->OnBinaryMessageAvailable() "
    370           "failed with error 0x%08" PRIx32,
    371           static_cast<uint32_t>(rv)));
    372    }
    373  }
    374 }
    375 
    376 class AcknowledgeEvent : public WebSocketEvent {
    377 public:
    378  explicit AcknowledgeEvent(const uint32_t& aSize) : mSize(aSize) {}
    379 
    380  void Run(WebSocketChannelChild* aChild) override {
    381    aChild->OnAcknowledge(mSize);
    382  }
    383 
    384 private:
    385  uint32_t mSize;
    386 };
    387 
    388 mozilla::ipc::IPCResult WebSocketChannelChild::RecvOnAcknowledge(
    389    const uint32_t& aSize) {
    390  mEventQ->RunOrEnqueue(
    391      new EventTargetDispatcher(this, new AcknowledgeEvent(aSize)));
    392 
    393  return IPC_OK();
    394 }
    395 
    396 void WebSocketChannelChild::OnAcknowledge(const uint32_t& aSize) {
    397  LOG(("WebSocketChannelChild::RecvOnAcknowledge() %p\n", this));
    398  if (mListenerMT) {
    399    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    400    nsresult rv =
    401        mListenerMT->mListener->OnAcknowledge(mListenerMT->mContext, aSize);
    402    if (NS_FAILED(rv)) {
    403      LOG(
    404          ("WebSocketChannel::OnAcknowledge "
    405           "mListenerMT->mListener->OnAcknowledge() "
    406           "failed with error 0x%08" PRIx32,
    407           static_cast<uint32_t>(rv)));
    408    }
    409  }
    410 }
    411 
    412 class ServerCloseEvent : public WebSocketEvent {
    413 public:
    414  ServerCloseEvent(const uint16_t aCode, const nsACString& aReason)
    415      : mCode(aCode), mReason(aReason) {}
    416 
    417  void Run(WebSocketChannelChild* aChild) override {
    418    aChild->OnServerClose(mCode, mReason);
    419  }
    420 
    421 private:
    422  uint16_t mCode;
    423  nsCString mReason;
    424 };
    425 
    426 mozilla::ipc::IPCResult WebSocketChannelChild::RecvOnServerClose(
    427    const uint16_t& aCode, const nsACString& aReason) {
    428  mEventQ->RunOrEnqueue(
    429      new EventTargetDispatcher(this, new ServerCloseEvent(aCode, aReason)));
    430 
    431  return IPC_OK();
    432 }
    433 
    434 void WebSocketChannelChild::OnServerClose(const uint16_t& aCode,
    435                                          const nsACString& aReason) {
    436  LOG(("WebSocketChannelChild::RecvOnServerClose() %p\n", this));
    437  if (mListenerMT) {
    438    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    439    DebugOnly<nsresult> rv = mListenerMT->mListener->OnServerClose(
    440        mListenerMT->mContext, aCode, aReason);
    441    MOZ_ASSERT(NS_SUCCEEDED(rv));
    442  }
    443 }
    444 
    445 void WebSocketChannelChild::SetupNeckoTarget() {
    446  mNeckoTarget = GetMainThreadSerialEventTarget();
    447 }
    448 
    449 NS_IMETHODIMP
    450 WebSocketChannelChild::AsyncOpen(nsIURI* aURI, const nsACString& aOrigin,
    451                                 JS::Handle<JS::Value> aOriginAttributes,
    452                                 uint64_t aInnerWindowID,
    453                                 nsIWebSocketListener* aListener,
    454                                 nsISupports* aContext, JSContext* aCx) {
    455  OriginAttributes attrs;
    456  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
    457    return NS_ERROR_INVALID_ARG;
    458  }
    459  return AsyncOpenNative(aURI, aOrigin, attrs, aInnerWindowID, aListener,
    460                         aContext);
    461 }
    462 
    463 NS_IMETHODIMP
    464 WebSocketChannelChild::AsyncOpenNative(
    465    nsIURI* aURI, const nsACString& aOrigin,
    466    const OriginAttributes& aOriginAttributes, uint64_t aInnerWindowID,
    467    nsIWebSocketListener* aListener, nsISupports* aContext) {
    468  LOG(("WebSocketChannelChild::AsyncOpen() %p\n", this));
    469 
    470  MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    471  MOZ_ASSERT((aURI && !mIsServerSide) || (!aURI && mIsServerSide),
    472             "Invalid aURI for WebSocketChannelChild::AsyncOpen");
    473  MOZ_ASSERT(aListener && !mListenerMT,
    474             "Invalid state for WebSocketChannelChild::AsyncOpen");
    475 
    476  mozilla::dom::BrowserChild* browserChild = nullptr;
    477  nsCOMPtr<nsIBrowserChild> iBrowserChild;
    478  NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
    479                                NS_GET_IID(nsIBrowserChild),
    480                                getter_AddRefs(iBrowserChild));
    481  if (iBrowserChild) {
    482    browserChild =
    483        static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
    484  }
    485 
    486  ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
    487  if (cc->IsShuttingDown()) {
    488    return NS_ERROR_FAILURE;
    489  }
    490 
    491  // Corresponding release in DeallocPWebSocket
    492  AddIPDLReference();
    493 
    494  nsCOMPtr<nsIURI> uri;
    495  LoadInfoArgs loadInfoArgs;
    496  Maybe<NotNull<PTransportProviderChild*>> transportProvider;
    497 
    498  if (!mIsServerSide) {
    499    uri = aURI;
    500    nsresult rv = LoadInfoToLoadInfoArgs(mLoadInfo, &loadInfoArgs);
    501    NS_ENSURE_SUCCESS(rv, rv);
    502 
    503    transportProvider = Nothing();
    504  } else {
    505    MOZ_ASSERT(mServerTransportProvider);
    506    PTransportProviderChild* ipcChild;
    507    nsresult rv = mServerTransportProvider->GetIPCChild(&ipcChild);
    508    NS_ENSURE_SUCCESS(rv, rv);
    509 
    510    transportProvider = Some(WrapNotNull(ipcChild));
    511  }
    512 
    513  // This must be called before sending constructor message.
    514  SetupNeckoTarget();
    515 
    516  if (!gNeckoChild->SendPWebSocketConstructor(
    517          this, browserChild, IPC::SerializedLoadContext(this), mSerial)) {
    518    return NS_ERROR_UNEXPECTED;
    519  }
    520  if (!SendAsyncOpen(uri, aOrigin, aOriginAttributes, aInnerWindowID, mProtocol,
    521                     mEncrypted, mPingInterval, mClientSetPingInterval,
    522                     mPingResponseTimeout, mClientSetPingTimeout, loadInfoArgs,
    523                     transportProvider, mNegotiatedExtensions)) {
    524    return NS_ERROR_UNEXPECTED;
    525  }
    526 
    527  if (mIsServerSide) {
    528    mServerTransportProvider = nullptr;
    529  }
    530 
    531  mOriginalURI = aURI;
    532  mURI = mOriginalURI;
    533  mListenerMT = new ListenerAndContextContainer(aListener, aContext);
    534  mOrigin = aOrigin;
    535  mWasOpened = 1;
    536 
    537  return NS_OK;
    538 }
    539 
    540 class CloseEvent : public Runnable {
    541 public:
    542  CloseEvent(WebSocketChannelChild* aChild, uint16_t aCode,
    543             const nsACString& aReason)
    544      : Runnable("net::CloseEvent"),
    545        mChild(aChild),
    546        mCode(aCode),
    547        mReason(aReason) {
    548    MOZ_RELEASE_ASSERT(!NS_IsMainThread());
    549    MOZ_ASSERT(aChild);
    550  }
    551  NS_IMETHOD Run() override {
    552    MOZ_RELEASE_ASSERT(NS_IsMainThread());
    553    mChild->Close(mCode, mReason);
    554    return NS_OK;
    555  }
    556 
    557 private:
    558  RefPtr<WebSocketChannelChild> mChild;
    559  uint16_t mCode;
    560  nsCString mReason;
    561 };
    562 
    563 NS_IMETHODIMP
    564 WebSocketChannelChild::Close(uint16_t code, const nsACString& reason) {
    565  if (!NS_IsMainThread()) {
    566    MOZ_RELEASE_ASSERT(IsOnTargetThread());
    567    nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
    568    return target->Dispatch(new CloseEvent(this, code, reason),
    569                            NS_DISPATCH_NORMAL);
    570  }
    571  LOG(("WebSocketChannelChild::Close() %p\n", this));
    572 
    573  {
    574    MutexAutoLock lock(mMutex);
    575    if (mIPCState != Opened) {
    576      return NS_ERROR_UNEXPECTED;
    577    }
    578  }
    579 
    580  if (!SendClose(code, reason)) {
    581    return NS_ERROR_UNEXPECTED;
    582  }
    583 
    584  return NS_OK;
    585 }
    586 
    587 class MsgEvent : public Runnable {
    588 public:
    589  MsgEvent(WebSocketChannelChild* aChild, const nsACString& aMsg,
    590           bool aBinaryMsg)
    591      : Runnable("net::MsgEvent"),
    592        mChild(aChild),
    593        mMsg(aMsg),
    594        mBinaryMsg(aBinaryMsg) {
    595    MOZ_RELEASE_ASSERT(!NS_IsMainThread());
    596    MOZ_ASSERT(aChild);
    597  }
    598  NS_IMETHOD Run() override {
    599    MOZ_RELEASE_ASSERT(NS_IsMainThread());
    600    if (mBinaryMsg) {
    601      mChild->SendBinaryMsg(mMsg);
    602    } else {
    603      mChild->SendMsg(mMsg);
    604    }
    605    return NS_OK;
    606  }
    607 
    608 private:
    609  RefPtr<WebSocketChannelChild> mChild;
    610  nsCString mMsg;
    611  bool mBinaryMsg;
    612 };
    613 
    614 NS_IMETHODIMP
    615 WebSocketChannelChild::SendMsg(const nsACString& aMsg) {
    616  if (!NS_IsMainThread()) {
    617    MOZ_RELEASE_ASSERT(IsOnTargetThread());
    618    nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
    619    return target->Dispatch(new MsgEvent(this, aMsg, false),
    620                            NS_DISPATCH_NORMAL);
    621  }
    622  LOG(("WebSocketChannelChild::SendMsg() %p\n", this));
    623 
    624  {
    625    MutexAutoLock lock(mMutex);
    626    if (mIPCState != Opened) {
    627      return NS_ERROR_UNEXPECTED;
    628    }
    629  }
    630 
    631  if (!SendSendMsg(aMsg)) {
    632    return NS_ERROR_UNEXPECTED;
    633  }
    634 
    635  return NS_OK;
    636 }
    637 
    638 NS_IMETHODIMP
    639 WebSocketChannelChild::SendBinaryMsg(const nsACString& aMsg) {
    640  if (!NS_IsMainThread()) {
    641    MOZ_RELEASE_ASSERT(IsOnTargetThread());
    642    nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
    643    return target->Dispatch(new MsgEvent(this, aMsg, true), NS_DISPATCH_NORMAL);
    644  }
    645  LOG(("WebSocketChannelChild::SendBinaryMsg() %p\n", this));
    646 
    647  {
    648    MutexAutoLock lock(mMutex);
    649    if (mIPCState != Opened) {
    650      return NS_ERROR_UNEXPECTED;
    651    }
    652  }
    653 
    654  if (!SendSendBinaryMsg(aMsg)) {
    655    return NS_ERROR_UNEXPECTED;
    656  }
    657 
    658  return NS_OK;
    659 }
    660 
    661 class BinaryStreamEvent : public Runnable {
    662 public:
    663  BinaryStreamEvent(WebSocketChannelChild* aChild, nsIInputStream* aStream,
    664                    uint32_t aLength)
    665      : Runnable("net::BinaryStreamEvent"),
    666        mChild(aChild),
    667        mStream(aStream),
    668        mLength(aLength) {
    669    MOZ_RELEASE_ASSERT(!NS_IsMainThread());
    670    MOZ_ASSERT(aChild);
    671  }
    672  NS_IMETHOD Run() override {
    673    MOZ_ASSERT(NS_IsMainThread());
    674    nsresult rv = mChild->SendBinaryStream(mStream, mLength);
    675    if (NS_FAILED(rv)) {
    676      LOG(
    677          ("WebSocketChannelChild::BinaryStreamEvent %p "
    678           "SendBinaryStream failed (%08" PRIx32 ")\n",
    679           this, static_cast<uint32_t>(rv)));
    680    }
    681    return NS_OK;
    682  }
    683 
    684 private:
    685  RefPtr<WebSocketChannelChild> mChild;
    686  nsCOMPtr<nsIInputStream> mStream;
    687  uint32_t mLength;
    688 };
    689 
    690 NS_IMETHODIMP
    691 WebSocketChannelChild::SendBinaryStream(nsIInputStream* aStream,
    692                                        uint32_t aLength) {
    693  if (!NS_IsMainThread()) {
    694    MOZ_RELEASE_ASSERT(IsOnTargetThread());
    695    nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
    696    return target->Dispatch(new BinaryStreamEvent(this, aStream, aLength),
    697                            NS_DISPATCH_NORMAL);
    698  }
    699 
    700  LOG(("WebSocketChannelChild::SendBinaryStream() %p\n", this));
    701 
    702  IPCStream ipcStream;
    703  if (NS_WARN_IF(!mozilla::ipc::SerializeIPCStream(do_AddRef(aStream),
    704                                                   ipcStream,
    705                                                   /* aAllowLazy */ false))) {
    706    return NS_ERROR_UNEXPECTED;
    707  }
    708 
    709  {
    710    MutexAutoLock lock(mMutex);
    711    if (mIPCState != Opened) {
    712      return NS_ERROR_UNEXPECTED;
    713    }
    714  }
    715 
    716  if (!SendSendBinaryStream(ipcStream, aLength)) {
    717    return NS_ERROR_UNEXPECTED;
    718  }
    719 
    720  return NS_OK;
    721 }
    722 
    723 NS_IMETHODIMP
    724 WebSocketChannelChild::GetSecurityInfo(
    725    nsITransportSecurityInfo** aSecurityInfo) {
    726  LOG(("WebSocketChannelChild::GetSecurityInfo() %p\n", this));
    727  return NS_ERROR_NOT_AVAILABLE;
    728 }
    729 
    730 }  // namespace net
    731 }  // namespace mozilla