chromium/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java

// Copyright 2017 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 androidx.annotation.Nullable;

import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.JniType;
import org.jni_zero.NativeMethods;

import org.chromium.base.Callback;
import org.chromium.base.UnguessableToken;
import org.chromium.blink.mojom.AuthenticatorStatus;
import org.chromium.content_public.browser.GlobalRenderFrameHostId;
import org.chromium.content_public.browser.JavaScriptCallback;
import org.chromium.content_public.browser.LifecycleState;
import org.chromium.content_public.browser.PermissionsPolicyFeature;
import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.mojo.bindings.Interface;
import org.chromium.mojo.bindings.InterfaceRequest;
import org.chromium.mojo.system.Pair;
import org.chromium.mojo.system.impl.CoreImpl;
import org.chromium.url.GURL;
import org.chromium.url.Origin;

import java.util.List;

/**
 * The RenderFrameHostImpl Java wrapper to allow communicating with the native RenderFrameHost
 * object.
 */
@JNINamespace("content")
public class RenderFrameHostImpl implements RenderFrameHost {
    private long mNativeRenderFrameHostAndroid;
    // mDelegate can be null.
    private final RenderFrameHostDelegate mDelegate;
    private final boolean mIncognito;
    private final GlobalRenderFrameHostId mRenderFrameHostId;

    private RenderFrameHostImpl(
            long nativeRenderFrameHostAndroid,
            RenderFrameHostDelegate delegate,
            boolean isIncognito,
            int renderProcessId,
            int renderFrameId) {
        mNativeRenderFrameHostAndroid = nativeRenderFrameHostAndroid;
        mDelegate = delegate;
        mIncognito = isIncognito;
        mRenderFrameHostId = new GlobalRenderFrameHostId(renderProcessId, renderFrameId);

        mDelegate.renderFrameCreated(this);
    }

    @CalledByNative
    private static RenderFrameHostImpl create(
            long nativeRenderFrameHostAndroid,
            RenderFrameHostDelegate delegate,
            boolean isIncognito,
            int renderProcessId,
            int renderFrameId) {
        return new RenderFrameHostImpl(
                nativeRenderFrameHostAndroid,
                delegate,
                isIncognito,
                renderProcessId,
                renderFrameId);
    }

    @CalledByNative
    private void clearNativePtr() {
        mNativeRenderFrameHostAndroid = 0;
        mDelegate.renderFrameDeleted(this);
    }

    @CalledByNative
    private long getNativePointer() {
        return mNativeRenderFrameHostAndroid;
    }

    /**
     * Get the delegate associated with this RenderFrameHost.
     *
     * @return The delegate associated with this RenderFrameHost.
     */
    public RenderFrameHostDelegate getRenderFrameHostDelegate() {
        return mDelegate;
    }

    public long getNativePtr() {
        return mNativeRenderFrameHostAndroid;
    }

    @Override
    @Nullable
    public GURL getLastCommittedURL() {
        if (mNativeRenderFrameHostAndroid == 0) return null;
        return RenderFrameHostImplJni.get().getLastCommittedURL(mNativeRenderFrameHostAndroid);
    }

    @Override
    @Nullable
    public Origin getLastCommittedOrigin() {
        if (mNativeRenderFrameHostAndroid == 0) return null;
        return RenderFrameHostImplJni.get().getLastCommittedOrigin(mNativeRenderFrameHostAndroid);
    }

    @Override
    @Nullable
    public RenderFrameHost getMainFrame() {
        if (mNativeRenderFrameHostAndroid == 0) return null;
        return RenderFrameHostImplJni.get().getMainFrame(mNativeRenderFrameHostAndroid);
    }

    @Override
    public void getCanonicalUrlForSharing(Callback<GURL> callback) {
        if (mNativeRenderFrameHostAndroid == 0) {
            callback.onResult(null);
            return;
        }
        RenderFrameHostImplJni.get()
                .getCanonicalUrlForSharing(mNativeRenderFrameHostAndroid, callback);
    }

    @Override
    public List<RenderFrameHost> getAllRenderFrameHosts() {
        if (mNativeRenderFrameHostAndroid == 0) return null;
        return RenderFrameHostImplJni.get().getAllRenderFrameHosts(mNativeRenderFrameHostAndroid);
    }

    @Override
    public boolean isFeatureEnabled(@PermissionsPolicyFeature int feature) {
        return mNativeRenderFrameHostAndroid != 0
                && RenderFrameHostImplJni.get()
                        .isFeatureEnabled(mNativeRenderFrameHostAndroid, feature);
    }

    /**
     * TODO(timloh): This function shouldn't really be on here. If we end up needing more logic from
     * the native BrowserContext, we should add a wrapper for that and move this function there.
     */
    @Override
    public boolean isIncognito() {
        return mIncognito;
    }

    @Override
    public void notifyUserActivation() {
        if (mNativeRenderFrameHostAndroid == 0) return;
        RenderFrameHostImplJni.get().notifyUserActivation(mNativeRenderFrameHostAndroid);
    }

    @Override
    public void notifyWebAuthnAssertionRequestSucceeded() {
        if (mNativeRenderFrameHostAndroid == 0) return;
        RenderFrameHostImplJni.get()
                .notifyWebAuthnAssertionRequestSucceeded(mNativeRenderFrameHostAndroid);
    }

    @Override
    public boolean isCloseWatcherActive() {
        return RenderFrameHostImplJni.get().isCloseWatcherActive(mNativeRenderFrameHostAndroid);
    }

    @Override
    public boolean signalCloseWatcherIfActive() {
        return RenderFrameHostImplJni.get()
                .signalCloseWatcherIfActive(mNativeRenderFrameHostAndroid);
    }

    @Override
    public boolean isRenderFrameLive() {
        if (mNativeRenderFrameHostAndroid == 0) return false;
        return RenderFrameHostImplJni.get().isRenderFrameLive(mNativeRenderFrameHostAndroid);
    }

    @Override
    public <I extends Interface, P extends Interface.Proxy> P getInterfaceToRendererFrame(
            Interface.Manager<I, P> manager) {
        if (mNativeRenderFrameHostAndroid == 0) return null;
        Pair<P, InterfaceRequest<I>> result = manager.getInterfaceRequest(CoreImpl.getInstance());
        RenderFrameHostImplJni.get()
                .getInterfaceToRendererFrame(
                        mNativeRenderFrameHostAndroid,
                        manager.getName(),
                        result.second.passHandle().releaseNativeHandle());
        return result.first;
    }

    @Override
    public void terminateRendererDueToBadMessage(int reason) {
        if (mNativeRenderFrameHostAndroid == 0) return;
        RenderFrameHostImplJni.get()
                .terminateRendererDueToBadMessage(mNativeRenderFrameHostAndroid, reason);
    }

    /** Return the AndroidOverlay routing token for this RenderFrameHostImpl. */
    @Nullable
    public UnguessableToken getAndroidOverlayRoutingToken() {
        if (mNativeRenderFrameHostAndroid == 0) return null;
        return RenderFrameHostImplJni.get()
                .getAndroidOverlayRoutingToken(mNativeRenderFrameHostAndroid);
    }

    @Override
    public boolean areInputEventsIgnored() {
        if (mNativeRenderFrameHostAndroid == 0) return false;
        return RenderFrameHostImplJni.get().isProcessBlocked(mNativeRenderFrameHostAndroid);
    }

    @Override
    public void performGetAssertionWebAuthSecurityChecks(
            String relyingPartyId,
            Origin effectiveOrigin,
            boolean isPaymentCredentialGetAssertion,
            Callback<RenderFrameHost.WebAuthSecurityChecksResults> callback) {
        if (mNativeRenderFrameHostAndroid == 0) {
            var result =
                    new WebAuthSecurityChecksResults(
                            AuthenticatorStatus.UNKNOWN_ERROR, /* isCrossOrigin= */ false);
            callback.onResult(result);
            return;
        }
        RenderFrameHostImplJni.get()
                .performGetAssertionWebAuthSecurityChecks(
                        mNativeRenderFrameHostAndroid,
                        relyingPartyId,
                        effectiveOrigin,
                        isPaymentCredentialGetAssertion,
                        callback);
    }

    @CalledByNative
    private static RenderFrameHost.WebAuthSecurityChecksResults createWebAuthSecurityChecksResults(
            @AuthenticatorStatus.EnumType int securityCheckResult, boolean isCrossOrigin) {
        return new WebAuthSecurityChecksResults(securityCheckResult, isCrossOrigin);
    }

    @CalledByNative
    private static void onEvaluateJavaScriptResult(String jsonResult, JavaScriptCallback callback) {
        callback.handleJavaScriptResult(jsonResult);
    }

