Bug 1264479: added implementation for [current|pending][Local|Remote]Description. r?bwc draft
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Fri, 30 Jun 2017 01:19:19 -0700
changeset 605496 13886b1b8470a5e2e18ecb0b0f756f5ab8fc6629
parent 605034 33d50b73653a4db0e22cfc3a8f31cbc05bc561e2
child 605497 0a64d24cf0e7ccfe255cff1f69a285dfabc2622d
child 606199 8d1ca91d4b808f6f1861ae0481f509818f9d1f4d
child 607695 d9af1e0681611c9c2d26b71f3e4597d8b22b7985
push id67422
push userdrno@ohlmeier.org
push dateFri, 07 Jul 2017 20:28:13 +0000
reviewersbwc
bugs1264479
milestone56.0a1
Bug 1264479: added implementation for [current|pending][Local|Remote]Description. r?bwc MozReview-Commit-ID: AAnuN3YRhFH
dom/media/PeerConnection.js
media/webrtc/signaling/gtest/jsep_session_unittest.cpp
media/webrtc/signaling/src/jsep/JsepSession.h
media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
media/webrtc/signaling/src/jsep/JsepSessionImpl.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -1178,25 +1178,61 @@ class RTCPeerConnection {
     this._checkClosed();
     let sdp = this._impl.localDescription;
     if (sdp.length == 0) {
       return null;
     }
     return new this._win.RTCSessionDescription({ type: this._localType, sdp });
   }
 
+  get currentLocalDescription() {
+    this._checkClosed();
+    let sdp = this._impl.currentLocalDescription;
+    if (sdp.length == 0) {
+      return null;
+    }
+    return new this._win.RTCSessionDescription({ type: this._localType, sdp });
+  }
+
+  get pendingLocalDescription() {
+    this._checkClosed();
+    let sdp = this._impl.pendingLocalDescription;
+    if (sdp.length == 0) {
+      return null;
+    }
+    return new this._win.RTCSessionDescription({ type: this._localType, sdp });
+  }
+
   get remoteDescription() {
     this._checkClosed();
     let sdp = this._impl.remoteDescription;
     if (sdp.length == 0) {
       return null;
     }
     return new this._win.RTCSessionDescription({ type: this._remoteType, sdp });
   }
 
