diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/bindings/trace.rs | 4 | ||||
-rw-r--r-- | components/script/dom/blob.rs | 7 | ||||
-rw-r--r-- | components/script/dom/document.rs | 46 | ||||
-rw-r--r-- | components/script/dom/element.rs | 219 | ||||
-rw-r--r-- | components/script/dom/file.rs | 9 | ||||
-rw-r--r-- | components/script/dom/htmlbodyelement.rs | 54 | ||||
-rw-r--r-- | components/script/dom/htmlimageelement.rs | 16 | ||||
-rw-r--r-- | components/script/dom/htmlinputelement.rs | 8 | ||||
-rw-r--r-- | components/script/dom/htmltablecellelement.rs | 37 | ||||
-rw-r--r-- | components/script/dom/htmltableelement.rs | 92 | ||||
-rw-r--r-- | components/script/dom/htmltablerowelement.rs | 63 | ||||
-rw-r--r-- | components/script/dom/htmltablesectionelement.rs | 66 | ||||
-rw-r--r-- | components/script/dom/htmltextareaelement.rs | 5 | ||||
-rw-r--r-- | components/script/dom/node.rs | 80 | ||||
-rw-r--r-- | components/script/dom/virtualmethods.rs | 27 | ||||
-rw-r--r-- | components/script/dom/window.rs | 22 | ||||
-rw-r--r-- | components/script/layout_interface.rs | 3 | ||||
-rw-r--r-- | components/script/page.rs | 153 | ||||
-rw-r--r-- | components/script/script_task.rs | 94 |
19 files changed, 687 insertions, 318 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index afd329137c0..b877d022c2e 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -32,6 +32,7 @@ use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler}; use dom::node::{Node, TrustedNodeAddress}; use collections::hash::{Hash, Hasher}; +use cssparser::RGBA; use geom::rect::Rect; use html5ever::tree_builder::QuirksMode; use hyper::header::Headers; @@ -48,7 +49,7 @@ use script_traits::UntrustedNodeAddress; use servo_msg::compositor_msg::ScriptListener; use servo_msg::constellation_msg::ConstellationChan; use servo_util::smallvec::{SmallVec1, SmallVec}; -use servo_util::str::LengthOrPercentageOrAuto; +use servo_util::str::{LengthOrPercentageOrAuto}; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::comm::{Receiver, Sender}; @@ -214,6 +215,7 @@ no_jsmanaged_fields!(LayoutChan) no_jsmanaged_fields!(WindowProxyHandler) no_jsmanaged_fields!(UntrustedNodeAddress) no_jsmanaged_fields!(LengthOrPercentageOrAuto) +no_jsmanaged_fields!(RGBA) impl<'a> JSTraceable for &'a str { #[inline] diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index b0d0b155821..cbe98b13a47 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -30,10 +30,11 @@ pub struct Blob { } impl Blob { - pub fn new_inherited(global: &GlobalRef, bytes: Option<Vec<u8>>) -> Blob { + pub fn new_inherited(global: &GlobalRef, type_: BlobType, + bytes: Option<Vec<u8>>) -> Blob { Blob { reflector_: Reflector::new(), - type_: BlobTypeId, + type_: type_, bytes: bytes, typeString: "".to_string(), global: GlobalField::from_rooted(global) @@ -42,7 +43,7 @@ impl Blob { } pub fn new(global: &GlobalRef, bytes: Option<Vec<u8>>) -> Temporary<Blob> { - reflect_dom_object(box Blob::new_inherited(global, bytes), + reflect_dom_object(box Blob::new_inherited(global, BlobTypeId, bytes), *global, BlobBinding::Wrap) } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 738ac6be90e..4fdb8a9befd 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; @@ -61,6 +61,7 @@ use servo_util::namespace; use servo_util::str::{DOMString, split_html_space_chars}; use html5ever::tree_builder::{QuirksMode, NoQuirks, LimitedQuirks, Quirks}; +use layout_interface::{LayoutChan, SetQuirksModeMsg}; use string_cache::{Atom, QualName}; use url::Url; @@ -176,10 +177,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 +189,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> { @@ -218,6 +218,15 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { fn set_quirks_mode(self, mode: QuirksMode) { self.quirks_mode.set(mode); + + match mode { + Quirks => { + let window = self.window.root(); + let LayoutChan(ref layout_chan) = window.page().layout_chan; + layout_chan.send(SetQuirksModeMsg); + } + NoQuirks | LimitedQuirks => {} + } } fn set_last_modified(self, value: DOMString) { @@ -228,24 +237,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 +375,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..69846d40543 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -15,8 +15,12 @@ use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods; -use dom::bindings::codegen::InheritTypes::{ElementDerived, HTMLInputElementDerived, HTMLTableCellElementDerived}; -use dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, NodeCast, EventTargetCast, ElementCast}; +use dom::bindings::codegen::InheritTypes::{ElementCast, ElementDerived, EventTargetCast}; +use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLInputElementCast}; +use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLTableElementCast}; +use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, HTMLTableCellElementDerived}; +use dom::bindings::codegen::InheritTypes::{HTMLTableRowElementDerived}; +use dom::bindings::codegen::InheritTypes::{HTMLTableSectionElementDerived, NodeCast}; use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, TemporaryPushable}; use dom::bindings::js::{OptionalRootable, Root}; use dom::bindings::utils::{Reflectable, Reflector}; @@ -29,21 +33,28 @@ use dom::document::{Document, DocumentHelpers, LayoutDocumentHelpers}; use dom::domtokenlist::DOMTokenList; use dom::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers}; +use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementHelpers}; use dom::htmlcollection::HTMLCollection; use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers}; use dom::htmlserializer::serialize; +use dom::htmltableelement::{HTMLTableElement, HTMLTableElementHelpers}; 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::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementHelpers}; +use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementHelpers}; +use dom::node::{CLICK_IN_PROGRESS, ElementNodeTypeId, LayoutNodeHelpers, Node, NodeHelpers}; +use dom::node::{NodeIterator, NodeStyleDamaged, OtherNodeDamage, document_from_node}; +use dom::node::{window_from_node}; use dom::nodelist::NodeList; use dom::virtualmethods::{VirtualMethods, vtable_for}; use devtools_traits::AttrInfo; -use style::{IntegerAttribute, LengthAttribute, SizeIntegerAttribute, WidthLengthAttribute}; -use style::{matches, parse_selector_list_from_str}; -use style; +use style::{mod, AuthorOrigin, BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute}; +use style::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute, ParserContext}; +use style::{SimpleColorAttribute, SizeIntegerAttribute, UnsignedIntegerAttribute}; +use style::{WidthLengthAttribute, matches}; use servo_util::namespace; use servo_util::str::{DOMString, LengthOrPercentageOrAuto}; +use cssparser::RGBA; use std::ascii::AsciiExt; use std::cell::{Ref, RefMut}; use std::default::Default; @@ -200,6 +211,10 @@ pub trait RawLayoutElementHelpers { unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute) -> Option<i32>; unsafe fn get_checked_state_for_layout(&self) -> bool; + unsafe fn get_unsigned_integer_attribute_for_layout(&self, attribute: UnsignedIntegerAttribute) + -> Option<u32>; + unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute) + -> Option<RGBA>; fn local_name<'a>(&'a self) -> &'a Atom; fn namespace<'a>(&'a self) -> &'a Namespace; fn style_attribute<'a>(&'a self) -> &'a DOMRefCell<Option<style::PropertyDeclarationBlock>>; @@ -284,11 +299,15 @@ impl RawLayoutElementHelpers for Element { -> LengthOrPercentageOrAuto { match length_attribute { WidthLengthAttribute => { - if !self.is_htmltablecellelement() { - panic!("I'm not a table cell!") + if self.is_htmltableelement() { + let this: &HTMLTableElement = mem::transmute(self); + this.get_width() + } else if self.is_htmltablecellelement() { + let this: &HTMLTableCellElement = mem::transmute(self); + this.get_width() + } else { + panic!("I'm not a table or table cell!") } - let this: &HTMLTableCellElement = mem::transmute(self); - this.get_width() } } } @@ -318,6 +337,61 @@ impl RawLayoutElementHelpers for Element { this.get_checked_state_for_layout() } + unsafe fn get_unsigned_integer_attribute_for_layout(&self, + attribute: UnsignedIntegerAttribute) + -> Option<u32> { + match attribute { + BorderUnsignedIntegerAttribute => { + if self.is_htmltableelement() { + let this: &HTMLTableElement = mem::transmute(self); + this.get_border() + } else { + // Don't panic since `:-servo-nonzero-border` can cause this to be called on + // arbitrary elements. + None + } + } + ColSpanUnsignedIntegerAttribute => { + if self.is_htmltablecellelement() { + let this: &HTMLTableCellElement = mem::transmute(self); + this.get_colspan() + } else { + // Don't panic since `display` can cause this to be called on arbitrary + // elements. + None + } + } + } + } + + #[inline] + #[allow(unrooted_must_root)] + unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute) + -> Option<RGBA> { + match attribute { + BgColorSimpleColorAttribute => { + if self.is_htmlbodyelement() { + let this: &HTMLBodyElement = mem::transmute(self); + this.get_background_color() + } else if self.is_htmltableelement() { + let this: &HTMLTableElement = mem::transmute(self); + this.get_background_color() + } else if self.is_htmltablecellelement() { + let this: &HTMLTableCellElement = mem::transmute(self); + this.get_background_color() + } else if self.is_htmltablerowelement() { + let this: &HTMLTableRowElement = mem::transmute(self); + this.get_background_color() + } else if self.is_htmltablesectionelement() { + let this: &HTMLTableSectionElement = mem::transmute(self); + this.get_background_color() + } else { + None + } + } + } + } + // Getters used in components/layout/wrapper.rs fn local_name<'a>(&'a self) -> &'a Atom { @@ -441,7 +515,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 +573,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 +618,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 +823,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 +850,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); @@ -962,7 +1020,10 @@ impl<'a> ElementMethods for JSRef<'a, Element> { // http://dom.spec.whatwg.org/#dom-element-matches fn Matches(self, selectors: DOMString) -> Fallible<bool> { - match parse_selector_list_from_str(selectors.as_slice()) { + let parser_context = ParserContext { + origin: AuthorOrigin, + }; + match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) { Err(()) => Err(Syntax), Ok(ref selectors) => { let root: JSRef<Node> = NodeCast::from_ref(self); @@ -999,25 +1060,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 +1112,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 { @@ -1190,6 +1298,17 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> { } } } + fn has_nonzero_border(self) -> bool { + match HTMLTableElementCast::to_ref(self) { + None => false, + Some(this) => { + match this.get_border() { + None | Some(0) => false, + Some(_) => true, + } + } + } + } } pub trait ActivationElementHelpers<'a> { diff --git a/components/script/dom/file.rs b/components/script/dom/file.rs index 52fb22abd12..985386321c3 100644 --- a/components/script/dom/file.rs +++ b/components/script/dom/file.rs @@ -14,22 +14,21 @@ use servo_util::str::DOMString; pub struct File { blob: Blob, name: DOMString, - type_: BlobType } impl File { - fn new_inherited(global: &GlobalRef, _file_bits: JSRef<Blob>, name: DOMString) -> File { + fn new_inherited(global: &GlobalRef, type_: BlobType, + _file_bits: JSRef<Blob>, name: DOMString) -> File { File { - blob: Blob::new_inherited(global, None), + blob: Blob::new_inherited(global, type_, None), name: name, - type_: FileTypeId } // XXXManishearth Once Blob is able to store data // the relevant subfields of file_bits should be copied over } pub fn new(global: &GlobalRef, file_bits: JSRef<Blob>, name: DOMString) -> Temporary<File> { - reflect_dom_object(box File::new_inherited(global, file_bits, name), + reflect_dom_object(box File::new_inherited(global, FileTypeId, file_bits, name), *global, FileBinding::Wrap) } diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs index d088a6b8626..917b02913a7 100644 --- a/components/script/dom/htmlbodyelement.rs +++ b/components/script/dom/htmlbodyelement.rs @@ -2,11 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use dom::attr::Attr; -use dom::attr::AttrHelpers; +use dom::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; -use dom::bindings::codegen::Bindings::HTMLBodyElementBinding; -use dom::bindings::codegen::Bindings::HTMLBodyElementBinding::HTMLBodyElementMethods; +use dom::bindings::codegen::Bindings::HTMLBodyElementBinding::{mod, HTMLBodyElementMethods}; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::EventTargetCast; use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCast}; @@ -19,11 +17,14 @@ use dom::htmlelement::HTMLElement; use dom::node::{Node, ElementNodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; -use servo_util::str::DOMString; +use cssparser::RGBA; +use servo_util::str::{mod, DOMString}; +use std::cell::Cell; #[dom_struct] pub struct HTMLBodyElement { - htmlelement: HTMLElement + htmlelement: HTMLElement, + background_color: Cell<Option<RGBA>>, } impl HTMLBodyElementDerived for EventTarget { @@ -33,14 +34,20 @@ impl HTMLBodyElementDerived for EventTarget { } impl HTMLBodyElement { - fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLBodyElement { + fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> HTMLBodyElement { HTMLBodyElement { - htmlelement: HTMLElement::new_inherited(HTMLBodyElementTypeId, localName, prefix, document) + htmlelement: HTMLElement::new_inherited(HTMLBodyElementTypeId, + localName, + prefix, + document), + background_color: Cell::new(None), } } #[allow(unrooted_must_root)] - pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLBodyElement> { + pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> Temporary<HTMLBodyElement> { let element = HTMLBodyElement::new_inherited(localName, prefix, document); Node::reflect_node(box element, document, HTMLBodyElementBinding::Wrap) } @@ -58,6 +65,16 @@ impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> { } } +pub trait HTMLBodyElementHelpers { + fn get_background_color(&self) -> Option<RGBA>; +} + +impl HTMLBodyElementHelpers for HTMLBodyElement { + fn get_background_color(&self) -> Option<RGBA> { + self.background_color.get() + } +} + impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> { fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> { let element: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self); @@ -91,6 +108,25 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> { name.slice_from(2), attr.value().as_slice().to_string()); } + + match attr.local_name() { + &atom!("bgcolor") => { + self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok()) + } + _ => {} + } + } + + fn before_remove_attr(&self, attr: JSRef<Attr>) { + match self.super_type() { + Some(ref s) => s.before_remove_attr(attr), + _ => {} + } + + match attr.local_name() { + &atom!("bgcolor") => self.background_color.set(None), + _ => {} + } } } diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 12175c9de9a..dc1ef7d6e26 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -15,7 +15,7 @@ use dom::element::{Element, HTMLImageElementTypeId}; use dom::element::AttributeHandlers; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; -use dom::node::{Node, ElementNodeTypeId, NodeHelpers, window_from_node}; +use dom::node::{Node, ElementNodeTypeId, NodeHelpers, OtherNodeDamage, window_from_node}; use dom::virtualmethods::VirtualMethods; use servo_net::image_cache_task; use servo_util::geometry::to_px; @@ -112,7 +112,14 @@ impl<'a> HTMLImageElementMethods for JSRef<'a, HTMLImageElement> { } fn Width(self) -> u32 { + // FIXME(pcwalton): This is a really nasty thing to do, but the interaction between the + // image cache task, the reflow messages that it sends to us via layout, and the image + // holders seem to just plain be racy, and this works around it by ensuring that we + // recreate the flow (picking up image changes on the way). The image cache task needs a + // rewrite to modern Rust. let node: JSRef<Node> = NodeCast::from_ref(self); + node.dirty(OtherNodeDamage); + let rect = node.get_bounding_content_box(); to_px(rect.size.width) as u32 } @@ -123,7 +130,14 @@ impl<'a> HTMLImageElementMethods for JSRef<'a, HTMLImageElement> { } fn Height(self) -> u32 { + // FIXME(pcwalton): This is a really nasty thing to do, but the interaction between the + // image cache task, the reflow messages that it sends to us via layout, and the image + // holders seem to just plain be racy, and this works around it by ensuring that we + // recreate the flow (picking up image changes on the way). The image cache task needs a + // rewrite to modern Rust. let node: JSRef<Node> = NodeCast::from_ref(self); + node.dirty(OtherNodeDamage); + let rect = node.get_bounding_content_box(); to_px(rect.size.height) as u32 } 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/htmltablecellelement.rs b/components/script/dom/htmltablecellelement.rs index cee07540301..9ca84b0e70f 100644 --- a/components/script/dom/htmltablecellelement.rs +++ b/components/script/dom/htmltablecellelement.rs @@ -2,8 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use dom::attr::Attr; -use dom::attr::AttrHelpers; +use dom::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCellElementDerived}; use dom::bindings::js::JSRef; use dom::bindings::utils::{Reflectable, Reflector}; @@ -15,13 +14,15 @@ use dom::htmlelement::HTMLElement; use dom::node::ElementNodeTypeId; use dom::virtualmethods::VirtualMethods; -use servo_util::str::{AutoLpa, DOMString, LengthOrPercentageOrAuto}; -use servo_util::str; +use cssparser::RGBA; +use servo_util::str::{mod, AutoLpa, DOMString, LengthOrPercentageOrAuto}; use std::cell::Cell; #[dom_struct] pub struct HTMLTableCellElement { htmlelement: HTMLElement, + background_color: Cell<Option<RGBA>>, + colspan: Cell<Option<u32>>, width: Cell<LengthOrPercentageOrAuto>, } @@ -36,10 +37,16 @@ impl HTMLTableCellElementDerived for EventTarget { } impl HTMLTableCellElement { - pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableCellElement { + pub fn new_inherited(type_id: ElementTypeId, + tag_name: DOMString, + prefix: Option<DOMString>, + document: JSRef<Document>) + -> HTMLTableCellElement { HTMLTableCellElement { htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document), - width: Cell::new(AutoLpa) + background_color: Cell::new(None), + colspan: Cell::new(None), + width: Cell::new(AutoLpa), } } @@ -50,10 +57,20 @@ impl HTMLTableCellElement { } pub trait HTMLTableCellElementHelpers { + fn get_background_color(&self) -> Option<RGBA>; + fn get_colspan(&self) -> Option<u32>; fn get_width(&self) -> LengthOrPercentageOrAuto; } impl HTMLTableCellElementHelpers for HTMLTableCellElement { + fn get_background_color(&self) -> Option<RGBA> { + self.background_color.get() + } + + fn get_colspan(&self) -> Option<u32> { + self.colspan.get() + } + fn get_width(&self) -> LengthOrPercentageOrAuto { self.width.get() } @@ -72,6 +89,12 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> { } match attr.local_name() { + &atom!("bgcolor") => { + self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok()) + } + &atom!("colspan") => { + self.colspan.set(str::parse_unsigned_integer(attr.value().as_slice().chars())); + } &atom!("width") => self.width.set(str::parse_length(attr.value().as_slice())), _ => () } @@ -84,6 +107,8 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> { } match attr.local_name() { + &atom!("bgcolor") => self.background_color.set(None), + &atom!("colspan") => self.colspan.set(None), &atom!("width") => self.width.set(AutoLpa), _ => () } diff --git a/components/script/dom/htmltableelement.rs b/components/script/dom/htmltableelement.rs index 7dddf98bdc4..d2c9e723b78 100644 --- a/components/script/dom/htmltableelement.rs +++ b/components/script/dom/htmltableelement.rs @@ -2,10 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use dom::bindings::codegen::Bindings::HTMLTableElementBinding; +use dom::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::Bindings::HTMLTableElementBinding::HTMLTableElementMethods; -use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast, HTMLTableCaptionElementCast}; +use dom::bindings::codegen::Bindings::HTMLTableElementBinding; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCaptionElementCast}; +use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast}; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::document::Document; @@ -14,11 +16,18 @@ use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::htmltablecaptionelement::HTMLTableCaptionElement; use dom::node::{Node, NodeHelpers, ElementNodeTypeId}; -use servo_util::str::DOMString; +use dom::virtualmethods::VirtualMethods; + +use cssparser::RGBA; +use servo_util::str::{mod, AutoLpa, DOMString, LengthOrPercentageOrAuto}; +use std::cell::Cell; #[dom_struct] pub struct HTMLTableElement { htmlelement: HTMLElement, + background_color: Cell<Option<RGBA>>, + border: Cell<Option<u32>>, + width: Cell<LengthOrPercentageOrAuto>, } impl HTMLTableElementDerived for EventTarget { @@ -28,14 +37,22 @@ impl HTMLTableElementDerived for EventTarget { } impl HTMLTableElement { - fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableElement { + fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> HTMLTableElement { HTMLTableElement { - htmlelement: HTMLElement::new_inherited(HTMLTableElementTypeId, localName, prefix, document) + htmlelement: HTMLElement::new_inherited(HTMLTableElementTypeId, + localName, + prefix, + document), + background_color: Cell::new(None), + border: Cell::new(None), + width: Cell::new(AutoLpa), } } #[allow(unrooted_must_root)] - pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableElement> { + pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> Temporary<HTMLTableElement> { let element = HTMLTableElement::new_inherited(localName, prefix, document); Node::reflect_node(box element, document, HTMLTableElementBinding::Wrap) } @@ -77,3 +94,66 @@ impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> { }); } } + +pub trait HTMLTableElementHelpers { + fn get_background_color(&self) -> Option<RGBA>; + fn get_border(&self) -> Option<u32>; + fn get_width(&self) -> LengthOrPercentageOrAuto; +} + +impl HTMLTableElementHelpers for HTMLTableElement { + fn get_background_color(&self) -> Option<RGBA> { + self.background_color.get() + } + + fn get_border(&self) -> Option<u32> { + self.border.get() + } + + fn get_width(&self) -> LengthOrPercentageOrAuto { + self.width.get() + } +} + +impl<'a> VirtualMethods for JSRef<'a, HTMLTableElement> { + fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> { + let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self); + Some(htmlelement as &VirtualMethods) + } + + fn after_set_attr(&self, attr: JSRef<Attr>) { + match self.super_type() { + Some(ref s) => s.after_set_attr(attr), + _ => () + } + + match attr.local_name() { + &atom!("bgcolor") => { + self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok()) + } + &atom!("border") => { + // According to HTML5 § 14.3.9, invalid values map to 1px. + self.border.set(Some(str::parse_unsigned_integer(attr.value() + .as_slice() + .chars()).unwrap_or(1))) + } + &atom!("width") => self.width.set(str::parse_length(attr.value().as_slice())), + _ => () + } + } + + fn before_remove_attr(&self, attr: JSRef<Attr>) { + match self.super_type() { + Some(ref s) => s.before_remove_attr(attr), + _ => () + } + + match attr.local_name() { + &atom!("bgcolor") => self.background_color.set(None), + &atom!("border") => self.border.set(None), + &atom!("width") => self.width.set(AutoLpa), + _ => () + } + } +} + diff --git a/components/script/dom/htmltablerowelement.rs b/components/script/dom/htmltablerowelement.rs index 3edf4a0e64f..500c7d74472 100644 --- a/components/script/dom/htmltablerowelement.rs +++ b/components/script/dom/htmltablerowelement.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::Bindings::HTMLTableRowElementBinding; -use dom::bindings::codegen::InheritTypes::HTMLTableRowElementDerived; +use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableRowElementDerived}; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::document::Document; @@ -11,11 +12,16 @@ use dom::element::HTMLTableRowElementTypeId; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::node::{Node, ElementNodeTypeId}; -use servo_util::str::DOMString; +use dom::virtualmethods::VirtualMethods; + +use cssparser::RGBA; +use servo_util::str::{mod, DOMString}; +use std::cell::Cell; #[dom_struct] pub struct HTMLTableRowElement { htmlelement: HTMLElement, + background_color: Cell<Option<RGBA>>, } impl HTMLTableRowElementDerived for EventTarget { @@ -25,9 +31,14 @@ impl HTMLTableRowElementDerived for EventTarget { } impl HTMLTableRowElement { - fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableRowElement { + fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> HTMLTableRowElement { HTMLTableRowElement { - htmlelement: HTMLElement::new_inherited(HTMLTableRowElementTypeId, localName, prefix, document) + htmlelement: HTMLElement::new_inherited(HTMLTableRowElementTypeId, + localName, + prefix, + document), + background_color: Cell::new(None), } } @@ -45,3 +56,47 @@ impl Reflectable for HTMLTableRowElement { self.htmlelement.reflector() } } + +pub trait HTMLTableRowElementHelpers { + fn get_background_color(&self) -> Option<RGBA>; +} + +impl HTMLTableRowElementHelpers for HTMLTableRowElement { + fn get_background_color(&self) -> Option<RGBA> { + self.background_color.get() + } +} + +impl<'a> VirtualMethods for JSRef<'a, HTMLTableRowElement> { + fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> { + let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self); + Some(htmlelement as &VirtualMethods) + } + + fn after_set_attr(&self, attr: JSRef<Attr>) { + match self.super_type() { + Some(ref s) => s.after_set_attr(attr), + _ => () + } + + match attr.local_name() { + &atom!("bgcolor") => { + self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok()) + } + _ => {} + } + } + + fn before_remove_attr(&self, attr: JSRef<Attr>) { + match self.super_type() { + Some(ref s) => s.before_remove_attr(attr), + _ => () + } + + match attr.local_name() { + &atom!("bgcolor") => self.background_color.set(None), + _ => {} + } + } +} + diff --git a/components/script/dom/htmltablesectionelement.rs b/components/script/dom/htmltablesectionelement.rs index d10e4b82b44..b191e21bde0 100644 --- a/components/script/dom/htmltablesectionelement.rs +++ b/components/script/dom/htmltablesectionelement.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::Bindings::HTMLTableSectionElementBinding; -use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementDerived; +use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableSectionElementDerived}; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::document::Document; @@ -11,11 +12,16 @@ use dom::element::HTMLTableSectionElementTypeId; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::node::{Node, ElementNodeTypeId}; -use servo_util::str::DOMString; +use dom::virtualmethods::VirtualMethods; + +use cssparser::RGBA; +use servo_util::str::{mod, DOMString}; +use std::cell::Cell; #[dom_struct] pub struct HTMLTableSectionElement { htmlelement: HTMLElement, + background_color: Cell<Option<RGBA>>, } impl HTMLTableSectionElementDerived for EventTarget { @@ -25,14 +31,20 @@ impl HTMLTableSectionElementDerived for EventTarget { } impl HTMLTableSectionElement { - fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableSectionElement { + fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> HTMLTableSectionElement { HTMLTableSectionElement { - htmlelement: HTMLElement::new_inherited(HTMLTableSectionElementTypeId, localName, prefix, document) + htmlelement: HTMLElement::new_inherited(HTMLTableSectionElementTypeId, + localName, + prefix, + document), + background_color: Cell::new(None), } } #[allow(unrooted_must_root)] - pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableSectionElement> { + pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) + -> Temporary<HTMLTableSectionElement> { let element = HTMLTableSectionElement::new_inherited(localName, prefix, document); Node::reflect_node(box element, document, HTMLTableSectionElementBinding::Wrap) } @@ -43,3 +55,47 @@ impl Reflectable for HTMLTableSectionElement { self.htmlelement.reflector() } } + +pub trait HTMLTableSectionElementHelpers { + fn get_background_color(&self) -> Option<RGBA>; +} + +impl HTMLTableSectionElementHelpers for HTMLTableSectionElement { + fn get_background_color(&self) -> Option<RGBA> { + self.background_color.get() + } +} + +impl<'a> VirtualMethods for JSRef<'a, HTMLTableSectionElement> { + fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> { + let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self); + Some(htmlelement as &VirtualMethods) + } + + fn after_set_attr(&self, attr: JSRef<Attr>) { + match self.super_type() { + Some(ref s) => s.after_set_attr(attr), + _ => () + } + + match attr.local_name() { + &atom!("bgcolor") => { + self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok()) + } + _ => {} + } + } + + fn before_remove_attr(&self, attr: JSRef<Attr>) { + match self.super_type() { + Some(ref s) => s.before_remove_attr(attr), + _ => () + } + + match attr.local_name() { + &atom!("bgcolor") => self.background_color.set(None), + _ => {} + } + } +} + 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..0871a266df3 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -50,7 +50,7 @@ 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, SelectorList}; +use style::{matches, AuthorOrigin, ParserContext, SelectorList}; use js::jsapi::{JSContext, JSObject, JSTracer, JSRuntime}; use js::jsfriendapi; @@ -60,8 +60,7 @@ use std::cell::{Cell, RefCell, Ref, RefMut}; use std::default::Default; use std::iter::{FilterMap, Peekable}; use std::mem; -use style; -use style::ComputedValues; +use style::{mod, ComputedValues}; use sync::Arc; use uuid; use string_cache::QualName; @@ -287,20 +286,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 +305,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 +345,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 +416,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 +446,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 +458,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 +643,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 @@ -750,7 +740,10 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { // http://dom.spec.whatwg.org/#dom-parentnode-queryselector fn query_selector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> { // Step 1. - match parse_selector_list_from_str(selectors.as_slice()) { + let parser_context = ParserContext { + origin: AuthorOrigin, + }; + match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) { // Step 2. Err(()) => return Err(Syntax), // Step 3. @@ -767,11 +760,15 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { /// Get an iterator over all nodes which match a set of selectors /// Be careful not to do anything which may manipulate the DOM tree whilst iterating, otherwise /// the iterator may be invalidated - unsafe fn query_selector_iter(self, selectors: DOMString) -> Fallible<QuerySelectorIterator<'a>> { + unsafe fn query_selector_iter(self, selectors: DOMString) + -> Fallible<QuerySelectorIterator<'a>> { // Step 1. let nodes; let root = self.ancestors().last().unwrap_or(self.clone()); - match parse_selector_list_from_str(selectors.as_slice()) { + let parser_context = ParserContext { + origin: AuthorOrigin, + }; + match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) { // Step 2. Err(()) => return Err(Syntax), // Step 3. @@ -830,11 +827,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 +1818,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 +2356,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/virtualmethods.rs b/components/script/dom/virtualmethods.rs index a90055bc50d..94a74124eca 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -22,7 +22,10 @@ use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast; use dom::bindings::codegen::InheritTypes::HTMLScriptElementCast; use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast; use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast; +use dom::bindings::codegen::InheritTypes::HTMLTableElementCast; use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast; +use dom::bindings::codegen::InheritTypes::HTMLTableRowElementCast; +use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementCast; use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast; use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast; use dom::bindings::js::JSRef; @@ -46,7 +49,10 @@ use dom::element::HTMLScriptElementTypeId; use dom::element::HTMLSelectElementTypeId; use dom::element::HTMLStyleElementTypeId; use dom::element::HTMLTableDataCellElementTypeId; +use dom::element::HTMLTableElementTypeId; use dom::element::HTMLTableHeaderCellElementTypeId; +use dom::element::HTMLTableRowElementTypeId; +use dom::element::HTMLTableSectionElementTypeId; use dom::element::HTMLTextAreaElementTypeId; use dom::element::HTMLTitleElementTypeId; use dom::event::Event; @@ -67,7 +73,10 @@ use dom::htmloptionelement::HTMLOptionElement; use dom::htmlscriptelement::HTMLScriptElement; use dom::htmlselectelement::HTMLSelectElement; use dom::htmlstyleelement::HTMLStyleElement; +use dom::htmltableelement::HTMLTableElement; use dom::htmltablecellelement::HTMLTableCellElement; +use dom::htmltablerowelement::HTMLTableRowElement; +use dom::htmltablesectionelement::HTMLTableSectionElement; use dom::htmltextareaelement::HTMLTextAreaElement; use dom::htmltitleelement::HTMLTitleElement; use dom::node::{Node, NodeHelpers, ElementNodeTypeId, CloneChildrenFlag}; @@ -226,9 +235,25 @@ pub fn vtable_for<'a>(node: &'a JSRef<'a, Node>) -> &'a VirtualMethods + 'a { let element: &'a JSRef<'a, HTMLStyleElement> = HTMLStyleElementCast::to_borrowed_ref(node).unwrap(); element as &'a VirtualMethods + 'a } + ElementNodeTypeId(HTMLTableElementTypeId) => { + let element: &'a JSRef<'a, HTMLTableElement> = + HTMLTableElementCast::to_borrowed_ref(node).unwrap(); + element as &'a VirtualMethods + 'a + } ElementNodeTypeId(HTMLTableDataCellElementTypeId) | ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => { - let element: &'a JSRef<'a, HTMLTableCellElement> = HTMLTableCellElementCast::to_borrowed_ref(node).unwrap(); + let element: &'a JSRef<'a, HTMLTableCellElement> = + HTMLTableCellElementCast::to_borrowed_ref(node).unwrap(); + element as &'a VirtualMethods + 'a + } + ElementNodeTypeId(HTMLTableRowElementTypeId) => { + let element: &'a JSRef<'a, HTMLTableRowElement> = + HTMLTableRowElementCast::to_borrowed_ref(node).unwrap(); + element as &'a VirtualMethods + 'a + } + ElementNodeTypeId(HTMLTableSectionElementTypeId) => { + let element: &'a JSRef<'a, HTMLTableSectionElement> = + HTMLTableSectionElementCast::to_borrowed_ref(node).unwrap(); element as &'a VirtualMethods + 'a } ElementNodeTypeId(HTMLTextAreaElementTypeId) => { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 2a245ea228a..be5d8ad35b2 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); } } diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index c3afb15c057..0f3f4fbacbf 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -29,6 +29,9 @@ pub enum Msg { /// Adds the given stylesheet to the document. LoadStylesheetMsg(Url), + /// Puts a document into quirks mode, causing the quirks mode stylesheet to be loaded. + SetQuirksModeMsg, + /// Requests a reflow. ReflowMsg(Box<Reflow>), diff --git a/components/script/page.rs b/components/script/page.rs index d634e86a84a..c5b1f74c73a 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -13,9 +13,9 @@ use dom::node::{Node, NodeHelpers}; use dom::window::Window; use layout_interface::{ ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery, ContentBoxesResponse, - GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, NoQuery, - Reflow, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg, - ReflowQueryType, TrustedNodeAddress + GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, Reflow, + ReflowForScriptQuery, ReflowGoal, ReflowMsg, ReflowQueryType, + TrustedNodeAddress }; use script_traits::{UntrustedNodeAddress, ScriptControlChan}; @@ -29,7 +29,7 @@ use servo_net::storage_task::StorageTask; use servo_util::geometry::{Au, MAX_RECT}; use servo_util::geometry; use servo_util::str::DOMString; -use servo_util::smallvec::{SmallVec1, SmallVec}; +use servo_util::smallvec::SmallVec; use std::cell::{Cell, Ref, RefMut}; use std::comm::{channel, Receiver, Empty, Disconnected}; use std::mem::replace; @@ -77,9 +77,6 @@ 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: DOMRefCell<SmallVec1<UntrustedNodeAddress>>, - /// Pending scroll to fragment event, if any pub fragment_name: DOMRefCell<Option<String>>, @@ -95,15 +92,6 @@ pub struct Page { // Child Pages. pub children: DOMRefCell<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>, - - /// Number of unnecessary potential reflows that were skipped since the last reflow - pub avoided_reflows: Cell<int>, - /// An enlarged rectangle around the page contents visible in the viewport, used /// to prevent creating display list items for content that is far away from the viewport. pub page_clip_rect: Cell<Rect<Au>>, @@ -165,57 +153,35 @@ impl Page { url: DOMRefCell::new(None), next_subpage_id: Cell::new(SubpageId(0)), resize_event: Cell::new(None), - pending_dirty_nodes: DOMRefCell::new(SmallVec1::new()), fragment_name: DOMRefCell::new(None), last_reflow_id: Cell::new(0), resource_task: resource_task, storage_task: storage_task, constellation_chan: constellation_chan, children: DOMRefCell::new(vec!()), - damaged: Cell::new(false), - pending_reflows: Cell::new(0), - avoided_reflows: Cell::new(0), page_clip_rect: Cell::new(MAX_RECT), } } - pub fn flush_layout(&self, query: ReflowQueryType) { - // If we are damaged, we need to force a full reflow, so that queries interact with - // an accurate flow tree. - let (reflow_goal, force_reflow) = if self.damaged.get() { - (ReflowForDisplay, true) - } else { - match query { - ContentBoxQuery(_) | ContentBoxesQuery(_) => (ReflowForScriptQuery, true), - NoQuery => (ReflowForDisplay, false), - } - }; - - if force_reflow { - let frame = self.frame(); - let window = frame.as_ref().unwrap().window.root(); - self.reflow(reflow_goal, window.control_chan().clone(), &mut **window.compositor(), query); - } else { - self.avoided_reflows.set(self.avoided_reflows.get() + 1); - } + pub fn flush_layout(&self, goal: ReflowGoal, query: ReflowQueryType) { + let frame = self.frame(); + let window = frame.as_ref().unwrap().window.root(); + self.reflow(goal, window.control_chan().clone(), &mut **window.compositor(), query); } - pub fn layout(&self) -> &LayoutRPC { - self.flush_layout(NoQuery); - self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? - let layout_rpc: &LayoutRPC = &*self.layout_rpc; - layout_rpc + pub fn layout(&self) -> &LayoutRPC { + &*self.layout_rpc } pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> { - self.flush_layout(ContentBoxQuery(content_box_request)); + self.flush_layout(ReflowForScriptQuery, ContentBoxQuery(content_box_request)); self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? let ContentBoxResponse(rect) = self.layout_rpc.content_box(); rect } pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> { - self.flush_layout(ContentBoxesQuery(content_boxes_request)); + self.flush_layout(ReflowForScriptQuery, ContentBoxesQuery(content_boxes_request)); self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); rects @@ -276,6 +242,13 @@ impl Page { } } } + + pub fn dirty_all_nodes(&self) { + match *self.frame.borrow() { + None => {} + Some(ref frame) => frame.document.root().dirty_all_nodes(), + } + } } impl Iterator<Rc<Page>> for PageIterator { @@ -334,7 +307,7 @@ impl Page { /// Sends a ping to layout and waits for the response. The response will arrive when the /// layout task has finished any pending request messages. - pub fn join_layout(&self) { + fn join_layout(&self) { let mut layout_join_port = self.layout_join_port.borrow_mut(); if layout_join_port.is_some() { let join_port = replace(&mut *layout_join_port, None); @@ -358,11 +331,11 @@ impl Page { } } - /// Reflows the page if it's possible to do so. This method will wait until the layout task has - /// completed its current action, join the layout task, and then request a new layout run. It - /// won't wait for the new layout computation to finish. + /// Reflows the page if it's possible to do so and the page is dirty. This method will wait + /// for the layout thread to complete (but see the `TODO` below). If there is no window size + /// yet, the page is presumed invisible and no reflow is performed. /// - /// If there is no window size yet, the page is presumed invisible and no reflow is performed. + /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. pub fn reflow(&self, goal: ReflowGoal, script_chan: ScriptControlChan, @@ -375,54 +348,54 @@ impl Page { } }; - match root.root() { - None => {}, - Some(root) => { - debug!("avoided {:d} reflows", self.avoided_reflows.get()); - self.avoided_reflows.set(0); - - debug!("script: performing reflow for goal {}", goal); + let root = match root.root() { + None => return, + Some(root) => root, + }; - // Now, join the layout so that they will see the latest changes we have made. - self.join_layout(); + debug!("script: performing reflow for goal {}", goal); - // Layout will let us know when it's done. - let (join_chan, join_port) = channel(); - let mut layout_join_port = self.layout_join_port.borrow_mut(); - *layout_join_port = Some(join_port); + let root: JSRef<Node> = NodeCast::from_ref(*root); + if !root.get_has_dirty_descendants() { + debug!("root has no dirty descendants; avoiding reflow"); + return + } - let last_reflow_id = &self.last_reflow_id; - last_reflow_id.set(last_reflow_id.get() + 1); + debug!("script: performing reflow for goal {}", goal); - let root: JSRef<Node> = NodeCast::from_ref(*root); + // Layout will let us know when it's done. + let (join_chan, join_port) = channel(); - let window_size = self.window_size.get(); - self.damaged.set(false); + { + let mut layout_join_port = self.layout_join_port.borrow_mut(); + *layout_join_port = Some(join_port); + } - // Send new document and relevant styles to layout. - let reflow = box Reflow { - document_root: root.to_trusted_node_address(), - url: self.get_url(), - iframe: self.subpage_id.is_some(), - goal: goal, - window_size: window_size, - script_chan: script_chan, - script_join_chan: join_chan, - id: last_reflow_id.get(), - query_type: query_type, - page_clip_rect: self.page_clip_rect.get(), - }; + let last_reflow_id = &self.last_reflow_id; + last_reflow_id.set(last_reflow_id.get() + 1); + + let window_size = self.window_size.get(); + + // Send new document and relevant styles to layout. + let reflow = box Reflow { + document_root: root.to_trusted_node_address(), + url: self.get_url(), + iframe: self.subpage_id.is_some(), + goal: goal, + window_size: window_size, + script_chan: script_chan, + script_join_chan: join_chan, + id: last_reflow_id.get(), + query_type: query_type, + page_clip_rect: self.page_clip_rect.get(), + }; - let LayoutChan(ref chan) = self.layout_chan; - chan.send(ReflowMsg(reflow)); + let LayoutChan(ref chan) = self.layout_chan; + chan.send(ReflowMsg(reflow)); - debug!("script: layout forked") - } - } - } + debug!("script: layout forked"); - pub fn damage(&self) { - self.damaged.set(true); + self.join_layout(); } /// Attempt to find a named element in this page's document. diff --git a/components/script/script_task.rs b/components/script/script_task.rs index d44bec8e268..9a20afe4a7b 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -23,8 +23,7 @@ use dom::event::{Event, EventHelpers, Bubbles, DoesNotBubble, Cancelable, NotCan use dom::uievent::UIEvent; use dom::eventtarget::{EventTarget, EventTargetHelpers}; use dom::keyboardevent::KeyboardEvent; -use dom::node; -use dom::node::{ElementNodeTypeId, Node, NodeHelpers}; +use dom::node::{mod, ElementNodeTypeId, Node, NodeHelpers, OtherNodeDamage}; use dom::window::{Window, WindowHelpers}; use dom::worker::{Worker, TrustedWorkerAddress}; use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress}; @@ -40,10 +39,9 @@ use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS, GetDocumentElement}; use devtools_traits::{GetChildren, GetLayout, ModifyAttribute}; use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent}; use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory}; -use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, ViewportMsg, SendEventMsg}; +use script_traits::{ResizeMsg, AttachLayoutMsg, GetTitleMsg, KeyEvent, LoadMsg, ViewportMsg}; use script_traits::{ResizeInactiveMsg, ExitPipelineMsg, NewLayoutInfo, OpaqueScriptLayoutChannel}; -use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress, KeyEvent}; -use script_traits::{GetTitleMsg}; +use script_traits::{ScriptControlChan, ReflowCompleteMsg, SendEventMsg}; use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading, PerformingLayout}; use servo_msg::compositor_msg::{ScriptListener}; use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg}; @@ -57,7 +55,7 @@ use servo_net::resource_task::{ResourceTask, Load}; use servo_net::resource_task::LoadData as NetLoadData; use servo_net::storage_task::StorageTask; use servo_util::geometry::to_frac_px; -use servo_util::smallvec::{SmallVec1, SmallVec}; +use servo_util::smallvec::SmallVec; use servo_util::task::spawn_named_with_send_on_failure; use servo_util::task_state; @@ -74,7 +72,6 @@ use url::Url; use libc::size_t; use std::any::{Any, AnyRefExt}; -use std::collections::HashSet; use std::comm::{channel, Sender, Receiver, Select}; use std::fmt::{mod, Show}; use std::mem::replace; @@ -480,8 +477,6 @@ impl ScriptTask { } }; - let mut needs_reflow = HashSet::new(); - // Squash any pending resize and reflow events in the queue. loop { match event { @@ -496,18 +491,12 @@ 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 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); - } FromConstellation(ViewportMsg(id, rect)) => { let page = self.page.borrow_mut(); let inner_page = page.find(id).expect("Page rect message sent to nonexistent pipeline"); if inner_page.set_page_clip_rect_with_new_viewport(rect) { - needs_reflow.insert(id); + let page = get_page(&*self.page.borrow(), id); + self.force_reflow(&*page); } } _ => { @@ -541,11 +530,6 @@ impl ScriptTask { } } - // Now process any pending reflows. - for id in needs_reflow.into_iter() { - self.handle_event(id, ReflowEvent(SmallVec1::new())); - } - true } @@ -666,11 +650,6 @@ impl ScriptTask { } self.compositor.borrow_mut().set_ready_state(pipeline_id, FinishedLoading); - - if page.pending_reflows.get() > 0 { - page.pending_reflows.set(0); - self.force_reflow(&*page); - } } /// Handles a navigate forward or backward message. @@ -838,8 +817,12 @@ impl ScriptTask { // Kick off the initial reflow of the page. debug!("kicking off initial reflow of {}", final_url); - document.content_changed(NodeCast::from_ref(*document)); - window.flush_layout(); + { + let document_js_ref = (&*document).clone(); + let document_as_node = NodeCast::from_ref(document_js_ref); + document.content_changed(document_as_node, OtherNodeDamage); + } + window.flush_layout(ReflowForDisplay, NoQuery); { // No more reflow required @@ -895,18 +878,9 @@ impl ScriptTask { self.compositor.borrow_mut().scroll_fragment_point(pipeline_id, LayerId::null(), point); } + /// Reflows non-incrementally. 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.dirty_all_nodes(); page.reflow(ReflowForDisplay, self.control_chan.clone(), &mut **self.compositor.borrow_mut(), @@ -922,9 +896,27 @@ impl ScriptTask { self.handle_resize_event(pipeline_id, new_size); } - // FIXME(pcwalton): This reflows the entire document and is not incremental-y. - ReflowEvent(to_dirty) => { - self.handle_reflow_event(pipeline_id, to_dirty); + ReflowEvent(nodes) => { + // FIXME(pcwalton): This event seems to only be used by the image cache task, and + // the interaction between it and the image holder is really racy. I think that, in + // order to fix this race, we need to rewrite the image cache task to make the + // image holder responsible for the lifecycle of image loading instead of having + // the image holder and layout task both be observers. Then we can have the DOM + // image element observe the state of the image holder and have it send reflows + // via the normal dirtying mechanism, and ultimately remove this event. + // + // See the implementation of `Width()` and `Height()` in `HTMLImageElement` for + // fallout of this problem. + for node in nodes.iter() { + let node_to_dirty = node::from_untrusted_node_address(self.js_runtime.ptr, + *node).root(); + let page = get_page(&*self.page.borrow(), pipeline_id); + let frame = page.frame(); + let document = frame.as_ref().unwrap().document.root(); + document.content_changed(*node_to_dirty, OtherNodeDamage); + } + + self.handle_reflow_event(pipeline_id); } ClickEvent(_button, point) => { @@ -1020,7 +1012,7 @@ impl ScriptTask { _ => () } - window.flush_layout(); + window.flush_layout(ReflowForDisplay, NoQuery); } /// The entry point for content to notify that a new load has been requested @@ -1084,18 +1076,12 @@ impl ScriptTask { } } - fn handle_reflow_event(&self, pipeline_id: PipelineId, to_dirty: SmallVec1<UntrustedNodeAddress>) { + fn handle_reflow_event(&self, pipeline_id: PipelineId) { 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() { - let in_layout = page.layout_join_port.borrow().is_some(); - if in_layout { - page.pending_reflows.set(page.pending_reflows.get() + 1); - } else { - self.force_reflow(&*page); - } + self.force_reflow(&*page); } } @@ -1138,7 +1124,7 @@ impl ScriptTask { el.authentic_click_activation(*event); doc.commit_focus_transaction(); - window.flush_layout(); + window.flush_layout(ReflowForDisplay, NoQuery); } None => {} } @@ -1220,8 +1206,6 @@ impl ScriptTask { /// Shuts down layout for the given page tree. fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime) { for page in page_tree.iter() { - page.join_layout(); - // Tell the layout task to begin shutting down, and wait until it // processed this message. let (response_chan, response_port) = channel(); |