blob: 71424cbc89086c3017fc68c40b9970993cac6319 [file] [log] [blame]
// Copyright 2019 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 "weblayer/browser/navigation_controller_impl.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/page_transition_types.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/public/navigation_observer.h"
#if defined(OS_ANDROID)
#include "base/android/jni_string.h"
#include "base/trace_event/trace_event.h"
#include "weblayer/browser/java/jni/NavigationControllerImpl_jni.h"
#endif
#if defined(OS_ANDROID)
using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
#endif
namespace weblayer {
NavigationControllerImpl::NavigationControllerImpl(TabImpl* tab)
: WebContentsObserver(tab->web_contents()) {}
NavigationControllerImpl::~NavigationControllerImpl() = default;
#if defined(OS_ANDROID)
void NavigationControllerImpl::SetNavigationControllerImpl(
JNIEnv* env,
const JavaParamRef<jobject>& java_controller) {
java_controller_ = java_controller;
}
void NavigationControllerImpl::GoToIndex(JNIEnv* env,
const JavaParamRef<jobject>& obj,
int index) {
return GoToIndex(index);
}
void NavigationControllerImpl::Navigate(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jstring>& url) {
Navigate(GURL(base::android::ConvertJavaStringToUTF8(env, url)));
}
ScopedJavaLocalRef<jstring>
NavigationControllerImpl::GetNavigationEntryDisplayUri(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
int index) {
return ScopedJavaLocalRef<jstring>(base::android::ConvertUTF8ToJavaString(
env, GetNavigationEntryDisplayURL(index).spec()));
}
ScopedJavaLocalRef<jstring> NavigationControllerImpl::GetNavigationEntryTitle(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
int index) {
return ScopedJavaLocalRef<jstring>(base::android::ConvertUTF8ToJavaString(
env, GetNavigationEntryTitle(index)));
}
#endif
void NavigationControllerImpl::AddObserver(NavigationObserver* observer) {
observers_.AddObserver(observer);
}
void NavigationControllerImpl::RemoveObserver(NavigationObserver* observer) {
observers_.RemoveObserver(observer);
}
void NavigationControllerImpl::Navigate(const GURL& url) {
content::NavigationController::LoadURLParams params(url);
params.transition_type = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
web_contents()->GetController().LoadURLWithParams(params);
// So that if the user had entered the UI in a bar it stops flashing the
// caret.
web_contents()->Focus();
}
void NavigationControllerImpl::GoBack() {
web_contents()->GetController().GoBack();
}
void NavigationControllerImpl::GoForward() {
web_contents()->GetController().GoForward();
}
bool NavigationControllerImpl::CanGoBack() {
return web_contents()->GetController().CanGoBack();
}
bool NavigationControllerImpl::CanGoForward() {
return web_contents()->GetController().CanGoForward();
}
void NavigationControllerImpl::GoToIndex(int index) {
web_contents()->GetController().GoToIndex(index);
}
void NavigationControllerImpl::Reload() {
web_contents()->GetController().Reload(content::ReloadType::NORMAL, false);
}
void NavigationControllerImpl::Stop() {
web_contents()->Stop();
}
int NavigationControllerImpl::GetNavigationListSize() {
return web_contents()->GetController().GetEntryCount();
}
int NavigationControllerImpl::GetNavigationListCurrentIndex() {
return web_contents()->GetController().GetCurrentEntryIndex();
}
GURL NavigationControllerImpl::GetNavigationEntryDisplayURL(int index) {
auto* entry = web_contents()->GetController().GetEntryAtIndex(index);
if (!entry)
return GURL();
return entry->GetVirtualURL();
}
std::string NavigationControllerImpl::GetNavigationEntryTitle(int index) {
auto* entry = web_contents()->GetController().GetEntryAtIndex(index);
if (!entry)
return std::string();
return base::UTF16ToUTF8(entry->GetTitle());
}
void NavigationControllerImpl::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame())
return;
navigation_map_[navigation_handle] =
std::make_unique<NavigationImpl>(navigation_handle);
auto* navigation = navigation_map_[navigation_handle].get();
#if defined(OS_ANDROID)
if (java_controller_) {
JNIEnv* env = AttachCurrentThread();
{
TRACE_EVENT0("weblayer",
"Java_NavigationControllerImpl_createNavigation");
Java_NavigationControllerImpl_createNavigation(
env, java_controller_, reinterpret_cast<jlong>(navigation));
}
TRACE_EVENT0("weblayer", "Java_NavigationControllerImpl_navigationStarted");
Java_NavigationControllerImpl_navigationStarted(
env, java_controller_, navigation->java_navigation());
}
#endif
for (auto& observer : observers_)
observer.NavigationStarted(navigation);
}
void NavigationControllerImpl::DidRedirectNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame())
return;
DCHECK(navigation_map_.find(navigation_handle) != navigation_map_.end());
auto* navigation = navigation_map_[navigation_handle].get();
#if defined(OS_ANDROID)
if (java_controller_) {
TRACE_EVENT0("weblayer",
"Java_NavigationControllerImpl_navigationRedirected");
Java_NavigationControllerImpl_navigationRedirected(
AttachCurrentThread(), java_controller_, navigation->java_navigation());
}
#endif
for (auto& observer : observers_)
observer.NavigationRedirected(navigation);
}
void NavigationControllerImpl::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame())
return;
DCHECK(navigation_map_.find(navigation_handle) != navigation_map_.end());
auto* navigation = navigation_map_[navigation_handle].get();
#if defined(OS_ANDROID)
if (java_controller_) {
TRACE_EVENT0("weblayer",
"Java_NavigationControllerImpl_readyToCommitNavigation");
Java_NavigationControllerImpl_readyToCommitNavigation(
AttachCurrentThread(), java_controller_, navigation->java_navigation());
}
#endif
for (auto& observer : observers_)
observer.ReadyToCommitNavigation(navigation);
}
void NavigationControllerImpl::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame())
return;
DCHECK(navigation_map_.find(navigation_handle) != navigation_map_.end());
auto* navigation = navigation_map_[navigation_handle].get();
if (navigation_handle->GetNetErrorCode() == net::OK &&
!navigation_handle->IsErrorPage()) {
#if defined(OS_ANDROID)
if (java_controller_) {
TRACE_EVENT0("weblayer",
"Java_NavigationControllerImpl_navigationCompleted");
Java_NavigationControllerImpl_navigationCompleted(
AttachCurrentThread(), java_controller_,
navigation->java_navigation());
}
#endif
for (auto& observer : observers_)
observer.NavigationCompleted(navigation);
} else {
#if defined(OS_ANDROID)
if (java_controller_) {
TRACE_EVENT0("weblayer",
"Java_NavigationControllerImpl_navigationFailed");
Java_NavigationControllerImpl_navigationFailed(
AttachCurrentThread(), java_controller_,
navigation->java_navigation());
}
#endif
for (auto& observer : observers_)
observer.NavigationFailed(navigation);
}
navigation_map_.erase(navigation_map_.find(navigation_handle));
}
void NavigationControllerImpl::DidStartLoading() {
NotifyLoadStateChanged();
}
void NavigationControllerImpl::DidStopLoading() {
NotifyLoadStateChanged();
}
void NavigationControllerImpl::LoadProgressChanged(double progress) {
#if defined(OS_ANDROID)
if (java_controller_) {
TRACE_EVENT0("weblayer",
"Java_NavigationControllerImpl_loadProgressChanged");
Java_NavigationControllerImpl_loadProgressChanged(
AttachCurrentThread(), java_controller_, progress);
}
#endif
for (auto& observer : observers_)
observer.LoadProgressChanged(progress);
}
void NavigationControllerImpl::DidFirstVisuallyNonEmptyPaint() {
#if defined(OS_ANDROID)
TRACE_EVENT0("weblayer",
"Java_NavigationControllerImpl_onFirstContentfulPaint");
Java_NavigationControllerImpl_onFirstContentfulPaint(AttachCurrentThread(),
java_controller_);
#endif
for (auto& observer : observers_)
observer.OnFirstContentfulPaint();
}
void NavigationControllerImpl::NotifyLoadStateChanged() {
#if defined(OS_ANDROID)
if (java_controller_) {
TRACE_EVENT0("weblayer", "Java_NavigationControllerImpl_loadStateChanged");
Java_NavigationControllerImpl_loadStateChanged(
AttachCurrentThread(), java_controller_, web_contents()->IsLoading(),
web_contents()->IsLoadingToDifferentDocument());
}
#endif
for (auto& observer : observers_) {
observer.LoadStateChanged(web_contents()->IsLoading(),
web_contents()->IsLoadingToDifferentDocument());
}
}
#if defined(OS_ANDROID)
static jlong JNI_NavigationControllerImpl_GetNavigationController(JNIEnv* env,
jlong tab) {
return reinterpret_cast<jlong>(
reinterpret_cast<Tab*>(tab)->GetNavigationController());
}
#endif
} // namespace weblayer