[Perfect Negotiation] Fire onnegotiationneeded when chain is empty.

This CL generates "negotiationneeded" events if negotiation is needed
when the Operations Chain becomes empty. This is only implemented in
Unified Plan to avoid Plan B regressions (the event is pretty useless
in Plan B as it fires repeatedly).

In order to implement the spec-compliant behavior of only firing the
event when the chain is empty, this CL introduces
PeerConnectionObserver::OnNegotiationNeededEvent() and
PeerConnectionInterface::ShouldFireNegotiationNeededEvent() to allow
validating the event before firing it. This is needed because the event
must not be fired until a task has been posted and subsequently chained
operations could invalidate it in the meantime.

Test coverage is added for both legacy and modern "negotiationneeded"
events.

Bug: chromium:1060083
Change-Id: I1dbaa8f6ddb1c6e7c8abd8da3b92efcb64060383
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180620
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31989}
diff --git a/rtc_base/operations_chain_unittest.cc b/rtc_base/operations_chain_unittest.cc
index ed3c924..988ad34 100644
--- a/rtc_base/operations_chain_unittest.cc
+++ b/rtc_base/operations_chain_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "rtc_base/operations_chain.h"
 
+#include <atomic>
 #include <functional>
 #include <memory>
 #include <utility>
@@ -120,6 +121,31 @@
     return event;
   }
 
+  void SetOnChainEmptyCallback(std::function<void()> on_chain_empty_callback) {
+    Event event;
+    operations_chain_thread_->PostTask(
+        RTC_FROM_HERE,
+        [this, &event,
+         on_chain_empty_callback = std::move(on_chain_empty_callback)]() {
+          operations_chain_->SetOnChainEmptyCallback(
+              std::move(on_chain_empty_callback));
+          event.Set();
+        });
+    event.Wait(Event::kForever);
+  }
+
+  bool IsEmpty() {
+    Event event;
+    bool is_empty = false;
+    operations_chain_thread_->PostTask(
+        RTC_FROM_HERE, [this, &event, &is_empty]() {
+          is_empty = operations_chain_->IsEmpty();
+          event.Set();
+        });
+    event.Wait(Event::kForever);
+    return is_empty;
+  }
+
   std::unique_ptr<Event> ReleaseOperationChain() {
     std::unique_ptr<Event> event = std::make_unique<Event>();
     operations_chain_thread_->PostTask(RTC_FROM_HERE,
@@ -326,6 +352,87 @@
           operation6_completed_event.get()));
 }
 
+TEST(OperationsChainTest, IsEmpty) {
+  OperationTrackerProxy operation_tracker_proxy;
+  operation_tracker_proxy.Initialize()->Wait(Event::kForever);
+
+  // The chain is initially empty.
+  EXPECT_TRUE(operation_tracker_proxy.IsEmpty());
+  // Chain a single event.
+  Event unblock_async_operation_event0;
+  auto async_operation_completed_event0 =
+      operation_tracker_proxy.PostAsynchronousOperation(
+          &unblock_async_operation_event0);
+  // The chain is not empty while an event is pending.
+  EXPECT_FALSE(operation_tracker_proxy.IsEmpty());
+  // Completing the operation empties the chain.
+  unblock_async_operation_event0.Set();
+  async_operation_completed_event0->Wait(Event::kForever);
+  EXPECT_TRUE(operation_tracker_proxy.IsEmpty());
+
+  // Chain multiple events.
+  Event unblock_async_operation_event1;
+  auto async_operation_completed_event1 =
+      operation_tracker_proxy.PostAsynchronousOperation(
+          &unblock_async_operation_event1);
+  Event unblock_async_operation_event2;
+  auto async_operation_completed_event2 =
+      operation_tracker_proxy.PostAsynchronousOperation(
+          &unblock_async_operation_event2);
+  // Again, the chain is not empty while an event is pending.
+  EXPECT_FALSE(operation_tracker_proxy.IsEmpty());
+  // Upon completing the first event, the chain is still not empty.
+  unblock_async_operation_event1.Set();
+  async_operation_completed_event1->Wait(Event::kForever);
+  EXPECT_FALSE(operation_tracker_proxy.IsEmpty());
+  // Completing the last evenet empties the chain.
+  unblock_async_operation_event2.Set();
+  async_operation_completed_event2->Wait(Event::kForever);
+  EXPECT_TRUE(operation_tracker_proxy.IsEmpty());
+}
+
+TEST(OperationsChainTest, OnChainEmptyCallback) {
+  OperationTrackerProxy operation_tracker_proxy;
+  operation_tracker_proxy.Initialize()->Wait(Event::kForever);
+
+  std::atomic<size_t> on_empty_callback_counter(0u);
+  operation_tracker_proxy.SetOnChainEmptyCallback(
+      [&on_empty_callback_counter] { ++on_empty_callback_counter; });
+
+  // Chain a single event.
+  Event unblock_async_operation_event0;
+  auto async_operation_completed_event0 =
+      operation_tracker_proxy.PostAsynchronousOperation(
+          &unblock_async_operation_event0);
+  // The callback is not invoked until the operation has completed.
+  EXPECT_EQ(0u, on_empty_callback_counter);
+  // Completing the operation empties the chain, invoking the callback.
+  unblock_async_operation_event0.Set();
+  async_operation_completed_event0->Wait(Event::kForever);
+  EXPECT_EQ(1u, on_empty_callback_counter);
+
+  // Chain multiple events.
+  Event unblock_async_operation_event1;
+  auto async_operation_completed_event1 =
+      operation_tracker_proxy.PostAsynchronousOperation(
+          &unblock_async_operation_event1);
+  Event unblock_async_operation_event2;
+  auto async_operation_completed_event2 =
+      operation_tracker_proxy.PostAsynchronousOperation(
+          &unblock_async_operation_event2);
+  // Again, the callback is not invoked until the operation has completed.
+  EXPECT_EQ(1u, on_empty_callback_counter);
+  // Upon completing the first event, the chain is still not empty, so the
+  // callback must not be invoked yet.
+  unblock_async_operation_event1.Set();
+  async_operation_completed_event1->Wait(Event::kForever);
+  EXPECT_EQ(1u, on_empty_callback_counter);
+  // Completing the last evenet empties the chain, invoking the callback.
+  unblock_async_operation_event2.Set();
+  async_operation_completed_event2->Wait(Event::kForever);
+  EXPECT_EQ(2u, on_empty_callback_counter);
+}
+
 TEST(OperationsChainTest,
      SafeToReleaseReferenceToOperationChainWhileOperationIsPending) {
   OperationTrackerProxy operation_tracker_proxy;