[iOS] Show toolbar when a new foreground tab is opened.

This CL creates NewForegroundTabFullscreenDisabler, which creates an
AnimatedScopedFullscreenDisabler when a new foreground tab is added to
the WebStateList.

To support this, this CL also updates AnimatedScopedFullscreenDisabler
to have an observer API that can be notified when the animation
finishes.

Additionally. this CL updates BVC to be updated for fullscreen events
even when not broadcasting UI.

TBR=kkhorimoto@chromium.org

(cherry picked from commit 65c25f9ad8a5b11f86d4f44753eba9e726515362)

Bug: 809120
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: I4d4d4adc0de009f759b5136b32d267c7c68cac3d
Reviewed-on: https://chromium-review.googlesource.com/920792
Commit-Queue: Kurt Horimoto <kkhorimoto@chromium.org>
Reviewed-by: Mark Cogan <marq@chromium.org>
Reviewed-by: Justin Cohen <justincohen@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#538372}
Reviewed-on: https://chromium-review.googlesource.com/942222
Reviewed-by: Kurt Horimoto <kkhorimoto@chromium.org>
Cr-Commit-Position: refs/branch-heads/3325@{#630}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn
index 91366e8..1235178 100644
--- a/ios/chrome/browser/ui/BUILD.gn
+++ b/ios/chrome/browser/ui/BUILD.gn
@@ -231,6 +231,8 @@
     "fade_truncated_label.mm",
     "key_commands_provider.h",
     "key_commands_provider.mm",
+    "new_foreground_tab_fullscreen_disabler.h",
+    "new_foreground_tab_fullscreen_disabler.mm",
     "open_in_controller.h",
     "open_in_controller.mm",
     "open_in_controller_testing.h",
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index a2253bc..6735a71 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -163,6 +163,7 @@
 #import "ios/chrome/browser/ui/main_content/main_content_ui_broadcasting_util.h"
 #import "ios/chrome/browser/ui/main_content/main_content_ui_state.h"
 #import "ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.h"
+#import "ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
 #import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_handset_coordinator.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h"
@@ -597,6 +598,10 @@
   // The updater that adjusts the toolbar's layout for fullscreen events.
   std::unique_ptr<FullscreenUIUpdater> _fullscreenUIUpdater;
 
+  // The fullscreen disabler for the new foreground tab animation.
+  std::unique_ptr<NewForegroundTabFullscreenDisabler>
+      _foregroundTabAnimationFullscreenDisabler;
+
   // Coordinator for the External Search UI.
   ExternalSearchCoordinator* _externalSearchCoordinator;
 
@@ -999,6 +1004,9 @@
     [[UpgradeCenter sharedInstance] registerClient:self
                                     withDispatcher:self.dispatcher];
     _inNewTabAnimation = NO;
+
+    _footerFullscreenProgress = 1.0;
+
     if (model && browserState)
       [self updateWithTabModel:model browserState:browserState];
   }
@@ -1205,10 +1213,7 @@
              webStateList:[_model webStateList]];
       StartBroadcastingMainContentUI(self, broadcaster);
 
-      _fullscreenUIUpdater = std::make_unique<FullscreenUIUpdater>(self);
       fullscreenController->AddObserver(_fullscreenUIUpdater.get());
-
-      fullscreenController->SetWebStateList([_model webStateList]);
     } else {
       StopBroadcastingToolbarUI(broadcaster);
       StopBroadcastingMainContentUI(broadcaster);
@@ -1218,8 +1223,6 @@
       [_webMainContentUIForwarder disconnect];
       _webMainContentUIForwarder = nil;
       fullscreenController->RemoveObserver(_fullscreenUIUpdater.get());
-      _fullscreenUIUpdater = nullptr;
-      fullscreenController->SetWebStateList(nullptr);
     }
   }
 }
@@ -1350,6 +1353,17 @@
   [self.legacyToolbarCoordinator browserStateDestroyed];
   [_model browserStateDestroyed];
 
+  if (base::FeatureList::IsEnabled(fullscreen::features::kNewFullscreen)) {
+    FullscreenController* fullscreenController =
+        FullscreenControllerFactory::GetInstance()->GetForBrowserState(
+            _browserState);
+    _foregroundTabAnimationFullscreenDisabler->Disconnect();
+    _foregroundTabAnimationFullscreenDisabler = nullptr;
+    fullscreenController->RemoveObserver(_fullscreenUIUpdater.get());
+    _fullscreenUIUpdater = nullptr;
+    fullscreenController->SetWebStateList(nullptr);
+  }
+
   // Disconnect child coordinators.
   [_activityServiceCoordinator disconnect];
   [_qrScannerCoordinator disconnect];
@@ -1916,6 +1930,20 @@
     _bookmarkModelBridge.reset(
         new bookmarks::BookmarkModelBridge(self, _bookmarkModel));
   }
