1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
content / browser / navigation_transitions / physics_model.h [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_NAVIGATION_TRANSITIONS_PHYSICS_MODEL_H_
#define CONTENT_BROWSER_NAVIGATION_TRANSITIONS_PHYSICS_MODEL_H_
#include <deque>
#include <memory>
#include "base/time/time.h"
#include "content/common/content_export.h"
namespace content {
// The spring models. Internal to `PhysicsModel`.
class Spring;
// The animation model that drives the animations for session history
// navigations. See `animation_driver_` for what this model is composed of.
class CONTENT_EXPORT PhysicsModel {
public:
// The live page of the current content will stop at 85% of the screen width
// while wait for the navigation to the new page to commit.
static constexpr float kTargetCommitPendingRatio = 0.85f;
// Initially the screenshot is placed at (-0.25W, 0) with respect to the
// viewport.
static constexpr float kScreenshotInitialPositionRatio = -0.25f;
// The calculated layer offsets by this physics model.
struct Result {
// The calculated offsets for the foreground and background layers. They are
// physical pixel values.
float foreground_offset_physical;
float background_offset_physical;
// Indicating if the animation has finished. Set to true when the invoke
// animation or cancel animation has finished playing.
bool done;
};
PhysicsModel(int screen_width_physical, float device_scale_factor);
PhysicsModel(const PhysicsModel&) = delete;
PhysicsModel& operator=(const PhysicsModel&) = delete;
~PhysicsModel();
// Called when the user swipes the finger across the screen. Uses
// `FingerDragCurve()` to calculate the layers' positions. `movement_physical`
// is the delta pixels since the last user gesture, meaning it can be positive
// or negative.
Result OnGestureProgressed(float movement_physical,
base::TimeTicks timestamp);
// Called when a frame is requested at `request_animation_frame`. Uses the
// corresponding spring models to calculate the layers' positions. It is
// called at each vsync, except when the UI thread is busy.
Result OnAnimate(base::TimeTicks request_animation_frame);
enum SwitchSpringReason {
// Switch to `kSpringCancel` because the user lifts the finger and signals
// to not start the navigation.
kGestureCancelled = 0,
// Switch to `kSpringCommitPending` because the user lifts the finger and
// signals to start the navigation.
kGestureInvoked,
// Switch to `kSpringCancel` because the browser has dispatched the
// BeforeUnload message to the renderer.
kBeforeUnloadDispatched,
// Switch to `kSpringCommitPending` because the renderer has acked to
// proceed the navigation, in response to the BeforeUnload message.
kBeforeUnloadAckProceed,
};
// Switch to a different spring model for various reasons.
void SwitchSpringForReason(SwitchSpringReason reason);
// Called when the navigation is finished (destruction of the navigation
// request). The caller is responsible for reacting to the targeted navigation
// request (when there are multiple navigation requests).
void OnNavigationFinished(bool navigation_committed);
private:
// The "state" of the physics model. The animations can be driven by four
// models:
// - A drag curve when the user swipes across the screen and before the user
// lifts the finger.
// - A spring model that bounces around the commit-pending point. It drives
// the commit-pending animation: to bounce the live page around the
// commit-pending point while waiting for the history navigation to commit.
// Equilibrium is the commit-pending position.
// - A spring model that plays the invoke animation: to bring the page from
// the commit-pending point to completely out of the view port. Equilibrium
// is at the right edge for a back navigation.
// - A spring model that plays the cancel animation: to bring the old live
// content back to the center of the viewport. Equilibrium is at the left
// edge for a back navigation.
//
// A big difference between a drag curve and a spring model is the drag curve
// user-gesture driven while the spring models are vsync driven. The
// implication is that for the drag curve the caller will need to provide a
// timestamp associated with the finger movement, whereas for spring models we
// get the timestamp from the wallclock.
enum class Driver {
kDragCurve = 0,
kSpringCommitPending,
kSpringInvoke,
kSpringCancel,
};
// Record the starting point of the next animation driver. Called every time
// `driver_` changes.
void StartAnimating(base::TimeTicks time);
// Calculates the background layer's viewport offset based on the foreground.
float ForegroundToBackGroundOffset(float foreground_offset_viewport);
// Calculate the foreground layer's viewport offset based on the finger's
// movement.
float FingerDragCurve(float movement_viewport);
// Interpolates the velocity based off `touch_points_history_`. Used to set
// the initial velocity of the spring model when the physics model switches
// from drag cruve to any of the spring models.
float CalculateVelocity();
// Record `commit_pending_acceleration_start_`, if needed.
void RecordCommitPendingAccelerationStartIfNeeded(
base::TimeTicks request_animation_frame);
// Advance the physics model to the next animation driver at
// `request_animation_frame`. Updates `animation_driver_` and sets its initial
// velocity. No-op for the terminal drivers (the invoke and cancel springs).
void AdvanceToNextAnimationDriver(base::TimeTicks request_animation_frame);
// Normalizes `request_animation_frame` with respect to the start of the
// animation (i.e., when we first switched to the current animation driver).
base::TimeDelta CalculateRequestAnimationFrameSinceStart(
base::TimeTicks request_animation_frame);
const float viewport_width_;
// Used to convert the physical sizes into CSS/viewport sizes.
const float device_scale_factor_;
// Tracks the current state of the navigation.
enum class NavigationState {
kNotStarted = 0,
// The navigation never starts. This is a terminal state for
// `OnGestureCancelled()`.
kNeverStarted,
// The navigation has started, WITHOUT a BeforeUnload handler.
kStarted,
// The browser has sent the BeforeUnload message to the renderer.
kBeforeUnloadDispatched,
// The renderer has acked the BeforeUnload message and to start the
// navigation.
kBeforeUnloadAckedProceed,
// No state when BeforeUnload ack'ed to not proceed.
// The navigation has committed in the browser. This is one of the two
// terminal states for `OnNavigationFinished()`.
kCommitted,
// The navigation is cancelled. This is another terminal state for
// `OnNavigationFinished()`.
kCancelled,
};
NavigationState navigation_state_ = NavigationState::kNotStarted;
// The spring models correspond to
// `Driver::{kSpringCommitPending|kSpringInvoke|kSpringCancel}`. See the
// comments on `Driver` that describe the springs behavior. Always non-null.
std::unique_ptr<Spring> spring_commit_pending_;
std::unique_ptr<Spring> spring_invoke_;
std::unique_ptr<Spring> spring_cancel_;
// Wallclock.
base::TimeTicks last_request_animation_frame_;
// The physics model always starts with the drag curve.
Driver animation_driver_ = Driver::kDragCurve;
// Used to "speed up" the animation on `spring_commit_pending_` when the
// invoke animation is ready to play. Set in
// `RecordCommitPendingAccelerationStartIfNeeded()` and applied in
// `CalculateRequestAnimationFrameSinceStart()`. Wallclock.
base::TimeTicks commit_pending_acceleration_start_;
// Wallclock.
base::TimeTicks animation_start_time_;
float animation_start_offset_viewport_ = 0.f;
// Measured with respect to the left edge of the device.
float foreground_offset_viewport_ = 0.f;
bool foreground_has_reached_target_commit_pending_ = false;
struct TouchEvent {
float position_viewport;
base::TimeTicks timestamp;
};
// Records the last few touch events. Used to interpolate the velocity. It has
// a max size defined in the .cc file.
std::deque<TouchEvent> touch_points_history_;
};
} // namespace content
#endif // CONTENT_BROWSER_NAVIGATION_TRANSITIONS_PHYSICS_MODEL_H_