diff options
Diffstat (limited to 'src/components/script/script_task.rs')
-rw-r--r-- | src/components/script/script_task.rs | 188 |
1 files changed, 109 insertions, 79 deletions
diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 8a56291b95f..d0c53384231 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -6,14 +6,16 @@ //! and layout tasks. use dom::bindings::codegen::RegisterBindings; -use dom::bindings::utils::{Reflectable, GlobalStaticData}; -use dom::document::AbstractDocument; +use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, DocumentCast, ElementCast}; +use dom::bindings::js::JS; +use dom::bindings::utils::{Reflectable, GlobalStaticData, with_gc_enabled}; +use dom::document::Document; use dom::element::Element; use dom::event::{Event_, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; use dom::event::Event; -use dom::eventtarget::AbstractEventTarget; +use dom::eventtarget::EventTarget; use dom::htmldocument::HTMLDocument; -use dom::node::AbstractNode; +use dom::node::{Node, NodeHelpers}; use dom::window::{TimerData, TimerHandle, Window}; use html::hubbub_html_parser::HtmlParserResult; use html::hubbub_html_parser::{HtmlDiscoveredStyle, HtmlDiscoveredIFrame, HtmlDiscoveredScript}; @@ -32,7 +34,7 @@ use geom::size::Size2D; use js::JSVAL_NULL; use js::global::debug_fns; use js::glue::RUST_JSVAL_TO_OBJECT; -use js::jsapi::{JSContext, JSObject}; +use js::jsapi::{JSContext, JSObject, JS_InhibitGC, JS_AllowGC}; use js::jsapi::{JS_CallFunctionValue, JS_GetContextPrivate}; use js::rust::{Compartment, Cx}; use js; @@ -52,6 +54,8 @@ use std::ptr; use std::task; use std::util::replace; +use extra::serialize::{Encoder, Encodable}; + /// Messages used to control the script task. pub enum ScriptMsg { /// Loads a new URL on the specified pipeline. @@ -86,6 +90,11 @@ pub struct NewLayoutInfo { #[deriving(Clone)] pub struct ScriptChan(SharedChan<ScriptMsg>); +impl<S: Encoder> Encodable<S> for ScriptChan { + fn encode(&self, _s: &mut S) { + } +} + impl ScriptChan { /// Creates a new script chan. pub fn new() -> (Port<ScriptMsg>, ScriptChan) { @@ -131,7 +140,13 @@ pub struct Page { resize_event: Option<Size2D<uint>>, /// Pending scroll to fragment event, if any - fragment_node: Option<AbstractNode> + fragment_node: Option<JS<Element>> +} + +impl<S: Encoder> Encodable<S> for Page { + fn encode(&self, s: &mut S) { + self.fragment_node.encode(s); + } } pub struct PageTree { @@ -223,23 +238,24 @@ impl Page { pub fn damage(&mut self, level: DocumentDamageLevel) { let root = match self.frame { None => return, - Some(ref frame) => frame.document.document().GetDocumentElement() + Some(ref frame) => frame.document.get().GetDocumentElement() }; match root { None => {}, Some(root) => { + let root: JS<Node> = NodeCast::from(&root); match self.damage { None => {} Some(ref mut damage) => { // FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor. - damage.root = root; + damage.root = root.to_trusted_node_address(); damage.level.add(level); return } } self.damage = Some(DocumentDamage { - root: root, + root: root.to_trusted_node_address(), level: level, }) } @@ -292,7 +308,7 @@ impl Page { let root = match self.frame { None => return, Some(ref frame) => { - frame.document.document().GetDocumentElement() + frame.document.get().GetDocumentElement() } }; @@ -314,8 +330,9 @@ impl Page { self.last_reflow_id += 1; // Send new document and relevant styles to layout. + let root: JS<Node> = NodeCast::from(&root); let reflow = ~Reflow { - document_root: root, + document_root: root.to_trusted_node_address(), url: self.url.get_ref().first().clone(), goal: goal, window_size: self.window_size, @@ -333,6 +350,8 @@ impl Page { } pub fn initialize_js_info(&mut self, js_context: @Cx, global: *JSObject) { + assert!(global.is_not_null()); + // Note that the order that these variables are initialized is _not_ arbitrary. Switching // them around can -- and likely will -- lead to things breaking. @@ -352,6 +371,8 @@ impl Page { unsafe { js_context.set_cx_private(page_ptr as *()); + + JS_InhibitGC(js_context.ptr); } self.js_info = Some(JSPageInfo { @@ -363,11 +384,12 @@ impl Page { } /// Information for one frame in the browsing context. +#[deriving(Encodable)] pub struct Frame { /// The document for this frame. - document: AbstractDocument, + document: JS<Document>, /// The window object for this frame. - window: @mut Window, + window: JS<Window>, } /// Encapsulation of the javascript information associated with each frame. @@ -406,7 +428,7 @@ pub struct ScriptTask { /// The JavaScript runtime. js_runtime: js::rust::rt, - mouse_over_targets:Option<~[AbstractNode]> + mouse_over_targets: Option<~[JS<Node>]> } /// Returns the relevant page from the associated JS Context. @@ -565,28 +587,27 @@ impl ScriptTask { fn handle_fire_timer_msg(&mut self, id: PipelineId, timer_data: ~TimerData) { let page = self.page_tree.find(id).expect("ScriptTask: received fire timer msg for a pipeline ID not associated with this script task. This is a bug.").page; - let window = page.frame.expect("ScriptTask: Expect a timeout to have a document").window; - if !window.active_timers.contains(&TimerHandle { handle: timer_data.handle, cancel_chan: None }) { + let mut window = page.frame.get_ref().window.clone(); + if !window.get().active_timers.contains(&TimerHandle { handle: timer_data.handle, cancel_chan: None }) { return; } - window.active_timers.remove(&TimerHandle { handle: timer_data.handle, cancel_chan: None }); - unsafe { - let this_value = if timer_data.args.len() > 0 { + window.get_mut().active_timers.remove(&TimerHandle { handle: timer_data.handle, cancel_chan: None }); + let this_value = if timer_data.args.len() > 0 { + unsafe { RUST_JSVAL_TO_OBJECT(timer_data.args[0]) - } else { - page.js_info.get_ref().js_compartment.global_obj.ptr - }; - - // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`. - let rval = JSVAL_NULL; - JS_CallFunctionValue(page.js_info.get_ref().js_context.ptr, - this_value, - timer_data.funval, - 0, - ptr::null(), - &rval); + } + } else { + page.js_info.get_ref().js_compartment.global_obj.ptr + }; - } + // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`. + let rval = JSVAL_NULL; + let cx = page.js_info.get_ref().js_context.ptr; + with_gc_enabled(cx, || { + unsafe { + JS_CallFunctionValue(cx, this_value, timer_data.funval, 0, ptr::null(), &rval); + } + }); } /// Handles a notification that reflow completed. @@ -697,9 +718,9 @@ impl ScriptTask { // Parse HTML. // // Note: We can parse the next document in parallel with any previous documents. - let document = HTMLDocument::new(window, Some(url.clone())); + let mut document = DocumentCast::from(&HTMLDocument::new(&window, Some(url.clone()))); let html_parsing_result = hubbub_html_parser::parse_html(cx.ptr, - document, + &mut document, url.clone(), self.resource_task.clone(), self.image_cache_task.clone(), @@ -711,8 +732,8 @@ impl ScriptTask { // Create the root frame. page.frame = Some(Frame { - document: document, - window: window, + document: document.clone(), + window: window.clone(), }); // Send style sheets over to layout. @@ -747,7 +768,7 @@ impl ScriptTask { } // Kick off the initial reflow of the page. - document.document().content_changed(); + document.get().content_changed(); let fragment = url.fragment.as_ref().map(|ref fragment| fragment.to_owned()); @@ -766,47 +787,50 @@ impl ScriptTask { // Evaluate every script in the document. for file in js_scripts.iter() { - let _ = cx.evaluate_script(compartment.global_obj, - file.data.clone(), - file.url.to_str(), - 1); + with_gc_enabled(cx.ptr, || { + cx.evaluate_script(compartment.global_obj, + file.data.clone(), + file.url.to_str(), + 1); + }); } // We have no concept of a document loader right now, so just dispatch the // "load" event as soon as we've finished executing all scripts parsed during // the initial load. - let event = Event::new(window); - event.mut_event().InitEvent(~"load", false, false); - let doctarget = AbstractEventTarget::from_document(document); - let wintarget = AbstractEventTarget::from_window(window); - window.eventtarget.dispatch_event_with_target(wintarget, Some(doctarget), event); + let mut event = Event::new(&window); + event.get_mut().InitEvent(~"load", false, false); + let doctarget = EventTargetCast::from(&document); + let mut wintarget: JS<EventTarget> = EventTargetCast::from(&window); + let winclone = wintarget.clone(); + wintarget.get_mut().dispatch_event_with_target(&winclone, Some(doctarget), &mut event); page.fragment_node = fragment.map_default(None, |fragid| self.find_fragment_node(page, fragid)); self.constellation_chan.send(LoadCompleteMsg(page.id, url)); } - fn find_fragment_node(&self, page: &mut Page, fragid: ~str) -> Option<AbstractNode> { - let document = page.frame.expect("root frame is None").document; - match document.document().GetElementById(fragid.to_owned()) { + fn find_fragment_node(&self, page: &mut Page, fragid: ~str) -> Option<JS<Element>> { + let document = page.frame.get_ref().document.clone(); + match document.get().GetElementById(fragid.to_owned()) { Some(node) => Some(node), None => { - let doc_node = AbstractNode::from_document(document); + let doc_node: JS<Node> = NodeCast::from(&document); let mut anchors = doc_node.traverse_preorder().filter(|node| node.is_anchor_element()); anchors.find(|node| { - node.with_imm_element(|elem| { - elem.get_attribute(Null, "name").map_default(false, |attr| { - attr.value_ref() == fragid - }) + let elem: JS<Element> = ElementCast::to(node); + elem.get().get_attribute(Null, "name").map_default(false, |attr| { + attr.get().value_ref() == fragid }) - }) + }).map(|node| ElementCast::to(&node)) } } } - fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &mut Page, node: AbstractNode) { + fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &mut Page, node: JS<Element>) { let (port, chan) = Chan::new(); - match page.query_layout(ContentBoxQuery(node, chan), port) { + let node: JS<Node> = NodeCast::from(&node); + match page.query_layout(ContentBoxQuery(node.to_trusted_node_address(), chan), port) { ContentBoxResponse(rect) => { let point = Point2D(to_frac_px(rect.origin.x).to_f32().unwrap(), to_frac_px(rect.origin.y).to_f32().unwrap()); @@ -852,18 +876,19 @@ impl ScriptTask { ClickEvent(_button, point) => { debug!("ClickEvent: clicked at {:?}", point); - let document = page.frame.expect("root frame is None").document; - let root = document.document().GetDocumentElement(); + let document = page.frame.get_ref().document.clone(); + let root = document.get().GetDocumentElement(); if root.is_none() { return; } let (port, chan) = Chan::new(); - match page.query_layout(HitTestQuery(root.unwrap(), point, chan), port) { + let root: JS<Node> = NodeCast::from(&root.unwrap()); + match page.query_layout(HitTestQuery(root.to_trusted_node_address(), point, chan), port) { Ok(HitTestResponse(node_address)) => { debug!("node address is {:?}", node_address); - let mut node = AbstractNode::from_untrusted_node_address(self.js_runtime - .ptr, - node_address); + let mut node: JS<Node> = + NodeHelpers::from_untrusted_node_address(self.js_runtime.ptr, + node_address); debug!("clicked on {:s}", node.debug_str()); // Traverse node generations until a node that is an element is @@ -876,11 +901,10 @@ impl ScriptTask { } if node.is_element() { - node.with_imm_element(|element| { - if "a" == element.tag_name { - self.load_url_from_element(page, element) - } - }) + let element: JS<Element> = ElementCast::to(&node); + if "a" == element.get().tag_name { + self.load_url_from_element(page, element.get()) + } } }, Err(()) => debug!("layout query error"), @@ -889,21 +913,22 @@ impl ScriptTask { MouseDownEvent(..) => {} MouseUpEvent(..) => {} MouseMoveEvent(point) => { - let document = page.frame.expect("root frame is None").document; - let root = document.document().GetDocumentElement(); + let document = page.frame.get_ref().document.clone(); + let root = document.get().GetDocumentElement(); if root.is_none() { return; } + let root: JS<Node> = NodeCast::from(&root.unwrap()); let (port, chan) = Chan::new(); - match page.query_layout(MouseOverQuery(root.unwrap(), point, chan), port) { + match page.query_layout(MouseOverQuery(root.to_trusted_node_address(), point, chan), port) { Ok(MouseOverResponse(node_address)) => { - let mut target_list:~[AbstractNode] = ~[]; + let mut target_list: ~[JS<Node>] = ~[]; let mut target_compare = false; match self.mouse_over_targets { Some(ref mut mouse_over_targets) => { - for node in mouse_over_targets.iter() { + for node in mouse_over_targets.mut_iter() { node.set_hover_state(false); } } @@ -911,9 +936,9 @@ impl ScriptTask { } for node_address in node_address.iter() { - let mut node = AbstractNode::from_untrusted_node_address(self.js_runtime - .ptr, - *node_address); + let mut node: JS<Node> = + NodeHelpers::from_untrusted_node_address( + self.js_runtime.ptr, *node_address); // Traverse node generations until a node that is an element is // found. while !node.is_element() { @@ -964,13 +989,13 @@ impl ScriptTask { // if the node's element is "a," load url from href attr let attr = element.get_attribute(Null, "href"); for href in attr.iter() { - debug!("ScriptTask: clicked on link to {:s}", href.Value()); - let click_frag = href.value_ref().starts_with("#"); + debug!("ScriptTask: clicked on link to {:s}", href.get().Value()); + let click_frag = href.get().value_ref().starts_with("#"); let base_url = page.url.as_ref().map(|&(ref url, _)| { url.clone() }); debug!("ScriptTask: current url is {:?}", base_url); - let url = parse_url(href.value_ref(), base_url); + let url = parse_url(href.get().value_ref(), base_url); if click_frag { match self.find_fragment_node(page, url.fragment.unwrap()) { @@ -995,6 +1020,11 @@ fn shut_down_layout(page: @mut Page) { // Destroy all nodes. Setting frame and js_info to None will trigger our // compartment to shutdown, run GC, etc. + + unsafe { + JS_AllowGC(page.js_info.get_ref().js_context.ptr); + } + page.frame = None; page.js_info = None; |