CSS parsing/selector-matching for pseudo elements

This commit is contained in:
Jaeman Park 2014-01-07 14:40:26 +09:00 committed by Sangeun Kim
parent daebaf1eea
commit 138e425607
6 changed files with 144 additions and 28 deletions

View file

@ -18,12 +18,15 @@ use std::rt;
use std::task; use std::task;
use std::vec; use std::vec;
use style::{TNode, Stylist, cascade}; use style::{TNode, Stylist, cascade};
use style::{Before, After};
pub trait MatchMethods { pub trait MatchMethods {
fn match_node(&self, stylist: &Stylist); fn match_node(&self, stylist: &Stylist);
fn match_subtree(&self, stylist: RWArc<Stylist>); fn match_subtree(&self, stylist: ~[RWArc<Stylist>]);
fn cascade_before_node(&self, parent: Option<LayoutNode>);
fn cascade_node(&self, parent: Option<LayoutNode>); fn cascade_node(&self, parent: Option<LayoutNode>);
fn cascade_after_node(&self, parent: Option<LayoutNode>);
fn cascade_subtree(&self, parent: Option<LayoutNode>); fn cascade_subtree(&self, parent: Option<LayoutNode>);
} }
@ -34,17 +37,22 @@ impl<'self> MatchMethods for LayoutNode<'self> {
None => None, None => None,
Some(ref style_attribute) => Some(style_attribute) Some(ref style_attribute) => Some(style_attribute)
}; };
stylist.get_applicable_declarations(self, style_attribute, None) stylist.get_applicable_declarations(self, style_attribute)
}; };
match *self.mutate_layout_data().ptr { match *self.mutate_layout_data().ptr {
Some(ref mut layout_data) => { Some(ref mut layout_data) => {
layout_data.applicable_declarations = applicable_declarations match stylist.get_pseudo_element() {
Some(Before) => layout_data.before_applicable_declarations = applicable_declarations,
Some(After) => layout_data.after_applicable_declarations = applicable_declarations,
None => layout_data.applicable_declarations = applicable_declarations,
_ => {}
}
} }
None => fail!("no layout data") None => fail!("no layout data")
} }
} }
fn match_subtree(&self, stylist: RWArc<Stylist>) { fn match_subtree(&self, stylists: ~[RWArc<Stylist>]) {
let num_tasks = rt::default_sched_threads() * 2; let num_tasks = rt::default_sched_threads() * 2;
let mut node_count = 0; let mut node_count = 0;
let mut nodes_per_task = vec::from_elem(num_tasks, ~[]); let mut nodes_per_task = vec::from_elem(num_tasks, ~[]);
@ -63,7 +71,7 @@ impl<'self> MatchMethods for LayoutNode<'self> {
for nodes in nodes_per_task.move_iter() { for nodes in nodes_per_task.move_iter() {
if nodes.len() > 0 { if nodes.len() > 0 {
let chan = chan.clone(); let chan = chan.clone();
let stylist = stylist.clone(); let stylists = stylists.clone();
// FIXME(pcwalton): This transmute is to work around the fact that we have no // FIXME(pcwalton): This transmute is to work around the fact that we have no
// mechanism for safe fork/join parallelism. If we had such a thing, then we could // mechanism for safe fork/join parallelism. If we had such a thing, then we could
@ -73,15 +81,19 @@ impl<'self> MatchMethods for LayoutNode<'self> {
cast::transmute(nodes) cast::transmute(nodes)
}; };
do task::spawn_with((evil, stylist)) |(evil, stylist)| { do task::spawn_with((evil, stylists)) |(evil, stylists)| {
let nodes: ~[LayoutNode] = unsafe { let nodes: ~[LayoutNode] = unsafe {
cast::transmute(evil) cast::transmute(evil)
}; };
let nodes = Cell::new(nodes); let nodes = Cell::new(nodes);
do stylist.read |stylist| { for i in range(0, stylists.len()) {
for node in nodes.take().move_iter() { do stylists[i].read |stylist| {
node.match_node(stylist); nodes.with_ref(|nodes|{
for node in nodes.iter() {
node.match_node(stylist);
}
});
} }
} }
chan.send(()); chan.send(());
@ -94,6 +106,37 @@ impl<'self> MatchMethods for LayoutNode<'self> {
} }
} }
fn cascade_before_node(&self, parent: Option<LayoutNode>) {
let parent_style = match parent {
Some(ref parent) => Some(parent.style()),
None => None
};
let computed_values = unsafe {
Arc::new(cascade(self.borrow_layout_data_unchecked()
.as_ref()
.unwrap()
.before_applicable_declarations,
parent_style.map(|parent_style| parent_style.get())))
};
match *self.mutate_layout_data().ptr {
None => fail!("no layout data"),
Some(ref mut layout_data) => {
let style = &mut layout_data.before_style;
match *style {
None => (),
Some(ref previous_style) => {
layout_data.restyle_damage =
Some(incremental::compute_damage(previous_style.get(),
computed_values.get()).to_int())
}
}
*style = Some(computed_values)
}
}
}
fn cascade_node(&self, parent: Option<LayoutNode>) { fn cascade_node(&self, parent: Option<LayoutNode>) {
let parent_style = match parent { let parent_style = match parent {
Some(ref parent) => Some(parent.style()), Some(ref parent) => Some(parent.style()),
@ -125,8 +168,49 @@ impl<'self> MatchMethods for LayoutNode<'self> {
} }
} }
fn cascade_after_node(&self, parent: Option<LayoutNode>) {
let parent_style = match parent {
Some(ref parent) => Some(parent.style()),
None => None
};
let computed_values = unsafe {
Arc::new(cascade(self.borrow_layout_data_unchecked()
.as_ref()
.unwrap()
.after_applicable_declarations,
parent_style.map(|parent_style| parent_style.get())))
};
match *self.mutate_layout_data().ptr {
None => fail!("no layout data"),
Some(ref mut layout_data) => {
let style = &mut layout_data.after_style;
match *style {
None => (),
Some(ref previous_style) => {
layout_data.restyle_damage =
Some(incremental::compute_damage(previous_style.get(),
computed_values.get()).to_int())
}
}
*style = Some(computed_values)
}
}
}
fn cascade_subtree(&self, parent: Option<LayoutNode>) { fn cascade_subtree(&self, parent: Option<LayoutNode>) {
unsafe {
if self.borrow_layout_data_unchecked().as_ref().unwrap().before_applicable_declarations.len() > 0 {
self.cascade_before_node(parent);
}
}
self.cascade_node(parent); self.cascade_node(parent);
unsafe {
if self.borrow_layout_data_unchecked().as_ref().unwrap().after_applicable_declarations.len() > 0 {
self.cascade_after_node(parent);
}
}
for kid in self.children() { for kid in self.children() {
if kid.is_element() { if kid.is_element() {

View file

@ -3,16 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use style::{Stylesheet, Stylist, UserAgentOrigin, with_errors_silenced}; use style::{Stylesheet, Stylist, UserAgentOrigin, with_errors_silenced};
use style::PseudoElement;
use extra::url; use extra::url;
pub fn new_stylist() -> Stylist { pub fn new_stylist(pseudo_element: Option<PseudoElement>) -> Stylist {
let mut stylist = Stylist::new(); let mut stylist = Stylist::new(pseudo_element);
let ua_stylesheet = with_errors_silenced(|| Stylesheet::from_bytes( let ua_stylesheet = with_errors_silenced(|| Stylesheet::from_bytes(
include_bin!("user-agent.css"), include_bin!("user-agent.css"),
url::from_str("chrome:///user-agent.css").unwrap(), url::from_str("chrome:///user-agent.css").unwrap(),
None, None,
None)); None));
stylist.add_stylesheet(ua_stylesheet, UserAgentOrigin); stylist.add_stylesheet(&ua_stylesheet, UserAgentOrigin);
stylist stylist
} }

View file

@ -51,6 +51,7 @@ use std::comm::Port;
use std::task; use std::task;
use std::util; use std::util;
use style::{AuthorOrigin, Stylesheet, Stylist}; use style::{AuthorOrigin, Stylesheet, Stylist};
use style::{Before, After};
/// Information needed by the layout task. /// Information needed by the layout task.
struct LayoutTask { struct LayoutTask {
@ -81,7 +82,7 @@ struct LayoutTask {
/// A cached display list. /// A cached display list.
display_list: Option<Arc<DisplayList<OpaqueNode>>>, display_list: Option<Arc<DisplayList<OpaqueNode>>>,
stylist: RWArc<Stylist>, stylists: ~[RWArc<Stylist>],
/// The channel on which messages can be sent to the profiler. /// The channel on which messages can be sent to the profiler.
profiler_chan: ProfilerChan, profiler_chan: ProfilerChan,
@ -236,6 +237,12 @@ impl LayoutTask {
profiler_chan: ProfilerChan) profiler_chan: ProfilerChan)
-> LayoutTask { -> LayoutTask {
let mut stylists = ~[];
let stylist_owners = ~[Some(Before), Some(After), None];
for i in range(0, stylist_owners.len()) {
stylists.push(RWArc::new(new_stylist(stylist_owners[i])));
}
LayoutTask { LayoutTask {
id: id, id: id,
port: port, port: port,
@ -248,7 +255,7 @@ impl LayoutTask {
display_list: None, display_list: None,
stylist: RWArc::new(new_stylist()), stylists: stylists,
profiler_chan: profiler_chan, profiler_chan: profiler_chan,
opts: opts.clone() opts: opts.clone()
} }
@ -347,8 +354,12 @@ impl LayoutTask {
fn handle_add_stylesheet(&mut self, sheet: Stylesheet) { fn handle_add_stylesheet(&mut self, sheet: Stylesheet) {
let sheet = Cell::new(sheet); let sheet = Cell::new(sheet);
do self.stylist.write |stylist| { for i in range(0, self.stylists.len()) {
stylist.add_stylesheet(sheet.take(), AuthorOrigin) do self.stylists[i].write |stylist| {
sheet.with_ref(|sheet|{
stylist.add_stylesheet(sheet, AuthorOrigin);
});
}
} }
} }
@ -445,7 +456,7 @@ impl LayoutTask {
ReflowDocumentDamage => {} ReflowDocumentDamage => {}
_ => { _ => {
do profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone()) { do profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone()) {
node.match_subtree(self.stylist.clone()); node.match_subtree(self.stylists.clone());
node.cascade_subtree(None); node.cascade_subtree(None);
} }
} }

View file

@ -127,11 +127,19 @@ impl ElementMapping {
/// Data that layout associates with a node. /// Data that layout associates with a node.
pub struct LayoutData { pub struct LayoutData {
/// The results of CSS matching for this node. /// The results of CSS matching for this node.
before_applicable_declarations: ~[Arc<~[PropertyDeclaration]>],
applicable_declarations: ~[Arc<~[PropertyDeclaration]>], applicable_declarations: ~[Arc<~[PropertyDeclaration]>],
after_applicable_declarations: ~[Arc<~[PropertyDeclaration]>],
/// The results of CSS styling for this node. /// The results of CSS styling for this node.
before_style: Option<Arc<ComputedValues>>,
style: Option<Arc<ComputedValues>>, style: Option<Arc<ComputedValues>>,
after_style: Option<Arc<ComputedValues>>,
/// Description of how to account for recent style changes. /// Description of how to account for recent style changes.
restyle_damage: Option<int>, restyle_damage: Option<int>,
@ -145,7 +153,11 @@ impl LayoutData {
pub fn new() -> LayoutData { pub fn new() -> LayoutData {
LayoutData { LayoutData {
applicable_declarations: ~[], applicable_declarations: ~[],
before_applicable_declarations: ~[],
after_applicable_declarations: ~[],
before_style: None,
style: None, style: None,
after_style: None,
restyle_damage: None, restyle_damage: None,
flow_construction_result: NoConstructionResult, flow_construction_result: NoConstructionResult,
} }

View file

@ -244,20 +244,22 @@ pub struct Stylist {
priv author_rule_map: PerOriginSelectorMap, priv author_rule_map: PerOriginSelectorMap,
priv user_rule_map: PerOriginSelectorMap, priv user_rule_map: PerOriginSelectorMap,
priv stylesheet_index: uint, priv stylesheet_index: uint,
priv pseudo_element: Option<PseudoElement>,
} }
impl Stylist { impl Stylist {
#[inline] #[inline]
pub fn new() -> Stylist { pub fn new(pseudo_element: Option<PseudoElement>) -> Stylist {
Stylist { Stylist {
ua_rule_map: PerOriginSelectorMap::new(), ua_rule_map: PerOriginSelectorMap::new(),
author_rule_map: PerOriginSelectorMap::new(), author_rule_map: PerOriginSelectorMap::new(),
user_rule_map: PerOriginSelectorMap::new(), user_rule_map: PerOriginSelectorMap::new(),
stylesheet_index: 0u, stylesheet_index: 0u,
pseudo_element: pseudo_element,
} }
} }
pub fn add_stylesheet(&mut self, stylesheet: Stylesheet, origin: StylesheetOrigin) { pub fn add_stylesheet(&mut self, stylesheet: &Stylesheet, origin: StylesheetOrigin) {
let rule_map = match origin { let rule_map = match origin {
UserAgentOrigin => &mut self.ua_rule_map, UserAgentOrigin => &mut self.ua_rule_map,
AuthorOrigin => &mut self.author_rule_map, AuthorOrigin => &mut self.author_rule_map,
@ -275,12 +277,14 @@ impl Stylist {
$flag = true; $flag = true;
for selector in style_rule.selectors.iter() { for selector in style_rule.selectors.iter() {
// TODO: avoid copying? // TODO: avoid copying?
rule_map.$priority.insert(Rule { if selector.pseudo_element == self.pseudo_element {
selector: Arc::new(selector.clone()), rule_map.$priority.insert(Rule {
declarations: style_rule.declarations.$priority.clone(), selector: Arc::new(selector.clone()),
index: style_rule_index, declarations: style_rule.declarations.$priority.clone(),
stylesheet_index: self.stylesheet_index, index: style_rule_index,
stylesheet_index: self.stylesheet_index,
}); });
}
} }
} }
}; };
@ -301,11 +305,10 @@ impl Stylist {
N:TNode<E>>( N:TNode<E>>(
&self, &self,
element: &N, element: &N,
style_attribute: Option<&PropertyDeclarationBlock>, style_attribute: Option<&PropertyDeclarationBlock>)
pseudo_element: Option<PseudoElement>)
-> ~[Arc<~[PropertyDeclaration]>] { -> ~[Arc<~[PropertyDeclaration]>] {
assert!(element.is_element()); assert!(element.is_element());
assert!(style_attribute.is_none() || pseudo_element.is_none(), assert!(style_attribute.is_none() || self.pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements"); "Style attributes do not apply to pseudo-elements");
// In cascading order: // In cascading order:
@ -327,7 +330,7 @@ impl Stylist {
for (i, rule_map) in rule_map_list.iter().enumerate() { for (i, rule_map) in rule_map_list.iter().enumerate() {
rule_map_indices[i] = matching_rules_list.len(); rule_map_indices[i] = matching_rules_list.len();
rule_map.get_all_matching_rules(element, pseudo_element, &mut matching_rules_list); rule_map.get_all_matching_rules(element, self.pseudo_element, &mut matching_rules_list);
} }
let count = matching_rules_list.len(); let count = matching_rules_list.len();
@ -370,6 +373,10 @@ impl Stylist {
applicable_declarations applicable_declarations
} }
pub fn get_pseudo_element(&self) -> Option<PseudoElement> {
self.pseudo_element
}
} }
struct PerOriginRules { struct PerOriginRules {

View file

@ -26,6 +26,7 @@ pub use properties::{cascade, PropertyDeclaration, ComputedValues, computed_valu
pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes
pub use errors::with_errors_silenced; pub use errors::with_errors_silenced;
pub use node::{TElement, TNode}; pub use node::{TElement, TNode};
pub use selectors::{PseudoElement, Before, After};
mod stylesheets; mod stylesheets;
mod errors; mod errors;