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