// Copyright 2019 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_public.browser;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.chromium.base.UserDataHost;
import org.chromium.net.NetError;
import org.chromium.ui.base.PageTransition;
import org.chromium.url.GURL;
import org.chromium.url.Origin;
/** JNI bridge with content::NavigationHandle */
@JNINamespace("content")
public class NavigationHandle {
private long mNativeNavigationHandleProxy;
private long mNativeNavigationHandle;
private boolean mIsInPrimaryMainFrame;
private boolean mIsRendererInitiated;
private boolean mIsSameDocument;
private @PageTransition int mPageTransition;
private GURL mUrl;
private GURL mReferrerUrl;
private GURL mBaseUrlForDataUrl;
private boolean mHasCommitted;
private boolean mIsDownload;
private boolean mIsErrorPage;
private boolean mIsPrimaryMainFrameFragmentNavigation;
private boolean mIsValidSearchFormUrl;
private @NetError int mErrorCode;
private int mHttpStatusCode;
private Origin mInitiatorOrigin;
private boolean mIsPost;
private boolean mHasUserGesture;
private boolean mIsRedirect;
private boolean mIsExternalProtocol;
private long mNavigationId;
private boolean mIsPageActivation;
private boolean mIsReload;
private UserDataHost mUserDataHost;
private boolean mIsPdf;
private String mMimeType;
private boolean mIsSaveableNavigation;
public static NavigationHandle createForTesting(
@NonNull GURL url,
boolean isRendererInitiated,
@PageTransition int transition,
boolean hasUserGesture) {
return createForTesting(
url,
/* isInPrimaryMainFrame= */ true,
/* isSameDocument= */ false,
isRendererInitiated,
transition,
hasUserGesture,
/* isReload= */ false,
/* isSaveableNavigation= */ false);
}
public static NavigationHandle createForTesting(
@NonNull GURL url,
boolean isInPrimaryMainFrame,
boolean isSameDocument,
boolean isRendererInitiated,
@PageTransition int transition,
boolean hasUserGesture,
boolean isReload) {
return createForTesting(
url,
isInPrimaryMainFrame,
isSameDocument,
isRendererInitiated,
transition,
hasUserGesture,
isReload,
/* isSaveableNavigation= */ false);
}
public static NavigationHandle createForTesting(
@NonNull GURL url,
boolean isInPrimaryMainFrame,
boolean isSameDocument,
boolean isRendererInitiated,
@PageTransition int transition,
boolean hasUserGesture,
boolean isReload,
boolean isSaveableNavigation) {
NavigationHandle handle = new NavigationHandle(0);
handle.initialize(
0,
url,
GURL.emptyGURL(),
GURL.emptyGURL(),
isInPrimaryMainFrame,
isSameDocument,
isRendererInitiated,
null,
transition,
/* isPost= */ false,
hasUserGesture,
/* isRedirect= */ false,
/* isExternalProtocol= */ false,
/* navigationId= */ 0,
/* isPageActivation= */ false,
isReload,
/* isPdf= */ false,
/* mimeType= */ "",
isSaveableNavigation);
return handle;
}
@CalledByNative
private NavigationHandle(long nativeNavigationHandle) {
mNativeNavigationHandle = nativeNavigationHandle;
}
@CalledByNative
private void initialize(
long nativeNavigationHandleProxy,
@NonNull GURL url,
@NonNull GURL referrerUrl,
@NonNull GURL baseUrlForDataUrl,
boolean isInPrimaryMainFrame,
boolean isSameDocument,
boolean isRendererInitiated,
Origin initiatorOrigin,
@PageTransition int transition,
boolean isPost,
boolean hasUserGesture,
boolean isRedirect,
boolean isExternalProtocol,
long navigationId,
boolean isPageActivation,
boolean isReload,
boolean isPdf,
String mimeType,
boolean isSaveableNavigation) {
mNativeNavigationHandleProxy = nativeNavigationHandleProxy;
mUrl = url;
mReferrerUrl = referrerUrl;
mBaseUrlForDataUrl = baseUrlForDataUrl;
mIsInPrimaryMainFrame = isInPrimaryMainFrame;
mIsSameDocument = isSameDocument;
mIsRendererInitiated = isRendererInitiated;
mInitiatorOrigin = initiatorOrigin;
mPageTransition = transition;
mIsPost = isPost;
mHasUserGesture = hasUserGesture;
mIsRedirect = isRedirect;
mIsExternalProtocol = isExternalProtocol;
mNavigationId = navigationId;
mIsPageActivation = isPageActivation;
mIsReload = isReload;
mIsPdf = isPdf;
mMimeType = mimeType;
mIsSaveableNavigation = isSaveableNavigation;
}
/**
* The navigation received a redirect. Called once per redirect.
*
* @param url The new URL.
*/
@CalledByNative
@VisibleForTesting
public void didRedirect(GURL url, boolean isExternalProtocol) {
mUrl = url;
mIsRedirect = true;
mIsExternalProtocol = isExternalProtocol;
}
/** The navigation finished. Called once per navigation. */
@CalledByNative
@VisibleForTesting
public void didFinish(
@NonNull GURL url,
boolean isErrorPage,
boolean hasCommitted,
boolean isPrimaryMainFrameFragmentNavigation,
boolean isDownload,
boolean isValidSearchFormUrl,
@PageTransition int transition,
@NetError int errorCode,
int httpStatuscode,
boolean isExternalProtocol,
boolean isPdf,
String mimeType,
boolean isSaveableNavigation) {
mUrl = url;
mIsErrorPage = isErrorPage;
mHasCommitted = hasCommitted;
mIsPrimaryMainFrameFragmentNavigation = isPrimaryMainFrameFragmentNavigation;
mIsDownload = isDownload;
mIsValidSearchFormUrl = isValidSearchFormUrl;
mPageTransition = transition;
mErrorCode = errorCode;
mHttpStatusCode = httpStatuscode;
mIsExternalProtocol = isExternalProtocol;
mIsPdf = isPdf;
mMimeType = mimeType;
mIsSaveableNavigation = isSaveableNavigation;
}
/** Release the C++ pointer. */
@CalledByNative
private void release() {
mNativeNavigationHandle = 0;
mNativeNavigationHandleProxy = 0;
}
public long nativeNavigationHandlePtr() {
return mNativeNavigationHandle;
}
/**
* The URL the frame is navigating to. This may change during the navigation when encountering
* a server redirect.
*/
@NonNull
public GURL getUrl() {
return mUrl;
}
/** The referrer URL for the navigation. */
@NonNull
public GURL getReferrerUrl() {
return mReferrerUrl;
}
/** Used for specifying a base URL for pages loaded via data URLs. */
@NonNull
public GURL getBaseUrlForDataUrl() {
return mBaseUrlForDataUrl;
}
/**
* Whether the navigation is taking place in the main frame of the primary
* frame tree. With MPArch (crbug.com/1164280), a WebContents may have
* additional frame trees for prerendering pages in addition to the primary
* frame tree (holding the page currently shown to the user). This remains
* constant over the navigation lifetime.
*/
public boolean isInPrimaryMainFrame() {
return mIsInPrimaryMainFrame;
}
/**
* Whether the navigation was initiated by the renderer process. Examples of renderer-initiated
* navigations include:
* - <a> link click
* - changing window.location.href
* - redirect via the <meta http-equiv="refresh"> tag
* - using window.history.pushState
*
* This method returns false for browser-initiated navigations, including:
* - any navigation initiated from the omnibox
* - navigations via suggestions in browser UI
* - navigations via browser UI: Ctrl-R, refresh/forward/back/home buttons
* - using window.history.forward() or window.history.back()
* - any other "explicit" URL navigations, e.g. bookmarks
*/
public boolean isRendererInitiated() {
return mIsRendererInitiated;
}
/**
* 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() {
return mIsSameDocument;
}
public String errorDescription() {
// TODO(shaktisahu): Provide appropriate error description (crbug/690784).
return "";
}
public @NetError int errorCode() {
return mErrorCode;
}
/**
* Whether the navigation has committed. Navigations that end up being downloads or return
* 204/205 response codes do not commit (i.e. the WebContents stays at the existing URL). This
* returns true for either successful commits or error pages that replace the previous page
* (distinguished by |IsErrorPage|), and false for errors that leave the user on the previous
* page.
*/
public boolean hasCommitted() {
return mHasCommitted;
}
/**
* Return the HTTP status code. This can be used after the response is received in
* didFinishNavigationInPrimaryMainFrame()
*/
public int httpStatusCode() {
return mHttpStatusCode;
}
/** Returns the page transition type. */
public @PageTransition int pageTransition() {
return mPageTransition;
}
/** Returns true on same-document navigation with fragment change in the primary main frame. */
public boolean isPrimaryMainFrameFragmentNavigation() {
return mIsPrimaryMainFrameFragmentNavigation;
}
/**
* Whether the navigation resulted in an error page.
* Note that if an error page reloads, this will return true even though GetNetErrorCode will be
* net::OK.
*/
public boolean isErrorPage() {
return mIsErrorPage;
}
/**
* Returns true if 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.
*/
public boolean isDownload() {
return mIsDownload;
}
/** Returns true if the navigation is a search. */
public boolean isValidSearchFormUrl() {
return mIsValidSearchFormUrl;
}
/**
* Get the Origin that initiated this navigation. May be null in the case of navigations
* originating from the browser.
*/
public Origin getInitiatorOrigin() {
return mInitiatorOrigin;
}
/** True if the the navigation method is "POST". */
public boolean isPost() {
return mIsPost;
}
/** True if the navigation was initiated by the user. */
public boolean hasUserGesture() {
return mHasUserGesture;
}
/** Is the navigation a redirect (in which case URL is the "target" address). */
public boolean isRedirect() {
return mIsRedirect;
}
/** True if the target URL can't be handled by Chrome's internal protocol handlers. */
public boolean isExternalProtocol() {
return mIsExternalProtocol;
}
/** Get a unique ID for this navigation. */
public long getNavigationId() {
return mNavigationId;
}
/*
* Whether this navigation is activating an existing page (e.g. served from
* the BackForwardCache or Prerender).
*/
public boolean isPageActivation() {
return mIsPageActivation;
}
/** Whether this navigation was initiated by a page reload. */
public boolean isReload() {
return mIsReload;
}
/** Return any user data which has been set on the NavigationHandle. */
public UserDataHost getUserDataHost() {
if (mUserDataHost == null) {
mUserDataHost = new UserDataHost();
}
return mUserDataHost;
}
/** Sets the user data host. This should not be considered part of the content API. */
public void setUserDataHost(UserDataHost userDataHost) {
mUserDataHost = userDataHost;
}
/** Whether the navigation is for PDF content. */
public boolean isPdf() {
return mIsPdf;
}
/** MIME type of the page. */
public String getMimeType() {
return mMimeType;
}
/** Whether this navigation can be saved so that it be reloaded or synced. */
public boolean isSaveableNavigation() {
return mIsSaveableNavigation;
}
}