| // 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_ |