Skip to content

Commit 0d1b6d2

Browse files
committed
support loading resources.json
1 parent ea20a60 commit 0d1b6d2

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

src/lib.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

3133
enum 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)]
283358
pub fn run_app() {
284359
std::panic::set_hook(Box::new(console_error_panic_hook::hook));

static/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@
1414
textarea {
1515
height: 8em;
1616
}
17+
input[type="file"] {
18+
display: none;
19+
}
20+
label {
21+
display: inline-block;
22+
cursor: pointer;
23+
padding-block: 1px;
24+
padding-inline: 6px;
25+
border-width: 2px;
26+
border-style: outset;
27+
border-color: buttonborder;
28+
background-color: buttonface;
29+
}
1730
.error {
1831
color: #800;
1932
font-weight: bold;

0 commit comments

Comments
 (0)