aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/trace.rs14
-rw-r--r--components/script/dom/document.rs15
-rw-r--r--components/script/dom/element.rs20
-rw-r--r--components/script/dom/htmlinputelement.rs3
-rw-r--r--components/script/dom/node.rs175
-rw-r--r--components/script/dom/window.rs13
-rw-r--r--components/script/layout_interface.rs59
-rw-r--r--components/script/page.rs60
-rw-r--r--components/script/script_task.rs61
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);
}