    @Override
    public void performMakeCredentialWebAuthSecurityChecks(
            String relyingPartyId,
            Origin effectiveOrigin,
            boolean isPaymentCredentialCreation,
            Callback<RenderFrameHost.WebAuthSecurityChecksResults> callback) {
        if (mNativeRenderFrameHostAndroid == 0) {
            var result =
                    new WebAuthSecurityChecksResults(
                            AuthenticatorStatus.UNKNOWN_ERROR, /* isCrossOrigin= */ false);
            callback.onResult(result);
            return;
        }

        RenderFrameHostImplJni.get()
                .performMakeCredentialWebAuthSecurityChecks(
                        mNativeRenderFrameHostAndroid,
                        relyingPartyId,
                        effectiveOrigin,
                        isPaymentCredentialCreation,
                        callback);
    }

    @Override
    public GlobalRenderFrameHostId getGlobalRenderFrameHostId() {
        return mRenderFrameHostId;
    }

    @Override
    @LifecycleState
    public int getLifecycleState() {
        if (mNativeRenderFrameHostAndroid == 0) return LifecycleState.PENDING_DELETION;
        return RenderFrameHostImplJni.get().getLifecycleState(mNativeRenderFrameHostAndroid);
    }

    @Override
    public void insertVisualStateCallback(Callback<Boolean> callback) {
        if (mNativeRenderFrameHostAndroid == 0) {
            callback.onResult(false);
        }
        RenderFrameHostImplJni.get()
                .insertVisualStateCallback(mNativeRenderFrameHostAndroid, callback);
    }

    @Override
    public void executeJavaScriptInIsolatedWorld(
            String script, int worldId, @Nullable JavaScriptCallback callback) {
        RenderFrameHostImplJni.get()
                .executeJavaScriptInIsolatedWorld(
                        mNativeRenderFrameHostAndroid, script, worldId, callback);
    }

    @NativeMethods
    interface Natives {
        GURL getLastCommittedURL(long nativeRenderFrameHostAndroid);

        Origin getLastCommittedOrigin(long nativeRenderFrameHostAndroid);

        RenderFrameHost getMainFrame(long nativeRenderFrameHostAndroid);

        void getCanonicalUrlForSharing(long nativeRenderFrameHostAndroid, Callback<GURL> callback);

        @JniType("std::vector")
        List<RenderFrameHost> getAllRenderFrameHosts(long nativeRenderFrameHostAndroid);

        boolean isFeatureEnabled(
                long nativeRenderFrameHostAndroid, @PermissionsPolicyFeature int feature);

        @JniType("base::UnguessableToken")
        UnguessableToken getAndroidOverlayRoutingToken(long nativeRenderFrameHostAndroid);

        void notifyUserActivation(long nativeRenderFrameHostAndroid);

        void notifyWebAuthnAssertionRequestSucceeded(long nativeRenderFrameHostAndroid);

        boolean isCloseWatcherActive(long nativeRenderFrameHostAndroid);

        boolean signalCloseWatcherIfActive(long nativeRenderFrameHostAndroid);

        boolean isRenderFrameLive(long nativeRenderFrameHostAndroid);

        void getInterfaceToRendererFrame(
                long nativeRenderFrameHostAndroid, String interfacename, long messagePipeRawHandle);

        void terminateRendererDueToBadMessage(long nativeRenderFrameHostAndroid, int reason);

        boolean isProcessBlocked(long nativeRenderFrameHostAndroid);

        void performGetAssertionWebAuthSecurityChecks(
                long nativeRenderFrameHostAndroid,
                String relyingPartyId,
                Origin effectiveOrigin,
                boolean isPaymentCredentialGetAssertion,
                Callback<RenderFrameHost.WebAuthSecurityChecksResults> callback);

        void performMakeCredentialWebAuthSecurityChecks(
                long nativeRenderFrameHostAndroid,
                String relyingPartyId,
                Origin effectiveOrigin,
                boolean isPaymentCredentialCreation,
                Callback<RenderFrameHost.WebAuthSecurityChecksResults> callback);

        int getLifecycleState(long nativeRenderFrameHostAndroid);

        void insertVisualStateCallback(
                long nativeRenderFrameHostAndroid, Callback<Boolean> callback);

        void executeJavaScriptInIsolatedWorld(
                long nativeRenderFrameHostAndroid,
                String stript,
                int isolatedWorldId,
                JavaScriptCallback callback);
    }
}