diff options
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/document.rs | 55 | ||||
-rw-r--r-- | components/script/dom/domrectlist.rs | 11 | ||||
-rw-r--r-- | components/script/dom/element.rs | 17 | ||||
-rw-r--r-- | components/script/dom/htmlbodyelement.rs | 28 | ||||
-rw-r--r-- | components/script/dom/node.rs | 7 | ||||
-rw-r--r-- | components/script/dom/nodelist.rs | 7 | ||||
-rw-r--r-- | components/script/dom/servohtmlparser.rs | 3 | ||||
-rw-r--r-- | components/script/dom/window.rs | 2 |
8 files changed, 88 insertions, 42 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 13f035f3e3f..3d615df43de 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -148,6 +148,8 @@ pub struct Document { loader: DOMRefCell<DocumentLoader>, /// The current active HTML parser, to allow resuming after interruptions. current_parser: MutNullableHeap<JS<ServoHTMLParser>>, + /// When we should kick off a reflow. This happens during parsing. + reflow_timeout: Cell<Option<u64>>, } impl DocumentDerived for EventTarget { @@ -226,6 +228,9 @@ pub trait DocumentHelpers<'a> { fn set_encoding_name(self, name: DOMString); fn content_changed(self, node: JSRef<Node>, damage: NodeDamage); fn content_and_heritage_changed(self, node: JSRef<Node>, damage: NodeDamage); + fn reflow_if_reflow_timer_expired(self); + fn set_reflow_timeout(self, timeout: u64); + fn disarm_reflow_timeout(self); fn unregister_named_element(self, to_unregister: JSRef<Element>, id: Atom); fn register_named_element(self, element: JSRef<Element>, id: Atom); fn load_anchor_href(self, href: DOMString); @@ -343,11 +348,42 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { } fn content_and_heritage_changed(self, node: JSRef<Node>, damage: NodeDamage) { - debug!("content_and_heritage_changed on {}", node.debug_str()); node.force_dirty_ancestors(damage); node.dirty(damage); } + /// Reflows and disarms the timer if the reflow timer has expired. + fn reflow_if_reflow_timer_expired(self) { + if let Some(reflow_timeout) = self.reflow_timeout.get() { + if time::precise_time_ns() < reflow_timeout { + return + } + + self.reflow_timeout.set(None); + let window = self.window.root(); + window.r().reflow(ReflowGoal::ForDisplay, + ReflowQueryType::NoQuery, + ReflowReason::RefreshTick); + } + } + + /// Schedules a reflow to be kicked off at the given `timeout` (in `time::precise_time_ns()` + /// units). This reflow happens even if the event loop is busy. This is used to display initial + /// page content during parsing. + fn set_reflow_timeout(self, timeout: u64) { + if let Some(existing_timeout) = self.reflow_timeout.get() { + if existing_timeout < timeout { + return + } + } + self.reflow_timeout.set(Some(timeout)) + } + + /// Disables any pending reflow timeouts. + fn disarm_reflow_timeout(self) { + self.reflow_timeout.set(None) + } + /// Remove any existing association between the provided id and any elements in this document. fn unregister_named_element(self, to_unregister: JSRef<Element>, @@ -1004,6 +1040,7 @@ impl Document { animation_frame_list: RefCell::new(HashMap::new()), loader: DOMRefCell::new(doc_loader), current_parser: Default::default(), + reflow_timeout: Cell::new(None), } } @@ -1045,17 +1082,11 @@ trait PrivateDocumentHelpers { impl<'a> PrivateDocumentHelpers for JSRef<'a, Document> { fn create_node_list<F: Fn(JSRef<Node>) -> bool>(self, callback: F) -> Temporary<NodeList> { let window = self.window.root(); - let document_element = self.GetDocumentElement().root(); - let mut nodes = RootedVec::new(); - if let Some(ref root) = document_element { - for node in NodeCast::from_ref(root.r()).traverse_preorder() { - let node = node.root(); - if callback(node.r()) { - nodes.push(JS::from_rooted(node.r())); - } - } - }; - NodeList::new_simple_list(window.r(), &nodes) + let doc = self.GetDocumentElement().root(); + let maybe_node = doc.r().map(NodeCast::from_ref); + let iter = maybe_node.iter().flat_map(|node| node.traverse_preorder()) + .filter(|node| callback(node.root().r())); + NodeList::new_simple_list(window.r(), iter) } fn get_html_element(self) -> Option<Temporary<HTMLHtmlElement>> { diff --git a/components/script/dom/domrectlist.rs b/components/script/dom/domrectlist.rs index a6ad5b50771..fe3a45ecf02 100644 --- a/components/script/dom/domrectlist.rs +++ b/components/script/dom/domrectlist.rs @@ -6,7 +6,6 @@ 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 +18,17 @@ pub struct DOMRectList { } impl DOMRectList { - fn new_inherited(window: JSRef<Window>, - rects: &RootedVec<JS<DOMRect>>) -> DOMRectList { + fn new_inherited<T>(window: JSRef<Window>, rects: T) -> DOMRectList + where T: Iterator<Item=Temporary<DOMRect>> { DOMRectList { reflector_: Reflector::new(), - rects: (**rects).clone(), + rects: rects.map(JS::from_rooted).collect(), window: JS::from_rooted(window), } } - pub fn new(window: JSRef<Window>, - rects: &RootedVec<JS<DOMRect>>) -> Temporary<DOMRectList> { + pub fn new<T>(window: JSRef<Window>, rects: T) -> Temporary<DOMRectList> + where T: Iterator<Item=Temporary<DOMRect>> { 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 3b830a982d1..e0d0b79f640 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -1301,17 +1301,12 @@ impl<'a> ElementMethods for JSRef<'a, Element> { let win = window_from_node(self).root(); let node: JSRef<Node> = NodeCast::from_ref(self); 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) + let rects = raw_rects.iter().map(|rect| { + DOMRect::new(win.r(), + rect.origin.y, rect.origin.y + rect.size.height, + rect.origin.x, rect.origin.x + rect.size.width) + }); + DOMRectList::new(win.r(), rects) } // http://dev.w3.org/csswg/cssom-view/#dom-element-getboundingclientrect diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs index 8145c2f3866..97cd615f910 100644 --- a/components/script/dom/htmlbodyelement.rs +++ b/components/script/dom/htmlbodyelement.rs @@ -6,11 +6,11 @@ use dom::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::HTMLBodyElementBinding::{self, HTMLBodyElementMethods}; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::codegen::InheritTypes::EventTargetCast; +use dom::bindings::codegen::InheritTypes::{EventTargetCast}; use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCast}; use dom::bindings::js::{JSRef, Rootable, Temporary}; use dom::bindings::utils::Reflectable; -use dom::document::Document; +use dom::document::{Document, DocumentHelpers}; use dom::element::ElementTypeId; use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers}; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; @@ -23,6 +23,11 @@ use util::str::{self, DOMString}; use std::borrow::ToOwned; use std::cell::Cell; +use time; + +/// How long we should wait before performing the initial reflow after `<body>` is parsed, in +/// nanoseconds. +const INITIAL_REFLOW_DELAY: u64 = 200_000_000; #[dom_struct] pub struct HTMLBodyElement { @@ -32,9 +37,8 @@ pub struct HTMLBodyElement { impl HTMLBodyElementDerived for EventTarget { fn is_htmlbodyelement(&self) -> bool { - *self.type_id() == - EventTargetTypeId::Node( - NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement))) + *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLBodyElement))) } } @@ -91,6 +95,20 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> { Some(element as &VirtualMethods) } + fn bind_to_tree(&self, tree_in_doc: bool) { + if let Some(ref s) = self.super_type() { + s.bind_to_tree(tree_in_doc); + } + + if !tree_in_doc { + return + } + + let window = window_from_node(*self).root(); + let document = window.r().Document().root(); + document.r().set_reflow_timeout(time::precise_time_ns() + INITIAL_REFLOW_DELAY); + } + fn after_set_attr(&self, attr: JSRef<Attr>) { if let Some(ref s) = self.super_type() { s.after_set_attr(attr); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index d9e9ef25b7c..50f4db71f8f 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -917,12 +917,9 @@ impl<'a> NodeHelpers for JSRef<'a, Node> { // https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall #[allow(unsafe_code)] fn query_selector_all(self, selectors: DOMString) -> Fallible<Temporary<NodeList>> { - let mut nodes = RootedVec::new(); - for node in try!(unsafe { self.query_selector_iter(selectors) }) { - nodes.push(JS::from_rooted(node)); - } let window = window_from_node(self).root(); - Ok(NodeList::new_simple_list(window.r(), &nodes)) + let iter = try!(unsafe { self.query_selector_iter(selectors) }); + Ok(NodeList::new_simple_list(window.r(), iter)) } diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 66b2cb6c5c4..bb3df75e920 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -6,7 +6,6 @@ use dom::bindings::codegen::Bindings::NodeListBinding; use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, JSRef, Rootable, Temporary}; -use dom::bindings::trace::RootedVec; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::node::{Node, NodeHelpers}; use dom::window::Window; @@ -39,8 +38,10 @@ impl NodeList { GlobalRef::Window(window), NodeListBinding::Wrap) } - pub fn new_simple_list(window: JSRef<Window>, elements: &RootedVec<JS<Node>>) -> Temporary<NodeList> { - NodeList::new(window, NodeListType::Simple((**elements).clone())) + pub fn new_simple_list<T>(window: JSRef<Window>, iter: T) + -> Temporary<NodeList> + where T: Iterator<Item=Temporary<Node>> { + NodeList::new(window, NodeListType::Simple(iter.map(JS::from_rooted).collect())) } pub fn new_child_list(window: JSRef<Window>, node: JSRef<Node>) -> Temporary<NodeList> { diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index c4779a5666d..1d69b24c5c8 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -303,6 +303,9 @@ impl<'a> PrivateServoHTMLParserHelpers for JSRef<'a, ServoHTMLParser> { break; } + let document = self.document.root(); + document.r().reflow_if_reflow_timer_expired(); + let mut pending_input = self.pending_input.borrow_mut(); if !pending_input.is_empty() { let chunk = pending_input.remove(0); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 6ebff11c5f1..21e9bbc28f2 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -81,6 +81,7 @@ enum WindowState { #[derive(Debug)] pub enum ReflowReason { CachedPageNeededReflow, + RefreshTick, FirstLoad, KeyEvent, MouseEvent, @@ -1046,6 +1047,7 @@ fn debug_reflow_events(goal: &ReflowGoal, query_type: &ReflowQueryType, reason: debug_msg.push_str(match *reason { ReflowReason::CachedPageNeededReflow => "\tCachedPageNeededReflow", + ReflowReason::RefreshTick => "\tRefreshTick", ReflowReason::FirstLoad => "\tFirstLoad", ReflowReason::KeyEvent => "\tKeyEvent", ReflowReason::MouseEvent => "\tMouseEvent", |