diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/bindings/js.rs | 27 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 6 | ||||
-rw-r--r-- | components/script/dom/eventtarget.rs | 5 | ||||
-rw-r--r-- | components/script/dom/htmltabledatacellelement.rs | 10 | ||||
-rw-r--r-- | components/script/dom/htmltableelement.rs | 10 | ||||
-rw-r--r-- | components/script/dom/htmltablerowelement.rs | 8 | ||||
-rw-r--r-- | components/script/dom/node.rs | 92 | ||||
-rw-r--r-- | components/script/dom/window.rs | 8 | ||||
-rw-r--r-- | components/script/layout_interface.rs | 15 | ||||
-rw-r--r-- | components/script/lib.rs | 2 | ||||
-rw-r--r-- | components/script/page.rs | 55 | ||||
-rw-r--r-- | components/script/script_task.rs | 8 |
12 files changed, 151 insertions, 95 deletions
diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs index 14f717f7455..2cd327397ad 100644 --- a/components/script/dom/bindings/js.rs +++ b/components/script/dom/bindings/js.rs @@ -53,7 +53,8 @@ use js::jsapi::JSObject; use layout_interface::TrustedNodeAddress; use script_task::StackRoots; -use std::cell::{Cell, RefCell}; +use servo_util::smallvec::{SmallVec, SmallVec16}; +use std::cell::{Cell, UnsafeCell}; use std::default::Default; use std::kinds::marker::ContravariantLifetime; use std::mem; @@ -419,14 +420,14 @@ impl<T: Assignable<U>, U: Reflectable> TemporaryPushable<T> for Vec<JS<U>> { /// An opaque, LIFO rooting mechanism. pub struct RootCollection { - roots: RefCell<Vec<*mut JSObject>>, + roots: UnsafeCell<SmallVec16<*mut JSObject>>, } impl RootCollection { /// Create an empty collection of roots pub fn new() -> RootCollection { RootCollection { - roots: RefCell::new(vec!()), + roots: UnsafeCell::new(SmallVec16::new()), } } @@ -438,17 +439,23 @@ impl RootCollection { /// Track a stack-based root to ensure LIFO root ordering fn root<'a, 'b, T: Reflectable>(&self, untracked: &Root<'a, 'b, T>) { - let mut roots = self.roots.borrow_mut(); - roots.push(untracked.js_ptr); - debug!(" rooting {:?}", untracked.js_ptr); + unsafe { + let roots = self.roots.get(); + (*roots).push(untracked.js_ptr); + debug!(" rooting {:?}", untracked.js_ptr); + } } /// Stop tracking a stack-based root, asserting if LIFO root ordering has been violated fn unroot<'a, 'b, T: Reflectable>(&self, rooted: &Root<'a, 'b, T>) { - let mut roots = self.roots.borrow_mut(); - debug!("unrooting {:?} (expecting {:?}", roots.last().unwrap(), rooted.js_ptr); - assert!(*roots.last().unwrap() == rooted.js_ptr); - roots.pop().unwrap(); + unsafe { + let roots = self.roots.get(); + debug!("unrooting {:?} (expecting {:?}", + (*roots).as_slice().last().unwrap(), + rooted.js_ptr); + assert!(*(*roots).as_slice().last().unwrap() == rooted.js_ptr); + (*roots).pop().unwrap(); + } } } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 930fd59edbe..c4f59491c57 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -39,7 +39,7 @@ use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData}; use net::image_cache_task::ImageCacheTask; use script_traits::ScriptControlChan; use std::collections::hashmap::HashMap; -use collections::hash::Hash; +use collections::hash::{Hash, Hasher}; use style::PropertyDeclarationBlock; use std::comm::{Receiver, Sender}; use string_cache::{Atom, Namespace}; @@ -170,7 +170,9 @@ impl<T: JSTraceable> JSTraceable for Option<T> { } } -impl<K: Eq+Hash+JSTraceable, V: JSTraceable> JSTraceable for HashMap<K, V> { +impl<K,V,S,H> JSTraceable for HashMap<K, V, H> where K: Eq + Hash<S> + JSTraceable, + V: JSTraceable, + H: Hasher<S> { #[inline] fn trace(&self, trc: *mut JSTracer) { for e in self.iter() { diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index 233047691ea..0ced9c9c41e 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -18,6 +18,7 @@ use dom::xmlhttprequest::XMLHttpRequestId; use dom::virtualmethods::VirtualMethods; use js::jsapi::{JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject}; use js::jsapi::{JSContext, JSObject}; +use servo_util::fnv::FnvHasher; use servo_util::str::DOMString; use libc::{c_char, size_t}; use std::ptr; @@ -69,7 +70,7 @@ pub struct EventListenerEntry { pub struct EventTarget { type_id: EventTargetTypeId, reflector_: Reflector, - handlers: DOMRefCell<HashMap<DOMString, Vec<EventListenerEntry>>>, + handlers: DOMRefCell<HashMap<DOMString, Vec<EventListenerEntry>, FnvHasher>>, } impl EventTarget { @@ -77,7 +78,7 @@ impl EventTarget { EventTarget { type_id: type_id, reflector_: Reflector::new(), - handlers: DOMRefCell::new(HashMap::new()), + handlers: DOMRefCell::new(HashMap::with_hasher(FnvHasher)), } } diff --git a/components/script/dom/htmltabledatacellelement.rs b/components/script/dom/htmltabledatacellelement.rs index 535f2e4ffd5..a2a73bd86f8 100644 --- a/components/script/dom/htmltabledatacellelement.rs +++ b/components/script/dom/htmltabledatacellelement.rs @@ -32,9 +32,13 @@ impl HTMLTableDataCellElement { } #[allow(unrooted_must_root)] - pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableDataCellElement> { - let element = HTMLTableDataCellElement::new_inherited(localName, prefix, document); - Node::reflect_node(box element, document, HTMLTableDataCellElementBinding::Wrap) + pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> Temporary<HTMLTableDataCellElement> { + Node::reflect_node(box HTMLTableDataCellElement::new_inherited(localName, + prefix, + document), + document, + HTMLTableDataCellElementBinding::Wrap) } } diff --git a/components/script/dom/htmltableelement.rs b/components/script/dom/htmltableelement.rs index b39c8ebb3e3..7dddf98bdc4 100644 --- a/components/script/dom/htmltableelement.rs +++ b/components/script/dom/htmltableelement.rs @@ -9,7 +9,6 @@ use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::document::Document; -use dom::element::HTMLTableCaptionElementTypeId; use dom::element::HTMLTableElementTypeId; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; @@ -52,11 +51,10 @@ impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> { // http://www.whatwg.org/html/#dom-table-caption fn GetCaption(self) -> Option<Temporary<HTMLTableCaptionElement>> { let node: JSRef<Node> = NodeCast::from_ref(self); - node.children().find(|child| { - child.type_id() == ElementNodeTypeId(HTMLTableCaptionElementTypeId) - }).map(|node| { - Temporary::from_rooted(HTMLTableCaptionElementCast::to_ref(node).unwrap()) - }) + node.children() + .filter_map::<JSRef<HTMLTableCaptionElement>>(HTMLTableCaptionElementCast::to_ref) + .next() + .map(Temporary::from_rooted) } // http://www.whatwg.org/html/#dom-table-caption diff --git a/components/script/dom/htmltablerowelement.rs b/components/script/dom/htmltablerowelement.rs index 64bd1e66c11..3edf4a0e64f 100644 --- a/components/script/dom/htmltablerowelement.rs +++ b/components/script/dom/htmltablerowelement.rs @@ -32,9 +32,11 @@ impl HTMLTableRowElement { } #[allow(unrooted_must_root)] - pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableRowElement> { - let element = HTMLTableRowElement::new_inherited(localName, prefix, document); - Node::reflect_node(box element, document, HTMLTableRowElementBinding::Wrap) + pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> Temporary<HTMLTableRowElement> { + Node::reflect_node(box HTMLTableRowElement::new_inherited(localName, prefix, document), + document, + HTMLTableRowElementBinding::Wrap) } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index af1afccc3b1..938404bdc37 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -45,8 +45,7 @@ use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; use dom::window::Window; use geom::rect::Rect; -use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC, - LayoutChan, ReapLayoutDataMsg}; +use layout_interface::{LayoutChan, ReapLayoutDataMsg}; use devtools_traits::NodeInfo; use script_traits::UntrustedNodeAddress; use servo_util::geometry::Au; @@ -621,6 +620,10 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { // 1. Dirty self. self.set_has_changed(true); + if self.get_is_dirty() { + return + } + // 2. Dirty descendants. fn dirty_subtree(node: JSRef<Node>) { // Stop if this subtree is already dirty. @@ -689,20 +692,11 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } fn get_bounding_content_box(self) -> Rect<Au> { - let window = window_from_node(self).root(); - let page = window.page(); - let addr = self.to_trusted_node_address(); - - let ContentBoxResponse(rect) = page.layout().content_box(addr); - rect + window_from_node(self).root().page().content_box_query(self.to_trusted_node_address()) } fn get_content_boxes(self) -> Vec<Rect<Au>> { - let window = window_from_node(self).root(); - let page = window.page(); - let addr = self.to_trusted_node_address(); - let ContentBoxesResponse(rects) = page.layout().content_boxes(addr); - rects + window_from_node(self).root().page().content_boxes_query(self.to_trusted_node_address()) } // http://dom.spec.whatwg.org/#dom-parentnode-queryselector @@ -1151,7 +1145,7 @@ impl Node { layout_data: LayoutDataRef::new(), - unique_id: DOMRefCell::new("".to_string()), + unique_id: DOMRefCell::new(String::new()), } } @@ -1337,29 +1331,8 @@ impl Node { parent: JSRef<Node>, child: Option<JSRef<Node>>, suppress_observers: SuppressObserver) { - // XXX assert owner_doc - // Step 1-3: ranges. - // Step 4. - let mut nodes = match node.type_id() { - DocumentFragmentNodeTypeId => node.children().collect(), - _ => vec!(node.clone()), - }; - - // Step 5: DocumentFragment, mutation records. - // Step 6: DocumentFragment. - match node.type_id() { - DocumentFragmentNodeTypeId => { - for c in node.children() { - Node::remove(c, node, Suppressed); - } - }, - _ => (), - } - - // Step 7: mutation records. - // Step 8. - for node in nodes.iter_mut() { - parent.add_child(*node, child); + fn do_insert(node: JSRef<Node>, parent: JSRef<Node>, child: Option<JSRef<Node>>) { + parent.add_child(node, child); let is_in_doc = parent.is_in_doc(); for kid in node.traverse_preorder() { let mut flags = kid.flags.get(); @@ -1372,14 +1345,47 @@ impl Node { } } - // Step 9. - match suppress_observers { - Unsuppressed => { - for node in nodes.iter() { - node.node_inserted(); + fn fire_observer_if_necessary(node: JSRef<Node>, suppress_observers: SuppressObserver) { + match suppress_observers { + Unsuppressed => node.node_inserted(), + Suppressed => () + } + } + + // XXX assert owner_doc + // Step 1-3: ranges. + + match node.type_id() { + DocumentFragmentNodeTypeId => { + // Step 4. + // Step 5: DocumentFragment, mutation records. + // Step 6: DocumentFragment. + let mut kids = Vec::new(); + for kid in node.children() { + kids.push(kid.clone()); + Node::remove(kid, node, Suppressed); } + + // Step 7: mutation records. + // Step 8. + for kid in kids.iter() { + do_insert((*kid).clone(), parent, child); + } + + for kid in kids.into_iter() { + fire_observer_if_necessary(kid, suppress_observers); + } + } + _ => { + // Step 4. + // Step 5: DocumentFragment, mutation records. + // Step 6: DocumentFragment. + // Step 7: mutation records. + // Step 8. + do_insert(node, parent, child); + // Step 9. + fire_observer_if_necessary(node, suppress_observers); } - Suppressed => () } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 71c8890ab6f..c9eeedc2a2a 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -19,7 +19,7 @@ use dom::location::Location; use dom::navigator::Navigator; use dom::performance::Performance; use dom::screen::Screen; -use layout_interface::ReflowGoal; +use layout_interface::NoQuery; use page::Page; use script_task::{ExitWindowMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg}; use script_task::FromWindow; @@ -312,7 +312,7 @@ impl Reflectable for Window { pub trait WindowHelpers { fn reflow(self); - fn flush_layout(self, goal: ReflowGoal); + fn flush_layout(self); fn wait_until_safe_to_modify_dom(self); fn init_browser_context(self, doc: JSRef<Document>); fn load_url(self, href: DOMString); @@ -350,8 +350,8 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { self.page().damage(); } - fn flush_layout(self, goal: ReflowGoal) { - self.page().flush_layout(goal); + fn flush_layout(self) { + self.page().flush_layout(NoQuery); } fn wait_until_safe_to_modify_dom(self) { diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 735893f497a..2cf64f961fb 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -60,9 +60,9 @@ pub enum Msg { // 3) and really needs to be fast. pub trait LayoutRPC { /// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call. - fn content_box(&self, node: TrustedNodeAddress) -> ContentBoxResponse; + fn content_box(&self) -> ContentBoxResponse; /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. - fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse; + fn content_boxes(&self) -> ContentBoxesResponse; /// Requests the node containing the point of interest fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>; fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>; @@ -82,6 +82,13 @@ pub enum ReflowGoal { ReflowForScriptQuery, } +/// Any query to perform with this reflow. +pub enum ReflowQueryType { + NoQuery, + ContentBoxQuery(TrustedNodeAddress), + ContentBoxesQuery(TrustedNodeAddress), +} + /// Information needed for a reflow. pub struct Reflow { /// The document node. @@ -99,7 +106,9 @@ pub struct Reflow { /// The channel that we send a notification to. pub script_join_chan: Sender<()>, /// Unique identifier - pub id: uint + pub id: uint, + /// The type of query if any to perform during this reflow. + pub query_type: ReflowQueryType, } /// Encapsulates a channel to the layout task. diff --git a/components/script/lib.rs b/components/script/lib.rs index 1624a8c2653..cbacf516a3f 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -5,7 +5,7 @@ #![comment = "The Servo Parallel Browser Project"] #![license = "MPL"] -#![feature(globs, macro_rules, struct_variant, phase, unsafe_destructor)] +#![feature(default_type_params, globs, macro_rules, struct_variant, phase, unsafe_destructor)] #![deny(unused_imports, unused_variable)] #![allow(non_snake_case)] diff --git a/components/script/page.rs b/components/script/page.rs index 9fc7d74d2e9..01b396126ad 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -11,19 +11,22 @@ use dom::document::{Document, DocumentHelpers}; use dom::element::Element; use dom::node::{Node, NodeHelpers}; use dom::window::Window; -use layout_interface::{ReflowForDisplay}; -use layout_interface::{HitTestResponse, MouseOverResponse}; -use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC}; -use layout_interface::{Reflow, ReflowGoal, ReflowMsg}; +use layout_interface::{ + ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery, ContentBoxesResponse, + GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, NoQuery, + Reflow, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg, + ReflowQueryType, TrustedNodeAddress +}; use script_traits::{UntrustedNodeAddress, ScriptControlChan}; -use geom::point::Point2D; +use geom::{Point2D, Rect}; use js::rust::Cx; use servo_msg::compositor_msg::PerformingLayout; use servo_msg::compositor_msg::ScriptListener; use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData}; use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_net::resource_task::ResourceTask; +use servo_util::geometry::Au; use servo_util::str::DOMString; use servo_util::smallvec::{SmallVec1, SmallVec}; use std::cell::Cell; @@ -164,26 +167,48 @@ impl Page { } } - pub fn flush_layout(&self, goal: ReflowGoal) { - if self.damaged.get() { + pub fn flush_layout(&self, query: ReflowQueryType) { + // If we are damaged, we need to force a full reflow, so that queries interact with + // an accurate flow tree. + let (reflow_goal, force_reflow) = if self.damaged.get() { + (ReflowForDisplay, true) + } else { + match query { + ContentBoxQuery(_) | ContentBoxesQuery(_) => (ReflowForScriptQuery, true), + NoQuery => (ReflowForDisplay, false), + } + }; + + if force_reflow { let frame = self.frame(); let window = frame.as_ref().unwrap().window.root(); - self.reflow(goal, window.control_chan().clone(), window.compositor()); + self.reflow(reflow_goal, window.control_chan().clone(), window.compositor(), query); } else { self.avoided_reflows.set(self.avoided_reflows.get() + 1); } } - pub fn layout(&self) -> &LayoutRPC { - // FIXME This should probably be ReflowForQuery, not Display. All queries currently - // currently rely on the display list, which means we can't destroy it by - // doing a query reflow. - self.flush_layout(ReflowForDisplay); + pub fn layout(&self) -> &LayoutRPC { + self.flush_layout(NoQuery); self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? let layout_rpc: &LayoutRPC = &*self.layout_rpc; layout_rpc } + pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> { + self.flush_layout(ContentBoxQuery(content_box_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxResponse(rect) = self.layout_rpc.content_box(); + rect + } + + pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> { + self.flush_layout(ContentBoxesQuery(content_boxes_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); + rects + } + // must handle root case separately pub fn remove(&self, id: PipelineId) -> Option<Rc<Page>> { let remove_idx = { @@ -303,7 +328,8 @@ impl Page { pub fn reflow(&self, goal: ReflowGoal, script_chan: ScriptControlChan, - compositor: &ScriptListener) { + compositor: &ScriptListener, + query_type: ReflowQueryType) { let root = match *self.frame() { None => return, @@ -349,6 +375,7 @@ impl Page { script_chan: script_chan, script_join_chan: join_chan, id: last_reflow_id.get(), + query_type: query_type, }; let LayoutChan(ref chan) = self.layout_chan; diff --git a/components/script/script_task.rs b/components/script/script_task.rs index deb6aaded80..7ef0d095545 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -29,7 +29,7 @@ use dom::window::{Window, WindowHelpers}; use dom::worker::{Worker, TrustedWorkerAddress}; use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress}; use parse::html::{InputString, InputUrl, parse_html}; -use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowForDisplay}; +use layout_interface::{ScriptLayoutChan, LayoutChan, NoQuery, ReflowForDisplay}; use layout_interface; use page::{Page, IterablePage, Frame}; use timers::TimerId; @@ -815,7 +815,7 @@ impl ScriptTask { let document_as_node = NodeCast::from_ref(document_js_ref); document.content_changed(document_as_node); } - window.flush_layout(ReflowForDisplay); + window.flush_layout(); { // No more reflow required @@ -870,7 +870,7 @@ impl ScriptTask { } page.damage(); - page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor); + page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor, NoQuery); } /// This is the main entry point for receiving and dispatching DOM events. @@ -969,7 +969,7 @@ impl ScriptTask { let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(node); let _ = eventtarget.dispatch_event_with_target(None, *event); - window.flush_layout(ReflowForDisplay); + window.flush_layout(); } None => {} } |