/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use josephine::CanAccess; use josephine::CanAlloc; use josephine::Compartment; use josephine::InCompartment; use josephine::JSContext; use josephine::JSInitializable; use josephine::JSManaged; use josephine::JSLifetime; use josephine::JSString; use josephine::SOMEWHERE; use fake_codegen::ConsoleInitializer; use fake_codegen::ConsoleMethods; use fake_codegen::DocumentInitializer; use fake_codegen::DocumentMethods; use fake_codegen::ElementInitializer; use fake_codegen::ElementMethods; use fake_codegen::WindowInitializer; use fake_codegen::WindowMethods; // ------------------------------------------------------------------- // TODO: the contents are pub so that codegen can get at it, this should be fixed! // https://github.com/asajeffrey/josephine/issues/27 #[derive(Copy, Clone, Debug, Eq, PartialEq, JSTraceable, JSLifetime, JSCompartmental)] pub struct Window<'a, C> (pub JSManaged<'a, C, NativeWindow<'a, C>>); #[derive(JSTraceable, JSLifetime, JSCompartmental)] pub struct NativeWindow<'a, C> { console: Console<'a, C>, document: Document<'a, C>, } impl<'a, C> JSInitializable for NativeWindow<'a, C> { type Init = WindowInitializer; } impl<'a> Window<'a, SOMEWHERE> { pub fn new(cx: &'a mut JSContext) -> Window<'a, SOMEWHERE> where S: CanAccess + CanAlloc, { let mut cx = cx.create_compartment(); let ref mut console_root = cx.new_root(); let ref mut document_root = cx.new_root(); let console = Console::new(&mut cx).in_root(console_root); let document = Document::new(&mut cx).in_root(document_root); let cx = cx.global_manage(NativeWindow { console: console, document: document, }); Window(cx.global().forget_compartment()) } } impl<'a, C> WindowMethods<'a, C> for Window<'a, C> where C: 'a { fn Console(self, cx: &'a JSContext) -> Console<'a, C> where S: CanAccess, C: Compartment, { self.0.borrow(cx).console } fn Document(self, cx: &'a JSContext) -> Document<'a, C> where S: CanAccess, C: Compartment, { self.0.borrow(cx).document } fn Window(self, _cx: &'a JSContext) -> Window<'a, C> { self } } // ------------------------------------------------------------------- #[derive(Copy, Clone, JSTraceable, JSLifetime, JSCompartmental)] pub struct Console<'a, C> (pub JSManaged<'a, C, NativeConsole>); #[derive(JSTraceable, JSLifetime, JSCompartmental)] pub struct NativeConsole(()); impl JSInitializable for NativeConsole { type Init = ConsoleInitializer; } impl<'a, C> Console<'a, C> { fn new(cx: &'a mut JSContext) -> Console<'a, C> where S: CanAlloc + InCompartment, C: Compartment, { Console(cx.manage(NativeConsole(()))) } } impl<'a, C> ConsoleMethods<'a, C> for Console<'a, C> { fn Log(self, _cx: &mut JSContext, arg: JSString<'a, C>) { debug!("Logging"); println!("{}", arg); } } // ------------------------------------------------------------------- #[derive(Copy, Clone, Debug, Eq, PartialEq, JSTraceable, JSLifetime, JSCompartmental)] pub struct Document<'a, C> (pub JSManaged<'a, C, NativeDocument<'a, C>>); #[derive(JSTraceable, JSLifetime, JSCompartmental)] pub struct NativeDocument<'a, C> { body: Element<'a, C>, } impl<'a, C> JSInitializable for NativeDocument<'a, C> { type Init = DocumentInitializer; } impl<'a, C:'a> Document<'a, C> { pub fn new(cx: &'a mut JSContext) -> Document<'a, C> where S: CanAlloc + InCompartment, C: Compartment, { let ref mut root1 = cx.new_root(); let ref mut root2 = cx.new_root(); let name = JSString::from_str(cx, "body").in_root(root1); let body = Element::new(cx, name).in_root(root2); Document(cx.manage(NativeDocument { body: body, })) } } impl<'a, C> DocumentMethods<'a, C> for Document<'a, C> where C: 'a { fn Body(self, cx: &'a mut JSContext) -> Element<'a, C> where S: CanAccess, C: Compartment, { self.0.borrow(cx).body } } // ------------------------------------------------------------------- #[derive(Copy, Clone, Debug, Eq, PartialEq, JSTraceable, JSLifetime, JSCompartmental)] pub struct Element<'a, C> (pub JSManaged<'a, C, NativeElement<'a, C>>); #[derive(JSTraceable, JSLifetime, JSCompartmental)] pub struct NativeElement<'a, C> { name: JSString<'a, C>, parent: Option>, children: Vec>, } impl<'a, C> JSInitializable for NativeElement<'a, C> { type Init = ElementInitializer; } impl<'a, C:'a> Element<'a, C> { pub fn new(cx: &'a mut JSContext, name: JSString) -> Element<'a, C> where S: CanAlloc + InCompartment, C: Compartment, { Element(cx.manage(NativeElement { name: name, parent: None, children: Vec::new(), })) } fn shallow_clone(self, cx: &'a mut JSContext) -> Element<'a, D> where S: CanAccess + CanAlloc + InCompartment, C: Compartment, D: Compartment, { let ref mut root1 = cx.new_root(); let ref mut root2 = cx.new_root(); let name = self.0.borrow(cx).name.in_root(root1).clone_in(cx).in_root(root2); Element::new(cx, name) } fn clone_children_from(self, cx: &mut JSContext, element: Element) where S: CanAccess + CanAlloc + InCompartment, C: Compartment, D: Compartment, { let mut i = 0; loop { let ref mut root = cx.new_root(); let child = match element.0.borrow(cx).children.get(i).cloned().in_root(root) { None => return, Some(child) => child, }; self.append_clone(cx, child); i = i+1; } } fn append_clone(self, cx: &mut JSContext, child: Element) where S: CanAccess + CanAlloc + InCompartment, C: Compartment, D: Compartment, { let ref mut root = cx.new_root(); let clone = child.shallow_clone(cx).in_root(root); clone.clone_children_from(cx, child); self.append_child(cx, clone); } fn append_child(self, cx: &'a mut JSContext, child: Element<'a, C>) where S: CanAccess + CanAlloc + InCompartment, C: Compartment, { self.0.borrow_mut(cx).children.push(child); child.0.borrow_mut(cx).parent = Some(self); } fn in_compartment(self, cx: &JSContext) -> Option> where S: InCompartment, { self.0.in_compartment(cx).map(Element) } } impl<'a, C> ElementMethods<'a, C> for Element<'a, C> where C: 'a { fn Append(self, cx: &'a mut JSContext, child: Element<'a, D>) where S: CanAccess + CanAlloc, C: Compartment, D: Compartment, { let ref mut cx = cx.enter_known_compartment(self.0); if let Some(child) = child.in_compartment(cx) { self.append_child(cx, child); } else { self.append_clone(cx, child); } } fn Parent(self, cx: &'a mut JSContext) -> Option> where S: CanAccess, C: Compartment, { self.0.borrow(cx).parent } fn TagName(self, cx: &'a mut JSContext) -> JSString<'a, C> where S: CanAccess, C: Compartment, { self.0.borrow(cx).name } }