mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Make JNI code more robust
This commit is contained in:
parent
021a24d5bb
commit
e0ce73abb2
7 changed files with 248 additions and 150 deletions
|
@ -28,6 +28,14 @@ thread_local! {
|
||||||
/// and that perform_updates need to be called
|
/// and that perform_updates need to be called
|
||||||
pub use servo::embedder_traits::EventLoopWaker;
|
pub use servo::embedder_traits::EventLoopWaker;
|
||||||
|
|
||||||
|
pub struct InitOptions {
|
||||||
|
pub args: Option<String>,
|
||||||
|
pub url: Option<String>,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub density: f32,
|
||||||
|
}
|
||||||
|
|
||||||
/// Delegate resource file reading to the embedder.
|
/// Delegate resource file reading to the embedder.
|
||||||
pub trait ReadFileTrait {
|
pub trait ReadFileTrait {
|
||||||
fn readfile(&self, file: &str) -> Vec<u8>;
|
fn readfile(&self, file: &str) -> Vec<u8>;
|
||||||
|
@ -93,20 +101,16 @@ pub fn servo_version() -> String {
|
||||||
/// Initialize Servo. At that point, we need a valid GL context.
|
/// Initialize Servo. At that point, we need a valid GL context.
|
||||||
/// In the future, this will be done in multiple steps.
|
/// In the future, this will be done in multiple steps.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
|
init_opts: InitOptions,
|
||||||
gl: Rc<gl::Gl>,
|
gl: Rc<gl::Gl>,
|
||||||
argsline: String,
|
|
||||||
embedder_url: Option<String>,
|
|
||||||
waker: Box<EventLoopWaker>,
|
waker: Box<EventLoopWaker>,
|
||||||
readfile: Box<ReadFileTrait + Send + Sync>,
|
readfile: Box<ReadFileTrait + Send + Sync>,
|
||||||
callbacks: Box<HostTrait>,
|
callbacks: Box<HostTrait>,
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
density: f32,
|
|
||||||
) -> Result<(), &'static str> {
|
) -> Result<(), &'static str> {
|
||||||
resources::set(Box::new(ResourceReader(readfile)));
|
resources::set(Box::new(ResourceReader(readfile)));
|
||||||
|
|
||||||
if !argsline.is_empty() {
|
if let Some(args) = init_opts.args {
|
||||||
let mut args: Vec<String> = serde_json::from_str(&argsline).map_err(|_| {
|
let mut args: Vec<String> = serde_json::from_str(&args).map_err(|_| {
|
||||||
"Invalid arguments. Servo arguments must be formatted as a JSON array"
|
"Invalid arguments. Servo arguments must be formatted as a JSON array"
|
||||||
})?;
|
})?;
|
||||||
// opts::from_cmdline_args expects the first argument to be the binary name.
|
// opts::from_cmdline_args expects the first argument to be the binary name.
|
||||||
|
@ -114,7 +118,7 @@ pub fn init(
|
||||||
opts::from_cmdline_args(&args);
|
opts::from_cmdline_args(&args);
|
||||||
}
|
}
|
||||||
|
|
||||||
let embedder_url = embedder_url.as_ref().and_then(|s| {
|
let embedder_url = init_opts.url.as_ref().and_then(|s| {
|
||||||
ServoUrl::parse(s).ok()
|
ServoUrl::parse(s).ok()
|
||||||
});
|
});
|
||||||
let cmdline_url = opts::get().url.clone();
|
let cmdline_url = opts::get().url.clone();
|
||||||
|
@ -135,9 +139,9 @@ pub fn init(
|
||||||
let callbacks = Rc::new(ServoCallbacks {
|
let callbacks = Rc::new(ServoCallbacks {
|
||||||
gl: gl.clone(),
|
gl: gl.clone(),
|
||||||
host_callbacks: callbacks,
|
host_callbacks: callbacks,
|
||||||
width: Cell::new(width),
|
width: Cell::new(init_opts.width),
|
||||||
height: Cell::new(height),
|
height: Cell::new(init_opts.height),
|
||||||
density,
|
density: init_opts.density,
|
||||||
waker,
|
waker,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use api::{self, EventLoopWaker, ServoGlue, SERVO, HostTrait, ReadFileTrait};
|
use api::{self, EventLoopWaker, InitOptions, ServoGlue, SERVO, HostTrait, ReadFileTrait};
|
||||||
use gl_glue;
|
use gl_glue;
|
||||||
use servo::gl;
|
use servo::gl;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
|
@ -37,6 +37,16 @@ pub struct CHostCallbacks {
|
||||||
pub on_animating_changed: extern fn(animating: bool),
|
pub on_animating_changed: extern fn(animating: bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Servo options
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CInitOptions {
|
||||||
|
pub args: *const c_char,
|
||||||
|
pub url: *const c_char,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub density: f32,
|
||||||
|
}
|
||||||
|
|
||||||
/// The returned string is not freed. This will leak.
|
/// The returned string is not freed. This will leak.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn servo_version() -> *const c_char {
|
pub extern "C" fn servo_version() -> *const c_char {
|
||||||
|
@ -48,66 +58,52 @@ pub extern "C" fn servo_version() -> *const c_char {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(
|
fn init(
|
||||||
|
opts: CInitOptions,
|
||||||
gl: Rc<gl::Gl>,
|
gl: Rc<gl::Gl>,
|
||||||
args: *const c_char,
|
|
||||||
url: *const c_char,
|
|
||||||
wakeup: extern fn(),
|
wakeup: extern fn(),
|
||||||
readfile: extern fn(*const c_char) -> *const c_char,
|
readfile: extern fn(*const c_char) -> *const c_char,
|
||||||
callbacks: CHostCallbacks,
|
callbacks: CHostCallbacks) {
|
||||||
width: u32,
|
let args = unsafe { CStr::from_ptr(opts.args) };
|
||||||
height: u32,
|
let args = args.to_str().map(|s| s.to_string()).ok();
|
||||||
density: f32) {
|
|
||||||
let args = unsafe { CStr::from_ptr(args) };
|
|
||||||
let args = args.to_str().expect("Can't read string").to_string();
|
|
||||||
|
|
||||||
let url = unsafe { CStr::from_ptr(url) };
|
let url = unsafe { CStr::from_ptr(opts.url) };
|
||||||
let url = url.to_str().map(|s| s.to_string());
|
let url = url.to_str().map(|s| s.to_string()).ok();
|
||||||
|
|
||||||
|
let opts = InitOptions {
|
||||||
|
args,
|
||||||
|
url,
|
||||||
|
width: opts.width,
|
||||||
|
height: opts.height,
|
||||||
|
density: opts.density,
|
||||||
|
};
|
||||||
|
|
||||||
let wakeup = Box::new(WakeupCallback::new(wakeup));
|
let wakeup = Box::new(WakeupCallback::new(wakeup));
|
||||||
let readfile = Box::new(ReadFileCallback::new(readfile));
|
let readfile = Box::new(ReadFileCallback::new(readfile));
|
||||||
let callbacks = Box::new(HostCallbacks::new(callbacks));
|
let callbacks = Box::new(HostCallbacks::new(callbacks));
|
||||||
|
|
||||||
api::init(
|
api::init(opts, gl, wakeup, readfile, callbacks).unwrap();
|
||||||
gl,
|
|
||||||
args,
|
|
||||||
url.ok(),
|
|
||||||
wakeup,
|
|
||||||
readfile,
|
|
||||||
callbacks,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
density,
|
|
||||||
).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn init_with_egl(
|
pub extern "C" fn init_with_egl(
|
||||||
args: *const c_char,
|
opts: CInitOptions,
|
||||||
url: *const c_char,
|
|
||||||
wakeup: extern fn(),
|
wakeup: extern fn(),
|
||||||
readfile: extern fn(*const c_char) -> *const c_char,
|
readfile: extern fn(*const c_char) -> *const c_char,
|
||||||
callbacks: CHostCallbacks,
|
callbacks: CHostCallbacks) {
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
density: f32) {
|
|
||||||
let gl = gl_glue::egl::init().unwrap();
|
let gl = gl_glue::egl::init().unwrap();
|
||||||
init(gl, args, url, wakeup, readfile, callbacks, width, height, density)
|
init(opts, gl, wakeup, readfile, callbacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn init_with_gl(
|
pub extern "C" fn init_with_gl(
|
||||||
args: *const c_char,
|
opts: CInitOptions,
|
||||||
url: *const c_char,
|
|
||||||
wakeup: extern fn(),
|
wakeup: extern fn(),
|
||||||
readfile: extern fn(*const c_char) -> *const c_char,
|
readfile: extern fn(*const c_char) -> *const c_char,
|
||||||
callbacks: CHostCallbacks,
|
callbacks: CHostCallbacks) {
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
density: f32) {
|
|
||||||
let gl = gl_glue::gl::init().unwrap();
|
let gl = gl_glue::gl::init().unwrap();
|
||||||
init(gl, args, url, wakeup, readfile, callbacks, width, height, density)
|
init(opts, gl, wakeup, readfile, callbacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|
|
@ -5,15 +5,14 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use android_logger::{self, Filter};
|
use android_logger::{self, Filter};
|
||||||
use api::{self, EventLoopWaker, ServoGlue, SERVO, HostTrait, ReadFileTrait};
|
use api::{self, EventLoopWaker, InitOptions, ServoGlue, SERVO, HostTrait, ReadFileTrait};
|
||||||
use gl_glue;
|
use gl_glue;
|
||||||
use jni::{JNIEnv, JavaVM};
|
use jni::{errors, JNIEnv, JavaVM};
|
||||||
use jni::objects::{GlobalRef, JClass, JObject, JString, JValue};
|
use jni::objects::{GlobalRef, JClass, JObject, JString, JValue};
|
||||||
use jni::sys::{jboolean, jfloat, jint, jstring, JNI_TRUE};
|
use jni::sys::{jboolean, jfloat, jint, jstring, JNI_TRUE};
|
||||||
use libc::{pipe, dup2, read};
|
use libc::{pipe, dup2, read};
|
||||||
use log::Level;
|
use log::Level;
|
||||||
use std;
|
use std;
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -23,17 +22,16 @@ struct HostCallbacks {
|
||||||
jvm: JavaVM,
|
jvm: JavaVM,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call<F>(env: JNIEnv, f: F)
|
fn call<F>(env: &JNIEnv, f: F)
|
||||||
where
|
where
|
||||||
F: Fn(&mut ServoGlue) -> Result<(), &'static str>,
|
F: Fn(&mut ServoGlue) -> Result<(), &str>,
|
||||||
{
|
{
|
||||||
SERVO.with(|s| {
|
SERVO.with(|s| {
|
||||||
if let Err(error) = match s.borrow_mut().as_mut() {
|
if let Err(error) = match s.borrow_mut().as_mut() {
|
||||||
Some(ref mut s) => (f)(s),
|
Some(ref mut s) => (f)(s),
|
||||||
None => Err("Servo not available in this thread"),
|
None => Err("Servo not available in this thread"),
|
||||||
} {
|
} {
|
||||||
env.throw(("java/lang/Exception", error))
|
throw(env, error);
|
||||||
.expect("Error while throwing");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -41,8 +39,7 @@ where
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_version(env: JNIEnv, _class: JClass) -> jstring {
|
pub fn Java_org_mozilla_servoview_JNIServo_version(env: JNIEnv, _class: JClass) -> jstring {
|
||||||
let v = api::servo_version();
|
let v = api::servo_version();
|
||||||
let output = env.new_string(v).expect("Couldn't create java string");
|
new_string(&env, &v).unwrap_or_else(|null| null)
|
||||||
output.into_inner()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -50,16 +47,18 @@ pub fn Java_org_mozilla_servoview_JNIServo_init(
|
||||||
env: JNIEnv,
|
env: JNIEnv,
|
||||||
_: JClass,
|
_: JClass,
|
||||||
activity: JObject,
|
activity: JObject,
|
||||||
args: JString,
|
opts: JObject,
|
||||||
url: JString,
|
|
||||||
log_str: JString,
|
|
||||||
callbacks_obj: JObject,
|
callbacks_obj: JObject,
|
||||||
width: jint,
|
|
||||||
height: jint,
|
|
||||||
density: jfloat,
|
|
||||||
log: jboolean,
|
|
||||||
) {
|
) {
|
||||||
if log == JNI_TRUE {
|
let (opts, log, log_str) = match get_options(&env, opts) {
|
||||||
|
Ok((opts, log, log_str)) => (opts, log, log_str),
|
||||||
|
Err(err) => {
|
||||||
|
throw(&env, &err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if log {
|
||||||
// Note: Android debug logs are stripped from a release build.
|
// Note: Android debug logs are stripped from a release build.
|
||||||
// debug!() will only show in a debug build. Use info!() if logs
|
// debug!() will only show in a debug build. Use info!() if logs
|
||||||
// should show up in adb logcat with a release build.
|
// should show up in adb logcat with a release build.
|
||||||
|
@ -76,10 +75,10 @@ pub fn Java_org_mozilla_servoview_JNIServo_init(
|
||||||
for &module in &filters {
|
for &module in &filters {
|
||||||
filter = filter.with_allowed_module_path(module);
|
filter = filter.with_allowed_module_path(module);
|
||||||
}
|
}
|
||||||
let log_str = env.get_string(log_str).ok();
|
if let Some(log_str) = log_str {
|
||||||
let log_str = log_str.as_ref().map_or(Cow::Borrowed(""), |s| s.to_string_lossy());
|
for module in log_str.split(',') {
|
||||||
for module in log_str.split(',') {
|
filter = filter.with_allowed_module_path(module);
|
||||||
filter = filter.with_allowed_module_path(module);
|
}
|
||||||
}
|
}
|
||||||
android_logger::init_once(filter, Some("simpleservo"));
|
android_logger::init_once(filter, Some("simpleservo"));
|
||||||
}
|
}
|
||||||
|
@ -89,36 +88,23 @@ pub fn Java_org_mozilla_servoview_JNIServo_init(
|
||||||
initialize_android_glue(&env, activity);
|
initialize_android_glue(&env, activity);
|
||||||
redirect_stdout_to_logcat();
|
redirect_stdout_to_logcat();
|
||||||
|
|
||||||
let args = env.get_string(args)
|
let callbacks_ref = match env.new_global_ref(callbacks_obj) {
|
||||||
.expect("Couldn't get java string")
|
Ok(r) => r,
|
||||||
.into();
|
Err(_) => {
|
||||||
|
throw(&env, "Failed to get global reference of callback argument");
|
||||||
let url = if url.is_null() {
|
return;
|
||||||
None
|
}
|
||||||
} else {
|
|
||||||
Some(env.get_string(url).expect("Couldn't get java string").into())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let callbacks_ref = env.new_global_ref(callbacks_obj).unwrap();
|
|
||||||
|
|
||||||
let wakeup = Box::new(WakeupCallback::new(callbacks_ref.clone(), &env));
|
let wakeup = Box::new(WakeupCallback::new(callbacks_ref.clone(), &env));
|
||||||
let readfile = Box::new(ReadFileCallback::new(callbacks_ref.clone(), &env));
|
let readfile = Box::new(ReadFileCallback::new(callbacks_ref.clone(), &env));
|
||||||
let callbacks = Box::new(HostCallbacks::new(callbacks_ref, &env));
|
let callbacks = Box::new(HostCallbacks::new(callbacks_ref, &env));
|
||||||
|
|
||||||
gl_glue::egl::init().and_then(|gl| {
|
if let Err(err) =
|
||||||
api::init(
|
gl_glue::egl::init().and_then(|gl| api::init(opts, gl, wakeup, readfile, callbacks))
|
||||||
gl,
|
{
|
||||||
args,
|
throw(&env, err)
|
||||||
url,
|
};
|
||||||
wakeup,
|
|
||||||
readfile,
|
|
||||||
callbacks,
|
|
||||||
width as u32,
|
|
||||||
height as u32,
|
|
||||||
density as f32)
|
|
||||||
}).or_else(|err| {
|
|
||||||
env.throw(("java/lang/Exception", err))
|
|
||||||
}).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -128,7 +114,7 @@ pub fn Java_org_mozilla_servoview_JNIServo_setBatchMode(
|
||||||
batch: jboolean,
|
batch: jboolean,
|
||||||
) {
|
) {
|
||||||
debug!("setBatchMode");
|
debug!("setBatchMode");
|
||||||
call(env, |s| s.set_batch_mode(batch == JNI_TRUE));
|
call(&env, |s| s.set_batch_mode(batch == JNI_TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -139,50 +125,57 @@ pub fn Java_org_mozilla_servoview_JNIServo_resize(
|
||||||
height: jint,
|
height: jint,
|
||||||
) {
|
) {
|
||||||
debug!("resize {}/{}", width, height);
|
debug!("resize {}/{}", width, height);
|
||||||
call(env, |s| s.resize(width as u32, height as u32));
|
call(&env, |s| s.resize(width as u32, height as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_performUpdates(env: JNIEnv, _class: JClass) {
|
pub fn Java_org_mozilla_servoview_JNIServo_performUpdates(env: JNIEnv, _class: JClass) {
|
||||||
debug!("performUpdates");
|
debug!("performUpdates");
|
||||||
call(env, |s| s.perform_updates());
|
call(&env, |s| s.perform_updates());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_loadUri(env: JNIEnv, _class: JClass, url: JString) {
|
pub fn Java_org_mozilla_servoview_JNIServo_loadUri(env: JNIEnv, _class: JClass, url: JString) {
|
||||||
debug!("loadUri");
|
debug!("loadUri");
|
||||||
let url: String = env.get_string(url).unwrap().into();
|
match env.get_string(url) {
|
||||||
call(env, |s| s.load_uri(&url));
|
Ok(url) => {
|
||||||
|
let url: String = url.into();
|
||||||
|
call(&env, |s| s.load_uri(&url));
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
throw(&env, "Failed to convert Java string");
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_reload(env: JNIEnv, _class: JClass) {
|
pub fn Java_org_mozilla_servoview_JNIServo_reload(env: JNIEnv, _class: JClass) {
|
||||||
debug!("reload");
|
debug!("reload");
|
||||||
call(env, |s| s.reload());
|
call(&env, |s| s.reload());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_stop(env: JNIEnv, _class: JClass) {
|
pub fn Java_org_mozilla_servoview_JNIServo_stop(env: JNIEnv, _class: JClass) {
|
||||||
debug!("stop");
|
debug!("stop");
|
||||||
call(env, |s| s.stop());
|
call(&env, |s| s.stop());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_refresh(env: JNIEnv, _class: JClass) {
|
pub fn Java_org_mozilla_servoview_JNIServo_refresh(env: JNIEnv, _class: JClass) {
|
||||||
debug!("refresh");
|
debug!("refresh");
|
||||||
call(env, |s| s.refresh());
|
call(&env, |s| s.refresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_goBack(env: JNIEnv, _class: JClass) {
|
pub fn Java_org_mozilla_servoview_JNIServo_goBack(env: JNIEnv, _class: JClass) {
|
||||||
debug!("goBack");
|
debug!("goBack");
|
||||||
call(env, |s| s.go_back());
|
call(&env, |s| s.go_back());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_goForward(env: JNIEnv, _class: JClass) {
|
pub fn Java_org_mozilla_servoview_JNIServo_goForward(env: JNIEnv, _class: JClass) {
|
||||||
debug!("goForward");
|
debug!("goForward");
|
||||||
call(env, |s| s.go_forward());
|
call(&env, |s| s.go_forward());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -195,7 +188,7 @@ pub fn Java_org_mozilla_servoview_JNIServo_scrollStart(
|
||||||
y: jint,
|
y: jint,
|
||||||
) {
|
) {
|
||||||
debug!("scrollStart");
|
debug!("scrollStart");
|
||||||
call(env, |s| s.scroll_start(dx as i32, dy as i32, x as u32, y as u32));
|
call(&env, |s| s.scroll_start(dx as i32, dy as i32, x as u32, y as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -208,7 +201,7 @@ pub fn Java_org_mozilla_servoview_JNIServo_scrollEnd(
|
||||||
y: jint,
|
y: jint,
|
||||||
) {
|
) {
|
||||||
debug!("scrollEnd");
|
debug!("scrollEnd");
|
||||||
call(env, |s| s.scroll_end(dx as i32, dy as i32, x as u32, y as u32));
|
call(&env, |s| s.scroll_end(dx as i32, dy as i32, x as u32, y as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,7 +215,7 @@ pub fn Java_org_mozilla_servoview_JNIServo_scroll(
|
||||||
y: jint,
|
y: jint,
|
||||||
) {
|
) {
|
||||||
debug!("scroll");
|
debug!("scroll");
|
||||||
call(env, |s| s.scroll(dx as i32, dy as i32, x as u32, y as u32));
|
call(&env, |s| s.scroll(dx as i32, dy as i32, x as u32, y as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -234,7 +227,7 @@ pub fn Java_org_mozilla_servoview_JNIServo_pinchZoomStart(
|
||||||
y: jint,
|
y: jint,
|
||||||
) {
|
) {
|
||||||
debug!("pinchZoomStart");
|
debug!("pinchZoomStart");
|
||||||
call(env, |s| s.pinchzoom_start(factor as f32, x as u32, y as u32));
|
call(&env, |s| s.pinchzoom_start(factor as f32, x as u32, y as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -246,7 +239,7 @@ pub fn Java_org_mozilla_servoview_JNIServo_pinchZoom(
|
||||||
y: jint,
|
y: jint,
|
||||||
) {
|
) {
|
||||||
debug!("pinchZoom");
|
debug!("pinchZoom");
|
||||||
call(env, |s| s.pinchzoom(factor as f32, x as u32, y as u32));
|
call(&env, |s| s.pinchzoom(factor as f32, x as u32, y as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -258,14 +251,14 @@ pub fn Java_org_mozilla_servoview_JNIServo_pinchZoomEnd(
|
||||||
y: jint,
|
y: jint,
|
||||||
) {
|
) {
|
||||||
debug!("pinchZoomEnd");
|
debug!("pinchZoomEnd");
|
||||||
call(env, |s| s.pinchzoom_end(factor as f32, x as u32, y as u32));
|
call(&env, |s| s.pinchzoom_end(factor as f32, x as u32, y as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) {
|
pub fn Java_org_mozilla_servoview_JNIServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) {
|
||||||
debug!("click");
|
debug!("click");
|
||||||
call(env, |s| s.click(x as u32, y as u32));
|
call(&env, |s| s.click(x as u32, y as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WakeupCallback {
|
pub struct WakeupCallback {
|
||||||
|
@ -312,9 +305,10 @@ impl ReadFileTrait for ReadFileCallback {
|
||||||
fn readfile(&self, file: &str) -> Vec<u8> {
|
fn readfile(&self, file: &str) -> Vec<u8> {
|
||||||
// FIXME: we'd rather use attach_current_thread but it detaches the VM too early.
|
// FIXME: we'd rather use attach_current_thread but it detaches the VM too early.
|
||||||
let env = self.jvm.attach_current_thread_as_daemon().unwrap();
|
let env = self.jvm.attach_current_thread_as_daemon().unwrap();
|
||||||
let s = env.new_string(&file)
|
let s = match new_string(&env, &file) {
|
||||||
.expect("Couldn't create java string")
|
Ok(s) => s,
|
||||||
.into_inner();
|
Err(_) => return vec![],
|
||||||
|
};
|
||||||
let s = JValue::from(JObject::from(s));
|
let s = JValue::from(JObject::from(s));
|
||||||
let array = env.call_method(
|
let array = env.call_method(
|
||||||
self.callback.lock().unwrap().as_obj(),
|
self.callback.lock().unwrap().as_obj(),
|
||||||
|
@ -366,9 +360,10 @@ impl HostTrait for HostCallbacks {
|
||||||
fn on_title_changed(&self, title: String) {
|
fn on_title_changed(&self, title: String) {
|
||||||
debug!("on_title_changed");
|
debug!("on_title_changed");
|
||||||
let env = self.jvm.get_env().unwrap();
|
let env = self.jvm.get_env().unwrap();
|
||||||
let s = env.new_string(&title)
|
let s = match new_string(&env, &title) {
|
||||||
.expect("Couldn't create java string")
|
Ok(s) => s,
|
||||||
.into_inner();
|
Err(_) => return,
|
||||||
|
};
|
||||||
let s = JValue::from(JObject::from(s));
|
let s = JValue::from(JObject::from(s));
|
||||||
env.call_method(
|
env.call_method(
|
||||||
self.callbacks.as_obj(),
|
self.callbacks.as_obj(),
|
||||||
|
@ -381,9 +376,10 @@ impl HostTrait for HostCallbacks {
|
||||||
fn on_url_changed(&self, url: String) {
|
fn on_url_changed(&self, url: String) {
|
||||||
debug!("on_url_changed");
|
debug!("on_url_changed");
|
||||||
let env = self.jvm.get_env().unwrap();
|
let env = self.jvm.get_env().unwrap();
|
||||||
let s = env.new_string(&url)
|
let s = match new_string(&env, &url) {
|
||||||
.expect("Couldn't create java string")
|
Ok(s) => s,
|
||||||
.into_inner();
|
Err(_) => return,
|
||||||
|
};
|
||||||
let s = JValue::Object(JObject::from(s));
|
let s = JValue::Object(JObject::from(s));
|
||||||
env.call_method(
|
env.call_method(
|
||||||
self.callbacks.as_obj(),
|
self.callbacks.as_obj(),
|
||||||
|
@ -520,3 +516,92 @@ fn redirect_stdout_to_logcat() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn throw(env: &JNIEnv, err: &str) {
|
||||||
|
if let Err(e) = env.throw(("java/lang/Exception", err)) {
|
||||||
|
warn!("Failed to throw Java exception: `{}`. Exception was: `{}`", e, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_string(env: &JNIEnv, s: &str) -> Result<jstring, jstring> {
|
||||||
|
match env.new_string(s) {
|
||||||
|
Ok(s) => Ok(s.into_inner()),
|
||||||
|
Err(_) => {
|
||||||
|
throw(&env, "Couldn't create java string");
|
||||||
|
Err(JObject::null().into_inner())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_field<'a>(
|
||||||
|
env: &'a JNIEnv,
|
||||||
|
obj: JObject,
|
||||||
|
field: &str,
|
||||||
|
type_: &str,
|
||||||
|
) -> Result<Option<JValue<'a>>, String> {
|
||||||
|
if env.get_field_id(obj, field, type_).is_err() {
|
||||||
|
return Err(format!("Can't find `{}` field", &field));
|
||||||
|
}
|
||||||
|
env.get_field(obj, field, type_)
|
||||||
|
.map(|value| Some(value))
|
||||||
|
.or_else(|e| match *e.kind() {
|
||||||
|
errors::ErrorKind::NullPtr(_) => Ok(None),
|
||||||
|
_ => Err(format!(
|
||||||
|
"Can't find `{}` field: {}",
|
||||||
|
&field,
|
||||||
|
e.description()
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_non_null_field<'a>(
|
||||||
|
env: &'a JNIEnv,
|
||||||
|
obj: JObject,
|
||||||
|
field: &str,
|
||||||
|
type_: &str,
|
||||||
|
) -> Result<JValue<'a>, String> {
|
||||||
|
match get_field(env, obj, field, type_)? {
|
||||||
|
None => Err(format!("Field {} is null", field)),
|
||||||
|
Some(f) => Ok(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_string(env: &JNIEnv, obj: JObject, field: &str) -> Result<Option<String>, String> {
|
||||||
|
let value = get_field(env, obj, field, "Ljava/lang/String;")?;
|
||||||
|
match value {
|
||||||
|
Some(value) => {
|
||||||
|
let string = value
|
||||||
|
.l()
|
||||||
|
.map_err(|_| format!("field `{}` is not an Object", field))?
|
||||||
|
.into();
|
||||||
|
Ok(env.get_string(string).map(|s| s.into()).ok())
|
||||||
|
},
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_options(env: &JNIEnv, opts: JObject) -> Result<(InitOptions, bool, Option<String>), String> {
|
||||||
|
let args = get_string(env, opts, "args")?;
|
||||||
|
let url = get_string(env, opts, "url")?;
|
||||||
|
let log_str = get_string(env, opts, "logStr")?;
|
||||||
|
let width = get_non_null_field(env, opts, "width", "I")?
|
||||||
|
.i()
|
||||||
|
.map_err(|_| "width not an int")? as u32;
|
||||||
|
let height = get_non_null_field(env, opts, "height", "I")?
|
||||||
|
.i()
|
||||||
|
.map_err(|_| "height not an int")? as u32;
|
||||||
|
let density = get_non_null_field(env, opts, "density", "F")?
|
||||||
|
.f()
|
||||||
|
.map_err(|_| "densitiy not a float")? as f32;
|
||||||
|
let log = get_non_null_field(env, opts, "enableLogs", "Z")?
|
||||||
|
.z()
|
||||||
|
.map_err(|_| "enableLogs not a boolean")?;
|
||||||
|
let opts = InitOptions {
|
||||||
|
args,
|
||||||
|
url,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
density,
|
||||||
|
};
|
||||||
|
Ok((opts, log, log_str))
|
||||||
|
}
|
||||||
|
|
|
@ -20,13 +20,7 @@ public class JNIServo {
|
||||||
|
|
||||||
public native String version();
|
public native String version();
|
||||||
|
|
||||||
public native void init(Activity activity,
|
public native void init(Activity activity, ServoOptions options, Callbacks callbacks);
|
||||||
String args,
|
|
||||||
String url,
|
|
||||||
String logstr,
|
|
||||||
Callbacks callbacks,
|
|
||||||
int width, int height, float density,
|
|
||||||
boolean log);
|
|
||||||
|
|
||||||
public native void setBatchMode(boolean mode);
|
public native void setBatchMode(boolean mode);
|
||||||
|
|
||||||
|
@ -60,6 +54,16 @@ public class JNIServo {
|
||||||
|
|
||||||
public native void click(int x, int y);
|
public native void click(int x, int y);
|
||||||
|
|
||||||
|
public static class ServoOptions {
|
||||||
|
public String args;
|
||||||
|
public String url;
|
||||||
|
public int width = 0;
|
||||||
|
public int height = 0;
|
||||||
|
public float density = 1;
|
||||||
|
public String logStr;
|
||||||
|
public boolean enableLogs = false;
|
||||||
|
}
|
||||||
|
|
||||||
public interface Callbacks {
|
public interface Callbacks {
|
||||||
void wakeup();
|
void wakeup();
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import org.freedesktop.gstreamer.GStreamer;
|
import org.freedesktop.gstreamer.GStreamer;
|
||||||
|
import org.mozilla.servoview.JNIServo.ServoOptions;
|
||||||
|
|
||||||
public class Servo {
|
public class Servo {
|
||||||
private static final String LOGTAG = "Servo";
|
private static final String LOGTAG = "Servo";
|
||||||
|
@ -23,15 +24,11 @@ public class Servo {
|
||||||
private boolean mSuspended;
|
private boolean mSuspended;
|
||||||
|
|
||||||
public Servo(
|
public Servo(
|
||||||
|
ServoOptions options,
|
||||||
RunCallback runCallback,
|
RunCallback runCallback,
|
||||||
GfxCallbacks gfxcb,
|
GfxCallbacks gfxcb,
|
||||||
Client client,
|
Client client,
|
||||||
Activity activity,
|
Activity activity) {
|
||||||
String args,
|
|
||||||
String url,
|
|
||||||
String logstr,
|
|
||||||
int width, int height,
|
|
||||||
float density, boolean log) {
|
|
||||||
|
|
||||||
mRunCallback = runCallback;
|
mRunCallback = runCallback;
|
||||||
|
|
||||||
|
@ -40,7 +37,7 @@ public class Servo {
|
||||||
Callbacks cbs = new Callbacks(client, gfxcb);
|
Callbacks cbs = new Callbacks(client, gfxcb);
|
||||||
|
|
||||||
mRunCallback.inGLThread(() -> {
|
mRunCallback.inGLThread(() -> {
|
||||||
mJNI.init(activity, args, url, logstr, cbs, width, height, density, log);
|
mJNI.init(activity, options, cbs);
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.os.Message;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
|
import org.mozilla.servoview.JNIServo.ServoOptions;
|
||||||
import org.mozilla.servoview.Servo.Client;
|
import org.mozilla.servoview.Servo.Client;
|
||||||
import org.mozilla.servoview.Servo.GfxCallbacks;
|
import org.mozilla.servoview.Servo.GfxCallbacks;
|
||||||
import org.mozilla.servoview.Servo.RunCallback;
|
import org.mozilla.servoview.Servo.RunCallback;
|
||||||
|
@ -37,9 +38,9 @@ public class ServoSurface {
|
||||||
private int mHeight;
|
private int mHeight;
|
||||||
private Servo mServo;
|
private Servo mServo;
|
||||||
private Client mClient = null;
|
private Client mClient = null;
|
||||||
private String mServoArgs = "";
|
private String mServoArgs;
|
||||||
private String mServoLog = "";
|
private String mServoLog;
|
||||||
private String mInitialUri = null;
|
private String mInitialUri;
|
||||||
private Activity mActivity;
|
private Activity mActivity;
|
||||||
|
|
||||||
public ServoSurface(Surface surface, int width, int height) {
|
public ServoSurface(Surface surface, int width, int height) {
|
||||||
|
@ -55,8 +56,8 @@ public class ServoSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setServoArgs(String args, String log) {
|
public void setServoArgs(String args, String log) {
|
||||||
mServoArgs = args != null ? args : "";
|
mServoArgs = args;
|
||||||
mServoLog = log != null ? log : "";
|
mServoLog = log;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setActivity(Activity activity) {
|
public void setActivity(Activity activity) {
|
||||||
|
@ -204,9 +205,16 @@ public class ServoSurface {
|
||||||
};
|
};
|
||||||
|
|
||||||
inUIThread(() -> {
|
inUIThread(() -> {
|
||||||
final boolean showLogs = true;
|
ServoOptions options = new ServoOptions();
|
||||||
String uri = mInitialUri == null ? null : mInitialUri;
|
options.args = mServoArgs;
|
||||||
mServo = new Servo(this, surface, mClient, mActivity, mServoArgs, uri, mServoLog, mWidth, mHeight, 1, showLogs);
|
options.width = mWidth;
|
||||||
|
options.height = mHeight;
|
||||||
|
options.density = 1;
|
||||||
|
options.url = mInitialUri == null ? null : mInitialUri;
|
||||||
|
options.logStr = mServoLog;
|
||||||
|
options.enableLogs = true;
|
||||||
|
|
||||||
|
mServo = new Servo(options, this, surface, mClient, mActivity);
|
||||||
});
|
});
|
||||||
|
|
||||||
Looper.loop();
|
Looper.loop();
|
||||||
|
|
|
@ -19,6 +19,7 @@ import android.view.MotionEvent;
|
||||||
import android.view.ScaleGestureDetector;
|
import android.view.ScaleGestureDetector;
|
||||||
import android.widget.OverScroller;
|
import android.widget.OverScroller;
|
||||||
|
|
||||||
|
import org.mozilla.servoview.JNIServo.ServoOptions;
|
||||||
import org.mozilla.servoview.Servo.Client;
|
import org.mozilla.servoview.Servo.Client;
|
||||||
import org.mozilla.servoview.Servo.GfxCallbacks;
|
import org.mozilla.servoview.Servo.GfxCallbacks;
|
||||||
import org.mozilla.servoview.Servo.RunCallback;
|
import org.mozilla.servoview.Servo.RunCallback;
|
||||||
|
@ -41,8 +42,8 @@ public class ServoView extends GLSurfaceView
|
||||||
private Client mClient = null;
|
private Client mClient = null;
|
||||||
private Uri mInitialUri = null;
|
private Uri mInitialUri = null;
|
||||||
private boolean mAnimating;
|
private boolean mAnimating;
|
||||||
private String mServoArgs = "";
|
private String mServoArgs;
|
||||||
private String mServoLog = "";
|
private String mServoLog;
|
||||||
private GestureDetector mGestureDetector;
|
private GestureDetector mGestureDetector;
|
||||||
private ScaleGestureDetector mScaleGestureDetector;
|
private ScaleGestureDetector mScaleGestureDetector;
|
||||||
|
|
||||||
|
@ -75,8 +76,8 @@ public class ServoView extends GLSurfaceView
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setServoArgs(String args, String log) {
|
public void setServoArgs(String args, String log) {
|
||||||
mServoArgs = args != null ? args : "";
|
mServoArgs = args;
|
||||||
mServoLog = log != null ? log : "";
|
mServoLog = log;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reload() {
|
public void reload() {
|
||||||
|
@ -135,16 +136,19 @@ public class ServoView extends GLSurfaceView
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onGLReady() {
|
public void onGLReady() {
|
||||||
final boolean showLogs = true;
|
ServoOptions options = new ServoOptions();
|
||||||
int width = getWidth();
|
options.args = mServoArgs;
|
||||||
int height = getHeight();
|
options.width = getWidth();
|
||||||
|
options.height = getHeight();
|
||||||
|
options.enableLogs = true;
|
||||||
DisplayMetrics metrics = new DisplayMetrics();
|
DisplayMetrics metrics = new DisplayMetrics();
|
||||||
mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||||
float density = metrics.density;
|
options.density = metrics.density;
|
||||||
|
|
||||||
inGLThread(() -> {
|
inGLThread(() -> {
|
||||||
String uri = mInitialUri == null ? null : mInitialUri.toString();
|
String uri = mInitialUri == null ? null : mInitialUri.toString();
|
||||||
mServo = new Servo(this, this, mClient, mActivity, mServoArgs, uri, mServoLog, width, height, density, showLogs);
|
options.url = uri;
|
||||||
|
options.logStr = mServoLog;
|
||||||
|
mServo = new Servo(options, this, this, mClient, mActivity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue