Skip to content

Commit

Permalink
Support @supports (fixes servo#14786)
Browse files Browse the repository at this point in the history
  • Loading branch information
Manishearth committed Dec 30, 2016
1 parent c6ea1ec commit f7be374
Show file tree
Hide file tree
Showing 13 changed files with 393 additions and 12 deletions.
37 changes: 37 additions & 0 deletions components/script/dom/cssconditionrule.rs
@@ -0,0 +1,37 @@
/* 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::CSSConditionRuleBinding::CSSConditionRuleMethods;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::reflector::DomObject;
use dom::bindings::str::DOMString;
use dom::cssrule::CSSRule;
use dom::cssrulelist::{CSSRuleList, RulesSource};
use dom::cssstylesheet::CSSStyleSheet;
use parking_lot::RwLock;
use std::sync::Arc;
use style::stylesheets::CssRules as StyleCssRules;

#[dom_struct]
pub struct CSSConditionRule {
cssgroupingrule: CSSGroupingRule,
}

impl CSSConditionRule {
pub fn new_inherited(parent_stylesheet: &CSSStyleSheet,
rules: Arc<RwLock<StyleCssRules>>) -> CSSConditionRule {
CSSConditionRule {
cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet, rules),
}
}

}

impl CSSConditionRuleMethods for CSSConditionRule {
fn ConditionText(&self) -> DOMString {
"".into()
}
}
6 changes: 3 additions & 3 deletions components/script/dom/cssmediarule.rs
Expand Up @@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::CSSMediaRuleBinding::CSSMediaRuleMethods;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssgroupingrule::CSSGroupingRule;
use dom::cssconditionrule::CSSConditionRule;
use dom::cssrule::SpecificCSSRule;
use dom::cssstylesheet::CSSStyleSheet;
use dom::medialist::MediaList;
Expand All @@ -19,7 +19,7 @@ use style_traits::ToCss;

#[dom_struct]
pub struct CSSMediaRule {
cssrule: CSSGroupingRule,
cssrule: CSSConditionRule,
#[ignore_heap_size_of = "Arc"]
mediarule: Arc<RwLock<MediaRule>>,
medialist: MutNullableJS<MediaList>,
Expand All @@ -30,7 +30,7 @@ impl CSSMediaRule {
-> CSSMediaRule {
let list = mediarule.read().rules.clone();
CSSMediaRule {
cssrule: CSSGroupingRule::new_inherited(parent_stylesheet, list),
cssrule: CSSConditionRule::new_inherited(parent_stylesheet, list),
mediarule: mediarule,
medialist: MutNullableJS::new(None),
}
Expand Down
2 changes: 2 additions & 0 deletions components/script/dom/cssrule.rs
Expand Up @@ -15,6 +15,7 @@ use dom::cssmediarule::CSSMediaRule;
use dom::cssnamespacerule::CSSNamespaceRule;
use dom::cssstylerule::CSSStyleRule;
use dom::cssstylesheet::CSSStyleSheet;
use dom::csssupportsrule::CSSSupportsRule;
use dom::cssviewportrule::CSSViewportRule;
use dom::window::Window;
use std::cell::Cell;
Expand Down Expand Up @@ -77,6 +78,7 @@ impl CSSRule {
StyleCssRule::Media(s) => Root::upcast(CSSMediaRule::new(window, parent_stylesheet, s)),
StyleCssRule::Namespace(s) => Root::upcast(CSSNamespaceRule::new(window, parent_stylesheet, s)),
StyleCssRule::Viewport(s) => Root::upcast(CSSViewportRule::new(window, parent_stylesheet, s)),
StyleCssRule::Supports(s) => Root::upcast(CSSSupportsRule::new(window, parent_stylesheet, s)),,
}
}

Expand Down
61 changes: 61 additions & 0 deletions components/script/dom/csssupportsrule.rs
@@ -0,0 +1,61 @@
/* 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::CSSSupportsRuleBinding;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssconditionrule::CSSConditionRule;
use dom::cssrule::SpecificCSSRule;
use dom::cssstylesheet::CSSStyleSheet;
use dom::medialist::MediaList;
use dom::window::Window;
use parking_lot::RwLock;
use std::sync::Arc;
use style::stylesheets::SupportsRule;
use style_traits::ToCss;

#[dom_struct]
pub struct CSSSupportsRule {
cssrule: CSSConditionRule,
#[ignore_heap_size_of = "Arc"]
supportsrule: Arc<RwLock<SupportsRule>>,
medialist: MutNullableJS<MediaList>,
}

impl CSSSupportsRule {
fn new_inherited(parent_stylesheet: &CSSStyleSheet, supportsrule: Arc<RwLock<SupportsRule>>)
-> CSSSupportsRule {
let list = supportsrule.read().rules.clone();
CSSSupportsRule {
cssrule: CSSConditionRule::new_inherited(parent_stylesheet, list),
supportsrule: supportsrule,
medialist: MutNullableJS::new(None),
}
}

#[allow(unrooted_must_root)]
pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet,
supportsrule: Arc<RwLock<SupportsRule>>) -> Root<CSSSupportsRule> {
reflect_dom_object(box CSSSupportsRule::new_inherited(parent_stylesheet, supportsrule),
window,
CSSSupportsRuleBinding::Wrap)
}

fn medialist(&self) -> Root<MediaList> {
self.medialist.or_init(|| MediaList::new(self.global().as_window(),
self.supportsrule.read().media_queries.clone()))
}
}

impl SpecificCSSRule for CSSSupportsRule {
fn ty(&self) -> u16 {
use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleConstants;
CSSRuleConstants::SUPPORTS_RULE
}

fn get_css(&self) -> DOMString {
self.supportsrule.read().to_css_string().into()
}
}
2 changes: 2 additions & 0 deletions components/script/dom/mod.rs
Expand Up @@ -240,6 +240,7 @@ pub mod console;
mod create;
pub mod crypto;
pub mod css;
pub mod cssconditionrule;
pub mod cssfontfacerule;
pub mod cssgroupingrule;
pub mod cssimportrule;
Expand All @@ -252,6 +253,7 @@ pub mod cssrulelist;
pub mod cssstyledeclaration;
pub mod cssstylerule;
pub mod cssstylesheet;
pub mod csssupportsrule;
pub mod cssviewportrule;
pub mod customevent;
pub mod dedicatedworkerglobalscope;
Expand Down
9 changes: 9 additions & 0 deletions components/script/dom/webidls/CSSConditionRule.webidl
@@ -0,0 +1,9 @@
/* 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-conditional/#cssconditionrule
[Abstract, Exposed=Window]
interface CSSConditionRule : CSSGroupingRule {
attribute DOMString conditionText;
};
3 changes: 2 additions & 1 deletion components/script/dom/webidls/CSSMediaRule.webidl
Expand Up @@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://drafts.csswg.org/cssom/#the-cssmediarule-interface
// https://drafts.csswg.org/css-conditional/#cssmediarule
[Exposed=Window]
interface CSSMediaRule : CSSGroupingRule {
interface CSSMediaRule : CSSConditionRule {
[SameObject, PutForwards=mediaText] readonly attribute MediaList media;
};
8 changes: 8 additions & 0 deletions components/script/dom/webidls/CSSSupportsRule.webidl
@@ -0,0 +1,8 @@
/* 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-conditional/#csssupportsrule
[Exposed=Window]
interface CSSSupportsRule : CSSConditionRule {
};
1 change: 1 addition & 0 deletions components/style/lib.rs
Expand Up @@ -121,6 +121,7 @@ pub mod sequential;
pub mod sink;
pub mod str;
pub mod stylesheets;
pub mod supports;
pub mod thread_state;
pub mod timer;
pub mod traversal;
Expand Down
55 changes: 50 additions & 5 deletions components/style/stylesheets.rs
Expand Up @@ -26,6 +26,7 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use style_traits::ToCss;
use stylist::FnvHashMap;
use supports::SupportsCondition;
use values::specified::url::SpecifiedUrl;
use viewport::ViewportRule;

Expand Down Expand Up @@ -195,6 +196,7 @@ pub enum CssRule {
FontFace(Arc<RwLock<FontFaceRule>>),
Viewport(Arc<RwLock<ViewportRule>>),
Keyframes(Arc<RwLock<KeyframesRule>>),
Supports(Arc<RwLock<SupportsRule>>),
}

pub enum CssRuleType {
Expand Down Expand Up @@ -251,6 +253,7 @@ impl CssRule {
CssRule::Keyframes(_) => CssRuleType::Keyframes,
CssRule::Namespace(_) => CssRuleType::Namespace,
CssRule::Viewport(_) => CssRuleType::Viewport,
CssRule::Supports(_) => CssRuleType::Supports,
}
}

Expand All @@ -267,29 +270,37 @@ impl CssRule {
///
/// Note that only some types of rules can contain rules. An empty slice is
/// used for others.
///
/// `enabled` is set to false for invalid @supports rules
pub fn with_nested_rules_and_mq<F, R>(&self, mut f: F) -> R
where F: FnMut(&[CssRule], Option<&MediaList>) -> R {
where F: FnMut(&[CssRule], Option<&MediaList>, bool) -> R {
match *self {
CssRule::Import(ref lock) => {
let rule = lock.read();
let media = rule.stylesheet.media.read();
let rules = rule.stylesheet.rules.read();
// FIXME(emilio): Include the nested rules if the stylesheet is
// loaded.
f(&rules.0, Some(&media))
f(&rules.0, Some(&media), true)
}
CssRule::Namespace(_) |
CssRule::Style(_) |
CssRule::FontFace(_) |
CssRule::Viewport(_) |
CssRule::Keyframes(_) => {
f(&[], None)
f(&[], None, true)
}
CssRule::Media(ref lock) => {
let media_rule = lock.read();
let mq = media_rule.media_queries.read();
let rules = &media_rule.rules.read().0;
f(rules, Some(&mq))
f(rules, Some(&mq), true)
}
CssRule::Supports(ref lock) => {
let supports_rule = lock.read();
let enabled = supports_rule.enabled;
let rules = &supports_rule.rules.read().0;
f(rules, None, enabled)
}
}
}
Expand Down Expand Up @@ -342,6 +353,7 @@ impl ToCss for CssRule {
CssRule::Viewport(ref lock) => lock.read().to_css(dest),
CssRule::Keyframes(ref lock) => lock.read().to_css(dest),
CssRule::Media(ref lock) => lock.read().to_css(dest),
CssRule::Supports(ref lock) => lock.read().to_css(dest),
}
}
}
Expand Down Expand Up @@ -438,6 +450,21 @@ impl ToCss for MediaRule {
}
}


#[derive(Debug)]
pub struct SupportsRule {
pub condition: SupportsCondition,
pub rules: Arc<RwLock<CssRules>>,
pub enabled: bool,
}

impl ToCss for SupportsRule {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
unimplemented!()
}
}


#[derive(Debug)]
pub struct StyleRule {
pub selectors: SelectorList<SelectorImpl>,
Expand Down Expand Up @@ -625,7 +652,10 @@ impl Stylesheet {
fn effective_rules<F>(rules: &[CssRule], device: &Device, f: &mut F) where F: FnMut(&CssRule) {
for rule in rules {
f(rule);
rule.with_nested_rules_and_mq(|rules, mq| {
rule.with_nested_rules_and_mq(|rules, mq, enabled| {
if !enabled {
return
}
if let Some(media_queries) = mq {
if !media_queries.evaluate(device) {
return
Expand Down Expand Up @@ -659,6 +689,7 @@ rule_filter! {
effective_font_face_rules(FontFace => FontFaceRule),
effective_viewport_rules(Viewport => ViewportRule),
effective_keyframes_rules(Keyframes => KeyframesRule),
effective_supports_rules(Supports => SupportsRule),
}

/// The stylesheet loader is the abstraction used to trigger network requests
Expand Down Expand Up @@ -704,6 +735,8 @@ enum AtRulePrelude {
FontFace,
/// A @media rule prelude, with its media queries.
Media(Arc<RwLock<MediaList>>),
/// An @supports rule, with its conditional
Supports(SupportsCondition),
/// A @viewport rule prelude.
Viewport,
/// A @keyframes rule, with its animation name.
Expand Down Expand Up @@ -859,6 +892,10 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
let media_queries = parse_media_query_list(input);
Ok(AtRuleType::WithBlock(AtRulePrelude::Media(Arc::new(RwLock::new(media_queries)))))
},
"supports" => {
let cond = SupportsCondition::parse(input, true)?;
Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond)))
},
"font-face" => {
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
},
Expand Down Expand Up @@ -895,6 +932,14 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
rules: self.parse_nested_rules(input),
}))))
}
AtRulePrelude::Supports(cond) => {
let enabled = cond.eval(self.context);
Ok(CssRule::Supports(Arc::new(RwLock::new(SupportsRule {
condition: cond,
rules: self.parse_nested_rules(input),
enabled: enabled,
}))))
}
AtRulePrelude::Viewport => {
Ok(CssRule::Viewport(Arc::new(RwLock::new(
try!(ViewportRule::parse(input, self.context))))))
Expand Down
8 changes: 6 additions & 2 deletions components/style/stylist.rs
Expand Up @@ -388,14 +388,18 @@ impl Stylist {

fn mq_eval_changed(rules: &[CssRule], before: &Device, after: &Device) -> bool {
for rule in rules {
if rule.with_nested_rules_and_mq(|rules, mq| {
let changed = rule.with_nested_rules_and_mq(|rules, mq, enabled| {
if !enabled {
return false
}
if let Some(mq) = mq {
if mq.evaluate(before) != mq.evaluate(after) {
return true
}
}
mq_eval_changed(rules, before, after)
}) {
});
if changed {
return true
}
}
Expand Down

0 comments on commit f7be374

Please sign in to comment.