+  get currentRemoteDescription() {
+    this._checkClosed();
+    let sdp = this._impl.currentRemoteDescription;
+    if (sdp.length == 0) {
+      return null;
+    }
+    return new this._win.RTCSessionDescription({ type: this._remoteType, sdp });
+  }
+
+  get pendingRemoteDescription() {
+    this._checkClosed();
+    let sdp = this._impl.pendingRemoteDescription;
+    if (sdp.length == 0) {
+      return null;
+    }
+    return new this._win.RTCSessionDescription({ type: this._remoteType, sdp });
+  }
+
   get peerIdentity() { return this._peerIdentity; }
   get idpLoginUrl() { return this._localIdp.idpLoginUrl; }
   get id() { return this._impl.id; }
   set id(s) { this._impl.id = s; }
   get iceGatheringState() { return this._iceGatheringState; }
   get iceConnectionState() { return this._iceConnectionState; }
 
   get signalingState() {
--- a/media/webrtc/signaling/gtest/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/gtest/jsep_session_unittest.cpp
@@ -446,17 +446,17 @@ protected:
       if ((*i)->GetMediaType() == type) {
         ++result;
       }
     }
     return result;
   }
 
   UniquePtr<Sdp> GetParsedLocalDescription(const JsepSessionImpl& side) const {
-    return Parse(side.GetLocalDescription());
+    return Parse(side.GetLocalDescription(kJsepDescriptionCurrent));
   }
 
   SdpMediaSection* GetMsection(Sdp& sdp,
                                SdpMediaSection::MediaType type,
                                size_t index) const {
     for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
       auto& msection = sdp.GetMediaSection(i);
       if (msection.GetMediaType() != type) {
@@ -2487,17 +2487,18 @@ TEST_P(JsepSessionTest, ParseRejectsBadM
 
 TEST_P(JsepSessionTest, FullCallWithCandidates)
 {
   AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   mOffCandidates->Gather(*mSessionOff, types);
 
-  UniquePtr<Sdp> localOffer(Parse(mSessionOff->GetLocalDescription()));
+  UniquePtr<Sdp> localOffer(Parse(
+        mSessionOff->GetLocalDescription(kJsepDescriptionCurrent)));
   for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) {
     mOffCandidates->CheckRtpCandidates(
         true, localOffer->GetMediaSection(i), i,
         "Local offer after gathering should have RTP candidates.");
     mOffCandidates->CheckDefaultRtpCandidate(
         true, localOffer->GetMediaSection(i), i,
         "Local offer after gathering should have a default RTP candidate.");
     mOffCandidates->CheckRtcpCandidates(
@@ -2512,17 +2513,18 @@ TEST_P(JsepSessionTest, FullCallWithCand
         "(unless m=application)");
     CheckEndOfCandidates(true, localOffer->GetMediaSection(i),
         "Local offer after gathering should have an end-of-candidates.");
   }
 
   SetRemoteOffer(offer);
   mOffCandidates->Trickle(*mSessionAns);
 
-  UniquePtr<Sdp> remoteOffer(Parse(mSessionAns->GetRemoteDescription()));
+  UniquePtr<Sdp> remoteOffer(Parse(
+        mSessionAns->GetRemoteDescription(kJsepDescriptionCurrent)));
   for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) {
     mOffCandidates->CheckRtpCandidates(
         true, remoteOffer->GetMediaSection(i), i,
         "Remote offer after trickle should have RTP candidates.");
     mOffCandidates->CheckDefaultRtpCandidate(
         false, remoteOffer->GetMediaSection(i), i,
         "Initial remote offer should not have a default RTP candidate.");
     mOffCandidates->CheckRtcpCandidates(
@@ -2539,17 +2541,18 @@ TEST_P(JsepSessionTest, FullCallWithCand
 
   AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   // This will gather candidates that mSessionAns knows it doesn't need.
   // They should not be present in the SDP.
   mAnsCandidates->Gather(*mSessionAns, types);
 
-  UniquePtr<Sdp> localAnswer(Parse(mSessionAns->GetLocalDescription()));
+  UniquePtr<Sdp> localAnswer(Parse(
+        mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)));
   for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
     mAnsCandidates->CheckRtpCandidates(
         i == 0, localAnswer->GetMediaSection(i), i,
         "Local answer after gathering should have RTP candidates on level 0.");
     mAnsCandidates->CheckDefaultRtpCandidate(
         true, localAnswer->GetMediaSection(i), 0,
         "Local answer after gathering should have a default RTP candidate "
         "on all levels that matches transport level 0.");
@@ -2564,17 +2567,18 @@ TEST_P(JsepSessionTest, FullCallWithCand
     CheckEndOfCandidates(i == 0, localAnswer->GetMediaSection(i),
         "Local answer after gathering should have an end-of-candidates only for"
         " level 0.");
   }
 
   SetRemoteAnswer(answer);
   mAnsCandidates->Trickle(*mSessionOff);
 
-  UniquePtr<Sdp> remoteAnswer(Parse(mSessionOff->GetRemoteDescription()));
+  UniquePtr<Sdp> remoteAnswer(Parse(
+        mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent)));
   for (size_t i = 0; i < remoteAnswer->GetMediaSectionCount(); ++i) {
     mAnsCandidates->CheckRtpCandidates(
         i == 0, remoteAnswer->GetMediaSection(i), i,
         "Remote answer after trickle should have RTP candidates on level 0.");
     mAnsCandidates->CheckDefaultRtpCandidate(
         false, remoteAnswer->GetMediaSection(i), i,
         "Remote answer after trickle should not have a default RTP candidate.");
     mAnsCandidates->CheckRtcpCandidates(
@@ -2673,17 +2677,18 @@ TEST_P(JsepSessionTest, RenegotiationWit
     if (types[level] != SdpMediaSection::kApplication) {
       mOffCandidates->Gather(*mSessionOff, level, RTCP);
     }
   }
   mOffCandidates->FinishGathering(*mSessionOff);
 
   mOffCandidates->Trickle(*mSessionAns);
 
-  UniquePtr<Sdp> localOffer(Parse(mSessionOff->GetLocalDescription()));
+  UniquePtr<Sdp> localOffer(Parse(
+        mSessionOff->GetLocalDescription(kJsepDescriptionCurrent)));
   for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) {
     mOffCandidates->CheckRtpCandidates(
         true, localOffer->GetMediaSection(i), i,
         "Local reoffer after gathering should have RTP candidates.");
     mOffCandidates->CheckDefaultRtpCandidate(
         true, localOffer->GetMediaSection(i), i,
         "Local reoffer after gathering should have a default RTP candidate.");
     mOffCandidates->CheckRtcpCandidates(
@@ -2695,17 +2700,18 @@ TEST_P(JsepSessionTest, RenegotiationWit
         types[i] != SdpMediaSection::kApplication,
         localOffer->GetMediaSection(i), i,
         "Local reoffer after gathering should have a default RTCP candidate "
         "(unless m=application)");
     CheckEndOfCandidates(true, localOffer->GetMediaSection(i),
         "Local reoffer after gathering should have an end-of-candidates.");
   }
 
-  UniquePtr<Sdp> remoteOffer(Parse(mSessionAns->GetRemoteDescription()));
+  UniquePtr<Sdp> remoteOffer(Parse(
+        mSessionAns->GetRemoteDescription(kJsepDescriptionCurrent)));
   for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) {
     mOffCandidates->CheckRtpCandidates(
         true, remoteOffer->GetMediaSection(i), i,
         "Remote reoffer after trickle should have RTP candidates.");
     mOffCandidates->CheckDefaultRtpCandidate(
         i == 0, remoteOffer->GetMediaSection(i), i,
         "Remote reoffer should have a default RTP candidate on level 0 "
         "(because it was gathered last offer/answer).");
@@ -2722,17 +2728,18 @@ TEST_P(JsepSessionTest, RenegotiationWit
 
   answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
   // No candidates should be gathered at the answerer, but default candidates
   // should be set.
   mAnsCandidates->FinishGathering(*mSessionAns);
 
-  UniquePtr<Sdp> localAnswer(Parse(mSessionAns->GetLocalDescription()));
+  UniquePtr<Sdp> localAnswer(Parse(
+        mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)));
   for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
     mAnsCandidates->CheckRtpCandidates(
         i == 0, localAnswer->GetMediaSection(i), i,
         "Local reanswer after gathering should have RTP candidates on level "
         "0.");
     mAnsCandidates->CheckDefaultRtpCandidate(
         true, localAnswer->GetMediaSection(i), 0,
         "Local reanswer after gathering should have a default RTP candidate "
@@ -2745,17 +2752,18 @@ TEST_P(JsepSessionTest, RenegotiationWit
         false, localAnswer->GetMediaSection(i), i,
         "Local reanswer after gathering should not have a default RTCP "
         "candidate (because we're reanswering with rtcp-mux)");
     CheckEndOfCandidates(i == 0, localAnswer->GetMediaSection(i),
         "Local reanswer after gathering should have an end-of-candidates only "
         "for level 0.");
   }
 
-  UniquePtr<Sdp> remoteAnswer(Parse(mSessionOff->GetRemoteDescription()));
+  UniquePtr<Sdp> remoteAnswer(Parse(
+        mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent)));
   for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
     mAnsCandidates->CheckRtpCandidates(
         i == 0, remoteAnswer->GetMediaSection(i), i,
         "Remote reanswer after trickle should have RTP candidates on level 0.");
     mAnsCandidates->CheckDefaultRtpCandidate(
         i == 0, remoteAnswer->GetMediaSection(i), i,
         "Remote reanswer should have a default RTP candidate on level 0 "
         "(because it was gathered last offer/answer).");
@@ -3001,49 +3009,53 @@ TEST_F(JsepSessionTest, OfferAnswerSendO
 
 TEST_F(JsepSessionTest, OfferToReceiveAudioNotUsed)
 {
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some<size_t>(1);
 
   OfferAnswer(CHECK_SUCCESS, Some(options));
 
-  UniquePtr<Sdp> offer(Parse(mSessionOff->GetLocalDescription()));
+  UniquePtr<Sdp> offer(Parse(
+        mSessionOff->GetLocalDescription(kJsepDescriptionCurrent)));
   ASSERT_TRUE(offer.get());
   ASSERT_EQ(1U, offer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             offer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             offer->GetMediaSection(0).GetAttributeList().GetDirection());
 
-  UniquePtr<Sdp> answer(Parse(mSessionAns->GetLocalDescription()));
+  UniquePtr<Sdp> answer(Parse(
+        mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)));
   ASSERT_TRUE(answer.get());
   ASSERT_EQ(1U, answer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             answer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kInactive,
             answer->GetMediaSection(0).GetAttributeList().GetDirection());
 }
 
 TEST_F(JsepSessionTest, OfferToReceiveVideoNotUsed)
 {
   JsepOfferOptions options;
   options.mOfferToReceiveVideo = Some<size_t>(1);
 
   OfferAnswer(CHECK_SUCCESS, Some(options));
 
-  UniquePtr<Sdp> offer(Parse(mSessionOff->GetLocalDescription()));
+  UniquePtr<Sdp> offer(Parse(
+        mSessionOff->GetLocalDescription(kJsepDescriptionCurrent)));
   ASSERT_TRUE(offer.get());
   ASSERT_EQ(1U, offer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kVideo,
             offer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             offer->GetMediaSection(0).GetAttributeList().GetDirection());
 
-  UniquePtr<Sdp> answer(Parse(mSessionAns->GetLocalDescription()));
+  UniquePtr<Sdp> answer(Parse(
+        mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)));
   ASSERT_TRUE(answer.get());
   ASSERT_EQ(1U, answer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kVideo,
             answer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kInactive,
             answer->GetMediaSection(0).GetAttributeList().GetDirection());
 }
 
@@ -4403,17 +4415,17 @@ TEST_P(JsepSessionTest, TestBalancedBund
 TEST_P(JsepSessionTest, TestMaxBundle)
 {
   AddTracks(*mSessionOff);
   AddTracks(*mSessionAns);
 
   mSessionOff->SetBundlePolicy(kBundleMaxBundle);
   OfferAnswer();
 
-  std::string offer = mSessionOff->GetLocalDescription();
+  std::string offer = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent);
   SipccSdpParser parser;
   UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
   ASSERT_TRUE(parsedOffer.get());
 
   ASSERT_FALSE(
       parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
         SdpAttribute::kBundleOnlyAttribute));
   ASSERT_NE(0U, parsedOffer->GetMediaSection(0).GetPort());
@@ -4833,19 +4845,21 @@ TEST_F(JsepSessionTest, AudioOnlyG722Onl
   std::size_t pos = offer.find(audio);
   ASSERT_NE(pos, std::string::npos);
   offer.replace(pos, audio.length(),
                 "m=audio 65375 UDP/TLS/RTP/SAVPF 9\r\n");
   SetRemoteOffer(offer);
 
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
-  ASSERT_NE(mSessionAns->GetLocalDescription().find("UDP/TLS/RTP/SAVPF 9\r"),
+  ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+            .find("UDP/TLS/RTP/SAVPF 9\r"),
             std::string::npos);
-  ASSERT_NE(mSessionAns->GetLocalDescription().find("a=rtpmap:9 G722/8000"),
+  ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+            .find("a=rtpmap:9 G722/8000"),
             std::string::npos);
 }
 
 TEST_F(JsepSessionTest, AudioOnlyG722Rejected)
 {
   types.push_back(SdpMediaSection::kAudio);
   AddTracks(*mSessionOff, "audio");
   AddTracks(*mSessionAns, "audio");
@@ -4859,22 +4873,27 @@ TEST_F(JsepSessionTest, AudioOnlyG722Rej
                 "m=audio 65375 UDP/TLS/RTP/SAVPF 0 8\r\n");
   SetRemoteOffer(offer);
 
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
   // TODO(bug 814227): Use commented out code instead.
-  ASSERT_NE(mSessionAns->GetLocalDescription().find("UDP/TLS/RTP/SAVPF 0\r"),
+  ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+            .find("UDP/TLS/RTP/SAVPF 0\r"),
             std::string::npos);
-  // ASSERT_NE(mSessionAns->GetLocalDescription().find("UDP/TLS/RTP/SAVPF 0 8\r"), std::string::npos);
-  ASSERT_NE(mSessionAns->GetLocalDescription().find("a=rtpmap:0 PCMU/8000"), std::string::npos);
-  ASSERT_EQ(mSessionAns->GetLocalDescription().find("a=rtpmap:109 opus/48000/2"), std::string::npos);
-  ASSERT_EQ(mSessionAns->GetLocalDescription().find("a=rtpmap:9 G722/8000"), std::string::npos);
+  // ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+  //           .find("UDP/TLS/RTP/SAVPF 0 8\r"), std::string::npos);
+  ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+            .find("a=rtpmap:0 PCMU/8000"), std::string::npos);
+  ASSERT_EQ(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+            .find("a=rtpmap:109 opus/48000/2"), std::string::npos);
+  ASSERT_EQ(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+            .find("a=rtpmap:9 G722/8000"), std::string::npos);
 }
 
 // This test doesn't make sense for bundle
 TEST_F(JsepSessionTest, DISABLED_FullCallAudioNoMuxVideoMux)
 {
   types.push_back(SdpMediaSection::kAudio);
   AddTracks(*mSessionOff, "audio,video");
   AddTracks(*mSessionAns, "audio,video");
@@ -4882,19 +4901,21 @@ TEST_F(JsepSessionTest, DISABLED_FullCal
   SetLocalOffer(offer);
   std::string rtcp_mux = "a=rtcp-mux\r\n";
   std::size_t pos = offer.find(rtcp_mux);
   ASSERT_NE(pos, std::string::npos);
   offer.replace(pos, rtcp_mux.length(), "");
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
 
-  size_t match = mSessionAns->GetLocalDescription().find("\r\na=rtcp-mux");
+  size_t match = mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+                                                  .find("\r\na=rtcp-mux");
   ASSERT_NE(match, std::string::npos);
-  match = mSessionAns->GetLocalDescription().find("\r\na=rtcp-mux", match + 1);
+  match = mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
+                                           .find("\r\na=rtcp-mux", match + 1);
   ASSERT_EQ(match, std::string::npos);
 }
 
 // Disabled pending resolution of bug 818640.
 // Actually, this test is completely broken; you can't just call
 // SetRemote/CreateAnswer over and over again.
 TEST_F(JsepSessionTest, DISABLED_OfferAllDynamicTypes)
 {
--- a/media/webrtc/signaling/src/jsep/JsepSession.h
+++ b/media/webrtc/signaling/src/jsep/JsepSession.h
@@ -34,16 +34,22 @@ enum JsepSignalingState {
 
 enum JsepSdpType {
   kJsepSdpOffer,
   kJsepSdpAnswer,
   kJsepSdpPranswer,
   kJsepSdpRollback
 };
 
+enum JsepDescriptionPendingOrCurrent {
+  kJsepDescriptionCurrent,
+  kJsepDescriptionPending,
+  kJsepDescriptionPendingOrCurrent
+};
+
 struct JsepOAOptions {};
 struct JsepOfferOptions : public JsepOAOptions {
   Maybe<size_t> mOfferToReceiveAudio;
   Maybe<size_t> mOfferToReceiveVideo;
   Maybe<bool> mDontOfferDataChannel;
   Maybe<bool> mIceRestart; // currently ignored by JsepSession
 };
 struct JsepAnswerOptions : public JsepOAOptions {};
@@ -164,18 +170,20 @@ public:
   // Access transports.
   virtual std::vector<RefPtr<JsepTransport>> GetTransports() const = 0;
 
   // Basic JSEP operations.
   virtual nsresult CreateOffer(const JsepOfferOptions& options,
                                std::string* offer) = 0;
   virtual nsresult CreateAnswer(const JsepAnswerOptions& options,
                                 std::string* answer) = 0;
-  virtual std::string GetLocalDescription() const = 0;
-  virtual std::string GetRemoteDescription() const = 0;
+  virtual std::string GetLocalDescription(JsepDescriptionPendingOrCurrent type)
+                                          const = 0;
+  virtual std::string GetRemoteDescription(JsepDescriptionPendingOrCurrent type)
+                                           const = 0;
   virtual nsresult SetLocalDescription(JsepSdpType type,
                                        const std::string& sdp) = 0;
   virtual nsresult SetRemoteDescription(JsepSdpType type,
                                         const std::string& sdp) = 0;
   virtual nsresult AddRemoteIceCandidate(const std::string& candidate,
                                          const std::string& mid,
                                          uint16_t level) = 0;
   virtual nsresult AddLocalIceCandidate(const std::string& candidate,
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -767,31 +767,31 @@ JsepSessionImpl::CreateOffer(const JsepO
   *offer = sdp->ToString();
   mGeneratedLocalDescription = Move(sdp);
   ++mSessionVersion;
 
   return NS_OK;
 }
 
 std::string
-JsepSessionImpl::GetLocalDescription() const
+JsepSessionImpl::GetLocalDescription(JsepDescriptionPendingOrCurrent type) const
 {
   std::ostringstream os;
-  mozilla::Sdp* sdp = GetParsedLocalDescription();
+  mozilla::Sdp* sdp = GetParsedLocalDescription(type);
   if (sdp) {
     sdp->Serialize(os);
   }
   return os.str();
 }
 
 std::string
-JsepSessionImpl::GetRemoteDescription() const
+JsepSessionImpl::GetRemoteDescription(JsepDescriptionPendingOrCurrent type) const
 {
   std::ostringstream os;
-  mozilla::Sdp* sdp =  GetParsedRemoteDescription();
+  mozilla::Sdp* sdp =  GetParsedRemoteDescription(type);
   if (sdp) {
     sdp->Serialize(os);
   }
   return os.str();
 }
 
 void
 JsepSessionImpl::AddExtmap(SdpMediaSection* msection) const
@@ -2405,17 +2405,17 @@ JsepSessionImpl::SetState(JsepSignalingS
 
 nsresult
 JsepSessionImpl::AddRemoteIceCandidate(const std::string& candidate,
                                        const std::string& mid,
                                        uint16_t level)
 {
   mLastError.clear();
 
-  mozilla::Sdp* sdp = GetParsedRemoteDescription();
+  mozilla::Sdp* sdp = GetParsedRemoteDescription(kJsepDescriptionPendingOrCurrent);
 
   if (!sdp) {
     JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState));
     return NS_ERROR_UNEXPECTED;
   }
 
   return mSdpHelper.AddCandidateToSdp(sdp, candidate, mid, level);
 }
@@ -2423,17 +2423,17 @@ JsepSessionImpl::AddRemoteIceCandidate(c
 nsresult
 JsepSessionImpl::AddLocalIceCandidate(const std::string& candidate,
                                       uint16_t level,
                                       std::string* mid,
                                       bool* skipped)
 {
   mLastError.clear();
 
-  mozilla::Sdp* sdp = GetParsedLocalDescription();
+  mozilla::Sdp* sdp = GetParsedLocalDescription(kJsepDescriptionPendingOrCurrent);
 
   if (!sdp) {
     JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState));
     return NS_ERROR_UNEXPECTED;
   }
 
   if (sdp->GetMediaSectionCount() <= level) {
     // mainly here to make some testing less complicated, but also just in case
@@ -2466,17 +2466,17 @@ JsepSessionImpl::UpdateDefaultCandidate(
     const std::string& defaultCandidateAddr,
     uint16_t defaultCandidatePort,
     const std::string& defaultRtcpCandidateAddr,
     uint16_t defaultRtcpCandidatePort,
     uint16_t level)
 {
   mLastError.clear();
 
-  mozilla::Sdp* sdp = GetParsedLocalDescription();
+  mozilla::Sdp* sdp = GetParsedLocalDescription(kJsepDescriptionPendingOrCurrent);
 
   if (!sdp) {
     JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState));
     return NS_ERROR_UNEXPECTED;
   }
 
   if (level >= sdp->GetMediaSectionCount()) {
     return NS_OK;
@@ -2513,17 +2513,17 @@ JsepSessionImpl::UpdateDefaultCandidate(
   return NS_OK;
 }
 
 nsresult
 JsepSessionImpl::EndOfLocalCandidates(uint16_t level)
 {
   mLastError.clear();
 
-  mozilla::Sdp* sdp = GetParsedLocalDescription();
+  mozilla::Sdp* sdp = GetParsedLocalDescription(kJsepDescriptionPendingOrCurrent);
 
   if (!sdp) {
     JSEP_SET_ERROR("Cannot mark end of local ICE candidates in state "
                    << GetStateStr(mState));
     return NS_ERROR_UNEXPECTED;
   }
 
   if (level >= sdp->GetMediaSectionCount()) {
@@ -2589,28 +2589,34 @@ JsepSessionImpl::EnableOfferMsection(Sdp
   std::ostringstream osMid;
   osMid << "sdparta_" << msection->GetLevel();
   AddMid(osMid.str(), msection);
 
   return NS_OK;
 }
 
 mozilla::Sdp*
-JsepSessionImpl::GetParsedLocalDescription() const
+JsepSessionImpl::GetParsedLocalDescription(JsepDescriptionPendingOrCurrent type) const
 {
-  if (mPendingLocalDescription) {
+  if (type == kJsepDescriptionPending) {
+    return mPendingLocalDescription.get();
+  } else if (mPendingLocalDescription &&
+             type == kJsepDescriptionPendingOrCurrent) {
     return mPendingLocalDescription.get();
   }
   return mCurrentLocalDescription.get();
 }
 
 mozilla::Sdp*
-JsepSessionImpl::GetParsedRemoteDescription() const
+JsepSessionImpl::GetParsedRemoteDescription(JsepDescriptionPendingOrCurrent type) const
 {
-  if (mPendingRemoteDescription) {
+  if (type == kJsepDescriptionPending) {
+    return mPendingRemoteDescription.get();
+  } else if (mPendingRemoteDescription &&
+             type == kJsepDescriptionPendingOrCurrent) {
     return mPendingRemoteDescription.get();
   }
   return mCurrentRemoteDescription.get();
 }
 
 const Sdp*
 JsepSessionImpl::GetAnswer() const
 {
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
@@ -124,19 +124,21 @@ public:
     GetRemoteTracksRemoved() const override;
 
   virtual nsresult CreateOffer(const JsepOfferOptions& options,
                                std::string* offer) override;
 
   virtual nsresult CreateAnswer(const JsepAnswerOptions& options,
                                 std::string* answer) override;
 
-  virtual std::string GetLocalDescription() const override;
+  virtual std::string GetLocalDescription(JsepDescriptionPendingOrCurrent type)
+                                          const override;
 
-  virtual std::string GetRemoteDescription() const override;
+  virtual std::string GetRemoteDescription(JsepDescriptionPendingOrCurrent type)
+                                           const override;
 
   virtual nsresult SetLocalDescription(JsepSdpType type,
                                        const std::string& sdp) override;
 
   virtual nsresult SetRemoteDescription(JsepSdpType type,
                                         const std::string& sdp) override;
 
   virtual nsresult AddRemoteIceCandidate(const std::string& candidate,
@@ -294,18 +296,20 @@ private:
   nsresult FinalizeTransport(const SdpAttributeList& remote,
                              const SdpAttributeList& answer,
                              const RefPtr<JsepTransport>& transport);
 
   nsresult GetNegotiatedBundledMids(SdpHelper::BundledMids* bundledMids);
 
   nsresult EnableOfferMsection(SdpMediaSection* msection);
 
-  mozilla::Sdp* GetParsedLocalDescription() const;
-  mozilla::Sdp* GetParsedRemoteDescription() const;
+  mozilla::Sdp* GetParsedLocalDescription(JsepDescriptionPendingOrCurrent type)
+                                          const;
+  mozilla::Sdp* GetParsedRemoteDescription(JsepDescriptionPendingOrCurrent type)
+                                           const;
   const Sdp* GetAnswer() const;
 
   std::vector<JsepSendingTrack> mLocalTracks;
   std::vector<JsepReceivingTrack> mRemoteTracks;
   // By the most recent SetRemoteDescription
   std::vector<JsepReceivingTrack> mRemoteTracksAdded;
   std::vector<JsepReceivingTrack> mRemoteTracksRemoved;
   std::vector<RefPtr<JsepTransport> > mTransports;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1504,17 +1504,18 @@ PeerConnectionImpl::CreateOffer(const Js
         WrapRunnableNM(DeferredCreateOffer, mHandle, aOptions));
     STAMP_TIMECARD(mTimeCard, "Deferring CreateOffer (not ready)");
     return NS_OK;
   }
 
   CSFLogDebug(logTag, "CreateOffer()");
 
   nsresult nrv;
-  if (restartIce && !mJsepSession->GetLocalDescription().empty()) {
+  if (restartIce &&
+      !mJsepSession->GetLocalDescription(kJsepDescriptionCurrent).empty()) {
     // If restart is requested and a restart is already in progress, we
     // need to make room for the restart request so we either rollback
     // or finalize to "clear" the previous restart.
     if (mMedia->GetIceRestartState() ==
             PeerConnectionMedia::ICE_RESTART_PROVISIONAL) {
       // we're mid-restart and can rollback
       RollbackIceRestart();
     } else if (mMedia->GetIceRestartState() ==
@@ -2787,42 +2788,80 @@ PeerConnectionImpl::GetFingerprint(char*
   std::copy(fpStr.begin(), fpStr.end(), tmp);
   tmp[fpStr.size()] = '\0';
 
   *fingerprint = tmp;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetLocalDescription(char** aSDP)
+PeerConnectionImpl::GetLocalDescription(nsAString& aSDP)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
-  MOZ_ASSERT(aSDP);
-  std::string localSdp = mJsepSession->GetLocalDescription();
-
-  char* tmp = new char[localSdp.size() + 1];
-  std::copy(localSdp.begin(), localSdp.end(), tmp);
-  tmp[localSdp.size()] = '\0';
-
-  *aSDP = tmp;
+
+  std::string localSdp = mJsepSession->GetLocalDescription(
+      kJsepDescriptionPendingOrCurrent);
+  aSDP = NS_ConvertASCIItoUTF16(localSdp.c_str());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PeerConnectionImpl::GetCurrentLocalDescription(nsAString& aSDP)
+{
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+
+  std::string localSdp = mJsepSession->GetLocalDescription(kJsepDescriptionCurrent);
+  aSDP = NS_ConvertASCIItoUTF16(localSdp.c_str());
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetRemoteDescription(char** aSDP)
+PeerConnectionImpl::GetPendingLocalDescription(nsAString& aSDP)
+{
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+
+  std::string localSdp = mJsepSession->GetLocalDescription(kJsepDescriptionPending);
+  aSDP = NS_ConvertASCIItoUTF16(localSdp.c_str());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PeerConnectionImpl::GetRemoteDescription(nsAString& aSDP)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
-  MOZ_ASSERT(aSDP);
-  std::string remoteSdp = mJsepSession->GetRemoteDescription();
-
-  char* tmp = new char[remoteSdp.size() + 1];
-  std::copy(remoteSdp.begin(), remoteSdp.end(), tmp);
-  tmp[remoteSdp.size()] = '\0';
-
-  *aSDP = tmp;
+
+  std::string remoteSdp = mJsepSession->GetRemoteDescription(
+      kJsepDescriptionPendingOrCurrent);
+  aSDP = NS_ConvertASCIItoUTF16(remoteSdp.c_str());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PeerConnectionImpl::GetCurrentRemoteDescription(nsAString& aSDP)
+{
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+
+  std::string remoteSdp = mJsepSession->GetRemoteDescription(kJsepDescriptionCurrent);
+  aSDP = NS_ConvertASCIItoUTF16(remoteSdp.c_str());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PeerConnectionImpl::GetPendingRemoteDescription(nsAString& aSDP)
+{
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+
+  std::string remoteSdp = mJsepSession->GetRemoteDescription(kJsepDescriptionPending);
+  aSDP = NS_ConvertASCIItoUTF16(remoteSdp.c_str());
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::SignalingState(PCImplSignalingState* aState)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aState);
@@ -3502,18 +3541,23 @@ PeerConnectionImpl::BuildStatsQuery_m(
       query->now);
 
   query->iceStartTime = mIceStartTime;
   query->failed = isFailed(mIceConnectionState);
 
   // Populate SDP on main
   if (query->internalStats) {
     if (mJsepSession) {
-      std::string localDescription = mJsepSession->GetLocalDescription();
-      std::string remoteDescription = mJsepSession->GetRemoteDescription();
+      // TODO we probably should report Current and Pending SDPs here
+      // separately. Plus the raw SDP we got from JS (mLocalRequestedSDP).
+      // And if it's the offer or answer would also be nice.
+      std::string localDescription = mJsepSession->GetLocalDescription(
+          kJsepDescriptionPendingOrCurrent);
+      std::string remoteDescription = mJsepSession->GetRemoteDescription(
+          kJsepDescriptionPendingOrCurrent);
       query->report->mLocalSdp.Construct(
           NS_ConvertASCIItoUTF16(localDescription.c_str()));
       query->report->mRemoteSdp.Construct(
           NS_ConvertASCIItoUTF16(remoteDescription.c_str()));
     }
   }
 
   // Gather up pipelines from mMedia so they may be inspected on STS
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -504,35 +504,23 @@ public:
   void GetFingerprint(nsAString& fingerprint)
   {
     char *tmp;
     GetFingerprint(&tmp);
     fingerprint.AssignASCII(tmp);
     delete[] tmp;
   }
 
-  NS_IMETHODIMP GetLocalDescription(char** aSDP);
-
-  void GetLocalDescription(nsAString& aSDP)
-  {
-    char *tmp;
-    GetLocalDescription(&tmp);
-    aSDP.AssignASCII(tmp);
-    delete[] tmp;
-  }
+  NS_IMETHODIMP GetLocalDescription(nsAString& aSDP);
+  NS_IMETHODIMP GetCurrentLocalDescription(nsAString& aSDP);
+  NS_IMETHODIMP GetPendingLocalDescription(nsAString& aSDP);
 
-  NS_IMETHODIMP GetRemoteDescription(char** aSDP);
-
-  void GetRemoteDescription(nsAString& aSDP)
-  {
-    char *tmp;
-    GetRemoteDescription(&tmp);
-    aSDP.AssignASCII(tmp);
-    delete[] tmp;
-  }
+  NS_IMETHODIMP GetRemoteDescription(nsAString& aSDP);
+  NS_IMETHODIMP GetCurrentRemoteDescription(nsAString& aSDP);
+  NS_IMETHODIMP GetPendingRemoteDescription(nsAString& aSDP);
 
   NS_IMETHODIMP SignalingState(mozilla::dom::PCImplSignalingState* aState);
 
   mozilla::dom::PCImplSignalingState SignalingState()
   {
     mozilla::dom::PCImplSignalingState state;
     SignalingState(&state);
     return state;