Auto merge of #14241 - Manishearth:mut-cssom, r=SimonSapin

CSSOM: Whole ton of things

CSSOM is now starting to be useful!

Based on #14190. Only the <s>last commit</s> last two commits need review.

cc @xidorn . This doesn't change the style API, but adds useful methods.

part of #11420

This adds:
 - `insertRule()` and `deleteRule()` on `CSSStyleSheet`, `CSSGroupingRule`
 - `.style` getters on link and style elements
 - Keyframes-backed `CSSRules` and `CSSKeyframesRule.cssRules`
 - `CSSGroupingRule.cssRules`
 - `prefix` and `namespaceURI` attributes of `CSSNamespaceRule`
 - Fixups regarding parent stylesheets

r? @SimonSapin

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14241)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-11-23 10:27:49 -08:00 committed by GitHub
commit 996756687c
34 changed files with 656 additions and 192 deletions

View file

@ -22,7 +22,7 @@ pub struct CSSFontFaceRule {
} }
impl CSSFontFaceRule { impl CSSFontFaceRule {
fn new_inherited(parent: &CSSStyleSheet, fontfacerule: Arc<RwLock<FontFaceRule>>) -> CSSFontFaceRule { fn new_inherited(parent: Option<&CSSStyleSheet>, fontfacerule: Arc<RwLock<FontFaceRule>>) -> CSSFontFaceRule {
CSSFontFaceRule { CSSFontFaceRule {
cssrule: CSSRule::new_inherited(parent), cssrule: CSSRule::new_inherited(parent),
fontfacerule: fontfacerule, fontfacerule: fontfacerule,
@ -30,7 +30,7 @@ impl CSSFontFaceRule {
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: &CSSStyleSheet, pub fn new(window: &Window, parent: Option<&CSSStyleSheet>,
fontfacerule: Arc<RwLock<FontFaceRule>>) -> Root<CSSFontFaceRule> { fontfacerule: Arc<RwLock<FontFaceRule>>) -> Root<CSSFontFaceRule> {
reflect_dom_object(box CSSFontFaceRule::new_inherited(parent, fontfacerule), reflect_dom_object(box CSSFontFaceRule::new_inherited(parent, fontfacerule),
window, window,

View file

@ -3,28 +3,67 @@
* 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 dom::bindings::codegen::Bindings::CSSGroupingRuleBinding; use dom::bindings::codegen::Bindings::CSSGroupingRuleBinding;
use dom::bindings::js::Root; use dom::bindings::codegen::Bindings::CSSGroupingRuleBinding::CSSGroupingRuleMethods;
use dom::bindings::reflector::reflect_dom_object; use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleBinding::CSSRuleMethods;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::reflector::{Reflectable, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssrule::CSSRule; use dom::cssrule::CSSRule;
use dom::cssrulelist::{CSSRuleList, RulesSource};
use dom::cssstylesheet::CSSStyleSheet; use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window; use dom::window::Window;
use style::stylesheets::CssRules as StyleCssRules;
#[dom_struct] #[dom_struct]
pub struct CSSGroupingRule { pub struct CSSGroupingRule {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_heap_size_of = "Arc"]
rules: StyleCssRules,
rulelist: MutNullableHeap<JS<CSSRuleList>>,
} }
impl CSSGroupingRule { impl CSSGroupingRule {
pub fn new_inherited(parent: &CSSStyleSheet) -> CSSGroupingRule { pub fn new_inherited(parent: Option<&CSSStyleSheet>,
rules: StyleCssRules) -> CSSGroupingRule {
CSSGroupingRule { CSSGroupingRule {
cssrule: CSSRule::new_inherited(parent), cssrule: CSSRule::new_inherited(parent),
rules: rules,
rulelist: MutNullableHeap::new(None),
} }
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: &CSSStyleSheet) -> Root<CSSGroupingRule> { pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, rules: StyleCssRules) -> Root<CSSGroupingRule> {
reflect_dom_object(box CSSGroupingRule::new_inherited(parent), reflect_dom_object(box CSSGroupingRule::new_inherited(parent, rules),
window, window,
CSSGroupingRuleBinding::Wrap) CSSGroupingRuleBinding::Wrap)
} }
fn rulelist(&self) -> Root<CSSRuleList> {
let sheet = self.upcast::<CSSRule>().GetParentStyleSheet();
let sheet = sheet.as_ref().map(|s| &**s);
self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(),
sheet,
RulesSource::Rules(self.rules.clone())))
}
}
impl CSSGroupingRuleMethods for CSSGroupingRule {
// https://drafts.csswg.org/cssom/#dom-cssgroupingrule-cssrules
fn CssRules(&self) -> Root<CSSRuleList> {
// XXXManishearth check origin clean flag
self.rulelist()
}
// https://drafts.csswg.org/cssom/#dom-cssgroupingrule-insertrule
fn InsertRule(&self, rule: DOMString, index: u32) -> Fallible<u32> {
self.rulelist().insert_rule(&rule, index, /* nested */ true)
}
// https://drafts.csswg.org/cssom/#dom-cssgroupingrule-deleterule
fn DeleteRule(&self, index: u32) -> ErrorResult {
self.rulelist().remove_rule(index)
}
} }

View file

@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::CSSKeyframeRuleBinding;
use dom::bindings::js::Root;
use dom::bindings::reflector::reflect_dom_object;
use dom::bindings::str::DOMString;
use dom::cssrule::{CSSRule, SpecificCSSRule};
use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window;
use parking_lot::RwLock;
use std::sync::Arc;
use style::keyframes::Keyframe;
use style_traits::ToCss;
#[dom_struct]
pub struct CSSKeyframeRule {
cssrule: CSSRule,
#[ignore_heap_size_of = "Arc"]
keyframerule: Arc<RwLock<Keyframe>>,
}
impl CSSKeyframeRule {
fn new_inherited(parent: Option<&CSSStyleSheet>, keyframerule: Arc<RwLock<Keyframe>>) -> CSSKeyframeRule {
CSSKeyframeRule {
cssrule: CSSRule::new_inherited(parent),
keyframerule: keyframerule,
}
}
#[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: Option<&CSSStyleSheet>,
keyframerule: Arc<RwLock<Keyframe>>) -> Root<CSSKeyframeRule> {
reflect_dom_object(box CSSKeyframeRule::new_inherited(parent, keyframerule),
window,
CSSKeyframeRuleBinding::Wrap)
}
}
impl SpecificCSSRule for CSSKeyframeRule {
fn ty(&self) -> u16 {
use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleConstants;
CSSRuleConstants::KEYFRAME_RULE
}
fn get_css(&self) -> DOMString {
self.keyframerule.read().to_css_string().into()
}
}

View file

@ -2,16 +2,25 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* 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 cssparser::Parser;
use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding; use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding;
use dom::bindings::js::Root; use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding::CSSKeyframesRuleMethods;
use dom::bindings::reflector::reflect_dom_object; use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::reflector::{Reflectable, reflect_dom_object};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::csskeyframerule::CSSKeyframeRule;
use dom::cssrule::{CSSRule, SpecificCSSRule}; use dom::cssrule::{CSSRule, SpecificCSSRule};
use dom::cssrulelist::{CSSRuleList, RulesSource};
use dom::cssstylesheet::CSSStyleSheet; use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window; use dom::window::Window;
use parking_lot::RwLock; use parking_lot::RwLock;
use std::sync::Arc; use std::sync::Arc;
use style::stylesheets::KeyframesRule; use style::keyframes::{Keyframe, KeyframeSelector};
use style::parser::ParserContextExtraData;
use style::stylesheets::{KeyframesRule, Origin};
use style_traits::ToCss; use style_traits::ToCss;
#[dom_struct] #[dom_struct]
@ -19,23 +28,84 @@ pub struct CSSKeyframesRule {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_heap_size_of = "Arc"] #[ignore_heap_size_of = "Arc"]
keyframesrule: Arc<RwLock<KeyframesRule>>, keyframesrule: Arc<RwLock<KeyframesRule>>,
rulelist: MutNullableHeap<JS<CSSRuleList>>,
} }
impl CSSKeyframesRule { impl CSSKeyframesRule {
fn new_inherited(parent: &CSSStyleSheet, keyframesrule: Arc<RwLock<KeyframesRule>>) -> CSSKeyframesRule { fn new_inherited(parent: Option<&CSSStyleSheet>, keyframesrule: Arc<RwLock<KeyframesRule>>) -> CSSKeyframesRule {
CSSKeyframesRule { CSSKeyframesRule {
cssrule: CSSRule::new_inherited(parent), cssrule: CSSRule::new_inherited(parent),
keyframesrule: keyframesrule, keyframesrule: keyframesrule,
rulelist: MutNullableHeap::new(None),
} }
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: &CSSStyleSheet, pub fn new(window: &Window, parent: Option<&CSSStyleSheet>,
keyframesrule: Arc<RwLock<KeyframesRule>>) -> Root<CSSKeyframesRule> { keyframesrule: Arc<RwLock<KeyframesRule>>) -> Root<CSSKeyframesRule> {
reflect_dom_object(box CSSKeyframesRule::new_inherited(parent, keyframesrule), reflect_dom_object(box CSSKeyframesRule::new_inherited(parent, keyframesrule),
window, window,
CSSKeyframesRuleBinding::Wrap) CSSKeyframesRuleBinding::Wrap)
} }
fn rulelist(&self) -> Root<CSSRuleList> {
self.rulelist.or_init(|| {
let sheet = self.upcast::<CSSRule>().GetParentStyleSheet();
let sheet = sheet.as_ref().map(|s| &**s);
CSSRuleList::new(self.global().as_window(),
sheet,
RulesSource::Keyframes(self.keyframesrule.clone()))
})
}
/// Given a keyframe selector, finds the index of the first corresponding rule if any
fn find_rule(&self, selector: &str) -> Option<usize> {
let mut input = Parser::new(selector);
if let Ok(sel) = KeyframeSelector::parse(&mut input) {
// This finds the *last* element matching a selector
// because that's the rule that applies. Thus, rposition
self.keyframesrule.read()
.keyframes.iter().rposition(|frame| {
frame.read().selector == sel
})
} else {
None
}
}
}
impl CSSKeyframesRuleMethods for CSSKeyframesRule {
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-cssrules
fn CssRules(&self) -> Root<CSSRuleList> {
self.rulelist()
}
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-appendrule
fn AppendRule(&self, rule: DOMString) {
let global = self.global();
let window = global.as_window();
let doc = window.Document();
let rule = Keyframe::parse(&rule, Origin::Author, doc.url().clone(),
ParserContextExtraData::default());
if let Ok(rule) = rule {
self.keyframesrule.write().keyframes.push(rule);
self.rulelist().append_lazy_dom_rule();
}
}
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-deleterule
fn DeleteRule(&self, selector: DOMString) {
if let Some(idx) = self.find_rule(&selector) {
let _ = self.rulelist().remove_rule(idx as u32);
}
}
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-findrule
fn FindRule(&self, selector: DOMString) -> Option<Root<CSSKeyframeRule>> {
self.find_rule(&selector).and_then(|idx| {
self.rulelist().item(idx as u32)
}).and_then(Root::downcast)
}
} }
impl SpecificCSSRule for CSSKeyframesRule { impl SpecificCSSRule for CSSKeyframesRule {
@ -47,4 +117,8 @@ impl SpecificCSSRule for CSSKeyframesRule {
fn get_css(&self) -> DOMString { fn get_css(&self) -> DOMString {
self.keyframesrule.read().to_css_string().into() self.keyframesrule.read().to_css_string().into()
} }
fn deparent_children(&self) {
self.rulelist.get().map(|list| list.deparent_all());
}
} }

View file

@ -23,15 +23,16 @@ pub struct CSSMediaRule {
} }
impl CSSMediaRule { impl CSSMediaRule {
fn new_inherited(parent: &CSSStyleSheet, mediarule: Arc<RwLock<MediaRule>>) -> CSSMediaRule { fn new_inherited(parent: Option<&CSSStyleSheet>, mediarule: Arc<RwLock<MediaRule>>) -> CSSMediaRule {
let list = mediarule.read().rules.clone();
CSSMediaRule { CSSMediaRule {
cssrule: CSSGroupingRule::new_inherited(parent), cssrule: CSSGroupingRule::new_inherited(parent, list),
mediarule: mediarule, mediarule: mediarule,
} }
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: &CSSStyleSheet, pub fn new(window: &Window, parent: Option<&CSSStyleSheet>,
mediarule: Arc<RwLock<MediaRule>>) -> Root<CSSMediaRule> { mediarule: Arc<RwLock<MediaRule>>) -> Root<CSSMediaRule> {
reflect_dom_object(box CSSMediaRule::new_inherited(parent, mediarule), reflect_dom_object(box CSSMediaRule::new_inherited(parent, mediarule),
window, window,

View file

@ -3,6 +3,7 @@
* 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 dom::bindings::codegen::Bindings::CSSNamespaceRuleBinding; use dom::bindings::codegen::Bindings::CSSNamespaceRuleBinding;
use dom::bindings::codegen::Bindings::CSSNamespaceRuleBinding::CSSNamespaceRuleMethods;
use dom::bindings::js::Root; use dom::bindings::js::Root;
use dom::bindings::reflector::reflect_dom_object; use dom::bindings::reflector::reflect_dom_object;
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
@ -22,7 +23,7 @@ pub struct CSSNamespaceRule {
} }
impl CSSNamespaceRule { impl CSSNamespaceRule {
fn new_inherited(parent: &CSSStyleSheet, namespacerule: Arc<RwLock<NamespaceRule>>) -> CSSNamespaceRule { fn new_inherited(parent: Option<&CSSStyleSheet>, namespacerule: Arc<RwLock<NamespaceRule>>) -> CSSNamespaceRule {
CSSNamespaceRule { CSSNamespaceRule {
cssrule: CSSRule::new_inherited(parent), cssrule: CSSRule::new_inherited(parent),
namespacerule: namespacerule, namespacerule: namespacerule,
@ -30,7 +31,7 @@ impl CSSNamespaceRule {
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: &CSSStyleSheet, pub fn new(window: &Window, parent: Option<&CSSStyleSheet>,
namespacerule: Arc<RwLock<NamespaceRule>>) -> Root<CSSNamespaceRule> { namespacerule: Arc<RwLock<NamespaceRule>>) -> Root<CSSNamespaceRule> {
reflect_dom_object(box CSSNamespaceRule::new_inherited(parent, namespacerule), reflect_dom_object(box CSSNamespaceRule::new_inherited(parent, namespacerule),
window, window,
@ -38,6 +39,20 @@ impl CSSNamespaceRule {
} }
} }
impl CSSNamespaceRuleMethods for CSSNamespaceRule {
// https://drafts.csswg.org/cssom/#dom-cssnamespacerule-prefix
fn Prefix(&self) -> DOMString {
self.namespacerule.read().prefix
.as_ref().map(|s| s.to_string().into())
.unwrap_or(DOMString::new())
}
// https://drafts.csswg.org/cssom/#dom-cssnamespacerule-namespaceuri
fn NamespaceURI(&self) -> DOMString {
(*self.namespacerule.read().url).into()
}
}
impl SpecificCSSRule for CSSNamespaceRule { impl SpecificCSSRule for CSSNamespaceRule {
fn ty(&self) -> u16 { fn ty(&self) -> u16 {
use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleConstants; use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleConstants;

View file

@ -9,6 +9,7 @@ use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::cssfontfacerule::CSSFontFaceRule; use dom::cssfontfacerule::CSSFontFaceRule;
use dom::csskeyframerule::CSSKeyframeRule;
use dom::csskeyframesrule::CSSKeyframesRule; use dom::csskeyframesrule::CSSKeyframesRule;
use dom::cssmediarule::CSSMediaRule; use dom::cssmediarule::CSSMediaRule;
use dom::cssnamespacerule::CSSNamespaceRule; use dom::cssnamespacerule::CSSNamespaceRule;
@ -27,15 +28,15 @@ pub struct CSSRule {
impl CSSRule { impl CSSRule {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new_inherited(parent: &CSSStyleSheet) -> CSSRule { pub fn new_inherited(parent: Option<&CSSStyleSheet>) -> CSSRule {
CSSRule { CSSRule {
reflector_: Reflector::new(), reflector_: Reflector::new(),
parent: MutNullableHeap::new(Some(parent)), parent: MutNullableHeap::new(parent),
} }
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: &CSSStyleSheet) -> Root<CSSRule> { pub fn new(window: &Window, parent: Option<&CSSStyleSheet>) -> Root<CSSRule> {
reflect_dom_object(box CSSRule::new_inherited(parent), reflect_dom_object(box CSSRule::new_inherited(parent),
window, window,
CSSRuleBinding::Wrap) CSSRuleBinding::Wrap)
@ -54,6 +55,8 @@ impl CSSRule {
rule as &SpecificCSSRule rule as &SpecificCSSRule
} else if let Some(rule) = self.downcast::<CSSViewportRule>() { } else if let Some(rule) = self.downcast::<CSSViewportRule>() {
rule as &SpecificCSSRule rule as &SpecificCSSRule
} else if let Some(rule) = self.downcast::<CSSKeyframeRule>() {
rule as &SpecificCSSRule
} else { } else {
unreachable!() unreachable!()
} }
@ -61,7 +64,7 @@ impl CSSRule {
// Given a StyleCssRule, create a new instance of a derived class of // Given a StyleCssRule, create a new instance of a derived class of
// CSSRule based on which rule it is // CSSRule based on which rule it is
pub fn new_specific(window: &Window, parent: &CSSStyleSheet, pub fn new_specific(window: &Window, parent: Option<&CSSStyleSheet>,
rule: StyleCssRule) -> Root<CSSRule> { rule: StyleCssRule) -> Root<CSSRule> {
// be sure to update the match in as_specific when this is updated // be sure to update the match in as_specific when this is updated
match rule { match rule {
@ -73,6 +76,21 @@ impl CSSRule {
StyleCssRule::Viewport(s) => Root::upcast(CSSViewportRule::new(window, parent, s)), StyleCssRule::Viewport(s) => Root::upcast(CSSViewportRule::new(window, parent, s)),
} }
} }
/// Sets owner sheet/rule to null
pub fn detach(&self) {
self.deparent();
// should set parent rule to None when we add parent rule support
}
/// Sets owner sheet to null (and does the same for all children)
pub fn deparent(&self) {
self.parent.set(None);
// https://github.com/w3c/csswg-drafts/issues/722
// Spec doesn't ask us to do this, but it makes sense
// and browsers implement this behavior
self.as_specific().deparent_children();
}
} }
impl CSSRuleMethods for CSSRule { impl CSSRuleMethods for CSSRule {
@ -100,4 +118,8 @@ impl CSSRuleMethods for CSSRule {
pub trait SpecificCSSRule { pub trait SpecificCSSRule {
fn ty(&self) -> u16; fn ty(&self) -> u16;
fn get_css(&self) -> DOMString; fn get_css(&self) -> DOMString;
/// Remove CSSStyleSheet parent from all transitive children
fn deparent_children(&self) {
// most CSSRules do nothing here
}
} }

View file

@ -2,61 +2,248 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* 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 dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::CSSRuleListBinding; use dom::bindings::codegen::Bindings::CSSRuleListBinding;
use dom::bindings::codegen::Bindings::CSSRuleListBinding::CSSRuleListMethods; use dom::bindings::codegen::Bindings::CSSRuleListBinding::CSSRuleListMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::csskeyframerule::CSSKeyframeRule;
use dom::cssrule::CSSRule; use dom::cssrule::CSSRule;
use dom::cssstylesheet::CSSStyleSheet; use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window; use dom::window::Window;
use style::stylesheets::CssRules; use parking_lot::RwLock;
use std::sync::Arc;
use style::parser::ParserContextExtraData;
use style::stylesheets::{CssRules, KeyframesRule, Origin};
use style::stylesheets::CssRule as StyleCssRule;
no_jsmanaged_fields!(RulesSource);
no_jsmanaged_fields!(CssRules); no_jsmanaged_fields!(CssRules);
#[dom_struct] #[dom_struct]
pub struct CSSRuleList { pub struct CSSRuleList {
reflector_: Reflector, reflector_: Reflector,
sheet: JS<CSSStyleSheet>, sheet: MutNullableHeap<JS<CSSStyleSheet>>,
#[ignore_heap_size_of = "Arc"] #[ignore_heap_size_of = "Arc"]
rules: CssRules, rules: RulesSource,
dom_rules: Vec<MutNullableHeap<JS<CSSRule>>> dom_rules: DOMRefCell<Vec<MutNullableHeap<JS<CSSRule>>>>
}
pub enum RulesSource {
Rules(CssRules),
Keyframes(Arc<RwLock<KeyframesRule>>),
} }
impl CSSRuleList { impl CSSRuleList {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new_inherited(sheet: &CSSStyleSheet, rules: CssRules) -> CSSRuleList { pub fn new_inherited(sheet: Option<&CSSStyleSheet>, rules: RulesSource) -> CSSRuleList {
let dom_rules = rules.0.read().iter().map(|_| MutNullableHeap::new(None)).collect(); let dom_rules = match rules {
RulesSource::Rules(ref rules) => {
rules.0.read().iter().map(|_| MutNullableHeap::new(None)).collect()
}
RulesSource::Keyframes(ref rules) => {
rules.read().keyframes.iter().map(|_| MutNullableHeap::new(None)).collect()
}
};
CSSRuleList { CSSRuleList {
reflector_: Reflector::new(), reflector_: Reflector::new(),
sheet: JS::from_ref(sheet), sheet: MutNullableHeap::new(sheet),
rules: rules, rules: rules,
dom_rules: dom_rules, dom_rules: DOMRefCell::new(dom_rules),
} }
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, sheet: &CSSStyleSheet, rules: CssRules) -> Root<CSSRuleList> { pub fn new(window: &Window, sheet: Option<&CSSStyleSheet>,
rules: RulesSource) -> Root<CSSRuleList> {
reflect_dom_object(box CSSRuleList::new_inherited(sheet, rules), reflect_dom_object(box CSSRuleList::new_inherited(sheet, rules),
window, window,
CSSRuleListBinding::Wrap) 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 {
panic!("Called insert_rule on non-CssRule-backed CSSRuleList");
};
let global = self.global();
let window = global.as_window();
let doc = window.Document();
let index = idx as usize;
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)));
Ok((idx))
}
// https://drafts.csswg.org/cssom/#remove-a-css-rule
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-deleterule
// In case of a keyframe rule, index must be valid.
pub fn remove_rule(&self, index: u32) -> ErrorResult {
let index = index as usize;
match self.rules {
RulesSource::Rules(ref css_rules) => {
// https://drafts.csswg.org/cssom/#remove-a-css-rule
{
let rules = css_rules.0.read();
// Step 1, 2
if index >= rules.len() {
return Err(Error::IndexSize);
}
// Step 3
let ref rule = rules[index];
// Step 4
if let StyleCssRule::Namespace(..) = *rule {
if !CssRules::only_ns_or_import(&rules) {
return Err(Error::InvalidState);
}
}
}
// Step 5, 6
let mut dom_rules = self.dom_rules.borrow_mut();
css_rules.0.write().remove(index);
dom_rules[index].get().map(|r| r.detach());
dom_rules.remove(index);
Ok(())
}
RulesSource::Keyframes(ref kf) => {
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-deleterule
let mut dom_rules = self.dom_rules.borrow_mut();
dom_rules[index].get().map(|r| r.detach());
dom_rules.remove(index);
kf.write().keyframes.remove(index);
Ok(())
}
}
}
// Remove parent stylesheets from all children
pub fn deparent_all(&self) {
for rule in self.dom_rules.borrow().iter() {
rule.get().map(|r| Root::upcast(r).deparent());
}
}
pub fn item(&self, idx: u32) -> Option<Root<CSSRule>> {
self.dom_rules.borrow().get(idx as usize).map(|rule| {
rule.or_init(|| {
let sheet = self.sheet.get();
let sheet = sheet.as_ref().map(|sheet| &**sheet);
match self.rules {
RulesSource::Rules(ref rules) => {
CSSRule::new_specific(self.global().as_window(),
sheet,
rules.0.read()[idx as usize].clone())
}
RulesSource::Keyframes(ref rules) => {
Root::upcast(CSSKeyframeRule::new(self.global().as_window(),
sheet,
rules.read()
.keyframes[idx as usize]
.clone()))
}
}
})
})
}
/// Add a rule to the list of DOM rules. This list is lazy,
/// so we just append a placeholder.
///
/// Should only be called for keyframes-backed rules, use insert_rule
/// for CssRules-backed rules
pub fn append_lazy_dom_rule(&self) {
if let RulesSource::Rules(..) = self.rules {
panic!("Can only call append_lazy_rule with keyframes-backed CSSRules");
}
self.dom_rules.borrow_mut().push(MutNullableHeap::new(None));
}
} }
impl CSSRuleListMethods for CSSRuleList { impl CSSRuleListMethods for CSSRuleList {
// https://drafts.csswg.org/cssom/#ref-for-dom-cssrulelist-item-1 // https://drafts.csswg.org/cssom/#ref-for-dom-cssrulelist-item-1
fn Item(&self, idx: u32) -> Option<Root<CSSRule>> { fn Item(&self, idx: u32) -> Option<Root<CSSRule>> {
self.dom_rules.get(idx as usize).map(|rule| { self.item(idx)
rule.or_init(|| {
CSSRule::new_specific(self.global().as_window(),
&self.sheet,
self.rules.0.read()[idx as usize].clone())
})
})
} }
// https://drafts.csswg.org/cssom/#dom-cssrulelist-length // https://drafts.csswg.org/cssom/#dom-cssrulelist-length
fn Length(&self) -> u32 { fn Length(&self) -> u32 {
self.dom_rules.len() as u32 self.dom_rules.borrow().len() as u32
} }
// check-tidy: no specs after this line // check-tidy: no specs after this line

View file

@ -22,7 +22,7 @@ pub struct CSSStyleRule {
} }
impl CSSStyleRule { impl CSSStyleRule {
fn new_inherited(parent: &CSSStyleSheet, stylerule: Arc<RwLock<StyleRule>>) -> CSSStyleRule { fn new_inherited(parent: Option<&CSSStyleSheet>, stylerule: Arc<RwLock<StyleRule>>) -> CSSStyleRule {
CSSStyleRule { CSSStyleRule {
cssrule: CSSRule::new_inherited(parent), cssrule: CSSRule::new_inherited(parent),
stylerule: stylerule, stylerule: stylerule,
@ -30,7 +30,7 @@ impl CSSStyleRule {
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: &CSSStyleSheet, pub fn new(window: &Window, parent: Option<&CSSStyleSheet>,
stylerule: Arc<RwLock<StyleRule>>) -> Root<CSSStyleRule> { stylerule: Arc<RwLock<StyleRule>>) -> Root<CSSStyleRule> {
reflect_dom_object(box CSSStyleRule::new_inherited(parent, stylerule), reflect_dom_object(box CSSStyleRule::new_inherited(parent, stylerule),
window, window,

View file

@ -4,10 +4,11 @@
use dom::bindings::codegen::Bindings::CSSStyleSheetBinding; use dom::bindings::codegen::Bindings::CSSStyleSheetBinding;
use dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods; use dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::js::{JS, Root, MutNullableHeap}; use dom::bindings::js::{JS, Root, MutNullableHeap};
use dom::bindings::reflector::{reflect_dom_object, Reflectable}; use dom::bindings::reflector::{reflect_dom_object, Reflectable};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::cssrulelist::CSSRuleList; use dom::cssrulelist::{CSSRuleList, RulesSource};
use dom::stylesheet::StyleSheet; use dom::stylesheet::StyleSheet;
use dom::window::Window; use dom::window::Window;
use std::sync::Arc; use std::sync::Arc;
@ -40,12 +41,33 @@ impl CSSStyleSheet {
window, window,
CSSStyleSheetBinding::Wrap) CSSStyleSheetBinding::Wrap)
} }
fn rulelist(&self) -> Root<CSSRuleList> {
self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(),
Some(self),
RulesSource::Rules(self.style_stylesheet
.rules.clone())))
}
} }
impl CSSStyleSheetMethods for CSSStyleSheet { impl CSSStyleSheetMethods for CSSStyleSheet {
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules // https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules
fn CssRules(&self) -> Root<CSSRuleList> { fn CssRules(&self) -> Root<CSSRuleList> {
self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(), self, self.style_stylesheet.rules.clone())) // XXXManishearth check origin clean flag
// https://github.com/servo/servo/issues/14327
self.rulelist()
}
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule
fn InsertRule(&self, rule: DOMString, index: u32) -> Fallible<u32> {
// XXXManishearth check origin clean flag
self.rulelist().insert_rule(&rule, index, /* nested */ false)
}
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule
fn DeleteRule(&self, index: u32) -> ErrorResult {
// XXXManishearth check origin clean flag
self.rulelist().remove_rule(index)
} }
} }

View file

@ -22,7 +22,7 @@ pub struct CSSViewportRule {
} }
impl CSSViewportRule { impl CSSViewportRule {
fn new_inherited(parent: &CSSStyleSheet, viewportrule: Arc<RwLock<ViewportRule>>) -> CSSViewportRule { fn new_inherited(parent: Option<&CSSStyleSheet>, viewportrule: Arc<RwLock<ViewportRule>>) -> CSSViewportRule {
CSSViewportRule { CSSViewportRule {
cssrule: CSSRule::new_inherited(parent), cssrule: CSSRule::new_inherited(parent),
viewportrule: viewportrule, viewportrule: viewportrule,
@ -30,7 +30,7 @@ impl CSSViewportRule {
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(window: &Window, parent: &CSSStyleSheet, pub fn new(window: &Window, parent: Option<&CSSStyleSheet>,
viewportrule: Arc<RwLock<ViewportRule>>) -> Root<CSSViewportRule> { viewportrule: Arc<RwLock<ViewportRule>>) -> Root<CSSViewportRule> {
reflect_dom_object(box CSSViewportRule::new_inherited(parent, viewportrule), reflect_dom_object(box CSSViewportRule::new_inherited(parent, viewportrule),
window, window,

View file

@ -22,6 +22,7 @@ use dom::eventtarget::EventTarget;
use dom::globalscope::GlobalScope; use dom::globalscope::GlobalScope;
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node, window_from_node}; use dom::node::{Node, document_from_node, window_from_node};
use dom::stylesheet::StyleSheet as DOMStyleSheet;
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use encoding::EncodingRef; use encoding::EncodingRef;
use encoding::all::UTF_8; use encoding::all::UTF_8;
@ -452,4 +453,9 @@ impl HTMLLinkElementMethods for HTMLLinkElement {
// https://html.spec.whatwg.org/multipage/#dom-link-target // https://html.spec.whatwg.org/multipage/#dom-link-target
make_setter!(SetTarget, "target"); make_setter!(SetTarget, "target");
// https://drafts.csswg.org/cssom/#dom-linkstyle-sheet
fn GetSheet(&self) -> Option<Root<DOMStyleSheet>> {
self.get_cssom_stylesheet().map(Root::upcast)
}
} }

View file

@ -5,6 +5,7 @@
use cssparser::Parser as CssParser; use cssparser::Parser as CssParser;
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HTMLStyleElementBinding; use dom::bindings::codegen::Bindings::HTMLStyleElementBinding;
use dom::bindings::codegen::Bindings::HTMLStyleElementBinding::HTMLStyleElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::js::{JS, MutNullableHeap, Root};
@ -14,6 +15,7 @@ use dom::document::Document;
use dom::element::Element; use dom::element::Element;
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::node::{ChildrenMutation, Node, document_from_node, window_from_node}; use dom::node::{ChildrenMutation, Node, document_from_node, window_from_node};
use dom::stylesheet::StyleSheet as DOMStyleSheet;
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use html5ever_atoms::LocalName; use html5ever_atoms::LocalName;
use script_layout_interface::message::Msg; use script_layout_interface::message::Msg;
@ -117,3 +119,10 @@ impl VirtualMethods for HTMLStyleElement {
} }
} }
} }
impl HTMLStyleElementMethods for HTMLStyleElement {
// https://drafts.csswg.org/cssom/#dom-linkstyle-sheet
fn GetSheet(&self) -> Option<Root<DOMStyleSheet>> {
self.get_cssom_stylesheet().map(Root::upcast)
}
}

View file

@ -244,6 +244,7 @@ pub mod crypto;
pub mod css; pub mod css;
pub mod cssfontfacerule; pub mod cssfontfacerule;
pub mod cssgroupingrule; pub mod cssgroupingrule;
pub mod csskeyframerule;
pub mod csskeyframesrule; pub mod csskeyframesrule;
pub mod cssmediarule; pub mod cssmediarule;
pub mod cssnamespacerule; pub mod cssnamespacerule;

View file

@ -5,8 +5,8 @@
// https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface // https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface
[Exposed=Window] [Exposed=Window]
interface CSSGroupingRule : CSSRule { interface CSSGroupingRule : CSSRule {
// [SameObject] readonly attribute CSSRuleList cssRules; [SameObject] readonly attribute CSSRuleList cssRules;
// unsigned long insertRule(DOMString rule, unsigned long index); [Throws] unsigned long insertRule(DOMString rule, unsigned long index);
// void deleteRule(unsigned long index); [Throws] void deleteRule(unsigned long index);
}; };

View file

@ -0,0 +1,10 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// https://drafts.csswg.org/css-animations/#interface-csskeyframerule
[Exposed=Window]
interface CSSKeyframeRule : CSSRule {
// attribute DOMString keyText;
// readonly attribute CSSStyleDeclaration style;
};

View file

@ -6,9 +6,9 @@
[Exposed=Window] [Exposed=Window]
interface CSSKeyframesRule : CSSRule { interface CSSKeyframesRule : CSSRule {
// attribute DOMString name; // attribute DOMString name;
// readonly attribute CSSRuleList cssRules; readonly attribute CSSRuleList cssRules;
// void appendRule(DOMString rule); void appendRule(DOMString rule);
// void deleteRule(DOMString select); void deleteRule(DOMString select);
// CSSKeyframeRule? findRule(DOMString select); CSSKeyframeRule? findRule(DOMString select);
}; };

View file

@ -5,6 +5,6 @@
// https://drafts.csswg.org/cssom/#the-cssnamespacerule-interface // https://drafts.csswg.org/cssom/#the-cssnamespacerule-interface
[Exposed=Window] [Exposed=Window]
interface CSSNamespaceRule : CSSRule { interface CSSNamespaceRule : CSSRule {
// readonly attribute DOMString namespaceURI; readonly attribute DOMString namespaceURI;
// readonly attribute DOMString prefix; readonly attribute DOMString prefix;
}; };

View file

@ -7,6 +7,6 @@
interface CSSStyleSheet : StyleSheet { interface CSSStyleSheet : StyleSheet {
// readonly attribute CSSRule? ownerRule; // readonly attribute CSSRule? ownerRule;
[SameObject] readonly attribute CSSRuleList cssRules; [SameObject] readonly attribute CSSRuleList cssRules;
// unsigned long insertRule(DOMString rule, unsigned long index); [Throws] unsigned long insertRule(DOMString rule, unsigned long index);
// void deleteRule(unsigned long index); [Throws] void deleteRule(unsigned long index);
}; };

View file

@ -15,7 +15,7 @@ interface HTMLLinkElement : HTMLElement {
// also has obsolete members // also has obsolete members
}; };
//HTMLLinkElement implements LinkStyle; HTMLLinkElement implements LinkStyle;
// https://html.spec.whatwg.org/multipage/#HTMLLinkElement-partial // https://html.spec.whatwg.org/multipage/#HTMLLinkElement-partial
partial interface HTMLLinkElement { partial interface HTMLLinkElement {

View file

@ -8,4 +8,4 @@ interface HTMLStyleElement : HTMLElement {
// attribute DOMString type; // attribute DOMString type;
// attribute boolean scoped; // attribute boolean scoped;
}; };
//HTMLStyleElement implements LinkStyle; HTMLStyleElement implements LinkStyle;

View file

@ -15,3 +15,9 @@ interface StyleSheet {
// [SameObject, PutForwards=mediaText] readonly attribute MediaList media; // [SameObject, PutForwards=mediaText] readonly attribute MediaList media;
// attribute boolean disabled; // attribute boolean disabled;
}; };
// https://drafts.csswg.org/cssom/#the-linkstyle-interface
[NoInterfaceObject]
interface LinkStyle {
readonly attribute StyleSheet? sheet;
};

View file

@ -26,11 +26,11 @@ impl ToCss for Source {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self { match *self {
Source::Url(ref url) => { Source::Url(ref url) => {
try!(dest.write_str("local(\"")); try!(dest.write_str("url(\""));
try!(url.to_css(dest)); try!(url.to_css(dest));
}, },
Source::Local(ref family) => { Source::Local(ref family) => {
try!(dest.write_str("url(\"")); try!(dest.write_str("local(\""));
try!(family.to_css(dest)); try!(family.to_css(dest));
}, },
} }

View file

@ -3,15 +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 cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser}; use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
use cssparser::{DeclarationListParser, DeclarationParser}; use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule};
use parking_lot::RwLock; use parking_lot::RwLock;
use parser::{ParserContext, log_css_error}; use parser::{ParserContext, ParserContextExtraData, log_css_error};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock}; use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
use properties::PropertyDeclarationParseResult; use properties::PropertyDeclarationParseResult;
use properties::animated_properties::TransitionProperty; use properties::animated_properties::TransitionProperty;
use servo_url::ServoUrl;
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use style_traits::ToCss; use style_traits::ToCss;
use stylesheets::{MemoryHoleReporter, Origin};
/// A number from 1 to 100, indicating the percentage of the animation where /// A number from 1 to 100, indicating the percentage of the animation where
/// this keyframe should run. /// this keyframe should run.
@ -68,6 +70,11 @@ impl KeyframeSelector {
pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector { pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
KeyframeSelector(percentages) KeyframeSelector(percentages)
} }
pub fn parse(input: &mut Parser) -> Result<Self, ()> {
input.parse_comma_separated(KeyframePercentage::parse)
.map(KeyframeSelector)
}
} }
/// A keyframe. /// A keyframe.
@ -99,6 +106,24 @@ impl ToCss for Keyframe {
} }
} }
impl Keyframe {
pub fn parse(css: &str, origin: Origin,
base_url: ServoUrl,
extra_data: ParserContextExtraData) -> Result<Arc<RwLock<Self>>, ()> {
let error_reporter = Box::new(MemoryHoleReporter);
let context = ParserContext::new_with_extra_data(origin, &base_url,
error_reporter,
extra_data);
let mut input = Parser::new(css);
let mut rule_parser = KeyframeListParser {
context: &context,
};
parse_one_rule(&mut input, &mut rule_parser)
}
}
/// A keyframes step value. This can be a synthetised keyframes animation, that /// A keyframes step value. This can be a synthetised keyframes animation, that
/// is, one autogenerated from the current computed values, or a list of /// is, one autogenerated from the current computed values, or a list of
/// declarations to apply. /// declarations to apply.
@ -260,8 +285,8 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
fn parse_prelude(&mut self, input: &mut Parser) -> Result<Self::Prelude, ()> { fn parse_prelude(&mut self, input: &mut Parser) -> Result<Self::Prelude, ()> {
let start = input.position(); let start = input.position();
match input.parse_comma_separated(|input| KeyframePercentage::parse(input)) { match KeyframeSelector::parse(input) {
Ok(percentages) => Ok(KeyframeSelector(percentages)), Ok(sel) => Ok(sel),
Err(()) => { Err(()) => {
let message = format!("Invalid keyframe rule: '{}'", input.slice_from(start)); let message = format!("Invalid keyframe rule: '{}'", input.slice_from(start));
log_css_error(input, start, &message, self.context); log_css_error(input, start, &message, self.context);

View file

@ -6,7 +6,7 @@
use {Atom, Prefix, Namespace}; use {Atom, Prefix, Namespace};
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, decode_stylesheet_bytes}; use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, decode_stylesheet_bytes};
use cssparser::{AtRuleType, RuleListParser, Token}; use cssparser::{AtRuleType, RuleListParser, SourcePosition, Token, parse_one_rule};
use cssparser::ToCss as ParserToCss; use cssparser::ToCss as ParserToCss;
use encoding::EncodingRef; use encoding::EncodingRef;
use error_reporting::ParseErrorReporter; use error_reporting::ParseErrorReporter;
@ -60,6 +60,50 @@ impl From<Vec<CssRule>> for CssRules {
} }
} }
impl CssRules {
// used in CSSOM
pub fn only_ns_or_import(rules: &[CssRule]) -> bool {
rules.iter().all(|r| {
match *r {
CssRule::Namespace(..) /* | CssRule::Import(..) */ => true,
_ => false
}
})
}
// Provides the parser state at a given insertion index
pub fn state_at_index(rules: &[CssRule], at: usize) -> State {
let mut state = State::Start;
if at > 0 {
if let Some(rule) = rules.get(at - 1) {
state = match *rule {
// CssRule::Charset(..) => State::Start,
// CssRule::Import(..) => State::Imports,
CssRule::Namespace(..) => State::Namespaces,
_ => State::Body,
};
}
}
state
}
// Provides the maximum allowed parser state at a given index,
// searching in reverse. If inserting at this index, the parser
// must not be in a state greater than this post-insertion.
pub fn state_at_index_rev(rules: &[CssRule], at: usize) -> State {
if let Some(rule) = rules.get(at) {
match *rule {
// CssRule::Charset(..) => State::Start,
// CssRule::Import(..) => State::Imports,
CssRule::Namespace(..) => State::Namespaces,
_ => State::Body,
}
} else {
State::Body
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Stylesheet { pub struct Stylesheet {
/// List of rules in the order they were found (important for /// List of rules in the order they were found (important for
@ -92,6 +136,26 @@ pub enum CssRule {
Keyframes(Arc<RwLock<KeyframesRule>>), Keyframes(Arc<RwLock<KeyframesRule>>),
} }
/// Error reporter which silently forgets errors
pub struct MemoryHoleReporter;
impl ParseErrorReporter for MemoryHoleReporter {
fn report_error(&self,
_: &mut Parser,
_: SourcePosition,
_: &str) {
// do nothing
}
fn clone(&self) -> Box<ParseErrorReporter + Send + Sync> {
Box::new(MemoryHoleReporter)
}
}
pub enum SingleRuleParseError {
Syntax,
Hierarchy,
}
impl CssRule { impl CssRule {
/// Call `f` with the slice of rules directly contained inside this rule. /// Call `f` with the slice of rules directly contained inside this rule.
/// ///
@ -114,6 +178,39 @@ impl CssRule {
} }
} }
} }
// input state is None for a nested rule
// Returns a parsed CSS rule and the final state of the parser
pub fn parse(css: &str, origin: Origin,
base_url: ServoUrl,
extra_data: ParserContextExtraData,
state: Option<State>) -> Result<(Self, State), SingleRuleParseError> {
let error_reporter = Box::new(MemoryHoleReporter);
let mut namespaces = Namespaces::default();
let context = ParserContext::new_with_extra_data(origin, &base_url,
error_reporter.clone(),
extra_data);
let mut input = Parser::new(css);
// nested rules are in the body state
let state = state.unwrap_or(State::Body);
let mut rule_parser = TopLevelRuleParser {
stylesheet_origin: origin,
context: context,
state: Cell::new(state),
namespaces: &mut namespaces,
};
match parse_one_rule(&mut input, &mut rule_parser) {
Ok(result) => Ok((result, rule_parser.state.get())),
Err(_) => {
if let State::Invalid = rule_parser.state.get() {
Err(SingleRuleParseError::Hierarchy)
} else {
Err(SingleRuleParseError::Syntax)
}
}
}
}
} }
impl ToCss for CssRule { impl ToCss for CssRule {
@ -220,7 +317,6 @@ impl ToCss for StyleRule {
} }
} }
impl Stylesheet { impl Stylesheet {
pub fn from_bytes(bytes: &[u8], pub fn from_bytes(bytes: &[u8],
base_url: ServoUrl, base_url: ServoUrl,
@ -370,11 +466,12 @@ impl<'b> TopLevelRuleParser<'b> {
} }
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)] #[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum State { pub enum State {
Start = 1, Start = 1,
Imports = 2, Imports = 2,
Namespaces = 3, Namespaces = 3,
Body = 4, Body = 4,
Invalid = 5,
} }
@ -403,6 +500,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
// TODO: support @import // TODO: support @import
return Err(()) // "@import is not supported yet" return Err(()) // "@import is not supported yet"
} else { } else {
self.state.set(State::Invalid);
return Err(()) // "@import must be before any rule but @charset" return Err(()) // "@import must be before any rule but @charset"
} }
}, },
@ -429,6 +527,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
} }
))))) )))))
} else { } else {
self.state.set(State::Invalid);
return Err(()) // "@namespace must be before any rule but @charset and @import" return Err(()) // "@namespace must be before any rule but @charset and @import"
} }
}, },
@ -437,7 +536,11 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
"charset" => return Err(()), // (insert appropriate error message) "charset" => return Err(()), // (insert appropriate error message)
_ => {} _ => {}
} }
// Don't allow starting with an invalid state
if self.state.get() > State::Body {
self.state.set(State::Invalid);
return Err(());
}
self.state.set(State::Body); self.state.set(State::Body);
AtRuleParser::parse_prelude(&mut self.nested(), name, input) AtRuleParser::parse_prelude(&mut self.nested(), name, input)
} }

View file

@ -1,6 +1,5 @@
[css-style-declaration-modifications.htm] [css-style-declaration-modifications.htm]
type: testharness type: testharness
expected: ERROR
[CSSStyleDeclaration_accessible] [CSSStyleDeclaration_accessible]
expected: FAIL expected: FAIL

View file

@ -1,6 +1,5 @@
[cssimportrule.htm] [cssimportrule.htm]
type: testharness type: testharness
expected: ERROR
[CSSRule and CSSImportRule types] [CSSRule and CSSImportRule types]
expected: FAIL expected: FAIL

View file

@ -1,3 +1,20 @@
[cssstylerule.htm] [cssstylerule.htm]
type: testharness type: testharness
expected: ERROR [Rule_type_property]
expected: FAIL
[Rule_properties]
expected: FAIL
[Rule_properties_readonly]
expected: FAIL
[Rule_properties_values]
expected: FAIL
[StyleRule_properties]
expected: FAIL
[StyleRule_properties_values]
expected: FAIL

View file

@ -120,51 +120,18 @@
[CSSStyleSheet interface: attribute ownerRule] [CSSStyleSheet interface: attribute ownerRule]
expected: FAIL expected: FAIL
[CSSStyleSheet interface: operation insertRule(DOMString,unsigned long)]
expected: FAIL
[CSSStyleSheet interface: operation deleteRule(unsigned long)]
expected: FAIL
[CSSStyleSheet must be primary interface of style_element.sheet]
expected: FAIL
[Stringification of style_element.sheet]
expected: FAIL
[CSSStyleSheet interface: style_element.sheet must inherit property "ownerRule" with the proper type (0)] [CSSStyleSheet interface: style_element.sheet must inherit property "ownerRule" with the proper type (0)]
expected: FAIL expected: FAIL
[CSSStyleSheet interface: style_element.sheet must inherit property "cssRules" with the proper type (1)]
expected: FAIL
[CSSStyleSheet interface: style_element.sheet must inherit property "insertRule" with the proper type (2)]
expected: FAIL
[CSSStyleSheet interface: calling insertRule(DOMString,unsigned long) on style_element.sheet with too few arguments must throw TypeError]
expected: FAIL
[CSSStyleSheet interface: style_element.sheet must inherit property "deleteRule" with the proper type (3)]
expected: FAIL
[CSSStyleSheet interface: calling deleteRule(unsigned long) on style_element.sheet with too few arguments must throw TypeError]
expected: FAIL
[StyleSheet interface: style_element.sheet must inherit property "type" with the proper type (0)] [StyleSheet interface: style_element.sheet must inherit property "type" with the proper type (0)]
expected: FAIL expected: FAIL
[StyleSheet interface: style_element.sheet must inherit property "href" with the proper type (1)]
expected: FAIL
[StyleSheet interface: style_element.sheet must inherit property "ownerNode" with the proper type (2)] [StyleSheet interface: style_element.sheet must inherit property "ownerNode" with the proper type (2)]
expected: FAIL expected: FAIL
[StyleSheet interface: style_element.sheet must inherit property "parentStyleSheet" with the proper type (3)] [StyleSheet interface: style_element.sheet must inherit property "parentStyleSheet" with the proper type (3)]
expected: FAIL expected: FAIL
[StyleSheet interface: style_element.sheet must inherit property "title" with the proper type (4)]
expected: FAIL
[StyleSheet interface: style_element.sheet must inherit property "media" with the proper type (5)] [StyleSheet interface: style_element.sheet must inherit property "media" with the proper type (5)]
expected: FAIL expected: FAIL
@ -177,21 +144,6 @@
[CSSRuleList interface: existence and properties of interface prototype object] [CSSRuleList interface: existence and properties of interface prototype object]
expected: FAIL expected: FAIL
[CSSRuleList must be primary interface of style_element.sheet.cssRules]
expected: FAIL
[Stringification of style_element.sheet.cssRules]
expected: FAIL
[CSSRuleList interface: style_element.sheet.cssRules must inherit property "item" with the proper type (0)]
expected: FAIL
[CSSRuleList interface: calling item(unsigned long) on style_element.sheet.cssRules with too few arguments must throw TypeError]
expected: FAIL
[CSSRuleList interface: style_element.sheet.cssRules must inherit property "length" with the proper type (1)]
expected: FAIL
[CSSRule interface: attribute parentRule] [CSSRule interface: attribute parentRule]
expected: FAIL expected: FAIL
@ -201,54 +153,15 @@
[CSSStyleRule interface: attribute style] [CSSStyleRule interface: attribute style]
expected: FAIL expected: FAIL
[CSSStyleRule must be primary interface of style_element.sheet.cssRules[0\]]
expected: FAIL
[Stringification of style_element.sheet.cssRules[0\]]
expected: FAIL
[CSSStyleRule interface: style_element.sheet.cssRules[0\] must inherit property "selectorText" with the proper type (0)] [CSSStyleRule interface: style_element.sheet.cssRules[0\] must inherit property "selectorText" with the proper type (0)]
expected: FAIL expected: FAIL
[CSSStyleRule interface: style_element.sheet.cssRules[0\] must inherit property "style" with the proper type (1)] [CSSStyleRule interface: style_element.sheet.cssRules[0\] must inherit property "style" with the proper type (1)]
expected: FAIL expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "STYLE_RULE" with the proper type (0)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "CHARSET_RULE" with the proper type (1)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "IMPORT_RULE" with the proper type (2)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "MEDIA_RULE" with the proper type (3)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "FONT_FACE_RULE" with the proper type (4)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "PAGE_RULE" with the proper type (5)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "MARGIN_RULE" with the proper type (6)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "NAMESPACE_RULE" with the proper type (7)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "type" with the proper type (8)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "cssText" with the proper type (9)]
expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "parentRule" with the proper type (10)] [CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "parentRule" with the proper type (10)]
expected: FAIL expected: FAIL
[CSSRule interface: style_element.sheet.cssRules[0\] must inherit property "parentStyleSheet" with the proper type (11)]
expected: FAIL
[CSSImportRule interface: existence and properties of interface object] [CSSImportRule interface: existence and properties of interface object]
expected: FAIL expected: FAIL
@ -270,15 +183,6 @@
[CSSImportRule interface: attribute styleSheet] [CSSImportRule interface: attribute styleSheet]
expected: FAIL expected: FAIL
[CSSGroupingRule interface: attribute cssRules]
expected: FAIL
[CSSGroupingRule interface: operation insertRule(DOMString,unsigned long)]
expected: FAIL
[CSSGroupingRule interface: operation deleteRule(unsigned long)]
expected: FAIL
[CSSMediaRule interface: attribute media] [CSSMediaRule interface: attribute media]
expected: FAIL expected: FAIL
@ -318,12 +222,6 @@
[CSSMarginRule interface: attribute style] [CSSMarginRule interface: attribute style]
expected: FAIL expected: FAIL
[CSSNamespaceRule interface: attribute namespaceURI]
expected: FAIL
[CSSNamespaceRule interface: attribute prefix]
expected: FAIL
[CSSStyleDeclaration interface: attribute parentRule] [CSSStyleDeclaration interface: attribute parentRule]
expected: FAIL expected: FAIL

