mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
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:
commit
996756687c
34 changed files with 656 additions and 192 deletions
|
@ -22,7 +22,7 @@ pub struct 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 {
|
||||
cssrule: CSSRule::new_inherited(parent),
|
||||
fontfacerule: fontfacerule,
|
||||
|
@ -30,7 +30,7 @@ impl CSSFontFaceRule {
|
|||
}
|
||||
|
||||
#[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> {
|
||||
reflect_dom_object(box CSSFontFaceRule::new_inherited(parent, fontfacerule),
|
||||
window,
|
||||
|
|
|
@ -3,28 +3,67 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::bindings::codegen::Bindings::CSSGroupingRuleBinding;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::reflector::reflect_dom_object;
|
||||
use dom::bindings::codegen::Bindings::CSSGroupingRuleBinding::CSSGroupingRuleMethods;
|
||||
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::cssrulelist::{CSSRuleList, RulesSource};
|
||||
use dom::cssstylesheet::CSSStyleSheet;
|
||||
use dom::window::Window;
|
||||
use style::stylesheets::CssRules as StyleCssRules;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct CSSGroupingRule {
|
||||
cssrule: CSSRule,
|
||||
#[ignore_heap_size_of = "Arc"]
|
||||
rules: StyleCssRules,
|
||||
rulelist: MutNullableHeap<JS<CSSRuleList>>,
|
||||
}
|
||||
|
||||
impl CSSGroupingRule {
|
||||
pub fn new_inherited(parent: &CSSStyleSheet) -> CSSGroupingRule {
|
||||
pub fn new_inherited(parent: Option<&CSSStyleSheet>,
|
||||
rules: StyleCssRules) -> CSSGroupingRule {
|
||||
CSSGroupingRule {
|
||||
cssrule: CSSRule::new_inherited(parent),
|
||||
rules: rules,
|
||||
rulelist: MutNullableHeap::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new(window: &Window, parent: &CSSStyleSheet) -> Root<CSSGroupingRule> {
|
||||
reflect_dom_object(box CSSGroupingRule::new_inherited(parent),
|
||||
pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, rules: StyleCssRules) -> Root<CSSGroupingRule> {
|
||||
reflect_dom_object(box CSSGroupingRule::new_inherited(parent, rules),
|
||||
window,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
50
components/script/dom/csskeyframerule.rs
Normal file
50
components/script/dom/csskeyframerule.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -2,16 +2,25 @@
|
|||
* 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 cssparser::Parser;
|
||||
use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::reflector::reflect_dom_object;
|
||||
use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding::CSSKeyframesRuleMethods;
|
||||
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::csskeyframerule::CSSKeyframeRule;
|
||||
use dom::cssrule::{CSSRule, SpecificCSSRule};
|
||||
use dom::cssrulelist::{CSSRuleList, RulesSource};
|
||||
use dom::cssstylesheet::CSSStyleSheet;
|
||||
use dom::window::Window;
|
||||
use parking_lot::RwLock;
|
||||
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;
|
||||
|
||||
#[dom_struct]
|
||||
|
@ -19,23 +28,84 @@ pub struct CSSKeyframesRule {
|
|||
cssrule: CSSRule,
|
||||
#[ignore_heap_size_of = "Arc"]
|
||||
keyframesrule: Arc<RwLock<KeyframesRule>>,
|
||||
rulelist: MutNullableHeap<JS<CSSRuleList>>,
|
||||
}
|
||||
|
||||
impl CSSKeyframesRule {
|
||||
fn new_inherited(parent: &CSSStyleSheet, keyframesrule: Arc<RwLock<KeyframesRule>>) -> CSSKeyframesRule {
|
||||
fn new_inherited(parent: Option<&CSSStyleSheet>, keyframesrule: Arc<RwLock<KeyframesRule>>) -> CSSKeyframesRule {
|
||||
CSSKeyframesRule {
|
||||
cssrule: CSSRule::new_inherited(parent),
|
||||
keyframesrule: keyframesrule,
|
||||
rulelist: MutNullableHeap::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[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> {
|
||||
reflect_dom_object(box CSSKeyframesRule::new_inherited(parent, keyframesrule),
|
||||
window,
|
||||
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 {
|
||||
|
@ -47,4 +117,8 @@ impl SpecificCSSRule for CSSKeyframesRule {
|
|||
fn get_css(&self) -> DOMString {
|
||||
self.keyframesrule.read().to_css_string().into()
|
||||
}
|
||||
|
||||
fn deparent_children(&self) {
|
||||
self.rulelist.get().map(|list| list.deparent_all());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,15 +23,16 @@ pub struct 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 {
|
||||
cssrule: CSSGroupingRule::new_inherited(parent),
|
||||
cssrule: CSSGroupingRule::new_inherited(parent, list),
|
||||
mediarule: mediarule,
|
||||
}
|
||||
}
|
||||
|
||||
#[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> {
|
||||
reflect_dom_object(box CSSMediaRule::new_inherited(parent, mediarule),
|
||||
window,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::bindings::codegen::Bindings::CSSNamespaceRuleBinding;
|
||||
use dom::bindings::codegen::Bindings::CSSNamespaceRuleBinding::CSSNamespaceRuleMethods;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::reflector::reflect_dom_object;
|
||||
use dom::bindings::str::DOMString;
|
||||
|
@ -22,7 +23,7 @@ pub struct 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 {
|
||||
cssrule: CSSRule::new_inherited(parent),
|
||||
namespacerule: namespacerule,
|
||||
|
@ -30,7 +31,7 @@ impl CSSNamespaceRule {
|
|||
}
|
||||
|
||||
#[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> {
|
||||
reflect_dom_object(box CSSNamespaceRule::new_inherited(parent, namespacerule),
|
||||
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 {
|
||||
fn ty(&self) -> u16 {
|
||||
use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleConstants;
|
||||
|
|
|
@ -9,6 +9,7 @@ use dom::bindings::js::{JS, MutNullableHeap, Root};
|
|||
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||
use dom::bindings::str::DOMString;
|
||||
use dom::cssfontfacerule::CSSFontFaceRule;
|
||||
use dom::csskeyframerule::CSSKeyframeRule;
|
||||
use dom::csskeyframesrule::CSSKeyframesRule;
|
||||
use dom::cssmediarule::CSSMediaRule;
|
||||
use dom::cssnamespacerule::CSSNamespaceRule;
|
||||
|
@ -27,15 +28,15 @@ pub struct CSSRule {
|
|||
|
||||
impl CSSRule {
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new_inherited(parent: &CSSStyleSheet) -> CSSRule {
|
||||
pub fn new_inherited(parent: Option<&CSSStyleSheet>) -> CSSRule {
|
||||
CSSRule {
|
||||
reflector_: Reflector::new(),
|
||||
parent: MutNullableHeap::new(Some(parent)),
|
||||
parent: MutNullableHeap::new(parent),
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
window,
|
||||
CSSRuleBinding::Wrap)
|
||||
|
@ -54,6 +55,8 @@ impl CSSRule {
|
|||
rule as &SpecificCSSRule
|
||||
} else if let Some(rule) = self.downcast::<CSSViewportRule>() {
|
||||
rule as &SpecificCSSRule
|
||||
} else if let Some(rule) = self.downcast::<CSSKeyframeRule>() {
|
||||
rule as &SpecificCSSRule
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -61,7 +64,7 @@ impl CSSRule {
|
|||
|
||||
// Given a StyleCssRule, create a new instance of a derived class of
|
||||
// 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> {
|
||||
// be sure to update the match in as_specific when this is updated
|
||||
match rule {
|
||||
|
@ -73,6 +76,21 @@ impl CSSRule {
|
|||
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 {
|
||||
|
@ -100,4 +118,8 @@ impl CSSRuleMethods for CSSRule {
|
|||
pub trait SpecificCSSRule {
|
||||
fn ty(&self) -> u16;
|
||||
fn get_css(&self) -> DOMString;
|
||||
/// Remove CSSStyleSheet parent from all transitive children
|
||||
fn deparent_children(&self) {
|
||||
// most CSSRules do nothing here
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,61 +2,248 @@
|
|||
* 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::cell::DOMRefCell;
|
||||
use dom::bindings::codegen::Bindings::CSSRuleListBinding;
|
||||
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::reflector::{Reflectable, Reflector, reflect_dom_object};
|
||||
use dom::csskeyframerule::CSSKeyframeRule;
|
||||
use dom::cssrule::CSSRule;
|
||||
use dom::cssstylesheet::CSSStyleSheet;
|
||||
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);
|
||||
|
||||
#[dom_struct]
|
||||
pub struct CSSRuleList {
|
||||
reflector_: Reflector,
|
||||
sheet: JS<CSSStyleSheet>,
|
||||
sheet: MutNullableHeap<JS<CSSStyleSheet>>,
|
||||
#[ignore_heap_size_of = "Arc"]
|
||||
rules: CssRules,
|
||||
dom_rules: Vec<MutNullableHeap<JS<CSSRule>>>
|
||||
rules: RulesSource,
|
||||
dom_rules: DOMRefCell<Vec<MutNullableHeap<JS<CSSRule>>>>
|
||||
}
|
||||
|
||||
pub enum RulesSource {
|
||||
Rules(CssRules),
|
||||
Keyframes(Arc<RwLock<KeyframesRule>>),
|
||||
}
|
||||
|
||||
impl CSSRuleList {
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new_inherited(sheet: &CSSStyleSheet, rules: CssRules) -> CSSRuleList {
|
||||
let dom_rules = rules.0.read().iter().map(|_| MutNullableHeap::new(None)).collect();
|
||||
pub fn new_inherited(sheet: Option<&CSSStyleSheet>, rules: RulesSource) -> CSSRuleList {
|
||||
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 {
|
||||
reflector_: Reflector::new(),
|
||||
sheet: JS::from_ref(sheet),
|
||||
sheet: MutNullableHeap::new(sheet),
|
||||
rules: rules,
|
||||
dom_rules: dom_rules,
|
||||
dom_rules: DOMRefCell::new(dom_rules),
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
window,
|
||||
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 {
|
||||
// https://drafts.csswg.org/cssom/#ref-for-dom-cssrulelist-item-1
|
||||
fn Item(&self, idx: u32) -> Option<Root<CSSRule>> {
|
||||
self.dom_rules.get(idx as usize).map(|rule| {
|
||||
rule.or_init(|| {
|
||||
CSSRule::new_specific(self.global().as_window(),
|
||||
&self.sheet,
|
||||
self.rules.0.read()[idx as usize].clone())
|
||||
})
|
||||
})
|
||||
self.item(idx)
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom/#dom-cssrulelist-length
|
||||
fn Length(&self) -> u32 {
|
||||
self.dom_rules.len() as u32
|
||||
self.dom_rules.borrow().len() as u32
|
||||
}
|
||||
|
||||
// check-tidy: no specs after this line
|
||||
|
|
|
@ -22,7 +22,7 @@ pub struct 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 {
|
||||
cssrule: CSSRule::new_inherited(parent),
|
||||
stylerule: stylerule,
|
||||
|
@ -30,7 +30,7 @@ impl CSSStyleRule {
|
|||
}
|
||||
|
||||
#[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> {
|
||||
reflect_dom_object(box CSSStyleRule::new_inherited(parent, stylerule),
|
||||
window,
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
|
||||
use dom::bindings::codegen::Bindings::CSSStyleSheetBinding;
|
||||
use dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods;
|
||||
use dom::bindings::error::{ErrorResult, Fallible};
|
||||
use dom::bindings::js::{JS, Root, MutNullableHeap};
|
||||
use dom::bindings::reflector::{reflect_dom_object, Reflectable};
|
||||
use dom::bindings::str::DOMString;
|
||||
use dom::cssrulelist::CSSRuleList;
|
||||
use dom::cssrulelist::{CSSRuleList, RulesSource};
|
||||
use dom::stylesheet::StyleSheet;
|
||||
use dom::window::Window;
|
||||
use std::sync::Arc;
|
||||
|
@ -40,12 +41,33 @@ impl CSSStyleSheet {
|
|||
window,
|
||||
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 {
|
||||
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ pub struct 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 {
|
||||
cssrule: CSSRule::new_inherited(parent),
|
||||
viewportrule: viewportrule,
|
||||
|
@ -30,7 +30,7 @@ impl CSSViewportRule {
|
|||
}
|
||||
|
||||
#[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> {
|
||||
reflect_dom_object(box CSSViewportRule::new_inherited(parent, viewportrule),
|
||||
window,
|
||||
|
|
|
@ -22,6 +22,7 @@ use dom::eventtarget::EventTarget;
|
|||
use dom::globalscope::GlobalScope;
|
||||
use dom::htmlelement::HTMLElement;
|
||||
use dom::node::{Node, document_from_node, window_from_node};
|
||||
use dom::stylesheet::StyleSheet as DOMStyleSheet;
|
||||
use dom::virtualmethods::VirtualMethods;
|
||||
use encoding::EncodingRef;
|
||||
use encoding::all::UTF_8;
|
||||
|
@ -452,4 +453,9 @@ impl HTMLLinkElementMethods for HTMLLinkElement {
|
|||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-link-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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use cssparser::Parser as CssParser;
|
||||
use dom::bindings::cell::DOMRefCell;
|
||||
use dom::bindings::codegen::Bindings::HTMLStyleElementBinding;
|
||||
use dom::bindings::codegen::Bindings::HTMLStyleElementBinding::HTMLStyleElementMethods;
|
||||
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::js::{JS, MutNullableHeap, Root};
|
||||
|
@ -14,6 +15,7 @@ use dom::document::Document;
|
|||
use dom::element::Element;
|
||||
use dom::htmlelement::HTMLElement;
|
||||
use dom::node::{ChildrenMutation, Node, document_from_node, window_from_node};
|
||||
use dom::stylesheet::StyleSheet as DOMStyleSheet;
|
||||
use dom::virtualmethods::VirtualMethods;
|
||||
use html5ever_atoms::LocalName;
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,6 +244,7 @@ pub mod crypto;
|
|||
pub mod css;
|
||||
pub mod cssfontfacerule;
|
||||
pub mod cssgroupingrule;
|
||||
pub mod csskeyframerule;
|
||||
pub mod csskeyframesrule;
|
||||
pub mod cssmediarule;
|
||||
pub mod cssnamespacerule;
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
// https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface
|
||||
[Exposed=Window]
|
||||
interface CSSGroupingRule : CSSRule {
|
||||
// [SameObject] readonly attribute CSSRuleList cssRules;
|
||||
// unsigned long insertRule(DOMString rule, unsigned long index);
|
||||
// void deleteRule(unsigned long index);
|
||||
[SameObject] readonly attribute CSSRuleList cssRules;
|
||||
[Throws] unsigned long insertRule(DOMString rule, unsigned long index);
|
||||
[Throws] void deleteRule(unsigned long index);
|
||||
};
|
||||
|
||||
|
|
10
components/script/dom/webidls/CSSKeyframeRule.webidl
Normal file
10
components/script/dom/webidls/CSSKeyframeRule.webidl
Normal 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;
|
||||
};
|
|
@ -6,9 +6,9 @@
|
|||
[Exposed=Window]
|
||||
interface CSSKeyframesRule : CSSRule {
|
||||
// attribute DOMString name;
|
||||
// readonly attribute CSSRuleList cssRules;
|
||||
readonly attribute CSSRuleList cssRules;
|
||||
|
||||
// void appendRule(DOMString rule);
|
||||
// void deleteRule(DOMString select);
|
||||
// CSSKeyframeRule? findRule(DOMString select);
|
||||
void appendRule(DOMString rule);
|
||||
void deleteRule(DOMString select);
|
||||
CSSKeyframeRule? findRule(DOMString select);
|
||||
};
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
// https://drafts.csswg.org/cssom/#the-cssnamespacerule-interface
|
||||
[Exposed=Window]
|
||||
interface CSSNamespaceRule : CSSRule {
|
||||
// readonly attribute DOMString namespaceURI;
|
||||
// readonly attribute DOMString prefix;
|
||||
readonly attribute DOMString namespaceURI;
|
||||
readonly attribute DOMString prefix;
|
||||
};
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
interface CSSStyleSheet : StyleSheet {
|
||||
// readonly attribute CSSRule? ownerRule;
|
||||
[SameObject] readonly attribute CSSRuleList cssRules;
|
||||
// unsigned long insertRule(DOMString rule, unsigned long index);
|
||||
// void deleteRule(unsigned long index);
|
||||
[Throws] unsigned long insertRule(DOMString rule, unsigned long index);
|
||||
[Throws] void deleteRule(unsigned long index);
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ interface HTMLLinkElement : HTMLElement {
|
|||
|
||||
// also has obsolete members
|
||||
};
|
||||
//HTMLLinkElement implements LinkStyle;
|
||||
HTMLLinkElement implements LinkStyle;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#HTMLLinkElement-partial
|
||||
partial interface HTMLLinkElement {
|
||||
|
|
|
@ -8,4 +8,4 @@ interface HTMLStyleElement : HTMLElement {
|
|||
// attribute DOMString type;
|
||||
// attribute boolean scoped;
|
||||
};
|
||||
//HTMLStyleElement implements LinkStyle;
|
||||
HTMLStyleElement implements LinkStyle;
|
||||
|
|
|
@ -15,3 +15,9 @@ interface StyleSheet {
|
|||
// [SameObject, PutForwards=mediaText] readonly attribute MediaList media;
|
||||
// attribute boolean disabled;
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/cssom/#the-linkstyle-interface
|
||||
[NoInterfaceObject]
|
||||
interface LinkStyle {
|
||||
readonly attribute StyleSheet? sheet;
|
||||
};
|
||||
|
|
|
@ -26,11 +26,11 @@ impl ToCss for Source {
|
|||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
Source::Url(ref url) => {
|
||||
try!(dest.write_str("local(\""));
|
||||
try!(dest.write_str("url(\""));
|
||||
try!(url.to_css(dest));
|
||||
},
|
||||
Source::Local(ref family) => {
|
||||
try!(dest.write_str("url(\""));
|
||||
try!(dest.write_str("local(\""));
|
||||
try!(family.to_css(dest));
|
||||
},
|
||||
}
|
||||
|
|
|
@ -3,15 +3,17 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
|
||||
use cssparser::{DeclarationListParser, DeclarationParser};
|
||||
use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule};
|
||||
use parking_lot::RwLock;
|
||||
use parser::{ParserContext, log_css_error};
|
||||
use parser::{ParserContext, ParserContextExtraData, log_css_error};
|
||||
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
|
||||
use properties::PropertyDeclarationParseResult;
|
||||
use properties::animated_properties::TransitionProperty;
|
||||
use servo_url::ServoUrl;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use style_traits::ToCss;
|
||||
use stylesheets::{MemoryHoleReporter, Origin};
|
||||
|
||||
/// A number from 1 to 100, indicating the percentage of the animation where
|
||||
/// this keyframe should run.
|
||||
|
@ -68,6 +70,11 @@ impl KeyframeSelector {
|
|||
pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
|
||||
KeyframeSelector(percentages)
|
||||
}
|
||||
|
||||
pub fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||
input.parse_comma_separated(KeyframePercentage::parse)
|
||||
.map(KeyframeSelector)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// is, one autogenerated from the current computed values, or a list of
|
||||
/// declarations to apply.
|
||||
|
@ -260,8 +285,8 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
|
|||
|
||||
fn parse_prelude(&mut self, input: &mut Parser) -> Result<Self::Prelude, ()> {
|
||||
let start = input.position();
|
||||
match input.parse_comma_separated(|input| KeyframePercentage::parse(input)) {
|
||||
Ok(percentages) => Ok(KeyframeSelector(percentages)),
|
||||
match KeyframeSelector::parse(input) {
|
||||
Ok(sel) => Ok(sel),
|
||||
Err(()) => {
|
||||
let message = format!("Invalid keyframe rule: '{}'", input.slice_from(start));
|
||||
log_css_error(input, start, &message, self.context);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
use {Atom, Prefix, Namespace};
|
||||
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 encoding::EncodingRef;
|
||||
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)]
|
||||
pub struct Stylesheet {
|
||||
/// List of rules in the order they were found (important for
|
||||
|
@ -92,6 +136,26 @@ pub enum CssRule {
|
|||
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 {
|
||||
/// 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 {
|
||||
|
@ -220,7 +317,6 @@ impl ToCss for StyleRule {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl Stylesheet {
|
||||
pub fn from_bytes(bytes: &[u8],
|
||||
base_url: ServoUrl,
|
||||
|
@ -370,11 +466,12 @@ impl<'b> TopLevelRuleParser<'b> {
|
|||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
|
||||
enum State {
|
||||
pub enum State {
|
||||
Start = 1,
|
||||
Imports = 2,
|
||||
Namespaces = 3,
|
||||
Body = 4,
|
||||
Invalid = 5,
|
||||
}
|
||||
|
||||
|
||||
|
@ -403,6 +500,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
|
|||
// TODO: support @import
|
||||
return Err(()) // "@import is not supported yet"
|
||||
} else {
|
||||
self.state.set(State::Invalid);
|
||||
return Err(()) // "@import must be before any rule but @charset"
|
||||
}
|
||||
},
|
||||
|
@ -429,6 +527,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
|
|||
}
|
||||
)))))
|
||||
} else {
|
||||
self.state.set(State::Invalid);
|
||||
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)
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// 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);
|
||||
AtRuleParser::parse_prelude(&mut self.nested(), name, input)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[css-style-declaration-modifications.htm]
|
||||
type: testharness
|
||||
expected: ERROR
|
||||
[CSSStyleDeclaration_accessible]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[cssimportrule.htm]
|
||||
type: testharness
|
||||
expected: ERROR
|
||||
[CSSRule and CSSImportRule types]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
[cssstylerule.htm]
|
||||
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
|
||||
|
||||
|
|
|
@ -120,51 +120,18 @@
|
|||
[CSSStyleSheet interface: attribute ownerRule]
|
||||
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)]
|
||||
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)]
|
||||
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)]
|
||||
expected: FAIL
|
||||
|
||||
[StyleSheet interface: style_element.sheet must inherit property "parentStyleSheet" with the proper type (3)]
|
||||
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)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -177,21 +144,6 @@
|
|||
[CSSRuleList interface: existence and properties of interface prototype object]
|
||||
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]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -201,54 +153,15 @@
|
|||
[CSSStyleRule interface: attribute style]
|
||||
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)]
|
||||
expected: FAIL
|
||||
|
||||
[CSSStyleRule interface: style_element.sheet.cssRules[0\] must inherit property "style" with the proper type (1)]
|
||||
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)]
|
||||
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]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -270,15 +183,6 @@
|
|||
[CSSImportRule interface: attribute styleSheet]
|
||||
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]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -318,12 +222,6 @@
|
|||
[CSSMarginRule interface: attribute style]
|
||||
expected: FAIL
|
||||
|
||||
[CSSNamespaceRule interface: attribute namespaceURI]
|
||||
expected: FAIL
|
||||
|
||||
[CSSNamespaceRule interface: attribute prefix]
|
||||
expected: FAIL
|
||||
|
||||
[CSSStyleDeclaration interface: attribute parentRule]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
[medialist-interfaces-002.htm]
|
||||
type: testharness
|
||||
[deleteMedium_called_without_argument]
|
||||
expected: FAIL
|
||||
|
||||
[deleteMedium_removes_correct_medium]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
[style-sheet-interfaces-002.htm]
|
||||
type: testharness
|
||||
[add_rule]
|
||||
expected: FAIL
|
||||
|
||||
[delete_rule]
|
||||
expected: FAIL
|
||||
|
|
@ -1359,9 +1359,6 @@
|
|||
[HTMLLinkElement interface: document.createElement("link") must inherit property "sizes" with the proper type (7)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLLinkElement interface: document.createElement("link") must inherit property "sheet" with the proper type (11)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLMetaElement interface: attribute httpEquiv]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1392,9 +1389,6 @@
|
|||
[HTMLStyleElement interface: document.createElement("style") must inherit property "scoped" with the proper type (2)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLStyleElement interface: document.createElement("style") must inherit property "sheet" with the proper type (3)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLBodyElement interface: attribute link]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
[The LinkStyle interface's sheet attribute must return null; the disabled attribute must be false]
|
||||
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]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ test_interfaces([
|
|||
"CSS",
|
||||
"CSSFontFaceRule",
|
||||
"CSSGroupingRule",
|
||||
"CSSKeyframeRule",
|
||||
"CSSKeyframesRule",
|
||||
"CSSMediaRule",
|
||||
"CSSNamespaceRule",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue