diff --git a/Cargo.lock b/Cargo.lock index 624be1ce309..7cf65c6422a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,23 +238,21 @@ dependencies = [ [[package]] name = "android-activity" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", "bitflags 2.11.0", "cc", - "cesu8", - "jni 0.21.1", - "jni-sys 0.3.1", + "jni 0.22.4", "libc", "log", "ndk", "ndk-context", "ndk-sys", "num_enum", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] @@ -4518,18 +4516,32 @@ dependencies = [ [[package]] name = "jni" -version = "0.21.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ - "cesu8", "cfg-if", "combine", - "jni-sys 0.3.1", + "jni-macros", + "jni-sys 0.4.1", "log", - "thiserror 1.0.69", + "simd_cesu8", + "thiserror 2.0.18", "walkdir", - "windows-sys 0.45.0", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", ] [[package]] @@ -7113,13 +7125,13 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni 0.21.1", + "jni 0.22.4", "log", "once_cell", "rustls", @@ -9042,7 +9054,7 @@ dependencies = [ "hitrace", "image", "ipc-channel", - "jni 0.21.1", + "jni 0.22.4", "keyboard-types", "libc", "log", @@ -9157,6 +9169,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simd_helpers" version = "0.1.0" @@ -9166,6 +9188,12 @@ dependencies = [ "quote", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "simplecss" version = "0.2.2" @@ -11250,7 +11278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core 0.58.0", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -11306,7 +11334,7 @@ dependencies = [ "windows-interface 0.58.0", "windows-result 0.2.0", "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -11439,7 +11467,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -11467,7 +11495,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -11488,22 +11516,13 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -11515,35 +11534,20 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -11564,36 +11568,18 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -11606,48 +11592,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index a7f0e61822e..36fcba68abf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -162,7 +162,7 @@ rsa = { version = "0.9.10", features = ["sha1", "sha2"] } rustc-hash = "2.1.2" rustls = { version = "0.23", default-features = false, features = ["logging", "std", "tls12"] } rustls-pki-types = "1.14" -rustls-platform-verifier = "0.6.2" +rustls-platform-verifier = "0.7.0" sea-query = { version = "=1.0.0-rc.31", default-features = false, features = ["backend-sqlite", "derive"] } sea-query-rusqlite = { version = "=0.8.0-rc.15" } sec1 = "0.7" diff --git a/deny.toml b/deny.toml index 0573f66ece5..961cd7c29e1 100644 --- a/deny.toml +++ b/deny.toml @@ -107,8 +107,9 @@ skip = [ # New versions of these dependencies is pulled in by GStreamer / GLib. "itertools", - # btleplug requires 0.19 + # Duplicated by btleplug "jni", + "jni-sys", # Duplicated by egui "foldhash", @@ -127,14 +128,6 @@ skip = [ # Duplicated by winit. "windows-sys", - "windows-targets", - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", # wgpu has the latest and greatest. "windows-core", diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index e42dbc7dc0b..9502f197938 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -87,7 +87,7 @@ webdriver_server = { workspace = true } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.15" -jni = "0.21.1" +jni = "0.22" [target.'cfg(not(target_os = "android"))'.dependencies] backtrace = { workspace = true } diff --git a/ports/servoshell/egl/android/mod.rs b/ports/servoshell/egl/android/mod.rs index dfc7e2d0a65..2e6ea97f862 100644 --- a/ports/servoshell/egl/android/mod.rs +++ b/ports/servoshell/egl/android/mod.rs @@ -8,13 +8,15 @@ use std::cell::RefCell; use std::os::raw::{c_char, c_int, c_void}; use std::ptr::NonNull; use std::rc::Rc; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use android_logger::{self, Config, FilterBuilder}; use euclid::{Point2D, Rect, Scale, Size2D}; -use jni::objects::{GlobalRef, JClass, JObject, JString, JValue, JValueOwned}; +use jni::errors::{Error, ThrowRuntimeExAndDefault}; +use jni::objects::{Global, JClass, JObject, JString, JValue, JValueOwned}; +use jni::strings::JNIStr; use jni::sys::{jboolean, jfloat, jint, jobject}; -use jni::{JNIEnv, JavaVM}; +use jni::{Env, EnvUnowned, JavaVM, jni_sig, jni_str}; use keyboard_types::{Key, NamedKey}; use log::{debug, error, info, warn}; use raw_window_handle::{ @@ -35,6 +37,12 @@ thread_local! { pub static APP: RefCell>> = const { RefCell::new(None) }; } +static CALLBACK_OBJECT: OnceLock>> = OnceLock::new(); + +fn callback_ref() -> &'static JObject<'static> { + CALLBACK_OBJECT.get().expect("Servo init failed").as_ref() +} + struct InitOptions { args: Vec, url: Option, @@ -47,7 +55,6 @@ struct InitOptions { } struct HostCallbacks { - callbacks: GlobalRef, jvm: JavaVM, } @@ -64,267 +71,287 @@ pub extern "C" fn android_main() { // this currently. } -fn call(env: &mut JNIEnv, f: F) +fn call(env: &mut Env, f: F) where F: FnOnce(&App), { APP.with(|app| match app.borrow().as_ref() { Some(app) => (f)(app), - None => throw(env, "Servo not available in this thread"), + None => throw(env, jni_str!("Servo not available in this thread")), }); } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_version<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ) -> JString<'local> { let version = crate::VERSION; - env.new_string(version) - .unwrap_or_else(|_str| JObject::null().into()) + env.with_env(|env| -> jni::errors::Result<_> { env.new_string(version) }) + .resolve::() } /// Initialize Servo. At that point, we need a valid GL context. In the future, this will /// be done in multiple steps. #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_init<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, _activity: JObject<'local>, opts: JObject<'local>, callbacks_obj: JObject<'local>, surface: JObject<'local>, ) { - let (init_opts, log, log_str, _gst_debug_str) = match get_options(&mut env, &opts, &surface) { - Ok((opts, log, log_str, gst_debug_str)) => (opts, log, log_str, gst_debug_str), - Err(err) => { - throw(&mut env, &err); - return; - }, - }; + env.with_env(|env| -> jni::errors::Result<_> { + let (init_opts, log, log_str, _gst_debug_str) = get_options(env, &opts, &surface)?; - if log { - // Note: Android debug logs are stripped from a release build. - // debug!() will only show in a debug build. Use info!() if logs - // should show up in adb logcat with a release build. - let filters = [ - "servo", - "servoshell", - "servoshell::egl:gl_glue", - // Show redirected stdout / stderr by default - "servoshell::egl::log", - // Show JS errors by default. - "script::dom::bindings::error", - // Show GL errors by default. - "servo_canvas::webgl_thread", - "paint::paint", - "servo_constellation::constellation", - ]; - let mut filter_builder = FilterBuilder::new(); - for &module in &filters { - filter_builder.filter_module(module, log::LevelFilter::Debug); - } - if let Some(log_str) = log_str { - for module in log_str.split(',') { + if log { + // Note: Android debug logs are stripped from a release build. + // debug!() will only show in a debug build. Use info!() if logs + // should show up in adb logcat with a release build. + let filters = [ + "servo", + "servoshell", + "servoshell::egl:gl_glue", + // Show redirected stdout / stderr by default + "servoshell::egl::log", + // Show JS errors by default. + "script::dom::bindings::error", + // Show GL errors by default. + "servo_canvas::webgl_thread", + "paint::paint", + "servo_constellation::constellation", + ]; + let mut filter_builder = FilterBuilder::new(); + for &module in &filters { filter_builder.filter_module(module, log::LevelFilter::Debug); } + if let Some(log_str) = log_str { + for module in log_str.split(',') { + filter_builder.filter_module(module, log::LevelFilter::Debug); + } + } + + android_logger::init_once( + Config::default() + .with_max_level(log::LevelFilter::Debug) + .with_filter(filter_builder.build()) + .with_tag("servoshell"), + ) } - android_logger::init_once( - Config::default() - .with_max_level(log::LevelFilter::Debug) - .with_filter(filter_builder.build()) - .with_tag("servoshell"), - ) - } + info!("init"); - info!("init"); + // We only redirect stdout and stderr for non-production builds, since it is + // only used for debugging purposes. This saves us one thread in production. + #[cfg(not(servo_production))] + if let Err(e) = super::log::redirect_stdout_and_stderr() { + error!("Failed to redirect stdout and stderr to logcat due to: {e:?}"); + } - // We only redirect stdout and stderr for non-production builds, since it is - // only used for debugging purposes. This saves us one thread in production. - #[cfg(not(servo_production))] - if let Err(e) = super::log::redirect_stdout_and_stderr() { - error!("Failed to redirect stdout and stderr to logcat due to: {e:?}"); - } + let callbacks: Global> = env.new_global_ref(callbacks_obj)?; - let callbacks_ref = match env.new_global_ref(callbacks_obj) { - Ok(r) => r, - Err(_) => { - throw( - &mut env, - "Failed to get global reference of callback argument", - ); - return; - }, - }; + CALLBACK_OBJECT + .set(callbacks) + .expect("CALLBACK_OBJECT was already initialized."); - let event_loop_waker = Box::new(WakeupCallback::new(callbacks_ref.clone(), &env)); - let host = Rc::new(HostCallbacks::new(callbacks_ref, &env)); + let jvm = env.get_java_vm()?; + let event_loop_waker = Box::new(WakeupCallback::new(jvm.clone())); - crate::init_crypto(); + let host = Rc::new(HostCallbacks::new(jvm)); - let (opts, mut preferences, servoshell_preferences) = - match parse_command_line_arguments(init_opts.args.as_slice()) { - ArgumentParsingResult::ContentProcess(..) => { - unreachable!("Android does not have support for multiprocess yet.") - }, - ArgumentParsingResult::ChromeProcess(opts, preferences, servoshell_preferences) => { - (opts, preferences, servoshell_preferences) - }, - ArgumentParsingResult::Exit => { - std::process::exit(0); - }, - ArgumentParsingResult::ErrorParsing => std::process::exit(1), + crate::init_crypto(); + + let (opts, mut preferences, servoshell_preferences) = + match parse_command_line_arguments(init_opts.args.as_slice()) { + ArgumentParsingResult::ContentProcess(..) => { + unreachable!("Android does not have support for multiprocess yet.") + }, + ArgumentParsingResult::ChromeProcess(opts, preferences, servoshell_preferences) => { + (opts, preferences, servoshell_preferences) + }, + ArgumentParsingResult::Exit => { + std::process::exit(0); + }, + ArgumentParsingResult::ErrorParsing => std::process::exit(1), + }; + + preferences.set_value("viewport_meta_enabled", servo::PrefValue::Bool(true)); + + crate::init_tracing(servoshell_preferences.tracing_filter.as_deref()); + + let (display_handle, window_handle) = unsafe { + ( + DisplayHandle::borrow_raw(init_opts.display_handle), + WindowHandle::borrow_raw(init_opts.window_handle), + ) }; - preferences.set_value("viewport_meta_enabled", servo::PrefValue::Bool(true)); + let hidpi_scale_factor = Scale::new(init_opts.density); - crate::init_tracing(servoshell_preferences.tracing_filter.as_deref()); - - let (display_handle, window_handle) = unsafe { - ( - DisplayHandle::borrow_raw(init_opts.display_handle), - WindowHandle::borrow_raw(init_opts.window_handle), - ) - }; - - let hidpi_scale_factor = Scale::new(init_opts.density); - - APP.with(|app| { - let new_app = App::new(AppInitOptions { - host, - event_loop_waker, - initial_url: init_opts.url, - opts, - preferences, - servoshell_preferences, - #[cfg(feature = "webxr")] - xr_discovery: init_opts.xr_discovery, + APP.with(|app| { + let new_app = App::new(AppInitOptions { + host, + event_loop_waker, + initial_url: init_opts.url, + opts, + preferences, + servoshell_preferences, + #[cfg(feature = "webxr")] + xr_discovery: init_opts.xr_discovery, + }); + new_app.add_platform_window( + display_handle, + window_handle, + init_opts.viewport_rect, + hidpi_scale_factor, + ); + *app.borrow_mut() = Some(new_app); }); - new_app.add_platform_window( - display_handle, - window_handle, - init_opts.viewport_rect, - hidpi_scale_factor, - ); - *app.borrow_mut() = Some(new_app); - }); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_setExperimentalMode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, enable: jboolean, ) { debug!("setExperimentalMode {enable}"); - call(&mut env, |s| { - for pref in EXPERIMENTAL_PREFS { - s.servo().set_preference(pref, PrefValue::Bool(enable != 0)); - } - }); + env.with_env(|env| -> jni::errors::Result<_> { + call(env, |s| { + for pref in EXPERIMENTAL_PREFS { + s.servo().set_preference(pref, PrefValue::Bool(enable)); + } + }); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_resize<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, coordinates: JObject<'local>, ) { - let viewport_rect = jni_coordinate_to_rust_viewport_rect(&mut env, &coordinates); - debug!("resize {viewport_rect:#?}"); - match viewport_rect { - Ok(viewport_rect) => call(&mut env, |s| s.resize(viewport_rect)), - Err(error) => throw(&mut env, &error), - } + env.with_env(|env| -> jni::errors::Result<_> { + let viewport_rect = jni_coordinate_to_rust_viewport_rect(env, &coordinates)?; + debug!("resize {viewport_rect:#?}"); + call(env, |s| s.resize(viewport_rect)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_performUpdates<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ) { - debug!("performUpdates"); - call(&mut env, |app| { - app.spin_event_loop(); - }); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("performUpdates"); + call(env, |app| app.spin_event_loop()); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_loadUri<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, url: JString<'local>, ) { - debug!("loadUri"); - match env.get_string(&url) { - Ok(url) => { - let url: String = url.into(); - call(&mut env, |s| s.load_uri(&url)); - }, - Err(_) => { - throw(&mut env, "Failed to convert Java string"); - }, - }; + env.with_env(|env| -> jni::errors::Result<_> { + debug!("loadUri"); + call(env, |s| s.load_uri(&url.to_string())); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_reload<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ) { - debug!("reload"); - call(&mut env, |s| s.reload()); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("reload"); + call(env, |s| s.reload()); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_stop<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ) { - debug!("stop"); - call(&mut env, |s| s.stop()); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("stop"); + call(env, |s| s.stop()); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_goBack<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ) { - debug!("goBack"); - call(&mut env, |s| s.go_back()); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("goBack"); + call(env, |s| s.go_back()); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_goForward<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ) { - debug!("goForward"); - call(&mut env, |s| s.go_forward()); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("goForward"); + call(env, |s| s.go_forward()); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_scroll<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, dx: jint, dy: jint, x: jint, y: jint, ) { - debug!("scroll"); - call(&mut env, |s| { - s.scroll(dx as f32, dy as f32, x as f32, y as f32) - }); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("scroll"); + call(env, |s| s.scroll(dx as f32, dy as f32, x as f32, y as f32)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_doFrame<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, ) { - call(&mut env, |s| s.notify_vsync()); + env.with_env(|env| -> jni::errors::Result<_> { + call(env, |s| s.notify_vsync()); + Ok(()) + }) + .resolve::() } enum KeyCode { @@ -378,239 +405,304 @@ fn key_from_unicode_keycode(unicode: u32, keycode: i32) -> Option { #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_keydown<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, keycode: jint, unicode: jint, ) { - debug!("keydown {keycode}"); - if let Some(key) = key_from_unicode_keycode(unicode as u32, keycode) { - call(&mut env, move |s| s.key_down(key)); - } + env.with_env(|env| -> jni::errors::Result<_> { + debug!("keydown {keycode}"); + if let Some(key) = key_from_unicode_keycode(unicode as u32, keycode) { + call(env, move |s| s.key_down(key)); + } + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_keyup<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, keycode: jint, unicode: jint, ) { - debug!("keyup {keycode}"); - if let Some(key) = key_from_unicode_keycode(unicode as u32, keycode) { - call(&mut env, move |s| s.key_up(key)); - } + env.with_env(|env| -> jni::errors::Result<_> { + debug!("keyup {keycode}"); + if let Some(key) = key_from_unicode_keycode(unicode as u32, keycode) { + call(env, move |s| s.key_up(key)); + } + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_touchDown<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, x: jfloat, y: jfloat, pointer_id: jint, ) { - debug!("touchDown"); - call(&mut env, |s| s.touch_down(x, y, pointer_id)); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("touchDown"); + call(env, |s| s.touch_down(x, y, pointer_id)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_touchUp<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, x: jfloat, y: jfloat, pointer_id: jint, ) { - debug!("touchUp"); - call(&mut env, |s| s.touch_up(x, y, pointer_id)); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("touchUp"); + call(env, |s| s.touch_up(x, y, pointer_id)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_touchMove<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, x: jfloat, y: jfloat, pointer_id: jint, ) { - debug!("touchMove"); - call(&mut env, |s| s.touch_move(x, y, pointer_id)); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("touchMove"); + call(env, |s| s.touch_move(x, y, pointer_id)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_touchCancel<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, x: jfloat, y: jfloat, pointer_id: jint, ) { - debug!("touchCancel"); - call(&mut env, |s| s.touch_cancel(x, y, pointer_id)); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("touchCancel"); + call(env, |s| s.touch_cancel(x, y, pointer_id)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_pinchZoomStart<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, factor: jfloat, x: jfloat, y: jfloat, ) { - debug!("pinchZoomStart"); - call(&mut env, |s| s.pinchzoom_start(factor, x, y)); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("pinchZoomStart"); + call(env, |s| s.pinchzoom_start(factor, x, y)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_pinchZoom<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, factor: jfloat, x: jfloat, y: jfloat, ) { - debug!("pinchZoom"); - call(&mut env, |s| s.pinchzoom(factor, x, y)); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("pinchZoom"); + call(env, |s| s.pinchzoom(factor, x, y)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_pinchZoomEnd<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, factor: jfloat, x: jfloat, y: jfloat, ) { - debug!("pinchZoomEnd"); - call(&mut env, |s| s.pinchzoom_end(factor, x, y)); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("pinchZoomEnd"); + call(env, |s| s.pinchzoom_end(factor, x, y)); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_click( - mut env: JNIEnv, + mut env: EnvUnowned, _: JClass, x: jfloat, y: jfloat, ) { - debug!("click"); - call(&mut env, |s| { - s.mouse_down(x, y, MouseButton::Left); - s.mouse_up(x, y, MouseButton::Left); - }); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("click"); + call(env, |s| { + s.mouse_down(x, y, MouseButton::Left); + s.mouse_up(x, y, MouseButton::Left); + }); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] -pub extern "C" fn Java_org_servo_servoview_JNIServo_pausePainting(mut env: JNIEnv, _: JClass<'_>) { - debug!("pausePainting"); - call(&mut env, |s| s.pause_painting()); +pub extern "C" fn Java_org_servo_servoview_JNIServo_pausePainting( + mut env: EnvUnowned, + _: JClass<'_>, +) { + env.with_env(|env| -> jni::errors::Result<_> { + debug!("pausePainting"); + call(env, |s| s.pause_painting()); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_resumePainting<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, surface: JObject<'local>, coordinates: JObject<'local>, ) { - debug!("resumePainting"); - let viewport_rect = match jni_coordinate_to_rust_viewport_rect(&mut env, &coordinates) { - Ok(viewport_rect) => viewport_rect, - Err(error) => return throw(&mut env, &error), - }; + env.with_env(|env| -> jni::errors::Result<_> { + debug!("resumePainting"); + let viewport_rect = jni_coordinate_to_rust_viewport_rect(env, &coordinates)?; + let (_, window_handle) = display_and_window_handle(env, &surface); - let (_, window_handle) = display_and_window_handle(&mut env, &surface); - call(&mut env, |app| { - app.resume_painting(window_handle, viewport_rect); - }); + call(env, |s| { + s.resume_painting(window_handle, viewport_rect); + }); + Ok(()) + }) + .resolve::() } #[unsafe(no_mangle)] pub extern "C" fn Java_org_servo_servoview_JNIServo_mediaSessionAction<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _: JClass<'local>, action: jint, ) { - debug!("mediaSessionAction"); + env.with_env(|env| -> jni::errors::Result<_> { + debug!("mediaSessionAction"); - let action = match action { - 1 => MediaSessionActionType::Play, - 2 => MediaSessionActionType::Pause, - 3 => MediaSessionActionType::SeekBackward, - 4 => MediaSessionActionType::SeekForward, - 5 => MediaSessionActionType::PreviousTrack, - 6 => MediaSessionActionType::NextTrack, - 7 => MediaSessionActionType::SkipAd, - 8 => MediaSessionActionType::Stop, - 9 => MediaSessionActionType::SeekTo, - _ => return warn!("Ignoring unknown MediaSessionAction"), - }; - call(&mut env, |s| s.media_session_action(action.clone())); + let action = match action { + 1 => MediaSessionActionType::Play, + 2 => MediaSessionActionType::Pause, + 3 => MediaSessionActionType::SeekBackward, + 4 => MediaSessionActionType::SeekForward, + 5 => MediaSessionActionType::PreviousTrack, + 6 => MediaSessionActionType::NextTrack, + 7 => MediaSessionActionType::SkipAd, + 8 => MediaSessionActionType::Stop, + 9 => MediaSessionActionType::SeekTo, + _ => { + warn!("Ignoring unknown MediaSessionAction"); + return Ok(()); + }, + }; + call(env, |s| s.media_session_action(action.clone())); + Ok(()) + }) + .resolve::() } pub struct WakeupCallback { - callback: GlobalRef, jvm: Arc, } impl WakeupCallback { - pub fn new(callback: GlobalRef, env: &JNIEnv) -> WakeupCallback { - let jvm = Arc::new(env.get_java_vm().unwrap()); - WakeupCallback { callback, jvm } + fn new(jvm: JavaVM) -> WakeupCallback { + WakeupCallback { jvm: Arc::new(jvm) } } } impl EventLoopWaker for WakeupCallback { fn clone_box(&self) -> Box { - Box::new(WakeupCallback { - callback: self.callback.clone(), - jvm: self.jvm.clone(), - }) + let jvm = self.jvm.clone(); + Box::new(WakeupCallback { jvm }) } fn wake(&self) { debug!("wakeup"); - let mut env = self.jvm.attach_current_thread().unwrap(); - env.call_method(self.callback.as_obj(), "wakeup", "()V", &[]) + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + env.call_method(callback_ref(), jni_str!("wakeup"), jni_sig!("()V"), &[])?; + Ok(()) + }) .unwrap(); } } impl HostCallbacks { - pub fn new(callbacks: GlobalRef, env: &JNIEnv) -> HostCallbacks { - let jvm = env.get_java_vm().unwrap(); - HostCallbacks { callbacks, jvm } + fn new(jvm: JavaVM) -> HostCallbacks { + HostCallbacks { jvm } } } impl HostTrait for HostCallbacks { fn show_alert(&self, message: String) { - let mut env = self.jvm.get_env().unwrap(); - let Ok(string) = new_string_as_jvalue(&mut env, &message) else { - return; - }; - env.call_method( - self.callbacks.as_obj(), - "onAlert", - "(Ljava/lang/String;)V", - &[(&string).into()], - ) - .unwrap(); + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + let Ok(string) = new_string_as_jvalue(env, &message) else { + return Ok(()); + }; + env.call_method( + callback_ref(), + jni_str!("onAlert"), + jni_sig!("(Ljava/lang/String;)V"), + &[(&string).into()], + )?; + Ok(()) + }) + .unwrap(); } fn notify_load_status_changed(&self, load_status: LoadStatus) { debug!("notify_load_status_changed: {load_status:?}"); - let mut env = self.jvm.get_env().unwrap(); - match load_status { - LoadStatus::Started => { - env.call_method(self.callbacks.as_obj(), "onLoadStarted", "()V", &[]) - .unwrap(); - }, - LoadStatus::HeadParsed => {}, - LoadStatus::Complete => { - env.call_method(self.callbacks.as_obj(), "onLoadEnded", "()V", &[]) - .unwrap(); - }, - }; + self.jvm + .attach_current_thread(|env| match load_status { + LoadStatus::Started => env + .call_method( + callback_ref(), + jni_str!("onLoadStarted"), + jni_sig!("()V"), + &[], + ) + .map(|_| ()), + LoadStatus::HeadParsed => Ok(()), + LoadStatus::Complete => env + .call_method( + callback_ref(), + jni_str!("onLoadEnded"), + jni_sig!("()V"), + &[], + ) + .map(|_| ()), + }) + .unwrap(); } fn on_shutdown_complete(&self) { @@ -619,97 +711,119 @@ impl HostTrait for HostCallbacks { fn on_title_changed(&self, title: Option) { debug!("on_title_changed"); - let mut env = self.jvm.get_env().unwrap(); - let title = title.unwrap_or_default(); - let Ok(title_string) = new_string_as_jvalue(&mut env, &title) else { - return; - }; - env.call_method( - self.callbacks.as_obj(), - "onTitleChanged", - "(Ljava/lang/String;)V", - &[(&title_string).into()], - ) - .unwrap(); + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + let title = title.unwrap_or_default(); + let Ok(title_string) = new_string_as_jvalue(env, &title) else { + return Ok(()); + }; + env.call_method( + callback_ref(), + jni_str!("onTitleChanged"), + jni_sig!("(Ljava/lang/String;)V"), + &[(&title_string).into()], + )?; + Ok(()) + }) + .unwrap(); } fn on_url_changed(&self, url: String) { debug!("on_url_changed"); - let mut env = self.jvm.get_env().unwrap(); - let Ok(url_string) = new_string_as_jvalue(&mut env, &url) else { - return; - }; - env.call_method( - self.callbacks.as_obj(), - "onUrlChanged", - "(Ljava/lang/String;)V", - &[(&url_string).into()], - ) - .unwrap(); + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + let Ok(url_string) = new_string_as_jvalue(env, &url) else { + return Ok(()); + }; + + env.call_method( + callback_ref(), + jni_str!("onUrlChanged"), + jni_sig!("(Ljava/lang/String;)V"), + &[(&url_string).into()], + )?; + Ok(()) + }) + .unwrap(); } fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool) { debug!("on_history_changed"); - let mut env = self.jvm.get_env().unwrap(); - let can_go_back = JValue::Bool(can_go_back as jboolean); - let can_go_forward = JValue::Bool(can_go_forward as jboolean); - env.call_method( - self.callbacks.as_obj(), - "onHistoryChanged", - "(ZZ)V", - &[can_go_back, can_go_forward], - ) - .unwrap(); + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + let can_go_back = JValue::Bool(can_go_back as jboolean); + let can_go_forward = JValue::Bool(can_go_forward as jboolean); + env.call_method( + callback_ref(), + jni_str!("onHistoryChanged"), + jni_sig!("(ZZ)V"), + &[can_go_back, can_go_forward], + )?; + Ok(()) + }) + .unwrap(); } fn on_ime_show(&self, _: InputMethodControl) { - let mut env = self.jvm.get_env().unwrap(); - env.call_method(self.callbacks.as_obj(), "onImeShow", "()V", &[]) + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + env.call_method(callback_ref(), jni_str!("onImeShow"), jni_sig!("()V"), &[])?; + Ok(()) + }) .unwrap(); } fn on_ime_hide(&self) { - let mut env = self.jvm.get_env().unwrap(); - env.call_method(self.callbacks.as_obj(), "onImeHide", "()V", &[]) + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + env.call_method(callback_ref(), jni_str!("onImeHide"), jni_sig!("()V"), &[])?; + Ok(()) + }) .unwrap(); } fn on_media_session_metadata(&self, title: String, artist: String, album: String) { info!("on_media_session_metadata"); - let mut env = self.jvm.get_env().unwrap(); - let Ok(title) = new_string_as_jvalue(&mut env, &title) else { - return; - }; + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + let Ok(title) = new_string_as_jvalue(env, &title) else { + return Ok(()); + }; - let Ok(artist) = new_string_as_jvalue(&mut env, &artist) else { - return; - }; + let Ok(artist) = new_string_as_jvalue(env, &artist) else { + return Ok(()); + }; - let Ok(album) = new_string_as_jvalue(&mut env, &album) else { - return; - }; + let Ok(album) = new_string_as_jvalue(env, &album) else { + return Ok(()); + }; - env.call_method( - self.callbacks.as_obj(), - "onMediaSessionMetadata", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", - &[(&title).into(), (&artist).into(), (&album).into()], - ) - .unwrap(); + env.call_method( + callback_ref(), + jni_str!("onMediaSessionMetadata"), + jni_sig!("(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"), + &[(&title).into(), (&artist).into(), (&album).into()], + )?; + Ok(()) + }) + .unwrap(); } fn on_media_session_playback_state_change(&self, state: MediaSessionPlaybackState) { info!("on_media_session_playback_state_change {:?}", state); - let mut env = self.jvm.get_env().unwrap(); - let state = state as i32; - let state = JValue::Int(state as jint); - env.call_method( - self.callbacks.as_obj(), - "onMediaSessionPlaybackStateChange", - "(I)V", - &[state], - ) - .unwrap(); + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + let state = state as i32; + let state = JValue::Int(state as jint); + env.call_method( + callback_ref(), + jni_str!("onMediaSessionPlaybackStateChange"), + jni_sig!("(I)V"), + &[state], + )?; + Ok(()) + }) + .unwrap(); } fn on_media_session_set_position_state( @@ -722,18 +836,21 @@ impl HostTrait for HostCallbacks { "on_media_session_playback_state_change ({:?}, {:?}, {:?})", duration, position, playback_rate ); - let mut env = self.jvm.get_env().unwrap(); - let duration = JValue::Float(duration as jfloat); - let position = JValue::Float(position as jfloat); - let playback_rate = JValue::Float(playback_rate as jfloat); + self.jvm + .attach_current_thread(|env| -> Result<(), Error> { + let duration = JValue::Float(duration as jfloat); + let position = JValue::Float(position as jfloat); + let playback_rate = JValue::Float(playback_rate as jfloat); - env.call_method( - self.callbacks.as_obj(), - "onMediaSessionSetPositionState", - "(FFF)V", - &[duration, position, playback_rate], - ) - .unwrap(); + env.call_method( + callback_ref(), + jni_str!("onMediaSessionSetPositionState"), + jni_sig!("(FFF)V"), + &[duration, position, playback_rate], + )?; + Ok(()) + }) + .unwrap(); } fn on_panic(&self, _reason: String, _backtrace: Option) {} @@ -743,8 +860,8 @@ unsafe extern "C" { pub fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int; } -fn throw(env: &mut JNIEnv, err: &str) { - if let Err(e) = env.throw(("java/lang/Exception", err)) { +fn throw(env: &mut Env, err: &JNIStr) { + if let Err(e) = env.throw(err) { warn!( "Failed to throw Java exception: `{}`. Exception was: `{}`", e, err @@ -753,13 +870,13 @@ fn throw(env: &mut JNIEnv, err: &str) { } fn new_string_as_jvalue<'local>( - env: &mut JNIEnv<'local>, + env: &mut Env<'local>, input_string: &str, ) -> Result, &'static str> { let jstring = match env.new_string(input_string) { Ok(jstring) => jstring, Err(_) => { - throw(env, "Couldn't create Java string"); + throw(env, jni_str!("Couldn't create Java string")); return Err("Couldn't create Java string"); }, }; @@ -767,107 +884,71 @@ fn new_string_as_jvalue<'local>( } fn jni_coordinate_to_rust_viewport_rect<'local>( - env: &mut JNIEnv<'local>, + env: &mut Env<'local>, obj: &JObject<'local>, -) -> Result, String> { - let x = get_non_null_field(env, obj, "x", "I")? - .i() - .map_err(|_| "x not an int")? as i32; - let y = get_non_null_field(env, obj, "y", "I")? - .i() - .map_err(|_| "y not an int")? as i32; - let width = get_non_null_field(env, obj, "width", "I")? - .i() - .map_err(|_| "width not an int")? as i32; - let height = get_non_null_field(env, obj, "height", "I")? - .i() - .map_err(|_| "height not an int")? as i32; +) -> Result, Error> { + let x = env.get_field(obj, jni_str!("x"), jni_sig!("I"))?.i()?; + let y = env.get_field(obj, jni_str!("y"), jni_sig!("I"))?.i()?; + + let width = env.get_field(obj, jni_str!("width"), jni_sig!("I"))?.i()?; + let height = env.get_field(obj, jni_str!("height"), jni_sig!("I"))?.i()?; + Ok(Rect::new(Point2D::new(x, y), Size2D::new(width, height))) } -fn get_field<'local>( - env: &mut JNIEnv<'local>, - obj: &JObject<'local>, - field: &str, - type_: &str, -) -> Result>, String> { - let Ok(class) = env.get_object_class(obj) else { - return Err("Can't get object class".to_owned()); - }; - - if env.get_field_id(class, field, type_).is_err() { - return Err(format!("Can't find `{}` field", field)); - } - - env.get_field(obj, field, type_) - .map(Some) - .map_err(|_| format!("Can't find `{}` field", field)) -} - -fn get_non_null_field<'local>( - env: &mut JNIEnv<'local>, - obj: &JObject<'local>, - field: &str, - type_: &str, -) -> Result, String> { - match get_field(env, obj, field, type_)? { - None => Err(format!("Field {} is null", field)), - Some(f) => Ok(f), - } -} - fn get_field_as_string<'local>( - env: &mut JNIEnv<'local>, + env: &mut Env<'local>, obj: &JObject<'local>, - field: &str, -) -> Result, String> { - let string = { - let value = get_field(env, obj, field, "Ljava/lang/String;")?; - let Some(value) = value else { - return Ok(None); - }; - value - .l() - .map_err(|_| format!("field `{}` is not an Object", field))? - }; - - Ok(env.get_string((&string).into()).map(|s| s.into()).ok()) + field: &JNIStr, +) -> Result { + let string_value = env + .get_field(obj, field, jni_sig!("Ljava/lang/String;"))? + .l()?; + JString::cast_local(env, string_value)?.try_to_string(env) } fn get_options<'local>( - env: &mut JNIEnv<'local>, + env: &mut Env<'local>, opts: &JObject<'local>, surface: &JObject<'local>, -) -> Result<(InitOptions, bool, Option, Option), String> { - let args = get_field_as_string(env, opts, "args")?; - let url = get_field_as_string(env, opts, "url")?; - let log_str = get_field_as_string(env, opts, "logStr")?; - let gst_debug_str = get_field_as_string(env, opts, "gstDebugStr")?; - let experimental_mode = get_non_null_field(env, opts, "experimentalMode", "Z")? - .z() - .map_err(|_| "experimentalMode not a boolean")?; - let density = get_non_null_field(env, opts, "density", "F")? - .f() - .map_err(|_| "density not a float")? as f32; - let log = get_non_null_field(env, opts, "enableLogs", "Z")? - .z() - .map_err(|_| "enableLogs not a boolean")?; - let coordinates = get_non_null_field( - env, - opts, - "coordinates", - "Lorg/servo/servoview/JNIServo$ServoCoordinates;", - )? - .l() - .map_err(|_| "coordinates is not an object")?; +) -> Result<(InitOptions, bool, Option, Option), Error> { + let args = get_field_as_string(env, opts, jni_str!("args")).ok(); + let url = get_field_as_string(env, opts, jni_str!("url")).ok(); + let log_str = get_field_as_string(env, opts, jni_str!("logStr")).ok(); + let gst_debug_str = get_field_as_string(env, opts, jni_str!("gstDebugStr")).ok(); + + let experimental_mode = env + .get_field(opts, jni_str!("experimentalMode"), jni_sig!("Z"))? + .z()?; + + let density = env + .get_field(opts, jni_str!("density"), jni_sig!("F"))? + .f()?; + + let log = env + .get_field(opts, jni_str!("enableLogs"), jni_sig!("Z"))? + .z()?; + + let coordinates = env + .get_field( + opts, + jni_str!("coordinates"), + jni_sig!("Lorg/servo/servoview/JNIServo$ServoCoordinates;"), + )? + .l()?; + let viewport_rect = jni_coordinate_to_rust_viewport_rect(env, &coordinates)?; - let mut args: Vec = match args { - Some(args) => serde_json::from_str(&args) - .map_err(|_| "Invalid arguments. Servo arguments must be formatted as a JSON array")?, - None => None, - } - .unwrap_or_default(); + let mut args: Vec = args + .and_then(|args| { + serde_json::from_str(&args) + .inspect_err(|_| { + error!("Invalid arguments. Servo arguments must be formatted as a JSON array") + }) + .ok() + }) + .unwrap_or_default(); + if experimental_mode { args.push("--enable-experimental-web-platform-features".to_owned()); } @@ -887,11 +968,10 @@ fn get_options<'local>( } fn display_and_window_handle( - env: &mut JNIEnv<'_>, + env: &mut Env<'_>, surface: &JObject<'_>, ) -> (RawDisplayHandle, RawWindowHandle) { - let native_window = - unsafe { ANativeWindow_fromSurface(env.get_native_interface(), surface.as_raw()) }; + let native_window = unsafe { ANativeWindow_fromSurface(env.get_raw(), surface.as_raw()) }; let native_window = NonNull::new(native_window).expect("Could not get Android window"); ( RawDisplayHandle::Android(AndroidDisplayHandle::new()),