11use core:: fmt;
22use css_inline:: { CSSInliner , StylesheetCache } ;
33use 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
1112trait 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