Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include <cstdlib>
6 #include <cerrno>
7 #include <deque>
8 #include <sstream>
10 #include "base/histogram.h"
11 #include "vcm.h"
12 #include "CSFLog.h"
13 #include "timecard.h"
14 #include "ccapi_call_info.h"
15 #include "CC_SIPCCCallInfo.h"
16 #include "ccapi_device_info.h"
17 #include "CC_SIPCCDeviceInfo.h"
18 #include "cpr_string.h"
19 #include "cpr_stdlib.h"
21 #include "jsapi.h"
22 #include "nspr.h"
23 #include "nss.h"
24 #include "pk11pub.h"
26 #include "nsNetCID.h"
27 #include "nsIProperty.h"
28 #include "nsIPropertyBag2.h"
29 #include "nsIServiceManager.h"
30 #include "nsISimpleEnumerator.h"
31 #include "nsServiceManagerUtils.h"
32 #include "nsISocketTransportService.h"
33 #include "nsIConsoleService.h"
34 #include "nsThreadUtils.h"
35 #include "nsProxyRelease.h"
36 #include "prtime.h"
38 #include "AudioConduit.h"
39 #include "VideoConduit.h"
40 #include "runnable_utils.h"
41 #include "PeerConnectionCtx.h"
42 #include "PeerConnectionImpl.h"
43 #include "PeerConnectionMedia.h"
44 #include "nsDOMDataChannelDeclarations.h"
45 #include "dtlsidentity.h"
47 #ifdef MOZILLA_INTERNAL_API
48 #include "nsPerformance.h"
49 #include "nsGlobalWindow.h"
50 #include "nsDOMDataChannel.h"
51 #include "mozilla/TimeStamp.h"
52 #include "mozilla/Telemetry.h"
53 #include "mozilla/Preferences.h"
54 #include "mozilla/PublicSSL.h"
55 #include "nsXULAppAPI.h"
56 #include "nsContentUtils.h"
57 #include "nsDOMJSUtils.h"
58 #include "nsIDocument.h"
59 #include "nsIScriptError.h"
60 #include "nsPrintfCString.h"
61 #include "nsURLHelper.h"
62 #include "nsNetUtil.h"
63 #include "nsIDOMDataChannel.h"
64 #include "nsIDOMLocation.h"
65 #include "mozilla/dom/RTCConfigurationBinding.h"
66 #include "mozilla/dom/RTCStatsReportBinding.h"
67 #include "mozilla/dom/RTCPeerConnectionBinding.h"
68 #include "mozilla/dom/PeerConnectionImplBinding.h"
69 #include "mozilla/dom/DataChannelBinding.h"
70 #include "MediaStreamList.h"
71 #include "MediaStreamTrack.h"
72 #include "AudioStreamTrack.h"
73 #include "VideoStreamTrack.h"
74 #include "nsIScriptGlobalObject.h"
75 #include "DOMMediaStream.h"
76 #include "rlogringbuffer.h"
77 #include "WebrtcGlobalInformation.h"
78 #endif
80 #ifndef USE_FAKE_MEDIA_STREAMS
81 #include "MediaSegment.h"
82 #endif
84 #ifdef USE_FAKE_PCOBSERVER
85 #include "FakePCObserver.h"
86 #else
87 #include "mozilla/dom/PeerConnectionObserverBinding.h"
88 #endif
89 #include "mozilla/dom/PeerConnectionObserverEnumsBinding.h"
91 #define ICE_PARSING "In RTCConfiguration passed to RTCPeerConnection constructor"
93 using namespace mozilla;
94 using namespace mozilla::dom;
96 typedef PCObserverString ObString;
98 static const char* logTag = "PeerConnectionImpl";
100 #ifdef MOZILLA_INTERNAL_API
101 static nsresult InitNSSInContent()
102 {
103 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
105 if (XRE_GetProcessType() != GeckoProcessType_Content) {
106 MOZ_ASSUME_UNREACHABLE("Must be called in content process");
107 }
109 static bool nssStarted = false;
110 if (nssStarted) {
111 return NS_OK;
112 }
114 if (NSS_NoDB_Init(nullptr) != SECSuccess) {
115 CSFLogError(logTag, "NSS_NoDB_Init failed.");
116 return NS_ERROR_FAILURE;
117 }
119 if (NS_FAILED(mozilla::psm::InitializeCipherSuite())) {
120 CSFLogError(logTag, "Fail to set up nss cipher suite.");
121 return NS_ERROR_FAILURE;
122 }
124 mozilla::psm::DisableMD5();
126 nssStarted = true;
128 return NS_OK;
129 }
130 #endif // MOZILLA_INTERNAL_API
132 namespace mozilla {
133 class DataChannel;
134 }
136 class nsIDOMDataChannel;
138 static const int MEDIA_STREAM_MUTE = 0x80;
140 PRLogModuleInfo *signalingLogInfo() {
141 static PRLogModuleInfo *logModuleInfo = nullptr;
142 if (!logModuleInfo) {
143 logModuleInfo = PR_NewLogModule("signaling");
144 }
145 return logModuleInfo;
146 }
149 namespace sipcc {
151 #ifdef MOZILLA_INTERNAL_API
152 RTCStatsQuery::RTCStatsQuery(bool internal) : internalStats(internal) {
153 }
155 RTCStatsQuery::~RTCStatsQuery() {
156 MOZ_ASSERT(NS_IsMainThread());
157 }
159 #endif
161 // Getting exceptions back down from PCObserver is generally not harmful.
162 namespace {
163 class JSErrorResult : public ErrorResult
164 {
165 public:
166 ~JSErrorResult()
167 {
168 #ifdef MOZILLA_INTERNAL_API
169 WouldReportJSException();
170 if (IsJSException()) {
171 MOZ_ASSERT(NS_IsMainThread());
172 AutoJSContext cx;
173 Optional<JS::Handle<JS::Value> > value(cx);
174 StealJSException(cx, &value.Value());
175 }
176 #endif
177 }
178 };
180 // The WrapRunnable() macros copy passed-in args and passes them to the function
181 // later on the other thread. ErrorResult cannot be passed like this because it
182 // disallows copy-semantics.
183 //
184 // This WrappableJSErrorResult hack solves this by not actually copying the
185 // ErrorResult, but creating a new one instead, which works because we don't
186 // care about the result.
187 //
188 // Since this is for JS-calls, these can only be dispatched to the main thread.
190 class WrappableJSErrorResult {
191 public:
192 WrappableJSErrorResult() : isCopy(false) {}
193 WrappableJSErrorResult(WrappableJSErrorResult &other) : mRv(), isCopy(true) {}
194 ~WrappableJSErrorResult() {
195 if (isCopy) {
196 #ifdef MOZILLA_INTERNAL_API
197 MOZ_ASSERT(NS_IsMainThread());
198 #endif
199 }
200 }
201 operator JSErrorResult &() { return mRv; }
202 private:
203 JSErrorResult mRv;
204 bool isCopy;
205 };
206 }
208 class PeerConnectionObserverDispatch : public nsRunnable {
210 public:
211 PeerConnectionObserverDispatch(CSF::CC_CallInfoPtr aInfo,
212 nsRefPtr<PeerConnectionImpl> aPC,
213 PeerConnectionObserver* aObserver)
214 : mPC(aPC),
215 mObserver(aObserver),
216 mCode(static_cast<PeerConnectionImpl::Error>(aInfo->getStatusCode())),
217 mReason(aInfo->getStatus()),
218 mSdpStr(),
219 mCandidateStr(),
220 mCallState(aInfo->getCallState()),
221 mFsmState(aInfo->getFsmState()),
222 mStateStr(aInfo->callStateToString(mCallState)),
223 mFsmStateStr(aInfo->fsmStateToString(mFsmState)) {
224 if (mCallState == REMOTESTREAMADD) {
225 MediaStreamTable *streams = nullptr;
226 streams = aInfo->getMediaStreams();
227 mRemoteStream = mPC->media()->GetRemoteStream(streams->media_stream_id);
228 MOZ_ASSERT(mRemoteStream);
229 } else if (mCallState == FOUNDICECANDIDATE) {
230 mCandidateStr = aInfo->getCandidate();
231 } else if ((mCallState == CREATEOFFERSUCCESS) ||
232 (mCallState == CREATEANSWERSUCCESS)) {
233 mSdpStr = aInfo->getSDP();
234 }
235 }
237 ~PeerConnectionObserverDispatch(){}
239 #ifdef MOZILLA_INTERNAL_API
240 class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
241 {
242 public:
243 TracksAvailableCallback(DOMMediaStream::TrackTypeHints aTrackTypeHints,
244 nsRefPtr<PeerConnectionObserver> aObserver)
245 : DOMMediaStream::OnTracksAvailableCallback(aTrackTypeHints)
246 , mObserver(aObserver) {}
248 virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE
249 {
250 MOZ_ASSERT(NS_IsMainThread());
252 // Start currentTime from the point where this stream was successfully
253 // returned.
254 aStream->SetLogicalStreamStartTime(aStream->GetStream()->GetCurrentTime());
256 CSFLogInfo(logTag, "Returning success for OnAddStream()");
257 // We are running on main thread here so we shouldn't have a race
258 // on this callback
259 JSErrorResult rv;
260 mObserver->OnAddStream(*aStream, rv);
261 if (rv.Failed()) {
262 CSFLogError(logTag, ": OnAddStream() failed! Error: %d", rv.ErrorCode());
263 }
264 }
265 private:
266 nsRefPtr<PeerConnectionObserver> mObserver;
267 };
268 #endif
270 NS_IMETHOD Run() {
272 CSFLogInfo(logTag, "PeerConnectionObserverDispatch processing "
273 "mCallState = %d (%s), mFsmState = %d (%s)",
274 mCallState, mStateStr.c_str(), mFsmState, mFsmStateStr.c_str());
276 if (mCallState == SETLOCALDESCERROR || mCallState == SETREMOTEDESCERROR) {
277 const std::vector<std::string> &errors = mPC->GetSdpParseErrors();
278 std::vector<std::string>::const_iterator i;
279 for (i = errors.begin(); i != errors.end(); ++i) {
280 mReason += " | SDP Parsing Error: " + *i;
281 }
282 if (errors.size()) {
283 mCode = PeerConnectionImpl::kInvalidSessionDescription;
284 }
285 mPC->ClearSdpParseErrorMessages();
286 }
288 if (mReason.length()) {
289 CSFLogInfo(logTag, "Message contains error: %d: %s",
290 mCode, mReason.c_str());
291 }
293 /*
294 * While the fsm_states_t (FSM_DEF_*) constants are a proper superset
295 * of SignalingState, and the order in which the SignalingState values
296 * appear matches the order they appear in fsm_states_t, their underlying
297 * numeric representation is different. Hence, we need to perform an
298 * offset calculation to map from one to the other.
299 */
301 if (mFsmState >= FSMDEF_S_STABLE && mFsmState <= FSMDEF_S_CLOSED) {
302 int offset = FSMDEF_S_STABLE - int(PCImplSignalingState::SignalingStable);
303 mPC->SetSignalingState_m(static_cast<PCImplSignalingState>(mFsmState - offset));
304 } else {
305 CSFLogError(logTag, ": **** UNHANDLED SIGNALING STATE : %d (%s)",
306 mFsmState, mFsmStateStr.c_str());
307 }
309 JSErrorResult rv;
311 switch (mCallState) {
312 case CREATEOFFERSUCCESS:
313 mObserver->OnCreateOfferSuccess(ObString(mSdpStr.c_str()), rv);
314 break;
316 case CREATEANSWERSUCCESS:
317 mObserver->OnCreateAnswerSuccess(ObString(mSdpStr.c_str()), rv);
318 break;
320 case CREATEOFFERERROR:
321 mObserver->OnCreateOfferError(mCode, ObString(mReason.c_str()), rv);
322 break;
324 case CREATEANSWERERROR:
325 mObserver->OnCreateAnswerError(mCode, ObString(mReason.c_str()), rv);
326 break;
328 case SETLOCALDESCSUCCESS:
329 // TODO: The SDP Parse error list should be copied out and sent up
330 // to the Javascript layer before being cleared here. Even though
331 // there was not a failure, it is possible that the SDP parse generated
332 // warnings. The WebRTC spec does not currently have a mechanism for
333 // providing non-fatal warnings.
334 mPC->ClearSdpParseErrorMessages();
335 mObserver->OnSetLocalDescriptionSuccess(rv);
336 break;
338 case SETREMOTEDESCSUCCESS:
339 // TODO: The SDP Parse error list should be copied out and sent up
340 // to the Javascript layer before being cleared here. Even though
341 // there was not a failure, it is possible that the SDP parse generated
342 // warnings. The WebRTC spec does not currently have a mechanism for
343 // providing non-fatal warnings.
344 mPC->ClearSdpParseErrorMessages();
345 mObserver->OnSetRemoteDescriptionSuccess(rv);
346 #ifdef MOZILLA_INTERNAL_API
347 mPC->startCallTelem();
348 #endif
349 break;
351 case SETLOCALDESCERROR:
352 mObserver->OnSetLocalDescriptionError(mCode,
353 ObString(mReason.c_str()), rv);
354 break;
356 case SETREMOTEDESCERROR:
357 mObserver->OnSetRemoteDescriptionError(mCode,
358 ObString(mReason.c_str()), rv);
359 break;
361 case ADDICECANDIDATE:
362 mObserver->OnAddIceCandidateSuccess(rv);
363 break;
365 case ADDICECANDIDATEERROR:
366 mObserver->OnAddIceCandidateError(mCode, ObString(mReason.c_str()), rv);
367 break;
369 case FOUNDICECANDIDATE:
370 {
371 size_t end_of_level = mCandidateStr.find('\t');
372 if (end_of_level == std::string::npos) {
373 MOZ_ASSERT(false);
374 return NS_OK;
375 }
376 std::string level = mCandidateStr.substr(0, end_of_level);
377 if (!level.size()) {
378 MOZ_ASSERT(false);
379 return NS_OK;
380 }
381 char *endptr;
382 errno = 0;
383 unsigned long level_long =
384 strtoul(level.c_str(), &endptr, 10);
385 if (errno || *endptr != 0 || level_long > 65535) {
386 /* Conversion failure */
387 MOZ_ASSERT(false);
388 return NS_OK;
389 }
390 size_t end_of_mid = mCandidateStr.find('\t', end_of_level + 1);
391 if (end_of_mid == std::string::npos) {
392 MOZ_ASSERT(false);
393 return NS_OK;
394 }
396 std::string mid = mCandidateStr.substr(end_of_level + 1,
397 end_of_mid - (end_of_level + 1));
399 std::string candidate = mCandidateStr.substr(end_of_mid + 1);
401 mObserver->OnIceCandidate(level_long & 0xffff,
402 ObString(mid.c_str()),
403 ObString(candidate.c_str()), rv);
404 }
405 break;
406 case REMOTESTREAMADD:
407 {
408 DOMMediaStream* stream = nullptr;
410 if (!mRemoteStream) {
411 CSFLogError(logTag, "%s: GetRemoteStream returned NULL", __FUNCTION__);
412 } else {
413 stream = mRemoteStream->GetMediaStream();
414 }
416 if (!stream) {
417 CSFLogError(logTag, "%s: GetMediaStream returned NULL", __FUNCTION__);
418 } else {
419 #ifdef MOZILLA_INTERNAL_API
420 TracksAvailableCallback* tracksAvailableCallback =
421 new TracksAvailableCallback(mRemoteStream->mTrackTypeHints, mObserver);
423 stream->OnTracksAvailable(tracksAvailableCallback);
424 #else
425 mObserver->OnAddStream(stream, rv);
426 #endif
427 }
428 break;
429 }
431 case UPDATELOCALDESC:
432 /* No action necessary */
433 break;
435 default:
436 CSFLogError(logTag, ": **** UNHANDLED CALL STATE : %d (%s)",
437 mCallState, mStateStr.c_str());
438 break;
439 }
440 return NS_OK;
441 }
443 private:
444 nsRefPtr<PeerConnectionImpl> mPC;
445 nsRefPtr<PeerConnectionObserver> mObserver;
446 PeerConnectionImpl::Error mCode;
447 std::string mReason;
448 std::string mSdpStr;
449 std::string mCandidateStr;
450 cc_call_state_t mCallState;
451 fsmdef_states_t mFsmState;
452 std::string mStateStr;
453 std::string mFsmStateStr;
454 nsRefPtr<RemoteSourceStreamInfo> mRemoteStream;
455 };
457 NS_IMPL_ISUPPORTS0(PeerConnectionImpl)
459 #ifdef MOZILLA_INTERNAL_API
460 JSObject*
461 PeerConnectionImpl::WrapObject(JSContext* aCx)
462 {
463 return PeerConnectionImplBinding::Wrap(aCx, this);
464 }
465 #endif
467 struct PeerConnectionImpl::Internal {
468 CSF::CC_CallPtr mCall;
469 };
471 PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
472 : mTimeCard(PR_LOG_TEST(signalingLogInfo(),PR_LOG_ERROR) ?
473 create_timecard() : nullptr)
474 , mInternal(new Internal())
475 , mReadyState(PCImplReadyState::New)
476 , mSignalingState(PCImplSignalingState::SignalingStable)
477 , mIceConnectionState(PCImplIceConnectionState::New)
478 , mIceGatheringState(PCImplIceGatheringState::New)
479 , mWindow(nullptr)
480 , mIdentity(nullptr)
481 , mSTSThread(nullptr)
482 , mLoadManager(nullptr)
483 , mMedia(nullptr)
484 , mNumAudioStreams(0)
485 , mNumVideoStreams(0)
486 , mHaveDataStream(false)
487 , mTrickle(true) // TODO(ekr@rtfm.com): Use pref
488 {
489 #ifdef MOZILLA_INTERNAL_API
490 MOZ_ASSERT(NS_IsMainThread());
491 if (aGlobal) {
492 mWindow = do_QueryInterface(aGlobal->GetAsSupports());
493 }
494 #endif
495 MOZ_ASSERT(mInternal);
496 CSFLogInfo(logTag, "%s: PeerConnectionImpl constructor for %s",
497 __FUNCTION__, mHandle.c_str());
498 STAMP_TIMECARD(mTimeCard, "Constructor Completed");
499 }
501 PeerConnectionImpl::~PeerConnectionImpl()
502 {
503 if (mTimeCard) {
504 STAMP_TIMECARD(mTimeCard, "Destructor Invoked");
505 print_timecard(mTimeCard);
506 destroy_timecard(mTimeCard);
507 mTimeCard = nullptr;
508 }
509 // This aborts if not on main thread (in Debug builds)
510 PC_AUTO_ENTER_API_CALL_NO_CHECK();
511 if (PeerConnectionCtx::isActive()) {
512 PeerConnectionCtx::GetInstance()->mPeerConnections.erase(mHandle);
513 } else {
514 CSFLogError(logTag, "PeerConnectionCtx is already gone. Ignoring...");
515 }
517 CSFLogInfo(logTag, "%s: PeerConnectionImpl destructor invoked for %s",
518 __FUNCTION__, mHandle.c_str());
519 CloseInt();
521 #ifdef MOZILLA_INTERNAL_API
522 {
523 // Deregister as an NSS Shutdown Object
524 nsNSSShutDownPreventionLock locker;
525 if (!isAlreadyShutDown()) {
526 destructorSafeDestroyNSSReference();
527 shutdown(calledFromObject);
528 }
529 }
530 if (mLoadManager) {
531 mozilla::LoadManagerDestroy(mLoadManager);
532 mLoadManager = nullptr;
533 }
534 #endif
536 // Since this and Initialize() occur on MainThread, they can't both be
537 // running at once
539 // Right now, we delete PeerConnectionCtx at XPCOM shutdown only, but we
540 // probably want to shut it down more aggressively to save memory. We
541 // could shut down here when there are no uses. It might be more optimal
542 // to release off a timer (and XPCOM Shutdown) to avoid churn
543 }
545 already_AddRefed<DOMMediaStream>
546 PeerConnectionImpl::MakeMediaStream(nsPIDOMWindow* aWindow,
547 uint32_t aHint)
548 {
549 nsRefPtr<DOMMediaStream> stream =
550 DOMMediaStream::CreateSourceStream(aWindow, aHint);
551 #ifdef MOZILLA_INTERNAL_API
552 nsIDocument* doc = aWindow->GetExtantDoc();
553 if (!doc) {
554 return nullptr;
555 }
556 // Make the stream data (audio/video samples) accessible to the receiving page.
557 stream->CombineWithPrincipal(doc->NodePrincipal());
558 #endif
560 CSFLogDebug(logTag, "Created media stream %p, inner: %p", stream.get(), stream->GetStream());
562 return stream.forget();
563 }
565 nsresult
566 PeerConnectionImpl::CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo>*
567 aInfo)
568 {
569 MOZ_ASSERT(aInfo);
570 PC_AUTO_ENTER_API_CALL_NO_CHECK();
572 // We need to pass a dummy hint here because FakeMediaStream currently
573 // needs to actually propagate a hint for local streams.
574 // TODO(ekr@rtfm.com): Clean up when we have explicit track lists.
575 // See bug 834835.
576 nsRefPtr<DOMMediaStream> stream = MakeMediaStream(mWindow, 0);
577 if (!stream) {
578 return NS_ERROR_FAILURE;
579 }
581 static_cast<SourceMediaStream*>(stream->GetStream())->SetPullEnabled(true);
583 nsRefPtr<RemoteSourceStreamInfo> remote;
584 remote = new RemoteSourceStreamInfo(stream.forget(), mMedia);
585 *aInfo = remote;
587 return NS_OK;
588 }
590 /**
591 * In JS, an RTCConfiguration looks like this:
592 *
593 * { "iceServers": [ { url:"stun:stun.example.org" },
594 * { url:"turn:turn.example.org?transport=udp",
595 * username: "jib", credential:"mypass"} ] }
596 *
597 * This function converts that into an internal IceConfiguration object.
598 */
599 nsresult
600 PeerConnectionImpl::ConvertRTCConfiguration(const RTCConfiguration& aSrc,
601 IceConfiguration *aDst)
602 {
603 #ifdef MOZILLA_INTERNAL_API
604 if (!aSrc.mIceServers.WasPassed()) {
605 return NS_OK;
606 }
607 for (uint32_t i = 0; i < aSrc.mIceServers.Value().Length(); i++) {
608 const RTCIceServer& server = aSrc.mIceServers.Value()[i];
609 NS_ENSURE_TRUE(server.mUrl.WasPassed(), NS_ERROR_UNEXPECTED);
611 // Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than
612 // nsStandardURL. To parse STUN/TURN URI's to spec
613 // http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3
614 // http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3
615 // we parse out the query-string, and use ParseAuthority() on the rest
616 nsRefPtr<nsIURI> url;
617 nsresult rv = NS_NewURI(getter_AddRefs(url), server.mUrl.Value());
618 NS_ENSURE_SUCCESS(rv, rv);
619 bool isStun = false, isStuns = false, isTurn = false, isTurns = false;
620 url->SchemeIs("stun", &isStun);
621 url->SchemeIs("stuns", &isStuns);
622 url->SchemeIs("turn", &isTurn);
623 url->SchemeIs("turns", &isTurns);
624 if (!(isStun || isStuns || isTurn || isTurns)) {
625 return NS_ERROR_FAILURE;
626 }
627 nsAutoCString spec;
628 rv = url->GetSpec(spec);
629 NS_ENSURE_SUCCESS(rv, rv);
631 // TODO(jib@mozilla.com): Revisit once nsURI supports STUN/TURN (Bug 833509)
632 int32_t port;
633 nsAutoCString host;
634 nsAutoCString transport;
635 {
636 uint32_t hostPos;
637 int32_t hostLen;
638 nsAutoCString path;
639 rv = url->GetPath(path);
640 NS_ENSURE_SUCCESS(rv, rv);
642 // Tolerate query-string + parse 'transport=[udp|tcp]' by hand.
643 int32_t questionmark = path.FindChar('?');
644 if (questionmark >= 0) {
645 const nsCString match = NS_LITERAL_CSTRING("transport=");
647 for (int32_t i = questionmark, endPos; i >= 0; i = endPos) {
648 endPos = path.FindCharInSet("&", i + 1);
649 const nsDependentCSubstring fieldvaluepair = Substring(path, i + 1,
650 endPos);
651 if (StringBeginsWith(fieldvaluepair, match)) {
652 transport = Substring(fieldvaluepair, match.Length());
653 ToLowerCase(transport);
654 }
655 }
656 path.SetLength(questionmark);
657 }
659 rv = net_GetAuthURLParser()->ParseAuthority(path.get(), path.Length(),
660 nullptr, nullptr,
661 nullptr, nullptr,
662 &hostPos, &hostLen, &port);
663 NS_ENSURE_SUCCESS(rv, rv);
664 if (!hostLen) {
665 return NS_ERROR_FAILURE;
666 }
667 if (hostPos > 1) /* The username was removed */
668 return NS_ERROR_FAILURE;
669 path.Mid(host, hostPos, hostLen);
670 }
671 if (port == -1)
672 port = (isStuns || isTurns)? 5349 : 3478;
674 if (isTurn || isTurns) {
675 NS_ConvertUTF16toUTF8 credential(server.mCredential);
676 NS_ConvertUTF16toUTF8 username(server.mUsername);
678 #ifdef MOZ_WIDGET_GONK
679 if (transport == kNrIceTransportTcp)
680 continue;
681 #endif
682 if (!aDst->addTurnServer(host.get(), port,
683 username.get(),
684 credential.get(),
685 (transport.IsEmpty() ?
686 kNrIceTransportUdp : transport.get()))) {
687 return NS_ERROR_FAILURE;
688 }
689 } else {
690 if (!aDst->addStunServer(host.get(), port)) {
691 return NS_ERROR_FAILURE;
692 }
693 }
694 }
695 #endif
696 return NS_OK;
697 }
699 NS_IMETHODIMP
700 PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
701 nsGlobalWindow* aWindow,
702 const IceConfiguration* aConfiguration,
703 const RTCConfiguration* aRTCConfiguration,
704 nsISupports* aThread)
705 {
706 nsresult res;
708 // Invariant: we receive configuration one way or the other but not both (XOR)
709 MOZ_ASSERT(!aConfiguration != !aRTCConfiguration);
710 #ifdef MOZILLA_INTERNAL_API
711 MOZ_ASSERT(NS_IsMainThread());
712 #endif
713 MOZ_ASSERT(aThread);
714 mThread = do_QueryInterface(aThread);
716 mPCObserver = do_GetWeakReference(&aObserver);
718 // Find the STS thread
720 mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
721 MOZ_ASSERT(mSTSThread);
722 #ifdef MOZILLA_INTERNAL_API
724 // Initialize NSS if we are in content process. For chrome process, NSS should already
725 // been initialized.
726 if (XRE_GetProcessType() == GeckoProcessType_Default) {
727 // This code interferes with the C++ unit test startup code.
728 nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &res);
729 NS_ENSURE_SUCCESS(res, res);
730 } else {
731 NS_ENSURE_SUCCESS(res = InitNSSInContent(), res);
732 }
734 // Currently no standalone unit tests for DataChannel,
735 // which is the user of mWindow
736 MOZ_ASSERT(aWindow);
737 mWindow = aWindow;
738 NS_ENSURE_STATE(mWindow);
740 #endif // MOZILLA_INTERNAL_API
742 PRTime timestamp = PR_Now();
743 // Ok if we truncate this.
744 char temp[128];
746 #ifdef MOZILLA_INTERNAL_API
747 nsAutoCString locationCStr;
748 nsIDOMLocation* location;
749 res = mWindow->GetLocation(&location);
751 if (location && NS_SUCCEEDED(res)) {
752 nsAutoString locationAStr;
753 location->ToString(locationAStr);
754 location->Release();
756 CopyUTF16toUTF8(locationAStr, locationCStr);
757 }
759 PR_snprintf(
760 temp,
761 sizeof(temp),
762 "%llu (id=%llu url=%s)",
763 static_cast<unsigned long long>(timestamp),
764 static_cast<unsigned long long>(mWindow ? mWindow->WindowID() : 0),
765 locationCStr.get() ? locationCStr.get() : "NULL");
767 #else
768 PR_snprintf(temp, sizeof(temp), "%llu", (unsigned long long)timestamp);
769 #endif // MOZILLA_INTERNAL_API
771 mName = temp;
773 // Generate a random handle
774 unsigned char handle_bin[8];
775 SECStatus rv;
776 rv = PK11_GenerateRandom(handle_bin, sizeof(handle_bin));
777 if (rv != SECSuccess) {
778 MOZ_CRASH();
779 return NS_ERROR_UNEXPECTED;
780 }
782 char hex[17];
783 PR_snprintf(hex,sizeof(hex),"%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
784 handle_bin[0],
785 handle_bin[1],
786 handle_bin[2],
787 handle_bin[3],
788 handle_bin[4],
789 handle_bin[5],
790 handle_bin[6],
791 handle_bin[7]);
793 mHandle = hex;
795 STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx");
796 res = PeerConnectionCtx::InitializeGlobal(mThread, mSTSThread);
797 NS_ENSURE_SUCCESS(res, res);
799 PeerConnectionCtx *pcctx = PeerConnectionCtx::GetInstance();
800 MOZ_ASSERT(pcctx);
801 STAMP_TIMECARD(mTimeCard, "Done Initializing PC Ctx");
803 mInternal->mCall = pcctx->createCall();
804 if (!mInternal->mCall.get()) {
805 CSFLogError(logTag, "%s: Couldn't Create Call Object", __FUNCTION__);
806 return NS_ERROR_FAILURE;
807 }
809 IceConfiguration converted;
810 if (aRTCConfiguration) {
811 res = ConvertRTCConfiguration(*aRTCConfiguration, &converted);
812 if (NS_FAILED(res)) {
813 CSFLogError(logTag, "%s: Invalid RTCConfiguration", __FUNCTION__);
814 return res;
815 }
816 aConfiguration = &converted;
817 }
819 mMedia = new PeerConnectionMedia(this);
821 // Connect ICE slots.
822 mMedia->SignalIceGatheringStateChange.connect(
823 this,
824 &PeerConnectionImpl::IceGatheringStateChange);
825 mMedia->SignalIceConnectionStateChange.connect(
826 this,
827 &PeerConnectionImpl::IceConnectionStateChange);
829 // Initialize the media object.
830 res = mMedia->Init(aConfiguration->getStunServers(),
831 aConfiguration->getTurnServers());
832 if (NS_FAILED(res)) {
833 CSFLogError(logTag, "%s: Couldn't initialize media object", __FUNCTION__);
834 return res;
835 }
837 // Store under mHandle
838 mInternal->mCall->setPeerConnection(mHandle);
839 PeerConnectionCtx::GetInstance()->mPeerConnections[mHandle] = this;
841 STAMP_TIMECARD(mTimeCard, "Generating DTLS Identity");
842 // Create the DTLS Identity
843 mIdentity = DtlsIdentity::Generate();
844 STAMP_TIMECARD(mTimeCard, "Done Generating DTLS Identity");
846 if (!mIdentity) {
847 CSFLogError(logTag, "%s: Generate returned NULL", __FUNCTION__);
848 return NS_ERROR_FAILURE;
849 }
851 mFingerprint = mIdentity->GetFormattedFingerprint();
852 if (mFingerprint.empty()) {
853 CSFLogError(logTag, "%s: unable to get fingerprint", __FUNCTION__);
854 return res;
855 }
857 #ifdef MOZILLA_INTERNAL_API
858 if (mozilla::Preferences::GetBool("media.navigator.load_adapt", false)) {
859 mLoadManager = mozilla::LoadManagerBuild();
860 }
861 #endif
863 return NS_OK;
864 }
866 RefPtr<DtlsIdentity> const
867 PeerConnectionImpl::GetIdentity() const
868 {
869 PC_AUTO_ENTER_API_CALL_NO_CHECK();
870 return mIdentity;
871 }
873 std::string
874 PeerConnectionImpl::GetFingerprint() const
875 {
876 PC_AUTO_ENTER_API_CALL_NO_CHECK();
877 return mFingerprint;
878 }
880 NS_IMETHODIMP
881 PeerConnectionImpl::FingerprintSplitHelper(std::string& fingerprint,
882 size_t& spaceIdx) const
883 {
884 fingerprint = GetFingerprint();
885 spaceIdx = fingerprint.find_first_of(' ');
886 if (spaceIdx == std::string::npos) {
887 CSFLogError(logTag, "%s: fingerprint is messed up: %s",
888 __FUNCTION__, fingerprint.c_str());
889 return NS_ERROR_FAILURE;
890 }
891 return NS_OK;
892 }
894 std::string
895 PeerConnectionImpl::GetFingerprintAlgorithm() const
896 {
897 std::string fp;
898 size_t spc;
899 if (NS_SUCCEEDED(FingerprintSplitHelper(fp, spc))) {
900 return fp.substr(0, spc);
901 }
902 return "";
903 }
905 std::string
906 PeerConnectionImpl::GetFingerprintHexValue() const
907 {
908 std::string fp;
909 size_t spc;
910 if (NS_SUCCEEDED(FingerprintSplitHelper(fp, spc))) {
911 return fp.substr(spc + 1);
912 }
913 return "";
914 }
917 nsresult
918 PeerConnectionImpl::CreateFakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aRetval)
919 {
920 MOZ_ASSERT(aRetval);
921 PC_AUTO_ENTER_API_CALL(false);
923 bool mute = false;
925 // Hack to allow you to mute the stream
926 if (aHint & MEDIA_STREAM_MUTE) {
927 mute = true;
928 aHint &= ~MEDIA_STREAM_MUTE;
929 }
931 nsRefPtr<DOMMediaStream> stream = MakeMediaStream(mWindow, aHint);
932 if (!stream) {
933 return NS_ERROR_FAILURE;
934 }
936 if (!mute) {
937 if (aHint & DOMMediaStream::HINT_CONTENTS_AUDIO) {
938 new Fake_AudioGenerator(stream);
939 } else {
940 #ifdef MOZILLA_INTERNAL_API
941 new Fake_VideoGenerator(stream);
942 #endif
943 }
944 }
946 stream.forget(aRetval);
947 return NS_OK;
948 }
950 // Stubbing this call out for now.
951 // We can remove it when we are confident of datachannels being started
952 // correctly on SDP negotiation (bug 852908)
953 NS_IMETHODIMP
954 PeerConnectionImpl::ConnectDataConnection(uint16_t aLocalport,
955 uint16_t aRemoteport,
956 uint16_t aNumstreams)
957 {
958 return NS_OK; // InitializeDataChannel(aLocalport, aRemoteport, aNumstreams);
959 }
961 // Data channels won't work without a window, so in order for the C++ unit
962 // tests to work (it doesn't have a window available) we ifdef the following
963 // two implementations.
964 NS_IMETHODIMP
965 PeerConnectionImpl::EnsureDataConnection(uint16_t aNumstreams)
966 {
967 PC_AUTO_ENTER_API_CALL_NO_CHECK();
969 #ifdef MOZILLA_INTERNAL_API
970 if (mDataConnection) {
971 CSFLogDebug(logTag,"%s DataConnection already connected",__FUNCTION__);
972 // Ignore the request to connect when already connected. This entire
973 // implementation is temporary. Ignore aNumstreams as it's merely advisory
974 // and we increase the number of streams dynamically as needed.
975 return NS_OK;
976 }
977 mDataConnection = new DataChannelConnection(this);
978 if (!mDataConnection->Init(5000, aNumstreams, true)) {
979 CSFLogError(logTag,"%s DataConnection Init Failed",__FUNCTION__);
980 return NS_ERROR_FAILURE;
981 }
982 CSFLogDebug(logTag,"%s DataChannelConnection %p attached to %s",
983 __FUNCTION__, (void*) mDataConnection.get(), mHandle.c_str());
984 #endif
985 return NS_OK;
986 }
988 nsresult
989 PeerConnectionImpl::InitializeDataChannel(int track_id,
990 uint16_t aLocalport,
991 uint16_t aRemoteport,
992 uint16_t aNumstreams)
993 {
994 PC_AUTO_ENTER_API_CALL_NO_CHECK();
996 #ifdef MOZILLA_INTERNAL_API
997 nsresult rv = EnsureDataConnection(aNumstreams);
998 if (NS_SUCCEEDED(rv)) {
999 // use the specified TransportFlow
1000 nsRefPtr<TransportFlow> flow = mMedia->GetTransportFlow(track_id, false).get();
1001 CSFLogDebug(logTag, "Transportflow[%d] = %p", track_id, flow.get());
1002 if (flow) {
1003 if (mDataConnection->ConnectViaTransportFlow(flow, aLocalport, aRemoteport)) {
1004 return NS_OK;
1005 }
1006 }
1007 // If we inited the DataConnection, call Destroy() before releasing it
1008 mDataConnection->Destroy();
1009 }
1010 mDataConnection = nullptr;
1011 #endif
1012 return NS_ERROR_FAILURE;
1013 }
1015 already_AddRefed<nsDOMDataChannel>
1016 PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel,
1017 const nsAString& aProtocol,
1018 uint16_t aType,
1019 bool outOfOrderAllowed,
1020 uint16_t aMaxTime,
1021 uint16_t aMaxNum,
1022 bool aExternalNegotiated,
1023 uint16_t aStream,
1024 ErrorResult &rv)
1025 {
1026 #ifdef MOZILLA_INTERNAL_API
1027 nsRefPtr<nsDOMDataChannel> result;
1028 rv = CreateDataChannel(aLabel, aProtocol, aType, outOfOrderAllowed,
1029 aMaxTime, aMaxNum, aExternalNegotiated,
1030 aStream, getter_AddRefs(result));
1031 return result.forget();
1032 #else
1033 return nullptr;
1034 #endif
1035 }
1037 NS_IMETHODIMP
1038 PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel,
1039 const nsAString& aProtocol,
1040 uint16_t aType,
1041 bool outOfOrderAllowed,
1042 uint16_t aMaxTime,
1043 uint16_t aMaxNum,
1044 bool aExternalNegotiated,
1045 uint16_t aStream,
1046 nsDOMDataChannel** aRetval)
1047 {
1048 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1049 MOZ_ASSERT(aRetval);
1051 #ifdef MOZILLA_INTERNAL_API
1052 nsRefPtr<DataChannel> dataChannel;
1053 DataChannelConnection::Type theType =
1054 static_cast<DataChannelConnection::Type>(aType);
1056 nsresult rv = EnsureDataConnection(WEBRTC_DATACHANNEL_STREAMS_DEFAULT);
1057 if (NS_FAILED(rv)) {
1058 return rv;
1059 }
1060 dataChannel = mDataConnection->Open(
1061 NS_ConvertUTF16toUTF8(aLabel), NS_ConvertUTF16toUTF8(aProtocol), theType,
1062 !outOfOrderAllowed,
1063 aType == DataChannelConnection::PARTIAL_RELIABLE_REXMIT ? aMaxNum :
1064 (aType == DataChannelConnection::PARTIAL_RELIABLE_TIMED ? aMaxTime : 0),
1065 nullptr, nullptr, aExternalNegotiated, aStream
1066 );
1067 NS_ENSURE_TRUE(dataChannel,NS_ERROR_FAILURE);
1069 CSFLogDebug(logTag, "%s: making DOMDataChannel", __FUNCTION__);
1071 if (!mHaveDataStream) {
1072 // XXX stream_id of 0 might confuse things...
1073 mInternal->mCall->addStream(0, 2, DATA, 0);
1074 mHaveDataStream = true;
1075 }
1076 nsIDOMDataChannel *retval;
1077 rv = NS_NewDOMDataChannel(dataChannel.forget(), mWindow, &retval);
1078 if (NS_FAILED(rv)) {
1079 return rv;
1080 }
1081 *aRetval = static_cast<nsDOMDataChannel*>(retval);
1082 #endif
1083 return NS_OK;
1084 }
1086 // do_QueryObjectReferent() - Helps get PeerConnectionObserver from nsWeakPtr.
1087 //
1088 // nsWeakPtr deals in XPCOM interfaces, while webidl bindings are concrete objs.
1089 // TODO: Turn this into a central (template) function somewhere (Bug 939178)
1090 //
1091 // Without it, each weak-ref call in this file would look like this:
1092 //
1093 // nsCOMPtr<nsISupportsWeakReference> tmp = do_QueryReferent(mPCObserver);
1094 // if (!tmp) {
1095 // return;
1096 // }
1097 // nsRefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp);
1098 // nsRefPtr<PeerConnectionObserver> pco = static_cast<PeerConnectionObserver*>(&*tmp2);
1100 static already_AddRefed<PeerConnectionObserver>
1101 do_QueryObjectReferent(nsIWeakReference* aRawPtr) {
1102 nsCOMPtr<nsISupportsWeakReference> tmp = do_QueryReferent(aRawPtr);
1103 if (!tmp) {
1104 return nullptr;
1105 }
1106 nsRefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp);
1107 nsRefPtr<PeerConnectionObserver> tmp3 = static_cast<PeerConnectionObserver*>(&*tmp2);
1108 return tmp3.forget();
1109 }
1111 void
1112 PeerConnectionImpl::NotifyConnection()
1113 {
1114 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1116 CSFLogDebug(logTag, "%s", __FUNCTION__);
1118 #ifdef MOZILLA_INTERNAL_API
1119 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1120 if (!pco) {
1121 return;
1122 }
1123 WrappableJSErrorResult rv;
1124 RUN_ON_THREAD(mThread,
1125 WrapRunnable(pco,
1126 &PeerConnectionObserver::NotifyConnection,
1127 rv, static_cast<JSCompartment*>(nullptr)),
1128 NS_DISPATCH_NORMAL);
1129 #endif
1130 }
1132 void
1133 PeerConnectionImpl::NotifyClosedConnection()
1134 {
1135 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1137 CSFLogDebug(logTag, "%s", __FUNCTION__);
1139 #ifdef MOZILLA_INTERNAL_API
1140 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1141 if (!pco) {
1142 return;
1143 }
1144 WrappableJSErrorResult rv;
1145 RUN_ON_THREAD(mThread,
1146 WrapRunnable(pco, &PeerConnectionObserver::NotifyClosedConnection,
1147 rv, static_cast<JSCompartment*>(nullptr)),
1148 NS_DISPATCH_NORMAL);
1149 #endif
1150 }
1153 #ifdef MOZILLA_INTERNAL_API
1154 // Not a member function so that we don't need to keep the PC live.
1155 static void NotifyDataChannel_m(nsRefPtr<nsIDOMDataChannel> aChannel,
1156 nsRefPtr<PeerConnectionObserver> aObserver)
1157 {
1158 MOZ_ASSERT(NS_IsMainThread());
1159 JSErrorResult rv;
1160 nsRefPtr<nsDOMDataChannel> channel = static_cast<nsDOMDataChannel*>(&*aChannel);
1161 aObserver->NotifyDataChannel(*channel, rv);
1162 NS_DataChannelAppReady(aChannel);
1163 }
1164 #endif
1166 void
1167 PeerConnectionImpl::NotifyDataChannel(already_AddRefed<DataChannel> aChannel)
1168 {
1169 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1171 // XXXkhuey this is completely fucked up. We can't use nsRefPtr<DataChannel>
1172 // here because DataChannel's AddRef/Release are non-virtual and not visible
1173 // if !MOZILLA_INTERNAL_API, but this function leaks the DataChannel if
1174 // !MOZILLA_INTERNAL_API because it never transfers the ref to
1175 // NS_NewDOMDataChannel.
1176 DataChannel* channel = aChannel.take();
1177 MOZ_ASSERT(channel);
1179 CSFLogDebug(logTag, "%s: channel: %p", __FUNCTION__, channel);
1181 #ifdef MOZILLA_INTERNAL_API
1182 nsCOMPtr<nsIDOMDataChannel> domchannel;
1183 nsresult rv = NS_NewDOMDataChannel(already_AddRefed<DataChannel>(channel),
1184 mWindow, getter_AddRefs(domchannel));
1185 NS_ENSURE_SUCCESS_VOID(rv);
1187 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1188 if (!pco) {
1189 return;
1190 }
1192 RUN_ON_THREAD(mThread,
1193 WrapRunnableNM(NotifyDataChannel_m,
1194 domchannel.get(),
1195 pco),
1196 NS_DISPATCH_NORMAL);
1197 #endif
1198 }
1200 NS_IMETHODIMP
1201 PeerConnectionImpl::CreateOffer(const MediaConstraintsInternal& aConstraints)
1202 {
1203 return CreateOffer(MediaConstraintsExternal (aConstraints));
1204 }
1206 // Used by unit tests and the IDL CreateOffer.
1207 NS_IMETHODIMP
1208 PeerConnectionImpl::CreateOffer(const MediaConstraintsExternal& aConstraints)
1209 {
1210 PC_AUTO_ENTER_API_CALL(true);
1212 Timecard *tc = mTimeCard;
1213 mTimeCard = nullptr;
1214 STAMP_TIMECARD(tc, "Create Offer");
1216 cc_media_constraints_t* cc_constraints = aConstraints.build();
1217 NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED);
1218 mInternal->mCall->createOffer(cc_constraints, tc);
1219 return NS_OK;
1220 }
1222 NS_IMETHODIMP
1223 PeerConnectionImpl::CreateAnswer(const MediaConstraintsInternal& aConstraints)
1224 {
1225 return CreateAnswer(MediaConstraintsExternal (aConstraints));
1226 }
1228 NS_IMETHODIMP
1229 PeerConnectionImpl::CreateAnswer(const MediaConstraintsExternal& aConstraints)
1230 {
1231 PC_AUTO_ENTER_API_CALL(true);
1233 Timecard *tc = mTimeCard;
1234 mTimeCard = nullptr;
1235 STAMP_TIMECARD(tc, "Create Answer");
1237 cc_media_constraints_t* cc_constraints = aConstraints.build();
1238 NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED);
1239 mInternal->mCall->createAnswer(cc_constraints, tc);
1240 return NS_OK;
1241 }
1243 NS_IMETHODIMP
1244 PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
1245 {
1246 PC_AUTO_ENTER_API_CALL(true);
1248 if (!aSDP) {
1249 CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
1250 return NS_ERROR_FAILURE;
1251 }
1253 Timecard *tc = mTimeCard;
1254 mTimeCard = nullptr;
1255 STAMP_TIMECARD(tc, "Set Local Description");
1257 mLocalRequestedSDP = aSDP;
1258 mInternal->mCall->setLocalDescription((cc_jsep_action_t)aAction,
1259 mLocalRequestedSDP, tc);
1260 return NS_OK;
1261 }
1263 NS_IMETHODIMP
1264 PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
1265 {
1266 PC_AUTO_ENTER_API_CALL(true);
1268 if (!aSDP) {
1269 CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
1270 return NS_ERROR_FAILURE;
1271 }
1273 Timecard *tc = mTimeCard;
1274 mTimeCard = nullptr;
1275 STAMP_TIMECARD(tc, "Set Remote Description");
1277 mRemoteRequestedSDP = aSDP;
1278 mInternal->mCall->setRemoteDescription((cc_jsep_action_t)action,
1279 mRemoteRequestedSDP, tc);
1280 return NS_OK;
1281 }
1283 // WebRTC uses highres time relative to the UNIX epoch (Jan 1, 1970, UTC).
1285 #ifdef MOZILLA_INTERNAL_API
1286 nsresult
1287 PeerConnectionImpl::GetTimeSinceEpoch(DOMHighResTimeStamp *result) {
1288 MOZ_ASSERT(NS_IsMainThread());
1289 nsPerformance *perf = mWindow->GetPerformance();
1290 NS_ENSURE_TRUE(perf && perf->Timing(), NS_ERROR_UNEXPECTED);
1291 *result = perf->Now() + perf->Timing()->NavigationStart();
1292 return NS_OK;
1293 }
1295 class RTCStatsReportInternalConstruct : public RTCStatsReportInternal {
1296 public:
1297 RTCStatsReportInternalConstruct(const nsString &pcid, DOMHighResTimeStamp now) {
1298 mPcid = pcid;
1299 mInboundRTPStreamStats.Construct();
1300 mOutboundRTPStreamStats.Construct();
1301 mMediaStreamTrackStats.Construct();
1302 mMediaStreamStats.Construct();
1303 mTransportStats.Construct();
1304 mIceComponentStats.Construct();
1305 mIceCandidatePairStats.Construct();
1306 mIceCandidateStats.Construct();
1307 mCodecStats.Construct();
1308 }
1309 };
1311 // Specialized helper - push map[key] if specified or all map values onto array
1313 static void
1314 PushBackSelect(nsTArray<RefPtr<MediaPipeline>>& aDst,
1315 const std::map<TrackID, RefPtr<mozilla::MediaPipeline>> & aSrc,
1316 TrackID aKey = 0) {
1317 auto begin = aKey ? aSrc.find(aKey) : aSrc.begin(), it = begin;
1318 for (auto end = (aKey && begin != aSrc.end())? ++begin : aSrc.end();
1319 it != end; ++it) {
1320 aDst.AppendElement(it->second);
1321 }
1322 }
1323 #endif
1325 NS_IMETHODIMP
1326 PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) {
1327 PC_AUTO_ENTER_API_CALL(true);
1329 #ifdef MOZILLA_INTERNAL_API
1330 if (!mMedia) {
1331 // Since we zero this out before the d'tor, we should check.
1332 return NS_ERROR_UNEXPECTED;
1333 }
1335 nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(false));
1337 nsresult rv = BuildStatsQuery_m(aSelector, query.get());
1339 NS_ENSURE_SUCCESS(rv, rv);
1341 RUN_ON_THREAD(mSTSThread,
1342 WrapRunnableNM(&PeerConnectionImpl::GetStatsForPCObserver_s,
1343 mHandle,
1344 query),
1345 NS_DISPATCH_NORMAL);
1346 #endif
1347 return NS_OK;
1348 }
1350 NS_IMETHODIMP
1351 PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) {
1352 PC_AUTO_ENTER_API_CALL(true);
1354 Timecard *tc = mTimeCard;
1355 mTimeCard = nullptr;
1356 STAMP_TIMECARD(tc, "Add Ice Candidate");
1358 #ifdef MOZILLA_INTERNAL_API
1359 // When remote candidates are added before our ICE ctx is up and running
1360 // (the transition to New is async through STS, so this is not impossible),
1361 // we won't record them as trickle candidates. Is this what we want?
1362 if(!mIceStartTime.IsNull()) {
1363 TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime;
1364 if (mIceConnectionState == PCImplIceConnectionState::Failed) {
1365 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_LATE_TRICKLE_ARRIVAL_TIME,
1366 timeDelta.ToMilliseconds());
1367 } else {
1368 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_ON_TIME_TRICKLE_ARRIVAL_TIME,
1369 timeDelta.ToMilliseconds());
1370 }
1371 }
1372 #endif
1374 mInternal->mCall->addICECandidate(aCandidate, aMid, aLevel, tc);
1375 return NS_OK;
1376 }
1378 NS_IMETHODIMP
1379 PeerConnectionImpl::CloseStreams() {
1380 PC_AUTO_ENTER_API_CALL(false);
1382 if (mReadyState != PCImplReadyState::Closed) {
1383 ChangeReadyState(PCImplReadyState::Closing);
1384 }
1386 CSFLogInfo(logTag, "%s: Ending associated call", __FUNCTION__);
1388 mInternal->mCall->endCall();
1389 return NS_OK;
1390 }
1392 NS_IMETHODIMP
1393 PeerConnectionImpl::AddStream(DOMMediaStream &aMediaStream,
1394 const MediaConstraintsInternal& aConstraints)
1395 {
1396 return AddStream(aMediaStream, MediaConstraintsExternal(aConstraints));
1397 }
1399 NS_IMETHODIMP
1400 PeerConnectionImpl::AddStream(DOMMediaStream& aMediaStream,
1401 const MediaConstraintsExternal& aConstraints) {
1402 PC_AUTO_ENTER_API_CALL(true);
1404 uint32_t hints = aMediaStream.GetHintContents();
1406 // XXX Remove this check once addStream has an error callback
1407 // available and/or we have plumbing to handle multiple
1408 // local audio streams.
1409 if ((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) &&
1410 mNumAudioStreams > 0) {
1411 CSFLogError(logTag, "%s: Only one local audio stream is supported for now",
1412 __FUNCTION__);
1413 return NS_ERROR_FAILURE;
1414 }
1416 // XXX Remove this check once addStream has an error callback
1417 // available and/or we have plumbing to handle multiple
1418 // local video streams.
1419 if ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) &&
1420 mNumVideoStreams > 0) {
1421 CSFLogError(logTag, "%s: Only one local video stream is supported for now",
1422 __FUNCTION__);
1423 return NS_ERROR_FAILURE;
1424 }
1426 uint32_t stream_id;
1427 nsresult res = mMedia->AddStream(&aMediaStream, &stream_id);
1428 if (NS_FAILED(res))
1429 return res;
1431 // TODO(ekr@rtfm.com): these integers should be the track IDs
1432 if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
1433 cc_media_constraints_t* cc_constraints = aConstraints.build();
1434 NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED);
1435 mInternal->mCall->addStream(stream_id, 0, AUDIO, cc_constraints);
1436 mNumAudioStreams++;
1437 }
1439 if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
1440 cc_media_constraints_t* cc_constraints = aConstraints.build();
1441 NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED);
1442 mInternal->mCall->addStream(stream_id, 1, VIDEO, cc_constraints);
1443 mNumVideoStreams++;
1444 }
1446 return NS_OK;
1447 }
1449 NS_IMETHODIMP
1450 PeerConnectionImpl::RemoveStream(DOMMediaStream& aMediaStream) {
1451 PC_AUTO_ENTER_API_CALL(true);
1453 uint32_t stream_id;
1454 nsresult res = mMedia->RemoveStream(&aMediaStream, &stream_id);
1456 if (NS_FAILED(res))
1457 return res;
1459 uint32_t hints = aMediaStream.GetHintContents();
1461 if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
1462 mInternal->mCall->removeStream(stream_id, 0, AUDIO);
1463 MOZ_ASSERT(mNumAudioStreams > 0);
1464 mNumAudioStreams--;
1465 }
1467 if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
1468 mInternal->mCall->removeStream(stream_id, 1, VIDEO);
1469 MOZ_ASSERT(mNumVideoStreams > 0);
1470 mNumVideoStreams--;
1471 }
1473 return NS_OK;
1474 }
1476 /*
1477 NS_IMETHODIMP
1478 PeerConnectionImpl::SetRemoteFingerprint(const char* hash, const char* fingerprint)
1479 {
1480 MOZ_ASSERT(hash);
1481 MOZ_ASSERT(fingerprint);
1483 if (fingerprint != nullptr && (strcmp(hash, "sha-1") == 0)) {
1484 mRemoteFingerprint = std::string(fingerprint);
1485 CSFLogDebug(logTag, "Setting remote fingerprint to %s", mRemoteFingerprint.c_str());
1486 return NS_OK;
1487 } else {
1488 CSFLogError(logTag, "%s: Invalid Remote Finger Print", __FUNCTION__);
1489 return NS_ERROR_FAILURE;
1490 }
1491 }
1492 */
1494 NS_IMETHODIMP
1495 PeerConnectionImpl::GetFingerprint(char** fingerprint)
1496 {
1497 MOZ_ASSERT(fingerprint);
1499 if (!mIdentity) {
1500 return NS_ERROR_FAILURE;
1501 }
1503 char* tmp = new char[mFingerprint.size() + 1];
1504 std::copy(mFingerprint.begin(), mFingerprint.end(), tmp);
1505 tmp[mFingerprint.size()] = '\0';
1507 *fingerprint = tmp;
1508 return NS_OK;
1509 }
1511 NS_IMETHODIMP
1512 PeerConnectionImpl::GetLocalDescription(char** aSDP)
1513 {
1514 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1515 MOZ_ASSERT(aSDP);
1517 char* tmp = new char[mLocalSDP.size() + 1];
1518 std::copy(mLocalSDP.begin(), mLocalSDP.end(), tmp);
1519 tmp[mLocalSDP.size()] = '\0';
1521 *aSDP = tmp;
1522 return NS_OK;
1523 }
1525 NS_IMETHODIMP
1526 PeerConnectionImpl::GetRemoteDescription(char** aSDP)
1527 {
1528 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1529 MOZ_ASSERT(aSDP);
1531 char* tmp = new char[mRemoteSDP.size() + 1];
1532 std::copy(mRemoteSDP.begin(), mRemoteSDP.end(), tmp);
1533 tmp[mRemoteSDP.size()] = '\0';
1535 *aSDP = tmp;
1536 return NS_OK;
1537 }
1539 NS_IMETHODIMP
1540 PeerConnectionImpl::ReadyState(PCImplReadyState* aState)
1541 {
1542 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1543 MOZ_ASSERT(aState);
1545 *aState = mReadyState;
1546 return NS_OK;
1547 }
1549 NS_IMETHODIMP
1550 PeerConnectionImpl::SignalingState(PCImplSignalingState* aState)
1551 {
1552 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1553 MOZ_ASSERT(aState);
1555 *aState = mSignalingState;
1556 return NS_OK;
1557 }
1559 NS_IMETHODIMP
1560 PeerConnectionImpl::SipccState(PCImplSipccState* aState)
1561 {
1562 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1563 MOZ_ASSERT(aState);
1565 PeerConnectionCtx* pcctx = PeerConnectionCtx::GetInstance();
1566 // Avoid B2G error: operands to ?: have different types
1567 // 'mozilla::dom::PCImplSipccState' and 'mozilla::dom::PCImplSipccState::Enum'
1568 if (pcctx) {
1569 *aState = pcctx->sipcc_state();
1570 } else {
1571 *aState = PCImplSipccState::Idle;
1572 }
1573 return NS_OK;
1574 }
1576 NS_IMETHODIMP
1577 PeerConnectionImpl::IceConnectionState(PCImplIceConnectionState* aState)
1578 {
1579 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1580 MOZ_ASSERT(aState);
1582 *aState = mIceConnectionState;
1583 return NS_OK;
1584 }
1586 NS_IMETHODIMP
1587 PeerConnectionImpl::IceGatheringState(PCImplIceGatheringState* aState)
1588 {
1589 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1590 MOZ_ASSERT(aState);
1592 *aState = mIceGatheringState;
1593 return NS_OK;
1594 }
1596 nsresult
1597 PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const
1598 {
1599 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1600 MOZ_ASSERT(mTrickle || !assert_ice_ready ||
1601 (mIceGatheringState == PCImplIceGatheringState::Complete));
1603 if (mReadyState == PCImplReadyState::Closed) {
1604 CSFLogError(logTag, "%s: called API while closed", __FUNCTION__);
1605 return NS_ERROR_FAILURE;
1606 }
1607 if (!mMedia) {
1608 CSFLogError(logTag, "%s: called API with disposed mMedia", __FUNCTION__);
1609 return NS_ERROR_FAILURE;
1610 }
1611 return NS_OK;
1612 }
1614 NS_IMETHODIMP
1615 PeerConnectionImpl::Close()
1616 {
1617 CSFLogDebug(logTag, "%s: for %s", __FUNCTION__, mHandle.c_str());
1618 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1620 nsresult res = CloseInt();
1622 SetSignalingState_m(PCImplSignalingState::SignalingClosed);
1624 return res;
1625 }
1628 nsresult
1629 PeerConnectionImpl::CloseInt()
1630 {
1631 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1633 // We do this at the end of the call because we want to make sure we've waited
1634 // for all trickle ICE candidates to come in; this can happen well after we've
1635 // transitioned to connected. As a bonus, this allows us to detect race
1636 // conditions where a stats dispatch happens right as the PC closes.
1637 if (!IsClosed()) {
1638 RecordLongtermICEStatistics();
1639 }
1641 if (mInternal->mCall) {
1642 CSFLogInfo(logTag, "%s: Closing PeerConnectionImpl %s; "
1643 "ending call", __FUNCTION__, mHandle.c_str());
1644 mInternal->mCall->endCall();
1645 }
1646 #ifdef MOZILLA_INTERNAL_API
1647 if (mDataConnection) {
1648 CSFLogInfo(logTag, "%s: Destroying DataChannelConnection %p for %s",
1649 __FUNCTION__, (void *) mDataConnection.get(), mHandle.c_str());
1650 mDataConnection->Destroy();
1651 mDataConnection = nullptr; // it may not go away until the runnables are dead
1652 }
1653 #endif
1655 ShutdownMedia();
1657 // DataConnection will need to stay alive until all threads/runnables exit
1659 return NS_OK;
1660 }
1662 void
1663 PeerConnectionImpl::ShutdownMedia()
1664 {
1665 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1667 if (!mMedia)
1668 return;
1670 #ifdef MOZILLA_INTERNAL_API
1671 // End of call to be recorded in Telemetry
1672 if (!mStartTime.IsNull()){
1673 TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
1674 Telemetry::Accumulate(Telemetry::WEBRTC_CALL_DURATION, timeDelta.ToSeconds());
1675 }
1676 #endif
1678 // Forget the reference so that we can transfer it to
1679 // SelfDestruct().
1680 mMedia.forget().take()->SelfDestruct();
1681 }
1683 #ifdef MOZILLA_INTERNAL_API
1684 // If NSS is shutting down, then we need to get rid of the DTLS
1685 // identity right now; otherwise, we'll cause wreckage when we do
1686 // finally deallocate it in our destructor.
1687 void
1688 PeerConnectionImpl::virtualDestroyNSSReference()
1689 {
1690 destructorSafeDestroyNSSReference();
1691 }
1693 void
1694 PeerConnectionImpl::destructorSafeDestroyNSSReference()
1695 {
1696 MOZ_ASSERT(NS_IsMainThread());
1697 CSFLogDebug(logTag, "%s: NSS shutting down; freeing our DtlsIdentity.", __FUNCTION__);
1698 mIdentity = nullptr;
1699 }
1700 #endif
1702 void
1703 PeerConnectionImpl::onCallEvent(const OnCallEventArgs& args)
1704 {
1705 const ccapi_call_event_e &aCallEvent = args.mCallEvent;
1706 const CSF::CC_CallInfoPtr &aInfo = args.mInfo;
1708 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1709 MOZ_ASSERT(aInfo.get());
1711 cc_call_state_t event = aInfo->getCallState();
1712 std::string statestr = aInfo->callStateToString(event);
1713 Timecard *timecard = aInfo->takeTimecard();
1715 if (timecard) {
1716 mTimeCard = timecard;
1717 STAMP_TIMECARD(mTimeCard, "Operation Completed");
1718 }
1720 if (CCAPI_CALL_EV_CREATED != aCallEvent && CCAPI_CALL_EV_STATE != aCallEvent) {
1721 CSFLogDebug(logTag, "%s: **** CALL HANDLE IS: %s, **** CALL STATE IS: %s",
1722 __FUNCTION__, mHandle.c_str(), statestr.c_str());
1723 return;
1724 }
1726 switch (event) {
1727 case SETLOCALDESCSUCCESS:
1728 case UPDATELOCALDESC:
1729 mLocalSDP = aInfo->getSDP();
1730 break;
1732 case SETREMOTEDESCSUCCESS:
1733 case ADDICECANDIDATE:
1734 mRemoteSDP = aInfo->getSDP();
1735 break;
1737 case CONNECTED:
1738 CSFLogDebug(logTag, "Setting PeerConnnection state to kActive");
1739 ChangeReadyState(PCImplReadyState::Active);
1740 break;
1741 default:
1742 break;
1743 }
1745 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1746 if (!pco) {
1747 return;
1748 }
1750 PeerConnectionObserverDispatch* runnable =
1751 new PeerConnectionObserverDispatch(aInfo, this, pco);
1753 if (mThread) {
1754 mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
1755 return;
1756 }
1757 runnable->Run();
1758 delete runnable;
1759 }
1761 void
1762 PeerConnectionImpl::ChangeReadyState(PCImplReadyState aReadyState)
1763 {
1764 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1765 mReadyState = aReadyState;
1767 // Note that we are passing an nsRefPtr which keeps the observer live.
1768 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1769 if (!pco) {
1770 return;
1771 }
1772 WrappableJSErrorResult rv;
1773 RUN_ON_THREAD(mThread,
1774 WrapRunnable(pco,
1775 &PeerConnectionObserver::OnStateChange,
1776 PCObserverStateType::ReadyState,
1777 rv, static_cast<JSCompartment*>(nullptr)),
1778 NS_DISPATCH_NORMAL);
1779 }
1781 void
1782 PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
1783 {
1784 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1785 if (mSignalingState == aSignalingState ||
1786 mSignalingState == PCImplSignalingState::SignalingClosed) {
1787 return;
1788 }
1790 mSignalingState = aSignalingState;
1791 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1792 if (!pco) {
1793 return;
1794 }
1795 JSErrorResult rv;
1796 pco->OnStateChange(PCObserverStateType::SignalingState, rv);
1797 MOZ_ASSERT(!rv.Failed());
1798 }
1800 bool
1801 PeerConnectionImpl::IsClosed() const
1802 {
1803 return mSignalingState == PCImplSignalingState::SignalingClosed;
1804 }
1806 bool
1807 PeerConnectionImpl::HasMedia() const
1808 {
1809 return mMedia;
1810 }
1812 PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle)
1813 : impl_(nullptr) {
1814 if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) ==
1815 PeerConnectionCtx::GetInstance()->mPeerConnections.end()) {
1816 return;
1817 }
1819 PeerConnectionImpl *impl = PeerConnectionCtx::GetInstance()->mPeerConnections[handle];
1821 if (!impl->media())
1822 return;
1824 impl_ = impl;
1825 }
1827 const std::string&
1828 PeerConnectionImpl::GetHandle()
1829 {
1830 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1831 return mHandle;
1832 }
1834 const std::string&
1835 PeerConnectionImpl::GetName()
1836 {
1837 PC_AUTO_ENTER_API_CALL_NO_CHECK();
1838 return mName;
1839 }
1841 static mozilla::dom::PCImplIceConnectionState
1842 toDomIceConnectionState(NrIceCtx::ConnectionState state) {
1843 switch (state) {
1844 case NrIceCtx::ICE_CTX_INIT:
1845 return PCImplIceConnectionState::New;
1846 case NrIceCtx::ICE_CTX_CHECKING:
1847 return PCImplIceConnectionState::Checking;
1848 case NrIceCtx::ICE_CTX_OPEN:
1849 return PCImplIceConnectionState::Connected;
1850 case NrIceCtx::ICE_CTX_FAILED:
1851 return PCImplIceConnectionState::Failed;
1852 }
1853 MOZ_CRASH();
1854 }
1856 static mozilla::dom::PCImplIceGatheringState
1857 toDomIceGatheringState(NrIceCtx::GatheringState state) {
1858 switch (state) {
1859 case NrIceCtx::ICE_CTX_GATHER_INIT:
1860 return PCImplIceGatheringState::New;
1861 case NrIceCtx::ICE_CTX_GATHER_STARTED:
1862 return PCImplIceGatheringState::Gathering;
1863 case NrIceCtx::ICE_CTX_GATHER_COMPLETE:
1864 return PCImplIceGatheringState::Complete;
1865 }
1866 MOZ_CRASH();
1867 }
1869 #ifdef MOZILLA_INTERNAL_API
1870 static bool isDone(PCImplIceConnectionState state) {
1871 return state != PCImplIceConnectionState::Checking &&
1872 state != PCImplIceConnectionState::New;
1873 }
1875 static bool isSucceeded(PCImplIceConnectionState state) {
1876 return state == PCImplIceConnectionState::Connected ||
1877 state == PCImplIceConnectionState::Completed;
1878 }
1880 static bool isFailed(PCImplIceConnectionState state) {
1881 return state == PCImplIceConnectionState::Failed ||
1882 state == PCImplIceConnectionState::Disconnected;
1883 }
1884 #endif
1886 void PeerConnectionImpl::IceConnectionStateChange(
1887 NrIceCtx* ctx,
1888 NrIceCtx::ConnectionState state) {
1889 PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
1891 CSFLogDebug(logTag, "%s", __FUNCTION__);
1893 auto domState = toDomIceConnectionState(state);
1895 #ifdef MOZILLA_INTERNAL_API
1896 if (!isDone(mIceConnectionState) && isDone(domState)) {
1897 // mIceStartTime can be null if going directly from New to Closed, in which
1898 // case we don't count it as a success or a failure.
1899 if (!mIceStartTime.IsNull()){
1900 TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime;
1901 if (isSucceeded(domState)) {
1902 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_SUCCESS_TIME,
1903 timeDelta.ToMilliseconds());
1904 } else if (isFailed(domState)) {
1905 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FAILURE_TIME,
1906 timeDelta.ToMilliseconds());
1907 }
1908 }
1909 }
1910 #endif
1912 mIceConnectionState = domState;
1914 // Would be nice if we had a means of converting one of these dom enums
1915 // to a string that wasn't almost as much text as this switch statement...
1916 switch (mIceConnectionState) {
1917 case PCImplIceConnectionState::New:
1918 STAMP_TIMECARD(mTimeCard, "Ice state: new");
1919 break;
1920 case PCImplIceConnectionState::Checking:
1921 #ifdef MOZILLA_INTERNAL_API
1922 // For telemetry
1923 mIceStartTime = TimeStamp::Now();
1924 #endif
1925 STAMP_TIMECARD(mTimeCard, "Ice state: checking");
1926 break;
1927 case PCImplIceConnectionState::Connected:
1928 STAMP_TIMECARD(mTimeCard, "Ice state: connected");
1929 break;
1930 case PCImplIceConnectionState::Completed:
1931 STAMP_TIMECARD(mTimeCard, "Ice state: completed");
1932 break;
1933 case PCImplIceConnectionState::Failed:
1934 STAMP_TIMECARD(mTimeCard, "Ice state: failed");
1935 break;
1936 case PCImplIceConnectionState::Disconnected:
1937 STAMP_TIMECARD(mTimeCard, "Ice state: disconnected");
1938 break;
1939 case PCImplIceConnectionState::Closed:
1940 STAMP_TIMECARD(mTimeCard, "Ice state: closed");
1941 break;
1942 }
1944 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1945 if (!pco) {
1946 return;
1947 }
1948 WrappableJSErrorResult rv;
1949 RUN_ON_THREAD(mThread,
1950 WrapRunnable(pco,
1951 &PeerConnectionObserver::OnStateChange,
1952 PCObserverStateType::IceConnectionState,
1953 rv, static_cast<JSCompartment*>(nullptr)),
1954 NS_DISPATCH_NORMAL);
1955 }
1957 void
1958 PeerConnectionImpl::IceGatheringStateChange(
1959 NrIceCtx* ctx,
1960 NrIceCtx::GatheringState state)
1961 {
1962 PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
1964 CSFLogDebug(logTag, "%s", __FUNCTION__);
1966 mIceGatheringState = toDomIceGatheringState(state);
1968 // Would be nice if we had a means of converting one of these dom enums
1969 // to a string that wasn't almost as much text as this switch statement...
1970 switch (mIceGatheringState) {
1971 case PCImplIceGatheringState::New:
1972 STAMP_TIMECARD(mTimeCard, "Ice gathering state: new");
1973 break;
1974 case PCImplIceGatheringState::Gathering:
1975 STAMP_TIMECARD(mTimeCard, "Ice gathering state: gathering");
1976 break;
1977 case PCImplIceGatheringState::Complete:
1978 STAMP_TIMECARD(mTimeCard, "Ice state: complete");
1979 break;
1980 }
1982 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1983 if (!pco) {
1984 return;
1985 }
1986 WrappableJSErrorResult rv;
1987 RUN_ON_THREAD(mThread,
1988 WrapRunnable(pco,
1989 &PeerConnectionObserver::OnStateChange,
1990 PCObserverStateType::IceGatheringState,
1991 rv, static_cast<JSCompartment*>(nullptr)),
1992 NS_DISPATCH_NORMAL);
1993 }
1995 #ifdef MOZILLA_INTERNAL_API
1996 nsresult
1997 PeerConnectionImpl::BuildStatsQuery_m(
1998 mozilla::dom::MediaStreamTrack *aSelector,
1999 RTCStatsQuery *query) {
2001 if (!HasMedia()) {
2002 return NS_OK;
2003 }
2005 if (!mMedia->ice_ctx() || !mThread) {
2006 CSFLogError(logTag, "Could not build stats query, critical components of "
2007 "PeerConnectionImpl not set.");
2008 return NS_ERROR_UNEXPECTED;
2009 }
2011 nsresult rv = GetTimeSinceEpoch(&(query->now));
2013 if (NS_FAILED(rv)) {
2014 CSFLogError(logTag, "Could not build stats query, could not get timestamp");
2015 return rv;
2016 }
2018 // We do not use the pcHandle here, since that's risky to expose to content.
2019 query->report = RTCStatsReportInternalConstruct(
2020 NS_ConvertASCIItoUTF16(mName.c_str()),
2021 query->now);
2023 // Gather up pipelines from mMedia so they may be inspected on STS
2024 TrackID trackId = aSelector ? aSelector->GetTrackID() : 0;
2026 for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) {
2027 PushBackSelect(query->pipelines,
2028 mMedia->GetLocalStream(i)->GetPipelines(),
2029 trackId);
2030 }
2032 for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) {
2033 PushBackSelect(query->pipelines,
2034 mMedia->GetRemoteStream(i)->GetPipelines(),
2035 trackId);
2036 }
2038 query->iceCtx = mMedia->ice_ctx();
2040 // From the list of MediaPipelines, determine the set of NrIceMediaStreams
2041 // we are interested in.
2042 std::set<size_t> levelsToGrab;
2043 if (trackId) {
2044 for (size_t p = 0; p < query->pipelines.Length(); ++p) {
2045 size_t level = query->pipelines[p]->level();
2046 MOZ_ASSERT(level);
2047 levelsToGrab.insert(level);
2048 }
2049 } else {
2050 // We want to grab all streams, so ignore the pipelines (this also ends up
2051 // grabbing DataChannel streams, which is what we want)
2052 for (size_t s = 0; s < mMedia->num_ice_media_streams(); ++s) {
2053 levelsToGrab.insert(s + 1); // mIceStreams is 0-indexed
2054 }
2055 }
2057 for (auto s = levelsToGrab.begin(); s != levelsToGrab.end(); ++s) {
2058 // TODO(bcampen@mozilla.com): I may need to revisit this for bundle.
2059 // (Bug 786234)
2060 RefPtr<NrIceMediaStream> temp(mMedia->ice_media_stream(*s - 1));
2061 RefPtr<TransportFlow> flow(mMedia->GetTransportFlow(*s, false));
2062 // flow can be null for unused levels, such as unused DataChannels
2063 if (temp && flow) {
2064 query->streams.AppendElement(temp);
2065 }
2066 }
2068 return rv;
2069 }
2071 static void ToRTCIceCandidateStats(
2072 const std::vector<NrIceCandidate>& candidates,
2073 RTCStatsType candidateType,
2074 const nsString& componentId,
2075 DOMHighResTimeStamp now,
2076 RTCStatsReportInternal* report) {
2078 MOZ_ASSERT(report);
2079 for (auto c = candidates.begin(); c != candidates.end(); ++c) {
2080 RTCIceCandidateStats cand;
2081 cand.mType.Construct(candidateType);
2082 NS_ConvertASCIItoUTF16 codeword(c->codeword.c_str());
2083 cand.mComponentId.Construct(componentId);
2084 cand.mId.Construct(codeword);
2085 cand.mTimestamp.Construct(now);
2086 cand.mCandidateType.Construct(
2087 RTCStatsIceCandidateType(c->type));
2088 cand.mIpAddress.Construct(
2089 NS_ConvertASCIItoUTF16(c->cand_addr.host.c_str()));
2090 cand.mPortNumber.Construct(c->cand_addr.port);
2091 cand.mTransport.Construct(
2092 NS_ConvertASCIItoUTF16(c->cand_addr.transport.c_str()));
2093 if (candidateType == RTCStatsType::Localcandidate) {
2094 cand.mMozLocalTransport.Construct(
2095 NS_ConvertASCIItoUTF16(c->local_addr.transport.c_str()));
2096 }
2097 report->mIceCandidateStats.Value().AppendElement(cand);
2098 }
2099 }
2101 static void RecordIceStats_s(
2102 NrIceMediaStream& mediaStream,
2103 bool internalStats,
2104 DOMHighResTimeStamp now,
2105 RTCStatsReportInternal* report) {
2107 NS_ConvertASCIItoUTF16 componentId(mediaStream.name().c_str());
2108 if (internalStats) {
2109 std::vector<NrIceCandidatePair> candPairs;
2110 nsresult res = mediaStream.GetCandidatePairs(&candPairs);
2111 if (NS_FAILED(res)) {
2112 CSFLogError(logTag, "%s: Error getting candidate pairs", __FUNCTION__);
2113 return;
2114 }
2116 for (auto p = candPairs.begin(); p != candPairs.end(); ++p) {
2117 NS_ConvertASCIItoUTF16 codeword(p->codeword.c_str());
2118 NS_ConvertASCIItoUTF16 localCodeword(p->local.codeword.c_str());
2119 NS_ConvertASCIItoUTF16 remoteCodeword(p->remote.codeword.c_str());
2120 // Only expose candidate-pair statistics to chrome, until we've thought
2121 // through the implications of exposing it to content.
2123 RTCIceCandidatePairStats s;
2124 s.mId.Construct(codeword);
2125 s.mComponentId.Construct(componentId);
2126 s.mTimestamp.Construct(now);
2127 s.mType.Construct(RTCStatsType::Candidatepair);
2128 s.mLocalCandidateId.Construct(localCodeword);
2129 s.mRemoteCandidateId.Construct(remoteCodeword);
2130 s.mNominated.Construct(p->nominated);
2131 s.mMozPriority.Construct(p->priority);
2132 s.mSelected.Construct(p->selected);
2133 s.mState.Construct(RTCStatsIceCandidatePairState(p->state));
2134 report->mIceCandidatePairStats.Value().AppendElement(s);
2135 }
2136 }
2138 std::vector<NrIceCandidate> candidates;
2139 if (NS_SUCCEEDED(mediaStream.GetLocalCandidates(&candidates))) {
2140 ToRTCIceCandidateStats(candidates,
2141 RTCStatsType::Localcandidate,
2142 componentId,
2143 now,
2144 report);
2145 }
2146 candidates.clear();
2148 if (NS_SUCCEEDED(mediaStream.GetRemoteCandidates(&candidates))) {
2149 ToRTCIceCandidateStats(candidates,
2150 RTCStatsType::Remotecandidate,
2151 componentId,
2152 now,
2153 report);
2154 }
2155 }
2157 nsresult
2158 PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) {
2160 ASSERT_ON_THREAD(query->iceCtx->thread());
2162 // Gather stats from pipelines provided (can't touch mMedia + stream on STS)
2164 for (size_t p = 0; p < query->pipelines.Length(); ++p) {
2165 const MediaPipeline& mp = *query->pipelines[p];
2166 bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO);
2167 nsString idstr = isAudio ?
2168 NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_");
2169 idstr.AppendInt(mp.trackid());
2171 switch (mp.direction()) {
2172 case MediaPipeline::TRANSMIT: {
2173 nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr;
2174 nsString remoteId;
2175 nsString ssrc;
2176 unsigned int ssrcval;
2177 if (mp.Conduit()->GetLocalSSRC(&ssrcval)) {
2178 ssrc.AppendInt(ssrcval);
2179 }
2180 {
2181 // First, fill in remote stat with rtcp receiver data, if present.
2182 // ReceiverReports have less information than SenderReports,
2183 // so fill in what we can.
2184 DOMHighResTimeStamp timestamp;
2185 uint32_t jitterMs;
2186 uint32_t packetsReceived;
2187 uint64_t bytesReceived;
2188 uint32_t packetsLost;
2189 int32_t rtt;
2190 if (mp.Conduit()->GetRTCPReceiverReport(×tamp, &jitterMs,
2191 &packetsReceived,
2192 &bytesReceived,
2193 &packetsLost,
2194 &rtt)) {
2195 remoteId = NS_LITERAL_STRING("outbound_rtcp_") + idstr;
2196 RTCInboundRTPStreamStats s;
2197 s.mTimestamp.Construct(timestamp);
2198 s.mId.Construct(remoteId);
2199 s.mType.Construct(RTCStatsType::Inboundrtp);
2200 if (ssrc.Length()) {
2201 s.mSsrc.Construct(ssrc);
2202 }
2203 s.mJitter.Construct(double(jitterMs)/1000);
2204 s.mRemoteId.Construct(localId);
2205 s.mIsRemote = true;
2206 s.mPacketsReceived.Construct(packetsReceived);
2207 s.mBytesReceived.Construct(bytesReceived);
2208 s.mPacketsLost.Construct(packetsLost);
2209 s.mMozRtt.Construct(rtt);
2210 query->report.mInboundRTPStreamStats.Value().AppendElement(s);
2211 }
2212 }
2213 // Then, fill in local side (with cross-link to remote only if present)
2214 {
2215 RTCOutboundRTPStreamStats s;
2216 s.mTimestamp.Construct(query->now);
2217 s.mId.Construct(localId);
2218 s.mType.Construct(RTCStatsType::Outboundrtp);
2219 if (ssrc.Length()) {
2220 s.mSsrc.Construct(ssrc);
2221 }
2222 s.mRemoteId.Construct(remoteId);
2223 s.mIsRemote = false;
2224 s.mPacketsSent.Construct(mp.rtp_packets_sent());
2225 s.mBytesSent.Construct(mp.rtp_bytes_sent());
2226 query->report.mOutboundRTPStreamStats.Value().AppendElement(s);
2227 }
2228 break;
2229 }
2230 case MediaPipeline::RECEIVE: {
2231 nsString localId = NS_LITERAL_STRING("inbound_rtp_") + idstr;
2232 nsString remoteId;
2233 nsString ssrc;
2234 unsigned int ssrcval;
2235 if (mp.Conduit()->GetRemoteSSRC(&ssrcval)) {
2236 ssrc.AppendInt(ssrcval);
2237 }
2238 {
2239 // First, fill in remote stat with rtcp sender data, if present.
2240 DOMHighResTimeStamp timestamp;
2241 uint32_t packetsSent;
2242 uint64_t bytesSent;
2243 if (mp.Conduit()->GetRTCPSenderReport(×tamp,
2244 &packetsSent, &bytesSent)) {
2245 remoteId = NS_LITERAL_STRING("inbound_rtcp_") + idstr;
2246 RTCOutboundRTPStreamStats s;
2247 s.mTimestamp.Construct(timestamp);
2248 s.mId.Construct(remoteId);
2249 s.mType.Construct(RTCStatsType::Outboundrtp);
2250 if (ssrc.Length()) {
2251 s.mSsrc.Construct(ssrc);
2252 }
2253 s.mRemoteId.Construct(localId);
2254 s.mIsRemote = true;
2255 s.mPacketsSent.Construct(packetsSent);
2256 s.mBytesSent.Construct(bytesSent);
2257 query->report.mOutboundRTPStreamStats.Value().AppendElement(s);
2258 }
2259 }
2260 // Then, fill in local side (with cross-link to remote only if present)
2261 RTCInboundRTPStreamStats s;
2262 s.mTimestamp.Construct(query->now);
2263 s.mId.Construct(localId);
2264 s.mType.Construct(RTCStatsType::Inboundrtp);
2265 if (ssrc.Length()) {
2266 s.mSsrc.Construct(ssrc);
2267 }
2268 unsigned int jitterMs, packetsLost;
2269 if (mp.Conduit()->GetRTPStats(&jitterMs, &packetsLost)) {
2270 s.mJitter.Construct(double(jitterMs)/1000);
2271 s.mPacketsLost.Construct(packetsLost);
2272 }
2273 if (remoteId.Length()) {
2274 s.mRemoteId.Construct(remoteId);
2275 }
2276 s.mIsRemote = false;
2277 s.mPacketsReceived.Construct(mp.rtp_packets_received());
2278 s.mBytesReceived.Construct(mp.rtp_bytes_received());
2280 if (query->internalStats && isAudio) {
2281 int32_t jitterBufferDelay;
2282 int32_t playoutBufferDelay;
2283 int32_t avSyncDelta;
2284 if (mp.Conduit()->GetAVStats(&jitterBufferDelay,
2285 &playoutBufferDelay,
2286 &avSyncDelta)) {
2287 s.mMozJitterBufferDelay.Construct(jitterBufferDelay);
2288 s.mMozAvSyncDelay.Construct(avSyncDelta);
2289 }
2290 }
2291 query->report.mInboundRTPStreamStats.Value().AppendElement(s);
2292 break;
2293 }
2294 }
2295 }
2297 // Gather stats from ICE
2298 for (size_t s = 0; s != query->streams.Length(); ++s) {
2299 RecordIceStats_s(*query->streams[s],
2300 query->internalStats,
2301 query->now,
2302 &(query->report));
2303 }
2305 // NrIceCtx and NrIceMediaStream must be destroyed on STS, so it is not safe
2306 // to dispatch them back to main.
2307 // We clear streams first to maintain destruction order
2308 query->streams.Clear();
2309 query->iceCtx = nullptr;
2310 return NS_OK;
2311 }
2313 void PeerConnectionImpl::GetStatsForPCObserver_s(
2314 const std::string& pcHandle, // The Runnable holds the memory
2315 nsAutoPtr<RTCStatsQuery> query) {
2317 MOZ_ASSERT(query);
2318 MOZ_ASSERT(query->iceCtx);
2319 ASSERT_ON_THREAD(query->iceCtx->thread());
2321 nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get());
2323 NS_DispatchToMainThread(
2324 WrapRunnableNM(
2325 &PeerConnectionImpl::DeliverStatsReportToPCObserver_m,
2326 pcHandle,
2327 rv,
2328 query),
2329 NS_DISPATCH_NORMAL);
2330 }
2332 void PeerConnectionImpl::DeliverStatsReportToPCObserver_m(
2333 const std::string& pcHandle,
2334 nsresult result,
2335 nsAutoPtr<RTCStatsQuery> query) {
2337 // Is the PeerConnectionImpl still around?
2338 PeerConnectionWrapper pcw(pcHandle);
2339 if (pcw.impl()) {
2340 nsRefPtr<PeerConnectionObserver> pco =
2341 do_QueryObjectReferent(pcw.impl()->mPCObserver);
2342 if (pco) {
2343 JSErrorResult rv;
2344 if (NS_SUCCEEDED(result)) {
2345 pco->OnGetStatsSuccess(query->report, rv);
2346 } else {
2347 pco->OnGetStatsError(kInternalError,
2348 ObString("Failed to fetch statistics"),
2349 rv);
2350 }
2352 if (rv.Failed()) {
2353 CSFLogError(logTag, "Error firing stats observer callback");
2354 }
2355 }
2356 }
2357 }
2359 #endif
2361 void
2362 PeerConnectionImpl::RecordLongtermICEStatistics() {
2363 #ifdef MOZILLA_INTERNAL_API
2364 WebrtcGlobalInformation::StoreLongTermICEStatistics(*this);
2365 #endif
2366 }
2368 void
2369 PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
2370 {
2371 PC_AUTO_ENTER_API_CALL_NO_CHECK();
2372 MOZ_ASSERT(aStream);
2374 CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
2375 }
2377 void
2378 PeerConnectionImpl::OnSdpParseError(const char *message) {
2379 CSFLogError(logTag, "%s SDP Parse Error: %s", __FUNCTION__, message);
2380 // Save the parsing errors in the PC to be delivered with OnSuccess or OnError
2381 mSDPParseErrorMessages.push_back(message);
2382 }
2384 void
2385 PeerConnectionImpl::ClearSdpParseErrorMessages() {
2386 mSDPParseErrorMessages.clear();
2387 }
2389 const std::vector<std::string> &
2390 PeerConnectionImpl::GetSdpParseErrors() {
2391 return mSDPParseErrorMessages;
2392 }
2394 #ifdef MOZILLA_INTERNAL_API
2395 //Telemetry for when calls start
2396 void
2397 PeerConnectionImpl::startCallTelem() {
2398 // Start time for calls
2399 mStartTime = TimeStamp::Now();
2401 // Increment session call counter
2402 #ifdef MOZILLA_INTERNAL_API
2403 int &cnt = PeerConnectionCtx::GetInstance()->mConnectionCounter;
2404 Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Subtract(cnt);
2405 cnt++;
2406 Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Add(cnt);
2407 #endif
2408 }
2409 #endif
2411 NS_IMETHODIMP
2412 PeerConnectionImpl::GetLocalStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result)
2413 {
2414 PC_AUTO_ENTER_API_CALL_NO_CHECK();
2415 #ifdef MOZILLA_INTERNAL_API
2416 for(uint32_t i=0; i < media()->LocalStreamsLength(); i++) {
2417 LocalSourceStreamInfo *info = media()->GetLocalStream(i);
2418 NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED);
2419 result.AppendElement(info->GetMediaStream());
2420 }
2421 return NS_OK;
2422 #else
2423 return NS_ERROR_FAILURE;
2424 #endif
2425 }
2427 NS_IMETHODIMP
2428 PeerConnectionImpl::GetRemoteStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result)
2429 {
2430 PC_AUTO_ENTER_API_CALL_NO_CHECK();
2431 #ifdef MOZILLA_INTERNAL_API
2432 for(uint32_t i=0; i < media()->RemoteStreamsLength(); i++) {
2433 RemoteSourceStreamInfo *info = media()->GetRemoteStream(i);
2434 NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED);
2435 result.AppendElement(info->GetMediaStream());
2436 }
2437 return NS_OK;
2438 #else
2439 return NS_ERROR_FAILURE;
2440 #endif
2441 }
2443 } // end sipcc namespace