// 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.
#include "chrome/browser/gesturenav/android/tab_on_back_gesture_handler.h"
#include "content/public/browser/back_forward_transition_animation_manager.h"
#include "content/public/browser/web_contents.h"
#include "ui/android/view_android.h"
#include "ui/android/window_android.h"
#include "ui/gfx/geometry/point_f.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/browser/gesturenav/android/jni_headers/TabOnBackGestureHandler_jni.h"
namespace gesturenav {
namespace {
using NavDirection =
content::BackForwardTransitionAnimationManager::NavigationDirection;
void AssertHasWindowAndCompositor(content::WebContents* web_contents) {
CHECK(web_contents);
auto* window = web_contents->GetNativeView()->GetWindowAndroid();
CHECK(window);
CHECK(window->GetCompositor());
}
} // namespace
TabOnBackGestureHandler::TabOnBackGestureHandler(TabAndroid* tab_android)
: tab_android_(tab_android) {}
void TabOnBackGestureHandler::OnBackStarted(JNIEnv* env,
float progress,
int edge,
bool forward) {
// Ideally the OS shouldn't start a new gesture without finishing the previous
// gesture but we see this pattern on multiple devices.
// See crbug.com/41484247.
if (is_in_progress_) {
base::debug::DumpWithoutCrashing();
OnBackCancelled(env);
CHECK(!is_in_progress_);
}
is_in_progress_ = true;
content::WebContents* web_contents = tab_android_->web_contents();
CHECK(web_contents, base::NotFatalUntil::M123);
AssertHasWindowAndCompositor(web_contents);
ui::BackGestureEvent back_gesture(progress);
started_edge_ = static_cast<ui::BackGestureEventSwipeEdge>(edge);
web_contents->GetBackForwardTransitionAnimationManager()->OnGestureStarted(
back_gesture, static_cast<ui::BackGestureEventSwipeEdge>(edge),
forward ? NavDirection::kForward : NavDirection::kBackward);
}
void TabOnBackGestureHandler::OnBackProgressed(JNIEnv* env,
float progress,
int edge) {
CHECK(is_in_progress_);
content::WebContents* web_contents = tab_android_->web_contents();
AssertHasWindowAndCompositor(web_contents);
CHECK_EQ(started_edge_, static_cast<ui::BackGestureEventSwipeEdge>(edge));
if (progress > 1.f) {
// TODO(crbug.com/41483519): Happens in fling. Should figure out why
// before launch. Cap the progress at 1.f for now.
LOG(ERROR) << "TabOnBackGestureHandler::OnBackProgressed " << progress;
progress = 1.f;
}
ui::BackGestureEvent back_gesture(progress);
web_contents->GetBackForwardTransitionAnimationManager()->OnGestureProgressed(
back_gesture);
}
void TabOnBackGestureHandler::OnBackCancelled(JNIEnv* env) {
CHECK(is_in_progress_);
is_in_progress_ = false;
content::WebContents* web_contents = tab_android_->web_contents();
AssertHasWindowAndCompositor(web_contents);
web_contents->GetBackForwardTransitionAnimationManager()
->OnGestureCancelled();
}
void TabOnBackGestureHandler::OnBackInvoked(JNIEnv* env) {
CHECK(is_in_progress_);
is_in_progress_ = false;
content::WebContents* web_contents = tab_android_->web_contents();
AssertHasWindowAndCompositor(web_contents);
web_contents->GetBackForwardTransitionAnimationManager()->OnGestureInvoked();
}
void TabOnBackGestureHandler::Destroy(JNIEnv* env) {
if (is_in_progress_) {
OnBackCancelled(env);
}
delete this;
}
// ----------------------------------------------------------------------------
// Native JNI methods
// ----------------------------------------------------------------------------
// static
jlong JNI_TabOnBackGestureHandler_Init(JNIEnv* env,
const JavaParamRef<jobject>& jtab) {
TabOnBackGestureHandler* handler =
new TabOnBackGestureHandler(TabAndroid::GetNativeTab(env, jtab));
return reinterpret_cast<intptr_t>(handler);
}
// static
jboolean JNI_TabOnBackGestureHandler_ShouldAnimateNavigationTransition(
JNIEnv* env,
jboolean forward,
jint edge) {
return static_cast<jboolean>(
content::BackForwardTransitionAnimationManager::
ShouldAnimateNavigationTransition(
static_cast<bool>(forward) ? NavDirection::kForward
: NavDirection::kBackward,
static_cast<ui::BackGestureEventSwipeEdge>(edge)));
}
} // namespace gesturenav