// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser.framehost;
import android.graphics.Bitmap;
import android.os.SystemClock;
import androidx.annotation.VisibleForTesting;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;
import org.chromium.base.Log;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.content_public.browser.AdditionalNavigationParams;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.NavigationEntry;
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.NavigationHistory;
import org.chromium.content_public.common.ResourceRequestBody;
import org.chromium.url.GURL;
import org.chromium.url.Origin;
/**
* The NavigationControllerImpl Java wrapper to allow communicating with the native
* NavigationControllerImpl object.
*/
@JNINamespace("content")
// TODO(tedchoc): Remove the package restriction once this class moves to a non-public content
// package whose visibility will be enforced via DEPS.
/* package */ class NavigationControllerImpl implements NavigationController {
private static final String TAG = "NavigationController";
private long mNativeNavigationControllerAndroid;
private NavigationControllerImpl(long nativeNavigationControllerAndroid) {
mNativeNavigationControllerAndroid = nativeNavigationControllerAndroid;
}
@CalledByNative
private static NavigationControllerImpl create(long nativeNavigationControllerAndroid) {
return new NavigationControllerImpl(nativeNavigationControllerAndroid);
}
@CalledByNative
private void destroy() {
mNativeNavigationControllerAndroid = 0;
}
@Override
public boolean canGoBack() {
return mNativeNavigationControllerAndroid != 0
&& NavigationControllerImplJni.get()
.canGoBack(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
@Override
public boolean canGoForward() {
return mNativeNavigationControllerAndroid != 0
&& NavigationControllerImplJni.get()
.canGoForward(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
@Override
@VisibleForTesting
public boolean canGoToOffset(int offset) {
return mNativeNavigationControllerAndroid != 0
&& NavigationControllerImplJni.get()
.canGoToOffset(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
offset);
}
@Override
public void goToOffset(int offset) {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.goToOffset(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
offset);
}
}
@Override
public void goToNavigationIndex(int index) {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.goToNavigationIndex(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
index);
}
}
@Override
public void goBack() {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.goBack(mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
}
@Override
public void goForward() {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.goForward(mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
}
@Override
public boolean isInitialNavigation() {
return mNativeNavigationControllerAndroid != 0
&& NavigationControllerImplJni.get()
.isInitialNavigation(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
@Override
public void loadIfNecessary() {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.loadIfNecessary(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
}
@Override
public boolean needsReload() {
return mNativeNavigationControllerAndroid != 0
&& NavigationControllerImplJni.get()
.needsReload(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
@Override
public void setNeedsReload() {
NavigationControllerImplJni.get()
.setNeedsReload(mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
@Override
public void reload(boolean checkForRepost) {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.reload(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
checkForRepost);
}
}
@Override
public void reloadBypassingCache(boolean checkForRepost) {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.reloadBypassingCache(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
checkForRepost);
}
}
@Override
public void cancelPendingReload() {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.cancelPendingReload(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
}
@Override
public void continuePendingReload() {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.continuePendingReload(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
}
@Override
public NavigationHandle loadUrl(LoadUrlParams params) {
NavigationHandle navigationHandle = null;
if (mNativeNavigationControllerAndroid != 0) {
String headers =
params.getExtraHeaders() == null
? params.getVerbatimHeaders()
: params.getExtraHeadersString();
long inputStart =
params.getInputStartTimestamp() == 0
? params.getIntentReceivedTimestamp()
: params.getInputStartTimestamp();
RecordHistogram.recordTimesHistogram(
"Android.Omnibox.InputToNavigationControllerStart",
SystemClock.uptimeMillis() - inputStart);
navigationHandle =
NavigationControllerImplJni.get()
.loadUrl(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
params.getUrl(),
params.getLoadUrlType(),
params.getTransitionType(),
params.getReferrer() != null
? params.getReferrer().getUrl()
: null,
params.getReferrer() != null
? params.getReferrer().getPolicy()
: 0,
params.getUserAgentOverrideOption(),
headers,
params.getPostData(),
params.getBaseUrl(),
params.getVirtualUrlForSpecialCases(),
params.getDataUrlAsString(),
params.getCanLoadLocalResources(),
params.getIsRendererInitiated(),
params.getShouldReplaceCurrentEntry(),
params.getInitiatorOrigin(),
params.getHasUserGesture(),
params.getShouldClearHistoryList(),
params.getAdditionalNavigationParams(),
inputStart,
params.getNavigationUIDataSupplier() == null
? 0
: params.getNavigationUIDataSupplier().get(),
params.getIsPdf());
// Use the navigation handle object to store user data passed in.
if (navigationHandle != null) {
navigationHandle.setUserDataHost(params.takeNavigationHandleUserData());
}
}
return navigationHandle;
}
@Override
public void clearHistory() {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.clearHistory(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
}
@Override
public NavigationHistory getNavigationHistory() {
if (mNativeNavigationControllerAndroid == 0) return null;
NavigationHistory history = new NavigationHistory();
int currentIndex =
NavigationControllerImplJni.get()
.getNavigationHistory(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
history);
history.setCurrentEntryIndex(currentIndex);
return history;
}
@Override
public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
if (mNativeNavigationControllerAndroid == 0) return null;
NavigationHistory history = new NavigationHistory();
NavigationControllerImplJni.get()
.getDirectedNavigationHistory(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
history,
isForward,
itemLimit);
return history;
}
@Override
public void clearSslPreferences() {
if (mNativeNavigationControllerAndroid != 0) {
NavigationControllerImplJni.get()
.clearSslPreferences(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
}
@Override
public boolean getUseDesktopUserAgent() {
if (mNativeNavigationControllerAndroid == 0) return false;
return NavigationControllerImplJni.get()
.getUseDesktopUserAgent(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
@Override
public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange, int caller) {
if (mNativeNavigationControllerAndroid != 0) {
Log.i(
TAG,
"Thread dump for debugging, override: "
+ override
+ " reloadOnChange: "
+ reloadOnChange
+ " caller: "
+ caller);
Thread.dumpStack();
NavigationControllerImplJni.get()
.setUseDesktopUserAgent(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
override,
reloadOnChange,
caller);
}
}
@Override
public NavigationEntry getEntryAtIndex(int index) {
if (mNativeNavigationControllerAndroid != 0) {
return NavigationControllerImplJni.get()
.getEntryAtIndex(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
index);
}
return null;
}
@Override
public NavigationEntry getVisibleEntry() {
if (mNativeNavigationControllerAndroid != 0) {
return NavigationControllerImplJni.get()
.getVisibleEntry(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
return null;
}
@Override
public NavigationEntry getPendingEntry() {
if (mNativeNavigationControllerAndroid != 0) {
return NavigationControllerImplJni.get()
.getPendingEntry(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
return null;
}
@Override
public int getLastCommittedEntryIndex() {
if (mNativeNavigationControllerAndroid != 0) {
return NavigationControllerImplJni.get()
.getLastCommittedEntryIndex(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
return -1;
}
@Override
public boolean removeEntryAtIndex(int index) {
if (mNativeNavigationControllerAndroid != 0) {
return NavigationControllerImplJni.get()
.removeEntryAtIndex(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
index);
}
return false;
}
@Override
public void pruneForwardEntries() {
if (mNativeNavigationControllerAndroid == 0) return;
NavigationControllerImplJni.get()
.pruneForwardEntries(
mNativeNavigationControllerAndroid, NavigationControllerImpl.this);
}
@Override
public String getEntryExtraData(int index, String key) {
if (mNativeNavigationControllerAndroid == 0) return null;
return NavigationControllerImplJni.get()
.getEntryExtraData(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
index,
key);
}
@Override
public void setEntryExtraData(int index, String key, String value) {
if (mNativeNavigationControllerAndroid == 0) return;
NavigationControllerImplJni.get()
.setEntryExtraData(
mNativeNavigationControllerAndroid,
NavigationControllerImpl.this,
index,
key,
value);
}
@CalledByNative
private static void addToNavigationHistory(Object history, Object navigationEntry) {
((NavigationHistory) history).addEntry((NavigationEntry) navigationEntry);
}
@CalledByNative
private static NavigationEntry createNavigationEntry(
int index,
GURL url,
GURL virtualUrl,
GURL originalUrl,
String title,
Bitmap favicon,
int transition,
long timestamp,
boolean isInitialEntry) {
return new NavigationEntry(
index,
url,
virtualUrl,
originalUrl,
title,
favicon,
transition,
timestamp,
isInitialEntry);
}
@NativeMethods
interface Natives {
boolean canGoBack(long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
boolean canGoForward(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
boolean isInitialNavigation(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
void loadIfNecessary(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
boolean needsReload(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
void setNeedsReload(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
boolean canGoToOffset(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
int offset);
void goBack(long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
void goForward(long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
void goToOffset(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
int offset);
void goToNavigationIndex(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller, int index);
void cancelPendingReload(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
void continuePendingReload(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
void reload(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
boolean checkForRepost);
void reloadBypassingCache(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
boolean checkForRepost);
NavigationHandle loadUrl(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
String url,
int loadUrlType,
int transitionType,
String referrerUrl,
int referrerPolicy,
int uaOverrideOption,
String extraHeaders,
ResourceRequestBody postData,
String baseUrlForDataUrl,
String virtualUrlForSpecialCases,
String dataUrlAsString,
boolean canLoadLocalResources,
boolean isRendererInitiated,
boolean shouldReplaceCurrentEntry,
Origin initiatorOrigin,
boolean hasUserGesture,
boolean shouldClearHistoryList,
AdditionalNavigationParams additionalNavigationParams,
long inputStart,
long navigationUIDataPtr,
boolean isPdf);
void clearHistory(long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
int getNavigationHistory(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
Object history);
void getDirectedNavigationHistory(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
NavigationHistory history,
boolean isForward,
int itemLimit);
void clearSslPreferences(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
boolean getUseDesktopUserAgent(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
void setUseDesktopUserAgent(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
boolean override,
boolean reloadOnChange,
int source);
NavigationEntry getEntryAtIndex(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller, int index);
NavigationEntry getVisibleEntry(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
NavigationEntry getPendingEntry(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
int getLastCommittedEntryIndex(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
boolean removeEntryAtIndex(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller, int index);
void pruneForwardEntries(
long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
String getEntryExtraData(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
int index,
String key);
void setEntryExtraData(
long nativeNavigationControllerAndroid,
NavigationControllerImpl caller,
int index,
String key,
String value);
}
}