tor-browser

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

JsepSessionImpl.cpp (88439B)


      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/. */
      4 
      5 #include "jsep/JsepSessionImpl.h"
      6 
      7 #include <stdlib.h>
      8 
      9 #include <bitset>
     10 #include <set>
     11 #include <string>
     12 #include <utility>
     13 
     14 #include "api/rtp_parameters.h"
     15 #include "jsep/JsepTrack.h"
     16 #include "jsep/JsepTransport.h"
     17 #include "mozilla/Preferences.h"
     18 #include "mozilla/StaticPrefs_media.h"
     19 #include "mozilla/UniquePtr.h"
     20 #include "mozilla/net/DataChannelProtocol.h"
     21 #include "nsDebug.h"
     22 #include "nspr.h"
     23 #include "nss.h"
     24 #include "pk11pub.h"
     25 #include "sdp/HybridSdpParser.h"
     26 #include "sdp/SipccSdp.h"
     27 #include "transport/logging.h"
     28 
     29 namespace mozilla {
     30 
     31 MOZ_MTLOG_MODULE("jsep")
     32 
     33 #define JSEP_SET_ERROR(error)                                 \
     34  do {                                                        \
     35    std::ostringstream os;                                    \
     36    os << error;                                              \
     37    mLastError = os.str();                                    \
     38    MOZ_MTLOG(ML_ERROR, "[" << mName << "]: " << mLastError); \
     39  } while (0);
     40 
     41 static std::bitset<128> GetForbiddenSdpPayloadTypes() {
     42  std::bitset<128> forbidden(0);
     43  forbidden[1] = true;
     44  forbidden[2] = true;
     45  forbidden[19] = true;
     46  for (uint16_t i = 64; i < 96; ++i) {
     47    forbidden[i] = true;
     48  }
     49  return forbidden;
     50 }
     51 
     52 static std::string GetRandomHex(size_t words) {
     53  std::ostringstream os;
     54 
     55  for (size_t i = 0; i < words; ++i) {
     56    uint32_t rand;
     57    SECStatus rv = PK11_GenerateRandom(reinterpret_cast<unsigned char*>(&rand),
     58                                       sizeof(rand));
     59    if (rv != SECSuccess) {
     60      MOZ_CRASH();
     61      return "";
     62    }
     63 
     64    os << std::hex << std::setfill('0') << std::setw(8) << rand;
     65  }
     66  return os.str();
     67 }
     68 
     69 JsepSessionImpl::JsepSessionImpl(const JsepSessionImpl& aOrig)
     70    : JsepSession(aOrig),
     71      JsepSessionCopyableStuff(aOrig),
     72      mUuidGen(aOrig.mUuidGen->Clone()),
     73      mGeneratedOffer(aOrig.mGeneratedOffer ? aOrig.mGeneratedOffer->Clone()
     74                                            : nullptr),
     75      mGeneratedAnswer(aOrig.mGeneratedAnswer ? aOrig.mGeneratedAnswer->Clone()
     76                                              : nullptr),
     77      mCurrentLocalDescription(aOrig.mCurrentLocalDescription
     78                                   ? aOrig.mCurrentLocalDescription->Clone()
     79                                   : nullptr),
     80      mCurrentRemoteDescription(aOrig.mCurrentRemoteDescription
     81                                    ? aOrig.mCurrentRemoteDescription->Clone()
     82                                    : nullptr),
     83      mPendingLocalDescription(aOrig.mPendingLocalDescription
     84                                   ? aOrig.mPendingLocalDescription->Clone()
     85                                   : nullptr),
     86      mPendingRemoteDescription(aOrig.mPendingRemoteDescription
     87                                    ? aOrig.mPendingRemoteDescription->Clone()
     88                                    : nullptr),
     89      mSdpHelper(&mLastError),
     90      mParser(new HybridSdpParser()) {
     91  for (const auto& codec : aOrig.mSupportedCodecs) {
     92    mSupportedCodecs.emplace_back(codec->Clone());
     93  }
     94 }
     95 
     96 nsresult JsepSessionImpl::Init() {
     97  mLastError.clear();
     98 
     99  MOZ_ASSERT(!mSessionId, "Init called more than once");
    100 
    101  nsresult rv = SetupIds();
    102  NS_ENSURE_SUCCESS(rv, rv);
    103 
    104  mEncodeTrackId =
    105      Preferences::GetBool("media.peerconnection.sdp.encode_track_id", true);
    106 
    107  mIceUfrag = GetRandomHex(1);
    108  mIcePwd = GetRandomHex(4);
    109  return NS_OK;
    110 }
    111 
    112 static void GetIceCredentials(
    113    const Sdp& aSdp,
    114    std::set<std::pair<std::string, std::string>>* aCredentials) {
    115  for (size_t i = 0; i < aSdp.GetMediaSectionCount(); ++i) {
    116    const SdpAttributeList& attrs = aSdp.GetMediaSection(i).GetAttributeList();
    117    if (attrs.HasAttribute(SdpAttribute::kIceUfragAttribute) &&
    118        attrs.HasAttribute(SdpAttribute::kIcePwdAttribute)) {
    119      aCredentials->insert(
    120          std::make_pair(attrs.GetIceUfrag(), attrs.GetIcePwd()));
    121    }
    122  }
    123 }
    124 
    125 std::set<std::pair<std::string, std::string>>
    126 JsepSessionImpl::GetLocalIceCredentials() const {
    127  std::set<std::pair<std::string, std::string>> result;
    128  if (mCurrentLocalDescription) {
    129    GetIceCredentials(*mCurrentLocalDescription, &result);
    130  }
    131  if (mPendingLocalDescription) {
    132    GetIceCredentials(*mPendingLocalDescription, &result);
    133  }
    134  return result;
    135 }
    136 
    137 void JsepSessionImpl::AddTransceiver(const JsepTransceiver& aTransceiver) {
    138  mLastError.clear();
    139  MOZ_MTLOG(ML_DEBUG,
    140            "[" << mName << "]: Adding transceiver " << aTransceiver.GetUuid());
    141 #ifdef DEBUG
    142  if (aTransceiver.GetMediaType() == SdpMediaSection::kApplication) {
    143    // Make sure we don't add more than one DataChannel transceiver
    144    for (const auto& transceiver : mTransceivers) {
    145      MOZ_ASSERT(transceiver.GetMediaType() != SdpMediaSection::kApplication);
    146    }
    147  }
    148 #endif
    149  mTransceivers.push_back(aTransceiver);
    150  InitTransceiver(mTransceivers.back());
    151 }
    152 
    153 void JsepSessionImpl::InitTransceiver(JsepTransceiver& aTransceiver) {
    154  mLastError.clear();
    155 
    156  if (aTransceiver.GetMediaType() != SdpMediaSection::kApplication) {
    157    // Make sure we have an ssrc. Might already be set.
    158    aTransceiver.mSendTrack.EnsureSsrcs(mSsrcGenerator, 1U);
    159    aTransceiver.mSendTrack.SetCNAME(mCNAME);
    160 
    161    // Make sure we have identifiers for send track, just in case.
    162    // (man I hate this)
    163    if (mEncodeTrackId) {
    164      aTransceiver.mSendTrack.SetTrackId(aTransceiver.GetUuid());
    165    }
    166  } else {
    167    // Datachannel transceivers should always be sendrecv. Just set it instead
    168    // of asserting.
    169    aTransceiver.mJsDirection = SdpDirectionAttribute::kSendrecv;
    170  }
    171 
    172  aTransceiver.mSendTrack.PopulateCodecs(mSupportedCodecs);
    173  aTransceiver.mRecvTrack.PopulateCodecs(mSupportedCodecs);
    174  // We do not set mLevel yet, we do that either on createOffer, or setRemote
    175 }
    176 
    177 nsresult JsepSessionImpl::SetBundlePolicy(JsepBundlePolicy policy) {
    178  mLastError.clear();
    179 
    180  if (mBundlePolicy == policy) {
    181    return NS_OK;
    182  }
    183 
    184  if (mCurrentLocalDescription) {
    185    JSEP_SET_ERROR(
    186        "Changing the bundle policy is only supported before the "
    187        "first SetLocalDescription.");
    188    return NS_ERROR_UNEXPECTED;
    189  }
    190 
    191  mBundlePolicy = policy;
    192  return NS_OK;
    193 }
    194 
    195 nsresult JsepSessionImpl::AddDtlsFingerprint(
    196    const nsACString& algorithm, const std::vector<uint8_t>& value) {
    197  mLastError.clear();
    198  JsepDtlsFingerprint fp;
    199 
    200  fp.mAlgorithm = algorithm;
    201  fp.mValue = value;
    202 
    203  mDtlsFingerprints.push_back(fp);
    204 
    205  return NS_OK;
    206 }
    207 
    208 nsresult JsepSessionImpl::AddRtpExtension(
    209    JsepMediaType mediaType, const std::string& extensionName,
    210    SdpDirectionAttribute::Direction direction) {
    211  mLastError.clear();
    212 
    213  for (auto& ext : mRtpExtensions) {
    214    if (ext.mExtmap.direction == direction &&
    215        ext.mExtmap.extensionname == extensionName) {
    216      if (ext.mMediaType != mediaType) {
    217        ext.mMediaType = JsepMediaType::kAudioVideo;
    218      }
    219      return NS_OK;
    220    }
    221  }
    222 
    223  uint16_t freeEntry = GetNeverUsedExtmapEntry();
    224 
    225  if (freeEntry == 0) {
    226    return NS_ERROR_FAILURE;
    227  }
    228 
    229  JsepExtmapMediaType extMediaType = {
    230      mediaType,
    231      {freeEntry, direction,
    232       // do we want to specify direction?
    233       direction != SdpDirectionAttribute::kSendrecv, extensionName, ""}};
    234 
    235  mRtpExtensions.push_back(extMediaType);
    236  return NS_OK;
    237 }
    238 
    239 nsresult JsepSessionImpl::AddAudioRtpExtension(
    240    const std::string& extensionName,
    241    SdpDirectionAttribute::Direction direction) {
    242  return AddRtpExtension(JsepMediaType::kAudio, extensionName, direction);
    243 }
    244 
    245 nsresult JsepSessionImpl::AddVideoRtpExtension(
    246    const std::string& extensionName,
    247    SdpDirectionAttribute::Direction direction) {
    248  return AddRtpExtension(JsepMediaType::kVideo, extensionName, direction);
    249 }
    250 
    251 nsresult JsepSessionImpl::AddAudioVideoRtpExtension(
    252    const std::string& extensionName,
    253    SdpDirectionAttribute::Direction direction) {
    254  return AddRtpExtension(JsepMediaType::kAudioVideo, extensionName, direction);
    255 }
    256 
    257 nsresult JsepSessionImpl::CreateOfferMsection(const JsepOfferOptions& options,
    258                                              JsepTransceiver& transceiver,
    259                                              Sdp* local) {
    260  SdpMediaSection::Protocol protocol(
    261      SdpHelper::GetProtocolForMediaType(transceiver.GetMediaType()));
    262 
    263  const Sdp* answer(GetAnswer());
    264  const SdpMediaSection* lastAnswerMsection = nullptr;
    265 
    266  if (answer &&
    267      (local->GetMediaSectionCount() < answer->GetMediaSectionCount())) {
    268    lastAnswerMsection =
    269        &answer->GetMediaSection(local->GetMediaSectionCount());
    270    // Use the protocol the answer used, even if it is not what we would have
    271    // used.
    272    protocol = lastAnswerMsection->GetProtocol();
    273  }
    274 
    275  SdpMediaSection* msection = &local->AddMediaSection(
    276      transceiver.GetMediaType(), transceiver.mJsDirection, 0, protocol,
    277      sdp::kIPv4, "0.0.0.0");
    278 
    279  // Some of this stuff (eg; mid) sticks around even if disabled
    280  if (lastAnswerMsection) {
    281    MOZ_ASSERT(lastAnswerMsection->GetMediaType() ==
    282               transceiver.GetMediaType());
    283    nsresult rv = mSdpHelper.CopyStickyParams(*lastAnswerMsection, msection);
    284    NS_ENSURE_SUCCESS(rv, rv);
    285  }
    286 
    287  if (transceiver.IsStopping() || transceiver.IsStopped()) {
    288    SdpHelper::DisableMsection(local, msection);
    289    return NS_OK;
    290  }
    291 
    292  msection->SetPort(9);
    293 
    294  // We don't do this in AddTransportAttributes because that is also used for
    295  // making answers, and we don't want to unconditionally set rtcp-mux or
    296  // rtcp-rsize there.
    297  if (mSdpHelper.HasRtcp(msection->GetProtocol())) {
    298    // Set RTCP-MUX.
    299    msection->GetAttributeList().SetAttribute(
    300        new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
    301    // Set RTCP-RSIZE
    302    if (msection->GetMediaType() == SdpMediaSection::MediaType::kVideo &&
    303        Preferences::GetBool("media.navigator.video.offer_rtcp_rsize", false)) {
    304      msection->GetAttributeList().SetAttribute(
    305          new SdpFlagAttribute(SdpAttribute::kRtcpRsizeAttribute));
    306    }
    307  }
    308  // Ditto for extmap-allow-mixed
    309  msection->GetAttributeList().SetAttribute(
    310      new SdpFlagAttribute(SdpAttribute::kExtmapAllowMixedAttribute));
    311 
    312  nsresult rv = AddTransportAttributes(msection, SdpSetupAttribute::kActpass);
    313  NS_ENSURE_SUCCESS(rv, rv);
    314 
    315  transceiver.mSendTrack.AddToOffer(mSsrcGenerator, msection);
    316  transceiver.mRecvTrack.AddToOffer(mSsrcGenerator, msection);
    317 
    318  AddExtmap(msection);
    319 
    320  std::string mid;
    321  // We do not set the mid on the transceiver, that happens when a description
    322  // is set.
    323  if (transceiver.IsAssociated()) {
    324    mid = transceiver.GetMid();
    325  } else {
    326    mid = GetNewMid();
    327  }
    328 
    329  msection->GetAttributeList().SetAttribute(
    330      new SdpStringAttribute(SdpAttribute::kMidAttribute, mid));
    331 
    332  return NS_OK;
    333 }
    334 
    335 void JsepSessionImpl::SetupBundle(Sdp* sdp) const {
    336  std::vector<std::string> mids;
    337  std::set<SdpMediaSection::MediaType> observedTypes;
    338 
    339  // This has the effect of changing the bundle level if the first m-section
    340  // goes from disabled to enabled. This is kinda inefficient.
    341 
    342  for (size_t i = 0; i < sdp->GetMediaSectionCount(); ++i) {
    343    auto& attrs = sdp->GetMediaSection(i).GetAttributeList();
    344    if ((sdp->GetMediaSection(i).GetPort() != 0) &&
    345        attrs.HasAttribute(SdpAttribute::kMidAttribute)) {
    346      bool useBundleOnly = false;
    347      switch (mBundlePolicy) {
    348        case kBundleMaxCompat:
    349          // We don't use bundle-only for max-compat
    350          break;
    351        case kBundleBalanced:
    352          // balanced means we use bundle-only on everything but the first
    353          // m-section of a given type
    354          if (observedTypes.count(sdp->GetMediaSection(i).GetMediaType())) {
    355            useBundleOnly = true;
    356          }
    357          observedTypes.insert(sdp->GetMediaSection(i).GetMediaType());
    358          break;
    359        case kBundleMaxBundle:
    360          // max-bundle means we use bundle-only on everything but the first
    361          // m-section
    362          useBundleOnly = !mids.empty();
    363          break;
    364      }
    365 
    366      if (useBundleOnly) {
    367        attrs.SetAttribute(
    368            new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
    369        // Set port to 0 for sections with bundle-only attribute. (mjf)
    370        sdp->GetMediaSection(i).SetPort(0);
    371      }
    372 
    373      mids.push_back(attrs.GetMid());
    374    }
    375  }
    376 
    377  if (!mids.empty()) {
    378    UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList);
    379    groupAttr->PushEntry(SdpGroupAttributeList::kBundle, mids);
    380    sdp->GetAttributeList().SetAttribute(groupAttr.release());
    381  }
    382 }
    383 
    384 JsepSession::Result JsepSessionImpl::CreateOffer(
    385    const JsepOfferOptions& options, std::string* offer) {
    386  mLastError.clear();
    387 
    388  if (mState != kJsepStateStable && mState != kJsepStateHaveLocalOffer) {
    389    JSEP_SET_ERROR("Cannot create offer in state " << GetStateStr(mState));
    390    // Spec doesn't seem to say this is an error. It probably should.
    391    return dom::PCError::InvalidStateError;
    392  }
    393 
    394  // This is one of those places where CreateOffer sets some state.
    395  SetIceRestarting(options.mIceRestart.isSome() && *(options.mIceRestart));
    396 
    397  UniquePtr<Sdp> sdp;
    398 
    399  // Make the basic SDP that is common to offer/answer.
    400  nsresult rv = CreateGenericSDP(&sdp);
    401  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    402 
    403  for (size_t level = 0;
    404       Maybe<JsepTransceiver> transceiver = GetTransceiverForLocal(level);
    405       ++level) {
    406    rv = CreateOfferMsection(options, *transceiver, sdp.get());
    407    NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    408    SetTransceiver(*transceiver);
    409  }
    410 
    411  SetupBundle(sdp.get());
    412 
    413  if (mCurrentLocalDescription && GetAnswer()) {
    414    rv = CopyPreviousTransportParams(*GetAnswer(), *mCurrentLocalDescription,
    415                                     *sdp, sdp.get());
    416    NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    417  }
    418 
    419  *offer = sdp->ToString();
    420  mGeneratedOffer = std::move(sdp);
    421  ++mSessionVersion;
    422  MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: CreateOffer \nSDP=\n" << *offer);
    423 
    424  return Result();
    425 }
    426 
    427 std::string JsepSessionImpl::GetLocalDescription(
    428    JsepDescriptionPendingOrCurrent type) const {
    429  std::ostringstream os;
    430  mozilla::Sdp* sdp = GetParsedLocalDescription(type);
    431  if (sdp) {
    432    sdp->Serialize(os);
    433  }
    434  return os.str();
    435 }
    436 
    437 std::string JsepSessionImpl::GetRemoteDescription(
    438    JsepDescriptionPendingOrCurrent type) const {
    439  std::ostringstream os;
    440  mozilla::Sdp* sdp = GetParsedRemoteDescription(type);
    441  if (sdp) {
    442    sdp->Serialize(os);
    443  }
    444  return os.str();
    445 }
    446 
    447 void JsepSessionImpl::AddExtmap(SdpMediaSection* msection) {
    448  auto extensions = GetRtpExtensions(*msection);
    449 
    450  if (!extensions.empty()) {
    451    SdpExtmapAttributeList* extmap = new SdpExtmapAttributeList;
    452    extmap->mExtmaps = extensions;
    453    msection->GetAttributeList().SetAttribute(extmap);
    454  }
    455 }
    456 
    457 std::vector<SdpExtmapAttributeList::Extmap> JsepSessionImpl::GetRtpExtensions(
    458    const SdpMediaSection& msection) {
    459  std::vector<SdpExtmapAttributeList::Extmap> result;
    460  JsepMediaType mediaType = JsepMediaType::kNone;
    461  const auto direction = msection.GetDirection();
    462  const auto includes_send = direction == SdpDirectionAttribute::kSendrecv ||
    463                             direction == SdpDirectionAttribute::kSendonly;
    464  switch (msection.GetMediaType()) {
    465    case SdpMediaSection::kAudio:
    466      mediaType = JsepMediaType::kAudio;
    467      break;
    468    case SdpMediaSection::kVideo:
    469      mediaType = JsepMediaType::kVideo;
    470      // We need to add the dependency descriptor extension for simulcast
    471      if (includes_send && StaticPrefs::media_peerconnection_video_use_dd() &&
    472          msection.GetAttributeList().HasAttribute(
    473              SdpAttribute::kSimulcastAttribute)) {
    474        AddVideoRtpExtension(webrtc::RtpExtension::kDependencyDescriptorUri,
    475                             SdpDirectionAttribute::kSendonly);
    476      }
    477      if (msection.GetAttributeList().HasAttribute(
    478              SdpAttribute::kRidAttribute)) {
    479        // We need RID support
    480        // TODO: Would it be worth checking that the direction is sane?
    481        AddVideoRtpExtension(webrtc::RtpExtension::kRidUri,
    482                             SdpDirectionAttribute::kSendonly);
    483 
    484        if (mRtxIsAllowed &&
    485            Preferences::GetBool("media.peerconnection.video.use_rtx", false)) {
    486          AddVideoRtpExtension(webrtc::RtpExtension::kRepairedRidUri,
    487                               SdpDirectionAttribute::kSendonly);
    488        }
    489      }
    490      break;
    491    default:;
    492  }
    493  if (mediaType != JsepMediaType::kNone) {
    494    for (auto ext = mRtpExtensions.begin(); ext != mRtpExtensions.end();
    495         ++ext) {
    496      if (ext->mMediaType == mediaType ||
    497          ext->mMediaType == JsepMediaType::kAudioVideo) {
    498        result.push_back(ext->mExtmap);
    499      }
    500    }
    501  }
    502  return result;
    503 }
    504 
    505 std::string JsepSessionImpl::GetNewMid() {
    506  std::string mid;
    507 
    508  do {
    509    std::ostringstream osMid;
    510    osMid << mMidCounter++;
    511    mid = osMid.str();
    512  } while (mUsedMids.count(mid));
    513 
    514  mUsedMids.insert(mid);
    515  return mid;
    516 }
    517 
    518 void JsepSessionImpl::AddCommonExtmaps(const SdpMediaSection& remoteMsection,
    519                                       SdpMediaSection* msection) {
    520  auto negotiatedRtpExtensions = GetRtpExtensions(*msection);
    521  mSdpHelper.NegotiateAndAddExtmaps(remoteMsection, negotiatedRtpExtensions,
    522                                    msection);
    523 }
    524 
    525 uint16_t JsepSessionImpl::GetNeverUsedExtmapEntry() {
    526  uint16_t result = 1;
    527 
    528  // Walk the set in order, and return the first "hole" we find
    529  for (const auto used : mExtmapEntriesEverUsed) {
    530    if (result != used) {
    531      MOZ_ASSERT(result < used);
    532      break;
    533    }
    534 
    535    // RFC 5285 says entries >= 4096 are used in offers to force the answerer
    536    // to pick, so we do not want to actually use these
    537    if (used == 4095) {
    538      JSEP_SET_ERROR(
    539          "Too many rtp extensions have been added. "
    540          "That's 4095. Who _does_ that?");
    541      return 0;
    542    }
    543 
    544    result = used + 1;
    545  }
    546 
    547  mExtmapEntriesEverUsed.insert(result);
    548  return result;
    549 }
    550 
    551 JsepSession::Result JsepSessionImpl::CreateAnswer(
    552    const JsepAnswerOptions& options, std::string* answer) {
    553  mLastError.clear();
    554 
    555  if (mState != kJsepStateHaveRemoteOffer) {
    556    JSEP_SET_ERROR("Cannot create answer in state " << GetStateStr(mState));
    557    return dom::PCError::InvalidStateError;
    558  }
    559 
    560  UniquePtr<Sdp> sdp;
    561 
    562  // Make the basic SDP that is common to offer/answer.
    563  nsresult rv = CreateGenericSDP(&sdp);
    564  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    565 
    566  const Sdp& offer = *mPendingRemoteDescription;
    567 
    568  // Copy the bundle groups into our answer
    569  UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList);
    570  mSdpHelper.GetBundleGroups(offer, &groupAttr->mGroups);
    571  sdp->GetAttributeList().SetAttribute(groupAttr.release());
    572 
    573  // Copy EXTMAP-ALLOW-MIXED from the offer to the answer
    574  if (offer.GetAttributeList().HasAttribute(
    575          SdpAttribute::kExtmapAllowMixedAttribute)) {
    576    sdp->GetAttributeList().SetAttribute(
    577        new SdpFlagAttribute(SdpAttribute::kExtmapAllowMixedAttribute));
    578  } else {
    579    sdp->GetAttributeList().RemoveAttribute(
    580        SdpAttribute::kExtmapAllowMixedAttribute);
    581  }
    582 
    583  for (size_t i = 0; i < offer.GetMediaSectionCount(); ++i) {
    584    // The transceivers are already in place, due to setRemote
    585    Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i));
    586    if (!transceiver) {
    587      JSEP_SET_ERROR("No transceiver for level " << i);
    588      MOZ_ASSERT(false);
    589      return dom::PCError::OperationError;
    590    }
    591    rv = CreateAnswerMsection(options, *transceiver, offer.GetMediaSection(i),
    592                              sdp.get());
    593    NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    594    SetTransceiver(*transceiver);
    595  }
    596 
    597  // Ensure that each bundle-group starts with a mid that has a transport, in
    598  // case we've disabled what the offerer wanted to use. If the group doesn't
    599  // contain anything that has a transport, remove it.
    600  groupAttr.reset(new SdpGroupAttributeList);
    601  std::vector<SdpGroupAttributeList::Group> bundleGroups;
    602  mSdpHelper.GetBundleGroups(*sdp, &bundleGroups);
    603  for (auto& group : bundleGroups) {
    604    for (auto& mid : group.tags) {
    605      const SdpMediaSection* msection =
    606          mSdpHelper.FindMsectionByMid(offer, mid);
    607 
    608      if (msection && !msection->GetAttributeList().HasAttribute(
    609                          SdpAttribute::kBundleOnlyAttribute)) {
    610        std::swap(group.tags[0], mid);
    611        groupAttr->mGroups.push_back(group);
    612        break;
    613      }
    614    }
    615  }
    616  sdp->GetAttributeList().SetAttribute(groupAttr.release());
    617 
    618  if (mCurrentLocalDescription) {
    619    // per discussion with bwc, 3rd parm here should be offer, not *sdp. (mjf)
    620    rv = CopyPreviousTransportParams(*GetAnswer(), *mCurrentRemoteDescription,
    621                                     offer, sdp.get());
    622    NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    623  }
    624 
    625  *answer = sdp->ToString();
    626  mGeneratedAnswer = std::move(sdp);
    627  ++mSessionVersion;
    628  MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: CreateAnswer \nSDP=\n" << *answer);
    629 
    630  return Result();
    631 }
    632 
    633 nsresult JsepSessionImpl::CreateAnswerMsection(
    634    const JsepAnswerOptions& options, JsepTransceiver& transceiver,
    635    const SdpMediaSection& remoteMsection, Sdp* sdp) {
    636  MOZ_ASSERT(transceiver.GetMediaType() == remoteMsection.GetMediaType());
    637  SdpDirectionAttribute::Direction direction =
    638      reverse(remoteMsection.GetDirection()) & transceiver.mJsDirection;
    639  SdpMediaSection& msection =
    640      sdp->AddMediaSection(remoteMsection.GetMediaType(), direction, 9,
    641                           remoteMsection.GetProtocol(), sdp::kIPv4, "0.0.0.0");
    642 
    643  nsresult rv = mSdpHelper.CopyStickyParams(remoteMsection, &msection);
    644  NS_ENSURE_SUCCESS(rv, rv);
    645 
    646  if (mSdpHelper.MsectionIsDisabled(remoteMsection)) {
    647    SdpHelper::DisableMsection(sdp, &msection);
    648    return NS_OK;
    649  }
    650 
    651  MOZ_ASSERT(transceiver.IsAssociated());
    652  if (msection.GetAttributeList().GetMid().empty()) {
    653    msection.GetAttributeList().SetAttribute(new SdpStringAttribute(
    654        SdpAttribute::kMidAttribute, transceiver.GetMid()));
    655  }
    656 
    657  MOZ_ASSERT(transceiver.GetMid() == msection.GetAttributeList().GetMid());
    658 
    659  SdpSetupAttribute::Role role;
    660  if (transceiver.mTransport.mDtls && !IsIceRestarting()) {
    661    role = (transceiver.mTransport.mDtls->mRole ==
    662            JsepDtlsTransport::kJsepDtlsClient)
    663               ? SdpSetupAttribute::kActive
    664               : SdpSetupAttribute::kPassive;
    665  } else {
    666    rv = DetermineAnswererSetupRole(remoteMsection, &role);
    667    NS_ENSURE_SUCCESS(rv, rv);
    668  }
    669 
    670  rv = AddTransportAttributes(&msection, role);
    671  NS_ENSURE_SUCCESS(rv, rv);
    672 
    673  transceiver.mSendTrack.AddToAnswer(remoteMsection, mSsrcGenerator, &msection);
    674  transceiver.mRecvTrack.AddToAnswer(remoteMsection, mSsrcGenerator, &msection);
    675 
    676  // Add extmap attributes. This logic will probably be moved to the track,
    677  // since it can be specified on a per-sender basis in JS.
    678  // We will need some validation to ensure that the ids are identical for
    679  // RTP streams that are bundled together, though (bug 1406529).
    680  AddCommonExtmaps(remoteMsection, &msection);
    681 
    682  if (msection.GetFormats().empty()) {
    683    // Could not negotiate anything. Disable m-section.
    684    SdpHelper::DisableMsection(sdp, &msection);
    685  }
    686 
    687  return NS_OK;
    688 }
    689 
    690 nsresult JsepSessionImpl::DetermineAnswererSetupRole(
    691    const SdpMediaSection& remoteMsection, SdpSetupAttribute::Role* rolep) {
    692  // Determine the role.
    693  // RFC 5763 says:
    694  //
    695  //   The endpoint MUST use the setup attribute defined in [RFC4145].
    696  //   The endpoint that is the offerer MUST use the setup attribute
    697  //   value of setup:actpass and be prepared to receive a client_hello
    698  //   before it receives the answer.  The answerer MUST use either a
    699  //   setup attribute value of setup:active or setup:passive.  Note that
    700  //   if the answerer uses setup:passive, then the DTLS handshake will
    701  //   not begin until the answerer is received, which adds additional
    702  //   latency. setup:active allows the answer and the DTLS handshake to
    703  //   occur in parallel.  Thus, setup:active is RECOMMENDED.  Whichever
    704  //   party is active MUST initiate a DTLS handshake by sending a
    705  //   ClientHello over each flow (host/port quartet).
    706  //
    707  //   We default to assuming that the offerer is passive and we are active.
    708  SdpSetupAttribute::Role role = SdpSetupAttribute::kActive;
    709 
    710  if (remoteMsection.GetAttributeList().HasAttribute(
    711          SdpAttribute::kSetupAttribute)) {
    712    switch (remoteMsection.GetAttributeList().GetSetup().mRole) {
    713      case SdpSetupAttribute::kActive:
    714        role = SdpSetupAttribute::kPassive;
    715        break;
    716      case SdpSetupAttribute::kPassive:
    717      case SdpSetupAttribute::kActpass:
    718        role = SdpSetupAttribute::kActive;
    719        break;
    720      case SdpSetupAttribute::kHoldconn:
    721        // This should have been caught by ParseSdp
    722        MOZ_ASSERT(false);
    723        JSEP_SET_ERROR(
    724            "The other side used an illegal setup attribute"
    725            " (\"holdconn\").");
    726        return NS_ERROR_INVALID_ARG;
    727    }
    728  }
    729 
    730  *rolep = role;
    731  return NS_OK;
    732 }
    733 
    734 JsepSession::Result JsepSessionImpl::SetLocalDescription(
    735    JsepSdpType type, const std::string& constSdp) {
    736  mLastError.clear();
    737  std::string sdp = constSdp;
    738 
    739  MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: SetLocalDescription type=" << type
    740                          << "\nSDP=\n"
    741                          << sdp);
    742 
    743  switch (type) {
    744    case kJsepSdpOffer:
    745      if (!mGeneratedOffer) {
    746        JSEP_SET_ERROR(
    747            "Cannot set local offer when createOffer has not been called.");
    748        return dom::PCError::InvalidModificationError;
    749      }
    750      if (sdp.empty()) {
    751        sdp = mGeneratedOffer->ToString();
    752      }
    753      if (mState == kJsepStateHaveLocalOffer) {
    754        // Rollback previous offer before applying the new one.
    755        SetLocalDescription(kJsepSdpRollback, "");
    756        MOZ_ASSERT(mState == kJsepStateStable);
    757      }
    758      break;
    759    case kJsepSdpAnswer:
    760    case kJsepSdpPranswer:
    761      if (!mGeneratedAnswer) {
    762        JSEP_SET_ERROR(
    763            "Cannot set local answer when createAnswer has not been called.");
    764        return dom::PCError::InvalidModificationError;
    765      }
    766      if (sdp.empty()) {
    767        sdp = mGeneratedAnswer->ToString();
    768      }
    769      break;
    770    case kJsepSdpRollback:
    771      if (mState != kJsepStateHaveLocalOffer) {
    772        JSEP_SET_ERROR("Cannot rollback local description in "
    773                       << GetStateStr(mState));
    774        // Currently, spec allows this in any state except stable, and
    775        // sRD(rollback) and sLD(rollback) do exactly the same thing.
    776        return dom::PCError::InvalidStateError;
    777      }
    778 
    779      mPendingLocalDescription.reset();
    780      SetState(kJsepStateStable);
    781      RollbackLocalOffer();
    782      return Result();
    783  }
    784 
    785  switch (mState) {
    786    case kJsepStateStable:
    787      if (type != kJsepSdpOffer) {
    788        JSEP_SET_ERROR("Cannot set local answer in state "
    789                       << GetStateStr(mState));
    790        return dom::PCError::InvalidStateError;
    791      }
    792      break;
    793    case kJsepStateHaveRemoteOffer:
    794      if (type != kJsepSdpAnswer && type != kJsepSdpPranswer) {
    795        JSEP_SET_ERROR("Cannot set local offer in state "
    796                       << GetStateStr(mState));
    797        return dom::PCError::InvalidStateError;
    798      }
    799      break;
    800    default:
    801      JSEP_SET_ERROR("Cannot set local offer or answer in state "
    802                     << GetStateStr(mState));
    803      return dom::PCError::InvalidStateError;
    804  }
    805 
    806  UniquePtr<Sdp> parsed;
    807  nsresult rv = ParseSdp(sdp, &parsed);
    808  // Needs to be RTCError with sdp-syntax-error
    809  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    810 
    811  // Check that content hasn't done anything unsupported with the SDP
    812  rv = ValidateLocalDescription(*parsed, type);
    813  NS_ENSURE_SUCCESS(rv, dom::PCError::InvalidModificationError);
    814 
    815  switch (type) {
    816    case kJsepSdpOffer:
    817      rv = ValidateOffer(*parsed);
    818      break;
    819    case kJsepSdpAnswer:
    820    case kJsepSdpPranswer:
    821      rv = ValidateAnswer(*mPendingRemoteDescription, *parsed);
    822      break;
    823    case kJsepSdpRollback:
    824      MOZ_CRASH();  // Handled above
    825  }
    826  NS_ENSURE_SUCCESS(rv, dom::PCError::InvalidAccessError);
    827 
    828  if (type == kJsepSdpOffer) {
    829    // Save in case we need to rollback
    830    mOldTransceivers = mTransceivers;
    831  }
    832 
    833  SdpHelper::BundledMids bundledMids;
    834  rv = mSdpHelper.GetBundledMids(*parsed, &bundledMids);
    835  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    836 
    837  SdpHelper::BundledMids remoteBundledMids;
    838  if (type != kJsepSdpOffer) {
    839    rv = mSdpHelper.GetBundledMids(*mPendingRemoteDescription,
    840                                   &remoteBundledMids);
    841    NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    842  }
    843 
    844  for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) {
    845    Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i));
    846    if (!transceiver) {
    847      MOZ_ASSERT(false);
    848      JSEP_SET_ERROR("No transceiver for level " << i);
    849      return dom::PCError::OperationError;
    850    }
    851 
    852    const auto& msection = parsed->GetMediaSection(i);
    853    transceiver->Associate(msection.GetAttributeList().GetMid());
    854    transceiver->mRecvTrack.RecvTrackSetLocal(msection);
    855 
    856    if (mSdpHelper.MsectionIsDisabled(msection)) {
    857      transceiver->mTransport.Close();
    858      SetTransceiver(*transceiver);
    859      continue;
    860    }
    861 
    862    bool hasOwnTransport = mSdpHelper.OwnsTransport(
    863        msection, bundledMids,
    864        (type == kJsepSdpOffer) ? sdp::kOffer : sdp::kAnswer);
    865    if (type != kJsepSdpOffer) {
    866      const auto& remoteMsection =
    867          mPendingRemoteDescription->GetMediaSection(i);
    868      // Don't allow the answer to override what the offer allowed for
    869      hasOwnTransport &= mSdpHelper.OwnsTransport(
    870          remoteMsection, remoteBundledMids, sdp::kOffer);
    871    }
    872 
    873    if (hasOwnTransport) {
    874      EnsureHasOwnTransport(parsed->GetMediaSection(i), *transceiver);
    875    }
    876 
    877    if (type == kJsepSdpOffer) {
    878      if (!hasOwnTransport) {
    879        auto it = bundledMids.find(transceiver->GetMid());
    880        if (it != bundledMids.end()) {
    881          transceiver->SetBundleLevel(it->second->GetLevel());
    882        }
    883      }
    884    } else {
    885      auto it = remoteBundledMids.find(transceiver->GetMid());
    886      if (it != remoteBundledMids.end()) {
    887        transceiver->SetBundleLevel(it->second->GetLevel());
    888      }
    889    }
    890    SetTransceiver(*transceiver);
    891  }
    892 
    893  CopyBundleTransports();
    894 
    895  switch (type) {
    896    case kJsepSdpOffer:
    897      rv = SetLocalDescriptionOffer(std::move(parsed));
    898      break;
    899    case kJsepSdpAnswer:
    900    case kJsepSdpPranswer:
    901      rv = SetLocalDescriptionAnswer(type, std::move(parsed));
    902      break;
    903    case kJsepSdpRollback:
    904      MOZ_CRASH();  // Handled above
    905  }
    906 
    907  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
    908  return Result();
    909 }
    910 
    911 nsresult JsepSessionImpl::SetLocalDescriptionOffer(UniquePtr<Sdp> offer) {
    912  MOZ_ASSERT(mState == kJsepStateStable);
    913  mPendingLocalDescription = std::move(offer);
    914  mIsPendingOfferer = Some(true);
    915  SetState(kJsepStateHaveLocalOffer);
    916 
    917  std::vector<JsepTrack*> recvTracks;
    918  recvTracks.reserve(mTransceivers.size());
    919  for (auto& transceiver : mTransceivers) {
    920    if (transceiver.mJsDirection & sdp::kRecv) {
    921      recvTracks.push_back(&transceiver.mRecvTrack);
    922    } else {
    923      transceiver.mRecvTrack.ResetReceivePayloadTypes();
    924    }
    925  }
    926 
    927  JsepTrack::SetReceivePayloadTypes(recvTracks, true);
    928 
    929  return NS_OK;
    930 }
    931 
    932 nsresult JsepSessionImpl::SetLocalDescriptionAnswer(JsepSdpType type,
    933                                                    UniquePtr<Sdp> answer) {
    934  MOZ_ASSERT(mState == kJsepStateHaveRemoteOffer);
    935  mPendingLocalDescription = std::move(answer);
    936 
    937  nsresult rv = HandleNegotiatedSession(mPendingLocalDescription,
    938                                        mPendingRemoteDescription);
    939  NS_ENSURE_SUCCESS(rv, rv);
    940 
    941  mCurrentRemoteDescription = std::move(mPendingRemoteDescription);
    942  mCurrentLocalDescription = std::move(mPendingLocalDescription);
    943  MOZ_ASSERT(mIsPendingOfferer.isSome() && !*mIsPendingOfferer);
    944  mIsPendingOfferer.reset();
    945  mIsCurrentOfferer = Some(false);
    946 
    947  SetState(kJsepStateStable);
    948  return NS_OK;
    949 }
    950 
    951 JsepSession::Result JsepSessionImpl::SetRemoteDescription(
    952    JsepSdpType type, const std::string& sdp) {
    953  mLastError.clear();
    954 
    955  MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: SetRemoteDescription type=" << type
    956                          << "\nSDP=\n"
    957                          << sdp);
    958 
    959  if (mState == kJsepStateHaveRemoteOffer && type == kJsepSdpOffer) {
    960    // Rollback previous offer before applying the new one.
    961    SetRemoteDescription(kJsepSdpRollback, "");
    962    MOZ_ASSERT(mState == kJsepStateStable);
    963  }
    964 
    965  if (type == kJsepSdpRollback) {
    966    if (mState != kJsepStateHaveRemoteOffer) {
    967      JSEP_SET_ERROR("Cannot rollback remote description in "
    968                     << GetStateStr(mState));
    969      return dom::PCError::InvalidStateError;
    970    }
    971 
    972    mPendingRemoteDescription.reset();
    973    SetState(kJsepStateStable);
    974    RollbackRemoteOffer();
    975 
    976    return Result();
    977  }
    978 
    979  switch (mState) {
    980    case kJsepStateStable:
    981      if (type != kJsepSdpOffer) {
    982        JSEP_SET_ERROR("Cannot set remote answer in state "
    983                       << GetStateStr(mState));
    984        return dom::PCError::InvalidStateError;
    985      }
    986      break;
    987    case kJsepStateHaveLocalOffer:
    988    case kJsepStateHaveRemotePranswer:
    989      if (type != kJsepSdpAnswer && type != kJsepSdpPranswer) {
    990        JSEP_SET_ERROR("Cannot set remote offer in state "
    991                       << GetStateStr(mState));
    992        return dom::PCError::InvalidStateError;
    993      }
    994      break;
    995    default:
    996      JSEP_SET_ERROR("Cannot set remote offer or answer in current state "
    997                     << GetStateStr(mState));
    998      return dom::PCError::InvalidStateError;
    999  }
   1000 
   1001  // Parse.
   1002  UniquePtr<Sdp> parsed;
   1003  nsresult rv = ParseSdp(sdp, &parsed);
   1004  // Needs to be RTCError with sdp-syntax-error
   1005  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
   1006 
   1007  rv = ValidateRemoteDescription(*parsed);
   1008  NS_ENSURE_SUCCESS(rv, dom::PCError::InvalidAccessError);
   1009 
   1010  switch (type) {
   1011    case kJsepSdpOffer:
   1012      rv = ValidateOffer(*parsed);
   1013      break;
   1014    case kJsepSdpAnswer:
   1015    case kJsepSdpPranswer:
   1016      rv = ValidateAnswer(*mPendingLocalDescription, *parsed);
   1017      break;
   1018    case kJsepSdpRollback:
   1019      MOZ_CRASH();  // Handled above
   1020  }
   1021  NS_ENSURE_SUCCESS(rv, dom::PCError::InvalidAccessError);
   1022 
   1023  bool iceLite =
   1024      parsed->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute);
   1025 
   1026  // check for mismatch ufrag/pwd indicating ice restart
   1027  // can't just check the first one because it might be disabled
   1028  bool iceRestarting = false;
   1029  if (mCurrentRemoteDescription.get()) {
   1030    for (size_t i = 0; !iceRestarting &&
   1031                       i < mCurrentRemoteDescription->GetMediaSectionCount();
   1032         ++i) {
   1033      const SdpMediaSection& newMsection = parsed->GetMediaSection(i);
   1034      const SdpMediaSection& oldMsection =
   1035          mCurrentRemoteDescription->GetMediaSection(i);
   1036 
   1037      if (mSdpHelper.MsectionIsDisabled(newMsection) ||
   1038          mSdpHelper.MsectionIsDisabled(oldMsection)) {
   1039        continue;
   1040      }
   1041 
   1042      iceRestarting = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
   1043    }
   1044  }
   1045 
   1046  std::vector<std::string> iceOptions;
   1047  if (parsed->GetAttributeList().HasAttribute(
   1048          SdpAttribute::kIceOptionsAttribute)) {
   1049    iceOptions = parsed->GetAttributeList().GetIceOptions().mValues;
   1050  }
   1051 
   1052  if (type == kJsepSdpOffer) {
   1053    // Save in case we need to rollback.
   1054    mOldTransceivers = mTransceivers;
   1055    for (auto& transceiver : mTransceivers) {
   1056      if (!transceiver.IsNegotiated()) {
   1057        // We chose a level for this transceiver, but never negotiated it.
   1058        // Discard this state.
   1059        transceiver.ClearLevel();
   1060      }
   1061    }
   1062  }
   1063 
   1064  // TODO(bug 1095780): Note that we create remote tracks even when
   1065  // They contain only codecs we can't negotiate or other craziness.
   1066  rv = UpdateTransceiversFromRemoteDescription(*parsed);
   1067  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
   1068 
   1069  for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) {
   1070    MOZ_ASSERT(GetTransceiverForLevel(i));
   1071  }
   1072 
   1073  switch (type) {
   1074    case kJsepSdpOffer:
   1075      rv = SetRemoteDescriptionOffer(std::move(parsed));
   1076      break;
   1077    case kJsepSdpAnswer:
   1078    case kJsepSdpPranswer:
   1079      rv = SetRemoteDescriptionAnswer(type, std::move(parsed));
   1080      break;
   1081    case kJsepSdpRollback:
   1082      MOZ_CRASH();  // Handled above
   1083  }
   1084 
   1085  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
   1086 
   1087  mRemoteIsIceLite = iceLite;
   1088  mIceOptions = iceOptions;
   1089  SetIceRestarting(iceRestarting);
   1090  return Result();
   1091 }
   1092 
   1093 nsresult JsepSessionImpl::HandleNegotiatedSession(
   1094    const UniquePtr<Sdp>& local, const UniquePtr<Sdp>& remote) {
   1095  // local ufrag/pwd has been negotiated; we will never go back to the old ones
   1096  mOldIceUfrag.clear();
   1097  mOldIcePwd.clear();
   1098 
   1099  bool remoteIceLite =
   1100      remote->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute);
   1101 
   1102  mIceControlling = remoteIceLite || *mIsPendingOfferer;
   1103 
   1104  const Sdp& answer = *mIsPendingOfferer ? *remote : *local;
   1105 
   1106  SdpHelper::BundledMids bundledMids;
   1107  nsresult rv = mSdpHelper.GetBundledMids(answer, &bundledMids);
   1108  NS_ENSURE_SUCCESS(rv, rv);
   1109 
   1110  // First, set the bundle level on the transceivers
   1111  for (auto& [mid, transportOwner] : bundledMids) {
   1112    Maybe<JsepTransceiver> bundledTransceiver = GetTransceiverForMid(mid);
   1113    if (!bundledTransceiver) {
   1114      JSEP_SET_ERROR("No transceiver for bundled mid " << mid);
   1115      return NS_ERROR_INVALID_ARG;
   1116    }
   1117    bundledTransceiver->SetBundleLevel(transportOwner->GetLevel());
   1118    SetTransceiver(*bundledTransceiver);
   1119  }
   1120 
   1121  // Now walk through the m-sections, perform negotiation, and update the
   1122  // transceivers.
   1123  for (size_t i = 0; i < local->GetMediaSectionCount(); ++i) {
   1124    Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i));
   1125    if (!transceiver) {
   1126      MOZ_ASSERT(false);
   1127      JSEP_SET_ERROR("No transceiver for level " << i);
   1128      return NS_ERROR_FAILURE;
   1129    }
   1130 
   1131    if (mSdpHelper.MsectionIsDisabled(local->GetMediaSection(i))) {
   1132      transceiver->SetRemoved();
   1133    }
   1134 
   1135    // Skip disabled m-sections.
   1136    if (mSdpHelper.MsectionIsDisabled(answer.GetMediaSection(i))) {
   1137      transceiver->mTransport.Close();
   1138      transceiver->SetStopped();
   1139      transceiver->Disassociate();
   1140      transceiver->ClearBundleLevel();
   1141      transceiver->mSendTrack.SetActive(false);
   1142      transceiver->mRecvTrack.SetActive(false);
   1143      transceiver->SetCanRecycleMyMsection();
   1144      SetTransceiver(*transceiver);
   1145      // Do not clear mLevel yet! That will happen on the next negotiation.
   1146      continue;
   1147    }
   1148 
   1149    rv = MakeNegotiatedTransceiver(remote->GetMediaSection(i),
   1150                                   local->GetMediaSection(i), *transceiver);
   1151    NS_ENSURE_SUCCESS(rv, rv);
   1152    SetTransceiver(*transceiver);
   1153  }
   1154 
   1155  CopyBundleTransports();
   1156 
   1157  std::vector<JsepTrack*> receiveTracks;
   1158  receiveTracks.reserve(mTransceivers.size());
   1159  for (auto& transceiver : mTransceivers) {
   1160    // Do not count payload types for non-active recv tracks as duplicates. If
   1161    // we receive an RTP packet with a payload type that is used by both a
   1162    // sendrecv and a sendonly m-section, there is no ambiguity; it is for the
   1163    // sendrecv m-section. MediaPipelineFilter and conduits are informed of
   1164    // their active status, so they know whether they can process packets and
   1165    // learn new SSRCs.
   1166    if (transceiver.mRecvTrack.GetActive()) {
   1167      receiveTracks.push_back(&transceiver.mRecvTrack);
   1168    } else {
   1169      transceiver.mRecvTrack.ResetReceivePayloadTypes();
   1170    }
   1171  }
   1172  JsepTrack::SetReceivePayloadTypes(receiveTracks);
   1173 
   1174  mNegotiations++;
   1175 
   1176  mGeneratedAnswer.reset();
   1177  mGeneratedOffer.reset();
   1178 
   1179  return NS_OK;
   1180 }
   1181 
   1182 nsresult JsepSessionImpl::MakeNegotiatedTransceiver(
   1183    const SdpMediaSection& remote, const SdpMediaSection& local,
   1184    JsepTransceiver& transceiver) {
   1185  const SdpMediaSection& answer = *mIsPendingOfferer ? remote : local;
   1186 
   1187  bool sending = false;
   1188  bool receiving = false;
   1189 
   1190  // We do not pay any attention to whether the transceiver is stopped here,
   1191  // because that is only a signal to the JSEP engine to _attempt_ to reject
   1192  // the corresponding m-section the next time we're the offerer.
   1193  if (*mIsPendingOfferer) {
   1194    receiving = answer.IsSending();
   1195    sending = answer.IsReceiving();
   1196  } else {
   1197    sending = answer.IsSending();
   1198    receiving = answer.IsReceiving();
   1199  }
   1200 
   1201  MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: Negotiated m= line"
   1202                          << " index=" << local.GetLevel() << " type="
   1203                          << local.GetMediaType() << " sending=" << sending
   1204                          << " receiving=" << receiving);
   1205 
   1206  transceiver.SetNegotiated();
   1207 
   1208  // Ensure that this is finalized in case we need to copy it below
   1209  nsresult rv =
   1210      FinalizeTransport(remote.GetAttributeList(), answer.GetAttributeList(),
   1211                        &transceiver.mTransport);
   1212  NS_ENSURE_SUCCESS(rv, rv);
   1213 
   1214  transceiver.mSendTrack.SetActive(sending);
   1215  rv = transceiver.mSendTrack.Negotiate(answer, remote, local);
   1216  if (NS_FAILED(rv)) {
   1217    JSEP_SET_ERROR("Answer had no codecs in common with offer in m-section "
   1218                   << local.GetLevel());
   1219    return rv;
   1220  }
   1221 
   1222  JsepTrack& recvTrack = transceiver.mRecvTrack;
   1223  recvTrack.SetActive(receiving);
   1224  rv = recvTrack.Negotiate(answer, remote, local);
   1225  if (NS_FAILED(rv)) {
   1226    JSEP_SET_ERROR("Answer had no codecs in common with offer in m-section "
   1227                   << local.GetLevel());
   1228    return rv;
   1229  }
   1230 
   1231  if (transceiver.HasBundleLevel() && recvTrack.GetSsrcs().empty() &&
   1232      recvTrack.GetMediaType() != SdpMediaSection::kApplication) {
   1233    // TODO(bug 1105005): Once we have urn:ietf:params:rtp-hdrext:sdes:mid
   1234    // support, we should only fire this warning if that extension was not
   1235    // negotiated.
   1236    MOZ_MTLOG(ML_ERROR, "[" << mName
   1237                            << "]: Bundled m-section has no ssrc "
   1238                               "attributes. This may cause media packets to be "
   1239                               "dropped.");
   1240  }
   1241 
   1242  if (transceiver.mTransport.mComponents == 2) {
   1243    // RTCP MUX or not.
   1244    // TODO(bug 1095743): verify that the PTs are consistent with mux.
   1245    MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: RTCP-MUX is off");
   1246  }
   1247 
   1248  if (answer.GetAttributeList().HasAttribute(SdpAttribute::kExtmapAttribute)) {
   1249    const auto extmaps = answer.GetAttributeList().GetExtmap().mExtmaps;
   1250    for (const auto& negotiatedExtension : extmaps) {
   1251      if (negotiatedExtension.entry == 0) {
   1252        MOZ_ASSERT(false, "This should have been caught sooner");
   1253        continue;
   1254      }
   1255 
   1256      mExtmapEntriesEverNegotiated[negotiatedExtension.entry] =
   1257          negotiatedExtension.extensionname;
   1258 
   1259      for (auto& originalExtension : mRtpExtensions) {
   1260        if (negotiatedExtension.extensionname ==
   1261            originalExtension.mExtmap.extensionname) {
   1262          // Update extmap to match what was negotiated
   1263          originalExtension.mExtmap.entry = negotiatedExtension.entry;
   1264          mExtmapEntriesEverUsed.insert(negotiatedExtension.entry);
   1265        } else if (originalExtension.mExtmap.entry ==
   1266                   negotiatedExtension.entry) {
   1267          // If this extmap entry was claimed for a different extension, update
   1268          // it to a new value so we don't end up with a duplicate.
   1269          originalExtension.mExtmap.entry = GetNeverUsedExtmapEntry();
   1270        }
   1271      }
   1272    }
   1273  }
   1274 
   1275  return NS_OK;
   1276 }
   1277 
   1278 void JsepSessionImpl::EnsureHasOwnTransport(const SdpMediaSection& msection,
   1279                                            JsepTransceiver& transceiver) {
   1280  JsepTransport& transport = transceiver.mTransport;
   1281 
   1282  if (!transceiver.HasOwnTransport()) {
   1283    // Transceiver didn't own this transport last time, it won't now either
   1284    transport.Close();
   1285  }
   1286 
   1287  transport.mLocalUfrag = msection.GetAttributeList().GetIceUfrag();
   1288  transport.mLocalPwd = msection.GetAttributeList().GetIcePwd();
   1289 
   1290  transceiver.ClearBundleLevel();
   1291 
   1292  if (!transport.mComponents) {
   1293    if (mSdpHelper.HasRtcp(msection.GetProtocol())) {
   1294      transport.mComponents = 2;
   1295    } else {
   1296      transport.mComponents = 1;
   1297    }
   1298  }
   1299 
   1300  if (transport.mTransportId.empty()) {
   1301    // TODO: Once we use different ICE ufrag/pass for each m-section, we can
   1302    // use that here.
   1303    std::ostringstream os;
   1304    os << "transport_" << mTransportIdCounter++;
   1305    transport.mTransportId = os.str();
   1306  }
   1307 }
   1308 
   1309 void JsepSessionImpl::CopyBundleTransports() {
   1310  for (auto& transceiver : mTransceivers) {
   1311    if (transceiver.HasBundleLevel()) {
   1312      MOZ_MTLOG(ML_DEBUG,
   1313                "[" << mName << "] Transceiver " << transceiver.GetLevel()
   1314                    << " is in a bundle; transceiver "
   1315                    << transceiver.BundleLevel() << " owns the transport.");
   1316      Maybe<const JsepTransceiver> transportOwner =
   1317          GetTransceiverForLevel(transceiver.BundleLevel());
   1318      MOZ_ASSERT(transportOwner);
   1319      if (transportOwner) {
   1320        transceiver.mTransport = transportOwner->mTransport;
   1321      }
   1322    } else if (transceiver.HasLevel()) {
   1323      MOZ_MTLOG(ML_DEBUG, "[" << mName << "] Transceiver "
   1324                              << transceiver.GetLevel()
   1325                              << " is not necessarily in a bundle.");
   1326    }
   1327    if (transceiver.HasLevel()) {
   1328      MOZ_MTLOG(ML_DEBUG,
   1329                "[" << mName << "] Transceiver " << transceiver.GetLevel()
   1330                    << " transport-id: " << transceiver.mTransport.mTransportId
   1331                    << " components: " << transceiver.mTransport.mComponents);
   1332    }
   1333  }
   1334 }
   1335 
   1336 nsresult JsepSessionImpl::FinalizeTransport(const SdpAttributeList& remote,
   1337                                            const SdpAttributeList& answer,
   1338                                            JsepTransport* transport) const {
   1339  if (!transport->mComponents) {
   1340    return NS_OK;
   1341  }
   1342 
   1343  if (!transport->mIce || transport->mIce->mUfrag != remote.GetIceUfrag() ||
   1344      transport->mIce->mPwd != remote.GetIcePwd()) {
   1345    UniquePtr<JsepIceTransport> ice = MakeUnique<JsepIceTransport>();
   1346    transport->mDtls = nullptr;
   1347 
   1348    // We do sanity-checking for these in ParseSdp
   1349    ice->mUfrag = remote.GetIceUfrag();
   1350    ice->mPwd = remote.GetIcePwd();
   1351    transport->mIce = std::move(ice);
   1352  }
   1353 
   1354  if (remote.HasAttribute(SdpAttribute::kCandidateAttribute)) {
   1355    transport->mIce->mCandidates = remote.GetCandidate();
   1356  }
   1357 
   1358  if (!transport->mDtls) {
   1359    // RFC 5763 says:
   1360    //
   1361    //   The endpoint MUST use the setup attribute defined in [RFC4145].
   1362    //   The endpoint that is the offerer MUST use the setup attribute
   1363    //   value of setup:actpass and be prepared to receive a client_hello
   1364    //   before it receives the answer.  The answerer MUST use either a
   1365    //   setup attribute value of setup:active or setup:passive.  Note that
   1366    //   if the answerer uses setup:passive, then the DTLS handshake will
   1367    //   not begin until the answerer is received, which adds additional
   1368    //   latency. setup:active allows the answer and the DTLS handshake to
   1369    //   occur in parallel.  Thus, setup:active is RECOMMENDED.  Whichever
   1370    //   party is active MUST initiate a DTLS handshake by sending a
   1371    //   ClientHello over each flow (host/port quartet).
   1372    UniquePtr<JsepDtlsTransport> dtls = MakeUnique<JsepDtlsTransport>();
   1373    dtls->mFingerprints = remote.GetFingerprint();
   1374    if (!answer.HasAttribute(mozilla::SdpAttribute::kSetupAttribute)) {
   1375      dtls->mRole = *mIsPendingOfferer ? JsepDtlsTransport::kJsepDtlsServer
   1376                                       : JsepDtlsTransport::kJsepDtlsClient;
   1377    } else {
   1378      if (*mIsPendingOfferer) {
   1379        dtls->mRole = (answer.GetSetup().mRole == SdpSetupAttribute::kActive)
   1380                          ? JsepDtlsTransport::kJsepDtlsServer
   1381                          : JsepDtlsTransport::kJsepDtlsClient;
   1382      } else {
   1383        dtls->mRole = (answer.GetSetup().mRole == SdpSetupAttribute::kActive)
   1384                          ? JsepDtlsTransport::kJsepDtlsClient
   1385                          : JsepDtlsTransport::kJsepDtlsServer;
   1386      }
   1387    }
   1388 
   1389    transport->mDtls = std::move(dtls);
   1390  }
   1391 
   1392  if (answer.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
   1393    transport->mComponents = 1;
   1394  }
   1395 
   1396  return NS_OK;
   1397 }
   1398 
   1399 nsresult JsepSessionImpl::AddTransportAttributes(
   1400    SdpMediaSection* msection, SdpSetupAttribute::Role dtlsRole) {
   1401  if (mIceUfrag.empty() || mIcePwd.empty()) {
   1402    JSEP_SET_ERROR("Missing ICE ufrag or password");
   1403    return NS_ERROR_FAILURE;
   1404  }
   1405 
   1406  SdpAttributeList& attrList = msection->GetAttributeList();
   1407  attrList.SetAttribute(
   1408      new SdpStringAttribute(SdpAttribute::kIceUfragAttribute, mIceUfrag));
   1409  attrList.SetAttribute(
   1410      new SdpStringAttribute(SdpAttribute::kIcePwdAttribute, mIcePwd));
   1411 
   1412  msection->GetAttributeList().SetAttribute(new SdpSetupAttribute(dtlsRole));
   1413 
   1414  return NS_OK;
   1415 }
   1416 
   1417 nsresult JsepSessionImpl::CopyPreviousTransportParams(
   1418    const Sdp& oldAnswer, const Sdp& offerersPreviousSdp, const Sdp& newOffer,
   1419    Sdp* newLocal) {
   1420  for (size_t i = 0; i < oldAnswer.GetMediaSectionCount(); ++i) {
   1421    if (!mSdpHelper.MsectionIsDisabled(newLocal->GetMediaSection(i)) &&
   1422        mSdpHelper.AreOldTransportParamsValid(oldAnswer, offerersPreviousSdp,
   1423                                              newOffer, i)) {
   1424      // If newLocal is an offer, this will be the number of components we used
   1425      // last time, and if it is an answer, this will be the number of
   1426      // components we've decided we're using now.
   1427      Maybe<const JsepTransceiver> transceiver(GetTransceiverForLevel(i));
   1428      if (!transceiver) {
   1429        MOZ_ASSERT(false);
   1430        JSEP_SET_ERROR("No transceiver for level " << i);
   1431        return NS_ERROR_FAILURE;
   1432      }
   1433      size_t numComponents = transceiver->mTransport.mComponents;
   1434      nsresult rv = mSdpHelper.CopyTransportParams(
   1435          numComponents, mCurrentLocalDescription->GetMediaSection(i),
   1436          &newLocal->GetMediaSection(i));
   1437      NS_ENSURE_SUCCESS(rv, rv);
   1438    }
   1439  }
   1440 
   1441  return NS_OK;
   1442 }
   1443 
   1444 nsresult JsepSessionImpl::ParseSdp(const std::string& sdp,
   1445                                   UniquePtr<Sdp>* parsedp) {
   1446  auto results = mParser->Parse(sdp);
   1447  auto parsed = std::move(results->Sdp());
   1448  mLastSdpParsingErrors = results->Errors();
   1449  if (!parsed) {
   1450    std::string error = results->ParserName() + " Failed to parse SDP: ";
   1451    mSdpHelper.AppendSdpParseErrors(mLastSdpParsingErrors, &error);
   1452    JSEP_SET_ERROR(error);
   1453    return NS_ERROR_INVALID_ARG;
   1454  }
   1455  // Verify that the JSEP rules for all SDP are followed
   1456  for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) {
   1457    if (mSdpHelper.MsectionIsDisabled(parsed->GetMediaSection(i))) {
   1458      // Disabled, let this stuff slide.
   1459      continue;
   1460    }
   1461 
   1462    const SdpMediaSection& msection(parsed->GetMediaSection(i));
   1463    auto& mediaAttrs = msection.GetAttributeList();
   1464 
   1465    if (mediaAttrs.HasAttribute(SdpAttribute::kMidAttribute) &&
   1466        mediaAttrs.GetMid().length() > 16) {
   1467      JSEP_SET_ERROR(
   1468          "Invalid description, mid length greater than 16 "
   1469          "unsupported until 2-byte rtp header extensions are "
   1470          "supported in webrtc.org");
   1471      return NS_ERROR_INVALID_ARG;
   1472    }
   1473 
   1474    if (mediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)) {
   1475      std::set<uint16_t> extIds;
   1476      for (const auto& ext : mediaAttrs.GetExtmap().mExtmaps) {
   1477        uint16_t id = ext.entry;
   1478 
   1479        if (id < 1 || id > 14) {
   1480          JSEP_SET_ERROR("Description contains invalid extension id "
   1481                         << id << " on level " << i
   1482                         << " which is unsupported until 2-byte rtp"
   1483                            " header extensions are supported in webrtc.org");
   1484          return NS_ERROR_INVALID_ARG;
   1485        }
   1486 
   1487        if (extIds.find(id) != extIds.end()) {
   1488          JSEP_SET_ERROR("Description contains duplicate extension id "
   1489                         << id << " on level " << i);
   1490          return NS_ERROR_INVALID_ARG;
   1491        }
   1492        extIds.insert(id);
   1493      }
   1494    }
   1495 
   1496    static const std::bitset<128> forbidden = GetForbiddenSdpPayloadTypes();
   1497    if (msection.GetMediaType() == SdpMediaSection::kAudio ||
   1498        msection.GetMediaType() == SdpMediaSection::kVideo) {
   1499      // Sanity-check that payload type can work with RTP
   1500      for (const std::string& fmt : msection.GetFormats()) {
   1501        uint16_t payloadType;
   1502        if (!SdpHelper::GetPtAsInt(fmt, &payloadType)) {
   1503          JSEP_SET_ERROR("Payload type \""
   1504                         << fmt << "\" is not a 16-bit unsigned int at level "
   1505                         << i);
   1506          return NS_ERROR_INVALID_ARG;
   1507        }
   1508        if (payloadType > 127) {
   1509          JSEP_SET_ERROR("audio/video payload type \""
   1510                         << fmt << "\" is too large at level " << i);
   1511          return NS_ERROR_INVALID_ARG;
   1512        }
   1513        if (forbidden.test(payloadType)) {
   1514          JSEP_SET_ERROR("Illegal audio/video payload type \""
   1515                         << fmt << "\" at level " << i);
   1516          return NS_ERROR_INVALID_ARG;
   1517        }
   1518      }
   1519    }
   1520  }
   1521 
   1522  *parsedp = std::move(parsed);
   1523  return NS_OK;
   1524 }
   1525 
   1526 nsresult JsepSessionImpl::SetRemoteDescriptionOffer(UniquePtr<Sdp> offer) {
   1527  MOZ_ASSERT(mState == kJsepStateStable);
   1528 
   1529  mPendingRemoteDescription = std::move(offer);
   1530  mIsPendingOfferer = Some(false);
   1531 
   1532  SetState(kJsepStateHaveRemoteOffer);
   1533  return NS_OK;
   1534 }
   1535 
   1536 nsresult JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type,
   1537                                                     UniquePtr<Sdp> answer) {
   1538  MOZ_ASSERT(mState == kJsepStateHaveLocalOffer ||
   1539             mState == kJsepStateHaveRemotePranswer);
   1540 
   1541  mPendingRemoteDescription = std::move(answer);
   1542 
   1543  nsresult rv = HandleNegotiatedSession(mPendingLocalDescription,
   1544                                        mPendingRemoteDescription);
   1545  NS_ENSURE_SUCCESS(rv, rv);
   1546 
   1547  mCurrentRemoteDescription = std::move(mPendingRemoteDescription);
   1548  mCurrentLocalDescription = std::move(mPendingLocalDescription);
   1549  MOZ_ASSERT(mIsPendingOfferer.isSome() && *mIsPendingOfferer);
   1550  mIsPendingOfferer.reset();
   1551  mIsCurrentOfferer = Some(true);
   1552 
   1553  SetState(kJsepStateStable);
   1554  return NS_OK;
   1555 }
   1556 
   1557 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForLevel(
   1558    size_t level) const {
   1559  return FindTransceiver([level](const JsepTransceiver& transceiver) {
   1560    return transceiver.HasLevel() && (transceiver.GetLevel() == level);
   1561  });
   1562 }
   1563 
   1564 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForMid(
   1565    const std::string& mid) const {
   1566  return FindTransceiver([mid](const JsepTransceiver& transceiver) {
   1567    return transceiver.IsAssociated() && (transceiver.GetMid() == mid);
   1568  });
   1569 }
   1570 
   1571 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForLocal(size_t level) {
   1572  if (Maybe<JsepTransceiver> transceiver = GetTransceiverForLevel(level)) {
   1573    if (transceiver->CanRecycleMyMsection() &&
   1574        transceiver->GetMediaType() != SdpMediaSection::kApplication) {
   1575      // Attempt to recycle. If this fails, the old transceiver stays put.
   1576      transceiver->Disassociate();
   1577      Maybe<JsepTransceiver> newTransceiver =
   1578          FindUnassociatedTransceiver(transceiver->GetMediaType(), false);
   1579      if (newTransceiver) {
   1580        newTransceiver->SetLevel(level);
   1581        transceiver->ClearLevel();
   1582        transceiver->mSendTrack.ClearRids();
   1583        SetTransceiver(*newTransceiver);
   1584        SetTransceiver(*transceiver);
   1585        return newTransceiver;
   1586      }
   1587    }
   1588 
   1589    SetTransceiver(*transceiver);
   1590    return transceiver;
   1591  }
   1592 
   1593  // There is no transceiver for |level| right now.
   1594 
   1595  // Look for an RTP transceiver (spec requires us to give the lower levels to
   1596  // new RTP transceivers)
   1597  for (auto& transceiver : mTransceivers) {
   1598    if (transceiver.GetMediaType() != SdpMediaSection::kApplication &&
   1599        transceiver.IsFreeToUse()) {
   1600      transceiver.SetLevel(level);
   1601      return Some(transceiver);
   1602    }
   1603  }
   1604 
   1605  // Ok, look for a datachannel
   1606  for (auto& transceiver : mTransceivers) {
   1607    if (transceiver.IsFreeToUse()) {
   1608      transceiver.SetLevel(level);
   1609      return Some(transceiver);
   1610    }
   1611  }
   1612 
   1613  return Nothing();
   1614 }
   1615 
   1616 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForRemote(
   1617    const SdpMediaSection& msection) {
   1618  size_t level = msection.GetLevel();
   1619  Maybe<JsepTransceiver> transceiver = GetTransceiverForLevel(level);
   1620  if (transceiver) {
   1621    if (!transceiver->CanRecycleMyMsection()) {
   1622      return transceiver;
   1623    }
   1624    transceiver->Disassociate();
   1625    transceiver->ClearLevel();
   1626    transceiver->mSendTrack.ClearRids();
   1627    SetTransceiver(*transceiver);
   1628  }
   1629 
   1630  // No transceiver for |level|
   1631  transceiver = FindUnassociatedTransceiver(msection.GetMediaType(), true);
   1632  if (transceiver) {
   1633    transceiver->SetLevel(level);
   1634    SetTransceiver(*transceiver);
   1635    return transceiver;
   1636  }
   1637 
   1638  // Make a new transceiver
   1639  JsepTransceiver newTransceiver(msection.GetMediaType(), *mUuidGen,
   1640                                 SdpDirectionAttribute::kRecvonly);
   1641  newTransceiver.SetLevel(level);
   1642  newTransceiver.SetOnlyExistsBecauseOfSetRemote(true);
   1643  AddTransceiver(newTransceiver);
   1644  return Some(mTransceivers.back());
   1645 }
   1646 
   1647 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverWithTransport(
   1648    const std::string& transportId) const {
   1649  for (const auto& transceiver : mTransceivers) {
   1650    if (transceiver.HasOwnTransport() &&
   1651        (transceiver.mTransport.mTransportId == transportId)) {
   1652      MOZ_ASSERT(transceiver.HasLevel(),
   1653                 "Transceiver has a transport, but no level!");
   1654      return Some(transceiver);
   1655    }
   1656  }
   1657 
   1658  return Nothing();
   1659 }
   1660 
   1661 nsresult JsepSessionImpl::UpdateTransceiversFromRemoteDescription(
   1662    const Sdp& remote) {
   1663  // Iterate over the sdp, updating remote tracks as we go
   1664  for (size_t i = 0; i < remote.GetMediaSectionCount(); ++i) {
   1665    const SdpMediaSection& msection = remote.GetMediaSection(i);
   1666 
   1667    Maybe<JsepTransceiver> transceiver(GetTransceiverForRemote(msection));
   1668    if (!transceiver) {
   1669      return NS_ERROR_FAILURE;
   1670    }
   1671 
   1672    if (!mSdpHelper.MsectionIsDisabled(msection)) {
   1673      if (msection.GetAttributeList().HasAttribute(
   1674              SdpAttribute::kMidAttribute)) {
   1675        transceiver->Associate(msection.GetAttributeList().GetMid());
   1676      }
   1677      if (!transceiver->IsAssociated()) {
   1678        transceiver->Associate(GetNewMid());
   1679      } else {
   1680        mUsedMids.insert(transceiver->GetMid());
   1681      }
   1682    } else {
   1683      // We do not disassociate here, that happens when negotiation completes
   1684      // These things cannot be rolled back.
   1685      transceiver->mTransport.Close();
   1686      transceiver->SetStopped();
   1687      SetTransceiver(*transceiver);
   1688      continue;
   1689    }
   1690 
   1691    if (msection.GetMediaType() == SdpMediaSection::MediaType::kApplication) {
   1692      SetTransceiver(*transceiver);
   1693      continue;
   1694    }
   1695 
   1696    transceiver->mSendTrack.SendTrackSetRemote(mSsrcGenerator, msection);
   1697 
   1698    // Interop workaround for endpoints that don't support msid.
   1699    // Ensures that there is a default stream id set, provided the remote is
   1700    // sending.
   1701    // TODO(bug 1426005): Remove this, or at least move it to JsepTrack.
   1702    transceiver->mRecvTrack.UpdateStreamIds({mDefaultRemoteStreamId});
   1703 
   1704    // This will process a=msid if present, or clear the stream ids if the
   1705    // msection is not sending. If the msection is sending, and there are no
   1706    // a=msid, the previously set default will stay.
   1707    transceiver->mRecvTrack.RecvTrackSetRemote(remote, msection);
   1708    SetTransceiver(*transceiver);
   1709  }
   1710 
   1711  return NS_OK;
   1712 }
   1713 
   1714 Maybe<JsepTransceiver> JsepSessionImpl::FindUnassociatedTransceiver(
   1715    SdpMediaSection::MediaType type, bool magic) {
   1716  // Look through transceivers that are not mapped to an m-section
   1717  for (auto& transceiver : mTransceivers) {
   1718    if (type == SdpMediaSection::kApplication &&
   1719        type == transceiver.GetMediaType()) {
   1720      transceiver.RestartDatachannelTransceiver();
   1721      return Some(transceiver);
   1722    }
   1723    if (transceiver.IsFreeToUse() &&
   1724        (!magic || transceiver.HasAddTrackMagic()) &&
   1725        (transceiver.GetMediaType() == type)) {
   1726      return Some(transceiver);
   1727    }
   1728  }
   1729 
   1730  return Nothing();
   1731 }
   1732 
   1733 void JsepSessionImpl::RollbackLocalOffer() {
   1734  for (size_t i = 0; i < mTransceivers.size(); ++i) {
   1735    auto& transceiver = mTransceivers[i];
   1736    if (mOldTransceivers.size() > i) {
   1737      transceiver.Rollback(mOldTransceivers[i], false);
   1738      mOldTransceivers[i] = transceiver;
   1739      continue;
   1740    }
   1741 
   1742    JsepTransceiver temp(transceiver.GetMediaType(), *mUuidGen);
   1743    InitTransceiver(temp);
   1744    transceiver.Rollback(temp, false);
   1745    mOldTransceivers.push_back(transceiver);
   1746  }
   1747 
   1748  mTransceivers = std::move(mOldTransceivers);
   1749 }
   1750 
   1751 void JsepSessionImpl::RollbackRemoteOffer() {
   1752  for (size_t i = 0; i < mTransceivers.size(); ++i) {
   1753    auto& transceiver = mTransceivers[i];
   1754    if (mOldTransceivers.size() > i) {
   1755      // Some stuff cannot be rolled back. Save this information.
   1756      transceiver.Rollback(mOldTransceivers[i], true);
   1757      mOldTransceivers[i] = transceiver;
   1758      continue;
   1759    }
   1760 
   1761    if (transceiver.HasLevel()) {
   1762      // New transceiver, that was either created by the remote offer, or
   1763      // attached to the remote offer.
   1764      // We rollback even for transceivers we will remove, just to ensure we end
   1765      // up at the starting state.
   1766      JsepTransceiver temp(transceiver.GetMediaType(), *mUuidGen);
   1767      InitTransceiver(temp);
   1768      transceiver.Rollback(temp, true);
   1769 
   1770      if (transceiver.OnlyExistsBecauseOfSetRemote()) {
   1771        transceiver.SetStopped();
   1772        transceiver.Disassociate();
   1773        transceiver.SetRemoved();
   1774      } else {
   1775        // Oof. This hangs around because of addTrack. Make it magic!
   1776        transceiver.SetAddTrackMagic();
   1777      }
   1778    }  // else, _we_ added this and it is not attached to the remote offer yet
   1779 
   1780    mOldTransceivers.push_back(transceiver);
   1781  }
   1782 
   1783  mTransceivers = std::move(mOldTransceivers);
   1784 }
   1785 
   1786 nsresult JsepSessionImpl::ValidateLocalDescription(const Sdp& description,
   1787                                                   JsepSdpType type) {
   1788  Sdp* generated = nullptr;
   1789  // TODO(bug 1095226): Better checking.
   1790  if (type == kJsepSdpOffer) {
   1791    generated = mGeneratedOffer.get();
   1792  } else {
   1793    generated = mGeneratedAnswer.get();
   1794  }
   1795 
   1796  if (!generated) {
   1797    JSEP_SET_ERROR(
   1798        "Calling SetLocal without first calling CreateOffer/Answer"
   1799        " is not supported.");
   1800    return NS_ERROR_UNEXPECTED;
   1801  }
   1802 
   1803  if (description.GetMediaSectionCount() != generated->GetMediaSectionCount()) {
   1804    JSEP_SET_ERROR("Changing the number of m-sections is not allowed");
   1805    return NS_ERROR_INVALID_ARG;
   1806  }
   1807 
   1808  for (size_t i = 0; i < description.GetMediaSectionCount(); ++i) {
   1809    auto& origMsection = generated->GetMediaSection(i);
   1810    auto& finalMsection = description.GetMediaSection(i);
   1811    if (origMsection.GetMediaType() != finalMsection.GetMediaType()) {
   1812      JSEP_SET_ERROR("Changing the media-type of m-sections is not allowed");
   1813      return NS_ERROR_INVALID_ARG;
   1814    }
   1815 
   1816    // These will be present in reoffer
   1817    if (!mCurrentLocalDescription) {
   1818      if (finalMsection.GetAttributeList().HasAttribute(
   1819              SdpAttribute::kCandidateAttribute)) {
   1820        JSEP_SET_ERROR("Adding your own candidate attributes is not supported");
   1821        return NS_ERROR_INVALID_ARG;
   1822      }
   1823 
   1824      if (finalMsection.GetAttributeList().HasAttribute(
   1825              SdpAttribute::kEndOfCandidatesAttribute)) {
   1826        JSEP_SET_ERROR("Why are you trying to set a=end-of-candidates?");
   1827        return NS_ERROR_INVALID_ARG;
   1828      }
   1829    }
   1830 
   1831    if (mSdpHelper.MsectionIsDisabled(finalMsection)) {
   1832      continue;
   1833    }
   1834 
   1835    if (!finalMsection.GetAttributeList().HasAttribute(
   1836            SdpAttribute::kMidAttribute)) {
   1837      JSEP_SET_ERROR("Local descriptions must have a=mid attributes.");
   1838      return NS_ERROR_INVALID_ARG;
   1839    }
   1840 
   1841    if (finalMsection.GetAttributeList().GetMid() !=
   1842        origMsection.GetAttributeList().GetMid()) {
   1843      JSEP_SET_ERROR("Changing the mid of m-sections is not allowed.");
   1844      return NS_ERROR_INVALID_ARG;
   1845    }
   1846 
   1847    // TODO(bug 1095218): Check msid
   1848    // TODO(bug 1095226): Check ice-ufrag and ice-pwd
   1849    // TODO(bug 1095226): Check fingerprints
   1850    // TODO(bug 1095226): Check payload types (at least ensure that payload
   1851    // types we don't actually support weren't added)
   1852    // TODO(bug 1095226): Check ice-options?
   1853  }
   1854 
   1855  if (description.GetAttributeList().HasAttribute(
   1856          SdpAttribute::kIceLiteAttribute)) {
   1857    JSEP_SET_ERROR("Running ICE in lite mode is unsupported");
   1858    return NS_ERROR_INVALID_ARG;
   1859  }
   1860 
   1861  return NS_OK;
   1862 }
   1863 
   1864 nsresult JsepSessionImpl::ValidateRemoteDescription(const Sdp& description) {
   1865  if (!mCurrentLocalDescription) {
   1866    // Initial offer; nothing to validate besides the stuff in ParseSdp
   1867    return NS_OK;
   1868  }
   1869 
   1870  if (mCurrentLocalDescription->GetMediaSectionCount() >
   1871      description.GetMediaSectionCount()) {
   1872    JSEP_SET_ERROR(
   1873        "New remote description has fewer m-sections than the "
   1874        "previous remote description.");
   1875    return NS_ERROR_INVALID_ARG;
   1876  }
   1877 
   1878  for (size_t i = 0; i < description.GetMediaSectionCount(); ++i) {
   1879    const SdpAttributeList& attrs =
   1880        description.GetMediaSection(i).GetAttributeList();
   1881 
   1882    if (attrs.HasAttribute(SdpAttribute::kExtmapAttribute)) {
   1883      for (const auto& ext : attrs.GetExtmap().mExtmaps) {
   1884        if (mExtmapEntriesEverNegotiated.count(ext.entry) &&
   1885            mExtmapEntriesEverNegotiated[ext.entry] != ext.extensionname) {
   1886          JSEP_SET_ERROR(
   1887              "Remote description attempted to remap RTP extension id "
   1888              << ext.entry << " from "
   1889              << mExtmapEntriesEverNegotiated[ext.entry] << " to "
   1890              << ext.extensionname);
   1891          return NS_ERROR_INVALID_ARG;
   1892        }
   1893      }
   1894    }
   1895  }
   1896 
   1897  if (!mCurrentRemoteDescription) {
   1898    // No further checking for initial answers
   1899    return NS_OK;
   1900  }
   1901 
   1902  // These are solely to check that bundle is valid
   1903  SdpHelper::BundledMids bundledMids;
   1904  nsresult rv = GetNegotiatedBundledMids(&bundledMids);
   1905  NS_ENSURE_SUCCESS(rv, rv);
   1906 
   1907  SdpHelper::BundledMids newBundledMids;
   1908  rv = mSdpHelper.GetBundledMids(description, &newBundledMids);
   1909  NS_ENSURE_SUCCESS(rv, rv);
   1910 
   1911  // check for partial ice restart, which is not supported
   1912  Maybe<bool> iceCredsDiffer;
   1913  for (size_t i = 0; i < mCurrentRemoteDescription->GetMediaSectionCount();
   1914       ++i) {
   1915    const SdpMediaSection& newMsection = description.GetMediaSection(i);
   1916    const SdpMediaSection& oldMsection =
   1917        mCurrentRemoteDescription->GetMediaSection(i);
   1918 
   1919    if (mSdpHelper.MsectionIsDisabled(newMsection) ||
   1920        mSdpHelper.MsectionIsDisabled(oldMsection)) {
   1921      continue;
   1922    }
   1923 
   1924    if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
   1925      JSEP_SET_ERROR("Remote description changes the media type of m-line "
   1926                     << i);
   1927      return NS_ERROR_INVALID_ARG;
   1928    }
   1929 
   1930    bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
   1931 
   1932    if (mIsPendingOfferer.isSome() && *mIsPendingOfferer && differ &&
   1933        !IsIceRestarting()) {
   1934      JSEP_SET_ERROR(
   1935          "Remote description indicates ICE restart but offer did not "
   1936          "request ICE restart (new remote description changes either "
   1937          "the ice-ufrag or ice-pwd)");
   1938      return NS_ERROR_INVALID_ARG;
   1939    }
   1940 
   1941    // Detect whether all the creds are the same or all are different
   1942    if (!iceCredsDiffer.isSome()) {
   1943      // for the first msection capture whether creds are different or same
   1944      iceCredsDiffer = mozilla::Some(differ);
   1945    } else if (iceCredsDiffer.isSome() && *iceCredsDiffer != differ) {
   1946      // subsequent msections must match the first sections
   1947      JSEP_SET_ERROR(
   1948          "Partial ICE restart is unsupported at this time "
   1949          "(new remote description changes either the ice-ufrag "
   1950          "or ice-pwd on fewer than all msections)");
   1951      return NS_ERROR_INVALID_ARG;
   1952    }
   1953  }
   1954 
   1955  return NS_OK;
   1956 }
   1957 
   1958 nsresult JsepSessionImpl::ValidateOffer(const Sdp& offer) {
   1959  return mSdpHelper.ValidateTransportAttributes(offer, sdp::kOffer);
   1960 }
   1961 
   1962 nsresult JsepSessionImpl::ValidateAnswer(const Sdp& offer, const Sdp& answer) {
   1963  if (offer.GetMediaSectionCount() != answer.GetMediaSectionCount()) {
   1964    JSEP_SET_ERROR("Offer and answer have different number of m-lines "
   1965                   << "(" << offer.GetMediaSectionCount() << " vs "
   1966                   << answer.GetMediaSectionCount() << ")");
   1967    return NS_ERROR_INVALID_ARG;
   1968  }
   1969 
   1970  nsresult rv = mSdpHelper.ValidateTransportAttributes(answer, sdp::kAnswer);
   1971  NS_ENSURE_SUCCESS(rv, rv);
   1972 
   1973  for (size_t i = 0; i < offer.GetMediaSectionCount(); ++i) {
   1974    const SdpMediaSection& offerMsection = offer.GetMediaSection(i);
   1975    const SdpMediaSection& answerMsection = answer.GetMediaSection(i);
   1976 
   1977    if (offerMsection.GetMediaType() != answerMsection.GetMediaType()) {
   1978      JSEP_SET_ERROR("Answer and offer have different media types at m-line "
   1979                     << i);
   1980      return NS_ERROR_INVALID_ARG;
   1981    }
   1982 
   1983    if (mSdpHelper.MsectionIsDisabled(answerMsection)) {
   1984      continue;
   1985    }
   1986 
   1987    if (mSdpHelper.MsectionIsDisabled(offerMsection)) {
   1988      JSEP_SET_ERROR(
   1989          "Answer tried to enable an m-section that was disabled in the offer");
   1990      return NS_ERROR_INVALID_ARG;
   1991    }
   1992 
   1993    if (!offerMsection.IsSending() && answerMsection.IsReceiving()) {
   1994      JSEP_SET_ERROR("Answer tried to set recv when offer did not set send");
   1995      return NS_ERROR_INVALID_ARG;
   1996    }
   1997 
   1998    if (!offerMsection.IsReceiving() && answerMsection.IsSending()) {
   1999      JSEP_SET_ERROR("Answer tried to set send when offer did not set recv");
   2000      return NS_ERROR_INVALID_ARG;
   2001    }
   2002 
   2003    const SdpAttributeList& answerAttrs(answerMsection.GetAttributeList());
   2004    const SdpAttributeList& offerAttrs(offerMsection.GetAttributeList());
   2005    if (answerAttrs.HasAttribute(SdpAttribute::kMidAttribute) &&
   2006        offerAttrs.HasAttribute(SdpAttribute::kMidAttribute) &&
   2007        offerAttrs.GetMid() != answerAttrs.GetMid()) {
   2008      JSEP_SET_ERROR("Answer changes mid for level, was \'"
   2009                     << offerMsection.GetAttributeList().GetMid()
   2010                     << "\', now \'"
   2011                     << answerMsection.GetAttributeList().GetMid() << "\'");
   2012      return NS_ERROR_INVALID_ARG;
   2013    }
   2014 
   2015    // Sanity check extmap
   2016    if (answerAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)) {
   2017      if (!offerAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)) {
   2018        JSEP_SET_ERROR("Answer adds extmap attributes to level " << i);
   2019        return NS_ERROR_INVALID_ARG;
   2020      }
   2021 
   2022      for (const auto& ansExt : answerAttrs.GetExtmap().mExtmaps) {
   2023        bool found = false;
   2024        for (const auto& offExt : offerAttrs.GetExtmap().mExtmaps) {
   2025          if (ansExt.extensionname == offExt.extensionname) {
   2026            if ((ansExt.direction & reverse(offExt.direction)) !=
   2027                ansExt.direction) {
   2028              // FIXME we do not return an error here, because Chrome up to
   2029              // version 57 is actually tripping over this if they are the
   2030              // answerer. See bug 1355010 for details.
   2031              MOZ_MTLOG(ML_WARNING,
   2032                        "[" << mName
   2033                            << "]: Answer has inconsistent"
   2034                               " direction on extmap attribute at level "
   2035                            << i << " (" << ansExt.extensionname
   2036                            << "). Offer had " << offExt.direction
   2037                            << ", answer had " << ansExt.direction << ".");
   2038              // return NS_ERROR_INVALID_ARG;
   2039            }
   2040 
   2041            if (offExt.entry < 4096 && (offExt.entry != ansExt.entry)) {
   2042              JSEP_SET_ERROR("Answer changed id for extmap attribute at level "
   2043                             << i << " (" << offExt.extensionname << ") from "
   2044                             << offExt.entry << " to " << ansExt.entry << ".");
   2045              return NS_ERROR_INVALID_ARG;
   2046            }
   2047 
   2048            if (ansExt.entry >= 4096) {
   2049              JSEP_SET_ERROR("Answer used an invalid id ("
   2050                             << ansExt.entry
   2051                             << ") for extmap attribute at level " << i << " ("
   2052                             << ansExt.extensionname << ").");
   2053              return NS_ERROR_INVALID_ARG;
   2054            }
   2055 
   2056            found = true;
   2057            break;
   2058          }
   2059        }
   2060 
   2061        if (!found) {
   2062          JSEP_SET_ERROR("Answer has extmap "
   2063                         << ansExt.extensionname
   2064                         << " at "
   2065                            "level "
   2066                         << i << " that was not present in offer.");
   2067          return NS_ERROR_INVALID_ARG;
   2068        }
   2069      }
   2070    }
   2071  }
   2072 
   2073  return NS_OK;
   2074 }
   2075 
   2076 nsresult JsepSessionImpl::CreateGenericSDP(UniquePtr<Sdp>* sdpp) {
   2077  // draft-ietf-rtcweb-jsep-08 Section 5.2.1:
   2078  //  o  The second SDP line MUST be an "o=" line, as specified in
   2079  //     [RFC4566], Section 5.2.  The value of the <username> field SHOULD
   2080  //     be "-".  The value of the <sess-id> field SHOULD be a
   2081  //     cryptographically random number.  To ensure uniqueness, this
   2082  //     number SHOULD be at least 64 bits long.  The value of the <sess-
   2083  //     version> field SHOULD be zero.  The value of the <nettype>
   2084  //     <addrtype> <unicast-address> tuple SHOULD be set to a non-
   2085  //     meaningful address, such as IN IP4 0.0.0.0, to prevent leaking the
   2086  //     local address in this field.  As mentioned in [RFC4566], the
   2087  //     entire o= line needs to be unique, but selecting a random number
   2088  //     for <sess-id> is sufficient to accomplish this.
   2089  //
   2090  // Historical note: we used to report the actual version number here, after
   2091  // "SDPARTA-", but that becomes a problem starting with version 100, since
   2092  // some services parse 100 as "10" and give us legacy/broken behavior. So
   2093  // we're freezing the version number at 99.0 in this string.
   2094  auto origin = SdpOrigin("mozilla...THIS_IS_SDPARTA-99.0", mSessionId,
   2095                          mSessionVersion, sdp::kIPv4, "0.0.0.0");
   2096 
   2097  UniquePtr<Sdp> sdp = MakeUnique<SipccSdp>(origin);
   2098 
   2099  if (mDtlsFingerprints.empty()) {
   2100    JSEP_SET_ERROR("Missing DTLS fingerprint");
   2101    return NS_ERROR_FAILURE;
   2102  }
   2103 
   2104  UniquePtr<SdpFingerprintAttributeList> fpl =
   2105      MakeUnique<SdpFingerprintAttributeList>();
   2106  for (auto& dtlsFingerprint : mDtlsFingerprints) {
   2107    fpl->PushEntry(dtlsFingerprint.mAlgorithm, dtlsFingerprint.mValue);
   2108  }
   2109  sdp->GetAttributeList().SetAttribute(fpl.release());
   2110 
   2111  auto* iceOpts = new SdpOptionsAttribute(SdpAttribute::kIceOptionsAttribute);
   2112  iceOpts->PushEntry("trickle");
   2113  sdp->GetAttributeList().SetAttribute(iceOpts);
   2114 
   2115  // This assumes content doesn't add a bunch of msid attributes with a
   2116  // different semantic in mind.
   2117  std::vector<std::string> msids;
   2118  msids.push_back("*");
   2119  mSdpHelper.SetupMsidSemantic(msids, sdp.get());
   2120 
   2121  *sdpp = std::move(sdp);
   2122  return NS_OK;
   2123 }
   2124 
   2125 nsresult JsepSessionImpl::SetupIds() {
   2126  SECStatus rv = PK11_GenerateRandom(
   2127      reinterpret_cast<unsigned char*>(&mSessionId), sizeof(mSessionId));
   2128  // RFC 3264 says that session-ids MUST be representable as a _signed_
   2129  // 64 bit number, meaning the MSB cannot be set.
   2130  mSessionId = mSessionId >> 1;
   2131  if (rv != SECSuccess) {
   2132    JSEP_SET_ERROR("Failed to generate session id: " << rv);
   2133    return NS_ERROR_FAILURE;
   2134  }
   2135 
   2136  if (!mUuidGen->Generate(&mDefaultRemoteStreamId)) {
   2137    JSEP_SET_ERROR("Failed to generate default uuid for streams");
   2138    return NS_ERROR_FAILURE;
   2139  }
   2140 
   2141  if (!mUuidGen->Generate(&mCNAME)) {
   2142    JSEP_SET_ERROR("Failed to generate CNAME");
   2143    return NS_ERROR_FAILURE;
   2144  }
   2145 
   2146  return NS_OK;
   2147 }
   2148 
   2149 void JsepSessionImpl::SetDefaultCodecs(
   2150    const std::vector<UniquePtr<JsepCodecDescription>>& aPreferredCodecs) {
   2151  mSupportedCodecs.clear();
   2152 
   2153  for (const auto& codec : aPreferredCodecs) {
   2154    mSupportedCodecs.emplace_back(codec->Clone());
   2155  }
   2156 }
   2157 
   2158 void JsepSessionImpl::SetState(JsepSignalingState state) {
   2159  if (state == mState) return;
   2160 
   2161  MOZ_MTLOG(ML_NOTICE, "[" << mName << "]: " << GetStateStr(mState) << " -> "
   2162                           << GetStateStr(state));
   2163  mState = state;
   2164 }
   2165 
   2166 JsepSession::Result JsepSessionImpl::AddRemoteIceCandidate(
   2167    const std::string& candidate, const std::string& mid,
   2168    const Maybe<uint16_t>& level, const std::string& ufrag,
   2169    std::string* transportId) {
   2170  mLastError.clear();
   2171  if (!mCurrentRemoteDescription && !mPendingRemoteDescription) {
   2172    JSEP_SET_ERROR("Cannot add ICE candidate when there is no remote SDP");
   2173    return dom::PCError::InvalidStateError;
   2174  }
   2175 
   2176  if (mid.empty() && !level.isSome() && candidate.empty()) {
   2177    // Set end-of-candidates on SDP
   2178    if (mCurrentRemoteDescription) {
   2179      nsresult rv = mSdpHelper.SetIceGatheringComplete(
   2180          mCurrentRemoteDescription.get(), ufrag);
   2181      NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
   2182    }
   2183 
   2184    if (mPendingRemoteDescription) {
   2185      // If we had an error when adding the candidate to the current
   2186      // description, we stomp them here. This is deliberate.
   2187      nsresult rv = mSdpHelper.SetIceGatheringComplete(
   2188          mPendingRemoteDescription.get(), ufrag);
   2189      NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
   2190    }
   2191    return Result();
   2192  }
   2193 
   2194  Maybe<JsepTransceiver> transceiver;
   2195  if (!mid.empty()) {
   2196    transceiver = GetTransceiverForMid(mid);
   2197  } else if (level.isSome()) {
   2198    transceiver = GetTransceiverForLevel(level.value());
   2199  }
   2200 
   2201  if (!transceiver) {
   2202    JSEP_SET_ERROR("Cannot set ICE candidate for level="
   2203                   << level << " mid=" << mid << ": No such transceiver.");
   2204    return dom::PCError::OperationError;
   2205  }
   2206 
   2207  if (level.isSome() && transceiver->GetLevel() != level.value()) {
   2208    MOZ_MTLOG(ML_WARNING, "Mismatch between mid and level - \""
   2209                              << mid << "\" is not the mid for level "
   2210                              << level);
   2211  }
   2212 
   2213  *transportId = transceiver->mTransport.mTransportId;
   2214  nsresult rv = NS_ERROR_UNEXPECTED;
   2215  if (mCurrentRemoteDescription) {
   2216    rv =
   2217        mSdpHelper.AddCandidateToSdp(mCurrentRemoteDescription.get(), candidate,
   2218                                     transceiver->GetLevel(), ufrag);
   2219  }
   2220 
   2221  if (mPendingRemoteDescription) {
   2222    // If we had an error when adding the candidate to the current description,
   2223    // we stomp them here. This is deliberate.
   2224    rv =
   2225        mSdpHelper.AddCandidateToSdp(mPendingRemoteDescription.get(), candidate,
   2226                                     transceiver->GetLevel(), ufrag);
   2227  }
   2228 
   2229  NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
   2230  return Result();
   2231 }
   2232 
   2233 nsresult JsepSessionImpl::AddLocalIceCandidate(const std::string& candidate,
   2234                                               const std::string& transportId,
   2235                                               const std::string& ufrag,
   2236                                               uint16_t* level,
   2237                                               std::string* mid,
   2238                                               bool* skipped) {
   2239  mLastError.clear();
   2240  *skipped = true;
   2241  if (!mCurrentLocalDescription && !mPendingLocalDescription) {
   2242    JSEP_SET_ERROR("Cannot add ICE candidate when there is no local SDP");
   2243    return NS_ERROR_UNEXPECTED;
   2244  }
   2245 
   2246  Maybe<const JsepTransceiver> transceiver =
   2247      GetTransceiverWithTransport(transportId);
   2248  if (!transceiver || !transceiver->IsAssociated()) {
   2249    // mainly here to make some testing less complicated, but also just in case
   2250    return NS_OK;
   2251  }
   2252 
   2253  *level = transceiver->GetLevel();
   2254  *mid = transceiver->GetMid();
   2255 
   2256  nsresult rv = NS_ERROR_INVALID_ARG;
   2257  if (mCurrentLocalDescription) {
   2258    rv = mSdpHelper.AddCandidateToSdp(mCurrentLocalDescription.get(), candidate,
   2259                                      *level, ufrag);
   2260  }
   2261 
   2262  if (mPendingLocalDescription) {
   2263    // If we had an error when adding the candidate to the current description,
   2264    // we stomp them here. This is deliberate.
   2265    rv = mSdpHelper.AddCandidateToSdp(mPendingLocalDescription.get(), candidate,
   2266                                      *level, ufrag);
   2267  }
   2268 
   2269  *skipped = false;
   2270  return rv;
   2271 }
   2272 
   2273 nsresult JsepSessionImpl::UpdateDefaultCandidate(
   2274    const std::string& defaultCandidateAddr, uint16_t defaultCandidatePort,
   2275    const std::string& defaultRtcpCandidateAddr,
   2276    uint16_t defaultRtcpCandidatePort, const std::string& transportId) {
   2277  mLastError.clear();
   2278 
   2279  mozilla::Sdp* sdp =
   2280      GetParsedLocalDescription(kJsepDescriptionPendingOrCurrent);
   2281 
   2282  if (!sdp) {
   2283    JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState));
   2284    return NS_ERROR_UNEXPECTED;
   2285  }
   2286 
   2287  for (const auto& transceiver : mTransceivers) {
   2288    // We set the default address for bundled m-sections, but not candidate
   2289    // attributes. Ugh.
   2290    if (transceiver.mTransport.mTransportId == transportId) {
   2291      MOZ_ASSERT(transceiver.HasLevel(),
   2292                 "Transceiver has a transport, but no level! "
   2293                 "This should never happen.");
   2294      std::string defaultRtcpCandidateAddrCopy(defaultRtcpCandidateAddr);
   2295      if (mState == kJsepStateStable) {
   2296        if (transceiver.mTransport.mComponents == 1) {
   2297          // We know we're doing rtcp-mux by now. Don't create an rtcp attr.
   2298          defaultRtcpCandidateAddrCopy = "";
   2299          defaultRtcpCandidatePort = 0;
   2300        }
   2301      }
   2302 
   2303      size_t level = transceiver.GetLevel();
   2304      if (level >= sdp->GetMediaSectionCount()) {
   2305        MOZ_ASSERT(false, "Transceiver's level is too large!");
   2306        JSEP_SET_ERROR("Transceiver's level is too large!");
   2307        return NS_ERROR_FAILURE;
   2308      }
   2309 
   2310      auto& msection = sdp->GetMediaSection(level);
   2311 
   2312      // Do not add default candidate to a bundle-only m-section, sinice that
   2313      // might confuse endpoints that do not support bundle-only.
   2314      if (!msection.GetAttributeList().HasAttribute(
   2315              SdpAttribute::kBundleOnlyAttribute)) {
   2316        mSdpHelper.SetDefaultAddresses(
   2317            defaultCandidateAddr, defaultCandidatePort,
   2318            defaultRtcpCandidateAddrCopy, defaultRtcpCandidatePort, &msection);
   2319      }
   2320    }
   2321  }
   2322 
   2323  return NS_OK;
   2324 }
   2325 
   2326 nsresult JsepSessionImpl::GetNegotiatedBundledMids(
   2327    SdpHelper::BundledMids* bundledMids) {
   2328  const Sdp* answerSdp = GetAnswer();
   2329 
   2330  if (!answerSdp) {
   2331    return NS_OK;
   2332  }
   2333 
   2334  return mSdpHelper.GetBundledMids(*answerSdp, bundledMids);
   2335 }
   2336 
   2337 mozilla::Sdp* JsepSessionImpl::GetParsedLocalDescription(
   2338    JsepDescriptionPendingOrCurrent type) const {
   2339  if (type == kJsepDescriptionPending) {
   2340    return mPendingLocalDescription.get();
   2341  } else if (mPendingLocalDescription &&
   2342             type == kJsepDescriptionPendingOrCurrent) {
   2343    return mPendingLocalDescription.get();
   2344  }
   2345  return mCurrentLocalDescription.get();
   2346 }
   2347 
   2348 mozilla::Sdp* JsepSessionImpl::GetParsedRemoteDescription(
   2349    JsepDescriptionPendingOrCurrent type) const {
   2350  if (type == kJsepDescriptionPending) {
   2351    return mPendingRemoteDescription.get();
   2352  } else if (mPendingRemoteDescription &&
   2353             type == kJsepDescriptionPendingOrCurrent) {
   2354    return mPendingRemoteDescription.get();
   2355  }
   2356  return mCurrentRemoteDescription.get();
   2357 }
   2358 
   2359 const Sdp* JsepSessionImpl::GetAnswer() const {
   2360  return (mIsCurrentOfferer.isSome() && *mIsCurrentOfferer)
   2361             ? mCurrentRemoteDescription.get()
   2362             : mCurrentLocalDescription.get();
   2363 }
   2364 
   2365 void JsepSessionImpl::SetIceRestarting(bool restarting) {
   2366  if (restarting) {
   2367    // not restarting -> restarting
   2368    if (!IsIceRestarting()) {
   2369      // We don't set this more than once, so the old ufrag/pwd is preserved
   2370      // even if we CreateOffer({iceRestart:true}) multiple times in a row.
   2371      mOldIceUfrag = mIceUfrag;
   2372      mOldIcePwd = mIcePwd;
   2373    }
   2374    mIceUfrag = GetRandomHex(1);
   2375    mIcePwd = GetRandomHex(4);
   2376  } else if (IsIceRestarting()) {
   2377    // restarting -> not restarting, restore old ufrag/pwd
   2378    mIceUfrag = mOldIceUfrag;
   2379    mIcePwd = mOldIcePwd;
   2380    mOldIceUfrag.clear();
   2381    mOldIcePwd.clear();
   2382  }
   2383 }
   2384 
   2385 nsresult JsepSessionImpl::Close() {
   2386  mLastError.clear();
   2387  SetState(kJsepStateClosed);
   2388  return NS_OK;
   2389 }
   2390 
   2391 const std::string JsepSessionImpl::GetLastError() const { return mLastError; }
   2392 
   2393 const std::vector<std::pair<size_t, std::string>>&
   2394 JsepSessionImpl::GetLastSdpParsingErrors() const {
   2395  return mLastSdpParsingErrors;
   2396 }
   2397 
   2398 bool JsepSessionImpl::CheckNegotiationNeeded() const {
   2399  MOZ_ASSERT(mState == kJsepStateStable);
   2400 
   2401  for (const auto& transceiver : mTransceivers) {
   2402    if (transceiver.IsStopped()) {
   2403      // Nothing to do with this
   2404      continue;
   2405    }
   2406 
   2407    if (transceiver.IsStopping()) {
   2408      MOZ_MTLOG(ML_DEBUG, "[" << mName
   2409                              << "]: Negotiation needed because of "
   2410                                 "transceiver we need to stop");
   2411      return true;
   2412    }
   2413 
   2414    if (!transceiver.IsAssociated()) {
   2415      MOZ_MTLOG(ML_DEBUG, "[" << mName
   2416                              << "]: Negotiation needed because of "
   2417                                 "transceiver we need to associate.");
   2418      return true;
   2419    }
   2420 
   2421    MOZ_ASSERT(transceiver.IsAssociated() && !transceiver.IsStopping() &&
   2422               !transceiver.IsStopped());
   2423 
   2424    if (!mCurrentLocalDescription || !mCurrentRemoteDescription) {
   2425      MOZ_CRASH(
   2426          "Transceivers should not be associated if we're in stable "
   2427          "before the first negotiation.");
   2428      continue;
   2429    }
   2430 
   2431    if (!transceiver.HasLevel()) {
   2432      MOZ_CRASH("Associated transceivers should always have a level.");
   2433      continue;
   2434    }
   2435 
   2436    if (transceiver.GetMediaType() == SdpMediaSection::kApplication) {
   2437      continue;
   2438    }
   2439 
   2440    size_t level = transceiver.GetLevel();
   2441    if (NS_WARN_IF(mCurrentLocalDescription->GetMediaSectionCount() <= level) ||
   2442        NS_WARN_IF(mCurrentRemoteDescription->GetMediaSectionCount() <=
   2443                   level)) {
   2444      MOZ_ASSERT(false);
   2445      continue;
   2446    }
   2447 
   2448    const SdpMediaSection& local =
   2449        mCurrentLocalDescription->GetMediaSection(level);
   2450    const SdpMediaSection& remote =
   2451        mCurrentRemoteDescription->GetMediaSection(level);
   2452 
   2453    if (transceiver.mJsDirection & sdp::kSend) {
   2454      std::vector<std::string> sdpMsids;
   2455      if (local.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
   2456        for (const auto& msidAttr : local.GetAttributeList().GetMsid().mMsids) {
   2457          if (msidAttr.identifier != "-") {
   2458            sdpMsids.push_back(msidAttr.identifier);
   2459          }
   2460        }
   2461      }
   2462      std::sort(sdpMsids.begin(), sdpMsids.end());
   2463 
   2464      std::vector<std::string> jsepMsids;
   2465      for (const auto& jsepMsid : transceiver.mSendTrack.GetStreamIds()) {
   2466        jsepMsids.push_back(jsepMsid);
   2467      }
   2468      std::sort(jsepMsids.begin(), jsepMsids.end());
   2469 
   2470      if (!std::equal(sdpMsids.begin(), sdpMsids.end(), jsepMsids.begin(),
   2471                      jsepMsids.end())) {
   2472        MOZ_MTLOG(ML_DEBUG,
   2473                  "[" << mName
   2474                      << "]: Negotiation needed because transceiver "
   2475                         "is sending, and the local SDP has different "
   2476                         "msids than the send track");
   2477        MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: SDP msids = [");
   2478        for (const auto& msid : sdpMsids) {
   2479          MOZ_MTLOG(ML_DEBUG, msid << ", ");
   2480        }
   2481        MOZ_MTLOG(ML_DEBUG, "]");
   2482        MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: JSEP msids = [");
   2483        for (const auto& msid : jsepMsids) {
   2484          MOZ_MTLOG(ML_DEBUG, msid << ", ");
   2485        }
   2486        MOZ_MTLOG(ML_DEBUG, "]");
   2487        return true;
   2488      }
   2489    }
   2490 
   2491    if (mIsCurrentOfferer.isSome() && *mIsCurrentOfferer) {
   2492      if ((local.GetDirection() != transceiver.mJsDirection) &&
   2493          reverse(remote.GetDirection()) != transceiver.mJsDirection) {
   2494        MOZ_MTLOG(ML_DEBUG, "[" << mName
   2495                                << "]: Negotiation needed because "
   2496                                   "the direction on our offer, and the remote "
   2497                                   "answer, does not "
   2498                                   "match the direction on a transceiver.");
   2499        return true;
   2500      }
   2501    } else if (local.GetDirection() !=
   2502               (transceiver.mJsDirection & reverse(remote.GetDirection()))) {
   2503      MOZ_MTLOG(
   2504          ML_DEBUG,
   2505          "[" << mName
   2506              << "]: Negotiation needed because "
   2507                 "the direction on our answer doesn't match the direction on a "
   2508                 "transceiver, even though the remote offer would have allowed "
   2509                 "it.");
   2510      return true;
   2511    }
   2512  }
   2513 
   2514  return false;
   2515 }
   2516 
   2517 }  // namespace mozilla