2026-02-19 21:31:49 +01:00

300 lines
9.4 KiB
C++

// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "context.h"
#include "include/cef_app.h"
#include "client_app.h"
#include "jni_util.h"
#if defined(OS_MACOSX)
#include "util_mac.h"
#endif
namespace {
Context* g_context = nullptr;
CefSettings GetJNISettings(JNIEnv* env, jobject obj) {
CefString tmp;
CefSettings settings;
#if defined(OS_POSIX) && !defined(OS_ANDROID)
settings.disable_signal_handlers = true;
#endif
if (!obj)
return settings;
ScopedJNIClass cls(env, "org/cef/CefSettings");
if (!cls)
return settings;
if (GetJNIFieldString(env, cls, obj, "browser_subprocess_path", &tmp) &&
!tmp.empty()) {
CefString(&settings.browser_subprocess_path) = tmp;
tmp.clear();
}
GetJNIFieldBoolean(env, cls, obj, "windowless_rendering_enabled",
&settings.windowless_rendering_enabled);
GetJNIFieldBoolean(env, cls, obj, "command_line_args_disabled",
&settings.command_line_args_disabled);
if (GetJNIFieldString(env, cls, obj, "cache_path", &tmp) && !tmp.empty()) {
CefString(&settings.cache_path) = tmp;
tmp.clear();
}
if (GetJNIFieldString(env, cls, obj, "root_cache_path", &tmp) &&
!tmp.empty()) {
CefString(&settings.root_cache_path) = tmp;
tmp.clear();
}
GetJNIFieldBoolean(env, cls, obj, "persist_session_cookies",
&settings.persist_session_cookies);
if (GetJNIFieldString(env, cls, obj, "user_agent", &tmp) && !tmp.empty()) {
CefString(&settings.user_agent) = tmp;
tmp.clear();
}
if (GetJNIFieldString(env, cls, obj, "user_agent_product", &tmp) &&
!tmp.empty()) {
CefString(&settings.user_agent_product) = tmp;
tmp.clear();
}
if (GetJNIFieldString(env, cls, obj, "locale", &tmp) && !tmp.empty()) {
CefString(&settings.locale) = tmp;
tmp.clear();
}
if (GetJNIFieldString(env, cls, obj, "log_file", &tmp) && !tmp.empty()) {
CefString(&settings.log_file) = tmp;
tmp.clear();
}
jobject obj_sev = nullptr;
if (GetJNIFieldObject(env, cls, obj, "log_severity", &obj_sev,
"Lorg/cef/CefSettings$LogSeverity;")) {
ScopedJNIObjectLocal severity(env, obj_sev);
if (IsJNIEnumValue(env, severity, "org/cef/CefSettings$LogSeverity",
"LOGSEVERITY_VERBOSE")) {
settings.log_severity = LOGSEVERITY_VERBOSE;
} else if (IsJNIEnumValue(env, severity, "org/cef/CefSettings$LogSeverity",
"LOGSEVERITY_INFO")) {
settings.log_severity = LOGSEVERITY_INFO;
} else if (IsJNIEnumValue(env, severity, "org/cef/CefSettings$LogSeverity",
"LOGSEVERITY_WARNING")) {
settings.log_severity = LOGSEVERITY_WARNING;
} else if (IsJNIEnumValue(env, severity, "org/cef/CefSettings$LogSeverity",
"LOGSEVERITY_ERROR")) {
settings.log_severity = LOGSEVERITY_ERROR;
} else if (IsJNIEnumValue(env, severity, "org/cef/CefSettings$LogSeverity",
"LOGSEVERITY_DISABLE")) {
settings.log_severity = LOGSEVERITY_DISABLE;
} else {
settings.log_severity = LOGSEVERITY_DEFAULT;
}
}
if (GetJNIFieldString(env, cls, obj, "javascript_flags", &tmp) &&
!tmp.empty()) {
CefString(&settings.javascript_flags) = tmp;
tmp.clear();
}
if (GetJNIFieldString(env, cls, obj, "resources_dir_path", &tmp) &&
!tmp.empty()) {
CefString(&settings.resources_dir_path) = tmp;
tmp.clear();
}
if (GetJNIFieldString(env, cls, obj, "locales_dir_path", &tmp) &&
!tmp.empty()) {
CefString(&settings.locales_dir_path) = tmp;
tmp.clear();
}
GetJNIFieldInt(env, cls, obj, "remote_debugging_port",
&settings.remote_debugging_port);
if (GetJNIFieldString(env, cls, obj, "chrome_policy_id", &tmp) &&
!tmp.empty()) {
CefString(&settings.chrome_policy_id) = tmp;
tmp.clear();
}
GetJNIFieldInt(env, cls, obj, "uncaught_exception_stack_size",
&settings.uncaught_exception_stack_size);
jobject obj_col = nullptr;
if (GetJNIFieldObject(env, cls, obj, "background_color", &obj_col,
"Lorg/cef/CefSettings$ColorType;")) {
ScopedJNIObjectLocal color_type(env, obj_col);
jlong jcolor = 0;
JNI_CALL_METHOD(env, color_type, "getColor", "()J", Long, jcolor);
settings.background_color = (cef_color_t)jcolor;
}
if (GetJNIFieldString(env, cls, obj, "cookieable_schemes_list", &tmp) &&
!tmp.empty()) {
CefString(&settings.cookieable_schemes_list) = tmp;
tmp.clear();
}
GetJNIFieldBoolean(env, cls, obj, "cookieable_schemes_exclude_defaults",
&settings.cookieable_schemes_exclude_defaults);
return settings;
}
} // namespace
// static
void Context::Create() {
new Context();
}
// static
void Context::Destroy() {
DCHECK(g_context);
if (g_context)
delete g_context;
}
// static
Context* Context::GetInstance() {
return g_context;
}
bool Context::PreInitialize(JNIEnv* env, jobject c) {
DCHECK(thread_checker_.CalledOnValidThread());
JavaVM* jvm;
jint rs = env->GetJavaVM(&jvm);
DCHECK_EQ(rs, JNI_OK);
if (rs != JNI_OK)
return JNI_FALSE;
SetJVM(jvm);
ScopedJNIClass javaClass(env, env->GetObjectClass(c));
ScopedJNIObjectResult javaClassLoader(env);
JNI_CALL_METHOD(env, javaClass, "getClassLoader", "()Ljava/lang/ClassLoader;",
Object, javaClassLoader);
ASSERT(javaClassLoader);
if (!javaClassLoader)
return false;
SetJavaClassLoader(env, javaClassLoader);
return true;
}
bool Context::Initialize(JNIEnv* env,
jobject c,
jobject appHandler,
jobject jsettings) {
DCHECK(thread_checker_.CalledOnValidThread());
#if defined(OS_WIN)
CefMainArgs main_args(::GetModuleHandle(nullptr));
#else
CefMainArgs main_args(0, nullptr);
#endif
CefSettings settings = GetJNISettings(env, jsettings);
// Sandbox is not supported because:
// - Use of a separate sub-process executable on Windows.
// - Use of a temporary file to communicate custom schemes to the
// renderer process.
settings.no_sandbox = true;
#if defined(OS_WIN) || defined(OS_LINUX)
// Use external message pump with OSR.
external_message_pump_ = !!settings.windowless_rendering_enabled;
if (!external_message_pump_) {
// Windowed rendering on Windows requires multi-threaded message loop,
// otherwise something eats the messages required by Java and the Java
// window becomes unresponsive.
//
// Actually the same appears to be true for Linux, which is why we also
// need multithreaded message loops there. Note that however, on Linux
// it is more difficult to get this to work: it is necessary for the first
// call to Xlib to be a call to XInitThreads! Since Java itself calls
// Xlib when it initializes the first window, an application must make
// sure to invoke this method before any other Xlib functions are called
// - including by the Java runtime itself, which makes this feat a little
// tricky. The CefApp class exposes a static method for this purpose,
// initXlibForMultithreading(), but the host application must load the
// jcef native lib by itself in order to use it, and it must invoke it
// VERY early, ideally at the beginning of the main method.
// Another neat trick to get this done is to create a special native lib
// just for this purpose like described in this StackOverflow thread:
// https://stackoverflow.com/questions/24559368
settings.multi_threaded_message_loop = true;
}
#endif
// Use CefAppHandler.onScheduleMessagePumpWork to schedule calls to
// DoMessageLoopWork.
settings.external_message_pump = external_message_pump_;
CefRefPtr<ClientApp> client_app(
new ClientApp(CefString(&settings.cache_path), env, appHandler));
bool res = false;
#if defined(OS_MACOSX)
res = util_mac::CefInitializeOnMainThread(main_args, settings,
client_app.get());
#else
res = CefInitialize(main_args, settings, client_app.get(), nullptr);
#endif
return res;
}
void Context::OnContextInitialized() {
REQUIRE_UI_THREAD();
temp_window_.reset(new TempWindow());
}
void Context::DoMessageLoopWork() {
DCHECK(thread_checker_.CalledOnValidThread());
#if defined(OS_MACOSX)
util_mac::CefDoMessageLoopWorkOnMainThread();
#else
CefDoMessageLoopWork();
#endif
}
void Context::Shutdown() {
DCHECK(thread_checker_.CalledOnValidThread());
// Clear scheme handler factories on shutdown to avoid refcount DCHECK.
CefClearSchemeHandlerFactories();
ClientApp::eraseTempFiles();
#if defined(OS_MACOSX)
util_mac::CefShutdownOnMainThread();
#else
// Pump CefDoMessageLoopWork a few times before shutting down.
if (external_message_pump_) {
for (int i = 0; i < 10; ++i)
CefDoMessageLoopWork();
}
temp_window_.reset(nullptr);
CefShutdown();
#endif
}
Context::Context() : external_message_pump_(true) {
DCHECK(!g_context);
g_context = this;
#if defined(OS_MACOSX)
// On macOS we create this object very early to allow LibraryLoader
// assignment. However, we still want the PreInitialize() call to determine
// thread ownership.
thread_checker_.DetachFromThread();
#endif
}
Context::~Context() {
DCHECK(thread_checker_.CalledOnValidThread());
g_context = nullptr;
#if defined(OS_MACOSX)
cef_unload_library();
#endif
}