@@ -26,6 +26,8 @@ struct Model {
2626
2727 cosmetic_url : String ,
2828 cosmetic_result : Option < adblock:: cosmetic_filter_cache:: UrlSpecificResources > ,
29+
30+ resources : Vec < adblock:: resources:: Resource > ,
2931}
3032
3133enum Msg {
@@ -36,6 +38,7 @@ enum Msg {
3638 UpdateNetworkSourceUrl ( String ) ,
3739 UpdateNetworkRequestType ( String ) ,
3840 UpdateCosmeticUrl ( String ) ,
41+ LoadResourcesJson ( String ) ,
3942 DownloadDat ,
4043}
4144
@@ -62,6 +65,8 @@ impl Component for Model {
6265
6366 cosmetic_url : String :: new ( ) ,
6467 cosmetic_result : None ,
68+
69+ resources : vec ! [ ] ,
6570 }
6671 }
6772
@@ -93,6 +98,7 @@ impl Component for Model {
9398 let mut filter_set = adblock:: lists:: FilterSet :: new ( true ) ;
9499 self . metadata = filter_set. add_filter_list ( & self . filter_list , ParseOptions :: default ( ) ) ;
95100 self . engine = adblock:: Engine :: from_filter_set ( filter_set, false ) ;
101+ self . engine . use_resources ( self . resources . iter ( ) . map ( |r| r. clone ( ) ) ) ;
96102 self . check_network_urls ( ) ;
97103 }
98104 Msg :: UpdateNetworkUrl ( new_value) => {
@@ -111,6 +117,11 @@ impl Component for Model {
111117 self . cosmetic_url = new_value;
112118 self . cosmetic_result = Some ( self . engine . url_cosmetic_resources ( & self . cosmetic_url ) ) ;
113119 }
120+ Msg :: LoadResourcesJson ( new_value) => {
121+ let resources: Vec < _ > = serde_json:: from_str ( & new_value) . unwrap ( ) ;
122+ self . resources = resources;
123+ self . engine . use_resources ( self . resources . iter ( ) . map ( |r| r. clone ( ) ) ) ;
124+ }
114125 Msg :: DownloadDat => {
115126 let data = self . engine . serialize_raw ( ) . unwrap ( ) ;
116127 util:: save_bin_file ( "rs-ABPFilterParserData.dat" , & data[ ..] ) ;
@@ -165,6 +176,32 @@ impl Component for Model {
165176 <h2>{ "Test a list" } </h2>
166177 <h3>{ "List contents" } </h3>
167178 <textarea value={ self . filter_list. clone( ) } oninput={ ctx. link( ) . callback( |e: InputEvent | Msg :: UpdateFilterList ( e. target( ) . unwrap( ) . dyn_into:: <web_sys:: HtmlTextAreaElement >( ) . unwrap( ) . value( ) ) ) } />
179+ <input type ="file" accept=".json,application/json" id="load_resources_json" oninput={
180+ let link = ctx. link( ) . clone( ) ;
181+ move |e: InputEvent | {
182+ let link = link. clone( ) ;
183+ let input_element = e. target( ) . unwrap( ) . dyn_into:: <web_sys:: HtmlInputElement >( ) . unwrap( ) ;
184+ if let Some ( file) = input_element. files( ) . unwrap( ) . item( 0 ) {
185+ unsafe {
186+ read_file_text_and_then( & file, move |text| {
187+ let link = link. clone( ) ;
188+ link. send_message( Msg :: LoadResourcesJson ( text) ) ;
189+ } ) ;
190+ }
191+ }
192+ input_element. set_value( "" ) ;
193+ }
194+ } />
195+ <div>
196+ <label for ="load_resources_json" ><span>{ "Load " } </span><code>{ "resources.json" } </code></label>
197+ <i>{
198+ if self . resources. len( ) > 0 {
199+ format!( " {} resources loaded" , self . resources. len( ) )
200+ } else {
201+ " No resources loaded" . to_string( )
202+ }
203+ } </i>
204+ </div>
168205 { Self :: view_list_metadata( & self . metadata) }
169206 <h3>{ "Check a network request" } </h3>
170207 <h4>{ "Request URL" } </h4>
@@ -194,10 +231,17 @@ impl Component for Model {
194231 <input type ="text" value={ self . cosmetic_url. clone( ) } oninput={ ctx. link( ) . callback( |e: InputEvent | Msg :: UpdateCosmeticUrl ( e. target( ) . unwrap( ) . dyn_into:: <web_sys:: HtmlInputElement >( ) . unwrap( ) . value( ) ) ) } />
195232 {
196233 if let Some ( cosmetic_result) = self . cosmetic_result. as_ref( ) {
234+ let resources_disclaimer = if self . resources. is_empty( ) {
235+ html! {
236+ <p><i>{ "Note: scriptlets will not show up, as none have been loaded" } </i></p>
237+ }
238+ } else {
239+ html! { }
240+ } ;
197241 html! {
198242 <>
199243 <p><code>{ format!( "{:?}" , cosmetic_result) } </code></p>
200- <p><i> { "Note: scriptlets will not show up, as none have been loaded" } </i></p>
244+ { resources_disclaimer }
201245 </>
202246 }
203247 } else {
@@ -279,6 +323,37 @@ impl Model {
279323 }
280324}
281325
326+ /// Some massive hacks to make `FileReader` accessible to WASM.
327+ ///
328+ /// Don't use this with multiple file inputs.
329+ unsafe fn read_file_text_and_then ( file : & web_sys:: File , closure : impl FnOnce ( String ) + ' static ) {
330+ static mut FILEREADER : Option < web_sys:: FileReader > = None ;
331+ static mut FILEREADER_CLOSURE : Option < wasm_bindgen:: closure:: Closure < ( dyn FnMut ( yew:: ProgressEvent ) + ' static ) > > = None ;
332+
333+ fn onload_helper ( e : ProgressEvent , closure : impl FnOnce ( String ) ) {
334+ let text = e. target ( ) . unwrap ( ) . dyn_into :: < web_sys:: FileReader > ( ) . unwrap ( ) . result ( ) . unwrap ( ) . as_string ( ) . unwrap ( ) ;
335+ closure ( text) ;
336+ unsafe {
337+ FILEREADER = None ;
338+ FILEREADER_CLOSURE = None ;
339+ }
340+ }
341+
342+ let filereader = web_sys:: FileReader :: new ( ) . unwrap ( ) ;
343+ let closure = unsafe {
344+ FILEREADER_CLOSURE = Some ( wasm_bindgen:: closure:: Closure :: once ( move |e : ProgressEvent | {
345+ onload_helper ( e, closure) ;
346+ } ) ) ;
347+ ( * ( FILEREADER_CLOSURE . as_ref ( ) ) . unwrap ( ) ) . as_ref ( ) . unchecked_ref ( )
348+ } ;
349+ filereader. set_onload ( Some ( closure) ) ;
350+
351+ unsafe {
352+ FILEREADER = Some ( filereader) ;
353+ FILEREADER . as_ref ( ) . unwrap ( ) . read_as_text ( file) . unwrap ( ) ;
354+ }
355+ }
356+
282357#[ wasm_bindgen( start) ]
283358pub fn run_app ( ) {
284359 std:: panic:: set_hook ( Box :: new ( console_error_panic_hook:: hook) ) ;
0 commit comments