media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

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;
  1007     // If we inited the DataConnection, call Destroy() before releasing it
  1008     mDataConnection->Destroy();
  1010   mDataConnection = nullptr;
  1011 #endif
  1012   return NS_ERROR_FAILURE;
  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)
  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
  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)
  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;
  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;
  1076   nsIDOMDataChannel *retval;
  1077   rv = NS_NewDOMDataChannel(dataChannel.forget(), mWindow, &retval);
  1078   if (NS_FAILED(rv)) {
  1079     return rv;
  1081   *aRetval = static_cast<nsDOMDataChannel*>(retval);
  1082 #endif
  1083   return NS_OK;
  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;
  1106   nsRefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp);
  1107   nsRefPtr<PeerConnectionObserver> tmp3 = static_cast<PeerConnectionObserver*>(&*tmp2);
  1108   return tmp3.forget();
  1111 void
  1112 PeerConnectionImpl::NotifyConnection()
  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;
  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
  1132 void
  1133 PeerConnectionImpl::NotifyClosedConnection()
  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;
  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
  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)
  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);
  1164 #endif
  1166 void
  1167 PeerConnectionImpl::NotifyDataChannel(already_AddRefed<DataChannel> aChannel)
  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;
  1192   RUN_ON_THREAD(mThread,
  1193                 WrapRunnableNM(NotifyDataChannel_m,
  1194                                domchannel.get(),
  1195                                pco),
  1196                 NS_DISPATCH_NORMAL);
  1197 #endif
  1200 NS_IMETHODIMP
  1201 PeerConnectionImpl::CreateOffer(const MediaConstraintsInternal& aConstraints)
  1203   return CreateOffer(MediaConstraintsExternal (aConstraints));
  1206 // Used by unit tests and the IDL CreateOffer.
  1207 NS_IMETHODIMP
  1208 PeerConnectionImpl::CreateOffer(const MediaConstraintsExternal& aConstraints)
  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;
  1222 NS_IMETHODIMP
  1223 PeerConnectionImpl::CreateAnswer(const MediaConstraintsInternal& aConstraints)
  1225   return CreateAnswer(MediaConstraintsExternal (aConstraints));
  1228 NS_IMETHODIMP
  1229 PeerConnectionImpl::CreateAnswer(const MediaConstraintsExternal& aConstraints)
  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;
  1243 NS_IMETHODIMP
  1244 PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
  1246   PC_AUTO_ENTER_API_CALL(true);
  1248   if (!aSDP) {
  1249     CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
  1250     return NS_ERROR_FAILURE;
  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;
  1263 NS_IMETHODIMP
  1264 PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
  1266   PC_AUTO_ENTER_API_CALL(true);
  1268   if (!aSDP) {
  1269     CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
  1270     return NS_ERROR_FAILURE;
  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;
  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;
  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();
  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);
  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;
  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;
  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());
  1372 #endif
  1374   mInternal->mCall->addICECandidate(aCandidate, aMid, aLevel, tc);
  1375   return NS_OK;
  1378 NS_IMETHODIMP
  1379 PeerConnectionImpl::CloseStreams() {
  1380   PC_AUTO_ENTER_API_CALL(false);
  1382   if (mReadyState != PCImplReadyState::Closed)  {
  1383     ChangeReadyState(PCImplReadyState::Closing);
  1386   CSFLogInfo(logTag, "%s: Ending associated call", __FUNCTION__);
  1388   mInternal->mCall->endCall();
  1389   return NS_OK;
  1392 NS_IMETHODIMP
  1393 PeerConnectionImpl::AddStream(DOMMediaStream &aMediaStream,
  1394                               const MediaConstraintsInternal& aConstraints)
  1396   return AddStream(aMediaStream, MediaConstraintsExternal(aConstraints));
  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;
  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;
  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++;
  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++;
  1446   return NS_OK;
  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--;
  1467   if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
  1468     mInternal->mCall->removeStream(stream_id, 1, VIDEO);
  1469     MOZ_ASSERT(mNumVideoStreams > 0);
  1470     mNumVideoStreams--;
  1473   return NS_OK;
  1476 /*
  1477 NS_IMETHODIMP
  1478 PeerConnectionImpl::SetRemoteFingerprint(const char* hash, const char* fingerprint)
  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;
  1492 */
  1494 NS_IMETHODIMP
  1495 PeerConnectionImpl::GetFingerprint(char** fingerprint)
  1497   MOZ_ASSERT(fingerprint);
  1499   if (!mIdentity) {
  1500     return NS_ERROR_FAILURE;
  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;
  1511 NS_IMETHODIMP
  1512 PeerConnectionImpl::GetLocalDescription(char** aSDP)
  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;
  1525 NS_IMETHODIMP
  1526 PeerConnectionImpl::GetRemoteDescription(char** aSDP)
  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;
  1539 NS_IMETHODIMP
  1540 PeerConnectionImpl::ReadyState(PCImplReadyState* aState)
  1542   PC_AUTO_ENTER_API_CALL_NO_CHECK();
  1543   MOZ_ASSERT(aState);
  1545   *aState = mReadyState;
  1546   return NS_OK;
  1549 NS_IMETHODIMP
  1550 PeerConnectionImpl::SignalingState(PCImplSignalingState* aState)
  1552   PC_AUTO_ENTER_API_CALL_NO_CHECK();
  1553   MOZ_ASSERT(aState);
  1555   *aState = mSignalingState;
  1556   return NS_OK;
  1559 NS_IMETHODIMP
  1560 PeerConnectionImpl::SipccState(PCImplSipccState* aState)
  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;
  1573   return NS_OK;
  1576 NS_IMETHODIMP
  1577 PeerConnectionImpl::IceConnectionState(PCImplIceConnectionState* aState)
  1579   PC_AUTO_ENTER_API_CALL_NO_CHECK();
  1580   MOZ_ASSERT(aState);
  1582   *aState = mIceConnectionState;
  1583   return NS_OK;
  1586 NS_IMETHODIMP
  1587 PeerConnectionImpl::IceGatheringState(PCImplIceGatheringState* aState)
  1589   PC_AUTO_ENTER_API_CALL_NO_CHECK();
  1590   MOZ_ASSERT(aState);
  1592   *aState = mIceGatheringState;
  1593   return NS_OK;
  1596 nsresult
  1597 PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const
  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;
  1607   if (!mMedia) {
  1608     CSFLogError(logTag, "%s: called API with disposed mMedia", __FUNCTION__);
  1609     return NS_ERROR_FAILURE;
  1611   return NS_OK;
  1614 NS_IMETHODIMP
  1615 PeerConnectionImpl::Close()
  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;
  1628 nsresult
  1629 PeerConnectionImpl::CloseInt()
  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();
  1641   if (mInternal->mCall) {
  1642     CSFLogInfo(logTag, "%s: Closing PeerConnectionImpl %s; "
  1643                "ending call", __FUNCTION__, mHandle.c_str());
  1644     mInternal->mCall->endCall();
  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
  1653 #endif
  1655   ShutdownMedia();
  1657   // DataConnection will need to stay alive until all threads/runnables exit
  1659   return NS_OK;
  1662 void
  1663 PeerConnectionImpl::ShutdownMedia()
  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());
  1676 #endif
  1678   // Forget the reference so that we can transfer it to
  1679   // SelfDestruct().
  1680   mMedia.forget().take()->SelfDestruct();
  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()
  1690   destructorSafeDestroyNSSReference();
  1693 void
  1694 PeerConnectionImpl::destructorSafeDestroyNSSReference()
  1696   MOZ_ASSERT(NS_IsMainThread());
  1697   CSFLogDebug(logTag, "%s: NSS shutting down; freeing our DtlsIdentity.", __FUNCTION__);
  1698   mIdentity = nullptr;
  1700 #endif
  1702 void
  1703 PeerConnectionImpl::onCallEvent(const OnCallEventArgs& args)
  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");
  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;
  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;
  1745   nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
  1746   if (!pco) {
  1747     return;
  1750   PeerConnectionObserverDispatch* runnable =
  1751       new PeerConnectionObserverDispatch(aInfo, this, pco);
  1753   if (mThread) {
  1754     mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
  1755     return;
  1757   runnable->Run();
  1758   delete runnable;
  1761 void
  1762 PeerConnectionImpl::ChangeReadyState(PCImplReadyState aReadyState)
  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;
  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);
  1781 void
  1782 PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
  1784   PC_AUTO_ENTER_API_CALL_NO_CHECK();
  1785   if (mSignalingState == aSignalingState ||
  1786       mSignalingState == PCImplSignalingState::SignalingClosed) {
  1787     return;
  1790   mSignalingState = aSignalingState;
  1791   nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
  1792   if (!pco) {
  1793     return;
  1795   JSErrorResult rv;
  1796   pco->OnStateChange(PCObserverStateType::SignalingState, rv);
  1797   MOZ_ASSERT(!rv.Failed());
  1800 bool
  1801 PeerConnectionImpl::IsClosed() const
  1803   return mSignalingState == PCImplSignalingState::SignalingClosed;
  1806 bool
  1807 PeerConnectionImpl::HasMedia() const
  1809   return mMedia;
  1812 PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle)
  1813     : impl_(nullptr) {
  1814   if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) ==
  1815     PeerConnectionCtx::GetInstance()->mPeerConnections.end()) {
  1816     return;
  1819   PeerConnectionImpl *impl = PeerConnectionCtx::GetInstance()->mPeerConnections[handle];
  1821   if (!impl->media())
  1822     return;
  1824   impl_ = impl;
  1827 const std::string&
  1828 PeerConnectionImpl::GetHandle()
  1830   PC_AUTO_ENTER_API_CALL_NO_CHECK();
  1831   return mHandle;
  1834 const std::string&
  1835 PeerConnectionImpl::GetName()
  1837   PC_AUTO_ENTER_API_CALL_NO_CHECK();
  1838   return mName;
  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;
  1853   MOZ_CRASH();
  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;
  1866   MOZ_CRASH();
  1869 #ifdef MOZILLA_INTERNAL_API
  1870 static bool isDone(PCImplIceConnectionState state) {
  1871   return state != PCImplIceConnectionState::Checking &&
  1872          state != PCImplIceConnectionState::New;
  1875 static bool isSucceeded(PCImplIceConnectionState state) {
  1876   return state == PCImplIceConnectionState::Connected ||
  1877          state == PCImplIceConnectionState::Completed;
  1880 static bool isFailed(PCImplIceConnectionState state) {
  1881   return state == PCImplIceConnectionState::Failed ||
  1882          state == PCImplIceConnectionState::Disconnected;
  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());
  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;
  1944   nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
  1945   if (!pco) {
  1946     return;
  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);
  1957 void
  1958 PeerConnectionImpl::IceGatheringStateChange(
  1959     NrIceCtx* ctx,
  1960     NrIceCtx::GatheringState state)
  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;
  1982   nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
  1983   if (!pco) {
  1984     return;
  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);
  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;
  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;
  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;
  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);
  2032   for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) {
  2033     PushBackSelect(query->pipelines,
  2034                    mMedia->GetRemoteStream(i)->GetPipelines(),
  2035                    trackId);
  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);
  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
  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);
  2068   return rv;
  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()));
  2097     report->mIceCandidateStats.Value().AppendElement(cand);
  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;
  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);
  2138   std::vector<NrIceCandidate> candidates;
  2139   if (NS_SUCCEEDED(mediaStream.GetLocalCandidates(&candidates))) {
  2140     ToRTCIceCandidateStats(candidates,
  2141                            RTCStatsType::Localcandidate,
  2142                            componentId,
  2143                            now,
  2144                            report);
  2146   candidates.clear();
  2148   if (NS_SUCCEEDED(mediaStream.GetRemoteCandidates(&candidates))) {
  2149     ToRTCIceCandidateStats(candidates,
  2150                            RTCStatsType::Remotecandidate,
  2151                            componentId,
  2152                            now,
  2153                            report);
  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);
  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(&timestamp, &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);
  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);
  2213         // Then, fill in local side (with cross-link to remote only if present)
  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);
  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);
  2228         break;
  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);
  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(&timestamp,
  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);
  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);
  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);
  2268         unsigned int jitterMs, packetsLost;
  2269         if (mp.Conduit()->GetRTPStats(&jitterMs, &packetsLost)) {
  2270           s.mJitter.Construct(double(jitterMs)/1000);
  2271           s.mPacketsLost.Construct(packetsLost);
  2273         if (remoteId.Length()) {
  2274           s.mRemoteId.Construct(remoteId);
  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);
  2291         query->report.mInboundRTPStreamStats.Value().AppendElement(s);
  2292         break;
  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));
  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;
  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);
  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);
  2352       if (rv.Failed()) {
  2353         CSFLogError(logTag, "Error firing stats observer callback");
  2359 #endif
  2361 void
  2362 PeerConnectionImpl::RecordLongtermICEStatistics() {
  2363 #ifdef MOZILLA_INTERNAL_API
  2364   WebrtcGlobalInformation::StoreLongTermICEStatistics(*this);
  2365 #endif
  2368 void
  2369 PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
  2371   PC_AUTO_ENTER_API_CALL_NO_CHECK();
  2372   MOZ_ASSERT(aStream);
  2374   CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
  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);
  2384 void
  2385 PeerConnectionImpl::ClearSdpParseErrorMessages() {
  2386   mSDPParseErrorMessages.clear();
  2389 const std::vector<std::string> &
  2390 PeerConnectionImpl::GetSdpParseErrors() {
  2391   return mSDPParseErrorMessages;
  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
  2409 #endif
  2411 NS_IMETHODIMP
  2412 PeerConnectionImpl::GetLocalStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result)
  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());
  2421   return NS_OK;
  2422 #else
  2423   return NS_ERROR_FAILURE;
  2424 #endif
  2427 NS_IMETHODIMP
  2428 PeerConnectionImpl::GetRemoteStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result)
  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());
  2437   return NS_OK;
  2438 #else
  2439   return NS_ERROR_FAILURE;
  2440 #endif
  2443 }  // end sipcc namespace

mercurial