Add SctpTransport API

This exposes the "sctp" attribute on an RTCPeerConnection, with the
RTCSctpTransport type, and plumbs the surfacing of connection creation
and state changes up from the webrtc layer to the Blink layer.

Specification:
http://w3c.github.io/webrtc-pc/#rtcsctptransport-interface

Bug: chromium:818643
Change-Id: I77ad2eddcddfabd018e62ef8c5c73cfbe77ab74d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1491436
Commit-Queue: Harald Alvestrand <hta@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#641986}
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.h b/content/renderer/media/webrtc/mock_peer_connection_impl.h
index 389ca3c..f95fe28 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.h
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.h
@@ -15,6 +15,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/webrtc/api/dtls_transport_interface.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/sctp_transport_interface.h"
 #include "third_party/webrtc/api/stats/rtc_stats_report.h"
 
 namespace content {
@@ -166,6 +167,8 @@
       const override;
   std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> GetReceivers()
       const override;
+  MOCK_CONST_METHOD0(GetSctpTransport,
+                     rtc::scoped_refptr<webrtc::SctpTransportInterface>());
   rtc::scoped_refptr<webrtc::DataChannelInterface>
       CreateDataChannel(const std::string& label,
                         const webrtc::DataChannelInit* config) override;
diff --git a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h b/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
index 1736aa4..00026e3 100644
--- a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
+++ b/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
@@ -44,6 +44,8 @@
       std::unique_ptr<blink::WebRTCRtpReceiver> web_rtp_receiver) override {
     DidRemoveReceiverPlanBForMock(&web_rtp_receiver);
   }
+  MOCK_METHOD1(DidModifySctpTransport,
+               void(blink::WebRTCSctpTransportSnapshot snapshot));
   void DidModifyTransceivers(
       std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>>
           web_transceivers,
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index a2e2306..d67ebe9 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -786,6 +786,7 @@
       WebRtcSetDescriptionObserver::States states) {
     DCHECK_EQ(sdp_semantics_, webrtc::SdpSemantics::kUnifiedPlan);
     if (handler_) {
+      handler_->OnModifySctpTransport(std::move(states.sctp_transport_state));
       handler_->OnModifyTransceivers(
           std::move(states.transceiver_states),
           action_ == PeerConnectionTracker::ACTION_SET_REMOTE_DESCRIPTION);
@@ -1137,8 +1138,8 @@
               ? native_peer_connection_->GetTransceivers()
               : std::vector<
                     rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>();
-  transceiver_state_surfacer->Initialize(track_adapter_map_,
-                                         std::move(transceivers));
+  transceiver_state_surfacer->Initialize(
+      native_peer_connection_, track_adapter_map_, std::move(transceivers));
 }
 
 void RTCPeerConnectionHandler::CreateAnswer(
@@ -1604,7 +1605,8 @@
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> transceivers;
   if (error_or_transceiver->ok())
     transceivers.push_back(error_or_transceiver->value());
-  transceiver_state_surfacer->Initialize(track_adapter_map_, transceivers);
+  transceiver_state_surfacer->Initialize(native_peer_connection_,
+                                         track_adapter_map_, transceivers);
 }
 
 webrtc::RTCErrorOr<std::unique_ptr<blink::WebRTCRtpTransceiver>>
@@ -1657,7 +1659,8 @@
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> transceivers;
   if (error_or_transceiver->ok())
     transceivers.push_back(error_or_transceiver->value());
-  transceiver_state_surfacer->Initialize(track_adapter_map_, transceivers);
+  transceiver_state_surfacer->Initialize(native_peer_connection_,
+                                         track_adapter_map_, transceivers);
 }
 
 webrtc::RTCErrorOr<std::unique_ptr<blink::WebRTCRtpTransceiver>>
@@ -1765,8 +1768,8 @@
       transceivers = {transceiver_for_sender};
     }
   }
-  transceiver_state_surfacer->Initialize(track_adapter_map_,
-                                         std::move(transceivers));
+  transceiver_state_surfacer->Initialize(
+      native_peer_connection_, track_adapter_map_, std::move(transceivers));
 }
 
 webrtc::RTCErrorOr<std::unique_ptr<blink::WebRTCRtpTransceiver>>
@@ -1885,8 +1888,8 @@
     DCHECK(transceiver_for_sender);
     transceivers = {transceiver_for_sender};
   }
-  transceiver_state_surfacer->Initialize(track_adapter_map_,
-                                         std::move(transceivers));
+  transceiver_state_surfacer->Initialize(
+      native_peer_connection_, track_adapter_map_, std::move(transceivers));
 }
 
 void RTCPeerConnectionHandler::CloseClientPeerConnection() {
@@ -2168,6 +2171,11 @@
     client_->DidRemoveReceiverPlanB(std::move(receiver));
 }
 
