[iOS] Create ObjC container for fullscreen disabler observers.

This allows the proper memory management of ObserverLists used in
Objective-C blocks.

Bug: 816342
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: Ic3b232303586ec99129ab999740454a0180833bb
Reviewed-on: https://chromium-review.googlesource.com/939101
Reviewed-by: Justin Cohen <justincohen@chromium.org>
Commit-Queue: Kurt Horimoto <kkhorimoto@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#539541}(cherry picked from commit 212b7610b1f563d5753d5ad9c4b55c4cf637d514)
Reviewed-on: https://chromium-review.googlesource.com/942241
Reviewed-by: Kurt Horimoto <kkhorimoto@chromium.org>
Cr-Commit-Position: refs/branch-heads/3325@{#632}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
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 8da4092..3571be4 100644
--- a/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h
+++ b/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h
@@ -10,6 +10,7 @@
 
 class FullscreenController;
 class AnimatedScopedFullscreenDisablerObserver;
+@class AnimatedScopedFullscreenDisablerObserverListContainer;
 
 // A helper object that increments FullscrenController's disabled counter for
 // its entire lifetime after calling StartAnimation().  Any UI updates resulting
@@ -29,13 +30,11 @@
   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_;
+  // A container object for the list of observers.
+  __strong AnimatedScopedFullscreenDisablerObserverListContainer*
+      observer_list_container_;
   // Whether this disabler is contributing to |controller_|'s disabled counter.
   bool disabling_ = false;
 
@@ -51,11 +50,16 @@
   // 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.
+  // sent.  If the disabler is destroyed before the animation can finish,
+  // FullscreenDisablingAnimationDidFinish() will not be received.
   virtual void FullscreenDisablingAnimationDidStart(
-      AnimatedScopedFullscreenDisabler* disabler){};
+      AnimatedScopedFullscreenDisabler* disabler) {}
   virtual void FullscreenDisablingAnimationDidFinish(
-      AnimatedScopedFullscreenDisabler* disabler){};
+      AnimatedScopedFullscreenDisabler* disabler) {}
+
+  // Called before the disabler is destructed.
+  virtual void AnimatedFullscreenDisablerDestroyed(
+      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 8dfef6b..cb3758d2 100644
--- a/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.mm
+++ b/ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.mm
@@ -12,25 +12,103 @@
 #error "This file requires ARC support."
 #endif
 
+#pragma mark - AnimatedScopedFullscreenDisablerObserverListContainer
+
+// An Objective-C container used to store observers.  This is used to allow
+// correct memory management for use in UIView animation blocks.
+@interface AnimatedScopedFullscreenDisablerObserverListContainer : NSObject {
+  // The AnimatedScopedFullscreenDisablerObservers.
+  base::ObserverList<AnimatedScopedFullscreenDisablerObserver> observers_;
+}
+
+// The disabler passed on initialization.
+@property(nonatomic, readonly) AnimatedScopedFullscreenDisabler* disabler;
+
+// Designated initializer for a container containing |disabler|'s observer list.
+- (instancetype)initWithDisabler:(AnimatedScopedFullscreenDisabler*)disabler
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+
+// Adds and removes observers.
+- (void)addObserver:(AnimatedScopedFullscreenDisablerObserver*)observer;
+- (void)removeObserver:(AnimatedScopedFullscreenDisablerObserver*)observer;
+
+// Notifies observers when the animation starts and finishes.
+- (void)onAnimationStarted;
+- (void)onAnimationFinished;
+- (void)onDisablerDestroyed;
+
+@end
+
+@implementation AnimatedScopedFullscreenDisablerObserverListContainer
+@synthesize disabler = _disabler;
+
+- (instancetype)initWithDisabler:(AnimatedScopedFullscreenDisabler*)disabler {
+  if (self = [super init]) {
+    _disabler = disabler;
+    DCHECK(_disabler);
+  }
+  return self;
+}
+
+- (const base::ObserverList<AnimatedScopedFullscreenDisablerObserver>&)
+    observers {
+  return observers_;
+}
+
+- (void)addObserver:(AnimatedScopedFullscreenDisablerObserver*)observer {
+  observers_.AddObserver(observer);
+}
+
+- (void)removeObserver:(AnimatedScopedFullscreenDisablerObserver*)observer {
+  observers_.RemoveObserver(observer);
+}
+
+- (void)onAnimationStarted {
+  for (auto& observer : observers_) {
+    observer.FullscreenDisablingAnimationDidStart(_disabler);
+  }
+}
+
+- (void)onAnimationFinished {
+  for (auto& observer : observers_) {
+    observer.FullscreenDisablingAnimationDidFinish(_disabler);
+  }
+}
+
+- (void)onDisablerDestroyed {
+  for (auto& observer : observers_) {
+    observer.AnimatedFullscreenDisablerDestroyed(_disabler);
+  }
+}
+
+@end
+
+#pragma mark - AnimatedScopedFullscreenDisabler
+
 AnimatedScopedFullscreenDisabler::AnimatedScopedFullscreenDisabler(
     FullscreenController* controller)
     : controller_(controller) {
   DCHECK(controller_);
+  observer_list_container_ =
+      [[AnimatedScopedFullscreenDisablerObserverListContainer alloc]
+          initWithDisabler:this];
 }
 
 AnimatedScopedFullscreenDisabler::~AnimatedScopedFullscreenDisabler() {
   if (disabling_)
     controller_->DecrementDisabledCounter();
+  [observer_list_container_ onDisablerDestroyed];
 }
 
 void AnimatedScopedFullscreenDisabler::AddObserver(
     AnimatedScopedFullscreenDisablerObserver* observer) {
-  observers_.AddObserver(observer);
+  [observer_list_container_ addObserver:observer];
 }
 
 void AnimatedScopedFullscreenDisabler::RemoveObserver(
     AnimatedScopedFullscreenDisablerObserver* observer) {
-  observers_.RemoveObserver(observer);
+  [observer_list_container_ removeObserver:observer];
 }
 
 void AnimatedScopedFullscreenDisabler::StartAnimation() {
@@ -43,24 +121,18 @@
   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);
-    }
+    [observer_list_container_ onAnimationStarted];
+    __weak AnimatedScopedFullscreenDisablerObserverListContainer*
+        weak_observer_list_container = observer_list_container_;
     [UIView animateWithDuration:ios::material::kDuration1
         animations:^{
           controller_->IncrementDisabledCounter();
         }
         completion:^(BOOL finished) {
-          OnAnimationFinished();
+          [weak_observer_list_container onAnimationFinished];
         }];
   } else {
     // If |controller_| is already disabled, no animation is necessary.
     controller_->IncrementDisabledCounter();
   }
 }
-
-void AnimatedScopedFullscreenDisabler::OnAnimationFinished() {
-  for (auto& observer : observers_) {
-    observer.FullscreenDisablingAnimationDidFinish(this);
-  }
-}