[iOS] Ignore pixel alignments when updating the MainContentUIState.

UIScrollView does not send a |-didEndDecelerating| to its delegate after
pixel alignments, so the MainContentUIState should not be considered
decelerating if the target contentOffset is less than a pixel away from
the current value.

This will correctly update the MainContentUIState's |scrolling|
property to NO, allowing the FullscreenScrollEndAnimator to start.

Bug: 800757
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: I4407bf34c4a6c5cb485bba01528c34cf9e86c156
Reviewed-on: https://chromium-review.googlesource.com/922888
Reviewed-by: Justin Cohen <justincohen@chromium.org>
Commit-Queue: Kurt Horimoto <kkhorimoto@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#537382}(cherry picked from commit 24ec7cd715920382c1ab368cfc98efafc9530dd1)
Reviewed-on: https://chromium-review.googlesource.com/934942
Reviewed-by: Kurt Horimoto <kkhorimoto@chromium.org>
Cr-Commit-Position: refs/branch-heads/3325@{#572}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/ios/chrome/browser/ui/main_content/main_content_ui_state.h b/ios/chrome/browser/ui/main_content/main_content_ui_state.h
index 22f4f0b..72fa1d27 100644
--- a/ios/chrome/browser/ui/main_content/main_content_ui_state.h
+++ b/ios/chrome/browser/ui/main_content/main_content_ui_state.h
@@ -56,11 +56,11 @@
 // Called when a drag event with |panGesture| begins.
 - (void)scrollViewWillBeginDraggingWithGesture:
     (nonnull UIPanGestureRecognizer*)panGesture;
-// Called when a drag event with |panGesture| ends.  |velocity| is the regidual
-// velocity from the scroll event.
+// Called when a drag event with |panGesture| ends.  |target| is the final
+// content offset resulting from the residual velocity of the drag event.
 - (void)scrollViewDidEndDraggingWithGesture:
             (nonnull UIPanGestureRecognizer*)panGesture
-                           residualVelocity:(CGPoint)velocity;
+                        targetContentOffset:(CGPoint)target;
 // Called when the scroll view stops decelerating.
 - (void)scrollViewDidEndDecelerating;
 // Called when the scroll view starts and ends zooming.
diff --git a/ios/chrome/browser/ui/main_content/main_content_ui_state.mm b/ios/chrome/browser/ui/main_content/main_content_ui_state.mm
index a1a72ae..aa77f8fd 100644
--- a/ios/chrome/browser/ui/main_content/main_content_ui_state.mm
+++ b/ios/chrome/browser/ui/main_content/main_content_ui_state.mm
@@ -113,15 +113,23 @@
 }
 
 - (void)scrollViewDidEndDraggingWithGesture:(UIPanGestureRecognizer*)panGesture
-                           residualVelocity:(CGPoint)velocity {
+                        targetContentOffset:(CGPoint)target {
   // It's possible during the side-swipe gesture for a drag to end on the scroll
   // view without a corresponding begin dragging call.  Early return if there
   // is no pan gesture from the begin call.
   if (!self.panGesture)
     return;
   DCHECK_EQ(panGesture, self.panGesture);
-  if (!AreCGFloatsEqual(velocity.y, 0.0))
+  // UIScrollView does not sent a |-scrollViewDidEndDecelerating:| signal after
+  // pixel alignments, so the state should not be considered decelerating if the
+  // target content offset is less than a pixel away from the current value.
+  CGFloat singlePixel = 1.0 / [UIScreen mainScreen].scale;
+  CGFloat delta = std::abs(self.state.yContentOffset - target.y);
+  if (delta > singlePixel) {
     self.state.decelerating = YES;
+  } else {
+    self.state.yContentOffset = target.y;
+  }
   self.state.dragging = NO;
   self.panGesture = nil;
 }
diff --git a/ios/chrome/browser/ui/main_content/main_content_ui_state_unittest.mm b/ios/chrome/browser/ui/main_content/main_content_ui_state_unittest.mm
index d368c02..708b9a0 100644
--- a/ios/chrome/browser/ui/main_content/main_content_ui_state_unittest.mm
+++ b/ios/chrome/browser/ui/main_content/main_content_ui_state_unittest.mm
@@ -50,10 +50,27 @@
   EXPECT_TRUE(state().scrolling);
   EXPECT_TRUE(state().dragging);
   [updater() scrollViewDidEndDraggingWithGesture:pan
-                                residualVelocity:CGPointMake(0, 5)];
+                             targetContentOffset:CGPointMake(0, 5)];
   EXPECT_TRUE(state().scrolling);
   EXPECT_FALSE(state().dragging);
   [updater() scrollViewDidEndDecelerating];
   EXPECT_FALSE(state().scrolling);
   EXPECT_FALSE(state().dragging);
 }
+
+// Tests that pixel alignment don't count as residual deceleration.
+TEST_F(MainContentUIStateUpdaterTest, IgnorePixelAligntment) {
+  UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc] init];
+  const CGFloat kSinglePixel = 1.0 / [UIScreen mainScreen].scale;
+  const CGPoint kUnalignedOffset = CGPointMake(0.0, kSinglePixel / 2.0);
+  ASSERT_FALSE(state().scrolling);
+  ASSERT_FALSE(state().dragging);
+  [updater() scrollViewWillBeginDraggingWithGesture:pan];
+  EXPECT_TRUE(state().scrolling);
+  EXPECT_TRUE(state().dragging);
+  [updater() scrollViewDidScrollToOffset:kUnalignedOffset];
+  [updater() scrollViewDidEndDraggingWithGesture:pan
+                             targetContentOffset:CGPointZero];
+  EXPECT_FALSE(state().scrolling);
+  EXPECT_FALSE(state().dragging);
+}
diff --git a/ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.mm b/ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.mm
index a163285..452f128f 100644
--- a/ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.mm
+++ b/ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.mm
@@ -149,7 +149,7 @@
                      targetContentOffset:(inout CGPoint*)targetContentOffset {
   [self.updater
       scrollViewDidEndDraggingWithGesture:self.proxy.panGestureRecognizer
-                         residualVelocity:velocity];
+                      targetContentOffset:*targetContentOffset];
 }
 
 - (void)webViewScrollViewDidEndDecelerating: