mirror of
https://github.com/servo/servo.git
synced 2025-06-21 23:59:00 +01:00
CSS parsing/selector-matching for pseudo elements
This commit is contained in:
parent
daebaf1eea
commit
138e425607
6 changed files with 144 additions and 28 deletions
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue