aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorbors-servo <release+servo@mozilla.com>2014-01-08 09:58:29 -0800
committerbors-servo <release+servo@mozilla.com>2014-01-08 09:58:29 -0800
commit728fb9a7dedf67445e7f12eafb314117efede70d (patch)
tree4cc09cd77970d42b7172ca80736cec79307244f3 /src
parent99157c64128ad9317e4296b9e28fe4e10bc306f6 (diff)
parent8d8a8ad6e7cc20389eebbf25069ccfa629617c0c (diff)
downloadservo-728fb9a7dedf67445e7f12eafb314117efede70d.tar.gz
servo-728fb9a7dedf67445e7f12eafb314117efede70d.zip
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)
Diffstat (limited to 'src')
-rw-r--r--src/components/main/css/matching.rs165
-rw-r--r--src/components/main/css/select.rs7
-rw-r--r--src/components/main/layout/layout_task.rs23
-rw-r--r--src/components/style/selector_matching.rs312
-rw-r--r--src/components/style/selectors.rs54
5 files changed, 243 insertions, 318 deletions
diff --git a/src/components/main/css/matching.rs b/src/components/main/css/matching.rs
index e739a4c0c2e..cace6ffd5f1 100644
--- a/src/components/main/css/matching.rs
+++ b/src/components/main/css/matching.rs
@@ -22,37 +22,33 @@ use style::{Before, After};
pub trait MatchMethods {
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>);
}
impl<'self> MatchMethods for LayoutNode<'self> {
fn match_node(&self, stylist: &Stylist) {
- let applicable_declarations = do self.with_element |element| {
- let style_attribute = match *element.style_attribute() {
+ let style_attribute = do self.with_element |element| {
+ match *element.style_attribute() {
None => None,
Some(ref style_attribute) => Some(style_attribute)
- };
- stylist.get_applicable_declarations(self, style_attribute)
+ }
};
match *self.mutate_layout_data().ptr {
Some(ref mut layout_data) => {
- 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,
- _ => {}
- }
+ layout_data.applicable_declarations = stylist.get_applicable_declarations(
+ self, style_attribute, None);
+ layout_data.before_applicable_declarations = stylist.get_applicable_declarations(
+ self, None, Some(Before));
+ layout_data.after_applicable_declarations = stylist.get_applicable_declarations(
+ self, None, Some(After));
}
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 mut node_count = 0;
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() {
if nodes.len() > 0 {
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
// 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
@@ -81,19 +77,16 @@ impl<'self> MatchMethods for LayoutNode<'self> {
cast::transmute(nodes)
};
- do task::spawn_with((evil, stylists)) |(evil, stylists)| {
+ do task::spawn_with((evil, stylist)) |(evil, stylist)| {
let nodes: ~[LayoutNode] = unsafe {
cast::transmute(evil)
};
let nodes = Cell::new(nodes);
- for stylist in stylists.iter() {
- do stylist.read |stylist| {
- nodes.with_ref(|nodes|{
- for node in nodes.iter() {
- node.match_node(stylist);
- }
- });
+ do stylist.read |stylist| {
+ let nodes = nodes.take();
+ for node in nodes.iter() {
+ node.match_node(stylist);
}
}
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())))
+ 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
+ };
- 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())
+ 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)
}
}
- *style = Some(computed_values)
- }
- }
- }
+ }}
+ );
- 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);
+ cascade_node!(before_applicable_declarations, before_style);
}
}
- self.cascade_node(parent);
+ cascade_node!(applicable_declarations, style);
unsafe {
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);
}
}
diff --git a/src/components/main/css/select.rs b/src/components/main/css/select.rs
index 5292c963dc3..10ac49875d5 100644
--- a/src/components/main/css/select.rs
+++ b/src/components/main/css/select.rs
@@ -3,17 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use style::{Stylesheet, Stylist, UserAgentOrigin, with_errors_silenced};
-use style::PseudoElement;
use extra::url;
-pub fn new_stylist(pseudo_element: Option<PseudoElement>) -> Stylist {
- let mut stylist = Stylist::new(pseudo_element);
+pub fn new_stylist() -> Stylist {
+ let mut stylist = Stylist::new();
let ua_stylesheet = with_errors_silenced(|| Stylesheet::from_bytes(
include_bin!("user-agent.css"),
url::from_str("chrome:///user-agent.css").unwrap(),
None,
None));
- stylist.add_stylesheet(&ua_stylesheet, UserAgentOrigin);
+ stylist.add_stylesheet(ua_stylesheet, UserAgentOrigin);
stylist
}
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index 9ce50a0bf4c..2224401188a 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -51,7 +51,6 @@ use std::comm::Port;
use std::task;
use std::util;
use style::{AuthorOrigin, Stylesheet, Stylist};
-use style::{Before, After};
/// Information needed by the layout task.
struct LayoutTask {
@@ -82,7 +81,7 @@ struct LayoutTask {
/// A cached display list.
display_list: Option<Arc<DisplayList<OpaqueNode>>>,
- stylists: ~[RWArc<Stylist>],
+ stylist: RWArc<Stylist>,
/// The channel on which messages can be sent to the profiler.
profiler_chan: ProfilerChan,
@@ -237,14 +236,6 @@ impl LayoutTask {
profiler_chan: ProfilerChan)
-> 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 {
id: id,
port: port,
@@ -257,7 +248,7 @@ impl LayoutTask {
display_list: None,
- stylists: stylists,
+ stylist: RWArc::new(new_stylist()),
profiler_chan: profiler_chan,
opts: opts.clone()
}
@@ -356,12 +347,8 @@ impl LayoutTask {
fn handle_add_stylesheet(&mut self, sheet: Stylesheet) {
let sheet = Cell::new(sheet);
- for stylist in self.stylists.iter() {
- do stylist.write |stylist| {
- sheet.with_ref(|sheet|{
- stylist.add_stylesheet(sheet, AuthorOrigin);
- });
- }
+ do self.stylist.write |stylist| {
+ stylist.add_stylesheet(sheet.take(), AuthorOrigin);
}
}
@@ -458,7 +445,7 @@ impl LayoutTask {
ReflowDocumentDamage => {}
_ => {
do profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone()) {
- node.match_subtree(self.stylists.clone());
+ node.match_subtree(self.stylist.clone());
node.cascade_subtree(None);
}
}
diff --git a/src/components/style/selector_matching.rs b/src/components/style/selector_matching.rs
index f949b2b8221..3d48fd66a57 100644
--- a/src/components/style/selector_matching.rs
+++ b/src/components/style/selector_matching.rs
@@ -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,
/// element name, etc. will contain the Rules that actually match that
/// node.
-pub struct SelectorMap {
+struct SelectorMap {
// TODO: Tune the initial capacity of the HashMap
// FIXME: Use interned strings
- priv id_hash: HashMap<~str, ~[Rule]>,
- priv class_hash: HashMap<~str, ~[Rule]>,
- priv element_hash: HashMap<~str, ~[Rule]>,
+ id_hash: HashMap<~str, ~[Rule]>,
+ class_hash: HashMap<~str, ~[Rule]>,
+ element_hash: HashMap<~str, ~[Rule]>,
// For Rules that don't have ID, class, or element selectors.
- priv universal_rules: ~[Rule],
+ universal_rules: ~[Rule],
}
impl SelectorMap {
@@ -70,7 +70,6 @@ impl SelectorMap {
N:TNode<E>>(
&self,
node: &N,
- pseudo_element: Option<PseudoElement>,
matching_rules_list: &mut ~[Rule]) {
// 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();
@@ -78,7 +77,6 @@ impl SelectorMap {
match element.get_attr(None, "id") {
Some(id) => {
SelectorMap::get_matching_rules_from_hash(node,
- pseudo_element,
&self.id_hash,
id,
matching_rules_list)
@@ -90,7 +88,6 @@ impl SelectorMap {
Some(ref class_attr) => {
for class in class_attr.split_iter(SELECTOR_WHITESPACE) {
SelectorMap::get_matching_rules_from_hash(node,
- pseudo_element,
&self.class_hash,
class,
matching_rules_list)
@@ -102,12 +99,10 @@ impl SelectorMap {
// HTML elements in HTML documents must be matched case-insensitively.
// TODO(pradeep): Case-sensitivity depends on the document type.
SelectorMap::get_matching_rules_from_hash(node,
- pseudo_element,
&self.element_hash,
element.get_local_name().to_ascii_lower(),
matching_rules_list);
SelectorMap::get_matching_rules(node,
- pseudo_element,
self.universal_rules,
matching_rules_list);
});
@@ -119,27 +114,25 @@ impl SelectorMap {
fn get_matching_rules_from_hash<E:TElement,
N:TNode<E>>(
node: &N,
- pseudo_element: Option<PseudoElement>,
- hash: &HashMap<~str,~[Rule]>,
+ hash: &HashMap<~str,~[Rule]>,
key: &str,
matching_rules: &mut ~[Rule]) {
match hash.find(&key.to_str()) {
Some(rules) => {
- SelectorMap::get_matching_rules(node, pseudo_element, *rules, matching_rules)
+ SelectorMap::get_matching_rules(node, *rules, matching_rules)
}
None => {}
}
}
-
+
/// Adds rules in `rules` that match `node` to the `matching_rules` list.
fn get_matching_rules<E:TElement,
N:TNode<E>>(
node: &N,
- pseudo_element: Option<PseudoElement>,
rules: &[Rule],
matching_rules: &mut ~[Rule]) {
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?
matching_rules.push(rule.clone());
}
@@ -198,7 +191,7 @@ impl SelectorMap {
/// Retrieve the first ID name in Rule, or None otherwise.
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() {
match *ss {
// 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.
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() {
match *ss {
// 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.
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() {
match *ss {
// HTML elements in HTML documents must be matched case-insensitively
@@ -240,51 +233,59 @@ impl SelectorMap {
}
pub struct Stylist {
- priv ua_rule_map: PerOriginSelectorMap,
- priv author_rule_map: PerOriginSelectorMap,
- priv user_rule_map: PerOriginSelectorMap,
- priv stylesheet_index: uint,
- priv pseudo_element: Option<PseudoElement>,
+ priv element_map: PerPseudoElementSelectorMap,
+ priv before_map: PerPseudoElementSelectorMap,
+ priv after_map: PerPseudoElementSelectorMap,
+ priv rules_source_order: uint,
}
impl Stylist {
#[inline]
- pub fn new(pseudo_element: Option<PseudoElement>) -> Stylist {
+ pub fn new() -> Stylist {
Stylist {
- ua_rule_map: PerOriginSelectorMap::new(),
- author_rule_map: PerOriginSelectorMap::new(),
- user_rule_map: PerOriginSelectorMap::new(),
- stylesheet_index: 0u,
- pseudo_element: pseudo_element,
+ element_map: PerPseudoElementSelectorMap::new(),
+ before_map: PerPseudoElementSelectorMap::new(),
+ after_map: PerPseudoElementSelectorMap::new(),
+ rules_source_order: 0u,
}
}
- pub fn add_stylesheet(&mut self, stylesheet: &Stylesheet, origin: StylesheetOrigin) {
- let rule_map = match origin {
- UserAgentOrigin => &mut self.ua_rule_map,
- AuthorOrigin => &mut self.author_rule_map,
- UserOrigin => &mut self.user_rule_map,
+ pub fn add_stylesheet(&mut self, stylesheet: Stylesheet, origin: StylesheetOrigin) {
+ let (mut element_map, mut before_map, mut after_map) = match origin {
+ UserAgentOrigin => (
+ &mut self.element_map.user_agent,
+ &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
// them into the SelectorMap of that priority.
macro_rules! append(
- ($priority: ident, $flag: ident) => {
+ ($priority: ident) => {
if style_rule.declarations.$priority.get().len() > 0 {
- $flag = true;
for selector in style_rule.selectors.iter() {
- // TODO: avoid copying?
- if selector.pseudo_element == self.pseudo_element {
- rule_map.$priority.insert(Rule {
- selector: Arc::new(selector.clone()),
- declarations: style_rule.declarations.$priority.clone(),
- index: style_rule_index,
- stylesheet_index: self.stylesheet_index,
- });
- }
+ let map = match selector.pseudo_element {
+ None => &mut element_map,
+ Some(Before) => &mut before_map,
+ Some(After) => &mut after_map,
+ };
+ map.$priority.insert(Rule {
+ 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
do iter_style_rules(stylesheet.rules.as_slice(), device) |style_rule| {
- append!(normal, added_normal_declarations);
- append!(important, added_important_declarations);
- style_rule_index += 1u;
+ append!(normal);
+ append!(important);
+ self.rules_source_order += 1;
}
- self.stylesheet_index += 1;
}
/// Returns the applicable CSS declarations for the given element. This corresponds to
@@ -305,20 +305,26 @@ impl Stylist {
N:TNode<E>>(
&self,
element: &N,
- style_attribute: Option<&PropertyDeclarationBlock>)
+ style_attribute: Option<&PropertyDeclarationBlock>,
+ pseudo_element: Option<PseudoElement>)
-> ~[Arc<~[PropertyDeclaration]>] {
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");
-
+
+ let map = match pseudo_element {
+ None => &self.element_map,
+ Some(Before) => &self.before_map,
+ Some(After) => &self.after_map,
+ };
// In cascading order:
let rule_map_list = [
- &self.ua_rule_map.normal,
- &self.user_rule_map.normal,
- &self.author_rule_map.normal,
- &self.author_rule_map.important,
- &self.user_rule_map.important,
- &self.ua_rule_map.important
+ &map.user_agent.normal,
+ &map.user.normal,
+ &map.author.normal,
+ &map.author.important,
+ &map.user.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
@@ -330,9 +336,9 @@ impl Stylist {
for (i, rule_map) in rule_map_list.iter().enumerate() {
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 mut declaration_iter = matching_rules_list.move_iter().map(|rule| {
@@ -373,25 +379,6 @@ impl Stylist {
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 {
@@ -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)]
struct Rule {
// This is an Arc because Rule will essentially be cloned for every node
// that it matches. Selector contains an owned vector (through
// CompoundSelector) and we want to avoid the allocation.
- selector: Arc<Selector>,
+ selector: Arc<CompoundSelector>,
declarations: Arc<~[PropertyDeclaration]>,
- // Index of the parent StyleRule in the parent Stylesheet (useful for
- // breaking ties while cascading).
- index: uint,
- // Index of the parent stylesheet among all the stylesheets
- stylesheet_index: uint,
+ // Precedence among rules of equal specificity
+ source_order: uint,
+ specificity: u32,
}
impl Ord for Rule {
#[inline]
fn lt(&self, other: &Rule) -> bool {
- let this_rank = (self.selector.get().specificity, self.stylesheet_index, self.index);
- let other_rank = (other.selector.get().specificity, other.stylesheet_index, other.index);
+ let this_rank = (self.specificity, self.source_order);
+ let other_rank = (other.specificity, other.source_order);
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)
-> bool {
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.
-/// Each sublist of the result contains the Rules for one StyleRule.
-fn get_mock_rules(css_selectors: &[&str]) -> ~[~[Rule]] {
- use namespaces::NamespaceMap;
- use selectors::parse_selector_list;
- use cssparser::tokenize;
-
- let namespaces = NamespaceMap::new();
- 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 {
- selector: Arc::new(s),
- declarations: Arc::new(~[]),
- index: i,
- stylesheet_index: 0u,
- }
+#[cfg(test)]
+mod tests {
+ use extra::arc::Arc;
+ use super::{Rule, SelectorMap};
+
+ /// Helper method to get some Rules from selector strings.
+ /// Each sublist of the result contains the Rules for one StyleRule.
+ fn get_mock_rules(css_selectors: &[&str]) -> ~[~[Rule]] {
+ use namespaces::NamespaceMap;
+ use selectors::parse_selector_list;
+ use cssparser::tokenize;
+
+ let namespaces = NamespaceMap::new();
+ 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()
-}
+ }
-#[test]
-fn test_rule_ordering_same_specificity(){
- let rules_list = get_mock_rules(["a.intro", "img.sidebar"]);
- let rule1 = rules_list[0][0].clone();
- let rule2 = rules_list[1][0].clone();
- assert!(rule1 < rule2, "The rule that comes later should win.");
-}
+ #[test]
+ fn test_rule_ordering_same_specificity(){
+ let rules_list = get_mock_rules(["a.intro", "img.sidebar"]);
+ let rule1 = rules_list[0][0].clone();
+ let rule2 = rules_list[1][0].clone();
+ assert!(rule1 < rule2, "The rule that comes later should win.");
+ }
-#[test]
-fn test_get_id_name(){
- 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[1][0]), Some(~"top"));
-}
+ #[test]
+ fn test_get_id_name(){
+ 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[1][0]), Some(~"top"));
+ }
-#[test]
-fn test_get_class_name(){
- 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[1][0]), None);
-}
+ #[test]
+ fn test_get_class_name(){
+ 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[1][0]), None);
+ }
-#[test]
-fn test_get_element_name(){
- 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[1][0]), None);
- assert_eq!(SelectorMap::get_element_name(&rules_list[2][0]), Some(~"img"));
- assert_eq!(SelectorMap::get_element_name(&rules_list[3][0]), Some(~"img"));
-}
+ #[test]
+ fn test_get_element_name(){
+ 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[1][0]), None);
+ assert_eq!(SelectorMap::get_element_name(&rules_list[2][0]), Some(~"img"));
+ assert_eq!(SelectorMap::get_element_name(&rules_list[3][0]), Some(~"img"));
+ }
-#[test]
-fn test_insert(){
- let rules_list = get_mock_rules([".intro.foo", "#top"]);
- let mut selector_map = SelectorMap::new();
- selector_map.insert(rules_list[1][0].clone());
- assert_eq!(1, selector_map.id_hash.find(&~"top").unwrap()[0].index);
- selector_map.insert(rules_list[0][0].clone());
- assert_eq!(0, selector_map.class_hash.find(&~"intro").unwrap()[0].index);
- assert!(selector_map.class_hash.find(&~"foo").is_none());
+ #[test]
+ fn test_insert(){
+ let rules_list = get_mock_rules([".intro.foo", "#top"]);
+ let mut selector_map = SelectorMap::new();
+ selector_map.insert(rules_list[1][0].clone());
+ assert_eq!(1, selector_map.id_hash.find(&~"top").unwrap()[0].source_order);
+ selector_map.insert(rules_list[0][0].clone());
+ assert_eq!(0, selector_map.class_hash.find(&~"intro").unwrap()[0].source_order);
+ assert!(selector_map.class_hash.find(&~"foo").is_none());
+ }
}
-
diff --git a/src/components/style/selectors.rs b/src/components/style/selectors.rs
index 3935121fadc..2e401e1a407 100644
--- a/src/components/style/selectors.rs
+++ b/src/components/style/selectors.rs
@@ -4,14 +4,25 @@
use std::{vec, iter};
use std::ascii::StrAsciiExt;
+use extra::arc::Arc;
+
use cssparser::ast::*;
use cssparser::parse_nth;
+
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)]
pub struct Selector {
- compound_selectors: CompoundSelector,
+ compound_selectors: Arc<CompoundSelector>,
pseudo_element: Option<PseudoElement>,
specificity: u32,
}
@@ -23,8 +34,8 @@ pub static STYLE_ATTRIBUTE_SPECIFICITY: u32 = 1 << 31;
pub enum PseudoElement {
Before,
After,
- FirstLine,
- FirstLetter,
+// FirstLine,
+// FirstLetter,
}
@@ -157,7 +168,7 @@ fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap)
}
Some(Selector {
specificity: compute_specificity(&compound, &pseudo_element),
- compound_selectors: compound,
+ compound_selectors: Arc::new(compound),
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) {
None => {
// 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() {
// Supported CSS 2.1 pseudo-elements only.
// ** Do not add to this list! **
"before" => PseudoElementResult(Before),
"after" => PseudoElementResult(After),
- "first-line" => PseudoElementResult(FirstLine),
- "first-letter" => PseudoElementResult(FirstLetter),
+// "first-line" => PseudoElementResult(FirstLine),
+// "first-letter" => PseudoElementResult(FirstLetter),
_ => InvalidSimpleSelector
}
},
@@ -470,7 +481,7 @@ fn parse_functional_pseudo_class(name: ~str, arguments: ~[ComponentValue],
namespaces: &NamespaceMap, inside_negation: bool)
-> Option<SimpleSelector> {
// 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() {
// "lang" => parse_lang(arguments),
"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> {
// 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() {
// All supported pseudo-elements
"before" => Some(Before),
"after" => Some(After),
- "first-line" => Some(FirstLine),
- "first-letter" => Some(FirstLetter),
+// "first-line" => Some(FirstLine),
+// "first-letter" => Some(FirstLetter),
_ => None
}
}
@@ -549,6 +560,7 @@ fn skip_whitespace(iter: &mut Iter) -> bool {
#[cfg(test)]
mod tests {
+ use extra::arc::Arc;
use cssparser;
use namespaces::NamespaceMap;
use super::*;
@@ -567,48 +579,48 @@ mod tests {
fn test_parsing() {
assert_eq!(parse(""), None)
assert_eq!(parse("e"), Some(~[Selector{
- compound_selectors: CompoundSelector {
+ compound_selectors: Arc::new(CompoundSelector {
simple_selectors: ~[LocalNameSelector(~"e")],
next: None,
- },
+ }),
pseudo_element: None,
specificity: specificity(0, 0, 1),
}]))
assert_eq!(parse(".foo"), Some(~[Selector{
- compound_selectors: CompoundSelector {
+ compound_selectors: Arc::new(CompoundSelector {
simple_selectors: ~[ClassSelector(~"foo")],
next: None,
- },
+ }),
pseudo_element: None,
specificity: specificity(0, 1, 0),
}]))
assert_eq!(parse("#bar"), Some(~[Selector{
- compound_selectors: CompoundSelector {
+ compound_selectors: Arc::new(CompoundSelector {
simple_selectors: ~[IDSelector(~"bar")],
next: None,
- },
+ }),
pseudo_element: None,
specificity: specificity(1, 0, 0),
}]))
assert_eq!(parse("e.foo#bar"), Some(~[Selector{
- compound_selectors: CompoundSelector {
+ compound_selectors: Arc::new(CompoundSelector {
simple_selectors: ~[LocalNameSelector(~"e"),
ClassSelector(~"foo"),
IDSelector(~"bar")],
next: None,
- },
+ }),
pseudo_element: None,
specificity: specificity(1, 1, 1),
}]))
assert_eq!(parse("e.foo #bar"), Some(~[Selector{
- compound_selectors: CompoundSelector {
+ compound_selectors: Arc::new(CompoundSelector {
simple_selectors: ~[IDSelector(~"bar")],
next: Some((~CompoundSelector {
simple_selectors: ~[LocalNameSelector(~"e"),
ClassSelector(~"foo")],
next: None,
}, Descendant)),
- },
+ }),
pseudo_element: None,
specificity: specificity(1, 1, 1),
}]))