mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
auto merge of #1470 : SimonSapin/servo/before-after, r=metajack
No behavior change (hopefully).
a16087d
is the core of what I wanted to do, following up on #1464 (cc: @parkjaeman)
This commit is contained in:
commit
728fb9a7de
5 changed files with 246 additions and 321 deletions
|
@ -22,37 +22,33 @@ 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_after_node(&self, parent: Option<LayoutNode>);
|
|
||||||
fn cascade_subtree(&self, parent: Option<LayoutNode>);
|
fn cascade_subtree(&self, parent: Option<LayoutNode>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'self> MatchMethods for LayoutNode<'self> {
|
impl<'self> MatchMethods for LayoutNode<'self> {
|
||||||
fn match_node(&self, stylist: &Stylist) {
|
fn match_node(&self, stylist: &Stylist) {
|
||||||
let applicable_declarations = do self.with_element |element| {
|
let style_attribute = do self.with_element |element| {
|
||||||
let style_attribute = match *element.style_attribute() {
|
match *element.style_attribute() {
|
||||||
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)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match *self.mutate_layout_data().ptr {
|
match *self.mutate_layout_data().ptr {
|
||||||
Some(ref mut layout_data) => {
|
Some(ref mut layout_data) => {
|
||||||
match stylist.get_pseudo_element() {
|
layout_data.applicable_declarations = stylist.get_applicable_declarations(
|
||||||
Some(Before) => layout_data.before_applicable_declarations = applicable_declarations,
|
self, style_attribute, None);
|
||||||
Some(After) => layout_data.after_applicable_declarations = applicable_declarations,
|
layout_data.before_applicable_declarations = stylist.get_applicable_declarations(
|
||||||
None => layout_data.applicable_declarations = applicable_declarations,
|
self, None, Some(Before));
|
||||||
_ => {}
|
layout_data.after_applicable_declarations = stylist.get_applicable_declarations(
|
||||||
}
|
self, None, Some(After));
|
||||||
}
|
}
|
||||||
None => fail!("no layout data")
|
None => fail!("no layout data")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn match_subtree(&self, stylists: ~[RWArc<Stylist>]) {
|
fn match_subtree(&self, stylist: 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, ~[]);
|
||||||
|
@ -71,8 +67,8 @@ 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 stylists = stylists.clone();
|
let stylist = stylist.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
|
||||||
// close over the lifetime-bounded `LayoutNode`. But we can't, so we force it with
|
// close over the lifetime-bounded `LayoutNode`. But we can't, so we force it with
|
||||||
|
@ -81,19 +77,16 @@ impl<'self> MatchMethods for LayoutNode<'self> {
|
||||||
cast::transmute(nodes)
|
cast::transmute(nodes)
|
||||||
};
|
};
|
||||||
|
|
||||||
do task::spawn_with((evil, stylists)) |(evil, stylists)| {
|
do task::spawn_with((evil, stylist)) |(evil, stylist)| {
|
||||||
let nodes: ~[LayoutNode] = unsafe {
|
let nodes: ~[LayoutNode] = unsafe {
|
||||||
cast::transmute(evil)
|
cast::transmute(evil)
|
||||||
};
|
};
|
||||||
|
|
||||||
let nodes = Cell::new(nodes);
|
let nodes = Cell::new(nodes);
|
||||||
for stylist in stylists.iter() {
|
do stylist.read |stylist| {
|
||||||
do stylist.read |stylist| {
|
let nodes = nodes.take();
|
||||||
nodes.with_ref(|nodes|{
|
for node in nodes.iter() {
|
||||||
for node in nodes.iter() {
|
node.match_node(stylist);
|
||||||
node.match_node(stylist);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chan.send(());
|
chan.send(());
|
||||||
|
@ -106,109 +99,47 @@ 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>) {
|
|
||||||
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()
|
|
||||||
.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.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_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>) {
|
||||||
|
let layout_data = unsafe {
|
||||||
|
self.borrow_layout_data_unchecked().as_ref().unwrap()
|
||||||
|
};
|
||||||
|
macro_rules! cascade_node(
|
||||||
|
($applicable_declarations: ident, $style: ident) => {{
|
||||||
|
let parent_style = match parent {
|
||||||
|
Some(ref parent) => Some(parent.style()),
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let computed_values = Arc::new(cascade(
|
||||||
|
layout_data.$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.$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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.borrow_layout_data_unchecked().as_ref().unwrap().before_applicable_declarations.len() > 0 {
|
if self.borrow_layout_data_unchecked().as_ref().unwrap().before_applicable_declarations.len() > 0 {
|
||||||
self.cascade_before_node(parent);
|
cascade_node!(before_applicable_declarations, before_style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.cascade_node(parent);
|
cascade_node!(applicable_declarations, style);
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.borrow_layout_data_unchecked().as_ref().unwrap().after_applicable_declarations.len() > 0 {
|
if self.borrow_layout_data_unchecked().as_ref().unwrap().after_applicable_declarations.len() > 0 {
|
||||||
self.cascade_after_node(parent);
|
cascade_node!(after_applicable_declarations, after_style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,16 @@
|
||||||
* 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(pseudo_element: Option<PseudoElement>) -> Stylist {
|
pub fn new_stylist() -> Stylist {
|
||||||
let mut stylist = Stylist::new(pseudo_element);
|
let mut stylist = Stylist::new();
|
||||||
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,7 +51,6 @@ 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 {
|
||||||
|
@ -82,7 +81,7 @@ struct LayoutTask {
|
||||||
/// A cached display list.
|
/// A cached display list.
|
||||||
display_list: Option<Arc<DisplayList<OpaqueNode>>>,
|
display_list: Option<Arc<DisplayList<OpaqueNode>>>,
|
||||||
|
|
||||||
stylists: ~[RWArc<Stylist>],
|
stylist: 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,
|
||||||
|
@ -237,14 +236,6 @@ impl LayoutTask {
|
||||||
profiler_chan: ProfilerChan)
|
profiler_chan: ProfilerChan)
|
||||||
-> LayoutTask {
|
-> LayoutTask {
|
||||||
|
|
||||||
let mut stylists = ~[];
|
|
||||||
// We implemented parsing/selector-matching only for Before and After.
|
|
||||||
// FirstLine and FirstLetter have to be added later.
|
|
||||||
let stylist_owners = ~[Some(Before), Some(After), None];
|
|
||||||
for pseudo_element in stylist_owners.iter() {
|
|
||||||
stylists.push(RWArc::new(new_stylist(*pseudo_element)));
|
|
||||||
}
|
|
||||||
|
|
||||||
LayoutTask {
|
LayoutTask {
|
||||||
id: id,
|
id: id,
|
||||||
port: port,
|
port: port,
|
||||||
|
@ -257,7 +248,7 @@ impl LayoutTask {
|
||||||
|
|
||||||
display_list: None,
|
display_list: None,
|
||||||
|
|
||||||
stylists: stylists,
|
stylist: RWArc::new(new_stylist()),
|
||||||
profiler_chan: profiler_chan,
|
profiler_chan: profiler_chan,
|
||||||
opts: opts.clone()
|
opts: opts.clone()
|
||||||
}
|
}
|
||||||
|
@ -356,12 +347,8 @@ 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);
|
||||||
for stylist in self.stylists.iter() {
|
do self.stylist.write |stylist| {
|
||||||
do stylist.write |stylist| {
|
stylist.add_stylesheet(sheet.take(), AuthorOrigin);
|
||||||
sheet.with_ref(|sheet|{
|
|
||||||
stylist.add_stylesheet(sheet, AuthorOrigin);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +445,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.stylists.clone());
|
node.match_subtree(self.stylist.clone());
|
||||||
node.cascade_subtree(None);
|
node.cascade_subtree(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,14 +42,14 @@ static SELECTOR_WHITESPACE: &'static [char] = &'static [' ', '\t', '\n', '\r', '
|
||||||
/// Hence, the union of the rules keyed on each of node's classes, ID,
|
/// Hence, the union of the rules keyed on each of node's classes, ID,
|
||||||
/// element name, etc. will contain the Rules that actually match that
|
/// element name, etc. will contain the Rules that actually match that
|
||||||
/// node.
|
/// node.
|
||||||
pub struct SelectorMap {
|
struct SelectorMap {
|
||||||
// TODO: Tune the initial capacity of the HashMap
|
// TODO: Tune the initial capacity of the HashMap
|
||||||
// FIXME: Use interned strings
|
// FIXME: Use interned strings
|
||||||
priv id_hash: HashMap<~str, ~[Rule]>,
|
id_hash: HashMap<~str, ~[Rule]>,
|
||||||
priv class_hash: HashMap<~str, ~[Rule]>,
|
class_hash: HashMap<~str, ~[Rule]>,
|
||||||
priv element_hash: HashMap<~str, ~[Rule]>,
|
element_hash: HashMap<~str, ~[Rule]>,
|
||||||
// For Rules that don't have ID, class, or element selectors.
|
// For Rules that don't have ID, class, or element selectors.
|
||||||
priv universal_rules: ~[Rule],
|
universal_rules: ~[Rule],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectorMap {
|
impl SelectorMap {
|
||||||
|
@ -70,7 +70,6 @@ impl SelectorMap {
|
||||||
N:TNode<E>>(
|
N:TNode<E>>(
|
||||||
&self,
|
&self,
|
||||||
node: &N,
|
node: &N,
|
||||||
pseudo_element: Option<PseudoElement>,
|
|
||||||
matching_rules_list: &mut ~[Rule]) {
|
matching_rules_list: &mut ~[Rule]) {
|
||||||
// At the end, we're going to sort the rules that we added, so remember where we began.
|
// At the end, we're going to sort the rules that we added, so remember where we began.
|
||||||
let init_len = matching_rules_list.len();
|
let init_len = matching_rules_list.len();
|
||||||
|
@ -78,7 +77,6 @@ impl SelectorMap {
|
||||||
match element.get_attr(None, "id") {
|
match element.get_attr(None, "id") {
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
SelectorMap::get_matching_rules_from_hash(node,
|
SelectorMap::get_matching_rules_from_hash(node,
|
||||||
pseudo_element,
|
|
||||||
&self.id_hash,
|
&self.id_hash,
|
||||||
id,
|
id,
|
||||||
matching_rules_list)
|
matching_rules_list)
|
||||||
|
@ -90,7 +88,6 @@ impl SelectorMap {
|
||||||
Some(ref class_attr) => {
|
Some(ref class_attr) => {
|
||||||
for class in class_attr.split_iter(SELECTOR_WHITESPACE) {
|
for class in class_attr.split_iter(SELECTOR_WHITESPACE) {
|
||||||
SelectorMap::get_matching_rules_from_hash(node,
|
SelectorMap::get_matching_rules_from_hash(node,
|
||||||
pseudo_element,
|
|
||||||
&self.class_hash,
|
&self.class_hash,
|
||||||
class,
|
class,
|
||||||
matching_rules_list)
|
matching_rules_list)
|
||||||
|
@ -102,12 +99,10 @@ impl SelectorMap {
|
||||||
// HTML elements in HTML documents must be matched case-insensitively.
|
// HTML elements in HTML documents must be matched case-insensitively.
|
||||||
// TODO(pradeep): Case-sensitivity depends on the document type.
|
// TODO(pradeep): Case-sensitivity depends on the document type.
|
||||||
SelectorMap::get_matching_rules_from_hash(node,
|
SelectorMap::get_matching_rules_from_hash(node,
|
||||||
pseudo_element,
|
|
||||||
&self.element_hash,
|
&self.element_hash,
|
||||||
element.get_local_name().to_ascii_lower(),
|
element.get_local_name().to_ascii_lower(),
|
||||||
matching_rules_list);
|
matching_rules_list);
|
||||||
SelectorMap::get_matching_rules(node,
|
SelectorMap::get_matching_rules(node,
|
||||||
pseudo_element,
|
|
||||||
self.universal_rules,
|
self.universal_rules,
|
||||||
matching_rules_list);
|
matching_rules_list);
|
||||||
});
|
});
|
||||||
|
@ -119,27 +114,25 @@ impl SelectorMap {
|
||||||
fn get_matching_rules_from_hash<E:TElement,
|
fn get_matching_rules_from_hash<E:TElement,
|
||||||
N:TNode<E>>(
|
N:TNode<E>>(
|
||||||
node: &N,
|
node: &N,
|
||||||
pseudo_element: Option<PseudoElement>,
|
hash: &HashMap<~str,~[Rule]>,
|
||||||
hash: &HashMap<~str,~[Rule]>,
|
|
||||||
key: &str,
|
key: &str,
|
||||||
matching_rules: &mut ~[Rule]) {
|
matching_rules: &mut ~[Rule]) {
|
||||||
match hash.find(&key.to_str()) {
|
match hash.find(&key.to_str()) {
|
||||||
Some(rules) => {
|
Some(rules) => {
|
||||||
SelectorMap::get_matching_rules(node, pseudo_element, *rules, matching_rules)
|
SelectorMap::get_matching_rules(node, *rules, matching_rules)
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds rules in `rules` that match `node` to the `matching_rules` list.
|
/// Adds rules in `rules` that match `node` to the `matching_rules` list.
|
||||||
fn get_matching_rules<E:TElement,
|
fn get_matching_rules<E:TElement,
|
||||||
N:TNode<E>>(
|
N:TNode<E>>(
|
||||||
node: &N,
|
node: &N,
|
||||||
pseudo_element: Option<PseudoElement>,
|
|
||||||
rules: &[Rule],
|
rules: &[Rule],
|
||||||
matching_rules: &mut ~[Rule]) {
|
matching_rules: &mut ~[Rule]) {
|
||||||
for rule in rules.iter() {
|
for rule in rules.iter() {
|
||||||
if matches_selector(rule.selector.get(), node, pseudo_element) {
|
if matches_compound_selector(rule.selector.get(), node) {
|
||||||
// TODO(pradeep): Is the cloning inefficient?
|
// TODO(pradeep): Is the cloning inefficient?
|
||||||
matching_rules.push(rule.clone());
|
matching_rules.push(rule.clone());
|
||||||
}
|
}
|
||||||
|
@ -198,7 +191,7 @@ impl SelectorMap {
|
||||||
|
|
||||||
/// Retrieve the first ID name in Rule, or None otherwise.
|
/// Retrieve the first ID name in Rule, or None otherwise.
|
||||||
fn get_id_name(rule: &Rule) -> Option<~str> {
|
fn get_id_name(rule: &Rule) -> Option<~str> {
|
||||||
let simple_selector_sequence = &rule.selector.get().compound_selectors.simple_selectors;
|
let simple_selector_sequence = &rule.selector.get().simple_selectors;
|
||||||
for ss in simple_selector_sequence.iter() {
|
for ss in simple_selector_sequence.iter() {
|
||||||
match *ss {
|
match *ss {
|
||||||
// TODO(pradeep): Implement case-sensitivity based on the document type and quirks
|
// TODO(pradeep): Implement case-sensitivity based on the document type and quirks
|
||||||
|
@ -212,7 +205,7 @@ impl SelectorMap {
|
||||||
|
|
||||||
/// Retrieve the FIRST class name in Rule, or None otherwise.
|
/// Retrieve the FIRST class name in Rule, or None otherwise.
|
||||||
fn get_class_name(rule: &Rule) -> Option<~str> {
|
fn get_class_name(rule: &Rule) -> Option<~str> {
|
||||||
let simple_selector_sequence = &rule.selector.get().compound_selectors.simple_selectors;
|
let simple_selector_sequence = &rule.selector.get().simple_selectors;
|
||||||
for ss in simple_selector_sequence.iter() {
|
for ss in simple_selector_sequence.iter() {
|
||||||
match *ss {
|
match *ss {
|
||||||
// TODO(pradeep): Implement case-sensitivity based on the document type and quirks
|
// TODO(pradeep): Implement case-sensitivity based on the document type and quirks
|
||||||
|
@ -226,7 +219,7 @@ impl SelectorMap {
|
||||||
|
|
||||||
/// Retrieve the name if it is a type selector, or None otherwise.
|
/// Retrieve the name if it is a type selector, or None otherwise.
|
||||||
fn get_element_name(rule: &Rule) -> Option<~str> {
|
fn get_element_name(rule: &Rule) -> Option<~str> {
|
||||||
let simple_selector_sequence = &rule.selector.get().compound_selectors.simple_selectors;
|
let simple_selector_sequence = &rule.selector.get().simple_selectors;
|
||||||
for ss in simple_selector_sequence.iter() {
|
for ss in simple_selector_sequence.iter() {
|
||||||
match *ss {
|
match *ss {
|
||||||
// HTML elements in HTML documents must be matched case-insensitively
|
// HTML elements in HTML documents must be matched case-insensitively
|
||||||
|
@ -240,51 +233,59 @@ impl SelectorMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Stylist {
|
pub struct Stylist {
|
||||||
priv ua_rule_map: PerOriginSelectorMap,
|
priv element_map: PerPseudoElementSelectorMap,
|
||||||
priv author_rule_map: PerOriginSelectorMap,
|
priv before_map: PerPseudoElementSelectorMap,
|
||||||
priv user_rule_map: PerOriginSelectorMap,
|
priv after_map: PerPseudoElementSelectorMap,
|
||||||
priv stylesheet_index: uint,
|
priv rules_source_order: uint,
|
||||||
priv pseudo_element: Option<PseudoElement>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stylist {
|
impl Stylist {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(pseudo_element: Option<PseudoElement>) -> Stylist {
|
pub fn new() -> Stylist {
|
||||||
Stylist {
|
Stylist {
|
||||||
ua_rule_map: PerOriginSelectorMap::new(),
|
element_map: PerPseudoElementSelectorMap::new(),
|
||||||
author_rule_map: PerOriginSelectorMap::new(),
|
before_map: PerPseudoElementSelectorMap::new(),
|
||||||
user_rule_map: PerOriginSelectorMap::new(),
|
after_map: PerPseudoElementSelectorMap::new(),
|
||||||
stylesheet_index: 0u,
|
rules_source_order: 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 (mut element_map, mut before_map, mut after_map) = match origin {
|
||||||
UserAgentOrigin => &mut self.ua_rule_map,
|
UserAgentOrigin => (
|
||||||
AuthorOrigin => &mut self.author_rule_map,
|
&mut self.element_map.user_agent,
|
||||||
UserOrigin => &mut self.user_rule_map,
|
&mut self.before_map.user_agent,
|
||||||
|
&mut self.after_map.user_agent,
|
||||||
|
),
|
||||||
|
AuthorOrigin => (
|
||||||
|
&mut self.element_map.author,
|
||||||
|
&mut self.before_map.author,
|
||||||
|
&mut self.after_map.author,
|
||||||
|
),
|
||||||
|
UserOrigin => (
|
||||||
|
&mut self.element_map.user,
|
||||||
|
&mut self.before_map.user,
|
||||||
|
&mut self.after_map.user,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let mut added_normal_declarations = false;
|
|
||||||
let mut added_important_declarations = false;
|
|
||||||
let mut style_rule_index = 0u;
|
|
||||||
|
|
||||||
// Take apart the StyleRule into individual Rules and insert
|
// Take apart the StyleRule into individual Rules and insert
|
||||||
// them into the SelectorMap of that priority.
|
// them into the SelectorMap of that priority.
|
||||||
macro_rules! append(
|
macro_rules! append(
|
||||||
($priority: ident, $flag: ident) => {
|
($priority: ident) => {
|
||||||
if style_rule.declarations.$priority.get().len() > 0 {
|
if style_rule.declarations.$priority.get().len() > 0 {
|
||||||
$flag = true;
|
|
||||||
for selector in style_rule.selectors.iter() {
|
for selector in style_rule.selectors.iter() {
|
||||||
// TODO: avoid copying?
|
let map = match selector.pseudo_element {
|
||||||
if selector.pseudo_element == self.pseudo_element {
|
None => &mut element_map,
|
||||||
rule_map.$priority.insert(Rule {
|
Some(Before) => &mut before_map,
|
||||||
selector: Arc::new(selector.clone()),
|
Some(After) => &mut after_map,
|
||||||
declarations: style_rule.declarations.$priority.clone(),
|
};
|
||||||
index: style_rule_index,
|
map.$priority.insert(Rule {
|
||||||
stylesheet_index: self.stylesheet_index,
|
selector: selector.compound_selectors.clone(),
|
||||||
});
|
specificity: selector.specificity,
|
||||||
}
|
declarations: style_rule.declarations.$priority.clone(),
|
||||||
|
source_order: self.rules_source_order,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -292,11 +293,10 @@ impl Stylist {
|
||||||
|
|
||||||
let device = &Device { media_type: Screen }; // TODO, use Print when printing
|
let device = &Device { media_type: Screen }; // TODO, use Print when printing
|
||||||
do iter_style_rules(stylesheet.rules.as_slice(), device) |style_rule| {
|
do iter_style_rules(stylesheet.rules.as_slice(), device) |style_rule| {
|
||||||
append!(normal, added_normal_declarations);
|
append!(normal);
|
||||||
append!(important, added_important_declarations);
|
append!(important);
|
||||||
style_rule_index += 1u;
|
self.rules_source_order += 1;
|
||||||
}
|
}
|
||||||
self.stylesheet_index += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the applicable CSS declarations for the given element. This corresponds to
|
/// Returns the applicable CSS declarations for the given element. This corresponds to
|
||||||
|
@ -305,20 +305,26 @@ 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() || self.pseudo_element.is_none(),
|
assert!(style_attribute.is_none() || pseudo_element.is_none(),
|
||||||
"Style attributes do not apply to pseudo-elements");
|
"Style attributes do not apply to pseudo-elements");
|
||||||
|
|
||||||
|
let map = match pseudo_element {
|
||||||
|
None => &self.element_map,
|
||||||
|
Some(Before) => &self.before_map,
|
||||||
|
Some(After) => &self.after_map,
|
||||||
|
};
|
||||||
// In cascading order:
|
// In cascading order:
|
||||||
let rule_map_list = [
|
let rule_map_list = [
|
||||||
&self.ua_rule_map.normal,
|
&map.user_agent.normal,
|
||||||
&self.user_rule_map.normal,
|
&map.user.normal,
|
||||||
&self.author_rule_map.normal,
|
&map.author.normal,
|
||||||
&self.author_rule_map.important,
|
&map.author.important,
|
||||||
&self.user_rule_map.important,
|
&map.user.important,
|
||||||
&self.ua_rule_map.important
|
&map.user_agent.important
|
||||||
];
|
];
|
||||||
|
|
||||||
// We keep track of the indices of each of the rule maps in the list we're building so that
|
// We keep track of the indices of each of the rule maps in the list we're building so that
|
||||||
|
@ -330,9 +336,9 @@ 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, self.pseudo_element, &mut matching_rules_list);
|
rule_map.get_all_matching_rules(element, &mut matching_rules_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = matching_rules_list.len();
|
let count = matching_rules_list.len();
|
||||||
|
|
||||||
let mut declaration_iter = matching_rules_list.move_iter().map(|rule| {
|
let mut declaration_iter = matching_rules_list.move_iter().map(|rule| {
|
||||||
|
@ -373,25 +379,6 @@ impl Stylist {
|
||||||
|
|
||||||
applicable_declarations
|
applicable_declarations
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pseudo_element(&self) -> Option<PseudoElement> {
|
|
||||||
self.pseudo_element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PerOriginRules {
|
|
||||||
normal: ~[Rule],
|
|
||||||
important: ~[Rule],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PerOriginRules {
|
|
||||||
#[inline]
|
|
||||||
fn new() -> PerOriginRules {
|
|
||||||
PerOriginRules {
|
|
||||||
normal: ~[],
|
|
||||||
important: ~[],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PerOriginSelectorMap {
|
struct PerOriginSelectorMap {
|
||||||
|
@ -409,40 +396,44 @@ impl PerOriginSelectorMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PerPseudoElementSelectorMap {
|
||||||
|
user_agent: PerOriginSelectorMap,
|
||||||
|
author: PerOriginSelectorMap,
|
||||||
|
user: PerOriginSelectorMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerPseudoElementSelectorMap {
|
||||||
|
#[inline]
|
||||||
|
fn new() -> PerPseudoElementSelectorMap {
|
||||||
|
PerPseudoElementSelectorMap {
|
||||||
|
user_agent: PerOriginSelectorMap::new(),
|
||||||
|
author: PerOriginSelectorMap::new(),
|
||||||
|
user: PerOriginSelectorMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
struct Rule {
|
struct Rule {
|
||||||
// This is an Arc because Rule will essentially be cloned for every node
|
// This is an Arc because Rule will essentially be cloned for every node
|
||||||
// that it matches. Selector contains an owned vector (through
|
// that it matches. Selector contains an owned vector (through
|
||||||
// CompoundSelector) and we want to avoid the allocation.
|
// CompoundSelector) and we want to avoid the allocation.
|
||||||
selector: Arc<Selector>,
|
selector: Arc<CompoundSelector>,
|
||||||
declarations: Arc<~[PropertyDeclaration]>,
|
declarations: Arc<~[PropertyDeclaration]>,
|
||||||
// Index of the parent StyleRule in the parent Stylesheet (useful for
|
// Precedence among rules of equal specificity
|
||||||
// breaking ties while cascading).
|
source_order: uint,
|
||||||
index: uint,
|
specificity: u32,
|
||||||
// Index of the parent stylesheet among all the stylesheets
|
|
||||||
stylesheet_index: uint,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Rule {
|
impl Ord for Rule {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn lt(&self, other: &Rule) -> bool {
|
fn lt(&self, other: &Rule) -> bool {
|
||||||
let this_rank = (self.selector.get().specificity, self.stylesheet_index, self.index);
|
let this_rank = (self.specificity, self.source_order);
|
||||||
let other_rank = (other.selector.get().specificity, other.stylesheet_index, other.index);
|
let other_rank = (other.specificity, other.source_order);
|
||||||
this_rank < other_rank
|
this_rank < other_rank
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn matches_selector<E:TElement,
|
|
||||||
N:TNode<E>>(
|
|
||||||
selector: &Selector,
|
|
||||||
element: &N,
|
|
||||||
pseudo_element: Option<PseudoElement>)
|
|
||||||
-> bool {
|
|
||||||
selector.pseudo_element == pseudo_element &&
|
|
||||||
matches_compound_selector::<E,N>(&selector.compound_selectors, element)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector, element: &N)
|
fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector, element: &N)
|
||||||
-> bool {
|
-> bool {
|
||||||
if !do selector.simple_selectors.iter().all |simple_selector| {
|
if !do selector.simple_selectors.iter().all |simple_selector| {
|
||||||
|
@ -725,66 +716,71 @@ fn match_attribute<E:TElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Helper method to get some Rules from selector strings.
|
#[cfg(test)]
|
||||||
/// Each sublist of the result contains the Rules for one StyleRule.
|
mod tests {
|
||||||
fn get_mock_rules(css_selectors: &[&str]) -> ~[~[Rule]] {
|
use extra::arc::Arc;
|
||||||
use namespaces::NamespaceMap;
|
use super::{Rule, SelectorMap};
|
||||||
use selectors::parse_selector_list;
|
|
||||||
use cssparser::tokenize;
|
|
||||||
|
|
||||||
let namespaces = NamespaceMap::new();
|
/// Helper method to get some Rules from selector strings.
|
||||||
css_selectors.iter().enumerate().map(|(i, selectors)| {
|
/// Each sublist of the result contains the Rules for one StyleRule.
|
||||||
parse_selector_list(tokenize(*selectors).map(|(c, _)| c).to_owned_vec(), &namespaces)
|
fn get_mock_rules(css_selectors: &[&str]) -> ~[~[Rule]] {
|
||||||
.unwrap().move_iter().map(|s| {
|
use namespaces::NamespaceMap;
|
||||||
Rule {
|
use selectors::parse_selector_list;
|
||||||
selector: Arc::new(s),
|
use cssparser::tokenize;
|
||||||
declarations: Arc::new(~[]),
|
|
||||||
index: i,
|
let namespaces = NamespaceMap::new();
|
||||||
stylesheet_index: 0u,
|
css_selectors.iter().enumerate().map(|(i, selectors)| {
|
||||||
}
|
parse_selector_list(tokenize(*selectors).map(|(c, _)| c).to_owned_vec(), &namespaces)
|
||||||
|
.unwrap().move_iter().map(|s| {
|
||||||
|
Rule {
|
||||||
|
specificity: s.specificity,
|
||||||
|
selector: s.compound_selectors,
|
||||||
|
declarations: Arc::new(~[]),
|
||||||
|
source_order: i,
|
||||||
|
}
|
||||||
|
}).to_owned_vec()
|
||||||
}).to_owned_vec()
|
}).to_owned_vec()
|
||||||
}).to_owned_vec()
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rule_ordering_same_specificity(){
|
fn test_rule_ordering_same_specificity(){
|
||||||
let rules_list = get_mock_rules(["a.intro", "img.sidebar"]);
|
let rules_list = get_mock_rules(["a.intro", "img.sidebar"]);
|
||||||
let rule1 = rules_list[0][0].clone();
|
let rule1 = rules_list[0][0].clone();
|
||||||
let rule2 = rules_list[1][0].clone();
|
let rule2 = rules_list[1][0].clone();
|
||||||
assert!(rule1 < rule2, "The rule that comes later should win.");
|
assert!(rule1 < rule2, "The rule that comes later should win.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_id_name(){
|
fn test_get_id_name(){
|
||||||
let rules_list = get_mock_rules([".intro", "#top"]);
|
let rules_list = get_mock_rules([".intro", "#top"]);
|
||||||
assert_eq!(SelectorMap::get_id_name(&rules_list[0][0]), None);
|
assert_eq!(SelectorMap::get_id_name(&rules_list[0][0]), None);
|
||||||
assert_eq!(SelectorMap::get_id_name(&rules_list[1][0]), Some(~"top"));
|
assert_eq!(SelectorMap::get_id_name(&rules_list[1][0]), Some(~"top"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_class_name(){
|
fn test_get_class_name(){
|
||||||
let rules_list = get_mock_rules([".intro.foo", "#top"]);
|
let rules_list = get_mock_rules([".intro.foo", "#top"]);
|
||||||
assert_eq!(SelectorMap::get_class_name(&rules_list[0][0]), Some(~"intro"));
|
assert_eq!(SelectorMap::get_class_name(&rules_list[0][0]), Some(~"intro"));
|
||||||
assert_eq!(SelectorMap::get_class_name(&rules_list[1][0]), None);
|
assert_eq!(SelectorMap::get_class_name(&rules_list[1][0]), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_element_name(){
|
fn test_get_element_name(){
|
||||||
let rules_list = get_mock_rules(["img.foo", "#top", "IMG", "ImG"]);
|
let rules_list = get_mock_rules(["img.foo", "#top", "IMG", "ImG"]);
|
||||||
assert_eq!(SelectorMap::get_element_name(&rules_list[0][0]), Some(~"img"));
|
assert_eq!(SelectorMap::get_element_name(&rules_list[0][0]), Some(~"img"));
|
||||||
assert_eq!(SelectorMap::get_element_name(&rules_list[1][0]), None);
|
assert_eq!(SelectorMap::get_element_name(&rules_list[1][0]), None);
|
||||||
assert_eq!(SelectorMap::get_element_name(&rules_list[2][0]), Some(~"img"));
|
assert_eq!(SelectorMap::get_element_name(&rules_list[2][0]), Some(~"img"));
|
||||||
assert_eq!(SelectorMap::get_element_name(&rules_list[3][0]), Some(~"img"));
|
assert_eq!(SelectorMap::get_element_name(&rules_list[3][0]), Some(~"img"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_insert(){
|
fn test_insert(){
|
||||||
let rules_list = get_mock_rules([".intro.foo", "#top"]);
|
let rules_list = get_mock_rules([".intro.foo", "#top"]);
|
||||||
let mut selector_map = SelectorMap::new();
|
let mut selector_map = SelectorMap::new();
|
||||||
selector_map.insert(rules_list[1][0].clone());
|
selector_map.insert(rules_list[1][0].clone());
|
||||||
assert_eq!(1, selector_map.id_hash.find(&~"top").unwrap()[0].index);
|
assert_eq!(1, selector_map.id_hash.find(&~"top").unwrap()[0].source_order);
|
||||||
selector_map.insert(rules_list[0][0].clone());
|
selector_map.insert(rules_list[0][0].clone());
|
||||||
assert_eq!(0, selector_map.class_hash.find(&~"intro").unwrap()[0].index);
|
assert_eq!(0, selector_map.class_hash.find(&~"intro").unwrap()[0].source_order);
|
||||||
assert!(selector_map.class_hash.find(&~"foo").is_none());
|
assert!(selector_map.class_hash.find(&~"foo").is_none());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,25 @@
|
||||||
|
|
||||||
use std::{vec, iter};
|
use std::{vec, iter};
|
||||||
use std::ascii::StrAsciiExt;
|
use std::ascii::StrAsciiExt;
|
||||||
|
use extra::arc::Arc;
|
||||||
|
|
||||||
use cssparser::ast::*;
|
use cssparser::ast::*;
|
||||||
use cssparser::parse_nth;
|
use cssparser::parse_nth;
|
||||||
|
|
||||||
use namespaces::NamespaceMap;
|
use namespaces::NamespaceMap;
|
||||||
|
|
||||||
|
|
||||||
|
// Only used in tests
|
||||||
|
impl Eq for Arc<CompoundSelector> {
|
||||||
|
fn eq(&self, other: &Arc<CompoundSelector>) -> bool {
|
||||||
|
self.get() == other.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[deriving(Eq, Clone)]
|
#[deriving(Eq, Clone)]
|
||||||
pub struct Selector {
|
pub struct Selector {
|
||||||
compound_selectors: CompoundSelector,
|
compound_selectors: Arc<CompoundSelector>,
|
||||||
pseudo_element: Option<PseudoElement>,
|
pseudo_element: Option<PseudoElement>,
|
||||||
specificity: u32,
|
specificity: u32,
|
||||||
}
|
}
|
||||||
|
@ -23,8 +34,8 @@ pub static STYLE_ATTRIBUTE_SPECIFICITY: u32 = 1 << 31;
|
||||||
pub enum PseudoElement {
|
pub enum PseudoElement {
|
||||||
Before,
|
Before,
|
||||||
After,
|
After,
|
||||||
FirstLine,
|
// FirstLine,
|
||||||
FirstLetter,
|
// FirstLetter,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,7 +168,7 @@ fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap)
|
||||||
}
|
}
|
||||||
Some(Selector {
|
Some(Selector {
|
||||||
specificity: compute_specificity(&compound, &pseudo_element),
|
specificity: compute_specificity(&compound, &pseudo_element),
|
||||||
compound_selectors: compound,
|
compound_selectors: Arc::new(compound),
|
||||||
pseudo_element: pseudo_element,
|
pseudo_element: pseudo_element,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -310,14 +321,14 @@ fn parse_one_simple_selector(iter: &mut Iter, namespaces: &NamespaceMap, inside_
|
||||||
Some(Ident(name)) => match parse_simple_pseudo_class(name) {
|
Some(Ident(name)) => match parse_simple_pseudo_class(name) {
|
||||||
None => {
|
None => {
|
||||||
// FIXME: Workaround for https://github.com/mozilla/rust/issues/10683
|
// FIXME: Workaround for https://github.com/mozilla/rust/issues/10683
|
||||||
let name_lower = name.to_ascii_lower();
|
let name_lower = name.to_ascii_lower();
|
||||||
match name_lower.as_slice() {
|
match name_lower.as_slice() {
|
||||||
// Supported CSS 2.1 pseudo-elements only.
|
// Supported CSS 2.1 pseudo-elements only.
|
||||||
// ** Do not add to this list! **
|
// ** Do not add to this list! **
|
||||||
"before" => PseudoElementResult(Before),
|
"before" => PseudoElementResult(Before),
|
||||||
"after" => PseudoElementResult(After),
|
"after" => PseudoElementResult(After),
|
||||||
"first-line" => PseudoElementResult(FirstLine),
|
// "first-line" => PseudoElementResult(FirstLine),
|
||||||
"first-letter" => PseudoElementResult(FirstLetter),
|
// "first-letter" => PseudoElementResult(FirstLetter),
|
||||||
_ => InvalidSimpleSelector
|
_ => InvalidSimpleSelector
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -470,7 +481,7 @@ fn parse_functional_pseudo_class(name: ~str, arguments: ~[ComponentValue],
|
||||||
namespaces: &NamespaceMap, inside_negation: bool)
|
namespaces: &NamespaceMap, inside_negation: bool)
|
||||||
-> Option<SimpleSelector> {
|
-> Option<SimpleSelector> {
|
||||||
// FIXME: Workaround for https://github.com/mozilla/rust/issues/10683
|
// FIXME: Workaround for https://github.com/mozilla/rust/issues/10683
|
||||||
let name_lower = name.to_ascii_lower();
|
let name_lower = name.to_ascii_lower();
|
||||||
match name_lower.as_slice() {
|
match name_lower.as_slice() {
|
||||||
// "lang" => parse_lang(arguments),
|
// "lang" => parse_lang(arguments),
|
||||||
"nth-child" => parse_nth(arguments).map(|(a, b)| NthChild(a, b)),
|
"nth-child" => parse_nth(arguments).map(|(a, b)| NthChild(a, b)),
|
||||||
|
@ -485,13 +496,13 @@ fn parse_functional_pseudo_class(name: ~str, arguments: ~[ComponentValue],
|
||||||
|
|
||||||
fn parse_pseudo_element(name: ~str) -> Option<PseudoElement> {
|
fn parse_pseudo_element(name: ~str) -> Option<PseudoElement> {
|
||||||
// FIXME: Workaround for https://github.com/mozilla/rust/issues/10683
|
// FIXME: Workaround for https://github.com/mozilla/rust/issues/10683
|
||||||
let name_lower = name.to_ascii_lower();
|
let name_lower = name.to_ascii_lower();
|
||||||
match name_lower.as_slice() {
|
match name_lower.as_slice() {
|
||||||
// All supported pseudo-elements
|
// All supported pseudo-elements
|
||||||
"before" => Some(Before),
|
"before" => Some(Before),
|
||||||
"after" => Some(After),
|
"after" => Some(After),
|
||||||
"first-line" => Some(FirstLine),
|
// "first-line" => Some(FirstLine),
|
||||||
"first-letter" => Some(FirstLetter),
|
// "first-letter" => Some(FirstLetter),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -549,6 +560,7 @@ fn skip_whitespace(iter: &mut Iter) -> bool {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use extra::arc::Arc;
|
||||||
use cssparser;
|
use cssparser;
|
||||||
use namespaces::NamespaceMap;
|
use namespaces::NamespaceMap;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -567,48 +579,48 @@ mod tests {
|
||||||
fn test_parsing() {
|
fn test_parsing() {
|
||||||
assert_eq!(parse(""), None)
|
assert_eq!(parse(""), None)
|
||||||
assert_eq!(parse("e"), Some(~[Selector{
|
assert_eq!(parse("e"), Some(~[Selector{
|
||||||
compound_selectors: CompoundSelector {
|
compound_selectors: Arc::new(CompoundSelector {
|
||||||
simple_selectors: ~[LocalNameSelector(~"e")],
|
simple_selectors: ~[LocalNameSelector(~"e")],
|
||||||
next: None,
|
next: None,
|
||||||
},
|
}),
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(0, 0, 1),
|
specificity: specificity(0, 0, 1),
|
||||||
}]))
|
}]))
|
||||||
assert_eq!(parse(".foo"), Some(~[Selector{
|
assert_eq!(parse(".foo"), Some(~[Selector{
|
||||||
compound_selectors: CompoundSelector {
|
compound_selectors: Arc::new(CompoundSelector {
|
||||||
simple_selectors: ~[ClassSelector(~"foo")],
|
simple_selectors: ~[ClassSelector(~"foo")],
|
||||||
next: None,
|
next: None,
|
||||||
},
|
}),
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(0, 1, 0),
|
specificity: specificity(0, 1, 0),
|
||||||
}]))
|
}]))
|
||||||
assert_eq!(parse("#bar"), Some(~[Selector{
|
assert_eq!(parse("#bar"), Some(~[Selector{
|
||||||
compound_selectors: CompoundSelector {
|
compound_selectors: Arc::new(CompoundSelector {
|
||||||
simple_selectors: ~[IDSelector(~"bar")],
|
simple_selectors: ~[IDSelector(~"bar")],
|
||||||
next: None,
|
next: None,
|
||||||
},
|
}),
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(1, 0, 0),
|
specificity: specificity(1, 0, 0),
|
||||||
}]))
|
}]))
|
||||||
assert_eq!(parse("e.foo#bar"), Some(~[Selector{
|
assert_eq!(parse("e.foo#bar"), Some(~[Selector{
|
||||||
compound_selectors: CompoundSelector {
|
compound_selectors: Arc::new(CompoundSelector {
|
||||||
simple_selectors: ~[LocalNameSelector(~"e"),
|
simple_selectors: ~[LocalNameSelector(~"e"),
|
||||||
ClassSelector(~"foo"),
|
ClassSelector(~"foo"),
|
||||||
IDSelector(~"bar")],
|
IDSelector(~"bar")],
|
||||||
next: None,
|
next: None,
|
||||||
},
|
}),
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(1, 1, 1),
|
specificity: specificity(1, 1, 1),
|
||||||
}]))
|
}]))
|
||||||
assert_eq!(parse("e.foo #bar"), Some(~[Selector{
|
assert_eq!(parse("e.foo #bar"), Some(~[Selector{
|
||||||
compound_selectors: CompoundSelector {
|
compound_selectors: Arc::new(CompoundSelector {
|
||||||
simple_selectors: ~[IDSelector(~"bar")],
|
simple_selectors: ~[IDSelector(~"bar")],
|
||||||
next: Some((~CompoundSelector {
|
next: Some((~CompoundSelector {
|
||||||
simple_selectors: ~[LocalNameSelector(~"e"),
|
simple_selectors: ~[LocalNameSelector(~"e"),
|
||||||
ClassSelector(~"foo")],
|
ClassSelector(~"foo")],
|
||||||
next: None,
|
next: None,
|
||||||
}, Descendant)),
|
}, Descendant)),
|
||||||
},
|
}),
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(1, 1, 1),
|
specificity: specificity(1, 1, 1),
|
||||||
}]))
|
}]))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue