summaryrefslogtreecommitdiff
path: root/chromium/cc/input
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-02-13 15:05:36 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-02-14 10:33:47 +0000
commite684a3455bcc29a6e3e66a004e352dea4e1141e7 (patch)
treed55b4003bde34d7d05f558f02cfd82b2a66a7aac /chromium/cc/input
parent2b94bfe47ccb6c08047959d1c26e392919550e86 (diff)
downloadqtwebengine-chromium-e684a3455bcc29a6e3e66a004e352dea4e1141e7.tar.gz
BASELINE: Update Chromium to 72.0.3626.110 and Ninja to 1.9.0
Change-Id: Ic57220b00ecc929a893c91f5cc552f5d3e99e922 Reviewed-by: Michael Brüning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/cc/input')
-rw-r--r--chromium/cc/input/browser_controls_offset_manager.cc41
-rw-r--r--chromium/cc/input/browser_controls_offset_manager.h10
-rw-r--r--chromium/cc/input/browser_controls_offset_manager_client.h1
-rw-r--r--chromium/cc/input/browser_controls_offset_manager_unittest.cc71
-rw-r--r--chromium/cc/input/browser_controls_state.h3
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason.cc4
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason.h6
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason_unittest.cc5
-rw-r--r--chromium/cc/input/scroll_snap_data.cc172
-rw-r--r--chromium/cc/input/scroll_snap_data.h38
-rw-r--r--chromium/cc/input/scroll_snap_data_unittest.cc81
-rw-r--r--chromium/cc/input/snap_fling_controller.h4
-rw-r--r--chromium/cc/input/snap_selection_strategy.cc151
-rw-r--r--chromium/cc/input/snap_selection_strategy.h170
14 files changed, 603 insertions, 154 deletions
diff --git a/chromium/cc/input/browser_controls_offset_manager.cc b/chromium/cc/input/browser_controls_offset_manager.cc
index e55bf940440..afbe9770086 100644
--- a/chromium/cc/input/browser_controls_offset_manager.cc
+++ b/chromium/cc/input/browser_controls_offset_manager.cc
@@ -38,6 +38,7 @@ BrowserControlsOffsetManager::BrowserControlsOffsetManager(
float controls_show_threshold,
float controls_hide_threshold)
: client_(client),
+ animation_initialized_(false),
animation_start_value_(0.f),
animation_stop_value_(0.f),
animation_direction_(NO_ANIMATION),
@@ -47,7 +48,8 @@ BrowserControlsOffsetManager::BrowserControlsOffsetManager(
baseline_bottom_content_offset_(0.f),
controls_show_threshold_(controls_hide_threshold),
controls_hide_threshold_(controls_show_threshold),
- pinch_gesture_active_(false) {
+ pinch_gesture_active_(false),
+ constraint_changed_since_commit_(false) {
CHECK(client_);
}
@@ -92,6 +94,17 @@ void BrowserControlsOffsetManager::UpdateBrowserControlsState(
DCHECK(!(constraints == BrowserControlsState::kHidden &&
current == BrowserControlsState::kShown));
+ TRACE_EVENT2("cc", "BrowserControlsOffsetManager::UpdateBrowserControlsState",
+ "constraints", static_cast<int>(constraints), "current",
+ static_cast<int>(current));
+
+ // If the constraints have changed we need to inform Blink about it since
+ // that'll affect main thread scrolling as well as layout.
+ if (permitted_state_ != constraints) {
+ constraint_changed_since_commit_ = true;
+ client_->SetNeedsCommit();
+ }
+
permitted_state_ = constraints;
// Don't do anything if it doesn't matter which state the controls are in.
@@ -105,6 +118,7 @@ void BrowserControlsOffsetManager::UpdateBrowserControlsState(
current == BrowserControlsState::kHidden)
final_shown_ratio = 0.f;
if (final_shown_ratio == TopControlsShownRatio()) {
+ TRACE_EVENT_INSTANT0("cc", "Ratio Unchanged", TRACE_EVENT_SCOPE_THREAD);
ResetAnimations();
return;
}
@@ -113,10 +127,18 @@ void BrowserControlsOffsetManager::UpdateBrowserControlsState(
SetupAnimation(final_shown_ratio ? SHOWING_CONTROLS : HIDING_CONTROLS);
} else {
ResetAnimations();
- // We depend on the main thread to push the new ratio. crbug.com/754346 .
+ client_->SetCurrentBrowserControlsShownRatio(final_shown_ratio);
}
}
+BrowserControlsState BrowserControlsOffsetManager::PullConstraintForMainThread(
+ bool* out_changed_since_commit) {
+ DCHECK(out_changed_since_commit);
+ *out_changed_since_commit = constraint_changed_since_commit_;
+ constraint_changed_since_commit_ = false;
+ return permitted_state_;
+}
+
void BrowserControlsOffsetManager::ScrollBegin() {
if (pinch_gesture_active_)
return;
@@ -197,6 +219,16 @@ gfx::Vector2dF BrowserControlsOffsetManager::Animate(
if (!has_animation() || !client_->HaveRootScrollNode())
return gfx::Vector2dF();
+ if (!animation_initialized_) {
+ // Setup the animation start and time here so that they use the same clock
+ // as frame times. This is helpful for tests that mock time.
+ animation_start_time_ = monotonic_time;
+ animation_stop_time_ =
+ animation_start_time_ +
+ base::TimeDelta::FromMilliseconds(kShowHideMaxDurationMs);
+ animation_initialized_ = true;
+ }
+
float old_offset = ContentTopOffset();
float new_ratio = gfx::Tween::ClampedFloatValueBetween(
monotonic_time, animation_start_time_, animation_start_value_,
@@ -211,6 +243,7 @@ gfx::Vector2dF BrowserControlsOffsetManager::Animate(
}
void BrowserControlsOffsetManager::ResetAnimations() {
+ animation_initialized_ = false;
animation_start_time_ = base::TimeTicks();
animation_start_value_ = 0.f;
animation_stop_time_ = base::TimeTicks();
@@ -234,13 +267,9 @@ void BrowserControlsOffsetManager::SetupAnimation(
return;
}
- animation_start_time_ = base::TimeTicks::Now();
animation_start_value_ = TopControlsShownRatio();
const float max_ending_ratio = (direction == SHOWING_CONTROLS ? 1 : -1);
- animation_stop_time_ =
- animation_start_time_ +
- base::TimeDelta::FromMilliseconds(kShowHideMaxDurationMs);
animation_stop_value_ = animation_start_value_ + max_ending_ratio;
animation_direction_ = direction;
diff --git a/chromium/cc/input/browser_controls_offset_manager.h b/chromium/cc/input/browser_controls_offset_manager.h
index ae4d9e4268f..8d0435d2808 100644
--- a/chromium/cc/input/browser_controls_offset_manager.h
+++ b/chromium/cc/input/browser_controls_offset_manager.h
@@ -53,6 +53,9 @@ class CC_EXPORT BrowserControlsOffsetManager {
BrowserControlsState current,
bool animate);
+ BrowserControlsState PullConstraintForMainThread(
+ bool* out_changed_since_commit);
+
void ScrollBegin();
gfx::Vector2dF ScrollBy(const gfx::Vector2dF& pending_delta);
void ScrollEnd();
@@ -81,6 +84,9 @@ class CC_EXPORT BrowserControlsOffsetManager {
// The client manages the lifecycle of this.
BrowserControlsOffsetManagerClient* client_;
+ // animation_initialized_ tracks if we've initialized the start and end
+ // times since that must happen at a BeginFrame.
+ bool animation_initialized_;
base::TimeTicks animation_start_time_;
float animation_start_value_;
base::TimeTicks animation_stop_time_;
@@ -106,6 +112,10 @@ class CC_EXPORT BrowserControlsOffsetManager {
bool pinch_gesture_active_;
+ // Used to track whether the constraint has changed and we need up reflect
+ // the changes to Blink.
+ bool constraint_changed_since_commit_;
+
DISALLOW_COPY_AND_ASSIGN(BrowserControlsOffsetManager);
};
diff --git a/chromium/cc/input/browser_controls_offset_manager_client.h b/chromium/cc/input/browser_controls_offset_manager_client.h
index df1a742669d..5a2ce4cb5dc 100644
--- a/chromium/cc/input/browser_controls_offset_manager_client.h
+++ b/chromium/cc/input/browser_controls_offset_manager_client.h
@@ -15,6 +15,7 @@ class CC_EXPORT BrowserControlsOffsetManagerClient {
virtual float CurrentBrowserControlsShownRatio() const = 0;
virtual void DidChangeBrowserControlsPosition() = 0;
virtual bool HaveRootScrollNode() const = 0;
+ virtual void SetNeedsCommit() = 0;
protected:
virtual ~BrowserControlsOffsetManagerClient() {}
diff --git a/chromium/cc/input/browser_controls_offset_manager_unittest.cc b/chromium/cc/input/browser_controls_offset_manager_unittest.cc
index 3caad9cd2ec..3a8cfdc7df8 100644
--- a/chromium/cc/input/browser_controls_offset_manager_unittest.cc
+++ b/chromium/cc/input/browser_controls_offset_manager_unittest.cc
@@ -71,6 +71,8 @@ class MockBrowserControlsOffsetManagerClient
return top_controls_shown_ratio_;
}
+ void SetNeedsCommit() override {}
+
LayerImpl* rootScrollLayer() { return root_scroll_layer_.get(); }
BrowserControlsOffsetManager* manager() {
@@ -167,7 +169,12 @@ TEST(BrowserControlsOffsetManagerTest, PartialShownHideAnimation) {
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->TopControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -199,7 +206,12 @@ TEST(BrowserControlsOffsetManagerTest,
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->BottomControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -229,7 +241,12 @@ TEST(BrowserControlsOffsetManagerTest, PartialShownShowAnimation) {
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->TopControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -256,7 +273,12 @@ TEST(BrowserControlsOffsetManagerTest,
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->BottomControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -283,7 +305,12 @@ TEST(BrowserControlsOffsetManagerTest,
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->TopControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -310,7 +337,12 @@ TEST(BrowserControlsOffsetManagerTest,
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->TopControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -341,7 +373,12 @@ TEST(BrowserControlsOffsetManagerTest,
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->TopControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -372,7 +409,12 @@ TEST(BrowserControlsOffsetManagerTest,
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->TopControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -435,7 +477,12 @@ TEST(BrowserControlsOffsetManagerTest, PinchBeginStartsAnimationIfNecessary) {
EXPECT_TRUE(manager->has_animation());
base::TimeTicks time = base::TimeTicks::Now();
- float previous;
+
+ // First animate will establish the animaion.
+ float previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->TopControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
@@ -456,6 +503,12 @@ TEST(BrowserControlsOffsetManagerTest, PinchBeginStartsAnimationIfNecessary) {
EXPECT_TRUE(manager->has_animation());
time = base::TimeTicks::Now();
+
+ // First animate will establish the animaion.
+ previous = manager->TopControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->TopControlsShownRatio(), previous);
+
while (manager->has_animation()) {
previous = manager->TopControlsShownRatio();
time = base::TimeDelta::FromMicroseconds(100) + time;
diff --git a/chromium/cc/input/browser_controls_state.h b/chromium/cc/input/browser_controls_state.h
index 38310496c5a..8b2f93f4171 100644
--- a/chromium/cc/input/browser_controls_state.h
+++ b/chromium/cc/input/browser_controls_state.h
@@ -8,6 +8,7 @@
namespace cc {
enum class BrowserControlsState { kShown = 1, kHidden = 2, kBoth = 3 };
-}
+
+} // namespace cc
#endif // CC_INPUT_BROWSER_CONTROLS_STATE_H_
diff --git a/chromium/cc/input/main_thread_scrolling_reason.cc b/chromium/cc/input/main_thread_scrolling_reason.cc
index a85b60f97a7..728708ce776 100644
--- a/chromium/cc/input/main_thread_scrolling_reason.cc
+++ b/chromium/cc/input/main_thread_scrolling_reason.cc
@@ -5,7 +5,7 @@
#include "cc/input/main_thread_scrolling_reason.h"
#include "base/stl_util.h"
-#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/traced_value.h"
namespace cc {
@@ -47,8 +47,6 @@ void MainThreadScrollingReason::AddToTracedValue(
traced_value.AppendString("Has transform and LCD text");
if (reasons & kBackgroundNotOpaqueInRectAndLCDText)
traced_value.AppendString("Background is not opaque in rect and LCD text");
- if (reasons & kHasBorderRadius)
- traced_value.AppendString("Has border radius");
if (reasons & kHasClipRelatedProperty)
traced_value.AppendString("Has clip related property");
if (reasons & kHasBoxShadowFromNonRootLayer)
diff --git a/chromium/cc/input/main_thread_scrolling_reason.h b/chromium/cc/input/main_thread_scrolling_reason.h
index cf2deff0ce8..732c3fa904b 100644
--- a/chromium/cc/input/main_thread_scrolling_reason.h
+++ b/chromium/cc/input/main_thread_scrolling_reason.h
@@ -46,7 +46,6 @@ struct CC_EXPORT MainThreadScrollingReason {
kHasOpacityAndLCDText = 1 << 16,
kHasTransformAndLCDText = 1 << 17,
kBackgroundNotOpaqueInRectAndLCDText = 1 << 18,
- kHasBorderRadius = 1 << 19,
kHasClipRelatedProperty = 1 << 20,
kHasBoxShadowFromNonRootLayer = 1 << 21,
kIsNotStackingContextAndLCDText = 1 << 22,
@@ -72,9 +71,8 @@ struct CC_EXPORT MainThreadScrollingReason {
static const uint32_t kNonCompositedReasons =
kHasOpacityAndLCDText | kHasTransformAndLCDText |
- kBackgroundNotOpaqueInRectAndLCDText | kHasBorderRadius |
- kHasClipRelatedProperty | kHasBoxShadowFromNonRootLayer |
- kIsNotStackingContextAndLCDText;
+ kBackgroundNotOpaqueInRectAndLCDText | kHasClipRelatedProperty |
+ kHasBoxShadowFromNonRootLayer | kIsNotStackingContextAndLCDText;
// Returns true if the given MainThreadScrollingReason can be set by the main
// thread.
diff --git a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc
index 9f8e6b641d8..7560dda71d9 100644
--- a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc
+++ b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc
@@ -16,13 +16,14 @@ TEST_F(MainThreadScrollingReasonTest, AsText) {
"Has background-attachment:fixed,"
"Has non-layer viewport-constrained objects,"
"Threaded scrolling is disabled,"
- "Scrollbar scrolling,Page overlay,"
+ "Scrollbar scrolling,"
+ "Page overlay,"
"Handling scroll from main thread,"
"Custom scrollbar scrolling,"
"Has opacity and LCD text,"
"Has transform and LCD text,"
"Background is not opaque in rect and LCD text,"
- "Has border radius,Has clip related property,"
+ "Has clip related property,"
"Has box shadow from non-root layer,"
"Is not stacking context and LCD text,"
"Non fast scrollable region,"
diff --git a/chromium/cc/input/scroll_snap_data.cc b/chromium/cc/input/scroll_snap_data.cc
index 9e873aea523..3fc17474fc7 100644
--- a/chromium/cc/input/scroll_snap_data.cc
+++ b/chromium/cc/input/scroll_snap_data.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "cc/input/scroll_snap_data.h"
+#include "cc/input/snap_selection_strategy.h"
#include <algorithm>
#include <cmath>
@@ -11,8 +12,8 @@ namespace cc {
namespace {
bool IsMutualVisible(const SnapSearchResult& a, const SnapSearchResult& b) {
- return a.visible_range().Contains(b.snap_offset()) &&
- b.visible_range().Contains(a.snap_offset());
+ return a.visible_range().Contains(gfx::RangeF(b.snap_offset())) &&
+ b.visible_range().Contains(gfx::RangeF(a.snap_offset()));
}
bool SnappableOnAxis(const SnapAreaData& area, SearchAxis search_axis) {
@@ -21,34 +22,36 @@ bool SnappableOnAxis(const SnapAreaData& area, SearchAxis search_axis) {
: area.scroll_snap_align.alignment_block != SnapAlignment::kNone;
}
-} // namespace
-
-bool SnapVisibleRange::Contains(float value) const {
- if (start_ < end_)
- return start_ <= value && value <= end_;
- return end_ <= value && value <= start_;
+void SetOrUpdateResult(const SnapSearchResult& candidate,
+ base::Optional<SnapSearchResult>* result) {
+ if (result->has_value())
+ result->value().Union(candidate);
+ else
+ *result = candidate;
}
-SnapSearchResult::SnapSearchResult(float offset, const SnapVisibleRange& range)
+} // namespace
+
+SnapSearchResult::SnapSearchResult(float offset, const gfx::RangeF& range)
: snap_offset_(offset) {
set_visible_range(range);
}
-void SnapSearchResult::set_visible_range(const SnapVisibleRange& range) {
+void SnapSearchResult::set_visible_range(const gfx::RangeF& range) {
DCHECK(range.start() <= range.end());
visible_range_ = range;
}
void SnapSearchResult::Clip(float max_snap, float max_visible) {
snap_offset_ = std::max(std::min(snap_offset_, max_snap), 0.0f);
- visible_range_ = SnapVisibleRange(
- std::max(std::min(visible_range_.start(), max_visible), 0.0f),
- std::max(std::min(visible_range_.end(), max_visible), 0.0f));
+ visible_range_ =
+ gfx::RangeF(std::max(std::min(visible_range_.start(), max_visible), 0.0f),
+ std::max(std::min(visible_range_.end(), max_visible), 0.0f));
}
void SnapSearchResult::Union(const SnapSearchResult& other) {
DCHECK(snap_offset_ == other.snap_offset_);
- visible_range_ = SnapVisibleRange(
+ visible_range_ = gfx::RangeF(
std::min(visible_range_.start(), other.visible_range_.start()),
std::max(visible_range_.end(), other.visible_range_.end()));
}
@@ -88,13 +91,14 @@ void SnapContainerData::AddSnapAreaData(SnapAreaData snap_area_data) {
}
bool SnapContainerData::FindSnapPosition(
- const gfx::ScrollOffset& current_position,
- bool should_snap_on_x,
- bool should_snap_on_y,
+ const SnapSelectionStrategy& strategy,
gfx::ScrollOffset* snap_position) const {
+ gfx::ScrollOffset base_position = strategy.base_position();
SnapAxis axis = scroll_snap_type_.axis;
- should_snap_on_x &= (axis == SnapAxis::kX || axis == SnapAxis::kBoth);
- should_snap_on_y &= (axis == SnapAxis::kY || axis == SnapAxis::kBoth);
+ bool should_snap_on_x = strategy.ShouldSnapOnX() &&
+ (axis == SnapAxis::kX || axis == SnapAxis::kBoth);
+ bool should_snap_on_y = strategy.ShouldSnapOnY() &&
+ (axis == SnapAxis::kY || axis == SnapAxis::kBoth);
if (!should_snap_on_x && !should_snap_on_y)
return false;
@@ -102,18 +106,18 @@ bool SnapContainerData::FindSnapPosition(
// A region that includes every reachable scroll position.
gfx::RectF scrollable_region(0, 0, max_position_.x(), max_position_.y());
if (should_snap_on_x) {
- // Start from current offset in the cross axis and assume it's always
+ // Start from current position in the cross axis and assume it's always
// visible.
SnapSearchResult initial_snap_position_y = {
- current_position.y(), SnapVisibleRange(0, max_position_.x())};
- closest_x = FindClosestValidArea(SearchAxis::kX, current_position.x(),
- initial_snap_position_y);
+ base_position.y(), gfx::RangeF(0, max_position_.x())};
+ closest_x =
+ FindClosestValidArea(SearchAxis::kX, strategy, initial_snap_position_y);
}
if (should_snap_on_y) {
SnapSearchResult initial_snap_position_x = {
- current_position.x(), SnapVisibleRange(0, max_position_.y())};
- closest_y = FindClosestValidArea(SearchAxis::kY, current_position.y(),
- initial_snap_position_x);
+ base_position.x(), gfx::RangeF(0, max_position_.y())};
+ closest_y =
+ FindClosestValidArea(SearchAxis::kY, strategy, initial_snap_position_x);
}
if (!closest_x.has_value() && !closest_y.has_value())
@@ -126,17 +130,18 @@ bool SnapContainerData::FindSnapPosition(
if (closest_x.has_value() && closest_y.has_value() &&
!IsMutualVisible(closest_x.value(), closest_y.value())) {
bool candidate_on_x_axis_is_closer =
- std::abs(closest_x.value().snap_offset() - current_position.x()) <=
- std::abs(closest_y.value().snap_offset() - current_position.y());
- if (candidate_on_x_axis_is_closer)
- closest_y = FindClosestValidArea(SearchAxis::kY, current_position.y(),
- closest_x.value());
- else
- closest_x = FindClosestValidArea(SearchAxis::kX, current_position.x(),
- closest_y.value());
+ std::abs(closest_x.value().snap_offset() - base_position.x()) <=
+ std::abs(closest_y.value().snap_offset() - base_position.y());
+ if (candidate_on_x_axis_is_closer) {
+ closest_y =
+ FindClosestValidArea(SearchAxis::kY, strategy, closest_x.value());
+ } else {
+ closest_x =
+ FindClosestValidArea(SearchAxis::kX, strategy, closest_y.value());
+ }
}
- *snap_position = current_position;
+ *snap_position = strategy.current_position();
if (closest_x.has_value())
snap_position->set_x(closest_x.value().snap_offset());
if (closest_y.has_value())
@@ -146,13 +151,36 @@ bool SnapContainerData::FindSnapPosition(
base::Optional<SnapSearchResult> SnapContainerData::FindClosestValidArea(
SearchAxis axis,
- float current_offset,
+ const SnapSelectionStrategy& strategy,
const SnapSearchResult& cros_axis_snap_result) const {
- base::Optional<SnapSearchResult> result;
- base::Optional<SnapSearchResult> inplace;
- // The valid snap offsets immediately before and after the current offset.
+ // The search result from the snap area that's closest to the search origin.
+ base::Optional<SnapSearchResult> closest;
+ // The search result with the intended position if it makes a snap area cover
+ // the snapport.
+ base::Optional<SnapSearchResult> covering;
+ // The search result with the current position as a backup in case no other
+ // valid snap position exists.
+ base::Optional<SnapSearchResult> current;
+
+ // The valid snap positions immediately before and after the current position.
float prev = std::numeric_limits<float>::lowest();
float next = std::numeric_limits<float>::max();
+
+ // The current position before the scroll or snap happens. If no other snap
+ // position exists, this would become a backup option.
+ float current_position = axis == SearchAxis::kX
+ ? strategy.current_position().x()
+ : strategy.current_position().y();
+ // The intended position of the scroll operation if there's no snap. This
+ // scroll position becomes the covering candidate if there is a snap area that
+ // fully covers the snapport if this position is scrolled to.
+ float intended_position = axis == SearchAxis::kX
+ ? strategy.intended_position().x()
+ : strategy.intended_position().y();
+ // The position from which we search for the closest snap position.
+ float base_position = axis == SearchAxis::kX ? strategy.base_position().x()
+ : strategy.base_position().y();
+
float smallest_distance =
axis == SearchAxis::kX ? proximity_range_.x() : proximity_range_.y();
for (const SnapAreaData& area : snap_area_list_) {
@@ -160,52 +188,54 @@ base::Optional<SnapSearchResult> SnapContainerData::FindClosestValidArea(
continue;
SnapSearchResult candidate = GetSnapSearchResult(axis, area);
- if (IsSnapportCoveredOnAxis(axis, current_offset, area.rect)) {
- // Since snap area is currently covering the snapport, we consider the
- // current offset as a valid snap position.
- SnapSearchResult inplace_candidate = candidate;
- inplace_candidate.set_snap_offset(current_offset);
- if (IsMutualVisible(inplace_candidate, cros_axis_snap_result)) {
- // If we've already found a valid overflowing area before, we enlarge
- // the area's visible region.
- if (inplace.has_value())
- inplace.value().Union(inplace_candidate);
- else
- inplace = inplace_candidate;
- // Even if a snap area covers the snapport, we need to continue this
- // search to find previous and next snap positions and also to have
- // alternative snap candidates if this inplace candidate is ultimately
- // rejected. And this covering snap area has its own alignment that may
- // generates a snap position rejecting the current inplace candidate.
- }
+ if (IsSnapportCoveredOnAxis(axis, intended_position, area.rect)) {
+ // Since snap area will cover the snapport, we consider the intended
+ // position as a valid snap position.
+ SnapSearchResult covering_candidate = candidate;
+ covering_candidate.set_snap_offset(intended_position);
+ if (IsMutualVisible(covering_candidate, cros_axis_snap_result))
+ SetOrUpdateResult(covering_candidate, &covering);
+ // Even if a snap area covers the snapport, we need to continue this
+ // search to find previous and next snap positions and also to have
+ // alternative snap candidates if this covering candidate is ultimately
+ // rejected. And this covering snap area has its own alignment that may
+ // generates a snap position rejecting the current inplace candidate.
}
if (!IsMutualVisible(candidate, cros_axis_snap_result))
continue;
- float distance = std::abs(candidate.snap_offset() - current_offset);
+ if (!strategy.IsValidSnapPosition(axis, candidate.snap_offset())) {
+ if (candidate.snap_offset() == current_position &&
+ scroll_snap_type_.strictness == SnapStrictness::kMandatory) {
+ SetOrUpdateResult(candidate, &current);
+ }
+ continue;
+ }
+ float distance = std::abs(candidate.snap_offset() - base_position);
if (distance < smallest_distance) {
smallest_distance = distance;
- result = candidate;
+ closest = candidate;
}
- if (candidate.snap_offset() < current_offset &&
+ if (candidate.snap_offset() < intended_position &&
candidate.snap_offset() > prev)
prev = candidate.snap_offset();
- if (candidate.snap_offset() > current_offset &&
+ if (candidate.snap_offset() > intended_position &&
candidate.snap_offset() < next)
next = candidate.snap_offset();
}
// According to the spec [1], if the snap area is covering the snapport, the
- // scroll offset is a valid snap position only if the distance between the
+ // scroll position is a valid snap position only if the distance between the
// geometrically previous and subsequent snap positions in that axis is larger
// than size of the snapport in that axis.
// [1] https://drafts.csswg.org/css-scroll-snap-1/#snap-overflow
float size = axis == SearchAxis::kX ? rect_.width() : rect_.height();
- if (inplace.has_value() &&
- (prev == std::numeric_limits<float>::lowest() ||
- next == std::numeric_limits<float>::max() || next - prev > size)) {
- return inplace;
+ if (prev != std::numeric_limits<float>::lowest() &&
+ next != std::numeric_limits<float>::max() && next - prev <= size) {
+ covering = base::nullopt;
}
- return result;
+ const base::Optional<SnapSearchResult>& picked =
+ strategy.PickBestResult(closest, covering);
+ return picked.has_value() ? picked : current;
}
SnapSearchResult SnapContainerData::GetSnapSearchResult(
@@ -213,8 +243,8 @@ SnapSearchResult SnapContainerData::GetSnapSearchResult(
const SnapAreaData& area) const {
SnapSearchResult result;
if (axis == SearchAxis::kX) {
- result.set_visible_range(SnapVisibleRange(area.rect.y() - rect_.bottom(),
- area.rect.bottom() - rect_.y()));
+ result.set_visible_range(gfx::RangeF(area.rect.y() - rect_.bottom(),
+ area.rect.bottom() - rect_.y()));
// https://www.w3.org/TR/css-scroll-snap-1/#scroll-snap-align
switch (area.scroll_snap_align.alignment_inline) {
case SnapAlignment::kStart:
@@ -232,8 +262,8 @@ SnapSearchResult SnapContainerData::GetSnapSearchResult(
}
result.Clip(max_position_.x(), max_position_.y());
} else {
- result.set_visible_range(SnapVisibleRange(area.rect.x() - rect_.right(),
- area.rect.right() - rect_.x()));
+ result.set_visible_range(gfx::RangeF(area.rect.x() - rect_.right(),
+ area.rect.right() - rect_.x()));
switch (area.scroll_snap_align.alignment_block) {
case SnapAlignment::kStart:
result.set_snap_offset(area.rect.y() - rect_.y());
diff --git a/chromium/cc/input/scroll_snap_data.h b/chromium/cc/input/scroll_snap_data.h
index c18f7374229..3057acf339e 100644
--- a/chromium/cc/input/scroll_snap_data.h
+++ b/chromium/cc/input/scroll_snap_data.h
@@ -11,9 +11,12 @@
#include "cc/cc_export.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/scroll_offset.h"
+#include "ui/gfx/range/range_f.h"
namespace cc {
+class SnapSelectionStrategy;
+
// See https://www.w3.org/TR/css-scroll-snap-1/#snap-axis
enum class SnapAxis : unsigned {
kBoth,
@@ -85,28 +88,13 @@ struct ScrollSnapAlign {
SnapAlignment alignment_inline;
};
-// We should really use gfx::RangeF. However, it includes windows.h which would
-// bring in complexity to the compilation. https://crbug.com/855717
-class SnapVisibleRange {
- public:
- SnapVisibleRange() {}
- SnapVisibleRange(float start, float end) : start_(start), end_(end) {}
- bool Contains(float value) const;
- float start() const { return start_; }
- float end() const { return end_; }
-
- private:
- float start_;
- float end_;
-};
-
// This class includes snap offset and visible range needed to perform a snap
// operation on one axis for a specific area. The data can be used to determine
// whether this snap area provides a valid snap position for the current scroll.
class SnapSearchResult {
public:
SnapSearchResult() {}
- SnapSearchResult(float offset, const SnapVisibleRange& range);
+ SnapSearchResult(float offset, const gfx::RangeF& range);
// Clips the |snap_offset| between 0 and |max_snap|. And clips the
// |visible_range| between 0 and |max_visible|.
void Clip(float max_snap, float max_visible);
@@ -118,15 +106,15 @@ class SnapSearchResult {
float snap_offset() const { return snap_offset_; }
void set_snap_offset(float offset) { snap_offset_ = offset; }
- SnapVisibleRange visible_range() const { return visible_range_; }
- void set_visible_range(const SnapVisibleRange& range);
+ gfx::RangeF visible_range() const { return visible_range_; }
+ void set_visible_range(const gfx::RangeF& range);
private:
float snap_offset_;
// This is the range on the cross axis, within which the SnapArea generating
// this |snap_offset| is visible. We expect the range to be in order (as
- // opposed to reversed), i.e., start < end.
- SnapVisibleRange visible_range_;
+ // opposed to reversed), i.e., start() < end().
+ gfx::RangeF visible_range_;
};
// Snap area is a bounding box that could be snapped to when a scroll happens in
@@ -196,9 +184,7 @@ class CC_EXPORT SnapContainerData {
return !(*this == other);
}
- bool FindSnapPosition(const gfx::ScrollOffset& current_position,
- bool should_snap_on_x,
- bool should_snap_on_y,
+ bool FindSnapPosition(const SnapSelectionStrategy& strategy,
gfx::ScrollOffset* snap_position) const;
void AddSnapAreaData(SnapAreaData snap_area_data);
@@ -222,8 +208,8 @@ class CC_EXPORT SnapContainerData {
gfx::ScrollOffset proximity_range() const { return proximity_range_; }
private:
- // Finds the best SnapArea candidate that minimizes the distance between
- // current and candidate positions, while satisfying two invariants:
+ // Finds the best SnapArea candidate that's optimal for the given selection
+ // strategy, while satisfying two invariants:
// - |candidate.snap_offset| is within |cross_axis_snap_result|'s visible
// range on |axis|.
// - |cross_axis_snap_result.snap_offset| is within |candidate|'s visible
@@ -234,7 +220,7 @@ class CC_EXPORT SnapContainerData {
// |snap_offset| and its visible range on the cross axis.
base::Optional<SnapSearchResult> FindClosestValidArea(
SearchAxis axis,
- float current_offset,
+ const SnapSelectionStrategy& strategy,
const SnapSearchResult& cross_axis_snap_result) const;
// Returns all the info needed to snap at this area on the given axis,
diff --git a/chromium/cc/input/scroll_snap_data_unittest.cc b/chromium/cc/input/scroll_snap_data_unittest.cc
index d2347148d0b..6f994d543a0 100644
--- a/chromium/cc/input/scroll_snap_data_unittest.cc
+++ b/chromium/cc/input/scroll_snap_data_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "cc/input/scroll_snap_data.h"
+#include "cc/input/snap_selection_strategy.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -17,10 +18,12 @@ TEST_F(ScrollSnapDataTest, StartAlignmentCalculation) {
SnapAreaData area(ScrollSnapAlign(SnapAlignment::kStart),
gfx::RectF(100, 150, 100, 100), false);
container.AddSnapAreaData(area);
- gfx::ScrollOffset current_position(0, 0);
+
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(0, 0), true,
+ true);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
EXPECT_EQ(90, snap_position.x());
EXPECT_EQ(140, snap_position.y());
}
@@ -32,10 +35,12 @@ TEST_F(ScrollSnapDataTest, CenterAlignmentCalculation) {
SnapAreaData area(ScrollSnapAlign(SnapAlignment::kCenter),
gfx::RectF(100, 150, 100, 100), false);
container.AddSnapAreaData(area);
- gfx::ScrollOffset current_position(0, 0);
+
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(0, 0), true,
+ true);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
EXPECT_EQ(40, snap_position.x());
EXPECT_EQ(40, snap_position.y());
}
@@ -47,10 +52,12 @@ TEST_F(ScrollSnapDataTest, EndAlignmentCalculation) {
SnapAreaData area(ScrollSnapAlign(SnapAlignment::kEnd),
gfx::RectF(150, 200, 100, 100), false);
container.AddSnapAreaData(area);
- gfx::ScrollOffset current_position(0, 0);
+
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(0, 0), true,
+ true);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
EXPECT_EQ(40, snap_position.x());
EXPECT_EQ(90, snap_position.y());
}
@@ -62,10 +69,12 @@ TEST_F(ScrollSnapDataTest, UnreachableSnapPositionCalculation) {
SnapAreaData area(ScrollSnapAlign(SnapAlignment::kEnd, SnapAlignment::kStart),
gfx::RectF(200, 0, 100, 100), false);
container.AddSnapAreaData(area);
- gfx::ScrollOffset current_position(50, 50);
+
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(50, 50),
+ true, true);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
// Aligning to start on x would lead the scroll offset larger than max, and
// aligning to end on y would lead the scroll offset smaller than zero. So
// we expect these are clamped.
@@ -85,13 +94,15 @@ TEST_F(ScrollSnapDataTest, FindsClosestSnapPositionIndependently) {
gfx::RectF(0, 70, 150, 150), false);
SnapAreaData snap_on_both(ScrollSnapAlign(SnapAlignment::kStart),
gfx::RectF(50, 150, 150, 150), false);
- gfx::ScrollOffset current_position(100, 100);
container.AddSnapAreaData(snap_x_only);
container.AddSnapAreaData(snap_y_only);
container.AddSnapAreaData(snap_on_both);
+
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(100, 100),
+ true, true);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
EXPECT_EQ(80, snap_position.x());
EXPECT_EQ(70, snap_position.y());
}
@@ -108,13 +119,15 @@ TEST_F(ScrollSnapDataTest, FindsClosestSnapPositionOnAxisValueBoth) {
gfx::RectF(0, 70, 150, 150), false);
SnapAreaData snap_on_both(ScrollSnapAlign(SnapAlignment::kStart),
gfx::RectF(50, 150, 150, 150), false);
- gfx::ScrollOffset current_position(40, 120);
+
container.AddSnapAreaData(snap_x_only);
container.AddSnapAreaData(snap_y_only);
container.AddSnapAreaData(snap_on_both);
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(40, 120),
+ true, true);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
EXPECT_EQ(50, snap_position.x());
EXPECT_EQ(150, snap_position.y());
}
@@ -129,12 +142,14 @@ TEST_F(ScrollSnapDataTest, DoesNotSnapOnNonScrolledAxis) {
SnapAreaData snap_y_only(
ScrollSnapAlign(SnapAlignment::kStart, SnapAlignment::kNone),
gfx::RectF(0, 70, 150, 150), false);
- gfx::ScrollOffset current_position(100, 100);
container.AddSnapAreaData(snap_x_only);
container.AddSnapAreaData(snap_y_only);
+
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(container.FindSnapPosition(current_position, true, false,
- &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(100, 100),
+ true, false);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
EXPECT_EQ(80, snap_position.x());
EXPECT_EQ(100, snap_position.y());
}
@@ -149,19 +164,20 @@ TEST_F(ScrollSnapDataTest, DoesNotSnapOnNonVisibleAreas) {
SnapAreaData snap_y_only(
ScrollSnapAlign(SnapAlignment::kStart, SnapAlignment::kNone),
gfx::RectF(400, 300, 100, 100), false);
- gfx::ScrollOffset current_position(0, 0);
+
container.AddSnapAreaData(snap_x_only);
container.AddSnapAreaData(snap_y_only);
gfx::ScrollOffset snap_position;
- EXPECT_FALSE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(0, 0), true,
+ true);
+ EXPECT_FALSE(container.FindSnapPosition(*strategy, &snap_position));
}
TEST_F(ScrollSnapDataTest, SnapOnClosestAxisFirstIfVisibilityConflicts) {
SnapContainerData container(
ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory),
gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(600, 800));
- gfx::ScrollOffset current_position(0, 0);
// Both the areas are currently visible.
// However, if we snap to them on x and y independently, none is visible after
@@ -180,9 +196,12 @@ TEST_F(ScrollSnapDataTest, SnapOnClosestAxisFirstIfVisibilityConflicts) {
container.AddSnapAreaData(snap_x);
container.AddSnapAreaData(snap_y1);
container.AddSnapAreaData(snap_y2);
+
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(0, 0), true,
+ true);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
EXPECT_EQ(150, snap_position.x());
EXPECT_EQ(80, snap_position.y());
}
@@ -193,13 +212,15 @@ TEST_F(ScrollSnapDataTest, DoesNotSnapToPositionsOutsideProximityRange) {
gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(600, 800));
container.set_proximity_range(gfx::ScrollOffset(50, 50));
- gfx::ScrollOffset current_position(100, 100);
SnapAreaData area(ScrollSnapAlign(SnapAlignment::kStart),
gfx::RectF(80, 160, 100, 100), false);
container.AddSnapAreaData(area);
+
gfx::ScrollOffset snap_position;
- EXPECT_TRUE(
- container.FindSnapPosition(current_position, true, true, &snap_position));
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(100, 100),
+ true, true);
+ EXPECT_TRUE(container.FindSnapPosition(*strategy, &snap_position));
// The snap position on x, 80, is within the proximity range of [50, 150].
// However, the snap position on y, 160, is outside the proximity range of
diff --git a/chromium/cc/input/snap_fling_controller.h b/chromium/cc/input/snap_fling_controller.h
index 20f305651c2..eb07106796c 100644
--- a/chromium/cc/input/snap_fling_controller.h
+++ b/chromium/cc/input/snap_fling_controller.h
@@ -24,8 +24,8 @@ class SnapFlingCurve;
class SnapFlingClient {
public:
virtual bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement,
- gfx::Vector2dF* out_initial_offset,
- gfx::Vector2dF* out_target_offset) const = 0;
+ gfx::Vector2dF* out_initial_position,
+ gfx::Vector2dF* out_target_position) const = 0;
virtual gfx::Vector2dF ScrollByForSnapFling(const gfx::Vector2dF& delta) = 0;
virtual void ScrollEndForSnapFling() = 0;
virtual void RequestAnimationForSnapFling() = 0;
diff --git a/chromium/cc/input/snap_selection_strategy.cc b/chromium/cc/input/snap_selection_strategy.cc
new file mode 100644
index 00000000000..ce0f2655ecf
--- /dev/null
+++ b/chromium/cc/input/snap_selection_strategy.cc
@@ -0,0 +1,151 @@
+// 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.
+
+#include "cc/input/snap_selection_strategy.h"
+
+namespace cc {
+
+std::unique_ptr<SnapSelectionStrategy>
+SnapSelectionStrategy::CreateForEndPosition(
+ const gfx::ScrollOffset& current_position,
+ bool scrolled_x,
+ bool scrolled_y) {
+ return std::make_unique<EndPositionStrategy>(current_position, scrolled_x,
+ scrolled_y);
+}
+
+std::unique_ptr<SnapSelectionStrategy>
+SnapSelectionStrategy::CreateForDirection(gfx::ScrollOffset current_position,
+ gfx::ScrollOffset step) {
+ return std::make_unique<DirectionStrategy>(current_position, step);
+}
+
+std::unique_ptr<SnapSelectionStrategy>
+SnapSelectionStrategy::CreateForEndAndDirection(
+ gfx::ScrollOffset current_position,
+ gfx::ScrollOffset displacement) {
+ return std::make_unique<EndAndDirectionStrategy>(current_position,
+ displacement);
+}
+
+bool EndPositionStrategy::ShouldSnapOnX() const {
+ return scrolled_x_;
+}
+
+bool EndPositionStrategy::ShouldSnapOnY() const {
+ return scrolled_y_;
+}
+
+gfx::ScrollOffset EndPositionStrategy::intended_position() const {
+ return current_position_;
+}
+
+gfx::ScrollOffset EndPositionStrategy::base_position() const {
+ return current_position_;
+}
+
+// |position| is unused in this method.
+bool EndPositionStrategy::IsValidSnapPosition(SearchAxis axis,
+ float position) const {
+ return (scrolled_x_ && axis == SearchAxis::kX) ||
+ (scrolled_y_ && axis == SearchAxis::kY);
+}
+
+const base::Optional<SnapSearchResult>& EndPositionStrategy::PickBestResult(
+ const base::Optional<SnapSearchResult>& closest,
+ const base::Optional<SnapSearchResult>& covering) const {
+ return covering.has_value() ? covering : closest;
+}
+
+bool DirectionStrategy::ShouldSnapOnX() const {
+ return step_.x() != 0;
+}
+
+bool DirectionStrategy::ShouldSnapOnY() const {
+ return step_.y() != 0;
+}
+
+gfx::ScrollOffset DirectionStrategy::intended_position() const {
+ return current_position_ + step_;
+}
+
+gfx::ScrollOffset DirectionStrategy::base_position() const {
+ return current_position_;
+}
+
+bool DirectionStrategy::IsValidSnapPosition(SearchAxis axis,
+ float position) const {
+ if (axis == SearchAxis::kX) {
+ return (step_.x() > 0 &&
+ position > current_position_.x()) || // "Right" arrow
+ (step_.x() < 0 && position < current_position_.x()); // "Left" arrow
+ } else {
+ return (step_.y() > 0 &&
+ position > current_position_.y()) || // "Down" arrow
+ (step_.y() < 0 && position < current_position_.y()); // "Up" arrow
+ }
+}
+
+const base::Optional<SnapSearchResult>& DirectionStrategy::PickBestResult(
+ const base::Optional<SnapSearchResult>& closest,
+ const base::Optional<SnapSearchResult>& covering) const {
+ // We choose the |closest| result only if the default landing position (using
+ // the default step) is not a valid snap position (not making a snap area
+ // covering the snapport), or the |closest| is closer than the default landing
+ // position.
+ if (!closest.has_value())
+ return covering;
+ if (!covering.has_value())
+ return closest;
+
+ // "Right" or "Down" arrow.
+ if ((step_.x() > 0 || step_.y() > 0) &&
+ closest.value().snap_offset() < covering.value().snap_offset()) {
+ return closest;
+ }
+ // "Left" or "Up" arrow.
+ if ((step_.x() < 0 || step_.y() < 0) &&
+ closest.value().snap_offset() > covering.value().snap_offset()) {
+ return closest;
+ }
+
+ return covering;
+}
+
+bool EndAndDirectionStrategy::ShouldSnapOnX() const {
+ return displacement_.x() != 0;
+}
+
+bool EndAndDirectionStrategy::ShouldSnapOnY() const {
+ return displacement_.y() != 0;
+}
+
+gfx::ScrollOffset EndAndDirectionStrategy::intended_position() const {
+ return current_position_ + displacement_;
+}
+
+gfx::ScrollOffset EndAndDirectionStrategy::base_position() const {
+ return current_position_ + displacement_;
+}
+
+bool EndAndDirectionStrategy::IsValidSnapPosition(SearchAxis axis,
+ float position) const {
+ if (axis == SearchAxis::kX) {
+ return (displacement_.x() > 0 &&
+ position > current_position_.x()) || // Right
+ (displacement_.x() < 0 && position < current_position_.x()); // Left
+ } else {
+ return (displacement_.y() > 0 &&
+ position > current_position_.y()) || // Down
+ (displacement_.y() < 0 && position < current_position_.y()); // Up
+ }
+}
+
+const base::Optional<SnapSearchResult>& EndAndDirectionStrategy::PickBestResult(
+ const base::Optional<SnapSearchResult>& closest,
+ const base::Optional<SnapSearchResult>& covering) const {
+ return covering.has_value() ? covering : closest;
+}
+
+} // namespace cc
diff --git a/chromium/cc/input/snap_selection_strategy.h b/chromium/cc/input/snap_selection_strategy.h
new file mode 100644
index 00000000000..cebc63e96b5
--- /dev/null
+++ b/chromium/cc/input/snap_selection_strategy.h
@@ -0,0 +1,170 @@
+// 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 CC_INPUT_SNAP_SELECTION_STRATEGY_H_
+#define CC_INPUT_SNAP_SELECTION_STRATEGY_H_
+
+#include "scroll_snap_data.h"
+
+#include <memory>
+
+namespace cc {
+
+// This class represents an abstract strategy that decide which snap selection
+// should be considered valid. There are concrete implementations for three core
+// scrolling types: scroll with end position only, scroll with direction only,
+// and scroll with end position and direction.
+class CC_EXPORT SnapSelectionStrategy {
+ public:
+ SnapSelectionStrategy() = default;
+ virtual ~SnapSelectionStrategy() = default;
+ static std::unique_ptr<SnapSelectionStrategy> CreateForEndPosition(
+ const gfx::ScrollOffset& current_position,
+ bool scrolled_x,
+ bool scrolled_y);
+ static std::unique_ptr<SnapSelectionStrategy> CreateForDirection(
+ gfx::ScrollOffset current_position,
+ gfx::ScrollOffset step);
+ static std::unique_ptr<SnapSelectionStrategy> CreateForEndAndDirection(
+ gfx::ScrollOffset current_position,
+ gfx::ScrollOffset displacement);
+
+ // Returns whether it's snappable on x or y depending on the scroll performed.
+ virtual bool ShouldSnapOnX() const = 0;
+ virtual bool ShouldSnapOnY() const = 0;
+
+ // Returns the end position of the scroll if no snap interferes.
+ virtual gfx::ScrollOffset intended_position() const = 0;
+ // Returns the scroll position from which the snap position should minimize
+ // its distance.
+ virtual gfx::ScrollOffset base_position() const = 0;
+ // Returns the current scroll position of the snap container.
+ const gfx::ScrollOffset& current_position() const {
+ return current_position_;
+ }
+
+ // Returns true if the selection strategy considers the given snap offset
+ // valid for the current axis.
+ virtual bool IsValidSnapPosition(SearchAxis axis, float position) const = 0;
+
+ // Returns the best result according to snap selection strategy. This method
+ // is called at the end of selection process to make the final decision.
+ //
+ // -closest: snap search result representing closest match.
+ // -covering: snap search result representing the original target if it makes
+ // a snaparea covering the snapport.
+ virtual const base::Optional<SnapSearchResult>& PickBestResult(
+ const base::Optional<SnapSearchResult>& closest,
+ const base::Optional<SnapSearchResult>& covering) const = 0;
+
+ protected:
+ explicit SnapSelectionStrategy(const gfx::ScrollOffset& current_position)
+ : current_position_(current_position) {}
+ const gfx::ScrollOffset current_position_;
+};
+
+// Examples for intended end position scrolls include
+// - a panning gesture, released without momentum
+// - manupulating the scrollbar "thumb" explicitly
+// - programmatically scrolling via APIs such as scrollTo()
+// - tabbing through the document's focusable elements
+// - navigating to an anchor within the page
+// - homing operations such as the Home/End keys
+// For this type of scrolls, we want to
+// * Minimize the distance between the snap position and the end position.
+// * Return the end position if that makes a snap area covers the snapport.
+class EndPositionStrategy : public SnapSelectionStrategy {
+ public:
+ EndPositionStrategy(const gfx::ScrollOffset& current_position,
+ bool scrolled_x,
+ bool scrolled_y)
+ : SnapSelectionStrategy(current_position),
+ scrolled_x_(scrolled_x),
+ scrolled_y_(scrolled_y) {}
+ ~EndPositionStrategy() override = default;
+
+ bool ShouldSnapOnX() const override;
+ bool ShouldSnapOnY() const override;
+
+ gfx::ScrollOffset intended_position() const override;
+ gfx::ScrollOffset base_position() const override;
+
+ bool IsValidSnapPosition(SearchAxis axis, float position) const override;
+
+ const base::Optional<SnapSearchResult>& PickBestResult(
+ const base::Optional<SnapSearchResult>& closest,
+ const base::Optional<SnapSearchResult>& covering) const override;
+
+ private:
+ // Whether the x axis and y axis have been scrolled in this scroll gesture.
+ const bool scrolled_x_;
+ const bool scrolled_y_;
+};
+
+// Examples for intended direction scrolls include
+// - pressing an arrow key on the keyboard
+// - a swiping gesture interpreted as a fixed (rather than inertial) scroll
+// For this type of scrolls, we want to
+// * Minimize the distance between the snap position and the starting position,
+// so that we stop at the first snap position in that direction.
+// * Return the default intended position (using the default step) if that makes
+// a snap area covers the snapport.
+class DirectionStrategy : public SnapSelectionStrategy {
+ public:
+ DirectionStrategy(const gfx::ScrollOffset& current_position,
+ const gfx::ScrollOffset& step)
+ : SnapSelectionStrategy(current_position), step_(step) {}
+ ~DirectionStrategy() override = default;
+
+ bool ShouldSnapOnX() const override;
+ bool ShouldSnapOnY() const override;
+
+ gfx::ScrollOffset intended_position() const override;
+ gfx::ScrollOffset base_position() const override;
+
+ bool IsValidSnapPosition(SearchAxis axis, float position) const override;
+
+ const base::Optional<SnapSearchResult>& PickBestResult(
+ const base::Optional<SnapSearchResult>& closest,
+ const base::Optional<SnapSearchResult>& covering) const override;
+
+ private:
+ // The default step for this DirectionStrategy.
+ const gfx::ScrollOffset step_;
+};
+
+// Examples for intended direction and end position scrolls include
+// - a “fling” gesture, interpreted with momentum
+// - programmatically scrolling via APIs such as scrollBy()
+// - paging operations such as the PgUp/PgDn keys (or equivalent operations on
+// the scrollbar)
+// For this type of scrolls, we want to
+// * Minimize the distance between the snap position and the end position.
+// * Return the end position if that makes a snap area covers the snapport.
+class EndAndDirectionStrategy : public SnapSelectionStrategy {
+ public:
+ EndAndDirectionStrategy(const gfx::ScrollOffset& current_position,
+ const gfx::ScrollOffset& displacement)
+ : SnapSelectionStrategy(current_position), displacement_(displacement) {}
+ ~EndAndDirectionStrategy() override = default;
+
+ bool ShouldSnapOnX() const override;
+ bool ShouldSnapOnY() const override;
+
+ gfx::ScrollOffset intended_position() const override;
+ gfx::ScrollOffset base_position() const override;
+
+ bool IsValidSnapPosition(SearchAxis axis, float position) const override;
+
+ const base::Optional<SnapSearchResult>& PickBestResult(
+ const base::Optional<SnapSearchResult>& closest,
+ const base::Optional<SnapSearchResult>& covering) const override;
+
+ private:
+ const gfx::ScrollOffset displacement_;
+};
+
+} // namespace cc
+
+#endif // CC_INPUT_SNAP_SELECTION_STRATEGY_H_