Add base::RunLoop::NestingObserver::OnExitNestedRunLoop().

This method is called when a nested loop is done running work.
It will help simplify the TaskQueueManager code.

Use case:
The blink scheduler needs to adjust the time domain
when there is a transition between a nested/non-nested scope.
https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc?l=2302&rcl=614a67e2c14cf8bb9a06f4fe8da7625cbf1ea7d7
Currently, it checks base::RunLoop::IsNestedOnCurrentThread()
every time a task completes to detect transitions between
a nested/non-nested scope. Code would be simpler with
an explicit notification.

Bug: 783309
Change-Id: I9748c287ad8418831598d84f45a518e4138c5e1b
Reviewed-on: https://chromium-review.googlesource.com/766388
Reviewed-by: Gabriel Charette <[email protected]>
Reviewed-by: Alexander Timin <[email protected]>
Commit-Queue: François Doray <[email protected]>
Cr-Commit-Position: refs/heads/master@{#527112}
diff --git a/base/run_loop.cc b/base/run_loop.cc
index 3cda1cb0..3b7bfde 100644
--- a/base/run_loop.cc
+++ b/base/run_loop.cc
@@ -321,6 +321,11 @@
   RunLoop* previous_run_loop =
       active_run_loops_.empty() ? nullptr : active_run_loops_.top();
 
+  if (previous_run_loop) {
+    for (auto& observer : delegate_->nesting_observers_)
+      observer.OnExitNestedRunLoop();
+  }
+
   // Execute deferred Quit, if any:
   if (previous_run_loop && previous_run_loop->quit_called_)
     delegate_->Quit();
diff --git a/base/run_loop.h b/base/run_loop.h
index e43c4fc..b7c594e1 100644
--- a/base/run_loop.h
+++ b/base/run_loop.h
@@ -130,12 +130,13 @@
   // Safe to call before RegisterDelegateForCurrentThread().
   static bool IsNestedOnCurrentThread();
 
-  // A NestingObserver is notified when a nested RunLoop begins. The observers
-  // are notified before the current thread's RunLoop::Delegate::Run() is
-  // invoked and nested work begins.
+  // A NestingObserver is notified when a nested RunLoop begins and ends.
   class BASE_EXPORT NestingObserver {
    public:
+    // Notified before a nested loop starts running work on the current thread.
     virtual void OnBeginNestedRunLoop() = 0;
+    // Notified after a nested loop is done running work on the current thread.
+    virtual void OnExitNestedRunLoop() {}
 
    protected:
     virtual ~NestingObserver() = default;
diff --git a/base/run_loop_unittest.cc b/base/run_loop_unittest.cc
index 714bf82..96060f4a 100644
--- a/base/run_loop_unittest.cc
+++ b/base/run_loop_unittest.cc
@@ -570,21 +570,37 @@
   run_loop_.Run();
 }
 
+namespace {
+
 class MockNestingObserver : public RunLoop::NestingObserver {
  public:
   MockNestingObserver() = default;
 
   // RunLoop::NestingObserver:
   MOCK_METHOD0(OnBeginNestedRunLoop, void());
+  MOCK_METHOD0(OnExitNestedRunLoop, void());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockNestingObserver);
 };
 
+class MockTask {
+ public:
+  MockTask() = default;
+  MOCK_METHOD0(Task, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockTask);
+};
+
+}  // namespace
+
 TEST_P(RunLoopTest, NestingObservers) {
   EXPECT_TRUE(RunLoop::IsNestingAllowedOnCurrentThread());
 
   testing::StrictMock<MockNestingObserver> nesting_observer;
+  testing::StrictMock<MockTask> mock_task_a;
+  testing::StrictMock<MockTask> mock_task_b;
 
   RunLoop::AddNestingObserverOnCurrentThread(&nesting_observer);
 
@@ -599,14 +615,27 @@
     nested_run_loop.Run();
   });
 
-  // Generate a stack of nested RunLoops, an OnBeginNestedRunLoop() is
-  // expected when beginning each nesting depth.
+  // Generate a stack of nested RunLoops. OnBeginNestedRunLoop() is expected
+  // when beginning each nesting depth and OnExitNestedRunLoop() is expected
+  // when exiting each nesting depth.
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop);
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_a)));
   ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop);
-  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure());
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_b)));
 
-  EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop()).Times(2);
-  run_loop_.Run();
+  {
+    testing::InSequence in_sequence;
+    EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop());
+    EXPECT_CALL(mock_task_a, Task());
+    EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop());
+    EXPECT_CALL(mock_task_b, Task());
+    EXPECT_CALL(nesting_observer, OnExitNestedRunLoop()).Times(2);
+  }
+  run_loop_.RunUntilIdle();
 
   RunLoop::RemoveNestingObserverOnCurrentThread(&nesting_observer);
 }