+void RTCPeerConnectionHandler::OnModifySctpTransport(
+    blink::WebRTCSctpTransportSnapshot state) {
+  client_->DidModifySctpTransport(state);
+}
+
 void RTCPeerConnectionHandler::OnModifyTransceivers(
     std::vector<RtpTransceiverState> transceiver_states,
     bool is_remote_description) {
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.h b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
index 1816e1b..7a0261f 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.h
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
@@ -26,6 +26,7 @@
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
+#include "third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_rtc_stats_request.h"
 #include "third_party/blink/public/platform/web_rtc_stats_response.h"
@@ -223,6 +224,7 @@
   void OnRenegotiationNeeded();
   void OnAddReceiverPlanB(RtpReceiverState receiver_state);
   void OnRemoveReceiverPlanB(uintptr_t receiver_id);
+  void OnModifySctpTransport(blink::WebRTCSctpTransportSnapshot state);
   void OnModifyTransceivers(std::vector<RtpTransceiverState> transceiver_states,
                             bool is_remote_description);
   void OnDataChannel(scoped_refptr<webrtc::DataChannelInterface> channel);
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer.cc b/content/renderer/media/webrtc/transceiver_state_surfacer.cc
index 68b57de..cef5e59 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer.cc
+++ b/content/renderer/media/webrtc/transceiver_state_surfacer.cc
@@ -6,6 +6,7 @@
 
 #include "content/renderer/media/webrtc/webrtc_util.h"
 #include "third_party/webrtc/api/rtp_transceiver_interface.h"
+#include "third_party/webrtc/api/sctp_transport_interface.h"
 
 namespace content {
 
@@ -26,6 +27,7 @@
       signaling_task_runner_(other.signaling_task_runner_),
       is_initialized_(other.is_initialized_),
       states_obtained_(other.states_obtained_),
+      sctp_transport_snapshot_(other.sctp_transport_snapshot_),
       transceiver_states_(std::move(other.transceiver_states_)) {
   // Explicitly null |other|'s task runners for use in destructor.
   other.main_task_runner_ = nullptr;
@@ -43,6 +45,7 @@
   main_task_runner_ = other.main_task_runner_;
   signaling_task_runner_ = other.signaling_task_runner_;
   states_obtained_ = other.states_obtained_;
+  sctp_transport_snapshot_ = other.sctp_transport_snapshot_;
   transceiver_states_ = std::move(other.transceiver_states_);
   // Explicitly null |other|'s task runners for use in destructor.
   other.main_task_runner_ = nullptr;
@@ -51,11 +54,25 @@
 }
 
 void TransceiverStateSurfacer::Initialize(
+    scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
     scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
     std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
         webrtc_transceivers) {
   DCHECK(signaling_task_runner_->BelongsToCurrentThread());
   DCHECK(!is_initialized_);
+  DCHECK(native_peer_connection);
+  sctp_transport_snapshot_.transport =
+      native_peer_connection->GetSctpTransport();
+  if (sctp_transport_snapshot_.transport) {
+    sctp_transport_snapshot_.sctp_transport_state =
+        sctp_transport_snapshot_.transport->Information();
+    if (sctp_transport_snapshot_.sctp_transport_state.dtls_transport()) {
+      sctp_transport_snapshot_.dtls_transport_state =
+          sctp_transport_snapshot_.sctp_transport_state.dtls_transport()
+              ->Information();
+    }
+  }
+
   for (auto& webrtc_transceiver : webrtc_transceivers) {
     // Create the sender state.
     base::Optional<RtpSenderState> sender_state;
@@ -107,6 +124,13 @@
   is_initialized_ = true;
 }
 
+blink::WebRTCSctpTransportSnapshot
+TransceiverStateSurfacer::SctpTransportSnapshot() {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  DCHECK(is_initialized_);
+  return sctp_transport_snapshot_;
+}
+
 std::vector<RtpTransceiverState> TransceiverStateSurfacer::ObtainStates() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   DCHECK(is_initialized_);
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer.h b/content/renderer/media/webrtc/transceiver_state_surfacer.h
index d49de68..133a206 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer.h
+++ b/content/renderer/media/webrtc/transceiver_state_surfacer.h
@@ -7,7 +7,9 @@
 
 #include "content/renderer/media/webrtc/rtc_rtp_transceiver.h"
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
+#include "third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h"
 #include "third_party/webrtc/api/rtp_transceiver_interface.h"
+#include "third_party/webrtc/api/sctp_transport_interface.h"
 #include "third_party/webrtc/rtc_base/ref_count.h"
 #include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
@@ -39,11 +41,13 @@
 
   // Must be invoked on the signaling thread.
   void Initialize(
+      scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
       scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
       std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
           transceivers);
 
   // Must be invoked on the main thread.
+  blink::WebRTCSctpTransportSnapshot SctpTransportSnapshot();
   std::vector<RtpTransceiverState> ObtainStates();
 
  protected:
@@ -51,6 +55,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
   bool is_initialized_;
   bool states_obtained_;
+  blink::WebRTCSctpTransportSnapshot sctp_transport_snapshot_;
   std::vector<RtpTransceiverState> transceiver_states_;
 };
 
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc b/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
index 190acad..1b97d98 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
+++ b/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
@@ -25,8 +25,20 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_heap.h"
 
+using testing::AnyNumber;
+using testing::Return;
+
 namespace content {
 
+class MockSctpTransport : public webrtc::SctpTransportInterface {
+ public:
+  MOCK_CONST_METHOD0(dtls_transport,
+                     rtc::scoped_refptr<webrtc::DtlsTransportInterface>());
+  MOCK_CONST_METHOD0(Information, webrtc::SctpTransportInformation());
+  MOCK_METHOD1(RegisterObserver, void(webrtc::SctpTransportObserverInterface*));
+  MOCK_METHOD0(UnregisterObserver, void());
+};
+
 class TransceiverStateSurfacerTest : public ::testing::Test {
  public:
   void SetUp() override {
@@ -36,6 +48,11 @@
         dependency_factory_.get(), main_task_runner_);
     surfacer_.reset(new TransceiverStateSurfacer(main_task_runner_,
                                                  signaling_task_runner()));
+    peer_connection_ = dependency_factory_->CreatePeerConnection(
+        webrtc::PeerConnectionInterface::RTCConfiguration(), nullptr, nullptr);
+    EXPECT_CALL(*(static_cast<MockPeerConnectionImpl*>(peer_connection_.get())),
+                GetSctpTransport())
+        .Times(AnyNumber());
   }
 
   void TearDown() override {
@@ -136,6 +153,14 @@
 
   void ObtainStatesAndExpectInitialized(
       rtc::scoped_refptr<webrtc::RtpTransceiverInterface> webrtc_transceiver) {
+    // Inspect SCTP transport
+    auto sctp_snapshot = surfacer_->SctpTransportSnapshot();
+    EXPECT_EQ(peer_connection_->GetSctpTransport(), sctp_snapshot.transport);
+    if (peer_connection_->GetSctpTransport()) {
+      EXPECT_EQ(peer_connection_->GetSctpTransport()->dtls_transport(),
+                sctp_snapshot.sctp_transport_state.dtls_transport());
+    }
+    // Inspect transceivers
     auto transceiver_states = surfacer_->ObtainStates();
     EXPECT_EQ(1u, transceiver_states.size());
     auto& transceiver_state = transceiver_states[0];
@@ -216,7 +241,8 @@
           transceivers,
       base::WaitableEvent* waitable_event) {
     DCHECK(signaling_task_runner()->BelongsToCurrentThread());
-    surfacer_->Initialize(track_adapter_map_, std::move(transceivers));
+    surfacer_->Initialize(peer_connection_, track_adapter_map_,
+                          std::move(transceivers));
     waitable_event->Signal();
   }
 
@@ -226,7 +252,8 @@
       base::OnceCallback<void()> callback,
       base::RunLoop* run_loop) {
     DCHECK(signaling_task_runner()->BelongsToCurrentThread());
-    surfacer_->Initialize(track_adapter_map_, std::move(transceivers));
+    surfacer_->Initialize(peer_connection_, track_adapter_map_,
+                          std::move(transceivers));
     main_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&TransceiverStateSurfacerTest::
@@ -246,6 +273,7 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
  protected:
+  scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
   std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map_;
@@ -332,4 +360,26 @@
   EXPECT_FALSE(transceiver_state.current_direction());
 }
 
+TEST_F(TransceiverStateSurfacerTest, SurfaceTransceiverWithSctpTransport) {
+  auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
+  auto webrtc_transceiver = CreateWebRtcTransceiver(
+      local_track_adapter->webrtc_track(), "local_stream", "remote_track",
+      "remote_stream", nullptr);
+  rtc::scoped_refptr<MockSctpTransport> mock_sctp_transport =
+      new rtc::RefCountedObject<MockSctpTransport>();
+  webrtc::SctpTransportInformation sctp_transport_info(
+      webrtc::SctpTransportState::kNew);
+  EXPECT_CALL(*(static_cast<MockPeerConnectionImpl*>(peer_connection_.get())),
+              GetSctpTransport())
+      .WillRepeatedly(Return(mock_sctp_transport));
+  EXPECT_CALL(*mock_sctp_transport.get(), Information())
+      .WillRepeatedly(Return(sctp_transport_info));
+  EXPECT_CALL(*mock_sctp_transport.get(), dtls_transport()).Times(AnyNumber());
+  auto waitable_event =
+      AsyncInitializeSurfacerWithWaitableEvent({webrtc_transceiver});
+  waitable_event->Wait();
+  EXPECT_TRUE(surfacer_->SctpTransportSnapshot().transport);
+  ObtainStatesAndExpectInitialized(webrtc_transceiver);
+}
+
 }  // namespace content
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer.cc b/content/renderer/media/webrtc/webrtc_set_description_observer.cc
index dcd7ff1..bb3a737 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer.cc
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer.cc
@@ -15,6 +15,7 @@
 
 WebRtcSetDescriptionObserver::States::States(States&& other)
     : signaling_state(other.signaling_state),
+      sctp_transport_state(std::move(other.sctp_transport_state)),
       transceiver_states(std::move(other.transceiver_states)) {}
 
 WebRtcSetDescriptionObserver::States::~States() = default;
@@ -22,6 +23,7 @@
 WebRtcSetDescriptionObserver::States& WebRtcSetDescriptionObserver::States::
 operator=(States&& other) {
   signaling_state = other.signaling_state;
+  sctp_transport_state = std::move(other.sctp_transport_state);
   transceiver_states = std::move(other.transceiver_states);
   return *this;
 }
@@ -70,7 +72,7 @@
   }
   TransceiverStateSurfacer transceiver_state_surfacer(main_task_runner_,
                                                       signaling_task_runner_);
-  transceiver_state_surfacer.Initialize(track_adapter_map_,
+  transceiver_state_surfacer.Initialize(pc_, track_adapter_map_,
                                         std::move(transceivers));
   main_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&WebRtcSetDescriptionObserverHandlerImpl::
@@ -87,6 +89,8 @@
   CHECK(main_task_runner_->BelongsToCurrentThread());
   WebRtcSetDescriptionObserver::States states;
   states.signaling_state = signaling_state;
+  states.sctp_transport_state =
+      transceiver_state_surfacer.SctpTransportSnapshot();
   states.transceiver_states = transceiver_state_surfacer.ObtainStates();
   observer_->OnSetDescriptionComplete(std::move(error), std::move(states));
 }
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer.h b/content/renderer/media/webrtc/webrtc_set_description_observer.h
index 1dd218b8..7d8acef 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer.h
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer.h
@@ -49,6 +49,7 @@
     States& operator=(States&& other);
 
     webrtc::PeerConnectionInterface::SignalingState signaling_state;
+    blink::WebRTCSctpTransportSnapshot sctp_transport_state;
     std::vector<RtpTransceiverState> transceiver_states;
 
     DISALLOW_COPY_AND_ASSIGN(States);
diff --git a/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h b/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
index 34e2264..65bbb1e 100644
--- a/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
+++ b/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
@@ -37,6 +37,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/sctp_transport_interface.h"
 
 namespace blink {
 
@@ -44,6 +45,14 @@
 class WebRTCRtpReceiver;
 class WebRTCRtpTransceiver;
 
+struct BLINK_PLATFORM_EXPORT WebRTCSctpTransportSnapshot {
+  rtc::scoped_refptr<webrtc::SctpTransportInterface> transport;
+  webrtc::SctpTransportInformation sctp_transport_state =
+      webrtc::SctpTransportInformation(webrtc::SctpTransportState::kNew);
+  webrtc::DtlsTransportInformation dtls_transport_state =
+      webrtc::DtlsTransportInformation(webrtc::DtlsTransportState::kNew);
+};
+
 class BLINK_PLATFORM_EXPORT WebRTCPeerConnectionHandlerClient {
  public:
   virtual ~WebRTCPeerConnectionHandlerClient();
@@ -63,6 +72,7 @@
   virtual void DidModifyTransceivers(
       std::vector<std::unique_ptr<WebRTCRtpTransceiver>>,
       bool is_remote_description) = 0;
+  virtual void DidModifySctpTransport(WebRTCSctpTransportSnapshot) = 0;
   virtual void DidAddRemoteDataChannel(
       scoped_refptr<webrtc::DataChannelInterface>) = 0;
   virtual void DidNoteInterestingUsage(int usage_pattern) = 0;
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 9cd46b7..33f19430 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -325,6 +325,7 @@
     "peerconnection/rtc_quic_stream_test.cc",
     "peerconnection/rtc_quic_transport_test.cc",
     "peerconnection/rtc_quic_transport_test.h",
+    "peerconnection/rtc_sctp_transport_test.cc",
     "picture_in_picture/picture_in_picture_controller_test.cc",
     "presentation/mock_presentation_service.h",
     "presentation/presentation_availability_state_test.cc",
diff --git a/third_party/blink/renderer/modules/event_target_modules_names.json5 b/third_party/blink/renderer/modules/event_target_modules_names.json5
index b2f6898..d897487 100644
--- a/third_party/blink/renderer/modules/event_target_modules_names.json5
+++ b/third_party/blink/renderer/modules/event_target_modules_names.json5
@@ -34,6 +34,7 @@
     "RTCDataChannel",
     "RTCDtlsTransport",
     "RTCPeerConnection",
+    "RTCSctpTransport",
     "NetworkInformation",
     "Notification",
     "PaymentRequest",
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index a7ea572f..982d855 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -247,6 +247,7 @@
           "peerconnection/rtc_rtp_receiver.idl",
           "peerconnection/rtc_rtp_sender.idl",
           "peerconnection/rtc_rtp_transceiver.idl",
+          "peerconnection/rtc_sctp_transport.idl",
           "peerconnection/rtc_session_description.idl",
           "peerconnection/rtc_stats_report.idl",
           "peerconnection/rtc_stats_response.idl",
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index b4150fc..066dfcb 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -41,6 +41,8 @@
     "adapters/quic_transport_host.h",
     "adapters/quic_transport_proxy.cc",
     "adapters/quic_transport_proxy.h",
+    "adapters/sctp_transport_proxy.cc",
+    "adapters/sctp_transport_proxy.h",
     "adapters/web_rtc_cross_thread_copier.cc",
     "adapters/web_rtc_cross_thread_copier.h",
     "byte_buffer_queue.cc",
@@ -89,6 +91,8 @@
     "rtc_rtp_sender.h",
     "rtc_rtp_transceiver.cc",
     "rtc_rtp_transceiver.h",
+    "rtc_sctp_transport.cc",
+    "rtc_sctp_transport.h",
     "rtc_session_description.cc",
     "rtc_session_description.h",
     "rtc_session_description_enums.h",
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.cc b/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.cc
new file mode 100644
index 0000000..a9bfffe
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.cc
@@ -0,0 +1,72 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/location.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+
+namespace blink {
+
+// static
+std::unique_ptr<SctpTransportProxy> SctpTransportProxy::Create(
+    LocalFrame& frame,
+    scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
+    scoped_refptr<base::SingleThreadTaskRunner> host_thread,
+    rtc::scoped_refptr<webrtc::SctpTransportInterface> sctp_transport,
+    Delegate* delegate) {
+  DCHECK(proxy_thread->BelongsToCurrentThread());
+  std::unique_ptr<SctpTransportProxy> proxy =
+      base::WrapUnique(new SctpTransportProxy(frame, proxy_thread, host_thread,
+                                              sctp_transport, delegate));
+  PostCrossThreadTask(*host_thread, FROM_HERE,
+                      CrossThreadBind(&SctpTransportProxy::StartOnHostThread,
+                                      CrossThreadUnretained(proxy.get())));
+  return proxy;
+}
+
+SctpTransportProxy::SctpTransportProxy(
+    LocalFrame& frame,
+    scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
+    scoped_refptr<base::SingleThreadTaskRunner> host_thread,
+    rtc::scoped_refptr<webrtc::SctpTransportInterface> sctp_transport,
+    Delegate* delegate)
+    : proxy_thread_(std::move(proxy_thread)),
+      host_thread_(std::move(host_thread)),
+      sctp_transport_(std::move(sctp_transport)),
+      delegate_(delegate) {}
+
+void SctpTransportProxy::StartOnHostThread() {
+  DCHECK(host_thread_->BelongsToCurrentThread());
+  sctp_transport_->RegisterObserver(this);
+  PostCrossThreadTask(*proxy_thread_, FROM_HERE,
+                      CrossThreadBind(&Delegate::OnStartCompleted, delegate_,
+                                      sctp_transport_->Information()));
+}
+
+void SctpTransportProxy::OnStateChange(webrtc::SctpTransportInformation info) {
+  DCHECK(host_thread_->BelongsToCurrentThread());
+  DCHECK(delegate_);
+  // Closed is the last state that can happen, so unregister when we see this.
+  // Unregistering allows us to safely delete the proxy independent of the
+  // state of the webrtc::SctpTransport.
+  if (info.state() == webrtc::SctpTransportState::kClosed) {
+    sctp_transport_->UnregisterObserver();
+  }
+  PostCrossThreadTask(
+      *proxy_thread_, FROM_HERE,
+      CrossThreadBind(&Delegate::OnStateChange, delegate_, info));
+  if (info.state() == webrtc::SctpTransportState::kClosed) {
+    // Don't hold on to |delegate| any more.
+    delegate_ = nullptr;
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h
new file mode 100644
index 0000000..30d657b
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_SCTP_TRANSPORT_PROXY_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_SCTP_TRANSPORT_PROXY_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/webrtc/api/sctp_transport_interface.h"
+
+// The SctpTransportProxy class takes care of thread-jumping when
+// connecting callbacks from a webrtc::SctpTransport to a
+// blink::RTCSctpTransport object.
+
+// Its design is modeled on the IceTransportProxy design,
+// but does not use so many layers of indirection - there is
+// no control, and all information is passed via callbacks on the Delegate.
+
+// The proxy thread = the Blink main thread
+// The host thread = the webrtc signalling thread (the one that gets callbacks)
+
+namespace blink {
+
+class LocalFrame;
+
+class SctpTransportProxy : public webrtc::SctpTransportObserverInterface {
+ public:
+  // Delegate class for actions caused by the Proxy, but executed on the
+  // main thread. The Delegate must remain alive until it has observed
+  // the kClosed state, which is the last thing that can happen.
+  class Delegate : public blink::GarbageCollectedMixin {
+   public:
+    virtual ~Delegate() = default;
+
+    // Called when the Create() function is complete. Sends current state,
+    // but does not indicate a state change.
+    virtual void OnStartCompleted(webrtc::SctpTransportInformation info) = 0;
+    // Called when a state change is signalled from transport.
+    virtual void OnStateChange(webrtc::SctpTransportInformation info) = 0;
+    void Trace(blink::Visitor* visitor) override {}
+  };
+
+  // Constructs a SctpTransportProxy.  The caller is responsible for keeping
+  // |sctp_transport| alive until after the SctpTransportProxy is deleted.
+  // The SctpTransportProxy takes a <CrossThreadPersistent> reference to the
+  // Delegate, preventing it from being garbage collected until the |kClosed|
+  // state is reached.
+  // The SctpTransportProxy can only be safely deleted after seeing the state
+  // |kClosed|, since this is the last event that can happen on the transport.
+  static std::unique_ptr<SctpTransportProxy> Create(
+      LocalFrame& frame,
+      scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
+      scoped_refptr<base::SingleThreadTaskRunner> host_thread,
+      rtc::scoped_refptr<webrtc::SctpTransportInterface> sctp_transport,
+      Delegate* delegate);
+
+  ~SctpTransportProxy() override {}
+
+ private:
+  SctpTransportProxy(
+      LocalFrame& frame,
+      scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
+      scoped_refptr<base::SingleThreadTaskRunner> host_thread,
+      rtc::scoped_refptr<webrtc::SctpTransportInterface> sctp_transport,
+      Delegate* delegate);
+  // Implementation of webrtc::SctpTransportObserver
+  void OnStateChange(webrtc::SctpTransportInformation info) override;
+
+  // Internal helper for Create()
+  void StartOnHostThread();
+
+  const scoped_refptr<base::SingleThreadTaskRunner> proxy_thread_;
+  const scoped_refptr<base::SingleThreadTaskRunner> host_thread_;
+  const rtc::scoped_refptr<webrtc::SctpTransportInterface> sctp_transport_;
+  CrossThreadPersistent<Delegate> delegate_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_SCTP_TRANSPORT_PROXY_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h b/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h
index caf51a8..8cf5f92 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h
@@ -29,6 +29,7 @@
 
 namespace webrtc {
 class DtlsTransportInformation;
+class SctpTransportInformation;
 }
 
 namespace blink {
@@ -106,6 +107,12 @@
 };
 
 template <>
+struct CrossThreadCopier<webrtc::SctpTransportInformation>
+    : public CrossThreadCopierPassThrough<webrtc::SctpTransportInformation> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
+template <>
 struct CrossThreadCopier<P2PQuicTransport::StartConfig>
     : public CrossThreadCopierPassThrough<P2PQuicTransport::StartConfig> {
   STATIC_ONLY(CrossThreadCopier);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
index 4a329d75..1b7716d 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
@@ -11,23 +11,11 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/dtls_transport_proxy.h"
-#include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_cross_thread_factory.h"
-#include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_error_util.h"
-#include "third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate.h"
-#include "third_party/blink/renderer/modules/peerconnection/rtc_ice_gather_options.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.h"
-#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event.h"
-#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event_init.h"
-#include "third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/webrtc/api/dtls_transport_interface.h"
-#include "third_party/webrtc/api/jsep_ice_candidate.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
-#include "third_party/webrtc/p2p/base/port_allocator.h"
-#include "third_party/webrtc/p2p/base/transport_description.h"
-#include "third_party/webrtc/pc/ice_server_parsing.h"
-#include "third_party/webrtc/pc/webrtc_sdp.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h
index 6a84ee5..be17c4d 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h
@@ -51,7 +51,7 @@
   // letting it pass.
   MockEventListener* CreateMockEventListener();
 
- private:
+ protected:
   scoped_refptr<base::TestSimpleTaskRunner> main_thread_;
   scoped_refptr<base::TestSimpleTaskRunner> worker_thread_;
   std::vector<Persistent<MockEventListener>> mock_event_listeners_;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 126dae8f..e8302f9 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -97,6 +97,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_init.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_session_description.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_session_description_init.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_session_description_request_impl.h"
@@ -2221,6 +2222,10 @@
   }
 }
 
+RTCSctpTransport* RTCPeerConnection::sctp() const {
+  return sctp_transport_;
+}
+
 RTCDataChannel* RTCPeerConnection::createDataChannel(
     ScriptState* script_state,
     String label,
@@ -2691,6 +2696,27 @@
       MediaStreamSource::kReadyStateMuted);
 }
 
+void RTCPeerConnection::DidModifySctpTransport(
+    WebRTCSctpTransportSnapshot snapshot) {
+  if (!snapshot.transport) {
+    sctp_transport_ = nullptr;
+    return;
+  }
+  if (!sctp_transport_ ||
+      sctp_transport_->native_transport() != snapshot.transport) {
+    sctp_transport_ = MakeGarbageCollected<RTCSctpTransport>(
+        GetExecutionContext(), snapshot.transport);
+    sctp_transport_->ChangeState(snapshot.sctp_transport_state);
+  }
+  if (!sctp_transport_->transport() ||
+      sctp_transport_->transport()->native_transport() !=
+          snapshot.sctp_transport_state.dtls_transport()) {
+    sctp_transport_->SetTransport(CreateOrUpdateDtlsTransport(
+        snapshot.sctp_transport_state.dtls_transport(),
+        snapshot.dtls_transport_state));
+  }
+}
+
 void RTCPeerConnection::DidModifyTransceivers(
     std::vector<std::unique_ptr<WebRTCRtpTransceiver>> web_transceivers,
     bool is_remote_description) {
@@ -3001,6 +3027,9 @@
   for (auto& transceiver : transceivers_) {
     transceiver->OnPeerConnectionClosed();
   }
+  if (sctp_transport_) {
+    sctp_transport_->Close();
+  }
 
   Document* document = To<Document>(GetExecutionContext());
   HostsUsingFeatures::CountAnyWorld(
@@ -3078,6 +3107,7 @@
   visitor->Trace(scheduled_events_);
   visitor->Trace(dtls_transports_by_native_transport_);
   visitor->Trace(ice_transports_by_native_transport_);
+  visitor->Trace(sctp_transport_);
   EventTargetWithInlineData::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
   MediaStreamObserver::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index 2cd4164..0a49d21c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -73,6 +73,7 @@
 class RTCRtpReceiver;
 class RTCRtpSender;
 class RTCRtpTransceiverInit;
+class RTCSctpTransport;
 class RTCSessionDescription;
 class RTCSessionDescriptionInit;
 class ScriptState;
@@ -227,6 +228,7 @@
   void removeTrack(RTCRtpSender*, ExceptionState&);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(track, kTrack)
 
+  RTCSctpTransport* sctp() const;
   RTCDataChannel* createDataChannel(ScriptState*,
                                     String label,
                                     const RTCDataChannelInit*,
@@ -286,6 +288,7 @@
       webrtc::PeerConnectionInterface::PeerConnectionState) override;
   void DidAddReceiverPlanB(std::unique_ptr<WebRTCRtpReceiver>) override;
   void DidRemoveReceiverPlanB(std::unique_ptr<WebRTCRtpReceiver>) override;
+  void DidModifySctpTransport(WebRTCSctpTransportSnapshot) override;
   void DidModifyTransceivers(std::vector<std::unique_ptr<WebRTCRtpTransceiver>>,
                              bool is_remote_description) override;
   void DidAddRemoteDataChannel(
@@ -519,6 +522,7 @@
   String last_offer_;
   String last_answer_;
 
+  Member<RTCSctpTransport> sctp_transport_;
   bool has_data_channels_;  // For RAPPOR metrics
   // In Plan B, senders and receivers are added or removed independently of one
   // another. In Unified Plan, senders and receivers are created in pairs as
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
index a95aa30e..2875bebc 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
@@ -155,6 +155,7 @@
     attribute EventHandler ontrack;
 
     // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api
+    [RuntimeEnabled=RTCSctpTransport] readonly attribute RTCSctpTransport? sctp;
     [CallWith=ScriptState, RaisesException] RTCDataChannel createDataChannel(USVString label, optional RTCDataChannelInit dataChannelDict);
     attribute EventHandler ondatachannel;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.cc
new file mode 100644
index 0000000..017b59c
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.cc
@@ -0,0 +1,175 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.h"
+
+#include <limits>
+#include <memory>
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_error_util.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/sctp_transport_interface.h"
+
+namespace blink {
+
+namespace {
+String TransportStateToString(webrtc::SctpTransportState state) {
+  switch (state) {
+    case webrtc::SctpTransportState::kNew:
+      // Not supposed to happen. DtlsTransport should
+      // only be visible after reaching "connecting" state.
+      NOTREACHED();
+      return String("new");
+      break;
+    case webrtc::SctpTransportState::kConnecting:
+      return String("connecting");
+      break;
+    case webrtc::SctpTransportState::kConnected:
+      return String("connected");
+      break;
+    case webrtc::SctpTransportState::kClosed:
+      return String("closed");
+      break;
+    default:
+      NOTREACHED();
+      return String("failed");
+      break;
+  }
+}
+
+std::unique_ptr<SctpTransportProxy> CreateProxy(
+    ExecutionContext* context,
+    webrtc::SctpTransportInterface* native_transport,
+    SctpTransportProxy::Delegate* delegate,
+    scoped_refptr<base::SingleThreadTaskRunner> main_thread,
+    scoped_refptr<base::SingleThreadTaskRunner> worker_thread) {
+  DCHECK(main_thread);
+  DCHECK(worker_thread);
+  LocalFrame* frame = To<Document>(context)->GetFrame();
+  DCHECK(frame);
+  return SctpTransportProxy::Create(*frame, main_thread, worker_thread,
+                                    native_transport, delegate);
+}
+
+}  // namespace
+
+RTCSctpTransport::RTCSctpTransport(
+    ExecutionContext* context,
+    rtc::scoped_refptr<webrtc::SctpTransportInterface> native_transport)
+    : RTCSctpTransport(context,
+                       native_transport,
+                       To<Document>(context)->GetFrame()->GetTaskRunner(
+                           TaskType::kNetworking),
+                       Platform::Current()->GetWebRtcWorkerThread()) {}
+
+RTCSctpTransport::RTCSctpTransport(
+    ExecutionContext* context,
+    rtc::scoped_refptr<webrtc::SctpTransportInterface> native_transport,
+    scoped_refptr<base::SingleThreadTaskRunner> main_thread,
+    scoped_refptr<base::SingleThreadTaskRunner> worker_thread)
+    : ContextClient(context),
+      current_state_(webrtc::SctpTransportState::kNew),
+      native_transport_(native_transport),
+      proxy_(CreateProxy(context,
+                         native_transport,
+                         this,
+                         main_thread,
+                         worker_thread)) {}
+
+RTCSctpTransport::~RTCSctpTransport() {}
+
+String RTCSctpTransport::state() const {
+  if (closed_from_owner_) {
+    return TransportStateToString(webrtc::SctpTransportState::kClosed);
+  }
+  return TransportStateToString(current_state_.state());
+}
+
+double RTCSctpTransport::maxMessageSize() const {
+  if (current_state_.MaxMessageSize()) {
+    return *current_state_.MaxMessageSize();
+  }
+  // Spec says:
+  // If local size is unlimited and remote side is unknown, return infinity.
+  // http://w3c.github.io/webrtc-pc/#dfn-update-the-data-max-message-size
+  return std::numeric_limits<double>::infinity();
+}
+
+int16_t RTCSctpTransport::maxChannels(bool& isNull) const {
+  if (!current_state_.MaxChannels()) {
+    isNull = true;
+    return 0;
+  }
+  isNull = false;
+  return *current_state_.MaxChannels();
+}
+
+RTCDtlsTransport* RTCSctpTransport::transport() const {
+  return dtls_transport_;
+}
+
+rtc::scoped_refptr<webrtc::SctpTransportInterface>
+RTCSctpTransport::native_transport() {
+  return native_transport_;
+}
+
+void RTCSctpTransport::ChangeState(webrtc::SctpTransportInformation info) {
+  DCHECK(current_state_.state() != webrtc::SctpTransportState::kClosed);
+  current_state_ = info;
+}
+
+void RTCSctpTransport::SetTransport(RTCDtlsTransport* transport) {
+  dtls_transport_ = transport;
+}
+
+// Implementation of SctpTransportProxy::Delegate
+void RTCSctpTransport::OnStartCompleted(webrtc::SctpTransportInformation info) {
+  current_state_ = info;
+  start_completed_ = true;
+}
+
+void RTCSctpTransport::OnStateChange(webrtc::SctpTransportInformation info) {
+  // We depend on closed only happening once for safe garbage collection.
+  DCHECK(current_state_.state() != webrtc::SctpTransportState::kClosed);
+  current_state_ = info;
+  // When Close() has been called, we do not report the state change from the
+  // lower layer, but we keep the SctpTransport object alive until the
+  // lower layer has sent notice that the closing has been completed.
+  if (!closed_from_owner_) {
+    DispatchEvent(*Event::Create(event_type_names::kStatechange));
+  }
+}
+
+void RTCSctpTransport::Close() {
+  closed_from_owner_ = true;
+  if (current_state_.state() != webrtc::SctpTransportState::kClosed) {
+    DispatchEvent(*Event::Create(event_type_names::kStatechange));
+  }
+}
+
+const AtomicString& RTCSctpTransport::InterfaceName() const {
+  return event_target_names::kRTCSctpTransport;
+}
+
+ExecutionContext* RTCSctpTransport::GetExecutionContext() const {
+  return ContextClient::GetExecutionContext();
+}
+
+void RTCSctpTransport::Trace(Visitor* visitor) {
+  visitor->Trace(dtls_transport_);
+  EventTargetWithInlineData::Trace(visitor);
+  ContextClient::Trace(visitor);
+  SctpTransportProxy::Delegate::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.h b/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.h
new file mode 100644
index 0000000..2ecee06
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.h
@@ -0,0 +1,78 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_SCTP_TRANSPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_SCTP_TRANSPORT_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/event_target_modules.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h"
+#include "third_party/webrtc/api/scoped_refptr.h"
+#include "third_party/webrtc/api/sctp_transport_interface.h"
+
+namespace blink {
+
+class SctpTransportProxy;
+class RTCDtlsTransport;
+
+enum class RTCSctpTransportState { kChecking, kConnected, kClosed };
+
+// Blink bindings for the RTCSctpTransport JavaScript object.
+class MODULES_EXPORT RTCSctpTransport final
+    : public EventTargetWithInlineData,
+      public ContextClient,
+      public SctpTransportProxy::Delegate {
+  DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(RTCSctpTransport);
+
+ public:
+  RTCSctpTransport(
+      ExecutionContext* context,
+      rtc::scoped_refptr<webrtc::SctpTransportInterface> native_transport);
+  // Constructor with explicit thread injection, used for testing.
+  RTCSctpTransport(
+      ExecutionContext* context,
+      rtc::scoped_refptr<webrtc::SctpTransportInterface> native_transport,
+      scoped_refptr<base::SingleThreadTaskRunner> main_thread,
+      scoped_refptr<base::SingleThreadTaskRunner> worker_thread);
+  ~RTCSctpTransport() override;
+
+  // rtc_sctp_transport.idl
+  RTCDtlsTransport* transport() const;
+  String state() const;
+  double maxMessageSize() const;
+  int16_t maxChannels(bool& is_null) const;
+
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(statechange, kStatechange)
+
+  // SctpTransportProxy::Delegate
+  void OnStartCompleted(webrtc::SctpTransportInformation info) override;
+  void OnStateChange(webrtc::SctpTransportInformation info) override;
+
+  // EventTarget overrides.
+  const AtomicString& InterfaceName() const override;
+  ExecutionContext* GetExecutionContext() const override;
+  // Others
+  void ChangeState(webrtc::SctpTransportInformation info);
+  void SetTransport(RTCDtlsTransport*);
+  rtc::scoped_refptr<webrtc::SctpTransportInterface> native_transport();
+  // Called from owning RtcPeerConnection when it is closed.
+  void Close();
+  // For garbage collection.
+  void Trace(blink::Visitor* visitor) override;
+
+ private:
+  webrtc::SctpTransportInformation current_state_;
+  rtc::scoped_refptr<webrtc::SctpTransportInterface> native_transport_;
+  std::unique_ptr<SctpTransportProxy> proxy_;
+  Member<RTCDtlsTransport> dtls_transport_;
+  bool start_completed_ = false;
+  bool closed_from_owner_ = false;
+};
+
+}  // namespace blink
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_SCTP_TRANSPORT_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.idl b/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.idl
new file mode 100644
index 0000000..f99a289
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.idl
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// http://w3c.github.io/webrtc-pc/#dom-rtcsctptransportstate
+enum RTCSctpTransportState {
+    "connecting",
+    "connected",
+    "closed"
+};
+
+// http://w3c.github.io/webrtc-pc/#rtcdtlstransport-interface
+
+[
+    Exposed=Window,
+    RuntimeEnabled=RTCSctpTransport
+] interface RTCSctpTransport {
+    readonly        attribute RTCDtlsTransport transport;
+    readonly        attribute RTCSctpTransportState state;
+    readonly        attribute unrestricted double maxMessageSize;
+    readonly        attribute unsigned short? maxChannels;
+                    attribute EventHandler     onstatechange;
+};
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport_test.cc
new file mode 100644
index 0000000..d75aebb3
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport_test.cc
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/peerconnection/rtc_sctp_transport.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/web_heap.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/core/testing/null_execution_context.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.h"
+#include "third_party/webrtc/api/sctp_transport_interface.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
+
+namespace blink {
+
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+
+class MockSctpTransport : public webrtc::SctpTransportInterface {
+ public:
+  MockSctpTransport() {
+    ON_CALL(*this, Information()).WillByDefault(Return(info_));
+    ON_CALL(*this, RegisterObserver(_))
+        .WillByDefault(Invoke(this, &MockSctpTransport::SetObserver));
+  }
+  MOCK_CONST_METHOD0(dtls_transport,
+                     rtc::scoped_refptr<webrtc::DtlsTransportInterface>());
+  MOCK_CONST_METHOD0(Information, webrtc::SctpTransportInformation());
+  MOCK_METHOD1(RegisterObserver, void(webrtc::SctpTransportObserverInterface*));
+  MOCK_METHOD0(UnregisterObserver, void());
+
+  void SetObserver(webrtc::SctpTransportObserverInterface* observer) {
+    observer_ = observer;
+  }
+
+  void SendClose() {
+    if (observer_) {
+      observer_->OnStateChange(webrtc::SctpTransportInformation(
+          webrtc::SctpTransportState::kClosed));
+    }
+  }
+
+ private:
+  webrtc::SctpTransportInformation info_ =
+      webrtc::SctpTransportInformation(webrtc::SctpTransportState::kNew);
+  webrtc::SctpTransportObserverInterface* observer_ = nullptr;
+};
+
+class RTCSctpTransportTest : public RTCIceTransportTest {};
+
+TEST_F(RTCSctpTransportTest, CreateFromMocks) {
+  V8TestingScope scope;
+
+  ExecutionContext* context = scope.GetExecutionContext();
+  rtc::scoped_refptr<webrtc::SctpTransportInterface> mock_native_transport =
+      new rtc::RefCountedObject<NiceMock<MockSctpTransport>>();
+  RTCSctpTransport* transport = MakeGarbageCollected<RTCSctpTransport>(
+      context, mock_native_transport, main_thread_, worker_thread_);
+  WeakPersistent<RTCSctpTransport> garbage_collection_observer = transport;
+  RunUntilIdle();
+  transport = nullptr;
+  // An unclosed transport should not be garbage collected, since events
+  // might still trickle up.
+  ASSERT_TRUE(garbage_collection_observer);
+  // A closed transport should be garbage collected.
+  static_cast<MockSctpTransport*>(mock_native_transport.get())->SendClose();
+  RunUntilIdle();
+  WebHeap::CollectAllGarbageForTesting();
+  EXPECT_FALSE(garbage_collection_observer);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 87ca39b..61b56b1 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1214,6 +1214,11 @@
       name: "RTCRtpSenderParameters",
       status: "stable",
     },
+    // Enables the use of the RTCSctpTransport object.
+    {
+      name: "RTCSctpTransport",
+      status: "test",
+    },
     {
       name: "RTCStatsRelativePacketArrivalDelay",
       origin_trial_feature_name: "RTCStatsRelativePacketArrivalDelay",
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt
index eca828b..e4e58c43 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL RTCDtlsTransport.prototype.getRemoteCertificates RTCSctpTransport is not defined
+FAIL RTCDtlsTransport.prototype.getRemoteCertificates dtlsTransport.getCertificates is not a function
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCIceTransport-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCIceTransport-expected.txt
index 8fcf2e2..5de6f8a 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCIceTransport-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCIceTransport-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL Two connected iceTransports should has matching local/remote candidates returned promise_test: Unhandled rejection with value: object "ReferenceError: RTCSctpTransport is not defined"
-FAIL Unconnected iceTransport should have empty remote candidates and selected pair promise_test: Unhandled rejection with value: object "ReferenceError: RTCSctpTransport is not defined"
+FAIL Two connected iceTransports should has matching local/remote candidates returned promise_test: Unhandled rejection with value: object "ReferenceError: RTCIceTransport is not defined"
+FAIL Unconnected iceTransport should have empty remote candidates and selected pair promise_test: Unhandled rejection with value: object "ReferenceError: RTCIceTransport is not defined"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-connectionState-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-connectionState-expected.txt
index 8b1b808..1aca75f 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-connectionState-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-connectionState-expected.txt
@@ -2,6 +2,6 @@
 PASS Initial connectionState should be new
 PASS Closing the connection should set connectionState to closed
 PASS connection with one data channel should eventually have connected connection state
-FAIL connection with one data channel should eventually have transports in connected state Cannot read property 'transport' of undefined
+FAIL connection with one data channel should eventually have transports in connected state assert_true: Expect ICE transport to be in connected or completed state expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
index c5fd94e..96e7bba 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
@@ -2,7 +2,7 @@
 PASS Initial iceConnectionState should be new
 PASS Closing the connection should set iceConnectionState to closed
 PASS connection with one data channel should eventually have connected or completed connection state
-FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of undefined
+FAIL connection with one data channel should eventually have connected connection state Cannot read property 'state' of undefined
 PASS ICE can connect in a recvonly usecase
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt
index cf325d5..91d147a 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 PASS Initial iceGatheringState should be new
 PASS iceGatheringState should eventually become complete after setLocalDescription
-FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of undefined
+FAIL connection with one data channel should eventually have connected connection state Cannot read property 'gatheringState' of undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt
deleted file mode 100644
index f4f36f4..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL setRemoteDescription() with answer containing data media should initialize pc.sctp assert_equals: expected (object) null but got (undefined) undefined
-FAIL setLocalDescription() with answer containing data media should initialize pc.sctp assert_equals: expected (object) null but got (undefined) undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-constructor.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-constructor.html
index 7d3df05..28dae05 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-constructor.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-constructor.html
@@ -59,7 +59,7 @@
       assert_true(sctp.transport instanceof RTCDtlsTransport,
         'Expect sctp.transport to be instance of RTCDtlsTransport');
 
-      assert_true(typeof sctp.maxMessageSize, 'number',
+      assert_true(typeof sctp.maxMessageSize == 'number',
         'Expect sctp.maxMessageSize to be a number');
     });
   }, 'setRemoteDescription() with answer containing data media should initialize pc.sctp');
@@ -82,7 +82,7 @@
       assert_true(sctp.transport instanceof RTCDtlsTransport,
         'Expect sctp.transport to be instance of RTCDtlsTransport');
 
-      assert_true(typeof sctp.maxMessageSize, 'number',
+      assert_true(typeof sctp.maxMessageSize == 'number',
         'Expect sctp.maxMessageSize to be a number');
     });
   }, 'setLocalDescription() with answer containing data media should initialize pc.sctp');
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-maxMessageSize-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-maxMessageSize-expected.txt
index 4d1f9a1..95278159 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-maxMessageSize-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-maxMessageSize-expected.txt
@@ -1,8 +1,8 @@
 This is a testharness.js-based test.
-FAIL Determine the local side send limitation (canSendSize) by offering a max-message-size of 0 assert_equals: expected (object) null but got (undefined) undefined
-FAIL Remote offer SDP missing max-message-size attribute assert_equals: expected (object) null but got (undefined) undefined
-FAIL max-message-size with a (non-zero) value provided by the remote peer assert_equals: expected (object) null but got (undefined) undefined
-FAIL Renegotiate max-message-size with a (non-zero) value provided by the remote peer assert_equals: expected (object) null but got (undefined) undefined
-FAIL max-message-size with a (non-zero) value larger than canSendSize provided by the remote peer assert_equals: expected (object) null but got (undefined) undefined
+FAIL Determine the local side send limitation (canSendSize) by offering a max-message-size of 0 assert_not_equals: SDP should have max-message-size attribute got disallowed value null
+FAIL Remote offer SDP missing max-message-size attribute assert_not_equals: SDP should have max-message-size attribute got disallowed value null
+FAIL max-message-size with a (non-zero) value provided by the remote peer assert_not_equals: SDP should have max-message-size attribute got disallowed value null
+FAIL Renegotiate max-message-size with a (non-zero) value provided by the remote peer assert_not_equals: SDP should have max-message-size attribute got disallowed value null
+FAIL max-message-size with a (non-zero) value larger than canSendSize provided by the remote peer assert_not_equals: SDP should have max-message-size attribute got disallowed value null
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
index 8191ea2..3910ac5 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,8 +1,8 @@
 This is a testharness.js-based test.
-Found 494 tests; 378 PASS, 116 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 494 tests; 412 PASS, 82 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Test driver for asyncInitCertificate
-FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: ReferenceError: RTCSctpTransport is not defined Reached unreachable code
+PASS Test driver for asyncInitTransports
 PASS Test driver for asyncInitMediaStreamTrack
 PASS Partial interface RTCPeerConnection: original interface defined
 PASS Partial dictionary RTCOfferOptions: original dictionary defined
@@ -57,7 +57,7 @@
 PASS RTCPeerConnection interface: operation removeTrack(RTCRtpSender)
 PASS RTCPeerConnection interface: operation addTransceiver([object Object],[object Object], RTCRtpTransceiverInit)
 PASS RTCPeerConnection interface: attribute ontrack
-FAIL RTCPeerConnection interface: attribute sctp assert_true: The prototype object must have a property "sctp" expected true got false
+PASS RTCPeerConnection interface: attribute sctp
 PASS RTCPeerConnection interface: operation createDataChannel(USVString, RTCDataChannelInit)
 PASS RTCPeerConnection interface: attribute ondatachannel
 PASS RTCPeerConnection interface: operation getStats(MediaStreamTrack)
@@ -119,7 +119,7 @@
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addTransceiver([object Object],[object Object], RTCRtpTransceiverInit)" with the proper type
 PASS RTCPeerConnection interface: calling addTransceiver([object Object],[object Object], RTCRtpTransceiverInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "ontrack" with the proper type Unrecognized type EventHandler
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "sctp" with the proper type assert_inherits: property "sctp" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "sctp" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "createDataChannel(USVString, RTCDataChannelInit)" with the proper type
 PASS RTCPeerConnection interface: calling createDataChannel(USVString, RTCDataChannelInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "ondatachannel" with the proper type Unrecognized type EventHandler
@@ -315,13 +315,13 @@
 PASS RTCDtlsTransport interface: operation getRemoteCertificates()
 PASS RTCDtlsTransport interface: attribute onstatechange
 PASS RTCDtlsTransport interface: attribute onerror
-FAIL RTCDtlsTransport must be primary interface of idlTestObjects.dtlsTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL Stringification of idlTestObjects.dtlsTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "iceTransport" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "state" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "getRemoteCertificates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onerror" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
+PASS RTCDtlsTransport must be primary interface of idlTestObjects.dtlsTransport
+PASS Stringification of idlTestObjects.dtlsTransport
+PASS RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "iceTransport" with the proper type
+PASS RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "state" with the proper type
+PASS RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "getRemoteCertificates()" with the proper type
+FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onstatechange" with the proper type Unrecognized type EventHandler
+FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onerror" with the proper type Unrecognized type EventHandler
 FAIL RTCIceTransport interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
                 new interface_object();
             }" did not throw
@@ -342,20 +342,20 @@
 PASS RTCIceTransport interface: attribute onstatechange
 PASS RTCIceTransport interface: attribute ongatheringstatechange
 PASS RTCIceTransport interface: attribute onselectedcandidatepairchange
-FAIL RTCIceTransport must be primary interface of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL Stringification of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "role" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "component" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "state" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "gatheringState" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getLocalCandidates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getRemoteCandidates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getSelectedCandidatePair()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getLocalParameters()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getRemoteParameters()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "ongatheringstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "onselectedcandidatepairchange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
+PASS RTCIceTransport must be primary interface of idlTestObjects.iceTransport
+PASS Stringification of idlTestObjects.iceTransport
+FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "role" with the proper type assert_equals: expected "string" but got "object"
+FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "component" with the proper type assert_inherits: property "component" not found in prototype chain
+PASS RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "state" with the proper type
+PASS RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "gatheringState" with the proper type
+PASS RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getLocalCandidates()" with the proper type
+PASS RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getRemoteCandidates()" with the proper type
+PASS RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getSelectedCandidatePair()" with the proper type
+PASS RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getLocalParameters()" with the proper type
+PASS RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getRemoteParameters()" with the proper type
+FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "onstatechange" with the proper type Unrecognized type EventHandler
+FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "ongatheringstatechange" with the proper type Unrecognized type EventHandler
+FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "onselectedcandidatepairchange" with the proper type Unrecognized type EventHandler
 PASS RTCTrackEvent interface: existence and properties of interface object
 PASS RTCTrackEvent interface object length
 PASS RTCTrackEvent interface object name
@@ -372,24 +372,24 @@
 PASS RTCTrackEvent interface: initTrackEvent() must inherit property "track" with the proper type
 PASS RTCTrackEvent interface: initTrackEvent() must inherit property "streams" with the proper type
 PASS RTCTrackEvent interface: initTrackEvent() must inherit property "transceiver" with the proper type
-FAIL RTCSctpTransport interface: existence and properties of interface object assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface object length assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface object name assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: existence and properties of interface prototype object assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute transport assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute state assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute maxMessageSize assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute maxChannels assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute onstatechange assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport must be primary interface of idlTestObjects.sctpTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL Stringification of idlTestObjects.sctpTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "transport" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "state" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "maxMessageSize" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "maxChannels" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
+PASS RTCSctpTransport interface: existence and properties of interface object
+PASS RTCSctpTransport interface object length
+PASS RTCSctpTransport interface object name
+PASS RTCSctpTransport interface: existence and properties of interface prototype object
+PASS RTCSctpTransport interface: existence and properties of interface prototype object's "constructor" property
+PASS RTCSctpTransport interface: existence and properties of interface prototype object's @@unscopables property
+PASS RTCSctpTransport interface: attribute transport
+PASS RTCSctpTransport interface: attribute state
+PASS RTCSctpTransport interface: attribute maxMessageSize
+PASS RTCSctpTransport interface: attribute maxChannels
+PASS RTCSctpTransport interface: attribute onstatechange
+PASS RTCSctpTransport must be primary interface of idlTestObjects.sctpTransport
+PASS Stringification of idlTestObjects.sctpTransport
+PASS RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "transport" with the proper type
+PASS RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "state" with the proper type
+PASS RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "maxMessageSize" with the proper type
+PASS RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "maxChannels" with the proper type
+FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "onstatechange" with the proper type Unrecognized type EventHandler
 PASS RTCDataChannel interface: existence and properties of interface object
 PASS RTCDataChannel interface object length
 PASS RTCDataChannel interface object name
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt
new file mode 100644
index 0000000..81f49d68
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL RTCDtlsTransport.prototype.getRemoteCertificates assert_true: Expect pc.sctp to be set to valid RTCSctpTransport expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCIceTransport-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCIceTransport-expected.txt
new file mode 100644
index 0000000..465184fe
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCIceTransport-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Two connected iceTransports should has matching local/remote candidates returned assert_true: Expect pc.sctp to be instantiated from RTCSctpTransport expected true got false
+FAIL Unconnected iceTransport should have empty remote candidates and selected pair assert_true: Expect pc.sctp to be instantiated from RTCSctpTransport expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-connectionState-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-connectionState-expected.txt
new file mode 100644
index 0000000..22eabf6c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-connectionState-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS Initial connectionState should be new
+PASS Closing the connection should set connectionState to closed
+PASS connection with one data channel should eventually have connected connection state
+FAIL connection with one data channel should eventually have transports in connected state Cannot read property 'transport' of null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
index 9e2c9a37..4eccf89 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
@@ -2,7 +2,7 @@
 PASS Initial iceConnectionState should be new
 PASS Closing the connection should set iceConnectionState to closed
 PASS connection with one data channel should eventually have connected or completed connection state
-FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of undefined
+FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of null
 FAIL ICE can connect in a recvonly usecase promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt
new file mode 100644
index 0000000..61d27bf
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS Initial iceGatheringState should be new
+PASS iceGatheringState should eventually become complete after setLocalDescription
+FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt
new file mode 100644
index 0000000..cb1e2a05
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL setRemoteDescription() with answer containing data media should initialize pc.sctp assert_not_equals: got disallowed value null
+FAIL setLocalDescription() with answer containing data media should initialize pc.sctp assert_not_equals: got disallowed value null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
index f366bea..def4c14 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,8 +1,8 @@
 This is a testharness.js-based test.
-Found 494 tests; 341 PASS, 153 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 494 tests; 354 PASS, 140 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Test driver for asyncInitCertificate
-FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: ReferenceError: RTCSctpTransport is not defined Reached unreachable code
+FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: Error: assert_true: Expect pc.sctp to be instance of RTCSctpTransport expected true got false Reached unreachable code
 PASS Test driver for asyncInitMediaStreamTrack
 PASS Partial interface RTCPeerConnection: original interface defined
 PASS Partial dictionary RTCOfferOptions: original dictionary defined
@@ -57,7 +57,7 @@
 PASS RTCPeerConnection interface: operation removeTrack(RTCRtpSender)
 PASS RTCPeerConnection interface: operation addTransceiver([object Object],[object Object], RTCRtpTransceiverInit)
 PASS RTCPeerConnection interface: attribute ontrack
-FAIL RTCPeerConnection interface: attribute sctp assert_true: The prototype object must have a property "sctp" expected true got false
+PASS RTCPeerConnection interface: attribute sctp
 PASS RTCPeerConnection interface: operation createDataChannel(USVString, RTCDataChannelInit)
 PASS RTCPeerConnection interface: attribute ondatachannel
 PASS RTCPeerConnection interface: operation getStats(MediaStreamTrack)
@@ -119,7 +119,7 @@
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addTransceiver([object Object],[object Object], RTCRtpTransceiverInit)" with the proper type
 PASS RTCPeerConnection interface: calling addTransceiver([object Object],[object Object], RTCRtpTransceiverInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "ontrack" with the proper type Unrecognized type EventHandler
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "sctp" with the proper type assert_inherits: property "sctp" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "sctp" with the proper type
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "createDataChannel(USVString, RTCDataChannelInit)" with the proper type
 PASS RTCPeerConnection interface: calling createDataChannel(USVString, RTCDataChannelInit) on new RTCPeerConnection() with too few arguments must throw TypeError
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "ondatachannel" with the proper type Unrecognized type EventHandler
@@ -372,17 +372,17 @@
 FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "streams" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "transceiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCSctpTransport interface: existence and properties of interface object assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface object length assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface object name assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: existence and properties of interface prototype object assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute transport assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute state assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute maxMessageSize assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute maxChannels assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
-FAIL RTCSctpTransport interface: attribute onstatechange assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
+PASS RTCSctpTransport interface: existence and properties of interface object
+PASS RTCSctpTransport interface object length
+PASS RTCSctpTransport interface object name
+PASS RTCSctpTransport interface: existence and properties of interface prototype object
+PASS RTCSctpTransport interface: existence and properties of interface prototype object's "constructor" property
+PASS RTCSctpTransport interface: existence and properties of interface prototype object's @@unscopables property
+PASS RTCSctpTransport interface: attribute transport
+PASS RTCSctpTransport interface: attribute state
+PASS RTCSctpTransport interface: attribute maxMessageSize
+PASS RTCSctpTransport interface: attribute maxChannels
+PASS RTCSctpTransport interface: attribute onstatechange
 FAIL RTCSctpTransport must be primary interface of idlTestObjects.sctpTransport assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL Stringification of idlTestObjects.sctpTransport assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "transport" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 623faca38..5cc6e77 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5733,6 +5733,7 @@
     getter pendingLocalDescription
     getter pendingRemoteDescription
     getter remoteDescription
+    getter sctp
     getter signalingState
     method addIceCandidate
     method addStream
@@ -5840,6 +5841,15 @@
     getter stopped
     method constructor
     setter direction
+interface RTCSctpTransport
+    attribute @@toStringTag
+    getter maxChannels
+    getter maxMessageSize
+    getter onstatechange
+    getter state
+    getter transport
+    method constructor
+    setter onstatechange
 interface RTCSessionDescription
     attribute @@toStringTag
     getter sdp
@@ -10649,6 +10659,7 @@
     getter pendingLocalDescription
     getter pendingRemoteDescription
     getter remoteDescription
+    getter sctp
     getter signalingState
     method addIceCandidate
     method addStream