Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upPseudo element build flow and box #1496
Conversation
highfive
commented
Jan 15, 2014
|
hoppipolla-critic-bot
commented
Jan 15, 2014
|
Critic review: https://critic.hoppipolla.co.uk/r/555 This is an external review system which you may optionally use for the code review of your pull request. In order to help critic track your changes, please do not make in-place history rewrites (e.g. via |
|
@jdm r? |
|
I rebased it. |
|
Same for |
|
@SimonSapin I have refactored for this PR. Please check that. |
|
@SimonSapin r? |
| @@ -1546,29 +1560,49 @@ impl Node { | |||
| self.parent_node = new_parent_node | |||
| } | |||
|
|
|||
| pub fn set_parent_node_without_doc(&mut self, new_parent_node: Option<AbstractNode>) { | |||
| self.parent_node = new_parent_node | |||
| } | |||
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
I don't think we should add these methods to Node, because they are not safe to use from the script task. Instead, let's just set the fields directly from the methods in LayoutNode.
| /// Returns the interior of this node as a `Node`. This is highly unsafe for layout to call | ||
| /// and as such is marked `unsafe`. | ||
| pub unsafe fn get<'a>(&'a self) -> &'a Node { | ||
| cast::transmute(self.node.node()) | ||
| } | ||
|
|
||
| pub unsafe fn get_mut<'a>(&'a mut self) -> &'a mut Node { | ||
| cast::transmute_mut(self.node.mut_node()) |
This comment has been minimized.
This comment has been minimized.
| let p_ldw = p.borrow_layout_data(); | ||
| match *p_ldw.get() { | ||
| Some(ref layout_data_wrapper) => { | ||
| match layout_data_wrapper.data.before_style { |
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
before_style_exist = layout_data_wrapper.data.before_style.is_some();
after_style_exist = layout_data_wrapper.data.after_style.is_some();
| pub fn necessary_pseudo_elements(&self) -> ~[PseudoElement] { | ||
| let mut pseudo_elements = ~[]; | ||
| let mut before_style_exist = false; | ||
| let mut after_style_exist = false; |
This comment has been minimized.
This comment has been minimized.
| match *ldw.get() { | ||
| Some(ref layout_data_wrapper) => { | ||
| if before_style_exist { | ||
| match layout_data_wrapper.data.before_abstract_node { |
This comment has been minimized.
This comment has been minimized.
| let document = unsafe { node.get().owner_doc() }; | ||
|
|
||
| // Create pseudo parent_node | ||
| let pseudo_parent_element = ~Element::new_inherited(HTMLUnknownElementTypeId,~"pseudo_element", HTML, document); |
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
Let's create a new method for Element and Text called new_pseudo that does not require any arguments. Then we do not need the document variable any more.
| @@ -331,6 +331,20 @@ impl<'a> AbstractNode { | |||
| } | |||
| } | |||
|
|
|||
| pub unsafe fn from_element(element: ~Element) -> AbstractNode { | |||
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
Call this from_layout_pseudo and make it a generic method bounded by Reflectable (ie. <T: Reflectable>(node: ~T)). We don't need from_text if we do this.
| pub unsafe fn from_element(element: ~Element) -> AbstractNode { | ||
| let node = AbstractNode { | ||
| obj: transmute(element), | ||
| }; |
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
Right now, this will leak the memory for the Element and Text objects each time we use generated content. I think we need to add a new type LayoutPseudoNode that contains an AbstractNode, and has a destructor that frees the memory - the new fields in LayoutPrivateData should be this new type.
| let mut after_style_exist = false; | ||
|
|
||
| match self.parent_node() { | ||
| Some(p) => { |
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
if self.parent_node().is_none() {
return ~[];
}
let p = self.parent_node().unwrap();| LayoutNode { | ||
| node: node, | ||
| chain: self.chain, | ||
| } | ||
| } | ||
|
|
||
| pub fn set_parent_node_without_doc(&mut self, node: LayoutNode) { |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
In fact, let's replace all of these with a append_child method with sets all of the fields correctly.
This comment has been minimized.
This comment has been minimized.
| LayoutNode { | ||
| node: node, | ||
| chain: self.chain, | ||
| } | ||
| } | ||
|
|
||
| pub fn set_parent_node_without_doc(&mut self, node: LayoutNode) { | ||
| unsafe { self.get_mut().set_parent_node_without_doc(Some(node.node)) } |
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
As discussed below, just set the field manually instead of calling a method.
| @@ -142,6 +142,15 @@ pub struct PrivateLayoutData { | |||
|
|
|||
| after_style: Option<Arc<ComputedValues>>, | |||
|
|
|||
| before_parent_abstract_node: Option<AbstractNode>, | |||
This comment has been minimized.
This comment has been minimized.
| let iter = &mut value.clone().move_iter().peekable(); | ||
| match iter.next() { | ||
| Some(content::StringContent(str)) => str, | ||
| _ => ~"", |
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| fn get_content(content_list: &content::T) -> ~str{ | ||
| let content = match *content_list { |
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
No need for the let content =; this can just return the result of the match.
| None => fail!("node has no layout_data"), | ||
| } | ||
| } | ||
| _ => fail!("Text node should has its parent"), |
This comment has been minimized.
This comment has been minimized.
| @@ -497,6 +505,175 @@ impl<'fc> FlowConstructor<'fc> { | |||
| self.build_boxes_for_replaced_inline_content(node) | |||
| } | |||
| } | |||
|
|
|||
| fn build_flow_or_boxes_for_pseudo_element(&mut self, node: LayoutNode, pseudo_element: PseudoElement) { | |||
| match node.parent_node() { | |||
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
let p = node.parent_node().expect("Text node should have its parent");And then we don't need this match.
| } | ||
|
|
||
| // Store pseudo_parent_node & pseudo_node in node | ||
| match *layout_data_ref.get() { |
This comment has been minimized.
This comment has been minimized.
jdm
Jan 24, 2014
Member
let ref mut layout_data_wrapper = (*layout_data_ref.get()).expect("node has no layout_data");| pseudo_parent_node.set_next_sibling_without_doc(next_sibling); | ||
| } | ||
| None => { | ||
| match p.parent_node() { |
This comment has been minimized.
This comment has been minimized.
| pseudo_parent_node.set_prev_sibling_without_doc(prev_sibling); | ||
| } | ||
| None => { | ||
| match p.parent_node() { |
This comment has been minimized.
This comment has been minimized.
|
Ok, I've finished my review. This looks good to me, but we need to solve the memory leak I describe, and build_flow_or_boxes_for_pseudo_element is very long and has lots of unneeded matches, so it is difficult to read. I want all of the manual set_prev_sibling/set_first_child/etc. method calls to move into LayoutNode inside of descriptive methods like append_child, append_sibling, prepend_sibling, etc. That will make build_flow_or_boxes_for_pseudo_element much easier to read, and it will be harder to make mistakes when setting up pseudo DOM hierarchies manually. |
|
@jdm Thanks for your very kind comments. I modified this PR as you said. |
| let node: ~Element = unsafe { cast::transmute(self.node) }; | ||
| } else if self.node.is_text() { | ||
| let node: ~Text = unsafe { cast::transmute(self.node) }; | ||
| } |
This comment has been minimized.
This comment has been minimized.
jdm
Jan 25, 2014
Member
If LayoutPseudoNode is generic over type T, this can be replaced with
fn drop(&self) {
let _: ~T = unsafe { cast::transmute(self.node) };
}
This comment has been minimized.
This comment has been minimized.
sammykim
Feb 3, 2014
Contributor
@jdm
First way to solve this issue is changing LayoutPseudoNode as generic.
As long as LayoutPseudoNode is generic e.g) pub struct LayoutPseudoNode<T>
Then the struct which includes LayoutPseudoNode has to be generic.
e.g) LayoutDataWrapper has PrivateLayoutData has LayoutPseudoNode
Then all of those have to be generic. Not only I think that's not best way to do this, but also we have trouble to implement that.
Second, we tried to change priv node type to ~Reflectable and also remove all of the code relate to AbstractNode and LayoutNode. But as long as we want to build flow( e.g build_flow_for_block), we need to create abstract node also layout node.
We don't know how to make LayoutPseudoNode to be generic to drop easily and also remove from_layout_pseudo in node.rs
Anyway, I addressed another comments about function name and arguments.
If you have any opinion, let us know more detail.
Thanks for answer.
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| let prev_sibling = unsafe { | ||
| self.node.node().prev_sibling.map(|node| self.new_with_this_lifetime(node)) |
This comment has been minimized.
This comment has been minimized.
| if (self.is_element() && self.node.with_imm_element(|element| "before" == element.tag_name)) || | ||
| (self.is_text() && self.parent_node().unwrap().node.with_imm_element(|element| "before" == element.tag_name)) { | ||
| return unsafe { | ||
| self.node.node().next_sibling.map(|node| self.new_with_this_lifetime(node)) |
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| let next_sibling = unsafe{ | ||
| self.node.node().next_sibling.map(|node| self.new_with_this_lifetime(node)) |
This comment has been minimized.
This comment has been minimized.
| } else if self.node.is_text() { | ||
| let _: ~Text = unsafe { cast::transmute(self.node) }; | ||
| } | ||
| } |
This comment has been minimized.
This comment has been minimized.
|
|
||
| let ldw = self.borrow_layout_data(); | ||
| let ldw_ref = ldw.get().get_ref(); | ||
| if unsafe { self.parent_node().is_none() } { |
This comment has been minimized.
This comment has been minimized.
jdm
Feb 6, 2014
Member
Make a temporary variable for the parent node so you don't have to keep repeating the unsafe calls.
| } | ||
| } | ||
|
|
||
| pub unsafe fn parent_node(&self) -> Option<ThreadSafeLayoutNode<'ln>> { |
This comment has been minimized.
This comment has been minimized.
jdm
Feb 6, 2014
Member
@pcwalton Could you confirm that making methods like this available does not break our threadsafety guarantees?
This comment has been minimized.
This comment has been minimized.
|
@jdm We modified this PR according to your comments except for using to_pseudo_layout_node. |
|
Unfortunately Patrick has confirmed that we can't use parent nodes, so we're going to have to do something different. In |
|
Perhaps we can add |
|
@jdm Do you mean that we have to remove all variables regarding pseudo parent and use new initialize_layout_data for ThreadSafeLayoutNode in build_flow_or_boxes_for_pseudo_element? |
|
@jdm We currently insert before or after style to pseudo node's style if that is before or after text node, respectively. Why do we need parent_before_style and parent_after_style? |
|
@parkjaeman Any new layout code that is calling parent_node() is not threadsafe; layout code can only safely access a node, its siblings, and its children, or else it cannot be parallelized. We need to find ways to store the data required from parent nodes in the children instead. #1635 is one way of solving this problem for percentage heights, but the solution we need here will be more complicated. |
|
I believe that creating the pseudo parent in build_flow_or_boxes_for_pseudo_element is fine. The problem is when we have an already existing ThreadSafeLayoutNode and call parent_node on it. |
|
@jdm OK. We will try to transfer storage for before style and after style from target element to target text so that we don't need to call parent_node() in build_flow_or_boxes_for_pseudo_element. |
|
@pcwalton is best able to answer that question. Are the design for parallel layout and the constraints required written down anywhere? |
|
It's not thread safe because multiple children of a single parent can be laid out in parallel. So two siblings might both call It is possible to safely call I'll review this PR later today and see if I can suggest an alternative strategy. |
|
Here's a suggestion for how to do this. I apologize that this is significantly different from what you implemented.
|
|
@pcwalton Nit: I’d rather |
|
Oh, one more thing: You will need to do something about the borrow flags in |
|
@pcwalton I am little bit confused. :) I want to make sure.
Thanks for your kind comment. Please make me sure. :) |
|
I'm assuming the new PR in #1725 makes this obsolete. |
parkjaeman commentedJan 15, 2014
Implementation flow & box construction for pseudo element :before and :after.