Skip to content

Commit 623ba11

Browse files
committed
chore(java): Update jni to 0.22
Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
1 parent d47a55e commit 623ba11

3 files changed

Lines changed: 121 additions & 94 deletions

File tree

bindings/java/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Changed
6+
7+
- Update `jni` to `0.22`.
8+
59
### Fixed
610

711
- `file://` stylesheet URLs not percent-decoded and broken on Windows drive letters.

bindings/java/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
99
path = "src/main/rust/lib.rs"
1010

1111
[dependencies]
12-
jni = "0.21.1"
12+
jni = "0.22.4"
1313

1414
[dependencies.css-inline]
1515
path = "../../css-inline"

bindings/java/src/main/rust/lib.rs

Lines changed: 116 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,122 @@
11
use core::fmt;
22
use css_inline::{CSSInliner, StylesheetCache};
33
use jni::{
4-
JNIEnv,
5-
errors::Result as JNIResult,
4+
Env, EnvUnowned,
5+
errors::{ErrorPolicy, Result as JNIResult},
6+
jni_sig, jni_str,
67
objects::{JClass, JObject, JString},
7-
sys::jstring,
8+
strings::{JNIStr, JNIString},
89
};
9-
use std::{borrow::Cow, num::NonZeroUsize};
10+
use std::{any::Any, borrow::Cow, num::NonZeroUsize};
1011

1112
trait JNIExt {
12-
fn get_rust_string(&mut self, obj: &JString) -> String;
13-
fn to_jstring(&mut self, obj: String) -> jstring;
14-
fn get_bool_field(&mut self, obj: &JObject, name: &str) -> JNIResult<bool>;
15-
fn get_int_field(&mut self, obj: &JObject, name: &str) -> JNIResult<i32>;
16-
fn get_string_field_opt(&mut self, obj: &JObject, name: &str) -> JNIResult<Option<String>>;
13+
fn get_rust_string(&mut self, obj: &JString) -> JNIResult<String>;
14+
fn get_bool_field(&mut self, obj: &JObject, name: &JNIStr) -> JNIResult<bool>;
15+
fn get_int_field(&mut self, obj: &JObject, name: &JNIStr) -> JNIResult<i32>;
16+
fn get_string_field_opt(&mut self, obj: &JObject, name: &JNIStr) -> JNIResult<Option<String>>;
1717
}
1818

19-
impl<'a> JNIExt for JNIEnv<'a> {
20-
fn get_rust_string(&mut self, obj: &JString) -> String {
21-
self.get_string(obj)
22-
.expect("Failed to get Java String")
23-
.into()
19+
impl<'a> JNIExt for Env<'a> {
20+
fn get_rust_string(&mut self, obj: &JString) -> JNIResult<String> {
21+
Ok(obj.mutf8_chars(self)?.to_string())
2422
}
2523

26-
fn to_jstring(&mut self, obj: String) -> jstring {
27-
self.new_string(obj)
28-
.expect("Failed to get Java String")
29-
.into_raw()
24+
fn get_bool_field(&mut self, obj: &JObject, name: &JNIStr) -> JNIResult<bool> {
25+
self.get_field(obj, name, jni_sig!("Z"))?.z()
3026
}
3127

32-
fn get_bool_field(&mut self, obj: &JObject, name: &str) -> JNIResult<bool> {
33-
self.get_field(obj, name, "Z")?.z()
28+
fn get_int_field(&mut self, obj: &JObject, name: &JNIStr) -> JNIResult<i32> {
29+
self.get_field(obj, name, jni_sig!("I"))?.i()
3430
}
3531

36-
fn get_int_field(&mut self, obj: &JObject, name: &str) -> JNIResult<i32> {
37-
self.get_field(obj, name, "I")?.i()
38-
}
39-
40-
fn get_string_field_opt(&mut self, cfg: &JObject, name: &str) -> JNIResult<Option<String>> {
41-
let value = self.get_field(cfg, name, "Ljava/lang/String;")?.l()?;
32+
fn get_string_field_opt(&mut self, cfg: &JObject, name: &JNIStr) -> JNIResult<Option<String>> {
33+
let value = self
34+
.get_field(cfg, name, jni_sig!("Ljava/lang/String;"))?
35+
.l()?;
4236
if value.is_null() {
4337
Ok(None)
4438
} else {
45-
Ok(Some(self.get_string(&JString::from(value))?.into()))
39+
let value = self.new_cast_local_ref::<JString>(&value)?;
40+
self.get_rust_string(&value).map(Some)
4641
}
4742
}
4843
}
4944

50-
enum Error<E> {
45+
enum Error {
5146
Jni(jni::errors::Error),
52-
Other(E),
47+
Other(String),
5348
}
5449

55-
impl<E> From<jni::errors::Error> for Error<E> {
50+
impl From<jni::errors::Error> for Error {
5651
fn from(value: jni::errors::Error) -> Self {
5752
Error::Jni(value)
5853
}
5954
}
6055

61-
impl<E: fmt::Display> fmt::Display for Error<E> {
62-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56+
impl fmt::Display for Error {
57+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6358
match self {
6459
Error::Jni(error) => error.fmt(f),
65-
Error::Other(error) => error.fmt(f),
60+
Error::Other(error) => f.write_str(error),
6661
}
6762
}
6863
}
6964

70-
fn build_inliner(
71-
env: &mut JNIEnv,
72-
cfg: JObject,
73-
) -> Result<CSSInliner<'static>, Error<css_inline::ParseError>> {
74-
let inline_style_tags = env.get_bool_field(&cfg, "inlineStyleTags")?;
75-
let keep_style_tags = env.get_bool_field(&cfg, "keepStyleTags")?;
76-
let keep_link_tags = env.get_bool_field(&cfg, "keepLinkTags")?;
77-
let keep_at_rules = env.get_bool_field(&cfg, "keepAtRules")?;
78-
let minify_css = env.get_bool_field(&cfg, "minifyCss")?;
79-
let load_remote_stylesheets = env.get_bool_field(&cfg, "loadRemoteStylesheets")?;
80-
let cache_size = env.get_int_field(&cfg, "cacheSize")?;
81-
let preallocate_node_capacity = env.get_int_field(&cfg, "preallocateNodeCapacity")?;
82-
let remove_inlined_selectors = env.get_bool_field(&cfg, "removeInlinedSelectors")?;
83-
let apply_width_attributes = env.get_bool_field(&cfg, "applyWidthAttributes")?;
84-
let apply_height_attributes = env.get_bool_field(&cfg, "applyHeightAttributes")?;
85-
86-
let extra_css = env.get_string_field_opt(&cfg, "extraCss")?;
87-
let base_url = env.get_string_field_opt(&cfg, "baseUrl")?;
65+
/// Maps Rust errors & panics onto `CssInlineException`.
66+
struct ThrowCssInlineException;
67+
68+
impl<T: Default, E: fmt::Display> ErrorPolicy<T, E> for ThrowCssInlineException {
69+
type Captures<'local: 'method, 'method> = ();
70+
71+
fn on_error<'local: 'method, 'method>(
72+
env: &mut Env<'local>,
73+
_: &mut Self::Captures<'local, 'method>,
74+
error: E,
75+
) -> JNIResult<T> {
76+
throw(env, error.to_string())
77+
}
78+
79+
fn on_panic<'local: 'method, 'method>(
80+
env: &mut Env<'local>,
81+
_: &mut Self::Captures<'local, 'method>,
82+
payload: Box<dyn Any + Send + 'static>,
83+
) -> JNIResult<T> {
84+
let message = match payload.downcast_ref::<&'static str>() {
85+
Some(message) => (*message).to_string(),
86+
None => match payload.downcast_ref::<String>() {
87+
Some(message) => message.clone(),
88+
None => "Unknown panic".to_string(),
89+
},
90+
};
91+
throw(env, format!("Panic: {message}"))
92+
}
93+
}
94+
95+
fn throw<T: Default>(env: &mut Env, message: String) -> JNIResult<T> {
96+
if env.exception_check() {
97+
return Ok(T::default());
98+
}
99+
let exception = env.find_class(jni_str!("org/cssinline/CssInlineException"))?;
100+
// `throw_new` reports the exception it just threw via `Err(JavaException)`
101+
let _ = env.throw_new(exception, JNIString::new(message));
102+
Ok(T::default())
103+
}
104+
105+
fn build_inliner(env: &mut Env, cfg: &JObject) -> Result<CSSInliner<'static>, Error> {
106+
let inline_style_tags = env.get_bool_field(cfg, jni_str!("inlineStyleTags"))?;
107+
let keep_style_tags = env.get_bool_field(cfg, jni_str!("keepStyleTags"))?;
108+
let keep_link_tags = env.get_bool_field(cfg, jni_str!("keepLinkTags"))?;
109+
let keep_at_rules = env.get_bool_field(cfg, jni_str!("keepAtRules"))?;
110+
let minify_css = env.get_bool_field(cfg, jni_str!("minifyCss"))?;
111+
let load_remote_stylesheets = env.get_bool_field(cfg, jni_str!("loadRemoteStylesheets"))?;
112+
let cache_size = env.get_int_field(cfg, jni_str!("cacheSize"))?;
113+
let preallocate_node_capacity = env.get_int_field(cfg, jni_str!("preallocateNodeCapacity"))?;
114+
let remove_inlined_selectors = env.get_bool_field(cfg, jni_str!("removeInlinedSelectors"))?;
115+
let apply_width_attributes = env.get_bool_field(cfg, jni_str!("applyWidthAttributes"))?;
116+
let apply_height_attributes = env.get_bool_field(cfg, jni_str!("applyHeightAttributes"))?;
117+
118+
let extra_css = env.get_string_field_opt(cfg, jni_str!("extraCss"))?;
119+
let base_url = env.get_string_field_opt(cfg, jni_str!("baseUrl"))?;
88120
let mut builder = CSSInliner::options()
89121
.inline_style_tags(inline_style_tags)
90122
.keep_style_tags(keep_style_tags)
@@ -103,7 +135,7 @@ fn build_inliner(
103135
Ok(url) => {
104136
builder = builder.base_url(Some(url));
105137
}
106-
Err(error) => return Err(Error::Other(error)),
138+
Err(error) => return Err(Error::Other(error.to_string())),
107139
}
108140
}
109141

@@ -116,49 +148,40 @@ fn build_inliner(
116148
Ok(builder.build())
117149
}
118150

119-
fn throw(mut env: JNIEnv, message: String) -> jstring {
120-
let exception = env
121-
.find_class("org/cssinline/CssInlineException")
122-
.expect("CssInlineException class not found");
123-
env.throw_new(exception, message)
124-
.expect("Failed to throw CssInlineException");
125-
std::ptr::null_mut()
126-
}
127-
128151
#[unsafe(no_mangle)]
129-
pub extern "system" fn Java_org_cssinline_CssInline_nativeInline(
130-
mut env: JNIEnv,
131-
_class: JClass,
132-
input: JString,
133-
cfg: JObject,
134-
) -> jstring {
135-
let html = env.get_rust_string(&input);
136-
let inliner = match build_inliner(&mut env, cfg) {
137-
Ok(inliner) => inliner,
138-
Err(error) => return throw(env, error.to_string()),
139-
};
140-
match inliner.inline(&html) {
141-
Ok(out) => env.to_jstring(out),
142-
Err(error) => throw(env, error.to_string()),
143-
}
152+
pub extern "system" fn Java_org_cssinline_CssInline_nativeInline<'caller>(
153+
mut env: EnvUnowned<'caller>,
154+
_class: JClass<'caller>,
155+
input: JString<'caller>,
156+
cfg: JObject<'caller>,
157+
) -> JObject<'caller> {
158+
env.with_env(|env| -> Result<JObject, Error> {
159+
let html = env.get_rust_string(&input)?;
160+
let inliner = build_inliner(env, &cfg)?;
161+
let out = inliner
162+
.inline(&html)
163+
.map_err(|error| Error::Other(error.to_string()))?;
164+
Ok(JString::from_str(env, out)?.into())
165+
})
166+
.resolve::<ThrowCssInlineException>()
144167
}
145168

146169
#[unsafe(no_mangle)]
147-
pub extern "system" fn Java_org_cssinline_CssInline_nativeInlineFragment(
148-
mut env: JNIEnv,
149-
_class: JClass,
150-
input: JString,
151-
css: JString,
152-
cfg: JObject,
153-
) -> jstring {
154-
let html = env.get_rust_string(&input);
155-
let css = env.get_rust_string(&css);
156-
let inliner = match build_inliner(&mut env, cfg) {
157-
Ok(inliner) => inliner,
158-
Err(error) => return throw(env, error.to_string()),
159-
};
160-
match inliner.inline_fragment(&html, &css) {
161-
Ok(out) => env.to_jstring(out),
162-
Err(error) => throw(env, error.to_string()),
163-
}
170+
pub extern "system" fn Java_org_cssinline_CssInline_nativeInlineFragment<'caller>(
171+
mut env: EnvUnowned<'caller>,
172+
_class: JClass<'caller>,
173+
input: JString<'caller>,
174+
css: JString<'caller>,
175+
cfg: JObject<'caller>,
176+
) -> JObject<'caller> {
177+
env.with_env(|env| -> Result<JObject, Error> {
178+
let html = env.get_rust_string(&input)?;
179+
let css = env.get_rust_string(&css)?;
180+
let inliner = build_inliner(env, &cfg)?;
181+
let out = inliner
182+
.inline_fragment(&html, &css)
183+
.map_err(|error| Error::Other(error.to_string()))?;
184+
Ok(JString::from_str(env, out)?.into())
185+
})
186+
.resolve::<ThrowCssInlineException>()
164187
}

0 commit comments

Comments
 (0)