blob: 06685e9cc0cb149a0d92b63b81d7fdfbef29b101 [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.
package org.chromium.weblayer_private;
import android.os.RemoteException;
import android.os.SystemClock;
import android.webkit.WebResourceResponse;
import org.chromium.base.TimeUtilsJni;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.embedder_support.util.WebResourceResponseInfo;
import org.chromium.weblayer_private.interfaces.APICallException;
import org.chromium.weblayer_private.interfaces.IClientPage;
import org.chromium.weblayer_private.interfaces.INavigateParams;
import org.chromium.weblayer_private.interfaces.INavigationController;
import org.chromium.weblayer_private.interfaces.INavigationControllerClient;
import org.chromium.weblayer_private.interfaces.IObjectWrapper;
import org.chromium.weblayer_private.interfaces.NavigateParams;
import org.chromium.weblayer_private.interfaces.ObjectWrapper;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import java.util.HashMap;
import java.util.Map;
/**
* Acts as the bridge between java and the C++ implementation of of NavigationController.
*/
@JNINamespace("weblayer")
public final class NavigationControllerImpl extends INavigationController.Stub {
private final TabImpl mTab;
private long mNativeNavigationController;
private INavigationControllerClient mNavigationControllerClient;
// Conversion between native TimeTicks and SystemClock.uptimeMillis().
private long mNativeTickOffsetUs;
private boolean mNativeTickOffsetUsComputed;
private Map<Long, PageImpl> mPages = new HashMap<>();
public NavigationControllerImpl(TabImpl tab, INavigationControllerClient client) {
mTab = tab;
mNavigationControllerClient = client;
mNativeNavigationController =
NavigationControllerImplJni.get().getNavigationController(tab.getNativeTab());
NavigationControllerImplJni.get().setNavigationControllerImpl(
mNativeNavigationController, NavigationControllerImpl.this);
}
@Override
public void navigate(String uri, NavigateParams params) {
StrictModeWorkaround.apply();
if (WebLayerFactoryImpl.getClientMajorVersion() < 83) {
assert params == null;
}
NavigationControllerImplJni.get().navigate(mNativeNavigationController, uri,
params == null ? false : params.mShouldReplaceCurrentEntry, false, false, false,
false, null);
}
@Override
public void navigate2(String uri, boolean shouldReplaceCurrentEntry,
boolean disableIntentProcessing, boolean disableNetworkErrorAutoReload,
boolean enableAutoPlay) {
StrictModeWorkaround.apply();
NavigationControllerImplJni.get().navigate(mNativeNavigationController, uri,
shouldReplaceCurrentEntry, disableIntentProcessing,
/*allowIntentLaunchesInBackground=*/false, disableNetworkErrorAutoReload,
enableAutoPlay, null);
}
@Override
public INavigateParams createNavigateParams() {
StrictModeWorkaround.apply();
return new NavigateParamsImpl();
}
@Override
public void navigate3(String uri, INavigateParams iParams) {
StrictModeWorkaround.apply();
NavigateParamsImpl params = (NavigateParamsImpl) iParams;
WebResourceResponseInfo responseInfo = null;
if (params.getResponse() != null) {
if (mTab.isActiveTab()) {
BrowserImpl browser = mTab.getBrowser();
UrlBarControllerImpl urlBarController = browser.getUrlBarControllerImpl();
if (urlBarController != null && urlBarController.hasActiveView()) {
throw new IllegalStateException(
"Can't navigate to an InputStream if the stock URL bar is visible.");
}
}
WebResourceResponse response =
ObjectWrapper.unwrap(params.getResponse(), WebResourceResponse.class);
responseInfo = new WebResourceResponseInfo(response.getMimeType(),
response.getEncoding(), response.getData(), response.getStatusCode(),
response.getReasonPhrase(), response.getResponseHeaders());
}
NavigationControllerImplJni.get().navigate(mNativeNavigationController, uri,
params.shouldReplaceCurrentEntry(), params.isIntentProcessingDisabled(),
params.areIntentLaunchesAllowedInBackground(),
params.isNetworkErrorAutoReloadDisabled(), params.isAutoPlayEnabled(),
responseInfo);
}
@Override
public void goBack() {
StrictModeWorkaround.apply();
NavigationControllerImplJni.get().goBack(mNativeNavigationController);
}
@Override
public void goForward() {
StrictModeWorkaround.apply();
NavigationControllerImplJni.get().goForward(mNativeNavigationController);
}
@Override
public boolean canGoBack() {
StrictModeWorkaround.apply();
return NavigationControllerImplJni.get().canGoBack(mNativeNavigationController);
}
@Override
public boolean canGoForward() {
StrictModeWorkaround.apply();
return NavigationControllerImplJni.get().canGoForward(mNativeNavigationController);
}
@Override
public void goToIndex(int index) {
StrictModeWorkaround.apply();
NavigationControllerImplJni.get().goToIndex(mNativeNavigationController, index);
}
@Override
public void reload() {
StrictModeWorkaround.apply();
NavigationControllerImplJni.get().reload(mNativeNavigationController);
}
@Override
public void stop() {
StrictModeWorkaround.apply();
NavigationControllerImplJni.get().stop(mNativeNavigationController);
}
@Override
public int getNavigationListSize() {
StrictModeWorkaround.apply();
return NavigationControllerImplJni.get().getNavigationListSize(mNativeNavigationController);
}
@Override
public int getNavigationListCurrentIndex() {
StrictModeWorkaround.apply();
return NavigationControllerImplJni.get().getNavigationListCurrentIndex(
mNativeNavigationController);
}
@Override
public String getNavigationEntryDisplayUri(int index) {
StrictModeWorkaround.apply();
return NavigationControllerImplJni.get().getNavigationEntryDisplayUri(
mNativeNavigationController, index);
}
@Override
public String getNavigationEntryTitle(int index) {
StrictModeWorkaround.apply();
return NavigationControllerImplJni.get().getNavigationEntryTitle(
mNativeNavigationController, index);
}
@Override
public boolean isNavigationEntrySkippable(int index) {
StrictModeWorkaround.apply();
return NavigationControllerImplJni.get().isNavigationEntrySkippable(
mNativeNavigationController, index);
}
public NavigationImpl getNavigationImplFromId(long id) {
StrictModeWorkaround.apply();
return NavigationControllerImplJni.get().getNavigationImplFromId(
mNativeNavigationController, id);
}
public PageImpl getPage(long nativePageImpl) {
// Ensure that each C++ object has only one Java counterpart so that the embedder sees the
// same object for multiple navigations that have the same Page.
PageImpl page = mPages.get(nativePageImpl);
if (page == null) {
IClientPage clientPage = null;
if (WebLayerFactoryImpl.getClientMajorVersion() >= 90) {
try {
clientPage = mNavigationControllerClient.createClientPage();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
page = new PageImpl(clientPage, nativePageImpl, this);
mPages.put(nativePageImpl, page);
}
return page;
}
public void onPageDestroyed(PageImpl page) {
mPages.remove(page.getNativePageImpl());
if (WebLayerFactoryImpl.getClientMajorVersion() >= 90) {
try {
mNavigationControllerClient.onPageDestroyed(page.getClientPage());
} catch (RemoteException e) {
throw new APICallException(e);
}
}
}
@CalledByNative
private NavigationImpl createNavigation(long nativeNavigationImpl) {
return new NavigationImpl(mNavigationControllerClient, nativeNavigationImpl, this);
}
@CalledByNative
private void navigationStarted(NavigationImpl navigation) throws RemoteException {
mNavigationControllerClient.navigationStarted(navigation.getClientNavigation());
}
@CalledByNative
private void navigationRedirected(NavigationImpl navigation) throws RemoteException {
mNavigationControllerClient.navigationRedirected(navigation.getClientNavigation());
}
@CalledByNative
private void readyToCommitNavigation(NavigationImpl navigation) throws RemoteException {
mNavigationControllerClient.readyToCommitNavigation(navigation.getClientNavigation());
}
@CalledByNative
private void navigationCompleted(NavigationImpl navigation) throws RemoteException {
mNavigationControllerClient.navigationCompleted(navigation.getClientNavigation());
}
@CalledByNative
private void navigationFailed(NavigationImpl navigation) throws RemoteException {
mNavigationControllerClient.navigationFailed(navigation.getClientNavigation());
}
@CalledByNative
private void loadStateChanged(boolean isLoading, boolean toDifferentDocument)
throws RemoteException {
mNavigationControllerClient.loadStateChanged(isLoading, toDifferentDocument);
}
@CalledByNative
private void loadProgressChanged(double progress) throws RemoteException {
mNavigationControllerClient.loadProgressChanged(progress);
}
@CalledByNative
private void onFirstContentfulPaint() throws RemoteException {
mNavigationControllerClient.onFirstContentfulPaint();
}
@CalledByNative
private void onFirstContentfulPaint2(
long navigationStartTick, long firstContentfulPaintDurationMs) throws RemoteException {
if (WebLayerFactoryImpl.getClientMajorVersion() < 88) return;
mNavigationControllerClient.onFirstContentfulPaint2(
(navigationStartTick - getNativeTickOffsetUs()) / 1000,
firstContentfulPaintDurationMs);
}
@CalledByNative
private void onLargestContentfulPaint(long navigationStartTick,
long largestContentfulPaintDurationMs) throws RemoteException {
if (WebLayerFactoryImpl.getClientMajorVersion() < 88) return;
mNavigationControllerClient.onLargestContentfulPaint(
(navigationStartTick - getNativeTickOffsetUs()) / 1000,
largestContentfulPaintDurationMs);
}
@CalledByNative
private void onOldPageNoLongerRendered(String uri) throws RemoteException {
mNavigationControllerClient.onOldPageNoLongerRendered(uri);
}
private long getNativeTickOffsetUs() {
// See logic in CustomTabsConnection.java that this was based on.
if (!mNativeTickOffsetUsComputed) {
// Compute offset from time ticks to uptimeMillis.
mNativeTickOffsetUsComputed = true;
long nativeNowUs = TimeUtilsJni.get().getTimeTicksNowUs();
long javaNowUs = SystemClock.uptimeMillis() * 1000;
mNativeTickOffsetUs = nativeNowUs - javaNowUs;
}
return mNativeTickOffsetUs;
}
private static final class NavigateParamsImpl extends INavigateParams.Stub {
private boolean mReplaceCurrentEntry;
private boolean mIntentProcessingDisabled;
private boolean mIntentLaunchesAllowedInBackground;
private boolean mNetworkErrorAutoReloadDisabled;
private boolean mAutoPlayEnabled;
private IObjectWrapper mResponse;
@Override
public void replaceCurrentEntry() {
mReplaceCurrentEntry = true;
}
@Override
public void disableIntentProcessing() {
mIntentProcessingDisabled = true;
}
@Override
public void allowIntentLaunchesInBackground() {
mIntentLaunchesAllowedInBackground = true;
}
@Override
public void disableNetworkErrorAutoReload() {
mNetworkErrorAutoReloadDisabled = true;
}
@Override
public void enableAutoPlay() {
mAutoPlayEnabled = true;
}
@Override
public void setResponse(IObjectWrapper response) {
mResponse = response;
}
public boolean shouldReplaceCurrentEntry() {
return mReplaceCurrentEntry;
}
public boolean isIntentProcessingDisabled() {
return mIntentProcessingDisabled;
}
public boolean areIntentLaunchesAllowedInBackground() {
return mIntentLaunchesAllowedInBackground;
}
public boolean isNetworkErrorAutoReloadDisabled() {
return mNetworkErrorAutoReloadDisabled;
}
public boolean isAutoPlayEnabled() {
return mAutoPlayEnabled;
}
IObjectWrapper getResponse() {
return mResponse;
}
}
@NativeMethods
interface Natives {
void setNavigationControllerImpl(
long nativeNavigationControllerImpl, NavigationControllerImpl caller);
long getNavigationController(long tab);
void navigate(long nativeNavigationControllerImpl, String uri,
boolean shouldReplaceCurrentEntry, boolean disableIntentProcessing,
boolean allowIntentLaunchesInBackground, boolean disableNetworkErrorAutoReload,
boolean enableAutoPlay, WebResourceResponseInfo response);
void goBack(long nativeNavigationControllerImpl);
void goForward(long nativeNavigationControllerImpl);
boolean canGoBack(long nativeNavigationControllerImpl);
boolean canGoForward(long nativeNavigationControllerImpl);
void goToIndex(long nativeNavigationControllerImpl, int index);
void reload(long nativeNavigationControllerImpl);
void stop(long nativeNavigationControllerImpl);
int getNavigationListSize(long nativeNavigationControllerImpl);
int getNavigationListCurrentIndex(long nativeNavigationControllerImpl);
String getNavigationEntryDisplayUri(long nativeNavigationControllerImpl, int index);
String getNavigationEntryTitle(long nativeNavigationControllerImpl, int index);
boolean isNavigationEntrySkippable(long nativeNavigationControllerImpl, int index);
NavigationImpl getNavigationImplFromId(long nativeNavigationControllerImpl, long id);
}
}