Skip to content

Commit

Permalink
Move "insert a CSS rule" algorithm to style
Browse files Browse the repository at this point in the history
This also changes the algorithm to match w3c/csswg-drafts#754 instead of
the current cssom draft, because that makes the code simpler and matches
implementation of other browsers.
  • Loading branch information
upsuper committed Nov 24, 2016
1 parent c8e39dc commit 134ef33
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 62 deletions.
76 changes: 14 additions & 62 deletions components/script/dom/cssrulelist.rs
Expand Up @@ -15,13 +15,23 @@ use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window;
use parking_lot::RwLock;
use std::sync::Arc;
use style::parser::ParserContextExtraData;
use style::stylesheets::{CssRules, KeyframesRule, Origin};
use style::stylesheets::{CssRules, KeyframesRule, RulesMutateError};
use style::stylesheets::CssRule as StyleCssRule;

no_jsmanaged_fields!(RulesSource);
no_jsmanaged_fields!(CssRules);

impl From<RulesMutateError> for Error {
fn from(other: RulesMutateError) -> Self {
match other {
RulesMutateError::Syntax => Error::Syntax,
RulesMutateError::IndexSize => Error::IndexSize,
RulesMutateError::HierarchyRequest => Error::HierarchyRequest,
RulesMutateError::InvalidState => Error::InvalidState,
}
}
}

#[dom_struct]
pub struct CSSRuleList {
reflector_: Reflector,
Expand Down Expand Up @@ -64,21 +74,9 @@ impl CSSRuleList {
CSSRuleListBinding::Wrap)
}

/// https://drafts.csswg.org/cssom/#insert-a-css-rule
///
/// Should only be called for CssRules-backed rules. Use append_lazy_rule
/// for keyframes-backed rules.
pub fn insert_rule(&self, rule: &str, idx: u32, nested: bool) -> Fallible<u32> {
use style::stylesheets::SingleRuleParseError;
/// Insert an item into a vector, appending if it is out of bounds
fn insert<T>(vec: &mut Vec<T>, index: usize, item: T) {
if index >= vec.len() {
vec.push(item);
} else {
vec.insert(index, item);
}
}

let css_rules = if let RulesSource::Rules(ref rules) = self.rules {
rules
} else {
Expand All @@ -90,58 +88,12 @@ impl CSSRuleList {
let doc = window.Document();
let index = idx as usize;

let new_rule = css_rules.insert_rule(rule, doc.url().clone(), index, nested)?;

let new_rule = {
let rules = css_rules.0.read();
let state = if nested {
None
} else {
Some(CssRules::state_at_index(&rules, index))
};

let rev_state = CssRules::state_at_index_rev(&rules, index);

// Step 1, 2
// XXXManishearth get url from correct location
// XXXManishearth should we also store the namespace map?
let parse_result = StyleCssRule::parse(&rule, Origin::Author,
doc.url().clone(),
ParserContextExtraData::default(),
state);

if let Err(SingleRuleParseError::Syntax) = parse_result {
return Err(Error::Syntax)
}

// Step 3, 4
if index > rules.len() {
return Err(Error::IndexSize);
}

let (new_rule, new_state) = try!(parse_result.map_err(|_| Error::HierarchyRequest));

if new_state > rev_state {
// We inserted a rule too early, e.g. inserting
// a regular style rule before @namespace rules
return Err((Error::HierarchyRequest));
}

// Step 6
if let StyleCssRule::Namespace(..) = new_rule {
if !CssRules::only_ns_or_import(&rules) {
return Err(Error::InvalidState);
}
}

new_rule
};

insert(&mut css_rules.0.write(), index, new_rule.clone());
let sheet = self.sheet.get();
let sheet = sheet.as_ref().map(|sheet| &**sheet);
let dom_rule = CSSRule::new_specific(&window, sheet, new_rule);
insert(&mut self.dom_rules.borrow_mut(),
index, MutNullableHeap::new(Some(&*dom_rule)));
self.dom_rules.borrow_mut().insert(index, MutNullableHeap::new(Some(&*dom_rule)));
Ok((idx))
}

Expand Down
56 changes: 56 additions & 0 deletions components/style/stylesheets.rs
Expand Up @@ -60,6 +60,22 @@ impl From<Vec<CssRule>> for CssRules {
}
}

pub enum RulesMutateError {
Syntax,
IndexSize,
HierarchyRequest,
InvalidState,
}

impl From<SingleRuleParseError> for RulesMutateError {
fn from(other: SingleRuleParseError) -> Self {
match other {
SingleRuleParseError::Syntax => RulesMutateError::Syntax,
SingleRuleParseError::Hierarchy => RulesMutateError::HierarchyRequest,
}
}
}

impl CssRules {
// used in CSSOM
pub fn only_ns_or_import(rules: &[CssRule]) -> bool {
Expand Down Expand Up @@ -102,6 +118,46 @@ impl CssRules {
State::Body
}
}

/// https://drafts.csswg.org/cssom/#insert-a-css-rule
pub fn insert_rule(&self, rule: &str, base_url: ServoUrl, index: usize, nested: bool)
-> Result<CssRule, RulesMutateError> {
let mut rules = self.0.write();

// Step 1, 2
if index > rules.len() {
return Err(RulesMutateError::IndexSize);
}

let state = if nested {
None
} else {
Some(CssRules::state_at_index(&rules, index))
};

// Step 3, 4
// XXXManishearth should we also store the namespace map?
let (new_rule, new_state) = try!(CssRule::parse(&rule, Origin::Author, base_url,
ParserContextExtraData::default(), state));

// Step 5
let rev_state = CssRules::state_at_index_rev(&rules, index);
if new_state > rev_state {
// We inserted a rule too early, e.g. inserting
// a regular style rule before @namespace rules
return Err(RulesMutateError::HierarchyRequest);
}

// Step 6
if let CssRule::Namespace(..) = new_rule {
if !CssRules::only_ns_or_import(&rules) {
return Err(RulesMutateError::InvalidState);
}
}

rules.insert(index, new_rule.clone());
Ok(new_rule)
}
}

#[derive(Debug)]
Expand Down

0 comments on commit 134ef33

Please sign in to comment.