View file

@ -1,8 +1,5 @@
[medialist-interfaces-002.htm] [medialist-interfaces-002.htm]
type: testharness type: testharness
[deleteMedium_called_without_argument]
expected: FAIL
[deleteMedium_removes_correct_medium] [deleteMedium_removes_correct_medium]
expected: FAIL expected: FAIL

View file

@ -1,8 +0,0 @@
[style-sheet-interfaces-002.htm]
type: testharness
[add_rule]
expected: FAIL
[delete_rule]
expected: FAIL

View file

@ -1359,9 +1359,6 @@
[HTMLLinkElement interface: document.createElement("link") must inherit property "sizes" with the proper type (7)] [HTMLLinkElement interface: document.createElement("link") must inherit property "sizes" with the proper type (7)]
expected: FAIL expected: FAIL
[HTMLLinkElement interface: document.createElement("link") must inherit property "sheet" with the proper type (11)]
expected: FAIL
[HTMLMetaElement interface: attribute httpEquiv] [HTMLMetaElement interface: attribute httpEquiv]
expected: FAIL expected: FAIL
@ -1392,9 +1389,6 @@
[HTMLStyleElement interface: document.createElement("style") must inherit property "scoped" with the proper type (2)] [HTMLStyleElement interface: document.createElement("style") must inherit property "scoped" with the proper type (2)]
expected: FAIL expected: FAIL
[HTMLStyleElement interface: document.createElement("style") must inherit property "sheet" with the proper type (3)]
expected: FAIL
[HTMLBodyElement interface: attribute link] [HTMLBodyElement interface: attribute link]
expected: FAIL expected: FAIL

View file

@ -3,9 +3,6 @@
[The LinkStyle interface's sheet attribute must return null; the disabled attribute must be false] [The LinkStyle interface's sheet attribute must return null; the disabled attribute must be false]
expected: FAIL expected: FAIL
[The LinkStyle interface's sheet attribute must return null if the corresponding element is not in a Document]
expected: FAIL
[The LinkStyle interface's sheet attribute must return StyleSheet object; the disabled attribute must be same as the StyleSheet's disabled attribute] [The LinkStyle interface's sheet attribute must return StyleSheet object; the disabled attribute must be same as the StyleSheet's disabled attribute]
expected: FAIL expected: FAIL

View file

@ -22,6 +22,7 @@ test_interfaces([
"CSS", "CSS",
"CSSFontFaceRule", "CSSFontFaceRule",
"CSSGroupingRule", "CSSGroupingRule",
"CSSKeyframeRule",
"CSSKeyframesRule", "CSSKeyframesRule",
"CSSMediaRule", "CSSMediaRule",
"CSSNamespaceRule", "CSSNamespaceRule",