diff --git a/src/components/main/css/node_util.rs b/src/components/main/css/node_util.rs index d6caaad5f01..f78f0c7be60 100644 --- a/src/components/main/css/node_util.rs +++ b/src/components/main/css/node_util.rs @@ -5,7 +5,7 @@ use layout::incremental::RestyleDamage; use layout::util::LayoutDataAccess; use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode}; - +use layout::wrapper::{After, AfterBlock, Before, BeforeBlock, Normal}; use std::cast; use style::ComputedValues; use sync::Arc; @@ -25,13 +25,35 @@ impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> { fn get_css_select_results<'a>(&'a self) -> &'a Arc { unsafe { let layout_data_ref = self.borrow_layout_data(); - cast::transmute_region(layout_data_ref.get() - .as_ref() - .unwrap() - .data - .style - .as_ref() - .unwrap()) + match self.get_element_type() { + Before | BeforeBlock => { + return cast::transmute_region(layout_data_ref.get() + .as_ref() + .unwrap() + .data + .before_style + .as_ref() + .unwrap()) + } + After | AfterBlock => { + return cast::transmute_region(layout_data_ref.get() + .as_ref() + .unwrap() + .data + .after_style + .as_ref() + .unwrap()) + } + Normal => { + return cast::transmute_region(layout_data_ref.get() + .as_ref() + .unwrap() + .data + .style + .as_ref() + .unwrap()) + } + } } } diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs index 7d786e8fb77..f3ac77dab47 100644 --- a/src/components/main/layout/construct.rs +++ b/src/components/main/layout/construct.rs @@ -293,21 +293,28 @@ impl<'a> FlowConstructor<'a> { } /// Builds specific `Box` info for the given node. - pub fn build_specific_box_info_for_node(&mut self, node: &ThreadSafeLayoutNode) + pub fn build_specific_box_info_for_node(&mut self, + node: &ThreadSafeLayoutNode, + sub_box_kind: SubBoxKind) -> SpecificBoxInfo { match node.type_id() { - ElementNodeTypeId(HTMLImageElementTypeId) => self.build_box_info_for_image(node, node.image_url()), + ElementNodeTypeId(HTMLImageElementTypeId) => { + self.build_box_info_for_image(node, node.image_url()) + } ElementNodeTypeId(HTMLIFrameElementTypeId) => IframeBox(IframeBoxInfo::new(node)), ElementNodeTypeId(HTMLObjectElementTypeId) => { let data = node.get_object_data(&self.layout_context.url); self.build_box_info_for_image(node, data) } ElementNodeTypeId(HTMLTableElementTypeId) => TableWrapperBox, - ElementNodeTypeId(HTMLTableColElementTypeId) => TableColumnBox(TableColumnBoxInfo::new(node)), + ElementNodeTypeId(HTMLTableColElementTypeId) => { + TableColumnBox(TableColumnBoxInfo::new(node)) + } ElementNodeTypeId(HTMLTableDataCellElementTypeId) | ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => TableCellBox, ElementNodeTypeId(HTMLTableRowElementTypeId) | ElementNodeTypeId(HTMLTableSectionElementTypeId) => TableRowBox, + ElementNodeTypeId(HTMLPseudoElementTypeId) | TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(node)), _ => GenericBox, } @@ -352,6 +359,115 @@ impl<'a> FlowConstructor<'a> { } } + fn build_block_flow_using_children_construction_result(&mut self, + flow: &mut ~Flow, + consecutive_siblings: &mut ~[~Flow], + node: &ThreadSafeLayoutNode, + kid: ThreadSafeLayoutNode, + opt_boxes_for_inline_flow: &mut Option<~[Box]>, + abs_descendants: &mut Descendants, + first_box: &mut bool) { + match kid.swap_out_construction_result() { + NoConstructionResult => {} + FlowConstructionResult(kid_flow, kid_abs_descendants) => { + // If kid_flow is TableCaptionFlow, kid_flow should be added under + // TableWrapperFlow. + if flow.is_table() && kid_flow.is_table_caption() { + kid.set_flow_construction_result(FlowConstructionResult( + kid_flow, + Descendants::new())) + } else if flow.need_anonymous_flow(kid_flow) { + consecutive_siblings.push(kid_flow) + } else { + // Strip ignorable whitespace from the start of this flow per CSS 2.1 § + // 9.2.1.1. + if flow.is_table_kind() || *first_box { + strip_ignorable_whitespace_from_start(opt_boxes_for_inline_flow); + *first_box = false + } + + // Flush any inline boxes that we were gathering up. This allows us to handle + // {ib} splits. + debug!("flushing {} inline box(es) to flow A", + opt_boxes_for_inline_flow.as_ref() + .map_or(0, |boxes| boxes.len())); + self.flush_inline_boxes_to_flow_or_list_if_necessary( + opt_boxes_for_inline_flow, + flow, + consecutive_siblings, + node); + if !consecutive_siblings.is_empty() { + let consecutive_siblings = mem::replace(consecutive_siblings, ~[]); + self.generate_anonymous_missing_child(consecutive_siblings, + flow, + node); + } + flow.add_new_child(kid_flow); + } + abs_descendants.push_descendants(kid_abs_descendants); + } + ConstructionItemConstructionResult(InlineBoxesConstructionItem( + InlineBoxesConstructionResult { + splits: opt_splits, + boxes: boxes, + abs_descendants: kid_abs_descendants, + })) => { + // Add any {ib} splits. + match opt_splits { + None => {} + Some(splits) => { + for split in splits.move_iter() { + // Pull apart the {ib} split object and push its predecessor boxes + // onto the list. + let InlineBlockSplit { + predecessor_boxes: predecessor_boxes, + flow: kid_flow + } = split; + opt_boxes_for_inline_flow.push_all_move(predecessor_boxes); + + // If this is the first box in flow, then strip ignorable + // whitespace per CSS 2.1 § 9.2.1.1. + if *first_box { + strip_ignorable_whitespace_from_start( + opt_boxes_for_inline_flow); + *first_box = false + } + + // Flush any inline boxes that we were gathering up. + debug!("flushing {} inline box(es) to flow A", + opt_boxes_for_inline_flow.as_ref() + .map_or(0, |boxes| boxes.len())); + self.flush_inline_boxes_to_flow_or_list_if_necessary( + opt_boxes_for_inline_flow, + flow, + consecutive_siblings, + node); + + // Push the flow generated by the {ib} split onto our list of + // flows. + if flow.need_anonymous_flow(kid_flow) { + consecutive_siblings.push(kid_flow) + } else { + flow.add_new_child(kid_flow) + } + } + } + } + + // Add the boxes to the list we're maintaining. + opt_boxes_for_inline_flow.push_all_move(boxes); + abs_descendants.push_descendants(kid_abs_descendants); + } + ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => { + // Nothing to do here. + } + ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => { + // TODO: Implement anonymous table objects for missing parents + // CSS 2.1 § 17.2.1, step 3-2 + } + } + } + /// Build block flow for current node using information from children nodes. /// /// Consume results from children and combine them, handling {ib} splits. @@ -367,109 +483,22 @@ impl<'a> FlowConstructor<'a> { let mut opt_boxes_for_inline_flow = None; let mut consecutive_siblings = ~[]; let mut first_box = true; + // List of absolute descendants, in tree order. let mut abs_descendants = Descendants::new(); - let mut fixed_descendants = Descendants::new(); for kid in node.children() { - match kid.swap_out_construction_result() { - NoConstructionResult => {} - FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => { - // If kid_flow is TableCaptionFlow, kid_flow should be added under TableWrapperFlow. - if flow.is_table() && kid_flow.is_table_caption() { - kid.set_flow_construction_result(FlowConstructionResult(kid_flow, - Descendants::new(), - Descendants::new())) - } else if flow.need_anonymous_flow(kid_flow) { - consecutive_siblings.push(kid_flow) - } else { - // Strip ignorable whitespace from the start of this flow per CSS 2.1 § - // 9.2.1.1. - if flow.is_table_kind() || first_box { - strip_ignorable_whitespace_from_start(&mut opt_boxes_for_inline_flow); - first_box = false - } + if kid.get_element_type() != Normal { + self.process(&kid); + } - // Flush any inline boxes that we were gathering up. This allows us to handle - // {ib} splits. - debug!("flushing {} inline box(es) to flow A", - opt_boxes_for_inline_flow.as_ref() - .map_or(0, |boxes| boxes.len())); - self.flush_inline_boxes_to_flow_or_list_if_necessary(&mut opt_boxes_for_inline_flow, - &mut flow, - &mut consecutive_siblings, - node); - if !consecutive_siblings.is_empty() { - self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node); - consecutive_siblings = ~[]; - } - flow.add_new_child(kid_flow); - } - abs_descendants.push_descendants(kid_abs_descendants); - fixed_descendants.push_descendants(kid_fixed_descendants); - } - ConstructionItemConstructionResult(InlineBoxesConstructionItem( - InlineBoxesConstructionResult { - splits: opt_splits, - boxes: boxes, - abs_descendants: kid_abs_descendants, - fixed_descendants: kid_fixed_descendants, - })) => { - // Add any {ib} splits. - match opt_splits { - None => {} - Some(splits) => { - for split in splits.move_iter() { - // Pull apart the {ib} split object and push its predecessor boxes - // onto the list. - let InlineBlockSplit { - predecessor_boxes: predecessor_boxes, - flow: kid_flow - } = split; - opt_boxes_for_inline_flow.push_all_move(predecessor_boxes); - - // If this is the first box in flow, then strip ignorable - // whitespace per CSS 2.1 § 9.2.1.1. - if first_box { - strip_ignorable_whitespace_from_start( - &mut opt_boxes_for_inline_flow); - first_box = false - } - - // Flush any inline boxes that we were gathering up. - debug!("flushing {} inline box(es) to flow A", - opt_boxes_for_inline_flow.as_ref() - .map_or(0, - |boxes| boxes.len())); - self.flush_inline_boxes_to_flow_or_list_if_necessary( - &mut opt_boxes_for_inline_flow, - &mut flow, - &mut consecutive_siblings, - node); - - // Push the flow generated by the {ib} split onto our list of - // flows. - if flow.need_anonymous_flow(kid_flow) { - consecutive_siblings.push(kid_flow) - } else { - flow.add_new_child(kid_flow) - } - } - } - } - - // Add the boxes to the list we're maintaining. - opt_boxes_for_inline_flow.push_all_move(boxes); - abs_descendants.push_descendants(kid_abs_descendants); - fixed_descendants.push_descendants(kid_fixed_descendants); - } - ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => { - // Nothing to do here. - } - ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => { - // TODO: Implement anonymous table objects for missing parents - // CSS 2.1 § 17.2.1, step 3-2 - } - } + self.build_block_flow_using_children_construction_result( + &mut flow, + &mut consecutive_siblings, + node, + kid, + &mut opt_boxes_for_inline_flow, + &mut abs_descendants, + &mut first_box); } // Perform a final flush of any inline boxes that we were gathering up to handle {ib} @@ -1090,7 +1119,17 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { fn set_flow_construction_result(&self, result: ConstructionResult) { let mut layout_data_ref = self.mutate_layout_data(); match *layout_data_ref.get() { - Some(ref mut layout_data) => layout_data.data.flow_construction_result = result, + Some(ref mut layout_data) =>{ + match self.get_element_type() { + Before | BeforeBlock => { + layout_data.data.before_flow_construction_result = result + }, + After | AfterBlock => { + layout_data.data.after_flow_construction_result = result + }, + Normal => layout_data.data.flow_construction_result = result, + } + }, None => fail!("no layout data"), } } @@ -1100,7 +1139,15 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { let mut layout_data_ref = self.mutate_layout_data(); match *layout_data_ref.get() { Some(ref mut layout_data) => { - mem::replace(&mut layout_data.data.flow_construction_result, NoConstructionResult) + match self.get_element_type() { + Before | BeforeBlock => { + return mem::replace(&mut layout_data.data.before_flow_construction_result, NoConstructionResult) + }, + After | AfterBlock => { + return mem::replace(&mut layout_data.data.after_flow_construction_result, NoConstructionResult) + }, + Normal => { return mem::replace(&mut layout_data.data.flow_construction_result, NoConstructionResult) }, + } } None => fail!("no layout data"), } diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs index 60969ad19d7..9190668b22b 100644 --- a/src/components/main/layout/util.rs +++ b/src/components/main/layout/util.rs @@ -7,6 +7,7 @@ use layout::construct::{ConstructionResult, NoConstructionResult}; use layout::parallel::DomParallelInfo; use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; +use gfx::display_list::OpaqueNode; use script::dom::bindings::js::JS; use script::dom::bindings::utils::Reflectable; use script::dom::node::Node; @@ -146,6 +147,10 @@ pub struct PrivateLayoutData { /// `ConstructionItem`. See comments in `construct.rs` for more details. flow_construction_result: ConstructionResult, + before_flow_construction_result: ConstructionResult, + + after_flow_construction_result: ConstructionResult, + /// Information needed during parallel traversals. parallel: DomParallelInfo, } @@ -159,6 +164,8 @@ impl PrivateLayoutData { after_style: None, restyle_damage: None, flow_construction_result: NoConstructionResult, + before_flow_construction_result: NoConstructionResult, + after_flow_construction_result: NoConstructionResult, parallel: DomParallelInfo::new(), } } @@ -200,25 +207,32 @@ impl<'ln> LayoutDataAccess for LayoutNode<'ln> { } } -/// An opaque handle to a node. The only safe operation that can be performed on this node is to -/// compare it to another opaque handle or to another node. -/// -/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout -/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for -/// locality reasons. Using `OpaqueNode` enforces this invariant. -#[deriving(Clone, Eq)] -pub struct OpaqueNode(uintptr_t); - -impl OpaqueNode { +pub trait OpaqueNodeMethods { /// Converts a DOM node (layout view) to an `OpaqueNode`. - pub fn from_layout_node(node: &LayoutNode) -> OpaqueNode { + fn from_layout_node(node: &LayoutNode) -> Self; + + /// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`. + fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> Self; + + /// Converts a DOM node (script view) to an `OpaqueNode`. + fn from_script_node(node: TrustedNodeAddress) -> Self; + + /// Converts a DOM node to an `OpaqueNode'. + fn from_jsmanaged(node: &JS) -> Self; + + /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type + /// of node that script expects to receive in a hit test. + fn to_untrusted_node_address(&self) -> UntrustedNodeAddress; +} + +impl OpaqueNodeMethods for OpaqueNode { + fn from_layout_node(node: &LayoutNode) -> OpaqueNode { unsafe { - OpaqueNode::from_jsmanaged(node.get_jsmanaged()) + OpaqueNodeMethods::from_jsmanaged(node.get_jsmanaged()) } } - /// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`. - pub fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode { + fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode { unsafe { let abstract_node = node.get_jsmanaged(); let ptr: uintptr_t = cast::transmute(abstract_node.reflector().get_jsobject()); @@ -226,14 +240,12 @@ impl OpaqueNode { } } - /// Converts a DOM node (script view) to an `OpaqueNode`. - pub fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode { + fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode { unsafe { - OpaqueNode::from_jsmanaged(&JS::from_trusted_node_address(node)) + OpaqueNodeMethods::from_jsmanaged(&JS::from_trusted_node_address(node)) } } - /// Converts a DOM node to an `OpaqueNode'. fn from_jsmanaged(node: &JS) -> OpaqueNode { unsafe { let ptr: uintptr_t = cast::transmute(node.reflector().get_jsobject()); @@ -241,9 +253,7 @@ impl OpaqueNode { } } - /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type - /// of node that script expects to receive in a hit test. - pub fn to_untrusted_node_address(&self) -> UntrustedNodeAddress { + fn to_untrusted_node_address(&self) -> UntrustedNodeAddress { unsafe { let OpaqueNode(addr) = *self; let addr: UntrustedNodeAddress = cast::transmute(addr); @@ -251,11 +261,5 @@ impl OpaqueNode { } } - /// Returns the address of this node, for debugging purposes. - pub fn id(&self) -> uintptr_t { - unsafe { - cast::transmute_copy(self) - } - } } diff --git a/src/components/main/layout/wrapper.rs b/src/components/main/layout/wrapper.rs index ddd4a351b0b..2b8125dab00 100644 --- a/src/components/main/layout/wrapper.rs +++ b/src/components/main/layout/wrapper.rs @@ -37,7 +37,7 @@ use extra::url::Url; use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived}; use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived}; use script::dom::bindings::js::JS; -use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId}; +use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId, HTMLPseudoElementTypeId}; use script::dom::element::{HTMLLinkElementTypeId}; use script::dom::htmliframeelement::HTMLIFrameElement; use script::dom::htmlimageelement::HTMLImageElement; @@ -50,7 +50,7 @@ use std::cast; use std::cell::{Ref, RefMut}; use style::{PropertyDeclarationBlock, TElement, TNode, AttrSelector, SpecificNamespace}; use style::{AnyNamespace}; - +use style::computed_values::{content, display}; use layout::util::LayoutDataWrapper; /// Allows some convenience methods on generic layout nodes. @@ -114,22 +114,10 @@ pub trait TLayoutNode { /// If this is a text node, copies out the text. If this is not a text node, fails. /// /// FIXME(pcwalton): Don't copy text. Atomically reference count instead. - fn text(&self) -> ~str { - unsafe { - if !self.get().is_text() { - fail!("not text!") - } - let text: JS = self.get_jsmanaged().transmute_copy(); - (*text.unsafe_get()).characterdata.data.to_str() - } - } + fn text(&self) -> ~str; /// Returns the first child of this node. - fn first_child(&self) -> Option { - unsafe { - self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node)) - } - } + fn first_child(&self) -> Option; /// Dumps this node tree, for debugging. fn dump(&self) { @@ -144,7 +132,7 @@ pub struct LayoutNode<'a> { priv node: JS, /// Being chained to a value prevents `LayoutNode`s from escaping. - priv chain: &'a (), + chain: &'a (), } impl<'ln> Clone for LayoutNode<'ln> { @@ -178,6 +166,20 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> { unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS { &self.node } + fn first_child(&self) -> Option> { + unsafe { + self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node)) + } + } + fn text(&self) -> ~str { + unsafe { + if !self.get().is_text() { + fail!("not text!") + } + let text: JS = self.get_jsmanaged().transmute_copy(); + (*text.unsafe_get()).characterdata.data.to_str() + } + } } impl<'ln> LayoutNode<'ln> { @@ -205,6 +207,10 @@ impl<'ln> LayoutNode<'ln> { current_node: self.first_child(), } } + + pub unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS { + &self.node + } } impl<'ln> TNode> for LayoutNode<'ln> { @@ -371,29 +377,102 @@ impl<'le> TElement for LayoutElement<'le> { } } +fn get_content(content_list: &content::T) -> ~str{ + match *content_list { + content::Content(ref value) => { + let iter = &mut value.clone().move_iter().peekable(); + match iter.next() { + Some(content::StringContent(str)) => str, + _ => ~"", + } + } + _ => ~"", + } +} + +#[deriving(Eq, Clone)] +pub enum ElementType { + Normal, + Before, + After, + BeforeBlock, + AfterBlock, +} + /// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout /// node does not allow any parents or siblings of nodes to be accessed, to avoid races. pub struct ThreadSafeLayoutNode<'ln> { /// The wrapped node. - priv node: JS, + priv node: LayoutNode<'ln>, - /// Being chained to a value prevents `ThreadSafeLayoutNode`s from escaping. - priv chain: &'ln (), + priv pseudo: ElementType, } impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> { /// Creates a new layout node with the same lifetime as this layout node. unsafe fn new_with_this_lifetime(&self, node: &JS) -> ThreadSafeLayoutNode<'ln> { ThreadSafeLayoutNode { - node: node.transmute_copy(), - chain: self.chain, + node: LayoutNode { + node: node.transmute_copy(), + chain: self.node.chain, + }, + pseudo: Normal, } } + fn type_id(&self) -> NodeTypeId { - self.node.type_id() + if self.pseudo == Before || self.pseudo == After { + return ElementNodeTypeId(HTMLPseudoElementTypeId) + } else { + return self.node.type_id() + } } unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS { - &self.node + self.node.get_jsmanaged() + } + unsafe fn get<'a>(&'a self) -> &'a Node { // this change. + cast::transmute::<*mut Node,&'a Node>(self.get_jsmanaged().unsafe_get()) + } + fn first_child(&self) -> Option> { + if self.pseudo == Before { + return None + } + + if self.has_before_pseudo() { + if self.is_block(Before) && self.pseudo == Normal { + let pseudo_before_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, BeforeBlock); + return Some(pseudo_before_node) + } else if self.pseudo == Normal || self.pseudo == BeforeBlock{ + let pseudo_before_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, Before); + return Some(pseudo_before_node) + } + } + + unsafe { + self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node)) + } + } + fn text(&self) -> ~str { + if self.pseudo == Before || self.pseudo == After { + let layout_data_ref = self.borrow_layout_data(); + let node_ldw = layout_data_ref.get().get_ref(); + + if self.pseudo == Before { + let before_style = node_ldw.data.before_style.get_ref(); + return get_content(&before_style.get().Box.get().content) + } else { + let after_style = node_ldw.data.after_style.get_ref(); + return get_content(&after_style.get().Box.get().content) + } + } else { + unsafe { + if !self.get().is_text() { + fail!("not text!") + } + let text: JS = self.get_jsmanaged().transmute_copy(); + (*text.unsafe_get()).characterdata.data.to_str() + } + } } } @@ -401,7 +480,7 @@ impl<'ln> Clone for ThreadSafeLayoutNode<'ln> { fn clone(&self) -> ThreadSafeLayoutNode<'ln> { ThreadSafeLayoutNode { node: self.node.clone(), - chain: self.chain, + pseudo: self.pseudo, } } } @@ -410,13 +489,33 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { /// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`. pub fn new<'a>(node: &LayoutNode<'a>) -> ThreadSafeLayoutNode<'a> { ThreadSafeLayoutNode { - node: node.node.clone(), - chain: node.chain, + node: node.clone(), + pseudo: Normal, + } + } + + pub fn new_with_pseudo_without_self<'a>(node: &LayoutNode<'a>, element_type: ElementType) -> ThreadSafeLayoutNode<'a> { + ThreadSafeLayoutNode { + node: node.clone(), + pseudo: element_type, + } + } + + + /// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`. + pub fn new_with_pseudo<'a>(&'a self, element_type: ElementType) -> ThreadSafeLayoutNode<'a> { + ThreadSafeLayoutNode { + node: self.node.clone(), + pseudo: element_type, } } /// Returns the next sibling of this node. Unsafe and private because this can lead to races. unsafe fn next_sibling(&self) -> Option> { + if self.pseudo == Before || self.pseudo == BeforeBlock { + return self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node)) + } + self.node.get().next_sibling_ref().map(|node| self.new_with_this_lifetime(node)) } @@ -424,6 +523,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { pub fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln> { ThreadSafeLayoutNodeChildrenIterator { current_node: self.first_child(), + parent_node: Some(ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, self.pseudo)), } } @@ -431,7 +531,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { #[inline] pub fn as_element(&self) -> ThreadSafeLayoutElement { unsafe { - let elem: JS = self.node.transmute_copy(); + let elem: JS = self.node.get_jsmanaged().transmute_copy(); let element = elem.unsafe_get(); // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on // implementations. @@ -441,6 +541,48 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { } } + pub fn get_element_type(&self) -> ElementType { + self.pseudo + } + + pub fn is_block(&self, kind: ElementType) -> bool { + let mut layout_data_ref = self.mutate_layout_data(); + let node_ldw = layout_data_ref.get().get_mut_ref(); + + let display = match kind { + Before | BeforeBlock => { + let before_style = node_ldw.data.before_style.get_ref(); + before_style.get().Box.get().display + } + After | AfterBlock => { + let after_style = node_ldw.data.after_style.get_ref(); + after_style.get().Box.get().display + } + Normal => { + let after_style = node_ldw.data.style.get_ref(); + after_style.get().Box.get().display + } + }; + + if display == display::block { + return true + } + + false + } + + pub fn has_before_pseudo(&self) -> bool { + let ldw = self.borrow_layout_data(); + let ldw_ref = ldw.get().get_ref(); + ldw_ref.data.before_style.is_some() + } + + pub fn has_after_pseudo(&self) -> bool { + let ldw = self.borrow_layout_data(); + let ldw_ref = ldw.get().get_ref(); + ldw_ref.data.after_style.is_some() + } + /// Borrows the layout data immutably. Fails on a conflicting borrow. #[inline(always)] pub fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option> { @@ -487,16 +629,56 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { pub struct ThreadSafeLayoutNodeChildrenIterator<'a> { priv current_node: Option>, + priv parent_node: Option>, } impl<'a> Iterator> for ThreadSafeLayoutNodeChildrenIterator<'a> { fn next(&mut self) -> Option> { let node = self.current_node.clone(); - self.current_node = self.current_node.clone().and_then(|node| { - unsafe { - node.next_sibling() + + match node { + Some(ref node) => { + if node.pseudo == After || node.pseudo == AfterBlock { + return None + } + + match self.parent_node { + Some(ref parent_node) => { + if parent_node.pseudo == Normal { + self.current_node = self.current_node.clone().and_then(|node| { + unsafe { + node.next_sibling() + } + }); + } else { + self.current_node = None; + } + } + None => {} + } } - }); + None => { + match self.parent_node { + Some(ref parent_node) => { + if parent_node.has_after_pseudo() { + let pseudo_after_node = if parent_node.is_block(After) && parent_node.pseudo == Normal { + let pseudo_after_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&parent_node.node, AfterBlock); + Some(pseudo_after_node) + } else if parent_node.pseudo == Normal || parent_node.pseudo == AfterBlock { + let pseudo_after_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&parent_node.node, After); + Some(pseudo_after_node) + } else { + None + }; + self.current_node = pseudo_after_node; + return self.current_node.clone() + } + } + None => {} + } + } + } + node } } diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index aa092bce325..336c2433326 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -112,6 +112,7 @@ pub enum ElementTypeId { HTMLParamElementTypeId, HTMLPreElementTypeId, HTMLProgressElementTypeId, + HTMLPseudoElementTypeId, HTMLQuoteElementTypeId, HTMLScriptElementTypeId, HTMLSelectElementTypeId,