blob: 616738d321263b638d13211af26dfafcbbeb7415 [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 org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.weblayer_private.interfaces.APICallException;
import org.chromium.weblayer_private.interfaces.IClientNavigation;
import org.chromium.weblayer_private.interfaces.INavigation;
import org.chromium.weblayer_private.interfaces.INavigationControllerClient;
import org.chromium.weblayer_private.interfaces.LoadError;
import org.chromium.weblayer_private.interfaces.NavigationState;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import java.util.Arrays;
import java.util.List;
/**
* Implementation of INavigation.
*/
@JNINamespace("weblayer")
public final class NavigationImpl extends INavigation.Stub {
private final IClientNavigation mClientNavigation;
// WARNING: NavigationImpl may outlive the native side, in which case this member is set to 0.
private long mNativeNavigationImpl;
// Set to true if/when it is determined that an external intent was launched for this
// navigation.
private boolean mIntentLaunched;
// Set to true if/when it is determined that this navigation result in UI being presented to the
// user via which the user will determine whether an intent should be launched.
private boolean mIsUserDecidingIntentLaunch;
public NavigationImpl(INavigationControllerClient client, long nativeNavigationImpl) {
mNativeNavigationImpl = nativeNavigationImpl;
try {
mClientNavigation = client.createClientNavigation(this);
} catch (RemoteException e) {
throw new APICallException(e);
}
NavigationImplJni.get().setJavaNavigation(mNativeNavigationImpl, NavigationImpl.this);
}
public IClientNavigation getClientNavigation() {
return mClientNavigation;
}
@NavigationState
private static int implTypeToJavaType(@ImplNavigationState int type) {
switch (type) {
case ImplNavigationState.WAITING_RESPONSE:
return NavigationState.WAITING_RESPONSE;
case ImplNavigationState.RECEIVING_BYTES:
return NavigationState.RECEIVING_BYTES;
case ImplNavigationState.COMPLETE:
return NavigationState.COMPLETE;
case ImplNavigationState.FAILED:
return NavigationState.FAILED;
}
assert false;
return NavigationState.FAILED;
}
@Override
@NavigationState
public int getState() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return implTypeToJavaType(NavigationImplJni.get().getState(mNativeNavigationImpl));
}
@Override
public String getUri() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().getUri(mNativeNavigationImpl);
}
@Override
public List<String> getRedirectChain() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return Arrays.asList(NavigationImplJni.get().getRedirectChain(mNativeNavigationImpl));
}
@Override
public int getHttpStatusCode() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().getHttpStatusCode(mNativeNavigationImpl);
}
@Override
public boolean isSameDocument() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().isSameDocument(mNativeNavigationImpl);
}
@Override
public boolean isErrorPage() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().isErrorPage(mNativeNavigationImpl);
}
@Override
public int getLoadError() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return implLoadErrorToLoadError(
NavigationImplJni.get().getLoadError(mNativeNavigationImpl));
}
@Override
public void setRequestHeader(String name, String value) {
if (!NavigationImplJni.get().isValidRequestHeaderName(name)) {
throw new IllegalArgumentException("Invalid header");
}
if (!NavigationImplJni.get().isValidRequestHeaderValue(value)) {
throw new IllegalArgumentException("Invalid value");
}
if (!NavigationImplJni.get().setRequestHeader(mNativeNavigationImpl, name, value)) {
throw new IllegalStateException();
}
}
@Override
public void setUserAgentString(String value) {
if (!NavigationImplJni.get().isValidRequestHeaderValue(value)) {
throw new IllegalArgumentException("Invalid value");
}
if (!NavigationImplJni.get().setUserAgentString(mNativeNavigationImpl, value)) {
throw new IllegalStateException();
}
}
@Override
public boolean isDownload() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().isDownload(mNativeNavigationImpl);
}
@Override
public boolean isKnownProtocol() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().isKnownProtocol(mNativeNavigationImpl);
}
@Override
public boolean wasIntentLaunched() {
return mIntentLaunched;
}
@Override
public boolean isUserDecidingIntentLaunch() {
return mIsUserDecidingIntentLaunch;
}
@Override
public boolean wasStopCalled() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().wasStopCalled(mNativeNavigationImpl);
}
@Override
public boolean isPageInitiated() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().isPageInitiated(mNativeNavigationImpl);
}
@Override
public boolean isReload() {
StrictModeWorkaround.apply();
throwIfNativeDestroyed();
return NavigationImplJni.get().isReload(mNativeNavigationImpl);
}
public void setIntentLaunched() {
mIntentLaunched = true;
}
public void setIsUserDecidingIntentLaunch() {
mIsUserDecidingIntentLaunch = true;
}
private void throwIfNativeDestroyed() {
if (mNativeNavigationImpl == 0) {
throw new IllegalStateException("Using Navigation after native destroyed");
}
}
@LoadError
private static int implLoadErrorToLoadError(@ImplLoadError int loadError) {
switch (loadError) {
case ImplLoadError.NO_ERROR:
return LoadError.NO_ERROR;
case ImplLoadError.HTTP_CLIENT_ERROR:
return LoadError.HTTP_CLIENT_ERROR;
case ImplLoadError.HTTP_SERVER_ERROR:
return LoadError.HTTP_SERVER_ERROR;
case ImplLoadError.SSL_ERROR:
return LoadError.SSL_ERROR;
case ImplLoadError.CONNECTIVITY_ERROR:
return LoadError.CONNECTIVITY_ERROR;
case ImplLoadError.OTHER_ERROR:
return LoadError.OTHER_ERROR;
case ImplLoadError.SAFE_BROWSING_ERROR:
return LoadError.SAFE_BROWSING_ERROR;
default:
throw new IllegalArgumentException("Unexpected load error " + loadError);
}
}
@CalledByNative
private void onNativeDestroyed() {
mNativeNavigationImpl = 0;
// TODO: this should likely notify delegate in some way.
}
@NativeMethods
interface Natives {
void setJavaNavigation(long nativeNavigationImpl, NavigationImpl caller);
int getState(long nativeNavigationImpl);
String getUri(long nativeNavigationImpl);
String[] getRedirectChain(long nativeNavigationImpl);
int getHttpStatusCode(long nativeNavigationImpl);
boolean isSameDocument(long nativeNavigationImpl);
boolean isErrorPage(long nativeNavigationImpl);
boolean isDownload(long nativeNavigationImpl);
boolean isKnownProtocol(long nativeNavigationImpl);
boolean wasStopCalled(long nativeNavigationImpl);
int getLoadError(long nativeNavigationImpl);
boolean setRequestHeader(long nativeNavigationImpl, String name, String value);
boolean isValidRequestHeaderName(String name);
boolean isValidRequestHeaderValue(String value);
boolean setUserAgentString(long nativeNavigationImpl, String value);
boolean isPageInitiated(long nativeNavigationImpl);
boolean isReload(long nativeNavigationImpl);
}
}