diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/bindings/trace.rs | 14 | ||||
-rw-r--r-- | components/script/dom/document.rs | 15 | ||||
-rw-r--r-- | components/script/dom/element.rs | 20 | ||||
-rw-r--r-- | components/script/dom/htmlinputelement.rs | 3 | ||||
-rw-r--r-- | components/script/dom/node.rs | 175 | ||||
-rw-r--r-- | components/script/dom/window.rs | 13 | ||||
-rw-r--r-- | components/script/layout_interface.rs | 59 | ||||
-rw-r--r-- | components/script/page.rs | 60 | ||||
-rw-r--r-- | components/script/script_task.rs | 61 |
9 files changed, 251 insertions, 169 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 808f62ecf49..ba3642e79af 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -49,8 +49,10 @@ use http::headers::response::HeaderCollection as ResponseHeaderCollection; use http::headers::request::HeaderCollection as RequestHeaderCollection; use http::method::Method; use std::io::timer::Timer; +use script_traits::UntrustedNodeAddress; use servo_msg::compositor_msg::ScriptListener; use servo_msg::constellation_msg::ConstellationChan; +use servo_util::smallvec::{SmallVec1, SmallVec}; use layout_interface::{LayoutRPC, LayoutChan}; use dom::bindings::utils::WindowProxyHandler; @@ -148,6 +150,17 @@ impl<T: JSTraceable> JSTraceable for Vec<T> { } } +// XXXManishearth Check if the following three are optimized to no-ops +// if e.trace() is a no-op (e.g it is an untraceable type) +impl<T: JSTraceable + 'static> JSTraceable for SmallVec1<T> { + #[inline] + fn trace(&self, trc: *mut JSTracer) { + for e in self.iter() { + e.trace(trc); + } + } +} + impl<T: JSTraceable> JSTraceable for Option<T> { #[inline] fn trace(&self, trc: *mut JSTracer) { @@ -192,6 +205,7 @@ untraceable!(ResponseHeaderCollection, RequestHeaderCollection, Method) untraceable!(ConstellationChan) untraceable!(LayoutChan) untraceable!(WindowProxyHandler) +untraceable!(UntrustedNodeAddress) impl<'a> JSTraceable for &'a str { #[inline] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 406b1fd9dff..99dde0e9f0c 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -54,7 +54,6 @@ use dom::uievent::UIEvent; use dom::window::{Window, WindowHelpers}; use html::hubbub_html_parser::build_element_from_tag; use hubbub::hubbub::{QuirksMode, NoQuirks, LimitedQuirks, FullQuirks}; -use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage}; use servo_util::namespace; use servo_util::str::{DOMString, split_html_space_chars}; @@ -165,8 +164,8 @@ pub trait DocumentHelpers<'a> { fn set_quirks_mode(self, mode: QuirksMode); fn set_last_modified(self, value: DOMString); fn set_encoding_name(self, name: DOMString); - fn content_changed(self); - fn damage_and_reflow(self, damage: DocumentDamageLevel); + fn content_changed(self, node: JSRef<Node>); + fn reflow(self); fn wait_until_safe_to_modify_dom(self); fn unregister_named_element(self, to_unregister: JSRef<Element>, id: Atom); fn register_named_element(self, element: JSRef<Element>, id: Atom); @@ -195,19 +194,19 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { *self.encoding_name.borrow_mut() = name; } - fn content_changed(self) { - self.damage_and_reflow(ContentChangedDocumentDamage); + fn content_changed(self, node: JSRef<Node>) { + node.dirty(); + self.reflow(); } - fn damage_and_reflow(self, damage: DocumentDamageLevel) { - self.window.root().damage_and_reflow(damage); + fn reflow(self) { + self.window.root().reflow(); } fn wait_until_safe_to_modify_dom(self) { self.window.root().wait_until_safe_to_modify_dom(); } - /// Remove any existing association between the provided id and any elements in this document. fn unregister_named_element(self, to_unregister: JSRef<Element>, diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index f8b2fa73a60..ce1371f74ef 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -28,8 +28,6 @@ use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_fro use dom::node::{window_from_node, LayoutNodeHelpers}; use dom::nodelist::NodeList; use dom::virtualmethods::{VirtualMethods, vtable_for}; -use layout_interface::ContentChangedDocumentDamage; -use layout_interface::MatchSelectorsDocumentDamage; use devtools_traits::AttrInfo; use style::{matches, parse_selector_list_from_str}; use style; @@ -323,6 +321,7 @@ pub trait AttributeHandlers { fn remove_attribute(self, namespace: Namespace, name: &str); fn notify_attribute_changed(self, local_name: &Atom); fn has_class(&self, name: &str) -> bool; + fn notify_attribute_removed(self); fn set_atomic_attribute(self, name: &str, value: DOMString); @@ -436,19 +435,24 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> { } self.attrs.borrow_mut().remove(idx); + self.notify_attribute_removed(); } }; } - fn notify_attribute_changed(self, local_name: &Atom) { + fn notify_attribute_changed(self, _local_name: &Atom) { + let node: JSRef<Node> = NodeCast::from_ref(self); + if node.is_in_doc() { + let document = node.owner_doc().root(); + document.content_changed(node); + } + } + + fn notify_attribute_removed(self) { let node: JSRef<Node> = NodeCast::from_ref(self); if node.is_in_doc() { - let damage = match local_name.as_slice() { - "style" | "id" | "class" => MatchSelectorsDocumentDamage, - _ => ContentChangedDocumentDamage - }; let document = node.owner_doc().root(); - document.damage_and_reflow(damage); + document.content_changed(node); } } diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index ee3c15fd85b..9908807a713 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -175,7 +175,8 @@ fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<& impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> { fn force_relayout(self) { let doc = document_from_node(self).root(); - doc.content_changed() + let node: JSRef<Node> = NodeCast::from_ref(self); + doc.content_changed(node) } fn radio_group_updated(self, group: Option<&str>) { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 98706388666..9ef523ba9e9 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -46,8 +46,9 @@ use dom::window::Window; use geom::rect::Rect; use html::hubbub_html_parser::build_element_from_tag; use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC, - LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress, UntrustedNodeAddress}; + LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress}; use devtools_traits::NodeInfo; +use script_traits::UntrustedNodeAddress; use servo_util::geometry::Au; use servo_util::str::{DOMString, null_str_as_empty}; use style::{parse_selector_list_from_str, matches}; @@ -56,7 +57,7 @@ use js::jsapi::{JSContext, JSObject, JSTracer, JSRuntime}; use js::jsfriendapi; use libc; use libc::uintptr_t; -use std::cell::{RefCell, Ref, RefMut}; +use std::cell::{Cell, RefCell, Ref, RefMut}; use std::default::Default; use std::iter::{Map, Filter}; use std::mem; @@ -101,7 +102,7 @@ pub struct Node { child_list: MutNullableJS<NodeList>, /// A bitfield of flags for node items. - flags: RefCell<NodeFlags>, + flags: Cell<NodeFlags>, /// Layout information. Only the layout task may touch this data. /// @@ -132,14 +133,19 @@ bitflags! { #[doc = "Specifies whether this node is in disabled state."] static InDisabledState = 0x04, #[doc = "Specifies whether this node is in enabled state."] - static InEnabledState = 0x08 + static InEnabledState = 0x08, + #[doc = "Specifies whether this node has changed since the last reflow."] + static IsDirty = 0x10, + #[doc = "Specifies whether this node has descendants (inclusive of itself) which \ + have changed since the last reflow."] + static HasDirtyDescendants = 0x20, } } impl NodeFlags { pub fn new(type_id: NodeTypeId) -> NodeFlags { match type_id { - DocumentNodeTypeId => IsInDoc, + DocumentNodeTypeId => IsInDoc | IsDirty, // The following elements are enabled by default. ElementNodeTypeId(HTMLButtonElementTypeId) | ElementNodeTypeId(HTMLInputElementTypeId) | @@ -148,8 +154,8 @@ impl NodeFlags { ElementNodeTypeId(HTMLOptGroupElementTypeId) | ElementNodeTypeId(HTMLOptionElementTypeId) | //ElementNodeTypeId(HTMLMenuItemElementTypeId) | - ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState, - _ => NodeFlags::empty(), + ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState | IsDirty, + _ => IsDirty, } } } @@ -271,7 +277,7 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { let parent = self.parent_node().root(); parent.map(|parent| vtable_for(&*parent).child_inserted(self)); - document.content_changed(); + document.content_changed(self); } // http://dom.spec.whatwg.org/#node-is-removed @@ -283,7 +289,7 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { vtable_for(&node).unbind_from_tree(parent_in_doc); } - document.content_changed(); + document.content_changed(self); } // @@ -395,6 +401,9 @@ pub trait NodeHelpers<'a> { fn is_text(self) -> bool; fn is_anchor_element(self) -> bool; + fn get_flag(self, flag: NodeFlags) -> bool; + fn set_flag(self, flag: NodeFlags, value: bool); + fn get_hover_state(self) -> bool; fn set_hover_state(self, state: bool); @@ -404,6 +413,17 @@ pub trait NodeHelpers<'a> { fn get_enabled_state(self) -> bool; fn set_enabled_state(self, state: bool); + fn get_is_dirty(self) -> bool; + fn set_is_dirty(self, state: bool); + + fn get_has_dirty_descendants(self) -> bool; + fn set_has_dirty_descendants(self, state: bool); + + /// Marks the given node as `IsDirty`, its siblings as `IsDirty` (to deal + /// with sibling selectors), its ancestors as `HasDirtyDescendants`, and its + /// descendants as `IsDirty`. + fn dirty(self); + fn dump(self); fn dump_indent(self, indent: uint); fn debug_str(self) -> String; @@ -426,6 +446,7 @@ pub trait NodeHelpers<'a> { fn summarize(self) -> NodeInfo; } + impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { /// Dumps the subtree rooted at this node, for debugging. fn dump(self) { @@ -454,7 +475,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } fn is_in_doc(self) -> bool { - self.flags.borrow().contains(IsInDoc) + self.deref().flags.get().contains(IsInDoc) } /// Returns the type ID of this node. Fails if this node is borrowed mutably. @@ -512,39 +533,98 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { self.type_id == TextNodeTypeId } - fn get_hover_state(self) -> bool { - self.flags.borrow().contains(InHoverState) + fn get_flag(self, flag: NodeFlags) -> bool { + self.flags.get().contains(flag) } - fn set_hover_state(self, state: bool) { - if state { - self.flags.borrow_mut().insert(InHoverState); + fn set_flag(self, flag: NodeFlags, value: bool) { + let mut flags = self.flags.get(); + + if value { + flags.insert(flag); } else { - self.flags.borrow_mut().remove(InHoverState); + flags.remove(flag); } + + self.flags.set(flags); + } + + fn get_hover_state(self) -> bool { + self.get_flag(InHoverState) + } + + fn set_hover_state(self, state: bool) { + self.set_flag(InHoverState, state) } fn get_disabled_state(self) -> bool { - self.flags.borrow().contains(InDisabledState) + self.get_flag(InDisabledState) } fn set_disabled_state(self, state: bool) { - if state { - self.flags.borrow_mut().insert(InDisabledState); - } else { - self.flags.borrow_mut().remove(InDisabledState); - } + self.set_flag(InDisabledState, state) } fn get_enabled_state(self) -> bool { - self.flags.borrow().contains(InEnabledState) + self.get_flag(InEnabledState) } fn set_enabled_state(self, state: bool) { - if state { - self.flags.borrow_mut().insert(InEnabledState); - } else { - self.flags.borrow_mut().remove(InEnabledState); + self.set_flag(InEnabledState, state) + } + + fn get_is_dirty(self) -> bool { + self.get_flag(IsDirty) + } + + fn set_is_dirty(self, state: bool) { + self.set_flag(IsDirty, state) + } + + fn get_has_dirty_descendants(self) -> bool { + self.get_flag(HasDirtyDescendants) + } + + fn set_has_dirty_descendants(self, state: bool) { + self.set_flag(HasDirtyDescendants, state) + } + + fn dirty(self) { + // 1. Dirty descendants. + fn dirty_subtree(node: JSRef<Node>) { + node.set_is_dirty(true); + + let mut has_dirty_descendants = false; + + for kid in node.children() { + dirty_subtree(kid); + has_dirty_descendants = true; + } + + if has_dirty_descendants { + node.set_has_dirty_descendants(true); + } + } + dirty_subtree(self); + + // 2. Dirty siblings. + // + // TODO(cgaebel): This is a very conservative way to account for sibling + // selectors. Maybe we can do something smarter in the future. + let parent = + match self.parent_node() { + None => return, + Some(parent) => parent, + }; + + for sibling in parent.root().children() { + sibling.set_is_dirty(true); + } + + // 3. Dirty ancestors. + for ancestor in self.ancestors() { + if ancestor.get_has_dirty_descendants() { break } + ancestor.set_has_dirty_descendants(true); } } @@ -734,6 +814,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { incompleteValue: false, //FIXME: reflect truncation } } + } /// If the given untrusted node address represents a valid DOM node in the given runtime, @@ -764,6 +845,8 @@ pub trait LayoutNodeHelpers { unsafe fn owner_doc_for_layout(&self) -> JS<Document>; unsafe fn is_element_for_layout(&self) -> bool; + unsafe fn get_flag(self, flag: NodeFlags) -> bool; + unsafe fn set_flag(self, flag: NodeFlags, value: bool); } impl LayoutNodeHelpers for JS<Node> { @@ -806,6 +889,25 @@ impl LayoutNodeHelpers for JS<Node> { unsafe fn owner_doc_for_layout(&self) -> JS<Document> { (*self.unsafe_get()).owner_doc.get_inner().unwrap() } + + #[inline] + unsafe fn get_flag(self, flag: NodeFlags) -> bool { + (*self.unsafe_get()).flags.get().contains(flag) + } + + #[inline] + unsafe fn set_flag(self, flag: NodeFlags, value: bool) { + let this = self.unsafe_get(); + let mut flags = (*this).flags.get(); + + if value { + flags.insert(flag); + } else { + flags.remove(flag); + } + + (*this).flags.set(flags); + } } pub trait RawLayoutNodeHelpers { @@ -1034,8 +1136,7 @@ impl Node { prev_sibling: Default::default(), owner_doc: MutNullableJS::new(doc), child_list: Default::default(), - - flags: RefCell::new(NodeFlags::new(type_id)), + flags: Cell::new(NodeFlags::new(type_id)), layout_data: LayoutDataRef::new(), @@ -1236,11 +1337,13 @@ impl 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(); if is_in_doc { - kid.flags.borrow_mut().insert(IsInDoc); + flags.insert(IsInDoc); } else { - kid.flags.borrow_mut().remove(IsInDoc); + flags.remove(IsInDoc); } + kid.flags.set(flags); } } @@ -1326,7 +1429,7 @@ impl Node { // Step 8. parent.remove_child(node); - node.flags.borrow_mut().remove(IsInDoc); + node.set_flag(IsInDoc, false); // Step 9. match suppress_observers { @@ -1660,7 +1763,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> { // Notify the document that the content of this node is different let document = self.owner_doc().root(); - document.content_changed(); + document.content_changed(self); } DoctypeNodeTypeId | DocumentNodeTypeId => {} @@ -2120,6 +2223,12 @@ impl<'a> style::TNode<'a, JSRef<'a, Element>> for JSRef<'a, Node> { assert!(elem.is_some()); elem.unwrap().html_element_in_html_document() } + + fn is_dirty(self) -> bool { self.get_is_dirty() } + unsafe fn set_dirty(self, value: bool) { self.set_is_dirty(value) } + + fn has_dirty_descendants(self) -> bool { self.get_has_dirty_descendants() } + unsafe fn set_dirty_descendants(self, value: bool) { self.set_has_dirty_descendants(value) } } pub trait DisabledStateHelpers { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 0c29b1be815..02166f328e4 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -18,7 +18,7 @@ use dom::location::Location; use dom::navigator::Navigator; use dom::performance::Performance; use dom::screen::Screen; -use layout_interface::{ReflowGoal, DocumentDamageLevel}; +use layout_interface::{ReflowGoal, ReflowForDisplay}; use page::Page; use script_task::{ExitWindowMsg, FireTimerMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg}; use script_traits::ScriptControlChan; @@ -366,7 +366,7 @@ impl Reflectable for Window { } pub trait WindowHelpers { - fn damage_and_reflow(self, damage: DocumentDamageLevel); + fn reflow(self); fn flush_layout(self, goal: ReflowGoal); fn wait_until_safe_to_modify_dom(self); fn init_browser_context(self, doc: JSRef<Document>); @@ -399,9 +399,12 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { }) } - fn damage_and_reflow(self, damage: DocumentDamageLevel) { - self.page().damage(damage); - self.page().avoided_reflows.set(self.page().avoided_reflows.get() + 1); + fn reflow(self) { + self.page().damage(); + // 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.page().reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor); } fn flush_layout(self, goal: ReflowGoal) { diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 7de7260d8c5..a3c8de5a66a 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -14,11 +14,10 @@ use geom::point::Point2D; use geom::rect::Rect; use js::jsapi::JSTracer; use libc::c_void; -use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel}; +use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress}; use servo_msg::constellation_msg::WindowSizeData; use servo_util::geometry::Au; use std::any::{Any, AnyRefExt}; -use std::cmp; use std::comm::{channel, Receiver, Sender}; use std::owned::BoxAny; use style::Stylesheet; @@ -85,47 +84,13 @@ impl JSTraceable for TrustedNodeAddress { } } -/// The address of a node. Layout sends these back. They must be validated via -/// `from_untrusted_node_address` before they can be used, because we do not trust layout. -pub type UntrustedNodeAddress = *const c_void; - pub struct ContentBoxResponse(pub Rect<Au>); pub struct ContentBoxesResponse(pub Vec<Rect<Au>>); pub struct HitTestResponse(pub UntrustedNodeAddress); pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>); -/// Determines which part of the -#[deriving(PartialEq, PartialOrd, Eq, Ord)] -#[jstraceable] -pub enum DocumentDamageLevel { - /// Reflow, but do not perform CSS selector matching. - ReflowDocumentDamage, - /// Perform CSS selector matching and reflow. - MatchSelectorsDocumentDamage, - /// Content changed; set full style damage and do the above. - ContentChangedDocumentDamage, -} - -impl DocumentDamageLevel { - /// Sets this damage to the maximum of this damage and the given damage. - pub fn add(&mut self, new_damage: DocumentDamageLevel) { - *self = cmp::max(*self, new_damage); - } -} - -/// What parts of the document have changed, as far as the script task can tell. -/// -/// Note that this is fairly coarse-grained and is separate from layout's notion of the document -#[jstraceable] -pub struct DocumentDamage { - /// The topmost node in the tree that has changed. - pub root: TrustedNodeAddress, - /// The amount of damage that occurred. - pub level: DocumentDamageLevel, -} - /// Why we're doing reflow. -#[deriving(PartialEq)] +#[deriving(PartialEq, Show)] pub enum ReflowGoal { /// We're reflowing in order to send a display list to the screen. ReflowForDisplay, @@ -137,8 +102,6 @@ pub enum ReflowGoal { pub struct Reflow { /// The document node. pub document_root: TrustedNodeAddress, - /// The style changes that need to be done. - pub damage: DocumentDamage, /// The goal of reflow: either to render to the screen or to flush layout info for script. pub goal: ReflowGoal, /// The URL of the page. @@ -190,21 +153,3 @@ impl ScriptLayoutChan for OpaqueScriptLayoutChannel { *receiver.downcast::<Receiver<Msg>>().unwrap() } } - -#[test] -fn test_add_damage() { - fn assert_add(mut a: DocumentDamageLevel, b: DocumentDamageLevel, - result: DocumentDamageLevel) { - a.add(b); - assert!(a == result); - } - - assert_add(ReflowDocumentDamage, ReflowDocumentDamage, ReflowDocumentDamage); - assert_add(ContentChangedDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage); - assert_add(ReflowDocumentDamage, MatchSelectorsDocumentDamage, MatchSelectorsDocumentDamage); - assert_add(MatchSelectorsDocumentDamage, ReflowDocumentDamage, MatchSelectorsDocumentDamage); - assert_add(ReflowDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage); - assert_add(ContentChangedDocumentDamage, ReflowDocumentDamage, ContentChangedDocumentDamage); - assert_add(MatchSelectorsDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage); - assert_add(ContentChangedDocumentDamage, MatchSelectorsDocumentDamage, ContentChangedDocumentDamage); -} diff --git a/components/script/page.rs b/components/script/page.rs index a2788cfdec0..f8a2248d447 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -10,12 +10,11 @@ use dom::document::{Document, DocumentHelpers}; use dom::element::Element; use dom::node::{Node, NodeHelpers}; use dom::window::Window; -use layout_interface::{DocumentDamage, ReflowForDisplay}; -use layout_interface::{DocumentDamageLevel, HitTestResponse, MouseOverResponse}; +use layout_interface::{ReflowForDisplay}; +use layout_interface::{HitTestResponse, MouseOverResponse}; use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC}; use layout_interface::{Reflow, ReflowGoal, ReflowMsg}; -use layout_interface::UntrustedNodeAddress; -use script_traits::ScriptControlChan; +use script_traits::{UntrustedNodeAddress, ScriptControlChan}; use geom::point::Point2D; use js::rust::Cx; @@ -25,6 +24,7 @@ use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData}; use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_net::resource_task::ResourceTask; use servo_util::str::DOMString; +use servo_util::smallvec::{SmallVec1, SmallVec}; use std::cell::{Cell, RefCell, Ref, RefMut}; use std::comm::{channel, Receiver, Empty, Disconnected}; use std::mem::replace; @@ -55,9 +55,6 @@ pub struct Page { /// The port that we will use to join layout. If this is `None`, then layout is not running. pub layout_join_port: RefCell<Option<Receiver<()>>>, - /// What parts of the document are dirty, if any. - damage: RefCell<Option<DocumentDamage>>, - /// The current size of the window, in pixels. pub window_size: Cell<WindowSizeData>, @@ -74,6 +71,9 @@ pub struct Page { /// Pending resize event, if any. pub resize_event: Cell<Option<WindowSizeData>>, + /// Any nodes that need to be dirtied before the next reflow. + pub pending_dirty_nodes: RefCell<SmallVec1<UntrustedNodeAddress>>, + /// Pending scroll to fragment event, if any pub fragment_name: RefCell<Option<String>>, @@ -86,6 +86,9 @@ pub struct Page { // Child Pages. pub children: RefCell<Vec<Rc<Page>>>, + /// Whether layout needs to be run at all. + pub damaged: Cell<bool>, + /// Number of pending reflows that were sent while layout was active. pub pending_reflows: Cell<int>, @@ -143,25 +146,25 @@ impl Page { layout_chan: layout_chan, layout_rpc: layout_rpc, layout_join_port: RefCell::new(None), - damage: RefCell::new(None), window_size: Cell::new(window_size), js_info: RefCell::new(Some(js_info)), url: RefCell::new(None), next_subpage_id: Cell::new(SubpageId(0)), resize_event: Cell::new(None), + pending_dirty_nodes: RefCell::new(SmallVec1::new()), fragment_name: RefCell::new(None), last_reflow_id: Cell::new(0), resource_task: resource_task, constellation_chan: constellation_chan, children: RefCell::new(vec!()), + damaged: Cell::new(false), pending_reflows: Cell::new(0), avoided_reflows: Cell::new(0), } } pub fn flush_layout(&self, goal: ReflowGoal) { - let damaged = self.damage.borrow().is_some(); - if damaged { + if self.damaged.get() { let frame = self.frame(); let window = frame.as_ref().unwrap().window.root(); self.reflow(goal, window.control_chan.clone(), &*window.compositor); @@ -255,35 +258,6 @@ impl Page { subpage_id } - /// Adds the given damage. - pub fn damage(&self, level: DocumentDamageLevel) { - let root = match *self.frame() { - None => return, - Some(ref frame) => frame.document.root().GetDocumentElement() - }; - match root.root() { - None => {}, - Some(root) => { - let root: JSRef<Node> = NodeCast::from_ref(*root); - let mut damage = *self.damage.borrow_mut(); - match damage { - None => {} - Some(ref mut damage) => { - // FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor. - damage.root = root.to_trusted_node_address(); - damage.level.add(level); - return - } - } - - *self.damage.borrow_mut() = Some(DocumentDamage { - root: root.to_trusted_node_address(), - level: level, - }) - } - }; - } - pub fn get_url(&self) -> Url { self.url().as_ref().unwrap().ref0().clone() } @@ -360,8 +334,9 @@ impl Page { last_reflow_id.set(last_reflow_id.get() + 1); let root: JSRef<Node> = NodeCast::from_ref(*root); - let mut damage = self.damage.borrow_mut(); + let window_size = self.window_size.get(); + self.damaged.set(false); // Send new document and relevant styles to layout. let reflow = box Reflow { @@ -372,7 +347,6 @@ impl Page { window_size: window_size, script_chan: script_chan, script_join_chan: join_chan, - damage: replace(&mut *damage, None).unwrap(), id: last_reflow_id.get(), }; @@ -384,6 +358,10 @@ impl Page { } } + pub fn damage(&self) { + self.damaged.set(true); + } + /// Attempt to find a named element in this page's document. pub fn find_fragment_node(&self, fragid: DOMString) -> Option<Temporary<Element>> { let document = self.frame().as_ref().unwrap().document.root(); diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 7ae23dba2a2..e01a8caff39 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -29,9 +29,7 @@ use dom::worker::{Worker, TrustedWorkerAddress}; use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress}; use html::hubbub_html_parser::{InputString, InputUrl, HtmlParserResult, HtmlDiscoveredScript}; use html::hubbub_html_parser; -use layout_interface::{ScriptLayoutChan, LayoutChan, MatchSelectorsDocumentDamage}; -use layout_interface::{ReflowDocumentDamage, ReflowForDisplay}; -use layout_interface::ContentChangedDocumentDamage; +use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowForDisplay}; use layout_interface; use page::{Page, IterablePage, Frame}; @@ -52,6 +50,7 @@ use servo_msg::constellation_msg; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; use servo_util::geometry::to_frac_px; +use servo_util::smallvec::{SmallVec1, SmallVec}; use servo_util::task::spawn_named_with_send_on_failure; use geom::point::Point2D; @@ -66,6 +65,7 @@ use url::Url; use libc::size_t; use std::any::{Any, AnyRefExt}; use std::cell::RefCell; +use std::collections::HashSet; use std::comm::{channel, Sender, Receiver, Select}; use std::mem::replace; use std::rc::Rc; @@ -445,7 +445,9 @@ impl ScriptTask { } }; - // Squash any pending resize events in the queue. + let mut needs_reflow = HashSet::new(); + + // Squash any pending resize and reflow events in the queue. loop { match event { // This has to be handled before the ResizeMsg below, @@ -459,6 +461,13 @@ impl ScriptTask { let page = page.find(id).expect("resize sent to nonexistent pipeline"); page.resize_event.set(Some(size)); } + FromConstellation(SendEventMsg(id, ReflowEvent(node_addresses))) => { + let mut page = self.page.borrow_mut(); + let inner_page = page.find(id).expect("Reflow sent to nonexistent pipeline"); + let mut pending = inner_page.pending_dirty_nodes.borrow_mut(); + pending.push_all_move(node_addresses); + needs_reflow.insert(id); + } _ => { sequential.push(event); } @@ -507,6 +516,11 @@ impl ScriptTask { } } + // Now process any pending reflows. + for id in needs_reflow.into_iter() { + self.handle_event(id, ReflowEvent(SmallVec1::new())); + } + true } @@ -632,8 +646,7 @@ impl ScriptTask { if page.pending_reflows.get() > 0 { page.pending_reflows.set(0); - page.damage(MatchSelectorsDocumentDamage); - page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor); + self.force_reflow(&*page); } } @@ -711,8 +724,7 @@ impl ScriptTask { Some((ref loaded, needs_reflow)) if *loaded == url => { *page.mut_url() = Some((loaded.clone(), false)); if needs_reflow { - page.damage(ContentChangedDocumentDamage); - page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor); + self.force_reflow(&*page); } return; }, @@ -796,7 +808,11 @@ impl ScriptTask { // Kick off the initial reflow of the page. debug!("kicking off initial reflow of {}", url); - document.content_changed(); + { + let document_js_ref = (&*document).clone(); + let document_as_node = NodeCast::from_ref(document_js_ref); + document.content_changed(document_as_node); + } window.flush_layout(ReflowForDisplay); { @@ -856,6 +872,21 @@ impl ScriptTask { self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point); } + fn force_reflow(&self, page: &Page) { + { + let mut pending = page.pending_dirty_nodes.borrow_mut(); + let js_runtime = self.js_runtime.deref().ptr; + + for untrusted_node in pending.into_iter() { + let node = node::from_untrusted_node_address(js_runtime, untrusted_node).root(); + node.dirty(); + } + } + + page.damage(); + page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor); + } + /// This is the main entry point for receiving and dispatching DOM events. /// /// TODO: Actually perform DOM event dispatch. @@ -870,8 +901,7 @@ impl ScriptTask { let frame = page.frame(); if frame.is_some() { - page.damage(ReflowDocumentDamage); - page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor) + self.force_reflow(&*page); } let fragment_node = @@ -906,8 +936,9 @@ impl ScriptTask { } // FIXME(pcwalton): This reflows the entire document and is not incremental-y. - ReflowEvent => { + ReflowEvent(to_dirty) => { debug!("script got reflow event"); + assert_eq!(to_dirty.len(), 0); let page = get_page(&*self.page.borrow(), pipeline_id); let frame = page.frame(); if frame.is_some() { @@ -915,8 +946,7 @@ impl ScriptTask { if in_layout { page.pending_reflows.set(page.pending_reflows.get() + 1); } else { - page.damage(MatchSelectorsDocumentDamage); - page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor) + self.force_reflow(&*page); } } } @@ -1021,8 +1051,7 @@ impl ScriptTask { if target_compare { if mouse_over_targets.is_some() { - page.damage(MatchSelectorsDocumentDamage); - page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor); + self.force_reflow(&*page); } *mouse_over_targets = Some(target_list); } |