// 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.
#include "content/browser/renderer_host/render_frame_host_android.h"
#include <jni.h>
#include <utility>
#include "base/android/callback_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/unguessable_token_android.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "content/browser/bad_message.h"
#include "content/browser/closewatcher/close_listener_host.h"
#include "content/browser/renderer_host/render_frame_host_delegate.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/site_instance.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
#include "url/android/gurl_android.h"
#include "url/origin.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "content/public/android/content_jni_headers/RenderFrameHostImpl_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF16;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
namespace content {
namespace {
void OnGetCanonicalUrlForSharing(
const base::android::JavaRef<jobject>& jcallback,
const std::optional<GURL>& url) {
JNIEnv* env = base::android::AttachCurrentThread();
if (!url) {
base::android::RunObjectCallbackAndroid(jcallback,
url::GURLAndroid::EmptyGURL(env));
return;
}
base::android::RunObjectCallbackAndroid(
jcallback, url::GURLAndroid::FromNativeGURL(env, url.value()));
}
void JavaScriptResultCallback(
const base::android::ScopedJavaGlobalRef<jobject>& callback,
base::Value result) {
JNIEnv* env = base::android::AttachCurrentThread();
std::string json;
base::JSONWriter::Write(result, &json);
base::android::ScopedJavaLocalRef<jstring> j_json =
ConvertUTF8ToJavaString(env, json);
Java_RenderFrameHostImpl_onEvaluateJavaScriptResult(env, j_json, callback);
}
} // namespace
// static
RenderFrameHost* RenderFrameHost::FromJavaRenderFrameHost(
const JavaRef<jobject>& jrender_frame_host_android) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (jrender_frame_host_android.is_null())
return nullptr;
RenderFrameHostAndroid* render_frame_host_android =
reinterpret_cast<RenderFrameHostAndroid*>(
Java_RenderFrameHostImpl_getNativePointer(
AttachCurrentThread(), jrender_frame_host_android));
if (!render_frame_host_android)
return nullptr;
return render_frame_host_android->render_frame_host();
}
RenderFrameHostAndroid::RenderFrameHostAndroid(
RenderFrameHostImpl* render_frame_host)
: render_frame_host_(render_frame_host) {}
RenderFrameHostAndroid::~RenderFrameHostAndroid() {
// Avoid unnecessarily creating the java object from the destructor.
if (obj_.is_uninitialized())
return;
ScopedJavaLocalRef<jobject> jobj = GetJavaObject();
if (!jobj.is_null()) {
Java_RenderFrameHostImpl_clearNativePtr(AttachCurrentThread(), jobj);
obj_.reset();
}
}
base::android::ScopedJavaLocalRef<jobject>
RenderFrameHostAndroid::GetJavaObject() {
JNIEnv* env = base::android::AttachCurrentThread();
if (obj_.is_uninitialized()) {
const bool is_incognito = render_frame_host_->GetSiteInstance()
->GetBrowserContext()
->IsOffTheRecord();
const GlobalRenderFrameHostId rfh_id = render_frame_host_->GetGlobalId();
ScopedJavaLocalRef<jobject> local_ref = Java_RenderFrameHostImpl_create(
env, reinterpret_cast<intptr_t>(this),
render_frame_host_->delegate()->GetJavaRenderFrameHostDelegate(),
is_incognito, rfh_id.child_id, rfh_id.frame_routing_id);
obj_ = JavaObjectWeakGlobalRef(env, local_ref);
return local_ref;
}
return obj_.get(env);
}
ScopedJavaLocalRef<jobject> RenderFrameHostAndroid::GetLastCommittedURL(
JNIEnv* env) const {
return url::GURLAndroid::FromNativeGURL(
env, render_frame_host_->GetLastCommittedURL());
}
ScopedJavaLocalRef<jobject> RenderFrameHostAndroid::GetLastCommittedOrigin(
JNIEnv* env) {
return render_frame_host_->GetLastCommittedOrigin().ToJavaObject();
}
ScopedJavaLocalRef<jobject> RenderFrameHostAndroid::GetMainFrame(JNIEnv* env) {
return render_frame_host_->GetMainFrame()->GetJavaRenderFrameHost();
}
void RenderFrameHostAndroid::GetCanonicalUrlForSharing(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcallback) const {
render_frame_host_->GetCanonicalUrl(base::BindOnce(
&OnGetCanonicalUrlForSharing,
base::android::ScopedJavaGlobalRef<jobject>(env, jcallback)));
}
std::vector<ScopedJavaLocalRef<jobject>>
RenderFrameHostAndroid::GetAllRenderFrameHosts(JNIEnv* env) const {
std::vector<ScopedJavaLocalRef<jobject>> ret;
render_frame_host_->ForEachRenderFrameHost([&ret](RenderFrameHostImpl* rfh) {
ret.push_back(rfh->GetJavaRenderFrameHost());
});
return ret;
}
bool RenderFrameHostAndroid::IsFeatureEnabled(
JNIEnv* env,
jint feature) const {
return render_frame_host_->IsFeatureEnabled(
static_cast<blink::mojom::PermissionsPolicyFeature>(feature));
}
base::UnguessableToken RenderFrameHostAndroid::GetAndroidOverlayRoutingToken(
JNIEnv* env) const {
return render_frame_host_->GetOverlayRoutingToken();
}
void RenderFrameHostAndroid::NotifyUserActivation(JNIEnv* env) {
render_frame_host_->GetAssociatedLocalFrame()->NotifyUserActivation(
blink::mojom::UserActivationNotificationType::kVoiceSearch);
}
void RenderFrameHostAndroid::NotifyWebAuthnAssertionRequestSucceeded(
JNIEnv* env) {
render_frame_host_->WebAuthnAssertionRequestSucceeded();
}
jboolean RenderFrameHostAndroid::IsCloseWatcherActive(JNIEnv* env) const {
auto* close_listener_host =
CloseListenerHost::GetOrCreateForCurrentDocument(render_frame_host_);
return close_listener_host->IsActive();
}
jboolean RenderFrameHostAndroid::SignalCloseWatcherIfActive(JNIEnv* env) const {
auto* close_listener_host =
CloseListenerHost::GetOrCreateForCurrentDocument(render_frame_host_);
return close_listener_host->SignalIfActive();
}
jboolean RenderFrameHostAndroid::IsRenderFrameLive(JNIEnv* env) const {
return render_frame_host_->IsRenderFrameLive();
}
void RenderFrameHostAndroid::GetInterfaceToRendererFrame(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& interface_name,
jlong message_pipe_raw_handle) const {
DCHECK(render_frame_host_->IsRenderFrameLive());
render_frame_host_->GetRemoteInterfaces()->GetInterfaceByName(
ConvertJavaStringToUTF8(env, interface_name),
mojo::ScopedMessagePipeHandle(
mojo::MessagePipeHandle(message_pipe_raw_handle)));
}
void RenderFrameHostAndroid::TerminateRendererDueToBadMessage(
JNIEnv* env,
jint reason) const {
DCHECK_LT(reason, bad_message::BAD_MESSAGE_MAX);
ReceivedBadMessage(render_frame_host_->GetProcess(),
static_cast<bad_message::BadMessageReason>(reason));
}
jboolean RenderFrameHostAndroid::IsProcessBlocked(JNIEnv* env) const {
return render_frame_host_->GetProcess()->IsBlocked();
}
void RenderFrameHostAndroid::PerformGetAssertionWebAuthSecurityChecks(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& relying_party_id,
const base::android::JavaParamRef<jobject>& effective_origin,
jboolean is_payment_credential_get_assertion,
const base::android::JavaParamRef<jobject>& callback) const {
url::Origin origin = url::Origin::FromJavaObject(effective_origin);
render_frame_host_->PerformGetAssertionWebAuthSecurityChecks(
ConvertJavaStringToUTF8(env, relying_party_id), origin,
is_payment_credential_get_assertion,
base::BindOnce(
[](base::android::ScopedJavaGlobalRef<jobject> callback,
blink::mojom::AuthenticatorStatus status, bool is_cross_origin) {
base::android::RunObjectCallbackAndroid(
callback,
Java_RenderFrameHostImpl_createWebAuthSecurityChecksResults(
base::android::AttachCurrentThread(),
static_cast<jint>(status), is_cross_origin));
},
base::android::ScopedJavaGlobalRef<jobject>(callback)));
}
void RenderFrameHostAndroid::PerformMakeCredentialWebAuthSecurityChecks(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& relying_party_id,
const base::android::JavaParamRef<jobject>& effective_origin,
jboolean is_payment_credential_creation,
const base::android::JavaParamRef<jobject>& callback) const {
url::Origin origin = url::Origin::FromJavaObject(effective_origin);
render_frame_host_->PerformMakeCredentialWebAuthSecurityChecks(
ConvertJavaStringToUTF8(env, relying_party_id), origin,
is_payment_credential_creation,
base::BindOnce(
[](base::android::ScopedJavaGlobalRef<jobject> callback,
blink::mojom::AuthenticatorStatus status, bool is_cross_origin) {
base::android::RunObjectCallbackAndroid(
callback,
Java_RenderFrameHostImpl_createWebAuthSecurityChecksResults(
base::android::AttachCurrentThread(),
static_cast<jint>(status), is_cross_origin));
},
base::android::ScopedJavaGlobalRef<jobject>(callback)));
}
jint RenderFrameHostAndroid::GetLifecycleState(JNIEnv* env) const {
return static_cast<jint>(render_frame_host_->GetLifecycleState());
}
void RenderFrameHostAndroid::ExecuteJavaScriptInIsolatedWorld(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& jscript,
jint jworldId,
const base::android::JavaParamRef<jobject>& jcallback) {
if (!jcallback) {
render_frame_host()->ExecuteJavaScriptInIsolatedWorld(
ConvertJavaStringToUTF16(env, jscript), base::DoNothing(), jworldId);
return;
}
// Secure the Java callback in a scoped object and give ownership of it to the
// base::OnceCallback below.
base::android::ScopedJavaGlobalRef<jobject> java_callback;
java_callback.Reset(env, jcallback);
render_frame_host()->ExecuteJavaScriptInIsolatedWorld(
ConvertJavaStringToUTF16(env, jscript),
base::BindOnce(&JavaScriptResultCallback, java_callback), jworldId);
}
void RenderFrameHostAndroid::InsertVisualStateCallback(
JNIEnv* env,
const JavaParamRef<jobject>& jcallback) {
render_frame_host()->InsertVisualStateCallback(
base::BindOnce(&base::android::RunBooleanCallbackAndroid,
base::android::ScopedJavaGlobalRef<jobject>(jcallback)));
}
} // namespace content