blob: 4b7cde422b2faef3a3100ad1216b381958a7e63e [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;
import android.net.Uri;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import org.chromium.weblayer_private.interfaces.APICallException;
import org.chromium.weblayer_private.interfaces.IClientNavigation;
import org.chromium.weblayer_private.interfaces.INavigation;
import java.util.ArrayList;
import java.util.List;
/**
* Information about a navigation.
*/
public class Navigation extends IClientNavigation.Stub {
private final INavigation mNavigationImpl;
// Constructor for test mocking.
protected Navigation() {
mNavigationImpl = null;
}
Navigation(INavigation impl) {
mNavigationImpl = impl;
}
@NavigationState
public int getState() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.getState();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* The uri the main frame is navigating to. This may change during the navigation when
* encountering a server redirect.
*/
@NonNull
public Uri getUri() {
ThreadCheck.ensureOnUiThread();
try {
return Uri.parse(mNavigationImpl.getUri());
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Returns the redirects that occurred on the way to the current page. The current page is the
* last one in the list (so even when there's no redirect, there will be one entry in the list).
*/
@NonNull
public List<Uri> getRedirectChain() {
ThreadCheck.ensureOnUiThread();
try {
List<Uri> redirects = new ArrayList<Uri>();
for (String r : mNavigationImpl.getRedirectChain()) redirects.add(Uri.parse(r));
return redirects;
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Returns the status code of the navigation. Returns 0 if the navigation hasn't completed yet
* or if a response wasn't received.
*/
public int getHttpStatusCode() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.getHttpStatusCode();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Whether the navigation happened without changing document. Examples of same document
* navigations are:
* - reference fragment navigations
* - pushState/replaceState
* - same page history navigation
*/
public boolean isSameDocument() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.isSameDocument();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Whether the navigation resulted in an error page (e.g. interstitial). Note that if an error
* page reloads, this will return true even though GetNetErrorCode will be kNoError.
*/
public boolean isErrorPage() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.isErrorPage();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Return information about the error, if any, that was encountered while loading the page.
*/
@LoadError
public int getLoadError() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.getLoadError();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Whether this navigation resulted in a download. Returns false if this navigation did not
* result in a download, or if download status is not yet known for this navigation. Download
* status is determined for a navigation when processing final (post redirect) HTTP response
* headers. This means the only time the embedder can know if it's a download is in
* NavigationCallback.onNavigationFailed.
*
* @since 84
*/
public boolean isDownload() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.isDownload();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Whether the target URL can be handled by the browser's internal protocol handlers, i.e., has
* a scheme that the browser knows how to process internally. Examples of such URLs are
* http(s) URLs, data URLs, and file URLs. A typical example of a URL for which there is no
* internal protocol handler (and for which this method would return also) is an intent:// URL.
*
* @return Whether the target URL of the navigation has a known protocol.
*
* @since 89
*/
public boolean isKnownProtocol() {
ThreadCheck.ensureOnUiThread();
if (WebLayer.getSupportedMajorVersionInternal() < 89) {
throw new UnsupportedOperationException();
}
try {
return mNavigationImpl.isKnownProtocol();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Whether this navigation resulted in an external intent being launched. Returns false if this
* navigation did not do so, or if that status is not yet known for this navigation. This
* status is determined for a navigation when processing final (post redirect) HTTP response
* headers. This means the only time the embedder can know if the navigation resulted in an
* external intent being launched is in NavigationCallback.onNavigationFailed.
*
* @since 89
*/
public boolean wasIntentLaunched() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.wasIntentLaunched();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Whether this navigation resulted in the user deciding whether an external intent should be
* launched (e.g., via a dialog). Returns false if this navigation did not resolve to such a
* user decision, or if that status is not yet known for this navigation. This status is
* determined for a navigation when processing final (post redirect) HTTP response headers. This
* means the only time the embedder can know this status definitively is in
* NavigationCallback.onNavigationFailed.
*
* @since 89
*/
public boolean isUserDecidingIntentLaunch() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.isUserDecidingIntentLaunch();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Whether this navigation was stopped before it could complete because
* NavigationController.stop() was called.
*
* @since 84
*/
public boolean wasStopCalled() {
ThreadCheck.ensureOnUiThread();
try {
return mNavigationImpl.wasStopCalled();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Sets a header for a network request. If a header with the specified name exists it is
* overwritten. This method can only be called at two times, from
* {@link NavigationCallback.onNavigationStarted} and {@link
* NavigationCallback.onNavigationRedirected}. When called during start, the header applies to
* both the initial network request as well as redirects.
*
* This method may be used to set the referer. If the referer is set in navigation start, it is
* reset during the redirect. In other words, if you need to set a referer that applies to
* redirects, then this must be called from {@link onNavigationRedirected}.
*
* Note that any headers that are set here won't be sent again if the frame html is fetched
* again due to a user reloading the page, navigating back and forth etc... when this fetch
* couldn't be cached (either in the disk cache or in the back-forward cache).
*
* @param name The name of the header. The name must be rfc 2616 compliant.
* @param value The value of the header. The value must not contain '\0', '\n' or '\r'.
*
* @throws IllegalArgumentException If supplied invalid values.
* @throws IllegalStateException If not called during start or a redirect.
*
* @since 83
*/
public void setRequestHeader(@NonNull String name, @NonNull String value) {
ThreadCheck.ensureOnUiThread();
try {
mNavigationImpl.setRequestHeader(name, value);
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Sets the user-agent string that applies to the current navigation. This user-agent is not
* sticky, it applies to this navigation only (and any redirects or resources that are loaded).
* This method may only be called from {@link NavigationCallback.onNavigationStarted}.
*
* Note that this user agent won't be sent again if the frame html is fetched again due to a
* user reloading the page, navigating back and forth etc... when this fetch couldn't be cached
* (either in the disk cache or in the back-forward cache).
*
* @param value The user-agent string. The value must not contain '\0', '\n' or '\r'. An empty
* string results in the default user-agent string.
*
* @throws IllegalArgumentException If supplied an invalid value.
* @throws IllegalStateException If not called during start or if {@link
* Tab.setDesktopUserAgent} was called with a value of true.
*
* @since 84
*/
public void setUserAgentString(@NonNull String value) {
ThreadCheck.ensureOnUiThread();
try {
mNavigationImpl.setUserAgentString(value);
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Returns whether the navigation was initiated by the page. Examples of page-initiated
* navigations:
* * Clicking <a> links.
* * changing window.location.href
* * redirect via the <meta http-equiv="refresh"> tag
* * using window.history.pushState
*
* This method returns false for navigations initiated by the WebLayer API, including using
* window.history.forward() or window.history.back().
*
* @return Whether the navigation was initiated by the page.
*
* @since 86
*/
public boolean isPageInitiated() {
ThreadCheck.ensureOnUiThread();
if (WebLayer.getSupportedMajorVersionInternal() < 86) {
throw new UnsupportedOperationException();
}
try {
return mNavigationImpl.isPageInitiated();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Whether the navigation is a reload. Examples of reloads include:
* * embedder-specified through NavigationController::Reload
* * page-initiated reloads, e.g. location.reload()
* * reloads when the network interface is reconnected
*
* @since 86
*/
public boolean isReload() {
ThreadCheck.ensureOnUiThread();
if (WebLayer.getSupportedMajorVersionInternal() < 86) {
throw new UnsupportedOperationException();
}
try {
return mNavigationImpl.isReload();
} catch (RemoteException e) {
throw new APICallException(e);
}
}
}