tor-browser

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

RTCDataChannel.cpp (31796B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "RTCDataChannel.h"
      8 
      9 #include "DataChannel.h"
     10 #include "DataChannelLog.h"
     11 #include "RTCDataChannelDeclarations.h"
     12 #include "base/basictypes.h"
     13 #include "mozilla/DOMEventTargetHelper.h"
     14 #include "mozilla/EventListenerManager.h"
     15 #include "mozilla/Logging.h"
     16 #include "mozilla/dom/Blob.h"
     17 #include "mozilla/dom/File.h"
     18 #include "mozilla/dom/MessageEvent.h"
     19 #include "mozilla/dom/MessageEventBinding.h"
     20 #include "mozilla/dom/RTCStatsReportBinding.h"
     21 #include "mozilla/dom/ScriptSettings.h"
     22 #include "mozilla/dom/ToJSValue.h"
     23 #include "mozilla/dom/TypedArray.h"
     24 #include "mozilla/dom/WorkerCommon.h"
     25 #include "mozilla/dom/WorkerRef.h"
     26 #include "nsContentUtils.h"
     27 #include "nsCycleCollectionParticipant.h"
     28 #include "nsError.h"
     29 #include "nsIScriptContext.h"
     30 #include "nsIScriptObjectPrincipal.h"
     31 #include "nsProxyRelease.h"
     32 #include "nsThreadManager.h"
     33 
     34 // Since we've moved the windows.h include down here, we have to explicitly
     35 // undef GetBinaryType, otherwise we'll get really odd conflicts
     36 #ifdef GetBinaryType
     37 #  undef GetBinaryType
     38 #endif
     39 
     40 namespace mozilla {
     41 namespace dom {
     42 
     43 static constexpr const char* ToString(RTCDataChannelState state) {
     44  switch (state) {
     45    case RTCDataChannelState::Connecting:
     46      return "connecting";
     47    case RTCDataChannelState::Open:
     48      return "open";
     49    case RTCDataChannelState::Closing:
     50      return "closing";
     51    case RTCDataChannelState::Closed:
     52      return "closed";
     53  }
     54  return "";
     55 };
     56 
     57 RTCDataChannel::~RTCDataChannel() {
     58  DC_INFO(("%p: RTCDataChannel destroyed", this));
     59  if (NS_IsMainThread()) {
     60    mDataChannel->UnsetMainthreadDomDataChannel();
     61  } else {
     62    mDataChannel->UnsetWorkerDomDataChannel();
     63  }
     64 }
     65 
     66 /* virtual */
     67 JSObject* RTCDataChannel::WrapObject(JSContext* aCx,
     68                                     JS::Handle<JSObject*> aGivenProto) {
     69  return RTCDataChannel_Binding::Wrap(aCx, this, aGivenProto);
     70 }
     71 
     72 NS_IMPL_CYCLE_COLLECTION_CLASS(RTCDataChannel)
     73 
     74 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RTCDataChannel,
     75                                                  DOMEventTargetHelper)
     76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     77 
     78 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RTCDataChannel,
     79                                                DOMEventTargetHelper)
     80 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     81 
     82 NS_IMPL_ADDREF_INHERITED(RTCDataChannel, DOMEventTargetHelper)
     83 NS_IMPL_RELEASE_INHERITED(RTCDataChannel, DOMEventTargetHelper)
     84 
     85 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCDataChannel)
     86 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     87 
     88 RTCDataChannel::RTCDataChannel(const nsACString& aLabel,
     89                               const nsAString& aOrigin, bool aOrdered,
     90                               Nullable<uint16_t> aMaxLifeTime,
     91                               Nullable<uint16_t> aMaxRetransmits,
     92                               const nsACString& aProtocol, bool aNegotiated,
     93                               already_AddRefed<DataChannel>& aDataChannel,
     94                               nsPIDOMWindowInner* aWindow)
     95    : DOMEventTargetHelper(aWindow),
     96      mUuid(nsID::GenerateUUID()),
     97      mOrigin(aOrigin),
     98      mLabel(aLabel),
     99      mOrdered(aOrdered),
    100      mMaxPacketLifeTime(aMaxLifeTime),
    101      mMaxRetransmits(aMaxRetransmits),
    102      mDataChannelProtocol(aProtocol),
    103      mNegotiated(aNegotiated),
    104      mDataChannel(aDataChannel),
    105      mEventTarget(GetCurrentSerialEventTarget()) {
    106  DC_INFO(("%p: RTCDataChannel created on main (necko channel %p)", this,
    107           mDataChannel.get()));
    108  mDataChannel->SetMainthreadDomDataChannel(this);
    109 }
    110 
    111 nsresult RTCDataChannel::Init() {
    112  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    113 
    114  UpdateMustKeepAlive();
    115 
    116  if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
    117    // When the callback is executed, we cannot process messages anymore because
    118    // we cannot dispatch new runnables. Let's force a Close().
    119    RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create(
    120        workerPrivate, "RTCDataChannel::Init",
    121        [this, self = RefPtr<RTCDataChannel>(this)]() {
    122          // Make absolutely certain we do not get more
    123          // callbacks.
    124          DC_INFO(("%p: Worker is going away, breaking cycles", this));
    125          mDataChannel->UnsetWorkerDomDataChannel();
    126          // Also allow ourselves to be GC'ed
    127          UnsetWorkerNeedsUs();
    128          DontKeepAliveAnyMore();
    129          mWorkerRef = nullptr;
    130        });
    131    if (NS_WARN_IF(!strongWorkerRef)) {
    132      DC_WARN(("%p: Could not get worker ref, breaking cycles", this));
    133      // The worker is shutting down.
    134      // Make absolutely certain we do not get more callbacks.
    135      mDataChannel->UnsetWorkerDomDataChannel();
    136      // Also allow ourselves to be GC'ed
    137      UnsetWorkerNeedsUs();
    138      return NS_ERROR_FAILURE;
    139    }
    140 
    141    MOZ_ASSERT(!mWorkerRef);
    142    mWorkerRef = std::move(strongWorkerRef);
    143  }
    144 
    145  if (NS_IsMainThread()) {
    146    // Queue a task to run the following step:
    147    GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
    148        __func__, [this, self = RefPtr<RTCDataChannel>(this)]() {
    149          DisableWorkerTransfer();
    150        }));
    151  }
    152 
    153  // Attempt to kill "ghost" DataChannel (if one can happen): but usually too
    154  // early for check to fail
    155  nsresult rv = CheckCurrentGlobalCorrectness();
    156  NS_ENSURE_SUCCESS(rv, rv);
    157 
    158  DC_DEBUG(("%p: %s: origin = %s\n", this, __FUNCTION__,
    159            NS_LossyConvertUTF16toASCII(mOrigin).get()));
    160  return NS_OK;
    161 }
    162 
    163 // Most of the GetFoo()/SetFoo()s don't need to touch shared resources and
    164 // are safe after Close()
    165 void RTCDataChannel::GetLabel(nsACString& aLabel) const {
    166  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    167  aLabel = mLabel;
    168 }
    169 
    170 void RTCDataChannel::GetProtocol(nsACString& aProtocol) const {
    171  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    172  aProtocol = mDataChannelProtocol;
    173 }
    174 
    175 Nullable<uint16_t> RTCDataChannel::GetId() const {
    176  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    177  return mDataChannelId;
    178 }
    179 
    180 // https://w3c.github.io/webrtc-pc/#transfering-a-data-channel
    181 RTCDataChannel::DataHolder::DataHolder(const RTCDataChannel& aValue)
    182    :  // Set dataHolder.[[ReadyState]] to value.[[ReadyState]].
    183      mReadyState(aValue.mReadyState),
    184      // Set dataHolder.[[DataChannelLabel]] to value.[[DataChannelLabel]].
    185      mLabel(aValue.mLabel),
    186      // Set dataHolder.[[Ordered]] to value.[[Ordered]].
    187      mOrdered(aValue.mOrdered),
    188      // Set dataHolder.[[MaxPacketLifeTime]] to value..[[MaxPacketLifeTime]]
    189      mMaxPacketLifeTime(aValue.mMaxPacketLifeTime),
    190      // Set dataHolder.[[MaxRetransmits]] to value.[[MaxRetransmits]].
    191      mMaxRetransmits(aValue.mMaxRetransmits),
    192      // Set dataHolder.[[DataChannelProtocol]] to
    193      // value.[[DataChannelProtocol]].
    194      mDataChannelProtocol(aValue.mDataChannelProtocol),
    195      // Set dataHolder.[[Negotiated]] to value.[[Negotiated]].
    196      mNegotiated(aValue.mNegotiated),
    197      // Set dataHolder.[[DataChannelId]] to value.[[DataChannelId]].
    198      mDataChannelId(aValue.mDataChannelId),
    199      // Set dataHolder’s underlying data transport to value underlying data
    200      // transport.
    201      mDataChannel(aValue.mDataChannel),
    202      // We should keep track of this too
    203      mMaxMessageSize(aValue.mMaxMessageSize),
    204      mOrigin(aValue.mOrigin) {}
    205 
    206 RTCDataChannel::DataHolder::~DataHolder() = default;
    207 
    208 // https://w3c.github.io/webrtc-pc/#transfering-a-data-channel
    209 UniquePtr<RTCDataChannel::DataHolder> RTCDataChannel::Transfer() {
    210  MOZ_ASSERT(NS_IsMainThread());
    211  // The RTCDataChannel transfer steps, given value and dataHolder, are:
    212 
    213  // If value.[[IsTransferable]] is false, throw a DataCloneError DOMException.
    214  // (Failure in this function does appear to cause this up the callchain)
    215  if (!mIsTransferable) {
    216    return nullptr;
    217  }
    218 
    219  // Set dataHolder.**** yadda yadda ****
    220  UniquePtr<DataHolder> dataHolder = MakeUnique<DataHolder>(*this);
    221 
    222  // Set value.[[IsTransferable]] to false.
    223  mIsTransferable = false;
    224 
    225  // Set value.[[ReadyState]] to "closed".
    226  mReadyState = RTCDataChannelState::Closed;
    227 
    228  mDataChannel->OnWorkerTransferStarted();
    229 
    230  return dataHolder;
    231 }
    232 
    233 // https://w3c.github.io/webrtc-pc/#transfering-a-data-channel
    234 // The RTCDataChannel transfer-receiving steps, given dataHolder and channel,
    235 // are:
    236 RTCDataChannel::RTCDataChannel(nsIGlobalObject* aGlobal,
    237                               const DataHolder& aDataHolder)
    238    : DOMEventTargetHelper(aGlobal),
    239      mUuid(nsID::GenerateUUID()),
    240      mOrigin(aDataHolder.mOrigin),
    241      // Initialize channel.[[DataChannelLabel]] to
    242      // dataHolder.[[DataChannelLabel]].
    243      mLabel(aDataHolder.mLabel),
    244      // Initialize channel.[[Ordered]] to dataHolder.[[Ordered]].
    245      mOrdered(aDataHolder.mOrdered),
    246      // Initialize channel.[[MaxPacketLifeTime]] to
    247      // dataHolder.[[MaxPacketLifeTime]].
    248      mMaxPacketLifeTime(aDataHolder.mMaxPacketLifeTime),
    249      // Initialize channel.[[MaxRetransmits]] to dataHolder.[[MaxRetransmits]].
    250      mMaxRetransmits(aDataHolder.mMaxRetransmits),
    251      // Initialize channel.[[DataChannelProtocol]] to
    252      // dataHolder.[[DataChannelProtocol]].
    253      mDataChannelProtocol(aDataHolder.mDataChannelProtocol),
    254      // Initialize channel.[[Negotiated]] to dataHolder.[[Negotiated]].
    255      mNegotiated(aDataHolder.mNegotiated),
    256      // Initialize channel’s underlying data transport to dataHolder’s
    257      // underlying data transport.
    258      mDataChannel(aDataHolder.mDataChannel),
    259      // Initialize channel.[[DataChannelId]] to dataHolder.[[DataChannelId]].
    260      mDataChannelId(aDataHolder.mDataChannelId),
    261      // Initialize channel.[[ReadyState]] to dataHolder.[[ReadyState]].
    262      mReadyState(aDataHolder.mReadyState),
    263      // The user agent MUST keep a strong reference from channel's Window or
    264      // WorkerGlobalScope to channel while the RTCDataChannel object that
    265      // originally created its underlying data transport remains alive.
    266      mWorkerNeedsUs(true),
    267      // Spec doesn't say to do this, but this is the only sane value
    268      mIsTransferable(false),
    269      // Update this too
    270      mMaxMessageSize(aDataHolder.mMaxMessageSize),
    271      mEventTarget(GetCurrentSerialEventTarget()) {
    272  MOZ_ASSERT(!NS_IsMainThread());
    273  DC_INFO(("%p: RTCDataChannel created on worker", this));
    274  mDataChannel->OnWorkerTransferComplete(this);
    275 }
    276 
    277 void RTCDataChannel::SetId(uint16_t aId) {
    278  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    279  mDataChannelId.SetValue(aId);
    280 }
    281 
    282 void RTCDataChannel::SetMaxMessageSize(double aMaxMessageSize) {
    283  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    284  DC_INFO(("%p: RTCDataChannel updating maximum message size: %f -> %f", this,
    285           mMaxMessageSize, aMaxMessageSize));
    286  mMaxMessageSize = aMaxMessageSize;
    287 }
    288 
    289 Nullable<uint16_t> RTCDataChannel::GetMaxPacketLifeTime() const {
    290  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    291  return mMaxPacketLifeTime;
    292 }
    293 
    294 Nullable<uint16_t> RTCDataChannel::GetMaxRetransmits() const {
    295  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    296  return mMaxRetransmits;
    297 }
    298 
    299 bool RTCDataChannel::Negotiated() const {
    300  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    301  return mNegotiated;
    302 }
    303 
    304 bool RTCDataChannel::Ordered() const {
    305  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    306  return mOrdered;
    307 }
    308 
    309 RTCDataChannelState RTCDataChannel::ReadyState() const {
    310  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    311  return mReadyState;
    312 }
    313 
    314 void RTCDataChannel::SetReadyState(const RTCDataChannelState aState) {
    315  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    316 
    317  DC_DEBUG(
    318      ("%p: RTCDataChannel labeled %s (stream %d) changing ready "
    319       "state "
    320       "%s -> %s",
    321       this, mLabel.get(),
    322       mDataChannelId.IsNull() ? INVALID_STREAM : mDataChannelId.Value(),
    323       ToString(mReadyState), ToString(aState)));
    324 
    325  mReadyState = aState;
    326 }
    327 
    328 size_t RTCDataChannel::BufferedAmount() const {
    329  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    330  return mBufferedAmount;
    331 }
    332 
    333 size_t RTCDataChannel::BufferedAmountLowThreshold() const {
    334  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    335  return mBufferedThreshold;
    336 }
    337 
    338 void RTCDataChannel::SetBufferedAmountLowThreshold(size_t aThreshold) {
    339  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    340  mBufferedThreshold = aThreshold;
    341 }
    342 
    343 void RTCDataChannel::Close() {
    344  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    345  // When the close method is called, the user agent MUST run the following
    346  // steps:
    347 
    348  // Let channel be the RTCDataChannel object which is about to be closed.
    349 
    350  // If channel.[[ReadyState]] is "closing" or "closed", then abort these
    351  // steps.
    352  if (mReadyState == RTCDataChannelState::Closed ||
    353      mReadyState == RTCDataChannelState::Closing) {
    354    DC_DEBUG(("%p: Channel already closing/closed (%s)", this,
    355              ToString(mReadyState)));
    356    return;
    357  }
    358 
    359  // Set channel.[[ReadyState]] to "closing".
    360  SetReadyState(RTCDataChannelState::Closing);
    361 
    362  // If the closing procedure has not started yet, start it.
    363  GracefulClose();
    364 
    365  UpdateMustKeepAlive();
    366 }
    367 
    368 void RTCDataChannel::Send(const nsAString& aData, ErrorResult& aRv) {
    369  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    370 
    371  DisableWorkerTransfer();
    372  if (!CheckReadyState(aRv)) {
    373    return;
    374  }
    375 
    376  if (!CheckSendSize(aData.Length(), aRv)) {
    377    return;
    378  }
    379 
    380  nsCString msgString;
    381  if (!AppendUTF16toUTF8(aData, msgString, fallible_t())) {
    382    // Hmm, our max size was smaller than we thought...
    383    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
    384    return;
    385  }
    386 
    387  size_t length = msgString.Length();
    388  mDataChannel->SendMsg(std::move(msgString));
    389  ++mMessagesSent;
    390  mBytesSent += length;
    391  IncrementBufferedAmount(length);
    392 }
    393 
    394 void RTCDataChannel::Send(Blob& aData, ErrorResult& aRv) {
    395  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    396 
    397  DisableWorkerTransfer();
    398  if (!CheckReadyState(aRv)) {
    399    return;
    400  }
    401 
    402  uint64_t msgLength = aData.GetSize(aRv);
    403  if (NS_WARN_IF(aRv.Failed())) {
    404    return;
    405  }
    406 
    407  if (!CheckSendSize(msgLength, aRv)) {
    408    return;
    409  }
    410 
    411  nsCOMPtr<nsIInputStream> msgStream;
    412  aData.CreateInputStream(getter_AddRefs(msgStream), aRv);
    413  if (NS_WARN_IF(aRv.Failed())) {
    414    return;
    415  }
    416 
    417  // TODO: If we cannot support this, it needs to be declared during negotiation
    418  if (msgLength > UINT32_MAX) {
    419    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
    420    return;
    421  }
    422 
    423  mDataChannel->SendBinaryBlob(msgStream);
    424  ++mMessagesSent;
    425  mBytesSent += msgLength;
    426  IncrementBufferedAmount(msgLength);
    427 }
    428 
    429 void RTCDataChannel::Send(const ArrayBuffer& aData, ErrorResult& aRv) {
    430  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    431 
    432  DisableWorkerTransfer();
    433  if (!CheckReadyState(aRv)) {
    434    return;
    435  }
    436 
    437  nsCString msgString;
    438  if (!aData.AppendDataTo(msgString)) {
    439    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
    440    return;
    441  }
    442 
    443  if (!CheckSendSize(msgString.Length(), aRv)) {
    444    return;
    445  }
    446 
    447  size_t length = msgString.Length();
    448  mDataChannel->SendBinaryMsg(std::move(msgString));
    449  ++mMessagesSent;
    450  mBytesSent += length;
    451  IncrementBufferedAmount(length);
    452 }
    453 
    454 void RTCDataChannel::Send(const ArrayBufferView& aData, ErrorResult& aRv) {
    455  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    456 
    457  DisableWorkerTransfer();
    458  if (!CheckReadyState(aRv)) {
    459    return;
    460  }
    461 
    462  nsCString msgString;
    463  if (!aData.AppendDataTo(msgString)) {
    464    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
    465    return;
    466  }
    467 
    468  if (!CheckSendSize(msgString.Length(), aRv)) {
    469    return;
    470  }
    471 
    472  size_t length = msgString.Length();
    473  mDataChannel->SendBinaryMsg(std::move(msgString));
    474  ++mMessagesSent;
    475  mBytesSent += length;
    476  IncrementBufferedAmount(length);
    477 }
    478 
    479 void RTCDataChannel::GracefulClose() {
    480  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    481 
    482  // An RTCDataChannel object's underlying data transport may be torn down in a
    483  // non-abrupt manner by running the closing procedure. When that happens the
    484  // user agent MUST queue a task to run the following steps:
    485 
    486  GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
    487      __func__, [this, self = RefPtr<RTCDataChannel>(this)]() {
    488        // Let channel be the RTCDataChannel object whose underlying data
    489        // transport was closed.
    490 
    491        // Let connection be the RTCPeerConnection object associated with
    492        // channel.
    493 
    494        // Remove channel from connection.[[DataChannels]].
    495        // Note: We don't really have this slot. Reading the spec, it does not
    496        // appear this serves any function other than holding a ref to the
    497        // RTCDataChannel, which in our case is handled by mSelfRef.
    498 
    499        // Unless the procedure was initiated by channel.close, set
    500        // channel.[[ReadyState]] to "closing" and fire an event named closing
    501        // at channel. Note: channel.close will set [[ReadyState]] to Closing.
    502        // We also check for closed, just as belt and suspenders.
    503        if (mReadyState != RTCDataChannelState::Closing &&
    504            mReadyState != RTCDataChannelState::Closed) {
    505          SetReadyState(RTCDataChannelState::Closing);
    506          OnSimpleEvent(u"closing"_ns);
    507        }
    508 
    509        // Run the following steps in parallel:
    510        // Finish sending all currently pending messages of the channel.
    511        // Note: We detect when all pending messages are sent with
    512        // mBufferedAmount. We do an initial check here, and subsequent checks
    513        // in DecrementBufferedAmount.
    514        // Caveat(bug 1979692): mBufferedAmount is decremented when the bytes
    515        // are first transmitted, _not_ when they are acked. We might need to do
    516        // some work to ensure that the SCTP stack has delivered these last
    517        // bytes to the other end before that channel/connection is fully
    518        // closed.
    519        if (!mBufferedAmount && mReadyState != RTCDataChannelState::Closed &&
    520            mDataChannel) {
    521          mDataChannel->EndOfStream();
    522        }
    523      }));
    524 }
    525 
    526 void RTCDataChannel::AnnounceOpen() {
    527  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    528  // If the associated RTCPeerConnection object's [[IsClosed]] slot is true,
    529  // abort these steps.
    530  // TODO(bug 1978901): Fix this
    531 
    532  // Let channel be the RTCDataChannel object to be announced.
    533 
    534  // If channel.[[ReadyState]] is "closing" or "closed", abort these steps.
    535  if (mReadyState != RTCDataChannelState::Closing &&
    536      mReadyState != RTCDataChannelState::Closed) {
    537    // Set channel.[[ReadyState]] to "open".
    538    SetReadyState(RTCDataChannelState::Open);
    539    // Fire an event named open at channel.
    540    DC_INFO(("%p: sending open for %s/%s: %u", this, mLabel.get(),
    541             mDataChannelProtocol.get(), mDataChannelId.Value()));
    542    OnSimpleEvent(u"open"_ns);
    543  }
    544 }
    545 
    546 void RTCDataChannel::AnnounceClosed() {
    547  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    548  // Let channel be the RTCDataChannel object whose
    549  // underlying data transport was closed. If
    550  // channel.[[ReadyState]] is "closed", abort
    551  // these steps.
    552  if (mReadyState == RTCDataChannelState::Closed) {
    553    return;
    554  }
    555 
    556  // Set channel.[[ReadyState]] to "closed".
    557  SetReadyState(RTCDataChannelState::Closed);
    558 
    559  // Remove channel from
    560  // connection.[[DataChannels]] if it is still
    561  // there. Note: We don't really have this slot.
    562  // Reading the spec, it does not appear this
    563  // serves any function other than holding a ref
    564  // to the RTCDataChannel, which in our case is
    565  // handled by a self ref in nsDOMDataChannel.
    566 
    567  // If the transport was closed with an error,
    568  // fire an event named error using the
    569  // RTCErrorEvent interface with its errorDetail
    570  // attribute set to "sctp-failure" at channel.
    571  // Note: We don't support this yet.
    572 
    573  // Fire an event named close at channel.
    574  OnSimpleEvent(u"close"_ns);
    575  DontKeepAliveAnyMore();
    576 }
    577 
    578 dom::RTCDataChannelStats RTCDataChannel::GetStats(
    579    const DOMHighResTimeStamp aTimestamp) const {
    580  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    581  mozilla::dom::RTCDataChannelStats stats;
    582  nsString id = u"dc"_ns;
    583  id.Append(NS_ConvertASCIItoUTF16(mUuid.ToString().get()));
    584  stats.mId.Construct(id);
    585  stats.mTimestamp.Construct(aTimestamp);
    586  stats.mType.Construct(mozilla::dom::RTCStatsType::Data_channel);
    587  // webrtc-stats says the stats are DOMString, but webrtc-pc says the
    588  // attributes are USVString.
    589  stats.mLabel.Construct(NS_ConvertUTF8toUTF16(mLabel));
    590  stats.mProtocol.Construct(NS_ConvertUTF8toUTF16(mDataChannelProtocol));
    591  if (!mDataChannelId.IsNull()) {
    592    stats.mDataChannelIdentifier.Construct(mDataChannelId.Value());
    593  }
    594  stats.mState.Construct(mReadyState);
    595 
    596  stats.mMessagesSent.Construct(mMessagesSent);
    597  stats.mBytesSent.Construct(mBytesSent);
    598  stats.mMessagesReceived.Construct(mMessagesReceived);
    599  stats.mBytesReceived.Construct(mBytesReceived);
    600  return stats;
    601 }
    602 
    603 void RTCDataChannel::UnsetWorkerNeedsUs() {
    604  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    605  mWorkerNeedsUs = false;
    606  DC_INFO(("%p: Unsetting mWorkerNeedsUs, clearing worker weak ref", this));
    607  mWorkerRef = nullptr;
    608  UpdateMustKeepAlive();
    609 }
    610 
    611 void RTCDataChannel::IncrementBufferedAmount(size_t aSize) {
    612  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    613  mBufferedAmount += aSize;
    614 }
    615 
    616 void RTCDataChannel::DecrementBufferedAmount(size_t aSize) {
    617  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    618  MOZ_ASSERT(aSize <= mBufferedAmount);
    619  aSize = std::min(aSize, mBufferedAmount);
    620  bool wasLow = mBufferedAmount <= mBufferedThreshold;
    621  mBufferedAmount -= aSize;
    622  if (!wasLow && mBufferedAmount <= mBufferedThreshold) {
    623    DC_DEBUG(("%p: sending bufferedamountlow for %s/%s: %u", this, mLabel.get(),
    624              mDataChannelProtocol.get(), mDataChannelId.Value()));
    625    OnSimpleEvent(u"bufferedamountlow"_ns);
    626  }
    627  if (mBufferedAmount == 0) {
    628    DC_DEBUG(("%p: no queued sends for %s/%s: %u", this, mLabel.get(),
    629              mDataChannelProtocol.get(), mDataChannelId.Value()));
    630    // In the rare case that we held off GC to let the buffer drain
    631    UpdateMustKeepAlive();
    632    if (mReadyState == RTCDataChannelState::Closing) {
    633      if (mDataChannel) {
    634        // We're done sending
    635        mDataChannel->EndOfStream();
    636      }
    637    }
    638  }
    639 }
    640 
    641 bool RTCDataChannel::CheckSendSize(uint64_t aSize, ErrorResult& aRv) const {
    642  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    643  if (aSize > mMaxMessageSize) {
    644    nsPrintfCString err("Message size (%" PRIu64 ") exceeds maxMessageSize",
    645                        aSize);
    646    aRv.ThrowTypeError(err);
    647    return false;
    648  }
    649  return true;
    650 }
    651 
    652 void RTCDataChannel::DisableWorkerTransfer() {
    653  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    654  // If this is false, that means this has been transferred. Nothing to
    655  // do.
    656  if (mIsTransferable) {
    657    // Set channel.[[IsTransferable]] to false.
    658    mIsTransferable = false;
    659    // This task needs to run before any task enqueued by the receiving
    660    // messages on a data channel algorithm for channel. This ensures
    661    // that no message is lost during the transfer of a RTCDataChannel.
    662    mDataChannel->OnWorkerTransferDisabled();
    663  }
    664 }
    665 
    666 bool RTCDataChannel::CheckReadyState(ErrorResult& aRv) {
    667  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    668  // In reality, the DataChannel protocol allows this, but we want it to
    669  // look like WebSockets
    670  if (mReadyState == RTCDataChannelState::Connecting) {
    671    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    672    return false;
    673  }
    674 
    675  if (mReadyState == RTCDataChannelState::Closing ||
    676      mReadyState == RTCDataChannelState::Closed) {
    677    return false;
    678  }
    679 
    680  MOZ_ASSERT(mReadyState == RTCDataChannelState::Open,
    681             "Unknown state in RTCDataChannel::Send");
    682 
    683  return true;
    684 }
    685 
    686 nsresult RTCDataChannel::DoOnMessageAvailable(const nsACString& aData,
    687                                              bool aBinary) {
    688  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    689 
    690  if (mReadyState == RTCDataChannelState::Closed ||
    691      mReadyState == RTCDataChannelState::Closing) {
    692    // Closed by JS, probably
    693    return NS_OK;
    694  }
    695 
    696  DC_VERBOSE(("%p: DoOnMessageAvailable%s\n", this,
    697              aBinary
    698                  ? ((mBinaryType == RTCDataChannelType::Blob) ? " (blob)"
    699                                                               : " (binary)")
    700                  : ""));
    701 
    702  nsresult rv = CheckCurrentGlobalCorrectness();
    703  if (NS_FAILED(rv)) {
    704    DC_ERROR(("%p: RTCDataChannel::%s: CheckCurrentGlobalCorrectness failed",
    705              this, __func__));
    706    return NS_OK;
    707  }
    708 
    709  AutoJSAPI jsapi;
    710  if (NS_WARN_IF(!jsapi.Init(GetParentObject()))) {
    711    DC_ERROR(("%p: RTCDataChannel::%s: jsapi.Init failed", this, __func__));
    712    return NS_ERROR_FAILURE;
    713  }
    714  JSContext* cx = jsapi.cx();
    715 
    716  JS::Rooted<JS::Value> jsData(cx);
    717 
    718  if (aBinary) {
    719    if (mBinaryType == RTCDataChannelType::Blob) {
    720      RefPtr<Blob> blob =
    721          Blob::CreateStringBlob(GetOwnerGlobal(), aData, u""_ns);
    722      if (NS_WARN_IF(!blob)) {
    723        DC_ERROR(("%p: RTCDataChannel::%s: CreateStringBlob failed", this,
    724                  __func__));
    725        return NS_ERROR_FAILURE;
    726      }
    727 
    728      if (!ToJSValue(cx, blob, &jsData)) {
    729        DC_ERROR(("%p: RTCDataChannel::%s: ToJSValue failed", this, __func__));
    730        return NS_ERROR_FAILURE;
    731      }
    732    } else if (mBinaryType == RTCDataChannelType::Arraybuffer) {
    733      ErrorResult error;
    734      JS::Rooted<JSObject*> arrayBuf(cx, ArrayBuffer::Create(cx, aData, error));
    735      RETURN_NSRESULT_ON_FAILURE(error);
    736      jsData.setObject(*arrayBuf);
    737    } else {
    738      MOZ_CRASH("Unknown binary type!");
    739      return NS_ERROR_UNEXPECTED;
    740    }
    741  } else {
    742    NS_ConvertUTF8toUTF16 utf16data(aData);
    743    JSString* jsString =
    744        JS_NewUCStringCopyN(cx, utf16data.get(), utf16data.Length());
    745    NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
    746 
    747    jsData.setString(jsString);
    748  }
    749 
    750  RefPtr<MessageEvent> event = new MessageEvent(this, nullptr, nullptr);
    751 
    752  event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
    753                          Cancelable::eNo, jsData, mOrigin, u""_ns, nullptr,
    754                          Sequence<OwningNonNull<MessagePort>>());
    755  event->SetTrusted(true);
    756 
    757  ++mMessagesReceived;
    758  mBytesReceived += aData.Length();
    759 
    760  // Log message events, but stop after 5
    761  if (mMessagesReceived < 5) {
    762    DC_INFO(("%p: Firing \"message\" event #%zu", this, mMessagesReceived));
    763  } else if (mMessagesReceived == 5) {
    764    DC_INFO(
    765        ("%p: Firing \"message\" event #%zu, will not log more message events",
    766         this, mMessagesReceived));
    767  }
    768 
    769  DC_DEBUG(("%p: %s - Dispatching message event\n", this, __FUNCTION__));
    770  ErrorResult err;
    771  DispatchEvent(*event, err);
    772  if (err.Failed()) {
    773    DC_ERROR(("%p: %s - Failed to dispatch message", this, __FUNCTION__));
    774    NS_WARNING("Failed to dispatch the message event!!!");
    775  }
    776  return err.StealNSResult();
    777 }
    778 
    779 nsresult RTCDataChannel::OnSimpleEvent(const nsAString& aName) {
    780  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    781 
    782  nsresult rv = CheckCurrentGlobalCorrectness();
    783  if (NS_FAILED(rv)) {
    784    return NS_OK;
    785  }
    786 
    787  if (MOZ_LOG_TEST(mozilla::gDataChannelLog, mozilla::LogLevel::Info)) {
    788    // The "message" event does not go through here; that would be overkill at
    789    // Info.
    790    DC_INFO(
    791        ("%p: Firing \"%s\" event", this, NS_ConvertUTF16toUTF8(aName).get()));
    792  }
    793 
    794  RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
    795 
    796  event->InitEvent(aName, CanBubble::eNo, Cancelable::eNo);
    797  event->SetTrusted(true);
    798 
    799  ErrorResult err;
    800  DispatchEvent(*event, err);
    801  return err.StealNSResult();
    802 }
    803 
    804 //-----------------------------------------------------------------------------
    805 // Methods that keep alive the DataChannel object when:
    806 //   1. the object has registered event listeners that can be triggered
    807 //      ("strong event listeners");
    808 //   2. there are outgoing not sent messages.
    809 //-----------------------------------------------------------------------------
    810 
    811 void RTCDataChannel::UpdateMustKeepAlive() {
    812  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    813 
    814  if (!mCheckMustKeepAlive) {
    815    return;
    816  }
    817 
    818  bool shouldKeepAlive = mWorkerNeedsUs;
    819 
    820  if (!shouldKeepAlive) {
    821    switch (mReadyState) {
    822      case RTCDataChannelState::Connecting: {
    823        if (mListenerManager &&
    824            (mListenerManager->HasListenersFor(nsGkAtoms::onopen) ||
    825             mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
    826             mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
    827             mListenerManager->HasListenersFor(
    828                 nsGkAtoms::onbufferedamountlow) ||
    829             mListenerManager->HasListenersFor(nsGkAtoms::onclose))) {
    830          shouldKeepAlive = true;
    831        }
    832      } break;
    833 
    834      case RTCDataChannelState::Open:
    835      case RTCDataChannelState::Closing: {
    836        if (mBufferedAmount != 0 ||
    837            (mListenerManager &&
    838             (mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
    839              mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
    840              mListenerManager->HasListenersFor(
    841                  nsGkAtoms::onbufferedamountlow) ||
    842              mListenerManager->HasListenersFor(nsGkAtoms::onclose)))) {
    843          shouldKeepAlive = true;
    844        }
    845      } break;
    846 
    847      case RTCDataChannelState::Closed:;
    848    }
    849  }
    850 
    851  if (mSelfRef && !shouldKeepAlive) {
    852    DC_INFO(("%p: RTCDataChannel is no longer protected from GC.", this));
    853    ReleaseSelf();
    854  } else if (!mSelfRef && shouldKeepAlive) {
    855    DC_INFO(("%p: RTCDataChannel is protected from GC.", this));
    856    mSelfRef = this;
    857  }
    858 }
    859 
    860 void RTCDataChannel::DontKeepAliveAnyMore() {
    861  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    862  mCheckMustKeepAlive = false;
    863 
    864  mWorkerRef = nullptr;
    865 
    866  if (mSelfRef) {
    867    // Force an eventloop trip to avoid deleting ourselves.
    868    ReleaseSelf();
    869  }
    870 }
    871 
    872 void RTCDataChannel::ReleaseSelf() {
    873  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    874  DC_INFO(("%p: Releasing self-ref", this));
    875  // release our self-reference (safely) by putting it in an event (always)
    876  NS_ProxyRelease("RTCDataChannel::mSelfRef", mEventTarget, mSelfRef.forget());
    877 }
    878 
    879 void RTCDataChannel::EventListenerAdded(nsAtom* aType) {
    880  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    881  if (MOZ_LOG_TEST(mozilla::gDataChannelLog, mozilla::LogLevel::Info)) {
    882    nsString name;
    883    aType->ToString(name);
    884    DC_INFO(
    885        ("%p: RTCDataChannel \"%s\" event listener added, calling "
    886         "UpdateMustKeepAlive.",
    887         this, NS_ConvertUTF16toUTF8(name).get()));
    888  }
    889  UpdateMustKeepAlive();
    890 }
    891 
    892 void RTCDataChannel::EventListenerRemoved(nsAtom* aType) {
    893  MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
    894  if (MOZ_LOG_TEST(mozilla::gDataChannelLog, mozilla::LogLevel::Info)) {
    895    nsString name;
    896    aType->ToString(name);
    897    DC_INFO(
    898        ("%p: RTCDataChannel \"%s\" event listener removed, calling "
    899         "UpdateMustKeepAlive.",
    900         this, NS_ConvertUTF16toUTF8(name).get()));
    901  }
    902  UpdateMustKeepAlive();
    903 }
    904 
    905 /* static */
    906 nsresult NS_NewDOMDataChannel(already_AddRefed<DataChannel>&& aDataChannel,
    907                              const nsACString& aLabel,
    908                              const nsAString& aOrigin, bool aOrdered,
    909                              Nullable<uint16_t> aMaxLifeTime,
    910                              Nullable<uint16_t> aMaxRetransmits,
    911                              const nsACString& aProtocol, bool aNegotiated,
    912                              nsPIDOMWindowInner* aWindow,
    913                              RTCDataChannel** aDomDataChannel) {
    914  RefPtr<RTCDataChannel> domdc = new RTCDataChannel(
    915      aLabel, aOrigin, aOrdered, aMaxLifeTime, aMaxRetransmits, aProtocol,
    916      aNegotiated, aDataChannel, aWindow);
    917 
    918  nsresult rv = domdc->Init();
    919  NS_ENSURE_SUCCESS(rv, rv);
    920 
    921  domdc.forget(aDomDataChannel);
    922  return NS_OK;
    923 }
    924 
    925 }  // end namespace dom
    926 }  // end namespace mozilla