+
+  if (base::FeatureList::IsEnabled(fullscreen::features::kNewFullscreen)) {
+    // Add a FullscreenUIUpdater for self.
+    FullscreenController* controller =
+        FullscreenControllerFactory::GetInstance()->GetForBrowserState(
+            _browserState);
+    _fullscreenUIUpdater = std::make_unique<FullscreenUIUpdater>(self);
+    // Crate the disabler for any new foreground tab animations in the tab model.
+    _foregroundTabAnimationFullscreenDisabler =
+        std::make_unique<NewForegroundTabFullscreenDisabler>(_model.webStateList,
+                                                            controller);
+    // Set the FullscreenController's WebStateList.
+    controller->SetWebStateList(_model.webStateList);
+  }
 }
 
 - (void)installFakeStatusBar {
diff --git a/ios/chrome/browser/ui/browser_view_controller_unittest.mm b/ios/chrome/browser/ui/browser_view_controller_unittest.mm
index 774d2f4..fc4cc38 100644
--- a/ios/chrome/browser/ui/browser_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view_controller_unittest.mm
@@ -315,6 +315,7 @@
 
   void TearDown() override {
     [[bvc_ view] removeFromSuperview];
+    [bvc_ browserStateDestroyed];
     [bvc_ shutdown];
 
     BlockCleanupTest::TearDown();
diff --git a/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h b/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h
index 38811eb..8da4092 100644
--- a/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h
+++ b/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h
@@ -6,22 +6,56 @@
 #define IOS_CHROME_BROWSER_UI_FULLSCREEN_ANIMATED_SCOPED_FULLSCREEN_DISABLER_H_
 
 #include "base/macros.h"
+#include "base/observer_list.h"
 
 class FullscreenController;
+class AnimatedScopedFullscreenDisablerObserver;
 
 // A helper object that increments FullscrenController's disabled counter for
-// its entire lifetime. Since the disabling is happening inside an animation
-// block, any UI changes related to Fullscreen being disabled will be animated.
+// its entire lifetime after calling StartAnimation().  Any UI updates resulting
+// from the incremented disable counter will be animated.
 class AnimatedScopedFullscreenDisabler {
  public:
   explicit AnimatedScopedFullscreenDisabler(FullscreenController* controller);
   ~AnimatedScopedFullscreenDisabler();
 
+  // Adds and removes AnimatedScopedFullscreenDisablerObservers.
+  void AddObserver(AnimatedScopedFullscreenDisablerObserver* observer);
+  void RemoveObserver(AnimatedScopedFullscreenDisablerObserver* observer);
+
+  // Starts the disabling the FullscreenController, animating any resulting UI
+  // changes.  The FullscreenController will then remain disabled until this
+  // disabler is deallocated.
+  void StartAnimation();
+
  private:
+  // Called when the fullscreen disabling animation has finished.
+  void OnAnimationFinished();
+
   // The FullscreenController being disabled by this object.
   FullscreenController* controller_;
+  // The AnimatedScopedFullscreenDisablerObservers.
+  base::ObserverList<AnimatedScopedFullscreenDisablerObserver> observers_;
+  // Whether this disabler is contributing to |controller_|'s disabled counter.
+  bool disabling_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(AnimatedScopedFullscreenDisabler);
 };
 
+// Obsever class for listening to animated fullscreen disabling events.
+class AnimatedScopedFullscreenDisablerObserver {
+ public:
+  explicit AnimatedScopedFullscreenDisablerObserver() = default;
+  virtual ~AnimatedScopedFullscreenDisablerObserver() = default;
+
+  // Called when the fullscreen disabling animation begins and ends.  If
+  // AnimatedScopedFullscreenDisabler::StartAnimation() is called and for a
+  // FullscreenController that is already disabled, these callbacks will not be
+  // sent.
+  virtual void FullscreenDisablingAnimationDidStart(
+      AnimatedScopedFullscreenDisabler* disabler){};
+  virtual void FullscreenDisablingAnimationDidFinish(
+      AnimatedScopedFullscreenDisabler* disabler){};
+};
+
 #endif  // IOS_CHROME_BROWSER_UI_FULLSCREEN_ANIMATED_SCOPED_FULLSCREEN_DISABLER_H_
diff --git a/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.mm b/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.mm
index 60ef64f..8dfef6b 100644
--- a/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.mm
+++ b/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.mm
@@ -16,13 +16,51 @@
     FullscreenController* controller)
     : controller_(controller) {
   DCHECK(controller_);
-  [UIView animateWithDuration:ios::material::kDuration1
-                   animations:^{
-                     controller_->IncrementDisabledCounter();
-                   }
-                   completion:nil];
 }
 
 AnimatedScopedFullscreenDisabler::~AnimatedScopedFullscreenDisabler() {
-  controller_->DecrementDisabledCounter();
+  if (disabling_)
+    controller_->DecrementDisabledCounter();
+}
+
+void AnimatedScopedFullscreenDisabler::AddObserver(
+    AnimatedScopedFullscreenDisablerObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void AnimatedScopedFullscreenDisabler::RemoveObserver(
+    AnimatedScopedFullscreenDisablerObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void AnimatedScopedFullscreenDisabler::StartAnimation() {
+  // StartAnimation() should be idempotent, so early return if this disabler has
+  // already incremented the disabled counter.
+  if (disabling_)
+    return;
+  disabling_ = true;
+
+  if (controller_->IsEnabled()) {
+    // Increment the disabled counter in an animation block if the controller is
+    // not already disabled.
+    for (auto& observer : observers_) {
+      observer.FullscreenDisablingAnimationDidStart(this);
+    }
+    [UIView animateWithDuration:ios::material::kDuration1
+        animations:^{
+          controller_->IncrementDisabledCounter();
+        }
+        completion:^(BOOL finished) {
+          OnAnimationFinished();
+        }];
+  } else {
+    // If |controller_| is already disabled, no animation is necessary.
+    controller_->IncrementDisabledCounter();
+  }
+}
+
+void AnimatedScopedFullscreenDisabler::OnAnimationFinished() {
+  for (auto& observer : observers_) {
+    observer.FullscreenDisablingAnimationDidFinish(this);
+  }
 }
diff --git a/ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.h b/ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.h
new file mode 100644
index 0000000..3260da9
--- /dev/null
+++ b/ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_UI_NEW_FOREGROUND_TAB_FULLSCREEN_DISABLER_H_
+#define IOS_CHROME_BROWSER_UI_NEW_FOREGROUND_TAB_FULLSCREEN_DISABLER_H_
+
+#include <memory>
+
+#import "ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
+
+class FullscreenController;
+class WebStateList;
+
+// Helper object that shows the toolbar when a new foreground tab is inserted.
+class NewForegroundTabFullscreenDisabler
+    : public AnimatedScopedFullscreenDisablerObserver,
+      public WebStateListObserver {
+ public:
+  NewForegroundTabFullscreenDisabler(WebStateList* web_state_list,
+                                     FullscreenController* controller);
+  ~NewForegroundTabFullscreenDisabler() override;
+
+  // Stops observing the WebStateList and disabling the FullscreenController.
+  void Disconnect();
+
+ private:
+  // AnimatedScopedFullscreenDisablerObserver:
+  void FullscreenDisablingAnimationDidFinish(
+      AnimatedScopedFullscreenDisabler* disabler) override;
+
+  // WebStateListObserver:
+  void WebStateInsertedAt(WebStateList* web_state_list,
+                          web::WebState* web_state,
+                          int index,
+                          bool activating) override;
+
+  // The WebStateList.
+  WebStateList* web_state_list_;
+  // The FullscreenController.
+  FullscreenController* controller_;
+  // The disabler used for new foreground tabs.
+  std::unique_ptr<AnimatedScopedFullscreenDisabler> disabler_;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_NEW_FOREGROUND_TAB_FULLSCREEN_DISABLER_H_
diff --git a/ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.mm b/ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.mm
new file mode 100644
index 0000000..b7513be
--- /dev/null
+++ b/ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.mm
@@ -0,0 +1,55 @@
+// 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.
+
+#import "ios/chrome/browser/ui/new_foreground_tab_fullscreen_disabler.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NewForegroundTabFullscreenDisabler::NewForegroundTabFullscreenDisabler(
+    WebStateList* web_state_list,
+    FullscreenController* controller)
+    : web_state_list_(web_state_list), controller_(controller) {
+  DCHECK(web_state_list_);
+  DCHECK(controller_);
+  web_state_list_->AddObserver(this);
+}
+
+NewForegroundTabFullscreenDisabler::~NewForegroundTabFullscreenDisabler() {
+  // Disconnect() is expected to be called before destruction.
+  DCHECK(!web_state_list_);
+  DCHECK(!controller_);
+  DCHECK(!disabler_);
+}
+
+void NewForegroundTabFullscreenDisabler::Disconnect() {
+  web_state_list_->RemoveObserver(this);
+  web_state_list_ = nullptr;
+  controller_ = nullptr;
+  disabler_ = nullptr;
+}
+
+void NewForegroundTabFullscreenDisabler::FullscreenDisablingAnimationDidFinish(
+    AnimatedScopedFullscreenDisabler* disabler) {
+  DCHECK_EQ(disabler_.get(), disabler);
+  disabler_ = nullptr;
+}
+
+void NewForegroundTabFullscreenDisabler::WebStateInsertedAt(
+    WebStateList* web_state_list,
+    web::WebState* web_state,
+    int index,
+    bool activating) {
+  DCHECK_EQ(web_state_list_, web_state_list);
+  if (activating && controller_->IsEnabled()) {
+    disabler_ = std::make_unique<AnimatedScopedFullscreenDisabler>(controller_);
+    disabler_->AddObserver(this);
+    disabler_->StartAnimation();
+  }
+}
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
index 181db4c..5b5ac33 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
@@ -434,6 +434,7 @@
           std::make_unique<AnimatedScopedFullscreenDisabler>(
               FullscreenControllerFactory::GetInstance()->GetForBrowserState(
                   browserState_));
+      animatedFullscreenDisabler_->StartAnimation();
     }
 
     inSwipe_ = YES;