diff options
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/document.rs | 36 | ||||
-rw-r--r-- | components/script/dom/element.rs | 112 | ||||
-rw-r--r-- | components/script/dom/htmlinputelement.rs | 8 | ||||
-rw-r--r-- | components/script/dom/htmltextareaelement.rs | 5 | ||||
-rw-r--r-- | components/script/dom/node.rs | 62 | ||||
-rw-r--r-- | components/script/dom/window.rs | 22 |
6 files changed, 129 insertions, 116 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 738ac6be90e..c4dc787ac26 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -49,7 +49,7 @@ use dom::mouseevent::MouseEvent; use dom::keyboardevent::KeyboardEvent; use dom::messageevent::MessageEvent; use dom::node::{Node, ElementNodeTypeId, DocumentNodeTypeId, NodeHelpers}; -use dom::node::{CloneChildren, DoNotCloneChildren}; +use dom::node::{CloneChildren, DoNotCloneChildren, NodeDamage, OtherNodeDamage}; use dom::nodelist::NodeList; use dom::text::Text; use dom::processinginstruction::ProcessingInstruction; @@ -176,10 +176,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, node: JSRef<Node>); - fn content_and_heritage_changed(self, node: JSRef<Node>); - fn reflow(self); - fn wait_until_safe_to_modify_dom(self); + fn content_changed(self, node: JSRef<Node>, damage: NodeDamage); + fn content_and_heritage_changed(self, node: JSRef<Node>, damage: NodeDamage); 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); @@ -190,6 +188,7 @@ pub trait DocumentHelpers<'a> { fn request_focus(self, elem: JSRef<Element>); fn commit_focus_transaction(self); fn send_title_to_compositor(self); + fn dirty_all_nodes(self); } impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { @@ -228,24 +227,14 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { *self.encoding_name.borrow_mut() = name; } - fn content_changed(self, node: JSRef<Node>) { - debug!("content_changed on {}", node.debug_str()); - node.dirty(); - self.reflow(); + fn content_changed(self, node: JSRef<Node>, damage: NodeDamage) { + node.dirty(damage); } - fn content_and_heritage_changed(self, node: JSRef<Node>) { + fn content_and_heritage_changed(self, node: JSRef<Node>, damage: NodeDamage) { debug!("content_and_heritage_changed on {}", node.debug_str()); - node.force_dirty_ancestors(); - self.reflow(); - } - - fn reflow(self) { - self.window.root().reflow(); - } - - fn wait_until_safe_to_modify_dom(self) { - self.window.root().wait_until_safe_to_modify_dom(); + node.force_dirty_ancestors(damage); + node.dirty(damage); } /// Remove any existing association between the provided id and any elements in this document. @@ -376,6 +365,13 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { let window = self.window().root(); window.page().send_title_to_compositor(); } + + fn dirty_all_nodes(self) { + let root: JSRef<Node> = NodeCast::from_ref(self); + for node in root.traverse_preorder() { + node.dirty(OtherNodeDamage) + } + } } #[deriving(PartialEq)] diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index e24407d7957..1e55f9f73c2 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -33,8 +33,9 @@ use dom::htmlcollection::HTMLCollection; use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers}; use dom::htmlserializer::serialize; use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers}; -use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node, CLICK_IN_PROGRESS}; -use dom::node::{window_from_node, LayoutNodeHelpers}; +use dom::node::{CLICK_IN_PROGRESS, ElementNodeTypeId, Node, NodeHelpers, NodeIterator}; +use dom::node::{document_from_node, window_from_node, LayoutNodeHelpers, NodeStyleDamaged}; +use dom::node::{OtherNodeDamage}; use dom::nodelist::NodeList; use dom::virtualmethods::{VirtualMethods, vtable_for}; use devtools_traits::AttrInfo; @@ -441,7 +442,6 @@ pub trait AttributeHandlers { value: DOMString) -> AttrValue; fn remove_attribute(self, namespace: Namespace, name: &str); - fn notify_content_changed(self); fn has_class(&self, name: &Atom) -> bool; fn set_atomic_attribute(self, name: &Atom, value: DOMString); @@ -500,9 +500,6 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> { assert!(name.as_slice() == name.as_slice().to_ascii_lower().as_slice()); assert!(!name.as_slice().contains(":")); - let node: JSRef<Node> = NodeCast::from_ref(self); - node.wait_until_safe_to_modify_dom(); - self.do_set_attribute(name.clone(), value, name.clone(), ns!(""), None, |attr| *attr.local_name() == *name); } @@ -548,28 +545,26 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> { match idx { None => (), Some(idx) => { - let node: JSRef<Node> = NodeCast::from_ref(self); - node.wait_until_safe_to_modify_dom(); - if namespace == ns!("") { let attr = (*self.attrs.borrow())[idx].root(); vtable_for(&NodeCast::from_ref(self)).before_remove_attr(*attr); } self.attrs.borrow_mut().remove(idx); - self.notify_content_changed(); + + let node: JSRef<Node> = NodeCast::from_ref(self); + if node.is_in_doc() { + let document = document_from_node(self).root(); + if local_name == atom!("style") { + document.content_changed(node, NodeStyleDamaged); + } else { + document.content_changed(node, OtherNodeDamage); + } + } } }; } - fn notify_content_changed(self) { - let node: JSRef<Node> = NodeCast::from_ref(self); - if node.is_in_doc() { - let document = document_from_node(self).root(); - document.content_changed(node); - } - } - fn has_class(&self, name: &Atom) -> bool { self.get_attribute(ns!(""), &atom!("class")).root().map(|attr| { attr.value().tokens().map(|tokens| { @@ -755,11 +750,6 @@ impl<'a> ElementMethods for JSRef<'a, Element> { fn SetAttribute(self, name: DOMString, value: DOMString) -> ErrorResult { - { - let node: JSRef<Node> = NodeCast::from_ref(self); - node.wait_until_safe_to_modify_dom(); - } - // Step 1. match xml_name_type(name.as_slice()) { InvalidXMLName => return Err(InvalidCharacter), @@ -787,11 +777,6 @@ impl<'a> ElementMethods for JSRef<'a, Element> { namespace_url: Option<DOMString>, name: DOMString, value: DOMString) -> ErrorResult { - { - let node: JSRef<Node> = NodeCast::from_ref(self); - node.wait_until_safe_to_modify_dom(); - } - // Step 1. let namespace = namespace::from_domstring(namespace_url); @@ -999,25 +984,48 @@ impl<'a> VirtualMethods for JSRef<'a, Element> { match attr.local_name() { &atom!("style") => { + // Modifying the `style` attribute might change style. + let node: JSRef<Node> = NodeCast::from_ref(*self); let doc = document_from_node(*self).root(); let base_url = doc.url().clone(); let value = attr.value(); let style = Some(style::parse_style_attribute(value.as_slice(), &base_url)); *self.style_attribute.borrow_mut() = style; + + if node.is_in_doc() { + doc.content_changed(node, NodeStyleDamaged); + } + } + &atom!("class") => { + // Modifying a class can change style. + let node: JSRef<Node> = NodeCast::from_ref(*self); + if node.is_in_doc() { + let document = document_from_node(*self).root(); + document.content_changed(node, NodeStyleDamaged); + } } &atom!("id") => { + // Modifying an ID might change style. let node: JSRef<Node> = NodeCast::from_ref(*self); let value = attr.value(); - if node.is_in_doc() && !value.as_slice().is_empty() { + if node.is_in_doc() { let doc = document_from_node(*self).root(); - let value = Atom::from_slice(value.as_slice()); - doc.register_named_element(*self, value); + if !value.as_slice().is_empty() { + let value = Atom::from_slice(value.as_slice()); + doc.register_named_element(*self, value); + } + doc.content_changed(node, NodeStyleDamaged); + } + } + _ => { + // Modifying any other attribute might change arbitrary things. + let node: JSRef<Node> = NodeCast::from_ref(*self); + if node.is_in_doc() { + let document = document_from_node(*self).root(); + document.content_changed(node, OtherNodeDamage); } } - _ => () } - - self.notify_content_changed(); } fn before_remove_attr(&self, attr: JSRef<Attr>) { @@ -1028,21 +1036,45 @@ impl<'a> VirtualMethods for JSRef<'a, Element> { match attr.local_name() { &atom!("style") => { + // Modifying the `style` attribute might change style. *self.style_attribute.borrow_mut() = None; + + let node: JSRef<Node> = NodeCast::from_ref(*self); + if node.is_in_doc() { + let doc = document_from_node(*self).root(); + doc.content_changed(node, NodeStyleDamaged); + } } &atom!("id") => { + // Modifying an ID can change style. let node: JSRef<Node> = NodeCast::from_ref(*self); let value = attr.value(); - if node.is_in_doc() && !value.as_slice().is_empty() { + if node.is_in_doc() { let doc = document_from_node(*self).root(); - let value = Atom::from_slice(value.as_slice()); - doc.unregister_named_element(*self, value); + if !value.as_slice().is_empty() { + let value = Atom::from_slice(value.as_slice()); + doc.unregister_named_element(*self, value); + } + doc.content_changed(node, NodeStyleDamaged); + } + } + &atom!("class") => { + // Modifying a class can change style. + let node: JSRef<Node> = NodeCast::from_ref(*self); + if node.is_in_doc() { + let document = document_from_node(*self).root(); + document.content_changed(node, NodeStyleDamaged); + } + } + _ => { + // Modifying any other attribute might change arbitrary things. + let node: JSRef<Node> = NodeCast::from_ref(*self); + if node.is_in_doc() { + let doc = document_from_node(*self).root(); + doc.content_changed(node, OtherNodeDamage); } } - _ => () } - - self.notify_content_changed(); } fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 4e211bde251..5b0133395a7 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -26,8 +26,10 @@ use dom::event::{Event, Bubbles, NotCancelable, EventHelpers}; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::keyboardevent::KeyboardEvent; -use dom::htmlformelement::{InputElement, FormControl, HTMLFormElement, HTMLFormElementHelpers, NotFromFormSubmitMethod}; -use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node, window_from_node}; +use dom::htmlformelement::{InputElement, FormControl, HTMLFormElement, HTMLFormElementHelpers}; +use dom::htmlformelement::{NotFromFormSubmitMethod}; +use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, OtherNodeDamage}; +use dom::node::{document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; use textinput::{Single, TextInput, TriggerDefaultAction, DispatchInput, Nothing}; @@ -312,7 +314,7 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> { fn force_relayout(self) { let doc = document_from_node(self).root(); let node: JSRef<Node> = NodeCast::from_ref(self); - doc.content_changed(node) + doc.content_changed(node, OtherNodeDamage) } fn radio_group_updated(self, group: Option<&str>) { diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index c169a169cf9..05d3a5d0868 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -20,7 +20,8 @@ use dom::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::keyboardevent::KeyboardEvent; -use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node}; +use dom::node::{DisabledStateHelpers, Node, NodeHelpers, OtherNodeDamage, ElementNodeTypeId}; +use dom::node::{document_from_node}; use textinput::{Multiple, TextInput, TriggerDefaultAction, DispatchInput, Nothing}; use dom::virtualmethods::VirtualMethods; @@ -163,7 +164,7 @@ impl<'a> PrivateHTMLTextAreaElementHelpers for JSRef<'a, HTMLTextAreaElement> { fn force_relayout(self) { let doc = document_from_node(self).root(); let node: JSRef<Node> = NodeCast::from_ref(self); - doc.content_changed(node) + doc.content_changed(node, OtherNodeDamage) } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 161c8fc918b..ecfccf8aefc 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -287,20 +287,15 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { let parent = self.parent_node().root(); parent.map(|parent| vtable_for(&*parent).child_inserted(self)); - - document.content_and_heritage_changed(self); + document.content_and_heritage_changed(self, OtherNodeDamage); } // http://dom.spec.whatwg.org/#node-is-removed fn node_removed(self, parent_in_doc: bool) { assert!(self.parent_node().is_none()); - let document = document_from_node(self).root(); - for node in self.traverse_preorder() { vtable_for(&node).unbind_from_tree(parent_in_doc); } - - document.content_changed(self); } // @@ -311,9 +306,6 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { /// /// Fails unless `new_child` is disconnected from the tree. fn add_child(self, new_child: JSRef<Node>, before: Option<JSRef<Node>>) { - let doc = self.owner_doc().root(); - doc.wait_until_safe_to_modify_dom(); - assert!(new_child.parent_node().is_none()); assert!(new_child.prev_sibling().is_none()); assert!(new_child.next_sibling().is_none()); @@ -354,9 +346,6 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { /// /// Fails unless `child` is a child of this node. fn remove_child(self, child: JSRef<Node>) { - let doc = self.owner_doc().root(); - doc.wait_until_safe_to_modify_dom(); - assert!(child.parent_node().root().root_ref() == Some(self)); match child.prev_sibling.get().root() { @@ -428,8 +417,6 @@ pub trait NodeHelpers<'a> { fn set_owner_doc(self, document: JSRef<Document>); fn is_in_html_doc(self) -> bool; - fn wait_until_safe_to_modify_dom(self); - fn is_element(self) -> bool; fn is_document(self) -> bool; fn is_doctype(self) -> bool; @@ -460,10 +447,11 @@ pub trait NodeHelpers<'a> { fn get_has_dirty_descendants(self) -> bool; fn set_has_dirty_descendants(self, state: bool); - /// Marks the given node as `IS_DIRTY`, its siblings as `IS_DIRTY` (to deal - /// with sibling selectors), its ancestors as `HAS_DIRTY_DESCENDANTS`, and its - /// descendants as `IS_DIRTY`. - fn dirty(self); + /// Marks the given node as `IS_DIRTY`, its siblings as `HAS_DIRTY_SIBLINGS` (to deal with + /// sibling selectors), its ancestors as `HAS_DIRTY_DESCENDANTS`, and its descendants as + /// `IS_DIRTY`. If anything more than the node's style was damaged, this method also sets the + /// `HAS_CHANGED` flag. + fn dirty(self, damage: NodeDamage); /// Similar to `dirty`, but will always walk the ancestors to mark them dirty, /// too. This is useful when a node is reparented. The node will frequently @@ -471,9 +459,9 @@ pub trait NodeHelpers<'a> { /// still need to be marked as `HAS_DIRTY_DESCENDANTS`. /// /// See #4170 - fn force_dirty_ancestors(self); + fn force_dirty_ancestors(self, damage: NodeDamage); - fn dirty_impl(self, force_ancestors: bool); + fn dirty_impl(self, damage: NodeDamage, force_ancestors: bool); fn dump(self); fn dump_indent(self, indent: uint); @@ -656,17 +644,20 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { self.set_flag(HAS_DIRTY_DESCENDANTS, state) } - fn force_dirty_ancestors(self) { - self.dirty_impl(true) + fn force_dirty_ancestors(self, damage: NodeDamage) { + self.dirty_impl(damage, true) } - fn dirty(self) { - self.dirty_impl(false) + fn dirty(self, damage: NodeDamage) { + self.dirty_impl(damage, false) } - fn dirty_impl(self, force_ancestors: bool) { + fn dirty_impl(self, damage: NodeDamage, force_ancestors: bool) { // 1. Dirty self. - self.set_has_changed(true); + match damage { + NodeStyleDamaged => {} + OtherNodeDamage => self.set_has_changed(true), + } if self.get_is_dirty() && !force_ancestors { return @@ -830,11 +821,6 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { .peekable() } - fn wait_until_safe_to_modify_dom(self) { - let document = self.owner_doc().root(); - document.wait_until_safe_to_modify_dom(); - } - fn remove_self(self) { match self.parent_node().root() { Some(parent) => parent.remove_child(self), @@ -1826,14 +1812,12 @@ impl<'a> NodeMethods for JSRef<'a, Node> { CommentNodeTypeId | TextNodeTypeId | ProcessingInstructionNodeTypeId => { - self.wait_until_safe_to_modify_dom(); - let characterdata: JSRef<CharacterData> = CharacterDataCast::to_ref(self).unwrap(); characterdata.set_data(value); // Notify the document that the content of this node is different let document = self.owner_doc().root(); - document.content_changed(self); + document.content_changed(self, OtherNodeDamage); } DoctypeNodeTypeId | DocumentNodeTypeId => {} @@ -2366,3 +2350,13 @@ impl<'a> DisabledStateHelpers for JSRef<'a, Node> { self.set_enabled_state(!has_disabled_attrib); } } + +/// A summary of the changes that happened to a node. +#[deriving(Clone, PartialEq)] +pub enum NodeDamage { + /// The node's `style` attribute changed. + NodeStyleDamaged, + /// Other parts of a node changed; attributes, text content, etc. + OtherNodeDamage, +} + diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index ec12d983918..f305bf1344c 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -21,7 +21,7 @@ use dom::navigator::Navigator; use dom::performance::Performance; use dom::screen::Screen; use dom::storage::Storage; -use layout_interface::NoQuery; +use layout_interface::{NoQuery, ReflowForDisplay, ReflowGoal, ReflowQueryType}; use page::Page; use script_task::{ExitWindowMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg}; use script_task::FromWindow; @@ -299,9 +299,7 @@ impl Reflectable for Window { } pub trait WindowHelpers { - fn reflow(self); - fn flush_layout(self); - fn wait_until_safe_to_modify_dom(self); + fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType); fn init_browser_context(self, doc: JSRef<Document>); fn load_url(self, href: DOMString); fn handle_fire_timer(self, timer_id: TimerId); @@ -334,18 +332,8 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { }) } - fn reflow(self) { - self.page().damage(); - } - - fn flush_layout(self) { - self.page().flush_layout(NoQuery); - } - - fn wait_until_safe_to_modify_dom(self) { - // FIXME: This disables concurrent layout while we are modifying the DOM, since - // our current architecture is entirely unsafe in the presence of races. - self.page().join_layout(); + fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType) { + self.page().flush_layout(goal, query); } fn init_browser_context(self, doc: JSRef<Document>) { @@ -369,7 +357,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { fn handle_fire_timer(self, timer_id: TimerId) { self.timers.fire_timer(timer_id, self.clone()); - self.flush_layout(); + self.flush_layout(ReflowForDisplay, NoQuery); } } |