diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index b7d677a58784..bd598ff974f4 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -54,11 +54,13 @@ use msg::constellation_msg::ConstellationChan; use util::smallvec::{SmallVec1, SmallVec}; use util::str::{LengthOrPercentageOrAuto}; use std::cell::{Cell, RefCell}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::collections::hash_state::HashState; use std::ffi::CString; use std::hash::{Hash, Hasher}; +use std::intrinsics::return_address; use std::old_io::timer::Timer; +use std::ops::{Deref, DerefMut}; use std::rc::Rc; use std::sync::mpsc::{Receiver, Sender}; use string_cache::{Atom, Namespace}; @@ -262,3 +264,134 @@ impl JSTraceable for Box { // Do nothing } } + +/// Holds a set of vectors that need to be rooted +pub struct RootedCollectionSet { + set: Vec>> +} + +/// TLV Holds a set of vectors that need to be rooted +thread_local!(pub static ROOTED_COLLECTIONS: Rc> = + Rc::new(RefCell::new(RootedCollectionSet::new()))); + +enum CollectionType { + DOMObjects, + JSVals, + JSObjects, +} + + +impl RootedCollectionSet { + fn new() -> RootedCollectionSet { + RootedCollectionSet { + set: vec!(HashSet::new(), HashSet::new(), HashSet::new()) + } + } + + fn remove(collection: &RootedVec) { + ROOTED_COLLECTIONS.with(|ref collections| { + let type_ = VecRootableType::tag(None::); + let mut collections = collections.borrow_mut(); + assert!(collections.set[type_ as uint].remove(&(collection as *const _ as *const _))); + }); + } + + fn add(collection: &RootedVec) { + ROOTED_COLLECTIONS.with(|ref collections| { + let type_ = VecRootableType::tag(None::); + let mut collections = collections.borrow_mut(); + collections.set[type_ as uint].insert(collection as *const _ as *const _); + }) + } + + unsafe fn trace(&self, tracer: *mut JSTracer) { + fn trace_collection_type(tracer: *mut JSTracer, + collections: &HashSet<*const RootedVec<()>>) { + for collection in collections.iter() { + let collection = &(*collection as *const RootedVec); + unsafe { + let _ = (**collection).trace(tracer); + } + } + } + + let dom_collections = &self.set[CollectionType::DOMObjects as uint] as *const _ as *const HashSet<*const RootedVec<*const Reflector>>; + for dom_collection in (*dom_collections).iter() { + for reflector in (**dom_collection).iter() { + trace_reflector(tracer, "", &**reflector); + } + } + + trace_collection_type::(tracer, &self.set[CollectionType::JSVals as uint]); + trace_collection_type::<*mut JSObject>(tracer, &self.set[CollectionType::JSObjects as uint]); + } +} + + +/// Trait implemented by all types that can be used with RootedVec +trait VecRootableType { + /// Return the type tag used to determine how to trace RootedVec + fn tag(_a: Option) -> CollectionType; +} + +impl VecRootableType for JS { + fn tag(_a: Option>) -> CollectionType { CollectionType::DOMObjects } +} + +impl VecRootableType for JSVal { + fn tag(_a: Option) -> CollectionType { CollectionType::JSVals } +} + +impl VecRootableType for *mut JSObject { + fn tag(_a: Option<*mut JSObject>) -> CollectionType { CollectionType::JSObjects } +} + +/// A vector of items that are rooted for the lifetime +/// of this struct +#[allow(unrooted_must_root)] +#[jstraceable] +pub struct RootedVec { + v: Vec +} + + +impl RootedVec { + /// Create a vector of items of type T that is rooted for + /// the lifetime of this struct + pub fn new() -> RootedVec { + unsafe { + RootedCollectionSet::add::(&*(return_address() as *const _)); + } + RootedVec:: { v: vec!() } + } + +} + +#[unsafe_destructor] +impl Drop for RootedVec { + fn drop(&mut self) { + RootedCollectionSet::remove(self); + } +} + +impl Deref for RootedVec { + type Target = Vec; + fn deref(&self) -> &Vec { + &self.v + } +} + +impl DerefMut for RootedVec { + fn deref_mut(&mut self) -> &mut Vec { + &mut self.v + } +} + + +/// SM Callback that traces the rooted collections +pub unsafe extern fn trace_collections(tracer: *mut JSTracer, _data: *mut libc::c_void) { + ROOTED_COLLECTIONS.with(|ref collections| { + let collections = collections.borrow(); + collections.trace(tracer); + }); +} diff --git a/components/script/dom/domrectlist.rs b/components/script/dom/domrectlist.rs index 8ef7b8fce527..ec70329e0929 100644 --- a/components/script/dom/domrectlist.rs +++ b/components/script/dom/domrectlist.rs @@ -6,6 +6,7 @@ use dom::bindings::codegen::Bindings::DOMRectListBinding; use dom::bindings::codegen::Bindings::DOMRectListBinding::DOMRectListMethods; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, JSRef, Temporary}; +use dom::bindings::trace::RootedVec; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::domrect::DOMRect; use dom::window::Window; @@ -19,17 +20,16 @@ pub struct DOMRectList { impl DOMRectList { fn new_inherited(window: JSRef, - rects: Vec>) -> DOMRectList { - let rects = rects.iter().map(|rect| JS::from_rooted(*rect)).collect(); + rects: &RootedVec>) -> DOMRectList { DOMRectList { reflector_: Reflector::new(), - rects: rects, + rects: (**rects).clone(), window: JS::from_rooted(window), } } pub fn new(window: JSRef, - rects: Vec>) -> Temporary { + rects: &RootedVec>) -> Temporary { reflect_dom_object(box DOMRectList::new_inherited(window, rects), GlobalRef::Window(window), DOMRectListBinding::Wrap) } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 03e7247eb7a6..261f3758d540 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -25,7 +25,8 @@ use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{NamespaceError, InvalidCharacter, Syntax}; use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable}; -use dom::bindings::js::{OptionalRootable, Root}; +use dom::bindings::js::OptionalRootable; +use dom::bindings::trace::RootedVec; use dom::bindings::utils::xml_name_type; use dom::bindings::utils::XMLName::{QName, Name, InvalidXMLName}; use dom::create::create_element; @@ -1061,17 +1062,18 @@ impl<'a> ElementMethods for JSRef<'a, Element> { fn GetClientRects(self) -> Temporary { let win = window_from_node(self).root(); let node: JSRef = NodeCast::from_ref(self); - let rects = node.get_content_boxes(); - let rects: Vec> = rects.iter().map(|r| { - DOMRect::new( - win.r(), - r.origin.y, - r.origin.y + r.size.height, - r.origin.x, - r.origin.x + r.size.width).root() - }).collect(); - - DOMRectList::new(win.r(), rects.iter().map(|rect| rect.r()).collect()) + let raw_rects = node.get_content_boxes(); + let mut rects = RootedVec::new(); + for rect in raw_rects.iter() { + let rect = DOMRect::new(win.r(), + rect.origin.y, + rect.origin.y + rect.size.height, + rect.origin.x, + rect.origin.x + rect.size.width); + rects.push(JS::from_rooted(rect)); + } + + DOMRectList::new(win.r(), &rects) } // http://dev.w3.org/csswg/cssom-view/#dom-element-getboundingclientrect diff --git a/components/script/dom/eventdispatcher.rs b/components/script/dom/eventdispatcher.rs index 3518ff20eb61..029691f702c9 100644 --- a/components/script/dom/eventdispatcher.rs +++ b/components/script/dom/eventdispatcher.rs @@ -4,8 +4,9 @@ use dom::bindings::callback::ExceptionHandling::Report; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; -use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, NodeDerived}; -use dom::bindings::js::{JS, JSRef, OptionalRootable, Root}; +use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast}; +use dom::bindings::js::{JS, JSRef, OptionalRootable}; +use dom::bindings::trace::RootedVec; use dom::eventtarget::{EventTarget, ListenerPhase}; use dom::event::{Event, EventPhase}; use dom::node::{Node, NodeHelpers}; @@ -27,15 +28,13 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, let type_ = event.Type(); //TODO: no chain if not participating in a tree - let mut chain: Vec> = if target.is_node() { - let target_node: JSRef = NodeCast::to_ref(target).unwrap(); - target_node.ancestors().map(|ancestor| { + let mut chain: RootedVec> = RootedVec::new(); + if let Some(target_node) = NodeCast::to_ref(target) { + for ancestor in target_node.ancestors() { let ancestor_target: JSRef = EventTargetCast::from_ref(ancestor); - JS::from_rooted(ancestor_target).root() - }).collect() - } else { - vec!() - }; + chain.push(JS::from_rooted(ancestor_target)) + } + } event.set_phase(EventPhase::Capturing); @@ -43,6 +42,7 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, /* capturing */ for cur_target in chain.as_slice().iter().rev() { + let cur_target = cur_target.root(); let stopped = match cur_target.r().get_listeners_for(type_.as_slice(), ListenerPhase::Capturing) { Some(listeners) => { event.set_current_target(cur_target.r()); @@ -88,6 +88,7 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, event.set_phase(EventPhase::Bubbling); for cur_target in chain.iter() { + let cur_target = cur_target.root(); let stopped = match cur_target.r().get_listeners_for(type_.as_slice(), ListenerPhase::Bubbling) { Some(listeners) => { event.set_current_target(cur_target.r()); diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 51361bd1436e..79027b22d1e2 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -16,7 +16,7 @@ use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, RootedReference} use dom::bindings::js::{RootCollection, RootCollectionPtr}; use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference}; use dom::bindings::structuredclone::StructuredCloneData; -use dom::bindings::trace::JSTraceable; +use dom::bindings::trace::{JSTraceable, trace_collections}; use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap}; use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressHandler, DocumentProgressTask, DocumentSource}; use dom::element::{Element, AttributeHandlers}; @@ -63,7 +63,7 @@ use geom::point::Point2D; use hyper::header::{LastModified, Headers}; use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC}; use js::jsapi::{JSContext, JSRuntime, JSObject}; -use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES}; +use js::jsapi::{JS_SetExtraGCRootsTracer, JS_SetGCParameter, JSGC_MAX_BYTES}; use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END}; use js::rust::{Cx, RtUtils}; use js; @@ -75,6 +75,7 @@ use std::borrow::ToOwned; use std::cell::Cell; use std::mem::replace; use std::num::ToPrimitive; +use std::ptr; use std::rc::Rc; use std::result::Result; use std::sync::mpsc::{channel, Sender, Receiver, Select}; @@ -386,6 +387,10 @@ impl ScriptTask { !ptr.is_null() }); + + unsafe { + JS_SetExtraGCRootsTracer((*js_runtime).ptr, Some(trace_collections), ptr::null_mut()); + } // Unconstrain the runtime's threshold on nominal heap size, to avoid // triggering GC too often if operating continuously near an arbitrary // finite threshold. This leaves the maximum-JS_malloc-bytes threshold