Update SetHeaderExtensionsToNegotiate to match specification

following the updates from
  https://github.com/w3c/webrtc-extensions/pull/142

BUG=chromium:1051821

Change-Id: I2d561bad1ddffb412bdd7e66cf62a3cb5fc73791
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/296480
Reviewed-by: Markus Handell <handellm@webrtc.org>
Reviewed-by: Markus Handell <handellm@google.com>
Commit-Queue: Philipp Hancke <phancke@microsoft.com>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39500}
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index a0faf6e..8e5ea9f 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -745,47 +745,34 @@
 
 RTCError RtpTransceiver::SetHeaderExtensionsToNegotiate(
     rtc::ArrayView<const RtpHeaderExtensionCapability> header_extensions) {
-  for (const auto& entry : header_extensions) {
-    // Handle unsupported requests for mandatory extensions as per
-    // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface.
-    // Note:
-    // - We do not handle setOfferedRtpHeaderExtensions algorithm step 2.1,
-    //   this has to be checked on a higher level. We naturally error out
-    //   in the handling of Step 2.2 if an unset URI is encountered.
-
-    // Step 2.2.
-    // Handle unknown extensions.
-    auto it = std::find_if(
-        header_extensions_to_negotiate_.begin(),
-        header_extensions_to_negotiate_.end(),
-        [&entry](const auto& offered) { return entry.uri == offered.uri; });
-    if (it == header_extensions_to_negotiate_.end()) {
-      return RTCError(RTCErrorType::UNSUPPORTED_PARAMETER,
-                      "Attempted to modify an unoffered extension.");
+  // https://w3c.github.io/webrtc-extensions/#methods
+  if (header_extensions.size() != header_extensions_to_negotiate_.size()) {
+    return RTCError(RTCErrorType::INVALID_MODIFICATION,
+                    "Size of extensions to negotiate does not match.");
+  }
+  // For each index i of extensions, run the following steps: ...
+  for (size_t i = 0; i < header_extensions.size(); i++) {
+    const auto& extension = header_extensions[i];
+    if (extension.uri != header_extensions_to_negotiate_[i].uri) {
+      return RTCError(RTCErrorType::INVALID_MODIFICATION,
+                      "Reordering extensions is not allowed.");
     }
-
-    // Step 2.4-2.5.
-    if (IsMandatoryHeaderExtension(entry.uri) &&
-        entry.direction != RtpTransceiverDirection::kSendRecv) {
+    if (IsMandatoryHeaderExtension(extension.uri) &&
+        extension.direction != RtpTransceiverDirection::kSendRecv) {
       return RTCError(RTCErrorType::INVALID_MODIFICATION,
                       "Attempted to stop a mandatory extension.");
     }
+
+    // TODO(bugs.webrtc.org/7477): Currently there are no recvonly extensions so
+    // this can not be checked: "When there exists header extension capabilities
+    // that have directions other than kSendRecv, restrict extension.direction
+    // as to not exceed that capability."
   }
 
-  // Set all current extensions but the mandatory ones to stopped.
-  // This means that anything filtered from the input will not show up.
-  for (auto& entry : header_extensions_to_negotiate_) {
-    if (!IsMandatoryHeaderExtension(entry.uri)) {
-      entry.direction = RtpTransceiverDirection::kStopped;
-    }
-  }
   // Apply mutation after error checking.
-  for (const auto& entry : header_extensions) {
-    auto it = std::find_if(
-        header_extensions_to_negotiate_.begin(),
-        header_extensions_to_negotiate_.end(),
-        [&entry](const auto& offered) { return entry.uri == offered.uri; });
-    it->direction = entry.direction;
+  for (size_t i = 0; i < header_extensions.size(); i++) {
+    header_extensions_to_negotiate_[i].direction =
+        header_extensions[i].direction;
   }
 
   return RTCError::OK();
diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc
index efb651c..0ada470 100644
--- a/pc/rtp_transceiver_unittest.cc
+++ b/pc/rtp_transceiver_unittest.cc
@@ -292,17 +292,47 @@
             modified_extensions);
 }
 
-TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsUnsupportedExtension) {
+TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsDifferentSize) {
   EXPECT_CALL(*receiver_.get(), Stop());
   EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
   EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
   EXPECT_CALL(*sender_.get(), Stop());
 
-  std::vector<RtpHeaderExtensionCapability> modified_extensions(
-      {RtpHeaderExtensionCapability("uri3", 1,
-                                    RtpTransceiverDirection::kSendRecv)});
+  auto modified_extensions = extensions_;
+  modified_extensions.pop_back();
+
   EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions),
-              Property(&RTCError::type, RTCErrorType::UNSUPPORTED_PARAMETER));
+              Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION));
+  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
+}
+
+TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsChangedUri) {
+  EXPECT_CALL(*receiver_.get(), Stop());
+  EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
+  EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
+  EXPECT_CALL(*sender_.get(), Stop());
+
+  auto modified_extensions = extensions_;
+  ASSERT_TRUE(!modified_extensions.empty());
+  modified_extensions[0].uri = "http://webrtc.org";
+
+  EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions),
+              Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION));
+  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
+}
+
+TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsReorder) {
+  EXPECT_CALL(*receiver_.get(), Stop());
+  EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
+  EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
+  EXPECT_CALL(*sender_.get(), Stop());
+
+  auto modified_extensions = extensions_;
+  ASSERT_GE(modified_extensions.size(), 2u);
+  std::swap(modified_extensions[0], modified_extensions[1]);
+
+  EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions),
+              Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION));
   EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
 }