mirror of
https://github.com/servo/servo.git
synced 2025-08-09 07:25:35 +01:00
layout: Implement pseudo-elements.
This commit is contained in:
parent
901c4483b2
commit
4c53a21aa3
5 changed files with 427 additions and 171 deletions
|
@ -5,7 +5,7 @@
|
||||||
use layout::incremental::RestyleDamage;
|
use layout::incremental::RestyleDamage;
|
||||||
use layout::util::LayoutDataAccess;
|
use layout::util::LayoutDataAccess;
|
||||||
use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
|
use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
|
||||||
|
use layout::wrapper::{After, AfterBlock, Before, BeforeBlock, Normal};
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use style::ComputedValues;
|
use style::ComputedValues;
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
|
@ -25,13 +25,35 @@ impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
|
||||||
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues> {
|
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let layout_data_ref = self.borrow_layout_data();
|
let layout_data_ref = self.borrow_layout_data();
|
||||||
cast::transmute_region(layout_data_ref.get()
|
match self.get_element_type() {
|
||||||
.as_ref()
|
Before | BeforeBlock => {
|
||||||
.unwrap()
|
return cast::transmute_region(layout_data_ref.get()
|
||||||
.data
|
.as_ref()
|
||||||
.style
|
.unwrap()
|
||||||
.as_ref()
|
.data
|
||||||
.unwrap())
|
.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())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -293,21 +293,28 @@ impl<'a> FlowConstructor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds specific `Box` info for the given node.
|
/// 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 {
|
-> SpecificBoxInfo {
|
||||||
match node.type_id() {
|
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(HTMLIFrameElementTypeId) => IframeBox(IframeBoxInfo::new(node)),
|
||||||
ElementNodeTypeId(HTMLObjectElementTypeId) => {
|
ElementNodeTypeId(HTMLObjectElementTypeId) => {
|
||||||
let data = node.get_object_data(&self.layout_context.url);
|
let data = node.get_object_data(&self.layout_context.url);
|
||||||
self.build_box_info_for_image(node, data)
|
self.build_box_info_for_image(node, data)
|
||||||
}
|
}
|
||||||
ElementNodeTypeId(HTMLTableElementTypeId) => TableWrapperBox,
|
ElementNodeTypeId(HTMLTableElementTypeId) => TableWrapperBox,
|
||||||
ElementNodeTypeId(HTMLTableColElementTypeId) => TableColumnBox(TableColumnBoxInfo::new(node)),
|
ElementNodeTypeId(HTMLTableColElementTypeId) => {
|
||||||
|
TableColumnBox(TableColumnBoxInfo::new(node))
|
||||||
|
}
|
||||||
ElementNodeTypeId(HTMLTableDataCellElementTypeId) |
|
ElementNodeTypeId(HTMLTableDataCellElementTypeId) |
|
||||||
ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => TableCellBox,
|
ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => TableCellBox,
|
||||||
ElementNodeTypeId(HTMLTableRowElementTypeId) |
|
ElementNodeTypeId(HTMLTableRowElementTypeId) |
|
||||||
ElementNodeTypeId(HTMLTableSectionElementTypeId) => TableRowBox,
|
ElementNodeTypeId(HTMLTableSectionElementTypeId) => TableRowBox,
|
||||||
|
ElementNodeTypeId(HTMLPseudoElementTypeId) |
|
||||||
TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(node)),
|
TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(node)),
|
||||||
_ => GenericBox,
|
_ => 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.
|
/// Build block flow for current node using information from children nodes.
|
||||||
///
|
///
|
||||||
/// Consume results from children and combine them, handling {ib} splits.
|
/// 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 opt_boxes_for_inline_flow = None;
|
||||||
let mut consecutive_siblings = ~[];
|
let mut consecutive_siblings = ~[];
|
||||||
let mut first_box = true;
|
let mut first_box = true;
|
||||||
|
|
||||||
// List of absolute descendants, in tree order.
|
// List of absolute descendants, in tree order.
|
||||||
let mut abs_descendants = Descendants::new();
|
let mut abs_descendants = Descendants::new();
|
||||||
let mut fixed_descendants = Descendants::new();
|
|
||||||
for kid in node.children() {
|
for kid in node.children() {
|
||||||
match kid.swap_out_construction_result() {
|
if kid.get_element_type() != Normal {
|
||||||
NoConstructionResult => {}
|
self.process(&kid);
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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}
|
// 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) {
|
fn set_flow_construction_result(&self, result: ConstructionResult) {
|
||||||
let mut layout_data_ref = self.mutate_layout_data();
|
let mut layout_data_ref = self.mutate_layout_data();
|
||||||
match *layout_data_ref.get() {
|
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"),
|
None => fail!("no layout data"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1100,7 +1139,15 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
|
||||||
let mut layout_data_ref = self.mutate_layout_data();
|
let mut layout_data_ref = self.mutate_layout_data();
|
||||||
match *layout_data_ref.get() {
|
match *layout_data_ref.get() {
|
||||||
Some(ref mut layout_data) => {
|
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"),
|
None => fail!("no layout data"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use layout::construct::{ConstructionResult, NoConstructionResult};
|
||||||
use layout::parallel::DomParallelInfo;
|
use layout::parallel::DomParallelInfo;
|
||||||
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
|
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
|
||||||
|
|
||||||
|
use gfx::display_list::OpaqueNode;
|
||||||
use script::dom::bindings::js::JS;
|
use script::dom::bindings::js::JS;
|
||||||
use script::dom::bindings::utils::Reflectable;
|
use script::dom::bindings::utils::Reflectable;
|
||||||
use script::dom::node::Node;
|
use script::dom::node::Node;
|
||||||
|
@ -146,6 +147,10 @@ pub struct PrivateLayoutData {
|
||||||
/// `ConstructionItem`. See comments in `construct.rs` for more details.
|
/// `ConstructionItem`. See comments in `construct.rs` for more details.
|
||||||
flow_construction_result: ConstructionResult,
|
flow_construction_result: ConstructionResult,
|
||||||
|
|
||||||
|
before_flow_construction_result: ConstructionResult,
|
||||||
|
|
||||||
|
after_flow_construction_result: ConstructionResult,
|
||||||
|
|
||||||
/// Information needed during parallel traversals.
|
/// Information needed during parallel traversals.
|
||||||
parallel: DomParallelInfo,
|
parallel: DomParallelInfo,
|
||||||
}
|
}
|
||||||
|
@ -159,6 +164,8 @@ impl PrivateLayoutData {
|
||||||
after_style: None,
|
after_style: None,
|
||||||
restyle_damage: None,
|
restyle_damage: None,
|
||||||
flow_construction_result: NoConstructionResult,
|
flow_construction_result: NoConstructionResult,
|
||||||
|
before_flow_construction_result: NoConstructionResult,
|
||||||
|
after_flow_construction_result: NoConstructionResult,
|
||||||
parallel: DomParallelInfo::new(),
|
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
|
pub trait OpaqueNodeMethods {
|
||||||
/// 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 {
|
|
||||||
/// Converts a DOM node (layout view) to an `OpaqueNode`.
|
/// 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<Node>) -> 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 {
|
unsafe {
|
||||||
OpaqueNode::from_jsmanaged(node.get_jsmanaged())
|
OpaqueNodeMethods::from_jsmanaged(node.get_jsmanaged())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`.
|
fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
|
||||||
pub fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let abstract_node = node.get_jsmanaged();
|
let abstract_node = node.get_jsmanaged();
|
||||||
let ptr: uintptr_t = cast::transmute(abstract_node.reflector().get_jsobject());
|
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`.
|
fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
|
||||||
pub fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
|
|
||||||
unsafe {
|
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<Node>) -> OpaqueNode {
|
fn from_jsmanaged(node: &JS<Node>) -> OpaqueNode {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr: uintptr_t = cast::transmute(node.reflector().get_jsobject());
|
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
|
fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
|
||||||
/// of node that script expects to receive in a hit test.
|
|
||||||
pub fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let OpaqueNode(addr) = *self;
|
let OpaqueNode(addr) = *self;
|
||||||
let addr: UntrustedNodeAddress = cast::transmute(addr);
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ use extra::url::Url;
|
||||||
use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived};
|
use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived};
|
||||||
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
|
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
|
||||||
use script::dom::bindings::js::JS;
|
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::element::{HTMLLinkElementTypeId};
|
||||||
use script::dom::htmliframeelement::HTMLIFrameElement;
|
use script::dom::htmliframeelement::HTMLIFrameElement;
|
||||||
use script::dom::htmlimageelement::HTMLImageElement;
|
use script::dom::htmlimageelement::HTMLImageElement;
|
||||||
|
@ -50,7 +50,7 @@ use std::cast;
|
||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use style::{PropertyDeclarationBlock, TElement, TNode, AttrSelector, SpecificNamespace};
|
use style::{PropertyDeclarationBlock, TElement, TNode, AttrSelector, SpecificNamespace};
|
||||||
use style::{AnyNamespace};
|
use style::{AnyNamespace};
|
||||||
|
use style::computed_values::{content, display};
|
||||||
use layout::util::LayoutDataWrapper;
|
use layout::util::LayoutDataWrapper;
|
||||||
|
|
||||||
/// Allows some convenience methods on generic layout nodes.
|
/// 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.
|
/// 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.
|
/// FIXME(pcwalton): Don't copy text. Atomically reference count instead.
|
||||||
fn text(&self) -> ~str {
|
fn text(&self) -> ~str;
|
||||||
unsafe {
|
|
||||||
if !self.get().is_text() {
|
|
||||||
fail!("not text!")
|
|
||||||
}
|
|
||||||
let text: JS<Text> = self.get_jsmanaged().transmute_copy();
|
|
||||||
(*text.unsafe_get()).characterdata.data.to_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the first child of this node.
|
/// Returns the first child of this node.
|
||||||
fn first_child(&self) -> Option<Self> {
|
fn first_child(&self) -> Option<Self>;
|
||||||
unsafe {
|
|
||||||
self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dumps this node tree, for debugging.
|
/// Dumps this node tree, for debugging.
|
||||||
fn dump(&self) {
|
fn dump(&self) {
|
||||||
|
@ -144,7 +132,7 @@ pub struct LayoutNode<'a> {
|
||||||
priv node: JS<Node>,
|
priv node: JS<Node>,
|
||||||
|
|
||||||
/// Being chained to a value prevents `LayoutNode`s from escaping.
|
/// Being chained to a value prevents `LayoutNode`s from escaping.
|
||||||
priv chain: &'a (),
|
chain: &'a (),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ln> Clone for LayoutNode<'ln> {
|
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<Node> {
|
unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
|
||||||
&self.node
|
&self.node
|
||||||
}
|
}
|
||||||
|
fn first_child(&self) -> Option<LayoutNode<'ln>> {
|
||||||
|
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<Text> = self.get_jsmanaged().transmute_copy();
|
||||||
|
(*text.unsafe_get()).characterdata.data.to_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ln> LayoutNode<'ln> {
|
impl<'ln> LayoutNode<'ln> {
|
||||||
|
@ -205,6 +207,10 @@ impl<'ln> LayoutNode<'ln> {
|
||||||
current_node: self.first_child(),
|
current_node: self.first_child(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
|
||||||
|
&self.node
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> {
|
impl<'ln> TNode<LayoutElement<'ln>> 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
|
/// 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.
|
/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
|
||||||
pub struct ThreadSafeLayoutNode<'ln> {
|
pub struct ThreadSafeLayoutNode<'ln> {
|
||||||
/// The wrapped node.
|
/// The wrapped node.
|
||||||
priv node: JS<Node>,
|
priv node: LayoutNode<'ln>,
|
||||||
|
|
||||||
/// Being chained to a value prevents `ThreadSafeLayoutNode`s from escaping.
|
priv pseudo: ElementType,
|
||||||
priv chain: &'ln (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> {
|
impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> {
|
||||||
/// Creates a new layout node with the same lifetime as this layout node.
|
/// Creates a new layout node with the same lifetime as this layout node.
|
||||||
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> ThreadSafeLayoutNode<'ln> {
|
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> ThreadSafeLayoutNode<'ln> {
|
||||||
ThreadSafeLayoutNode {
|
ThreadSafeLayoutNode {
|
||||||
node: node.transmute_copy(),
|
node: LayoutNode {
|
||||||
chain: self.chain,
|
node: node.transmute_copy(),
|
||||||
|
chain: self.node.chain,
|
||||||
|
},
|
||||||
|
pseudo: Normal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_id(&self) -> NodeTypeId {
|
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<Node> {
|
unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
|
||||||
&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<ThreadSafeLayoutNode<'ln>> {
|
||||||
|
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<Text> = 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> {
|
fn clone(&self) -> ThreadSafeLayoutNode<'ln> {
|
||||||
ThreadSafeLayoutNode {
|
ThreadSafeLayoutNode {
|
||||||
node: self.node.clone(),
|
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`.
|
/// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`.
|
||||||
pub fn new<'a>(node: &LayoutNode<'a>) -> ThreadSafeLayoutNode<'a> {
|
pub fn new<'a>(node: &LayoutNode<'a>) -> ThreadSafeLayoutNode<'a> {
|
||||||
ThreadSafeLayoutNode {
|
ThreadSafeLayoutNode {
|
||||||
node: node.node.clone(),
|
node: node.clone(),
|
||||||
chain: node.chain,
|
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.
|
/// Returns the next sibling of this node. Unsafe and private because this can lead to races.
|
||||||
unsafe fn next_sibling(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
|
unsafe fn next_sibling(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
|
||||||
|
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))
|
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> {
|
pub fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln> {
|
||||||
ThreadSafeLayoutNodeChildrenIterator {
|
ThreadSafeLayoutNodeChildrenIterator {
|
||||||
current_node: self.first_child(),
|
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]
|
#[inline]
|
||||||
pub fn as_element(&self) -> ThreadSafeLayoutElement {
|
pub fn as_element(&self) -> ThreadSafeLayoutElement {
|
||||||
unsafe {
|
unsafe {
|
||||||
let elem: JS<Element> = self.node.transmute_copy();
|
let elem: JS<Element> = self.node.get_jsmanaged().transmute_copy();
|
||||||
let element = elem.unsafe_get();
|
let element = elem.unsafe_get();
|
||||||
// FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
|
// FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
|
||||||
// implementations.
|
// 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.
|
/// Borrows the layout data immutably. Fails on a conflicting borrow.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
|
pub fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
|
||||||
|
@ -487,16 +629,56 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
||||||
|
|
||||||
pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
|
pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
|
||||||
priv current_node: Option<ThreadSafeLayoutNode<'a>>,
|
priv current_node: Option<ThreadSafeLayoutNode<'a>>,
|
||||||
|
priv parent_node: Option<ThreadSafeLayoutNode<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator<ThreadSafeLayoutNode<'a>> for ThreadSafeLayoutNodeChildrenIterator<'a> {
|
impl<'a> Iterator<ThreadSafeLayoutNode<'a>> for ThreadSafeLayoutNodeChildrenIterator<'a> {
|
||||||
fn next(&mut self) -> Option<ThreadSafeLayoutNode<'a>> {
|
fn next(&mut self) -> Option<ThreadSafeLayoutNode<'a>> {
|
||||||
let node = self.current_node.clone();
|
let node = self.current_node.clone();
|
||||||
self.current_node = self.current_node.clone().and_then(|node| {
|
|
||||||
unsafe {
|
match node {
|
||||||
node.next_sibling()
|
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
|
node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ pub enum ElementTypeId {
|
||||||
HTMLParamElementTypeId,
|
HTMLParamElementTypeId,
|
||||||
HTMLPreElementTypeId,
|
HTMLPreElementTypeId,
|
||||||
HTMLProgressElementTypeId,
|
HTMLProgressElementTypeId,
|
||||||
|
HTMLPseudoElementTypeId,
|
||||||
HTMLQuoteElementTypeId,
|
HTMLQuoteElementTypeId,
|
||||||
HTMLScriptElementTypeId,
|
HTMLScriptElementTypeId,
|
||||||
HTMLSelectElementTypeId,
|
HTMLSelectElementTypeId,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue