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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
|
// 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 `kSpringCommitPending` because the user lifts the finger but
// the navigation does not start. The navigation is waiting for the renderer
// to run the BeforeUnload handler to start.
kBeforeUnloadDispatched,
// Switch to `kSpringCancel` because the BeforeUnload dialog is shown.
kBeforeUnloadShown,
// Switch to `kSpringCommitPending` because the renderer has acked to
// proceed the navigation, in response to the BeforeUnload message.
kBeforeUnloadAckProceed,
// Switch to `kSpringCancel` because the navigation is cancelled before it
// starts. The renderer can ack the BeforeUnload to not start the navigation
// without showing a BeforeUnload dialog.
kCancelledBeforeStart,
};
// 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);
// Returns true if the current animation is driven by the commit-pending
// spring, and the animation has reached the commit-pending position.
bool ReachedCommitPending() const;
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(base::TimeTicks time);
// 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 browser has asked the renderer to run the BeforeUnload handler.
//
// Note: the navigation starts in this state if the navigation starts
// without showing a BeforeUnload dialog. In this case we never switch away
// from the commit-pending spring during the BeforeUnload IPC exchange
// between the browser and the renderer.
kBeforeUnloadDispatched,
// The browser has shown a BeforeUnload dialog.
kBeforeUnloadShown,
// The renderer has acked the BeforeUnload message and to start the
// navigation.
kBeforeUnloadAckedProceed,
// The navigation has started, WITHOUT a BeforeUnload handler.
kStarted,
// 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()`. Also used to signal the navigation is cancelled
// before it even starts.
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_
|