https_everywhere_lib_wasm/
lib.rs1use wasm_bindgen::prelude::*;
2use js_sys::{Array, Reflect, Boolean, Set, Object};
3use std::rc::Rc;
4
5mod debugging;
6
7pub use https_everywhere_lib_core::{Rule, CookieRule, RuleSet};
8use https_everywhere_lib_core::RuleSets as CoreRuleSets;
9
10const ERR: &str = "could not convert property to JS";
11
12#[derive(Debug)]
13struct StaticJsStrings {
14 active: JsValue,
15 cookierules: JsValue,
16 default_off: JsValue,
17 default_state: JsValue,
18 exclusion: JsValue,
19 exclusions: JsValue,
20 from: JsValue,
21 host: JsValue,
22 mixed_content: JsValue,
23 name: JsValue,
24 note: JsValue,
25 platform: JsValue,
26 rule: JsValue,
27 rules: JsValue,
28 scope: JsValue,
29 securecookie: JsValue,
30 state: JsValue,
31 target: JsValue,
32 to: JsValue,
33 user_rule: JsValue,
34}
35
36thread_local! {
37 static JS_STRINGS: StaticJsStrings = StaticJsStrings {
38 active: JsValue::from("active"),
39 cookierules: JsValue::from("cookierules"),
40 default_off: JsValue::from("default_off"),
41 default_state: JsValue::from("default_state"),
42 exclusion: JsValue::from("exclusion"),
43 exclusions: JsValue::from("exclusions"),
44 from: JsValue::from("from"),
45 host: JsValue::from("host"),
46 mixed_content: JsValue::from("mixedcontent"),
47 name: JsValue::from("name"),
48 note: JsValue::from("note"),
49 platform: JsValue::from("platform"),
50 rule: JsValue::from("rule"),
51 rules: JsValue::from("rules"),
52 scope: JsValue::from("scope"),
53 securecookie: JsValue::from("securecookie"),
54 state: JsValue::from("state"),
55 target: JsValue::from("target"),
56 to: JsValue::from("to"),
57 user_rule: JsValue::from("user rule"),
58 };
59}
60
61pub trait ToJavaScript {
62 fn to_javascript(&self) -> JsValue;
63}
64
65impl ToJavaScript for Vec<Rc<RuleSet>>{
66 fn to_javascript(&self) -> JsValue {
68 let results = Set::new(&Array::new());
69 for rs in self {
70 results.add(&rs.to_javascript());
71 }
72 results.into()
73 }
74}
75
76impl ToJavaScript for Rule {
77 fn to_javascript(&self) -> JsValue {
79 let object = Object::new();
80 JS_STRINGS.with(|jss| {
81 match &self {
82 Rule::Trivial => {
83 Reflect::set(&object, &jss.from, &JsValue::from("^http:")).expect(ERR);
84 Reflect::set(&object, &jss.to, &JsValue::from("https:")).expect(ERR);
85 },
86 Rule::NonTrivial(from_regex, to) => {
87 Reflect::set(&object, &jss.from, &JsValue::from(from_regex)).expect(ERR);
88 Reflect::set(&object, &jss.to, &JsValue::from(to)).expect(ERR);
89 }
90 }
91 });
92 object.into()
93 }
94}
95
96impl ToJavaScript for CookieRule {
97 fn to_javascript(&self) -> JsValue {
99 let object = Object::new();
100 JS_STRINGS.with(|jss| {
101 Reflect::set(&object, &jss.host, &JsValue::from(&self.host_regex)).expect(ERR);
102 Reflect::set(&object, &jss.name, &JsValue::from(&self.name_regex)).expect(ERR);
103 });
104 object.into()
105 }
106}
107
108impl ToJavaScript for RuleSet {
109 fn to_javascript(&self) -> JsValue {
111 let object = Object::new();
112 JS_STRINGS.with(|jss| {
113 Reflect::set(&object, &jss.name, &JsValue::from(&self.name)).expect(ERR);
114 Reflect::set(&object, &jss.active, &JsValue::from_bool(self.active.clone())).expect(ERR);
115 Reflect::set(&object, &jss.default_state, &JsValue::from_bool(self.default_state.clone())).expect(ERR);
116 match self.scope.as_ref() {
117 Some(scope) => { Reflect::set(&object, &jss.scope, &JsValue::from(scope)).expect(ERR); },
118 None => {}
119 }
120 match &self.note {
121 Some(note) => { Reflect::set(&object, &jss.note, &JsValue::from(note)).expect(ERR); },
122 None => {}
123 }
124
125 let rules = Array::new();
126 for rule in &self.rules {
127 rules.push(&rule.to_javascript());
128 }
129 Reflect::set(&object, &jss.rules, &rules).expect(ERR);
130
131 match &self.exclusions {
132 Some(exclusions) => {
133 Reflect::set(&object, &jss.exclusions, &JsValue::from(exclusions)).expect(ERR);
134 },
135 None => {}
136 }
137
138 match &self.cookierules {
139 Some(cookierules) => {
140 let cookierules_array = Array::new();
141 for cookierule in cookierules {
142 cookierules_array.push(&cookierule.to_javascript());
143 }
144 Reflect::set(&object, &jss.cookierules, &cookierules_array).expect(ERR);
145 },
146 None => {}
147 }
148 });
149 object.into()
150 }
151}
152
153pub trait JsRuleSet {
154 fn add_rules(&mut self, rules_array: &Array);
155 fn add_exclusions(&mut self, exclusions_array: &Array);
156 fn add_cookierules(&mut self, cookierules_array: &Array);
157 fn is_equivalent_to(&self, ruleset_jsval: &JsValue) -> bool;
158}
159
160impl JsRuleSet for RuleSet {
161 fn add_rules(&mut self, rules_array: &Array){
163 for rule_result in rules_array.values() {
164 let rule_obj = rule_result.unwrap();
165 if rule_obj.is_object() {
166 JS_STRINGS.with(|jss| {
167 let from_string = match Reflect::get(&rule_obj, &jss.from) {
168 Ok(from) => {
169 if from.is_string() {
170 from.as_string().unwrap()
171 } else {
172 String::new()
173 }
174 },
175 _ => String::new()
176 };
177 let to_string = match Reflect::get(&rule_obj, &jss.to) {
178 Ok(to) => {
179 if to.is_string() {
180 to.as_string().unwrap()
181 } else {
182 String::new()
183 }
184 },
185 _ => String::new()
186 };
187
188 self.rules.push(Rule::new(from_string, to_string));
189 });
190 }
191 };
192 }
193
194 fn add_exclusions(&mut self, exclusions_array: &Array){
196 let mut exclusions = vec![];
197 for exclusion_result in exclusions_array.values() {
198 let exclusion_string = exclusion_result.unwrap();
199 if exclusion_string.is_string() {
200 exclusions.push(exclusion_string.as_string().unwrap());
201 }
202 }
203
204 self.exclusions = Some(exclusions.join("|"));
205 }
206
207 fn add_cookierules(&mut self, cookierules_array: &Array){
209 let mut cookierules = vec![];
210 for cookierule_result in cookierules_array.values() {
211 let cookierule_obj = cookierule_result.unwrap();
212 if cookierule_obj.is_object() {
213 JS_STRINGS.with(|jss| {
214 let host_string = Reflect::get(&cookierule_obj, &jss.host).unwrap();
215 let name_string = Reflect::get(&cookierule_obj, &jss.name).unwrap();
216 if host_string.is_string() && name_string.is_string() {
217 cookierules.push(
218 CookieRule::new(
219 host_string.as_string().unwrap(),
220 name_string.as_string().unwrap()));
221 }
222 });
223 }
224 }
225
226 self.cookierules = Some(cookierules);
227 }
228
229 fn is_equivalent_to(&self, ruleset_jsval: &JsValue) -> bool {
231 let mut result = false;
232
233 if ruleset_jsval.is_object() {
234 JS_STRINGS.with(|jss| {
235 let name_jsval = Reflect::get(&ruleset_jsval, &jss.name).unwrap();
236 let note_jsval = Reflect::get(&ruleset_jsval, &jss.note).unwrap();
237 let active_jsval = Reflect::get(&ruleset_jsval, &jss.active).unwrap();
238 let rules_jsval = Reflect::get(&ruleset_jsval, &jss.rules).unwrap();
239
240 let name_is_equiv = name_jsval.is_string() && name_jsval.as_string().unwrap() == self.name;
241 let note_is_equiv = (note_jsval.is_null() && self.note == None) ||
242 (note_jsval.is_string() && self.note == Some(note_jsval.as_string().unwrap()));
243 let active_is_equiv = Boolean::from(active_jsval) == self.active;
244
245 if name_is_equiv && note_is_equiv && active_is_equiv && Array::is_array(&rules_jsval) {
246 let rules_array = Array::from(&rules_jsval);
247 if rules_array.length() == self.rules.len() as u32 {
248 let mut each_rule_equiv = true;
249 let mut counter = 0;
250 for rule_result in rules_array.values() {
251 let rule_jsval = rule_result.unwrap();
252 if rule_jsval.is_object() {
253 let to_jsval = Reflect::get(&rule_jsval, &jss.to).unwrap();
254 if let Some(to_string) = to_jsval.as_string() {
255 match &self.rules[counter] {
256 Rule::Trivial => {
257 each_rule_equiv = to_string == "https:";
258 },
259 Rule::NonTrivial(_, this_to_val) => {
260 each_rule_equiv = &to_string == this_to_val;
261 }
262 }
263 }
264 counter += 1;
265 } else {
266 each_rule_equiv = false;
267 }
268 }
269 result = each_rule_equiv;
270 }
271 }
272 });
273 }
274 result
275 }
276
277}
278
279
280#[wasm_bindgen]
282#[derive(Debug)]
283pub struct RuleSets(CoreRuleSets);
284
285#[wasm_bindgen]
286impl RuleSets {
287
288 pub fn new() -> RuleSets {
290 RuleSets(CoreRuleSets::new())
291 }
292
293 pub fn count_targets(&self) -> usize {
295 self.0.count_targets()
296 }
297
298 pub fn add_all_from_js_array(&mut self, array: &Array, enable_mixed_rulesets: &Boolean, rule_active_states: &JsValue, scope: &JsValue) {
310
311 let scope: Rc<Option<String>> = if scope.is_string() {
312 Rc::new(Some(scope.as_string().unwrap()))
313 } else {
314 Rc::new(None)
315 };
316
317 let mut add_one_from_js = |jsval| {
318 JS_STRINGS.with(|jss| {
319 let mut ruleset_name: String;
320 let mut default_state = true;
321 let mut note = String::new();
322
323 let default_off = Reflect::get(&jsval, &jss.default_off).unwrap();
324 if default_off.is_string() {
325 if default_off != jss.user_rule {
326 default_state = false;
327 }
328 if let Some(default_off_string) = default_off.as_string() {
329 note += &(default_off_string + "\n");
330 }
331 }
332
333 let platform = Reflect::get(&jsval, &jss.platform).unwrap();
334 if platform.is_string() {
335 if platform == jss.mixed_content {
336 if !enable_mixed_rulesets.value_of() {
337 default_state = false;
338 }
339 } else if !platform.is_undefined() {
340 default_state = false;
341 }
342 if let Some(platform_string) = platform.as_string() {
343 note.push_str("Platform(s): ");
344 note += &(platform_string + "\n");
345 }
346 }
347
348 let mut active = default_state;
349 let name = Reflect::get(&jsval, &jss.name).unwrap();
350 if name.is_string() {
351 ruleset_name = name.as_string().unwrap();
352 if rule_active_states.is_object() {
353 let active_state = Reflect::get(&rule_active_states, &JsValue::from_str(&ruleset_name)).unwrap();
354 match active_state.as_bool() {
355 Some(false) => { active = false; }
356 Some(true) => { active = true; }
357 _ => {}
358 }
359 }
360
361 let mut rs = RuleSet::new(ruleset_name, Rc::clone(&scope));
362 rs.default_state = default_state;
363 rs.note = match note.len() {
364 0 => None,
365 _ => Some(note.trim().to_string())
366 };
367 rs.active = active;
368
369 if let Ok(rules_jsval) = Reflect::get(&jsval, &jss.rule) {
370 if Array::is_array(&rules_jsval) {
371 rs.add_rules(&Array::from(&rules_jsval));
372 }
373 }
374
375 if let Ok(exclusion_jsval) = Reflect::get(&jsval, &jss.exclusion) {
376 if Array::is_array(&exclusion_jsval) {
377 rs.add_exclusions(&Array::from(&exclusion_jsval));
378 }
379 }
380
381 if let Ok(securecookie_jsval) = Reflect::get(&jsval, &jss.securecookie) {
382 if Array::is_array(&securecookie_jsval) {
383 rs.add_cookierules(&Array::from(&securecookie_jsval));
384 }
385 }
386
387 let rs_rc = Rc::new(rs);
388 let target_jsval = Reflect::get(&jsval, &jss.target).unwrap();
389 if Array::is_array(&target_jsval) {
390 for target_result in &Array::from(&target_jsval).values() {
391 let target = target_result.unwrap();
392 if target.is_string() {
393 let target = target.as_string().unwrap();
394 match (self.0).0.get_mut(&target) {
395 Some(rs_vector) => {
396 rs_vector.push(Rc::clone(&rs_rc));
397 },
398 None => {
399 (self.0).0.insert(target, vec![Rc::clone(&rs_rc)]);
400 }
401 }
402 }
403 }
404 }
405 }
406 });
407 };
408
409 for val in array.values() {
410 let jsval = val.unwrap();
411 if jsval.is_object() {
412 add_one_from_js(jsval);
413 }
414 }
415 }
416
417 #[cfg(debug_assertions)]
418 pub fn print_targets (&self) {
420 console_log!("{:?}", (self.0).0);
421 }
422
423 pub fn remove_ruleset (&mut self, ruleset_jsval: &JsValue) {
425 if ruleset_jsval.is_object() {
426 JS_STRINGS.with(|jss| {
427 if let Ok(name) = Reflect::get(&ruleset_jsval, &jss.name) {
428 if name.is_string() {
429 let name = name.as_string().unwrap();
430 if (self.0).0.contains_key(&name) {
431 let ruleset_vec = (self.0).0.remove(&name).unwrap();
432 let mut new_ruleset_vec = vec![];
433
434 for ruleset in ruleset_vec {
435 if !ruleset.is_equivalent_to(ruleset_jsval) {
436 new_ruleset_vec.push(Rc::clone(&ruleset));
437 }
438 }
439
440 if new_ruleset_vec.len() > 0 {
441 (self.0).0.insert(name, new_ruleset_vec);
442 }
443 }
444 }
445 }
446 });
447 }
448 }
449
450 pub fn potentially_applicable (&self, host: &JsValue) -> JsValue {
456 if host.is_string() {
457 let host = host.as_string().unwrap();
458 self.0.potentially_applicable(&host).to_javascript()
459 } else {
460 Set::new(&Array::new()).into()
461 }
462 }
463}