webplatform/
lib.rs

1#![allow(unused_unsafe)]
2
3extern crate libc;
4
5use std::ffi::{CString, CStr};
6use std::{mem, fmt};
7use std::str;
8use std::borrow::ToOwned;
9use std::ops::Deref;
10use std::cell::RefCell;
11use std::clone::Clone;
12use std::rc::Rc;
13use std::collections::HashSet;
14use std::char;
15use std::iter::IntoIterator;
16
17mod webplatform {
18    pub use emscripten_asm_const;
19    pub use emscripten_asm_const_int;
20}
21
22trait Interop {
23    fn as_int(self, _:&mut Vec<CString>) -> libc::c_int;
24}
25
26impl Interop for i32 {
27    fn as_int(self, _:&mut Vec<CString>) -> libc::c_int {
28        return self;
29    }
30}
31
32impl<'a> Interop for &'a str {
33    fn as_int(self, arena:&mut Vec<CString>) -> libc::c_int {
34        let c = CString::new(self).unwrap();
35        let ret = c.as_ptr() as libc::c_int;
36        arena.push(c);
37        return ret;
38    }
39}
40
41impl<'a> Interop for *const libc::c_void {
42    fn as_int(self, _:&mut Vec<CString>) -> libc::c_int {
43        return self as libc::c_int;
44    }
45}
46
47#[macro_export]
48macro_rules! js {
49    ( ($( $x:expr ),*) $y:expr ) => {
50        {
51            let mut arena:Vec<CString> = Vec::new();
52            const LOCAL: &'static [u8] = $y;
53            unsafe { ::webplatform::emscripten_asm_const_int(&LOCAL[0] as *const _ as *const libc::c_char, $(Interop::as_int($x, &mut arena)),*) }
54        }
55    };
56    ( $y:expr ) => {
57        {
58            const LOCAL: &'static [u8] = $y;
59            unsafe { ::webplatform::emscripten_asm_const_int(&LOCAL[0] as *const _ as *const libc::c_char) }
60        }
61    };
62}
63
64extern "C" {
65    pub fn emscripten_asm_con(s: *const libc::c_char);
66    pub fn emscripten_asm_const(s: *const libc::c_char);
67    pub fn emscripten_asm_const_int(s: *const libc::c_char, ...) -> libc::c_int;
68    pub fn emscripten_pause_main_loop();
69    pub fn emscripten_set_main_loop(m: extern fn(), fps: libc::c_int, infinite: libc::c_int);
70}
71
72pub struct HtmlNode<'a> {
73    id: libc::c_int,
74    doc: *const Document<'a>,
75}
76
77impl<'a> fmt::Debug for HtmlNode<'a> {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        write!(f, "HtmlNode({:?})", self.id)
80    }
81}
82
83impl<'a> Drop for HtmlNode<'a> {
84    fn drop(&mut self) {
85        println!("dropping HTML NODE {:?}", self.id);
86    }
87}
88
89pub struct JSRef<'a> {
90    ptr: *const HtmlNode<'a>,
91}
92
93impl<'a> fmt::Debug for JSRef<'a> {
94    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95        write!(f, "JSRef(HtmlNode({:?}))", self.id)
96    }
97}
98
99impl<'a> Clone for JSRef<'a> {
100    fn clone(&self) -> JSRef<'a> {
101        JSRef {
102            ptr: self.ptr,
103        }
104    }
105}
106
107impl<'a> HtmlNode<'a> {
108    pub fn root_ref(&self) -> JSRef<'a> {
109        JSRef {
110            ptr: &*self,
111        }
112    }
113}
114
115impl<'a> Deref for JSRef<'a> {
116    type Target = HtmlNode<'a>;
117
118    fn deref(&self) -> &HtmlNode<'a> {
119        unsafe {
120            &*self.ptr
121        }
122    }
123}
124
125pub struct Event<'a> {
126    pub target: Option<HtmlNode<'a>>
127}
128
129extern fn rust_caller<F: FnMut(Event)>(a: *const libc::c_void, docptr: *const libc::c_void, id: i32) {
130    let v:&mut F = unsafe { mem::transmute(a) };
131    v(Event {
132        target: if id == -1 {
133            None
134        } else {
135            Some(HtmlNode {
136                id: id,
137                doc: unsafe { mem::transmute(docptr) },
138            })
139        }
140        // target: None,
141    });
142}
143
144impl<'a> HtmlNode<'a> {
145    pub fn tagname(&self) -> String {
146        let a = js! { (self.id) b"\
147            var str = WEBPLATFORM.rs_refs[$0].tagName.toLowerCase();\
148            return allocate(intArrayFromString(str), 'i8', ALLOC_STACK);\
149        \0" };
150        unsafe {
151            str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
152        }
153    }
154
155    pub fn focus(&self) {
156        js! { (self.id) b"\
157            WEBPLATFORM.rs_refs[$0].focus();\
158        \0" };
159    }
160
161    pub fn html_set(&self, s: &str) {
162        js! { (self.id, s) b"\
163            WEBPLATFORM.rs_refs[$0].innerHTML = UTF8ToString($1);\
164        \0" };
165    }
166
167    pub fn html_get(&self) -> String {
168        let a = js! { (self.id) b"\
169            return allocate(intArrayFromString(WEBPLATFORM.rs_refs[$0].innerHTML), 'i8', ALLOC_STACK);\
170        \0" };
171        unsafe {
172            str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
173        }
174    }
175
176    pub fn class_get(&self) -> HashSet<String> {
177        let a = js! { (self.id) b"\
178            return allocate(intArrayFromString(WEBPLATFORM.rs_refs[$0].className), 'i8', ALLOC_STACK);\
179        \0" };
180        let class = unsafe {
181            str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
182        };
183        class.trim().split(char::is_whitespace).map(|x| x.to_string()).collect()
184    }
185
186    pub fn class_add(&self, s: &str) {
187        js! { (self.id, s) b"\
188            WEBPLATFORM.rs_refs[$0].classList.add(UTF8ToString($1));\
189        \0" };
190    }
191
192    pub fn class_remove(&self, s: &str) {
193        js! { (self.id, s) b"\
194            WEBPLATFORM.rs_refs[$0].classList.remove(UTF8ToString($1));\
195        \0" };
196    }
197
198    pub fn parent(&self) -> Option<HtmlNode<'a>> {
199        let id = js! { (self.id) b"\
200            var value = WEBPLATFORM.rs_refs[$0].parentNode;\
201            if (!value) {\
202                return -1;\
203            }\
204            return WEBPLATFORM.rs_refs.push(value) - 1;\
205        \0" };
206        if id < 0 {
207            None
208        } else {
209            Some(HtmlNode {
210                id: id,
211                doc: self.doc,
212            })
213        }
214    }
215
216    pub fn data_set(&self, s: &str, v: &str) {
217        js! { (self.id, s, v) b"\
218            WEBPLATFORM.rs_refs[$0].dataset[UTF8ToString($1)] = UTF8ToString($2);\
219        \0" };
220    }
221
222    pub fn data_get(&self, s: &str) -> Option<String> {
223        let a = js! { (self.id, s) b"\
224            var str = WEBPLATFORM.rs_refs[$0].dataset[UTF8ToString($1)];\
225            if (str == null) return -1;\
226            return allocate(intArrayFromString(str), 'i8', ALLOC_STACK);\
227        \0" };
228        if a == -1 {
229            None
230        } else {
231            Some(unsafe {
232                str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
233            })
234        }
235    }
236
237    pub fn style_set_str(&self, s: &str, v: &str) {
238        js! { (self.id, s, v) b"\
239            WEBPLATFORM.rs_refs[$0].style[UTF8ToString($1)] = UTF8ToString($2);\
240        \0" };
241    }
242
243    pub fn style_get_str(&self, s: &str) -> String {
244        let a = js! { (self.id, s) b"\
245            return allocate(intArrayFromString(WEBPLATFORM.rs_refs[$0].style[UTF8ToString($1)]), 'i8', ALLOC_STACK);\
246        \0" };
247        unsafe {
248            str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
249        }
250    }
251
252    pub fn prop_set_i32(&self, s: &str, v: i32) {
253        js! { (self.id, s, v) b"\
254            WEBPLATFORM.rs_refs[$0][UTF8ToString($1)] = $2;\
255        \0" };
256    }
257
258    pub fn prop_set_str(&self, s: &str, v: &str) {
259        js! { (self.id, s, v) b"\
260            WEBPLATFORM.rs_refs[$0][UTF8ToString($1)] = UTF8ToString($2);\
261        \0" };
262    }
263
264    pub fn prop_get_i32(&self, s: &str) -> i32 {
265        return js! { (self.id, s) b"\
266            return Number(WEBPLATFORM.rs_refs[$0][UTF8ToString($1)])\
267        \0" };
268    }
269
270    pub fn prop_get_str(&self, s: &str) -> String {
271        let a = js! { (self.id, s) b"\
272            var a = allocate(intArrayFromString(WEBPLATFORM.rs_refs[$0][UTF8ToString($1)]), 'i8', ALLOC_STACK); console.log(WEBPLATFORM.rs_refs[$0]); return a;\
273        \0" };
274        unsafe {
275            str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
276        }
277    }
278
279    pub fn append(&self, s: &HtmlNode) {
280        js! { (self.id, s.id) b"\
281            WEBPLATFORM.rs_refs[$0].appendChild(WEBPLATFORM.rs_refs[$1]);\
282        \0" };
283    }
284
285    pub fn html_append(&self, s: &str) {
286        js! { (self.id, s) b"\
287            WEBPLATFORM.rs_refs[$0].insertAdjacentHTML('beforeEnd', UTF8ToString($1));\
288        \0" };
289    }
290
291    pub fn html_prepend(&self, s: &str) {
292        js! { (self.id, s) b"\
293            WEBPLATFORM.rs_refs[$0].insertAdjacentHTML('afterBegin', UTF8ToString($1));\
294        \0" };
295    }
296
297    pub fn on<F: FnMut(Event) + 'a>(&self, s: &str, f: F) {
298        unsafe {
299            let b = Box::new(f);
300            let a = &*b as *const _;
301            js! { (self.id, s, a as *const libc::c_void,
302                rust_caller::<F> as *const libc::c_void,
303                self.doc as *const libc::c_void)
304                b"\
305                WEBPLATFORM.rs_refs[$0].addEventListener(UTF8ToString($1), function (e) {\
306                    Runtime.dynCall('viii', $3, [$2, $4, e.target ? WEBPLATFORM.rs_refs.push(e.target) - 1 : -1]);\
307                }, false);\
308            \0" };
309            (&*self.doc).refs.borrow_mut().push(b);
310        }
311    }
312
313    pub fn captured_on<F: FnMut(Event) + 'a>(&self, s: &str, f: F) {
314        unsafe {
315            let b = Box::new(f);
316            let a = &*b as *const _;
317            js! { (self.id, s, a as *const libc::c_void,
318                rust_caller::<F> as *const libc::c_void,
319                self.doc as *const libc::c_void)
320                b"\
321                WEBPLATFORM.rs_refs[$0].addEventListener(UTF8ToString($1), function (e) {\
322                    Runtime.dynCall('viii', $3, [$2, $4, e.target ? WEBPLATFORM.rs_refs.push(e.target) - 1 : -1]);\
323                }, true);\
324            \0" };
325            (&*self.doc).refs.borrow_mut().push(b);
326        }
327    }
328
329    pub fn remove_self(&self) {
330        js! { (self.id) b"\
331            var s = WEBPLATFORM.rs_refs[$0];\
332            s.parentNode.removeChild(s);\
333        \0" };
334    }
335}
336
337pub fn alert(s: &str) {
338    js! { (s) b"\
339        alert(UTF8ToString($0));\
340    \0" };
341}
342
343pub struct Document<'a> {
344    refs: Rc<RefCell<Vec<Box<FnMut(Event<'a>) + 'a>>>>,
345}
346
347impl<'a> Document<'a> {
348    pub fn element_create<'b>(&'b self, s: &str) -> Option<HtmlNode<'a>> {
349        let id = js! { (s) b"\
350            var value = document.createElement(UTF8ToString($0));\
351            if (!value) {\
352                return -1;\
353            }\
354            return WEBPLATFORM.rs_refs.push(value) - 1;\
355        \0" };
356
357        if id < 0 {
358            None
359        } else {
360            Some(HtmlNode {
361                id: id,
362                doc: &*self,
363            })
364        }
365    }
366
367    pub fn location_hash_get(&self) -> String {
368        let a = js! { b"\
369            return allocate(intArrayFromString(window.location.hash), 'i8', ALLOC_STACK);\
370        \0" };
371        unsafe {
372            str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
373        }
374    }
375
376    pub fn on<F: FnMut(Event) + 'a>(&self, s: &str, f: F) {
377        unsafe {
378            let b = Box::new(f);
379            let a = &*b as *const _;
380            js! { (0, s, a as *const libc::c_void,
381                rust_caller::<F> as *const libc::c_void,
382                &*self as *const _ as *const libc::c_void)
383                b"\
384                window.addEventListener(UTF8ToString($1), function (e) {\
385                    Runtime.dynCall('viii', $3, [$2, $4, e.target ? WEBPLATFORM.rs_refs.push(e.target) - 1 : -1]);\
386                }, false);\
387            \0" };
388            self.refs.borrow_mut().push(b);
389        }
390    }
391
392    pub fn element_query<'b>(&'b self, s: &str) -> Option<HtmlNode<'a>> {
393        let id = js! { (s) b"\
394            var value = document.querySelector(UTF8ToString($0));\
395            if (!value) {\
396                return -1;\
397            }\
398            return WEBPLATFORM.rs_refs.push(value) - 1;\
399        \0" };
400
401        if id < 0 {
402            None
403        } else {
404            Some(HtmlNode {
405                id: id,
406                doc: self,
407            })
408        }
409    }
410}
411
412pub struct LocalStorageInterface;
413
414pub struct LocalStorageIterator {
415    index: i32,
416}
417
418impl LocalStorageInterface {
419    pub fn len(&self) -> i32 {
420        js! { b"\
421            return window.localStorage.length;\
422        \0" }
423    }
424
425    pub fn clear(&self) {
426        js! { b"\
427            window.localStorage.clear();\
428        \0" };
429    }
430
431    pub fn remove(&self, s: &str) {
432        js! { (s) b"\
433            window.localStorage.removeItem(UTF8ToString($0));\
434        \0" };
435    }
436
437    pub fn set(&self, s: &str, v: &str) {
438        js! { (s, v) b"\
439            window.localStorage.setItem(UTF8ToString($0), UTF8ToString($1));\
440        \0" };
441    }
442
443    pub fn get(&self, name: &str) -> Option<String> {
444        let a = js! { (name) b"\
445            var str = window.localStorage.getItem(UTF8ToString($0));\
446            if (str == null) {\
447                return -1;\
448            }\
449            return allocate(intArrayFromString(str), 'i8', ALLOC_STACK);\
450        \0" };
451        if a == -1 {
452            None
453        } else {
454            Some(unsafe {
455                str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
456            })
457        }
458    }
459
460    pub fn key(&self, index: i32) -> String {
461        let a = js! { (index) b"\
462            var key = window.localStorage.key($0);\
463            return allocate(intArrayFromString(str), 'i8', ALLOC_STACK);\
464        \0" };
465        unsafe {
466            str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
467        }
468    }
469}
470
471impl IntoIterator for LocalStorageInterface {
472    type Item = String;
473    type IntoIter = LocalStorageIterator;
474
475    fn into_iter(self) -> LocalStorageIterator {
476        LocalStorageIterator { index: 0 }
477    }
478}
479
480impl Iterator for LocalStorageIterator {
481    type Item = String;
482    fn next(&mut self) -> Option<String> {
483        if self.index >= LocalStorage.len() {
484            None
485        } else {
486            LocalStorage.get(&LocalStorage.key(self.index))
487        }
488    }
489}
490
491#[allow(non_upper_case_globals)]
492pub const LocalStorage: LocalStorageInterface = LocalStorageInterface;
493
494pub fn init<'a>() -> Document<'a> {
495    js! { b"\
496        console.log('hi');\
497        window.WEBPLATFORM || (window.WEBPLATFORM = {\
498            rs_refs: [],\
499        });\
500    \0" };
501    Document {
502        refs: Rc::new(RefCell::new(Vec::new())),
503    }
504}
505
506extern fn leavemebe() {
507    unsafe {
508        emscripten_pause_main_loop();
509    }
510}
511
512pub fn spin() {
513    unsafe {
514        emscripten_set_main_loop(leavemebe, 0, 1);
515
516    }
517}
518
519#[no_mangle]
520pub extern "C" fn syscall(a: i32) -> i32 {
521    if a == 355 {
522        return 55
523    }
524    return -1
525}